pax_global_header00006660000000000000000000000064123346611200014510gustar00rootroot0000000000000052 comment=29475884c15079a25c6d277eb8efa3a242918f5f .qmake.conf000066400000000000000000000000561233466112000131000ustar00rootroot00000000000000load(qt_build_config) MODULE_VERSION = 0.0.0 .tag000066400000000000000000000000511233466112000116240ustar00rootroot0000000000000029475884c15079a25c6d277eb8efa3a242918f5f LGPL_EXCEPTION.txt000066400000000000000000000022431233466112000137720ustar00rootroot00000000000000Digia Qt LGPL Exception version 1.1 As an additional permission to the GNU Lesser General Public License version 2.1, the object code form of a "work that uses the Library" may incorporate material from a header file that is part of the Library. You may distribute such object code under terms of your choice, provided that: (i) the header files of the Library have not been modified; and (ii) the incorporated material is limited to numerical parameters, data structure layouts, accessors, macros, inline functions and templates; and (iii) you comply with the terms of Section 6 of the GNU Lesser General Public License version 2.1. Moreover, you may apply this exception to a modified version of the Library, provided that such modification does not involve copying material from the Library into the modified Library's header files unless such material is limited to (i) numerical parameters; (ii) data structure layouts; (iii) accessors; and (iv) small macros, templates and inline functions of five lines or less in length. Furthermore, you are not required to apply this additional permission to a modified version of the Library. LICENSE.FDL000066400000000000000000000546611233466112000125010ustar00rootroot00000000000000 GNU Free Documentation License Version 1.3, 3 November 2008 Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. 0. PREAMBLE The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others. This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software. We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference. 1. APPLICABILITY AND DEFINITIONS This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law. A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language. A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them. The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none. The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words. A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque". Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only. The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text. The "publisher" means any person or entity that distributes copies of the Document to the public. A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition. The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License. 2. VERBATIM COPYING You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3. You may also lend copies, under the same conditions stated above, and you may publicly display copies. 3. COPYING IN QUANTITY If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects. If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages. If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public. It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document. 4. MODIFICATIONS You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version: A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission. B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement. C. State on the Title page the name of the publisher of the Modified Version, as the publisher. D. Preserve all the copyright notices of the Document. E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices. F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below. G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice. H. Include an unaltered copy of this License. I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence. J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission. K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein. L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles. M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version. N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section. O. Preserve any Warranty Disclaimers. If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles. You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard. You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one. The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version. 5. COMBINING DOCUMENTS You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers. The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work. In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements". 6. COLLECTIONS OF DOCUMENTS You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects. You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document. 7. AGGREGATION WITH INDEPENDENT WORKS A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document. If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate. 8. TRANSLATION Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail. If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title. 9. TERMINATION You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights under this License. However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, receipt of a copy of some or all of the same material does not give you any rights to use it. 10. FUTURE REVISIONS OF THIS LICENSE The Free Software Foundation may publish new, revised versions of the GNU Free Documentation 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. See http://www.gnu.org/copyleft/. Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. If the Document specifies that a proxy can decide which future versions of this License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Document. 11. RELICENSING "Massive Multiauthor Collaboration Site" (or "MMC Site") means any World Wide Web server that publishes copyrightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody can edit is an example of such a server. A "Massive Multiauthor Collaboration" (or "MMC") contained in the site means any set of copyrightable works thus published on the MMC site. "CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 license published by Creative Commons Corporation, a not-for-profit corporation with a principal place of business in San Francisco, California, as well as future copyleft versions of that license published by that same organization. "Incorporate" means to publish or republish a Document, in whole or in part, as part of another Document. An MMC is "eligible for relicensing" if it is licensed under this License, and if all works that were first published under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC, (1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008. The operator of an MMC Site may republish an MMC contained in the site under CC-BY-SA on the same site at any time before August 1, 2009, provided the MMC is eligible for relicensing. ADDENDUM: How to use this License for your documents To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page: Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License". If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this: with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation. If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software. LICENSE.GPL000066400000000000000000001045131233466112000125060ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. 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. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. 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 state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program 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, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . LICENSE.LGPL000066400000000000000000000643011233466112000126220ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE The Qt Toolkit is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). Contact: http://www.qt-project.org/legal You may use, distribute and copy the Qt GUI Toolkit under the terms of GNU Lesser General Public License version 2.1, which is displayed below. ------------------------------------------------------------------------- GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! doc/000077500000000000000000000000001233466112000116215ustar00rootroot00000000000000doc/compat.qdocconf000077500000000000000000000024061233466112000146270ustar00rootroot00000000000000alias.include = input macro.0 = "\\\\0" macro.b = "\\\\b" macro.n = "\\\\n" macro.r = "\\\\r" macro.i = "\\li" macro.i11 = "\\li{1,1}" macro.i12 = "\\li{1,2}" macro.i13 = "\\li{1,3}" macro.i14 = "\\li{1,4}" macro.i15 = "\\li{1,5}" macro.i16 = "\\li{1,6}" macro.i17 = "\\li{1,7}" macro.i18 = "\\li{1,8}" macro.i19 = "\\li{1,9}" macro.i21 = "\\li{2,1}" macro.i31 = "\\li{3,1}" macro.i41 = "\\li{4,1}" macro.i51 = "\\li{5,1}" macro.i61 = "\\li{6,1}" macro.i71 = "\\li{7,1}" macro.i81 = "\\li{8,1}" macro.i91 = "\\li{9,1}" macro.img = "\\image" macro.endquote = "\\endquotation" macro.relatesto = "\\relates" spurious = "Missing comma in .*"\ "Missing pattern .*" doc/macros.qdocconf000077500000000000000000000031761233466112000146350ustar00rootroot00000000000000macro.aacute.HTML = "á" macro.Aring.HTML = "Å" macro.aring.HTML = "å" macro.Auml.HTML = "Ä" macro.author = "\\b{Author:}" macro.br.HTML = "
" macro.BR.HTML = "
" macro.copyright.HTML = "©" macro.eacute.HTML = "é" macro.gui = "\\b" macro.hr.HTML = "
" macro.iacute.HTML = "í" macro.key = "\\b" macro.menu = "\\b" macro.note = "\\b{Note:}" macro.oslash.HTML = "ø" macro.ouml.HTML = "ö" macro.QA = "Qt Assistant" macro.QC = "Creator" macro.QD = "Qt Designer" macro.QL = "Qt Linguist" macro.QMLD = "Qt Quick Designer" macro.QQV = "Qt QML Viewer" macro.QSDK = "Qt SDK" #macro.qtcversion = $QTC_VERSION macro.qtcversion = "0.0" macro.param = "\\e" macro.raisedaster.HTML = "*" macro.rarrow.HTML = "→" macro.reg.HTML = "®" macro.return = "Returns" macro.starslash = "\\c{*/}" macro.begincomment = "\\c{/*}" macro.endcomment = "\\c{*/}" macro.uuml.HTML = "ü" macro.mdash.HTML = "—" macro.pi.HTML = "Π" macro.beginfloatleft.HTML = "
" macro.beginfloatright.HTML = "
" macro.endfloat.HTML = "
" macro.clearfloat.HTML = "
" macro.emptyspan.HTML = "" doc/qt-cpp-ignore.qdocconf000077500000000000000000000110271233466112000160300ustar00rootroot00000000000000Cpp.ignoretokens = QAXFACTORY_EXPORT \ QDESIGNER_COMPONENTS_LIBRARY \ QDESIGNER_EXTENSION_LIBRARY \ QDESIGNER_SDK_LIBRARY \ QDESIGNER_SHARED_LIBRARY \ QDESIGNER_UILIB_LIBRARY \ QM_EXPORT_CANVAS \ QM_EXPORT_DNS \ QM_EXPORT_DOM \ QM_EXPORT_FTP \ QM_EXPORT_HTTP \ QM_EXPORT_ICONVIEW \ QM_EXPORT_NETWORK \ QM_EXPORT_OPENGL \ QM_EXPORT_OPENVG \ QM_EXPORT_SQL \ QM_EXPORT_TABLE \ QM_EXPORT_WORKSPACE \ QM_EXPORT_XML \ QT_ASCII_CAST_WARN \ QT_ASCII_CAST_WARN_CONSTRUCTOR \ QT_BEGIN_HEADER \ QT_DESIGNER_STATIC \ QT_END_HEADER \ QT_FASTCALL \ QT_WIDGET_PLUGIN_EXPORT \ Q_COMPAT_EXPORT \ Q_CORE_EXPORT \ Q_CORE_EXPORT_INLINE \ Q_EXPLICIT \ Q_EXPORT \ Q_EXPORT_CODECS_CN \ Q_EXPORT_CODECS_JP \ Q_EXPORT_CODECS_KR \ Q_EXPORT_PLUGIN \ Q_GFX_INLINE \ Q_AUTOTEST_EXPORT \ Q_GUI_EXPORT \ Q_GUI_EXPORT_INLINE \ Q_GUI_EXPORT_STYLE_CDE \ Q_GUI_EXPORT_STYLE_COMPACT \ Q_GUI_EXPORT_STYLE_MAC \ Q_GUI_EXPORT_STYLE_MOTIF \ Q_GUI_EXPORT_STYLE_MOTIFPLUS \ Q_GUI_EXPORT_STYLE_PLATINUM \ Q_GUI_EXPORT_STYLE_POCKETPC \ Q_GUI_EXPORT_STYLE_SGI \ Q_GUI_EXPORT_STYLE_WINDOWS \ Q_GUI_EXPORT_STYLE_WINDOWSXP \ QHELP_EXPORT \ Q_INLINE_TEMPLATE \ Q_INTERNAL_WIN_NO_THROW \ Q_LOCATION_EXPORT \ Q_NETWORK_EXPORT \ Q_OPENGL_EXPORT \ Q_OPENVG_EXPORT \ Q_OUTOFLINE_TEMPLATE \ Q_SQL_EXPORT \ Q_SVG_EXPORT \ Q_SCRIPT_EXPORT \ Q_SCRIPTTOOLS_EXPORT \ Q_TESTLIB_EXPORT \ Q_TYPENAME \ Q_XML_EXPORT \ Q_XMLSTREAM_EXPORT \ Q_XMLPATTERNS_EXPORT \ QDBUS_EXPORT \ Q_DBUS_EXPORT \ QT_BEGIN_NAMESPACE \ QT_BEGIN_INCLUDE_NAMESPACE \ QT_END_NAMESPACE \ QT_END_INCLUDE_NAMESPACE \ PHONON_EXPORT \ Q_DECLARATIVE_EXPORT \ Q_GADGET \ QWEBKIT_EXPORT \ Q_INVOKABLE \ Q_DECL_CONSTEXPR Cpp.ignoredirectives = Q_DECLARE_HANDLE\ Q_DECLARE_INTERFACE \ Q_DECLARE_METATYPE \ Q_DECLARE_OPERATORS_FOR_FLAGS \ Q_DECLARE_PRIVATE \ Q_DECLARE_PUBLIC \ Q_DECLARE_SHARED \ Q_DECLARE_TR_FUNCTIONS \ Q_DECLARE_TYPEINFO \ Q_DISABLE_COPY \ QT_FORWARD_DECLARE_CLASS \ Q_DUMMY_COMPARISON_OPERATOR \ Q_ENUMS \ Q_FLAGS \ Q_INTERFACES \ __attribute__ \ K_DECLARE_PRIVATE \ PHONON_OBJECT \ PHONON_HEIR \ Q_PRIVATE_PROPERTY \ Q_DECLARE_PRIVATE_D \ Q_CLASSINFO doc/qt-defines.qdocconf000077500000000000000000000012071233466112000154010ustar00rootroot00000000000000defines = Q_QDOC \ QT_.*_SUPPORT \ QT_.*_LIB \ QT_COMPAT \ QT_KEYPAD_NAVIGATION \ QT_NO_EGL \ QT3_SUPPORT \ Q_WS_.* \ Q_OS_.* \ Q_BYTE_ORDER \ QT_DEPRECATED \ Q_NO_USING_KEYWORD \ __cplusplus\ Q_COMPILER_INITIALIZER_LISTS versionsym = QT_VERSION_STR codeindent = 1 doc/qt5-dita.qdocconf000066400000000000000000000015151233466112000147710ustar00rootroot00000000000000# Name of the project. project = qtpim # Directories in which to search for files to document and images. # By default set to the root directory of the project for sources # and headers and qdoc will therefore generate output for each file. # Images should be placed in /dic/images and examples in # /examples. # Paths are relative to the location of this file. headerdirs += .. sourcedirs += .. \ ../doc/src exampledirs += ../.. \ ../doc/src \ ../examples imagedirs += ../doc/src/images \ ../examples excludedirs += #Do not change the variables after this line unless you know what you are doing. outputdir = ditaxml outputformats = DITAXML sources.fileextensions = "*.cpp *.qdoc *.mm *.qml" headers.fileextensions = "*.h *.ch *.h++ *.hh *.hpp *.hxx" doc/qt5.qdocconf000066400000000000000000000054721233466112000140600ustar00rootroot00000000000000# Name of the project. project = qtpim include(macros.qdocconf) include(compat.qdocconf) include(qt-cpp-ignore.qdocconf) include(qt-defines.qdocconf) include(qtpim-cpp-ignore.qdocconf) # Directories in which to search for files to document and images. # By default set to the root directory of the project for sources # and headers and qdoc will therefore generate output for each file. # Images should be placed in /dic/images and examples in # /examples. # Paths are relative to the location of this file. headerdirs += .. sourcedirs += .. \ ../doc/src exampledirs += .. \ ../doc/src \ ../examples imagedirs += ../doc/src/images \ ../examples excludedirs += # The following parameters are for creating a qhp file, the qhelpgenerator # program can convert the qhp file into a qch file which can be opened in # Qt Assistant and/or Qt Creator. # Defines the name of the project. You cannot use operators (+, =, -) in # the name. Properties for this project are set using a qhp..property # format. qhp.projects = qtpim # Sets the name of the output qhp file. qhp.qtpim.file = qtpim.qhp # Namespace for the output file. This namespace is used to distinguish between # different documentation files in Creator/Assistant. Normal format for MP # projects should be: com.nokia.mp..version with version being # a number containing a major, minor and revision element. E.g. version 1.0 # becomes 100. qhp.qtpim.namespace = com.nokia.mp.qtpim.100 # Title for the package, will be the main title for the package in # Assistant/Creator. qhp.qtpim.indexTitle = QtPim Documentation # Extra files to add to the output which are not linked to from anywhere # using a qdoc \l command. qhp.qtpim.extraFiles = style/style.css \ index.html # Only update the name of the project for the next variables. qhp.qtpim.virtualFolder = qdoc qhp.qtpim.subprojects = classes qhp.qtpim.subprojects.classes.title = Classes qhp.qtpim.subprojects.classes.selectors = class fake:headerfile qhp.qtpim.subprojects.classes.sortPages = true # Do NOT change the variables after this line unless you know what you are doing. outputdir = html outputformats = HTML examples.fileextensions = "*.cpp *.h *.js *.svg *.xml *.ui *.qml" examples.imageextensions = "*.png *.jpeg *.jpg *.gif *.mng" headers.fileextensions = "*.h *.ch *.h++ *.hh *.hpp *.hxx" sources.fileextensions = "*.cpp *.qdoc *.mm *.qml" HTML.nobreadcrumbs = "true" HTML.templatedir = . HTML.stylesheets = style/style.css HTML.headerstyles = " \n" HTML.endheader = "\n\n" HTML.footer = "
Copyright (C) 2012 Nokia Corporation and/or its subsidiaries. All rights reserved.
\n" doc/qtpim-cpp-ignore.qdocconf000066400000000000000000000010341233466112000165300ustar00rootroot00000000000000Cpp.ignoretokens += \ Q_CONTACTS_EXPORT \ Q_VERSIT_EXPORT \ Q_GALLERY_EXPORT \ Q_ORGANIZER_EXPORT \ QT_BEGIN_NAMESPACE_ORGANIZER \ QT_END_NAMESPACE_ORGANIZER \ QT_BEGIN_NAMESPACE_VERSIT \ QT_END_NAMESPACE_VERSIT \ QT_BEGIN_NAMESPACE_CONTACTS \ QT_END_NAMESPACE_CONTACTS \ QT_BEGIN_NAMESPACE_VERSITORGANIZER \ QT_END_NAMESPACE_VERSITORGANIZER \ Q_DECLARE_CUSTOM_CONTACT_DETAIL \ Q_DECLARE_CUSTOM_ORGANIZER_DETAIL \ Q_DECLARE_CUSTOM_ORGANIZER_ITEM \ QTVERSIT_PREPEND_NAMESPACE doc/src/000077500000000000000000000000001233466112000124105ustar00rootroot00000000000000doc/src/snippets/000077500000000000000000000000001233466112000142555ustar00rootroot00000000000000doc/src/snippets/code/000077500000000000000000000000001233466112000151675ustar00rootroot00000000000000doc/src/snippets/code/doc_src_lgpl.qdoc000066400000000000000000000674021233466112000205020ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //! [LGPL v2.1] GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! //! [LGPL v2.1] doc/src/snippets/legal/000077500000000000000000000000001233466112000153415ustar00rootroot00000000000000doc/src/snippets/legal/CatharonLicense.txt000066400000000000000000000121261233466112000211460ustar00rootroot00000000000000 The Catharon Open Source LICENSE ---------------------------- 2000-Jul-04 Copyright (C) 2000 by Catharon Productions, Inc. Introduction ============ This license applies to source files distributed by Catharon Productions, Inc. in several archive packages. This license applies to all files found in such packages which do not fall under their own explicit license. This license was inspired by the BSD, Artistic, and IJG (Independent JPEG Group) licenses, which all encourage inclusion and use of free software in commercial and freeware products alike. As a consequence, its main points are that: o We don't promise that this software works. However, we are interested in any kind of bug reports. (`as is' distribution) o You can use this software for whatever you want, in parts or full form, without having to pay us. (`royalty-free' usage) o You may not pretend that you wrote this software. If you use it, or only parts of it, in a program, you must acknowledge somewhere in your documentation that you have used the Catharon Code. (`credits') We specifically permit and encourage the inclusion of this software, with or without modifications, in commercial products. We disclaim all warranties covering the packages distributed by Catharon Productions, Inc. and assume no liability related to their use. Legal Terms =========== 0. Definitions -------------- Throughout this license, the terms `Catharon Package', `package', and `Catharon Code' refer to the set of files originally distributed by Catharon Productions, Inc. `You' refers to the licensee, or person using the project, where `using' is a generic term including compiling the project's source code as well as linking it to form a `program' or `executable'. This program is referred to as `a program using one of the Catharon Packages'. This license applies to all files distributed in the original Catharon Package(s), including all source code, binaries and documentation, unless otherwise stated in the file in its original, unmodified form as distributed in the original archive. If you are unsure whether or not a particular file is covered by this license, you must contact us to verify this. The Catharon Packages are copyright (C) 2000 by Catharon Productions, Inc. All rights reserved except as specified below. 1. No Warranty -------------- THE CATHARON PACKAGES ARE PROVIDED `AS IS' WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OF OR THE INABILITY TO USE THE CATHARON PACKAGE. 2. Redistribution ----------------- This license grants a worldwide, royalty-free, perpetual and irrevocable right and license to use, execute, perform, compile, display, copy, create derivative works of, distribute and sublicense the Catharon Packages (in both source and object code forms) and derivative works thereof for any purpose; and to authorize others to exercise some or all of the rights granted herein, subject to the following conditions: o Redistribution of source code must retain this license file (`license.txt') unaltered; any additions, deletions or changes to the original files must be clearly indicated in accompanying documentation. The copyright notices of the unaltered, original files must be preserved in all copies of source files. o Redistribution in binary form must provide a disclaimer that states that the software is based in part on the work of Catharon Productions, Inc. in the distribution documentation. These conditions apply to any software derived from or based on the Catharon Packages, not just the unmodified files. If you use our work, you must acknowledge us. However, no fee need be paid to us. 3. Advertising -------------- Neither Catharon Productions, Inc. and contributors nor you shall use the name of the other for commercial, advertising, or promotional purposes without specific prior written permission. We suggest, but do not require, that you use the following phrase to refer to this software in your documentation: 'this software is based in part on the Catharon Typography Project'. As you have not signed this license, you are not required to accept it. However, as the Catharon Packages are copyrighted material, only this license, or another one contracted with the authors, grants you the right to use, distribute, and modify it. Therefore, by using, distributing, or modifying the Catharon Packages, you indicate that you understand and accept all the terms of this license. --- end of license.txt --- doc/style/000077500000000000000000000000001233466112000127615ustar00rootroot00000000000000doc/style/style.css000066400000000000000000000043601233466112000146360ustar00rootroot00000000000000a:link, a:visited { color: #00732F; text-decoration: none; font-weight: bold; } body { font: normal 400 14px/1.2 Arial; margin-top: 85px; } h1 { margin: 0; } h2 { font: 500 20px/1.2 Arial; } h3.fn, span.fn { -moz-border-radius: 7px 7px 7px 7px; -webkit-border-radius: 7px 7px 7px 7px; border-radius: 7px 7px 7px 7px; background-color: #F6F6F6; border-width: 1px; border-style: solid; border-color: #E6E6E6; word-spacing: 3px; padding: 3px 5px; } table, pre { -moz-border-radius: 7px 7px 7px 7px; -webkit-border-radius: 7px 7px 7px 7px; border-radius: 7px 7px 7px 7px; background-color: #F6F6F6; border: 1px solid #E6E6E6; border-collapse: separate; font-size: 12px; line-height: 1.2; margin-bottom: 25px; margin-left: 15px; } table td { padding: 3px 15px 3px 20px; } table tr.even { background-color: white; color: #66666E; } table tr.odd { background-color: #F6F6F6; color: #66666E; } li { margin-bottom: 10px; padding-left: 12px; } .cpp { display: block; margin: 10; overflow: hidden; overflow-x: hidden; overflow-y: hidden; padding: 20px 0 20px 0; } .footer { margin-top: 50px; } .memItemLeft { padding-right: 3px; } .memItemRight { padding: 3px 15px 3px 0; } .qml { display: block; margin: 10; overflow: hidden; overflow-x: hidden; overflow-y: hidden; padding: 20px 0 20px 0; } .qmldefault { padding-left: 5px; float: right; color: red; } .qmlreadonly { padding-left: 5px; float: right; color: #254117; } .rightAlign { padding: 3px 5px 3px 10px; text-align: right; } .title { background-color: white; color: #44A51C; font-family: Verdana; font-size: 35px; font-weight: normal; left: 0; padding-bottom: 5px; padding-left: 16px; padding-top: 20px; position: absolute; right: 0; top: 0; } .toc { float: right; -moz-border-radius: 7px 7px 7px 7px; -webkit-border-radius: 7px 7px 7px 7px; border-radius: 7px 7px 7px 7px; background-color: #F6F6F6; border: 1px solid #DDD; margin: 0 20px 10px 10px; padding: 20px 15px 20px 20px; height: auto; width: 200px; } examples/000077500000000000000000000000001233466112000126725ustar00rootroot00000000000000examples/contacts/000077500000000000000000000000001233466112000145105ustar00rootroot00000000000000examples/contacts/contacts.pro000066400000000000000000000000361233466112000170470ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS += examples/contacts/qmlcontactslistview/000077500000000000000000000000001233466112000206275ustar00rootroot00000000000000examples/contacts/qmlcontactslistview/ContactEditor.qml000066400000000000000000000103411233466112000241030ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Pim Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 Rectangle { width: parent.width color: "pink" property variant contact // contact that is shown in editView // ![Widgets for manipulating contact details] Column { spacing: 8 anchors { fill: parent leftMargin: 10 topMargin: 10 } DetailEditWidget { id: nameField label: "Name " value: contact ? contact.name.firstName : " " } DetailEditWidget { id: emailField label: "Email Address " value: contact ? contact.email.emailAddress : " " showPreferredField: true isPreferred: contact ? contact.isPreferredDetail("MESSAGE", contact.email) : false } DetailEditWidget { id: phoneField label: "Phone Number " value: contact ? contact.phoneNumber.number : " " showPreferredField: true isPreferred: contact ? contact.isPreferredDetail("CALL", contact.phoneNumber) : false } } // ![Widgets for manipulating contact details] function deleteContact() { contactsModel.removeContact(contactEditor.contact.contactId) statusBar.updateMsg("contact successfully deleted") } function updateContact() { // read in values from the input fields var values = [nameField.value, emailField.value, emailField.requestPreferred || emailField.isPreferred, phoneField.value, phoneField.requestPreferred || phoneField.isPreferred] if (!contact) { // create new contact var newContact = Qt.createQmlObject("import QtContacts 5.0; Contact{ }", contactEditor) setDetailValues(newContact, values) newContact.save() contactsModel.saveContact(newContact) statusBar.updateMsg("new contact successfully created") } else { // update existing contact setDetailValues(contact, values) if (contact.modified) { contact.save() statusBar.updateMsg("contact successfully updated") } else { statusBar.updateMsg("nothing to update, contact already is up-to-date") } } } function setDetailValues(c, values) { c.name.firstName = values[0] c.email.emailAddress = values[1] c.phoneNumber.number = values[3] if (values[2]) { c.setPreferredDetail("MESSAGE", c.email) } if (values[4]) { c.setPreferredDetail("CALL", c.phoneNumber) } } function cancel() { contact = "" } function resetToDefaults() { nameField.inputFocus = false emailField.inputFocus = false phoneField.inputFocus = false emailField.requestPreferred = false phoneField.requestPreferred = false nameField.color = "black" emailField.color = "black" phoneField.color = "black" } } examples/contacts/qmlcontactslistview/DetailEditWidget.qml000066400000000000000000000067201233466112000245230ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Pim Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 // ![Custom DetailsEditWidget is used for entering new or modifying existing contact detlais] Item { width: parent.width - 30 height: 20 property alias label: label.text property alias value: valueText.text property alias color: valueText.color property alias inputFocus: valueText.focus property bool isPreferred: false property bool requestPreferred: false property bool showPreferredField: false property string old Text { id: label font { family: "Helvetica" pixelSize: 15 bold: true italic: true } } Rectangle { id: inputField width: 180 height: 20 anchors.left: label.right anchors.leftMargin: 6 color: "white" border { width: 2 color: "darkgreen" } TextInput { id: valueText anchors.fill: parent anchors.leftMargin: 5 font { pixelSize: 16 family: "Helvetica" } onFocusChanged: { if (focus === true) { // when entering text field old = valueText.text } else { // when exiting text field if (valueText.text !== old) { valueText.color = "red" } } } } } Text { id: labelFav anchors.left: inputField.right anchors.leftMargin: 5 font { family: "Helvetica" pixelSize: 15 bold: true italic: true } text: "favorite:" visible: showPreferredField } Rectangle { anchors.left: labelFav.right anchors.leftMargin: 5 anchors.verticalCenter: labelFav.verticalCenter width: 10 height: 10 color: isPreferred || requestPreferred ? "black" : "white" visible: showPreferredField MouseArea { anchors.fill: parent onClicked: requestPreferred = !requestPreferred } } } // ![Custom DetailsEditWidget is used for entering new or modifying existing contact detlais] examples/contacts/qmlcontactslistview/GenericButton.qml000066400000000000000000000041061233466112000241130ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Pim Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 //![Generic Button] //![Create Button Rectangle] Rectangle { //radius: 5 height: buttonLabel.height color: "black" //![Create Button Rectangle] //![Create Button Border] border { width: 1 color: "darkred" } //![Create Button Border] //![Button Clicked] signal clicked property alias buttonText: buttonLabel.text //![Button Clicked] //![Button Text] Text { id: buttonLabel font.pixelSize: 12 anchors.centerIn: parent color: "white" } //![Button Text] //![Setup Button Mouse Area] MouseArea { anchors.fill: parent onPressed: { parent.color = "white" } onReleased: { parent.color = "black" parent.clicked() } } //![Setup Button Mouse Area] } //![Generic Button] examples/contacts/qmlcontactslistview/doc/000077500000000000000000000000001233466112000213745ustar00rootroot00000000000000examples/contacts/qmlcontactslistview/doc/images/000077500000000000000000000000001233466112000226415ustar00rootroot00000000000000examples/contacts/qmlcontactslistview/doc/images/qmlcontactslistview-edit.png000066400000000000000000000506261233466112000304220ustar00rootroot00000000000000PNG  IHDRXL찈sBIT|dtEXtSoftwaregnome-screenshot> IDATxwx&M6ɦCMzQDޛkC˽*t ]HB!$7nzu1$ȽP9Cl{5ct @ˡW(|XG%SmKDA z }`[NQ!Uװ>\Y 3dTՠ3v]ݻB[c8q.]?ugM'K aPsǹ^fL% M#/{^:cFa8Z@țSn}ed_dx!#ڷon/gHk>䝹DZn=Z2GcXv2*x.|'}v\8˸ QnB^eT vLFǁu=C1w *nGdtJ9{0_yݙ"Yv|=36s>TH G㶟`|{_qmow5Pqy5/ݼ\<ȃQ؟S(5ƔLyɻpj/[woyx S?ORFs8[n%qz!@Dc@ARs`Rs^C4bwg8r8u( Z˽IWt Ծ`Lp#jt2%ûmcmEpn7m0=gw-otc^A;IbYʪey}ڻWz {1e&%:e/$nH=*xf'ͽoTaxkYܭ6̗f(R<^PYFe܉DCX3O&*+DH$3@L)Z]}O&# b!vbk=" -a}0&5%5ŕpD#ugA0~ Y2/]'9-ܢ =_AO&?30|# L@%YqThӜoGe&jm=T].Z?.#@ IR͗{3P4WvaI' `Q`YEZWغn#|h8$|g/jHK$7_+@ϵb 8Ш ՟0o_*K]~î\fHθ Ldm8ڶ|h9"2a-xׁ{q2$sSt_{a"gif=ч7'$HY"\@F׍:b38=g5QbgCK|crND>TF(yhA t>Հ*_Ϝ*m|});3sOSdZlKϣP YJ_CNÞi;KW:c+=Mj;Q|>cJM@5y'eofS@a\ Jɭ&PԞ> lLlnЅb(̗˿bv9c-1CWYFUM1,e4 `( '?:I- O-6ŧacxs&N7R.t VoЗp;;3{$g^x)JgN0}91/d">ݡcyp%`r vg Λ9n["4~PscPSjN~෶ -xːY,|KȿhDp`|c[pץ{xdׄZ y= ed  5 ~0}*Jp ^fLiRPdm}]ef(d"k\gZ UcT{c,jޟR+)7ȱ<֢/Ω ɔS][,Uk-98x6U2굢y?h+QeX?o4^a>I ?p@ @@\̈́z @ x.At@ >@ @@\""@ <!m(DxC=)a,"|=Ó`E dav٧3bКH̑MZ_YS2v;yob$3`zNy~Nf s0}^kǯ!o4&m1||zzѵs@8x"pHG1f )ҳ !o8M,] zB}>eX&Am} ϼU.%E踖]c̈́Whj O۶'o{&(_ mKc[_)&6q]B&F'=M!wR+"Yo4sUGM!WcBLtwӸ_B% *Uzc":=]=9=>j M"WiK=Mc٢jǧ{61;L29e&;-};y3LC)#=(3U3?&Z獬J(6G*cP吢#pAOAz9"w`'6.NzP"% і%6Jn8Jk5`>=_G,wHnF<5 #iӢkRYfqsБ_ZMfz>x`3)MFN%.)IT)|2U.FLnhƛ^PSDZI ̐;T # aD[͜rVnP3ON{!<Ws4+;O=]fdW,7bJ#y_g璨Ѹ ^1G3֋6O&ur0q/ZJ=־T=Xwz58yaέۄ|PfMF;{_F) w[ kӛݛ)cI6H<{R;CojWٹs0]nލ{OPMHѾ[(&0FXF{tЮ~X&NNzѾ%JB{@9?a̿ďs)Uܳ>/Egeؽe7QyByi^)ڋGЧTtx7kRMSlFp瑌.F^GwI($`CsG!Ts1Oµ@:bjm WY*?YƢ6NH*?qyе6o!o>?Iy}zv,VL%XP/,lM]+He r_He2߯_œWHW0Fw#!a(mGw_pՄ$%EIq5>{Y?^͵b,?uo8)ʹk9m݋ Цv-pe,{e&|@sFޝŠ/N`vb3|Eg҄5-<0g0eng+8oDVX%껒kG(5Pp[\ xԡ ?5[9unַ|GeGsJG|8 фR߰lܻ>~_k#Λ5]C'T網lLa'_xl 7eݾ#٘D6Acv&ڲ#=}M7mۆbYxb'[I0{z5ؿ$cX=ovp\F&ռa<6ֿXU=xRL5]pTrnNLs*1X "(h(ɩ[$#K@'ʒ uel_9w^!۶)cdo>\ BpTpi\F9LY<@<l';5l ĩ֓^U#I@ؕF?`a2P|e-cY}F x_ <nf{OlK5K://(=PG[7\edÆ9r2w<w&GP}O{߽].'|93xo`ڒ(3u|Ʉ``m7)=S2?ɽ{9-V$ų3,oЅN\J5Pzj3~2|Y9c ro*:\Ƹa-_f唼2Oa8^2+M?eqDianUu6!5߱ɭT7wdlC9}* ?v!N`SDXL\|wimV}ޓ}[wr)w)dٜ(|Wߟm[{(_*e]]ɥ!ˢ.ssTcM=rn^O r|W8Q j*x3_׫9=yimY6s.],;ñ| dUYܬfim8Sʁ Y9,]A/$G3Pfr.uӛؔ"_nJ;%2˓T9wd/y }YqҎQb+0}7ERҀw͖9 qD$JK˦,]ݤ]GA9o"mL6GLNTNrG >/0P9%)9&)8cPud:=S .KCe޳zu5U:28*2M]Ěo9}&/vu,iLLqi<&u GTpq'}XD\l2Gnj mЩ5(̶㔄`T/,k7Cx ]pd dD.SZ"7 >_s5N^uSJj0d u+c՚. Vk7}qA~;Ζܪ`0ɭ? V0;D|yWBۥ 6qxZK#V~+YpCDEN.ŕoEp\H"·=],{݋Aќ5g U\xyD z0*ft&D-陔1 \D/#>= 4A¸Ł47B%*8z#DtRxGubm:MZ:z|f^ѿ 9qAfu1Հ}'%VmA][,6lh%#\*^.l.\bG N#: ȝo5wP=QE+c1T38.$f-]Fg@}^ԓLCk1cw3韗K-;2fЛ>WKg}&0[>yKlDI|,\hݦ+}h܍' ,BU(y#R(Ϻ^Xu*zN[)7[@ߖf^)P̴d&%:z\MTݳ=F F d(5T@tj!:ıʢ42j6*z_2Z猪+M𡹛TYĖAG[te6W v~!?b[8sFxaN%f|UL5rn+xc|g+7H*L`Dgc\:?ӷCL1H}+{"otov=en$m(u绘/T1֦|6ŠpD)*Jkk$ "RɽSu[Iqy@gׯ+o# #3>fAdmwMvC` IDATow18d*,Bi48~E(֧SS3Ȋ}?bqC+k$iXzP} ?TæǢͽw>%+'2*YxΔfubNe!F~˃T8̿v0Aw[s=Z@llB !xH*9ͲjMV6[O5{aN{[ ܽ={ϓzouC~4b>ۛ4qy7 n6/8Jr_ N"jbٲ`#wzФcPĉEآU,|~ՃJdK 5=wPx{~Ʉ4 丹 yDMD22P|7-mK]=Jua5-G .R<ċs=A:>\vC#>PHb <Wy$@-LG𰾿 K+ZR>4l`Rer YFrR[8!RRKk*/`ݾM9s𥹛UxV}WY: i:Jj6l$z͆ڿSbTUH6@)/b޾]L7WA8bE`6hut-EF2oTH%]+5)ѫ.T񢹇52Xb!%S6 œS,F^k5o{f͟Ũ -|(ԁ[M~}Vvȩ 7 @M&QkbMďF(j17Pj9>_w E,QsO2q4ikfҧ/2ph3v/eBlŻ(N|JzӾz*n`7VwLaO^ܕȨt\0ˏo'@yv)S60l>9\?c~0vrs(R~G><c3(a/8yW^ϕ?gjZ KZnO{w̩lBc|\6]" (`a 2}#m Xs(ш6K̻5}59Gx-JY1'$.%rn67pQm'/x^M&Ck_pݽ܊F-;6`[<ҧk O"JO:ى1)0GKsGGM~^)ܩ9~W=AnXg(+0Ƥ%{ T%^)@= GN#sz:Y,ggpcU5O.h@?0e܄DMGd^&/ gE;1o7qYQ^UԅG|pSr>|QIlޔf,pڏަoKu9*KX=[iуe>x[Nfr{;ȔI_j፣#z|;ab˰ɴ@"/Cƍoxh) m.C%bb1 W~VceԊ!?6@RdԓY #/ƨ*ݭ1 -hX{ۜ8OO_Ĕ;oE,vuDVʷ+#L*Q_28}?rVCm[>Z޾*R`:K$|`dloOd:uWbUWzBĖ EV0̰ L%jb Ę0Ԩ(՚pj tңc2(R[coeD[AI+kK:S$Ba-}d ФVR1!PXw7,STh捝^MI*Lx0B_~U\K"FOZ[Dܝ$Wbt 0 ]dEy %q\*$+.=)@4^}1WDԚM}P]pT tj G_ CiHtL&jkBk]4*j[bspIDaimTL&+yqXz(: HN]qyuhssR2egQ֨PMe l>L*\WFčSϡDiС#V*:K?&(~~}U%U[bJM-+dnC;^5JlZ!IWUw=7ŤVHʭW뗹T1mZIE %/q&.7 ڶ%FRoɣۧGWYE[=W DWGu,ZKJF& 2\@Yx(AK 翢Ӟ xnBc 7XٴSH[DykgOq W k"#EU^t=~+1a}xc="f9EB !\O!"@@\@ @@\""@ <!7M A@ {@=TQ%@@\/h5iN>>03?ZIʄL'dY{(~b=pb.i)1F K{1\.'WjJAMyy19ͪqLzȡ@׈F,nfW y7Nr*!^ SeAwK(D0WǕ4 _X8D`;y5$s6cəRxQ7+':&}k?c-wv2fo=ї@b17{>Q&%sAH[!1!kvb!:˾0$z$T`&V+on']+$*AK=; i$W +!C%p7K~tH;P p"USLrf.Z |Cyjua"{Ϗ689R$R`;Bl ԔqڈƓ@E:TĖ.:IQ˜Ss7.4wZ :èwLbKt`Csy鱤il)CE$fR/'X2)!҈ڍ+hxog'f3(RК~d7Vssd/^ގdFbx$/cҫ;xw$j1u?`W3j, E*>Jx\c3%!s'oTV㶪 BT(zl*I*^XJ,I `%d![vR/wQ|aL*laҷ9UU 0wiѬ8frd{_sEщV%5<,fa;M\ ܸZ &3lZ2Ӂ}gľ VJnJ9mᨿ29FJi1_5!i4j GGqidD`bK 645Af١dkAd9˙؆T R& 6jƧ 1C1gw[sD+"xol>{1_+"^9+߳E"W'9 T\=9!y{H}n3GZR'^]tE&ѫǬɇQcKscףEp߻hǗYy9jWY`Ĉ v]$s"eF 3o&=1v_9&m+{wfT!gϨIO2 |9 Ƚ[ڥ1ɔYHQtbBˌ.FG-k9XJoq6HJ9-SRx2JLC 9MRO,aׇ0H&v"5߮bwR Jn^O+B귣MEd.WU qq=,T`tcjJjW[mrFMbLM{10\ۿn 0\Ř*rEh.rBӔ HEQm8M.5\gĀ>[eV`!J-׶sԖp~b )gueOi@$b3,O9OWF&X`iU,A"ai"Dud *^@<5Iή`CiUW r~hʺ[q N6.G[F҄HCcReզihni" S3Ŝv<Y5^u(f(JnJC ٣ii'ͳΐK] +Dqײ>sf-9feQ& Eɜϫm:J-WeԨS0Ku-q w>bRd[E;ׯlɫփͼ(J8<VV+j2lTDSȵBtЭw7RD:a&~դ$>Kr<˾ dbk$?6 #.U֥$~*DsIT)}@5IY(֖@M*æ"v_1|ZZ~vXb3)lj3!f8_(+* C (cORGB Z9u"71@L4; y$(0ǕҐPw DAFX<8[=*F[duI$ `(:@ .~߻C37 T*t~S: Td^Tԋ@IKNk^N_C5Dǚ`oIl:5 %c(AN!8LU,ΘUav"HV ں{LEWYq4 @}`:GL_L̏VNŬMCqx/S ~g ݬq '􉃴ܺF96$'ܺL)ffaNG ȿ~(,]rFeO1'+9ZPx#̋'Qf~(lk˞,70cs MsصGU+ JnfR-/4N!ඏ<HiT{jPX#~Hр;MuC1/6ƁbN$䣓(h(.ՐJlE`hV"[)L9˚ob1R&L)S;˚;Vfth#io,{]S-&yss3r3 ȐW:+ Iǘ 49b;4f4`@dF49/h4g@Ks\ ;mi@Y4%gi}C3y V_M䥴}Yݝeɀ K,Y $YE {xS +ߒive`l:Uɋe 3ﯟѻ 7ܺ*7ovgqOWyy/{HStZ~1c;ΜZ_nd5mi]0,ZFӠ mIcmynmJ,K_O._Y|rMҌM^A__ֿ0vK=ܓ56fM̓zwenoˈa/乿/KϢx8 {dsLדiweAo#C}sM ґfqK I볅oqW,am5z?,C<YtȪx?  <Ȫ(J\v/rlP :%@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(,VIDATJD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JD@(JdF^lP2pe'n_W^?dJ$I. 5V_ʏsִ4Z2|⫳U=#@ ?ߔ!~lVxݧ+o]wߑ-|}ĺBj!drN޻O=>c._woM_JzEGcO>'3Mʁ>79CG|S~i6nϜ#M|[aiZ2wC[?|7g'&We>lش.~83f$ö?b\ul2;}sw'I229;g|Dz̾}ۗi>wn7>-o'S.{ I.^x>=^\sWɳ/7&-kg߳C07_|rřo<ؘmA;LYI_w/'ksi+CI׵A?_ڷ/&i?9gɂZ{M68-G\unq}~[^[yV3zՙdq㨯MC.]IlQg5=7Ϟt,y0WyTzs毒%}+Nm9۵/x*+͝2(<ͷ^9&\tov/r;X>Mg7MignH>ݓ eLo̹{Ss?fP29s)Zz^a4,~yE޼ˤfdv<9rO)']/gz];͗;7o֙NFNuWw;gCg77]KS2>mj۱78%'2LXpfr#{ߴM&W^Ko')߿,}|~|ĮsgkW +mӯ=6ߝ#wmW'>+;}AN|ϺsX-@f6Œt}l4q|œ~r{ސ5/5ássgȩɯfU{':U?uYeY\tndn{?ֺ,~x~=. '\#>sN/ɲgy˲1i;>~l{r̯Ͳ4 7o+2iwk^)u6 ?LsGk˩g~m>ѻߵ,i\sG_ܻ4<+5&_N䈴-ۮI/cs17q3|~:銼se/ok=Wtm΀M+|C9vi%+rr~'wYxOamoP}kg]?4л -Jƶ$g\CᖇI-Yg҆9eӆHO[3C gа֜ 7boI>oC޸.kל56(#G*'L+'eXgxfm+̑ONߑ.ɽ_ceދOu7kwxK>ysrG֍>g%teq6+gEޒ&\Pұ3z;O2";g䮞U/" n[[.3{Nбm<ݹϘ.^7^ɢjkI_dQWOs_SGs2;c߻`ôlSvߕ?^kn?gW~0kS/ɢI6,{}Y=s37#3gEY:.䏽Oh,~4?%ypf[[eÑc6\F2gfq+3/W4/;w?IҶhQ]rp=ƒyt5Giy~}SN[s涬=swˤMijjdɢK2p]wNʐFs?-ړ):ی{;?yg>SG;;9'fY=^ؗ#f1cIa̟|)0?y\v|otvvs~sbÌ<4{QLg[3[o~dqӛ!kЖڒ̿mntǞ;"m9!Zm|;Ϻi-2pw49Kd~e;\u'?\o^^?_k"sx{:V=Hҙ |v9rxsyO$34SOY~G3gPs;˪XԗL W^Rq GnWm<6便ܚW, YwR^̿sfkQߗn,XdPFF>>qYwMA̻6Uզq3+>3w=,$dFg^؟v?8hM159Yh/X%k̺o #o.ȢFYwhWɓ{M_9>ct󎙙HS-1zxe,M!kgIE{ןs8iϢex֚L\-]짝 IDATxwtTI&3iz(]ԋ~ŮE+APz'tBB{&dyH$$!}=359Bq!+ $"' B!|""' B!|""' B!|""' B!|""' B!|""'ڦ_IZi;U&2 !85!@lY嫰`$= WN!MX4v,\v6.hqCoPP.ަVSΦCq !i@mJ.z`/$'k+66Urp[,^HfF ^ֽzcx5_= i.50\XUXI[s\Lrz.yÌh%31Kk%/#@{r>0!UN Nr6/#ŗrdBrSٶd {lrgmbdT!0<8HL"iw2?pEg#G7'CGȱ+9V ^3ik( jO.EYGHں_CQ?F#{6묪Co33+yrC#VMX4/D)CJ}XVXVoߣ JsVku[PzM+ s n|4MŨ|KMpYc8=h1$^ H_'Bq4zdocב2D!# "dW/8k+)62:^>kY|?%帽^\F-`h&@@=N?h>@ ^C \z잹{N{I:y Sxhbtv`]j&BuN&k$@ln]]ill_cq?_S֢ѨSC\u?vc+( ` &,xߊً^Ji Ns\K,:5Z5Ql|4B!pر},}6*`'srJө`V'= !Dɒ{e~g5ZLhj>6U=vSfsֈѠq 5ZU5BwhdhC[Ӫ͉(3:ģ+ 9?q1S_xҪfٰr/* ͛5iޣBA.r,#жfrې;wW2Ym6&Ҏ@H[q. $) h M=t)1LNha@3^3BqB!q,{M*>ҝH͵H J.=e lɌ Lfutx~:O{Iw Ġq L '2_BsOyeyڍVӫQV* BgO^k*i#G(V@oߨF Tz3z-xS9v&43(p*q*u,P@Go2!8!qBH!BH!B42KXd#)R)肈XrO^~1vs.FnˉѺ{X7o~m4F\>Vr88GmIzz.ti3 \?x2Q!ΙF@:o֒P/nĝu@ԜP?R:`nN\lj 1=n).\&o. A'bKg9XюnxXWi5%"r-d+.&>rvlI|AF.uJΜ,O*ң*%Gw%,K*QdE2&N{}G;޺Y2qڬ(у\<Z7?݉YL+Co[EP#ڷKOUF7?c7]O_~ͩaH⡧G3<B4pG.i Sb)߱M;r 35ۏ8v%e$]_$IZEWZi&6,NeD:vhV)S^ aiЖ ;&S݆[_ީ K+ZuL "K 6}{R;B%g77޿!txW?J6[XlgzpLn&Cb:s1?7[qm׺!9_,/?WXdw!8x":7=ۚ!aAٯ%$r+oEϱ (>JZ^e#i3|(J)4=n PJ8ɡB!Ϊ3nXnP뎔]Lls(%1*f,Cj$+MVUFMY`:yBWAV;g$kNΉ-ˊ!t2b ;J.m 6( ?c Zp .2m֧3xsI^=w,ݚWUǶ3~O災2-f)m;CP~96R + 5t !i;oK_÷v^@) C~lÉʣxj s$'?[O+VPТĒ0WQAYEA/wГO]pl6en< .f].e/0~!ĹrNt%LkA_=5gRT\GDp\ki("ڄ%5ᡆz:]8򎑛WL[)%fBSBrB!|""' B!|""' B!|""' B!|""' B!|""' B!|""' B!|b ?] !-M?]!iB !>B !>B !>B !>B !>B9kY!./*pK0as俓2LQ6BX[TBQ4r9Fkd֙ܚT!CX< 2@ wk΋!4!@<$gfAA б߯7FYRǃ P++Bq~;}GVÙHγ- ^WXY̎Q&n( ަZ!.0QXUІÉ$GQ$p`}Q)6D嶞c1„N)fަE|Shǭ!WrYt;Sm'SQ>7HrfpY9.7^!u,7E1TqHQٛkd>;tiޏ7O1+GjSWAlrj̦(.0TX~._ySmRS.ͤ)L4!9{/*}D YCQjslb z =y]|;ODse,Z8Z2ߐ(Vn^Ə(A}S'`5eh&B^+rR˖oЪK$\KkGI9bXЯ58صowm9L%d^@jl)w Nn]#˼'\"5o?楳&'2XY.T?mo+Bqh Szs<"Z06D*yg ("VM7'2mFAБWERwO/ť΁Tu8}@~Io|=~$:56 W[Ԁ=sTeƾ$m`fL|-]J;kce,.#]o 'r[bkWڈr@wbiBojD)OceөyDiEdrNӌe/Kc{dxe%7 ki{9@CL<dL@O1<Pp kׅYM݉SQpkՀbiI` Q\78mTwΕ ;X02'*b0ba)`nN+]*;*X]T,5Nlhɳ#PiBok$@HA D|^@9~2:"RU˾࣢ThC*@127hPfwUE]5M<pʙ W1/hNo_3k`xX]&ss:ǭqk+.KGlP(WЄseĩ9O*plz iOKLvUֿIKMGKNfAC,FCZQ Qq}iCk@tD!5p!%J-#vsӉ\7Yƃ"-wk_;H:\]/%WX.*ƦQAgebnpṾ$sc~I+u9E v˩1 _ŶL+RO;_1/*5Hp` Ƅj%ϰ z y9HZGޏPC:>ՕFnfSbl0 z;WBOP`G^׋Hx1Y݇ȷt鞽f-EnݗQff Pk 5#D9 ^̓0ᡲ,C րYB]KQ#j. 24eӎrBpWOf:BA:O{p\T*R1iZT*Zѯ!Y!.,Ư#&m2n7!_¸e/2sFc'7,XB[ :GRs~ŊOL4!Y1׿4̾Rٵi:R/6z6؂5{.dk`wz7C1ΥKv5pLp7JX!.\P<5[ߜUkSi>~QUT?!F^ފXKpkPAcg֙fj=q]}(FB !>B !>BćagE !D^Ҝb슂(e~1 x<UnmŇRRHiMCRNJpW4ZGv?lyQQAJS=hPb*d**T؛TmZ5SvEexQt9{RPc Ɋ"lByT _ jM[D)b˱L2 jʒOѠR.-$e$EVמThDߺRD&/˪Sbұ}p%u!}se 1XB !>B !>B !>B !>B?]3*ã: tBF{g>%jW|jQ9˖| x.hB8ٲWaHz 5YT!9p>W;o;YqtoTPgBf2l1Osۍ]XKl󢋻[G!s_'9RXK9~!9٩l[O6cysٳ)MKP#dy^ț %1܇g#/XTrA|56gUy~_ڕ.]\p]> !4ބJcMژ>ދ 4֌9?s%-mv_yy. 8W(Qa<1zpgOP@Xwn|B!|h(u 0bHb<~7k+)S6+.]1؋):<{3pĴǸ{&ӪΠ.StBA6-N9[b":D@!o '9[i׼k MݕG*ۗlѹ^Ee"\ѨŋWQYFy}P4걺ڀ0Nʐq1QAE= H^50){\iXX|E>>wKka9i%tYFWZCC4zt5]i4ʱVLm$Ȧ?\U\5UB!V ?|VĴjs/:ʌfo#hEfUn5IP‘I/r-ӯC 8곟qC`q$HBqeɅ5ۆVQ IDATwZ+ϲ$mK1GBΦ-WhL$܋ҏ@ m+8Qp1f8^B;w8;B@|ow1I67GoQs%l 24h- h۱mw!J12#z$C_+Tu@>r?!>P1ecYuvU*=%i PYas*~LqasT`_IТ#QJ*+\xP1jpx3X=b>^SJ襟 >B3@,C)sǀތIO=4#:6c~u*ǶEߕGJx!y<]/$7@5w3i%qb5Osb$(/]v$HOWJ!qoX1nt-B4@s !B ˺`_{MDt˸k!eg*9cse=m ~mofǘ[ncɌfe9=z1~ A/]B7ΐa'[VTʜZ;;36IYnNjje7@++عe%4h 7\AW3m%+ ů{e-%RB)Â'obD auSl=^HVGRC"Bg it"vM_oٓ\FR~zo~MY5v+8̎{u|}$MQ])p:6uk.9&J|̫;K4f !+I3}3e'BU}ƍCs7f? =ˮbtV sf .e /:N,_L/2;}.@&+7ϺcD*Տ6_{lux&6I}1NooЧ~BV&1ǟsQE^zd.ٻUHġPzzf6x'iWB.=K4(EKٌl^7m-J:ѽ[3NLW)O䗩1'}(8'&MָBr>soGyxL8SطA=ivbNL9GgOW<4P̑0C5^z[7 F@rسO 0WԉLt/AMUd7|YB7C4]3<twL|w:Ԃ&.:[+Y˙6NB;[ ohG QT^NFB['T-ԢoE5 A# , ft`IDM !y!X1Dx^Jl((H̺ NѾU̕wq m౒zbk;u~ƱַrNuM]Tc4xpawe}ɵ>kr$SGXFY!R܉.<cRB-h׹&o˯/ciCLIjHc;*9|0NG)KO9_5?=6qɌ3:Hj҉ݔs0:y`JJ b5L hH|i (r;#7i'xts&;!85 ^7n4:FӠRxuaӼu_K \pQYIJ@oƣׂHȺ'z82GQ&)eo2_+rBoZhb}W|<]ON0BKض|Sʣto} ֔ 斷C`=e)Σ%H.Kqk8@~x앸khUcYȓZM@;R?zG7;YƑ_o+ ~SROb]͒GoߥCÖp z=/C*,JB 2P;yk޻MOWM}#ǚa}x|{2u&?wudɯ/r0}t6M{/З>ڡ ^!ą1EnžB O/҉S~rp|R]q\ڣt {czNށjtZ88~f.TtK ߎd!J#<˗~vێӭICiDUS{_&9~?UmjLm:c69REϻ\ڥjL}ϭ3q\|}$Bq>i8@'9"!z,U^v)$ꮭ^;C|`2cO_Eƒo?I3QY[d99Ҭ|K_Dr{1WP#<>MJǹLt#-N۷i4fZm[l)ĄɣO /߷itQM^97gxǝt1bC'^UvXݕ=:jfqxW,J;U=QF6||H|>$-W1ض-b <1Z_ʷ%;r-[a(NʚNGgf0$Vx1)Ȗe'k A1(dxfodv/K>y~m8,[!L.wx(x cyKdcFl3WTR*Oc8PE:w,~ [AQtր^5 cM!؀t9sX^ZJ4U B[ΪZ mr[/0[ucR4J1>dWCҩg e,O!¿דb` JoH<5Bq^j<>Z *Ƹn$TpM!yɝ螲\Jݔ^r3F9BX`]3V{n-P!yQ0\(s qH~NTM'稫 M: gRBTLAXV:˨pgFhPYăJL]nׁfYN!ą&,}SAH@Ck<6`0!jB!|""' B!|""' B!|(,O9g7nUᶊ cz{ֲپe.0VORֵ9/lX?Z7w9_9'){I޷ܦL|z j[Jl\ĝ(,nAQ3_MlXvWT* V'0fu 99RxЊ?BZuS7ה\-k"5=Dh |/;Mʓ?QJu⯾kf7cADrHI*SwpLZ+dyؿfVz7|qt2,\Y܃]E` d,,}N^z_Kc3{^"wxoc09vԨ5-kDwۘ e>^sK6ϧ&K7-m7>~!8&w}UR^|wO)t-#e4Vvg@JmBن1AcX}5StD}nCTx k^c㋸jp{O\ xf{+?䭁8qSɘ3 Xv G ;>ȋ j-c󛅌v:F_#K>CDz' fӹ 9XYOLq:+} {eӡ3 #m8c#W3OvYd4|1^3<BI5fW:(ڏU3`)8?Aq6v⩮X:uloz YN,/@5 Mܧy7u` mMd4;1J]v+>#hCZyM M˒wgQ91ƏhN mI/c_е[Tvd]md),ۖ*fuUYI[3ף{-5rT~եS+eEg0^qnnDG,d1=Hl]B1&ɳ_ 3/ӢQ<~C{xqVTHK7!RT`7+Ƃo|/ޑB|/'~ȎFu.@˙- 7%GYم>}nu h| Tv> /A W l(#4Z4~#F@A'@Mkw\!Fo@KC' # ӭ`?M'ua˥ WxqP\FYتqR\P@]EQQR>Kso1 e/ˏ[ߥ7_bb~~yC-[NƯ/1!ą̧!28l܀.88j'қ0\nps(G_5,fe_/ҕ¤#Lg*t}0~ -tYߢ |},} 8ĪWeκ`.}{:W ihKe#X˪ {kR׾"T3w^s,CӼ"GZJNq?rC8aFM]=ɄĀ?qV0cK^{!pf~x^͟~Ĩ Oe_ʬU}c:Wh-%p}7OuRI7|^灇/i]!. 7X5[*;$} b՝FPuQ}e79'k~iew]%uSO]?^ A<=|LC;osۖ8QNԙ.3`tmGp gq:`D_q5#>=aSGKE8LTa!F\;f2˩Grci7yKjhϸߧ|VP4! ^ Ce Pkhէ4%2ͿQ:__aˋ>L;z^V~@ n+G Ql5*"wI_ОƦ~j=Vt*U^\XQZ?o6Ԯ2F^!;oiwq99E*!qD6ÿqPLf]6Ě9$kˀTļe6yPg9ooQ,͎)'*c`΋^< 4نמO~V>%6Taĵ įE8 Om@o26~Bq:M!܅BDD!O$@BDD!O$@BDD!O$@BDD!O$@BDD!O$@BDD!O$@BDXk\!h@sBq K!O$@BDD!O$@BDD!O$@BDD!O$@BDD!O$@BDD!O+ң6mBV@!Hdٜ[)99"Ugv̿FVꈙ90OBOkē ~u-|>섊 p9ey!5$l$^y{_~ˆ6Gd$\8q{Z&Bqhb14\6ftl?#i85[aT(up`s,qYV#QG&!^Bsɝ莜U|:wT`U: 6{$ǘ5icFKiM,W,^݅?$I|ϑ;wsŽw^ IE|l Rezˇ:gF .7`nǒ7'XIOQ3#r!S.M̯ԣK@xW'x}^f/Aβi</FNBm'be 39B_~B!z MQ@qG]c]n2i䳴kApA %5W2,ty;#,3' 'rDr*B!.@87tG@nr)]bB׫1@>+˦,q &$j;2.oEUx=jB^屑(+77AGɰ50XUO oZ!c/%ꭦB S(Q8qQR,Xvl$'`TyW/:4c.cw]ERu7/.!D!"! B!""! B!""!P{]Z jџ;ye㇬;VPve=^{A՟pv%0+=h܁nsύMl'85C!9%719r<hy)nzm-Ӑp0>s)pH IDAT4=Oѯn>G%(=l7#َEhe;x|#*PKG~w$՜)+ڮt !?_W0?糧Q(0ud G{Ys%,YG9r:iuڵhf~eG K*ښ;}Qp7ۄSmSG`颪 7'}|NǡC)7o-G쾪^At,0f!dBa-{;iaϯ2q;.͔_`њ DuӠ4 -8ݬB#uqwPHx!vK>b!ohT;ϗ^#Ks=Uja5f,NNCߘ\n9~~I߿]t?oB>y])M dm|It ތ^3#Jϑ1lpsDIV{Eye:h9١B5x4 )3 Қz8>|/6bߒ5TlW4m3IԚ? D6!7C%I;K&@R'W'ի-ݻ&<*' o56t-;Q!<#&\!ƊeS7hanjʔzо|xx4MXJX߇ FPWcR`!YH1#Sisf掝Ĺb}*WzϬu$/}!kUa!T 6 +(T'NTrR[ڪ8xwS x`\PHfWMO*ǝM283 q 8no5Fڼ6Mm?ۄP 8*>9+@Ԯ(a0S\XMKB֤:gcw܆£,~ivX]i5e=/*CQ'lOtʷ4?KƜh<<Ítsx řUV%eѦOŕM`sSHn<'?CTUOv?:x&}˒e}xodz|4~Iƚeԥ“J"E%Ru*7DLh;ř !LGVb&nkVcpw ǫ_|\;d@Zu92 ) $,z 9Ն z˗8v+y1&ƯùR K5CD>Ʉ)/Ѥ|UK{tG.\b{D$|>Aq=V?_7y:L.DDqh<3M>K}crmэGN'fӢ^ Mj_3ciYX5q񘔖%0s>FfSRnB"M基3^ß'3Y,:NLmkS҂f3l8%:swg8s5~w!7&zOϳD8]93pc}o םYlj$Zg^/QE\=fLنr~ڛ8ٹ=!~Bpst$i\w&=:'?}e(xY6ӜEzupK8Ďoqtx<ϻ}CƛPE0\$v'66 o3Gپ` R#]U\B^!ОGtk?5ɖ]w3[ Pi43ypTp]Vi`\n)_eyC{BRՊ X \sAQenX'{(w~ݧ$@5,^?ѝy賉 nQ\0瓖Tʞޯ>Fݦ"Ĵ.},S~b^Zibq¥n; Iʄj"1)XlTi2f,"j4-y[=!̴, QyOqgGrCT#F-zǒ: E/B -)+2s%0 lŢaV]7n7c+L%=-B w(xJC,i+!S"! B!""! B!""! B!""! B!""! B!""! B!""! T3BQ5L.ϒ.,!B !B !B !B !B !Q,YkE^FBbBwv̙ V}iЮ/]x7L^pcP^Y:WD!%5= kI-.m%OZJl6l. !Ŀc:甆w,]z6͂]5\ubfbSR+PԦV VUVږ!W}莳q\+{bwGUQKE~y+r9-FH}!!@ŅoˎtlN 3=FZN{aIyq% h7p$ 2B^b9˥"\Cv7ã'KWwnἹ~N(gv\xh/qIJ\?[ơ ]57fT@cL!!Ně'\7} B!j5%pƿq3/f ۇkbad<c\Гc2+y/xgh/q$^-k:nJ߰/`!j[ 6@ũƱU@z`ߪ/|-"V`ԧA`?i6u)9J5>ZG?9̩wh_BDF1P04>zn&(TjTXM-OT͟ZtEv@& 'uM5 !TۅlF_IΥTجݎݖ y'7]m᮸.g5S$xLzj\<*!7U?[zn@F}ceb0QsӿOcǫ3RPXs8c]Dz»> zW@!;דGMp:Bjxćf#e'Xy(|vOziPˣ.k+O~Ō'5'0Ԫ v+POօOJ"pn:~QkZ!}:0_ "QZ 39V+:Em~^p搲' =:~WCsi;<ϋ>OnWIDŗOh_B WB1ؗz=3HN#_g7 OUMf5 5˦EoJ:’8ip*L9d%]#WQzPa-]g NB+j B!D9BH!pBH!pB"a1of8If^*vJGa.eݪ?|"9yp?-G6un.!bp } )}mKԞƮ.=AqiǗ}]y4ӛ`W!Tvйun܅\y>|(A%}`&fg /cOq'L1vo!=MoEnMi#'p6>]0`/d\~ 5v s4%?;{8֐m)kP|/=w'jȊgSc;O 1=w,mӸ- o#|nMZ+Ef!y!P*ˇ;#;!cǕ@A+v @O£4miQߋdEeW uMX-Xl%3+Bо|xhVx2*<|&Gg׬qJhՓvܟD_;E֋V99A;Pӟy \#~-8b+Fi֝GӢ7k%%7͉On~B/̟֭ɯ3VRSic.G?{ckvϻ?0`p-Y_8yq 3:rT&;Zs!:[+#Y]p'Vl"4/⏖zz5ӠHx2S<&j c|fy[y~-00̄!;WBdh;*-ʢwtַLj̆lROl3(TT1":رdr ZesbL a5.[$k&̦q<4MZ!_Q)guǷVBDNߐp%[I֤|{禿MGg^"+ ^ϚXLVl Ey .m.xeOp-] 6{:~]rKi0XԵ2?GZwY^D0_qm5{n0~ 5\WLQ|o|Ox[^e)Vtx7:cBQ3FQOConVE4px4u'6._w(Myn#$BN̘Eq heJZ&C7z<~=Fl6z@Q6)ױD?FXc6`*;`>}'JS(poܗ.D#.ٓYMq%k26prxћK\H]B!)@,ZP*8G,ͮ$4kQwU|9:LV@邋CZ]:m 6֝_8v.@6ц%J:w|_V]T6}2jԖBqvydπZso%j ?H1&#/O!԰"AG]q+o.$3-"׭k$}x8L4]Z*Yx6$ߥbYB5%x>ĬPd J]pwdI[!B8DD!C$@B8DD!C$@B8DD!C$@B8DD!C$@B8DD!C$@B8DD!C$@B8DjgCB!܅BY҅%! B!""! B!""! B!""! B!""! B!""!꿤D3)1c.ifuFs !V-Ą&6,[Ǚ3 8Ӡ]_<0!N0$YFUؚ/}%C0]lfB\B!Wsس9-q^d꜡xBɜV+e. !Ŀc:甆w,]z6͂]5\ufƊv[Z(*ZjČK-mحvP)XUR]>B!jT}莳q\+{bwlġQK=[,dS5?=ڙ 9v"Y8yQ ;!%`88_ńq#@ ۝ymh59dXP'nhHGi29x< iQ B5=,Rpmv=W O[5|3IxwpY͘.>}xǓɖMnB 9uF˻߽MSoPC=)Pj q&B7Z9{:m'O h4^ W rKZ)1<Ƿ/f߅dt#6璞\˕AÇ'1jlgJ{^8Vtn+ (VF; DCFF"nj CsR'ZҠE !(Sm=ps,E%Q ߶87PJnv;`O]OgUS*'ԘX!}̍𨒱<俧 Wmz.f.>$V6^Z#c>g:uBQ lFK;g7p.W;nǮ(WLB*?p <' h2e9~y]UT43nFȽMQWzW7"<̡tJ%4vC:3S !b{p+68p.+}?^M^OeF -6 DfiUGm<grECߊ2 ٛ9=ϾOQ("GkݙBS{ >4..$cu!$;=G]ˍ=-iٷy+wzƟV}Z;E^6sϒGYgD,m>M߾Ѹ=o[=;8m;._`UGTԭ8k.VAĽy_Y I_1x̻35zN.eݚ\@~7ͺqoZͤv vchз[閼sHL¨pǿQG?6α>7[Or=5 }M4;~ Y\\ #9) +h&,OljZ|wݝCh"ә(6^,#3 `xtqxmbC>z|,;7J8(&FL^Ē '({ty)_7^DB^$,3EUWGŠlsU˜b(7P[ц!Nը=mB!m\g&kL'7q¦;ga>7wp!~+y]I>Ga)۱$xPm: FAZ"N|AQ!+ՊMp+6ˇ& uWٝ؆4>׻̨kSpGSu#,}j˵ӹ-rH(ϛbᘑl0 ݟ'S?E n̉i:3㻕d!chޜfoZ V;&߻Ҭi_kM~1 O0Wn@ OnZ`/ߜ2vm%31;Qnlix!fm aԒ%T6l6% #ǧY]=>!<R)<fb4S27Q} bݣ; L-JApfS#ܴmʇ_C]FAuПfowyޱ^f*$r\ԇev_@̗X86^*Ę)nwϘDv5ć,g~yMW -|)sa4a4/`|) '/5r{( iIttIX98 棷<_yl`$.[xsZѤ]7: %,_qwS=Ղ50aD3~" V{r룈p.ؠC߭/Q~*N8@Kq^Cgέ^Ue_i֒mܟ}ꓼl6[xjv݂tM$nZis Ftչ#Ih gVXREגF;!4Uc7> lϺ4kIt}3=wСo a*?1RBޖўH8EfCu281E-ʥy(Ϲcy2V6tG3A 8&.'ͼ̷w۲-Ř şgӾNէV⡱/Z.%uS_;CBdp̋܎bLLQ/,'3s4~VwxovI$\H&_~]3_pd$Dw:2 Qy\Öy/iľ24+:F'^7c8/"K2JݓxKž#|7ޞBAxnԉD] P^scOR(9=YVWY'tq"26}ʌV0*zfCz\ Q5,;('TO2gEI薥ͨUUFp?Br.aUADx !jlX2a"~*(bTnoT`] $A%(4w6G X8'6(bǎW8M^i9<:疳x u*T[]uRhsF3iq/ϼ!o ۱c'Y_\q f5T_}xvOBD-knl˭buh/t=v@Q~vG/ҵWcsI?K Z̩qxJذYlUjTW]POZCs S*>lQBkjxةL)[09_ gf-͗ V$o8z< EnWPװ`Ϥ{ϱIδ>t‚ٝb.,yvΛ'ЪB2ֿBQ?7s8FLF%٣=+Yv NmȱA0K9éusKKB[HqSw{M 3g]؅!pcb!-%Hヷ1:@Zijb6c0^dtB !P %;gǨd<(}6 ߈B]XV dE+(<]G"fmǼsT ۏ`'(lJ6ڞ՜Sťf"sf y8Bj 6t0˅.xVrK[ADuBDXHOrޭ7GP%e" 67yDΩH-{q_BP3[+Lxd@ɠw7S;¯JcHKC T*ϿF+"9逺>mo UPMs8+uy1PS ]XBpw/o<| %(5-ᙯVwXK4 `'}BTO{F,܌sYg[2gRN>5`WwZ ;cK֓^ /-"N(=]} f H!j=ܝCq33~hWxWnL@׊n`>յ",dZŹt|775c@ZAoԺ*<ãub6KS><\lكz2 cѱXNQGWse hK^g%-[K›ZJBiH;Z | w΄j } D5 )i!ig) ߩFM!%Pu_ ]oUi/q?3A [E.Es:vxC87[{,ݯҫ,i1LT8nz! $."rPĊWR.V(Uz"C!gK}H!$zsrv|Wf&/K׵ᩪl^&][[sY.W\@Dg!gٽdWҺMk()5 KKZ9TAxV>j0;\H JۏW~Y O%%c4brDz־EY;Hӡ+ k:hyY9u!ܩN#adӋh 5BĮ0b+D^ūy9k}ղdqb45׈^TɅ⣓xИhX|j5;[yt<L,/h]϶̬Wh)7BܻZ4pldv*,Aa5Wlăͪie솳} "JM^BiГ|h>y@5g6I1 fq>7'%?kٛ4*NvOí0?_>6?%,\7gpŹ:pi8f/I'=|. 7F]6[9`*E6~?ݢyzxލ|6g2;ƷVCLIH ƫAژ6đSY43oABZ_wZ6aw: 7O9 ۛ4 뉿Iѽ O/{֊`\"XF%~%oSc u 9d^b[bLy﹘Q?-k{7| f@E݁j61e1>ہSx酎xeDu9$]8* ^yyX~9JVDy Mq,!=吡$q}\;EZ*^iK8~<.aeO$xS;NQЗHMҭdeh\;K}8ª TҲTr= t-w rקK^(,3PrMujflq$^}?>w}6Qxy:tl%N`[k˺=U-X~][r|(N{rJ^݆KnDES&7.x?4Oy٫čzhNqtө(\Ө]$*DW '> g<%{"oT֜}MpXAiPۧi"gۧLh/гKqd[7@+hNgA% 6'W lRgUB 61اx76P*;!WNۃgDFㄎ;1ܭB~ؖi+i}Gr49>JX<Y,XCh2?Auz+:L+-: (qeLvF[bƂ 6>ɫG&*6\;L;Y5i-mWll򵆊̭N+OkVFw|"aI4>zs7mZB* 9EW/V`A\ /j,% ثqra`[tۂ\'=-ނGAo-ckkwtEXr,XЙ8)n)!;5|-fG<"VeձqQp3Z4idecQbԢӛj{prCY1姐FN!y+!H[K>|U5܃zEGY2j4Iݿr!wLtKQ (}p2oki1`=^'qW  `ȋT!oI!* B!""* B!""* B!""* B!""* B!""* FӓȅBfwBQ4a !BH!BH!BH!BH!BH!B<)' ^ysmBVKݖ^Z#!~[Vi ;11#f,Rw\#.,Pk :a_IBQ k ٥>h ߨ4*t )K~w&#πڟ:m{HevdM?ɰ8֝g8l8#Bq79@B7U y$_%I2l2_8vY7~ >x^?ũ8tzlFGY:)ʻYL#+8^h6lR%>wt4l}dpw8Bܭ{n²3df<Œmi)4K7nSVW3ՅQ?dڹgKc7 `$ctÌf1m~~tq-: ݒsV3' SKߙ4Bܵ{׏& ?9YMf J@KIrDeoն% R`R+to=D@@{z~?qKגz+{l+*Cȷھv9Wuz Rs0Bqw1@ 8U&OD;[l,F,LfPush.J$}rV[(kCFCA7Jo|lk_o ag,қ.{k=ś2+[vr مqj&/͈WKV@i>V+&gʲ\RFPhtead±ragW ;lH!KaP8ۖzxH8A偺8`yo|~/}OB򯓯w'E *c9zftt+).%[քDdr RvbgœĺW,updsA2 o>Ola\Ȗw0Cr"YZ5u[+wSs Ĥ߶8yGK$OA1٣VSzG6b\+T1`(! Lk\ʒt=Gu %=b+V+Z큫3!#x3K%dq|$]̋|XEti\NKnؗ+#*'F C[l3`OmY&2i9غꇪrȠAWb(]Q:0iuAQBD!l7agc_Û#L]27ʑ*q';5Qm@;CնSd!j X;6OgB>N'!!Kr!bo_*B!xB!""* d2SpG6lOR ڻа#=OîJ.c]\t5v1rHٹ睈ۮh-y7}o$ҫm%`v!ƿbĐqQܖD@X lPDIn!otmY5>r9(vK`{Oχq]NLzϾ=N!Sc:auýN1?Sh,BccyA;W)x'>{KRh$ͼ+/h]϶̬WhYc0_aXsb{{wtk[u 859lAWHo;5r待1aa <4 4I4OwoԠ1Ѹߛ1I">=uU}1B{Bz ╙/!S҉^FAƯA#C=cJG% uZ \HAIa>Na6Eג޷J5MDL]-9nmmQetDVHVժː^OHْg/!MՕj5 %Z BGѼu* MWas8LLRlCZs}AC?*WV̚m#FQ+ܚ\t:DC˥M8H}-Nt!ğNjSV3mث)xߚa/y㈭eŐĎO2P}^X<A5-9sG[cVCn(!k|6B'ᵇoӨ(0#};>x}Mr |5%|'_ %.(<җbU^,OhZ݄ 09&~M9?DՐߌ}-cWƀs I,{,qAq9Dף)\ͬQDu%KUOsEt1d=Or8{@м]]/^h}(Mz4%2ӵЍȎ K%)1Wφ ( D,{:2U{UsJLy]Lѥrvz\U7(v<*U+n̻J[<4s 8T[AM Qy־-ϧRrgXZg&Z;n)Z{^nRB)j>&αe" S/mF"Ma9O9j2:ҰQ]ɫWit?k$&J- x7͈O>k-,[?|l[ԸiE ѷ5`=手Y_ :o@w3؊UXZJJ-`〃gML`n*l"b غQ[ƋDTtkN{sow 6"hԼ91^*|b4Ѻy9,k 1|lFʹYx=MV1v}m8]sy$ˁ~ˁmmA\6vRy_Lƹ#o7F'PU Ƌc0o̥=r#GQDVW^݊Na_bRPlZo"ٽoRulRR"sl<^o>7sc0ot~yp $ClA7aAә}SlgG^Hh; nw$;Fo^HJTKtr޵RZS>y6,U v2mW)є!_uQx^px{ɶD ?`bcY{݁C'z|r+/'~؅ď3h_?+R jhx-~vX7/gn&I.^apwvlsh/Bl*zQJ\żPc_g䇉lq݈y9ҝ:nf./2_*=yf ?嗭F팗vbŵj=5l7WY@i4ٟβy֭3iTV|0u+=yzኟIݬZ Pxt+38/^`ސCקR12eE~1JTO2u@c;8!#,CU~DyB#,nLg0o_>E8wRqiC8p>篧t /ֆ/S'1wgc%6]6'0YwDO.bF297V9פL1 ן:S ~e',i>57SVyh|NlϕUz1#?~/HȧB,>ʳ3g08BޅCIRr_neSxӤ GWbka\?>`\&MŶk΄z8y&gna_8p1޾GQ+tرm=7NXh7Og4>-݉s`= Kܴ-[fuQiv+V1eUD̈ylҕ53rB_Z =:tŰa8[c7ZOAARuh*׶@Iy6'}ö~Ny#CfˬG)=&MgK=&oCQϹ}ٺɐ?ܠ\1aS"9kِ;'zŰs5xƲ$v xNc8lw>[N9%}[׺<?^ʠoY| ɱhrf3X~_;؇ qDpW%2mhй1tcTY>,m +҄Fqt\;ޟu#>G lp΂G32%&-];s7NAc]18FN)>9SVל^oͱ֯({S@d^{ŝ-.tx|?RBʲv=G;7e PvC^^<0vQĕCiB0{()B`?jyߍ{AA*q4gE"Ӥq<,x*Q:rraNKpJ>”oD{45nc~~ ᩻n_6ƾbX &!7lةkʣ%F4 wCF%d_-b#ϴ$}2eUpso0m+f?Nqjdzb PB¢*L~id 暮#+_Yn,u8ϱs\ǩR7q϶bOf0GNPlk8yI ?2, Y:/6_•<}{ؿc`{tna炇;E)UT~yaʻNVC`cڒx[?< U<iX+SmA U<7JsOr35o@:9ȼtKQ" @T2 њA@mvwxDѨ?.Ϟ#rgubhMRy寫r) 3Miv(}qե~.xx2e]ɗ:1x6O/\o~&vnԋ!HS$f+U䅯Ҫ~/Iwne[j, A&49pո{6琑CXL$TΟ@Ҿf_Dt CNz+7v BT3@76a !MvNw& 7kBCBX'$^!"* B!""* B!""* B!""* B!""* B!""* B!""* B!""* B!""* B!""* B!""* B!""*c890 IENDB`examples/contacts/qmlcontactslistview/doc/src/000077500000000000000000000000001233466112000221635ustar00rootroot00000000000000examples/contacts/qmlcontactslistview/doc/src/qmlcontactslistview.qdoc000066400000000000000000000030071233466112000271520ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \example qmlcontactslistview \title Qt Quick Contacts List view \brief An example using Qt Contacts' QML API \image qmlcontactslistview-main.png This example demonstrates usage of \l{Qt Contacts}' QML API. \image qmlcontactslistview-edit.png */ examples/contacts/qmlcontactslistview/example.vcf000066400000000000000000000144711233466112000227710ustar00rootroot00000000000000BEGIN:VCARD VERSION:3.0 FN:Ms. Aaron Gates N:Gates;Aaron;;; NICKNAME:Aaron nickname EMAIL;TYPE=INTERNET:aaron@example.com EMAIL;TYPE=INTERNET:aaron_extra@example.com TEL;TYPE=WORK:33333 TEL;TYPE=HOME:333331 TEL;TYPE=HOME;TYPE=FAX:333332 TEL;TYPE=PAGER:333333 TEL;TYPE=CELL:333334 ADR;WORK;POSTAL;CHARSET=UTF-8:PO Box;Extended Address;Work Street Address;Work Locality(Town);Work Region(State);Work Post Code;Work Country;Work City;Work State;Work PostCode;Work Country ADR;HOME;POSTAL;CHARSET=UTF-8:PO Box;Extended Address;Home Street Address;Home Locality(Town);Home Region(State);Home Post Code;Home Country;Work City;Work State;Work PostCode;Work Country LABEL;WORK;ENCODING=QUOTED-PRINTABLE:BusinessOffice=0D=0A=0D=0A, =0D=0A LABEL;HOME;ENCODING=QUOTED-PRINTABLE:=0D=0A, =0D=0A ORG:Example TITLE:Engineer BDAY:2009-01-01 PHOTO;ENCODING=BASE64;TYPE=GOOGLECONTACTSPHOTO_1140C7078B06ECD7:/9j/4A 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 JXSS45r//Z URL;TYPE=WORK:http://qt.nokia.com/logo.png URL;TYPE=HOME:http://qt.nokia.com/logo.png NOTE:Some notes are here END:VCARD BEGIN:VCARD VERSION:3.0 FN:Mr. Alexander Mcdonald N:Mcdonald;Alexander;;; EMAIL;TYPE=INTERNET:alex@example.com TEL;TYPE=WORK:111111111 ADR;TYPE=WORK;POSTAL;CHARSET=UTF-8:PO Box;Extended Address;Work Street Address;Work Locality(Town);Work Region(State);Work Post Code;Work Country;Work City;Work State;Work PostCode;Work Country ADR;TYPE=HOME;POSTAL;CHARSET=UTF-8:PO Box;Extended Address;Home Street Address;Home Locality(Town);Home Region(State);Home Post Code;Home Country;Work City;Work State;Work PostCode;Work Country ORG:Example TITLE:Engineer BDAY:2010-02-02 END:VCARD BEGIN:VCARD VERSION:3.0 FN:Mr. Andy Clark N:Clark;Andy;;; EMAIL;TYPE=INTERNET:andy@example.com TEL;TYPE=WORK:07-2342322 ORG:Example TITLE:Engineer BDAY:2011-03-03 PHOTO;VALUE=URL;TYPE=PNG:http://qt.nokia.com/logo.png END:VCARD BEGIN:VCARD VERSION:3.0 FN:Mr. Bill Chilton N:Chilton;Bill;;; EMAIL;TYPE=INTERNET:bill@example.com TEL;TYPE=WORK:564412232 ORG:Example TITLE:Manager BDAY:2012-04-04 END:VCARD BEGIN:VCARD VERSION:3.0 FN:Mr. Bob Green N:Green;Bob;;; EMAIL;TYPE=INTERNET:bob@example.com TEL;TYPE=WORK:07-3242325 ORG:Example TITLE:Engineer BDAY:2013-05-05 END:VCARD BEGIN:VCARD VERSION:3.0 FN:Mr. Charles Brows N:Brows;Charles;;; EMAIL;TYPE=INTERNET:charles@example.com TEL;TYPE=WORK:32324534233 ORG:Example TITLE:Engineer BDAY:2015-07-07 END:VCARD BEGIN:VCARD VERSION:3.0 FN:Mr. David Edie N:Edie;David;;; EMAIL;TYPE=INTERNET:david@example.com TEL;TYPE=WORK:(07) 3245-2323 ORG:Example TITLE:Manager BDAY:2015-08-08 END:VCARD BEGIN:VCARD VERSION:3.0 FN:Mr. Jason Black N:Black;Jason;;; EMAIL;TYPE=INTERNET:jason@example.com TEL;TYPE=WORK:33333333333 ORG:Example TITLE:Engineer BDAY:2016-09-09 END:VCARD BEGIN:VCARD VERSION:3.0 FN:Mrs. Carol Eden N:Eden;Carol;;; EMAIL;TYPE=INTERNET:carol@example.com TEL;TYPE=WORK:2323242 ORG:Example TITLE:Manager BDAY:2014-06-06 END:VCARD examples/contacts/qmlcontactslistview/qmlcontactslistview.qml000066400000000000000000000171031233466112000254630ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Pim Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtContacts 5.0 Rectangle { id: contactsApplication width: 400 height: 600 state: "listView" Rectangle { id: browsingArea anchors { top: parent.top left: parent.left right: parent.right } height: contactsApplication.height - btnArea.height - statusBar.height color: "lightblue" ContactModel { id: contactsModel manager: "memory" Component.onCompleted: { contactsModel.importContacts(Qt.resolvedUrl("example.vcf")) } sortOrders: [ SortOrder { detail: ContactDetail.Name field: Name.FirstName direction: Qt.AscendingOrder } ] } // ![Contact delegate] Component { id: contactDelegate Rectangle { id: delRect width: parent.width height: 60 border.width: 1 border.color: "darkgreen" color: delRect.ListView.isCurrentItem ? "#F5678A" : browsingArea.color Column { anchors { verticalCenter: parent.verticalCenter left: parent.left leftMargin: 6 } Text { text: contact.name.firstName font.bold: true } Text { text: contact.phoneNumber.number } } Keys.onReturnPressed: { contactEditor.contact = contact goToEditView() } MouseArea { anchors.fill: parent onClicked: { contactsView.currentIndex = index } } } } // ![Contact delegate] // ![ListView for showing the contacts] ListView { id: contactsView anchors.fill: browsingArea width: browsingArea.width height: browsingArea.height model: contactsModel focus: true clip: true delegate: contactDelegate } // ![ListView for showing the contacts] } // ![Button area] Rectangle { id: btnArea width: parent.width height: 20 color: "#C7BFBF" anchors { bottom: statusBar.top right: parent.right left: parent.left } Row { id: btnRow anchors.centerIn: parent spacing: 10 // buttons for 'listView' state GenericButton { id: btnNew width: 160 buttonText: "Add New Contact" visible: true onClicked: { contactEditor.contact = 0 goToEditView() } } GenericButton { id: btnEdit width: 160 buttonText: "Edit Contact" visible: true onClicked: { contactEditor.contact = contactsView.model.contacts[contactsView.currentIndex] goToEditView() } } // buttons for 'editView' state GenericButton { id: btnCancel width: 120 buttonText: "Cancel" visible: false onClicked: { contactEditor.cancel() goToListView() } } GenericButton { id: btnDelete width: 120 buttonText: "Delete" visible: false onClicked: { contactEditor.deleteContact() goToListView() } } GenericButton { id: btnSave width: 120 buttonText: "Save" visible: false onClicked: { contactEditor.updateContact() goToListView() } } } } // ![Button area] // ![Status bar area] Rectangle { id: statusBar anchors { bottom: parent.bottom right: parent.right left: parent.left } width: parent.width height: 24 color: "gainsboro" Text { id: barText anchors.centerIn: parent text: " " } Timer { id: barTimer interval: 2000 running: false onTriggered: { barText.text = " " } } function updateMsg(msg) { barText.text = msg barTimer.restart() } } // ![Status bar area] // ![Custom contact editor] ContactEditor { id: contactEditor height: parent.height - statusBar.height - btnArea.height z: -1 } // ![Custom contact editor] // ![Applications state changes] states: [ State { name: "listView" PropertyChanges { target: contactEditor z: -1 } PropertyChanges { target: contactsView focus: true } }, State { name: "editView" PropertyChanges { target: contactEditor z: 1 } PropertyChanges { target: btnNew; visible: false } PropertyChanges { target: btnEdit; visible: false } PropertyChanges { target: btnCancel; visible: true } PropertyChanges { target: btnDelete; visible: true } PropertyChanges { target: btnSave; visible: true } StateChangeScript { script: contactEditor.resetToDefaults() } } ] // ![Applications state changes] function goToListView() { contactEditor.contact = "" contactsApplication.state = "listView" } function goToEditView() { contactsApplication.state = "editView" } } examples/contacts/qmlcontactslistview/qmlcontactslistview.qmlproject000066400000000000000000000032101233466112000270440ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Pim Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ import QmlProject 1.1 Project { mainFile: "qmlcontactslistview.qml" /* Include .qml, .js, and image files from current directory and subdirectories */ QmlFiles { directory: "." } JavaScriptFiles { directory: "." } ImageFiles { directory: "." } /* List of plugin directories passed to QML runtime */ // importPaths: [ "../exampleplugin" ] } examples/examples.pro000066400000000000000000000001011233466112000152220ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS = organizer \ contacts examples/organizer/000077500000000000000000000000001233466112000146725ustar00rootroot00000000000000examples/organizer/calendardemo/000077500000000000000000000000001233466112000173105ustar00rootroot00000000000000examples/organizer/calendardemo/calendardemo.pro000066400000000000000000000013241233466112000224500ustar00rootroot00000000000000TEMPLATE = app TARGET = calendardemo QT += organizer versit widgets # Input SOURCES += src/main.cpp \ src/calendardemo.cpp \ src/monthpage.cpp \ src/daypage.cpp \ src/eventeditpage.cpp \ src/todoeditpage.cpp \ src/journaleditpage.cpp \ src/eventoccurrenceeditpage.cpp \ src/addcalendarpage.cpp \ src/editcalendarspage.cpp HEADERS += src/calendardemo.h \ src/monthpage.h \ src/daypage.h \ src/eventeditpage.h \ src/todoeditpage.h \ src/journaleditpage.h \ src/eventoccurrenceeditpage.h \ src/addcalendarpage.h \ src/editcalendarspage.h examples/organizer/calendardemo/doc/000077500000000000000000000000001233466112000200555ustar00rootroot00000000000000examples/organizer/calendardemo/doc/src/000077500000000000000000000000001233466112000206445ustar00rootroot00000000000000examples/organizer/calendardemo/doc/src/calendardemo.qdoc000066400000000000000000000070131233466112000241330ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \example calendardemo \title CalendarDemo \tableofcontents \section1 Overview This example shows how to write a simple calendar application using the \l{Qt Organizer C++ API}. \section2 Use Case Mobile devices allow people to lead an "always connected" lifestyle, which makes the ability to organize and schedule events and activities vitally important. A calendar application on a device can allow three different levels of organization: \list \li Allow the user to explicitly organize their life by manually entering and scheduling events \li Allow the user to explicitly organize their life by manually scheduling events which can be populated automatically from other sources (online calendars, "communal" calendars, published agendas, gig-guides, etc) \li Automatically suggest schedules based on the current context of the user (including their interests and previous or current scheduling requirements), and the available events which are populated from online calendars, "communal" calendars, published agendas, gig-guides, etc. \endlist This application is an example of the first type of application: it allows the user to specify some simple events with simple recurrence options, and to view those manually-specified events. \section2 Interface The application is designed to work on desktop and mobile platforms with minimal differences in code between the platforms. The interface consists of an Organizer backend selection widget, a "month view" table (which also includes the week number of every given week in the month), and some buttons which allow the user to add an event for the currently selected day. The user may cycle to the next month by clicking on the "greyed-out days" in the last row of the table (which actually occur in the next month), and may cycle to the previous month by clicking on the "greyed-out days" in the first row of the table (which actually occur in the previous month). \section2 Known Issues The example is not intended to exercise the entire API. Instead, it is a simple example which illustrates some simple uses of the API. Only simple events may be added with this example, and no event-centric view is provided. */ examples/organizer/calendardemo/src/000077500000000000000000000000001233466112000200775ustar00rootroot00000000000000examples/organizer/calendardemo/src/addcalendarpage.cpp000066400000000000000000000072071233466112000236700ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "addcalendarpage.h" #include #include QTORGANIZER_USE_NAMESPACE AddCalendarPage::AddCalendarPage(QWidget *parent) :QWidget(parent), m_manager(0) { QHBoxLayout* hbLayout = new QHBoxLayout(); QPushButton *okButton = new QPushButton("Save", this); connect(okButton,SIGNAL(clicked()),this,SLOT(saveClicked())); hbLayout->addWidget(okButton); QPushButton *cancelButton = new QPushButton("Cancel", this); connect(cancelButton,SIGNAL(clicked()),this,SLOT(cancelClicked())); hbLayout->addWidget(cancelButton); QVBoxLayout *scrollAreaLayout = new QVBoxLayout(); scrollAreaLayout->addStretch(); scrollAreaLayout->addLayout(hbLayout); QScrollArea *scrollArea = new QScrollArea(this); scrollArea->setWidgetResizable(true); QWidget *formContainer = new QWidget(scrollArea); formContainer->setLayout(scrollAreaLayout); scrollArea->setWidget(formContainer); QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->addWidget(scrollArea); setLayout(mainLayout); } AddCalendarPage::~AddCalendarPage() { } void AddCalendarPage::cancelClicked() { emit showPreviousPage(); } void AddCalendarPage::saveClicked() { m_manager->saveCollection(&m_collection); if (m_manager->error()) QMessageBox::warning(this, "Failed!", QString("Failed to save calendar!\n(error code %1)").arg(m_manager->error())); else emit showPreviousPage(); } void AddCalendarPage::calendarChanged(QOrganizerManager *manager, QOrganizerCollection& calendar) { m_manager = manager; m_collection = calendar; window()->setWindowTitle(!calendar.id().isNull() ? "Edit calendar" : "Add calendar"); } examples/organizer/calendardemo/src/addcalendarpage.h000066400000000000000000000053721233466112000233360ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef ADDCALENDARPAGE_H #define ADDCALENDARPAGE_H #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManager; class QOrganizerCollection; QT_END_NAMESPACE_ORGANIZER QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE class QComboBox; class QLineEdit; class QCheckBox; class QVBoxLayout; class QString; QT_END_NAMESPACE class AddCalendarPage : public QWidget { Q_OBJECT public: AddCalendarPage(QWidget *parent = 0); ~AddCalendarPage(); public Q_SLOTS: void cancelClicked(); void saveClicked(); void calendarChanged(QOrganizerManager *manager, QOrganizerCollection& calendar); Q_SIGNALS: void showPreviousPage(); private: QOrganizerManager *m_manager; QOrganizerCollection m_collection; }; #endif // ADDCALENDARPAGE_H examples/organizer/calendardemo/src/calendardemo.cpp000066400000000000000000000571241233466112000232320ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "calendardemo.h" #include "monthpage.h" #include "daypage.h" #include "eventeditpage.h" #include "todoeditpage.h" #include "journaleditpage.h" #include "eventoccurrenceeditpage.h" #include "addcalendarpage.h" #include "editcalendarspage.h" #ifdef BUILD_VERSIT #include "qversitreader.h" #include "qversitwriter.h" #include "qversitorganizerimporter.h" #include "qversitorganizerexporter.h" #endif #include #include #include QTORGANIZER_USE_NAMESPACE CalendarDemo::CalendarDemo(QWidget *parent) :QMainWindow(parent), m_stackedWidget(0), m_monthPage(0), m_dayPage(0), m_eventEditPage(0), m_eventOccurrenceEditPage(0) { m_stackedWidget = new QStackedWidget(this); m_dayPage = new DayPage(m_stackedWidget); m_monthPage = new MonthPage(m_stackedWidget); m_eventEditPage = new EventEditPage(m_stackedWidget); m_todoEditPage = new TodoEditPage(m_stackedWidget); m_journalEditPage = new JournalEditPage(m_stackedWidget); m_eventOccurrenceEditPage = new EventOccurrenceEditPage(m_stackedWidget); m_addCalendarPage = new AddCalendarPage(m_stackedWidget); m_editCalendarsPage = new EditCalendarsPage(m_stackedWidget); //qRegisterMetaType("QOrganizerManager"); qRegisterMetaType("QOrganizerItem"); qRegisterMetaType("QOrganizerItemId"); qRegisterMetaType("QOrganizerCollection"); qRegisterMetaType("QOrganizerAbstractRequest::State"); connect(m_monthPage, SIGNAL(showDayPage(QDate)), this, SLOT(activateDayPage()), Qt::QueuedConnection); connect(m_monthPage, SIGNAL(showEditPage(const QOrganizerItem &)), this, SLOT(activateEditPage(const QOrganizerItem &)), Qt::QueuedConnection); connect(m_monthPage, SIGNAL(addNewEvent()), this, SLOT(addNewEvent()), Qt::QueuedConnection); connect(m_monthPage, SIGNAL(addNewTodo()), this, SLOT(addNewTodo()), Qt::QueuedConnection); connect(m_monthPage, SIGNAL(managerChanged(QOrganizerManager*)), this, SLOT(changeManager(QOrganizerManager*)), Qt::QueuedConnection); connect(m_monthPage, SIGNAL(managerChanged(QOrganizerManager*)), m_dayPage, SLOT(changeManager(QOrganizerManager*)), Qt::QueuedConnection); connect(m_monthPage, SIGNAL(currentDayChanged(QDate)), this, SLOT(updateSelectedDay(QDate))); connect(m_dayPage, SIGNAL(showMonthPage()), this, SLOT(activateMonthPage()), Qt::QueuedConnection); connect(m_dayPage, SIGNAL(showEditPage(const QOrganizerItem &)), this, SLOT(activateEditPage(const QOrganizerItem &)), Qt::QueuedConnection); connect(m_dayPage, SIGNAL(addNewEvent()), this, SLOT(addNewEvent()), Qt::QueuedConnection); connect(m_dayPage, SIGNAL(addNewTodo()), this, SLOT(addNewTodo()), Qt::QueuedConnection); connect(m_eventEditPage, SIGNAL(showDayPage()), this, SLOT(activateDayPage()), Qt::QueuedConnection); connect(m_todoEditPage, SIGNAL(showDayPage()), this, SLOT(activateDayPage()), Qt::QueuedConnection); connect(m_journalEditPage, SIGNAL(showDayPage()), this, SLOT(activateDayPage()), Qt::QueuedConnection); connect(m_eventOccurrenceEditPage, SIGNAL(showDayPage()), this, SLOT(activateDayPage()), Qt::QueuedConnection); connect(m_addCalendarPage, SIGNAL(showPreviousPage()), this, SLOT(activatePreviousPage()), Qt::QueuedConnection); connect(m_editCalendarsPage, SIGNAL(showAddCalendarPage(QOrganizerManager*,QOrganizerCollection*)), this, SLOT(editExistingCalendar(QOrganizerManager*,QOrganizerCollection*)), Qt::QueuedConnection); connect(m_editCalendarsPage, SIGNAL(showPreviousPage()), this, SLOT(activateMonthPage()), Qt::QueuedConnection); connect(m_editCalendarsPage, SIGNAL(addClicked()), this, SLOT(addCalendar()), Qt::QueuedConnection); // Connect to the save and remove request status change signals connect(&m_saveReq, SIGNAL(stateChanged(QOrganizerAbstractRequest::State)), this, SLOT(saveReqStateChanged(QOrganizerAbstractRequest::State))); connect(&m_remReq, SIGNAL(stateChanged(QOrganizerAbstractRequest::State)), this, SLOT(removeReqStateChanged(QOrganizerAbstractRequest::State))); m_monthPage->init(); m_stackedWidget->addWidget(m_monthPage); m_stackedWidget->addWidget(m_dayPage); m_stackedWidget->addWidget(m_eventEditPage); m_stackedWidget->addWidget(m_todoEditPage); m_stackedWidget->addWidget(m_journalEditPage); m_stackedWidget->addWidget(m_eventOccurrenceEditPage); m_stackedWidget->addWidget(m_addCalendarPage); m_stackedWidget->addWidget(m_editCalendarsPage); m_stackedWidget->setCurrentIndex(0); setCentralWidget(m_stackedWidget); buildMenu(); activateMonthPage(); } CalendarDemo::~CalendarDemo() { } void CalendarDemo::buildMenu() { // Build Options menu QMenu *optionsMenu = new QMenu("&Options", this); menuBar()->addMenu(optionsMenu); // Add editing options in the menu QOrganizerManager defaultManager; QList supportedItemTypes = defaultManager.supportedItemTypes(); if (supportedItemTypes.contains(QOrganizerItemType::TypeEvent)) { QAction* addEventAction = optionsMenu->addAction("Add E&vent"); connect(addEventAction, SIGNAL(triggered(bool)), this, SLOT(addNewEvent())); } if (supportedItemTypes.contains(QOrganizerItemType::TypeTodo)) { QAction* addTodoAction = optionsMenu->addAction("Add &Todo"); connect(addTodoAction, SIGNAL(triggered(bool)), this, SLOT(addNewTodo())); } if (supportedItemTypes.contains(QOrganizerItemType::TypeJournal)) { QAction* addJournalAction = optionsMenu->addAction("Add &Journal"); connect(addJournalAction, SIGNAL(triggered(bool)), this, SLOT(addNewJournal())); } optionsMenu->addSeparator(); QAction* editAction = optionsMenu->addAction("&Edit"); connect(editAction, SIGNAL(triggered(bool)), this, SLOT(editItem())); QAction* removeAction = optionsMenu->addAction("&Remove"); connect(removeAction, SIGNAL(triggered(bool)), this, SLOT(removeItem())); optionsMenu->addSeparator(); m_switchViewAction = optionsMenu->addAction("&Open Day"); connect(m_switchViewAction, SIGNAL(triggered(bool)), this, SLOT(switchView())); optionsMenu->addSeparator(); QAction* addHugeEntries = optionsMenu->addAction("Add Test Events"); connect(addHugeEntries, SIGNAL(triggered(bool)), this, SLOT(addEvents())); QAction* importItems = optionsMenu->addAction("&Import Items..."); connect(importItems, SIGNAL(triggered(bool)), this, SLOT(importItems())); QAction* exportItems = optionsMenu->addAction("Ex&port Items..."); connect(exportItems, SIGNAL(triggered(bool)), this, SLOT(exportItems())); QAction* deleteAllEntries = optionsMenu->addAction("Delete All Items"); connect(deleteAllEntries, SIGNAL(triggered(bool)), this, SLOT(deleteAllEntries())); QAction* addCalendar = optionsMenu->addAction("New calendar"); connect(addCalendar, SIGNAL(triggered(bool)), this, SLOT(addCalendar())); QAction* editCalendar = optionsMenu->addAction("Edit calendars"); connect(editCalendar, SIGNAL(triggered(bool)), this, SLOT(editCalendar())); } void CalendarDemo::activateMonthPage() { menuBar()->setVisible(true); m_monthPage->refresh(); m_stackedWidget->setCurrentWidget(m_monthPage); m_switchViewAction->setText("&Open Day"); } void CalendarDemo::activateDayPage() { menuBar()->setVisible(true); m_dayPage->refresh(); m_stackedWidget->setCurrentWidget(m_dayPage); m_switchViewAction->setText("View &Month"); } void CalendarDemo::activateEditPage(const QOrganizerItem &item) { m_previousItem = item; menuBar()->setVisible(false); if (item.type() == QOrganizerItemType::TypeEvent) { QOrganizerEvent event = static_cast(item); m_dayPage->dayChanged(event.startDateTime().date()); // edit always comes back to day page m_eventEditPage->eventChanged(m_manager, event); m_stackedWidget->setCurrentWidget(m_eventEditPage); } else if (item.type() == QOrganizerItemType::TypeTodo) { QOrganizerTodo todo = static_cast(item); m_dayPage->dayChanged(todo.startDateTime().date()); // edit always comes back to day page m_todoEditPage->todoChanged(m_manager, todo); m_stackedWidget->setCurrentWidget(m_todoEditPage); } else if (item.type() == QOrganizerItemType::TypeJournal) { QOrganizerJournal journal = static_cast(item); m_dayPage->dayChanged(journal.dateTime().date()); // edit always comes back to day page m_journalEditPage->journalChanged(m_manager, journal); m_stackedWidget->setCurrentWidget(m_journalEditPage); } else if (item.type() == QOrganizerItemType::TypeEventOccurrence) { QOrganizerEventOccurrence eventOccurrence = static_cast(item); QMessageBox msgBox; msgBox.setText(tr("This is a recurring event")); msgBox.setInformativeText(tr("Do you want to open this occurrence or the recurring event series?")); QAbstractButton *occurrenceButton = msgBox.addButton(tr("Occurrence"), QMessageBox::ActionRole); QAbstractButton *seriesButton = msgBox.addButton(tr("Series"), QMessageBox::ActionRole); msgBox.exec(); if (msgBox.clickedButton() == seriesButton) { QOrganizerItemId parentEventId = eventOccurrence.parentId(); QOrganizerEvent parentEvent = m_manager->item(parentEventId); m_dayPage->dayChanged(parentEvent.startDateTime().date()); // edit always comes back to day page m_eventEditPage->eventChanged(m_manager, parentEvent); m_stackedWidget->setCurrentWidget(m_eventEditPage); } else if (msgBox.clickedButton() == occurrenceButton) { m_dayPage->dayChanged(eventOccurrence.startDateTime().date()); // edit always comes back to day page m_eventOccurrenceEditPage->eventOccurrenceChanged(m_manager, eventOccurrence); m_stackedWidget->setCurrentWidget(m_eventOccurrenceEditPage); } } // TODO: //else if (item.type() == QOrganizerItemType::TypeNote) } void CalendarDemo::activatePreviousPage() { if (m_previousPage == m_stackedWidget->indexOf(m_monthPage)) activateMonthPage(); else if (m_previousPage == m_stackedWidget->indexOf(m_dayPage)) activateDayPage(); else if (m_previousPage == m_stackedWidget->indexOf(m_editCalendarsPage)) editCalendar(); else activateEditPage(m_previousItem); } void CalendarDemo::addNewEvent() { QOrganizerEvent newEvent; QDateTime time(m_currentDate); newEvent.setStartDateTime(time); time = time.addSecs(60*30); // add 30 minutes to end time newEvent.setEndDateTime(time); activateEditPage(newEvent); } void CalendarDemo::addNewTodo() { QOrganizerTodo newTodo; QDateTime time(m_currentDate); newTodo.setStartDateTime(time); time = time.addSecs(60*30); // add 30 minutes to due time newTodo.setDueDateTime(time); activateEditPage(newTodo); } void CalendarDemo::addNewJournal() { QOrganizerJournal newJournal; QDateTime time(m_currentDate); newJournal.setDateTime(time); activateEditPage(newJournal); } void CalendarDemo::switchView() { if (m_stackedWidget->currentWidget() == m_dayPage) { activateMonthPage(); } else if (m_stackedWidget->currentWidget() == m_monthPage) { activateDayPage(); } } void CalendarDemo::editItem() { if (m_stackedWidget->currentWidget() == m_dayPage) { m_dayPage->editItem(); } else if (m_stackedWidget->currentWidget() == m_monthPage) { m_monthPage->editItem(); } } void CalendarDemo::removeItem() { if (m_stackedWidget->currentWidget() == m_dayPage) { m_dayPage->removeItem(); } else if (m_stackedWidget->currentWidget() == m_monthPage) { m_monthPage->removeItem(); } } void CalendarDemo::addEvents() { QList items; // Create a large number of events asynchronously QOrganizerCollectionId defaultCollectionId = m_manager->defaultCollection().id(); for(int index=0 ; index < 100 ; index++) { QOrganizerItem item; item.setType(QOrganizerItemType::TypeEvent); item.setDescription(QString("Event %1").arg(index)); item.setDisplayLabel(QString("Subject for event %1").arg(index + 1)); // Set the start date to index to add events to next 5000 days QOrganizerEventTime timeRange; timeRange.setStartDateTime(QDateTime::currentDateTime().addDays(index)); timeRange.setEndDateTime(QDateTime::currentDateTime().addDays(index).addSecs(60 * 60)); item.saveDetail(&timeRange); item.setCollectionId(defaultCollectionId); items.append(item); } // Now create a save request and execute it m_saveReq.setItems(items); m_saveReq.setManager(m_manager); m_saveReq.start(); } void CalendarDemo::importItems() { #ifdef BUILD_VERSIT QString messageTitle(tr("Import of Items failed")); if (!m_manager) { QMessageBox::warning(this, messageTitle, tr("No manager selected; cannot import")); return; } QString docPath = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); if (docPath.isEmpty()) docPath = QDesktopServices::storageLocation(QDesktopServices::HomeLocation); if (docPath.isEmpty()) docPath = "."; QString fileName = QFileDialog::getOpenFileName(this, tr("Select iCalendar file"), docPath, tr("iCalendar files (*.ics)")); // user chose to cancel file dialog? if (fileName.isEmpty()) return; QFile file(fileName); if (!file.open(QIODevice::ReadOnly) || !file.isReadable()){ QMessageBox::warning(this, messageTitle, tr("Unable to read from file: %1").arg(fileName)); return; } QVersitReader reader; reader.setDevice(&file); if (!reader.startReading() || !reader.waitForFinished()) { QMessageBox::warning(this, messageTitle, tr("Versit reader failed: %1").arg(reader.error())); return; } QVersitOrganizerImporter importer; QList allItems; QString errorMessage; foreach (const QVersitDocument& document, reader.results()) { if (!importer.importDocument(document)) { errorMessage += tr("Import failed,"); QMap::const_iterator iterator = importer.errorMap().constBegin(); while(iterator != importer.errorMap().constEnd()){ switch (iterator.value()){ case QVersitOrganizerImporter::InvalidDocumentError: errorMessage += QString(" index %1:").arg(iterator.key()); errorMessage += tr("One of the documents is not an iCalendar file"); break; case QVersitOrganizerImporter::EmptyDocumentError: errorMessage += QString(" index %1:").arg(iterator.key()); errorMessage += tr("One of the documents is empty"); break; default: errorMessage += QString(" index %1:").arg(iterator.key()); errorMessage += tr("Unknown error"); } ++iterator; } errorMessage += QLatin1String("\n"); continue; } QList items = importer.items(); foreach (const QOrganizerItem& item, items) { allItems.append(item); } } if (!errorMessage.isEmpty()) QMessageBox::warning(this, messageTitle, errorMessage); m_manager->saveItems(&allItems); m_monthPage->refresh(); m_dayPage->refresh(); #endif } void CalendarDemo::exportItems() { #ifdef BUILD_VERSIT QString messageTitle(tr("Export of Items failed")); if (!m_manager) { QMessageBox::warning(this, messageTitle, tr("No manager selected; cannot export")); return; } QString docPath = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); if (docPath.isEmpty()) docPath = QDesktopServices::storageLocation(QDesktopServices::HomeLocation); if (docPath.isEmpty()) docPath = "."; docPath.append("/calendar.ics"); QString fileName = QFileDialog::getSaveFileName(this, tr("Save iCalendar"), docPath, tr("iCalendar files (*.ics)")); // user chose to cancel file dialog? if (fileName.isEmpty()) return; QFile file(fileName); if (!file.open(QIODevice::WriteOnly) || !file.isWritable()) { QMessageBox::warning(this, messageTitle, tr("Unable to write to file: %1").arg(fileName)); return; } QList items(m_manager->itemsForExport()); QVersitOrganizerExporter exporter; QString errorMessage; if (!exporter.exportItems(items)) { errorMessage += tr("Export failed,"); QMap::const_iterator iterator = exporter.errorMap().constBegin(); while(iterator != exporter.errorMap().constEnd()){ switch (iterator.value()){ case QVersitOrganizerExporter::EmptyOrganizerError: errorMessage += QString(" index %1:").arg(iterator.key()); errorMessage += tr("One of the documents is not an iCalendar file"); break; case QVersitOrganizerExporter::UnknownComponentTypeError: errorMessage += QString(" index %1:").arg(iterator.key()); errorMessage += tr("One of the components in the iCalendar file is" " not supported"); break; case QVersitOrganizerExporter::UnderspecifiedOccurrenceError: errorMessage += QString(" index %1:").arg(iterator.key()); errorMessage += tr("An event or todo exception was found which" " did not specify both its parent and a specifier for" " which instance to override"); break; default: errorMessage += QString(" index %1:%2 ").arg(iterator.key()) .arg(tr("Unknown error")); } ++iterator; } errorMessage += QLatin1String("\n"); if (!errorMessage.isEmpty()){ QMessageBox::warning(this, messageTitle, errorMessage); return; } } QVersitDocument document = exporter.document(); QVersitWriter writer; writer.setDevice(&file); if (!writer.startWriting(QList() << document) || !writer.waitForFinished()) { QMessageBox::warning(this, messageTitle, tr("Versit writing of organizer items failed: %1").arg(writer.error())); } #endif } void CalendarDemo::deleteAllEntries() { // Fetch all the entries QList ids = m_manager->itemIds(); if(ids.count()) { m_remReq.setItemIds(ids); m_remReq.setManager(m_manager); m_remReq.start(); } } void CalendarDemo::addCalendar() { // Get default collection QOrganizerCollection defaultCollection = m_manager->defaultCollection(); QOrganizerCollection newCollection = defaultCollection; newCollection.setId(QOrganizerCollectionId()); // reset collection id m_addCalendarPage->calendarChanged(m_manager, newCollection); m_previousPage = m_stackedWidget->currentIndex(); m_stackedWidget->setCurrentWidget(m_addCalendarPage); } void CalendarDemo::editCalendar() { m_editCalendarsPage->showPage(m_manager); m_previousPage = m_stackedWidget->currentIndex(); m_stackedWidget->setCurrentWidget(m_editCalendarsPage); } void CalendarDemo::editExistingCalendar(QOrganizerManager *manager, QOrganizerCollection* calendar) { m_addCalendarPage->calendarChanged(manager, *calendar); m_previousPage = m_stackedWidget->currentIndex(); m_stackedWidget->setCurrentWidget(m_addCalendarPage); } void CalendarDemo::saveReqStateChanged(QOrganizerAbstractRequest::State reqState) { if(QOrganizerAbstractRequest::ActiveState == reqState) { // Request started. Show a progress or wait dialog m_progressDlg = new QProgressDialog("Saving events..", "Cancel", 100, 100, this); connect(m_progressDlg, SIGNAL(canceled()), &m_saveReq, SLOT(cancel())); m_progressDlg->show(); } else if (QOrganizerAbstractRequest::FinishedState == reqState || QOrganizerAbstractRequest::CanceledState == reqState) { // Request finished or cancelled. Stop showing the progress dialog and refresh m_progressDlg->hide(); m_monthPage->refresh(); m_dayPage->refresh(); } } void CalendarDemo::removeReqStateChanged(QOrganizerAbstractRequest::State reqState) { if(QOrganizerAbstractRequest::ActiveState == reqState) { // Request started. Show a progress or wait dialog m_progressDlg = new QProgressDialog("Removing events..", "Cancel", 100, 100, this); connect(m_progressDlg, SIGNAL(canceled()), &m_remReq, SLOT(cancel())); m_progressDlg->show(); } else if (QOrganizerAbstractRequest::FinishedState == reqState || QOrganizerAbstractRequest::CanceledState == reqState) { // Request finished or cancelled. Stop showing the progress dialog and refresh m_progressDlg->hide(); m_monthPage->refresh(); m_dayPage->refresh(); } } void CalendarDemo::changeManager(QOrganizerManager *manager) { m_manager = manager; } void CalendarDemo::updateSelectedDay(const QDate& date) { m_dayPage->dayChanged(date); m_currentDate = date; } examples/organizer/calendardemo/src/calendardemo.h000066400000000000000000000102461233466112000226710ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef CALENDARDEMO_H #define CALENDARDEMO_H #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManager; class QOrganizerItem; QT_END_NAMESPACE_ORGANIZER QTORGANIZER_USE_NAMESPACE #define ORGANIZER_ITEM_ROLE Qt::UserRole+1 #define ORGANIZER_CALENDAR_ROLE Qt::UserRole+2 QT_BEGIN_NAMESPACE class QProgressDialog; class QStackedWidget; QT_END_NAMESPACE class MonthPage; class DayPage; class EventEditPage; class TodoEditPage; class JournalEditPage; class EventOccurrenceEditPage; class AddCalendarPage; class EditCalendarsPage; class CalendarDemo : public QMainWindow { Q_OBJECT public: CalendarDemo(QWidget *parent = 0); ~CalendarDemo(); public Q_SLOTS: void activateMonthPage(); void activateDayPage(); void activateEditPage(const QOrganizerItem &item); void activatePreviousPage(); void addNewEvent(); void addNewTodo(); void addNewJournal(); void changeManager(QOrganizerManager *manager); void updateSelectedDay(const QDate& date); private Q_SLOTS: void switchView(); void editItem(); void removeItem(); void addEvents(); void importItems(); void exportItems(); void deleteAllEntries(); void addCalendar(); void editCalendar(); void editExistingCalendar(QOrganizerManager *manager, QOrganizerCollection* calendar); void saveReqStateChanged(QOrganizerAbstractRequest::State); void removeReqStateChanged(QOrganizerAbstractRequest::State); private: void buildMenu(); QDate m_currentDate; QOrganizerManager *m_manager; QStackedWidget *m_stackedWidget; MonthPage *m_monthPage; DayPage *m_dayPage; EventEditPage *m_eventEditPage; TodoEditPage *m_todoEditPage; JournalEditPage *m_journalEditPage; EventOccurrenceEditPage *m_eventOccurrenceEditPage; AddCalendarPage *m_addCalendarPage; EditCalendarsPage *m_editCalendarsPage; int m_previousPage; QOrganizerItem m_previousItem; QAction *m_switchViewAction; QOrganizerItemSaveRequest m_saveReq; QOrganizerItemRemoveByIdRequest m_remReq; QProgressDialog *m_progressDlg; }; #endif // CALENDARDEMO_H examples/organizer/calendardemo/src/daypage.cpp000066400000000000000000000172221233466112000222210ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "daypage.h" #include "calendardemo.h" #include #include QTORGANIZER_USE_NAMESPACE Q_DECLARE_METATYPE(QOrganizerItem) DayPage::DayPage(QWidget *parent) :QWidget(parent), m_manager(0), m_dateLabel(0), m_itemList(0), m_menuBar(0) { QVBoxLayout *mainLayout = new QVBoxLayout(); QHBoxLayout *dateLayout = new QHBoxLayout(); m_dateLabel = new QLabel(this); m_dateLabel->setAlignment(Qt::AlignCenter); dateLayout->addWidget(m_dateLabel); dateLayout->addStretch(); QPushButton* backButton = new QPushButton("View Month",this); connect(backButton,SIGNAL(clicked()),this,SLOT(viewMonthClicked())); dateLayout->addWidget(backButton); mainLayout->addLayout(dateLayout); m_itemList = new QListWidget(this); mainLayout->addWidget(m_itemList); connect(m_itemList, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(itemDoubleClicked(QListWidgetItem *))); QHBoxLayout* hbLayout = new QHBoxLayout(); QPushButton* editEventButton = new QPushButton("Edit",this); connect(editEventButton,SIGNAL(clicked()),this,SLOT(editItem())); hbLayout->addWidget(editEventButton); QPushButton* removeEventButton = new QPushButton("Remove",this); connect(removeEventButton,SIGNAL(clicked()),this,SLOT(removeItem())); hbLayout->addWidget(removeEventButton); mainLayout->addLayout(hbLayout); setLayout(mainLayout); } DayPage::~DayPage() { } void DayPage::refresh() { m_dateLabel->setText(m_day.toString()); m_itemList->clear(); // Today's item QList items = m_manager->items(QDateTime(m_day), QDateTime(m_day, QTime(23, 59, 59))); foreach (const QOrganizerItem &item, items) { QOrganizerEventTime eventTime = item.detail(QOrganizerItemDetail::TypeEventTime); if (!eventTime.isEmpty()) { QString time = eventTime.startDateTime().time().toString("hh:mm"); QListWidgetItem* listItem = new QListWidgetItem(); if (item.type() == QOrganizerItemType::TypeEventOccurrence) listItem->setText(QString("Event occurrence:%1-%2").arg(time).arg(item.displayLabel())); else listItem->setText(QString("Event:%1-%2").arg(time).arg(item.displayLabel())); QVariant data = QVariant::fromValue(item); listItem->setData(ORGANIZER_ITEM_ROLE, data); m_itemList->addItem(listItem); } QOrganizerTodoTime todoTime = item.detail(QOrganizerItemDetail::TypeTodoTime); if (!todoTime.isEmpty()) { QString time = todoTime.startDateTime().time().toString("hh:mm"); QListWidgetItem* listItem = new QListWidgetItem(); listItem->setText(QString("Todo:%1-%2").arg(time).arg(item.displayLabel())); QVariant data = QVariant::fromValue(item); listItem->setData(ORGANIZER_ITEM_ROLE, data); m_itemList->addItem(listItem); } QOrganizerJournalTime journalTime = item.detail(QOrganizerItemDetail::TypeJournalTime); if (!journalTime.isEmpty()) { QString time = journalTime.entryDateTime().time().toString("hh:mm"); QListWidgetItem* listItem = new QListWidgetItem(); listItem->setText(QString("Journal:%1-%2").arg(time).arg(item.displayLabel())); QVariant data = QVariant::fromValue(item); listItem->setData(ORGANIZER_ITEM_ROLE, data); m_itemList->addItem(listItem); } // TODO: other item types } if (m_itemList->count() == 0) m_itemList->addItem("(no entries)"); } void DayPage::changeManager(QOrganizerManager *manager) { m_manager = manager; } void DayPage::dayChanged(QDate date) { m_day = date; } void DayPage::itemDoubleClicked(QListWidgetItem *listItem) { if (!listItem) return; QOrganizerItem organizerItem = listItem->data(ORGANIZER_ITEM_ROLE).value(); if (!organizerItem.isEmpty()) emit showEditPage(organizerItem); } void DayPage::viewMonthClicked() { emit showMonthPage(); } void DayPage::editItem() { QListWidgetItem *listItem = m_itemList->currentItem(); if (!listItem) return; QOrganizerItem organizerItem = listItem->data(ORGANIZER_ITEM_ROLE).value(); if (!organizerItem.isEmpty()) emit showEditPage(organizerItem); } void DayPage::removeItem() { QListWidgetItem *listItem = m_itemList->currentItem(); if (!listItem) return; QOrganizerItem organizerItem = listItem->data(ORGANIZER_ITEM_ROLE).value(); if (organizerItem.isEmpty()) return; if (organizerItem.type() == QOrganizerItemType::TypeEventOccurrence || organizerItem.type() == QOrganizerItemType::TypeTodoOccurrence) { // Here we could ask if the user wishes to remove only the occurrence (meaning we would // add an exception date to the parent item), or the parent item. The current // implementation is to remove the parent (including all the occurrences). m_manager->removeItem(organizerItem.detail(QOrganizerItemDetail::TypeParent).value(QOrganizerItemParent::FieldParentId)); } else { m_manager->removeItem(organizerItem.id()); } if (m_manager->error()) QMessageBox::information(this, "Failed!", QString("Failed to remove item!\n(error code %1)").arg(m_manager->error())); else delete m_itemList->takeItem(m_itemList->currentRow()); if (m_itemList->count() == 0) m_itemList->addItem("(no entries)"); } void DayPage::showEvent(QShowEvent *event) { window()->setWindowTitle(m_day.toString("dddd")); QWidget::showEvent(event); } examples/organizer/calendardemo/src/daypage.h000066400000000000000000000061201233466112000216610ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef DAYPAGE_H_ #define DAYPAGE_H_ #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManager; class QOrganizerItem; QT_END_NAMESPACE_ORGANIZER QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE class QMainWindow; class QLabel; class QListWidget; class QListWidgetItem; class QMenuBar; class QMenu; QT_END_NAMESPACE class DayPage : public QWidget { Q_OBJECT public: DayPage(QWidget *parent = 0); ~DayPage(); public Q_SLOTS: void refresh(); void changeManager(QOrganizerManager *manager); void dayChanged(QDate date); void editItem(); void removeItem(); private Q_SLOTS: void itemDoubleClicked(QListWidgetItem *listItem); void viewMonthClicked(); Q_SIGNALS: void showMonthPage(); void showEditPage(const QOrganizerItem &item); void addNewEvent(); void addNewTodo(); void addNewJournal(); protected: // from QWidget void showEvent(QShowEvent *event); private: QOrganizerManager *m_manager; QDate m_day; QLabel *m_dateLabel; QListWidget *m_itemList; QMenuBar *m_menuBar; }; #endif // DAYPAGE_H_ examples/organizer/calendardemo/src/editcalendarspage.cpp000066400000000000000000000136571233466112000242560ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "editcalendarspage.h" #include "calendardemo.h" #include #include QTORGANIZER_USE_NAMESPACE Q_DECLARE_METATYPE(QOrganizerCollection) EditCalendarsPage::EditCalendarsPage(QWidget *parent) :QWidget(parent), m_manager(0) { m_calendarList = new QListWidget(this); connect(m_calendarList, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(itemDoubleClicked(QListWidgetItem *))); // Add push buttons QHBoxLayout* hbLayout = new QHBoxLayout(); QPushButton *addButton = new QPushButton("Add new", this); connect(addButton,SIGNAL(clicked()), this, SIGNAL(addClicked())); hbLayout->addWidget(addButton); QPushButton *editButton = new QPushButton("Edit", this); connect(editButton,SIGNAL(clicked()),this,SLOT(editClicked())); hbLayout->addWidget(editButton); QPushButton *deleteButton = new QPushButton("Delete", this); connect(deleteButton,SIGNAL(clicked()),this,SLOT(deleteClicked())); hbLayout->addWidget(deleteButton); QPushButton *backButton = new QPushButton("Back", this); connect(backButton,SIGNAL(clicked()),this,SLOT(backClicked())); hbLayout->addWidget(backButton); QVBoxLayout *scrollAreaLayout = new QVBoxLayout(); scrollAreaLayout->addWidget(m_calendarList); scrollAreaLayout->addLayout(hbLayout); QScrollArea *scrollArea = new QScrollArea(this); scrollArea->setWidgetResizable(true); QWidget *formContainer = new QWidget(scrollArea); formContainer->setLayout(scrollAreaLayout); scrollArea->setWidget(formContainer); QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->addWidget(scrollArea); setLayout(mainLayout); } EditCalendarsPage::~EditCalendarsPage() { } void EditCalendarsPage::editClicked() { if (m_calendarList->currentItem()) itemDoubleClicked(m_calendarList->currentItem()); } void EditCalendarsPage::deleteClicked() { if (!m_calendarList->currentItem()) return; QMessageBox msgBox; msgBox.setText("Are you sure you want to delete this calendar?"); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); int ret = msgBox.exec(); if (ret == QMessageBox::Yes) { m_collection = m_calendarList->currentItem()->data(ORGANIZER_CALENDAR_ROLE).value(); if(!m_manager->removeCollection(m_collection.id())) QMessageBox::information(this, "Failed!", QString("Failed to remove calendar!\n(error code %1)").arg(m_manager->error())); showPage(m_manager); } } void EditCalendarsPage::backClicked() { emit showPreviousPage(); } void EditCalendarsPage::itemDoubleClicked(QListWidgetItem *listItem) { if (!listItem) return; m_collection = listItem->data(ORGANIZER_CALENDAR_ROLE).value(); emit showAddCalendarPage(m_manager, &m_collection); } void EditCalendarsPage::showPage(QOrganizerManager *manager) { m_manager = manager; m_calendarList->clear(); QList collections = manager->collections(); int index = 0; foreach(QOrganizerCollection collection, collections) { QString visibleName; if (collection.metaData(QOrganizerCollection::KeyName).canConvert(QVariant::String)) visibleName = collection.metaData(QOrganizerCollection::KeyName).toString(); else // We currently have no way of stringifying ids // visibleName = "Calendar id " + QString::number(collection.id().localId()); visibleName = "Calendar " + QString::number(index++); QListWidgetItem* listItem = new QListWidgetItem(); listItem->setText(visibleName); QVariant data = QVariant::fromValue(collection); listItem->setData(ORGANIZER_CALENDAR_ROLE, data); m_calendarList->addItem(listItem); } } void EditCalendarsPage::showEvent(QShowEvent *event) { window()->setWindowTitle("Edit calendars"); QWidget::showEvent(event); } examples/organizer/calendardemo/src/editcalendarspage.h000066400000000000000000000057151233466112000237170ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef EDITCALENDARSPAGE_H #define EDITCALENDARSPAGE_H #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManager; QT_END_NAMESPACE_ORGANIZER QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE class QListWidget; class QListWidgetItem; class QVBoxLayout; class QString; QT_END_NAMESPACE class EditCalendarsPage : public QWidget { Q_OBJECT public: EditCalendarsPage(QWidget *parent = 0); ~EditCalendarsPage(); public Q_SLOTS: void editClicked(); void deleteClicked(); void backClicked(); void itemDoubleClicked(QListWidgetItem *listItem); void showPage(QOrganizerManager *manager); Q_SIGNALS: void addClicked(); void showPreviousPage(); void showAddCalendarPage(QOrganizerManager*, QOrganizerCollection*); protected: // from QWidget void showEvent(QShowEvent *event); private: QOrganizerManager *m_manager; QOrganizerCollection m_collection; QListWidget *m_calendarList; }; #endif // EDITCALENDARSPAGE_H examples/organizer/calendardemo/src/eventeditpage.cpp000066400000000000000000000350741233466112000234400ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "eventeditpage.h" #include #include QTORGANIZER_USE_NAMESPACE EventEditPage::EventEditPage(QWidget *parent) :QWidget(parent), m_manager(0), m_alarmComboBox(0), m_typeComboBox(0), m_subjectEdit(0), m_countSpinBox(0), m_repeatUntilDate(0) { //create asynch request to save an item m_saveItemRequest = new QOrganizerItemSaveRequest(this); // Create widgets QLabel *subjectLabel = new QLabel("Subject:", this); m_subjectEdit = new QLineEdit(this); QLabel *startTimeLabel = new QLabel("Start time:", this); m_startTimeEdit = new QDateTimeEdit(this); m_startTimeEdit->setDisplayFormat(QString("yyyy-MM-dd hh:mm:ss AP")); QLabel *endTimeLabel = new QLabel("End time:", this); m_endTimeEdit = new QDateTimeEdit(this); m_endTimeEdit->setDisplayFormat(QString("yyyy-MM-dd hh:mm:ss AP")); QLabel *repeatLabel = new QLabel("Repeat:", this); QLabel *alarmLabel = new QLabel("Alarm:", this); QLabel *calendarLabel = new QLabel("Calendar:", this); m_alarmComboBox = new QComboBox(this); m_typeComboBox = new QComboBox(this); m_typeComboBox->addItem("None"); m_typeComboBox->addItem("Daily"); m_typeComboBox->addItem("Weekly"); m_typeComboBox->addItem("Monthly"); m_typeComboBox->addItem("Yearly"); connect(m_typeComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(frequencyChanged(const QString&))); QStringList alarmList; alarmList << "None" << "0 minutes before" << "5 minutes before" << "15 minutes before" << "30 minutes before" << "1 hour before"; m_alarmComboBox->addItems(alarmList); connect(m_alarmComboBox, SIGNAL(currentIndexChanged(const QString)), this, SLOT(alarmIndexChanged(const QString))); m_endConditionComboBox = new QComboBox(this); m_endConditionComboBox->addItem("Forever"); m_endConditionComboBox->addItem("Until a date"); m_endConditionComboBox->addItem("For a number of occurrences"); m_endConditionComboBox->setVisible(false); connect(m_endConditionComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(endConditionChanged(const QString&))); m_countSpinBox = new QSpinBox(this); m_countSpinBox->setRange(1, 100); m_countSpinBox->setSingleStep(1); m_countSpinBox->setVisible(false); connect(m_countSpinBox, SIGNAL(valueChanged(int)), this, SLOT(countChanged(int))); m_repeatUntilDate = new QDateEdit(this); m_repeatUntilDate->setVisible(false); connect(m_repeatUntilDate, SIGNAL(dateChanged(QDate)), this, SLOT(untilChanged(QDate))); m_calendarComboBox = new QComboBox(this); // the calendar names are not know here, fill the combo box later... // Add push buttons QHBoxLayout* hbLayout = new QHBoxLayout(); QPushButton *okButton = new QPushButton("Save", this); connect(okButton,SIGNAL(clicked()),this,SLOT(saveClicked())); hbLayout->addWidget(okButton); QPushButton *cancelButton = new QPushButton("Cancel", this); connect(cancelButton,SIGNAL(clicked()),this,SLOT(cancelClicked())); hbLayout->addWidget(cancelButton); // check to see whether we support alarms. QOrganizerManager defaultManager; QList supportedDetails = defaultManager.supportedItemDetails(QOrganizerItemType::TypeEvent); QVBoxLayout *scrollAreaLayout = new QVBoxLayout(); scrollAreaLayout->addWidget(subjectLabel); scrollAreaLayout->addWidget(m_subjectEdit); scrollAreaLayout->addWidget(startTimeLabel); scrollAreaLayout->addWidget(m_startTimeEdit); scrollAreaLayout->addWidget(endTimeLabel); scrollAreaLayout->addWidget(m_endTimeEdit); if (supportedDetails.contains(QOrganizerItemDetail::TypeVisualReminder)) { scrollAreaLayout->addWidget(alarmLabel); scrollAreaLayout->addWidget(m_alarmComboBox); } scrollAreaLayout->addWidget(repeatLabel); scrollAreaLayout->addWidget(m_typeComboBox); scrollAreaLayout->addWidget(m_endConditionComboBox); scrollAreaLayout->addWidget(m_countSpinBox); scrollAreaLayout->addWidget(m_repeatUntilDate); scrollAreaLayout->addWidget(calendarLabel); scrollAreaLayout->addWidget(m_calendarComboBox); scrollAreaLayout->addStretch(); scrollAreaLayout->addLayout(hbLayout); QScrollArea *scrollArea = new QScrollArea(this); scrollArea->setWidgetResizable(true); QWidget *formContainer = new QWidget(scrollArea); formContainer->setLayout(scrollAreaLayout); scrollArea->setWidget(formContainer); QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->addWidget(scrollArea); setLayout(mainLayout); m_listOfEvents.clear(); } EventEditPage::~EventEditPage() { } void EventEditPage::eventChanged(QOrganizerManager *manager, const QOrganizerEvent &event) { m_manager = manager; m_organizerEvent = event; m_subjectEdit->setText(event.displayLabel()); m_startTimeEdit->setDateTime(event.startDateTime()); m_endTimeEdit->setDateTime(event.endDateTime()); QSet rrules(m_organizerEvent.recurrenceRules()); // Check whether existing entry and if it is repeating. if (rrules.count() != 0) { QOrganizerRecurrenceRule rrule(rrules.values().at(0)); QOrganizerRecurrenceRule::Frequency freq(rrule.frequency()); switch (freq) { case QOrganizerRecurrenceRule::Daily: m_typeComboBox->setCurrentIndex(1); break; case QOrganizerRecurrenceRule::Weekly: m_typeComboBox->setCurrentIndex(2); break; case QOrganizerRecurrenceRule::Monthly: m_typeComboBox->setCurrentIndex(3); break; case QOrganizerRecurrenceRule::Yearly: m_typeComboBox->setCurrentIndex(4); break; case QOrganizerRecurrenceRule::Invalid: m_typeComboBox->setCurrentIndex(0); // No repeat return; } if (rrule.limitType() == QOrganizerRecurrenceRule::DateLimit) { m_endConditionComboBox->setCurrentIndex(1); // End date specified m_repeatUntilDate->setDate(rrule.limitDate()); } else if (rrule.limitType() == QOrganizerRecurrenceRule::CountLimit) { m_endConditionComboBox->setCurrentIndex(2); // Count specified m_countSpinBox->setValue(rrule.limitCount()); } } else { m_typeComboBox->setCurrentIndex(0); // No repeat } // set calendar selection m_calendarComboBox->clear(); QOrganizerItemReminder reminder = event.detail(QOrganizerItemDetail::TypeReminder); if (!reminder.isEmpty()) { // Alarm combo is only able to handle certain time limits correctly; for example time // limit 3 minutes is rounded up to 5 minutes if (reminder.secondsBeforeStart() == 0) m_alarmComboBox->setCurrentIndex(1); else if (reminder.secondsBeforeStart() < 300) m_alarmComboBox->setCurrentIndex(2); else if (reminder.secondsBeforeStart() < 900) m_alarmComboBox->setCurrentIndex(3); else if (reminder.secondsBeforeStart() < 1800) m_alarmComboBox->setCurrentIndex(4); else m_alarmComboBox->setCurrentIndex(5); } else { m_alarmComboBox->setCurrentIndex(0); } // resolve metadata field that contains calendar name (if any) m_collections = m_manager->collections(); int index = 0; int eventCalendarIndex = -1; foreach(QOrganizerCollection collection, m_collections) { // We currently have no way of stringifying ids //QString visibleName = "Calendar id = " + QString::number(collection.id().localId()); QString visibleName = collection.metaData(QOrganizerCollection::KeyName).toString(); if (visibleName.isEmpty()) visibleName = "Calendar " + QString::number(index); m_calendarComboBox->addItem(visibleName); if (collection.id() == event.collectionId()) eventCalendarIndex = index; ++index; } if (eventCalendarIndex > -1) { m_calendarComboBox->setCurrentIndex(eventCalendarIndex); m_calendarComboBox->setEnabled(false); // when modifying existing events, the calendar can't be changed anymore } else { m_calendarComboBox->setEnabled(true); } } void EventEditPage::cancelClicked() { emit showDayPage(); } void EventEditPage::saveClicked() { QDateTime start(m_startTimeEdit->dateTime()); QDateTime end(m_endTimeEdit->dateTime()); if (start > end) { QMessageBox::warning(this, "Failed!", "Start date is not before end date"); return; } m_organizerEvent.setDisplayLabel(m_subjectEdit->text()); m_organizerEvent.setStartDateTime(start); m_organizerEvent.setEndDateTime(end); m_listOfEvents.append(m_organizerEvent); if (m_calendarComboBox->currentIndex() > 0) { m_organizerEvent.setCollectionId(m_collections[m_calendarComboBox->currentIndex()].id()); } m_manager->saveItem(&m_organizerEvent); if (m_manager->error()) QMessageBox::warning(this, "Failed!", QString("Failed to save event!\n(error code %1)").arg(m_manager->error())); else emit showDayPage(); } void EventEditPage::frequencyChanged(const QString& frequency) { QOrganizerRecurrenceRule rrule; if (frequency != "None") { m_endConditionComboBox->setVisible(true); if (frequency == "Daily") { rrule.setFrequency(QOrganizerRecurrenceRule::Daily); } else if (frequency == "Weekly") { rrule.setFrequency(QOrganizerRecurrenceRule::Weekly); } else if (frequency == "Monthly") { rrule.setFrequency(QOrganizerRecurrenceRule::Monthly); } else if (frequency == "Yearly") { rrule.setFrequency(QOrganizerRecurrenceRule::Yearly); } m_organizerEvent.setRecurrenceRule(rrule); } else { m_endConditionComboBox->setCurrentIndex(0); m_endConditionComboBox->setVisible(false); } } void EventEditPage::alarmIndexChanged(const QString time) { bool noVisualReminders = !m_manager->supportedItemDetails(QOrganizerItemType::TypeEvent).contains(QOrganizerItemDetail::TypeVisualReminder); QScopedPointer reminder; if (noVisualReminders) { reminder.reset(new QOrganizerItemReminder()); } else { reminder.reset(new QOrganizerItemVisualReminder()); static_cast(reminder.data())->setMessage(m_subjectEdit->text()); } if (time == "None") { QOrganizerItemVisualReminder fetchedReminder = m_organizerEvent.detail(QOrganizerItemDetail::TypeVisualReminder); m_organizerEvent.removeDetail(&fetchedReminder); return; } else if (time == "0 minutes before") { reminder->setSecondsBeforeStart(0); } else if (time == "5 minutes before") { reminder->setSecondsBeforeStart(5*60); } else if (time == "15 minutes before") { reminder->setSecondsBeforeStart(15*60); } else if (time == "30 minutes before") { reminder->setSecondsBeforeStart(30*60); } else if (time == "1 hour before") { reminder->setSecondsBeforeStart(60*60); } m_organizerEvent.saveDetail(reminder.data()); } void EventEditPage::showEvent(QShowEvent *event) { window()->setWindowTitle("Edit event"); QWidget::showEvent(event); } void EventEditPage::countChanged(int i) { QOrganizerRecurrenceRule rrule; rrule.setFrequency(m_organizerEvent.recurrenceRules().values().at(0).frequency()); rrule.setLimit(i); m_organizerEvent.setRecurrenceRule(rrule); } void EventEditPage::untilChanged(QDate date) { QOrganizerRecurrenceRule rrule; rrule.setFrequency(m_organizerEvent.recurrenceRules().values().at(0).frequency()); rrule.setLimit(date); m_organizerEvent.setRecurrenceRule(rrule); } void EventEditPage::endConditionChanged(const QString& endCondition) { if (endCondition == "Forever") { m_countSpinBox->setVisible(false); m_repeatUntilDate->setVisible(false); } else if (endCondition == "Until a date") { setRepeatUntilField(); } else if (endCondition == "For a number of occurrences") { setCountField(); } } void EventEditPage::setCountField() { m_countSpinBox->setVisible(true); m_repeatUntilDate->setVisible(false); m_countSpinBox->setValue(5); countChanged(5); // default value. } void EventEditPage::setRepeatUntilField() { m_countSpinBox->setVisible(false); m_repeatUntilDate->setVisible(true); m_repeatUntilDate->setDate(m_endTimeEdit->date()); untilChanged(m_endTimeEdit->date()); // default value. } examples/organizer/calendardemo/src/eventeditpage.h000066400000000000000000000072671233466112000231100ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef EVENTEDITPAGE_H_ #define EVENTEDITPAGE_H_ #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManager; class QOrganizerItemSaveRequest; class QOrganizerEvent; class QOrganizerItem; QT_END_NAMESPACE_ORGANIZER QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE class QComboBox; class QLineEdit; class QDateTimeEdit; class QVBoxLayout; class QString; class QSpinBox; class QDateEdit; QT_END_NAMESPACE class EventEditPage : public QWidget { Q_OBJECT public: EventEditPage(QWidget *parent = 0); ~EventEditPage(); public Q_SLOTS: void cancelClicked(); void saveClicked(); void eventChanged(QOrganizerManager *manager, const QOrganizerEvent &event); void frequencyChanged(const QString&); void countChanged(int); void untilChanged(QDate); void endConditionChanged(const QString&); void alarmIndexChanged(const QString); void setCountField(); void setRepeatUntilField(); Q_SIGNALS: void showDayPage(); protected: // from QWidget void showEvent(QShowEvent *event); private: QOrganizerManager *m_manager; QOrganizerEvent m_organizerEvent; QList m_listOfEvents; QOrganizerItemSaveRequest *m_saveItemRequest; QVBoxLayout *m_repeatControls; QComboBox *m_alarmComboBox; QComboBox *m_typeComboBox; QLineEdit *m_subjectEdit; QDateTimeEdit *m_startTimeEdit; QDateTimeEdit *m_endTimeEdit; QComboBox *m_endConditionComboBox; QSpinBox *m_countSpinBox; QDateEdit *m_repeatUntilDate; QComboBox *m_calendarComboBox; QList m_collections; }; #endif // EVENTEDITPAGE_H_ examples/organizer/calendardemo/src/eventoccurrenceeditpage.cpp000066400000000000000000000140601233466112000255010ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "eventoccurrenceeditpage.h" #include #include QTORGANIZER_USE_NAMESPACE EventOccurrenceEditPage::EventOccurrenceEditPage(QWidget *parent) :QWidget(parent), m_manager(0), scrollAreaLayout(0), m_typeComboBox(0), m_subjectEdit(0), m_countSpinBox(0), m_repeatUntilDate(0) { //create asynch request to save an item m_saveItemRequest = new QOrganizerItemSaveRequest(this); // Create widgets QLabel *subjectLabel = new QLabel("Subject:", this); m_subjectEdit = new QLineEdit(this); QLabel *startTimeLabel = new QLabel("Start time:", this); m_startTimeEdit = new QDateTimeEdit(this); m_startTimeEdit->setDisplayFormat(QString("yyyy-MM-dd hh:mm:ss AP")); QLabel *endTimeLabel = new QLabel("End time:", this); m_endTimeEdit = new QDateTimeEdit(this); m_endTimeEdit->setDisplayFormat(QString("yyyy-MM-dd hh:mm:ss AP")); // Add push buttons QHBoxLayout* hbLayout = new QHBoxLayout(); QPushButton *okButton = new QPushButton("Ok", this); connect(okButton,SIGNAL(clicked()),this,SLOT(saveOrNextClicked())); hbLayout->addWidget(okButton); QPushButton *cancelButton = new QPushButton("Cancel", this); connect(cancelButton,SIGNAL(clicked()),this,SLOT(cancelClicked())); hbLayout->addWidget(cancelButton); scrollAreaLayout = new QVBoxLayout(); scrollAreaLayout->addWidget(subjectLabel); scrollAreaLayout->addWidget(m_subjectEdit); scrollAreaLayout->addWidget(startTimeLabel); scrollAreaLayout->addWidget(m_startTimeEdit); scrollAreaLayout->addWidget(endTimeLabel); scrollAreaLayout->addWidget(m_endTimeEdit); scrollAreaLayout->addStretch(); scrollAreaLayout->addLayout(hbLayout); QScrollArea *scrollArea = new QScrollArea(this); scrollArea->setWidgetResizable(true); QWidget *formContainer = new QWidget(scrollArea); formContainer->setLayout(scrollAreaLayout); scrollArea->setWidget(formContainer); QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->addWidget(scrollArea); setLayout(mainLayout); m_countFieldAdded = false; m_multipleEntries = false; m_listOfEvents.clear(); } EventOccurrenceEditPage::~EventOccurrenceEditPage() { } void EventOccurrenceEditPage::eventOccurrenceChanged(QOrganizerManager *manager, const QOrganizerEventOccurrence &eventOccurrence) { m_manager = manager; m_organizerEventOccurrence = eventOccurrence; m_subjectEdit->setText(eventOccurrence.displayLabel()); m_startTimeEdit->setDateTime(eventOccurrence.startDateTime()); m_endTimeEdit->setDateTime(eventOccurrence.endDateTime()); } void EventOccurrenceEditPage::cancelClicked() { emit showDayPage(); } void EventOccurrenceEditPage::saveOrNextClicked() { QDateTime start(m_startTimeEdit->dateTime()); QDateTime end(m_endTimeEdit->dateTime()); if (start > end) { QMessageBox::warning(this, "Failed!", "Start date is not before end date"); return; } m_organizerEventOccurrence.setDisplayLabel(m_subjectEdit->text()); m_organizerEventOccurrence.setStartDateTime(start); m_organizerEventOccurrence.setEndDateTime(end); m_listOfEvents.append(m_organizerEventOccurrence); if(m_numOfEntiresToBeCreated > 1){ m_numOfEntiresToBeCreated--; // Clear subject field indicating its a new editor. m_subjectEdit->clear(); } else { if(!m_multipleEntries){ // If single entry m_manager->saveItem(&m_organizerEventOccurrence); } else { // If multiple entries, save asynchronously. m_saveItemRequest->setItems(m_listOfEvents); m_saveItemRequest->setManager(m_manager); m_saveItemRequest->start(); } if (m_manager->error()) QMessageBox::warning(this, "Failed!", QString("Failed to save event occurrence!\n(error code %1)").arg(m_manager->error())); else emit showDayPage(); } } void EventOccurrenceEditPage::showEvent(QShowEvent *event) { window()->setWindowTitle("Edit event occurrence"); QWidget::showEvent(event); } examples/organizer/calendardemo/src/eventoccurrenceeditpage.h000066400000000000000000000067721233466112000251610ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef EVENTOCCURRENCEEDITPAGE_H_ #define EVENTOCCURRENCEEDITPAGE_H_ #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManager; class QOrganizerItemSaveRequest; class QOrganizerEvent; class QOrganizerItem; QT_END_NAMESPACE_ORGANIZER QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE class QComboBox; class QLineEdit; class QDateTimeEdit; class QVBoxLayout; class QString; class QSpinBox; class QDateEdit; QT_END_NAMESPACE class EventOccurrenceEditPage : public QWidget { Q_OBJECT public: EventOccurrenceEditPage(QWidget *parent = 0); ~EventOccurrenceEditPage(); public Q_SLOTS: void cancelClicked(); void saveOrNextClicked(); void eventOccurrenceChanged(QOrganizerManager *manager, const QOrganizerEventOccurrence &eventOccurrence); Q_SIGNALS: void showDayPage(); protected: // from QWidget void showEvent(QShowEvent *event); private: QOrganizerManager *m_manager; QOrganizerEventOccurrence m_organizerEventOccurrence; QList m_listOfEvents; QOrganizerItemSaveRequest *m_saveItemRequest; QVBoxLayout *scrollAreaLayout; QComboBox *m_typeComboBox; QLineEdit *m_subjectEdit; QDateTimeEdit *m_startTimeEdit; QDateTimeEdit *m_endTimeEdit; QSpinBox *m_countSpinBox; QDateEdit *m_repeatUntilDate; int m_numOfEntiresToBeCreated; bool m_countFieldAdded; bool m_multipleEntries; }; #endif // EVENTOCCURRENCEEDITPAGE_H_ examples/organizer/calendardemo/src/journaleditpage.cpp000066400000000000000000000174571233466112000237760ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "journaleditpage.h" #include #include #include QTORGANIZER_USE_NAMESPACE JournalEditPage::JournalEditPage(QWidget *parent) :QWidget(parent), m_manager(0), m_subjectEdit(0), m_timeEdit(0), m_alarmComboBox(0) { // Create widgets QLabel *subjectLabel = new QLabel("Subject:", this); m_subjectEdit = new QLineEdit(this); QLabel *startTimeLabel = new QLabel("Time:", this); m_timeEdit = new QDateTimeEdit(this); m_timeEdit->setDisplayFormat(QString("yyyy-MM-dd hh:mm:ss AP")); QLabel *alarmLabel = new QLabel("Alarm:", this); m_alarmComboBox = new QComboBox(this); QLabel *calendarLabel = new QLabel("Calendar:", this); QStringList alarmList; alarmList << "None" << "0 minutes before" << "5 minutes before" << "15 minutes before" << "30 minutes before" << "1 hour before"; m_alarmComboBox->addItems(alarmList); connect(m_alarmComboBox, SIGNAL(currentIndexChanged(const QString)), this, SLOT(handleAlarmIndexChanged(const QString))); m_calendarComboBox = new QComboBox(this); // the calendar names are not know here, fill the combo box later... // Add push buttons QHBoxLayout* hbLayout = new QHBoxLayout(); QPushButton *okButton = new QPushButton("Ok", this); connect(okButton,SIGNAL(clicked()),this,SLOT(saveClicked())); hbLayout->addWidget(okButton); QPushButton *cancelButton = new QPushButton("Cancel", this); connect(cancelButton,SIGNAL(clicked()),this,SLOT(cancelClicked())); hbLayout->addWidget(cancelButton); // check to see whether we support alarms. QOrganizerManager defaultManager; QList supportedDetails = defaultManager.supportedItemDetails(QOrganizerItemType::TypeJournal); QVBoxLayout *scrollAreaLayout = new QVBoxLayout(); scrollAreaLayout->addWidget(subjectLabel); scrollAreaLayout->addWidget(m_subjectEdit); scrollAreaLayout->addWidget(startTimeLabel); scrollAreaLayout->addWidget(m_timeEdit); if (supportedDetails.contains(QOrganizerItemDetail::TypeVisualReminder)) { scrollAreaLayout->addWidget(alarmLabel); scrollAreaLayout->addWidget(m_alarmComboBox); } scrollAreaLayout->addWidget(calendarLabel); scrollAreaLayout->addWidget(m_calendarComboBox); scrollAreaLayout->addStretch(); scrollAreaLayout->addLayout(hbLayout); QScrollArea *scrollArea = new QScrollArea(this); scrollArea->setWidgetResizable(true); QWidget *formContainer = new QWidget(scrollArea); formContainer->setLayout(scrollAreaLayout); scrollArea->setWidget(formContainer); QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->addWidget(scrollArea); setLayout(mainLayout); } JournalEditPage::~JournalEditPage() { } void JournalEditPage::journalChanged(QOrganizerManager *manager, const QOrganizerJournal &journal) { m_manager = manager; m_organizerJournal = journal; m_subjectEdit->setText(journal.displayLabel()); m_timeEdit->setDateTime(journal.dateTime()); // set calendar selection m_calendarComboBox->clear(); // resolve metadata field that contains calendar name (if any) m_collections = m_manager->collections(); int index = 0; int journalCalendarIndex = -1; foreach(QOrganizerCollection collection, m_collections) { // We currently have no way of stringifying ids // QString visibleName = "Calendar id = " + QString::number(collection.id().localId()); QString visibleName = collection.metaData(QOrganizerCollection::KeyName).toString(); if (visibleName.isEmpty()) visibleName = "Calendar " + QString::number(index); m_calendarComboBox->addItem(visibleName); if (collection.id() == journal.collectionId()) journalCalendarIndex = index; ++index; } if (journalCalendarIndex > -1) { m_calendarComboBox->setCurrentIndex(journalCalendarIndex); m_calendarComboBox->setEnabled(false); // when modifying existing events, the calendar can't be changed anymore } else { m_calendarComboBox->setEnabled(true); } } void JournalEditPage::cancelClicked() { emit showDayPage(); } void JournalEditPage::saveClicked() { // Read data from page m_organizerJournal.setDisplayLabel(m_subjectEdit->text()); m_organizerJournal.setDateTime(m_timeEdit->dateTime()); // Save if (m_calendarComboBox->currentIndex() > -1) { m_organizerJournal.setCollectionId(m_collections[m_calendarComboBox->currentIndex()].id()); } m_manager->saveItem(&m_organizerJournal); if (m_manager->error()) QMessageBox::warning(this, "Failed!", QString("Failed to save journal!\n(error code %1)").arg(m_manager->error())); else emit showDayPage(); } void JournalEditPage::showEvent(QShowEvent *event) { window()->setWindowTitle("Edit journal"); QWidget::showEvent(event); } void JournalEditPage::handleAlarmIndexChanged(const QString time) { QOrganizerItemVisualReminder reminder; reminder.setMessage(m_subjectEdit->text()); if (time == "None") { QOrganizerItemVisualReminder fetchedReminder = m_organizerJournal.detail(QOrganizerItemDetail::TypeVisualReminder); m_organizerJournal.removeDetail(&fetchedReminder); return; } else if (time == "0 minutes before") { reminder.setSecondsBeforeStart(0); } else if (time == "5 minutes before") { reminder.setSecondsBeforeStart(5*60); } else if (time == "15 minutes before") { reminder.setSecondsBeforeStart(15*60); } else if (time == "30 minutes before") { reminder.setSecondsBeforeStart(30*60); } else if (time == "1 hour before") { reminder.setSecondsBeforeStart(60*60); } m_organizerJournal.saveDetail(&reminder); } examples/organizer/calendardemo/src/journaleditpage.h000066400000000000000000000061711233466112000234320ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef JOURNALEDITPAGE_H_ #define JOURNALEDITPAGE_H_ #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManager; class QOrganizerEvent; class QOrganizerItem; QT_END_NAMESPACE_ORGANIZER QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE class QComboBox; class QLineEdit; class QDateTimeEdit; QT_END_NAMESPACE class JournalEditPage : public QWidget { Q_OBJECT public: JournalEditPage(QWidget *parent = 0); ~JournalEditPage(); public Q_SLOTS: void cancelClicked(); void saveClicked(); void journalChanged(QOrganizerManager *manager, const QOrganizerJournal &journal); void handleAlarmIndexChanged(const QString); Q_SIGNALS: void showDayPage(); protected: // from QWidget void showEvent(QShowEvent *event); private: QOrganizerManager *m_manager; QOrganizerJournal m_organizerJournal; QLineEdit *m_subjectEdit; QDateTimeEdit *m_timeEdit; QComboBox *m_alarmComboBox; QComboBox *m_calendarComboBox; QList m_collections; }; #endif // JOURNALEDITPAGE_H_ examples/organizer/calendardemo/src/main.cpp000066400000000000000000000042201233466112000215250ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "calendardemo.h" #include #include int main(int argc, char *argv[]) { QApplication a(argc, argv); CalendarDemo w; w.show(); return a.exec(); } examples/organizer/calendardemo/src/monthpage.cpp000066400000000000000000000265001233466112000225700ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include "monthpage.h" #include "calendardemo.h" QTORGANIZER_USE_NAMESPACE Q_DECLARE_METATYPE(QOrganizerItem) MonthPage::MonthPage(QWidget *parent) :QWidget(parent), m_manager(0), m_calendarWidget(0), m_dateLabel(0), m_itemList(0), m_ignoreShowDayPageOnceFlag(false) { // Create widgets QFormLayout *mainlayout = new QFormLayout(this); m_managerComboBox = new QComboBox(this); foreach (const QString& manager, QOrganizerManager::availableManagers()) { if (manager != "invalid" && manager != "skeleton") m_managerComboBox->addItem(manager); } mainlayout->addRow("Backend:", m_managerComboBox); connect(m_managerComboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(backendChanged(const QString &))); m_dateLabel = new QLabel(this); m_dateLabel->setAlignment(Qt::AlignCenter); mainlayout->addRow(m_dateLabel); m_calendarWidget = new QCalendarWidget(this); m_calendarWidget->setGridVisible(true); m_calendarWidget->setHorizontalHeaderFormat(QCalendarWidget::SingleLetterDayNames); m_calendarWidget->setNavigationBarVisible(false); mainlayout->addRow(m_calendarWidget); connect(m_calendarWidget, SIGNAL(selectionChanged()), this, SLOT(refreshDayItems())); connect(m_calendarWidget, SIGNAL(currentPageChanged(int, int)), this, SLOT(currentMonthChanged())); connect(m_calendarWidget, SIGNAL(activated(QDate)), this, SLOT(dayDoubleClicked(QDate))); m_itemList = new QListWidget(this); m_itemList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); m_itemList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_itemList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_itemList->setFocusPolicy(Qt::NoFocus); mainlayout->addRow(m_itemList); connect(m_itemList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(itemDoubleClicked(QListWidgetItem*))); setLayout(mainlayout); } // This is separate from the constructor so we can do it after connecting the signals void MonthPage::init() { backendChanged(m_managerComboBox->currentText()); currentMonthChanged(); } MonthPage::~MonthPage() { } void MonthPage::backendChanged(const QString &managerName) { // Nothing to change if (m_manager && m_manager->managerName() == managerName) return; // Try creating a new manager QMap parameters; QOrganizerManager* newManager = new QOrganizerManager(managerName, parameters, this); if (!newManager || newManager->error()) { QMessageBox::information(this, tr("Failed!"), QString("Failed to create manager")); delete newManager; m_managerComboBox->setCurrentIndex(m_managerComboBox->findText(m_manager->managerName())); } else { // Success: Replace the old one delete m_manager; m_manager = newManager; emit managerChanged(m_manager); refresh(); } } void MonthPage::editItem() { QListWidgetItem *listItem = m_itemList->currentItem(); if (!listItem) return; QOrganizerItem organizerItem = listItem->data(ORGANIZER_ITEM_ROLE).value(); if (!organizerItem.isEmpty()) emit showEditPage(organizerItem); } void MonthPage::removeItem() { QListWidgetItem *listItem = m_itemList->currentItem(); if (!listItem) return; QOrganizerItem organizerItem = listItem->data(ORGANIZER_ITEM_ROLE).value(); if (organizerItem.isEmpty()) return; m_manager->removeItem(organizerItem.id()); refresh(); } void MonthPage::refresh() { // fetch all current months items QDate start(m_calendarWidget->yearShown(), m_calendarWidget->monthShown(), 1); QDate end = start.addDays(start.daysInMonth()); QDateTime startDateTime(start, QTime(0, 0, 0, 0)); QDateTime endDateTime(end, QTime(23, 59, 59, 0)); // Remove all previous formatting for (int i=0; idateTextFormat(start.addDays(i)); cf.setFontItalic(false); cf.setFontUnderline(false); cf.clearBackground(); m_calendarWidget->setDateTextFormat(start.addDays(i), cf); } // Underline current date QTextCharFormat cf = m_calendarWidget->dateTextFormat(QDate::currentDate()); cf.setFontUnderline(true); m_calendarWidget->setDateTextFormat(QDate::currentDate(), cf); // TODO: switch to item instances when theres a backed QList items = m_manager->items(startDateTime, endDateTime); // Get dates for all items QList dates; foreach (const QOrganizerItem &item, items) { QDate startDate; QDate endDate; QOrganizerEventTime eventTime = item.detail(QOrganizerItemDetail::TypeEventTime); if (!eventTime.isEmpty()) { startDate = eventTime.startDateTime().date(); endDate = eventTime.endDateTime().date(); } else { QOrganizerTodoTime todoTime = item.detail(QOrganizerItemDetail::TypeTodoTime); if (!todoTime.isEmpty()) { startDate = todoTime.startDateTime().date(); endDate = todoTime.dueDateTime().date(); } else { QOrganizerJournalTime journalTime = item.detail(QOrganizerItemDetail::TypeJournalTime); if (!journalTime.isEmpty()) startDate = endDate = journalTime.entryDateTime().date(); } } if (!startDate.isNull()) { while (startDate <= endDate) { dates << startDate; startDate = startDate.addDays(1); } } } // Mark all dates which has events. QBrush brush; brush.setColor(Qt::green); foreach (QDate date, dates) { if (date == QDate()) continue; QTextCharFormat cf = m_calendarWidget->dateTextFormat(date); cf.setFontItalic(true); // TODO: find a better way to mark dates cf.setBackground(brush); m_calendarWidget->setDateTextFormat(date, cf); } // As the day item list is not showed do not refresh // the day items to improve performance refreshDayItems(); } void MonthPage::refreshDayItems() { QDate selectedDate = m_calendarWidget->selectedDate(); emit currentDayChanged(selectedDate); QDateTime startOfDay(selectedDate, QTime(0, 0, 0)); QDateTime endOfDay(selectedDate, QTime(23, 59, 59)); // Clear items shown m_itemList->clear(); // Find all items for today QList items = m_manager->items(startOfDay, endOfDay); foreach (const QOrganizerItem &item, items) { QOrganizerEventTime eventTime = item.detail(QOrganizerItemDetail::TypeEventTime); if (!eventTime.isEmpty()) { QString time = eventTime.startDateTime().time().toString("hh:mm"); QListWidgetItem* listItem = new QListWidgetItem(); listItem->setText(QString("Event:%1-%2").arg(time).arg(item.displayLabel())); QVariant data = QVariant::fromValue(item); listItem->setData(ORGANIZER_ITEM_ROLE, data); m_itemList->addItem(listItem); } QOrganizerTodoTime todoTime = item.detail(QOrganizerItemDetail::TypeTodoTime); if (!todoTime.isEmpty()) { QString time = todoTime.startDateTime().time().toString("hh:mm"); QListWidgetItem* listItem = new QListWidgetItem(); listItem->setText(QString("Todo:%1-%2").arg(time).arg(item.displayLabel())); QVariant data = QVariant::fromValue(item); listItem->setData(ORGANIZER_ITEM_ROLE, data); m_itemList->addItem(listItem); } QOrganizerJournalTime journalTime = item.detail(QOrganizerItemDetail::TypeJournalTime); if (!journalTime.isEmpty()) { QString time = journalTime.entryDateTime().time().toString("hh:mm"); QListWidgetItem* listItem = new QListWidgetItem(); listItem->setText(QString("Journal:%1-%2").arg(time).arg(item.displayLabel())); QVariant data = QVariant::fromValue(item); listItem->setData(ORGANIZER_ITEM_ROLE, data); m_itemList->addItem(listItem); } // TODO: other item types // TODO: icons for event/todo/journal would be nice } if (m_itemList->count() == 0) m_itemList->addItem("(no entries)"); } void MonthPage::currentMonthChanged() { int month = m_calendarWidget->monthShown(); int year = m_calendarWidget->yearShown(); m_dateLabel->setText(QString("%1 %2").arg(QDate::longMonthName(month)).arg(year)); refresh(); m_ignoreShowDayPageOnceFlag = true; } void MonthPage::dayDoubleClicked(QDate date) { if (!m_ignoreShowDayPageOnceFlag) emit showDayPage(date); else m_ignoreShowDayPageOnceFlag = false; } void MonthPage::openDay() { emit showDayPage(m_calendarWidget->selectedDate()); } void MonthPage::itemDoubleClicked(QListWidgetItem *listItem) { if (!listItem) return; QOrganizerItem organizerItem = listItem->data(ORGANIZER_ITEM_ROLE).value(); if (!organizerItem.isEmpty()) emit showEditPage(organizerItem); } void MonthPage::showEvent(QShowEvent *event) { window()->setWindowTitle("Calendar Demo"); QWidget::showEvent(event); } examples/organizer/calendardemo/src/monthpage.h000066400000000000000000000064741233466112000222450ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef MONTHPAGE_H_ #define MONTHPAGE_H_ #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManager; class QOrganizerItem; QT_END_NAMESPACE_ORGANIZER QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE class QComboBox; class QCalendarWidget; class QLabel; class QListWidget; class QListWidgetItem; class QMenu; QT_END_NAMESPACE class MonthPage : public QWidget { Q_OBJECT public: MonthPage(QWidget *parent = 0); ~MonthPage(); void init(); private Q_SLOTS: void backendChanged(const QString &managerName); void refreshDayItems(); void currentMonthChanged(); void dayDoubleClicked(QDate date); void openDay(); void itemDoubleClicked(QListWidgetItem *listItem); public Q_SLOTS: void editItem(); void removeItem(); void refresh(); Q_SIGNALS: void managerChanged(QOrganizerManager *manager); void currentDayChanged(QDate date); void showDayPage(QDate date); void showEditPage(const QOrganizerItem &item); void addNewEvent(); void addNewTodo(); protected: // from QWidget void showEvent(QShowEvent *event); private: QComboBox* m_managerComboBox; QOrganizerManager *m_manager; QCalendarWidget *m_calendarWidget; QLabel *m_dateLabel; QListWidget *m_itemList; bool m_ignoreShowDayPageOnceFlag; }; #endif // MONTHPAGE_H_ examples/organizer/calendardemo/src/todoeditpage.cpp000066400000000000000000000264531233466112000232650ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "todoeditpage.h" #include #include #include QTORGANIZER_USE_NAMESPACE TodoEditPage::TodoEditPage(QWidget *parent) :QWidget(parent), m_manager(0), m_subjectEdit(0), m_startTimeEdit(0), m_dueTimeEdit(0), m_priorityEdit(0), m_statusEdit(0), m_alarmComboBox(0) { // Create widgets QLabel *subjectLabel = new QLabel("Subject:", this); m_subjectEdit = new QLineEdit(this); QLabel *startTimeLabel = new QLabel("Start time:", this); m_startTimeEdit = new QDateTimeEdit(this); m_startTimeEdit->setDisplayFormat(QString("yyyy-MM-dd hh:mm:ss AP")); QLabel *dueTimeLabel = new QLabel("Due time:", this); m_dueTimeEdit = new QDateTimeEdit(this); m_dueTimeEdit->setDisplayFormat(QString("yyyy-MM-dd hh:mm:ss AP")); QLabel *priorityLabel = new QLabel("Priority:", this); m_priorityEdit = new QComboBox(this); QLabel *statusLabel = new QLabel("Status:", this); m_statusEdit = new QComboBox(this); QLabel *alarmLabel = new QLabel("Alarm:", this); m_alarmComboBox = new QComboBox(this); QLabel *calendarLabel = new QLabel("Calendar:", this); QStringList alarmList; alarmList << "None" << "0 minutes before" << "5 minutes before" << "15 minutes before" << "30 minutes before" << "1 hour before"; m_alarmComboBox->addItems(alarmList); connect(m_alarmComboBox, SIGNAL(currentIndexChanged(const QString)), this, SLOT(handleAlarmIndexChanged(const QString))); m_calendarComboBox = new QComboBox(this); // the calendar names are not know here, fill the combo box later... // Add push buttons QHBoxLayout* hbLayout = new QHBoxLayout(); QPushButton *okButton = new QPushButton("Save", this); connect(okButton,SIGNAL(clicked()),this,SLOT(saveClicked())); hbLayout->addWidget(okButton); QPushButton *cancelButton = new QPushButton("Cancel", this); connect(cancelButton,SIGNAL(clicked()),this,SLOT(cancelClicked())); hbLayout->addWidget(cancelButton); // check to see whether we support alarms. QOrganizerManager defaultManager; QList supportedDetails = defaultManager.supportedItemDetails(QOrganizerItemType::TypeTodo); QVBoxLayout *scrollAreaLayout = new QVBoxLayout(); scrollAreaLayout->addWidget(subjectLabel); scrollAreaLayout->addWidget(m_subjectEdit); scrollAreaLayout->addWidget(startTimeLabel); scrollAreaLayout->addWidget(m_startTimeEdit); scrollAreaLayout->addWidget(dueTimeLabel); scrollAreaLayout->addWidget(m_dueTimeEdit); scrollAreaLayout->addWidget(priorityLabel); scrollAreaLayout->addWidget(m_priorityEdit); scrollAreaLayout->addWidget(statusLabel); scrollAreaLayout->addWidget(m_statusEdit); if (supportedDetails.contains(QOrganizerItemDetail::TypeVisualReminder)) { scrollAreaLayout->addWidget(alarmLabel); scrollAreaLayout->addWidget(m_alarmComboBox); } scrollAreaLayout->addWidget(calendarLabel); scrollAreaLayout->addWidget(m_calendarComboBox); scrollAreaLayout->addStretch(); scrollAreaLayout->addLayout(hbLayout); QScrollArea *scrollArea = new QScrollArea(this); scrollArea->setWidgetResizable(true); QWidget *formContainer = new QWidget(scrollArea); formContainer->setLayout(scrollAreaLayout); scrollArea->setWidget(formContainer); QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->addWidget(scrollArea); setLayout(mainLayout); // Fill priority combo m_priorityEdit->addItem("Unknown", QVariant(QOrganizerItemPriority::UnknownPriority)); m_priorityEdit->addItem("Highest", QVariant(QOrganizerItemPriority::HighestPriority)); m_priorityEdit->addItem("Extremely high", QVariant(QOrganizerItemPriority::ExtremelyHighPriority)); m_priorityEdit->addItem("Very high", QVariant(QOrganizerItemPriority::VeryHighPriority)); m_priorityEdit->addItem("High", QVariant(QOrganizerItemPriority::HighPriority)); m_priorityEdit->addItem("Medium", QVariant(QOrganizerItemPriority::MediumPriority)); m_priorityEdit->addItem("Low", QVariant(QOrganizerItemPriority::LowPriority)); m_priorityEdit->addItem("Very low", QVariant(QOrganizerItemPriority::VeryLowPriority)); m_priorityEdit->addItem("Extremely low", QVariant(QOrganizerItemPriority::ExtremelyLowPriority)); m_priorityEdit->addItem("Lowest", QVariant(QOrganizerItemPriority::LowestPriority)); // Fill status combo m_statusEdit->addItem("Not started", QVariant(QOrganizerTodoProgress::StatusNotStarted)); m_statusEdit->addItem("In progress", QVariant(QOrganizerTodoProgress::StatusInProgress)); m_statusEdit->addItem("Complete", QVariant(QOrganizerTodoProgress::StatusComplete)); } TodoEditPage::~TodoEditPage() { } void TodoEditPage::todoChanged(QOrganizerManager *manager, const QOrganizerTodo &todo) { m_manager = manager; m_organizerTodo = todo; m_subjectEdit->setText(todo.displayLabel()); m_startTimeEdit->setDateTime(todo.startDateTime()); m_dueTimeEdit->setDateTime(todo.dueDateTime()); int index = m_priorityEdit->findData(QVariant(todo.priority())); m_priorityEdit->setCurrentIndex(index); index = m_priorityEdit->findData(QVariant(todo.status())); m_statusEdit->setCurrentIndex(index); // set calendar selection m_calendarComboBox->clear(); // resolve metadata field that contains calendar name (if any) m_collections = m_manager->collections(); int counter = 0; int todoCalendarIndex = -1; foreach(QOrganizerCollection collection, m_collections) { // We currently have no way of stringifying ids // QString visibleName = "Calendar id = " + QString::number(collection.id().localId()); QString visibleName = collection.metaData(QOrganizerCollection::KeyName).toString(); if (visibleName.isEmpty()) visibleName = "Calendar " + QString::number(index); m_calendarComboBox->addItem(visibleName); if (collection.id() == todo.collectionId()) todoCalendarIndex = counter; ++counter; } if (todoCalendarIndex > -1) { m_calendarComboBox->setCurrentIndex(todoCalendarIndex); m_calendarComboBox->setEnabled(false); // when modifying existing todos, the calendar can't be changed anymore } else { m_calendarComboBox->setEnabled(true); } } void TodoEditPage::cancelClicked() { emit showDayPage(); } void TodoEditPage::saveClicked() { // Read data from page QDateTime start(m_startTimeEdit->dateTime()); QDateTime due(m_dueTimeEdit->dateTime()); if (start > due) { QMessageBox::warning(this, "Failed!", "Start date is not before due date"); return; } m_organizerTodo.setDisplayLabel(m_subjectEdit->text()); m_organizerTodo.setStartDateTime(start); m_organizerTodo.setDueDateTime(due); int index = m_priorityEdit->currentIndex(); m_organizerTodo.setPriority((QOrganizerItemPriority::Priority) m_priorityEdit->itemData(index).toInt()); index = m_statusEdit->currentIndex(); QOrganizerTodoProgress::Status currentStatus = (QOrganizerTodoProgress::Status) m_statusEdit->itemData(index).toInt(); QOrganizerTodoProgress oldStatus = m_organizerTodo.detail(QOrganizerItemDetail::TypeTodoProgress); m_organizerTodo.removeDetail(&oldStatus); if (currentStatus == QOrganizerTodoProgress::StatusComplete && oldStatus.status() != QOrganizerTodoProgress::StatusComplete) m_organizerTodo.setFinishedDateTime(QDateTime::currentDateTime()); m_organizerTodo.setStatus(currentStatus); // Save if (m_calendarComboBox->currentIndex() > -1) { m_organizerTodo.setCollectionId(m_collections[m_calendarComboBox->currentIndex()].id()); } m_manager->saveItem(&m_organizerTodo); if (m_manager->error()) QMessageBox::warning(this, "Failed!", QString("Failed to save todo!\n(error code %1)").arg(m_manager->error())); else emit showDayPage(); } void TodoEditPage::showEvent(QShowEvent *event) { window()->setWindowTitle("Edit todo"); QWidget::showEvent(event); } void TodoEditPage::handleAlarmIndexChanged(const QString time) { bool noVisualReminders = !m_manager->supportedItemDetails(QOrganizerItemType::TypeEvent).contains(QOrganizerItemDetail::TypeVisualReminder); QScopedPointer reminder; if (noVisualReminders) { reminder.reset(new QOrganizerItemReminder()); } else { reminder.reset(new QOrganizerItemVisualReminder()); static_cast(reminder.data())->setMessage(m_subjectEdit->text()); } if (time == "None") { QOrganizerItemVisualReminder fetchedReminder = m_organizerTodo.detail(QOrganizerItemDetail::TypeVisualReminder); m_organizerTodo.removeDetail(&fetchedReminder); return; } else if (time == "0 minutes before") { reminder->setSecondsBeforeStart(0); } else if (time == "5 minutes before") { reminder->setSecondsBeforeStart(5*60); } else if (time == "15 minutes before") { reminder->setSecondsBeforeStart(15*60); } else if (time == "30 minutes before") { reminder->setSecondsBeforeStart(30*60); } else if (time == "1 hour before") { reminder->setSecondsBeforeStart(60*60); } m_organizerTodo.saveDetail(reminder.data()); } examples/organizer/calendardemo/src/todoeditpage.h000066400000000000000000000062701233466112000227250ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef TODOEDITPAGE_H_ #define TODOEDITPAGE_H_ #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManager; class QOrganizerEvent; class QOrganizerItem; QT_END_NAMESPACE_ORGANIZER QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE class QComboBox; class QLineEdit; class QDateTimeEdit; QT_END_NAMESPACE class TodoEditPage : public QWidget { Q_OBJECT public: TodoEditPage(QWidget *parent = 0); ~TodoEditPage(); public Q_SLOTS: void cancelClicked(); void saveClicked(); void todoChanged(QOrganizerManager *manager, const QOrganizerTodo &todo); void handleAlarmIndexChanged(const QString); Q_SIGNALS: void showDayPage(); protected: // from QWidget void showEvent(QShowEvent *event); private: QOrganizerManager *m_manager; QOrganizerTodo m_organizerTodo; QLineEdit *m_subjectEdit; QDateTimeEdit *m_startTimeEdit; QDateTimeEdit *m_dueTimeEdit; QComboBox *m_priorityEdit; QComboBox *m_statusEdit; QComboBox *m_alarmComboBox; QComboBox *m_calendarComboBox; QList m_collections; }; #endif // TODOEDITPAGE_H_ examples/organizer/organizer.pro000066400000000000000000000001371233466112000174150ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS += qtHaveModule(widgets): SUBDIRS += \ calendardemo \ todo examples/organizer/qmlorganizerlistview/000077500000000000000000000000001233466112000211735ustar00rootroot00000000000000examples/organizer/qmlorganizerlistview/content/000077500000000000000000000000001233466112000226455ustar00rootroot00000000000000examples/organizer/qmlorganizerlistview/content/EventDateTime.qml000066400000000000000000000047161233466112000260660ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Pim Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ //![Event Date Time Component] //![Create Event Date Time Component] import QtQuick 2.0 Rectangle { property alias date: eventDate.text property alias month: eventMonth.text property alias year: eventYear.text radius: 5 border {color: "black"; width: 2} height: eventDate.height + eventMonth.height + eventYear.height * 1.3 width: parent.width //![Create Event Date Time Component] //![Grid to edit Date Month Year] Grid { columns: 2 spacing: 3 //anchors.centerIn: parent Text { text: "Date:" } TextInput { id: eventDate validator: IntValidator { bottom: 1 top: 31 } } Text { text: "Month:" } TextInput { id: eventMonth validator: IntValidator { bottom: 1 top: 12 } } Text { text: "Year:" } TextInput { id: eventYear validator: IntValidator { bottom: 1970 top: 2020 } } } //![Grid to edit Date Month Year] } //![Event Date Time Component] examples/organizer/qmlorganizerlistview/content/EventEditor.qml000066400000000000000000000111071233466112000256100ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Organizer Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ //![Event Editor] //![Event Editor Setup] import QtQuick 2.0 Rectangle { property variant eventItem //![Event Editor Setup] //![Save Item Function] function saveItem() { eventItem.displayLabel = displayLabel.text eventItem.startDateTime = new Date(startEvent.year + "," + startEvent.month + "," + startEvent.date) eventItem.endDateTime = new Date(endEvent.year + "," + endEvent.month + "," + endEvent.date) organizer.saveItem(eventItem) } //![Save Item Function] //![Grid Editable Data] Column { anchors.fill: parent spacing: 5 //![Grid Editable Data] //![Create Event Title] Column { spacing: 2 Text { id: editorTitleLabel text: "Event Label:" } Rectangle { id: organizerTitle border.color: "black" border.width: 2 radius: 5 width: displayLabel.width height: displayLabel.height TextEdit { id: displayLabel text: (eventItem) ? eventItem.displayLabel : "" } } } //![Create Event Title] //![Start Title] Text { text: "Start:" } //![Start Title] //![Events Start Date] EventDateTime { id: startEvent date: Qt.formatDate((eventItem) ? eventItem.startDateTime : "", "dd") month: Qt.formatDate((eventItem) ? eventItem.startDateTime : "", "MM") year: Qt.formatDate((eventItem) ? eventItem.startDateTime : "", "yyyy") } //![Events Start Date] //![End Title] Text { text: "End:" } //![End Title] //![Events End Date] EventDateTime { id:endEvent date: Qt.formatDate((eventItem) ? eventItem.endDateTime : "", "dd") month: Qt.formatDate((eventItem) ? eventItem.endDateTime : "", "MM") year: Qt.formatDate((eventItem) ? eventItem.endDateTime : "", "yyyy") } } //![Events End Date] //![Cancel and Save Buttons] Row { spacing: 2 anchors { bottom: parent.bottom left: parent.left right: parent.right } GenericButton { width: parent.width / 3 buttonText: "Cancel" onClicked: { organizerApplication.state = "EventListView" } } GenericButton { width: parent.width / 3 buttonText: "Delete" onClicked: { organizerApplication.state = "EventListView" organizer.removeItem(eventItem) } } GenericButton { width: parent.width / 3 buttonText: "Save" onClicked: { saveItem() organizerApplication.state = "EventListView" } } } //![Cancel and Save Buttons] }//![Event Editor] examples/organizer/qmlorganizerlistview/content/GenericButton.qml000066400000000000000000000040411233466112000261270ustar00rootroot00000000000000 /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Organizer Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 //![Generic Button] //![Create Button Rectangle] Rectangle { signal clicked property alias buttonText: buttonLabel.text radius: 5 height: buttonLabel.height color: "black" //![Create Button Rectangle] //![Create Button Border] border { width: 1 color: "black" } //![Create Button Border] //![Button Text] Text { id: buttonLabel font.pixelSize: 12 anchors.centerIn: parent color: "white" } //![Button Text] //![Setup Button Mouse Area] MouseArea { anchors.fill: parent onPressed: { parent.color = "white" } onReleased: { parent.color = "black" parent.clicked() } } //![Setup Button Mouse Area] } //![Generic Button] examples/organizer/qmlorganizerlistview/content/organizer_ical_test.ics000066400000000000000000000041731233466112000274010ustar00rootroot00000000000000BEGIN:VCALENDAR VERSION:2.0 CALSCALE:GREGORIAN METHOD:PUBLISH BEGIN:VTIMEZONE BEGIN:STANDARD TZOFFSETFROM:+1000 TZOFFSETTO:+1000 TZNAME:EST DTSTART:19700101T000000 END:STANDARD END:VTIMEZONE BEGIN:VEVENT DTSTART:20101208T220000Z DTEND:20101209T070000Z DTSTAMP:20101208T051153Z UID:Event CREATED:20101208T050327Z DESCRIPTION:starts 2010-12-09 8AM finishes 5PM LAST-MODIFIED:20101208T050327Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 2 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART:20101207T220000Z DTEND:20101208T030000Z DTSTAMP:20101208T051153Z UID:Event2 CREATED:20101208T050202Z DESCRIPTION:starts 2010-12-08 8AM finishes 1PM LAST-MODIFIED:20101208T050202Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 1 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART:20101216T000000Z DTEND:20101216T060000Z DTSTAMP:20101208T051153Z UID:Event1 CREATED:20101208T050550Z DESCRIPTION:starts after Event 3 and finishes 4PM LAST-MODIFIED:20101208T050550Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 4 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART:20101216T060000Z DTEND:20101216T070000Z DTSTAMP:20101208T051153Z UID:Event4 CREATED:20101208T050643Z DESCRIPTION:start after end of Event 4 finishing at 5PM LAST-MODIFIED:20101208T050643Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 5 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART:20101215T220000Z DTEND:20101216T000000Z DTSTAMP:20101208T051153Z UID:Event5 CREATED:20101208T050446Z DESCRIPTION:starts 2010-12-15 at 8AM finishes 10AM LAST-MODIFIED:20101208T051055Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 3 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART:20110108T010000Z DTEND:20110108T020000Z DTSTAMP:20101208T051153Z UID:Event3 CREATED:20101208T050752Z DESCRIPTION:start a month from 2010-12-08 at 11AM finish at 2PM LAST-MODIFIED:20101208T050752Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 6 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTAMP:20101208T051153Z UID:Event6 CREATED:20101208T050926Z DESCRIPTION:starts 2010-12-10 at 11AM finishing 1PM\, repeating for 4 weeks LAST-MODIFIED:20101208T050926Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 7 TRANSP:OPAQUE END:VEVENT END:VCALENDAR examples/organizer/qmlorganizerlistview/doc/000077500000000000000000000000001233466112000217405ustar00rootroot00000000000000examples/organizer/qmlorganizerlistview/doc/images/000077500000000000000000000000001233466112000232055ustar00rootroot00000000000000examples/organizer/qmlorganizerlistview/doc/images/qmlorganizerlistview-edit.png000066400000000000000000001063341233466112000311460ustar00rootroot00000000000000PNG  IHDRRlsBIT|dtEXtSoftwaregnome-screenshot> IDATxu|acltw R(&nDEB*tI  6ul666~|>|y(('(`(I EQN:J}x)L!%h4/qd/㳄OlWN|> ^NSg9<znúDc:i;c|nN錘Np|~\N!Cm>)=EƲ7r.{w#n >!S뤉Tvn^ǚuk&yN=Aԓ>}Ӿq86%BZw{]{r=1gSx3w#h9cLYE_';>N)9 >G8as:9T~~l! AOP n{l_љhj(g^ = ]:Q[3fAP8p#?;pX<> [I&1dE߿"М_Ǭyb$p2.I"@ ԇKd&Ag.|ՋO^tlIR$ԕ]Z}<*2EPY5zrxr!FbOQ)-H:1Drxdɑ< oЈD"HݳiAD5YR#BO؈-xJsIߟ,K}C"MhBpgx:/8^W˗:zp|:{2;爃'>Y""Ju?iLŝvIS ,c%%l9R^T";i;w4YgX:8&_b:_.O]+v_:Re}]$2,Ixs?I:/YlF|M'~cSgʸnQdы3K*VU5)ϗjg%sI:|{mW^p|6{S 3Nz5GK Je[Q/)-Fu~B;'tro\$.PfF䪧߲ծ%i~D\ͯ#k,q̜,ە/nzNHǥWOZ&o-GT ˏY0o Oe`ble1wзKG7A wMns+;'e,b|7{c%]sAA~>gG 9wz7mDlpLg7gO&3v{Gb>zb#ݸ[gpj~K{?y.>|vzEUWF7wsHpN4Б_7ƐY^M=#|LhirZbR2&L#M mgxp@AL>H-^,k,ZAR a\`CMǗ\2>b?Jvӣ7B/&#wJ}?Z/'Qbvw ?ȶs{K/X&~daf2olhdW=KUb͢4Cn)j8sȷHEڌ&;K*ɣ5 Dәq`x_n,\zqҔrd+sew'C7MqEFnr{k%[' JN'vzN#-.Z?H2\rjhӄ&Z-s1͞?~-bʈUʤ9$.ߜC<_MŨox_o \DZu1IsQ}O)ܐl]K*Ub?[wHU,N'E[>kNno`6uT5=9K^'^YmY|Yeo0JQɚ|%;^.$=Яś/KLfIyzNoN~|?%CJihR7*Å\ܷ jDHi͇e GuW[3os)a=ӿ]Ա̭^wo@wN" ǏBDдe2ڶGWlU,$tFqаiA2s-tM\fu$R;.<9%͸R9tĎ4daiv.z$?TRJ~CGo[϶DZؼrH.ё&!zMTRkٗ뤃,'q3|C,x{yngPcڍu{a/箦y}Whu;-5sYZ*?p[vGMvt/Y{؍fLgG3#_GÞɧ#Ty#M "a854MM½+Ob~K9LӃk)eҬѐGiChfėAFN^Pl.5Mq_| Ɍ|S[4zAge?_:o_=ø>7soz5ƏhC=)is9#m3$-ϼu/0sWpHߔCz$QO@T",=ågH˄"0;6n.AC5!r MBYV ^h4ч6Qd@ &!1ÚLR1{?|ss7}Н_xG/oG?w.eV҃m,y1+W-f L-0[ m ѻG"Ɯ8kVmॴjN|,fda0ƹ{!?BQ fUٮmb-N.?̚C ]Fan99O??|s;v_+;a~ゅ=Mvvퟜ2 m43ލ|,ɟl?ڜw]ߛ7c51a1>z?[ނQ%$MY~_=?n}Z.HPdCmkX-Bۍ]ܔ~X‰kڈ~~e>2ǮUG2Cޘ|݉N1vM:eO) 06`he(GBh9ZI޲I<,O-U:wiɭ.Kyx~AG=_83^doތ oǡdU哝C 1)JjPR|kV~|9Gī`iPeo֦ܷ13,`܋ta0M''dP=|0HH4G>{~rVrt\[tg,s]ϛdBgnʥWEfܵxH?ԛïeČ |գܔKw}ˬ̈́ mnWi=~K.8 Zf63η= [Tj I>}o,#N!>1?䥧e*VI!ztTϋ)8m>kzU|sgzoL`O׸߆[ޝ==c@jퟠV jLkl2m/``;Tty>K?㻹?ix|c@q-:sɽ1y$4 4I`5tHJ Xg>%4f.ʁTvp#A rկKL@vÎj2nL[:NFtBw‡&q ]v깩Lh$Bpי|ćΌz>m8~jf{SL'.dh'eqyIU߁oFlMg jBR@Ոu2ϗ:h&KϗӉÊr4b/ȣQvg9P>GyEv1-c=>g!ɧԍf 8$h"CD}nʊ (qϊ-t|{IQA9Ȇկx=`VKe4BzNuf+6ÙIZf69VҨ 8*ҬK`']I.¡'AUxfgqv<(#B𫲿)+ ؅ڂ1W>g1v<1-XKY=_'|y˟{}jkKPhWӨ"X((Jq(EQO EQN*X((uRBQE (JTPEQ꤂(R',EQ:`(I EQN*X((uRBQE (JjUs+UY2@ض+ְa^2 mDDF0%ڷ$)1؈`LŒs;qzMol{K𺜸u:EQ} w&fϼ3=3`ayf%/wVK>{'9@谇yDմj͙eǏt/ѓ(ia{*s|{_\HNXg.܉@,:eۺEsf _E(He4zoV;#Dp䧰bvWE9} ϴiK9lhOë7w%ZhcïCiE*(Kƶ Ƅ ߦZ8JCNBrxK;>|8ٷg7%P΂-&ҼLҲ8K~,V"biGD~%Yidˆí=)8C'My2Iu Ñݻ)M-j()˾f6zOW2ULlj5D*Sw;wL{r 6^tH:|TN2o^%ɶ4SY0W3|<3re0 4jLGD;۔ȁտ̙3䇹P׫(_eֆYulݷY^4_> o%Sx XQ׌8ٱdG喑iEN[f|;"׶QoZܔ=w]ɯmzSI [Qs>@r %44 8_bK x@l WE9!OjygTS1h咻y~݇Z߾Y s$Ufe*qU;W2;U,=$/?LbtfKY;ry >x>O,yX B9%$dkvm*(Ga8+Y7-| M-):vߐ `P'0UrTQkmDԈmjF_t'_5,urxܖZhO3AC1x+DU"Lt+yҚPEB xկ,YEKV~>¯nK駸N] #YdPT! `#e驤9`ams.E(N.fnٰ?sEQ*Y!0FѶHn)%p&wobLlg zErRB\Z&3u֯lNͦl""DR"T FGEQζ{̀5xk{pz>Թ|3k=# Q>1w*;q]{*. M:OE EQ:)aWWJ@NhAAn~]ʯ[E率;4cQ1b4柳xmc 7lXz^zf=̏1:m5oM/]1{v*MG\ɐuȩ^].`Do05n>1 胻p14בKmhjvZEQ0WP/\ [Yf+ X⼛q\uAwT7F׾AiZC)l/󠋌-7;:_}?韼'yoF<>73U첄`ҡIjB@ۙ vfi)d=$Ri>Ԭ֕UEMhWu$#0bE gֈAYYN|ncoI{fy;8EcQXP˧b %|t 0bIqKR;N4#6F< ;bUqEQP)X((JT+(R',EQ:`(I EQN*X((uRBQE (JTPEQtUVyffΜܹs4)(ӿ DXXX(/>>>U>/gtM<$$$`6QEQN';vwa tM)TEQ67n|IyظqR(Ra׮]4o޼tztuM~ܮ>>A;}EQNb quOpUDCg4@5 &:,p+x]N\^Aӛ1t'^;Gpe,X)Yh1ڏz.Z:V-]̲5؟ܐfϡ$f)7i+Mf?Yjtb Fmϓя(?oL^,Yh:`cgy<{Fm^(njFIi*W| ?ȫ7tHv&oi ]LYD/ $D}nd[JʾXi﯉hp}TeQI~J_ol/~%z2]|o*IZ0Y6cMp|jj[lw_jHxS~=_fU(;lr/b %*6 YF<ŇIKGz#&b3MOXLCB'9?ӳ)_h48& ^ IDATCJz<ك6 M7Fu/byl?q7벝؍b7T;rzO[yXbZu9\E9'a%̜y2},#C;R7>dMۇhzaI6o4T4%QOˡBJּ{?;\P1bS_a6 ˼0bmܓK<7ehF{-_ρ53:oaڃ-ڵ[ѠmwĿϦ=kX+ c0"839$G\԰1MxʍFЙ$EQN2X5*- 3!zvvtmFH )po,-?|]i%| ,f8V;'Qg!&"WXIwsWv$|ٹx4 ęίo}J|v Mi#5 PƓ'sy/5DXHa2pVZ{^:FNF6E.QBQz36 (vx}eH]تʛshe8Ț_7r&Kj=9l5h}8@G.qۘ4.O?`Y$S\׵35|e8}7c1ru&Lpc/s/Dsf-UϓEsX|88]QO;0L4WySIu.%/0G(;q{Q'Ǿ.@P+o~'G߈QQ_9i4oMungdv@cpNOup@30ʵfh2r᭸$]H N7BM,Jc'Lf^[EQStjBKKp@L@GRm?gJ/'^rz Fv*Ve0Cn gaEv/eo0Z0vU8n=? * Z͇}L}t&y OV ;val1WflŠ(ߩ W!(A؎ '1O0s0c-H%)sy(~,)ж ZjoE3ŷU\@:X[R@o)0f!(8ʭU =b; of?> oơn1:E9#N)Xrvۆ8 CnR_>~E0x$YEd/MG3gЂ1gĉ/@3wJ:=F6Qdk\G4"|7:`,m@QA!.`.W^M')9,E9N>Xx>f-9Ӓ ʙ¦i‡p^dp\44Mٰ`b6q vs ۍ-ydZ7 ټ+Ҟ6~W.WџBCΕ,ٔ>RO~(tRĝ9܋3{Lw*2y C{sה365ť8kh=79gɏsؘUӁN碴 C,cg~zu|eCTtmb?'|ˎXh5bmV}EQNy}IsjC3 pW[Jn[޿aьDyXVBX>\YsSX)HV7'.dc)1F8?d7I/HKݏeiQCiZ87|)YA.^;yGfhuwBnSPQ Gs_3bſċa̎qJM\Le{p͍dxQ<3*< r:d8hzM ui Cؾi-koa, =&Biݥ7}tu\&8E9D\i2WƒhʄZxȊ.1q@r+(+(X/o (3IKpQ忨ZNo$}wb~al\%Z"E]_L| hY 鿟鰶?_EDE">厑+SK0>Fo%iԻҥ7m*ع;=:%vmt>_L[%ݯZ[Q3ZEkC:e֎aPb27qfa\6:sIVX̭^y餦Oό5"&i y)N# PƄarp`nRӏP"4&99ȀJ#i?'}]ZыiCbVޝJFN)E$)17/&Cւ6*o'K{28u = {E7: )(^5}"$ZU*gf,k\\G:SxCfL@3аp}6+[)Fxk4w[Ɂb/> WSBLTfOIL|= wL9.Ic_˓okykzrCx^U=)^#..IDGXB tT(p|0\wSK"bZ`Kc[jkws)w342d}|w<6NРp$qSVOIB>xl qѸiOI ~[8#Q@LcxȶMgW@n=AB\VVޟkpGiIc>;鋙)<5 Ⅿp3J^=L*Xx 9'lFqDZ(gBalùC˘l#v%TRs7 WhGyK-Qco<5E \䛼0v0B331}u:unݽ"%Cҟ |:&6.o\EcY>[_?f]l;h`ɔI mc[Xqc }n O\ۃ@ G cMZk7m߭/GL~9D8/Bo(s|ihz-wlVY\/`rznC& v,/;\D{n#Psm00I[8J3~M8!\¥}lڙSM5DG[cTϊ@Q~ 9/NP/7SuGcX/yē_aov­`SBQ3E5CΉ'mc粹Ȉא=,-atp"Na7'1bv֤ntiv\GIp"cjtޙ!կ@Px x W1#>C ;ӟ*o~yI i{  kZՂsȹ4-YϜI=ſ&//C$a=A)k#3=1ĄhkVOI(9Αw 4l@_ ;-Z1p=`BimRC5@8\6c;sj2'^{Fo+V{m0[zز`&_M E4>w0=pxnЙ Āբsaw_0k 4ZML0OֳWv?Ƹgx縭_0YX83YٓW^ZKQ:CxG䎪|e\q.o[{GY!w1㷘8C+|Xj0uJ(b"̸wu݌sy7`lݕWo}t^ '^ܥVJh^򏐕) iCl'b픧OdȨ;rE\YdU;3gE9PT#nNv _6fHٶb?D;>2ڇrZTQ:rS;_̒IhA@bEMPWMܔdb0뮽2!YL!;.Ksy SIqs6䩬?ȊYYNڇ?xɫSE9^E0DHI4h,a$7'; ]8I|·|p:, C@4#cA>1Mb 4ک7RmR>c=nz/:Տy(exz }@`5J1Lx܈,Aٖ@RR Az5 M"JlJT.TyĒt4 >_0> O-)Y `/̥Cg&4C}"b^ SP(fSܥdJ#+E(`S @pSPc!!5Lk/ȧCgl8^GYeN7>0` 29)/0?>.O_H(f](((~6BkJRWJf$ 4 O%8fTE94MQC(e(('(R',EQ:`(I EQN*X((uRBQE (JTPEQ꤂(R',EQ:`q ^ǩ%^/޿bYzO9݊s>,:+ncդis[T1~ E|\`"ZЩ9ގİfqk3 ~OƮ%uw=?=[XۿMYQ9Yd'J<)w"΅=ŠYv;i^СBRE8?BZubNR^-n;[V}$ubv|HmSg<6[`4A >X_Lݐ%N_pmoPQb 2^{ Iw԰s2ksAFivW쒶|#&M9HlV 0DI-y?3+ U:$]ʮoɨ&хt)*RKO&!b2}rYq_5_}N9Kd凔R9.wfȒ/D&~)dӮٳux}9Ќ=RR} Έ1$<2\ti F溂OJvM{N&]nxUf*{RvɦESыO3IɲîzE{9,v|+v 9Y|{T2b٤}N)4u|3c.w[*sc >N.{wW+>Mu;mBe3C^' gei~@ AChV3ޚH3#;_Ov/BRVj΂LPf.E.A15nLBS->W19ig吗OKH i(:wӲ(v%ƪQ|8={r85{v'Zb.=S~ƶqv"Rl12.Lp{c~ b!E_GA b4 $D{m%>'tJo"G C||[*kם"= Jrm0񎼪c-R^kpboYܴ$I,Yh0p8Fl|wԮmsSS.c1j@z~̡9o_F-Lȡu# 4.L#11GbIYlԱ$r]frsIEKx[|Ua:f$r<OS+s$]mk6#;<}m:_qٟV`5O- mE,cƍ IDAT[1?}*]jɘef3H`,rYnҕ<IX~ɋM_E(@Pͥ?@s|<~="1wsBݰ;?L?]TLPx(~9)8au '=\kX8AfVJxq2Ԍ$I"TlUX0e|"ۺ3k`-}tboYzFZ:GF$$m5?/ہFZ<.bZf( g I@hh@Dk4Kv3XNsK .ة֌٨=Aa4#pzhgIxq8p Tm;FglԂʒ$]*{ m z}c-/Yټ Ŗ&tԔ#Vi nӦD8)T>xJrH[HÅW4?M>IǓsAфӮOwTSdݣyO_R|b"+$<7JǍ-P4Zt6L+huzt\nr:]iECӂryd+Zp,1ĄPP!r:pdhr1|3і N +s?8' fބɬ"UFB^Q .P;"eFPO3W&RksLyu=*6*1DXtVt&3&y(J):f:n +ƈln$:*f" wo&K_}ب%!:T,Xxs|sv) >wnLtp6?3z5ڴ@ggL3&3u_4f 8ޜ$}ޏ@w.( f?+ -FPOK4b^Gy.&$  Z ^Aу'?z;@=ҦYljU|lvx-ٞ_2#poW/vc97ϐb"PeCV-Q HONX[cb&$:pPZn|b#;L7J@uj|7KtE8+G[9IR.sf~ԁe/jZ1Px H?L agKeʬcyvVg򔧹efhy>L zrٳ$N]m4KΎf4вnں{:`?*i''+Y}BХG3d jXMCހJݺZ.M&\^{ILYxq&9QGpha<Jx)<ӧAOoD~77JJs*l2vv;v{&ťP]d˾[ܑL[./.Tg:ֹۆv&fEPi ns;ϽDo1;DHؾi=;;mt/=Z'Æb٫y{H\sN HnMaլe S8swo6<0K|ҽ`Lf`|ޣt >ua}8~>|;^Mr$a<ȦVgyvdV]GsP(#%x8˫ܵm 4o:?[ q}G2]<ƯL| m7 S]#{WO+9m㸣C'IW8SP UlMdmM>1/0RAg$ak>4[ C'3۟,𣪠֦yxs]Hɑ pWvQ 4 7=@AWAZ!\(,*B#W=B{=f-͔nфk{яgm !P5P?RZDiJߐR{~#??~ 0Ԓ5PtS(H^9y{@g $f[pL="'Bo? Ј"}<+oM)")Ҳ qi,GSŕCN@pךּȷdb_11D_g+IҿCQj$I$QEv$Ij&$IT# @~~N$It @YؼyEM$Itiڽ{7*A'i$I.!'O$66C `֬Yu1'I$]D۷ÇPo6!!APZ߿X~BAݻ 8$I)::8ڶmX:"GI$I5,$I`!I$H I$F2XH$I5B$I $IR.0E=BZрQ~A%I,'=1yFK\)píĴ;`!I/+wE ~ol# y[q=xAIKW?Vbv`= &* gO8d=#˟@OP}P6_C@ע$oD$IsW7Thzu}lLXRki^T%eE=EV/e Zѿg S,tGf3ض%54<ƃN%IsVeEH[7i8-gy&JDT~>`h Vl6#KIV It1Q d~aDF]-I$鼪ZDP4h)g֢9W_ 6vG`$I$I:/.⬳tF3&;?Ͻ"r$I.bb &&T;əŨ>rq*Pv$I.zЪJF.Izwcktoq5gPqdIVN!._F$IǪ * +Î^'3ߕFgy:Z3X|pNjz3Ջd{$IBq &rƷU- ?p&o_?Oh0ק aȎ ;*1背hdÆ$I!x)&qf}4FAH#<PhMٌU붼N )( T-o1YI9MjILl$A99x@k"f8}~$/ ZL$IyW,$I$z7$ItyB$I $IRd$Ij$$IT#,$I`!I$H I$F ްavb,]bI$IO޽ׯ[.2//OL'OyyyBG1a~ƏOݺu1H$IN'c„ (yl7$I%#??cg۷$I2qƥpȪ@Ѣ9W(r$IYr:L`!YY+S\vQ荘-VIDZ pA: .'.@14Rl GrVoHz-&{ҷO7ZvuB7vc/dzl4j߃޽Ѧ~0+aIt Q FWXt{aE\9XiyOɢ#/OXb`A|#"&Ri,l&Pxx={S…^A(Z؄E-n$I*JKi8OӉ=GQ>9iٶW'> Ouu竘>̚ҡe]/C̭̚48J5cxcpշ!יW-z-_:-ępbq۰\T9k[6{ KªCCi>H"IyW5X-}Xnx,ܽ h؞\r>}._˶#v*@b M7^4 ,2^b<(뚦#chݲ[|ÇS?`ݤ{ˮ'^Wnz)ƍOJ2%)l[*ڭ1 :vǦMر a{ݲ3b?b?|0k#prK$qaXq-ǻw뷟A_[$Y -Aӿk4Mf'ZSpM дF">*'#H=>Bf|bL.1OT|K2qFrweE,Wsxb[f'xV R8_/I}p#ELfKy/tP)N?1 Mp ᫃)8)=I Q(zfg0Έ٨oǎnl$<;`6Wv(iZKOeu5$^$r9 dGxeLf.DǑm Ԁ_k$dgdPaUToT3 ey呱mfvkT1b\7EY tT)V1nC&=-lZ--"9|\TAIv2'ҝBf|D`!IyvnZ# , r)pxdkH&V{$ΩJ^RMvd/yJ;2N 0!+ʯ 7r|Bh~^2wwG  Zǭ1`6ত؁[-\ ZqbuW+dϊ%lL**g$IΩd:2."۞FFv>%N^ekYT8g86$S[[PP_>iV7'.`6xWmk=7>v@uptrt(\.e lBߛ*#vΙkyzU1g F^Tl%Iѹ ᦤ Z?]EںY3dMLΥS J(q^-w^2'rPshƮ7-DNyxᢍ>Р71h@uӍ@lO!cTƽ3ƊMi\7OOBz(:Ov`b@!Iwn•G$p NW^dqͮC#V0Vz(<&}v;D>nT}-!9Bx"r*&mVVi0hD˷s~aն#۟V#{E҈Sb0\+T6nKtASpeb8ҦMsbl?0o?ٟ}J/1r̘ +}70Z: Բ)'bF* ELRRQu Ri6}#?7"@o fE)GRpjCoC $]g,9[6yr!7%,rfaǮ$\3JIn R|EQf6L ԋ ɜ1׺Xj7y~\w]S)Z)*'XF4oZi0 }~`=vdbğ$I %9X^>fCQe:]S{ۂsJ[pEQANG[}:hGzU =׉(7Lt!MzuKXd5r*]Փߗc3Q<9{Yͷ0lPz5:c%IFռUDnN6dgg©{ٲ{/}Ubeℛix7?Xd3'C$|..IF5b$v4'l`ۑL.n7n}CJ!2? ;?9T\ټ#\{fُ{lc9G;@Eᜍ$%[߿njai\8Xn73 q5 o ىx]6{` eB R5_]zpeǞS[(k O2OMԌwƜuw+:Rx]떰`F_JRt7^#uF\ " 4kuao2z}9SF'wc0Ed:MY9ͧG:޽EXRS-A-oɧr];>Qt/@' T#yvO-#:̌7;]ױhF71UtX$]ʂ5z p΢BFglEmڒm;йS;׋J=]8,-(bƨ]Lݾ@+zbۄNo 1c#"ċN#=e;)fb{?λ˙|*fMYDt|h#^s:={`PvRG&جkt}(Ȍޙ% ʚoUx`hMfSqe6Z+P4z;9viQ6S$IeJrsTOWz ɀLą$R2*`!"QW>9y~X| ^\KpyTU PSLNz )Yњ^P :ͧ# K^JrR8y*|'%Vu擙=;B7ٟ(b"CV$IQҐ$It^)";H$I5B$I $IRd$Ij$$IT#,$I`!I$H I$F2XH$I5B$I $IRm ˝;thp;9 IDAT~2EEAѢ9%I O6{]iPu+$ZkSv&q<rˋC4Ggc1\Tqe'=-'HujiZV. 1Ħ-9t2<8wFiAI.0!\Ib텹t"\ Dtţsר"{ boA1}H-1:.4MNju]w2.L5B9 omj5Gy{7cVQ~B P^WvE'["^GdUcI$3T%v4!E[Y4ê% "*Ҫ\7`L"58ajݛkaB/y\ f0=1Q7v$R6/s؅o|#kɼzNx(h1t} /? =ZJ{29Ymsb7s?O;c-w5wa[o|ORJV]O3w_Eb;u{piu[pgQ Q/F*$IP5Xlmۋ?UKYwd(M[ Bo\J~C= wf뻗yhFO0W[9?=cY2.I)ˡ07>~Vo"Bu,{~|0)QC<Y`X_/ ˢ%4VTRхy~lJm}V㛹+YxyH},⅚wp p'S`wkC-Y$¨,4 ЙY͒eG5 ~*:0O+jwWsZ0_/kQ`if'zvUa!_y+|\]VZe幃;G37X;{3FИGh`W?r$}Awc\]Żahq?mǹ+w=Z.+2uq6j^E$IT]ED-%/ Z˒5' 4-|7Qsٟ0]~'?(~t}B$E[|DGg}kl4q7uaǁtJN'#ՔF FȰe:N^wHf~F%0aE[ܩoնb=!-B EC@|w%gVfs`R֥WӤ{@q*.Bhӧ«fڐfԕ(MD~vNthRS'&7\5@&fS/KlO^E [ѧxz8/YKwD;}t %IV_zѠp Kxr7+o$;jt'ob%٤&gчW~ULFn%Q讴ڄ}5N 9 ELd L> :vp]6Pg"̋o=/yIlgN|;{KSWۮQWEt 0)bJ\UK:@\t&)gԽߤ`G3՟8u=Ͻ5Q=c'I.f(lL~klT1%3eR*G N@c4c2Sc_xy8I}mM1rH*סNuZK1\NQ("AN+آc_OE))!('ɬ"_~2уaBI ;h;Q<\~1 FkӴC"E.\gPNVqJ έk;% _0lMiaBs31s]۹uP|t:'ƀŒdՈ@ciաlݕ;SF>d oo"rzm (^s2H{JKCK`~v髗0ecr[0\YWa_VIt\Sk;[ZsjTmxsf= =죏pgfTPO 0mT0 SIt _G8p*#(a.P6;Zkm XmbG#_Y-Ơ?7yEAQ@UJD\"6 BM|IaT O5$I)BPECEchAw&SLnN? T,RN%C Na6` p[@ϙiKrs(phLی>u;qSt % I~N%"rsṕ،rۊ-ĥ }Gsy.YS!Dc ƿ솅Bgj$I:E) WH!I$Se*I$IG I$F2XH$I5B$I $IRd$Ij$$IT#,$I`!I$H I$F2XH$I5_'zm37I!:hbpjbEQ#:s ouJ):<_]lw ibXvc? d8#{>wG_D]Vl޾]lٰ\|cA+{>#~>R$dD^C_;l޼Ot mXâ[{7F *BW "t-Ej (<8W"4)b=bNJs7f X:LK_"bt@16O * Wz1  ] HqƊsE]93%h@#nyȩW!nh;"sz8)M("Q{kbeL8IlIX&~߼G9!rs_&t3 kDF+"*t dwxPMĈ/vjJE$]o8Nc޴ti[pT?h=4x?kUH<Ω,]CW0TS LI"9= \,DQN(K w>iI5X¢ P(HKѓe!ѹSBkwYֺlX:z}үػvʶ|YĊ­}b+lc4o~_Ɨb\/w0GѮO-Fh`wN˞|% H[9Q T6vxG^1 /5G|,nZ ,4~ 8j#6>Ȼ?Ogj6ԹI)J5 pgxlqqܥQVIW7s60'r>`>18u'+71lۘP ]kXO~aWn_@8)dϏv&ܝfԱ:pa3 =c?z淯_mRpP ŏ躑NosG[#X‰,6sX.3T99u"i<->VaO0k39V9NL5X `l|~> 6~Jį8ƍOxo'}V]}ㆶ"\{nޭ ̟>.WdL j3_^L0m,{7w 7|uU3$,qzPϦ/]H\"( 9v? ܻ9n؝AjAD^*Ok&(<?tr:HuBG5, h%O_yDهJ%(-j:=: x].cdvZ4ѡiAxp<2XHW-8bM((Dj9[8Oq4D[hcLUPCKJ3wagow|dVn*#!TQWCx#(IZ˧+s59:zꍘ ]]Vv:q :<F EuutyAcl6p 7 IeSRLSO7>lԒZ,9t;7&:8IҚxmJTI `3]/^KwoN~>Gk Oi;@TEy KuφFY#(˧ȥk1Bh\IY фY͝~n iӬ*~6;]yr{<{c LlOPw7+cÛogHjC@1~(HJ!b'S,ԭ[1D1CAp(-7T>JR% :l%I"uSH -x$)9Kމl?ڲ{5(<J&WM%Moeı<;c+3y2o3X_4[żlv&цJb=y6M%\g@p\3Fh}r7{Orm=J K4 J Mң~{X`r}5,R֦Vo@_n]Pxt- lU(7Li.$wvLÄ@0<˭Ck~7FТ[X\^G *n_A\1},~ȥ_=:Eβ:+c9Yn4?/QJKٿ {~I.bb,8rٿfל(uՎ^X0y?[%Xᠧ7gh?s=1|JR\\'{'On:Q{:?|:]okLJgQ0C/Y@nOC[9g~6v;v= |e-SHֈ~n-_ s^3m?yP\mC;uM"Iuc^M"鷘luvq?ZW}lߴ۝kiדa{=$npg$djֲs)T9ٻ7]E%wbr ^0nfw3g0a>xQ:Ằ>n?w>Wr}&9ٰpwdS߳<;ޝGGU;d1Y7l*Z76"X-VDDE׀" `A$ " $$D$63:c眜wfR2=GŒiL.9T P4y}A؅Ԛ}b轌y+<ϮSgӷzf;ZpO[\qMdqC'a;{z65{ZrU[TͥW/+ݿ 'b6.X7 _gCy#^F߁p9%-OAWZTF,{Y* RRVHpXa>yV(ϣT!%|-DDg^VLqY5}1^zk uAkQ v%AΞNf𦞥ZΐKRt,QƄZDLŻqvVa=KN)lN!*нV4:B!8O4Q!kR,BXVwC!MԩSbc6F!i߾}dgg6 ! zju}H||4򱄪e6涶h^;=wuR 4/ +Cyyy)//Oa4(]y75W:z/7C 9q=oP Vfou$RSS߈ g*YNTB-D >IDATjjٌ{ 1#>U;WfTII9SV{v]?v^%o\Mj_ VNUss1"zQSRBx9qozsyړqPefRWsVݶ7III=:awȼ#;%'ՋnM>gk~1k Sj(MC4Yt ]Pʉzc5M:GM~qW2o^l^7nOXtwge~ \3׌_|W[NƂ$_=AgyU휨w;&İ|LRs6H:&O .k\g-4s{F>8v(tj=W[Trj泙jDPeEv= ͯzॅjdQ-aTmSoOQWV-}G=_i^ T&jռqxO/jNZjQa+ݔӴzlbj]{W}QWxK>3[-߸U$oQkM ld%qKg]K/}-˦(jLAa8ٺ2U9ZmM!&3;/6da?FvÌNDyHV%|CNq<0y6=X[d~L[஥!3'%;W2,m;l ~m(Yd-ތ{U\=h~?i6ӆ70iKפw{73nCŎ,]O2u(yZʚ#-g`VD3QsA #l7%oʘi(+%uקr<]1+)nM'?7w`t=hbS| י(yG6͈,d xTn"9ڶ"3ۇCL?1r*IVu{bh/3{=Ťe984)E!,xjcWٜVN:akFa;_x}[-`׎ -qI,6/<Ͼ vyYT&kycv&._G=&%61?[{^;> +»?R|}ěo}jdpQnدC#8fՔWa7嗅l@U1'PS (.HoLpbMdyY'*0?a MC_Fh&xWٰiQHp|j8h|52+~xF>=V =?o ǰX?$7;ڥ1-]&?9q9 [Һ]g>8Y 1,0q\NbwE{0GxERߢ_Py{yP_bjƻHD\,;e8Bmڈ0wt @s#)tO-e<_x{3"-oF5[zi ^N9+UטM=IgweXpH'- G&wuJL0OHwlU`4*J6|5L`)CɉC4' */;7+(rM7_0pbS'$|as^_DǙA&O?qTRYY_>ۢE fNpTa]1'ƙ);kp*4g:b^kxZS^K"AuE9NP[MM/c-`2Q}p)R7qp|{e}o9fً3}LO3 IOm` 9q 71w<GEv^{_쭾K6Pf\ p$N|뤼M}|{4}^NøJ0yg tc Zjjҥ9oJ:MMS"eggT)tl0GX = 39|0Ӕx"@|K#u<0ci}m44p.ŕ11M?vUn^47tfN oF@/4堺FIa>T_aĵiO8 |K208ycE5g nߚANfoy(@ $*6@oES@mhfQ]\]QϺnDtS0q[,._e%P2 phx4M瑞!,UIVa5)F"8#lpuі&T@~薼_~ ŐDyhy"IENDB`examples/organizer/qmlorganizerlistview/doc/images/qmlorganizerlistview-main.png000066400000000000000000001142221233466112000311400ustar00rootroot00000000000000PNG  IHDRRlsBIT|dtEXtSoftwaregnome-screenshot> IDATxwxTٞ;I7E XAԫ(`ظbU,ElXiһ{ {f%@@ЈbQ1o%3ضr)cp6tq?wy{k;(Hye k|0)4kӉ=;Gp6/E܌'c\֫NI ]MwrOFN"I!2| cL{EdǙ|&O <+Rr2 p1G%(N#5%&tꘄB2RsD%Q,v3)w'`#gu|v̅dfQX\LَKHD,qq& e9'ȯp2拪"écU{@R(k/pp"Ɠ0̹Y*U PЮbӏKى'nlY(Bh ,!h*Dz)jmޕ\\twU[M <_],W0!gY ',;NV^1n=&$hD]@. Y[:0= P D'̋]UEdgep7gYiq|Q tNy^ǎe_F:<}",2P?Z* @HW^MU Y §oE>]'%9VE ~׉3 03B!,uxgLO P#TnR&" xTaqz[Fw>-"N-oE%먗żUnw&z>1Ol4WLFTկGZd18N%7OD_PPl|k>> QqHWxn"W+TZx7=3GU%\g&2ND^o!xLXmbBN4}hH+Ŕ!?x#g}.59r׈׆ REEjWd?+n-58W4&67E^ϒ*} h,F~B}1w9zE%<":Qo,˝5^6XxBW%&>YV]. aD1e0ϔ Ejm{"K؄N}ʢ(Z#{.SbDZ%mͭq/jEM}}8Mڛ& -szk{U*YB~OQJ(:%6eVSP@luhaD'!BC_/ָqE 6Q>c(9}:Xhm@(j,QyqK8/pB+^|xXjXUuӽu(]qCZhsf $<u"qO])oO%I7%3v[ί݃T£}}5mIs$ -P_](N?H_ T?Eɯyy\&pWD]E3o]5c(zm9iX~ bl(n;eX}񸸑npWPRVvԭ"zx֮x]V*ҟ֐gw3ugn.ܗ;XO>/MPkWc̈7kp]{Mt0W4C[{nUS<]WᎶAh#u| IL]-~)硑tHcU(9BO}s/џg>ݛu 7mET&``Dj?Byޚ`oǀޭ=9?.;ȯs籹"&<]NFxp` ?9ؠ.ݗd?LmNfW*?x{ Jqh8g8br#&ۉʆ ހVuq_EEUpb98\A>]I|YؕZ:z(:7Szٮ=S>bA8(-% asaRQv"2;ԾGMh=w_FtK((.uPaĚy~468;0'LncoIsѣS4TwjД5[!5Jqx[ԄưxW<l8sw]$9[C#Zԋ-+U{~,Ym[&"x&Sm`4"P,Zsg"}^ELBm'U@"c,OC[A.`PT:L^&fn.+Y0[4rM;C|XRG^mD>u7?n+y{ͬ]}ET:\g!BURUP|Z 5cB%X.0'E;gcf 7*Ooqػ%Gsp(ܵ3)Afp[sS.pQLIEV5p.y~65ne#Hȗ(^u@䯙q kNۮsV#!{$TŝT#- ,)OO.*OƢ8A*o1bu&tb.-G =oaO&0i܍nݺйC[Z4N *ܿs銓 㧆 Jl|U'(t.ZPtxua m݁I1`2N Τ1]sH ۄVO_}UCV=꒟dn*Ӗdv'pN+UN dKC,^^taPD:{q~ی¯rgkG6dQD 잌qX,X@Ev:ȸ@$is=ܮZ!RQj [ϥ#X:_|͏Kֲu,6)ړs#ޅ8V7W,Ф4SHŎC OlQA(rjFKXŭBkĨ{YUN꺒qY(,čTgj.ǴnyF!(۞wʥ_Y-|3wQ!#_GsnTj= ~3^> ScKC2Dж_ZzKV. pxR֝Pp&, A xa o /+ؕWyzZY?ʾYx>civ PX KF*d:R/:b*:}6 ^$hq䧑eS #&)eq*zEӳd^`*?w&œxWa:{*c0q ayϮ@폿~|WV}֏򝿲l[6Xdd DY Sh9V`| EUc6xw=ϴ:WsΛ&+j&h[rmCin4f$lC/:7!{Vt:oZ^G>;ףuBT/SkiU4Ƣ(~Y^%Ol6E&K{xy?j0ZtJc`nik`,{Xh7dyx4Gќ=`jMѨ69Qu6db [Rf꒖J(hciݥq&SlK5$,EuyofSg9\`yzAE^|8GH{$v=*<^h1{1[*)=̚9Sxx 4a.ͯדZ<=8/yp\R&ϼ GÇ+Oqk *$lūSf2YCg?zlf ?OaiչWHp;W~ˬ9 RE}i~ʧ1IN7>_DZ2Aea:;Ei?^!)aw8p8y b8V?&mq"]o`jxg2\ӹ9~ZGٵq vebHbؤxe|b=Hl LN/,(oY9m=w0$+-S}Z ~G1{ˋi5_@UTX",RMzg[xlLo" =_$>7oAmb*+%9i6x߿_TO涵9G)t$>3'$)5ǰ`|wTEޭa X.ѸX ފ *&^nM'\: l>ۙLi ԢHga15dN扄pp跥_^LkM|e߶!`!94;WE8IcLA I{woLG_E?h? P1D"+_ջNPVb'-Ϸ_=M?L9O֯_`c,#*l,egx?4bЈBjƅW˗/[n |u8b"0rrҏr$=&S#h/$f~z{wg I$IM$IF%I$G^YH$Iඐs s7nQ%'pv6*ݥ|qu8fJ /,JCLRAh$I]OYyT!>15_ki[<oX'Gj~dmJ%Ȍd~4AG$/tH_~f؇Lxs)`lNПoaVS,pV?7vU'EKuC/z'\b%IBgn %TlVϘ[_?8S o1>+x잞I8PB[>9A:[ O]}5G{`5 h1_8dI$zgCC/6|x WmXL (afE˙?~D^9v]qQnM'N?ݒgm8tU_3?yczYa*(2Rǜiجɽ~ayck?\+ S_r-:'uS3,d X.X?I$]Q^Wl L=X$W/$m<Ҩ L%{mЖb̯q>CڂʃRD QEfjfIߗψɴkzq{3")ĽϤwlCiþ|n>o$I^4P;GPj}2mH+ gu1-D*Fhz;:Qnv7n*p;Fva% 89&<Ǝroy]u$I{9=gxи9UѼ }5^) z)j[4aП@ģ0&H`ԧǾEZmN$I "wSEW\x. 6ɱF}yoKaȞ^YʍK.\nPTj._$Iș`QǑqL6GI7ȕKLJ{SiC0dxSF!_&>-BFY^B~ 較  !4̗ Yty9nsjvsX:%63iU`/".||D'㫻zK$]fZOԻf0ag=|&5($tLU;1]:M_SxoĻJJ-4 Z67Ӆwxexnzˆ50;rp[:]hsdQ! y`S_`l+,$Ij8pۅ0R 6d ꨤ*< T$L 8=^a({9LnBqa C Aq\JيSI`/jK>e. /"pp̓.+:- =xgI?Kr&V$IuV$I $IRd$I%$IT/,$Iz`!I$K I$^7**\j=FF=?J IDAT قKk9;݀RrAUV*j#^^uRQ]eۤ?,…Bjpc,ҩˈVv$I 4+9[~_dW6a0`#7,I=t(޽_snD/wp|2SlIԿLRM/PT:~a܋~}:8]~8K|OP w}&D8KpT>bC#5oDAYr?/b햝<7xz`$=s1*|h~{zZlK$9*[2ޞ2{[⋙o1dTf NTd`ڜm9]-.Z-ܸ _y﫝^x_<eKYuUe"~]~*8{|n{27f}ΧSn#pSLxoYUȖ Q|?ˎc\ؑGD8kA֚x8/?B=/ȕ5$I4Y{^؞nDnfm X-e/{)9goAnI~ZGqx7[+?m_0}Y1NS,!)Mayh=u}RRc%.KtכNMP;==^(JO]CLޑ4Ѝ>}׻cszDh_͏/`r<u[+-(91~$u> ^5 c/W:Uy8oY wc4?_K硅_,Qxb@xiRjߑ *mM^wbޫKuW7B/r7zI:q$61oXdcqVw5Q$'ENѵ%Q&p'dskGeO=ʇp"f };<>O -;{Dۧ>cwߚJИMlZ5wun .*.)BZnR&Aם;EO6+N=LX`csG/"+OPPYnj4ې>16s03ƱTD4#Gȭ(( :]eGS+Hh@_L qH+:$I@w-ON+g3pcx݌?>h8 om{W0Ydcmڕ~GxG ԙ1)FAc1 я6.ʚb5V:\w3^xF8!f#:EDWP "ʫطs$ǧw'˶{hG׏sGKoT@Uxsi"c?C,V3n&é5: ] YᨢRѤ;,Lyk-IT j/{.xN&7[/+^0m$rs7xz/G-r z\j|u_R̕?c̯7`4cދ0iF,?ݛЋub0c dM'0 B[i\XJ.&${]dKf+ym*w @4ztOޙ p98u^hܞ֢ӹq\SN6Fno.Iҟsz [&$ ! ofk(sҢ ܅fJ⟙ xuҍPIj splj=Zd6Eɮ`!,l_]AH5oML`iSģ n ǖg慷y_,ƓGCF^N`D&V.c Q 37dc0\$IQXȷr$se9ٱ ֈH:t&dfPPTF͉өhP+vd 5zO EdfRPT`׼*t]J}Èb+)9֭GI 1ZNUg5xҸ{ذ]*| DUYDur_Lke YGSɩ|]ӫo76R|v6)=QX`  +)K̒EGh׻=QFa08YX\kI6Ogv!6tSK,9_RZ^$IRJw~"HLi-:u"ڥĊ>e QY|2hݼha1w1yƽSrkѣ_/ѵ߭bn3lSd/{ZTs7-Z5o)Z~LpTihi$!.;{:{8QCtn (Y$=[?{HII"))Y4mK~ktU?m/z5o)$l/Zt->V,JX3MDWAXSl#۱-ExDOL' V8.a>@pS+Ѥ}7ѥE{lq7wF4 ":<\D&ľbB8wO ͚w:4͇L %IQp |J+&8"w2*X8N^wh~2CX7s5 ³ƈVNqQ.->AAyT [,v( ֋hB}t{&7r˫'UjO|}LjmdUXKhaƓ+X8]U1IgM5mRj;:L :U(!DU_"b8YG%TE?PoSICO`d,uY$ȴ$IRA$IRd$I%$IT/,$Iz`!I$K I$^ynmR/sQ[<8kECdyvUq-p:{ΔðdT]n7B=Ly-ܗbBsHTVcė''2 ^.jf[}mBէx 5G$ 'ѭg#ޟAY|YK03[Wo!،A#vaɗsf]if?hGTz $'շe#3Ly$]^'[2AY~vnζmضmbCPUo};utm}6Yq~}a&;x܎䕅b;ƂɷLlWR2SLy$]N'elU*I "{WƟf7.ȝ3qkTDkW}ivKLy̔'IsUc3ux<ړs* # Dxka**2iNţ1V=®xd$ILNd&L^,$&kCc0a:\'e<)Ot d-tӫgb7Es6~uf %G6hvldd1aɬw2_ZLy2S$IFln}jh=Y3 Pb*gRh AP{pLxӌSm?}1?ӯ`+(I&NA{&A+&݆0uZ ^; /Zfi/$ք>J0{yi\;'M9E>xkyָ"pdl/8]kuQL5kBIlYn!b {x{*;GRȏ?grPOz) @%$I:"fQ_Dv6h+sȵ C^NAA9"/V4a譔;apWR_LXe\v1d<)OFfʓ$I%w$I%$IT/,$Iz`!I$l6 ##*$IԀ<==INN&66z-黡VXcO=OA7^/WJ$lVҏ`ӚEXl/5{=uFSVW7uN?: vK9 @}[BI$Uv;_͜C@ٱ}sLkn ȱyeZ,{oûEOeqG'FwsFV%8sx!IRtt{3gڙDu;|UN?室0"HӜMƑd;DQL2?10E׸~z'H",d݀&:!ʆ!Pio]ꭓYy75F_77m!m%IjQhVZE[C΍?Ԃ}5)<܃4;jUx0Q`^˂^cYN)qe|a{#R-߁OC[)nOTvmF~U.Sꡠψ(3cNI!:*t^>gQQhP4:u&$I2:ݙ[gm4pѽN ;khrQ.~Y}!&s?+$5GZ "mG"[PwE:D !uKZʡ8z<ʋdSڜGQ%tYgu_m;H@eO;Faq9c<6A-FJEvcU\f5cx:fV[^1m:+WIp@>!I$] $IRd$I%$IT/,$Iz`!I$K I$^2XH$IB$IUEm//,"L<-0 PsU:|_MTU̡E(InKL@][8(Kľf܊ ƀw aфT8gqd?X!D6nKpXRH`/{2"[$XS;{;vL= [ ˆrvX˄+o4G):;sh 0oÙ{7neתM8 U`!e.)/C(>GnH;R|l$I=a/TSnѢ&/[BVTF|cLJ̸t6"W^|bɤPEʄ; [E>YG"sbw)h:ԗy0r|5W?iC[CY{|^}eѵxp*zrw`\4 JCa/&/(%6QDE;Yxa"7YX1Gx?I"~O`5ފ@ƶ?\:wh7!?̒{iqW'}[3乹>̢)oRY s/&b y VЩhd$]%TLx5(:o|t`=[S1xυy|׽œZCزwqT>\GL~.MhJR9 t5 wڇ|uErc7S$,ȫ:))PR?^((ZKXa 跜wNN$ba6yowOrd/;z$stZ܉ ֔SE`h2i=FOj:4#GI!O(0%SNz$sY9~8᭚lT^&ݜ(%w$WdSBKinlOT? Cq4To2OQYʚd4>gBhSa iK( (.3c}V 9qTnQ太 Bsc=2iv}GB)K܊b*mn}{)%.@¦ı ߒv0SIř9vsDӑۚbT+ X`s鑀U@T_(B7iҙC ~* 2{$I@ܨ:at\-Qy3_1Y8,vi:M= ꯈQ&vSu[6/"Ŧ?;<*{d&@B (XPQAŎbvQDDD*JD@Bz~~$Cpyxd!s>*3a釿 N V|0xH{ESe;ilTXA~ԬzE[pk5ඤEm[ɗ_]C_YFt'9sBy[Wشf-\O%oW,XFl|~{PTYBMJ IDAT&ogE͡,fFbr:<8*HJk`; oˁ}{/AcmBtr{Z4ϤmȡMӎ]tck8X%8 \ )oYXH ஥$s+2rt۱3kXw@bǮDUc rb8,6H)Il8PhGc}(Ϯ P!NSM["5pVH5lݙA6IkDU?su_W;vtuWc6C=iAfcoVse1ڇ4Iulmٵ>DIX/BHqzFH%<"]3zX}je B\ b߾}t܅b!S YfJudf7cȐIr\$''|e!.iR,n7zt,`0puR0%KBa0ر# .`СM&z E $IrG.4 ݻwgW_q`0g& qɑb!rG @g2jh #z h2Ks_,ҢjN4$6}d(J)Cٳ0Fm:h4I =_Sd;~ O3Rғ=۽M>ߗc~wʜMat4.Äi|bsQ+v1]5T##׋Wň˻u*m?b|!ɷ5׿nj7!wªd7덫p_>68}Mi) {2T2/U><5n}Fs.'8BӡjIOOg cmxwiN/u qցqGhV =>^nJssч@]Kdb Ecs=rr:h/D'Iy;\V^رc0aSŠ+`Ab01x8ns`"͵䬚w~2 l*xYoBFHR$]\.\uTW0|png\78@Qy҆ڡ%݌%(җ֓g`?ݟ1qE/}7U}q1VldM!XMr\$)ҕͨQjOy ӦM'{f7Э/^/J\zꇡt+چ0Fbj`Ky fYJEcZңeӺD['xW14>-3ź (I;%)Rr, nc 1i$&MkMe߮tOUJJ#)=n';FVP(DžKkpdLDh<;sL$)ܺ+i|؄Oc=Fzz:;#U)sdB?~i򏡉~9etl^HރU}騤)+l0꘤me{ןMR媢h{}R5uIyRSaPY4)[Ɓ2RQqhIy&)o,[jHʻꬓEDk$)j5~!I4V ^zvVXɶѮE>S!NM<ŶRrao?ؼfrV`({oj3z 4//̝ZOkdl5 on|'?jz>) 6U'Iywh4$$˂VEJ\b %etwiN'cĥE y/zҲcW17dOA]roR#,xjsl_):xJ QwHϕ~"S.\z5Gt9AQX.{2K ˈVi[ ?Ep;'ǓNnixjݲ%):D !ŦP cʹ4 \gmhx$бoZ13E0>PYvbP /2TgWhmS|O݈3y{ YNL^X vl7Xa+`&qDKM'rccᰪ8c!EuX/c+9+i, {C`'}ŘwY? HdzG_憾 F4`Ը&j4U لSxUPX\AbBJ}sRAQH p K9%v-zPCo?Ӟl ԁ10I{YA֞͵a eR4~ SYZDAA1n/"f=A顝8PEDp!YPꇗWۨWߠnnRzgTNIuطԺ_rS԰ M1jCUjl SX]|cSY+q6:v|MjƈIuUujR%&uT}S-YN-pDq~խE0~Z{P6sԲkUVm՘7~VvUUQ*jq_V#ՀƨzQe _+vwJ1cK㤪0@ JSXM͈w`~zey6 UUDaUCE~UJOeI Yyg]m'(_ytSa{pd>@7#jCEeq)4礌tm%ctREo?Μ) c+#/;2hG8(&ĆHxP|ǟҌ9ꭥr\w]֏֣ٷoe2d>9K)Zڝ'un>y!SzJK`7&-&|Iv~Œp萙O'{?ŤY[p[ ؊ 0C-Me3{iWɆM5O?gr| ~xxt|˼qt ;C!`tw.%>zpM9='SgE068>腗o~@;FETݟBI5rw:Z|gByB?1b çy7vJ$CV=^9TaTm!Y9v"18+)tQ~K^-j7,t.][NM< ษ^+aS.+)/Nw*ؓwUǯ5##ط#+8ϤPbĄ5 WwQ}.yæ-݈05ދЄDž}8e?-Gc9mxGk6(fDb$'{Ve,II>t#u?㗪`U 1s/m~{${Z3]MlǚҞ}l &Pm?tؔ-xӇm͛[Gll ~ֿɇiSʯMCLV-U3X7Vo*g'+ 都c.[ss 7}5#Jq?6b,X#SS@!sf$@vy|heQ=%TDWI Mǖ5sP3Z6se3NxMY`ZunM.5^gO 1Eqnd: b ]*٭kDŽV&gyثK)Ԧ'4m07?pc$4fSХI kdsvIPd:u|1ݮ+m8v^QY&"B哗G^^>tH FrTt;?:@u*~#6_p_MrV{֎?߲,LaR!q4)΀lY{\8=ptrAg@8qNܸ\4zß[ɮ<Q.N^B\`*QBFIB()B!%B!DX!h !ҫ"Ȣq`9]^!LѽhHHR$ !%v/_=p onBUWe ޝc b*'>ً+G=[ ?u+OgDo^|t$e'Y5vEYSNIA7r1K?~R=s!d"@юm8O&%"Ι=o#u(e[1 ^؝p'bwq51ts 9:+>E޹O}7q#m`!άPy=^~XތլpVRXۢ(/FHdT1Wnj*a:f$IR:rI3x#G2xgU-Thwk|g/"VǓOeUm ޖHZƖꏪʶs 6*;'IyBs[e·xz-u;[#[rP-Me:w>!ˇvdd>{y:EW͂[wOded}Wlo]îп5s!IR IyB?Xh1G46M}n5؋[cJ%:浆%T6zO[Ѕ rkPo+ XO/xOOM[{weKœ;a}Y5!ؐUsp~>\Tx~O Ɛ> x_O9|݇IbO3\&|+?0:Mym1r嘖͍t1c/dž|iӸym3zF)$5%-g}-փ,=;VC>u3kb[6G\vaY{PUX5c b6M:1zkoύw(K3=Ȥ l8KͫOgˣ@jd|Qc^ IDATAU"(3"ÉKoCXΏ|2;:O]BM;9) IʓxnF3?&O y{wsRK@l dxJF)$4 hfn}'sX+w~Ɣ~"Wl;tU{wo?`WK!5A:TNnqb`tjiwљ0FUK\]<1fTړ|Jʄ;ɪB@H8'ga;Sޟ{Dӆ fɏs/"tX3>5ф3ັ,{2fN]CWB.a}؂Z3扌!{)5|104]w^Nl;l?!dHm0NcfLۋ+>}ٷU!Σ q?+pp푗d9!: c-K^[@̕cS)=T+иٻK>>^LxnEl{_`*t>Mi)S;gC%ҭ0q|w揵"3T'=LMI7/,COBK@-}zdc:?v&>sM d$9)xJY̪Lk|3 <{_?n>5%tmZNۦ,}w[{NavhߣBWz =O #`h%@-Z3`Pn]3Jϋ~%[hf7Ѕ泷U q9SMjCH;r"=+Y!$X3V̦#W<6(3PJchh'НC6Ue's U{|6Weu p BqUܛZb?Z6y*)-` $6)N^ +7nhsqt/tenU8͉Rqک}Bqa-U۱ϱy: aqYuhkp{ޚ9yt'>CX[.ђfI\* /`~HЅг8o(9ܴAјM~GS Mׇb\ٷ|3m rliΰI2ū_`76 w n!EU7mFvqƎO24wBϙ0[ЃC4y^wK s>jjIw^ -Fo4hLfl҅s5(v~"v yMLƒZ ,Fy2Fg/8c}ꬦZzFi^9E85&|‰ נUO(!>ծJJq 7Ս+eWp GWS@As`(A=KMF.̉?`Y !y"B!% !h !b!QR,B4JBFIB()B!udrRYY[gb>/Ev:qk OVUIGj=u˅ˣ@Eӡi Wm%n~YMe zN\ :Nue n7l#"8+z̏Y9Ķ ;[Ga۾o{cH/bh)nJ,=ҝ˹n)8|ZY^^~/,lff0ud;t#OBVW'?\h:"k܂9slc'Ulͮ+=W|'/O'h|fNŷ[NIw2}F\oyWa.u9h=w-9[`kM$@ !g.{|Jƽ5'GypCQx<&T+-/噫|?y2o#O1nl,:ze"6ڞ7&zn&ƱǙYBUml(߮Lvj~ϗm>xo͑+ٰo"݆dD$ ̥$4uuoZc AYF.s^g)oM;>NJ9:1rţ3̓GCy^=M`pV< 4N?>m¸Ww{^wڛa=HӃrPk%w Mѫ)FqbC75ղ{ȣO0[L5M2lm>Q14S-y,XdiҧU(f,7nν +!h-q$ˏ>߶/Wݵ7xDֹ=muRqm1Scƚ|\(%fXVIʰьߙfAޥ'=iUm;?`KؚJFS6jNfθkaCThLDĐ6e} }7?7p记h23NE/yr@'Fv #{RyۉM Vt=Y4L-vSDl Iͪٳ/4yܽq%L͠TB^qCyxz3g+'Sl瞱KlNZrfHHGwfEV`kٍ53e­$ $ӹx@[_7-od:9E9*.tN~m N5♉OMƧݤɏ %lۏ7YI\ C~JMU#wme޴W9NHh, s :OVG=o6%]ߏ}NKe't/6%?k>jdʌGX-׹mю[coV3UU@#9,xu2՛^/cbwj)V-Zb`9c` iJckPF4JE--߹,\Ǿ*(uEFl1ba)!++r.\. zAYJ(,.Ϙ\61ՅY̡ʍCdTLD~b/{( kJo mj$sTuCvim{+7t'4+Xb%zBBCj..¦ !{w\KxcU6U%ٷn|cj,|rtk:]TQXs%Po y8\X'ؘZJG nь_ ܶr3s(vc "I]0LrKh-4nB ܵ%V`?! Vo !6OR#VEFN).s1d q*QBFIB()B!%B!DX!h !ң<\s)z ?2k?jy9?fb;8$IR\+4hvV8!09L$XclVf:@܎¶&p`ŇMS kRu6*g?k'6G v`&ZAH`yl?3/,r:9&t8[-ߺաLA z d? 7 I/lxˌZ:^;4Oʜ&!hK ޙz|=%;xy^ғ<=+AM< ~ݛr?=2Cl&{]v'xxN-$dC(sY%?K} !Dc4Q >.Bc:!AxA9*(,pB\Tgp>00 ̞j pC{HR^IB 'QBFIB()B!%B!DX!h !ץ9p"Bc0v Iq8O,̧NB H3_WWqbת^6ux,'ƪcj qs6Ջݥ&NɞL|z*@i_:ۻ !guVޒ$W89q;yRn5]m[9 ej~4;m`m[<܊pUdm[[rƑޱ!fض0mkaAk'U":&wlS*Bt uR,MV!W:c'SZ׳ &/Ć%1!hJ8#A<; KSٷ=CFxj55S~٭'Һj˾[áll? [!JMˋ!?]m:MI_Yׄb}FWU s[M!@H C]TT@:( l8+ N"%TCH~H"0u?ឳe] Avw)X4dȒJW x #|oۏM!* .NBQwDߔ÷K_ 4V|=ʎ,&@"Ky?j_'÷'n43fs4;]̓}$lP1PۀwcueUI.gߪEd(C ̼WW1%Bl+qcoAF6 +Izfԭۨ4h/gj"L)GHͩ,x'900RrWф|1oBR%@gX#ݲuNYØnwl%avo]1U*yOAh_z!ԃ^;%rP ?  ; ]+0Ԕrh͊MN8Y^Z,t &ϼln:8z_ug۰7m_?Hjt_A.YHO˖iz8½-v6YK|\1\$jtV:k}ȍEJuRD2LJtlh0`c4qqֳcHZk2<^-݈g$iAr 찳CoojP[RYEf&i~_n:]Hua.EʹGN|F AڕZXkd-X^*c+O1Sj7bGasgr_bә3 Ly Ɖ*rd>|.|[V_ܺUyo,O<130wWM4\4QE:᪮!;u[2y0KLo о$XdF_^Թk Z2iW Ex{hW.1Q[ƁI(Vǻs7z{ 4v>.M].;P9p7ti*g/ɦ32g'[KTke֮S8W:w)G9y6zw0Z BiUA6^ BDA$ &,A6`! I AM"X mBAhPdR2h:ߙm5K+ )l$ܼ|Khrjsv-?`4mNtd(φ"R$s NnaV1TSyGQԈW0R2<&@f5V403J5ͧ /RDhoC%gfϑ7dݽAhj ⟻וUs!G]J;Q~%^/F EfW:湻6a0,ۋ-u xx HI_44Y|; o6`D]6KDJW_;47锿,yqi"Ajfyl}=:2׾Iof3a{l۷$CJ+5;SA۹ƣXU!FN5IDAT} ?O]}z_mdf w@MUkPU呕[F}ę㩨Vtĝ2ɿP\,Qh8 vzҜ*$9eWq"@<-4$Z=`Ь~@ygEG3鏥`jn(JGix0l eY$;O=o11 %8~& :v%]k̍edJ'B*;~:lyUD؇k6팥J`.<!{}m_,p{b1S6e,^pm_x).TgyyR,*TVlOCߴ_?#$Qj)PBe-jd=..P{0[QQhņ} svjkC}ARѼdctTGM͈l *ϳ|l,p0x+mxx7[ 6`*V)8$,c7 ïRe$@4X91lC|7AA4]K3qȾ]m9v\8_U'XӼKGřTGsrED"R-ңQђ\ ) ˜l=*..[ώb/omque|XKC xIj:%M=>Akb̙2fXby=KSDHI5f ?i z`(xsDΦg9*=NO$)fuӆp~zr)8Ûcۙ0QnmREK*v+Z"w3⩩%}sL]a㘰W5sh\My%oTwS5'7A|IDbM35%]ӵ~QQMiqM 4VSf傳MqvN4Q`! %.%8α9#L\sVHRK9YV!2Ri6|Gsl4Q[PIg)uDN+ * jYIF{h͇ZPdڋ>H' gc3:5#fBS)HOYXވƹL~;DNTk8:n}3wl6$]OB$f3m( U%IߗVEjY_̸{'sqW|' /(i39>\,j93|`6ssKMA@QG7IAtwD#ЀF[F#-X8}րO 4,spI <.pBvozw,aךI &h€Sw;,n[Xqlȡh@i" mP7Rlv ,8<$hQ M-YM5g0౑ ㄹ2J0#ZahdILCq69*;t?ջPP r囷]{\F~i psb*\DXKH`='\0vb(- UF@ UG`6*Hj${rw/e>.όYvE`f s 1ҽ'W&޻LUbJO/9Vȝ誳9OA!(AڠzvN{>{L쇭G\OyEdEe>_1]>eb01 >ْ ` #5r5 8s2?Wt;t7C%Wɛw *)>b7º{U*XIs];Wϗc5c!.#m@lϪ$H"[6e4zu#Y9nW9PDMQɧEjOakzFN6j<- fC>ͧi[ [8PAX;X~aj8C y9U'Qq R9v i;\kP9;g@w?b $CU~(G$B-]B ``D2\Jt!ؐζ;8v^EHj:'(r~3kPq}[؛QU`?Gm9rbC%z`:%z0n K;. A"Ҫ mgC mBAh BDA$ &,A6`a6a4)mU$ P+OCq]5pyX V7Ȕ'2 pS~Zfq"m=;ohH^8$l68w؛` MU{|b˖B~,i>hKd%~83A>XݬLy;yck:p?-_.絏G'hԘ͖7Lͩ,[p;8L6Vl"оJ _s;qco1yAh6;áA֓ kK|UIBL])NWq1r>;jAa[2qqrkѺ{"כ.]1ԄIeBdA. M ȭmҢU)>)ofBD}x^@g:N2=0lK)5Ubvog1=tȔw-)OGKPII&f-7 (4V:0|<DOx $MIG^ϯbѤX&l: kg-մD<@dwi  #![{G_$t]#vnڦ4\Ut%z`C}/CT])rEJ_sYg2NZӐ3x鄌Νg̳;I2c~WMp7fobHqe (`;2uF^|a!|)NXOTOgd,%PZkދ{n}cj/7K?+J`n KɏIKK#));we?Ĵi:t(wq$)TWW3sL/_v}vbbbڻpˑBRijըژv Ĩ}޸ƝAcvy IA\ IFsZNըX` ^Æ cǎ] Aiii@L$i^yUY*cfzK2swJ)ٛ}KMsOK.z&_Co}G߿'y-ovǬWqO3ۢEڻpQ'%%wc$6<'G515\@B Y&H2*Iiه ɨ /?oǶRtS{OӿyPdbh-PDF^* ,#)&_ I}ƌD7kpJHH 77. 2ٳgwCd|$B_A>˃Hۇ߲3q7 kbc|.q`lG2SD(Lvв>;-]$,~g>9A .Y `Iؑt3Ys"˄vW5F#GJ)_ĠLԇ/ u`wuG1?>r>}x4<#5i#"uɡ{w 2wĢs!Dp HT8ų/$?c$GaT ;6cx )ړYcBa+yt dj37SoҠ!#l)N &oPqSAuI%݇0AuAJ 3޸u&)\*ClqA*I$m973EWmbV0YMy,toGw,a= $ {U&2RӨ؃} v#ý<ڛvkԡ4*L #2U)Fj63ouSihnmЕ*\\5"beuw7$1)?|΋IL9ޟECG)4rYt:ęW،uxZ㉏g7x mxLp6ZPg) ?Jvw";aK5`jjajDOٸi5ʟN:w"%''+QQQ]Geg` Φ"N-]pcd4Ad 4Nhq'?+ HgFH')pǺ ^#iq K. 448tOo<4ٕF@BeKXx(*jp)jM(Ʃ#!ˆ·NvNqzJ:\:cwUTOV+tv9͹+ƪ$z0z㨪;UHícg 9ddWa4NtvS(ʢQARYw Ο?T\\[lib-ER}%·_z=o6ӦMkBB CiIENDB`examples/organizer/qmlorganizerlistview/doc/images/qmlorganizerlistview-new.png000066400000000000000000001134661233466112000310160ustar00rootroot00000000000000PNG  IHDRRlsBIT|dtEXtSoftwaregnome-screenshot> IDATxwtUl&ͦ$tz^EQDEl(]_{{(6{5Jzd$9gG2;s}>Ό"H$I9H$I|2XH$I B$Ij.nG9icME{I[11RlW&ǕؘaoW2s([aw ^rkpSt` s֥an0/BIOsnk?nEn}Ý}@wmt;Uq~ `ʰAm+9rپh۳\c{syaǢlr4b:B: chRm,ZlNXUm Y#ZB:ghbc X#.C/A`=9+a4\ro3]އ8 …#Th:i=B8'; /46pPXſU8bb s YSd.~NtFh*_W2q;EsZČF؛;!?):y)"hćbsWSRþbQʏ:LⲗS*%YƜ6DžM$& *%H }guױ]gXe¬ PŐ֊W˹N~ZDLl+l>M˃ˍJ Ol̻}`O_";T q,ԸRNӑ6u\q[%Xca9< dʌQ (?>pvYP1܌PFӦC<9k9}/nViy2/3f9m/ij.G(ƏNZyWPI}`<}b (TSmiI_|5*'*gha/j\sX]ZDtĴk FvD̲Pcnޏ]Mlc Mڶ'ΰ{vr(N;_CE2}o>Algɾʲdܶxi6l}$;X. )I:JObwY.؄ߐB}oxd[ADD])KDIҳ(u0G۴)Ahj]N^F:y)#4:Fjv.? $4 W{[>驜.ŃUEÈ J)4Po. HK$'/2>ò+ |ʪ'0!vZvfjVwl'.dx?r)f]>^x*9?=iNtщ$rA#U-N rnT",9LҳKq X"RXURIN$؎'&4 \kgQ&٥GaVJ<~#'2ɷ1F,!HlNOIS _$\Puc{' - g9E%tİ?ˏW?%׼!DψY׊iύI!:?!ߚ%+k&FS!PsefsbhnQLM@Kzia|.iU+"wr>NYĽE->/ా5x s|"Ok!K޸sqQXPHxeƌ" e|~q?0& 5"qOqhhnQ#YñmI 0`yH&#q![ zaʻ2;mF ?}|޿!U)#yѴ阀i:OfY7*gTl;5?Z'i>n~f|CgA }65^qAkTZoc@jfD1-Xʞ|ӃgrnxIˉs=OA(iY4~aGm8{ s0ݕ,ߩcMs{Bei̝>O| )E@},Sfqak-+xņpίU-ecy՝jq5i@my̕ Ywl$-MտfEl3v`:G+`#eٗL^ū΄ X*k<;ڙty'us1?_}CiPqRzn˚mcmdC5B:q-HPoi1{\+MqqtUr[nAo?W_14#{6{>m*:ȋ<7?^=iuwG,u%/WcNJ4׽яf~ڊ$+zE.7%T)o- QfYzh4!(O˺~$9O5~4KjKOeéW9nq`niSD*:Įy[$2ZNoزk6cWؖ,+5NVo Њ?{K?Bj=Y5'QeXx*O,WV~xUޢ#xI #J}'nK UjDZ+ZsSn]X0Dq}˯047z r5WƧԷ55N cxK: cGٱ/aXDɻquM׶юYܗJpEɻrĪ&u-BumAwJҝZb|tشm1KoD왁uW>V.Hw%tЙǑ-8PE.hf!j$ŶUEws0ʐȪ,4atMƷ~֙ WFaNVKP>k7Ңś讃{>ڴ{okߐ[OiRB8/mNMnY'8vʉMgpdSjPqRƞ[@KPm*Khx u$:oL&=HpQtl= ^(𜮯!f+eʬ` 'Yg zxf[3fq"9M꒏^=X5hi.:2Ũ| *-pӍ1ŕ|FvJ j2޸& Ǵ[2n ;NcXCzоy3<ٶe]xٿ0rBg=/PFO'oh>_%K2aP,MdZ-yیc`(lKLlƫ?4#9n;n7 pB Lf*fو;ӥͬ|gPzEtbvL~{+KV涞 J7ha\W2o*7Pypڬ;™͑I?hXUa FR<&_ xxsI.!0I 䃷^p/?xO +Y_Ւl\&119)j6|rZPVX@C] vي)(vڀѠXBkW>ѯN z}/Ƀf!}b|W*=A+ؑ=Y$dI5q!t61MCNv?EkSI+.K> żY> mflg=`pDBXE`ТRϙxk[|QM2$/zvvsVZ-w'֑nƸ=hZLUG-NU!D+v#Gࡃ[c lyd9Q&AYV X3_<7\'j(+O(qh6e|? ڏ~]KEt\cXKڷ dؾVsFreWkGxBmL  n+ Xp-Ggb2px8!fQ;IM+@"T*ӪY hM scټ߰3еb=mfɲKuRi0Z1kXK(yxޑ :mSl_2\29JqͫoWgC 8+m+S*w/F*s)H,(/<]s񊹌nNs3SQpfBwBm8Hq`+ZR= iH|ZW8Ae_tLx ib:/XAg$=]F˚Ңe1طprG f-tW+XG y9dg-,VТPzEy8j:^4QSst KyU+W?Z69۲q%UN w&6K܎2hNJs_ D7u5 3vžÞ`j T*ʢMU@9l*OY)]KDkKeЛo0?|")pZsIޱv]vt\ݟl۪R[h3nOζ,pc-ō8Ok7?ڵ'䆅,ܔW$ZG Qmio mboIel'+\urgS*xteV370o8]^ g8KOdkwU ?k1oo6evm+$Z^Mr韩Yj/Qc$[98N\nrnXfn;C:UVci5k}]h}u͸Hw|^y#~G|d/:QO0>k iO;V`n]L?TCzd~ Oz#!Hѡ ,}.3twEjS (y)ǨU3|e$E9Gٳu6%tt{g_\wU5 LXq=OOUk %$.Bˎ'IEOW;#4!},ջt-x'o-=aJ@^6cj\dyEy`MygGECķUmI_#5 ޛ<7nХE8fرuw`!s^'jVwI2[8[+Wk LE|ڿN!SI|3S9oz^>[Tn7I!jr|ߕ'Eqy(*D_ɰU%14YWeBg">.OImi N^²5o umZXH./?LF4/l8/_? tl*=&? ZH Zo [yVGTOV,$I$~I$I? $IRd$I$$I ,$I`!I$5H I$A2XH$I B$Ij $IRd$I$$I ,$I,˅_'I$?S!^}Yn#Іh!(8V$&">. g/8]ECS7-s-a!Iϙ`ɬ_汿* :zrN@7{dlE!;}~9΃};^cOʬtBF>[:MI$BiDy2>|\Fn@'{NHl', IDATLـ)`+Lfu4pwQ(om/{$IZ?┦%7 хjHlMRnRӊ3o$Iu4 ?y2\hc{041؄3搖~rLt9DFh$I.E=Bn2X%{&q^-*0l6 OKL;hB!~|q1VXjRDNS72wͭ]ƊK8aX"X_74>zUphe<bYE[Dj$I_8*_~{OʯN۔O^ïrRmdWHՆA pd0'o:m?& Beh>ϬD$K&_Ach4w}*;Yx?=|?ぞWbƷ6w )u94!B@2Ash(^r|A$RW4xC1<^CF2rGPNtA}'3bԩpeoe{OϣRc4w.I?QMbn3K>D#dZQ/dϟ1e}9/SbR!+ mkE>I$]0u_AQT(T4n?H)$)z7֜4R\^T *!+IQl\]tObEC@D371Eiq):Vs&4+ll?mW[q`-.^ou 8c|,@[o@G"IIl=Ér2, }A_<Ǚe۱4V=~霤ȶ#9)Gtw[!1tl͗B$!OurL]Bөc"c0me`U{ *s{#pF Ȅ.!OWE[>if(6ru6yr9u9'l #sr.*z;`T_yy{1r1"xiP:3Gmm}(鴒$I D,[m{Y&;+4La4Q7hW}\\ҝN?2w!R3̅*8SZBƫ|3f,WpHp^z QΌ7pڀ%")so {'~O?Ǧ#e%܅:,2ޒ5_/++In{)9PX\ͭBgl "4,s77N2NO\? gvSAZf(^f" 6QTXã`Ǥ?B]l·*G9er.(Z >Z Eܠ6gq%IP%XH$IRdF$Ij $IRd$I$$I ,$I`!I$5H I$A2XH$I :s ؽ{7gfѢEg$Iѐ!C߿?&))[o%jzK _|SO=y'E#I$o8pO>z rǏd2%$I1yGo*Ŏ;$I$ҡChѢEEl2tI D$6zoUrxra *SF"=$'HIϦCkJ ?Cz DQ3mUw6W\7m| (IgjL}L];{qE+E$I{jsR;`⇙o.ӗЄNUFKIXG)sz}@<ǀ~T4kCyb̋,H-Š`ӧw3mZ?UIW1?%{8U܆s\3q}k4l_ aE~:6mNihM.- \‹v;lbˡ pyVV3úܫ#\z/\'ּLJ̵P:U hJ` j>4$⸰Ed`TQRH}O[FVP"qWZ¦5;HXxI=͹+sڌ}<(T^w'I6EO[/qK0 =jxpʰ{@7bֳU:tpR^fوl+o'3&DF"c$I,@VCpqjsN+٧/܎-EjLFOmuo)9 UG1[Ѿ UMD6ęd(r`Lx7am0*}nM}k+:  KyeND[x?+^$I_‚pRn- SVRcx3 )wUrG Vs ](xrxλ<^wR5hIu*؋).wCF.Z:xʀY݉@@}8K>9y彗KVCI7\Xp~"b70˙`a'}g<2f0Ѻp&ّP̾Pzt6s7޹e{@oVC. 9CH׌ܥ:z.+EV܊_wSv_<ˣ+y׸o F~$"`=m'Mm6UV ߏ1x!ěUF d䚩FR+({o:w ^^(l.H'# Osc"5b0SYp5 jlՕUKؒ !ХAL6|"?;=>yyV]I QEwX),'??_R/,@[7h.VӉ4rc:>o"`:[J>˃$[2s~QAuZ7\mT̟l$PRNO^F-# bԗx)unA8ʪɣ!$q}7w+x\K)& P M Kv_eiͰuǫwR:wXu Ne^OSYW(^DuQ=g7m;w #$מ%˻w F Ih :ڼu[2il.Xunlyٷϊw9Zj,'qo[8*$IʢsٔiCOs T)3w's&OcK1ubuZߛ'|^JI?2Xz$n-4L4Ib`v$uLi$ԣݛ>ſiEg&&|r?"v3qF2*&Qygm9JFa2hS\H"|85dڻ)w @A7Ҥ ]oèˢwRFK 1@:*_EG1oDAQ H?C\%Tޑǟ1P <n *}x~E׮k',jE(/a&Z(Ft'>XvBXݵYv|x{`RQIDE/$IQ#Iu䔣ʅ5Gd {!ɛP.KFpncګO<¤ykٴyW QmKOͬ|1ǖá5rcl'^91} dyI.BKtR7$ٵd mu|A֮,ZBLtj$V-ʤY-0npZ4mFù7xXʶĤגfu7u6n8Û/a%I*([%*5Sx%`=mKւưtQpSNdҲ z"mڄ :nidOxD:{.'&9=Ra$0 q{WSqƉ<=($a[+GQ_$A^.>LF|Cn@|t ^ 4d+7u kPmkC:pu#:1':(>WfJv)$Ij|_aXhɗl^KBov,‘SI^L"ɢh o9=wBj/1yGĻB[|2}='K^zN+Gd|=r2p ]3ӫD1LU,Mcm{FŻ5q}ɗ-=\EGٸn/9nJ 6mNihM.- 5EXntG6P#"$IU1缴r^E$sECrb"Б:7Qzr%&<%|Ճמ(M6y}wMXFT8)+*4y_>Յ\۴4Rzts~<`~xs -}Ph2`OZV3 m̷2Z(jLz(e1Ϯ݄G?)V؏b/76nN8 Cxh33gMhgQ'ּLJuEe&0؄ʝO֩ln0$I_hвPzg~*ײpM ]ҿmP)Y,8 <4lPm`"m93os*b iMMg1첫w("{/;fQ~-4 11=*E:j$]8d7NBzH"A""v/r(`"V삊 wDzwu"QB!ȥ&3s=9Koq8^g/FO1$%4ÎSf n*5DI$]du^&dPx6/fo_u lPo9agwnrE(Eũ6N[wqf ܅NMBkU4 C-% jhѵ#A5E%2P< mSpxtx.n]]ӵz˅W_Kts ŒHA}H*[9U?m07z&x1deчШ!UL ( ]-AQ}ܬ5N.|:?+Zہu64Mf P.ueTuth1MU$IX. sx[)}ҽA #&0hRPv*\Q IDATk:@t&)`.%ŔTx|_QRdëXe$sbӑ+hw ~8mΦЅF/FIb mԬ3TR8*pzXB hKlOQZ3ܶ|2JQuadžw)$IK;}d]3fqp)4m~ŇSIOͦ}ivkL< -hTK`= Z&Y V+K*e{yԆ& -€$]r~8Lɓut:=̑4Ԏ(M1Ħ4GϤږFs9`!Ģc+q~ OSls5]h3nM7WF'ݿ,cK.DtMƁrI.9K2i*Tљ3hiznol\f39&h?Kf`Ϊ\tIaH;| :+Í3vqr8nn74\YJ&|]㕷04ICYF2UA[_>+1bMiRcdꢼ(#[n[!y9UͣVG$I<-4&?Ⱦlܲmz#N˞uKqٯvɏp}4}D{_׏}W;M C26Gns{O(M%yt&GGpdUk5}+K/`SV =L恡)rc#yneBuRQQ ~π] ׽ {sHOS,$$f֟( E)40 5Xh2l'ޛj73Tt i>&?p? hAh "0hA4J#ؤ;5~O":؟v8vN7Pl1QPQ0X oQ\låj0`5jS\\Kh|LRӲ((u9xb+MyQ!4,!_ZHק(JUF I$I&Q$It:2XH$IB$I $IRd$I%$IT/,$Iz`!I$K I$^2XH$IB$I 4)xxgPs+I%A0{b2«aڧArzT{t?;nb.¥ҠQ3:tEϮmHrI#Wl:v˥BB\ˮ/[DP׿^ZH~nY9GG`hЎ;QƇ‹$w}f=BFBNnKמ>:,IߍƔ -f,er_uۡD|^( P4B VPVtcbT}.M|0$@#`VQ1u"}gn:71f z{]v5o=SDQ#F+z*i(Xbr߁$B0~dGL1cƗl5-_0}1wf{bmXg@urbh27KVaf6]W?HF.xq_gQ{)Wf?ä$5jO|u7y|HZWc֓7{dU;p&epuD L.dgǕٸe#W'_GLz1_k+.Iߒ(%Hh)Dn+g~c0hEq_e[Lَ_[+ڄǿ&-"8q[[E)xEňP,ă%gUGX:ejDĪlשK/[viyX0G/YOb<ҝ")jf[uM-O-I.Hw%e0Nƻf0otO *@ݹlb:3ſX;*,N;NZV. hi0CUUF~f:9QAT|a]TKNϡ̭/<@c:Jv ܄]VWEר/74ƏWqmXI:{v%ف SyQ5XyCx빡_ jb72|~!+wQnh:>yǍ@jbh@!3tz3)"`$mUNjHtGm7e]it?5-_ƌʸ0() \Y1oΘ7kQݣh5ǭMḓ=iPw^s~e3p{E}z6j,Fp[17-qK/ZN}S\ϞE0khPj"R]wጹaûE}KwZკ8bEd8y]ͱcx i8 G_c0-McE#exwo~:o#U>'7#V}FeP+z#_;ɀ! ےj/.#+`",'Ӛ nFCQfEb')DI.S5$ wvr|)|֞=n$W28X?sZq@_?rBW_uq6L=5Phv k~|bDŽ^N@hh@L+'KS=KKɾy٬-n@Qq}STAkl}0*8<~sH8H[=Wf|qSn}]$IUw?w>̭+evzQ_U* e~ܥ9F,FD͉1.o,Sz?brr)(.p O tjhС_OѶdcl B~gӼQ#P=n\nѢcZAӣӀ}^gX/=* ȓAp3$I熯Vpk`BA!".00#vElt -V} N +^TDK*KՑ˞_0/Y~7?:PBGP"#0e;Skxc48>bz#&(srUcPq;UɌI{Rn v~ -xy4^U7IΡ?VSaTSı][(]j5iMDU 8wOߔ$z [6%&$kIya*TI `1+pfmgg ?W^àdњ Av)ť^zu27 ;mAhKYFI֎,Y6+;pJq/e3q(eڔkkI?П)7dxL}g{r+N-Gq5'  $6Qk){kl >{lr•|L9)WS1_}^тrrN=r Jq Lv{`7ʀ!ܔ\o~@:]{R5U撛Knn>R ssK~qjَ.ccςL\F^N|\Z$I_fZBϧSk|-lqCwic{ٱy=w8?Yytk>7ckѣI0}l\ ryw[PqgϞBt=yb?Gk1k ==/}C 7s dz|ܶoWiF_ i8`2Gj,_لMQGAx8yqZR'&B P53ga(ܷi-cw~r0غ\$;GrcAS}ZH&^\[C>[SRAg"*-{3b}/1Y?୭|*#iF^/ZexMz(xRB;,PDL E_daATk,U.^z?dxuhh>؇n5jRРZ5KyUsUQc $!4+G^ZJDIu^E%= "jspAVn!%6'B%0"B,>WdS܀txlܠ'0^'ť8 FK0:r:)-*#,qF 'ފ uS)Z3`+&& 8Fva9. RgTqQdw[ 6>WY1gp@P?zٙ!IK,$I$(r`$IT?,$IziJKK/v>$IKPnn.P,lrQ3#I$]vKp .f$IKHjj*(>|8s%!!苙?I$"*,,d޽z,_r5 yC< (֯_/B}Eii){!//#I$3Đ@1+Qr$IT/,$Iz`!I$K I$^2XH$IB$IYlzymd9LvDRt_ Ӓ$I/宧K1yFĐN'K=\2V cmMe$I*]QA/3xTw>Qtc ׍#J$]5>ҵDn@x]Ȗ!{$IKEܓkoɐ$ItQPPL ^YU6SΑGH-m!Q2)#NYu~x?R&LD6")9H=J$IWqFΦco1C:_:V% ƈ6\yS<0Z|=m=Mk@N!w1zv9'6aG,'O:I$ Eꠤ-s:wG)!cbl)_??%8JXzS3=ah3m+v]6.yϮ0E~ $IA`!pdaϫH7nZ+ mZZ"6-XoCifDTǵ\0wL_ȶk30jZžf-F[SuY,~nhw^0WQEdK$ISpbTn_:- @P[y`Ԑ?Ca3sEt@-rn =2bU,\=(&";/JHkO'"I$Z ލֱBCFweV1D%T3böy( 14OU#> {h: T$I:kYSoߟLI'":0V# KEA&tq4 dDEX-I$霪]EP4hϪd֢W}@Vv@Pڳɒ$ItN\Ug5fL:wi%W W9EE68w$II1Xh !.L0|;\٤eN$IE/4De;ɩ9NstBב| q6$IV2 rͭ/O><)F+s?~SUPdof-c7>}vh!It>Ԛgq)ܶq:[_g_+RDŽ;ַvA+LI$UŽ8IZW'PO?P(* k I 1qNԒ|SWxmVC11ݮ~ww&ؙ%@W"5XKNFlD#;6$I E!KEq!%3s1Z 4ƒr@k $jݶuRVRJGD_*)H?ΉBkXqQ<Q9dKEI!eN&[Is*XH$IR.h(I$A I$^2XH$IB$I $IRd$I%$IT/,$Iz1zÆ o̟?%K\G˓%?} HZŢ_< )l5_T\k' ,z>PpTXwb|a{xDybRp S^$韆5 k5~E.opcSIin:陹Rd!"6ZeqBGAG2EGht!'pv8W9! Idl\=V\s#[RGGg G?`뢥lݗ5!7ԑk\{mw>^=ipCy'IyjRЇdұ|g_/ŠUEfZVch2=FүIŚL~J+`[0}45Ż_U5xJ9.>B IDATKޙ6-E[ =ݹ 5!5 ΊLʉrSdztNrWrdbTܦt܌pC+DJw&(9"_c&42:8=I$tܴ'0 vmG#Ag=eټ|/V16$G`}x;ŢY9^'0 (肚2D/d0nF\4hJuo)ǽƊt0nC`p˝O]Cj@83X3.-yx='.4! $S0ēgA9ɑ|*8`)!arbhh IΏE"rCG.ܿ2)cLf0Q6u`f~ÜEC|w{4t#hyA ;gCՔR;{+[2:ڳ޹E{CI.XJ8N` k@Iw?nE!{WhǨKx{,vOO:W JI۳ RߒaFz1Gi1Y[^LIQ()ULXu ~U>#Gj7T<”IѪ~C`L&/ ]|;3P|}+F"c388ucspݸ= ]mN2N׿fKz9Rqpxb]jAGӱGpXsVt&iH]4wH*Sr)o^AoLj?+??WC{n|n Sܼ\r$IGS'~zѿ]YQNIA.EvBܰ WO{K&u#_{Eҥ~n1?iǬ3]qe/2ʟhEY.aG H?N}GWG{߀hAQv׳nKO՟Qp={l۽uN81nc߄h~moel ǘ<04jyrs沥HEʺ9LkBz2ir!^J`ANbr.i+ ht&ֆ4oKi۾]tebvXh2l;y퍏Xk+P ĵȽo>5ѿ1}F+McO n9LfA2+b`T]omAϏw?3ETx5ZHlI7qM8O;),$' 1kB-mS3>bY󹊢1҃;ǎe4Q 1:NӺ$]R=TR8=]1  FL&3nJsΣ܃JDL,!~(RJx5HTxq9*+pyT NQN&9WњX mА0+:(-.#-RQIjZN0O\@jNx+)*y&A@a +!IAGT$IMQ\J$IGB$I $IRd$I%$IT/,$Iz`!I$K I$^2XH$IB$I $IR[KH=t|KL2/p^4Z4N$I{{ltc6?̓ڷSJ~\{}|40_lMxݸ\TEXJ#,xz8,ispa/Xz;sl(huz&._OEd~9W0{LmX%I!\bѣrϭ.QOX0cK7GEE]_0(>[?t+beVoPUPhm yϗGX Zk"&PtP ̣9hEQA1ĄO7\79x6D80uuۜ!a;KSa572U)?>=n?>q[-e%0Fπjo@U I.mHd.뫰Xddž&"d^wCzyKv,;qcxY_bwe#'Pxx\ә'~zY~wCo~e^ Z8xg4MV[k w2kŲѤS0s,=o ~[bSX<ʪ5cW7t}GU7ֳ%pc+"\ G^i3۸tVz]͵="rw*NfRz x!~S@Q>)}N/S{8.=^ǐT\;NF?L:i 4;p%ItUzܓA)ڼe6AiV=m׋ j\>;њ2lʼnҚ%B`r:5 U7 6. 2\g߫ E׎$|xCuxJ(yN9k5Lщ㹣o: \UPt:tuMVt :T\.^-I%U.K"!|+oX.V LiVQGNBV1O<;rw EEjqD@`z<8g_*cQknNР71h@upוQՍFd6QWD$bXR2=Z>?m$HƖӎ7h:BIAuةpծY z>RәPt:򧜃 ZL@,zSR}FI bbBKi%sLG%|<9o@UtFL&=/QPIhfLc 'A,AZW$ fΗɰW1o9f Ԋ7 UHYMWѯM,MY3P;qO乕EIIF [ʊWG1= t1\<3$!I?MUP@Y检R0ԸKcɰI{oPQ7i653[LJ5(%`#6U>?l`Z&(DbY󹊢1҃;ǎe4FZw=DEU+Tr-ZIBPQR@CEcL2^%`JNNA)a 8h­@./?AA>W(.̥1b5Lv۱;ݨBADŀFuRZTBG` &ȯvFq ըvNqq9.U5=|^*2IMˢ @£kT7EkB`i/"I_(U5$I$]GJ$I`!I$K I$^2XH$IB$I $IRd$I%$IT/,$Iz`!I$K I$^2X\pǃǫvӤӦ=G{sCUυ$Ic SK6nW0wchfENQܺ;p,JFн=!!ʹ'<n^Ųy, mq-犾toM@]{i!9dXjA;@D] /lN͛پv !:-]{{"|$I7sFSn37 ;WlyH6jD`}[׵l'wz@^/tZEBZEarS7D@!"bZEƘ^։ oU TNE>u-׼!FLaFP@(BTbE{f7 1nj_5֜n|rܙQeHEaYȉGP~,Y͆͛ٸv)_ y8ƍG\Y ^Ԩa<m ?#i]YONf#j쵓gVHD)=p604}Khv1Wg㖍_|m3Y31 ~$IKtWbl 1gn5bI  }-o3e;B|9o8k^8xhomE\ſ#R Bt~GxNUb"J_]oO[/l-#b.hrg=5-WKwmՙ#6;RSC<$]|#I t;a$>{7u̜oc$ɾ'AP[V;h+jiWJZ-ԮT&JDb"$~~$)_z]{Osb8#w'bŽ)?3f\@oVLX͚Dzx\HN9ҲrͣЬ3Hj\ sYZT8P̉Sg-Fxեeh|_E#:;: U΄z';,a7H8rӽxj˖ ='c6νݩ΍B_*n_uנ N7Zp[?!Rџ&9$Iu K$ݵJG'UDu~{jfܒf_?d]srڥ%oDڮa!a̛U'5>ݣDayo["\vì?H(be9~?= gԠ4sN g0ϙL|b~:r]EH4_28BNЦE+*;FNTbj/(iw}`khh7+{);w͟@WTŒd?.IwKʵ6 InՔ'լ=u.<0b$G, } #y@];u߮b}^El2پȇ\z:#9s( +ױlk@pd,bw壼tB]7(S?y4`a/Jt+!Ax:^^f)$99:Zp#ٟȨtb/ϑo??d`w$"ZGK яb)hkص.V#9]JEi?bc X[XDƼ껬;Z7Ͷe_i?8a8݊^G[1n|BVl[~4cۏy+ƿ܎?$n!JoqMcm3k4z[-dɋ_Kux⪲EQŷdslJ0!?$U+U.DuĨWZvf+|3m8j ]k2S[-gi(^{@qfy_{|2cփd[.-31 #} jBx{߇{~v%F~ ۞Ze@QQ)яi*;FA b_|IIDATo] &;u*~L\bhBHo>g=gyq2$Uknt}un?Ȝ >?~kRV+[.co1 ߢ>NP:X^ړ qƩCIu!O$Y\/`4c(!ꂫJϹ3iW{QsoNߒ./ ͞Ž!e'O'5;~Un`u4*Xfif1՗#ywItkT oaZu\QsS9fǾo'1l^p9f9< vFI`s^(4 !4,.\jxTmS%b|ܛL\L`̘`ao2p)ieG:T&,b;I4θj7Sn0 ˒<ޠ{[ǣy骁L=z#Vv=b}swz+OYQo17L~Si[($Iw  j'-:u@;t&5d&gJŘo/#ޡo̞ǘ'O[nk@ՌL|q_ƥL>G{^"8zr2r1k[TBҲ0'""BLl$md[/Lf{/2ږ$Iw~ 'Z!؎\>y{')9pʈK<ԡZHVjvь?Y:icØ6-l]NeT!Ի'#$>ycS<K's$,&M GXn M=Rz?2PHҿ Y8F@ӆ$i(vpᚾuAѩX6tÍf< 䐫7r'ȒsSO´c/NӦfNJܩ«]&2lg[|>갖<<[V(<z=fN@P|'$ ߽66_ѺptهLY++eaØFbƤU\kDR_V6ǜzqTFQJ,cG`oVq"+F=B];ca_57Sd8,cȰi,"+ʔ}Ah%<%۱5,l-CX(83K}E\=ٛNuܯcr>;ls7~ݬ'7;lrKۮ}k8ݘNPyc`uϺ6 XI-$/nx7}ϑGzYН;^js!G9GqLt.mKtٞv`x$M]/,6>عt_[aǔw\4ݬU<1Fle_`EPk!|255~ȑ?1s;xC4^dvoZÏs :1;qt"W=+ `x!8Z#u^$@ PBJro< 9gfVx/=D^.InBFT-;kD?6šݒ/Yi_LY.'Auypz5 b7 Ðiu ` lvK v)sObN>܄/3:8XqA(/|U޷.5j{;Oc'Z`""gcenA(j2t<Ç};B{s͎^*/\7T9uiOUĂd^'Zk&ٌ 0XA쉗uU"r2Υ؄pp߀*|Ffv&5.^ZO~4.xx\fBhSprCwM ( Tnxy:^[hN厶CX HKI%3/`pncgTR;y8sa>c;+.8 I+)r`!I$IeEH$IB$I @Wu>$I;Pvv6P,[$IL.JBXXXUI$I={p׋G}KIpppUO$IB=z}m۶fm&(ma$L2$DnD\\B$ ^OBBϟ')) I$)$$H5kS{|9#O$Ig!I$UJ I$R2XH$IB$I $IRd$I*%$IT),$IJ`!I$UJ I$R2XH$IB$I $IRd$I*%$IT),$IJ`!I$UJ I$R2XH$IB$I $IRd$I*%$IT)zalٲ*%cǎԯ_&M\\Y&],!DAA߿dKdI Hվ(/j Z(UEi+BtUDX9T2prrP)7'ZY U n(M*(}NSuG5IqqqUJ"b޺Xq !I$%%vvA*ȏZػ3T@ (((նL(ubb}{։EN5u2P{U9'?Pɺaת۝4111T?*aDn!>iϣ:ЦP)] *ڕR^rJA;ꅊ ")-;*q?zRpǂ%08՜-:u>;P\n\{k.5[kdk?\B…^]zjz XgjŻUuTSTS7T.UyĺX}83] R_q.".I1UbcuK..b̂bkǫ]8=Gkbců[W/<@<.?q1?3jjQwJݲXtiᦓ(8]Jws "ط)Bz)bb+fEIYBqm ذ#V&V"/6gSm׊ug ũ>|XKC[0B<XZ&*HJbQ 6eJiuxqnN+FfxSp@#"/XT-$r8.U~bw =kk.j*)4p" L["dp&ۂ'O{ Yu{(~PQEwz= vI1ƽYf7ō^a4UKTNgj-=4$]Bi@?e9n)+RV}=x]8jU(>8 9YX:/2hZ} h*_ΣKk%?%Y'/v9Aa0iK47b#s!ۤ!)4a)_1`dbFw.Ȅg)jvp`څbm9zdg ZZn}:}h/4Tb \c˛#ЫG?̻ߞƪ5LJzV3} ~IJsIጙZTW|!4\ƪ;eEhG2ϿmEvMaP= $gfFuc_mOI/2GgV|Ή<ԁ%?qȹ-SCѼ񟔨TxJǗѥѧW\y`2XK)puuAXYCfe{X(.1cxz75s>R 3B6-I$ .!{<<'P4xp;]wO*L o|_uσM!?mp$5|~6{]oOu2^^ֻ8Ae뫴xird3mCcW!M[g~=# gh-& ?_CeNM7;],Gղ+ T,ؖտ>~Dx:9e_Κvé*iڽ(7pPx43|~į&^ȺqWXPVLQ^C19O/9k=NP}ss,&BnOckǐyOK JS:.}.lrSL8GPhĘžţ#rK-( ⚧Kn a/!^˯DO:DE>E~\DnKe[Y9s ^{;ت>3{x1c~[sTuMGvRu֭p zy 0i]OO?"MK-j Vf.}kAoےNx^Z^aҺUv;ݝVu$$OqNHPd+w5Q-Dy'B_ㅉt )! Nq 'Ku>BǺfN+jD㩶R\GxڄиgLN?;~ai v2Sz~ )ʨ7y]= GN-p, #EFV,f3Fѣ4ܒʦߋԫY;8c{ڟSGOj9u)$;V)&|̸ﳴ0A/3ٵGS`2sq@c51#%fPYݩ #Uo+lf6%lx} ֑l&L& jle\cgxu/((6Nbә) (=^gEXthKdD 6,,v1?3>ȂZrl9om؞%y^3EI3ySRgkL;̨:DLU}rnRU[S/쫯a3jqs*ؚ,ܚΉn_TN`X_3.|3cLZ魢0_0 cΌ(2V*m me߭?JZ|; ]`]6KD:xԼ@āSV|9IՇ5Q$qPVH$NXPԮxcc*wqUDۄ::9?™{/+tS~L7dRA&/I :?E!,cIIENDB`examples/organizer/qmlorganizerlistview/doc/src/000077500000000000000000000000001233466112000225275ustar00rootroot00000000000000examples/organizer/qmlorganizerlistview/doc/src/qmlorganizerlistviewexample.qdoc000066400000000000000000000126521233466112000312620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \example qmlorganizerlistview \title Qt Quick Organizer List View Example \tableofcontents \section1 Events List This example shows how to write a simple Calendar Event List with the \l {Organizer QML API}{Organizer QML API} \section2 Motivation This application was written to provide an example to developers of how to use the Qt Quick Organizer and Qt Versit APIs. It provides an example of how to use the Qt Organizer and Versit libraries to: \list \li select a backend for volatile memory or persistent storage \li list the stored Calendar Events \li add Events \li edit Events \li Remove Events \li import Event lists as ICal\reg * \endlist [*]\e {iCal iCalendar is a standard \l{http://tools.ietf.org/html/rfc5545}{(RFC 5545)} for calendar data exchange} \section2 Use Case Most people have a need to store Calendar reminders for various events ranging from weddings, hairdresser appointments, work meetings to reccuring football training every week. This sample application provides a simple Event List book that allows users to retrieve their Calendar Events and modify them, delete them or add new events using either volatile memory of the supported backend for the device in use. \section2 Interface The application is designed to work on desktop and mobile platforms with minimal differences in code between the platforms. The interface is organized into a QML List View, showing the Organizer Event List by default. Events can be added or deleted using the buttons at the bottom of the contact list. Selecting an event is simply done by clicking the mouse on the desired event, and using the save button to store it. Here we see the \l {OrganizerModel} used with a QML List View to show some Events added by default. \image qmlorganizerlistview-main.png When editing you click on the edit button and an Event Editor will be displayed showing the display label along with the start and end dates of the events. \image qmlorganizerlistview-edit.png Here start and end dates or label for the event can be modified. Notice that the boundaries have been set so that any character can be used in the label. However the date, month and the year are limited to integers which match those fields to give some kind of simple but sensible user interaction. When finished select either the cancel button or the save button and it returns to the list view showing all events. If cancel button is pressed nothing will change, or if saved the modifications to the event will be updated in this main list view. Adding new events from the main list view is equally simple. Click on add new event button and the same editor will be displayed with NEW in the event title as follows. \image qmlorganizerlistview-new.png Simply Edit this as with any other event, and cancel or save it. Cancelling will mean this new event will be lost, where saving will add the new event to the main list view along with all the other existing events. Deleting events from the main list view is simply done by pressing the delete button and the currently highlighted event will be removed from the organizer list view and also the backend where this event is stored. \section2 Known Issues The example is not intended to be feature complete. Only a very limited subset of detail types are supported by the application. It exercises only a very small portion of the Qt Organizer QML API. It is intended to be very simple to show the API and allow the developers skilled in QML to utilise this interface to its full potential. In particular: \list \li The example only exercises the QML OrganizerModel API to save, edit and delete Events. \li Everything is done with minimal external components or states to keep things simple and focused on how the API works. \li Editing a Date/Time is in string format. In a real application would be a more advanced component on its own which would allow easy and simple user input with good error checking and error messages for the user. \endlist */ examples/organizer/qmlorganizerlistview/qmlorganizerlistview.qml000066400000000000000000000175201233466112000262140ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Pim Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ //![QML Organizer List View Example] //![Create Organizer Rectangle] import QtQuick 2.0 import QtOrganizer 5.0 import "content" Rectangle { id: organizerApplication width: 400 height: 600 state: "EventListView" //![Create Organizer Rectangle] //![Create Empty Item Function] function createEmptyItem() { var newEvent = Qt.createQmlObject("import QtOrganizer 5.0; Event { }", organizer) newEvent.displayLabel = "NEW Event" newEvent.startDateTime = new Date() newEvent.endDateTime = new Date() return newEvent } //![Create Empty Item Function] //![ListView Mouse Position Function] function setListViewMouseItem(mouseX, mouseY) { var indexedItem = eventView.indexAt(mouseX, mouseY) if (indexedItem != -1) eventView.currentIndex = indexedItem } //![ListView Mouse Position Function] //![Create a title] Rectangle { id: organizerTitle border.color: "black" border.width: 4 radius: 5 anchors { top: parent.top left: parent.left right: parent.right } height: organizerTitleLabel.height Text { id: organizerTitleLabel height: font.pixelSize + 5 anchors.horizontalCenter: parent.horizontalCenter text: "Organizer Events" font.pixelSize: style.fontPixelSize * 1.4 } } //![Create a title] //![Create calendar Window Rectangle] Rectangle { id: organizerRectangle anchors { top: organizerTitle.bottom left: parent.left right: parent.right } height: organizerApplication.height - organizerTitle.height - eventButtons.height color: "white" //![Create calendar Window Rectangle] //![ListView Highlight Component] Component { id: eventHighlight Rectangle { height: eventView.currentItem.height width: organizerRectangle.width color: "lightsteelblue" radius: 5 border { color: "black" width: 1 } y: eventView.currentItem.y Behavior on y { SpringAnimation { spring: 3 damping: 0.2 } } } } //![ListView Highlight Component] //![ListView] ListView { id: eventView anchors.fill: parent clip: true property bool readOnlyEvent: true delegate: eventDelegate model: organizer focus: true highlight: eventHighlight MouseArea { anchors.fill: parent id: listViewMouseArea onClicked: { setListViewMouseItem(mouseX, mouseY) organizerApplication.state = "EventEditorView" eventEditor.eventItem = organizer.items[eventView.currentIndex] } } } } //![ListView] //![Organizer Model] OrganizerModel { id: organizer startPeriod: new Date("1970-01-01") endPeriod: new Date("2012-12-31") //manager:"jsondb" manager: "memory" Component.onCompleted: { organizer.importItems(Qt.resolvedUrl( "content/organizer_ical_test.ics")) } } //![Organizer Model] Component { id: eventDelegate //![Organizer ListView Delegate] Column { id: eventDelegateWrapper width: organizerRectangle.width height: ListView.isCurrentItem ? (displayLabel.height + startTime.height + endTime.height) * 1.6 : (displayLabel.height + startTime.height + endTime.height) * 1.2 Grid { columns: 2 spacing: 3 Text { font.pixelSize: 14 text: "Event: " } TextEdit { id: displayLabel font.pixelSize: 14 text: (model.item) ? model.item.displayLabel : "" readOnly: true } Text { font.pixelSize: 12 text: "Start: " } TextEdit { id: startTime font.pixelSize: 12 text: (model.item) ? Qt.formatDate( model.item.startDateTime) : "" readOnly: true } Text { font.pixelSize: 12 text: "End: " } TextEdit { id: endTime font.pixelSize: 12 text: (model.item) ? Qt.formatDate( model.item.endDateTime) : "" readOnly: true } } } } //![Organizer ListView Delegate] //![Create and Delete Event Buttons] Row { spacing: 2 id: eventButtons anchors { top: organizerRectangle.bottom left: parent.left right: parent.right } GenericButton { width: parent.width / 2 buttonText: "Add New Event" onClicked: { organizerApplication.state = "EventEditorView" eventEditor.eventItem = organizerApplication.createEmptyItem() } } } //![Create and Delete Event Buttons] //![Event Editor] EventEditor { id: eventEditor anchors.fill: parent color: "white" } //![Event Editor] //![States] states: [ State { name: "EventListView" PropertyChanges { target: eventEditor visible: false } PropertyChanges { target: eventView visible: true } }, State { name: "EventEditorView" PropertyChanges { target: eventEditor visible: true } PropertyChanges { target: eventView visible: false } } ] //![States] } //![QML Organizer List View Example] examples/organizer/qmlorganizerlistview/qmlorganizerlistview.qmlproject000066400000000000000000000005571233466112000276050ustar00rootroot00000000000000import QmlProject 1.0 Project { /* Include .qml, .js, and image files from current directory and subdirectories */ QmlFiles { directory: "." } JavaScriptFiles { directory: "." } ImageFiles { directory: "." } /* List of plugin directories passed to QML runtime */ // importPaths: [ " ../exampleplugin " ] } examples/organizer/todo/000077500000000000000000000000001233466112000156375ustar00rootroot00000000000000examples/organizer/todo/doc/000077500000000000000000000000001233466112000164045ustar00rootroot00000000000000examples/organizer/todo/doc/images/000077500000000000000000000000001233466112000176515ustar00rootroot00000000000000examples/organizer/todo/doc/images/todoeditor.png000066400000000000000000000540141233466112000225370ustar00rootroot00000000000000PNG  IHDR1ej} pHYs:duh%tEXtDescriptionWindow Class: todoexample}tEXtTitleToDo ExampleM] IDATxw\ǟݻR tD`A`;&Xػb b(E+qw?V㚀yx3>7Ydf{" Afڛw&K\%$-.wɽ4ZgC0MMM1l0D<&o>'iza705aɁ6&Fz$K5{AMTn SAmЀzP/WAYVx5ŪW\UY5Jr됥$WUV7,5l@1/˩_S%ŅO^o $o̯ڠ>ώ B,W A귧6اr _6MɥUsZɩ;|3i)"P(AS9JBT]]\T$[TژٻX$*))f*c. ֆ_+n3#j ͶVZV׭$ B(<).,kkꚘضlչTj" ޳T֠nMiSh$A ˙ZgllܺM}!"B rVYkg*XZɂ/`eA2 ; -%U.gnfe)O8:;lRj4$_>~ UUS˖-) zϦ$W^:9_3}R@qVq@CpIpݸyk.\.W[YYiجYqI@=|vs33=MMLJJJ={v֭˗/vpt/FZDoM]},Uq4T+*κN)ZQI}kn߹canޣgOٳg7odjCI4037'G.ܜ6vvcǍ{}P(I+*@Aq{%4Meu300~\\lB#"+'ƃIMZOOOiARU[ sR N^/N)$KlX_Ip9O!$#I09$8$ ok 8HsIzs@/3>k^I95Um޴r̘"IxT-9'׮^IT]Rvu¡v=7ٳ¦27o޲e?777ܙ *uIf(-=܇}U%%V\/^^EEo3CWN?G%I P{)7_/+-%HV=ݻBa+<===~]riΜ1|;sf^rU{ҟ\IN NNNsf !s,oGB^^^>9ι?lժɓ]VTTڷk+K_7nPQQ1klW7ys>}t!k֭p8`Z"…{yWY4tNNΏ?.|P(^lyeϓs *(]ץJ{8вQ)ؼɕiwkDII/Ԋ'Ollmޤ)4@'ׯfIDqI/>qG͚5[ݩSH(k"Hu=z֬Yk׮izʔ-[lٲy7oNQwN7JK 斏R:#5J _|]^yiqyyyqˮ x xAf͚5r)$]]]-HKUQQ1zsιffĉׯ_+n%ի#2 pugXjA8"0G <ۛ7m7w@ XVZ'y ??#ҥI&^ѭZ}?ɓ8+kvЉ[juK:+*гW_|qʕWEI$"XQ$I^J$4E1MSWjn`Q ## NImӦM HdjX,X6oڴo;"vDH95UKJM=j4I&Ϝ9sݺuS&O޲esc#QJd"ݞ`hhO"(/_zᳶ5Z'-% (PhhhH ǎSUU%]I(<+g< 72T,cٲp,&F(NNo׾egϚߣׯccO(o]G?Y^%ԡcFRVXY[3w;(ڼy[͛TVe*V-7oٺe 6~;sOYooWzЪΆ`(8qOrJrn5k&(Hj4M}M1 NVFs#OY^NRZJ"9CCau@ xR|>?2r?˕DZZ*@jϟ?6mڐ$ m 5r_,?ݽvd?qYEIǫݓ^Ͷ\CDjBAZM'sj:^sHmr tB(EQׯOr5ey!!!ҺVLu?}uVi 4Emܰa` ϗD U_rj$Flߞҭ[ЉR{XT5jxh6=ɮџ^x|+YO3N_ƽ,,έ~qD 466ʊ CCY<X$"pPQ:!sj,,-ERNee殮99#RR~~B?u=zСCvvv7n,Hd~+޶';wl{`ddf[ZY@۶m.]$XX:qvi⇁ϙOYB/QW嗩ʚ@EyaM0!d߾}_NrtHnBv i S,NI N:SSSD5UOJz1iz#""bGĜsBƌٳwϦi "$4D0q {7SXҮ]GřS/tN_sÇήw왅<5rS8N5kwD~/<OZjdpx2fLo5ٽ?oNΚY^^j Oŭ^ᄃh  O>7^ō C={ٳ0|p}naaq::9|gNMI$Pߎ}@_O޽ܾu̙3vvwwW/dFQqm[]ڽ{!!˗.355ݺeKZjjPPB[k3tM/^{ޓ^yqGLD"p<s#SX]۷ouҒ y׌0NȜE 2~j9Z7]wSGk[ z{+>QUQQJqԹL(.ԊӧOՊ[n1+(QpbccfNj/ƩQAb=wv~sDB/znooD"J4){ButWzv9VWG^R-P\s/@Q5z Gau5ԩݥ?ϝ(аj:x(c1(щLČ (Sj MS|ʗ CӴ<v2_\G@$HkŹg $N٪%YxiRu<9+i= k?T ]sOb0J]U" RGdOki17UU@GGr X,&d9}vߚ1Ū2w캾F62um{NVvp\X,kA0wHqbDf\Hw AA,\.bIzpUW+Ժvj,i 4"X,rZ<7i$)D$׺^%'-0pLj"z3YH՛AsB˙Ppܾ4HϻKh4S+H*LiX,9\._KKgH(J,Z!ՊWr0<AI$"Hb0hUͿڂuW.;UXmN3eS۬ 񞍤JjUe̼w j[Jd~ơ3E"HxBv鯗Mi`~dRތh]Jf MM4z5si463* "5Ix/\;Z̿{`~4)rA#B6E6&EUB}>hQ8ey)m$j6&UUU̎0CD"{DL}F}i沧r &A%p12:sPc SBDE hYƂAŵ4vqfY'ʲH֐_e)L.[3"$֚ Hc#UD1ijQҴH5 46kWaIaIaIaIaIaIaIaIaIaIaIaIaIa\k $.$ $ $ $ Mjhtt xbUfbM1]inR*x5Fmac#޽[_eu3fw/r4N蘙&q*c  vvvvneRRKJ̞|]նm[p8咽7g.bYј&F۷OFz۷tmIQT""v&*dVڵkp*4֟|l?)++200H$pbԘX~d|4;ĴX,.++mmJ,=-ӧ:iB4N4y\x`~ZnJj>TZVtrٞ WX.hvss[|ٯ \,K~vuu2&00PWW7::z[vO"D/qޚUwTTTʕ A. s箳bσ :i⤦A 4 sl(DD jAjAjAjAگSo0IIIA>.ǃ5v/E #W=e)Ecd#]v{vT$ $ $FԤ]'._AkUV~bq k+Da';=ԩ6mijy7j$'O55,lJ޽|;7D":d!C$ճݻǺve!99D/N)?/Dl_ǝ<4PVV:={~ͷR/_q'O6onáCgd@/\F=!NWqqr׮b9nqI.%v8ABB EVWW'''+&$IQԠAO1zhY&O| -Zw |}h$ H4_MI9p`3gbcVe~: '${#FxZZZJ+~vbhP(uf՟s7`.-(((..nԓ ivrَ>ȑAF͌^~dM<_[XXlݶ}ڴ/֭[߱cV52 6mR~Z IDATdeǍW&KK˽"*6n|?.& RBI33F='"scuoEg}=w?(1f˺uHAM"@M"v.Ǟݻ7qhiٳ{"!&+7؟DvȺu4 Yb; 5 5 5 5 5 5 B<ݿ}.^IܺeK=(5٧OΛ]iǫ׬}gϞM#!88Iߺmk=H$Uƻv㏄x_Ǐ{Iw Dc] ttt:;;k%I s׮Orr*++[ib/b˫~0+뺬tѐrARS7oysի֭VWs9 ½;@UUUfn-(j#XGiٲmۮMׯ'NػoinnNhhٳ\/0"(G $IvFDxAFFӧ} ]]]YϜ=!9%emڴFӽ{H:3P7)))%%sfa>ji񋊊͙klbDG988888Էo.]J2cDE=sL|9VzjV7)Eu!"b\g5n464//#=~SVVfieڕPV^naiɴO'(u"5VH$>rRѣUyz|4&FzZӧOpX;I-srsgVC2&00PWW7***&ԩazzz HexBCC\ ˾}-ll-,za+ ܖ-_"ca3AȎ<aIaIaIaF4=5$IaIa[F$a A O+II|u7n5y4IFJR@  r鎎oߖK92ȑhMU~O"r(5;)5 ʡiZVx...,$|hh..h4hBOwwܑ&޻w/o.~ ͕ؿeЭk]]]]Q4[5(MEϠ.ڷ '33S˗/,-Pt~ܼyYsӓqqjNde||Oaee%ԩ|;rҕٳfJlƝ̺q3r&a㦬7ܽ8b99ܯ_#Gd'==kW2G:99)]hҧsgkkkٔǏ<Ǝw%9ݻxy E֠nD*ZOi9ڶp41$$$** _niiinaH@QTYЬ&5zY[`|I^^ `ƄN>>˖-9:m@ 2nx5>'N |^l"@iii$I5*={2۷o E= DFo~PM"!ᏡC2F %K~rj؉89F?6of=Em߶mIgΞ͊:x0xH2t?..))i֬}ĉBM~}W:~PMش>@ ((((*rʕ-[3v9:mXNNuIE B>/5;q⸮ݻر'1Cƌ˗#wލIܾ=bu>cb[jpqvrrtqƶm (M\fŚEaS&8;9;99sz}{:xPvؿG'Oܽ{W2/qW^7ull|׮]NƟ266nhիVMZVV&lʦ'bǏ1㗗M\0lJJJ3[@6# $DjJRR;|?H4$ P9΀F֭gA j!@M"@M"@M"@M"@M"@M"@M"@M"Ν}(vrv1}F'-[ϘШ]hܵk$t?~Ӽj붭 FvrAݺu:tHiitʕ+F6|XPPеkW Λ;28h_3gڵ$t„ $RUUU"1A4N#>?ꚘxQiׯ/[w>ܜгgq*Zhq3CCX~ݾ}_jٻvڳw3g7`DPP(hFԤgMRRKJ~읮b?s 䔔iӦn͙klb^ FdVVV[IJ%(C;*#==XLT!]]3g/yg5(IH}ҥѣGH$p11|4;/TꪬRWWNNhkkWVVqS'4n'CCC\ ˾}-ll`~Znي1377߰aMnnn˖x+s S6+,,`,DŽ ՍVv[vO" o֒bE~iIyā?p $Zkdz #@vAjAjAjA؅& $%%eggk!|4ʻ FnJR7|]G7A+si$/4$B\ӳG >r$ZS$qttGGGt;5 a +EₚD Mrݥ&-\nΝ;{wР/_ uءc1r*‚ѣ;o7&$ŋ+4"ŹCv}dffk+.6V[jy&'xBLQN80VVV_Nͷ3/'] =k4=!!vlɬ7n hP/"hjI׳n޺mfnx"ܰqS֍w 1oRǎOOXfӥoӿv͂]G:999oKvUGIΝeSr?:x`;vܕRwYUIf}p\ӻW|&ZO~d֭ER!!!QQQpuKKKs sէ a E jQI^u f盚9::?_TʘPgٲ gPGu I"ܹsa C7~'^|Y_O/6N~A )Nlmm۷o E=u iZ"#7~?&U!Clmm%?}95DlizΜ'NbR~\iK޽۶MIhBPeŵic!cF :d=ZPGӟTu6VV̐:wlhhz횋 SS+**?bx߾NNN kPw#izܹEE۷Gp8izJr23ĪgqqÇ##.0wwysM>H#;gQς./_8\J#֯_;&ukiiipg''G7nl۶]ΠnGrF־}{SRR<=]]X^ |VVVMx={LȎ*l$MAշHl|׮]NƟ266nhիVM*lʦ'bǏ19gVӏ\0lJJJ3[@6# $DjJRR;|3?H4$ޭ)Ҵ}K?|A|V AjAjAjAjAjAjAjAjA؅&5y4:]x1ui )4G^={6Ꮔ`&}붭 AShlkaa9p\gggXvD" 0v INNeee6mV@`5Çx1JKK xbbee?|$V[^\.T4I7W7~=<=d``0k]wٻYhq3CCX~ݾ}_J֠Hmbb"ıcI9kf#4<.4M#.7"bǃ22O>g'NHSd=~3SRVڦMurt_/[ߟI7g{ AVwuppc8#=]6+#==X̎1}D,+K42XltMII (v49ii^Tsr,@[[-,-iAD")(( ܗ/_x޽{/XyA@zZӧO59 'i޼esNn.yjcBFEE:5LOOаYaaSPj=?|AXXXV-[lߕW)$&O655m۱?|A k-Շ`0n%?3 @M"@M"@M"@M"@M"@M":IIIt ,}7#ș3ՔCiB>Ҷfw o'5TcDҀ'V AaIY8CPU]]ѣܯÿF,w 5yO&MܳGX;n츇S[G;bv$,Ec&&ƾp\)KK7μys+++.\IIZ_S4x6v]&33`\pL)<<9\s{{5A@(};p"&1M2t^Pa!u,]47u]&]&]&]&]hrA4`; l5 5 B٧O}3g JjuŇ n'wufI$uq"kuVF ,Qhŋ^^EÇ{^ǖ-[H462^lY XhnZD:auZP'O&LDl6kC@Qgmm "ćLӧO^$"bGll\>}~&]rg޽{|i@ ;on\llcď MP L׬Y#dm:zyYYY@Ppo+DA(ZbddԵk?N4xpɸ}{161lhX{iٲl QAuc.^yxz6^jӧO w5}BmmJfCG~ڐ!8AO&Ф… 6xs-_\1̢#GW|Cf}Wj`1c:$">G3C  jXh^Yciiw@Ƣ|WaB& zxxh5 ,A)K HSQQQ!kWa ׮wMJJ֠C(Ȼ $#ș3ՔCiB>|yUEOv]&33`&A!ɓp904lu(uVӧMC'NH~mm{"ۙM&&ONv)D[:t(*.6vڴ/moㆍ;wnAA+ŨaC7G p͝H$JOO%Gv&&&;w+UEOdFFl~FF@C{/$""֖f |9)ٳWKKI&Iϯ EQ))%%™ZZB##cӫWo:8::::RR͛7gvahh ?p4Z9N@@EQ..2YTZZzG/O###?EQj ɴ;JnvGҴl)h4MK(]v6m-+HH$Ii$|S$ T5rҥݭyhe=vifD֛"ȧ455YM.Kt>SEQWp_v͒P+**(kLAWIDAT:zzݸq##=xm9h6MQ-^(Rm~im޴)hDlTrA'LQkjjׯ)?KUӀVEQիW?VM ٸq97nعw--1C-qڵcƌٵk4D͛oV 4M;;,ZvK4MJKIcc6r;ʆ8*NZY[}W&N226#UZ5տY(22r„/5[ҥ۷B[@[[bbbRSSWX8!l!>>~РAu4H$ƏUWWZQyŜ9sLϯo;ߙzM …l>LD#4a;jffl7iӧlD4‡.ƥ&s!'['Ac}d~/B@XQQQG .kj$@ 5AmmWf!EI??? F Mp:aIaIaIaIaIaIaIaIaׯ\oX/Mqww.FÚ;ףGqq}@D"h6HѰ&;6mڴ-\"5wܑA믘7bww5WWǎŸmټyĈ駟2lϟ3D"fFIMfee$١СC?.h#GOж}LH$rؼy"]SL ᄑY3bc50M>rC@@~V,--544?~))+mӦ Hdop8\bcO056QИ&B :::j?Nf:{̙L:a)$wUc@Jkhp,\v|LLLܴiTeptBvfai٢E A؆5֟<~}}}!5||wʢvR!HQ^MBv>r~棧edl="Hc4|{(7@irc<4ӦmظknAEQE>s:bv$,Eb744G_}5HNNٸq̛7["IwwwELf|:u?п&ӧ˗-?t谕.\s4MD"3f{zxXXS5lذ5kB \7EQ[ϧ(*%%$|^8JK_XXؼys  POuQxvvvkc{H-$M@AA4/Mj׮ݦMGƂVRRSSRSS>v(M>k,irws[re^^uLL+a^L [^kFz;ܽw}AԠjիW۟TVeR<=<Ni2<<|o 062Yx\ټojjP@ӴˢE4-VN6f&v[w9iʢŋ-^|ӧO>}dqEiK.1۞{QAOdn~D>>vA>^>v5|| s##wהCiBaEEE/\hoo]kL.\ԔCiB^Yi02o!@M"@M"@M"@M"@M"@M"@M"@M"Ф&;wӧ?j[njrnRGKa9n'w >Ǐ4/O#n%Id~5[mnD(K3rܠkQ͛7ʕ+֭VWs9 ½;UVVOHlպURb%%?-qtt/>ݻ%?ef^:3իWyyO¦kڵ$tعk23@B[l!Ixٲe-llbWI'?|0''oٳWci0\kbׯ_/_t}&&9gϞKLL8v0Oļxbwٳm۶ D"ǜs`ɒbcǎ{~üf͞k={ӧ?/Ys츕~a}ώϜ-ja8<,:g -\/**rrt|3gp8z5۫m۶p$$I433uҽu򲲲 H\.a4b;֡-EQ:t!r?,*gp>iZ"x<Kuy3t$I IWE@XH"(:ȥKG|4&K׻w tNKK>bȍ=𡳳3hkkWVV0k>}p`d ,Gdhh(ˣvuqٷ/ lذqŊi77eWߕW)$&O655_-|AX$"9t<#Jʗ-[n``cBFGG3f .rjA&&˗/"H#Arnt/oshkk7u<8eUA @+n<a&\.6ȧGIAM"@M"@M"@M"䜁$>3 ,}7#wB+siA4A>5IO"@M"@M"@M"@M"@M"h\M۵-y&'aʕr)b{'rwx Fo']l>\V}"U9itMDEE----Û?%%%vEE ={6D":d!C$e˖~/wBBr…}w֫g˗/Ʌ_߿{jZvm==N~c]|u2tȐܜUeI ҧw/>Ǎ---U<"u~9wǏs?~yV\$]6?zS |݂Ǐ{uq"RxkKFPP żpnL`IuD:J3ATMF3BuY.>/NzAգq;dz *yL #srr:dm۷=jEi373i'M"޽{ٳ7nNTO;"?,(XKD! r cCfY,ݻQ捻,jnn~{x7ގ={r cݭ&o?fϼI"<坉'}||K=ò,ҳj9|xժւfW:S O>Ms}x7۶m5U񻐈ys|=߹s8(7^v{WuuuD+P^BDTQQ񏊊ظbMp}ъleeeD$lqdd aW{je.\Xd<?88N6<>FD&_ #GܺUن- ~ ڐWqS̙{TI'hm nnq[fN0;ܸSfޑ?/]lȑshs933B҄yi&B;ާO'H>*_[''72L:Gt޽ƍ8}+W=`@h$!ܟqzԠҳgȍeee.ܫ]aGꏓ j3ނ_ӻtJD ̟3gi;v\ؽ2ϠԵk665 !OY GvM !t]_h=pXw FݻQ4~YXX(8댌nJηI"Bq<\|zaؒoD#x6o%.=RaDoc mB۷4̭qqh+|6?ߪj@$ ΃ M&xAI^$/h4 M&xAI^$/h!Ip8Ν;HIgHhU~~~o" 0cpI#_Έi=/p87_?K/"2ϓL/<Vsd00@PTX(ٴV1[]H(,..t>ޞm`h$ yvnL>KpúJ<ާJcRAg(UbW)gUMu2 RAɤS 80^e Vcdd訫'ui* x<Ǐ Mեd֧A )uڹ1@f1*vVGtpqJ" _'%{neeebb"!o7pILHxMQIIyYIiki{zQ$)O^kNQruU6lZkdTȪ$ak]Zod`ȐYY [EZ8`8򥼦eggGdC]'㹹ڹ Y>޽"ظYf :wMÌ@x.Qj|gU3 bYYYAï+q a< E<}MPmīLL̼}}%'_pn֤EtӒ163363}-bhdJ\Av.Q+ciiY 6/PTw`@@zzH]VVWXXXk@tt4Sؽ[7=$IICoٺ5) 'q>m%eooW/-5&oS`9s^ʔPm6VիW u7RZJZy RlP]GHJL|448U62A899?o߼JDRRݴdTÆl:5%E&))NNc~x]r2kRu4u BW#VEbC{fgf&~]R9n\7{qtrrKNNx?$%[I?V0B$GP`$ƫrr* 1wE6Y^XPcl€X8eal@',c,*: Z\rvyּVQ}M[SU/G_Sϟ%%/hlbi:_tv;wvvNKI hѢiӦv޽gwҕg2x>V_JOdZUYA%ߔWj@ߗz`P::T+8dܻ_?yŋ{EiI 㔬ȑ󛻹[ZnӧUV>}T xyy-^s.:_`aees̹{Ί˻DFip8?~ㆧ|>kɒ]v)kE .xyxz^z-==g,SL.,,lݪՆ76wwfع|E-|.Y1xР_~bAk֮=999/ZZZ5MRRR֯_c>qfwwwemm'7.9Ñ %Y:yJN7V\\sVfck+VP/_().0 =İϟTecL6mED?i8NbHE3… WNgϞ={vXԔ$I?Ғ#SPq%4nz}+.-,,+H𦬤sȮ#Ia%%\.@+/++JY, MM@@DTGu떏O>}&M tuܰrssKFqުo'xBjivl1p@6 >`ݻv-][lVZ=|hI#۶{)S`v̙N:5q${{ oQV^>tk׮u}O> >@rJ$AP=bI!AP$IE5+aX * ILKMջDUivݷO$I8N H$Ri޵trYMK$Yɓ,X~:eʞ=/[*UB~/%ǓųBn6=+eS99.nչ5@WG$I|}}}ǥ :R644$HR s#rl;cР"(,xMm|V'NܵkCD"c*z!%*b-[ZXXl߱="2GttƍϘwߟl6ǏYbϱcE5#G^I6o2{M6sk7oڼh.]]om(bH'2L,!jfVVt=pIw޳wҥLML$[P ۴޽ge˖ ~O1uE$j*/s !IrҤ;uSmȪ=20 HR(+Q|( y!pҥ%g@VzJ.,,,Xx+gp8ba.?l@B(jkkkjh=qpsuD)RR> &%&yΝ;kƮfdd4&4_~3gk|ի]V\\,޶\\]///(} P66PTT\xmVf&y˫~ʧI,ºIzǩ=d N|CM"g ޼jY='VY8^*l8a"I{ӈȨȥKFUr)9?.)dfdݷW )c}wh.+5DCֲjWN矑:uzC'M$/R5L,8i }W::n99eYYzڵ0TK s8vDB!azzz@E Ccai ?}9uJK׭[澾))G/H]IKM=??_sٓ'O:99:#G Bg6?M'~mgo044r%W[6k7n9"^4}W}|Vՙ%U0V:@yY~u0abqqѣGgL1:$D6DUH^ȡ(ef׮]733o8a~}$)nZ|R~u(n;?/Z2fGEM0=qPƓjXQYkGvhaWoh޼yǸ8@6:+A~WqAYYCOA=7{mnn6jHs +WX! ޿Gnp[1,+dLDk_v5k/\PVV}\ ߱cʕH |grnlH%\nCJmػw 䰰0O/Ϝm'7h.!F_:qRpGu;%޿mBcyAA7o.yήjՉ%!Ӣ,WRӴ$Qd2t;`FMq)ܳ{w߾LMQ]y\aU:\,k .l˺N})$uڵoPXP y A꯲E\Uq>* j WZYT'ͮUW/sΣ(< "W-ZlgkCijf͚5TAY/{]Z3ā%Y{&)y 7OVߴ])Ool񪛟 q|(ZHtd]kёtp8u8SU#R'OdӢK c< i<^Lt}Qtf\=cהɂ?kjdW &wߤ%d 59wtv9A,VVpƍ>>>666\.}#/fg'&&ŅAH]hK5:"}XБTёY+ܛnxVO+-[ M'J*r%S\bJ$ͦ[}||lmmZūWVreT$p55_U7- .W$ԓQCBvmccYG|~t;wvvp8BP OJPm֝jkbdiMWf{XI(nP0  \.Ço"IRO_;85D+ZBdDtigmNۓ7ouTkx(''_nP([7ol8WNd 6- j<@Q nZvރ#9s>V;}[fe}bVFUe:JFCڈ䁣as8\ 5iiiq55l6 ׅ1 )њEe3!UY)K_ld;yj,f E"H$*0(%"nZb!aE!S~AJ^wCBl.+ba&5vmP4CߴM]5P(lr) q "Q*ǟToCU2V]#ճI{8>&TudVR,ƃ]+OJ0,KԤVA?$gt'0Xl6WCC˥ED5PhZQCSlS1 # B(ԭLϠJ^pCQuMdؾal9`k7A,6*vrtsR j$$NRS (_yP( eoCO0$Ruez3UA?BQ=ؠӫފeø\.լFz꺉O}0ҏaV30L2E2:*I_}hQLPsŝD8Kee%&QAtH.ݞŵfd`bj*yH@?!ab_KGRDJ:}_v2IXWO_k g,M'<:U48UO]]Yk%S]rZv+B|~0Dc#BG"Ƅ@I4B .bhl$݇Ʈz<@ȓz<@ȓz<@ȓz<@ȓz<@ȓz<@ȓz<@l4  z$`a))[sLFtP?@ȓz<@ȓzԞԳ34lB%`~6'qnghsԟu&_ ?CӄNib4M4lo|v~Qj@H^峻/\RDi m|Ob`iгٰͧnin((lB MM=>|X Yv0[[CKۯ_U6Ec6WC06ֺz54;{H}V֒-[zXUivoKlU[i!EulvMFP_X`޴܍;۷hW7ҥ-,t֯5ppҭFvEUV Wre d_jivݎVXZ*ؼ!p8'u;Y?&EDE+U$EdV暷n?,l+K#zRZߤIVVB8`/Q$IP(y[&=`ݯk^^E.KKp^MkϏ{vް[q,,t֕ uu7~}WOO Iߠel|߾fgM~>n?ۼwKoh.{:/::s/9^鹛 (03Y lgg<0pmh-]zNm-I-^X<, EADBKc͞4L]QHuhiKC.F=<u++J' BR $B'Kx_| D2o6mtڐp{hӦcׯ7oՌҹs`Ĉ3cǞoّVVzs^yԩs -e&[<7<{v*+={֭=~.D$mHMMv/77HM-b{{MMg/ͯ`0=0\X#y )r!;[TڴuEEŅ @S Y,6AQPHW K32>foՓuZAl#n}$^pl"|]' ;x0vG2}kVB/tlo`fぁ6WR={:˫;HţVI4wŜ9W?N?`,:B 2 Mٺ$WCq )J(xϟs*Jy<"t YP9PXGWWKpp' p!YOd@zztWٻ d?AaHmPH *PQ x6UZɝD}i~%l )6XBH(J>Ps48Dk ^nE#աG<)֨t,W044r\ˆ`-k&ꫛW&ڶ rJO/9p FΘѶ2+\gG%%|KKݹs߾Ϳ|?t:uZZq.$Ѳ'7[7B^ddFnnņ ݆|$[7Gر`b1\dww5k:x}ۛ7ߧ|ylXzc8 IDAT޽mm \L*/$!u4 ub]eTS3s< R?I,`@6-v,xО-:; )biVcӶ}'ց[ V BS]BBml y7o_vZZ ]}}-B򯿞ϟm?ddն!CYi)…׋]rٹ^}4-4`@uu룻w?.{s˖ʿ%b|c{v*{4Fo-XwSW,^̪fA y֋&$[aʽsߡw@&zf+N#j?|w&$ϼ)g.*i"iBdH! *'i )i@!i'@AD IB@D .fJ (g XO"$^ O"$^ O"$^ O"7Ikh͝7Yq_n]?߿իLv۴nL/XBu۷_=|RCvv6qWyEEEVH$j.Ϗ.h%WRbÛ֭Jp׋d5k+?"A$_3^nlLL6m=<<rO#" ,4͛7ˑQ-ZZV@˿+FݩcN;<}T iࡃ#FTZP^kŃ1'gnXkW+]V#ǮfW/Ċ`GedD@ m/6ӭ[˗ܿwժէϜhkk+iF@8رfdd{UrD XA@6m<pIz*p֭>v}XQzͿFMYX#Azx td c"b8_xx ן]+٣88@~I5j8.!VU$I//#G";u;w-lNOp8*5 b8/at7ՑjB9zJG}iFJi OIaETuwPTDXS a99ySR|}}ϛ?eqdddzz:;ѦM[ }}} {tbcc? ~m߼y{}???߷o{s]]]~1ϞUVVcdw: Aq emEqfJK +oy =+زtwZCNNs= cImlܸqرR֬?n#haa?GQT֭mQgEe!m뗚:uTy666,jkk[R d6!''!C(=}ͥ Ojtu+>h)%֘-:(7ܢfR7*OSGip֋o{@<@ȓz<@ȓz<@ȓz-=3@wE 7AD IB@D IB@D IBhƎ}P(&__8w?vbfkk_MMM8\vVIփHtII@ܷ Xfc `V?..{`hd4lp]]]ȑrs 0440`5\8^ hikL]:߯]{򥴴)}L䒗&@@f {9++Yfpࡋ/_֧O׮9r4铽)PP@yyyFF+\ (G|`Сl6H6!O_[*++̠P(ܲyӌ,,,>}t"k~aa=gΚmllѣC}}}xI|3g=t8adDdD]޽N>ü'sr :z ӁǃJuinV}!)9::@n]?@I:::nnU.%%%KGRR$-S_ Iqq[nì>ӣ@722bV?/7޾yC6$PŞ^кu$VVVH6X, 94uf͊>q45oII.] 9?ڪRIQ}wɚ={9 b޿{ecc>aÇ(D'LdV"??l ˗/_QRRO?}Tqvvvoߞ<6o v ܚq8LPˣF('O>c*R_fj P[ }]|ƍÃ++'E] C =Xm5551OSQQ.9E˖J+aggnn<{yy͜5kܹ-[bXnMi0 k/##]$BBu&` (-#G@T 1`'?}D}I''':~1"bcǍgJ@QK#FEttt߾} YYY&^וOYGGG9eF%%%?g@JJ A9SPD ܼq_~2??.(6&Ԕfz W3!?7Quݵ-tfeeM6577$I77u׻ 3|x*UIV,_vڵcccS32"[El,i@Fw!۩rݕ>>NǎzDd$Baʓ9B@D IB@D IB@D IB~`OQU4~P/'yP/'yP/'yP/ړ򋣃Cb+crrz1bD:‚,,=cL6[n135IHxɸrBx׿/[b2fsrr|}?77W'Gfĉ~ڷ z*p(Aл7Vr43 /_ZZZS_0o}ѕ+VkX 9|(r 3g6-_{ѬѢvy$zժg1\.755Yqss T%Lx<ޒŋnwɓǍ|s vpt@ X~(NIKM۷/9**:aIє@ \mWnN0$%+8;.alu;w4xMC325r222U lSCp0ܻp>3l߾m`[B*1ǏS.UƠMLLvlv}}֭֬b޹chhئe޽BFxΝgV[__8yBGGKM)&'_LylqMSٳȑ x"*2rʔ)J2`@N7?>. ά8IYYY..Q~ذa򤉌/iNøliiԩSNĽߵk/;;8|&M()mmP6tr4QaQQ>|ܩcrss'MCkkL:+((fv0cDEC!i">}]֭Zj3zԨw2>QTT婿?co[[1:th``.9"۷G2}BWgPPII1Sbx&Ls@T&)#>zc33ܹ嵫W~~~=Ja!ЍAh +:c̄WbǙ+ %{f%?F~~бtʇÇL Aq1('DMM͂z=8x `eU7Hm#SUE\>m?pTŎqph Ttc׺$F`j Sj8tАCC|Ҥnnؼe+MtsLHԩST[05WWhndX_$W(jEVVV&Mn Ç~ʹiӧNaX n;mff6c4I22ysHN!8 .B_3djgiiU˕ 󻑠(jeeev0Jq|ĉ|>A&igga֬Yccc{{cvCCx"d5cI6z`m gtd@Q%K s?b\ÇtAŅh젛 =Š89tW<p'_]``nn>}5g@v6 Uaau+AXvde>999Ç Pd&2eѣG"##[j=x f+**MyeVYꗔ}Tؿ!$SS~bI&@%*$ O20KP7'yP/'yP/'yP/'yP/'yP/U@]HFA$^ O"׌%YXP0yҤmL49''G%麱ƦTe2S6e3k3/++ +*=(ll3Uv4'bI9nODFEn޴I%--!=o4=Z y<ޒŋ}iD;ϛˬ~jJO>իw˖*-./,_aÆ-Zq}bmg2&Iِ `aKÐ?CH̜^4$KrÏ)&&ݺug,k.YY*б#^Ep(LV)T@`8"eyy93['''Gƍ@9q:,%}{t )>eff 8BC>())aP_N1/Pe07 tq"IЯFwUK G0c}A@8ݻ/d(]]]wyf=<޿ꕧׅ BavVJΗ KneUup\ctOOOLJ{7U .ܿπ~$)ZjUfc6ZÇa$UKKKNv"E]Ǐt?&?5oN:jiiaJWc*LJQ0}:Y5gBN̟#G0r,IVZ]^^}ǎƺ;׮8 ꕶ@ *))V2Lߝ>5z4ñVaT7,uff+,2,`ƌRR83f@Tx*0f,I[]QQ8~ܜqa{W "@ `C$M^^^~ݺI'2R:88Z6kv)<?Ϡ>gP3!?@,UXUkͫ~÷5J%q됧WBBLMMl6;'eS UXh,,>..p $ ^^s'(l$Ix <4($Baʓ9B@D IB@D IB@D IB`U;hbPB}AD IB@D IB@D ԋKƍ.0m6CX4qqJ [ةcOw_o_1/[b2fsrrx:9:0^bmg*&Щ|~$m۞FD<8pV)/-/ BLA-~)eB˄aÆ3zժg1/^͙;Y}sss/Ə?t0f)SXrF+V@b"ԩ*7K2H]Ð P saٰ /++;yM\.1/ SN eVX2 +lÇL Bq ǒ5kVDD(F* 0f t1!!|>? `Ӧ dmwnnݺ1/;mڴQQ\J1bmJ3kS*ldJ VSSHMB1_?޽{cbb&L4wlU[CqIDATÜ9K-<v"*::2*li$ &OXXXliNRe3kSJXM6R&_9dU%p6e֟XUHJ_P&(Ē,..PuI'''z 2c.\|p*ꥬ =d蛚޺u^/,,tvvfPٳ]v555S)Lڔ6YNTtɺTTT̛;'77$I77}?XX@u,@x2#fff[갅%%%#G –Zۧ|e]Ν֟8}0aaa+WT ---m1q:}0[-7cm\ur=&loXLW;o÷T \bI"J,I "bI"j$^ O"E5<@ȓz<@ȓz<@ȓz<@ȓz<@M/ΝW^RAR`o{@۶Ks/_կ뉉̝knj )***Ο?EC~Iȑrs 0440`Ν߻7klKe_e]TTTo0ǗY}HtII@fӤKR^2>\z_a̪C¢Exm<<<ڵo` /aˇ(W~ƍ#-baɓKg̜Ŭ>deffffҥ4&OV16] .Zfy|X2n,@ر&9踹5qqWגƋW(F \}И4y m|1Hdkg׷o?f{ٳg 5cI*0$ (~Q_Ͽ1G|ʀӧN1$c_pgϞaVS ԩKRqI%BQԥp>_0t0ɸ̂aX۶~"AٔOyy˯(++;v1q8;dc3'-ZfD4uɓTmxC^;wdb~PuRxEeȑ(ʊ bcbLMMSk׾]BC2{U |===$=V'-dcgo-ZdeeUVV2[bf,I& pxgO07xFJrׯʎ9:M]={/;ؘ)P(\C'3Q޼WPϸCQ2z{1!!eEPPӃw~~۷mk`DV4 U ֹii۾mK( ">JLL'v9<G  ,^\VVknqġ'{LDEF?~s._^t!0 /9ܐLq}vY2I>>>t|1<<3 {wÇݻuڄAԓ͚_EGp\<;;;8\[T# I͆*0Ñ Z!\ZF)ԋQE!\"a6$BA!%!}Ke.?v}}eiݺRv͗r:iSSS]]]jjjII֮u8R<^h{ɓ?|dff:c*R~mhhȑ#˵~]Jw9q]:w߯offy>|PG}Ĉ>qBk…VW/PSS;>}~x‰Xyp„ֵSN :$.vfLuun]QYy4cŎ5ʪ]q^ \Q|\luuuCCcr&㓟=;ߙKe"͖ݻ2Rgdk?SSF=a/l~W^0~܅ ܗ:$upǬ3ֻwH睷K~syo84(ߺem-[ ǮI0'h=]؆]U77|( حcW@ 9 &Yh&Yh&Yh&YB7zK[WydI@dI@dI@dI@dI@dI@dI@dI@dI@W0&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh&Yh%Ībbb{mՀ͢0&&ƪ,k2--M)UXXhՀ"&&eM*,2I@dI@dI@dI@d3%%%Ǐp@0#!s<Ϟo[Œ/;˚<~ԩS^LȆ 0#tZkuxNzYQw>2,>sRm۶(??ʔ'c>n A}}ƍ999^qHt\sTvUYyG)eFU0n IUG|`f.I+4&:vâcGbUHnJ)u '!v]{Mj H%(pXҢcbͳ6mקي.^8rHTccc]]]ǎR/bK7q\~OwsNJyAmuWE/C)OVX١Cdjj%TIscǖWbsgΜYk֬oN>m۶9M6i*=t߽~iZԼʫ7tu z%oiӦN:-_1_4dǝb.\[[{O=ڵkWZyf+ji$I݋cp!gö0̙,2ϖ:ϝ??w\sg{==SVV6}> 1a`Ν۷o{- a%%u01c,YfرaFP%CCCw9K|J)id0Vw~TTkvfw&_҃<eGKѷoe˖]IvmwQņݾxbgi#qFj/?j~V:8$ļ088}%ySRӯkyRt7U;:ozV)e8Ahg{Ͻ3f^v#2_|{5% JRJOAAArZi>|)7PyZ+.6WqIqg˛X=tcc͛<8nؖvps:AvdD/ZGGG}uTToo74h^XP)l_%ͮN%.JHLz#Vcj׮]͛OEFF.|aֺ[nӦ͚9[QQ?K^g^N JֽR70nږn kǴ=6n Kn-] Vݲ|yW=y5ed =\׮Y8,۷70ՁGrTJh[?"mCs=? #RyֺmC۷1f$0|r'&&&?L#H27aY5 ]߄=,4 B,4 B,4 B,4 B,4 B,4 B,4 B,4 r$p\ʋ l@*"""11184\)&K?Mni.]v!M?t#,Hk=mAA7z[劈0_&&&)]DzTXpj{ #""vuGT-΋hdgm+ۅ Uɳ {])ݤi_q[܊:Ye*IENDB`examples/organizer/todo/doc/src/000077500000000000000000000000001233466112000171735ustar00rootroot00000000000000examples/organizer/todo/doc/src/todo.qdoc000066400000000000000000000301331233466112000210100ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \example todo \title ToDo Example The ToDo example shows how to organize todo items using the Qt Organizer framework. \image todoexample.png Most organizing software, e.g., calendar applications, lets the user create todo items, which describe an activity that should be completed. Other items may include meetings, notes, and events. A todo item typically includes the following information: \list \li A timestamp for when the item was created. \li A timestamp for when the activity should be completed. \li A timestamp for when the activity was completed. \li A priority for how important the activity is. \li Information on whether the todo is recurring (i.e., if it should be repeated at regular intervals). \li A description of the activity. \endlist A todo item is represented in Qt with the QOrganizerTodo class. Instances are managed by a QOrganizerManager, which can save todos created by a program and return the todo items it manages. QOrganizerTodo contains the information mentioned in the list above. In Qt, we call this item details. They are represented by QOrganizerItemDetail and its subclasses. For instance, QOrganizerTodo keeps a QOrganizerItemPriority (which inherits QOrganizerItemDetail). The item details available for a QOrganizerTodo follows a standardized schema, i.e, a todo item has a standard set of item details. Most \l{QOrganizerManager} backends will follow this schema. A backend is the implementation of the \l{QOrganizerManager}'s functionality for a specific platform. Some backends may not support all details, and possibly include others. The example consists of two classes: \list \li \c Window: Lets the user select a date and create todo items for the date selected. It also displays a list with todo items for the date selected. \li \c TodoEditor: Lets the user edit a todo item using standard Qt widgets. \endlist We will now look at the definitions and implementations of \c Window and \c TodoEditor. \section1 Window Class Definition The \c Window class is responsible for setting up the GUI of the example. It creates QOrganizerTodo items and send them to the TodoEditor for editing. It saves and retrieves todo items from the organizer item manager. Let's take a look at its definition. \snippet examples/todo/window.h 0 The slots are connected to the widgets of \c Window, and handles user requests to create a new todo item, edit an existing item, and delete an item. The \c saveTodo() slot is invoked when the user has finished editing a todo item. \c refreshList() updates the \gui {Todo Item List} when todo items are added, deleted, or edited. We'll now go through the slots and constructor of \c Window. The only other function, \c setupGui(), initializes and lays out the widgets, and that is treated in other examples. \section1 Window Class Implementation The constructor creates the QOrganizerManager instance: \snippet examples/todo/window.cpp 0 We here instruct that the manger should use the \c memory backend. This backend implements the default schema and uses the computers memory for storing items. This way, we can be sure that the backend will behave equally on all platforms. The \c editNewTodo() slot is connected to the \gui {New Todo Button}, and sets up a new QOrganizerTodo for editing. \snippet examples/todo/window.cpp 1 Here we set the item details of the new QOrganizerTodo to reasonable defaults. The \c editTodo() slot sets up the widgets of the \c TodoEditor with the data from the new todo. Finally, the stacked widget is set to show the todo editor. The \c editTodo() slot is invoked when the player double clicks a todo item in the \gui { Todo Item List } with the mouse. \snippet examples/todo/window.cpp 2 \omit Should I mention item view roles as well? \endomit The slot is invoked with the QListWidgetItem that was double clicked. We have saved the QOrganizerTodo in the list widget item. The list widget item stores data in \l{QVariant}s, so we need to include the Q_DECLARE_METATYPE() macro, which helps make \l{QOrganizerTodo}s usable with QVariant. When we have retrieved the todo item, we send it to the \c TodoEditor for editing, which we show on the screen. The \c saveTodo() slot is invoked by the \c TodoEditor when the user has finished editing. \snippet examples/todo/window.cpp 3 Saving a QOrganizerTodo in the QOrganizerManager is easy using the \l{QOrganizerManager::}{saveItem()} function. We call the \c refreshList() slot to update the \gui { Todo Item List } so that new and edited todos is displayed correctly. The \c deleteTodo() slot is connected to the \gui {Delete Todo Button}, and will delete the currently selected todo in the \gui { Todo List } from the manager. \snippet examples/todo/window.cpp 4 Here we fetch the selected list widget item from the list. To delete the item in the manager, we send the items \l{QOrganizerItem::id()}{id} to the manager's \l{QOrganizerManager::}{removeItem()} function. An item's id uniquely identifies it in its manager. We now move on to the \c refreshList() function, which set's up the \gui { Todo List } with the todo items currently stored in the manager. \snippet examples/todo/window.cpp 5 First we remove all items from the list widget, i.e., we set up the list from scratch each time \c refreshList() is called. The \l{QOrganizerManager::}{items()} functions retrieves \l{QOrganizerItem}s from the manager. By giving the manager a QOrganizerItemSortOrder, the manager will sort the items for us. The sort order takes the item detail it should sort after. You also need to specify which field of the detail should be used for sorting. Note that all details have a DefinitionName constant declared. They also keep constants for all of their fields. The \l{QOrganizerManager::}{items()} takes a list of sort orders in case one wants to sort by more than one field. It is also possible to let the manager filter items. You can look up the QOrganizerItemFilter class description for details. \snippet examples/todo/window.cpp 6 We iterate through the todo items in the manager, keeping the items that are active, i.e., the date selected in the calendar is between the start and due dates of the item. We create a list widget item for the todo. We set its text to the item's start sate, due date, and \l{QOrganizerItem::}{displayLabel()}. We save the QOrganizerTodo itself in the Qt::UserRole of the list widget item. We have seen previously how to retrieve it. \section1 TodoEditor Class Definition The \c TodoEditor contains widgets for editing a QOrganizerTodo. \image todoeditor.png Here is the \c TodoEditor class's definition: \snippet examples/todo/todoeditor.h 0 The \c editTodo() slot is called by \c Window when a todo item should be edited. \c finishEditing() is connected to \c doneButton, and emits the \c editingFinished() signal. This signal is connected to the \c saveTodo() slot of the \c Window. The rest of slots are connected to the widgets that edit the todo item's details. \c setupGui() creates, lays out, and connects the widgets to the slots of \c TodoEditor. \c setupCombos() helps \c setupGui() by creating the comboboxes and by filling their drop-down lists. \section1 TodoEditor Class Implementation We start by taking a quick look at \c setupCombos(), which sets up the \l{QComboBox}es. \snippet examples/todo/todoeditor.cpp 0 As with list widget items, you can also store user data in an item of QComboBox's drop-down list. Here we save a \l{QOrganizerTodo}'s possible values for its \l{QOrganizerTodo::}{priority()} and \l{QOrganizerTodo::}{status()} details. The \c alarmCombo helps the user select a time for when to be reminded of the todo. The \c editTodo() slot is called when a new QOrganizerTodo should be edited. \snippet examples/todo/todoeditor.cpp 1 We set the contents of our widgets to the details of the todo item. The functions we use here are utility functions provided by QOrganizerTodo that accesses the \l{QOrganizerItemDetail}s for us. We could also have accessed them by using the \l{QOrganizerItemDetail::}{value()} functions of QOrganizerItemDetail. \snippet examples/todo/todoeditor.cpp 2 Many backends support notifying the user when a todo item is due. We can request this by adding a QOrganizerItemRemainder detail to the QOrganizerTodo. We first check whether a remainder detail is present on the todo item. If it is we update the \gui {Alarm Combo Box}. The \l{QDateTime::}{secsTo()} function returns the difference between two \l{QDateTime}s in seconds. The next two slots update the subject and description of the todo item. \snippet examples/todo/todoeditor.cpp 3 We save the subject in the item's \l{QOrganizerItem::}{displayLabel()}, which is meant for displaying a short description that can be used in item views. The \l{QOrganizerItem::}{description()} is a longer text describing the item. The \c updateDates() slot is connected to the two \l{QDateTimeEdit}s that let the user select start and due dates for the todo item. \snippet examples/todo/todoeditor.cpp 4 Note that we need to update the remainder detail when the due date changes because the remainder is calculated relative to the due date. We do that in \c updateAlarm(), which we will come back to later. The \c updateStatus() and \c updatePriority() functions are connected to the combo boxes that we created in \c setupCombos(). \snippet examples/todo/todoeditor.cpp 5 The only thing to notice here is that enum values are saved as \c {int}s in the drop-down list items. The \c updateAlarm() function is connected to the \c alarmCombo as we saw earlier. \snippet examples/todo/todoeditor.cpp 6 We first calculate the time before the todo is due the alarm should go off. We calculate this in seconds because \l{QDateTime}'s \l{QDateTime::}{addSecs()} function gives us an easy way of finding the time from the todo's due time. Before we add the new reminder, we need to remove any previously added reminders; if not, the QOrganizerTodo item would have several \l{QOrganizerItemVisualReminder}s registered with it. The reminder is not accessible through the convenience functions of QOrganizerTodo, so we add it using the item detail access functions from QOrganizerItem. */ examples/organizer/todo/main.cpp000077500000000000000000000041741233466112000173000ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include "window.h" int main(int argv, char **args) { QApplication app(argv, args); Window window; window.show(); return app.exec(); } examples/organizer/todo/todo.pro000066400000000000000000000002551233466112000173300ustar00rootroot00000000000000TEMPLATE = app TARGET = todo QT += organizer widgets HEADERS += todoeditor.h \ window.h SOURCES += window.cpp \ todoeditor.cpp \ main.cpp examples/organizer/todo/todoeditor.cpp000066400000000000000000000165671233466112000205360ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include "todoeditor.h" QTORGANIZER_USE_NAMESPACE TodoEditor::TodoEditor() { setupGui(); } //! [1] void TodoEditor::editTodo(const QOrganizerTodo &newTodo) { todo = newTodo; subjectLineEdit->setText(todo.displayLabel()); startDateEdit->setDateTime(todo.startDateTime()); dueDateEdit->setDateTime(todo.dueDateTime()); priorityCombo->setCurrentIndex( priorityCombo->findData(QVariant(todo.priority()))); statusCombo->setCurrentIndex( statusCombo->findData(QVariant(todo.status()))); descriptionTextEdit->setText(todo.description()); //! [1] //! [2] if (!todo.details(QOrganizerItemDetail::TypeVisualReminder).isEmpty()){ QOrganizerItemVisualReminder reminder = todo.detail(QOrganizerItemDetail::TypeVisualReminder); int seconds = reminder.secondsBeforeStart(); alarmCombo->setCurrentIndex(seconds/(15*60)); } else alarmCombo->setCurrentIndex(0); } //! [2] //! [3] void TodoEditor::updateSubject() { todo.setDisplayLabel(subjectLineEdit->text()); } void TodoEditor::updateDescription() { todo.setDescription(descriptionTextEdit->toPlainText()); } //! [3] //! [4] void TodoEditor::updateDates() { QDateTime startTime = startDateEdit->dateTime(); QDateTime dueDateTime = dueDateEdit->dateTime(); todo.setStartDateTime(startTime); todo.setDueDateTime(dueDateTime); updateAlarm(alarmCombo->currentIndex()); } //! [4] //! [5] void TodoEditor::updateStatus(int index) { QOrganizerTodoProgress::Status status = (QOrganizerTodoProgress::Status) statusCombo->itemData(index).toInt(); todo.setStatus(status); } void TodoEditor::updatePriority(int index) { QOrganizerItemPriority::Priority priority = (QOrganizerItemPriority::Priority) priorityCombo->itemData(index).toInt(); todo.setPriority(priority); } //! [5] //! [6] void TodoEditor::updateAlarm(int index) { int seconds = index * (15*60); QDateTime dueDate = todo.dueDateTime(); QOrganizerItemVisualReminder oldReminder = todo.detail(QOrganizerItemDetail::TypeVisualReminder); todo.removeDetail(&oldReminder); if (seconds == 0) return; QOrganizerItemVisualReminder reminder; reminder.setSecondsBeforeStart(seconds); todo.saveDetail(&reminder); } //! [6] void TodoEditor::finishEditing() { emit editingFinished(todo); } void TodoEditor::setupGui() { startDateEdit = new QDateTimeEdit; dueDateEdit = new QDateTimeEdit; subjectLineEdit = new QLineEdit; descriptionTextEdit = new QTextEdit; doneButton = new QPushButton(tr("Done")); setupCombos(); connect(startDateEdit, SIGNAL(editingFinished()), this, SLOT(updateDates())); connect(dueDateEdit, SIGNAL(editingFinished()), this, SLOT(updateDates())); connect(subjectLineEdit, SIGNAL(editingFinished()), this, SLOT(updateSubject())); connect(descriptionTextEdit, SIGNAL(textChanged()), this, SLOT(updateDescription())); connect(statusCombo, SIGNAL(activated(int)), this, SLOT(updateStatus(int))); connect(priorityCombo, SIGNAL(activated(int)), this, SLOT(updatePriority(int))); connect(alarmCombo, SIGNAL(activated(int)), this, SLOT(updateAlarm(int))); connect(doneButton, SIGNAL(clicked()), this, SLOT(finishEditing())); QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(new QLabel(tr("Subject:"))); layout->addWidget(subjectLineEdit); layout->addWidget(new QLabel(tr("Start Date:"))); layout->addWidget(startDateEdit); layout->addWidget(new QLabel(tr("Due Date:"))); layout->addWidget(dueDateEdit); layout->addWidget(new QLabel(tr("Status:"))); layout->addWidget(statusCombo); layout->addWidget(new QLabel(tr("Priority:"))); layout->addWidget(priorityCombo); layout->addWidget(new QLabel(tr("Alarm:"))); layout->addWidget(alarmCombo); layout->addWidget(new QLabel(tr("Description"))); layout->addWidget(descriptionTextEdit); layout->addWidget(doneButton); setLayout(layout); } //! [0] void TodoEditor::setupCombos() { priorityCombo = new QComboBox; priorityCombo->addItem("Unknown", QOrganizerItemPriority::UnknownPriority); priorityCombo->addItem("Highest", QOrganizerItemPriority::HighestPriority); priorityCombo->addItem("Extremely high", QOrganizerItemPriority::ExtremelyHighPriority); priorityCombo->addItem("Very high", QOrganizerItemPriority::VeryHighPriority); priorityCombo->addItem("High", QOrganizerItemPriority::HighPriority); priorityCombo->addItem("Medium", QOrganizerItemPriority::MediumPriority); priorityCombo->addItem("Low", QOrganizerItemPriority::LowPriority); priorityCombo->addItem("Very low", QOrganizerItemPriority::VeryLowPriority); priorityCombo->addItem("Extremely low", QOrganizerItemPriority::ExtremelyLowPriority); priorityCombo->addItem("Lowest", QOrganizerItemPriority::LowestPriority); statusCombo = new QComboBox; statusCombo->addItem("Not started", QOrganizerTodoProgress::StatusNotStarted); statusCombo->addItem("In progress", QOrganizerTodoProgress::StatusInProgress); statusCombo->addItem("Complete", QOrganizerTodoProgress::StatusComplete); alarmCombo = new QComboBox; QStringList alarmList; alarmList << "None" << "15 minutes" << "30 minutes" << "45 minutes" << "1 hour"; alarmCombo->addItems(alarmList); } //! [0] examples/organizer/todo/todoeditor.h000066400000000000000000000057311233466112000201720ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef TODOEDITOR_H #define TODOEDITOR_H #include #include QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDateTimeEdit; class QComboBox; class QLineEdit; class QTextEdit; class QPushButton; QT_END_NAMESPACE //! [0] class TodoEditor : public QWidget { Q_OBJECT public: TodoEditor(); signals: void editingFinished(QOrganizerTodo &todo); public slots: void editTodo(const QOrganizerTodo &todo); private slots: void updateSubject(); void updateDescription(); void updateDates(); void updateStatus(int index); void updatePriority(int index); void updateAlarm(int index); void finishEditing(); private: void setupGui(); void setupCombos(); QOrganizerTodo todo; QDateTimeEdit *startDateEdit; QDateTimeEdit *dueDateEdit; QComboBox *statusCombo; QComboBox *priorityCombo; QComboBox *alarmCombo; QLineEdit *subjectLineEdit; QTextEdit *descriptionTextEdit; QPushButton *doneButton; }; //! [0] #endif examples/organizer/todo/window.cpp000066400000000000000000000150761233466112000176630ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include "window.h" #include "todoeditor.h" QTORGANIZER_USE_NAMESPACE //! [0] Window::Window() { setupGui(); manager = new QOrganizerManager("memory"); setWindowTitle(tr("ToDo Example")); refreshList(); } //! [0] Window::~Window() { delete manager; } //! [1] void Window::editNewTodo() { QOrganizerTodo newTodo; newTodo.setPriority(QOrganizerItemPriority::HighPriority); newTodo.setStatus(QOrganizerTodoProgress::StatusNotStarted); QDateTime currentDateTime(calendarWidget->selectedDate(), QTime::currentTime()); newTodo.setStartDateTime(currentDateTime); newTodo.setDueDateTime(currentDateTime.addSecs(60*60)); todoEditor->editTodo(newTodo); stackedWidget->setCurrentWidget(todoEditor); } //! [1] //! [2] Q_DECLARE_METATYPE(QOrganizerTodo) void Window::editTodo(QListWidgetItem *item) { QVariant variant = item->data(Qt::UserRole); if (!variant.canConvert()) return; QOrganizerTodo todo = variant.value(); todoEditor->editTodo(todo); stackedWidget->setCurrentWidget(todoEditor); } //! [2] //! [3] void Window::saveTodo(QOrganizerTodo &todo) { manager->saveItem(&todo); stackedWidget->setCurrentIndex(0); refreshList(); } //! [3] //! [4] void Window::deleteTodo() { QList items = listWidget->selectedItems(); if (!items.isEmpty()) { QVariant variant = items.at(0)->data(Qt::UserRole); if (variant.canConvert()) { QOrganizerTodo theTodo = variant.value(); manager->removeItem(theTodo.id()); refreshList(); } } } //! [4] //! [5] void Window::refreshList() { listWidget->clear(); QOrganizerItemSortOrder sortOrder; sortOrder.setDetail(QOrganizerItemDetail::TypeTodoTime, QOrganizerTodoTime::FieldDueDateTime); QList items = manager->items(QDateTime(), QDateTime(), QOrganizerItemFilter(), -1, QList() << sortOrder); //! [5] if (items.isEmpty()) { new QListWidgetItem("", listWidget); } //! [6] foreach(QOrganizerItem item, items) { if (item.type() == QOrganizerItemType::TypeTodo) { QOrganizerTodo todo = static_cast(item); if (todo.startDateTime() > QDateTime(calendarWidget->selectedDate(), QTime(23,59)) || todo.dueDateTime() < QDateTime(calendarWidget->selectedDate(), QTime(0, 0))) continue; QString display = todo.startDateTime().toString("yy/MM/dd hh:mm") + "-" + todo.dueDateTime().toString("yy/MM/dd hh:mm") + " - "+ todo.displayLabel(); QListWidgetItem *listItem = new QListWidgetItem(display, listWidget); listItem->setData(Qt::UserRole, QVariant::fromValue(todo)); } } } //! [6] void Window::setupGui() { todoEditor = new TodoEditor; listWidget = new QListWidget; stackedWidget = new QStackedWidget; newTodoButton = new QPushButton(tr("New Todo")); deletTodoButton = new QPushButton(tr("Delete Todo")); calendarWidget = new QCalendarWidget; connect(newTodoButton, SIGNAL(clicked()), this, SLOT(editNewTodo())); connect(todoEditor, SIGNAL(editingFinished(QOrganizerTodo &)), this, SLOT(saveTodo(QOrganizerTodo &))); connect(listWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(editTodo(QListWidgetItem*))); connect(calendarWidget, SIGNAL(selectionChanged()), this, SLOT(refreshList())); connect(deletTodoButton, SIGNAL(clicked()), this, SLOT(deleteTodo())); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(calendarWidget); mainLayout->addWidget(listWidget); QHBoxLayout *buttonLayout = new QHBoxLayout; buttonLayout->addWidget(newTodoButton); buttonLayout->addWidget(deletTodoButton); mainLayout->addLayout(buttonLayout); QWidget *frontPage = new QWidget; frontPage->setLayout(mainLayout); stackedWidget->addWidget(frontPage); stackedWidget->addWidget(todoEditor); // Adding a scroll area to allow proper rendering of the UI on small screens QScrollArea *scrollArea = new QScrollArea; scrollArea->setWidgetResizable(true); scrollArea->setWidget(stackedWidget); QSize suggestedSize; suggestedSize = stackedWidget->sizeHint(); stackedWidget->setMinimumSize(suggestedSize); QGridLayout *layout = new QGridLayout; layout->addWidget(scrollArea); setLayout(layout); } examples/organizer/todo/window.h000066400000000000000000000055551233466112000173310ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef WINDOW_H #define WINDOW_H #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManager; class QOrganizerTodo; QT_END_NAMESPACE_ORGANIZER QTORGANIZER_USE_NAMESPACE class TodoEditor; QT_BEGIN_NAMESPACE class QListWidget; class QStackedWidget; class QPushButton; class QListWidgetItem; class QCalendarWidget; QT_END_NAMESPACE //![0] class Window : public QWidget { Q_OBJECT public: Window(); ~Window(); private slots: void editNewTodo(); void editTodo(QListWidgetItem *item); void saveTodo(QOrganizerTodo &todo); void refreshList(); void deleteTodo(); private: void setupGui(); QOrganizerManager *manager; TodoEditor *todoEditor; QListWidget *listWidget; QStackedWidget *stackedWidget; QPushButton *newTodoButton; QPushButton *deletTodoButton; QCalendarWidget *calendarWidget; }; //![0] #endif qtpim.pro000066400000000000000000000000201233466112000127200ustar00rootroot00000000000000load(qt_parts) src/000077500000000000000000000000001233466112000116435ustar00rootroot00000000000000src/contacts/000077500000000000000000000000001233466112000134615ustar00rootroot00000000000000src/contacts/contacts.pro000066400000000000000000000037441233466112000160310ustar00rootroot00000000000000TARGET = QtContacts QT = core-private MODULE_PLUGIN_TYPES = \ contacts QMAKE_DOCS = $$PWD/doc/qtcontacts.qdocconf include(details/details.pri) include(engines/engines.pri) include(filters/filters.pri) include(requests/requests.pri) PUBLIC_HEADERS += \ qcontact.h \ qcontactabstractrequest.h \ qcontactaction.h \ qcontactactiondescriptor.h \ qcontactactionfactory.h \ qcontactactiontarget.h \ qcontactchangeset.h \ qcontactdetail.h \ qcontactfetchhint.h \ qcontactfilter.h \ qcontactid.h \ qcontactmanager.h \ qcontactmanagerengine.h \ qcontactmanagerenginefactory.h \ qcontactobserver.h \ qcontactrelationship.h \ qcontactsortorder.h \ qcontactsglobal.h \ qcontacts.h \ qcontactengineid.h \ PRIVATE_HEADERS += \ qcontact_p.h \ qcontactabstractrequest_p.h \ qcontactactiondescriptor_p.h \ qcontactactionmanager_p.h \ qcontactactiontarget_p.h \ qcontactchangeset_p.h \ qcontactdetail_p.h \ qcontactfetchhint_p.h \ qcontactfilter_p.h \ qcontactmanager_p.h \ qcontactrelationship_p.h \ qcontactsortorder_p.h \ qcontactspluginsearch_p.h SOURCES += \ qcontact.cpp \ qcontactabstractrequest.cpp \ qcontactaction.cpp \ qcontactactiondescriptor.cpp \ qcontactactionfactory.cpp \ qcontactactionmanager_p.cpp \ qcontactactiontarget.cpp \ qcontactchangeset.cpp \ qcontactdetail.cpp \ qcontactfetchhint.cpp \ qcontactfilter.cpp \ qcontactid.cpp \ qcontactmanager_p.cpp \ qcontactmanager.cpp \ qcontactmanagerengine.cpp \ qcontactmanagerenginefactory.cpp \ qcontactobserver.cpp \ qcontactrelationship.cpp \ qcontactsortorder.cpp \ qcontactengineid.cpp \ qtHaveModule(jsondb) { isEmpty(CONTACTS_DEFAULT_ENGINE): CONTACTS_DEFAULT_ENGINE=jsondb } !isEmpty(CONTACTS_DEFAULT_ENGINE): DEFINES += Q_CONTACTS_DEFAULT_ENGINE=$$CONTACTS_DEFAULT_ENGINE HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS load(qt_module) src/contacts/details/000077500000000000000000000000001233466112000151065ustar00rootroot00000000000000src/contacts/details/details.pri000066400000000000000000000020331233466112000172450ustar00rootroot00000000000000INCLUDEPATH += details \ ./ PUBLIC_HEADERS += \ details/qcontactaddress.h \ details/qcontactanniversary.h \ details/qcontactavatar.h \ details/qcontactbirthday.h \ details/qcontactdetails.h \ details/qcontactdisplaylabel.h \ details/qcontactemailaddress.h \ details/qcontactfamily.h \ details/qcontactfavorite.h \ details/qcontactgender.h \ details/qcontactgeolocation.h \ details/qcontactglobalpresence.h \ details/qcontactguid.h \ details/qcontacthobby.h \ details/qcontactname.h \ details/qcontactnickname.h \ details/qcontactnote.h \ details/qcontactonlineaccount.h \ details/qcontactorganization.h \ details/qcontactphonenumber.h \ details/qcontactpresence.h \ details/qcontactringtone.h \ details/qcontactsynctarget.h \ details/qcontacttag.h \ details/qcontacttimestamp.h \ details/qcontacttype.h \ details/qcontacturl.h \ details/qcontactversion.h \ details/qcontactextendeddetail.h SOURCES += details/qcontactdetails.cpp src/contacts/details/qcontactaddress.h000066400000000000000000000075021233466112000204450ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTADDRESS_H #define QCONTACTADDRESS_H #include QT_BEGIN_NAMESPACE_CONTACTS class QContactFilter; /* Leaf class */ class Q_CONTACTS_EXPORT QContactAddress : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactAddress) #else static const DetailType Type; #endif enum AddressField { FieldStreet = 0, FieldLocality, FieldRegion, FieldPostcode, FieldCountry, FieldSubTypes, FieldPostOfficeBox }; enum SubType { SubTypeParcel = 0, SubTypePostal, SubTypeDomestic, SubTypeInternational }; void setStreet(const QString& street) {setValue(FieldStreet, street);} QString street() const {return value(FieldStreet).toString();} void setLocality(const QString& locality) {setValue(FieldLocality, locality);} QString locality() const {return value(FieldLocality).toString();} void setRegion(const QString& region) {setValue(FieldRegion, region);} QString region() const {return value(FieldRegion).toString();} void setPostcode(const QString& postcode) {setValue(FieldPostcode, postcode);} QString postcode() const {return value(FieldPostcode).toString();} void setCountry(const QString& country) {setValue(FieldCountry, country);} QString country() const {return value(FieldCountry).toString();} void setPostOfficeBox(const QString& postOfficeBox) {setValue(FieldPostOfficeBox, postOfficeBox);} QString postOfficeBox() const {return value(FieldPostOfficeBox).toString();} void setSubTypes(const QList &subTypes) {setValue(FieldSubTypes, QVariant::fromValue(subTypes));} QList subTypes() const {return value< QList >(FieldSubTypes);} // Convenience filter static QContactFilter match(const QString& subString); }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTADDRESS_H src/contacts/details/qcontactanniversary.h000066400000000000000000000070011233466112000213530ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTANNIVERSARY_H #define QCONTACTANNIVERSARY_H #include #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactAnniversary : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactAnniversary) #else static const DetailType Type; #endif enum AnniversaryField { FieldCalendarId = 0, FieldOriginalDate, FieldEvent, FieldSubType }; enum SubType { SubTypeWedding = 0, SubTypeEngagement, SubTypeHouse, SubTypeEmployment, SubTypeMemorial }; void setOriginalDate(const QDate& date) {setValue(FieldOriginalDate, date);} QDate originalDate() const {return value(FieldOriginalDate);} void setOriginalDateTime(const QDateTime& dateTime) {setValue(FieldOriginalDate, dateTime);} QDateTime originalDateTime() const {return value(FieldOriginalDate);} void setCalendarId(const QString& calendarId) {setValue(FieldCalendarId, calendarId);} QString calendarId() const {return value(FieldCalendarId).toString();} void setEvent(const QString& event) {setValue(FieldEvent, event);} QString event() const {return value(FieldEvent).toString();} void setSubType(const QContactAnniversary::SubType &subType) {setValue(FieldSubType, static_cast(subType));} QContactAnniversary::SubType subType() const {return static_cast(value(FieldSubType));} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTANNIVERSARY_H src/contacts/details/qcontactavatar.h000066400000000000000000000052751233466112000203030ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTAVATAR_H #define QCONTACTAVATAR_H #include #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactAvatar : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactAvatar) #else static const DetailType Type; #endif enum AvatarField { FieldImageUrl = 0, FieldVideoUrl }; void setImageUrl(const QUrl& imageUrl) {setValue(FieldImageUrl, imageUrl);} QUrl imageUrl() const {return value(FieldImageUrl);} void setVideoUrl(const QUrl& videoUrl) {setValue(FieldVideoUrl, videoUrl);} QUrl videoUrl() const {return value(FieldVideoUrl);} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTAVATAR_H src/contacts/details/qcontactbirthday.h000066400000000000000000000055671233466112000206370ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTBIRTHDAY_H #define QCONTACTBIRTHDAY_H #include #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactBirthday : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactBirthday) #else static const DetailType Type; #endif enum BirthdayField { FieldBirthday = 0, FieldCalendarId }; void setDate(const QDate& date) {setValue(FieldBirthday, date);} QDate date() const {return value(FieldBirthday);} void setDateTime(const QDateTime& dateTime) {setValue(FieldBirthday, dateTime);} QDateTime dateTime() const {return value(FieldBirthday);} void setCalendarId(const QString& calendarId) {setValue(FieldCalendarId, calendarId);} QString calendarId() const {return value(FieldCalendarId).toString();} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTBIRTHDAY_H src/contacts/details/qcontactdetails.cpp000066400000000000000000002137061233466112000210050ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactdetails.h" #include #include "qcontactmanager.h" #include "qcontactfilter.h" #include "qcontacts.h" /* When these conditions are satisfied, QStringLiteral is implemented by gcc's statement-expression extension. However, in this file it will not work, because "statement-expressions are not allowed outside functions nor in template-argument lists". Fall back to the less-performant QLatin1String in this case. */ #if defined(QStringLiteral) && defined(QT_UNICODE_LITERAL_II) && defined(Q_CC_GNU) && !defined(Q_COMPILER_LAMBDA) # undef QStringLiteral # define QStringLiteral QLatin1String #endif QT_BEGIN_NAMESPACE_CONTACTS /* template docs: XXXX::FieldYYYY: The field key constant for the YYYY value. \sa yyyy(), setYyyy() XXXX::Type: The enum constant for the type identifier of QContactXXXX details. XXXX::FieldSubType The field key constant for the field that stores the sub type of a XXXX. \sa subType(), setSubType() XXXX::SubTypeYYYY The predefined enum constant for a sub type value, indicating blah blah blah. \sa subTypes(), setSubTypes() */ /* ==================== QContactSyncTarget ======================= */ /*! \class QContactSyncTarget \brief The QContactSyncTarget class provides a sync target for a contact. \inmodule QtContacts \ingroup contacts-details */ /*! \variable QContactSyncTarget::Type The enum constant for the type identifier of QContactSyncTarget details. */ const QContactDetail::DetailType QContactSyncTarget::Type(QContactDetail::TypeSyncTarget); /*! \enum QContactSyncTarget::SyncTargetField This enumeration defines the fields supported by QContactSyncTarget. \value FieldSyncTarget The value stored in this field is the contact to which syncronize. \sa syncTarget(), setSyncTarget() */ /*! \fn QContactSyncTarget::syncTarget() const Returns the identifier of the backend store to which the contact containing this detail should be synchronized. */ /*! \fn QContactSyncTarget::setSyncTarget(const QString& syncTarget) Sets the identifier of the backend store to which the contact containing this detail should be synchronized to \a syncTarget. */ /* ==================== QContactEmailAddress ======================= */ /*! \class QContactEmailAddress \brief The QContactEmailAddress class contains an email address of a contact. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactEmailAddress::Type The enum constant for the type identifier of QContactEmailAddress details. */ const QContactDetail::DetailType QContactEmailAddress::Type(QContactDetail::TypeEmailAddress); /*! \enum QContactEmailAddress::EmailAddressField This enumeration defines the fields supported by QContactEmailAddress. \value FieldEmailAddress The value stored in this field is the email address value. \sa emailAddress(), setEmailAddress() */ /*! \fn QContactEmailAddress::emailAddress() const Returns the email address of the contact which is stored in this detail. */ /*! \fn QContactEmailAddress::setEmailAddress(const QString& emailAddress) Sets the email address of the contact which is stored in this detail to \a emailAddress. */ /* ==================== QContactFamily ======================= */ /*! \class QContactFamily \brief The QContactFamily class contains names of family members of a contact. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactFamily::Type The enum constant for the type identifier of QContactFamily details. */ const QContactDetail::DetailType QContactFamily::Type(QContactDetail::TypeFamily); /*! \enum QContactFamily::FamilyField This enumeration defines the fields supported by QContactFamily. \value FieldSpouse The value of this field contains the name of a spouse. \value FieldChildren The value of this field contains the names of children. \sa spouse(), setSpouse() \sa children(), setChildren() */ /*! \fn QContactFamily::spouse() const Returns the name of the spouse of the contact which is stored in this detail. */ /*! \fn QContactFamily::setSpouse(const QString& spouseName) Sets the name of the spouse of the contact which is stored in this detail to \a spouseName. */ /*! \fn QContactFamily::children() const Returns the names of the children of the contact which is stored in this detail. */ /*! \fn QContactFamily::setChildren(const QStringList& childrenNames) Sets the names of the children of the contact which is stored in this detail to \a childrenNames. */ /* ==================== QContactFavorite ======================= */ /*! \class QContactFavorite \brief The QContactFavorite class indicates if a contact is a favorite contact as well as the position it should appear in an ordered list of favorites. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactFavorite::Type The enum constant for the type identifier of QContactFavorite details. */ const QContactDetail::DetailType QContactFavorite::Type(QContactDetail::TypeFavorite); /*! \enum QContactFavorite::FavoriteField This enumeration defines the fields supported by QContactFavorite. \value FieldFavorite The value of this field indicates whether a contact is a favorite. \value FieldIndex The value of this field contains the index of the favorite contact (which determines the order they appear). \sa isFavorite(), setFavorite() \sa index(), setIndex() */ /*! \fn bool QContactFavorite::isFavorite() const Returns true if the contact is a favorite, false otherwise. */ /*! \fn QContactFavorite::setFavorite(bool isFavorite) If \a isFavorite is true, marks the contact as a favorite. Otherwise, marks the contact as not a favorite. */ /*! \fn int QContactFavorite::index() const Returns the index of the favorite contact. */ /*! \fn QContactFavorite::setIndex(int index) Sets the index of the favorite contact to \a index. */ /*! Returns a filter suitable for finding contacts which are marked as favorite contacts. */ QContactFilter QContactFavorite::match() { QContactDetailFilter f; f.setDetailType(QContactFavorite::Type, QContactFavorite::FieldFavorite); f.setValue(true); f.setMatchFlags(QContactFilter::MatchExactly); return f; } /* ==================== QContactAnniversary ======================= */ /*! \class QContactAnniversary \brief The QContactAnniversary class contains an anniversary of a contact. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactAnniversary::Type The enum constant for the type identifier of QContactAnniversary details. */ const QContactDetail::DetailType QContactAnniversary::Type(QContactDetail::TypeAnniversary); /*! \enum QContactAnniversary::AnniversaryField This enumeration defines the fields supported by QContactAnniversary. \value FieldCalendarId The value stored in this field is the id of the calendar event. \value FieldOriginalDate The value stored in this field is either a date, or a date and time. Some managers may support either type, while others may convert the value here to a specific type (either discarding the time if only a date is supported, or by using midnight if a time is not supplied). \value FieldEvent The value stored in this field is the name of the event value. \value FieldSubType The value stored in this field is the sub type of a QContactAnniversary. \sa originalDate(), setOriginalDate(), originalDateTime(), setOriginalDateTime() \sa event(), setEvent() \sa calendarId(), setCalendarId() */ /*! \enum QContactAnniversary::SubType This enumeration defines the predefined enum constants for a sub type value of a QContactAnniversary. \value SubTypeWedding The value stored is a wedding anniversary. \value SubTypeEngagement The value stored is the anniversary of an engagement. \value SubTypeHouse The value stored is the anniversary of a new residence. \value SubTypeEmployment The value stored is the anniversary of a start of employment. \value SubTypeMemorial The value stored is the anniversary of an event of sentimental significance. \sa subType(), setSubType() */ /*! \fn QContactAnniversary::originalDate() const Returns the original date of the event stored in this detail. If the original event occurrence stored is a QDateTime, this returns the date portion. */ /*! \fn QContactAnniversary::setOriginalDate(const QDate& date) Sets the original date of the event stored in this detail to \a date. */ /*! \fn QContactAnniversary::originalDateTime() const Returns the original date and time of the event stored in this detail. If the original event occurrence stored is a QDate, this returns a QDateTime with the time set to midnight. */ /*! \fn QContactAnniversary::setOriginalDateTime(const QDateTime& dateTime) Sets the original date and time of the event stored in this detail to \a dateTime. */ /*! \fn QContactAnniversary::calendarId() const * Returns the identifier of the calendar entry associated with this anniversary. */ /*! \fn QContactAnniversary::setCalendarId(const QString& calendarId) Sets the identifier of the calendar entry associated with this anniversary to \a calendarId. */ /*! \fn QContactAnniversary::event() const Returns the name of the event for which this detail contains information. */ /*! \fn QContactAnniversary::setEvent(const QString& event) Sets the name of the event for which this detail contains information to \a event. */ /*! \fn QContactAnniversary::setSubType(const QContactAnniversary::SubType &subType) Sets the subtype which this detail implements to be the given \a subType. */ /*! \fn QContactAnniversary::subType() const Returns the subtype that this detail implements, if defined. */ /* ==================== QContactAvatar ======================= */ /*! \class QContactAvatar \ingroup contacts-details \inmodule QtContacts \brief The QContactAvatar class contains avatar URLs of a contact. Users can specify avatar URLs for a contact using this detail. Generally, a URL will specify the location of a full-sized image (or video) avatar. Support for the detail is backend-specific; some managers will automatically load the URL and synthesize a (possibly scaled) thumbnail image for the contact and store it on a platform-specific location in the file system. The URLs which are contained in the detail may point to a file or resource whose content may dynamically change. The content of a resource identified by a URL specified in a QContactAvatar detail is set by whoever owns the resource which the URL identifies. */ /*! \variable QContactAvatar::Type The enum constant for the type identifier of QContactAvatar details. */ const QContactDetail::DetailType QContactAvatar::Type(QContactDetail::TypeAvatar); /*! \enum QContactAvatar::AvatarField This enumeration defines the fields supported by QContactAvatar. \value FieldImageUrl The value stored in this field contains the URL of the avatar image. \value FieldVideoUrl The value stored in this field contains the URL of the video avatar. \sa imageUrl(), setImageUrl() \sa videoUrl(), setVideoUrl() */ /*! \fn QContactAvatar::imageUrl() const Returns the url of an avatar image associated with the contact */ /*! \fn QContactAvatar::setImageUrl(const QUrl& imageUrl) Sets the url of an avatar image associated with the contact to \a imageUrl */ /*! \fn QContactAvatar::videoUrl() const Returns the url of an avatar video associated with the contact */ /*! \fn QContactAvatar::setVideoUrl(const QUrl& videoUrl) Sets the url of an avatar video associated with the contact to \a videoUrl */ /* ==================== QContactAddress ======================= */ /*! \class QContactAddress \brief The QContactAddress class contains an address of a contact. \ingroup contacts-details \inmodule QtContacts The fields in the QContactAddress class are based on the segments of the ADR property of a Versit vCard file. Versit \reg is a trademark of the Internet Mail Consortium. */ /*! \variable QContactAddress::Type The enum constant for the type identifier of QContactAddress details. */ const QContactDetail::DetailType QContactAddress::Type(QContactDetail::TypeAddress); /*! \enum QContactAddress::AddressField This enumeration defines the fields supported by QContactAddress. \value FieldStreet The value stored in this field contains the street number and street name of the address. \value FieldLocality The value stored in this field contains the name of the city, town or suburb of the address. \value FieldRegion The value stored in this field contains the region segment. The region segment contains the name or identifier of the state, province or region of the address. \value FieldPostcode The value stored in this field contains the postcode segment. The postcode segment contains the postal code for the address. \value FieldCountry The value stored in this field contains the country segment. The country segment contains the name of the country of the address. \value FieldPostOfficeBox The value stored in this field contains the post office box segment. The post office box segment contains the post office box identifier of the mailing address. \value FieldSubTypes The value stored in this field contains the sub types of a QContactAddress. \sa street(), setStreet() \sa locality(), setLocality() \sa region(), setRegion() \sa postcode(), setPostcode() \sa country(), setCountry() \sa postOfficeBox(), setPostOfficeBox() */ /*! \enum QContactAddress::SubType This enumeration defines the predefined enum constants for a sub type value of a QContactAddress. \value SubTypeParcel The value stored contains an address for parcel delivery. \value SubTypePostal The value stored contains an address for postal delivery. \value SubTypeDomestic The value stored contains an address for domestic mail delivery. \value SubTypeInternational The value stored contains an address for international mail delivery. \sa subTypes(), setSubTypes() */ /*! \fn QContactAddress::postOfficeBox() const Returns the post office box segment of the address stored in this detail. */ /*! \fn QContactAddress::setPostOfficeBox(const QString& postOfficeBox) Sets the post office box segment of the address stored in this detail to \a postOfficeBox. */ /*! \fn QContactAddress::street() const Returns the street segment of the address stored in this detail. */ /*! \fn QContactAddress::setStreet(const QString& street) Sets the street segment of the address stored in this detail to \a street. */ /*! \fn QContactAddress::locality() const Returns the locality segment of the address stored in this detail. */ /*! \fn QContactAddress::setLocality(const QString& locality) Sets the locality segment of the address stored in this detail to \a locality. */ /*! \fn QContactAddress::region() const Returns the region segment of the address stored in this detail. */ /*! \fn QContactAddress::setRegion(const QString& region) Sets the region segment of the address stored in this detail to \a region. */ /*! \fn QContactAddress::postcode() const Returns the postcode segment of the address stored in this detail. */ /*! \fn QContactAddress::setPostcode(const QString& postcode) Sets the postcode segment of the address stored in this detail to \a postcode. */ /*! \fn QContactAddress::country() const Returns the country segment of the address stored in this detail. */ /*! \fn QContactAddress::setCountry(const QString& country) Sets the country segment of the address stored in this detail to \a country. */ /*! \fn QContactAddress::setSubTypes(const QList &subTypes) Sets the subtypes which this detail implements to be those contained in the list of given \a subTypes. */ /*! \fn QContactAddress::subTypes() const Returns the list of subtypes that this detail implements. */ /*! Returns a filter suitable for finding contacts with an address which contains the given \a subString in any of its fields. */ QContactFilter QContactAddress::match(const QString &subString) { QContactDetailFilter f1; f1.setDetailType(QContactAddress::Type, QContactAddress::FieldStreet); f1.setValue(subString); f1.setMatchFlags(QContactFilter::MatchContains); QContactDetailFilter f2; f2.setDetailType(QContactAddress::Type, QContactAddress::FieldLocality); f2.setValue(subString); f2.setMatchFlags(QContactFilter::MatchContains); QContactDetailFilter f3; f3.setDetailType(QContactAddress::Type, QContactAddress::FieldRegion); f3.setValue(subString); f3.setMatchFlags(QContactFilter::MatchContains); QContactDetailFilter f4; f4.setDetailType(QContactAddress::Type, QContactAddress::FieldPostcode); f4.setValue(subString); f4.setMatchFlags(QContactFilter::MatchContains); QContactDetailFilter f5; f5.setDetailType(QContactAddress::Type, QContactAddress::FieldCountry); f5.setValue(subString); f5.setMatchFlags(QContactFilter::MatchContains); QContactDetailFilter f6; f6.setDetailType(QContactAddress::Type, QContactAddress::FieldPostOfficeBox); f6.setValue(subString); f6.setMatchFlags(QContactFilter::MatchContains); return (f1 | f2 | f3 | f4 | f5 | f6); } /* ==================== QContactUrl ======================= */ /*! \class QContactUrl \brief The QContactUrl class contains a url associated with a contact. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactUrl::Type The enum constant for the type identifier of QContactUrl details. */ const QContactDetail::DetailType QContactUrl::Type(QContactDetail::TypeUrl); /*! \enum QContactUrl::UrlField This enumeration defines the fields supported by QContactUrl. \value FieldUrl The value stored in this field contains the URL. \value FieldSubType The value stored in this field contains the sub type of a QContactUrl. \sa url(), setUrl() */ /*! \enum QContactUrl::SubType This enumeration defines the predefined enum constants for a sub type value of a QContactUrl. \value SubTypeHomePage The value stored is a contact's home page. \value SubTypeFavourite The value stored is a contact's favourite URLs (or bookmarks). \value SubTypeBlog The value stored is one of the contact's blogs. \sa subType(), setSubType() */ /*! \fn QContactUrl::url() const Returns the url stored in this detail. */ /*! \fn QContactUrl::setUrl(const QString& url) Sets the url stored in this detail to \a url. */ /*! \fn QContactUrl::setUrl(const QUrl& url) Sets the url stored in this detail to the string representation of the given \a url. */ /*! \fn QContactUrl::setSubType(const QContactUrl::SubType& subType) Sets the subtype which this detail implements to be the given \a subType. */ /*! \fn QContactUrl::subType() const Returns the subtype that this detail implements, if defined. */ /* ==================== QContactPhonenumber ======================= */ /*! \class QContactPhoneNumber \brief The QContactPhoneNumber class provides a phone number of a contact. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactPhoneNumber::Type The enum constant for the type identifier of QContactPhoneNumber details. */ const QContactDetail::DetailType QContactPhoneNumber::Type(QContactDetail::TypePhoneNumber); /*! \enum QContactPhoneNumber::PhoneNumberField This enumeration defines the fields supported by QContactPhoneNumber. \value FieldNumber The value stored in this field contains the phone number. \value FieldSubTypes The value stored in this field contains the sub types of a QContactPhoneNumber. \sa number(), setNumber() */ /*! \enum QContactPhoneNumber::SubType This enumeration defines the predefined enum constants for a sub type value of a QContactPhoneNumber. \value SubTypeLandline The value stored is a landline number. \value SubTypeMobile The value stored is a mobile (cellular) number. \value SubTypeFax The value stored is a fax number. \value SubTypePager The value stored is a pager number. \value SubTypeCar The value stored is a car phone. \value SubTypeBulletinBoardSystem The value stored is a bulletin board system. \value SubTypeVoice The value stored is indicating this phone number supports voice transmission. \value SubTypeModem The value stored is indicating this phone number supports data transmission. \value SubTypeVideo The value stored is indicating this phone number supports video transmission. \value SubTypeMessagingCapable The value stored is indicating this phone number supports messaging services. \value SubTypeAssistant The value stored is indicating this phone number is the number of an assistant. \value SubTypeDtmfMenu The value stored is indicating this phone number supports DTMF-controlled voice menu navigation. \sa subTypes(), setSubTypes() */ /*! \fn QContactPhoneNumber::number() const Returns the phone number stored in this detail. */ /*! \fn QContactPhoneNumber::setNumber(const QString& number) Sets the phone number stored in this detail to \a number. */ /*! \fn QContactPhoneNumber::setSubTypes(const QList& subTypes) Sets the subtypes which this detail implements to be those contained in the list of given \a subTypes */ /*! \fn QContactPhoneNumber::subTypes() const Returns the list of subtypes that this detail implements. */ /* ==================== QContactBirthday ======================= */ /*! \class QContactBirthday \brief The QContactBirthday class contains a birthday of a contact. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactBirthday::Type The enum constant for the type identifier of QContactBirthday details. */ const QContactDetail::DetailType QContactBirthday::Type(QContactDetail::TypeBirthday); /*! \enum QContactBirthday::BirthdayField This enumeration defines the fields supported by QContactBirthday. \value FieldBirthday The value stored in this field contains the birthday date. This field is either a date, or a date and time. Some managers may support either type, while others may convert the value here to a specific type (either discarding the time if only a date is supported, or by using midnight if a time is not supplied). \value FieldCalendarId The value stored in this field contains the id of the calendar event. \sa date(), setDate(), dateTime(), setDateTime() \sa calendarId(), setCalendarId() */ /*! \fn QContactBirthday::date() const Returns the date of the birthday which is stored in this detail. If the birthday stored is a QDateTime, this returns the date portion. */ /*! \fn QContactBirthday::setDate(const QDate& date) Sets the date of the birthday which is stored in this detail to \a date. */ /*! \fn QContactBirthday::dateTime() const Returns the date and time of the birthday which is stored in this detail. If the birthday stored is a QDate, this returns a QDateTime with the time set to midnight. */ /*! \fn QContactBirthday::setDateTime(const QDateTime& dateTime) Sets the date and time of the birthday which is stored in this detail to \a dateTime. */ /*! \fn QContactBirthday::calendarId() const * Returns the identifier of the calendar entry associated with this birthday. */ /*! \fn QContactBirthday::setCalendarId(const QString& calendarId) Sets the identifier of the calendar entry associated with this birthday to \a calendarId. */ /* ==================== QContactDisplayLabel ======================= */ /*! \class QContactDisplayLabel \brief The QContactDisplayLabel class contains a displayLabel of a contact. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactDisplayLabel::Type The enum constant for the type identifier of QContactDisplayLabel details. */ const QContactDetail::DetailType QContactDisplayLabel::Type(QContactType::TypeDisplayLabel); /*! \enum QContactDisplayLabel::DisplayLabelField This enumeration defines the fields supported by QContactDisplayLabel. \value FieldLabel The value stored in this field contains the displaylabel. \sa label(), setLabel() */ /*! \fn QContactDisplayLabel::setLabel(const QString& displayLabel) Sets the displayLabel of the contact which is stored in this detail to \a displayLabel. displayLabel can be for example the first name of a contact. */ /*! \fn QContactDisplayLabel::label() const Returns the displayLabel of the contact which is stored in this detail. */ /* ==================== QContactGender ======================= */ /*! \class QContactGender \brief The QContactGender class contains the gender of a contact. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactGender::Type The enum constant for the type identifier of QContactGender details. */ const QContactDetail::DetailType QContactGender::Type(QContactDetail::TypeGender); /*! \enum QContactGender::GenderField This enumeration defines the fields supported by QContactGender. \value FieldGender The value stored in this field contains the gender. \value GenderMale The value that identifies this contact as being male. \value GenderFemale The value that identifies this contact as being female. \value GenderUnspecified The value that identifies this contact as being of unspecified gender. \sa gender(), setGender() */ /*! \fn QContactGender::gender() const Returns the gender of the contact, as stored in this detail. The possible values for the value stored are "Male", "Female" and "Unspecified". */ /*! \fn QContactGender::setGender(const GenderField gender) Sets the gender of the contact (as stored in this detail) to \a gender, if \a gender is either GenderMale or GenderFemale, otherwise sets it to GenderUnspecified. */ /* ==================== QContactGeolocation ======================= */ /*! \class QContactGeoLocation \brief The QContactGeoLocation class contains a global location coordinate associated with a contact. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactGeoLocation::Type The enum constant for the type identifier of QContactGeoLocation details. */ const QContactDetail::DetailType QContactGeoLocation::Type(QContactDetail::TypeGeoLocation); /*! \enum QContactGeoLocation::GeoLocationField This enumeration defines the fields supported by QContactGeoLocation. \value FieldLabel The value stored in this field contains the location label. \value FieldLatitude The value stored in this field contains the latitude. \value FieldLongitude The value stored in this field contains the longitude. \value FieldAccuracy The value stored in this field contains the location (latitude/longitude) accuracy. \value FieldAltitude The value stored in this field contains the altitude. \value FieldAltitudeAccuracy The value stored in this field contains the accuracy of the altitude. \value FieldHeading The value stored in this field contains the heading. \value FieldSpeed The value stored in this field contains the speed. \value FieldTimestamp The value stored in this field contains the timestamp of the location information. \sa label(), setLabel() \sa latitude(), setLatitude() \sa longitude(), setLongitude() \sa accuracy(), setAccuracy() \sa altitude(), setAltitude() \sa altitudeAccuracy(), setAltitudeAccuracy() \sa heading(), setHeading() \sa speed(), setSpeed() \sa timestamp(), setTimestamp() */ /*! \fn QContactGeoLocation::setLabel(const QString& label) Sets the label of the location stored in the detail to \a label. */ /*! \fn QContactGeoLocation::label() const Returns the label of the location stored in the detail. */ /*! \fn QContactGeoLocation::setLatitude(double latitude) Sets the latitude portion of the coordinate (in decimal degrees) of the location stored in the detail to \a latitude. */ /*! \fn QContactGeoLocation::latitude() const Returns the latitude portion of the coordinate (specified in decimal degrees) of the location stored in the detail. */ /*! \fn QContactGeoLocation::setLongitude(double longitude) Sets the longitude portion of the coordinate (in decimal degrees) of the location stored in the detail to \a longitude. */ /*! \fn QContactGeoLocation::longitude() const Returns the longitude portion of the coordinate (specified in decimal degrees) of the location stored in the detail. */ /*! \fn QContactGeoLocation::setAccuracy(double accuracy) Specifies that the latitude and longitude portions of the location stored in the detail are accurate to within \a accuracy metres. */ /*! \fn QContactGeoLocation::accuracy() const Returns the accuracy (in metres) of the latitude and longitude of the location stored in the detail. */ /*! \fn QContactGeoLocation::setAltitude(double altitude) Sets the altitude portion of the coordinate (in metres above the ellipsoid) of the location stored in the detail to \a altitude. */ /*! \fn QContactGeoLocation::altitude() const Returns the altitude (in metres) of the location stored in the detail. */ /*! \fn QContactGeoLocation::setAltitudeAccuracy(double altitudeAccuracy) Sets the altitude-accuracy portion of the coordinate (in metres) of the location stored in the detail to \a altitudeAccuracy. */ /*! \fn QContactGeoLocation::altitudeAccuracy() const Returns the accuracy of the altitude portion of the location stored in the detail. */ /*! \fn QContactGeoLocation::setHeading(double heading) Sets the heading portion of the coordinate (in decimal degrees clockwise relative to true north) of the location-aware device at the time of measurement to \a heading. */ /*! \fn QContactGeoLocation::heading() const Returns the heading (at the time of measurement) of the location-aware device that recorded (or was provided) the measurement. */ /*! \fn QContactGeoLocation::setSpeed(double speed) Sets the speed portion of the coordinate (in metres per second) of the location-aware device at the time of measurement to \a speed. */ /*! \fn QContactGeoLocation::speed() const Returns the speed (at the time of measurement) of the location-aware device that recorded (or was provided) the measurement. */ /*! \fn QContactGeoLocation::setTimestamp(const QDateTime& timestamp) Sets the creation (or first-valid) timestamp of the location information to \a timestamp. */ /*! \fn QContactGeoLocation::timestamp() const Returns the timestamp associated with the location stored in the detail. */ /* ==================== QContactGuid ======================= */ /*! \class QContactGuid \brief The QContactGuid class contains a globally unique Id of a contact, for use in synchronization with other datastores. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactGuid::Type The enum constant for the type identifier of QContactGuid details. */ const QContactDetail::DetailType QContactGuid::Type(QContactDetail::TypeGuid); /*! \enum QContactGuid::GuidField This enumeration defines the fields supported by QContactGuid. \value FieldGuid The value stored in this field contains the GUID. \sa guid(), setGuid() */ /*! \fn QContactGuid::guid() const Returns the globally unique identifier which is stored in this detail. */ /*! \fn QContactGuid::setGuid(const QString& guid) Sets the globally unique identifier which is stored in this detail to \a guid. */ /* ==================== QContactHobby ======================= */ /*! \class QContactHobby \brief The QContactHobby class contains a hobby of the contact. \ingroup contacts-details \inmodule QtContacts A contact may have one or more hobbies. Each QContactHobby detail contains information about a single hobby of the contact. */ /*! \variable QContactHobby::Type The enum constant for the type identifier of QContactHobby details. */ const QContactDetail::DetailType QContactHobby::Type(QContactDetail::TypeHobby); /*! \enum QContactHobby::HobbyField This enumeration defines the fields supported by QContactHobby. \value FieldHobby The value stored in this field contains the name of the hobby. \sa hobby(), setHobby() */ /*! \fn QContactHobby::setHobby(const QString& hobby) Sets the hobby associated with a contact which is stored in this detail to \a hobby. */ /*! \fn QContactHobby::hobby() const Returns the hobby associated with a contact which is stored in this detail. */ /* ==================== QContactName ======================= */ /*! \class QContactName \brief The QContactName class contains a name of a contact. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactName::Type The enum constant for the type identifier of QContactName details. */ const QContactDetail::DetailType QContactName::Type(QContactDetail::TypeName); /*! \enum QContactName::NameField This enumeration defines the fields supported by QContactName. \value FieldPrefix The value stored in this field contains the prefix part of the name. \value FieldFirstName The value stored in this field contains the first part of the name. \value FieldMiddleName The value stored in this field contains the middle part of the name. \value FieldLastName The value stored in this field contains the last part of the name. \value FieldSuffix The value stored in this field contains the suffix part of the name. \sa prefix(), setPrefix() \sa firstName(), setFirstName() \sa middleName(), setMiddleName() \sa lastName(), setLastName() \sa suffix(), setSuffix() */ /*! \fn QContactName::prefix() const Returns the prefix segment of the name stored in this detail. */ /*! \fn QContactName::setPrefix(const QString& prefix) Sets the prefix segment of the name stored in this detail to \a prefix. */ /*! \fn QContactName::firstName() const Returns the first (given) name segment of the name stored in this detail. */ /*! \fn QContactName::setFirstName(const QString& firstName) Sets the first name segment of the name stored in this detail to \a firstName. */ /*! \fn QContactName::middleName() const Returns the middle (additional, or other) name segment of the name stored in this detail. */ /*! \fn QContactName::setMiddleName(const QString& middleName) Sets the middle name segment of the name stored in this detail to \a middleName. */ /*! \fn QContactName::lastName() const Returns the last (family, or surname) name segment of the name stored in this detail. */ /*! \fn QContactName::setLastName(const QString& lastName) Sets the last name segment of the name stored in this detail to \a lastName. */ /*! \fn QContactName::suffix() const Returns the suffix segment of the name stored in this detail. */ /*! \fn QContactName::setSuffix(const QString& suffix) Sets the suffix segment of the name stored in this detail to \a suffix. */ /* ==================== QContactNickname ======================= */ /*! \class QContactNickname \brief The QContactNickname class contains a nickname of a contact. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactNickname::Type The enum constant for the type identifier of QContactNickname details. */ const QContactDetail::DetailType QContactNickname::Type(QContactType::TypeNickname); /*! \enum QContactNickname::NicknameField This enumeration defines the fields supported by QContactNickname. \value FieldNickname The value stored in this field contains the nickname. \sa nickname(), setNickname() */ /*! \fn QContactNickname::setNickname(const QString& nickname) Sets the nickname of the contact which is stored in this detail to \a nickname. */ /*! \fn QContactNickname::nickname() const Returns the nickname of the contact which is stored in this detail. */ /* ==================== QContactNote ======================= */ /*! \class QContactNote \brief The QContactNote class contains a note associated with a contact. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactNote::Type The enum constant for the type identifier of QContactNote details. */ const QContactDetail::DetailType QContactNote::Type(QContactDetail::TypeNote); /*! \enum QContactNote::NoteField This enumeration defines the fields supported by QContactNote. \value FieldNote The value stored in this field contains the note. \sa note(), setNote() */ /*! \fn QContactNote::setNote(const QString& note) Sets a note associated with a contact to \a note. */ /*! \fn QContactNote::note() const Returns a string for a note associated with a contact. */ /* ==================== QContactTag ======================= */ /*! \class QContactTag \brief The QContactTag class contains a tag associated with a contact. \ingroup contacts-details \inmodule QtContacts Typically the tags associated with a contact will be distinct, although this is usually only enforced when the contact is saved in the manager. Here is an example of retrieving all the tags for a contact: \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Getting all tags Here is an example of checking for a specific tag value: \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Checking for a specific tag */ /*! \variable QContactTag::Type The enum constant for the type identifier of QContactTag details. */ const QContactDetail::DetailType QContactTag::Type(QContactDetail::TypeTag); /*! \enum QContactTag::TagField This enumeration defines the fields supported by QContactTag. \value FieldTag The value stored in this field contains the tag. \sa tag(), setTag() */ /*! \fn QContactTag::setTag(const QString& tag) Sets the tag associated with a contact which is stored in this detail to \a tag. */ /*! \fn QContactTag::tag() const Returns the tag associated with a contact which is stored in this detail. */ /*! Returns a filter suitable for finding contacts which have a tag which contains the specified \a subString. */ QContactFilter QContactTag::match(const QString &subString) { QContactDetailFilter f; f.setDetailType(QContactTag::Type, QContactTag::FieldTag); f.setValue(subString); f.setMatchFlags(QContactFilter::MatchContains); return f; } /* ==================== QContactTimestamp ======================= */ /*! \class QContactTimestamp \brief The QContactTimestamp class contains the creation and last-modified timestamp associated with the contact. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactTimestamp::Type The enum constant for the type identifier of QContactTimestamp details. */ const QContactDetail::DetailType QContactTimestamp::Type(QContactDetail::TypeTimestamp); /*! \enum QContactTimestamp::TimestampField This enumeration defines the fields supported by QContactTimestamp. \value FieldModificationTimestamp The value stored in this field contains the last modified timestamp. \value FieldCreationTimestamp The value stored in this field contains the value of the timestamp a contact was created. \sa lastModified(), setLastModified() \sa created(), setCreated() */ /*! \fn QContactTimestamp::created() const Returns the creation timestamp saved in this detail. */ /*! \fn QContactTimestamp::lastModified() const Returns the last-modified timestamp saved in this detail. */ /*! \fn QContactTimestamp::setCreated(const QDateTime& dateTime) Sets the creation timestamp saved in this detail to \a dateTime. */ /*! \fn QContactTimestamp::setLastModified(const QDateTime& dateTime) Sets the last-modified timestamp saved in this detail to \a dateTime. */ /* ==================== QContactType ======================= */ /*! \class QContactType \brief The QContactType class describes the type of the contact. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactType::Type The enum constant for the type identifier of QContactType details. */ const QContactDetail::DetailType QContactType::Type(QContactDetail::TypeType); /*! \enum QContactType::TypeField This enumeration defines the fields supported by QContactType. \value FieldType The value stored in this field contains the type value which is stored in details of the QContactType definition. \sa type(), setType() */ /*! \enum QContactType::TypeValues \value TypeContact The value indicates that this contact is an ordinary contact. \value TypeGroup The value indicates that this contact is a group contact. Contacts of this type are able to be the first contact in relationships of the \c QContactRelationship::HasMember type. To enumerate the ids of members of a group, the client should retrieve the relationships which involve the group from the manager in which the group is saved. \sa type(), setType() */ /*! \fn QContactType::type() const Returns the contact type value stored in this detail. */ /*! \fn QContactType::setType(const TypeValues type) Sets the type of the contact to be the give \a type. */ /* ==================== QContactOnlineAccount ======================= */ // XXX TODO explain link to QContactPresence /*! \class QContactOnlineAccount \brief The QContactOnlineAccount class provides an online account, which the contact uses to communicate with friends and family. \inmodule QtContacts A QContactOnlineAccount consists of the account details required to communicate with the contact, including the account URI, the capabilities of the account, the service provider of the account, and the type of the account. Presence information for a particular QContactOnlineAccount detail is provided in a QContactPresence detail which is linked (via linkedDetailUris()) to the account detail. This information is generally provided by the backend, and is not modifiable by clients. \sa QContactPresence \ingroup contacts-details */ /*! \variable QContactOnlineAccount::Type The enum constant for the type identifier of QContactOnlineAccount details. */ const QContactDetail::DetailType QContactOnlineAccount::Type(QContactDetail::TypeOnlineAccount); /*! \enum QContactOnlineAccount::OnlineAccountField This enumeration defines the fields supported by QContactOnlineAccount. \value FieldAccountUri The value stored in this field contains the value of the account uri. \value FieldServiceProvider The value stored in this field contains the value of the provider name. \value FieldProtocol The value stored in this field contains the value of the protocol. \value FieldCapabilities The value stored in this field contains the value of the account capabilities. \value FieldSubTypes The value stored in this field contains the value of the sub types of a QContactOnlineAccount. \sa accountUri(), setAccountUri() \sa serviceProvider(), setServiceProvider() \sa capabilities(), setCapabilities() */ /*! \enum QContactOnlineAccount::SubType This enumeration defines the predefined enum constants for a sub type value of a QContactOnlineAccount. \value SubTypeSip The value stored indicates if this online account supports SIP. \value SubTypeSipVoip The value stored indicates if this online account supports SIP based VOIP. \value SubTypeImpp The value stored indicates if this online account supports IMPP. \value SubTypeVideoShare The value stored indicates if this online account supports VideoShare. \sa subTypes(), setSubTypes() */ /*! \enum QContactOnlineAccount::Protocol This enumeration defines the predefined enum constants for a protocol value of a QContactOnlineAccount. \value ProtocolUnknown The value stored indicates this online account is for one unsupported protocol. \value ProtocolAim The value stored indicates this online account is for the AIM protocol. \value ProtocolIcq The value stored indicates this online account is for the ICQ protocol. \value ProtocolIrc The value stored indicates this online account is for the IRC protocol. \value ProtocolJabber The value stored indicates this online account is for the jabber protocol. \value ProtocolMsn The value stored indicates this online account is for the MSN protocol. \value ProtocolQq The value stored indicates this online account is for the QQ protocol. \value ProtocolSkype The value stored indicates this online account is for the Skype protocol. \value ProtocolYahoo The value stored indicates this online account is for the Yahoo protocol. \sa protocol(), setProtocol() */ /*! \fn QContactOnlineAccount::setAccountUri(const QString& accountUri) Sets the universal resource identifier of the contact's online account to \a accountUri. */ /*! \fn QContactOnlineAccount::accountUri() const Returns the universal resource identifier of the online account of the contact. */ /*! \fn QContactOnlineAccount::setServiceProvider(const QString& serviceProvider) Sets the service provider of the contact's online account to \a serviceProvider. */ /*! \fn QContactOnlineAccount::serviceProvider() const Returns the service provider of the online account of the contact. */ /*! \fn QContactOnlineAccount::protocol() const Returns the protocol value. */ /*! \fn QContactOnlineAccount::setProtocol(Protocol protocol) Set the protocol to \a protocol. */ /*! \fn QContactOnlineAccount::setSubTypes(const QList& subTypes) Sets the subtypes which this detail implements to be those contained in the list of given \a subTypes. */ /*! \fn QContactOnlineAccount::subTypes() const Returns the list of subtypes that this detail implements. */ /*! \fn QContactOnlineAccount::setCapabilities(const QStringList& capabilities) Sets the capabilities of the online account about which this detail stores presence information to \a capabilities. The \a capabilities list is a list of service-provider specified strings which together identify the types of communication which may be possible. */ /*! \fn QContactOnlineAccount::capabilities() const Returns the capabilities of the online account about which this detail stores presence information. */ /* ==================== QContactOrganization ======================= */ /*! \class QContactOrganization \brief The QContactOrganization class provides details about an organization that the contact is either a part of, or stands for. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactOrganization::Type The enum constant for the type identifier of QContactOrganization details. */ const QContactDetail::DetailType QContactOrganization::Type(QContactDetail::TypeOrganization); /*! \enum QContactOrganization::OrganizationField This enumeration defines the fields supported by QContactOrganization. \value FieldName The value stored in this field contains the organization name. \value FieldLogoUrl The value stored in this field contains the organization logo image. \value FieldDepartment The value stored in this field contains the department name. \value FieldLocation The value stored in this field contains the location of the organization. \value FieldRole The value stored in this field contains the contact's role in the organization. \value FieldTitle The value stored in this field contains the contact's title in the organization. \value FieldAssistantName The value stored in this field contains the contact's assistant. \sa department(), setDepartment() \sa name(), setName() \sa logoUrl(), setLogoUrl() \sa department(), setDepartment() \sa location(), setLocation() \sa role(), setRole() \sa title(), setTitle() \sa assistantName(), setAssistantName() */ /*! \fn QContactOrganization::setName(const QString& name) Sets the name of the organization stored in this detail to \a name. */ /*! \fn QContactOrganization::name() const Returns the name of the organization stored in this detail. */ /*! \fn QContactOrganization::setLogoUrl(const QUrl& logo) Sets the url of the logo of the organization stored in this detail to \a logo. */ /*! \fn QContactOrganization::logoUrl() const Returns the url of the logo of the organization stored in this detail. */ /*! \fn QContactOrganization::setDepartment(const QStringList& department) Sets the contact's department of the organization stored in this detail to \a department. The department is a list of progressively finer-grained information. */ /*! \fn QContactOrganization::department() const Returns the contact's department stored in this detail. */ /*! \fn QContactOrganization::setLocation(const QString& location) Sets the location (e.g. city or suburb) of the organization stored in this detail to \a location. */ /*! \fn QContactOrganization::location() const Returns the location of the organization stored in this detail. */ /*! \fn QContactOrganization::setRole(const QString& role) Sets the contact's role within the organization stored in this detail to \a role. */ /*! \fn QContactOrganization::role() const Returns the contact's role within the organization stored in this detail. */ /*! \fn QContactOrganization::setTitle(const QString& title) Sets the contact's title within the organization stored in this detail to \a title. */ /*! \fn QContactOrganization::title() const Returns the contact's title within the organization stored in this detail. */ /*! \fn QContactOrganization::setAssistantName(const QString& assistantName) Sets the name of the default assistant of contacts belonging to this organization to \a assistantName. */ /*! \fn QContactOrganization::assistantName() const Returns the name of the default assistant of contacts belonging to this organization. */ /* ==================== QContactRingtone ======================= */ /*! \class QContactRingtone \brief The QContactRingtone class provides a ringtone associated with a contact \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactRingtone::Type The enum constant for the type identifier of QContactRingtone details. */ const QContactDetail::DetailType QContactRingtone::Type(QContactDetail::TypeRingtone); /*! \enum QContactRingtone::RingtoneField This enumeration defines the fields supported by QContactRingtone. \value FieldAudioRingtoneUrl The value stored in this field contains the URL for an audio ringtone. \value FieldVideoRingtoneUrl The value stored in this field contains the URL for a video ringtone. \value FieldVibrationRingtoneUrl The value stored in this field contains the URL for a vibration ringtone. \sa setAudioRingtoneUrl(), audioRingtoneUrl() \sa setVideoRingtoneUrl(), videoRingtoneUrl() */ /*! \fn QContactRingtone::audioRingtoneUrl() const Returns the uri of the audio ringtone stored in the ringtone detail. */ /*! \fn QContactRingtone::setAudioRingtoneUrl(const QUrl& audioRingtoneUrl) Sets the uri of the audio ringtone stored in the ringtone detail to \a audioRingtoneUrl. */ /*! \fn QContactRingtone::videoRingtoneUrl() const Returns the uri of the video ringtone stored in the ringtone detail. */ /*! \fn QContactRingtone::setVideoRingtoneUrl(const QUrl& videoRingtoneUrl) Sets the uri of the video ringtone stored in the ringtone detail to \a videoRingtoneUrl. */ /*! \fn QContactRingtone::vibrationRingtoneUrl() const \internal Returns the uri of the vibration ringtone stored in the ringtone detail. */ /*! \fn QContactRingtone::setVibrationRingtoneUrl(const QUrl& vibrationRingtoneUrl) \internal Sets the uri of the vibration ringtone stored in the ringtone detail to \a vibrationRingtoneUrl. */ /* ==================== QContactPresence ======================= */ // XXX TODO add more stuff here /*! \class QContactPresence \brief The QContactPresence class provides presence information for an online account of a contact. \inmodule QtContacts Presence information for a particular QContactOnlineAccount detail is provided in a QContactPresence detail which is linked (via linkedDetailUris()) to the account detail. This information is generally provided by the backend, and is not modifiable by clients. Presence information can include update timestamp, screen name, and the status icon, status value, and status text provided by the service provider, as well as user defined status message. \sa QContactOnlineAccount \ingroup contacts-details */ /*! \variable QContactPresence::Type The enum constant for the type identifier of QContactPresence details. */ const QContactDetail::DetailType QContactPresence::Type(QContactDetail::TypePresence); /*! \enum QContactPresence::PresenceField This enumeration defines the fields supported by QContactPresence. \value FieldTimestamp The value stored in this field contains the timestamp value. \value FieldNickname The value stored in this field contains the nickname value. \value FieldPresenceState The value stored in this field contains the presence state enumeration value. \value FieldPresenceStateText The value stored in this field contains the presence state description value. \value FieldPresenceStateImageUrl The value stored in this field contains the presence state image URL. \value FieldCustomMessage The value stored in this field contains the user-entered custom presence message. \sa setTimestamp(), timestamp() \sa setNickname(), nickname() \sa setPresenceState(), presenceState() \sa setPresenceStateText(), presenceStateText() \sa setPresenceStateImageUrl(), presenceStateImageUrl() \sa setCustomMessage(), customMessage() */ /*! \fn QContactPresence::setTimestamp(const QDateTime& updateTimestamp) Sets the timestamp for the last update of the presence detail to be \a updateTimestamp. */ /*! \fn QContactPresence::timestamp() const Returns the timestamp at which the data in the presence detail was valid. */ /*! \fn QContactPresence::setNickname(const QString& nickname) Sets the last-known nickname used by the contact during communications via the online account about which this detail stores presence information to \a nickname. */ /*! \fn QContactPresence::nickname() const Returns the last-known nickname used by the contact during communications via the online account. */ /*! \enum QContactPresence::PresenceState This enum defines the possible presence states supported by the default schema. Not all presence providers support all of these states. \value PresenceUnknown Signifies that the presence state of the contact is not currently known \value PresenceAvailable Signifies that the contact is available \value PresenceHidden Signifies that the contact is hidden \value PresenceBusy Signifies that the contact is busy \value PresenceAway Signifies that the contact is away \value PresenceExtendedAway Signifies that the contact is away for an extended period of time \value PresenceOffline Signifies that the contact is offline */ /*! \fn QContactPresence::setPresenceState(QContactPresence::PresenceState presenceState) Sets the presence state of the online account according to the presence information provider to the given \a presenceState. */ /*! \fn QContactPresence::presenceState() const Returns the presence state of the online account according to the presence provider. */ /*! \fn QContactPresence::setPresenceStateText(const QString& presenceStateText) Sets the text corresponding to the presence state to \a presenceStateText. This function is generally called by presence providers to allow custom naming of states, or to allow finer grained state reporting than is provided by the presence state API. */ /*! \fn QContactPresence::presenceStateText() const Returns the text corresponding to the current presence state. */ /*! \fn QContactPresence::setCustomMessage(const QString& customMessage) Sets the custom status message from the contact for the online account about which this detail stores presence information, to \a customMessage. This custom message would have been set by the contact, and does not necessarily correspond to a particular presence state. */ /*! \fn QContactPresence::customMessage() const Returns the custom status message from the contact for the online account about which this detail stores presence information. */ /*! \fn QContactPresence::setPresenceStateImageUrl(const QUrl& presenceStateImageUrl) Sets the last-known status image url of the contact for the online account about which this detail stores presence information, to \a presenceStateImageUrl. */ /*! \fn QContactPresence::presenceStateImageUrl() const Returns the last-known status image url of the contact for the online account about which this detail stores presence information. */ /* ==================== QContactGlobalPresence ======================= */ /*! \class QContactGlobalPresence \brief The QContactGlobalPresence class provides aggregated presence information for a contact, synthesized or supplied by the backend. \ingroup contacts-details \inmodule QtContacts */ /*! \variable QContactGlobalPresence::Type The enum constant for the type identifier of QContactGlobalPresence details. */ const QContactDetail::DetailType QContactGlobalPresence::Type(QContactDetail::TypeGlobalPresence); /*! \enum QContactGlobalPresence::GlobalPresenceField This enumeration defines the fields supported by QContactGlobalPresence. \value FieldTimestamp The value stored in this field contains the timestamp value. \value FieldNickname The value stored in this field contains the nickname value. \value FieldPresenceState The value stored in this field contains the presence state enumeration value. \value FieldPresenceStateText The value stored in this field contains the presence state description value. \value FieldPresenceStateImageUrl The value stored in this field contains the presence state image URL. \value FieldCustomMessage The value stored in this field contains the user-entered custom presence message. \sa setTimestamp(), timestamp() \sa setNickname(), nickname() \sa setPresenceState(), presenceState() \sa setPresenceStateText(), presenceStateText() \sa setPresenceStateImageUrl(), presenceStateImageUrl() \sa setCustomMessage(), customMessage() */ /*! \fn QContactGlobalPresence::setTimestamp(const QDateTime& updateTimestamp) Sets the update timestamp of the global presence detail to be \a updateTimestamp. */ /*! \fn QContactGlobalPresence::timestamp() const Returns the timestamp at which the data in the global presence detail was valid. */ /*! \fn QContactGlobalPresence::setNickname(const QString& nickname) Sets the last-known nickname used by the contact during communications via any online account about which this detail aggregates presence information to \a nickname. */ /*! \fn QContactGlobalPresence::nickname() const Returns the last-known nickname used by the contact during communications via any online account about which this detail aggregates presence information. */ /*! \fn QContactGlobalPresence::setPresenceState(QContactPresence::PresenceState presenceState) Sets the presence state of this aggregate detail according to the presence information available from the presence providers which this detail aggregates to the given \a presenceState. */ /*! \fn QContactGlobalPresence::presenceState() const Returns the aggregate presence state of any online accounts about which this detail aggregates presence information. */ /*! \fn QContactGlobalPresence::setPresenceStateText(const QString& presenceStateText) Sets the text corresponding to the presence state to \a presenceStateText. This function is generally called by presence providers to allow custom naming of states, or to allow finer grained state reporting than is provided by the presence state API. */ /*! \fn QContactGlobalPresence::presenceStateText() const Returns the text corresponding to the current presence state. */ /*! \fn QContactGlobalPresence::setCustomMessage(const QString& customMessage) Sets the custom status message from the contact for the aggregate presence detail, to \a customMessage. */ /*! \fn QContactGlobalPresence::customMessage() const Returns the custom status message from the contact for the aggregate presence detail. */ /*! \fn QContactGlobalPresence::setPresenceStateImageUrl(const QUrl& presenceStateImageUrl) Sets the last-known status image url of the contact to \a presenceStateImageUrl. */ /*! \fn QContactGlobalPresence::presenceStateImageUrl() const Returns the last-known status image url of the contact. */ /*! Returns a filter which matches any contact whose global presence state is listed as \a state. */ QContactFilter QContactGlobalPresence::match(QContactPresence::PresenceState state) { QContactDetailFilter f; f.setDetailType(QContactGlobalPresence::Type, QContactGlobalPresence::FieldPresenceState); f.setValue(state); f.setMatchFlags(QContactFilter::MatchExactly); return f; } /* ==================== QContactExtendedDetail ======================= */ /*! \class QContactExtendedDetail \brief The QContactExtendedDetail class provides the possibility to save extended details to QContact objects. \inmodule QtContacts \ingroup contacts-details Different back-end engines may or may not support extended details for different contact types. Even if supported, they may accept different QVariant types as the data. The back-end engine may convert the data to another type that the engine supports. */ /*! \variable QContactExtendedDetail::Type The constant enum which identifies the type of details which are extended details. */ const QContactDetail::DetailType QContactExtendedDetail::Type(QContactDetail::TypeExtendedDetail); /*! \enum QContactExtendedDetail::ExtendedDetailField This enumeration defines the fields supported by QContactExtendedDetail. \value FieldName The value stored in this field contains the name of the extended detail. \value FieldData The value stored in this field contains the data of this extended detail. */ /*! \fn void QContactExtendedDetail::setName(const QString &name) Sets the \a name of this extended detail. */ /*! \fn QString QContactExtendedDetail::name() const Gets the name of this extended detail. */ /*! \fn void QContactExtendedDetail::setData(const QVariant &data) Sets the \a data of the extended detail. */ /*! \fn QVariant QContactExtendedDetail::data() const Gets the data of this extended detail. */ /* ==================== QContactVersion ======================= */ /*! \class QContactVersion \brief The QContactVersion class provides the versioning information of a QContact object. \inmodule QtContacts \ingroup contacts-details */ /*! \variable QContactVersion::Type The constant string which identifies the definition of details which are extended details. */ const QContactDetail::DetailType QContactVersion::Type(QContactDetail::TypeVersion); /*! \enum QContactVersion::VersionField This enumeration defines the fields supported by QContactVersion. \value FieldSequenceNumber Contains the integer sequence number of a QContact object. \value FieldExtendedVersion Contains the extended version of a QContact object. It can be used to represent the version stored in the back-end in cases when the back-end specific version cannot be represented only by a sequence number. */ /*! \fn void QContactVersion::setSequenceNumber(int sequenceNumber) Sets the integer \a sequenceNumber. */ /*! \fn int QContactVersion::sequenceNumber() const Gets the integer sequenceNumber. */ /*! \fn void QContactVersion::setExtendedVersion(const QByteArray &extendedVersion) Sets the \a extendedVersion. */ /*! \fn QByteArray QContactVersion::extendedVersion() const Gets the extendedVersion. */ /* ==================== Convenience Filters ======================= */ /*! Returns a filter suitable for finding contacts with a display label containing the specified \a label. */ QContactFilter QContactDisplayLabel::match(const QString &label) { QContactDetailFilter f; f.setDetailType(QContactDisplayLabel::Type, QContactDisplayLabel::FieldLabel); f.setValue(label); f.setMatchFlags(QContactFilter::MatchContains); return f; } /*! Returns a filter suitable for finding contacts with a name with a first name containing the specified \a firstName and a last name containing the specified \a lastName. If either parameter is empty, any value will match that component. */ QContactFilter QContactName::match(const QString &firstName, const QString &lastName) { if (firstName.isEmpty()) { if (lastName.isEmpty()) { // Matches contacts that have a name QContactDetailFilter f; f.setDetailType(QContactName::Type); return f; } else { // Contact with matching lastname QContactDetailFilter f; f.setDetailType(QContactName::Type, QContactName::FieldLastName); f.setValue(lastName); f.setMatchFlags(QContactFilter::MatchContains); return f; } } else { if (lastName.isEmpty()) { // Contact with matching firstName QContactDetailFilter f; f.setDetailType(QContactName::Type, QContactName::FieldFirstName); f.setValue(firstName); f.setMatchFlags(QContactFilter::MatchContains); return f; } else { // Match a contact with the specified first and last names // XXX This needs multi detail filter! // Best we can currently do is "and" and assume there's only one name per contact QContactDetailFilter f; f.setDetailType(QContactName::Type, QContactName::FieldFirstName); f.setValue(firstName); f.setMatchFlags(QContactFilter::MatchContains); QContactDetailFilter l; l.setDetailType(QContactName::Type, QContactName::FieldLastName); l.setValue(lastName); l.setMatchFlags(QContactFilter::MatchContains); return f & l; } } } /*! Returns a filter suitable for finding contacts with any name field (e.g. first, last) that contains the supplied \a name. */ QContactFilter QContactName::match(const QString &name) { QContactUnionFilter nameFilter; QList nameFields; nameFields << QContactName::FieldFirstName << QContactName::FieldLastName << QContactName::FieldMiddleName << QContactName::FieldPrefix << QContactName::FieldSuffix; foreach (int fieldName, nameFields) { QContactDetailFilter subFilter; subFilter.setDetailType(QContactName::Type, fieldName); subFilter.setValue(name); subFilter.setMatchFlags(QContactFilter::MatchContains); nameFilter.append(subFilter); } return nameFilter; } /*! Returns a filter suitable for finding contacts with an email address containing the specified \a emailAddress. */ QContactFilter QContactEmailAddress::match(const QString &emailAddress) { QContactDetailFilter l; l.setDetailType(QContactEmailAddress::Type, QContactEmailAddress::FieldEmailAddress); l.setValue(emailAddress); l.setMatchFlags(QContactFilter::MatchContains); return l; } /*! Returns a filter suitable for finding contacts with a phone number containing the specified \a number. */ QContactFilter QContactPhoneNumber::match(const QString &number) { QContactDetailFilter l; l.setDetailType(QContactPhoneNumber::Type, QContactPhoneNumber::FieldNumber); l.setValue(number); l.setMatchFlags(QContactFilter::MatchPhoneNumber); return l; } QT_END_NAMESPACE_CONTACTS src/contacts/details/qcontactdetails.h000066400000000000000000000064301233466112000204440ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTDETAILS_H #define QCONTACTDETAILS_H // this file includes all of the leaf detail classes // provided by the Qt Contacts API. #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 QT_BEGIN_NAMESPACE_CONTACTS QT_END_NAMESPACE_CONTACTS #endif // QCONTACTDETAILS_H src/contacts/details/qcontactdisplaylabel.h000066400000000000000000000051631233466112000214660ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTDISPLAYLABEL_H #define QCONTACTDISPLAYLABEL_H #include QT_BEGIN_NAMESPACE_CONTACTS class QContactFilter; /* Leaf class */ class Q_CONTACTS_EXPORT QContactDisplayLabel : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactDisplayLabel) #else static const DetailType Type; #endif enum DisplayLabelField { FieldLabel = 0 }; void setLabel(const QString& displayLabel) {setValue(FieldLabel, displayLabel);} QString label() const {return value(FieldLabel).toString();} static QContactFilter match(const QString& label); }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTDISPLAYLABEL_H src/contacts/details/qcontactemailaddress.h000066400000000000000000000052671233466112000214630ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTEMAILADDRESS_H #define QCONTACTEMAILADDRESS_H #include QT_BEGIN_NAMESPACE_CONTACTS class QContactFilter; /* Leaf class */ class Q_CONTACTS_EXPORT QContactEmailAddress : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactEmailAddress) #else static const DetailType Type; #endif enum EmailAddressField { FieldEmailAddress = 0 }; void setEmailAddress(const QString& emailAddress) {setValue(FieldEmailAddress, emailAddress);} QString emailAddress() const {return value(FieldEmailAddress).toString();} // Convenience filter static QContactFilter match(const QString& emailAddress); }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTEMAILADDRESS_H src/contacts/details/qcontactextendeddetail.h000066400000000000000000000053621233466112000220050ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTEXTENDEDDETAIL_H #define QCONTACTEXTENDEDDETAIL_H #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactExtendedDetail : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactExtendedDetail) #else static const DetailType Type; #endif enum ExtendedDetailField { FieldName = 0, FieldData }; void setName(const QString &name) { setValue(FieldName, name); } QString name() const { return value(FieldName).toString(); } void setData(const QVariant &data) { setValue(FieldData, data); } QVariant data() const { return value(FieldData); } }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTEXTENDEDDETAIL_H src/contacts/details/qcontactfamily.h000066400000000000000000000053061233466112000203010ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTFAMILY_H #define QCONTACTFAMILY_H #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactFamily : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactFamily) #else static const DetailType Type; #endif enum FamilyField { FieldSpouse = 0, FieldChildren }; void setSpouse(const QString& spouseName) {setValue(FieldSpouse, spouseName);} QString spouse() const {return value(FieldSpouse).toString();} void setChildren(const QStringList& childrenNames) {setValue(FieldChildren, childrenNames);} QStringList children() const {return value(FieldChildren);} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTFAMILY_H src/contacts/details/qcontactfavorite.h000066400000000000000000000053511233466112000206370ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTFAVORITE_H #define QCONTACTFAVORITE_H #include QT_BEGIN_NAMESPACE_CONTACTS class QContactFilter; /* Leaf class */ class Q_CONTACTS_EXPORT QContactFavorite : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactFavorite) #else static const DetailType Type; #endif enum FavoriteField { FieldFavorite = 0, FieldIndex }; void setFavorite(bool isFavorite) {setValue(FieldFavorite, isFavorite);} bool isFavorite() const {return value(FieldFavorite).toBool();} void setIndex(int index) {setValue(FieldIndex, index);} int index() const {return value(FieldIndex).toInt();} // Convenience filter static QContactFilter match(); }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTFAVORITE_H src/contacts/details/qcontactgender.h000066400000000000000000000051531233466112000202640ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTGENDER_H #define QCONTACTGENDER_H #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactGender : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactGender) #else static const DetailType Type; #endif enum GenderField { FieldGender = 0, GenderMale, GenderFemale, GenderUnspecified }; void setGender(const GenderField gender) {setValue(FieldGender, static_cast(gender));} GenderField gender() const {return static_cast(value(FieldGender));} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTGENDER_H src/contacts/details/qcontactgeolocation.h000066400000000000000000000076761233466112000213370ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTGEOLOCATION_H #define QCONTACTGEOLOCATION_H #include #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ // replaces the below class Q_CONTACTS_EXPORT QContactGeoLocation : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactGeoLocation) #else static const DetailType Type; #endif enum GeoLocationField { FieldLabel = 0, FieldLatitude, FieldLongitude, FieldAccuracy, FieldAltitude, FieldAltitudeAccuracy, FieldHeading, FieldSpeed, FieldTimestamp }; void setLabel(const QString& label) {setValue(FieldLabel, label);} QString label() const {return value(FieldLabel).toString();} void setLatitude(double latitude) {setValue(FieldLatitude, latitude);} double latitude() const {return value(FieldLatitude).toDouble();} void setLongitude(double longitude) {setValue(FieldLongitude, longitude);} double longitude() const {return value(FieldLongitude).toDouble();} void setAccuracy(double accuracy) {setValue(FieldAccuracy, accuracy);} double accuracy() const {return value(FieldAccuracy).toDouble();} void setAltitude(double altitude) {setValue(FieldAltitude, altitude);} double altitude() const {return value(FieldAltitude).toDouble();} void setAltitudeAccuracy(double altitudeAccuracy) {setValue(FieldAltitudeAccuracy, altitudeAccuracy);} double altitudeAccuracy() const {return value(FieldAltitudeAccuracy).toDouble();} void setHeading(double heading) {setValue(FieldHeading, heading);} double heading() const {return value(FieldHeading).toDouble();} void setSpeed(double speed) {setValue(FieldSpeed, speed);} double speed() const {return value(FieldSpeed).toDouble();} void setTimestamp(const QDateTime& timestamp) {setValue(FieldTimestamp, timestamp);} QDateTime timestamp() const {return value(FieldTimestamp).toDateTime();} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTGEOLOCATION_H src/contacts/details/qcontactglobalpresence.h000066400000000000000000000077271233466112000220160ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTGLOBALPRESENCE_H #define QCONTACTGLOBALPRESENCE_H #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactFilter; /* Leaf class */ class Q_CONTACTS_EXPORT QContactGlobalPresence : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactGlobalPresence) #else static const DetailType Type; #endif enum GlobalPresenceField { FieldTimestamp = 0, FieldNickname, FieldPresenceState, FieldPresenceStateText, FieldPresenceStateImageUrl, FieldCustomMessage }; void setTimestamp(const QDateTime& timestamp) {setValue(FieldTimestamp, timestamp);} QDateTime timestamp() const {return value(FieldTimestamp);} void setNickname(const QString& nickname) {setValue(FieldNickname, nickname);} QString nickname() const {return value(FieldNickname).toString();} void setPresenceState(QContactPresence::PresenceState presenceState) {setValue(FieldPresenceState, static_cast(presenceState));} QContactPresence::PresenceState presenceState() const {return static_cast(value(FieldPresenceState));} void setPresenceStateText(const QString& presenceStateText) {setValue(FieldPresenceStateText, presenceStateText);} QString presenceStateText() const {return value(FieldPresenceStateText).toString();} void setPresenceStateImageUrl(const QUrl& presenceStateImageUrl) {setValue(FieldPresenceStateImageUrl, presenceStateImageUrl);} QUrl presenceStateImageUrl() const {return value(FieldPresenceStateImageUrl);} void setCustomMessage(const QString& customMessage) {setValue(FieldCustomMessage, customMessage);} QString customMessage() const {return value(FieldCustomMessage).toString();} // convenience filtering functions static QContactFilter match(QContactPresence::PresenceState state); }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTGLOBALPRESENCE_H src/contacts/details/qcontactguid.h000066400000000000000000000047371233466112000177570ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTGUID_H #define QCONTACTGUID_H #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactGuid : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactGuid) #else static const DetailType Type; #endif enum GuidField { FieldGuid = 0 }; void setGuid(const QString& guid) {setValue(FieldGuid, guid);} QString guid() const {return value(FieldGuid).toString();} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTGUID_H src/contacts/details/qcontacthobby.h000066400000000000000000000047541233466112000201310ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTHOBBY_H #define QCONTACTHOBBY_H #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactHobby : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactHobby) #else static const DetailType Type; #endif enum HobbyField { FieldHobby = 0 }; void setHobby(const QString& hobby) {setValue(FieldHobby, hobby);} QString hobby() const {return value(FieldHobby).toString();} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTHOBBY_H src/contacts/details/qcontactname.h000066400000000000000000000065651233466112000177500ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTNAME_H #define QCONTACTNAME_H #include QT_BEGIN_NAMESPACE_CONTACTS class QContactFilter; /* Leaf class */ class Q_CONTACTS_EXPORT QContactName : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactName) #else static const DetailType Type; #endif enum NameField { FieldPrefix = 0, FieldFirstName, FieldMiddleName, FieldLastName, FieldSuffix }; QString prefix() const {return value(FieldPrefix).toString();} QString firstName() const {return value(FieldFirstName).toString();} QString middleName() const {return value(FieldMiddleName).toString();} QString lastName() const {return value(FieldLastName).toString();} QString suffix() const {return value(FieldSuffix).toString();} void setPrefix(const QString& prefix) {setValue(FieldPrefix, prefix);} void setFirstName(const QString& firstName) {setValue(FieldFirstName, firstName);} void setMiddleName(const QString& middleName) {setValue(FieldMiddleName, middleName);} void setLastName(const QString& lastName) {setValue(FieldLastName, lastName);} void setSuffix(const QString& suffix) {setValue(FieldSuffix, suffix);} // Convenience filter static QContactFilter match(const QString& name); static QContactFilter match(const QString& firstName, const QString& lastName); }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTNAME_H src/contacts/details/qcontactnickname.h000066400000000000000000000050231233466112000206010ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTNICKNAME_H #define QCONTACTNICKNAME_H #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactNickname : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactNickname) #else static const DetailType Type; #endif enum NicknameField { FieldNickname = 0 }; void setNickname(const QString& nickname) {setValue(FieldNickname, nickname);} QString nickname() const {return value(FieldNickname).toString();} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTNICKNAME_H src/contacts/details/qcontactnote.h000066400000000000000000000047371233466112000177740ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTNOTE_H #define QCONTACTNOTE_H #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactNote : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactNote) #else static const DetailType Type; #endif enum NoteField { FieldNote = 0 }; void setNote(const QString& note) {setValue(FieldNote, note);} QString note() const {return value(FieldNote).toString();} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTNOTE_H src/contacts/details/qcontactonlineaccount.h000066400000000000000000000073361233466112000216660ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTONLINEACCOUNT_H #define QCONTACTONLINEACCOUNT_H #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactOnlineAccount : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactOnlineAccount) #else static const DetailType Type; #endif enum OnlineAccountField { FieldAccountUri = 0, FieldServiceProvider, FieldProtocol, FieldCapabilities, FieldSubTypes }; enum SubType { SubTypeSip = 0, SubTypeSipVoip, SubTypeImpp, SubTypeVideoShare }; enum Protocol { ProtocolUnknown = 0, ProtocolAim, ProtocolIcq, ProtocolIrc, ProtocolJabber, ProtocolMsn, ProtocolQq, ProtocolSkype, ProtocolYahoo }; void setAccountUri(const QString& accountUri) {setValue(FieldAccountUri, accountUri);} QString accountUri() const {return value(FieldAccountUri).toString();} void setServiceProvider(const QString& serviceProvider) {setValue(FieldServiceProvider, serviceProvider);} QString serviceProvider() const {return value(FieldServiceProvider).toString();} void setProtocol(Protocol protocol) {setValue(FieldProtocol, protocol);} Protocol protocol() const {return Protocol(value(FieldProtocol).toInt());} void setCapabilities(const QStringList& capabilities) {setValue(FieldCapabilities, capabilities);} QStringList capabilities() const {return value(FieldCapabilities);} void setSubTypes(const QList &subTypes) {setValue(FieldSubTypes, QVariant::fromValue(subTypes));} QList subTypes() const {return value< QList >(FieldSubTypes);} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTONLINEACCOUNT_H src/contacts/details/qcontactorganization.h000066400000000000000000000071021233466112000215200ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTORGANIZATION_H #define QCONTACTORGANIZATION_H #include #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactOrganization : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactOrganization) #else static const DetailType Type; #endif enum OrganizationField { FieldName = 0, FieldLogoUrl, FieldDepartment, FieldLocation, FieldRole, FieldTitle, FieldAssistantName, }; void setName(const QString& name) {setValue(FieldName, name);} QString name() const {return value(FieldName).toString();} void setLogoUrl(const QUrl& logo) {setValue(FieldLogoUrl, logo);} QUrl logoUrl() const {return value(FieldLogoUrl);} void setDepartment(const QStringList& department) {setValue(FieldDepartment, department);} QStringList department() const {return value(FieldDepartment);} void setLocation(const QString& location) {setValue(FieldLocation, location);} QString location() const {return value(FieldLocation).toString();} void setRole(const QString& role) {setValue(FieldRole, role);} QString role() const {return value(FieldRole).toString();} void setTitle(const QString& title) {setValue(FieldTitle, title);} QString title() const {return value(FieldTitle).toString();} void setAssistantName(const QString& assistantName) {setValue(FieldAssistantName, assistantName);} QString assistantName() const {return value(FieldAssistantName).toString();} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTORGANIZATION_H src/contacts/details/qcontactphonenumber.h000066400000000000000000000062471233466112000213470ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTPHONENUMBER_H #define QCONTACTPHONENUMBER_H #include QT_BEGIN_NAMESPACE_CONTACTS class QContactFilter; /* Leaf class */ //! [0] class Q_CONTACTS_EXPORT QContactPhoneNumber : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactPhoneNumber) #else static const DetailType Type; #endif enum PhoneNumberField { FieldNumber = 0, FieldSubTypes }; enum SubType { SubTypeLandline = 0, SubTypeMobile, SubTypeFax, SubTypePager, SubTypeVoice, SubTypeModem, SubTypeVideo, SubTypeCar, SubTypeBulletinBoardSystem, SubTypeMessagingCapable, SubTypeAssistant, SubTypeDtmfMenu }; void setNumber(const QString& number) {setValue(FieldNumber, number);} QString number() const {return value(FieldNumber).toString();} void setSubTypes(const QList &subTypes) {setValue(FieldSubTypes, QVariant::fromValue(subTypes));} QList subTypes() const {return value< QList >(FieldSubTypes);} // Convenience filter static QContactFilter match(const QString& number); }; //! [0] QT_END_NAMESPACE_CONTACTS #endif // QCONTACTPHONENUMBER_H src/contacts/details/qcontactpresence.h000066400000000000000000000076261233466112000206330ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTPRESENCE_H #define QCONTACTPRESENCE_H #include #include #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactPresence : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactPresence) #else static const DetailType Type; #endif enum PresenceField { FieldTimestamp = 0, FieldNickname, FieldPresenceState, FieldPresenceStateText, FieldPresenceStateImageUrl, FieldCustomMessage }; enum PresenceState { PresenceUnknown = 0, PresenceAvailable, PresenceHidden, PresenceBusy, PresenceAway, PresenceExtendedAway, PresenceOffline }; void setTimestamp(const QDateTime& timestamp) {setValue(FieldTimestamp, timestamp);} QDateTime timestamp() const {return value(FieldTimestamp);} void setNickname(const QString& nickname) {setValue(FieldNickname, nickname);} QString nickname() const {return value(FieldNickname).toString();} void setPresenceState(PresenceState presence) {setValue(FieldPresenceState, static_cast(presence));} PresenceState presenceState() const {return static_cast(value(FieldPresenceState));} void setPresenceStateText(const QString& presenceStateText) {setValue(FieldPresenceStateText, presenceStateText);} QString presenceStateText() const {return value(FieldPresenceStateText).toString();} void setPresenceStateImageUrl(const QUrl& presenceStateImageUrl) {setValue(FieldPresenceStateImageUrl, presenceStateImageUrl);} QUrl presenceStateImageUrl() const {return value(FieldPresenceStateImageUrl);} void setCustomMessage(const QString& customMessage) {setValue(FieldCustomMessage, customMessage);} QString customMessage() const {return value(FieldCustomMessage).toString();} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTPRESENCE_H src/contacts/details/qcontactringtone.h000066400000000000000000000060501233466112000206420ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTRINGTONE_H #define QCONTACTRINGTONE_H #include #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactRingtone : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactRingtone) #else static const DetailType Type; #endif enum RingtoneField { FieldAudioRingtoneUrl = 0, FieldVideoRingtoneUrl, FieldVibrationRingtoneUrl }; void setAudioRingtoneUrl(const QUrl& audioRingtoneUrl) {setValue(FieldAudioRingtoneUrl, audioRingtoneUrl);} QUrl audioRingtoneUrl() const {return value(FieldAudioRingtoneUrl);} void setVideoRingtoneUrl(const QUrl& videoRingtoneUrl) {setValue(FieldVideoRingtoneUrl, videoRingtoneUrl);} QUrl videoRingtoneUrl() const {return value(FieldVideoRingtoneUrl);} void setVibrationRingtoneUrl(const QUrl& vibrationRingtoneUrl) {setValue(FieldVibrationRingtoneUrl, vibrationRingtoneUrl);} QUrl vibrationRingtoneUrl() const {return value(FieldVibrationRingtoneUrl);} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTRINGTONE_H src/contacts/details/qcontactsynctarget.h000066400000000000000000000050551233466112000212040ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTSYNCTARGET_H #define QCONTACTSYNCTARGET_H #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactSyncTarget : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactSyncTarget) #else static const DetailType Type; #endif enum SyncTargetField { FieldSyncTarget = 0 }; void setSyncTarget(const QString& syncTarget) {setValue(FieldSyncTarget, syncTarget);} QString syncTarget() const {return value(FieldSyncTarget).toString();} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTSYNCTARGET_H src/contacts/details/qcontacttag.h000066400000000000000000000050771233466112000176000ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTTAG_H #define QCONTACTTAG_H #include QT_BEGIN_NAMESPACE_CONTACTS class QContactFilter; /* Leaf class */ class Q_CONTACTS_EXPORT QContactTag : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactTag) #else static const DetailType Type; #endif enum TagField { FieldTag = 0 }; void setTag(const QString& tag) {setValue(FieldTag, tag);} QString tag() const {return value(FieldTag).toString();} // Convenience filter static QContactFilter match(const QString& subString); }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTTAG_H src/contacts/details/qcontacttimestamp.h000066400000000000000000000054751233466112000210320ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTTIMESTAMP_H #define QCONTACTTIMESTAMP_H #include #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactTimestamp : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactTimestamp) #else static const DetailType Type; #endif enum TimestampField { FieldModificationTimestamp = 0, FieldCreationTimestamp }; void setLastModified(const QDateTime& timestamp) {setValue(FieldModificationTimestamp, timestamp);} QDateTime lastModified() const {return value(FieldModificationTimestamp);} void setCreated(const QDateTime& timestamp) {setValue(FieldCreationTimestamp, timestamp);} QDateTime created() const {return value(FieldCreationTimestamp);} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTTIMESTAMP_H src/contacts/details/qcontacttype.h000066400000000000000000000052211233466112000177750ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTTYPE_H #define QCONTACTTYPE_H #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactType : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactType) #else static const DetailType Type; #endif enum TypeField { FieldType = 0 }; enum TypeValues { TypeContact = 0, TypeGroup }; void setType(const TypeValues newType) {setValue(FieldType, newType);} TypeValues type() const {return static_cast(value(FieldType));} }; QT_END_NAMESPACE_CONTACTS Q_DECLARE_METATYPE(QTCONTACTS_PREPEND_NAMESPACE(QContactType::TypeValues)) #endif // QCONTACTTYPE_H src/contacts/details/qcontacturl.h000066400000000000000000000056141233466112000176240ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTURL_H #define QCONTACTURL_H #include #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactUrl : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactUrl) #else static const DetailType Type; #endif enum UrlField { FieldUrl = 0, FieldSubType }; enum SubType { SubTypeHomePage = 0, SubTypeBlog, SubTypeFavourite }; void setUrl(const QString& url) {setValue(FieldUrl, url);} void setUrl(const QUrl& url) {setValue(FieldUrl, url.toString());} QString url() const {return value(FieldUrl).toString();} void setSubType(const QContactUrl::SubType &subType) {setValue(FieldSubType, static_cast(subType));} QContactUrl::SubType subType() const {return static_cast(value(FieldSubType));} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTURL_H src/contacts/details/qcontactversion.h000066400000000000000000000054721233466112000205110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTVERSION_H #define QCONTACTVERSION_H #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ class Q_CONTACTS_EXPORT QContactVersion : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactVersion) #else static const DetailType Type; #endif enum VersionField { FieldSequenceNumber = 0, FieldExtendedVersion }; void setSequenceNumber(int sequenceNumber) {setValue(FieldSequenceNumber, sequenceNumber);}; int sequenceNumber() const {return value(FieldSequenceNumber).toInt();}; void setExtendedVersion(const QByteArray &extendedVersion) { setValue(FieldExtendedVersion, extendedVersion); }; QByteArray extendedVersion() const { return value(FieldExtendedVersion).toByteArray(); }; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTVERSION_H src/contacts/doc/000077500000000000000000000000001233466112000142265ustar00rootroot00000000000000src/contacts/doc/qtcontacts.qdocconf000066400000000000000000000035631233466112000201360ustar00rootroot00000000000000include($QT_INSTALL_DOCS/global/qt-html-templates-offline.qdocconf) include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) project = QtContacts description = Qt Contacts Reference Documentation url = http://qt-project.org/doc/qt-5.0/qtcontacts-index.html version = 5.0.0 qhp.projects = QtContacts qhp.QtContacts.file = qtcontacts.qhp qhp.QtContacts.namespace = org.qt-project.qtcontacts.500 qhp.QtContacts.virtualFolder = qdoc qhp.QtContacts.indexTitle = Qt Contacts Reference Documentation qhp.QtContacts.indexRoot = qhp.QtContacts.filterAttributes = qtcontacts 5.0.0 qtrefdoc qhp.QtContacts.customFilters.Qt.name = QtContacts 5.0.0 qhp.QtContacts.customFilters.Qt.filterAttributes = qtcontacts 5.0.0 qhp.QtContacts.subprojects = classes overviews examples qhp.QtContacts.subprojects.classes.title = Classes qhp.QtContacts.subprojects.classes.indexTitle = Qt Contacts's Classes qhp.QtContacts.subprojects.classes.selectors = class fake:headerfile qhp.QtContacts.subprojects.classes.sortPages = true qhp.QtContacts.subprojects.overviews.title = Overviews qhp.QtContacts.subprojects.overviews.indexTitle = All Overviews and HOWTOs qhp.QtContacts.subprojects.overviews.selectors = fake:page,group,module qhp.QtContacts.subprojects.examples.title = Qt Contacts Examples qhp.QtContacts.subprojects.examples.indexTitle = Qt Contacts Examples qhp.QtContacts.subprojects.examples.selectors = fake:example tagfile = ../../../doc/qtcontacts/qtcontacts.tags headerdirs += .. \ ../../imports/contacts \ ../../plugins/contacts sourcedirs += .. \ ../../imports/contacts \ ../../plugins/contacts exampledirs += ../../../examples/contacts \ snippets/ imagedirs += images src/contacts/doc/snippets/000077500000000000000000000000001233466112000160735ustar00rootroot00000000000000src/contacts/doc/snippets/doc_src_qtcontacts.cpp000066400000000000000000000041621233466112000224610ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //! [include] #include //! [include] //! [namespace] QT_BEGIN_NAMESPACE_CONTACTS QT_END_NAMESPACE_CONTACTS QTCONTACTS_USE_NAMESPACE //! [namespace] src/contacts/doc/snippets/doc_src_qtcontacts.pro000066400000000000000000000040451233466112000224770ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #! [contacts project modification] QT += contacts #! [contacts project modification] src/contacts/doc/snippets/moduleimports.qml000066400000000000000000000040411233466112000215100ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //! [Contacts import] import QtContacts 5.0 //! [Contacts import] Rectangle { } src/contacts/doc/snippets/multiaction/000077500000000000000000000000001233466112000204235ustar00rootroot00000000000000src/contacts/doc/snippets/multiaction/multiaction.cpp000066400000000000000000000207341233466112000234650ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef ACTIONFACTORYPLUGINTARGET #define ACTIONFACTORYPLUGINTARGET contacts_multiactionfactory #endif #ifndef ACTIONFACTORYPLUGINNAME #define ACTIONFACTORYPLUGINNAME MultiActionFactory #endif #include "multiaction_p.h" #include #include #include #include #include //! [Example Contact Action Plugin Implementation] QObject* QContactMultiActionPlugin::createInstance(const QServiceInterfaceDescriptor& descriptor, QServiceContext* context, QAbstractSecuritySession* session) { Q_UNUSED(context); Q_UNUSED(session); if (descriptor.interfaceName() == QContactActionFactory::InterfaceName && descriptor.serviceName() == QString(QLatin1String("tst_qcontactactions:multiaction")) && descriptor.majorVersion() == 1 && descriptor.minorVersion() == 1 && descriptor.customAttribute("ActionName") == QString(QLatin1String("call"))) { return new QContactMultiActionFactory; } else { return 0; } } Q_EXPORT_PLUGIN2(contacts_multiaction, QContactMultiActionPlugin); QContactMultiActionFactory::QContactMultiActionFactory() : QContactActionFactory() { m_actionOneDescriptor = createDescriptor("call", "tst_qcontactactions:multiaction", "sip", 1); m_actionTwoDescriptor = createDescriptor("call", "tst_qcontactactions:multiaction", "prop", 1); } QContactMultiActionFactory::~QContactMultiActionFactory() { } QList QContactMultiActionFactory::actionDescriptors() const { QList retn; retn << m_actionOneDescriptor << m_actionTwoDescriptor; return retn; } QContactAction* QContactMultiActionFactory::create(const QContactActionDescriptor& which) const { if (which == m_actionOneDescriptor) return new QContactActionOne; else if (which == m_actionTwoDescriptor) return new QContactActionTwo; else return 0; } QSet QContactMultiActionFactory::supportedTargets(const QContact& contact, const QContactActionDescriptor& which) const { QSet retn; if (which == m_actionOneDescriptor || which == m_actionTwoDescriptor) { // in this example, they support the same targets. QList pndets = contact.details(); for (int i = 0; i < pndets.size(); ++i) { QContactActionTarget curr; curr.setContact(contact); curr.setDetails(QList() << pndets.at(i)); retn << curr; } } return retn; } QContactFilter QContactMultiActionFactory::contactFilter(const QContactActionDescriptor& which) const { if (which == m_actionOneDescriptor || which == m_actionTwoDescriptor) { QContactDetailFilter retn; retn.setDetailDefinitionName(QContactPhoneNumber::DefinitionName, QContactPhoneNumber::FieldNumber); return retn; } return QContactFilter(); } QVariant QContactMultiActionFactory::metaData(const QString& key, const QList& targets, const QVariantMap& parameters, const QContactActionDescriptor& which) const { Q_UNUSED(targets) Q_UNUSED(parameters) if (key == QContactActionDescriptor::MetaDataLabel) return QString("Call with VoIP"); // Label etc if (key == QLatin1String("Provider")) {// our custom metadata - just return which.actionIdentifier return which.actionIdentifier(); } return QVariant(); } bool QContactMultiActionFactory::supportsContact(const QContact& contact, const QContactActionDescriptor& which) const { if (which == m_actionOneDescriptor || which == m_actionTwoDescriptor) return !contact.details().isEmpty(); return false; } QContactActionOne::QContactActionOne() { } QContactActionOne::~QContactActionOne() { } bool QContactActionOne::invokeAction(const QContactActionTarget& target, const QVariantMap& params) { Q_UNUSED(params) // this action only works on (contact + phone number) targets. if (target.details().size() > 1 || target.details().at(0).definitionName() != QContactPhoneNumber::DefinitionName) return false; QTimer::singleShot(1, this, SLOT(performAction())); return true; } bool QContactActionOne::invokeAction(const QList& targets, const QVariantMap& params) { Q_UNUSED(params) foreach (const QContactActionTarget& target, targets) { if (target.details().size() > 1 || target.details().at(0).definitionName() != QContactPhoneNumber::DefinitionName) { return false; } } QTimer::singleShot(1, this, SLOT(performAction())); return true; } QVariantMap QContactActionOne::results() const { return QVariantMap(); } QContactAction::State QContactActionOne::state() const { return QContactAction::FinishedState; } void QContactActionOne::performAction() { QMessageBox::information(0, "ActionOne", "This is action one!"); emit stateChanged(QContactAction::FinishedState); } QContactActionTwo::QContactActionTwo() { } QContactActionTwo::~QContactActionTwo() { } bool QContactActionTwo::invokeAction(const QContactActionTarget& target, const QVariantMap& params) { Q_UNUSED(params) // this action only works on (contact + phone number) targets. Note that it doesn't // have to be the same as QContactActionOne -- it could have an entirely different implementation! if (target.details().size() > 1 || target.details().at(0).definitionName() != QContactPhoneNumber::DefinitionName) return false; QTimer::singleShot(1, this, SLOT(performAction())); return true; } bool QContactActionTwo::invokeAction(const QList& targets, const QVariantMap& params) { Q_UNUSED(params) foreach (const QContactActionTarget& target, targets) { if (target.details().size() > 1 || target.details().at(0).definitionName() != QContactPhoneNumber::DefinitionName) { return false; } } QTimer::singleShot(1, this, SLOT(performAction())); return true; } QVariantMap QContactActionTwo::results() const { return QVariantMap(); } QContactAction::State QContactActionTwo::state() const { return QContactAction::FinishedState; } void QContactActionTwo::performAction() { QMessageBox::information(0, "ActionTwo", "This is action two!"); emit stateChanged(QContactAction::FinishedState); } //! [Example Contact Action Plugin Implementation] src/contacts/doc/snippets/multiaction/multiaction_p.h000066400000000000000000000136741233466112000234560ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTMULTIACTION_P_H #define QCONTACTMULTIACTION_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include "qserviceinterfacedescriptor.h" #include "qserviceplugininterface.h" #include "qservicecontext.h" #include "qabstractsecuritysession.h" #include #include #include QTM_USE_NAMESPACE //! [Example Contact Action Plugin Declaration] /* This action plugin is capable of producing two actions which each have the same action name, service name, interface name and implementation (minor) version, but internally use a different implementation. This difference is reported via the meta data function of the factory (which is exposed to clients via the descriptor which provides a "front end" to the factory). Example use case: Company "Example VoIP Solutions" wants to provide a "Call" action with different implementations. -> it provides a SINGLE plugin which provides two actions, both of which are: - ServiceName = "Example VoIP Solution" - InterfaceName = "org.qt-project.Qt.SampleContactsActionPlugin" (QContactActionFactory::InterfaceName) - Major Version = "1" - Minor Version = "1" - ActionName = "call" (this is a custom property in the service interface xml) -> BUT one of the actions has the custom property: - Provider = "sip" -> where the other action has the custom property: - Provider = "example proprietary protocol" -> the custom properties are available to clients via the QContactActionDescriptor::metaData() function. */ class QContactMultiActionPlugin : public QObject, public QServicePluginInterface { Q_OBJECT Q_INTERFACES(QtMobility::QServicePluginInterface) public: QObject* createInstance(const QServiceInterfaceDescriptor& descriptor, QServiceContext* context, QAbstractSecuritySession* session); }; class QContactMultiActionFactory : public QContactActionFactory { Q_OBJECT public: QContactMultiActionFactory(); ~QContactMultiActionFactory(); QList actionDescriptors() const; QContactAction* create(const QContactActionDescriptor& which) const; QSet supportedTargets(const QContact& contact, const QContactActionDescriptor& which) const; QContactFilter contactFilter(const QContactActionDescriptor& which) const; QVariant metaData(const QString& key, const QList& targets, const QVariantMap& parameters, const QContactActionDescriptor& which) const; bool supportsContact(const QContact& contact, const QContactActionDescriptor& which) const; private: QContactActionDescriptor m_actionOneDescriptor; QContactActionDescriptor m_actionTwoDescriptor; }; class QContactActionOne : public QContactAction { Q_OBJECT public: QContactActionOne(); ~QContactActionOne(); bool invokeAction(const QContactActionTarget& target, const QVariantMap& params = QVariantMap()); bool invokeAction(const QList& targets, const QVariantMap& params = QVariantMap()); QVariantMap results() const; State state() const; private slots: void performAction(); }; class QContactActionTwo : public QContactAction { Q_OBJECT public: QContactActionTwo(); ~QContactActionTwo(); bool invokeAction(const QContactActionTarget& target, const QVariantMap& params = QVariantMap()); bool invokeAction(const QList& targets, const QVariantMap& params = QVariantMap()); QVariantMap results() const; State state() const; private slots: void performAction(); }; //! [Example Contact Action Plugin Declaration] #endif src/contacts/doc/snippets/qcontactphonenumber.h000066400000000000000000000063671233466112000223370ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTPHONENUMBER_H #define QCONTACTPHONENUMBER_H #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS /* Leaf class */ //! [0] class Q_CONTACTS_EXPORT QContactPhoneNumber : public QContactDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_CONTACT_DETAIL(QContactPhoneNumber) #else static const DetailType Type; #endif enum PhoneNumberField { FieldNumber = 0, FieldSubTypes }; enum SubType { SubTypeLandline = 0, SubTypeMobile, SubTypeFax, SubTypePager, SubTypeVoice, SubTypeModem, SubTypeVideo, SubTypeCar, SubTypeBulletinBoardSystem, SubTypeMessagingCapable, SubTypeAssistant, SubTypeDtmfMenu }; void setNumber(const QString& number) {setValue(FieldNumber, number);} QString number() const {return value(FieldNumber).toString();} void setSubTypes(const QList &subTypes) {setValue(FieldSubTypes, QVariant::fromValue(subTypes));} QList subTypes() const {return value< QList >(FieldSubTypes);} // Convenience filter static QContactFilter match(const QString& number); }; //! [0] QT_END_NAMESPACE_CONTACTS #endif src/contacts/doc/snippets/qtcontactsdocsample/000077500000000000000000000000001233466112000221465ustar00rootroot00000000000000src/contacts/doc/snippets/qtcontactsdocsample/qtcontactsdocsample.cpp000066400000000000000000000553701233466112000267370ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include "requestexample.h" #include #include #include #include QTCONTACTS_USE_NAMESPACE static void loadDefault(); static void queryManagerCapabilities(); static void contactDetailManipulation(); static void contactManipulation(); static void addContact(QContactManager*); static void callContact(QContactManager*); static void matchCall(QContactManager*, const QString&); static void viewSpecificDetail(QContactManager*); static void viewDetails(QContactManager*); static void detailSharing(QContactManager*); static void addPlugin(QContactManager*); static void editView(QContactManager*); static void loadManager(); static void loadManagerWithParameters(); int stopCompilerWarnings() { // manager configuration examples loadManager(); loadManagerWithParameters(); // synchronous API examples QContactManager* cm = new QContactManager(); addContact(cm); callContact(cm); matchCall(cm, "111-222-333"); // unknown number. matchCall(cm, "12345678"); // alice's number. viewSpecificDetail(cm); viewDetails(cm); detailSharing(cm); addPlugin(cm); editView(cm); // asynchronous API example RequestExample re; re.setManager(cm); QTimer::singleShot(10, &re, SLOT(performRequest())); delete cm; // more doc snippet examples loadDefault(); queryManagerCapabilities(); contactDetailManipulation(); contactManipulation(); // async doc snippet examples AsyncRequestExample example; QTimer::singleShot(10, &example, SLOT(performRequests())); return 0; } void loadDefault() { //! [Loading the default manager for the platform] QContactManager stackDefaultContactManager; //! [Loading the default manager for the platform] //! [Loading the default manager for the platform on heap] QContactManager *heapDefaultContactManager = new QContactManager; // ... perform contact manipulation delete heapDefaultContactManager; //! [Loading the default manager for the platform on heap] } void queryManagerCapabilities() { //! [Querying a manager for capabilities] QContactManager cm; qDebug() << "The default manager for the platform is:" << cm.managerName(); qDebug() << "It" << (cm.isRelationshipTypeSupported(QContactRelationship::HasAssistant()) ? "supports" : "does not support") << "assistant relationships."; qDebug() << "It" << (cm.supportedContactTypes().contains(QContactType::TypeGroup) ? "supports" : "does not support") << "groups."; //! [Querying a manager for capabilities] } void contactDetailManipulation() { //! [Adding a detail to a contact] QContact exampleContact; QContactName nameDetail; nameDetail.setFirstName("Adam"); nameDetail.setLastName("Unlikely"); QContactPhoneNumber phoneNumberDetail; phoneNumberDetail.setNumber("+123 4567"); exampleContact.saveDetail(&nameDetail); exampleContact.saveDetail(&phoneNumberDetail); //! [Adding a detail to a contact] //! [Updating a detail in a contact] phoneNumberDetail.setNumber("+123 9876"); exampleContact.saveDetail(&phoneNumberDetail); // overwrites old value on save //! [Updating a detail in a contact] //! [Removing a detail from a contact] exampleContact.removeDetail(&phoneNumberDetail); //! [Removing a detail from a contact] } void contactManipulation() { QContactManager m_manager("memory"); //! [Synchronously creating a new contact in a manager] QContact exampleContact; QContactName nameDetail; nameDetail.setFirstName("Adam"); nameDetail.setLastName("Unlikely"); QContactPhoneNumber phoneNumberDetail; phoneNumberDetail.setNumber("+123 4567"); exampleContact.saveDetail(&nameDetail); exampleContact.saveDetail(&phoneNumberDetail); // save the newly created contact in the manager if (!m_manager.saveContact(&exampleContact)) qDebug() << "Error" << m_manager.error() << "occurred whilst saving contact!"; //! [Synchronously creating a new contact in a manager] //! [Synchronously filtering contacts from a manager] QList results = m_manager.contacts(QContactPhoneNumber::match("+123 4567")); //! [Synchronously filtering contacts from a manager] //! [Synchronously retrieving an existing contact from a manager] QContact existing = m_manager.contact(exampleContact.id()); //! [Synchronously retrieving an existing contact from a manager] //! [Synchronously updating an existing contact in a manager] phoneNumberDetail.setNumber("+123 9876"); exampleContact.saveDetail(&phoneNumberDetail); m_manager.saveContact(&exampleContact); //! [Synchronously updating an existing contact in a manager] //! [Synchronously removing a contact from a manager] m_manager.removeContact(exampleContact.id()); //! [Synchronously removing a contact from a manager] //! [Synchronously creating a new relationship between two contacts] // first, create the group and the group member QContact exampleGroup; exampleGroup.setType(QContactType::TypeGroup); QContactNickname groupName; groupName.setNickname("Example Group"); exampleGroup.saveDetail(&groupName); QContact exampleGroupMember; QContactName groupMemberName; groupMemberName.setFirstName("Member"); exampleGroupMember.saveDetail(&groupMemberName); // second, save those contacts in the manager QMap errorMap; QList saveList; saveList << exampleGroup << exampleGroupMember; m_manager.saveContacts(&saveList, &errorMap); // third, create the relationship between those contacts QContactRelationship groupRelationship; groupRelationship.setFirst(exampleGroup); groupRelationship.setRelationshipType(QContactRelationship::HasMember()); groupRelationship.setSecond(exampleGroupMember); // finally, save the relationship in the manager m_manager.saveRelationship(&groupRelationship); //! [Synchronously creating a new relationship between two contacts] //! [Synchronously retrieving relationships between contacts] QList groupRelationships = m_manager.relationships(exampleGroup, QContactRelationship::First); QList result; for (int i = 0; i < groupRelationships.size(); i++) { if (groupRelationships.at(i).second() == exampleGroupMember) { result.append(groupRelationships.at(i)); } } //! [Synchronously retrieving relationships between contacts] //! [Retrieving relationships from cache] exampleGroup = m_manager.contact(exampleGroup.id()); // refresh the group contact groupRelationships = exampleGroup.relationships(QContactRelationship::HasMember()); for (int i = 0; i < groupRelationships.size(); i++) { if (groupRelationships.at(i).second() == exampleGroupMember) { result.append(groupRelationships.at(i)); } } //! [Retrieving relationships from cache] //! [Synchronously providing a fetch hint] QContactFetchHint hasMemberRelationshipsOnly; hasMemberRelationshipsOnly.setRelationshipTypesHint(QStringList(QContactRelationship::HasMember())); // retrieve all contacts, with no specified sort order, requesting that // HasMember relationships be included in the cache of result contacts QList allContacts = m_manager.contacts(QContactFilter(), QList(), hasMemberRelationshipsOnly); //! [Synchronously providing a fetch hint] //! [Synchronously removing a relationship] m_manager.removeRelationship(groupRelationship); //! [Synchronously removing a relationship] } //! [Creating a new contact] void addContact(QContactManager* cm) { QContact alice; QContactDisplayLabel displayLabel; displayLabel.setLabel("Ally Jones"); alice.saveDetail(&displayLabel); /* Set the contact's name */ QContactName aliceName; aliceName.setFirstName("Alice"); aliceName.setLastName("Jones"); alice.saveDetail(&aliceName); /* Add a phone number */ QContactPhoneNumber number; QList subTypeMobile; subTypeMobile << QContactPhoneNumber::SubTypeMobile; number.setSubTypes(subTypeMobile); number.setContexts(QContactDetail::ContextHome); number.setSubTypes(subTypeMobile); number.setNumber("12345678"); alice.saveDetail(&number); alice.setPreferredDetail("DialAction", number); /* Add a second phone number */ QContactPhoneNumber number2; QList subTypeLandline; subTypeLandline << QContactPhoneNumber::SubTypeMobile; number.setSubTypes(subTypeLandline); number2.setContexts(QContactDetail::ContextWork); number2.setSubTypes(subTypeLandline); number2.setNumber("555-4444"); alice.saveDetail(&number2); /* Save the contact */ cm->saveContact(&alice) ? qDebug() << "Successfully saved" : qDebug() << "Failed to save"; qDebug() << "The display label for the contact:" << alice.details(QContactDisplayLabel::Type).value(1).value(QContactDisplayLabel::FieldLabel); } //! [Creating a new contact] void callContact(QContactManager* cm) { QList contactIds = cm->contactIds(); QContact a = cm->contact(contactIds.first()); /* Get this contact's first phone number */ QContact contact; //! [Details with action] // Get the first "Call" action QContactActionDescriptor callDescriptor = QContactAction::actionDescriptors("Call").value(0); QContactAction* action = QContactAction::action(callDescriptor); QSet targets = callDescriptor.supportedTargets(contact); if (targets.count() == 0) { // Can't call this contact } else if (targets.count() == 1) { // Just call this specific detail action->invokeAction(*targets.begin()); } else { // Offer the user the choice of details to call // ... } //! [Details with action] } //! [Filtering by definition and value] void matchCall(QContactManager* cm, const QString& incomingCallNbr) { QContactDetailFilter phoneFilter; phoneFilter.setDetailType(QContactPhoneNumber::Type, QContactPhoneNumber::FieldNumber); phoneFilter.setValue(incomingCallNbr); phoneFilter.setMatchFlags(QContactFilter::MatchExactly); QList matchingContacts = cm->contactIds(phoneFilter); if (matchingContacts.size() == 0) { qDebug() << "Incoming call from unknown contact (" << incomingCallNbr << ")"; } else { QContact match = cm->contact(matchingContacts.at(0)); QContactDisplayLabel displayLabel; displayLabel.setLabel("Match"); match.saveDetail(&displayLabel); qDebug() << "Incoming call from" << match.details(QContactDisplayLabel::Type).value(0).value(QContactDisplayLabel::FieldLabel) << "(" << incomingCallNbr << ")"; } } //! [Filtering by definition and value] //! [Viewing a specific detail of a contact] void viewSpecificDetail(QContactManager* cm) { QList contactIds = cm->contactIds(); QContact exampleContact = cm->contact(contactIds.first()); qDebug() << "The first phone number is" << exampleContact.detail(QContactPhoneNumber::Type).value(QContactPhoneNumber::FieldNumber); } //! [Viewing a specific detail of a contact] //! [Viewing the details of a contact] void viewDetails(QContactManager* cm) { QList contactIds = cm->contactIds(); QContact exampleContact = cm->contact(contactIds.first()); QList allDetails = exampleContact.details(); for (int i = 0; i < allDetails.size(); i++) { QContactDetail detail = allDetails.at(i); QMap fields = detail.values(); qDebug("\tDetail #%d (%d):", i, detail.type()); foreach (const int& fieldKey, fields.keys()) { qDebug() << "\t\t" << fieldKey << "(" << fields.value(fieldKey) << ") =" << detail.value(fieldKey); } qDebug(); } } //! [Viewing the details of a contact] //! [Demonstration of detail sharing semantics] void detailSharing(QContactManager* cm) { QList contactIds = cm->contactIds(); QContact a = cm->contact(contactIds.first()); /* Create a new phone number detail. */ QContactPhoneNumber newNumber; newNumber.setNumber("123123123"); qDebug() << "\tThe new phone number is" << newNumber.number(); /* * Create a copy of that detail. These will be implicitly shared; * changes to nnCopy will not affect newNumber, and vice versa. * However, attempting to save them will cause overwrite to occur. * Removal is done purely via key() checking, also. */ QContactPhoneNumber nnCopy(newNumber); nnCopy.setNumber("456456456"); qDebug() << "\tThat number is still" << newNumber.number() << ", the copy is" << nnCopy.number(); /* Save the detail in the contact, then remove via the copy, then resave. */ a.saveDetail(&newNumber); a.removeDetail(&nnCopy); // identical to a.removeDetail(&newNumber); a.saveDetail(&newNumber); // since newNumber.key() == nnCopy.key(); /* Saving will cause overwrite */ qDebug() << "\tPrior to saving nnCopy has" << a.details().count() << "details."; a.saveDetail(&nnCopy); qDebug() << "\tAfter saving nnCopy still has" << a.details().count() << "details."; /* In order to save nnCopy as a new detail, we must reset its key */ nnCopy.resetKey(); qDebug() << "\tThe copy key is now" << nnCopy.key() << ", whereas the original key is" << newNumber.key(); qDebug() << "\tPrior to saving (key reset) nnCopy has" << a.details().count() << "details."; a.saveDetail(&nnCopy); qDebug() << "\tAfter saving (key reset) nnCopy still has" << a.details().count() << "details."; a.removeDetail(&nnCopy); /* * Note that changes made to details are not * propagated automatically to the contact. * To persist changes to a detail, you must call saveDetail(). */ QList allNumbers = a.details(); foreach (const QContactPhoneNumber& savedPhn, allNumbers) { if (savedPhn.key() != newNumber.key()) { continue; } /* * This phone number is the saved copy of the newNumber detail. * It is detached from the newNumber detail, so changes to newNumber * shouldn't affect savedPhn until saveDetail() is called again. */ qDebug() << "\tCurrently, the (stack) newNumber is" << newNumber.number() << ", and the saved newNumber is" << savedPhn.number(); newNumber.setNumber("678678678"); qDebug() << "\tNow, the (stack) newNumber is" << newNumber.number() << ", but the saved newNumber is" << savedPhn.number(); } /* * Removal of the detail depends only on the key of the detail; the fact * that the values differ is not taken into account by the remove operation. */ a.removeDetail(&newNumber) ? qDebug() << "\tSucceeded in removing the temporary detail." : qDebug() << "\tFailed to remove the temporary detail.\n"; } //! [Demonstration of detail sharing semantics] //! [Modifying an existing contact] void editView(QContactManager* cm) { QList contactIds = cm->contactIds(); QContact a = cm->contact(contactIds.first()); /* Change the first phone number */ QList numbers = a.details(QContactPhoneNumber::Type); QContactPhoneNumber phone = numbers.value(0); phone.setNumber("123-4445"); /* Add an email address */ QContactEmailAddress email; email.setEmailAddress("alice.jones@example"); email.setContexts(QContactDetail::ContextHome); int emailField = QContactEmailAddress::FieldEmailAddress; email.setValue(emailField, "Alice's Work Email Address"); /* Save the updated details to the contact. */ a.saveDetail(&phone); a.saveDetail(&email); /* Now we must save the updated contact back to the database. */ cm->saveContact(&a); viewDetails(cm); } //! [Modifying an existing contact] void displayLabel() { QContactManager *manager = new QContactManager(); QContactId myId; //! [Updating the display label of a contact] /* Retrieve a contact */ QContact exampleContact = manager->contact(myId); /* Set the display label */ QContactDisplayLabel displayLabel; displayLabel.setLabel("Abigail Arkansas"); exampleContact.saveDetail(&displayLabel); /* Update some fields that might influence the display label */ QContactName name = exampleContact.detail(); name.setFirstName("Abigail"); name.setLastName("Arkansas"); exampleContact.saveDetail(&name); qDebug() << "The display label for the contact:" << exampleContact.details(QContactDisplayLabel::Type).value(1).value(QContactDisplayLabel::FieldLabel); //! [Updating the display label of a contact] } //! [Asynchronous contact request] void RequestExample::performRequest() { // retrieve any contact whose first name is "Alice" QContactDetailFilter dfil; dfil.setDetailType(QContactName::Type, QContactName::FieldFirstName); dfil.setValue("Alice"); dfil.setMatchFlags(QContactFilter::MatchExactly); // m_fetchRequest was created with m_fetchRequest = new QContactFetchRequest() in the ctor. m_fetchRequest->setManager(this->m_manager); // m_manager is a QContactManager*. m_fetchRequest->setFilter(dfil); connect(m_fetchRequest, SIGNAL(resultsAvailable()), this, SLOT(printContacts())); connect(m_fetchRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(stateChanged(QContactAbstractRequest::State))); if (!m_fetchRequest->start()) { qDebug() << "Unable to request contacts!"; QCoreApplication::exit(0); } else { qDebug() << "Requested contacts; awaiting results..."; } } void RequestExample::printContacts() { QList results = m_fetchRequest->contacts(); for (m_previousLastIndex = 0; m_previousLastIndex < results.size(); ++m_previousLastIndex) { qDebug() << "Found an Alice:" << results.at(m_previousLastIndex); } } void RequestExample::stateChanged(QContactAbstractRequest::State state) { // once we've finished retrieving results, stop processing events. if (state == QContactAbstractRequest::FinishedState || state == QContactAbstractRequest::CanceledState) { qDebug() << "Finished displaying asynchronously retrieved contacts!"; QCoreApplication::exit(0); } } //! [Asynchronous contact request] void shortsnippets() { QContact contact; QContact groupContact; { //! [0] QContactDetail detail = contact.detail(QContactName::Type); //! [0] //! [1] QContactName name = contact.detail(); //! [1] //! [2] QList details = contact.details(QContactPhoneNumber::Type); //! [2] //! [3] QList phoneNumbers = contact.details(); //! [3] //! [4] //QList homePhones = contact.details(QContactPhoneNumber::Type).value(0).value(QContactPhoneNumber::FieldContext); //! [4] //! [5] QList spouseRelationships = contact.relationships(QContactRelationship::HasSpouse()); // For each relationship in spouseRelationships, contact.id() will either be first() or second() //! [5] //! [6] // Who are the members of a group contact? QList groupMembers = groupContact.relatedContacts(QContactRelationship::HasMember(), QContactRelationship::Second); // What groups is this contact in? QList contactGroups = contact.relatedContacts(QContactRelationship::HasMember(), QContactRelationship::First); // An alternative to QContact::relationships() QList spouses = contact.relatedContacts(QContactRelationship::HasSpouse(), QContactRelationship::Either); if (spouses.count() > 1) { // Custom relationship type QList therapists = contact.relatedContacts("HasTherapist", QContactRelationship::Second); } //! [6] //! [Getting all tags] QSet tags; foreach (const QContactTag& tag, contact.details()) { tags.insert(tag.tag()); } //! [Getting all tags] //! [Checking for a specific tag] if (contact.details().count() > 0) { // Do something with it } //! [Checking for a specific tag] } } void loadManager() { //! [Loading a specific manager backend] QContactManager contactManager("KABC"); //! [Loading a specific manager backend] } void loadManagerWithParameters() { //! [Loading a specific manager backend with parameters] QMap parameters; parameters.insert("Settings", "~/.qcontactmanager-kabc-settings.ini"); QContactManager contactManager("KABC", parameters); //! [Loading a specific manager backend with parameters] } src/contacts/doc/snippets/qtcontactsdocsample/qtcontactsdocsample.pro000066400000000000000000000011251233466112000267420ustar00rootroot00000000000000###################################################################### # # Simple example of how to use the contacts API # ###################################################################### TEMPLATE = lib TARGET = qtcontactsdocsample INCLUDEPATH += ../../../../src/global \ ../../../../src/contacts \ ../../../../src/contacts/requests \ ../../../../src/contacts/filters \ ../../../../src/contacts/details CONFIG += console QT += contacts SOURCES += qtcontactsdocsample.cpp qtcontactsdocsampleasync.cpp HEADERS += requestexample.h src/contacts/doc/snippets/qtcontactsdocsample/qtcontactsdocsampleasync.cpp000066400000000000000000000257211233466112000277720ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "requestexample.h" #include #include #include #include #include QTCONTACTS_USE_NAMESPACE AsyncRequestExample::AsyncRequestExample() : QObject() { m_manager = new QContactManager("memory"); } AsyncRequestExample::~AsyncRequestExample() { delete m_manager; } //! [Example of an asynchronous request slot] void AsyncRequestExample::contactFetchRequestStateChanged(QContactAbstractRequest::State newState) { if (newState == QContactAbstractRequest::FinishedState) { QContactFetchRequest *request = qobject_cast(QObject::sender()); if (request->error() != QContactManager::NoError) { qDebug() << "Error" << request->error() << "occurred during fetch request!"; return; } QList results = request->contacts(); for (int i = 0; i < results.size(); i++) { qDebug() << "Retrieved contact:" << results.at(i); } } else if (newState == QContactAbstractRequest::CanceledState) { qDebug() << "Fetch operation canceled!"; } } //! [Example of an asynchronous request slot] void AsyncRequestExample::contactSaveRequestStateChanged(QContactAbstractRequest::State newState) { if (newState == QContactAbstractRequest::FinishedState) qDebug() << "Finished saving the contacts!"; else if (newState == QContactAbstractRequest::CanceledState) qDebug() << "Save operation canceled!"; } void AsyncRequestExample::contactRemoveRequestStateChanged(QContactAbstractRequest::State newState) { if (newState == QContactAbstractRequest::FinishedState) qDebug() << "Finished removing the contacts!"; else if (newState == QContactAbstractRequest::CanceledState) qDebug() << "Remove operation canceled!"; } void AsyncRequestExample::relationshipFetchRequestStateChanged(QContactAbstractRequest::State newState) { if (newState == QContactAbstractRequest::FinishedState) qDebug() << "Finished fetching the contacts!"; else if (newState == QContactAbstractRequest::CanceledState) qDebug() << "Fetch operation canceled!"; } void AsyncRequestExample::relationshipSaveRequestStateChanged(QContactAbstractRequest::State newState) { if (newState == QContactAbstractRequest::FinishedState) qDebug() << "Finished saving the contacts!"; else if (newState == QContactAbstractRequest::CanceledState) qDebug() << "Save operation canceled!"; } void AsyncRequestExample::relationshipRemoveRequestStateChanged(QContactAbstractRequest::State newState) { if (newState == QContactAbstractRequest::FinishedState) qDebug() << "Finished removing the contacts!"; else if (newState == QContactAbstractRequest::CanceledState) qDebug() << "Remove operation canceled!"; } void AsyncRequestExample::performRequests() { //! [Creating a new contact in a manager] QContact exampleContact; QContactName nameDetail; nameDetail.setFirstName("Adam"); nameDetail.setLastName("Unlikely"); QContactPhoneNumber phoneNumberDetail; phoneNumberDetail.setNumber("+123 4567"); exampleContact.saveDetail(&nameDetail); exampleContact.saveDetail(&phoneNumberDetail); // save the newly created contact in the manager connect(&m_contactSaveRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(contactSaveRequestStateChanged(QContactAbstractRequest::State))); m_contactSaveRequest.setManager(m_manager); m_contactSaveRequest.setContacts(QList() << exampleContact); m_contactSaveRequest.start(); //! [Creating a new contact in a manager] m_contactSaveRequest.waitForFinished(); //! [Creating a new contact in a manager waiting until finished] m_contactSaveRequest.setManager(m_manager); m_contactSaveRequest.setContacts(QList() << exampleContact); m_contactSaveRequest.start(); m_contactSaveRequest.waitForFinished(); QList savedContacts = m_contactSaveRequest.contacts(); //! [Creating a new contact in a manager waiting until finished] //! [Filtering contacts from a manager] connect(&m_contactFetchRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(contactFetchRequestStateChanged(QContactAbstractRequest::State))); m_contactFetchRequest.setManager(m_manager); m_contactFetchRequest.setFilter(QContactPhoneNumber::match("+123 4567")); m_contactFetchRequest.start(); //! [Filtering contacts from a manager] m_contactFetchRequest.waitForFinished(); //! [Retrieving an existing contact from a manager] QContactIdFilter idListFilter; idListFilter.setIds(QList() << exampleContact.id()); m_contactFetchRequest.setManager(m_manager); m_contactFetchRequest.setFilter(idListFilter); m_contactFetchRequest.start(); //! [Retrieving an existing contact from a manager] m_contactFetchRequest.waitForFinished(); //! [Updating an existing contact in a manager] phoneNumberDetail.setNumber("+123 9876"); exampleContact.saveDetail(&phoneNumberDetail); m_contactSaveRequest.setManager(m_manager); m_contactSaveRequest.setContacts(QList() << exampleContact); m_contactSaveRequest.start(); //! [Updating an existing contact in a manager] m_contactFetchRequest.waitForFinished(); //! [Removing a contact from a manager] connect(&m_contactRemoveRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(contactRemoveRequestStateChanged(QContactAbstractRequest::State))); m_contactRemoveRequest.setManager(m_manager); m_contactRemoveRequest.setContactIds(QList() << exampleContact.id()); m_contactRemoveRequest.start(); //! [Removing a contact from a manager] m_contactFetchRequest.waitForFinished(); //! [Creating a new relationship between two contacts] // first, create the group and the group member QContact exampleGroup; exampleGroup.setType(QContactType::TypeGroup); QContactNickname groupName; groupName.setNickname("Example Group"); exampleGroup.saveDetail(&groupName); QContact exampleGroupMember; QContactName groupMemberName; groupMemberName.setFirstName("Member"); exampleGroupMember.saveDetail(&groupMemberName); // second, save those contacts in the manager QList saveList; saveList << exampleGroup << exampleGroupMember; m_contactSaveRequest.setContacts(saveList); m_contactSaveRequest.start(); m_contactSaveRequest.waitForFinished(); // third, create the relationship between those contacts QContactRelationship groupRelationship; groupRelationship.setFirst(exampleGroup); groupRelationship.setRelationshipType(QContactRelationship::HasMember()); groupRelationship.setSecond(exampleGroupMember); // finally, save the relationship in the manager connect(&m_relationshipSaveRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(relationshipSaveRequestStateChanged(QContactAbstractRequest::State))); m_relationshipSaveRequest.setManager(m_manager); m_relationshipSaveRequest.setRelationships(QList() << groupRelationship); m_relationshipSaveRequest.start(); //! [Creating a new relationship between two contacts] m_contactFetchRequest.waitForFinished(); //! [Retrieving relationships between contacts] connect(&m_relationshipFetchRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(relationshipFetchRequestStateChanged(QContactAbstractRequest::State))); m_relationshipFetchRequest.setManager(m_manager); // retrieve the list of relationships between the example group contact and the example member contact // where the group contact is the first contact in the relationship, and the member contact is the // second contact in the relationship. In order to fetch all relationships between them, another // relationship fetch must be performed with their roles reversed, and the results added together. m_relationshipFetchRequest.setFirst(exampleGroup); m_relationshipFetchRequest.setSecond(exampleGroupMember); m_relationshipFetchRequest.start(); //! [Retrieving relationships between contacts] m_contactFetchRequest.waitForFinished(); //! [Providing a fetch hint] QContactFetchHint hasMemberRelationshipsOnly; hasMemberRelationshipsOnly.setRelationshipTypesHint(QStringList(QContactRelationship::HasMember())); m_contactFetchRequest.setManager(m_manager); m_contactFetchRequest.setFilter(QContactFilter()); // all contacts m_contactFetchRequest.setFetchHint(hasMemberRelationshipsOnly); m_contactFetchRequest.start(); //! [Providing a fetch hint] //! [Removing a relationship] connect(&m_relationshipRemoveRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(relationshipRemoveRequestStateChanged(QContactAbstractRequest::State))); m_relationshipRemoveRequest.setManager(m_manager); m_relationshipRemoveRequest.setRelationships(QList() << groupRelationship); m_relationshipRemoveRequest.start(); //! [Removing a relationship] QCoreApplication::exit(0); } src/contacts/doc/snippets/qtcontactsdocsample/requestexample.h000066400000000000000000000101001233466112000253530ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef REQUESTEXAMPLE_H #define REQUESTEXAMPLE_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include //! [Class setup] QTCONTACTS_USE_NAMESPACE class AsyncRequestExample : public QObject { Q_OBJECT public: AsyncRequestExample(); ~AsyncRequestExample(); public slots: void performRequests(); private slots: void contactFetchRequestStateChanged(QContactAbstractRequest::State newState); void contactSaveRequestStateChanged(QContactAbstractRequest::State newState); void contactRemoveRequestStateChanged(QContactAbstractRequest::State newState); void relationshipFetchRequestStateChanged(QContactAbstractRequest::State newState); void relationshipSaveRequestStateChanged(QContactAbstractRequest::State newState); void relationshipRemoveRequestStateChanged(QContactAbstractRequest::State newState); private: QContactManager *m_manager; QContactFetchRequest m_contactFetchRequest; QContactSaveRequest m_contactSaveRequest; QContactRemoveRequest m_contactRemoveRequest; QContactRelationshipFetchRequest m_relationshipFetchRequest; QContactRelationshipSaveRequest m_relationshipSaveRequest; QContactRelationshipRemoveRequest m_relationshipRemoveRequest; }; //! [Class setup] class RequestExample : public QObject { Q_OBJECT public: RequestExample() : QObject(), m_previousLastIndex(-1), m_manager(0), m_fetchRequest(new QContactFetchRequest) { } ~RequestExample() { delete m_fetchRequest; } void setManager(QContactManager* manager) { m_manager = manager; m_fetchRequest->setManager(m_manager); } private slots: void performRequest(); void printContacts(); void stateChanged(QContactAbstractRequest::State state); private: int m_previousLastIndex; QContactManager *m_manager; QContactFetchRequest *m_fetchRequest; }; #endif src/contacts/doc/src/000077500000000000000000000000001233466112000150155ustar00rootroot00000000000000src/contacts/doc/src/contacts-index.qdoc000066400000000000000000000071521233466112000206150ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page qtcontacts-index.html \title Qt Contacts \brief Qt Contacts enable you to manage contact information, whether it is stored locally or remotely. A contact is the digital representation of a person, group, or entity that is stored in a platform-specific manner. A single contact may consist of details that are stored in several different datastores, and that are only relevant in different contexts. Developers can write platform-independent implementations of a contact manager engine, which may unify one or more platform-specific contact backends. This enables client applications to request contact data from local or remote backends in a platform-independent and datastore-agnostic manner. Client applications can retrieve, modify, or delete contacts, as well as sort or filter contacts and access them as a list. In addition, they can import and export contacts in vCard format. \section1 Getting Started To include the definitions of the module's classes, use the following directive: \snippet doc_src_qtcontacts.cpp include To use the C++ library in your application, add the following configuration option to your \c .pro file: \snippet doc_src_qtcontacts.pro contacts project modification To use the QML types in your application, add the following import statement to your \c .qml file: \snippet moduleimports.qml Contacts import \section1 Related Information \section2 Guides \list \li \l{Qt Contacts Overview} \li \l{Qt Contacts API Usage} \li \l{Qt Contacts Action API} \li \l{Qt Contacts Asynchronous API} \li \l{Qt Contacts Synchronous API} \li \l{Qt Contacts Manager Engines} \li \l{Qt Contacts C++ API} - overview of the C++ API \li \l{Qt Contacts QML API} - overview of the QML API \endlist \section2 References \list \li \l{Qt Contacts C++ Classes} - list of C++ classes \li \l{Qt Contacts QML Types} - list of QML types \endlist \section2 Examples \list \li \l{qmlcontactslistview}{Qt Quick Contacts List view} Import contact information from vCard files, select a backend for volatile memory or persistent storage, as well as list, add, edit, and remove contacts. \endlist */ src/contacts/doc/src/contacts.qdoc000066400000000000000000000277131233466112000175150ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page qtcontacts-overview.html \title Qt Contacts Overview \brief An API enabling clients to request contact data from local or remote backends. \ingroup qtpim-c++ \tableofcontents The Qt Contacts API enables a client to request contact data from local or remote backends in a platform-independent and datastore-agnostic manner. This is achieved by defining generic personal information data abstractions which can sufficiently describe contact data stored on any platform. Due to the cross-platform nature of the API, and the ability for developers to write platform-independent implementations of a QContactManager which may unify one or more platform-specific contact backends, it is intended that the semantics and quirks of the underlying datastores on any platform may be entirely opaque from the perspective of Qt-based, cross-platform client applications. Contact information is stored in datastores whose functionality is exposed via a \l{QContactManager}{manager}. The Qt Contacts API models a \l{QContact}{contact} as a collection of distinct details. Individual contacts may be related to one other, and these \l{QContactRelationship}{relationships} are stored separately from contacts themselves and may be manipulated directly by clients. \l{QContact}{Contact}, and \l{QContactRelationship}{relationship} information may all be retrieved, modified or deleted by clients using either the \l{Qt Contacts Synchronous API}{synchronous} or \l{Qt Contacts Asynchronous API}{asynchronous} API. For a full list of classes, see \l{Qt Contacts C++ Classes}. \sa {Qt Contacts API Usage} \sa {Qt Contacts QML API} \section1 Client-Facing API The client-facing API allows retrieval, modification and deletion of contacts, and relationships, as well as access to manager meta data and capability reporting. \section2 Container Classes Contact information is stored in container (value) classes. These classes are not derived from QObject, and hence can be used in lists. They do not have parents, do not emit signals, and so on. They represent data which may be manipulated and retrieved from a \l{Manager}{manager}. \section3 Contact A \l{QContact}{contact} is the digital representation of a person, group or entity, which is stored in a platform-specific manner. Information pertaining to a single contact may be located across several different datastores, and each datum (or detail) may or may not pertain to a particular context in which that information is valid. A contact may include semantically identical pieces of information that are relevant in different contexts. For example, a contact may have a phone number that is relevant to their \e home context, and another phone number that is relevant to their \e work context. It can be seen that the context of information defines its validity to the user, depending on the context of usage; and as such, the sum of information in a given context can be considered equivalent to a \e {contextual identity}. This allows great flexibility when consolidating data from various sources into a single, cohesive contact. Each contact stored in a manager is identified by an \l{QContactId}{id} which consists of a manager identifier (URI) and the QContactId manager-local id which is used to identify the contact in that manager. Note that a contact stored in one manager may have the same local id as a different contact stored in another manager; please see the QContactId documentation for more information. \section3 Detail A \l{QContactDetail}{detail} is a single, cohesive unit of information that is stored in a contact. As explained previously, it is valid for a particular context or set of contexts. A detail may have specific metadata associated with it, such as its sub-type or context, as well as access constraints which may apply to the the detail (such as read-only, irremovable, etc). There are a number of common details defined in the API which are intended for use by clients, as listed in \l{Contact Details Leaf Classes}. \section3 Relationships Contacts may participate in \l{QContactRelationship}{relationships} with other contacts. The details of any such relationship is stored by the manager which contains the contact. There are several standard relationship types supported by default, and arbitrary relationship types are also allowed if the manager supports that feature. One important relationship is that of group membership. Membership of a contact in a group can be modeled as that group contact participating in a \c HasMember relationship with the contact. \section2 Manager Access to contacts is provided by implementations of the Qt Contacts \l{QContactManager}{manager} API. A manager provides access to zero or more platform-specific datastores. Each datastore may support different capabilities (for example, the ability to store certain datatypes, the ability to natively filter on different details, the provision of locking mechanisms, the provision of changelog information, etc) which are reported by the manager on request. The manager therefore provides access to contacts and relationships stored in different datastores, in a platform and datastore independent manner. \section3 Meta Data API The API offered by the QContactManager exposes functionality which is implemented by plugins. These plugins may be platform specific, and may be provided by Nokia or by third party developers. As described above, each plugin will have different capabilities and implement the functionality exposed by the Qt Contacts API to a different degree. The QContactManager class provides a static function QContactManager::availableManagers() which allows clients of the API to determine (at run time) which plugins (managers) are available for use. Clients of the API also need to be able to determine (at run time) what the capabilities of a given plugin (contact manager) are. The QContactManager class provides an API to query the capabilities of a given manager with the following synchronous functions: \list \li isFilterSupported(const QContactFilter& filter) const \li isRelationshipTypeSupported(const QString& relationshipType, const QString& contactType = QContactType::TypeContact) const \li supportedDataTypes() const \li supportedContactTypes() const \endlist A given manager is identified by its URI. The URI consists of the manager's name, any relevant parameters which were used during instantiation of the manager, and the version of the manager. While the name of the manager identifies the plugin which provides the functionality, you cannot guarantee that the data available through one manager will be available through another with the same name (for example, if one parameter tells the plugin to store and retrieve contact information from a particular online service or local file). The synchronous API offered to allow run-time querying of a manager's metadata includes: \list \li managerName() const \li managerParameters() const \li managerUri() const \li managerVersion() const; \li (static) parseUri(const QString& uri, QString* managerName, QMap* params) \li (static) buildUri(const QString& managerName, const QMap& params, int implementationVersion = -1) \endlist The functionality that the above functions provide is only available through synchronous API. \section3 Asynchronous API The asynchronous API provides a way to access or modify the contact information managed by a particular backend via non-blocking, asynchronous requests. It is recommended for most applications that the asynchronous API be used where possible. The asynchronous API is offered through various classes derived from the QContactAbstractRequest class, including QContactIdFetchRequest, QContactFetchRequest, QContactSaveRequest, QContactRemoveRequest, QContactRelationshipFetchRequest, QContactRelationshipSaveRequest, and QContactRelationshipRemoveRequest. The asynchronous API allows manipulation of \l{QContact}{contacts} and \l{QContactRelationship}{contact relationships}, but does not provide manager capability or meta data information reporting. \sa {Qt Contacts Asynchronous API} \section3 Synchronous API The synchronous API provides the simplest way to access or modify the contact information managed by a particular backend. It has the disadvantage that calls block the current thread of execution until completion and is therefore most suitable only for applications which interact with local, high-speed datastores, or for applications which do not require a responsive user interface. The synchronous API is offered through the QContactManager class, and includes manipulation of \l{QContact}{contacts} and \l{QContactRelationship}{contact relationships}. As previously described, the meta data reporting and manipulation functions are also provided via synchronous API only. \sa {Qt Contacts Synchronous API} \section2 Actions Clients can perform \l{QContactAction}{actions} on contacts which support them. Actions are things like \e {Send Email} or \e Dial, and can be provided from various sources including Qt Plugins or the Qt Service Framework. Every action implementation is uniquely identified by a combination of its name, the name of the service which provided the implementation, and the version of the implementation. These pieces of data may be encapsulated in a \l{QContactActionDescriptor} which can be used to retrieve an instance of the implementation from a \l{QContactActionFactory}. Different actions will allow (or require) different parameters to invocation. For example, an action which allows clients to send emails to a contact may be able to accept attachments as a parameter to invocation. Each action must be invoked on an \l{QContactActionTarget}{action target} or list of targets, where a target may be a contact or a specific detail of a particular contact. \sa {Qt Contacts Action API} \section1 Non-Client-Facing API The non-client-facing API allows third party developers to implement a manager engine plugin from which clients may request data. \section2 Manager Engine The functionality exposed by the QContactManager class may be implemented by \l{QContactManagerEngine}{engine} plugins which interface directly to a platform-specific backend or provide their own data storage backend. As such, the terms \e manager, \e plugin and \e backend are used interchangeably in this documentation to refer to any engine plugin which implements the functionality exposed by the QContactManager interface. The plugin architecture allows dynamic loading of different manager engines at runtime. A manager backend may be implemented by subclassing \l{QContactManagerEngine}, and providing a \l{QContactManagerEngineFactory} which can instantiate it when required. For more information on the available engines and how to write your own engine, see \l{Qt Contacts Manager Engines}. */ src/contacts/doc/src/contactsactions.qdoc000066400000000000000000000164271233466112000210760ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page contactsactions.html \title Qt Contacts Action API \tableofcontents The Qt Contacts API supports the concept of a generic action which may be invoked upon an \l{QContactActionTarget}{action target} (e.g., a contact) or list thereof. The API allows clients to invoke an action upon a target (for example, to send an email to a contact) in a cross-platform manner, and allows third-party developers to provide platform-specific action plugins which may be used by clients. \section1 Invoking Actions upon Targets The client interface to actions consists of three classes: QContactAction, QContactActionTarget and QContactActionDescriptor. A \l{QContactActionDescriptor}{descriptor} uniquely identifies a particular implementation of an \l{QContactAction}{action}, and allows the client to query meta-data about the action. An \l{QContactActionTarget}{action target} consists of either a contact, a detail of a contact, or a list of details of a contact. The available actions may be queried by calling \l QContactAction::availableActions(). This function returns the list of names of actions which are provided by the given service name, or by any service if the parameter is omitted. There may be multiple implementations of any given action identified by a particular action name, since multiple third-party action providers could provide (for example) a "call" action, using various proprietary protocols and techologies. Once the client knows which action they wish to perform on a contact, they can retrieve the list of action descriptors for that action by calling \l QContactAction::actionDescriptors() which takes the action name as a parameter. Note that there are several predefined action names including QContactAction::ActionCall, QContactAction::ActionEmail, QContactAction::ActionSms etc, however there is no guarantee that all of these actions are implemented on any given platform. Finally, once the client has selected a particular implementation of the action, by inspecting the action descriptor (from which they can retrieve meta-data and check that it supports the contact that they wish to perform the action on), the client may request a pointer to the action implementation by calling \l QContactAction::action() and passing the action descriptor as a parameter. Note that the client takes ownership of the returned QContactAction pointer and must delete it to avoid leaking memory. The caller is able to delete the action at any time, however doing so prior to when the action transitions to a finished state may have an undefined outcome depending on the implementation of the action. \section1 Implementing Actions If you are a third-party developer who wants to provide an action for other clients to use, you must do four things: \list \li Implement a QServicePluginInterface-derived class \li Implement a QContactActionFactory-derived class \li Implement (one or more) QContactAction-derived classes \li Write an XML file which describes your service plugin \endlist For more information on the QServicePluginInterface and the format of the service description XML, see the \l{Qt Service Framework}{Qt Service Framework} documentation. An example action plugin is provided later in this document. \note While the plugins are loaded by the Qt Service Framework, clients of the Qt Contacts Action API are entirely shielded from this implementation detail. The QContactActionDescriptor class is actually a client-facing interface to an action factory, which allows the factory to provide meta-data and other implementation-specific information to clients on demand. \section2 Other Considerations We recommend that action implementors provide values for the default meta-data keys (including icons and labels) documented in QContactActionDescriptor, to allow client applications to provide meaningful user interface elements to represent the action. We recommend that action implementors read the documentation of the \l{Qt Service Framework}{Qt Service Framework} carefully, to better understand how their implementation plugin may be updated with patch releases or major releases, and how these considerations affect the implementation of the plugin. \section2 Example Implementation The following snippet provides an example of an action plugin. As previously described, the action plugin consists of a QServicePluginInterface, a QContactActionFactory, and one or more QContactAction derived classes. The QServicePluginInterface-derived class merely instantiates the QContactActionFactory-derived class on request for the Qt Service Framework. The QContactActionFactory-derived class then instantiates the actions when required. \snippet multiaction/multiaction_p.h Example Contact Action Plugin Declaration The implementation of these classes might be something like the following (example only): \snippet multiaction/multiaction.cpp Example Contact Action Plugin Implementation Once implemented, the plugin must be described by an XML file and installed in an appropriate location. For more information, see the Qt Service Framework documentation. \code tst_qcontactactions:multiaction plugins/contacts/libcontacts_multiaction This service provides two test QContactAction implementations for testing purposes. It is also an example of a single plugin providing multiple actions whose descriptors are identical except for their meta data. org.qt-project.Qt.SampleContactsActionPlugin 1.1 call This plugin can instantiate two different QContactAction instances; one which provides the "call" action via the "sip" provider, the other which provides the "call" action via the "example proprietary protocol" provider. \endcode \section2 Deploying Services Depending on the platform, the service which provides the action must be deployed in a certain way. */ src/contacts/doc/src/contactsasync.qdoc000066400000000000000000000133471233466112000205510ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page contactsasync.html \title Qt Contacts Asynchronous API \tableofcontents The Qt Contacts Asynchronous API enables a client to asynchronously fetch, update, or remove contact and relationship data from a contact manager. It offers mostly the same functionality as the \l{Qt Contacts Synchronous API}, but with greater flexibility when requesting information from remote datastores or slow local datastores. However, some information and reporting functionality, as well as the functions to set and retrieve the id of the self-contact are only provided through the synchronous API. The Qt Contacts Asynchronous API is available through classes derived from the QContactAbstractRequest class. It has the following main use cases: \list \li Manipulating contacts \li Manipulating relationships \endlist \section1 Manipulating Contacts The most common type of operation that clients will perform involves retrieval or modification of contacts. For in-depth information about contact manipulation, please refer to the \l{Qt Contacts Synchronous API}. There are four different types of operation which are supported by the asynchronous API: \list \li Fetch contact ids \li Fetch contacts \li Save contacts (create or update) \li Remove contacts \endlist These operations are supported via the QContactIdFetchRequest, QContactFetchRequest, QContactSaveRequest and QContactRemoveRequest classes, respectively. The synchronous API offered by the QContactManager class to allow manipulation of contacts consists of the following functions: \list \li contactIds(const QList& sortOrders = QList()) const \li contactIds(const QContactFilter& filter, const QList& sortOrders = QList()) const \li contacts(const QList& sortOrders = QList(), const QContactFetchHint& fetchHint = QContactFetchHint()) const \li contacts(const QContactFilter& filter, const QList& sortOrders = QList(), const QContactFetchHint& fetchHint = QContactFetchHint()) const \li saveContacts(QList* contacts, QMap* errorMap) \li removeContacts(QList* contactIds, QMap* errorMap) \endlist \section1 Manipulating Relationships Contacts may be related in various ways. The contacts API allows clients to define relationships between contacts. For in-depth information about relationship manipulation, please refer to the \l{Qt Contacts Synchronous API}. Support for relationships is backend specific. There are three different types of operation which are supported by the asynchronous API: \list \li Fetch relationships \li Save relationships (create or update, if supported by the backend) \li Remove relationships (if supported by the backend) \endlist These operations are supported via the QContactRelationshipFetchRequest, QContactRelationshipSaveRequest and QContactRelationshipRemoveRequest classes respectively. The synchronous API offered by the QContactManager class to allow manipulation of relationships consists of the following functions: \list \li relationships(const QContactId& participantId, QContactRelationshipFilter::Role role = QContactRelationshipFilter::Either) const; \li relationships(const QString& relationshipType = QString(), const QContactId& participantId = QContactId(), QContactRelationshipFilter::Role role = QContactRelationshipFilter::Either) const; \li saveRelationship(QContactRelationship* relationship); \li saveRelationships(QList* relationships); \li removeRelationship(const QContactRelationship& relationship); \li removeRelationships(const QList& relationships); \endlist \section1 Examples of Usage \section2 Fetching Contacts The client sets up a request for contacts matching a specific criteria from a particular manager. Results from the request will be displayed to the user as they are received. \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Asynchronous contact request \section2 Other Asynchronous Operations All other asynchronous operations are performed in a similar manner to the previous example. A request of the desired type (which is derived from QContactAbstractRequest) is created, certain criteria are set which determine the intent of the request, and the signals of the request are connected to slots which deals with the results. The request can then be started. */ src/contacts/doc/src/contactsclasses.qdoc000066400000000000000000000065441233466112000210720ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \title Qt Contacts C++ API \page contactsclasses.html \brief The Qt Contacts API enables you to manage contact information, whether it is stored locally or remotely. \section1 Main Classes \annotatedlist contacts-main \section1 Contact Details Leaf Classes Several subclasses of \l{QContactDetail} are provided as part of the Qt Contacts API. They are general in design but are intended to fulfill specific use-cases. Please note that certain backends may choose not to support one or more of these subclasses as they appear here; they may offer their own which provide similar functionality. \annotatedlist contacts-details Each of these subclasses provide access to information stored in fields which may have certain constraints, documented in each subclass documentation. \section1 Asynchronous Requests Clients may use either the \l{Qt Contacts Synchronous API}{synchronous} or \l{Qt Contacts Asynchronous API}{asynchronous} API to access functionality provided by a manager backend. The Qt Contacts Asynchronous API is offered through subclasses of the \l{QContactAbstractRequest} class: \annotatedlist contacts-requests \section1 Contact Selection Clients may select a contact by specifying a unique contact id, or by supplying a \l{QContactFilter} which matches the contact or contacts they wish to select. The various derivatives of \l{QContactFilter} allow for fine-grained and flexible selection of contacts according to various criteria: \annotatedlist contacts-filters A client can also request that the results of such a selection be sorted, by passing a \l{QContactSortOrder} (or list of sort orders) to the manager. \section1 Actions Actions are described by descriptors and are instantiated by factories. \annotatedlist contacts-actions \section1 Backends A backend implementor must implement the following interfaces: \annotatedlist contacts-backends For more information, see \l{Qt Contacts Manager Engines}. \section1 Synchronization and Serialization The Qt Contacts API is used by the \l{Qt Versit Overview}{Qt Versit} module. It allows serialization of a QContact into a vCard document, and vice versa. */ src/contacts/doc/src/contactsengines.qdoc000066400000000000000000000321211233466112000210530ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page contactsengines.html \title Qt Contacts Manager Engines \tableofcontents The QContactManager interface provided to clients to allow access to contact information depends on an implementation of QContactManagerEngine existing. This engine provides the methods which are called by the manager. An engine is identified by its URI, which is the name reported to clients through the QContactManager::managerUri() function. The URI of a manager is built by combining its name, version and relevant construction parameters. \section1 Information for Clients While clients never interact directly with instances of QContactManagerEngine, they may need to be aware of limitations of individual engines, or differences between engines. The API offered through QContactManager allows clients to retrieve this information for the engine which provides the functionality exposed through a particular QContactManager. \section2 Where Is the Data Stored? A QContactManagerEngine may provide an aggregated view of multiple physical datastores, zero or more of which may be remote datastores. Clients of the API are aware only that the data is managed by a QContactManagerEngine with a particular URI. It is possible that multiple different engines will have overlap in the datastores which they aggregate, and in that case the way in which those engines were implemented will determine whether operations are thread-safe or not. Since the data may physically be stored in a remote datastore, any operations may be dominated by the return-trip-time of communications with the remote datastore. As such, it is recommended that clients use the asynchronous client API to access contact information from any QContactManager. \section2 Schema Differences Each engine may support a different schema. All engines should attempt to support the default schema, described in the \l{Qt Contacts Schema}{default schema} documentation, however clients should never assume that any engine does support the default schema fully. Some engines support different types of contacts. Clients can retrieve the contact types supported by an engine by calling QContactManager::supportedContactTypes(). \section2 Provided Engines The Qt PIM AddOn includes the Contacts module which includes several backends already, some of which are designed to interface with the default addressbook on their particular platform. \section3 In-Memory Example Engine The in-memory engine identifies itself as the \e memory engine. It is available on all platforms which are supported by the Qt PIM AddOn. The in-memory engine supports the default schema, and provides all functionality available through the Contacts API; however, all data is stored in-memory and is not persisted in any way. \section1 Information For Engine Implementors Some developers may wish to provide implementations of QContactManagerEngine for use by clients. The engine that they provide may aggregate multiple datastores, or access a remote datastore, or provide some other functionality to clients. An engine is distributed as a Qt Plugin, and will be detected automatically by the plugin loading code in the QContactManager, so long as the plugin is located in the correct path ($QT_PLUGINS_DIR/contacts/). \section2 Which Functions Do I Need to Implement? Different engines provide different functionality and support different features. Depending on the feature set of the engine, it will need to implement a particular subset of the API. The default implementation for most functions will set the error to \c QContactManager::NotSupportedError and return the value which indicates that an error has occurred. \section3 Mandatory Functions All engines must implement the following functions: \list \li QContactManagerEngine::managerName() \li QContactManagerEngine::managerVersion() \li QContactManagerEngine::supportedContactTypes() \li QContactManagerEngine::supportedDataTypes() \li QContactManagerEngine::contactIds() \li QContactManagerEngine::contacts() \endlist Every engine implementation must also come with an implementation of QContactManagerEngineFactory for that engine. Note that you do not need to implement filtering and sorting natively in an engine; the default implementation offers the following static functions to perform filtering and sorting respectively, in memory: \list \li QContactManagerEngine::testFilter() \li QContactManagerEngine::sortContacts() \endlist However, engine implementors should be aware that the default implementation is naive and will have greatly reduced performance compared to a native implementation (e.g., SQL queries, if the contact data exposed by the engine implementation is stored in an SQL database). Similarly, any QContactFetchHint parameter may be ignored by an engine implementation, but if it does so it must return all information available for the contact. All engines must also implement the following functions to implement asynchronous requests: \list \li QContactManagerEngine::requestDestroyed() \li QContactManagerEngine::startRequest() \li QContactManagerEngine::cancelRequest() \li QContactManagerEngine::waitForRequestFinished() \endlist If the engine does not support asynchronous requests, it should always return false in the last three of those functions, and do nothing in the first. If the engine does support asynchronous requests, it must ensure that all information required to perform the request is saved in the engine within QContactManagerEngine::startRequest(), as the client owns the request object and may delete it at any time. In general, engine implementors should be aware of this ownership semantic, and never attempt an unsafe operation on a request pointer. It is recommended that all engine implementations support asynchronous requests, even if they use a "dummy" implementation which services the request synchronously during startRequest, and then emit the appropriate signals from the request via a zero-millisecond timeout timer. \section3 Optional Functionality The rest of the virtual functions are optional, and should be implemented only if the engine supports the operations. If the engine can be constructed with different parameters, which affects the operation of the engine (for example, a parameter might define which file to read contact information from, or it might be an access token to prove that the client has the access rights to read contact information from the engine, etc), it must report which parameters it was constructed with via the \list \li QContactManagerEngine::managerParameters() \endlist function. If the engine supports native filtering of any kind, it must report to clients which filters are supported natively by implementing: \list \li QContactManagerEngine::isFilterSupported() \endlist If the engine supports saving or removing contact information, as well as retrieval, it must implement: \list \li QContactManagerEngine::saveContacts() \li QContactManagerEngine::removeContacts() \endlist It may also choose to implement the "single contact" functions: \list \li QContactManagerEngine::saveContact() \li QContactManagerEngine::removeContact() \endlist If it does not, the default implementation of those functions will use the batch (plural) versions of those functions to implement the required behavior. Support for relationships are backend specific, see below convenience methods for more information. \list \li QContactManagerEngine::isRelationshipTypeSupported() \li QContactManagerEngine::relationships() \li QContactManagerEngine::saveRelationships() \li QContactManagerEngine::removeRelationships() \endlist Specifically, if the engine supports group contacts, it must support the \c QContactRelationship::HasMember relationship, and report this as a supported relationship type. It must then also report that it supports the \c QContactType::TypeGroup contact type as a supported contact type in QContactManagerEngine::supportedContactTypes(). Support for saving a "self" contact (that is, a contact which contains information about the owner of the device or online service account from which the engine provides contact information) is backend specific \list \li QContactManagerEngine::setSelfContactId() \li QContactManagerEngine::selfContactId() \endlist \section3 Optional Implementation Apart from areas of functionality which may be optionally implemented by the engine or not, the default implementation provides several functions which operate in a naive, in-memory manner. An engine implementation can override this default implementation with its own, if it wishes, in order to obtain performance gains, or to more accurately implement the function. As previously mentioned it may implement its own sorting or filtering, in functions such as QContactManagerEngine::contacts(). An engine may also implement: \list \li QContactManagerEngine::validateContact() \li QContactManagerEngine::synthesizedDisplayLabel() \endlist \section2 Which Signals Do I Need to Emit? An engine implementation must emit the appropriate signals for the subset of functionality that it supports. If the engine supports reading or saving contacts, it must emit the: \list \li QContactManagerEngine::contactsAdded() \li QContactManagerEngine::contactsChanged() \li QContactManagerEngine::contactsRemoved() \endlist signals as appropriate. Alternatively, it can emit the QContactManager::dataChanged() signal instead. If the engine supports reading or saving relationships, it must emit the: \list \li QContactManagerEngine::relationshipsAdded() \li QContactManagerEngine::relationshipsRemoved() \endlist signals as appropriate. Alternatively, it can emit the QContactManager::dataChanged() signal instead. If the engine supports the \c QContactManager::SelfContact feature, it must emit the: \list \li QContactManagerEngine::selfContactIdChanged() \endlist signal as appropriate. Alternatively, it can emit the QContactManager::dataChanged() signal instead. \section2 Other Considerations There are several other considerations that engine writers must be aware of: \list \li Most batch functions take an OPTIONAL error map as a parameter. This parameter may be null, in which case the client is not interested in fine-grained error reporting. Engines must check the pointer before attempting to dereference it. \li Every function takes a mandatory \c QContactManager::Error pointer argument. This argument is NEVER null, since it exists in the private implementation of QContactManager. Testing this argument for null is, therefore, superfluous. \li The single-item functions for contact and relationship retrieval, removal and save already have a default implementation which merely wraps the batch retrieval, removal or save function appropriately. This default implementation may not be as performant as a hand-rolled function. Engine implementations MUST implement the batch functions for each area of functionality supported by the engine. \li Most clients will prefer to use the asynchronous API to access contact information from the engine. It is therefore suggested that asynchronous requests be serviced, even if it is implemented in a similar manner to the (provided) memory engine's naive implementation. \endlist \section2 Example Implementation There are several implementations of QContactManagerEngine available in the Qt PIM AddOn source code repository. In particular, the "memory" engine provides an implementation of an in-memory, anonymous datastore which supports every feature in the API, and therefore is useful for demonstration purposes. Be aware, however, that the implementation of all functionality in the "memory" engine is naive and not performant, and should not be copied in any real engine implementation (e.g., to perform filtering, it reads all contacts from the (in-memory) database, and checks one by one for matches; a real engine, on the other hand, might perform a database query to return the results directly, rather than performing n-reads). */ src/contacts/doc/src/contactssync.qdoc000066400000000000000000000232711233466112000204050ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page contactssync.html \title Qt Contacts Synchronous API \tableofcontents The Qt Contacts Synchronous API provides the simplest way to access or modify the contact information managed by a particular backend. It has the disadvantage that calls block until completion and is therefore most suitable only for applications which interact with local, high-speed datastores. Most operations that may be performed using the synchronous API may also be performed using the Qt Contacts Asynchronous API. It is recommended for most applications that the asynchronous API be used where possible. The Qt Contacts Synchronous API is available through the QContactManager class. It has the following main use cases: \list \li Reporting Errors \li Manipulating Contacts \li Manipulating Relationships \endlist \section1 Reporting Errors When a synchronous operation fails, clients need to be able to retrieve error information associated with that synchronous operation. The QContactManager::error() function provides this information to clients. For some synchronous operations (for example, batch save or remove operations) it is possible that multiple errors may occur during the operation. In those cases, the synchronous function takes a pointer to a map of input index to error, which is filled by the function as required. The QContactManager::error() function then reports the overall operation error. Error reporting is handled slightly differently in the asynchronous API, in that each instance of an asynchronous request is able to report any overall operation error as well as the finer-grained map of errors, for the operation which it requested. \section1 Manipulating Contacts The most common type of operation that clients perform involves retrieval or modification of contacts. The QContactManager class offers synchronous API to retrieve, create, update and delete contacts. The create and update operations are provided through the same interface. Both singular and batch operations are offered by the API. A contact is identified by its QContactId. This id consists of two parts: a URI which identifies the contact manager which stores the contact, and the local id of the contact in that manager. Some operations which take a pointer to a contact as an argument may modify the contact during the operation; updating the contact id is a common example. The QContactManager class provides API for accessing the IDs of contacts which are stored in the manager: \list \li contactIds(const QList& sortOrders = QList()) const \li contactIds(const QContactFilter& filter, const QList& sortOrders = QList()) const \endlist The contact id retrieval functionality is also provided via asynchronous API through the QContactIdFetchRequest class. The synchronous, singular contact manipulation functions offered by the QContactManager class are: \list \li contact(const QContactId& contactId, const QContactFetchHint& fetchHint = QContactFetchHint()) const \li saveContact(QContact* contact) \li removeContact(const QContactId& contactId) \endlist The (optional) fetch argument to the contact accessor function allows clients to tell the plugin which types of information they wish to retrieve. This argument is a hint only, and may be ignored safely by the plugin, or used by the plugin to optimize the performance of the retrieve operation. The save operation entails a validation step, where the contact's details are checked against the supported schema. If the contact is valid, it is saved. Note that if the contact already exists in the database (determined by the id of the contact) it is replaced with the contact contained in the argument. This means that clients should not save any contact which was retrieved with a non-empty fetchHint defined, or data loss may occur. Any error which occurs during such singular contact manipulation functions may be accessed by calling QContactManager::error() directly after the original synchronous call. The synchronous, batch contact manipulation functions offered by the QContactManager class are: \list \li contacts(const QList& sortOrders = QList(), const QContactFetchHint& fetchHint = QContactFetchHint()) const \li contacts(const QContactFilter& filter, const QList& sortOrders = QList(), const QContactFetchHint& fetchHint = QContactFetchHint()) const \li saveContacts(QList* contacts, QMap* errorMap) \li removeContacts(QList* contactIds, QMap* errorMap) \endlist The batch save and remove functions both take an (optional) pointer to a map of errors. If the pointer is non-null, this map is filled out with any errors which occur. The overall operation error of any batch manipulation operation may be accessed by calling QContactManager::error() directly after the original synchronous call. The contact manipulation functionality is also provided via asynchronous API through the QContactFetchRequest, QContactSaveRequest, and QContactRemoveRequest classes. The \e self contact is a special concept, which has dedicated API. A client may instruct any backend which supports the concept of a self contact that a particular, previously saved contact is the self contact. Any backend which implements this functionality should report that it supports the QContactManager::SelfContact feature. The API which provides the self-contact functionality consists of: \list \li setSelfContactId(const QContactId& contactId) \li selfContactId() const \endlist In order to unset the self contact, a client may either delete the contact which is currently set as the self contact, or set the self contact id to be null id (constructed via QContactId()). The self-contact manipulation functionality is only available via the synchronous API. \section2 Adding Contacts The client creates a new contact, adds a name and a phone number, and saves it to the default store of the default manager. We assume the existence of a specialized leaf-class that allows simple access to details of the definition identified by the "PhoneNumber" identifier, and another that allows simple access to details of the definition identified by the "Name" identifier. These specialized leaf classes may be written by anyone, and simply wrap the functionality provided by QContactDetail in order to allow simpler access to fields supported by a particular definition. \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Creating a new contact \section2 Filtering by Detail Definition and Value The client utilizes a default manager and asks for any contacts with a particular phone number. The example assumes that the default manager supports the provided QContactPhoneNumber detail leaf class (which implements the default definition for phone number details). \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Filtering by definition and value \section2 Modifying Contact Details The client retrieves a contact, modifies one of its details, adds a new detail, and then saves the contact back to the manager. \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Modifying an existing contact \section1 Manipulating Relationships Contacts may be related in various ways. The contacts API allows clients to define relationships between contacts if the plugin providing the functionality supports such relationships. Some plugins support arbitrary relationship types. Clients can define custom relationships between contacts saved in such plugins. The API which provides the relationship manipulation functionality consists of: \list \li relationships(const QContactId& participantId, QContactRelationshipFilter::Role role = QContactRelationshipFilter::Either) const; \li relationships(const QString& relationshipType = QString(), const QContactId& participantId = QContactId(), QContactRelationshipFilter::Role role = QContactRelationshipFilter::Either) const; \li saveRelationship(QContactRelationship* relationship); \li saveRelationships(QList* relationships); \li removeRelationship(const QContactRelationship& relationship); \li removeRelationships(const QList& relationships); \endlist The relationship manipulation functionality is also provided via asynchronous API through the QContactRelationshipFetchRequest, QContactRelationshipSaveRequest, and QContactRelationshipRemoveRequest classes. */ src/contacts/doc/src/contactsusage.qdoc000066400000000000000000000405171233466112000205370ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page contactsusage.html \title Qt Contacts API Usage \tableofcontents With the Qt Contacts API, typical use cases are: \list \li Access a list of personal contacts from the contact database supported by the selected backend. \li Sort or filter contacts and access them as a list. \li Import contacts in vCard format into the selected contacts database. \li Export contacts to a vCard format to share elsewhere. \endlist This section provides some examples of common usage of the Qt Contacts API. \section1 Retrieving Contact Details The most common use of the API is to retrieve a contact and then display certain details of that contact. To do so, several steps must be taken: \list \li A contact manager must be instantiated \li The contact must be retrieved from the manager \li The required details of the contact must be selected from the contact \endlist \section2 Instantiating Contact Manager The first step is usually as simple as: \code QContactManager cm; // instantiate the default manager \endcode \section2 Retrieving a Contact from the Manager The second step requires either a filtering operation, or, if the id of the contact is already known, a direct selection operation. If you are interested in all contacts, a \e {default filter} retrieve operation is used. The retrieval operations may either be \l{Qt Contacts Synchronous API}{synchronous} or \l{Qt Contacts Asynchronous API}{asynchronous}; we recommend using asynchronous operations for applications which require a responsive user interface. For simplicity, however, the example below uses the synchronous API to retrieve all contacts: \code QList allContacts = cm.contacts(); \endcode \section2 Selecting a Detail The third step may be performed in several ways. The recommended way is to utilize the templated detail accessor, if you know which type of detail you are interested in: \code QContact firstContact = allContacts.first(); qDebug() << "The first contact has a phone number:" << firstContact.detail().number(); \endcode Alternatively, you can use the base \l QContactDetail class methods to select the detail in which you are interested in, and the field keys specified in the derived class to select the value which you are interested in: \code qDebug() << "The first contact has a phone number:" << firstContact.detail(QContactPhoneNumber::DefinitionName).value(QContactPhoneNumber::FieldNumber); \endcode Note that in each case, if the contact did not have a phone number detail, the return value of QContact::detail() is an empty detail. Also note that in the first case, the return value will be of the QContactPhoneNumber detail type, whereas in the second case, the return value will be of the QContactDetail (base-class detail) type -- although the actual detail returned in both cases is exactly the same. \section2 Retrieving All Details If you wish to retrieve all of the details of a contact, you may do something similar to: \code QList allDetails = firstContact.details(); \endcode \section2 Retrieving Details of a Type Alternatively, if you wish only to retrieve the details which are of some particular type, you can use either the templated or non-templated accessor: \code QList allPhoneNumbers = firstContact.details(); QList allPhoneNumbers2 = firstContact.details(QContactPhoneNumber::DefinitionName); \endcode Note that in each case, if the contact did not have any phone number details, the return value of QContact::details() is an empty list. Also note that in the first case, the return value will be a list of the QContactPhoneNumber detail type, whereas in the second case, the return value will be a list of the QContactDetail (base-class detail) type -- although the actual details returned in both cases will be exactly the same. \section1 Saving Contacts The next most common use of the API is to save a contact. Such an operation consists of two steps: \list \li Saving a detail in a contact \li Saving the contact in a manager \endlist Removing a contact is done similarly to saving a contact. An example of these two operations is given below. Note that it uses the synchronous API to save and remove the contact, although in a real application we recommend using the asynchronous API to perform such manager-related operations. \code QContactPhoneNumber newPhoneNumber; // create the detail to add newPhoneNumber.setNumber("12345"); // set the value(s) to save firstContact.saveDetail(&newPhoneNumber); // save the detail in the contact cm.saveContact(&firstContact); // save the contact in the manager cm.removeContact(firstContact.id()); // remove the contact from the manager \endcode That's it! For more in-depth discussion of usage of the API, see the sections below. \section1 Configuring Managers Users of the contacts API can define which backend they wish to access if a manager for that backend is available. The list of available managers can be queried programmatically at run-time, and the capabilities of different managers can be ascertained by inspecting a QContactManager instance. Furthermore, some managers can be constructed with parameters which affect the operation of the backend. \section2 Loading the Default Manager for the Platform Most users of the API will want to use the default manager for the platform, which provides access to the system address book. Instantiating a manager by using the default constructor will result in the default manager for that platform being instantiated. The default constructor can either be used to create a manager on the stack, in which case it will be deleted automatically when it goes out of scope: \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Loading the default manager for the platform or it can be used explicitly to create a manager on the heap, in which case the client must ensure that it deletes the manager when it is finished with it in order to avoid a memory leak: \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Loading the default manager for the platform on heap \section2 Querying a Manager for Capabilities Different managers will support different capabilities and details. Clients can use the meta data reporting functions of QContactManager to determine what the capabilities of the manager they have instantiated might be. \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Querying a manager for capabilities \section2 Loading the Manager for a Specific Backend In this example, the client loads a manager for a specific backend. While this could be found and retrieved using a more advanced plugin framework (such as the Qt Service Framework), this code assumes that the client has prior knowledge of the backend in question. Clients may wish to use this feature of the API if they wish to store or retrieve contact information to a particular manager (for example, one that interfaces with a particular online service). \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Loading a specific manager backend \section2 Loading a Manager with Specific Parameters The client loads a manager with specific parameters defined. The parameters which are available are backend specific, and so the client had to know that the \e Settings parameter was valid for the particular backend, and what argument it took. In this example, the client tells the backend to load detail definitions saved in a particular settings file. \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Loading a specific manager backend with parameters \section1 Manipulating Contact Details Once a contact has been created (or retrieved from a manager), the client can retrieve, create, update or delete details from the contact. Since QContact and QContactDetail are both container (value) classes, the API offered for these operations is purely synchronous. A contact consists of the details it contains, as well as an id. Some details are read-only (such as the display label of a contact) or irremovable (like the type of a contact), but most are freely modifiable by clients. \section2 Adding Details The client adds a name and a phone number to a contact. \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Adding a detail to a contact \section2 Updating Details The client updates the phone number of a contact. \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Updating a detail in a contact \section2 Removing Details The client removes the phone number of a contact. \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Removing a detail from a contact \section2 Viewing Details The client retrieves and displays the first phone number of a contact. \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Viewing a specific detail of a contact \section2 Viewing All Details of a Contact The client retrieves all of the details of a contact, and displays them. \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Viewing the details of a contact \note Details are implicitly shared objects with particular semantics surrounding saving, removal and modification. The following example demonstrates these semantics. \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Demonstration of detail sharing semantics \section1 Persistent Contact Information After instantiating a manager, clients will wish to retrieve or modify contact information (including relationships and possibly detail definitions) which is persistently stored in the manager (for example, in a database or online cloud). If the client wishes to use the asynchronous API, it is suggested that their class uses member variables for the manager and requests, similarly to: \snippet qtcontactsdocsample/requestexample.h Class setup This allows them to define slots which deal with the data as required when the state of the request changes: \snippet qtcontactsdocsample/qtcontactsdocsampleasync.cpp Example of an asynchronous request slot Note that if the client is interested in receiving the results of the request as they become available, rather than only the final set of results once the request changes state (to \c FinishedState, for example), the client should instead connect the QContactAbstractRequest::resultsAvailable() signal to the slot which deals with the results. \section2 Creating Contacts The client creates a new contact and saves it in a manager. \snippet qtcontactsdocsample/qtcontactsdocsampleasync.cpp Creating a new contact in a manager Alternatively, the client can explicitly block execution until the request is complete, by doing something like: \snippet qtcontactsdocsample/qtcontactsdocsampleasync.cpp Creating a new contact in a manager waiting until finished The equivalent code using the synchronous API looks like: \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Synchronously creating a new contact in a manager \section2 Retrieving Contacts The client requests all contacts from the manager which match a particular filter. \snippet qtcontactsdocsample/qtcontactsdocsampleasync.cpp Filtering contacts from a manager The equivalent code using the synchronous API looks like: \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Synchronously filtering contacts from a manager The client can also retrieve a particular existing contact from a manager, by directly requesting the contact with a particular (previously known) id. With the asynchronous API, this takes the form of another filter: \snippet qtcontactsdocsample/qtcontactsdocsampleasync.cpp Retrieving an existing contact from a manager The synchronous API provides a function specifically for this purpose: \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Synchronously retrieving an existing contact from a manager \section2 Updating Contacts The client updates a previously saved contact by saving the updated version of the contact. Any contact whose id is the same as that of the updated contact will be overwritten as a result of the save request. \snippet qtcontactsdocsample/qtcontactsdocsampleasync.cpp Updating an existing contact in a manager The equivalent code using the synchronous API looks like: \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Synchronously updating an existing contact in a manager \section2 Removing Contacts The client removes a contact from the manager by specifying its id. \snippet qtcontactsdocsample/qtcontactsdocsampleasync.cpp Removing a contact from a manager The equivalent code using the synchronous API looks like: \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Synchronously removing a contact from a manager \section2 Creating Relationships The client specifies a relationship between two contacts stored in the manager \snippet qtcontactsdocsample/qtcontactsdocsampleasync.cpp Creating a new relationship between two contacts The equivalent code using the synchronous API looks like: \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Synchronously creating a new relationship between two contacts \section2 Retrieving Relationships The client requests the relationships that a particular contact is involved in from the manager in which the contact is stored. \snippet qtcontactsdocsample/qtcontactsdocsampleasync.cpp Retrieving relationships between contacts The equivalent code using the synchronous API looks like: \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Synchronously retrieving relationships between contacts When a contact is retrieved, it will contain a cache of the relationships in which it is involved at the point in time at which it was retrieved. This provides clients with a simple way to retrieve the relationships in which a contact is involved, but carries the risk that the cache is stale. \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Retrieving relationships from cache Clients can inform the manager that they do not require this cache of relationships to be populated when retrieving a contact, which can allow a manager to optimize contact retrieval. Other retrieval optimizations are also possible to specify, for example that they do not require action preferences to be returned, or that they are only interested in certain types of details. The following code shows how the client can inform the manager that they are only interested in relationships of the \c HasMember type (groups): \snippet qtcontactsdocsample/qtcontactsdocsampleasync.cpp Providing a fetch hint The equivalent code using the synchronous API looks like: \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Synchronously providing a fetch hint \section2 Removing Relationships The client can remove a relationship directly from a manager. \snippet qtcontactsdocsample/qtcontactsdocsampleasync.cpp Removing a relationship The equivalent code using the synchronous API looks like: \snippet qtcontactsdocsample/qtcontactsdocsample.cpp Synchronously removing a relationship Alternatively, when a contact which is involved in a relationship is removed, any relationships in which it is involved will be removed also. */ src/contacts/doc/src/plugins/000077500000000000000000000000001233466112000164765ustar00rootroot00000000000000src/contacts/doc/src/plugins/qml-contacts.qdoc000066400000000000000000000161751233466112000217650ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page qml-contacts.html \title Qt Contacts QML API \brief A QML plugin for the Qt Contacts API \section1 Overview To be able to use this component the QML \e {import} statement needs to reference the module before it can used. Simply add the following to the QML file: \snippet moduleimports.qml Contacts import The Contacts API enables client to search for contacts data from local backends and use filters to get only the relevant results. The QML Contacts API delivers this capability in easy to use form. The following describes the API. The \l{Examples} section contains sample code for the QML API. \section2 Contact Model The Contact itself is represented by a model called a \l {ContactModel}. This model supplies a rich selection of properties to display or to use as filter criteria. The model is then used to perform requests on a contacts store. \section2 Filters Developers can construct a series of filters and combine them using the \l{IntersectionFilter} and \l{UnionFilter} types to represent a logical \e and and \e or of the results. There are also other properties that give control over the filter logic, such as \l {DetailRangeFilter::rangeFlags} {RangeFlags} and the \l {DetailFilter::matchFlags}{MatchFlags}. As an example, consider the follow QML code which has a \l ContactModel type containing a filter and a list of sort orders. The contacts will be restricted to those living in Finland who are either male or born between 1970 and 1984. The list of contacts in this ContactModel will be first sorted on the contacts' organization name and then on the contacts' first name. \code import QtContacts 5.0 ... model: ContactModel { filter: IntersectionFilter { DetailFilter { detail:ContactDetail.Address field: Address.Country value: "Finland" } UnionFilter { DetailRangeFilter { detail:ContactDetail.Birthday field:Birthday.Birthday min: '1970-01-01' max: '1984-12-31' } DetailFilter { detail:ContactDetail.Gender field:Gender.Gender value:Gender.Male } } } sortOrders: [ SortOrder { detail:ContactDetail.Organization field:Organization.Name direction:Qt.AscendingOrder }, SortOrder { detail:ContactDetail.Name field:Name.FirstName direction:Qt.AscendingOrder } ] } \endcode \section2 Contact type The \l {Contact} type represents a single contact instance from the contacts store. All contact details are organized as group properties. The following snippet shows how you can construct a Contact object in QML: \code Rectangle { id: topItem width: 360 height: 640 x: 0 y: 0 Contact { id: myContact Name { firstName:"John" lastName:"Gates" } EmailAddress { emailAddress:"john@example.com" } EmailAddress { emailAddress:"mygmailaccount@gmail.com" } address.street:"53 Mysteet St" address.locality: "My City" address.region: "My Region" address.postcode:"1111" address.country:"My Country" address.subTypes:[Address.Postal, Address.Domestic] address.postOfficeBox:"1111" Nickname { nickname:"John" } PhoneNumber { number: "1111111111" subTypes:[PhoneNumber.Mobile] } PhoneNumber { number: "2222222222" subTypes:[PhoneNumber.Fax] } PhoneNumber { number: "3333333333" subTypes:[PhoneNumber.Landline] } } Column { spacing:4 //access the same property with different syntaxes Text { text:"Name(from property name):" + myContact.name.firstName + " " + myContact.name.lastName } Text { text:"Name(from detail type):" + myContact.detail(ContactDetail.Name).firstName + " " + myContact.name.lastName } Text { text:"Address:" + myContact.address.street + " " + myContact.address.locality + " " + myContact.address.region + " " + myContact.address.postcode } //If a contact contains multiple details for the same detail type, you can access them with the property in plural Text { text:"How many email accounts?:" + myContact.emails.length } Text { text:"Email[0]:" + myContact.emails[0].emailAddress } Text { text:"How many phone numbers?:" + myContact.phoneNumbers.length } Text { text:"phone number[0]:" + myContact.phoneNumbers[0].number } Text { text:"phone number[1]:" + myContact.phoneNumbers[1].number } Text { text:"phone number[2]:" + myContact.phoneNumbers[2].number } } } \endcode \section1 Reference documentation \section2 Main Classes \annotatedlist qml-contacts-main \section2 Detail Leaf Classes \annotatedlist qml-contacts-details \section2 Item matching and filtering \annotatedlist qml-contacts-filters \section2 Examples The following sample applications show examples of API usage: \list \li \l{qmlcontactslistview}{Qt Quick Contacts List view} \endlist */ src/contacts/doc/src/qtcontacts.qdoc000066400000000000000000000047121233466112000200540ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \module QtContacts \title Qt Contacts C++ Classes \ingroup modules \brief The QtContacts module contains classes for management. The \l{Qt Contacts} C++ classes provide contact management. A contact is the digital representation of a person, group, or entity that is stored in a platform-specific manner. To include the definitions of the module's classes, use the following directive: \snippet doc_src_qtcontacts.cpp include To use the C++ library in your application, add the following configuration option to your \c .pro file: \snippet doc_src_qtcontacts.pro contacts project modification */ /*! \qmlmodule QtContacts 5.0 \title Qt Contacts QML Types \ingroup qmlmodules \brief Provides QML types for handling personal contacts and information \l{Qt Contacts}' QML API enables client to search for contacts data from local backends and use filters to get only the relevant results. The QML Contacts API delivers this capability in easy to use form. The \l{Qt Contacts QML API} page contains an overview of the QML types. To use the types, simply add the following to the QML file: \snippet moduleimports.qml Contacts import */ src/contacts/engines/000077500000000000000000000000001233466112000151115ustar00rootroot00000000000000src/contacts/engines/README000066400000000000000000000002441233466112000157710ustar00rootroot00000000000000SUBJECT TO CHANGE: this is the directory where implementation files for the "dummy" (i.e., in memory) and "invalid" (i.e., no capabilities) implementations reside. src/contacts/engines/engines.pri000066400000000000000000000002351233466112000172550ustar00rootroot00000000000000INCLUDEPATH += engines # invalid backend (nonoptional) PRIVATE_HEADERS += engines/qcontactinvalidbackend_p.h SOURCES += engines/qcontactinvalidbackend.cpp src/contacts/engines/qcontactinvalidbackend.cpp000066400000000000000000000050111233466112000223050ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactinvalidbackend_p.h" /*! \class QContactInvalidEngine \inmodule QtContacts \brief The QContactInvalidEngine class provides an implementation of QContactManagerEngine whose functions always return an error. The invalid engine may be used as a reference or test engine, but does nothing. */ QT_BEGIN_NAMESPACE_CONTACTS /*! Constructs a new invalid contacts backend. */ QContactInvalidEngine::QContactInvalidEngine() { } /*! \reimp */ QString QContactInvalidEngine::managerName() const { return QString(QStringLiteral("invalid")); } QT_END_NAMESPACE_CONTACTS src/contacts/engines/qcontactinvalidbackend_p.h000066400000000000000000000051031233466112000222730ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTINVALIDBACKEND_P_H #define QCONTACTINVALIDBACKEND_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include QT_BEGIN_NAMESPACE_CONTACTS class QContactInvalidEngine : public QContactManagerEngine { public: QContactInvalidEngine(); QString managerName() const; /*! \reimp */ int managerVersion() const {return 0;} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTINVALIDBACKEND_P_H src/contacts/filters/000077500000000000000000000000001233466112000151315ustar00rootroot00000000000000src/contacts/filters/filters.pri000066400000000000000000000021751233466112000173220ustar00rootroot00000000000000INCLUDEPATH += filters PUBLIC_HEADERS += \ filters/qcontactactionfilter.h \ filters/qcontactchangelogfilter.h \ filters/qcontactdetailfilter.h \ filters/qcontactdetailrangefilter.h \ filters/qcontactfilters.h \ filters/qcontactidfilter.h \ filters/qcontactintersectionfilter.h \ filters/qcontactinvalidfilter.h \ filters/qcontactrelationshipfilter.h \ filters/qcontactunionfilter.h PRIVATE_HEADERS += \ filters/qcontactactionfilter_p.h \ filters/qcontactchangelogfilter_p.h \ filters/qcontactdetailfilter_p.h \ filters/qcontactdetailrangefilter_p.h \ filters/qcontactidfilter_p.h \ filters/qcontactintersectionfilter_p.h \ filters/qcontactrelationshipfilter_p.h \ filters/qcontactunionfilter_p.h SOURCES += \ filters/qcontactactionfilter.cpp \ filters/qcontactchangelogfilter.cpp \ filters/qcontactdetailfilter.cpp \ filters/qcontactdetailrangefilter.cpp \ filters/qcontactidfilter.cpp \ filters/qcontactintersectionfilter.cpp \ filters/qcontactinvalidfilter.cpp \ filters/qcontactrelationshipfilter.cpp \ filters/qcontactunionfilter.cpp src/contacts/filters/qcontactactionfilter.cpp000066400000000000000000000063211233466112000220570ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactactionfilter.h" #include "qcontactactionfilter_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactActionFilter \brief The QContactActionFilter class provides a filter based around an action availability criterion. \inmodule QtContacts \ingroup contacts-filters It may be used to select contacts for which a particular action is available, or contacts which contain a detail of a particular value for which the action is available. */ Q_IMPLEMENT_CONTACTFILTER_PRIVATE(QContactActionFilter); /*! * \fn QContactActionFilter::QContactActionFilter(const QContactFilter& other) * Constructs a copy of \a other if possible, otherwise constructs a new action filter */ /*! * Constructs a new action filter */ QContactActionFilter::QContactActionFilter() : QContactFilter(new QContactActionFilterPrivate) { } /*! * Sets the name of the action whose availability is required to \a action * \sa actionName() */ void QContactActionFilter::setActionName(const QString& action) { Q_D(QContactActionFilter); d->m_action = action; } /*! * Returns the action name criterion of the filter * \sa setActionName() */ QString QContactActionFilter::actionName() const { Q_D(const QContactActionFilter); return d->m_action; } QT_END_NAMESPACE_CONTACTS src/contacts/filters/qcontactactionfilter.h000066400000000000000000000047471233466112000215360ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTACTIONFILTER_H #define QCONTACTACTIONFILTER_H #include QT_BEGIN_NAMESPACE_CONTACTS class QContactActionFilterPrivate; class Q_CONTACTS_EXPORT QContactActionFilter : public QContactFilter { public: QContactActionFilter(); QContactActionFilter(const QContactFilter& other); void setActionName(const QString& action); /* Accessors */ QString actionName() const; private: Q_DECLARE_CONTACTFILTER_PRIVATE(QContactActionFilter) }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTACTIONFILTER_H src/contacts/filters/qcontactactionfilter_p.h000066400000000000000000000073361233466112000220520ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTACTIONFILTER_P_H #define QCONTACTACTIONFILTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactActionFilterPrivate : public QContactFilterPrivate { public: QContactActionFilterPrivate() : QContactFilterPrivate() { } QContactActionFilterPrivate(const QContactActionFilterPrivate& other) : QContactFilterPrivate(other), m_action(other.m_action) { } bool compare(const QContactFilterPrivate* other) const { const QContactActionFilterPrivate *od = static_cast(other); if (m_action != od->m_action) return false; return true; } QDataStream& outputToStream(QDataStream& stream, quint8 formatVersion) const { if (formatVersion == 1) { stream << m_action; } return stream; } QDataStream& inputFromStream(QDataStream& stream, quint8 formatVersion) { if (formatVersion == 1) { stream >> m_action; } return stream; } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QContactActionFilter("; dbg.nospace() << "action=" << m_action; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif Q_IMPLEMENT_CONTACTFILTER_VIRTUALCTORS(QContactActionFilter, QContactFilter::ActionFilter) QString m_action; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTACTIONFILTER_P_H src/contacts/filters/qcontactchangelogfilter.cpp000066400000000000000000000077031233466112000225360ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactchangelogfilter.h" #include "qcontactchangelogfilter_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactChangeLogFilter \brief The QContactChangeLogFilter class provides a filter based around a contact timestamp criterion. \inmodule QtContacts \ingroup contacts-filters It may be used to select contacts which have been updated or created within a certain period of time. */ /*! * \enum QContactChangeLogFilter::EventType * Enumerates the type of changes which a changelog filter can match against * \value EventAdded * \value EventChanged * \value EventRemoved */ Q_IMPLEMENT_CONTACTFILTER_PRIVATE(QContactChangeLogFilter); /*! * \fn QContactChangeLogFilter::QContactChangeLogFilter(const QContactFilter& other) * Constructs a copy of \a other if possible, otherwise constructs a new changelog filter */ /*! * Constructs a new changelog filter which matches changes of the specified \a type */ QContactChangeLogFilter::QContactChangeLogFilter(QContactChangeLogFilter::EventType type) : QContactFilter(new QContactChangeLogFilterPrivate(type)) { } /*! * Sets the type of change that this filter will match against to \a type * \sa eventType() */ void QContactChangeLogFilter::setEventType(QContactChangeLogFilter::EventType type) { Q_D(QContactChangeLogFilter); d->m_eventType = type; } /*! * Sets the date and time lower-bound criterion of the filter to \a since * \sa since() */ void QContactChangeLogFilter::setSince(const QDateTime& since) { Q_D(QContactChangeLogFilter); d->m_since = since; } /*! * Returns the date and time lower-bound criterion of the filter * \sa setSince() */ QDateTime QContactChangeLogFilter::since() const { Q_D(const QContactChangeLogFilter); return d->m_since; } /*! * Returns the type of change that this filter will match against * \sa setEventType() */ QContactChangeLogFilter::EventType QContactChangeLogFilter::eventType() const { Q_D(const QContactChangeLogFilter); return d->m_eventType; } QT_END_NAMESPACE_CONTACTS src/contacts/filters/qcontactchangelogfilter.h000066400000000000000000000054561233466112000222060ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTCHANGELOGFILTER_H #define QCONTACTCHANGELOGFILTER_H #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactChangeLogFilterPrivate; class Q_CONTACTS_EXPORT QContactChangeLogFilter: public QContactFilter { public: enum EventType { EventAdded, EventChanged, EventRemoved }; explicit QContactChangeLogFilter(QContactChangeLogFilter::EventType type = QContactChangeLogFilter::EventAdded); QContactChangeLogFilter(const QContactFilter& other); void setEventType(QContactChangeLogFilter::EventType type); void setSince(const QDateTime& since); QDateTime since() const; QContactChangeLogFilter::EventType eventType() const; private: Q_DECLARE_CONTACTFILTER_PRIVATE(QContactChangeLogFilter) }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTCHANGELOGFILTER_H src/contacts/filters/qcontactchangelogfilter_p.h000066400000000000000000000103351233466112000225150ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTCHANGELOGFILTER_P_H #define QCONTACTCHANGELOGFILTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactChangeLogFilterPrivate : public QContactFilterPrivate { public: QContactChangeLogFilterPrivate(QContactChangeLogFilter::EventType type = QContactChangeLogFilter::EventAdded) : QContactFilterPrivate() , m_eventType(type) { } QContactChangeLogFilterPrivate(const QContactChangeLogFilterPrivate& other) : QContactFilterPrivate(other) , m_eventType(other.m_eventType) , m_since(other.m_since) { } bool compare(const QContactFilterPrivate* other) const { const QContactChangeLogFilterPrivate *od = static_cast(other); if (m_eventType != od->m_eventType) return false; if (m_since != od->m_since) return false; return true; } QDataStream& outputToStream(QDataStream& stream, quint8 formatVersion) const { if (formatVersion == 1) { stream << static_cast(m_eventType) << m_since; } return stream; } QDataStream& inputFromStream(QDataStream& stream, quint8 formatVersion) { if (formatVersion == 1) { quint32 eventType; stream >> eventType >> m_since; m_eventType = (QContactChangeLogFilter::EventType)eventType; } return stream; } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QContactChangeLogFilter("; dbg.nospace() << "eventType=" << static_cast(m_eventType) << ",since=" << m_since; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif Q_IMPLEMENT_CONTACTFILTER_VIRTUALCTORS(QContactChangeLogFilter, QContactFilter::ChangeLogFilter) QContactChangeLogFilter::EventType m_eventType; QDateTime m_since; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTCHANGELOGFILTER_P_H src/contacts/filters/qcontactdetailfilter.cpp000066400000000000000000000121241233466112000220420ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactdetailfilter.h" #include "qcontactdetailfilter_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactDetailFilter \brief The QContactDetailFilter class provides a filter based around a detail value criterion \inmodule QtContacts \ingroup contacts-filters It may be used to select contacts which contain a detail of a particular type with a particular value */ Q_IMPLEMENT_CONTACTFILTER_PRIVATE(QContactDetailFilter) /*! * \fn QContactDetailFilter::QContactDetailFilter(const QContactFilter& other) * Constructs a copy of \a other if possible, otherwise constructs a new detail filter */ /*! * Constructs a new detail filter */ QContactDetailFilter::QContactDetailFilter() : QContactFilter(new QContactDetailFilterPrivate) { } /*! * Sets the type of detail which will be matched to \a type, and the field of the detail * which will contain the value criterion to \a field. * * If \a type is QContactDetail::TypeUndefined, the detail filter will match no contacts. If \a field * is not specified, or equal to -1, the detail filter acts like a "detail exists" filter; if any * detail of the specified type is present in a contact, that contact will match the filter, regardless * of what values might be stored in that detail. * * \sa detailType(), detailField() */ void QContactDetailFilter::setDetailType(QContactDetail::DetailType type, int field) { Q_D(QContactDetailFilter); d->m_type = type; d->m_fieldId = field; } /*! * Sets the value criterion of the filter to \a value. * If the field criterion (set via setDetailType()) * of the filter is not specified or equal to -1, this value will be ignored. * Note that certain backends might perform backend specific sanitization of * \a value for those detail types that are supported by them. * If the provided value cannot be sanitized, the filter is considered * invalid. * \sa value(), setDetailType() */ void QContactDetailFilter::setValue(const QVariant& value) { Q_D(QContactDetailFilter); d->m_exactValue = value; } /*! * Sets the semantics of the value matching criterion to those defined in \a flags * \sa matchFlags() */ void QContactDetailFilter::setMatchFlags(QContactFilter::MatchFlags flags) { Q_D(QContactDetailFilter); d->m_flags = flags; } /*! * Returns the semantics of the value matching criterion * \sa setMatchFlags() */ QContactFilter::MatchFlags QContactDetailFilter::matchFlags() const { Q_D(const QContactDetailFilter); return d->m_flags; } /*! * Returns the type of the details which will be inspected for matching values * \sa setDetailType() */ QContactDetail::DetailType QContactDetailFilter::detailType() const { Q_D(const QContactDetailFilter); return d->m_type; } /*! * Returns the detail field containing the value which will be matched against the value criterion * \sa setDetailType() */ int QContactDetailFilter::detailField() const { Q_D(const QContactDetailFilter); return d->m_fieldId; } /*! * Returns the value criterion of the detail filter * \sa setValue() */ QVariant QContactDetailFilter::value() const { Q_D(const QContactDetailFilter); return d->m_exactValue; } QT_END_NAMESPACE_CONTACTS src/contacts/filters/qcontactdetailfilter.h000066400000000000000000000055311233466112000215130ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTDETAILFILTER_H #define QCONTACTDETAILFILTER_H #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactDetailFilterPrivate; class Q_CONTACTS_EXPORT QContactDetailFilter : public QContactFilter { public: QContactDetailFilter(); QContactDetailFilter(const QContactFilter& other); /* Mutators */ void setDetailType(QContactDetail::DetailType type, int field = -1); void setMatchFlags(QContactFilter::MatchFlags flags); /* Filter Criterion */ void setValue(const QVariant& value); /* Accessors */ QContactDetail::DetailType detailType() const; int detailField() const; QContactFilter::MatchFlags matchFlags() const; QVariant value() const; private: Q_DECLARE_CONTACTFILTER_PRIVATE(QContactDetailFilter) }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTDETAILFILTER_H src/contacts/filters/qcontactdetailfilter_p.h000066400000000000000000000113161233466112000220300ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTDETAILFILTER_P_H #define QCONTACTDETAILFILTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactDetailFilterPrivate : public QContactFilterPrivate { public: QContactDetailFilterPrivate() : QContactFilterPrivate() , m_type(QContactDetail::TypeUndefined) , m_fieldId(-1) , m_flags(0) { } QContactDetailFilterPrivate(const QContactDetailFilterPrivate& other) : QContactFilterPrivate(other), m_type(other.m_type), m_fieldId(other.m_fieldId), m_exactValue(other.m_exactValue), m_flags(other.m_flags) { } bool compare(const QContactFilterPrivate* other) const { const QContactDetailFilterPrivate *od = static_cast(other); if (m_type != od->m_type) return false; if (m_fieldId != od->m_fieldId) return false; if (m_exactValue != od->m_exactValue) return false; if (m_flags != od->m_flags) return false; return true; } QDataStream& outputToStream(QDataStream& stream, quint8 formatVersion) const { if (formatVersion == 1) { stream << static_cast(m_type) << m_fieldId << m_exactValue << static_cast(m_flags); } return stream; } QDataStream& inputFromStream(QDataStream& stream, quint8 formatVersion) { if (formatVersion == 1) { quint32 flags; quint32 type; stream >> type >> m_fieldId >> m_exactValue >> flags; m_flags = QContactFilter::MatchFlags(flags); m_type = QContactDetail::DetailType(type); } return stream; } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QContactDetailFilter("; dbg.nospace() << "detailType=" << static_cast(m_type) << "," << "detailFieldName=" << m_fieldId << "," << "value=" << m_exactValue << "," << "matchFlags=" << static_cast(m_flags); dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif Q_IMPLEMENT_CONTACTFILTER_VIRTUALCTORS(QContactDetailFilter, QContactFilter::ContactDetailFilter) QContactDetail::DetailType m_type; int m_fieldId; QVariant m_exactValue; QContactFilter::MatchFlags m_flags; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTDETAILFILTER_P_H src/contacts/filters/qcontactdetailrangefilter.cpp000066400000000000000000000161541233466112000230660ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactdetailrangefilter.h" #include "qcontactdetailrangefilter_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactDetailRangeFilter \brief The QContactDetailRangeFilter class provides a filter based around a detail value range criterion. \inmodule QtContacts \ingroup contacts-filters It may be used to select contacts which contain a detail of a particular type with a particular value that lies in a range (either open or closed). */ Q_IMPLEMENT_CONTACTFILTER_PRIVATE(QContactDetailRangeFilter); /*! * \fn QContactDetailRangeFilter::QContactDetailRangeFilter(const QContactFilter& other) * Constructs a copy of \a other if possible, otherwise constructs a new detail range filter */ /*! * \enum QContactDetailRangeFilter::RangeFlag * Enumerates the semantics of the boundary conditions of the detail range filter * \value IncludeLower The filter will match if the field value is equal to or greater than the minimum value * \value IncludeUpper The filter will match if the field value is equal to or less than the maximum value * \value ExcludeLower The filter will match if the field value is greater than the minimum value (but not equal) * \value ExcludeUpper The filter will match if the field value is less than the maximum value (but not equal) */ /*! * Constructs a new detail range filter */ QContactDetailRangeFilter::QContactDetailRangeFilter() : QContactFilter(new QContactDetailRangeFilterPrivate) { } /*! * Sets the value range criterion of the filter to within \a min and \a max, with boundary conditions specified in the given \a flags. * If \a min or \a max is a null variant, that condition will not be checked. For example, to check for * fields with a value greater than 7, you would specify: * \code * filter.setRange(7, QVariant(), QContactDetailRangeFilter::ExcludeLower); * \endcode * \sa minValue(), maxValue() */ void QContactDetailRangeFilter::setRange(const QVariant& min, const QVariant& max, RangeFlags flags) { Q_D(QContactDetailRangeFilter); d->m_minValue = min; d->m_maxValue = max; d->m_rangeflags = flags; } /*! * Sets the match flags of the filter criterion to \a flags * * Not all flags are supported by a range filter. The supported flags include: * * \list * \li QContactFilter::MatchExactly * \li QContactFilter::MatchFixedString * \li QContactFilter::MatchCaseSensitive * \endlist * * Unsupported flags will be ignored. * * \sa matchFlags() */ void QContactDetailRangeFilter::setMatchFlags(QContactFilter::MatchFlags flags) { Q_D(QContactDetailRangeFilter); flags &= (QContactFilter::MatchExactly | QContactFilter::MatchFixedString | QContactFilter::MatchCaseSensitive); d->m_flags = flags; } /*! * Sets the type of detail which will be matched to \a type, and the field of the detail * which will contain the value criterion to \a field. * * If \a type is QContactDetail::TypeUndefined, the detail filter will match no contacts. If \a field * is not specified, or equal to -1, the detail filter acts like a "detail exists" filter; if any * detail of the specified type is present in a contact, that contact will match the filter, regardless * of what values might be stored in that detail. * * \sa detailType(), detailField() */ void QContactDetailRangeFilter::setDetailType(QContactDetail::DetailType type, int field) { Q_D(QContactDetailRangeFilter); d->m_typeId = type; d->m_fieldId = field; } /*! * Returns the match flags of the criterion, which define semantics such as case sensitivity, and exact matching. * \sa setMatchFlags() */ QContactFilter::MatchFlags QContactDetailRangeFilter::matchFlags() const { Q_D(const QContactDetailRangeFilter); return d->m_flags; } /*! * Returns the type of the details which will be inspected for matching values * \sa setDetailType() */ QContactDetail::DetailType QContactDetailRangeFilter::detailType() const { Q_D(const QContactDetailRangeFilter); return d->m_typeId; } /*! * Returns the detail field containinig the value which will be matched against the value criterion * \sa setDetailType() */ int QContactDetailRangeFilter::detailField() const { Q_D(const QContactDetailRangeFilter); return d->m_fieldId; } /*! * Returns the lower bound of the value range criterion. * If this value is null, there is no lower bound. * If it is valid, the \l rangeFlags() determines whether this value is included in the valid values. * \sa setRange() */ QVariant QContactDetailRangeFilter::minValue() const { Q_D(const QContactDetailRangeFilter); return d->m_minValue; } /*! * Returns the upper bound of the value range criterion * If this value is null, there is no upper bound. * If it is valid, the \l rangeFlags() determines whether this value is included in the valid values. * \sa setRange() */ QVariant QContactDetailRangeFilter::maxValue() const { Q_D(const QContactDetailRangeFilter); return d->m_maxValue; } /*! * Returns a set of flags which defines the boundary condition semantics of the value range criterion * \sa setRange() */ QContactDetailRangeFilter::RangeFlags QContactDetailRangeFilter::rangeFlags() const { Q_D(const QContactDetailRangeFilter); return d->m_rangeflags; } QT_END_NAMESPACE_CONTACTS src/contacts/filters/qcontactdetailrangefilter.h000066400000000000000000000064101233466112000225250ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTDETAILRANGEFILTER_H #define QCONTACTDETAILRANGEFILTER_H #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactDetailRangeFilterPrivate; class Q_CONTACTS_EXPORT QContactDetailRangeFilter : public QContactFilter { public: QContactDetailRangeFilter(); QContactDetailRangeFilter(const QContactFilter& other); enum RangeFlag { IncludeLower = 0, // [ IncludeUpper = 1, // ] ExcludeLower = 2, // ( ExcludeUpper = 0 // ) - Default is [) }; Q_DECLARE_FLAGS(RangeFlags, RangeFlag) /* Mutators */ void setDetailType(QContactDetail::DetailType type, int field = -1); void setMatchFlags(QContactFilter::MatchFlags flags); /* Filter Criterion */ void setRange(const QVariant& min, const QVariant& max, RangeFlags flags = 0); /* Accessors */ QContactDetail::DetailType detailType() const; int detailField() const; QContactFilter::MatchFlags matchFlags() const; QVariant minValue() const; QVariant maxValue() const; RangeFlags rangeFlags() const; private: Q_DECLARE_CONTACTFILTER_PRIVATE(QContactDetailRangeFilter) }; Q_DECLARE_OPERATORS_FOR_FLAGS(QContactDetailRangeFilter::RangeFlags) QT_END_NAMESPACE_CONTACTS #endif // QCONTACTDETAILRANGEFILTER_H src/contacts/filters/qcontactdetailrangefilter_p.h000066400000000000000000000126521233466112000230510ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTDETAILRANGEFILTER_P_H #define QCONTACTDETAILRANGEFILTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactDetailRangeFilterPrivate : public QContactFilterPrivate { public: QContactDetailRangeFilterPrivate() : QContactFilterPrivate(), m_typeId(QContactDetail::TypeUndefined), m_fieldId(-1), m_flags(0), m_rangeflags(0) { } QContactDetailRangeFilterPrivate(const QContactDetailRangeFilterPrivate& other) : QContactFilterPrivate(other), m_typeId(other.m_typeId), m_fieldId(other.m_fieldId), m_minValue(other.m_minValue), m_maxValue(other.m_maxValue), m_flags(other.m_flags), m_rangeflags(other.m_rangeflags) { } bool compare(const QContactFilterPrivate* other) const { const QContactDetailRangeFilterPrivate *od = static_cast(other); if (m_typeId != od->m_typeId) return false; if (m_fieldId != od->m_fieldId) return false; if (m_minValue != od->m_minValue) return false; if (m_maxValue!= od->m_maxValue) return false; if (m_flags != od->m_flags) return false; if (m_rangeflags != od->m_rangeflags) return false; return true; } QDataStream& outputToStream(QDataStream& stream, quint8 formatVersion) const { if (formatVersion == 1) { stream << m_typeId << m_fieldId << m_minValue << m_maxValue << static_cast(m_flags) << static_cast(m_rangeflags); } return stream; } QDataStream& inputFromStream(QDataStream& stream, quint8 formatVersion) { if (formatVersion == 1) { quint32 flags; quint32 rangeFlags; quint32 type; stream >> type >> m_fieldId >> m_minValue >> m_maxValue >> flags >> rangeFlags; m_flags = QContactFilter::MatchFlags(flags); m_rangeflags = QContactDetailRangeFilter::RangeFlags(rangeFlags); m_typeId = QContactDetail::DetailType(type); } return stream; } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QContactDetailRangeFilter("; dbg.nospace() << "detailType=" << static_cast(m_typeId) << "," << "detailField=" << m_fieldId << "," << "minValue=" << m_minValue << "," << "maxValue=" << m_maxValue << "," << "matchFlags=" << static_cast(m_flags) << "," << "rangeFlags=" << static_cast(m_rangeflags); dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif Q_IMPLEMENT_CONTACTFILTER_VIRTUALCTORS(QContactDetailRangeFilter, QContactFilter::ContactDetailRangeFilter) QContactDetail::DetailType m_typeId; int m_fieldId; QVariant m_minValue; QVariant m_maxValue; QContactFilter::MatchFlags m_flags; QContactDetailRangeFilter::RangeFlags m_rangeflags; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTDETAILRANGEFILTER_P_H src/contacts/filters/qcontactfilters.h000066400000000000000000000050751233466112000205160ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTFILTERS_H #define QCONTACTFILTERS_H // this file includes all of the leaf filter classes // provided by the Qt Contacts API. #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS QT_END_NAMESPACE_CONTACTS #endif // QCONTACTFILTERS_H src/contacts/filters/qcontactidfilter.cpp000066400000000000000000000075051233466112000212030ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactidfilter.h" #include "qcontactidfilter_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactIdFilter \brief The QContactIdFilter class provides a filter based around a list of contact ids \inmodule QtContacts \ingroup contacts-filters It may be used to select contacts whose ids are contained in the given list of ids. */ Q_IMPLEMENT_CONTACTFILTER_PRIVATE(QContactIdFilter); /*! * \fn QContactIdFilter::QContactIdFilter(const QContactFilter& other) * Constructs a copy of \a other if possible, otherwise constructs a new contact id filter */ /*! * Constructs a new contact id filter */ QContactIdFilter::QContactIdFilter() : QContactFilter(new QContactIdFilterPrivate) { } /*! * Sets the list which contains the ids of possible matching contacts to \a ids * \sa ids() */ void QContactIdFilter::setIds(const QList& ids) { Q_D(QContactIdFilter); d->m_ids = ids; } /*! * Adds the id \a id into the list which contains the ids of possible matching contacts * \sa setIds() */ void QContactIdFilter::add(const QContactId& id) { Q_D(QContactIdFilter); if (!d->m_ids.contains(id)) d->m_ids.append(id); } /*! * Removes the id \a id from the list which contains the ids of possible matching contacts, * if it is contained in the list, otherwise has no effect. * \sa clear() */ void QContactIdFilter::remove(const QContactId& id) { Q_D(QContactIdFilter); d->m_ids.removeAll(id); } /*! * Clears the list which contains the ids of possible matching contacts. * An id filter with a cleared list will match no contacts. * \sa setIds() */ void QContactIdFilter::clear() { Q_D(QContactIdFilter); d->m_ids.clear(); } /*! * Returns the list of ids of contacts which match this filter * \sa setIds() */ QList QContactIdFilter::ids() const { Q_D(const QContactIdFilter); return d->m_ids; } QT_END_NAMESPACE_CONTACTS src/contacts/filters/qcontactidfilter.h000066400000000000000000000051701233466112000206440ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTIDFILTER_H #define QCONTACTIDFILTER_H #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactIdFilterPrivate; class Q_CONTACTS_EXPORT QContactIdFilter : public QContactFilter { public: QContactIdFilter(); QContactIdFilter(const QContactFilter& other); /* Mutators */ void setIds(const QList& ids); void add(const QContactId& id); void remove(const QContactId& id); void clear(); /* Accessors */ QList ids() const; private: Q_DECLARE_CONTACTFILTER_PRIVATE(QContactIdFilter) }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTIDFILTER_H src/contacts/filters/qcontactidfilter_p.h000066400000000000000000000072451233466112000211700ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTIDLISTFILTER_P_H #define QCONTACTIDLISTFILTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactIdFilterPrivate : public QContactFilterPrivate { public: QContactIdFilterPrivate() : QContactFilterPrivate() { } QContactIdFilterPrivate(const QContactIdFilterPrivate& other) : QContactFilterPrivate(other), m_ids(other.m_ids) { } bool compare(const QContactFilterPrivate* other) const { const QContactIdFilterPrivate *od = static_cast(other); if (m_ids != od->m_ids) return false; return true; } QDataStream& outputToStream(QDataStream& stream, quint8 formatVersion) const { if (formatVersion == 1) { stream << m_ids; } return stream; } QDataStream& inputFromStream(QDataStream& stream, quint8 formatVersion) { if (formatVersion == 1) { stream >> m_ids; } return stream; } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QContactIdFilter("; dbg.nospace() << "ids=" << m_ids; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif Q_IMPLEMENT_CONTACTFILTER_VIRTUALCTORS(QContactIdFilter, QContactFilter::IdFilter) QList m_ids; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTIDLISTFILTER_P_H src/contacts/filters/qcontactintersectionfilter.cpp000066400000000000000000000110331233466112000233040ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactintersectionfilter.h" #include "qcontactintersectionfilter_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactIntersectionFilter \brief The QContactIntersectionFilter class provides a filter which intersects the results of other filters. \inmodule QtContacts \ingroup contacts-filters It may be used to select contacts which match all of the filters in the intersection */ Q_IMPLEMENT_CONTACTFILTER_PRIVATE(QContactIntersectionFilter); /*! * \fn QContactIntersectionFilter::QContactIntersectionFilter(const QContactFilter& other) * Constructs a copy of \a other if possible, otherwise constructs a new intersection filter */ /*! * Constructs a new intersection filter */ QContactIntersectionFilter::QContactIntersectionFilter() : QContactFilter(new QContactIntersectionFilterPrivate) { } /*! * Sets the filters whose criteria will be intersected to \a filters * \sa filters(), clear() */ void QContactIntersectionFilter::setFilters(const QList& filters) { Q_D(QContactIntersectionFilter); d->m_filters = filters; } /*! * Clears the list of filters. A cleared intersection filter will match no contacts. * \sa filters(), setFilters() */ void QContactIntersectionFilter::clear() { Q_D(QContactIntersectionFilter); d->m_filters.clear(); } /*! * Prepends the given \a filter to the list of intersected filters * \sa append(), filters() */ void QContactIntersectionFilter::prepend(const QContactFilter& filter) { Q_D(QContactIntersectionFilter); d->m_filters.prepend(filter); } /*! * Appends the given \a filter to the list of intersected filters * \sa operator<<(), prepend(), filters() */ void QContactIntersectionFilter::append(const QContactFilter& filter) { Q_D(QContactIntersectionFilter); d->m_filters.append(filter); } /*! * Removes the given \a filter from the intersection list * \sa filters(), append(), prepend(), clear() */ void QContactIntersectionFilter::remove(const QContactFilter& filter) { Q_D(QContactIntersectionFilter); d->m_filters.removeAll(filter); } /*! * Appends the given \a filter to the list of intersected filters * \sa append() */ QContactIntersectionFilter& QContactIntersectionFilter::operator<<(const QContactFilter& filter) { Q_D(QContactIntersectionFilter); d->m_filters << filter; return *this; } /*! * Returns the list of filters which form the intersection filter * \sa setFilters(), prepend(), append(), remove() */ QList QContactIntersectionFilter::filters() const { Q_D(const QContactIntersectionFilter); return d->m_filters; } QT_END_NAMESPACE_CONTACTS src/contacts/filters/qcontactintersectionfilter.h000066400000000000000000000054661233466112000227660ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTINTERSECTIONFILTER_H #define QCONTACTINTERSECTIONFILTER_H #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactIntersectionFilterPrivate; class Q_CONTACTS_EXPORT QContactIntersectionFilter : public QContactFilter { public: QContactIntersectionFilter(); QContactIntersectionFilter(const QContactFilter& other); void setFilters(const QList& filters); void prepend(const QContactFilter& filter); void append(const QContactFilter& filter); void remove(const QContactFilter& filter); void clear(); QContactIntersectionFilter& operator<<(const QContactFilter& filter); /* Accessors */ QList filters() const; private: Q_DECLARE_CONTACTFILTER_PRIVATE(QContactIntersectionFilter) }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTINTERSECTIONFILTER_H src/contacts/filters/qcontactintersectionfilter_p.h000066400000000000000000000075021233466112000232760ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTINTERSECTIONFILTER_P_H #define QCONTACTINTERSECTIONFILTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactIntersectionFilterPrivate : public QContactFilterPrivate { public: QContactIntersectionFilterPrivate() : QContactFilterPrivate() { } QContactIntersectionFilterPrivate(const QContactIntersectionFilterPrivate& other) : QContactFilterPrivate(other), m_filters(other.m_filters) { } bool compare(const QContactFilterPrivate* other) const { const QContactIntersectionFilterPrivate *od = static_cast(other); if (m_filters != od->m_filters) return false; return true; } QDataStream& outputToStream(QDataStream& stream, quint8 formatVersion) const { if (formatVersion == 1) { stream << m_filters; } return stream; } QDataStream& inputFromStream(QDataStream& stream, quint8 formatVersion) { if (formatVersion == 1) { stream >> m_filters; } return stream; } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QContactIntersectionFilter("; dbg.nospace() << "filters=" << m_filters; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif Q_IMPLEMENT_CONTACTFILTER_VIRTUALCTORS(QContactIntersectionFilter, QContactFilter::IntersectionFilter) QList m_filters; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTINTERSECTIONFILTER_P_H src/contacts/filters/qcontactinvalidfilter.cpp000066400000000000000000000073341233466112000222350ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactinvalidfilter.h" #include "qcontactfilter_p.h" /*! \class QContactInvalidFilter \brief The QContactInvalidFilter class matches no contacts. \inmodule QtContacts \ingroup contacts-filters This class provides a filter which will never match any contacts */ QT_BEGIN_NAMESPACE_CONTACTS class QContactInvalidFilterPrivate : public QContactFilterPrivate { public: QContactInvalidFilterPrivate() : QContactFilterPrivate() { } bool compare(const QContactFilterPrivate*) const { return true; // all invalid filters are alike } QDataStream& outputToStream(QDataStream& stream, quint8 formatVersion) const { Q_UNUSED(formatVersion) return stream; } QDataStream& inputFromStream(QDataStream& stream, quint8 formatVersion) { Q_UNUSED(formatVersion) return stream; } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QContactInvalidFilter()"; return dbg.maybeSpace() ; } #endif /* There is no way this can be called - d is never detached */ QContactFilterPrivate* clone() const { return new QContactInvalidFilterPrivate(); } QContactFilter::FilterType type() const { return QContactFilter::InvalidFilter; } }; /*! * Constructs a new invalid filter */ QContactInvalidFilter::QContactInvalidFilter() : QContactFilter(new QContactInvalidFilterPrivate) { } /*! * Constructs a new invalid filter, ignoring the \a other filter */ QContactInvalidFilter::QContactInvalidFilter(const QContactFilter& other) : QContactFilter(new QContactInvalidFilterPrivate) { // Initializing a QCIF from anything is the same as just constructing a QCIF Q_UNUSED(other); } QT_END_NAMESPACE_CONTACTS src/contacts/filters/qcontactinvalidfilter.h000066400000000000000000000045051233466112000216770ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTINVALIDFILTER_H #define QCONTACTINVALIDFILTER_H #include QT_BEGIN_NAMESPACE_CONTACTS class QContactInvalidFilterPrivate; class Q_CONTACTS_EXPORT QContactInvalidFilter : public QContactFilter { public: QContactInvalidFilter(); QContactInvalidFilter(const QContactFilter& other); }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTINVALIDFILTER_H src/contacts/filters/qcontactrelationshipfilter.cpp000066400000000000000000000134731233466112000233110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactrelationshipfilter.h" #include "qcontactrelationshipfilter_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactRelationshipFilter \brief The QContactRelationshipFilter class provides a filter based around relationship criteria. \inmodule QtContacts \ingroup contacts-filters It may be used to select contacts which are involved in relationships which are of a certain type, or which involve certain contacts. One common use-case might be to select the contacts which are a member of a particular group. This use-case may be met with the following filter: \code QContactRelationshipFilter groupFilter; // select all contacts which are involved groupFilter.setRelationshipType(QContactRelationship::HasMember); // in a group relationship groupFilter.setRelatedContact(groupContact()); // with the group contact groupFilter.setRelatedContactRole(QContactRelationship::First); // where the group contact is the first participant \endcode Another common use-case might be to select the groups which a particular contact is a member of. This use-case may be met with the following filter: \code QContactRelationshipFilter whichGroupsFilter; // select all contacts which are involved whichGroupsFilter.setRelationshipType(QContactRelationship::HasMember); // in a group relationship whichGroupsFilter.setRelatedContact(particularContact()); // with the particular contact whichGroupsFilter.setRelatedContactRole(QContactRelationship::Second); // where the particular contact is the second participant \endcode */ Q_IMPLEMENT_CONTACTFILTER_PRIVATE(QContactRelationshipFilter) /*! \fn QContactRelationshipFilter::QContactRelationshipFilter(const QContactFilter& other) Constructs a copy of \a other if possible, else constructs a new QContactRelationshipFilter. */ /*! Constructs a new relationship filter */ QContactRelationshipFilter::QContactRelationshipFilter() : QContactFilter(new QContactRelationshipFilterPrivate) { } /*! Sets the type of relationship which a contact must have in order to match this filter to \a relationshipType */ void QContactRelationshipFilter::setRelationshipType(const QString& relationshipType) { Q_D(QContactRelationshipFilter); d->m_relationshipType = relationshipType; } /*! Returns the type of relationship that a contact must have in order to match the filter */ QString QContactRelationshipFilter::relationshipType() const { Q_D(const QContactRelationshipFilter); return d->m_relationshipType; } /*! Sets the contact with whom the tested contact must have a relationship in order for the tested contact to match this filter to be \a relatedContact */ void QContactRelationshipFilter::setRelatedContact(const QContact &relatedContact) { Q_D(QContactRelationshipFilter); d->m_relatedContact = relatedContact; } /*! Returns the contact with whom the tested contact must have a relationship in order for the tested contact to match this filter */ QContact QContactRelationshipFilter::relatedContact() const { Q_D(const QContactRelationshipFilter); return d->m_relatedContact; } /*! Sets the role in the relationship with the tested contact that the related contact must play in order for the tested contact to match this filter to be \a relatedContactRole */ void QContactRelationshipFilter::setRelatedContactRole(QContactRelationship::Role relatedContactRole) { Q_D(QContactRelationshipFilter); d->m_relatedContactRole = relatedContactRole; } /*! Returns the role in the relationship with the tested contact that the related contact must play in order for the tested contact to match this filter */ QContactRelationship::Role QContactRelationshipFilter::relatedContactRole() const { Q_D(const QContactRelationshipFilter); return d->m_relatedContactRole; } QT_END_NAMESPACE_CONTACTS src/contacts/filters/qcontactrelationshipfilter.h000066400000000000000000000055221233466112000227520ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTRELATIONSHIPFILTER_H #define QCONTACTRELATIONSHIPFILTER_H #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactRelationshipFilterPrivate; class Q_CONTACTS_EXPORT QContactRelationshipFilter : public QContactFilter { public: QContactRelationshipFilter(); QContactRelationshipFilter(const QContactFilter& other); void setRelationshipType(const QString& relationshipType); void setRelatedContact(const QContact& relatedContact); void setRelatedContactRole(QContactRelationship::Role relatedContactRole); QString relationshipType() const; QContact relatedContact() const; QContactRelationship::Role relatedContactRole() const; private: Q_DECLARE_CONTACTFILTER_PRIVATE(QContactRelationshipFilter) }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTRELATIONSHIPFILTER_H src/contacts/filters/qcontactrelationshipfilter_p.h000066400000000000000000000112211233466112000232620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTRELATIONSHIPFILTER_P_H #define QCONTACTRELATIONSHIPFILTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactRelationshipFilterPrivate : public QContactFilterPrivate { public: QContactRelationshipFilterPrivate() : QContactFilterPrivate(), m_relatedContactRole(QContactRelationship::Either) { } QContactRelationshipFilterPrivate(const QContactRelationshipFilterPrivate& other) : QContactFilterPrivate(other), m_relationshipType(other.m_relationshipType), m_relatedContact(other.m_relatedContact), m_relatedContactRole(other.m_relatedContactRole) { } bool compare(const QContactFilterPrivate* other) const { const QContactRelationshipFilterPrivate *od = static_cast(other); if (m_relatedContactRole != od->m_relatedContactRole) return false; if (m_relatedContact != od->m_relatedContact) return false; if (m_relationshipType != od->m_relationshipType) return false; return true; } QDataStream& outputToStream(QDataStream& stream, quint8 formatVersion) const { if (formatVersion == 1) { stream << m_relationshipType << m_relatedContact << static_cast(m_relatedContactRole); } return stream; } QDataStream& inputFromStream(QDataStream& stream, quint8 formatVersion) { if (formatVersion == 1) { quint32 role; stream >> m_relationshipType >> m_relatedContact >> role; m_relatedContactRole = QContactRelationship::Role(role); } return stream; } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QContactRelationshipFilter("; dbg.nospace() << "relationshipType=" << m_relationshipType << "," << "relatedContact=" << m_relatedContact << "," << "relatedContactRole=" << static_cast(m_relatedContactRole); dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif Q_IMPLEMENT_CONTACTFILTER_VIRTUALCTORS(QContactRelationshipFilter, QContactFilter::RelationshipFilter) QString m_relationshipType; QContact m_relatedContact; QContactRelationship::Role m_relatedContactRole; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTRELATIONSHIPFILTER_P_H src/contacts/filters/qcontactunionfilter.cpp000066400000000000000000000104611233466112000217320ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactunionfilter.h" #include "qcontactunionfilter_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactUnionFilter \brief The QContactUnionFilter class provides a filter which unions the results of other filters. \inmodule QtContacts \ingroup contacts-filters It may be used to select contacts which match all of the filters in the union */ Q_IMPLEMENT_CONTACTFILTER_PRIVATE(QContactUnionFilter); /*! * \fn QContactUnionFilter::QContactUnionFilter(const QContactFilter& other) * Constructs a copy of \a other if possible, otherwise constructs a new union filter */ /*! * Constructs a new intersection filter */ QContactUnionFilter::QContactUnionFilter() : QContactFilter(new QContactUnionFilterPrivate) { } /*! * Sets the filters whose criteria will be unioned to \a filters * \sa filters() */ void QContactUnionFilter::setFilters(const QList& filters) { Q_D(QContactUnionFilter); d->m_filters = filters; } /*! * Clears the list of filters. A cleared union filter will match no contacts. * \sa filters(), remove() */ void QContactUnionFilter::clear() { Q_D(QContactUnionFilter); d->m_filters.clear(); } /*! * Prepends the given \a filter to the list of unioned filters * \sa append(), filters() */ void QContactUnionFilter::prepend(const QContactFilter& filter) { Q_D(QContactUnionFilter); d->m_filters.prepend(filter); } /*! * Appends the given \a filter to the list of unioned filters * \sa operator<<(), prepend(), filters() */ void QContactUnionFilter::append(const QContactFilter& filter) { Q_D(QContactUnionFilter); d->m_filters.append(filter); } /*! * Removes the given \a filter from the union list * \sa filters(), append(), prepend(), clear() */ void QContactUnionFilter::remove(const QContactFilter& filter) { Q_D(QContactUnionFilter); d->m_filters.removeAll(filter); } /*! * Appends the given \a filter to the list of unioned filters * \sa append() */ QContactUnionFilter& QContactUnionFilter::operator<<(const QContactFilter& filter) { Q_D(QContactUnionFilter); d->m_filters << filter; return *this; } /*! * Returns the list of filters which form the union filter * \sa setFilters(), prepend(), append(), remove() */ QList QContactUnionFilter::filters() const { Q_D(const QContactUnionFilter); return d->m_filters; } QT_END_NAMESPACE_CONTACTS src/contacts/filters/qcontactunionfilter.h000066400000000000000000000053671233466112000214100ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTUNIONFILTER_H #define QCONTACTUNIONFILTER_H #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactUnionFilterPrivate; class Q_CONTACTS_EXPORT QContactUnionFilter : public QContactFilter { public: QContactUnionFilter(); QContactUnionFilter(const QContactFilter& other); void setFilters(const QList& filters); void prepend(const QContactFilter& filter); void append(const QContactFilter& filter); void remove(const QContactFilter& filter); void clear(); QContactUnionFilter& operator<<(const QContactFilter& filter); /* Accessors */ QList filters() const; private: Q_DECLARE_CONTACTFILTER_PRIVATE(QContactUnionFilter) }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTUNIONFILTER_H src/contacts/filters/qcontactunionfilter_p.h000066400000000000000000000073471233466112000217270ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTUNIONFILTER_P_H #define QCONTACTUNIONFILTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactUnionFilterPrivate : public QContactFilterPrivate { public: QContactUnionFilterPrivate() : QContactFilterPrivate() { } QContactUnionFilterPrivate(const QContactUnionFilterPrivate& other) : QContactFilterPrivate(other), m_filters(other.m_filters) { } bool compare(const QContactFilterPrivate* other) const { const QContactUnionFilterPrivate *od = static_cast(other); if (m_filters != od->m_filters) return false; return true; } QDataStream& outputToStream(QDataStream& stream, quint8 formatVersion) const { if (formatVersion == 1) { stream << m_filters; } return stream; } QDataStream& inputFromStream(QDataStream& stream, quint8 formatVersion) { if (formatVersion == 1) { stream >> m_filters; } return stream; } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QContactUnionFilter("; dbg.nospace() << "filters=" << m_filters; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif Q_IMPLEMENT_CONTACTFILTER_VIRTUALCTORS(QContactUnionFilter, QContactFilter::UnionFilter) QList m_filters; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTUNIONFILTER_P_H src/contacts/qcontact.cpp000066400000000000000000000655251233466112000160160ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontact.h" #include "qcontact_p.h" #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif #include #include "qcontactactiondescriptor.h" #include "qcontactdetail_p.h" #include "qcontactdetails.h" #include "qcontactmanager_p.h" #include "qcontactactionmanager_p.h" #include "qcontactaction.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContact \brief The QContact class represents an addressbook contact. \inmodule QtContacts \ingroup contacts-main Individual contacts, groups, and other types of contacts are represented with a QContact object. In addition to the type, a QContact consists of information that belongs to the contact, some information about the relationships that the contact has, and the preferred ways to interact with the contact. A QContact object has a collection of details (like a name, phone numbers and email addresses). Each detail (which can have multiple fields) is stored in an appropriate subclass of QContactDetail, and the QContact allows retrieving these details in various ways. Depending on the details of the QContact, certain actions are available for a contact. An instance of a QContact can return a list of actions that can be performed on it, and a list of details supported by a specific action can be retrieved (for example - a list of phone numbers supported by a specific "Call" action). It is also possible to store one detail for each type of action that is the "preferred" detail to use. A QContact may have zero or more relationships with other contacts. For example, a group contact would have a \c "HasMember" relationship with the QContacts that are its members. Spouses, managers and assistants can also be represented this way. A QContact instance represents the in-memory version of an addressbook contact, and has no tie to a specific QContactManager. It is possible for the contents of a QContact to change independently of the contents that are stored persistently in a QContactManager. A QContact has an ID associated with it when it is first retrieved from a QContactManager, or after it has been first saved, and this allows clients to track changes using the signals in QContactManager. A QContact has a number of mandatory details: \list \li A QContactType, with the type of the contact (individual contact, group etc) \endlist \sa QContactManager, QContactDetail */ /*! * \fn QList QContact::details() const * Returns a list of details of the template parameter type. The type must be * a subclass of QContactDetail. * * For example: * \snippet qtcontactsdocsample/qtcontactsdocsample.cpp 3 */ /*! * \fn T QContact::detail() const * Returns the first detail of the template parameter type, as returned by the template details() function. * The type must be a subclass of QContactDetail. */ /*! * \fn QContact::operator!=(const QContact &other) const * Returns true if this contacts id or details are different to those of the \a other contact. */ /*! Construct an empty contact. The contact will have an empty id, and have type \l QContactType::TypeContact. The isEmpty() function will return true. */ QContact::QContact() : d(new QContactData) { clearDetails(); } /*! Initializes this QContact from \a other */ QContact::QContact(const QContact& other) : d(other.d) { } /*! * Returns true if this QContact is empty, false if not. * * An empty QContact has no extra details. * The type of the contact is irrelevant. */ bool QContact::isEmpty() const { /* Every contact has a type field */ return (d->m_details.count() == 1); } /*! * Removes all details of the contact. * This function does not modify the id or type of the contact. * Calling isEmpty() after calling this function will return true. */ void QContact::clearDetails() { d->m_details.clear(); // insert the contact type detail. QContactType contactType; contactType.setType(QContactType::TypeContact); contactType.d->m_access = QContactDetail::Irremovable; d->m_details.insert(0, contactType); } /*! Replace the contents of this QContact with \a other */ QContact& QContact::operator=(const QContact& other) { d = other.d; return *this; } /*! Frees the memory used by this QContact */ QContact::~QContact() { } /*! Returns the QContactId that identifies this contact. This may have been set when the contact was retrieved from a particular manager, or when the contact was first saved in a manager. The QContactId is only valid with a specific manager. See \l QContactManager::saveContact() for more information. */ QContactId QContact::id() const { return d.constData()->m_id; } /*! * Sets the id of this contact to \a id. * * Note that this only affects this object, not any corresponding structures stored * by a QContactManager. * * If you change the id of a contact and save the contact * in a manager, the previously existing contact will still * exist. You can do this to create copies (possibly modified) * of an existing contact, or to save a contact in a different manager. * * \sa QContactManager::saveContact() */ void QContact::setId(const QContactId& id) { d->m_id = id; } /*! * Returns the type of the contact. Every contact has exactly one type which * is either set manually (by saving a modified copy of the QContactType * in the contact, or by calling \l setType()) or synthesized automatically. * * \sa setType() */ QContactType::TypeValues QContact::type() const { // type is detail 0 QContactType::TypeValues type = static_cast(d.constData()->m_details.at(0).value(QContactType::FieldType).toInt()); return type; } /*! * Sets the type of the contact to the given \a type. */ void QContact::setType(const QContactType::TypeValues& type) { // type is detail 0 d->m_details[0].setValue(QContactType::FieldType, type); d->m_details[0].d->m_access = QContactDetail::Irremovable; } /*! * Returns the list of tags for this contact. Tags are used for non-exclusive categorization. * * \sa QContactTag */ QStringList QContact::tags() const { QStringList tags; foreach (const QContactTag& tagDetail, details()) { tags.append(tagDetail.tag()); } return tags; } /*! * Removes all tags associated with the contact. * * \sa QContactTag */ void QContact::clearTags() { d->removeOnly(QContactTag::Type); } /*! * Adds the \a tag to this contact. * * \sa QContactTag */ void QContact::addTag(const QString& tag) { QContactTag tagDetail; tagDetail.setTag(tag); saveDetail(&tagDetail); } /*! * Sets the list of tags associated with the contact to \a tags. * * \sa QContactTag */ void QContact::setTags(const QStringList& tags) { d->removeOnly(QContactTag::Type); foreach (const QString& tag, tags) { addTag(tag); } } /*! \fn QContactDetail QContact::detail(QContactDetail::DetailType type) const Returns the first detail stored in the contact which with the given \a type. The \a type argument is typically the detail type constant provided by a specific subclass of QContactDetail. For example: \snippet qtcontactsdocsample/qtcontactsdocsample.cpp 0 It would usually be more convenient to use the template version of this function, in the following manner: \snippet qtcontactsdocsample/qtcontactsdocsample.cpp 1 */ QContactDetail QContact::detail(QContactDetail::DetailType type) const { // If type not defined, return first detail if (type == QContactDetail::TypeUndefined) return d.constData()->m_details.first(); // build the sub-list of matching details. for (int i = 0; i < d.constData()->m_details.size(); i++) { const QContactDetail& existing = d.constData()->m_details.at(i); if (QContactDetailPrivate::detailPrivate(existing)->m_type == type) { return existing; } } return QContactDetail(); } /*! \fn QList QContact::details(QContactDetail::DetailType type) const Returns a list of details of the given \a type. The \a type argument is typically the detail type constant provided by a specific subclass of QContactDetail. For example: \snippet qtcontactsdocsample/qtcontactsdocsample.cpp 2 It would usually be more convenient to use the template version of this function, in the following manner: \snippet qtcontactsdocsample/qtcontactsdocsample.cpp 3 */ QList QContact::details(QContactDetail::DetailType type) const { // build the sub-list of matching details. QList sublist; // special case if (type == QContactDetail::TypeUndefined) { sublist = d.constData()->m_details; } else { for (int i = 0; i < d->m_details.size(); i++) { const QContactDetail& existing = d.constData()->m_details.at(i); if (QContactDetailPrivate::detailPrivate(existing)->m_type == type) { sublist.append(existing); } } } return sublist; } /*! * Appends the given \a detail to the list of stored details. * This is a convenience method intended to be used e.g. by backend * developers to populate an empty QContact object when fetching * data from the backend. * If \a detail is a QContactType, the existing contact type will * be overwritten with \a detail. There is never more than one contact type * in a contact. * * Note that if another detail of the same type and id has been previously saved in * this contact, that detail is duplicated. For this reason, this method * should not be used to update an existing contact object with a newer version * of an existing detail. For this use case, the clients must use the * saveDetail() method. * * Returns true if the detail was appended successfully, otherwise returns false. * * \sa saveDetail() */ bool QContact::appendDetail(const QContactDetail &detail) { if (detail.isEmpty()) return false; /* Also handle contact type specially - only one of them. */ if (QContactDetailPrivate::detailPrivate(detail)->m_type == QContactType::Type) { d->m_details[0] = detail; d->m_details[0].d->m_access |= QContactDetail::Irremovable; return true; } d->m_details.append(detail); return true; } /*! * Saves the given \a detail in the list of stored details, and sets the detail's id. * If another detail of the same type and id has been previously saved in * this contact, that detail is overwritten. Otherwise, a new id is generated * and set in the detail, and the detail is added to the contact. * * If the detail's access constraint includes \c QContactDetail::ReadOnly, * this function will return true and save the detail in the contact, * however attempting to save the contact in a manager may fail (if that manager * decides that the read only detail should not be updated). * Details with the \c QContactDetail::ReadOnly constraint set are typically provided * in a contact by the manager, and are usually information that is either * synthesized, or not intended to be changed by the user (e.g. presence information * for other contacts). * * If \a detail is a QContactType, the existing contact type will * be overwritten with \a detail. There is never more than one contact type * in a contact. * * * Be aware that if a contact is retrieved (or reloaded) from the backend, the * keys of any details it contains may have been changed by the backend, or other * threads may have modified the contact details in the backend. Therefore, * clients should reload the detail that they wish to save in a contact after retrieving * the contact, in order to avoid creating unwanted duplicated details. * * Returns true if the detail was saved successfully, otherwise returns false. * * Note that the caller retains ownership of the detail. */ bool QContact::saveDetail(QContactDetail* detail) { if (!detail) return false; /* Also handle contact type specially - only one of them. */ if (QContactDetailPrivate::detailPrivate(*detail)->m_type == QContactType::Type) { detail->d->m_access |= QContactDetail::Irremovable; d->m_details[0] = *detail; return true; } // try to find the "old version" of this field // ie, the one with the same type and id, but different value or attributes. for (int i = 0; i < d.constData()->m_details.size(); i++) { const QContactDetail& curr = d.constData()->m_details.at(i); if (detail->d.constData()->m_type == curr.d.constData()->m_type && detail->d.constData()->m_id == curr.d.constData()->m_id) { // update the detail constraints of the supplied detail detail->d->m_access = curr.accessConstraints(); // Found the old version. Replace it with this one. d->m_details[i] = *detail; return true; } } // this is a new detail! add it to the contact. d->m_details.append(*detail); return true; } /*! * Removes the \a detail from the contact. * * The detail in the contact which has the same key as that of the given \a detail * will be removed if it exists. Only the key is used for comparison - that is, the * information in the detail may be different. * * Any action preferences for the matching detail is also removed. * * Be aware that if a contact is retrieved (or reloaded) from the backend, the * keys of any details it contains may have been changed by the backend, or other * threads may have modified the contact details in the backend. Therefore, * clients should reload the detail that they wish to remove from a contact after retrieving * the contact, in order to ensure that the remove operation is successful. * * If the detail's access constraint includes \c QContactDetail::Irremovable, * this function will return false. * * Returns true if the detail was removed successfully, false if an error occurred. * * Note that the caller retains ownership of the detail. */ bool QContact::removeDetail(QContactDetail* detail) { if (!detail) return false; // find the detail stored in the contact which has the same key as the detail argument int removeIndex = -1; for (int i = 0; i < d.constData()->m_details.size(); i++) { if (d.constData()->m_details.at(i).key() == detail->key()) { removeIndex = i; break; } } // make sure the detail exists (in some form) in the contact. if (removeIndex < 0) return false; if (detail->accessConstraints() & QContactDetail::Irremovable) return false; if (!d.constData()->m_details.contains(*detail)) return false; // remove any preferences we may have stored for the detail. QStringList keys = d.constData()->m_preferences.keys(); for (int i = 0; i < keys.size(); i++) { QString prefKey = keys.at(i); if (d.constData()->m_preferences.value(prefKey) == detail->d.constData()->m_id) { d->m_preferences.remove(prefKey); } } // then remove the detail. d->m_details.removeAt(removeIndex); return true; } /*! Returns true if this contact is equal to the \a other contact, false if either the id or stored details are not the same */ bool QContact::operator==(const QContact& other) const { // Id must be the same if (other.d.constData()->m_id != d.constData()->m_id) return false; // There must be same amount of details if (other.d.constData()->m_details.size() != d.constData()->m_details.size()) return false; // All details must match foreach (QContactDetail detail, other.d.constData()->m_details) { if (!d.constData()->m_details.contains(detail)) return false; } // All equal return true; } /*! \relates QContact Returns the hash value for \a key. */ uint qHash(const QContact &key) { uint hash = qHash(key.id()); foreach (const QContactDetail& detail, key.details()) { hash += qHash(detail); } return hash; } #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QContact& contact) { dbg.nospace() << "QContact(" << contact.id() << ")"; foreach (const QContactDetail& detail, contact.details()) { dbg.space() << '\n' << detail; } return dbg.maybeSpace(); } #endif #ifndef QT_NO_DATASTREAM /*! * Writes \a contact to the stream \a out. */ QDataStream& operator<<(QDataStream& out, const QContact& contact) { quint8 formatVersion = 1; // Version of QDataStream format for QContact return out << formatVersion << contact.id() << contact.details() << contact.d->m_preferences; } /*! * Reads a contact from stream \a in into \a contact. */ QDataStream& operator>>(QDataStream& in, QContact& contact) { contact = QContact(); quint8 formatVersion; in >> formatVersion; if (formatVersion == 1) { QContactId id; QList details; QMap preferences; in >> id >> contact.d->m_details >> contact.d->m_preferences; contact.setId(id); } else { in.setStatus(QDataStream::ReadCorruptData); } return in; } #endif /*! Returns a list of relationships of the given \a relationshipType in which this contact is a participant. If \a relationshipType is empty, all relationships will be returned. \note This function only examines the relationships that were present when this contact was retrieved from a manager. You can also query the manager directly, if you require the most up to date information. \snippet qtcontactsdocsample/qtcontactsdocsample.cpp 5 \sa QContactRelationshipFetchRequest, QContactManager::relationships() */ QList QContact::relationships(const QString& relationshipType) const { // if empty, then they want all relationships if (relationshipType.isEmpty()) return d.constData()->m_relationshipsCache; // otherwise, filter on type. QList retn; for (int i = 0; i < d.constData()->m_relationshipsCache.size(); i++) { QContactRelationship curr = d.constData()->m_relationshipsCache.at(i); if (curr.relationshipType() == relationshipType) { retn.append(curr); } } return retn; } /*! Returns a list of the ids of contacts which have a relationship of the given \a relationshipType with this contact. The \a role parameter describes the role that the related contacts have in the relationship. If \a relationshipType is empty, relationships of all types will be considered. \note This function only examines the relationships that were present when this contact was retrieved from a manager. You can also query the manager directly, if you require the most up to date information. \snippet qtcontactsdocsample/qtcontactsdocsample.cpp 6 \sa QContactRelationshipFetchRequest, QContactManager::relationships() */ QList QContact::relatedContacts(const QString& relationshipType, QContactRelationship::Role role) const { QList retn; for (int i = 0; i < d.constData()->m_relationshipsCache.size(); i++) { QContactRelationship curr = d.constData()->m_relationshipsCache.at(i); if (relationshipType.isEmpty() || curr.relationshipType() == relationshipType) { // check that the other contacts fill the given role if (role == QContactRelationship::First) { if (curr.first().id() != d.constData()->m_id) { if (!retn.contains(curr.first())) { retn.append(curr.first()); } } } else if (role == QContactRelationship::Second) { if (curr.first().id() == d.constData()->m_id) { if (!retn.contains(curr.second())) { retn.append(curr.second()); } } } else { // role == Either. if (curr.first().id() == d.constData()->m_id) { if (!retn.contains(curr.second())) { retn.append(curr.second()); } } else { if (!retn.contains(curr.first())) { retn.append(curr.first()); } } } } } return retn; } /*! * Return a list of descriptors for the actions available to be performed on this contact. * * The actions considered can be restricted by the optional parameters * The actions can be restricted to those provided by a specific service with the \a serviceName parameter. * If \a serviceName is empty, actions provided by any service will be returned if the * contact meets the required criteria (contains details of the correct type, etc). * * Each action that matches the above criteria will be tested to see if this contact is supported * by the action, and a list of the action descriptors that are supported will be returned. */ QList QContact::availableActions(const QString& serviceName) const { QList ret; QList allds = QContactActionManager::instance()->availableActions(*this); foreach (const QContactActionDescriptor& d, allds) { if (serviceName.isEmpty() || d.serviceName() == serviceName) { ret.append(d); } } return ret; } /*! * Set a particular detail (\a preferredDetail) as the preferred detail for any actions with the given \a actionName. * * The \a preferredDetail object must exist in this object, and the \a actionName cannot be empty. * * Returns true if the preference could be recorded, and false otherwise. * * \sa preferredDetail() */ bool QContact::setPreferredDetail(const QString& actionName, const QContactDetail& preferredDetail) { // if the given action name is empty, bad argument. if (actionName.isEmpty()) return false; // check to see whether the the given preferredDetail is saved in this contact if (!d.constData()->m_details.contains(preferredDetail)) return false; // otherwise, save the preference. d->m_preferences.insert(actionName, preferredDetail.d.constData()->m_id); return true; } /*! * Returns true if the given \a detail is a preferred detail for the given \a actionName, * or for any action if the \a actionName is empty. * * \sa preferredDetail() */ bool QContact::isPreferredDetail(const QString& actionName, const QContactDetail& detail) const { if (!d.constData()->m_details.contains(detail)) return false; if (actionName.isEmpty()) return d.constData()->m_preferences.values().contains(detail.d.constData()->m_id); QMap::const_iterator it = d.constData()->m_preferences.find(actionName); if (it != d.constData()->m_preferences.end() && it.value() == detail.d.constData()->m_id) return true; return false; } /*! * Returns the preferred detail for a given \a actionName. * * If the \a actionName is empty, or there is no preference recorded for * the supplied \a actionName, returns an empty QContactDetail. * * \sa preferredDetails() */ QContactDetail QContact::preferredDetail(const QString& actionName) const { // if the given action name is empty, bad argument. if (actionName.isEmpty()) return QContactDetail(); if (!d.constData()->m_preferences.contains(actionName)) return QContactDetail(); QContactDetail retn; int detId = d.constData()->m_preferences.value(actionName); for (int i = 0; i < d.constData()->m_details.size(); i++) { QContactDetail det = d.constData()->m_details.at(i); if (det.d.constData()->m_id == detId) { // found it. retn = det; break; } } return retn; } /*! * Returns the recorded detail preferences for action names. * * Each entry in the map has the action name as the key, and the corresponding * preferred detail as the value. */ QMap QContact::preferredDetails() const { QMap ret; QMap::const_iterator it = d.constData()->m_preferences.constBegin(); while (it != d.constData()->m_preferences.constEnd()) { ret.insert(it.key(), preferredDetail(it.key())); ++it; } return ret; } /* Helper functions for QContactData */ void QContactData::removeOnly(QContactDetail::DetailType type) { QList::iterator dit = m_details.begin(); while (dit != m_details.end()) { // XXX this doesn't check type if (dit->type() == type) dit = m_details.erase(dit); else ++dit; } } void QContactData::removeOnly(const QSet& types) { QList::iterator dit = m_details.begin(); while (dit != m_details.end()) { // XXX this doesn't check type if (types.contains(dit->type())) dit = m_details.erase(dit); else ++dit; } } QT_END_NAMESPACE_CONTACTS src/contacts/qcontact.h000066400000000000000000000131651233466112000154540ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACT_H #define QCONTACT_H #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactActionDescriptor; class QContactId; class QContactManager; class QContactData; class Q_CONTACTS_EXPORT QContact { public: QContact(); ~QContact(); QContact(const QContact& other); QContact& operator=(const QContact& other); bool operator==(const QContact &other) const; bool operator!=(const QContact &other) const {return !(other == *this);} /* Unique ID */ QContactId id() const; void setId(const QContactId& id); /* Type - contact, group, metacontact, ... */ QContactType::TypeValues type() const; void setType(const QContactType::TypeValues& type); QStringList tags() const; void clearTags(); void addTag(const QString& tag); void setTags(const QStringList& tags); /* Is this an empty contact? */ bool isEmpty() const; void clearDetails(); /* Access details of particular type */ QContactDetail detail(QContactDetail::DetailType type) const; QList details(QContactDetail::DetailType type = QContactDetail::TypeUndefined) const; bool appendDetail(const QContactDetail &detail); /* Templated (type-specific) detail retrieval */ template QList details() const { QList props = details(T::Type); QList ret; for (int i=0; i T detail() const { return T(detail(T::Type)); } /* generic detail addition/removal functions */ bool saveDetail(QContactDetail* detail); bool removeDetail(QContactDetail* detail); /* Relationships that this contact was involved in when it was retrieved from the manager */ QList relationships(const QString& relationshipType = QString()) const; QList relatedContacts(const QString& relationshipType = QString(), QContactRelationship::Role role = QContactRelationship::Either) const; /* Actions available to be performed on this contact */ QList availableActions(const QString& serviceName = QString()) const; /* Preferences (eg, set a particular detail preferred for the SMS action) - subject to change! */ bool setPreferredDetail(const QString& actionName, const QContactDetail& preferredDetail); bool isPreferredDetail(const QString& actionName, const QContactDetail& detail) const; QContactDetail preferredDetail(const QString& actionName) const; QMap preferredDetails() const; private: friend class QContactData; friend class QContactManager; friend class QContactManagerData; friend class QContactManagerEngine; Q_CONTACTS_EXPORT friend QDataStream& operator<<(QDataStream& out, const QContact& contact); Q_CONTACTS_EXPORT friend QDataStream& operator>>(QDataStream& in, QContact& contact); QSharedDataPointer d; }; Q_CONTACTS_EXPORT uint qHash(const QContact& key); #ifndef QT_NO_DEBUG_STREAM Q_CONTACTS_EXPORT QDebug operator<<(QDebug dbg, const QContact& contact); #endif #ifndef QT_NO_DATASTREAM Q_CONTACTS_EXPORT QDataStream& operator<<(QDataStream& out, const QContact& contact); Q_CONTACTS_EXPORT QDataStream& operator>>(QDataStream& in, QContact& contact); #endif QT_END_NAMESPACE_CONTACTS QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(QTCONTACTS_PREPEND_NAMESPACE(QContact), Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif // QCONTACT_H src/contacts/qcontact_p.h000066400000000000000000000064551233466112000157770ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACT_P_H #define QCONTACT_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactData : public QSharedData { public: QContactData() : QSharedData() { } QContactData(const QContactData& other) : QSharedData(other), m_id(other.m_id), m_details(other.m_details), m_relationshipsCache(other.m_relationshipsCache), m_preferences(other.m_preferences) { } ~QContactData() {} QContactId m_id; QList m_details; QList m_relationshipsCache; QMap m_preferences; // Helper function void removeOnly(QContactDetail::DetailType type); void removeOnly(const QSet& types); // Trampoline static QSharedDataPointer& contactData(QContact& contact) {return contact.d;} }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACT_P_H src/contacts/qcontactabstractrequest.cpp000066400000000000000000000344651233466112000211520ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactabstractrequest.h" #include "qcontactabstractrequest_p.h" #ifndef QT_NO_DEBUG_STREAM #include #endif #include "qcontactmanager_p.h" #include "qcontactmanagerengine.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactAbstractRequest \brief The QContactAbstractRequest class provides a mechanism for asynchronous requests to be made of a manager if it supports them. \inmodule QtContacts \ingroup contacts-main It allows a client to asynchronously request some functionality of a particular QContactManager. Instances of the class will emit signals when the state of the request changes, or when more results become available. Clients should not attempt to create instances of this class directly, but should instead use the use-case-specific classes derived from this class. All such request classes have a similar interface: clients set the parameters of the asynchronous call, including which manager the request will be made of, and then call the start() slot of the request. The manager will then enqueue or begin to process the request, at which point the request's state will transition from \c InactiveState to \c ActiveState. After any state transition, the request will emit the stateChanged() signal. The manager may periodically update the request with results, at which point the request will emit the resultsAvailable() signal. These results are not guaranteed to have a stable ordering. Error information is considered a result, so some requests will emit the resultsAvailable() signal even if no results are possible from the request (for example, a contact remove request) when the manager updates the request with information about any errors which may have occurred. Please see the class documentation of each of the use-case-specific classes derived from this class for information about how to retrieve the results of a request (including error information). In all cases, those functions are synchronous, and will return the cached result set with which the manager has updated the request instance if the resultsAvailable() signal has been emitted. Clients can choose which signals they wish to handle from a request. If the client is not interested in interim results, they can choose to handle only the stateChanged() signal, and in the slot to which that signal is connected, check whether the state has changed to either \c FinishedState or \c CanceledState (both of which signify that the manager has finished handling the request, and that the request will not be updated with any more results). If the client is not interested in any results (including error information), they may choose to delete the request after calling \l start(), or simply not connect the request's signals to any slots. If the request is allocated via operator new, the client must delete the request when they are no longer using it in order to avoid leaking memory. That is, the client retains ownership of the request. The client may delete a heap-allocated request in various ways: by deleting it directly (but not within a slot connected to a signal emitted by the request), or by using the deleteLater() slot to schedule the request for deletion when control returns to the event loop (from within a slot connected to a signal emitted by the request, for example \l stateChanged()). An active request may be deleted by the client, but the client will not receive any notifications about whether the request succeeded or not, nor any results of the request. Because clients retain ownership of any request object, and may delete a request object at any time, manager engine implementors must be careful to ensure that they do not assume that a request has not been deleted at some point during processing of a request, particularly if the engine has a multithreaded implementation. It is suggested that engine implementors read the \l{Qt Contacts Manager Engines} documentation for more information on this topic. */ /*! \fn QContactAbstractRequest::stateChanged(QContactAbstractRequest::State newState) This signal is emitted when the state of the request is changed. The new state of the request will be contained in \a newState. */ /*! \fn QContactAbstractRequest::resultsAvailable() This signal is emitted when new results are available. Results can include the operation error which may be accessed via error(), or derived-class-specific results which are accessible through the derived class API. \sa error() */ /*! \enum QContactAbstractRequest::RequestType Enumerates the various possible types of asynchronous requests \value InvalidRequest An invalid request \value ContactFetchRequest A request to fetch a list of contacts \value ContactIdFetchRequest A request to fetch a list of contact ids \value ContactRemoveRequest A request to remove a list of contacts \value ContactSaveRequest A request to save a list of contacts \value RelationshipFetchRequest A request to fetch relationships between contacts \value RelationshipRemoveRequest A request to remove any relationships which match the request criteria \value RelationshipSaveRequest A request to save a list of relationships \value ContactFetchByIdRequest A request to fetch a list of contacts given a list of ids */ /*! \enum QContactAbstractRequest::State Enumerates the various states that a request may be in at any given time \value InactiveState Operation not yet started \value ActiveState Operation started, not yet finished \value CanceledState Operation is finished due to cancellation \value FinishedState Operation successfully completed */ /*! \enum QContactAbstractRequest::StorageLocation Enumerates the different storage locations for a request. \value UserDataStorage A storage location where user data is stored. \value SystemStorage A storage location where system files are stored. Depending on the backend implementation, the access rights for different storage locations might vary. */ /*! \fn QContactAbstractRequest::QContactAbstractRequest(QObject* parent) Constructs a new, invalid asynchronous request with the specified \a parent */ /*! \internal Constructs a new request from the given request data \a otherd with the given parent \a parent */ QContactAbstractRequest::QContactAbstractRequest(QContactAbstractRequestPrivate* otherd, QObject* parent) : QObject(parent), d_ptr(otherd) { } /*! Cleans up the memory used by this request */ QContactAbstractRequest::~QContactAbstractRequest() { d_ptr->m_mutex.lock(); QContactManagerEngine *engine = QContactManagerData::engine(d_ptr->m_manager); d_ptr->m_mutex.unlock(); if (engine) engine->requestDestroyed(this); delete d_ptr; d_ptr = 0; } /*! \fn bool QContactAbstractRequest::isInactive() const Returns true if the request is in the \l QContactAbstractRequest::InactiveState state; returns false otherwise. \sa state(), isActive(), isCanceled(), isFinished() */ /*! \fn bool QContactAbstractRequest::isActive() const Returns true if the request is in the \l QContactAbstractRequest::ActiveState state; returns false otherwise. \sa state(), isInactive(), isCanceled(), isFinished() */ /*! \fn bool QContactAbstractRequest::isFinished() const Returns true if the request is in the \l QContactAbstractRequest::FinishedState; returns false otherwise. \sa state(), isActive(), isInactive(), isCanceled() */ /*! \fn bool QContactAbstractRequest::isCanceled() const Returns true if the request is in the \l QContactAbstractRequest::CanceledState; returns false otherwise. \sa state(), isActive(), isInactive(), isFinished() */ /*! Returns the overall error of the most recent asynchronous operation */ QContactManager::Error QContactAbstractRequest::error() const { QMutexLocker ml(&d_ptr->m_mutex); return d_ptr->m_error; } /*! Returns the type of this asynchronous request */ QContactAbstractRequest::RequestType QContactAbstractRequest::type() const { return d_ptr->m_type; } /*! Returns the current state of the request. */ QContactAbstractRequest::State QContactAbstractRequest::state() const { QMutexLocker ml(&d_ptr->m_mutex); return d_ptr->m_state; } /*! Returns a pointer to the manager of which this request instance requests operations */ QContactManager* QContactAbstractRequest::manager() const { QMutexLocker ml(&d_ptr->m_mutex); return d_ptr->m_manager; } /*! Sets the manager of which this request instance requests operations to \a manager If the request is currently active, this function will return without updating the \a manager object. */ void QContactAbstractRequest::setManager(QContactManager* manager) { QMutexLocker ml(&d_ptr->m_mutex); // In theory we might have been active and the manager didn't cancel/finish us if (d_ptr->m_state == QContactAbstractRequest::ActiveState && d_ptr->m_manager) return; d_ptr->m_manager = manager; } /*! Attempts to start the request. Returns false if the request is not in the \c QContactAbstractRequest::Inactive, \c QContactAbstractRequest::Finished or \c QContactAbstractRequest::Cancelled states, or if the request was unable to be performed by the manager engine; otherwise returns true. */ bool QContactAbstractRequest::start() { QMutexLocker ml(&d_ptr->m_mutex); QContactManagerEngine* engine = QContactManagerData::engine(d_ptr->m_manager); if (engine && (d_ptr->m_state == QContactAbstractRequest::CanceledState || d_ptr->m_state == QContactAbstractRequest::FinishedState || d_ptr->m_state == QContactAbstractRequest::InactiveState)) { ml.unlock(); return engine->startRequest(this); } return false; // unable to start operation; another operation already in progress or no engine. } /*! Attempts to cancel the request. Returns false if the request is not in the \c QContactAbstractRequest::Active state, or if the request is unable to be cancelled by the manager engine; otherwise returns true. */ bool QContactAbstractRequest::cancel() { QMutexLocker ml(&d_ptr->m_mutex); QContactManagerEngine* engine = QContactManagerData::engine(d_ptr->m_manager); if (engine && d_ptr->m_state == QContactAbstractRequest::ActiveState) { ml.unlock(); return engine->cancelRequest(this); } return false; // unable to cancel operation; not in progress or no engine. } /*! Blocks until the request has been completed by the manager engine, or until \a msecs milliseconds has elapsed. If \a msecs is zero or negative, this function will block until the request is complete, regardless of how long it takes. Returns true if the request was cancelled or completed successfully within the given period, otherwise false. Some backends are unable to support this operation safely, and will return false immediately. Note that any signals generated while waiting for the request to complete may be queued and delivered some time after this function has returned, when the calling thread's event loop is dispatched. If your code depends on your slots being invoked, you may need to process events after calling this function. */ bool QContactAbstractRequest::waitForFinished(int msecs) { QMutexLocker ml(&d_ptr->m_mutex); QContactManagerEngine* engine = QContactManagerData::engine(d_ptr->m_manager); if (engine) { switch (d_ptr->m_state) { case QContactAbstractRequest::ActiveState: ml.unlock(); return engine->waitForRequestFinished(this, msecs); case QContactAbstractRequest::CanceledState: case QContactAbstractRequest::FinishedState: return true; default: return false; } } return false; // unable to wait for operation; not in progress or no engine. } #ifndef QT_NO_DEBUG_STREAM /*! Outputs \a request to the debug stream \a dbg */ QDebug operator<<(QDebug dbg, const QContactAbstractRequest& request) { dbg.nospace() << "QContactAbstractRequest("; Q_ASSERT(request.d_ptr); if (request.d_ptr->m_type != QContactAbstractRequest::InvalidRequest) { QMutexLocker locker(&request.d_ptr->m_mutex); request.d_ptr->debugStreamOut(dbg); } else { dbg.nospace() << "(null)"; } dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif #include "moc_qcontactabstractrequest.cpp" QT_END_NAMESPACE_CONTACTS src/contacts/qcontactabstractrequest.h000066400000000000000000000113741233466112000206110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTABSTRACTREQUEST_H #define QCONTACTABSTRACTREQUEST_H #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactManagerEngine; class QContactAbstractRequestPrivate; class Q_CONTACTS_EXPORT QContactAbstractRequest : public QObject { Q_OBJECT public: ~QContactAbstractRequest(); Q_ENUMS(State) enum State { InactiveState = 0, // operation not yet started ActiveState, // operation started, not yet finished CanceledState, // operation is finished due to cancellation FinishedState // operation either completed successfully or failed. No further results will become available. }; State state() const; // replaces status() inline bool isInactive() const { return state() == QContactAbstractRequest::InactiveState; } inline bool isActive() const { return state() == QContactAbstractRequest::ActiveState; } inline bool isFinished() const { return state() == QContactAbstractRequest::FinishedState; } inline bool isCanceled() const { return state() == QContactAbstractRequest::CanceledState; } QContactManager::Error error() const; Q_ENUMS(RequestType) enum RequestType { InvalidRequest = 0, ContactFetchRequest, ContactIdFetchRequest, ContactRemoveRequest, ContactSaveRequest, RelationshipFetchRequest, RelationshipRemoveRequest, RelationshipSaveRequest, ContactFetchByIdRequest }; RequestType type() const; /* Which manager we want to perform the asynchronous request */ QContactManager* manager() const; void setManager(QContactManager* manager); enum StorageLocation { UserDataStorage = 0x1, SystemStorage = 0x2 }; Q_DECLARE_FLAGS(StorageLocations, StorageLocation) public Q_SLOTS: /* Verbs */ bool start(); bool cancel(); /* waiting for stuff */ bool waitForFinished(int msecs = 0); Q_SIGNALS: void stateChanged(QContactAbstractRequest::State newState); void resultsAvailable(); protected: QContactAbstractRequest(QContactAbstractRequestPrivate* otherd, QObject* parent = 0); QContactAbstractRequestPrivate* d_ptr; private: QContactAbstractRequest(QObject* parent_ = 0) : QObject(parent_), d_ptr(0) {} Q_DISABLE_COPY(QContactAbstractRequest) friend class QContactManagerEngine; friend class QContactAbstractRequestPrivate; #ifndef QT_NO_DEBUG_STREAM Q_CONTACTS_EXPORT friend QDebug operator<<(QDebug dbg, const QContactAbstractRequest& request); #endif }; Q_DECLARE_OPERATORS_FOR_FLAGS(QContactAbstractRequest::StorageLocations) #ifndef QT_NO_DEBUG_STREAM Q_CONTACTS_EXPORT QDebug operator<<(QDebug dbg, const QContactAbstractRequest& request); #endif QT_END_NAMESPACE_CONTACTS #endif // QCONTACTABSTRACTREQUEST_H src/contacts/qcontactabstractrequest_p.h000066400000000000000000000064571233466112000211360ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTABSTRACTREQUEST_P_H #define QCONTACTABSTRACTREQUEST_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactAbstractRequestPrivate { public: QContactAbstractRequestPrivate(QContactAbstractRequest::RequestType type = QContactAbstractRequest::InvalidRequest) : m_type(type), m_error(QContactManager::NoError), m_state(QContactAbstractRequest::InactiveState), m_manager(0) { } virtual ~QContactAbstractRequestPrivate() { } const QContactAbstractRequest::RequestType m_type; QContactManager::Error m_error; QContactAbstractRequest::State m_state; QPointer m_manager; mutable QMutex m_mutex; #ifndef QT_NO_DEBUG_STREAM // NOTE: on platforms where Qt is built without debug streams enabled, vtable will differ! virtual QDebug& debugStreamOut(QDebug& dbg) const = 0; #endif }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTABSTRACTREQUEST_P_H src/contacts/qcontactaction.cpp000066400000000000000000000261541233466112000172070ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactaction.h" #include #include "qcontactactiondescriptor.h" #include "qcontactactionmanager_p.h" #include "qcontactmanager_p.h" QT_BEGIN_NAMESPACE_CONTACTS QContactAction::~QContactAction() { } /*! \class QContactAction \brief The QContactAction class provides an interface for performing an action on a QContact or QContactDetail. \ingroup contacts-main \ingroup contacts-actions \inmodule QtContacts An action is anything that can be performed on a contact, or a detail of a contact. An example of an action might be "Send Email" or "Dial" or "Plot Navigation Route". One action may be implemented by multiple vendors, and indeed one vendor may provide multiple implementations of the same action. The name of an action identifies its semantics, while its implementation version distinguishes it from other implementations of the action by the same vendor. Invocation of an action is asynchronous; at some stage after calling \l invokeAction() the action instance will emit the \l stateChanged() signal. Any results of the action may be retrieved by calling \l results(), and as results become available the action will emit \l resultsAvailable(). Each instance of a QContactAction is created by a \l QContactActionFactory when \l QContactAction::action() is called; the caller takes ownership of the action instance. Each action is uniquely described by a \l QContactActionDescriptor, which is passed to the \l QContactAction::action() function to instantiate an action. \sa QContactActionFactory, QContactActionFilter */ /*! \fn QContactAction::~QContactAction() Clears any memory in use by this instance of the action implementation */ /*! \fn QContactAction::state() const Returns the current state of the action. \sa stateChanged() */ /*! \fn QContactAction::invokeAction(const QList& targets, const QVariantMap& parameters = QVariantMap()) \overload Initiates the action on the specified list of \a targets with the optional supplied \a parameters. At some point after invocation, one or more \l resultsAvailable() signals will be emitted by the action instance. The results of the action (if any) may be retrieved by calling \l results(). When the state of the action changes, the \l stateChanged() signal will be emitted. Returns true if the action was invoked successfully, otherwise false. The return value says nothing about whether the action which was invoked was successful or not, only whether it was initiated or the request for it to be initiated was sent successfully (e.g., if the action is implemented as a one-way RPC call). \sa results(), stateChanged() */ /*! \fn QContactAction::invokeAction(const QContact& contact, const QContactDetail& detail = QContactDetail(), const QVariantMap& parameters = QVariantMap()) \overload This is a convenience function. Initiates the action on the specified \a detail of the given \a contact, or on the first eligible detail saved in the contact if the given \a detail is empty, with the given \a parameters specified. \sa results(), stateChanged() */ /*! \fn QContactAction::invokeAction(const QContactActionTarget& target, const QVariantMap& parameters = QVariantMap()) \overload This is a convenience function, Initiates the action on the specified \a target with the given \a parameters specified. \sa results(), stateChanged() */ /*! \fn QContactAction::results() const Returns the result of the action, if any exists. Calling this function prior to receiving the \l resultsAvailable() signal will not return a meaningful result. */ /*! \enum QContactAction::State Describes the current status of the asynchronous action operation \value InactiveState The operation has not yet been initiated \value FinishedDetachedState The operation was initiated but no further information is or will be available \value ActiveState The operation was initiated and is not yet finished \value FinishedState The operation successfully completed \value FinishedWithErrorState The operation has finished, but an error occurred */ /*! \fn QContactAction::resultsAvailable() This signal is emitted by an action instance whose functionality has been initiated with \l invokeAction() when results of the action are available. Not all actions will have results, and these actions will not emit the resultsAvailable() signal. If the action implementation is incapable of reporting results of the operation (for example, the action is implemented via a one-way IPC call) it should transition to the \c QContactAction::FinishedDetachedState state immediately upon invocation. */ /*! \fn QContactAction::stateChanged(QContactAction::State newState) This signal is emitted when the state of an action changes to the given \a newState. \sa state() */ /*! Returns a list of identifiers of the available actions which are provided by the service provider with the given \a serviceName. If \a serviceName is empty, actions from all service providers and of any implementation version are returned. */ QStringList QContactAction::availableActions(const QString& serviceName) { // SLOW naive implementation... QSet ret; QList actionDescriptors = QContactActionManager::instance()->actionDescriptors(); for (int i = 0; i < actionDescriptors.size(); i++) { QContactActionDescriptor descriptor = actionDescriptors.at(i); if (serviceName.isEmpty() || serviceName == descriptor.serviceName()) { ret.insert(descriptor.actionName()); } } return ret.toList(); } /*! \fn QContactAction::ActionCall() The name of the default call action. Actions of this name will allow the client to call the specified action target (contact or detail of a contact). \sa actionDescriptors() */ /*! \fn QContactAction::ActionEmail() The name of the default send email action. Actions of this name will either open a graphical element which allows the client to send the specified action target an email, or directly send the specified action target an email if the correct parameters to invocation are specified. \sa actionDescriptors() */ /*! \fn QContactAction::ActionSms() The name of the default send sms action. Actions of this name will allow the client to send the specified action target an sms. \sa actionDescriptors() */ /*! \fn QContactAction::ActionMms() The name of the default send mms action. Actions of this name will allow the client to send the specified action target an mms. \sa actionDescriptors() */ /*! \fn QContactAction::ActionChat() The name of the default IM chat action. Actions of this name will allow the client to begin an IM chat session with the specified action target. \sa actionDescriptors() */ /*! \fn QContactAction::ActionVideoCall() The name of the default video call action. Actions of this name will allow clients to initiate a video call with the specified action target. \sa actionDescriptors() */ /*! \fn QContactAction::ActionOpenInEditor() The name of the default "edit contact" action. Actions of this name will open a graphical element which allows the user to edit the contact. \sa actionDescriptors() */ /*! \fn QContactAction::ActionOpenInViewer() The name of the default view contact action. Actions of this name will open a graphical element which allows the user to view the contact. \sa actionDescriptors() */ /*! Returns a list of QContactActionDescriptor instances which identified implementations of the given \a actionName. The action name may either be one of the default action names, or any other arbitrary string. Example: \code QList availableCallActions = QContactAction::actionDescriptors(QContactAction::ActionCall()); \endcode Example 2: \code QList customActions = QContactAction::actionDescriptors(QStringLiteral("customActionName")); \endcode The actions which are available depend on which action plugins have been installed. For more information on this topic (for example, if you are interested in providing an action plugin for third-party developers to use) please see the relevant documentation for \l{Qt Contacts Action API}{action providers}. */ QList QContactAction::actionDescriptors(const QString& actionName) { QContactActionManager* qcam = QContactActionManager::instance(); return qcam->actionDescriptors(actionName); } /*! Returns a pointer to a new instance of the action implementation identified by the given \a descriptor. The caller takes ownership of the action implementation and must delete it to avoid leaking memory. The caller is able to delete the action at any time, however doing so prior to when the action transitions to a finished state may have an undefined outcome depending on the implementation of the action. */ QContactAction* QContactAction::action(const QContactActionDescriptor& descriptor) { QContactActionManager* qcam = QContactActionManager::instance(); return qcam->action(descriptor); } #include "moc_qcontactaction.cpp" QT_END_NAMESPACE_CONTACTS src/contacts/qcontactaction.h000066400000000000000000000122241233466112000166450ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTACTION_H #define QCONTACTACTION_H #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactActionData; class Q_CONTACTS_EXPORT QContactAction : public QObject { Q_OBJECT public: virtual ~QContactAction() = 0; /* Initiate the asynchronous action on the given list of contacts (and optionally, per-contact-details) with the given parameters */ virtual bool invokeAction(const QList& targets, const QVariantMap& parameters = QVariantMap()) = 0; /* The possible states of an action */ enum State { InactiveState = 0, // operation not yet started ActiveState, // operation started, not yet finished FinishedState, // operation successfully completed FinishedDetachedState, // operation started, no further information available - name under discussion. FinishedWithErrorState // operation finished, but error occurred }; virtual State state() const = 0; /* Returns the most recently received result, or an empty QVariantMap if no results received */ virtual QVariantMap results() const = 0; /* Convenience functions */ bool invokeAction(const QContactActionTarget& target, const QVariantMap& parameters = QVariantMap()) { return invokeAction(QList() << target, parameters); } bool invokeAction(const QContact& contact, const QContactDetail& detail = QContactDetail(), const QVariantMap& parameters = QVariantMap()) { return invokeAction(QList() << QContactActionTarget(contact, detail), parameters); } // common actions inline static const QString ActionCall() {return QStringLiteral("call");}; inline static const QString ActionEmail() {return QStringLiteral("email");}; inline static const QString ActionSms() {return QStringLiteral("sms");}; inline static const QString ActionMms() {return QStringLiteral("mms");}; inline static const QString ActionChat() {return QStringLiteral("chat");}; inline static const QString ActionVideoCall() {return QStringLiteral("videocall");}; inline static const QString ActionOpenInEditor() {return QStringLiteral("edit");}; inline static const QString ActionOpenInViewer() {return QStringLiteral("view");}; Q_SIGNALS: void stateChanged(QContactAction::State); void resultsAvailable(); public: /* return a list of names of actions which are available */ static QStringList availableActions(const QString& serviceName = QString()); /* return a list of descriptors for action implementations matching the given criteria */ static QList actionDescriptors(const QString& actionName = QString()); /* return a pointer to an implementation of the action identified by the given descriptor */ static QContactAction* action(const QContactActionDescriptor& descriptor); }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTACTION_H src/contacts/qcontactactiondescriptor.cpp000066400000000000000000000265671233466112000213160ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactactiondescriptor.h" #include "qcontactactiondescriptor_p.h" #ifndef QT_NO_DEBUG_STREAM #include #endif #include "qcontact.h" #include "qcontactactionfactory.h" #include "qcontactinvalidfilter.h" /* When these conditions are satisfied, QStringLiteral is implemented by gcc's statement-expression extension. However, in this file it will not work, because "statement-expressions are not allowed outside functions nor in template-argument lists". Fall back to the less-performant QLatin1String in this case. */ #if defined(QStringLiteral) && defined(QT_UNICODE_LITERAL_II) && defined(Q_CC_GNU) && !defined(Q_COMPILER_LAMBDA) # undef QStringLiteral # define QStringLiteral QLatin1String #endif QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactActionDescriptor \brief The QContactActionDescriptor class provides information that uniquely identifies a specific implementation of an action \ingroup contacts-actions \inmodule QtContacts */ /*! * Constructs a new, invalid action descriptor */ QContactActionDescriptor::QContactActionDescriptor() : d(new QContactActionDescriptorPrivate()) { } /*! * Constructs a copy of the \a other action descriptor */ QContactActionDescriptor::QContactActionDescriptor(const QContactActionDescriptor& other) : d(other.d) { } /*! * Assigns this action descriptor to be equal to \a other */ QContactActionDescriptor& QContactActionDescriptor::operator=(const QContactActionDescriptor& other) { d = other.d; return *this; } /*! * Cleans up any memory in use by the action descriptor */ QContactActionDescriptor::~QContactActionDescriptor() { } /*! * Returns the name of the action which is identified by the action descriptor */ QString QContactActionDescriptor::actionName() const { return d.constData()->m_actionName; } /*! * Returns the name of the service of the action implementation which is identified by the action descriptor */ QString QContactActionDescriptor::serviceName() const { return d.constData()->m_serviceName; } /*! * Returns the identifier of the action, within the service. */ QString QContactActionDescriptor::actionIdentifier() const { return d.constData()->m_identifier; } /*! Returns the service-specified version of the action implementation which is identified by the action descriptor */ int QContactActionDescriptor::implementationVersion() const { return d.constData()->m_implementationVersion; } /*! Returns the set of action targets which are supported by this action for the given contact \a contact */ QSet QContactActionDescriptor::supportedTargets(const QContact& contact) const { if (d.constData()->m_factory) { return d.constData()->m_factory->supportedTargets(contact, *this); } return QSet(); } /*! Returns a filter which will match contacts for which this action has at least one supported action target */ QContactFilter QContactActionDescriptor::contactFilter() const { if (d.constData()->m_factory) { return d.constData()->m_factory->contactFilter(*this); } return QContactInvalidFilter(); } /*! \variable QContactActionDescriptor::MetaDataIcon The meta data key which corresponds to the meta data value which contains the icon which should be displayed for this action. \sa metaData() */ const QString QContactActionDescriptor::MetaDataIcon(QStringLiteral("Icon")); /*! \variable QContactActionDescriptor::MetaDataLabel The meta data key which corresponds to the meta data value which contains the display label for this action. \sa metaData() */ const QString QContactActionDescriptor::MetaDataLabel(QStringLiteral("Label")); /*! \variable QContactActionDescriptor::MetaDataSecondLabel The meta data key which corresponds to the meta data value which contains the second or additional display label for this action. \sa metaData() */ const QString QContactActionDescriptor::MetaDataSecondLabel(QStringLiteral("SecondLabel")); /*! \variable QContactActionDescriptor::MetaDataOptionalParameterKeys The meta data key which corresponds to the meta data value which contains the list of keys of parameters which the client may provide at invocation time which may affect the action. An example of an optional parameter might be an "attachment" parameter to a "send email" action. \sa metaData() */ const QString QContactActionDescriptor::MetaDataOptionalParameterKeys(QStringLiteral("OptionalParameterKeys")); /*! \variable QContactActionDescriptor::MetaDataMandatoryParameterKeys The meta data key which corresponds to the meta data value which contains the list of keys of parameters which the client must provide at invocation for the action to succeed. An example of a mandatory parameter might be a "recipient" parameter to a "send email" action. \sa metaData() */ const QString QContactActionDescriptor::MetaDataMandatoryParameterKeys(QStringLiteral("MandatoryParameterKeys")); /*! Returns the meta data for the given meta data key \a key for the the given action targets \a targets with the given invocation parameters \a parameters. */ QVariant QContactActionDescriptor::metaData(const QString& key, const QList& targets, const QVariantMap& parameters) const { if (d.constData()->m_factory) { return d.constData()->m_factory->metaData(key, targets, parameters, *this); } return QVariant(); } /*! Returns the meta data for the given meta data key \a key with the given invocation parameters \a parameters. */ QVariant QContactActionDescriptor::metaData(const QString& key, const QVariantMap& parameters) const { return metaData(key, QList(), parameters); } /*! Returns the meta data for the given meta data key \a key for the \a target, with the given invocation parameters \a parameters. */ QVariant QContactActionDescriptor::metaData(const QString& key, const QContactActionTarget& target, const QVariantMap& parameters) const { return metaData(key, QList() << target, parameters); } /*! Returns the meta data for the given meta data key \a key for a target identified by \a contact and \a detail, with the given invocation parameters \a parameters. */ QVariant QContactActionDescriptor::metaData(const QString& key, const QContact& contact, const QContactDetail& detail, const QVariantMap& parameters) const { return metaData(key, QList() << QContactActionTarget(contact, detail), parameters); } /*! Returns true if the action which this descriptor describes supports at least one action target for the given \a contact */ bool QContactActionDescriptor::supportsContact(const QContact& contact) const { if (d.constData()->m_factory) { return d.constData()->m_factory->supportsContact(contact, *this); } return false; } /*! * Returns false if either the name, service and version of the descriptor are missing from the descriptor, * or if the descriptor is not associated with a valid action factory which can create instances of an action. * An empty descriptor cannot uniquely identify an action. */ bool QContactActionDescriptor::isValid() const { if (d.constData()->m_actionName.isEmpty()) return false; if (d.constData()->m_serviceName.isEmpty()) return false; if (d.constData()->m_identifier.isEmpty()) return false; if (d.constData()->m_implementationVersion <= 0) return false; if (d.constData()->m_factory == 0) return false; return true; } /*! * Returns true if the action identified by this descriptor is the same as the action * identified by the \a other descriptor. Note that two actions with the same * action name, service name and implementation version may in fact be different (for example, * they may have different metaData), so using this function is the only way for clients * to tell whether or not the action descriptors identify different actions. */ bool QContactActionDescriptor::operator==(const QContactActionDescriptor& other) const { return (d.constData()->m_factory == other.d.constData()->m_factory && d.constData()->m_identifier == other.d.constData()->m_identifier); } /*! * Returns true if the action name, service name or service-specified implementation version * specified by this action descriptor are different to that specified by \a other */ bool QContactActionDescriptor::operator!=(const QContactActionDescriptor& other) const { return !(*this == other); } /*! Returns the hash value for \a key. */ uint qHash(const QContactActionDescriptor& key) { uint ret = 0; ret += QT_PREPEND_NAMESPACE(qHash)(key.serviceName()) + QT_PREPEND_NAMESPACE(qHash)(key.actionName()) + QT_PREPEND_NAMESPACE(qHash)(key.d.constData()->m_identifier) + QT_PREPEND_NAMESPACE(qHash)(key.implementationVersion()) + QT_PREPEND_NAMESPACE(qHash)(key.d.constData()->m_factory); return ret; } #ifndef QT_NO_DEBUG_STREAM QDebug& operator<<(QDebug dbg, const QContactActionDescriptor& descriptor) { dbg.nospace() << "QContactActionDescriptor(" << descriptor.serviceName() << "," << descriptor.actionName() << "," << descriptor.d.constData()->m_identifier << "," << descriptor.implementationVersion() << "," << descriptor.d.constData()->m_factory << ')'; return dbg.maybeSpace(); } #endif QT_END_NAMESPACE_CONTACTS src/contacts/qcontactactiondescriptor.h000066400000000000000000000114171233466112000207470ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTACTIONDESCRIPTOR_H #define QCONTACTACTIONDESCRIPTOR_H #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContact; class QContactActionFactory; class QContactActionDescriptorPrivate; class Q_CONTACTS_EXPORT QContactActionDescriptor { public: QContactActionDescriptor(); QContactActionDescriptor(const QContactActionDescriptor& other); QContactActionDescriptor& operator=(const QContactActionDescriptor& other); ~QContactActionDescriptor(); bool isValid() const; bool operator==(const QContactActionDescriptor& other) const; bool operator!=(const QContactActionDescriptor& other) const; QString actionName() const; QString serviceName() const; QString actionIdentifier() const; int implementationVersion() const; /* The descriptor provides the client with all information required in UI. */ QSet supportedTargets(const QContact& contact) const; QContactFilter contactFilter() const; bool supportsContact(const QContact& contact) const; QVariant metaData(const QString& key, const QList& targets, const QVariantMap& parameters = QVariantMap()) const; /* Convenience meta data accessors */ QVariant metaData(const QString& key, const QVariantMap& parameters = QVariantMap()) const; QVariant metaData(const QString& key, const QContactActionTarget& target, const QVariantMap& parameters = QVariantMap()) const; QVariant metaData(const QString& key, const QContact& contact, const QContactDetail& detail = QContactDetail(), const QVariantMap& parameters = QVariantMap()) const; // default meta-data keys static const QString MetaDataIcon; static const QString MetaDataLabel; static const QString MetaDataSecondLabel; static const QString MetaDataOptionalParameterKeys; static const QString MetaDataMandatoryParameterKeys; private: QSharedDataPointer d; friend class QContactActionFactory; friend class QContactActionServiceManager; #ifndef QT_NO_DEBUG_STREAM Q_CONTACTS_EXPORT friend QDebug& operator<<(QDebug dbg, const QContactActionDescriptor& descriptor); #endif Q_CONTACTS_EXPORT friend uint qHash(const QContactActionDescriptor& key); }; Q_CONTACTS_EXPORT uint qHash(const QContactActionDescriptor& key); #ifndef QT_NO_DEBUG_STREAM Q_CONTACTS_EXPORT QDebug& operator<<(QDebug dbg, const QContactActionDescriptor& descriptor); #endif QT_END_NAMESPACE_CONTACTS QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(QTCONTACTS_PREPEND_NAMESPACE(QContactActionDescriptor), Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif // QCONTACTACTIONDESCRIPTOR_H src/contacts/qcontactactiondescriptor_p.h000066400000000000000000000063171233466112000212710ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTACTIONDESCRIPTOR_P_H #define QCONTACTACTIONDESCRIPTOR_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactActionFactory; class QContactActionDescriptorPrivate : public QSharedData { public: QContactActionDescriptorPrivate() : QSharedData(), m_implementationVersion(-1), m_factory(0) { } QContactActionDescriptorPrivate(const QContactActionDescriptorPrivate& other) : QSharedData(), m_actionName(other.m_actionName), m_serviceName(other.m_serviceName), m_identifier(other.m_identifier), m_implementationVersion(other.m_implementationVersion), m_factory(other.m_factory) { } ~QContactActionDescriptorPrivate() { } QString m_actionName; QString m_serviceName; QString m_identifier; int m_implementationVersion; const QContactActionFactory* m_factory; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTACTIONDESCRIPTOR_P_H src/contacts/qcontactactionfactory.cpp000066400000000000000000000123601233466112000205710ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactactionfactory.h" #include "qcontact.h" #include "qcontactactiondescriptor_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactActionFactory \brief The QContactActionFactory class provides an interface for clients to retrieve instances of action implementations \inmodule QtContacts \ingroup contacts-actions */ /*! \fn QContactActionFactory::QContactActionFactory(QObject* parent) Default constructor for this interface. Passes \a parent to the QObject constructor. */ /*! \fn QContactActionFactory::~QContactActionFactory() Clears any memory in use by this factory */ QContactActionFactory::~QContactActionFactory() { } /*! \fn QContactActionFactory::actionDescriptors() const Return a list of action descriptors for the actions that this factory supports. */ /*! \fn QContactActionFactory::contactFilter(const QContactActionDescriptor& which) const Returns a filter to select contacts that are supported by the action specified by \a which. */ /*! \fn QContactActionFactory::supportedTargets(const QContact& contact, const QContactActionDescriptor& which) const Returns the targets which are supported by the action described by \a which that may be instantiated by this factory for the given \a contact. If there are no supported targets for the \a contact, then that contact is not supported by the action. \sa supportsContact() */ /*! \fn QContactActionFactory::supportsContact(const QContact& contact, const QContactActionDescriptor& which) const Returns true if there are any targets for the given \a contact supported by the action described by \a which. */ /*! \fn QContactActionFactory::create(const QContactActionDescriptor& which) const Returns a new \l QContactAction instance for the supplied action descriptor \a which. The caller will own this object. */ /*! \fn QContactActionFactory::metaData(const QString& key, const QList& targets, const QVariantMap& parameters = QVariantMap(), const QContactActionDescriptor& which) const Returns the meta-data associated with the action described by \a which for the given \a key (such as icon, label or sound cues). The meta-data may vary depending on the \a targets of the action and any \a parameters to invocation which the client may specify. */ /*! \fn QContactActionFactory::InterfaceName() The name of the interface that action plugins should implement. */ bool QContactActionFactory::supportsContact(const QContact& contact, const QContactActionDescriptor& which) const { // default implementation is naive. return !supportedTargets(contact, which).isEmpty(); } /*! Creates an action descriptor based on the supplied action name \a actionName, service name \a serviceName, action identifier \a actionIdentifier, and version \a implementationVersion. */ QContactActionDescriptor QContactActionFactory::createDescriptor(const QString& actionName, const QString& serviceName, const QString& actionIdentifier, int implementationVersion) const { QContactActionDescriptor retn; retn.d->m_actionName = actionName; retn.d->m_serviceName = serviceName; retn.d->m_identifier = actionIdentifier; retn.d->m_implementationVersion = implementationVersion; retn.d->m_factory = this; return retn; } #include "moc_qcontactactionfactory.cpp" QT_END_NAMESPACE_CONTACTS src/contacts/qcontactactionfactory.h000066400000000000000000000071611233466112000202410ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTACTIONFACTORY_H #define QCONTACTACTIONFACTORY_H #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContact; class QContactAction; class QContactFilter; class Q_CONTACTS_EXPORT QContactActionFactory : public QObject { Q_OBJECT public: QContactActionFactory(QObject *parent_ = 0) : QObject(parent_) {} virtual ~QContactActionFactory(); virtual QList actionDescriptors() const = 0; virtual QContactAction* create(const QContactActionDescriptor& which) const = 0; // Backend functionality for QCAD virtual QSet supportedTargets(const QContact& contact, const QContactActionDescriptor& which) const = 0; virtual QContactFilter contactFilter(const QContactActionDescriptor& which) const = 0; virtual QVariant metaData(const QString& key, const QList& targets, const QVariantMap& parameters, const QContactActionDescriptor& which) const = 0; virtual bool supportsContact(const QContact& contact, const QContactActionDescriptor& which) const; // virtual but not pure virtual; default impl. calls supportedTargets.isEmpty(). inline static const QString InterfaceName() {return QStringLiteral("org.qt-project.Qt.QContactActionFactory");}; protected: QContactActionDescriptor createDescriptor(const QString& actionName, const QString& serviceName, const QString& actionIdentifier, int implementationVersion) const; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTACTIONFACTORY_H src/contacts/qcontactactionmanager_p.cpp000066400000000000000000000107661233466112000210630ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactactionmanager_p.h" #include "qcontactaction.h" #include "qcontactactionfactory.h" #include "qcontactmanager_p.h" QT_BEGIN_NAMESPACE_CONTACTS Q_GLOBAL_STATIC(QContactActionManager, contactActionManagerInstance) /*! \internal \class QContactActionManager This class uses a plugin to delegate discovery of actions (to avoid a dependency on SFW for QtContacts) It is an implementation detail of QContactAction. */ QContactActionManager* QContactActionManager::instance() { return contactActionManagerInstance(); } QContactActionManager::QContactActionManager() : QObject(), m_plugin(0) { } void QContactActionManager::init() { // We ask the qcontactmanager engine loading code, since it has to enumerate things anyway QContactManagerData::loadFactoriesMetadata(); m_plugin = QContactManagerData::m_actionManagers.value(0); } QContactActionManager::~QContactActionManager() { // Allow our subordinates to do their thing when they want, // since we just cache stuff } QList QContactActionManager::availableActions(const QContact &contact) { QMutexLocker locker(&m_instanceMutex); init(); QList ret; if (m_plugin) { QHash hash = m_plugin->actionFactoryHash(); QHash::const_iterator it; for (it = hash.constBegin(); it != hash.constEnd(); ++it) { QContactActionFactory* curr = it.value(); if (curr && curr->supportsContact(contact, it.key())) { ret.append(it.key()); } } } return ret; } QList QContactActionManager::actionDescriptors(const QString& actionName) { QMutexLocker locker(&m_instanceMutex); init(); if (m_plugin) { if (actionName.isEmpty()) return m_plugin->descriptorHash().values(); return m_plugin->descriptorHash().values(actionName); } return QList(); } /*! The caller takes ownership of the returned action pointer, and must delete it to avoid leaking memory. */ QContactAction* QContactActionManager::action(const QContactActionDescriptor& descriptor) { QMutexLocker locker(&m_instanceMutex); init(); if (m_plugin) { QContactActionFactory *factory = m_plugin->actionFactoryHash().value(descriptor); if (factory) return factory->create(descriptor); } return 0; } #include "moc_qcontactactionmanager_p.cpp" QT_END_NAMESPACE_CONTACTS src/contacts/qcontactactionmanager_p.h000066400000000000000000000072111233466112000205170ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTACTIONMANAGER_P_H #define QCONTACTACTIONMANAGER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactAction; class QContactActionFactory; // For now this is a very direct interface, not really designed for extensibility class QContactActionManagerPlugin { public: virtual QHash actionFactoryHash() = 0; // descriptor to action factory ptr. virtual QMultiHash descriptorHash() = 0; // action name to descriptor }; class QContactActionManager : public QObject { Q_OBJECT public: static QContactActionManager* instance(); // this is a private class, so despite being a singleton we make this ctor public. QContactActionManager(); ~QContactActionManager(); QList availableActions(const QContact& contact); QList actionDescriptors(const QString& actionName = QString()); QContactAction* action(const QContactActionDescriptor& descriptor); private: void init(); QMutex m_instanceMutex; QContactActionManagerPlugin* m_plugin; }; QT_END_NAMESPACE_CONTACTS QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(QtContacts::QContactActionManagerPlugin, "org.qt-project.Qt.QContactActionManagerPlugin") QT_END_NAMESPACE #endif // QCONTACTACTIONMANAGER_P_H src/contacts/qcontactactiontarget.cpp000066400000000000000000000170321233466112000204110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactactiontarget.h" #include "qcontactactiontarget_p.h" #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactActionTarget \brief The QContactActionTarget class provides information about the target of an action. It may be either a contact, a contact and a detail of that contact, or a contact and a list of the details of the contact, which together should be used by the action. \ingroup contacts-actions \inmodule QtContacts */ /*! \enum QContactActionTarget::Type This enumerator defines the type of a QContactActionTarget. \value Invalid The type is invalid. \value WholeContact The type is a complete contact. \value SingleDetail The type is only a single detail. \value MultipleDetails The type contains multiple details. */ /*! * Constructs a new action target from the given \a contact and the given list of that contact's \a details. * If no \a contact is specified, the target will be invalid. If a \a contact but no \a details are specified, * the target will be valid, but the action which operates on the target may fail (for example, if it requires * a certain detail to be specified in order to perform the action). */ QContactActionTarget::QContactActionTarget(const QContact& contact, const QList& details) : d(new QContactActionTargetPrivate(contact, details)) { } /*! * Constructs a new action target from the given \a contact and a specific \a detail of that contact */ QContactActionTarget::QContactActionTarget(const QContact& contact, const QContactDetail& detail) : d(new QContactActionTargetPrivate(contact, QList() << detail)) { } /*! * Constructs a copy of the \a other action target */ QContactActionTarget::QContactActionTarget(const QContactActionTarget& other) : d(other.d) { } /*! * Assigns this action target to be equal to \a other */ QContactActionTarget& QContactActionTarget::operator=(const QContactActionTarget& other) { d = other.d; return *this; } /*! * Cleans up any memory in use by the action target */ QContactActionTarget::~QContactActionTarget() { } /*! * Returns the contact that this action target will operate on. * \sa details() */ QContact QContactActionTarget::contact() const { return d.constData()->m_contact; } /*! * Returns the details that this action target will operate on. * \sa contact() */ QList QContactActionTarget::details() const { return d.constData()->m_details; } /*! * Sets the contact that this action target will operate on to \a contact. * \sa setDetails() */ void QContactActionTarget::setContact(const QContact& contact) { d->m_contact = contact; } /*! * Sets the details that this action target will operate on to \a details. * \sa setContact() */ void QContactActionTarget::setDetails(const QList& details) { d->m_details = details; } /*! * Returns true if the target contact is not the default constructed contact. * The validity of any specified details is not checked by this function. */ bool QContactActionTarget::isValid() const { return (d.constData()->m_contact != QContact()); } /*! * Returns true if the contacts and details specified by this action target are equal to those specified by \a other */ bool QContactActionTarget::operator==(const QContactActionTarget& other) const { return d.constData()->m_contact == other.d.constData()->m_contact && d.constData()->m_details == other.d.constData()->m_details; } /*! * Returns true if the contacts or details specified by this action target are different to that specified by \a other */ bool QContactActionTarget::operator!=(const QContactActionTarget& other) const { return !(*this == other); } /*! Returns the type of this action target. The type is determined by the properties that have been set on this target. */ QContactActionTarget::Type QContactActionTarget::type() const { if (d.constData()->m_contact.isEmpty()) return QContactActionTarget::Invalid; switch (d.constData()->m_details.count()) { case 0: return QContactActionTarget::WholeContact; case 1: return QContactActionTarget::SingleDetail; default: return QContactActionTarget::MultipleDetails; } } /*! Returns the hash value for \a key. */ uint qHash(const QContactActionTarget& key) { uint ret = qHash(key.contact()); foreach (const QContactDetail& det, key.details()) { ret += qHash(det); } return ret; } #ifndef QT_NO_DATASTREAM /*! Streams the given \a target to the datastream \a out */ QDataStream& operator<<(QDataStream& out, const QContactActionTarget& target) { quint8 formatVersion = 1; // Version of QDataStream format for QContactActionTarget out << formatVersion; out << target.d.constData()->m_contact; out << target.d.constData()->m_details; return out; } /*! Streams \a target in from the datastream \a in */ QDataStream& operator>>(QDataStream& in, QContactActionTarget& target) { QContactActionTarget retn; quint8 formatVersion; in >> formatVersion; if (formatVersion == 1) { in >> retn.d->m_contact; in >> retn.d->m_details; } else { in.setStatus(QDataStream::ReadCorruptData); } target = retn; return in; } #endif #ifndef QT_NO_DEBUG_STREAM QDebug& operator<<(QDebug dbg, const QContactActionTarget& target) { dbg.nospace() << "QContactActionTarget(" << target.contact() << target.details() << ')'; return dbg.maybeSpace(); } #endif QT_END_NAMESPACE_CONTACTS src/contacts/qcontactactiontarget.h000066400000000000000000000100141233466112000200470ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTACTIONTARGET_H #define QCONTACTACTIONTARGET_H #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContact; class QContactActionTargetPrivate; class Q_CONTACTS_EXPORT QContactActionTarget { public: explicit QContactActionTarget(const QContact& contact = QContact(), const QList& details = QList()); QContactActionTarget(const QContact& contact, const QContactDetail& detail); QContactActionTarget(const QContactActionTarget& other); QContactActionTarget& operator=(const QContactActionTarget& other); ~QContactActionTarget(); enum Type { Invalid, // no contact WholeContact, // no details SingleDetail, // a single detail MultipleDetails, }; Type type() const; // No setter bool isValid() const; bool operator==(const QContactActionTarget& other) const; bool operator!=(const QContactActionTarget& other) const; void setContact(const QContact& contact); void setDetails(const QList& details); QContact contact() const; QList details() const; private: #ifndef QT_NO_DATASTREAM Q_CONTACTS_EXPORT friend QDataStream& operator<<(QDataStream& out, const QContactActionTarget& target); Q_CONTACTS_EXPORT friend QDataStream& operator>>(QDataStream& in, QContactActionTarget& target); #endif QSharedDataPointer d; }; Q_CONTACTS_EXPORT uint qHash(const QContactActionTarget& key); #ifndef QT_NO_DATASTREAM Q_CONTACTS_EXPORT QDataStream& operator<<(QDataStream& out, const QContactActionTarget& target); Q_CONTACTS_EXPORT QDataStream& operator>>(QDataStream& in, QContactActionTarget& target); #endif #ifndef QT_NO_DEBUG_STREAM Q_CONTACTS_EXPORT QDebug& operator<<(QDebug dbg, const QContactActionTarget& target); #endif QT_END_NAMESPACE_CONTACTS QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(QTCONTACTS_PREPEND_NAMESPACE(QContactActionTarget), Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif // QCONTACTACTIONTARGET_H src/contacts/qcontactactiontarget_p.h000066400000000000000000000057761233466112000204110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTACTIONTARGET_P_H #define QCONTACTACTIONTARGET_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactActionTargetPrivate : public QSharedData { public: QContactActionTargetPrivate(const QContact& contact, const QList& details) : QSharedData(), m_contact(contact), m_details(details) { } ~QContactActionTargetPrivate() { } QContactActionTargetPrivate(const QContactActionTargetPrivate& other) : QSharedData(), m_contact(other.m_contact), m_details(other.m_details) { } QContact m_contact; QList m_details; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTACTIONTARGET_P_H src/contacts/qcontactchangeset.cpp000066400000000000000000000266401233466112000176730ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactchangeset.h" #include "qcontactchangeset_p.h" #include "qcontactmanagerengine.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactChangeSet \inmodule QtContacts \brief The QContactChangeSet class provides a simple API to simplify the emission of state-change signals from QContactManagerEngine implementations. This class can be utilised by backend implementations to ensure correct emission of the \l QContactManagerEngine::dataChanged(), \l QContactManagerEngine::contactsAdded(), \l QContactManagerEngine::contactsChanged() and \l QContactManagerEngine::contactsRemoved(). \sa QContactManagerEngine */ /*! Constructs a new change set */ QContactChangeSet::QContactChangeSet() : d(new QContactChangeSetData) { } /*! Constructs a copy of the \a other change set */ QContactChangeSet::QContactChangeSet(const QContactChangeSet& other) : d(other.d) { } /*! Frees the memory used by this change set */ QContactChangeSet::~QContactChangeSet() { } /*! Assigns this change set to be equal to \a other */ QContactChangeSet& QContactChangeSet::operator=(const QContactChangeSet& other) { d = other.d; return *this; } /*! Sets the data changed flag to \a dataChanged. If this is set to true prior to calling \l emitSignals(), only the \l QContactManagerEngine::dataChanged() signal will be emitted; otherwise, the appropriate finer-grained signals will be emitted. */ void QContactChangeSet::setDataChanged(bool dataChanged) { d->m_dataChanged = dataChanged; } /*! Returns the value of the data changed flag */ bool QContactChangeSet::dataChanged() { return d.constData()->m_dataChanged; } /*! Returns the set of ids of contacts which have been added to the database. */ QSet QContactChangeSet::addedContacts() const { return d.constData()->m_addedContacts; } /*! Inserts the given contact id \a addedContactId into the set of ids of contacts which have been added to the database. */ void QContactChangeSet::insertAddedContact(QContactId addedContactId) { d->m_addedContacts.insert(addedContactId); } /*! Inserts each of the given contact ids \a addedContactIds into the set of ids of contacts which have been added to the database. */ void QContactChangeSet::insertAddedContacts(const QList& addedContactIds) { foreach (const QContactId& id, addedContactIds) d->m_addedContacts.insert(id); } /*! Clears the set of ids of contacts which have been added to the database */ void QContactChangeSet::clearAddedContacts() { d->m_addedContacts.clear(); } /*! Returns the set of ids of contacts which have been changed in the database. */ QSet QContactChangeSet::changedContacts() const { return d.constData()->m_changedContacts; } /*! Inserts the given contact id \a changedContactId into the set of ids of contacts which have been changed to the database. */ void QContactChangeSet::insertChangedContact(QContactId changedContactId) { d->m_changedContacts.insert(changedContactId); } /*! Inserts each of the given contact ids \a changedContactIds into the set of ids of contacts which have been changed to the database. */ void QContactChangeSet::insertChangedContacts(const QList& changedContactIds) { foreach (const QContactId& id, changedContactIds) d->m_changedContacts.insert(id); } /*! Clears the set of ids of contacts which have been changed to the database */ void QContactChangeSet::clearChangedContacts() { d->m_changedContacts.clear(); } /*! Returns the set of ids of contacts which have been removed from the database. */ QSet QContactChangeSet::removedContacts() const { return d.constData()->m_removedContacts; } /*! Inserts the given contact id \a removedContactId into the set of ids of contacts which have been removed to the database. */ void QContactChangeSet::insertRemovedContact(QContactId removedContactId) { d->m_removedContacts.insert(removedContactId); } /*! Inserts each of the given contact ids \a removedContactIds into the set of ids of contacts which have been removed to the database. */ void QContactChangeSet::insertRemovedContacts(const QList& removedContactIds) { foreach (const QContactId& id, removedContactIds) d->m_removedContacts.insert(id); } /*! Clears the set of ids of contacts which have been removed to the database */ void QContactChangeSet::clearRemovedContacts() { d->m_removedContacts.clear(); } /*! Returns the set of ids of contacts which have been affected by the addition of relationships to the database. */ QSet QContactChangeSet::addedRelationshipsContacts() const { return d.constData()->m_addedRelationships; } /*! Inserts the given contact id \a affectedContactId into the set of ids of contacts which have been affected by the addition of a relationship to the database. */ void QContactChangeSet::insertAddedRelationshipsContact(QContactId affectedContactId) { d->m_addedRelationships.insert(affectedContactId); } /*! Inserts each of the given contact ids \a affectedContactIds into the set of ids of contacts which have been affected by the addition of a relationship to the database. */ void QContactChangeSet::insertAddedRelationshipsContacts(const QList& affectedContactIds) { foreach (const QContactId& id, affectedContactIds) d->m_addedRelationships.insert(id); } /*! Clears the set of ids of contacts which have been affected by the addition of a relationship to the database. */ void QContactChangeSet::clearAddedRelationshipsContacts() { d->m_addedRelationships.clear(); } /*! Returns the set of ids of contacts which have been affected by the removal of relationships from the database. */ QSet QContactChangeSet::removedRelationshipsContacts() const { return d.constData()->m_removedRelationships; } /*! Inserts the given contact id \a affectedContactId into the set of ids of contacts which have been affected by the removal of a relationship to the database. */ void QContactChangeSet::insertRemovedRelationshipsContact(QContactId affectedContactId) { d->m_removedRelationships.insert(affectedContactId); } /*! Inserts each of the given contact ids \a affectedContactIds into the set of ids of contacts which have been affected by the removal of a relationship to the database. */ void QContactChangeSet::insertRemovedRelationshipsContacts(const QList& affectedContactIds) { foreach (const QContactId& id, affectedContactIds) d->m_removedRelationships.insert(id); } /*! Clears the set of ids of contacts which have been affected by the removal of a relationship to the database. */ void QContactChangeSet::clearRemovedRelationshipsContacts() { d->m_removedRelationships.clear(); } /*! Sets the pair of ids which represent the old and new self contact ids to the given pair of ids \a oldAndNewContactId. The first id in the pair is the old self contact id, while the second id in the pair is the new self contact id. If the new id is different to the old id at the point in time when emitSignals() is called, the QContactManagerEngine::selfContactIdChanged signal will be emitted. */ void QContactChangeSet::setOldAndNewSelfContactId(const QPair &oldAndNewContactId) { d->m_oldAndNewSelfContactId = oldAndNewContactId; } /*! Returns the pair of ids which represents the old and new self contact ids. The first id in the pair is the old self contact id, while the second id in the pair is the new self contact id. If the new id is different to the old id at the point in time when emitSignals() is called, the QContactManagerEngine::selfContactIdChanged() signal will be emitted. */ QPair QContactChangeSet::oldAndNewSelfContactId() const { return d.constData()->m_oldAndNewSelfContactId; } /*! Clears all flags and sets of ids in this change set */ void QContactChangeSet::clearAll() { d->m_dataChanged = false; d->m_addedContacts.clear(); d->m_changedContacts.clear(); d->m_removedContacts.clear(); d->m_addedRelationships.clear(); d->m_removedRelationships.clear(); d->m_oldAndNewSelfContactId = QPair(); } /*! Emits the appropriate signals from the given \a engine given the state of the change set */ void QContactChangeSet::emitSignals(QContactManagerEngine *engine) { if (!engine) return; if (d.constData()->m_dataChanged) { emit engine->dataChanged(); } else { if (!d.constData()->m_addedContacts.isEmpty()) emit engine->contactsAdded(d.constData()->m_addedContacts.toList()); if (!d.constData()->m_changedContacts.isEmpty()) emit engine->contactsChanged(d.constData()->m_changedContacts.toList()); if (!d.constData()->m_removedContacts.isEmpty()) emit engine->contactsRemoved(d.constData()->m_removedContacts.toList()); if (!d.constData()->m_addedRelationships.isEmpty()) emit engine->relationshipsAdded(d.constData()->m_addedRelationships.toList()); if (!d.constData()->m_removedRelationships.isEmpty()) emit engine->relationshipsRemoved(d.constData()->m_removedRelationships.toList()); if (d.constData()->m_oldAndNewSelfContactId.first != d.constData()->m_oldAndNewSelfContactId.second) emit engine->selfContactIdChanged(d.constData()->m_oldAndNewSelfContactId.first, d.constData()->m_oldAndNewSelfContactId.second); } } QT_END_NAMESPACE_CONTACTS src/contacts/qcontactchangeset.h000066400000000000000000000077511233466112000173420ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTCHANGESET_H #define QCONTACTCHANGESET_H #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactManagerEngine; class QContactChangeSetData; class Q_CONTACTS_EXPORT QContactChangeSet { public: QContactChangeSet(); QContactChangeSet(const QContactChangeSet& other); ~QContactChangeSet(); QContactChangeSet& operator=(const QContactChangeSet& other); void setDataChanged(bool dataChanged); bool dataChanged(); QSet addedContacts() const; void insertAddedContact(QContactId addedContactId); void insertAddedContacts(const QList& addedContactIds); void clearAddedContacts(); QSet changedContacts() const; void insertChangedContact(QContactId addedContactId); void insertChangedContacts(const QList& addedContactIds); void clearChangedContacts(); QSet removedContacts() const; void insertRemovedContact(QContactId addedContactId); void insertRemovedContacts(const QList& addedContactIds); void clearRemovedContacts(); QSet addedRelationshipsContacts() const; void insertAddedRelationshipsContact(QContactId affectedContactId); void insertAddedRelationshipsContacts(const QList& affectedContactIds); void clearAddedRelationshipsContacts(); QSet removedRelationshipsContacts() const; void insertRemovedRelationshipsContact(QContactId affectedContactId); void insertRemovedRelationshipsContacts(const QList& affectedContactIds); void clearRemovedRelationshipsContacts(); void setOldAndNewSelfContactId(const QPair& oldAndNewContactId); QPair oldAndNewSelfContactId() const; void clearAll(); void emitSignals(QContactManagerEngine *engine); private: QSharedDataPointer d; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTCHANGESET_H src/contacts/qcontactchangeset_p.h000066400000000000000000000066101233466112000176520ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTCHANGESET_P_H #define QCONTACTCHANGESET_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactChangeSetData : public QSharedData { public: QContactChangeSetData() : QSharedData(), m_dataChanged(false) { } QContactChangeSetData(const QContactChangeSetData& other) : QSharedData(other), m_dataChanged(other.m_dataChanged), m_addedContacts(other.m_addedContacts), m_changedContacts(other.m_changedContacts), m_removedContacts(other.m_removedContacts), m_addedRelationships(other.m_addedRelationships), m_removedRelationships(other.m_removedRelationships), m_oldAndNewSelfContactId(other.m_oldAndNewSelfContactId) { } ~QContactChangeSetData() { } bool m_dataChanged; QSet m_addedContacts; QSet m_changedContacts; QSet m_removedContacts; QSet m_addedRelationships; QSet m_removedRelationships; QPair m_oldAndNewSelfContactId; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTCHANGESET_P_H src/contacts/qcontactdetail.cpp000066400000000000000000000541131233466112000171700ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactdetail.h" #include "qcontactdetail_p.h" #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif #include "qcontactmanager.h" /* When these conditions are satisfied, QStringLiteral is implemented by gcc's statement-expression extension. However, in this file it will not work, because "statement-expressions are not allowed outside functions nor in template-argument lists". Fall back to the less-performant QLatin1String in this case. */ #if defined(QStringLiteral) && defined(QT_UNICODE_LITERAL_II) && defined(Q_CC_GNU) && !defined(Q_COMPILER_LAMBDA) # undef QStringLiteral # define QStringLiteral QLatin1String #endif QT_BEGIN_NAMESPACE_CONTACTS /* Initialise our static private data member */ QAtomicInt QContactDetailPrivate::lastDetailKey(1); /*! \class QContactDetail \brief The QContactDetail class represents a single, complete detail about a contact. \inmodule QtContacts \ingroup contacts-main All of the information for a contact is stored in one or more QContactDetail objects. A detail is a group of logically related bits of data - for example, a street address is a single detail that has multiple fields (number, region, country etc). Different contact managers might have different fields for the same detail type, depending on their capabilities. For example, for the QContactName detail one manager might not support the middle name field, while a different manager may add an extra field for specific extra information not present in the default schema. One field which is common to all details is the context field. This field is intended to store one or more contexts that this detail is associated with. Commonly this will be something like "Home" and/or "Work", although no limitations are placed on which values may be stored in this field in the default schema. There are two other, related fields which are common to all details. The first is \a QContactDetail::FieldDetailUri, which stores the unique URI of the detail if one exists. The field is not mandatory, and backends are not required to verify that the given URI is indeed unique within the contact. The second field is \a QContactDetail::LinkedDetailUris, which stores a list of detail URIs to which this detail is linked. The link is one-way, and can be used to show how or where a detail was derived. This is useful for things like presence information and avatars, which are linked to a particular online account detail of the contact. When a QContactDetail has been retrieved in a QContact from a QContactManager, it may have certain access constraints provided with it, like \l ReadOnly or \l Irremovable. This might mean that the supplied detail is calculated or otherwise not modifiable by the user - presence information is a good example. Also, some details may be marked \l Irremovable. These are typically things that a contact has to have - like a QContactType. It is possible to inherit from QContactDetail to provide convenience or standardized access to values. For example, \l QContactPhoneNumber provides a convenient API for manipulating a QContactDetail as a phone number, according to the schema. In general, QContactDetail and the built in subclasses (like \l QContactPhoneNumber) provide constants for the names of fields (like \l QContactPhoneNumber::FieldNumber), and for predefined common values like \l QContactDetail::ContextHome. Typically the constants for field names start with \c Field, and the constants for predefined values of a field start with the name of that field (e.g. \c ContextHome is a predefined constant for \c FieldContext). If you wish to create your own, customized contact detail, you should use the \l QContactExtendedDetail detail. QContactDetail objects act like type checked values. In general, you can assign them to and from and have reasonable behaviour, like the following example. \code QContactPhoneNumber number; number.setNumber("555-1212"); // number.value(QContactPhoneNumber::FieldNumber) == "555-1212"; // number.type() == QContactPhoneNumber::Type QContactDetail detail = number; // detail.value(QContactPhoneNumber::FieldNumber) == "555-1212"; // detail.type() == QContactPhoneNumber::Type QContactPhoneNumber otherNumber = detail; // otherNumber.number() == "555-1212"; // otherNumber.type() == QContactPhoneNumber::Type QContactAddress address = detail; // address is now a default constructed QContactAddress // address.value(QContactPhoneNumber::FieldNumber) is empty // address.type() == QContactAddress::Type QContactAddress otherAddress = number; // otherAddress is now a default constructed QContactAddress // otherAddress.value(QContactPhoneNumber::FieldNumber) is empty // otherAddress.type() == QContactAddress::Type \endcode \sa QContact, QContactDetail::DetailType, QContactDetailFilter, QContactDetailRangeFilter, Q_DECLARE_CUSTOM_CONTACT_DETAIL */ /*! \macro Q_DECLARE_CUSTOM_CONTACT_DETAIL \relates QContactDetail Macro for simplifying declaring custom (leaf) detail classes. The first argument is the detail type of the class. If you are creating a convenience class for a type of QContactDetail, you should use this macro when declaring your class to ensure that it interoperates with other contact functionality. Here is an example of a class (\l QContactPhoneNumber) using this macro. Note that the class provides some predefined constants and some convenience methods that return values associated with schema fields. \snippet qcontactphonenumber.h 0 */ /*! \enum QContactDetail::DetailType This enumeration defines the detail types provided by the contacts API. \value TypeUndefined Convenience value used to represent unknown / undefined detail types. \value TypeAddress Detail type of a QContactAddress detail. \value TypeAnniversary Detail type of a QContactAnniversary detail. \value TypeAvatar Detail type of a QContactAvatar detail. \value TypeBirthday Detail type of a QContactBirthday detail. \value TypeDisplayLabel Detail type of a QContactDisplayLabel detail. \value TypeEmailAddress Detail type of a QContactEmailAddress detail. \value TypeExtendedDetail Detail type of a QContactExtendedDetail detail. \value TypeFamily Detail type of a QContactFamily detail. \value TypeFavorite Detail type of a QContactFavorite detail. \value TypeGender Detail type of a QContactGender detail. \value TypeGeoLocation Detail type of a QContactGeoLocation detail. \value TypeGlobalPresence Detail type of a QContactGlobalPresence detail. \value TypeGuid Detail type of a QContactGuid detail. \value TypeHobby Detail type of a QContactHobby detail. \value TypeName Detail type of a QContactName detail. \value TypeNickname Detail type of a QContactNickname detail. \value TypeNote Detail type of a QContactNote detail. \value TypeOnlineAccount Detail type of a QContactOnlineAccount detail. \value TypeOrganization Detail type of a QContactOrganization detail. \value TypePhoneNumber Detail type of a QContactPhoneNumber detail. \value TypePresence Detail type of a QContactPresence detail. \value TypeRingtone Detail type of a QContactRingtone detail. \value TypeSyncTarget Detail type of a QContactSyncTarget detail. \value TypeTag Detail type of a QContactTag detail. \value TypeTimestamp Detail type of a QContactTimestamp detail. \value TypeType Detail type of a QContactType detail. \value TypeUrl Detail type of a QContactUrl detail. \value TypeVersion Detail type of a QContactVersion detail. \sa type() */ /*! \fn QContactDetail::operator!=(const QContactDetail& other) const Returns true if the values or id of this detail is different to those of the \a other detail */ /*! Constructs a new, empty detail */ QContactDetail::QContactDetail() : d(new QContactDetailPrivate) { d->m_type = QContactDetail::TypeUndefined; } /*! Constructs a new, empty detail of the type identified by \a type. */ QContactDetail::QContactDetail(QContactDetail::DetailType type) : d(new QContactDetailPrivate) { d->m_type = type; } /*! Constructs a detail that is a copy of \a other */ QContactDetail::QContactDetail(const QContactDetail& other) : d(other.d) { } /*! \internal Constructs a detail that is a copy of \a other if \a other is of the expected type identified by \a expectedType, else constructs a new, empty detail of the type identified by the \a expectedType */ QContactDetail::QContactDetail(const QContactDetail& other, DetailType expectedType) { if (other.d.constData()->m_type == expectedType) { d = other.d; } else { d = new QContactDetailPrivate; d->m_type = expectedType; } } /*! Assigns this detail to \a other */ QContactDetail& QContactDetail::operator=(const QContactDetail& other) { if (this != &other) d = other.d; return *this; } /*! \internal Assigns this detail to \a other if the type of \a other is that identified by the given \a expectedType, else assigns this detail to be a new, empty detail of the type identified by the given \a expectedType */ QContactDetail& QContactDetail::assign(const QContactDetail& other, DetailType expectedType) { if (this != &other) { if (other.d.constData()->m_type == expectedType) { d = other.d; } else { d = new QContactDetailPrivate; d->m_type = expectedType; } } return *this; } /*! Frees the memory used by this detail */ QContactDetail::~QContactDetail() { } /*! Returns the (unique) enum of the type which defines the semantics and structure of this detail. */ QContactDetail::DetailType QContactDetail::type() const { return d.constData()->m_type; } /*! Compares this detail to \a other. Returns true if the type, access constraints and values of \a other are equal to those of this detail. The keys of each detail are not considered during the comparison, in order to allow details from different contacts to be compared according to their values. */ bool QContactDetail::operator==(const QContactDetail& other) const { if (! (d.constData()->m_type == other.d.constData()->m_type)) return false; if (d.constData()->m_access != other.d.constData()->m_access) return false; if (d.constData()->m_values.size() != other.d.constData()->m_values.size()) return false; foreach (const QVariant &value, d.constData()->m_values.values()) { if (value.canConvert< QList >()) { if (other.d.constData()->m_values.value(d.constData()->m_values.key(value)).value< QList >() != value.value< QList >()) { return false; } } else if (!other.d.constData()->m_values.values().contains(value)) { return false; } } return true; } /*! Returns the hash value for \a key. */ uint qHash(const QContactDetail &key) { const QContactDetailPrivate* dptr= QContactDetailPrivate::detailPrivate(key); uint hash = qHash(QString().setNum(dptr->m_type)) + QT_PREPEND_NAMESPACE(qHash)(dptr->m_access); QMap::const_iterator it = dptr->m_values.constBegin(); while(it != dptr->m_values.constEnd()) { hash += QT_PREPEND_NAMESPACE(qHash)(it.key()) + QT_PREPEND_NAMESPACE(qHash)(it.value().toString()); ++it; } return hash; } #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QContactDetail& detail) { dbg.nospace() << "QContactDetail(detailType=" << detail.type() << ", key=" << detail.key(); QMap fields = detail.values(); QMap::const_iterator it; for (it = fields.constBegin(); it != fields.constEnd(); ++it) { dbg.nospace() << ", " << it.key() << '=' << it.value(); } dbg.nospace() << ')'; return dbg.maybeSpace(); } #endif #ifndef QT_NO_DATASTREAM /*! * Writes \a detail to the stream \a out. */ QDataStream& operator<<(QDataStream& out, const QContactDetail& detail) { quint8 formatVersion = 1; // Version of QDataStream format for QContactDetail return out << formatVersion << static_cast(detail.type()) << static_cast(detail.accessConstraints()) << detail.values(); } /*! * Reads a contact detail from stream \a in into \a detail. */ QDataStream& operator>>(QDataStream& in, QContactDetail& detail) { detail = QContactDetail(); quint8 formatVersion; in >> formatVersion; if (formatVersion == 1) { quint32 typeInt; quint32 accessConstraintsInt; QMap values; in >> typeInt >> accessConstraintsInt >> values; detail = QContactDetail(QContactDetail::DetailType(typeInt)); QContactDetail::AccessConstraints accessConstraints(accessConstraintsInt); detail.d->m_access = accessConstraints; QMapIterator it(values); while (it.hasNext()) { it.next(); detail.setValue(it.key(), it.value()); } } else { in.setStatus(QDataStream::ReadCorruptData); } return in; } #endif /*! Returns true if no values are contained in this detail. Note that context is stored as a value; hence, if a context is set, this function will return false. An empty value (for example, the string "") is still a value contained in this detail, so this function will return false. */ bool QContactDetail::isEmpty() const { return d.constData()->m_values.isEmpty(); } /*! * Returns the key of this detail. * * Be aware that if a contact is retrieved (or reloaded) from the backend, the * keys of any details it contains may have been changed by the backend, or other * threads may have modified the contact details in the backend. Therefore, * clients should reload the detail that they wish to save in or remove from a contact * after retrieving the contact from the backend, in order to ascertain the keys of * any such details. */ int QContactDetail::key() const { return d.constData()->m_id; } /*! Causes the implicitly-shared detail to be detached from any other copies, and generates a new key for it. * This ensures that calling QContact::saveDetail() will result in a new detail being saved, rather than * another detail being updated. */ void QContactDetail::resetKey() { d->m_id = QContactDetailPrivate::lastDetailKey.fetchAndAddOrdered(1); } /*! * Returns the value stored in this detail for the given \a field. An invalid QVariant is returned if * the value of \a field is not set. */ QVariant QContactDetail::value(int field) const { return d.constData()->m_values.value(field); } /*! * Returns true if this detail has a field with the given \a field, or false otherwise. */ bool QContactDetail::hasValue(int field) const { return d.constData()->m_values.contains(field); } /*! Inserts \a value into the detail for the given \a field if \a value is valid. If \a value is invalid, removes the field with the given \a field from the detail. Returns true if the given \a value was set for the \a field (if the \a value was valid), or if the given \a field was removed from detail (if the \a value was invalid), and returns false if the field could not be removed (and the \a value was invalid) */ bool QContactDetail::setValue(int field, const QVariant& value) { if (!value.isValid()) return removeValue(field); d->m_values.insert(field, value); return true; } /*! Removes the value stored in this detail for the given \a field. Returns true if a value was stored for the given \a field and the operation succeeded, and false otherwise. */ bool QContactDetail::removeValue(int field) { if (d->m_values.remove(field)) return true; return false; } /*! Returns the values stored in this detail as a field-to-value map. */ QMap QContactDetail::values() const { return d.constData()->m_values; } /*! \enum QContactDetail::AccessConstraint This enum defines the access constraints for a detail. This information is typically provided by the manager when a contact is retrieved. \value NoConstraint Users can read, write, and otherwise modify this detail in any manner. \value ReadOnly Users cannot write or modify values in this detail. \value Irremovable Users cannot remove this detail from a contact. */ /*! Returns the access constraints associated with the detail. Some details may not be written to, while other details may not be removed from a contact. \sa QContactDetail::AccessConstraints */ QContactDetail::AccessConstraints QContactDetail::accessConstraints() const { return d.constData()->m_access; } /*! \enum QContactDetail::DetailContext This enum defines the contexts for a detail. \value ContextHome The detail data relates to home / private detail about the contact. \value ContextWork The detail data relates to business / work detail about the contact. \value ContextOther Generic context, neither ContextHome nor ContextWork \sa setContexts(), contexts() */ /*! \fn void QContactDetail::setContexts(const QList& contexts) This is a convenience function that sets the \c Context field of this detail to the given \a contexts. It is equivalent to the following code: \code setValue(QContactDetail::FieldContext, contexts); \endcode \sa setValue() */ /*! \fn void QContactDetail::setContexts(int context) This is a convenience function that sets the \c Context field of this detail to the given \a context. It is useful if the detail is only valid in a single context. It is equivalent to the following code: \code setValue(FieldContext, QList() << context); \endcode \sa setValue() */ /*! \fn QStringList QContactDetail::contexts() const This is a convenience function to return the \c Context field of this detail. It is equivalent to the following code: \code value(QContactDetail::FieldContext); \endcode \sa value() */ /*! \enum QContactDetail::DetailField This enum defines the common fields supported by any detail. \value FieldContext The field containing the contexts of a detail. \value FieldDetailUri The field containing the detail URI of a detail. \value FieldLinkedDetailUris The field containing the URIs of other details linked to a detail. \sa setContexts(), contexts() \sa setDetailUri(), detailUri() \sa setLinkedDetailUris(), linkedDetailUris() */ /*! \fn void QContactDetail::setDetailUri(const QString& detailUri) This is a convenience function that sets the \c DetailUri field of this detail to the given \a detailUri. In order to be linked to, a detail must have a specific and (per-contact) unique detail URI set. It is equivalent to the following code: \code setValue(FieldDetailUri, detailUri); \endcode \sa setValue() */ /*! \fn QString QContactDetail::detailUri() const This is a convenience function to return the \c DetailUri field of this detail. It is equivalent to the following code: \code value(QContactDetail::FieldDetailUri); \endcode \sa value() */ /*! \fn void QContactDetail::setLinkedDetailUris(const QStringList& linkedDetailUris) This is a convenience function that sets the \c LinkedDetailUris field of this detail to the given \a linkedDetailUris. It is equivalent to the following code: \code setValue(QContactDetail::FieldLinkedDetailUris, linkedDetailUris); \endcode \sa setValue() */ /*! \fn void QContactDetail::setLinkedDetailUris(const QString& linkedDetailUri) This is a convenience function that sets the \c LinkedDetailUris field of this detail to the given \a linkedDetailUri. It is useful if the detail is linked to a single other detail in the contact. It is equivalent to the following code: \code setValue(FieldLinkedDetailUris, QStringList(linkedDetailUri)); \endcode \sa setValue() */ /*! \fn QStringList QContactDetail::linkedDetailUris() const This is a convenience function to return the \c Context field of this detail. It is equivalent to the following code: \code value(QContactDetail::FieldLinkedDetailUris); \endcode \sa value() */ QT_END_NAMESPACE_CONTACTS src/contacts/qcontactdetail.h000066400000000000000000000147371233466112000166450ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTDETAIL_H #define QCONTACTDETAIL_H #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContact; class QContactActionDescriptor; class QContactDetailPrivate; class Q_CONTACTS_EXPORT QContactDetail { public: enum DetailType { TypeUndefined = 0, TypeAddress, TypeAnniversary, TypeAvatar, TypeBirthday, TypeDisplayLabel, TypeEmailAddress, TypeExtendedDetail, TypeFamily, TypeFavorite, TypeGender, TypeGeoLocation, TypeGlobalPresence, TypeGuid, TypeHobby, TypeName, TypeNickname, TypeNote, TypeOnlineAccount, TypeOrganization, TypePhoneNumber, TypePresence, TypeRingtone, TypeSyncTarget, TypeTag, TypeTimestamp, TypeType, TypeUrl, TypeVersion }; QContactDetail(); explicit QContactDetail(DetailType type); ~QContactDetail(); QContactDetail(const QContactDetail& other); QContactDetail& operator=(const QContactDetail& other); enum AccessConstraint { NoConstraint = 0, ReadOnly = 0x01, Irremovable = 0x02 }; Q_DECLARE_FLAGS(AccessConstraints, AccessConstraint) AccessConstraints accessConstraints() const; enum DetailContext { ContextHome = 0, ContextWork, ContextOther }; enum DetailField { FieldContext = 5000, //to avoid clashing with other detail field values from leaf classes FieldDetailUri, FieldLinkedDetailUris }; bool operator==(const QContactDetail& other) const; bool operator!=(const QContactDetail& other) const {return !(other == *this);} DetailType type() const; bool isEmpty() const; int key() const; void resetKey(); bool setValue(int field, const QVariant& value); bool removeValue(int field); bool hasValue(int field) const; QMap values() const; QVariant value(int field) const; template T value(int field) const { return value(field).value(); } void setContexts(int newContext) { QList newContexts; newContexts << newContext; setValue(FieldContext, QVariant::fromValue(newContexts)); } void setContexts(const QList& newContexts) { setValue(FieldContext, QVariant::fromValue(newContexts)); } QList contexts() const { return value< QList >(FieldContext); } void setDetailUri(const QString& newDetailUri) { setValue(FieldDetailUri, newDetailUri); } QString detailUri() const { return value(FieldDetailUri).toString(); } void setLinkedDetailUris(const QStringList& newLinkedDetailUris) { setValue(FieldLinkedDetailUris, newLinkedDetailUris); } void setLinkedDetailUris(const QString& linkedDetailUri) { setValue(FieldLinkedDetailUris, QStringList(linkedDetailUri)); } QStringList linkedDetailUris() const { return value(FieldLinkedDetailUris); } protected: QContactDetail(const QContactDetail &other, DetailType expectedType); QContactDetail& assign(const QContactDetail &other, DetailType expectedType); private: friend class QContact; friend class QContactDetailPrivate; #ifndef QT_NO_DATASTREAM Q_CONTACTS_EXPORT friend QDataStream& operator>>(QDataStream& in, QContactDetail& detail); #endif QSharedDataPointer d; }; Q_CONTACTS_EXPORT uint qHash(const QContactDetail& key); #ifndef QT_NO_DEBUG_STREAM Q_CONTACTS_EXPORT QDebug operator<<(QDebug dbg, const QContactDetail& detail); #endif #ifndef QT_NO_DATASTREAM Q_CONTACTS_EXPORT QDataStream& operator<<(QDataStream& out, const QContactDetail& detail); Q_CONTACTS_EXPORT QDataStream& operator>>(QDataStream& in, QContactDetail& detail); #endif Q_DECLARE_OPERATORS_FOR_FLAGS(QContactDetail::AccessConstraints) #define Q_DECLARE_CUSTOM_CONTACT_DETAIL(className) \ className() : QContactDetail(Type) {} \ className(const QContactDetail &field) : QContactDetail(field, Type) {} \ className& operator=(const QContactDetail &other) {assign(other, Type); return *this;} \ static const DetailType Type; QT_END_NAMESPACE_CONTACTS QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(QTCONTACTS_PREPEND_NAMESPACE(QContactDetail), Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif // QCONTACTDETAIL_H src/contacts/qcontactdetail_p.h000066400000000000000000000066121233466112000171550ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTDETAIL_P_H #define QCONTACTDETAIL_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactDetailPrivate : public QSharedData { public: QContactDetailPrivate() : QSharedData(), m_id(lastDetailKey.fetchAndAddOrdered(1)), m_access(QContactDetail::NoConstraint) { } QContactDetailPrivate(const QContactDetailPrivate& other) : QSharedData(other), m_id(other.m_id), m_type(other.m_type), m_values(other.m_values), m_access(other.m_access) { } ~QContactDetailPrivate() { } int m_id; // internal, unique id. QContactDetail::DetailType m_type; QMap m_values; QContactDetail::AccessConstraints m_access; static QAtomicInt lastDetailKey; static void setAccessConstraints(QContactDetail *d, QContactDetail::AccessConstraints constraint) { d->d->m_access = constraint; } static const QContactDetailPrivate* detailPrivate(const QContactDetail& detail) { return detail.d.constData(); } }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTDETAIL_P_H src/contacts/qcontactengineid.cpp000066400000000000000000000107551233466112000175140ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Pim module. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactengineid.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactEngineId \relates QContactId \brief The QContactEngineId class uniquely identifies a contact within a particular engine plugin. \inmodule QtContacts \ingroup contacts-backends Clients of the Contacts API should never use this class. Every engine implementor must implement a class derived from QContactEngineId. This class is provided so that engine implementors can implement their own id class (which may contain arbitrary data, and which may implement the required functions in an arbitrary manner). */ /*! \fn QContactEngineId::~QContactEngineId() Cleans up any memory in use by this engine contact id. */ /*! \fn QContactEngineId::isEqualTo(const QContactEngineId* other) const Returns true if this id is equal to the \a other id; false otherwise. Note that when implementing this function, you do not have to check that the type is the same, since the function which calls this function (in QContactId) does that check for you. */ /*! \fn QContactEngineId::isLessThan(const QContactEngineId* other) const Returns true if this id is less than the \a other id; false otherwise. Note that when implementing this function, you do not have to check that the type is the same, since the function which calls this function (in QContactId) does that check for you. */ /*! \fn QContactEngineId::managerUri() const Returns the manager URI of the constructed manager which created the id. If the contact which the id identifies has not been deleted, the id should still be valid in the manager identified by the manager URI returned by this function. */ /*! \fn QContactAbstractRequest::StorageLocation QContactEngineId::storageLocation() const Returns the storage location where the contact is from. */ QContactAbstractRequest::StorageLocation QContactEngineId::storageLocation() const { return QContactAbstractRequest::UserDataStorage; } /*! \fn QContactEngineId::toString() const Serializes the id to a string. It contains all of the information required to identify a particular contact in the manager which created the id, formatted according to the serialization format of the manager. */ /*! \fn QContactEngineId::clone() const Returns a deep-copy clone of this id. The caller takes ownership of the returned engine contact id. */ /*! \fn QContactEngineId::debugStreamOut(QDebug& dbg) const Streams this id out to the debug stream \a dbg. */ /*! \fn QContactEngineId::hash() const Returns the hash value of this id. */ QT_END_NAMESPACE_CONTACTS src/contacts/qcontactengineid.h000066400000000000000000000055411233466112000171560ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTENGINEID_H #define QCONTACTENGINEID_H #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class Q_CONTACTS_EXPORT QContactEngineId: public QSharedData { public: virtual ~QContactEngineId() {} virtual bool isEqualTo(const QContactEngineId *other) const = 0; virtual bool isLessThan(const QContactEngineId *other) const = 0; virtual QString managerUri() const = 0; virtual QContactAbstractRequest::StorageLocation storageLocation() const; virtual QContactEngineId* clone() const = 0; virtual QString toString() const = 0; #ifndef QT_NO_DEBUG_STREAM // NOTE: on platforms where Qt is built without debug streams enabled, vtable will differ! virtual QDebug& debugStreamOut(QDebug &dbg) const = 0; #endif virtual uint hash() const = 0; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTENGINEID_H src/contacts/qcontactfetchhint.cpp000066400000000000000000000307341233466112000177050ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactfetchhint.h" #include "qcontactfetchhint_p.h" #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactFetchHint \inmodule QtContacts \brief The QContactFetchHint class provides hints to the manager about which contact information needs to be retrieved in an asynchronous fetch request or a synchronous function call. All of the hints may be ignored at the discretion of the manager, however if a manager is able to optimize retrieval of contacts due to hints, it may do so. If a manager ignores a hint, it must retrieve the full set of data that the hint refers to. The fetch hint contains: \list \li a list of detail types which the client is interested in (empty if interested in all detail types) \li a list of relationship types which the client is interested in (empty if interested in all relationships) \li some optimization flags which allow the client to tell the backend if they are not interested in any relationships, any action preferences, or any binary blobs (images etc). \li a preferred size for any images, if the backend supports multiple sizes or scaling \endlist Important note: a client should not make changes to a contact which has been retrieved using a fetch hint other than the default fetch hint. Doing so can result in information loss when saving the contact back to the manager (as the "new" restricted contact will replace the previously saved contact in the backend). */ /*! \enum QContactFetchHint::OptimizationHint This enum defines flags which may be set to inform the backend that the client does not require certain information. The backend may safely ignore the hint, but then must return the full set of information relating to the optimization hint. \value AllRequired Tells the backend that all information is required \value NoRelationships Tells the backend that the client does not require retrieved contacts to include a cache of relationships \value NoActionPreferences Tells the backend that the client does not require retrieved contacts to include a cache of action preferences \value NoBinaryBlobs Tells the backend that the client does not require retrieved contacts to include binary blobs */ /*! Constructs a new contact fetch hint which requests that the backend fetch all information */ QContactFetchHint::QContactFetchHint() : d(new QContactFetchHintPrivate) { } /*! Constructs a new contact fetch hint as a copy of \a other */ QContactFetchHint::QContactFetchHint(const QContactFetchHint &other) : d(other.d) { } /*! Frees any memory in use by the fetch hint */ QContactFetchHint::~QContactFetchHint() { } /*! Assigns this fetch hint to be equal to the \a other fetch hint */ QContactFetchHint& QContactFetchHint::operator=(const QContactFetchHint& other) { d = other.d; return *this; } /*! Returns the list of detail types that identify which detail type the manager should (at a minimum) retrieve when fetching contacts. This hint may be ignored by the backend, in which case it will return the full set of details for each contact retrieved. \sa setDetailTypesHint() */ QList QContactFetchHint::detailTypesHint() const { return d.constData()->m_typesHint; } /*! Sets the list of detail types that identify which detail type the manager should (at a minimum) retrieve when fetching contacts to \a types. This hint may be ignored by the backend, in which case it will return the full set of details for each contact retrieved. \sa detailTypesHint() */ void QContactFetchHint::setDetailTypesHint(const QList &types) { d->m_typesHint = types; } /*! Returns the list of relationship types that the manager should (at a minimum) retrieve when fetching contacts. This hint may be ignored by the backend, in which case it will return the full set of relationships for each contact retrieved. \sa setRelationshipTypesHint(), QContact::relationships() */ QStringList QContactFetchHint::relationshipTypesHint() const { return d.constData()->m_relationshipsHint; } /*! Sets the list of relationship types that the manager should (at a minimum) retrieve when fetching contacts to \a relationshipTypes. This hint may be ignored by the backend, in which case it will return the full set of relationships for each contact retrieved. \sa relationshipTypesHint(), QContact::relationships() */ void QContactFetchHint::setRelationshipTypesHint(const QStringList& relationshipTypes) { d->m_relationshipsHint = relationshipTypes; } /*! Returns the preferred pixel dimensions for any images returned by the manager for a given request. This hint may be ignored by the manager. This is useful when the backend supports multiple sizes of an image (or the image is natively scaleable) in order to get an image that will look good at the indicated dimensions. The caller should be prepared for images of any dimensions, in any case. */ QSize QContactFetchHint::preferredImageSize() const { return d.constData()->m_preferredImageSize; } /*! Sets the preferred pixel dimensions for any images returned by the manager for the given request to \a size. This hint may be ignored by the manager. This is useful when the backend supports multiple sizes of an image (or the image is natively scaleable) in order to get an image that will look good at the indicated dimensions. The caller should be prepared for images of any dimensions, in any case. */ void QContactFetchHint::setPreferredImageSize(const QSize& size) { d->m_preferredImageSize = size; } /*! Returns the optimization hint flags specified by the client. These hints may be ignored by the backend, in which case it will return the full set of information accessible in a contact, including relationships, action preferences, and binary blobs. \sa setOptimizationHints() */ QContactFetchHint::OptimizationHints QContactFetchHint::optimizationHints() const { return d.constData()->m_optimizationHints; } /*! Sets the optimization hint flags specified by the client to \a hints. These hints may be ignored by the backend, in which case it will return the full set of information accessible in a contact, including relationships, action preferences, and binary blobs. \sa optimizationHints() */ void QContactFetchHint::setOptimizationHints(OptimizationHints hints) { d->m_optimizationHints = hints; } /*! Returns the number of results which the client considers to be the maximum number of useful results. The client is only interested in this number of results, so returning any more results would be superfluous to the client's requirements. Note that this fetch hint only affects operations where the backend would return a list of contacts; this hint specifies the maximum number of contacts in the list which would be useful to the client. The backend may ignore this hint, in which case it must return all contacts which would otherwise have been returned as a result of the operation. A negative value for count denotes that the client wishes to retrieve all results. The default value is -1. */ int QContactFetchHint::maxCountHint() const { return d.constData()->m_maxCount; } /*! Sets the maximum number of results which the client is interested in to \a count. The client is only interested in this number of results, so returning any more results would be superfluous to the client's requirements. Note that this fetch hint only affects operations where the backend would return a list of contacts; this hint specifies the maximum number of contacts in the list which would be useful to the client. The backend may ignore this hint, in which case it must return all contacts which would otherwise have been returned as a result of the operation. A negative value for count denotes that the client wishes to retrieve all results. The default value is -1. */ void QContactFetchHint::setMaxCountHint(int count) { count < 0 ? (d->m_maxCount = -1) : (d->m_maxCount = count); } #ifndef QT_NO_DATASTREAM QDataStream& operator<<(QDataStream& out, const QContactFetchHint& hint) { quint8 formatVersion = 2; // Version of QDataStream format for QContactFetchHint QList detailTypeHintHelper; foreach (QContactDetail::DetailType hintType, hint.detailTypesHint()) detailTypeHintHelper.append(static_cast(hintType)); return out << formatVersion << detailTypeHintHelper << hint.relationshipTypesHint() << static_cast(hint.optimizationHints()) << hint.preferredImageSize() << hint.maxCountHint(); } QDataStream& operator>>(QDataStream& in, QContactFetchHint& hint) { hint = QContactFetchHint(); quint8 formatVersion; in >> formatVersion; if (formatVersion == 1 || formatVersion == 2) { QList detailTypeHintsHelper; QList detailTypeHints; QStringList relationshipTypeHints; quint32 optimizations; QSize dimensions; in >> detailTypeHintsHelper >> relationshipTypeHints >> optimizations >> dimensions; foreach (quint32 hintType, detailTypeHintsHelper) detailTypeHints.append(QContactDetail::DetailType(hintType)); hint.setDetailTypesHint(detailTypeHints); hint.setRelationshipTypesHint(relationshipTypeHints); hint.setOptimizationHints(QContactFetchHint::OptimizationHints(optimizations)); hint.setPreferredImageSize(dimensions); // version two also has a maximum fetch count hint. if (formatVersion == 2) { int maxCountHint = -1; in >> maxCountHint; hint.setMaxCountHint(maxCountHint); } } else { in.setStatus(QDataStream::ReadCorruptData); } return in; } #endif #ifndef QT_NO_DEBUG_STREAM /*! Outputs \a hint to the debug stream \a dbg */ QDebug operator<<(QDebug dbg, const QContactFetchHint& hint) { dbg.nospace() << "QContactFetchHint(" << "detailTypeHint=" << hint.detailTypesHint() << "," << "relationshipTypesHint=" << hint.relationshipTypesHint() << "," << "optimizationHints=" << static_cast(hint.optimizationHints()) << "," << "preferredImageSize=" << hint.preferredImageSize() << "maxCountHint=" << hint.maxCountHint() << ")"; return dbg.maybeSpace(); } #endif QT_END_NAMESPACE_CONTACTS src/contacts/qcontactfetchhint.h000066400000000000000000000072401233466112000173460ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTFETCHHINT_H #define QCONTACTFETCHHINT_H #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactFetchHintPrivate; class Q_CONTACTS_EXPORT QContactFetchHint { public: QContactFetchHint(); QContactFetchHint(const QContactFetchHint& other); ~QContactFetchHint(); QContactFetchHint& operator=(const QContactFetchHint& other); QList detailTypesHint() const; void setDetailTypesHint(const QList &types); QStringList relationshipTypesHint() const; void setRelationshipTypesHint(const QStringList& relationshipTypes); QSize preferredImageSize() const; void setPreferredImageSize(const QSize& size); enum OptimizationHint { AllRequired = 0x0, NoRelationships = 0x1, NoActionPreferences = 0x2, NoBinaryBlobs = 0x4 // any other optimization hints? }; Q_DECLARE_FLAGS(OptimizationHints, OptimizationHint) OptimizationHints optimizationHints() const; void setOptimizationHints(OptimizationHints hints); int maxCountHint() const; void setMaxCountHint(int count); private: QSharedDataPointer d; }; #ifndef QT_NO_DATASTREAM Q_CONTACTS_EXPORT QDataStream& operator<<(QDataStream& out, const QContactFetchHint& hint); Q_CONTACTS_EXPORT QDataStream& operator>>(QDataStream& in, QContactFetchHint& hint); #endif #ifndef QT_NO_DEBUG_STREAM Q_CONTACTS_EXPORT QDebug operator<<(QDebug dbg, const QContactFetchHint& hint); #endif Q_DECLARE_OPERATORS_FOR_FLAGS(QContactFetchHint::OptimizationHints) QT_END_NAMESPACE_CONTACTS #endif // QCONTACTFETCHHINT_H src/contacts/qcontactfetchhint_p.h000066400000000000000000000063531233466112000176710ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTFETCHHINT_P_H #define QCONTACTFETCHHINT_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactFetchHintPrivate : public QSharedData { public: QContactFetchHintPrivate() : QSharedData(), m_optimizationHints(QContactFetchHint::AllRequired), m_maxCount(-1) { } QContactFetchHintPrivate(const QContactFetchHintPrivate& other) : QSharedData(other), m_typesHint(other.m_typesHint), m_relationshipsHint(other.m_relationshipsHint), m_optimizationHints(other.m_optimizationHints), m_maxCount(other.m_maxCount) { } ~QContactFetchHintPrivate() { } QList m_typesHint; QStringList m_relationshipsHint; QSize m_preferredImageSize; QContactFetchHint::OptimizationHints m_optimizationHints; int m_maxCount; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTFETCHHINT_P_H src/contacts/qcontactfilter.cpp000066400000000000000000000254371233466112000172220ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactfilter.h" #include "qcontactfilter_p.h" #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif #include "qcontactfilters.h" #include "qcontactmanager.h" /*! \class QContactFilter \brief The QContactFilter class is used to select contacts made available through a QContactManager. \inmodule QtContacts \ingroup contacts-main This class is used as a parameter to various functions offered by QContactManager, to allow selection of contacts which have certain details or properties. */ /*! \enum QContactFilter::FilterType Describes the type of the filter \value InvalidFilter An invalid filter which matches nothing \value ContactDetailFilter A filter which matches contacts containing one or more details of a particular definition with a particular value \value ContactDetailRangeFilter A filter which matches contacts containing one or more details of a particular definition whose values are within a particular range \value ChangeLogFilter A filter which matches contacts whose timestamps have been updated since some particular date and time \value ActionFilter A filter which matches contacts for which a particular action is available, or which contain a detail with a particular value for which a particular action is available \value RelationshipFilter A filter which matches contacts which participate in a particular type of relationship, or relationship with a specified contact \value IntersectionFilter A filter which matches all contacts that are matched by all filters it includes \value UnionFilter A filter which matches any contact that is matched by any of the filters it includes \value IdFilter A filter which matches any contact whose id is contained in a particular list of contact ids \value DefaultFilter A filter which matches everything */ /*! \enum QContactFilter::MatchFlag Describes the semantics of matching followed by the filter \value MatchExactly Performs QVariant-based matching , combination of MatchExactly with other flags is not supported \value MatchContains The search term is contained in the item \value MatchStartsWith The search term matches the start of the item \value MatchEndsWith The search term matches the end of the item \value MatchFixedString Performs string-based matching. String-based comparisons are case-insensitive unless the \c MatchCaseSensitive flag is also specified \value MatchCaseSensitive The search is case sensitive \value MatchPhoneNumber The search term is considered to be in the form of a phone number, and special processing (removing dialing prefixes, non significant characters like '-'. ')' etc). may be performed when matching the item. \value MatchKeypadCollation The search term is in the form of text entered by a numeric phone keypad (such as ITU-T E.161 compliant keypads). Each digit in the search term can represent a number of alphanumeric symbols. For example, the search string "43556" would match items "HELLO", "GEKKO", "HELL6" and "43556" among others. Accented characters and other punctuation characters may additionally be matched by the QContactManager in a way consistent with the platform. */ /*! \fn QContactFilter::operator!=(const QContactFilter& other) const Returns true if this filter is not identical to the \a other filter. \sa operator==() */ #if !defined(Q_CC_MWERKS) template<> QTCONTACTS_PREPEND_NAMESPACE(QContactFilterPrivate) *QSharedDataPointer::clone() { return d->clone(); } #endif QT_BEGIN_NAMESPACE_CONTACTS /*! Constructs an empty filter */ QContactFilter::QContactFilter() : d_ptr(0) { } /*! Constructs a new copy of \a other */ QContactFilter::QContactFilter(const QContactFilter& other) : d_ptr(other.d_ptr) { } /*! Assigns this filter to be \a other */ QContactFilter& QContactFilter::operator=(const QContactFilter& other) { if (this != &other) { d_ptr = other.d_ptr; } return *this; } /*! Cleans up the memory used by this filter */ QContactFilter::~QContactFilter() { } /*! Returns the type of the filter */ QContactFilter::FilterType QContactFilter::type() const { if (!d_ptr) return QContactFilter::DefaultFilter; return d_ptr->type(); } /*! Returns true if the filter has the same type and criteria as \a other */ bool QContactFilter::operator==(const QContactFilter& other) const { /* A default filter is only equal to other default filters */ if (!d_ptr) return !other.d_ptr; /* Different types can't be equal */ if (other.type() != type()) return false; /* Otherwise, use the virtual op == */ return d_ptr->compare(other.d_ptr); } #ifndef QT_NO_DATASTREAM /*! * Writes \a filter to the stream \a out. */ QDataStream& operator<<(QDataStream& out, const QContactFilter& filter) { quint8 formatVersion = 1; // Version of QDataStream format for QContactDetailFilter out << formatVersion << static_cast(filter.type()); if (filter.d_ptr) filter.d_ptr->outputToStream(out, formatVersion); return out; } /*! * Reads a contact filter from stream \a in into \a filter. */ QDataStream& operator>>(QDataStream& in, QContactFilter& filter) { filter = QContactFilter(); quint8 formatVersion; in >> formatVersion; if (formatVersion == 1) { quint32 type; in >> type; switch (type) { case QContactFilter::InvalidFilter: filter = QContactInvalidFilter(); break; case QContactFilter::ContactDetailFilter: filter = QContactDetailFilter(); break; case QContactFilter::ContactDetailRangeFilter: filter = QContactDetailRangeFilter(); break; case QContactFilter::ChangeLogFilter: filter = QContactChangeLogFilter(); break; case QContactFilter::ActionFilter: filter = QContactActionFilter(); break; case QContactFilter::RelationshipFilter: filter = QContactRelationshipFilter(); break; case QContactFilter::IntersectionFilter: filter = QContactIntersectionFilter(); break; case QContactFilter::UnionFilter: filter = QContactUnionFilter(); break; case QContactFilter::IdFilter: filter = QContactIdFilter(); break; case QContactFilter::DefaultFilter: filter = QContactFilter(); break; } if (filter.d_ptr) filter.d_ptr->inputFromStream(in, formatVersion); } else { in.setStatus(QDataStream::ReadCorruptData); } return in; } #endif #ifndef QT_NO_DEBUG_STREAM /*! Outputs \a filter to the debug stream \a dbg */ QDebug operator<<(QDebug dbg, const QContactFilter& filter) { dbg.nospace() << "QContactFilter("; if (filter.d_ptr) filter.d_ptr->debugStreamOut(dbg); else dbg.nospace() << "(null)"; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif /*! \internal Constructs a new filter from the given data pointer \a d */ QContactFilter::QContactFilter(QContactFilterPrivate *d) : d_ptr(d) { } /*! \relates QContactFilter Returns a filter which is the intersection of the \a left and \a right filters \sa QContactIntersectionFilter */ const QContactFilter operator&(const QContactFilter& left, const QContactFilter& right) { // XXX TODO: empty intersection/union operations are not well defined yet. //if (left.type() == QContactFilter::Intersection) { // QContactIntersectionFilter bf(left); // /* we can just add the right to this one */ // bf.append(right); // return bf; //} //if (right.type() == QContactFilter::Intersection) { // QContactIntersectionFilter bf(right); // /* we can prepend the left to this one */ // bf.prepend(left); // return bf; //} /* usual fallback case */ QContactIntersectionFilter nif; nif << left << right; return nif; } /*! \relates QContactFilter Returns a filter which is the union of the \a left and \a right filters \sa QContactUnionFilter */ const QContactFilter operator|(const QContactFilter& left, const QContactFilter& right) { if (left.type() == QContactFilter::UnionFilter) { QContactUnionFilter bf(left); /* we can just add the right to this one */ bf.append(right); return bf; } if (right.type() == QContactFilter::UnionFilter) { QContactUnionFilter bf(right); /* we can prepend the left to this one */ bf.prepend(left); return bf; } /* usual fallback case */ QContactUnionFilter nif; nif << left << right; return nif; } QT_END_NAMESPACE_CONTACTS src/contacts/qcontactfilter.h000066400000000000000000000112231233466112000166530ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTFILTER_H #define QCONTACTFILTER_H #include #include QT_BEGIN_NAMESPACE_CONTACTS /* Manual Q_DECLARE_CONTACTFILTER_PRIVATE macro */ #define Q_DECLARE_CONTACTFILTER_PRIVATE(Class) \ inline Class##Private* d_func(); \ inline const Class##Private* d_func() const; \ friend class Class##Private; class QContactFilterPrivate; class Q_CONTACTS_EXPORT QContactFilter { public: QContactFilter(); ~QContactFilter(); QContactFilter(const QContactFilter& other); QContactFilter& operator=(const QContactFilter& other); enum FilterType { InvalidFilter, ContactDetailFilter, ContactDetailRangeFilter, ChangeLogFilter, ActionFilter, RelationshipFilter, IntersectionFilter, UnionFilter, IdFilter, DefaultFilter }; FilterType type() const; enum MatchFlag { MatchExactly = 0x0000, // 0 MatchContains = 0x0001, // 1 MatchStartsWith = 0x0002, // 2 MatchEndsWith = 0x0004, // 4 MatchFixedString = 0x0008, // 8 MatchCaseSensitive = 0x0010, // 16 MatchPhoneNumber = 0x400, //1024 MatchKeypadCollation = 0x800 //2048 }; Q_DECLARE_FLAGS(MatchFlags, MatchFlag) bool operator==(const QContactFilter& other) const; bool operator!=(const QContactFilter& other) const {return !operator==(other);} protected: explicit QContactFilter(QContactFilterPrivate* d); protected: friend class QContactFilterPrivate; #ifndef QT_NO_DATASTREAM Q_CONTACTS_EXPORT friend QDataStream& operator<<(QDataStream& out, const QContactFilter& filter); Q_CONTACTS_EXPORT friend QDataStream& operator>>(QDataStream& in, QContactFilter& filter); #endif #ifndef QT_NO_DEBUG_STREAM Q_CONTACTS_EXPORT friend QDebug operator<<(QDebug dbg, const QContactFilter& filter); #endif QSharedDataPointer d_ptr; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QContactFilter::MatchFlags) const Q_CONTACTS_EXPORT QContactFilter operator&(const QContactFilter& left, const QContactFilter& right); const Q_CONTACTS_EXPORT QContactFilter operator|(const QContactFilter& left, const QContactFilter& right); #ifndef QT_NO_DATASTREAM Q_CONTACTS_EXPORT QDataStream& operator<<(QDataStream& out, const QContactFilter& filter); Q_CONTACTS_EXPORT QDataStream& operator>>(QDataStream& in, QContactFilter& filter); #endif #ifndef QT_NO_DEBUG_STREAM Q_CONTACTS_EXPORT QDebug operator<<(QDebug dbg, const QContactFilter& filter); #endif QT_END_NAMESPACE_CONTACTS QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(QTCONTACTS_PREPEND_NAMESPACE(QContactFilter), Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif // QCONTACTFILTER_H src/contacts/qcontactfilter_p.h000066400000000000000000000115351233466112000172000ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTFILTER_P_H #define QCONTACTFILTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif #include #include #include /* Boiler plate code */ #define Q_IMPLEMENT_CONTACTFILTER_PRIVATE(Class) \ Class##Private* Class::d_func() { return reinterpret_cast(d_ptr.data()); } \ const Class##Private* Class::d_func() const { return reinterpret_cast(d_ptr.constData()); } \ Class::Class(const QContactFilter& other) : QContactFilter() { Class##Private::copyIfPossible(d_ptr, other); } #define Q_IMPLEMENT_CONTACTFILTER_VIRTUALCTORS(Class, Type) \ QContactFilterPrivate* clone() const { return new Class##Private(*this); } \ virtual QContactFilter::FilterType type() const {return Type;} \ static void copyIfPossible(QSharedDataPointer& d_ptr, const QContactFilter& other) \ { \ if (other.type() == Type) \ d_ptr = extract_d(other); \ else \ d_ptr = new Class##Private; \ } QT_BEGIN_NAMESPACE_CONTACTS class QContactFilterPrivate : public QSharedData { public: QContactFilterPrivate() { } virtual ~QContactFilterPrivate() { } virtual bool compare(const QContactFilterPrivate* other) const = 0; virtual QDataStream& outputToStream(QDataStream& stream, quint8 formatVersion) const = 0; virtual QDataStream& inputFromStream(QDataStream& stream, quint8 formatVersion) = 0; #ifndef QT_NO_DEBUG_STREAM // NOTE: on platforms where Qt is built without debug streams enabled, vtable will differ! virtual QDebug& debugStreamOut(QDebug& dbg) const = 0; #endif virtual QContactFilterPrivate* clone() const = 0; virtual QContactFilter::FilterType type() const = 0; /* Helper functions for C++ protection rules */ static const QSharedDataPointer& extract_d(const QContactFilter& other) {return other.d_ptr;} }; QT_END_NAMESPACE_CONTACTS QT_BEGIN_NAMESPACE #if defined(Q_CC_MWERKS) // This results in multiple symbol definition errors on all other compilers // but not having a definition here results in an attempt to use the unspecialized // clone (which fails because of the pure virtuals above) template<> QTCONTACTS_PREPEND_NAMESPACE(QContactFilterPrivate) *QSharedDataPointer::clone() { return d->clone(); } #else template<> QTCONTACTS_PREPEND_NAMESPACE(QContactFilterPrivate) *QSharedDataPointer::clone(); #endif QT_END_NAMESPACE #endif // QCONTACTFILTER_P_H src/contacts/qcontactid.cpp000066400000000000000000000253731233466112000163300ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactid.h" #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif #include "qcontactengineid.h" #include "qcontactmanager_p.h" QT_BEGIN_NAMESPACE template<> QTCONTACTS_PREPEND_NAMESPACE(QContactEngineId) *QSharedDataPointer::clone() { return d ? d->clone() : 0; } QT_END_NAMESPACE QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactId \inmodule QtContacts \brief The QContactId class provides information that uniquely identifies a contact in a particular manager. It consists of a manager URI which identifies the manager which contains the contact, A QContactId is "null" when it is default-constructed and therefore not associated with a contact in any manager. */ /*! * Constructs a new contact id. The contact is said to be "null", it has null engine id and isNull() for it returns true. This is the default constructor \sa isNull() */ QContactId::QContactId() : d(0) { } /*! * Cleans up the memory in use by the contact id */ QContactId::~QContactId() { } /*! Constructs a new contact id as a copy of \a other */ QContactId::QContactId(const QContactId &other) : d(other.d) { } /*! Constructs a manager-unique id which wraps the given engine-unique contact id \a engineId. This id takes ownership of the engine-unique contact id and will delete it when the id goes out of scope. Engine implementors must not delete the \a engineId or undefined behaviour will occur. The created contact id is not null, if the \a engineId is not null. */ QContactId::QContactId(QContactEngineId *engineId) : d(engineId) { } /*! Assigns the contact id to be equal to \a other */ QContactId& QContactId::operator=(const QContactId &other) { d = other.d; return *this; } /*! Returns true if the \a other contact id has the same manager URI and they have equal engine ids. Returns true also, if both are null contact ids. */ bool QContactId::operator==(const QContactId &other) const { // if both ids are null then they are equal. if (d == 0 && other.d == 0) return true; if (d && other.d) { // ensure they're of the same type (and therefore comparable) if (d->managerUri() == other.d->managerUri()) return d->isEqualTo(other.d); } return false; } /*! Returns true if either the manager URI or id of the contact id is different to that of \a other */ bool QContactId::operator!=(const QContactId &other) const { return !(*this == other); } /*! Returns true if this id is less than the \a other id. This id will be considered less than the \a other id if the manager URI of this id is alphabetically less than the manager URI of the \a other id. If both ids have the same manager URI, this id will be considered less than the \a other id if the engine id of this id is less than the engine id of the \a other id. The null contact id is less than any non-null id. This operator is provided primarily to allow use of a QContactId as a key in a QMap. */ bool QContactId::operator<(const QContactId &other) const { // a null id is always less than a non-null id. if (d == 0 && other.d != 0) return true; if (d && other.d) { // ensure they're of the same type (and therefore comparable) if (d->managerUri() == other.d->managerUri()) return d->isLessThan(other.d); // not the same type? just compare the manager uri. return d->managerUri() < other.d->managerUri(); } return false; } /*! * Returns the hash value for \a key. */ uint qHash(const QContactId &key) { if (key.d) return key.d->hash(); return 0; } /*! Returns true if this is the null (default constructed) id which has a null engine id; */ bool QContactId::isNull() const { return d == 0; } /*! Escapes the parameters for inclusion to URIs */ QString QContactId::escapeUriParam(const QString ¶m) { QString rich; const int len = param.length(); rich.reserve(int(len * 1.1)); for (int i = 0; i < len; ++i) { if (param.at(i) == QLatin1Char(':')) rich += QStringLiteral(":"); else if (param.at(i) == QLatin1Char('=')) rich += QStringLiteral("&equ;"); else if (param.at(i) == QLatin1Char('&')) rich += QStringLiteral("&"); else rich += param.at(i); } rich.squeeze(); return rich; } /*! Parses the individual components of the given \a idString and fills the \a managerName, \a params and \a engineIdString. Returns true if the parts could be parsed successfully, false otherwise. */ bool QContactId::parseIdString(const QString &idString, QString* managerName, QMap *params, QString *engineIdString) { QStringList colonSplit = idString.split(QLatin1Char(':')); QString prefix = colonSplit.value(0); if (prefix != QStringLiteral("qtcontacts") || colonSplit.size() != 4) return false; // invalid serialized string. we cannot continue. QString mgrName = colonSplit.value(1); QString paramString = colonSplit.value(2); QString engIdString = colonSplit.value(3); // Now we have to decode each parameter QMap outParams; if (!paramString.isEmpty()) { QStringList params = paramString.split(QStringLiteral("&(?!(amp;|equ;))"), QString::KeepEmptyParts); // If we have an empty string for paramstring, we get one entry in params, // so skip that case. for (int i = 0; i < params.count(); i++) { /* This should be something like "foo&bar&equ;=grob&" */ QStringList paramChunk = params.value(i).split(QStringLiteral("="), QString::KeepEmptyParts); if (paramChunk.count() != 2) return false; QString arg = paramChunk.value(0); QString param = paramChunk.value(1); arg.replace(QStringLiteral(":"), QStringLiteral(":")); arg.replace(QStringLiteral("&equ;"), QStringLiteral("=")); arg.replace(QStringLiteral("&"), QStringLiteral("&")); param.replace(QStringLiteral(":"), QStringLiteral(":")); param.replace(QStringLiteral("&equ;"), QStringLiteral("=")); param.replace(QStringLiteral("&"), QStringLiteral("&")); if (arg.isEmpty()) return false; outParams.insert(arg, param); } } // and unescape the engine id string. engIdString.replace(QStringLiteral(":"), QStringLiteral(":")); engIdString.replace(QStringLiteral("&"), QStringLiteral("&")); // now fill the return values. if (managerName) *managerName = mgrName; if (params) *params = outParams; if (engineIdString) *engineIdString = engIdString; // and return. return true; } /*! * Returns the URI of the manager which contains the contact identified by this id */ QString QContactId::managerUri() const { return d ? d->managerUri() : QString(); } /*! Returns the contact id as a string. This string can be converted back to equal contact id using fromString. \sa fromString() */ QString QContactId::toString() const { // rely on engine id to supply the full manager uri if (d) { QString result(QStringLiteral("%1:%2")); QString managerUri = d->managerUri(); QString escapedEngineId = QContactId::escapeUriParam(d->toString()); return result.arg(managerUri, escapedEngineId); } return QString(QStringLiteral("qtcontacts:::")); } #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QContactId &id) { dbg.nospace() << "QContactId("; if (id.isNull()) dbg.nospace() << "(null))"; else id.d->debugStreamOut(dbg) << ")"; return dbg.maybeSpace(); } #endif #ifndef QT_NO_DATASTREAM QDataStream& operator<<(QDataStream &out, const QContactId &contactId) { out << (contactId.toString()); return out; } QDataStream& operator>>(QDataStream &in, QContactId &id) { QString idString; in >> idString; id = QContactId::fromString(idString); return in; } #endif /*! Deserializes the given \a idString. Returns a default-constructed (null) contact id if the given \a idString is not a valid, serialized contact id, or if the manager engine from which the id came could not be found. \sa toString() */ QContactId QContactId::fromString(const QString &idString) { QString managerName; QMap params; QString engineIdString; if (!QContactId::parseIdString(idString, &managerName, ¶ms, &engineIdString)) return QContactId(); // invalid idString given. QContactEngineId* engineId = QContactManagerData::createEngineContactId(managerName, params, engineIdString); return QContactId(engineId); } QT_END_NAMESPACE_CONTACTS src/contacts/qcontactid.h000066400000000000000000000074041233466112000157700ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTID_H #define QCONTACTID_H #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactManagerEngine; class QContactEngineId; class Q_CONTACTS_EXPORT QContactId { public: QContactId(); ~QContactId(); QContactId(const QContactId &other); QContactId& operator=(const QContactId &other); explicit QContactId(QContactEngineId *engineId); bool operator==(const QContactId &other) const; bool operator!=(const QContactId &other) const; bool operator<(const QContactId &other) const; QString managerUri() const; bool isNull() const; QString toString() const; static QContactId fromString(const QString &idString); private: static bool parseIdString(const QString &idString, QString* managerName, QMap *params, QString *engineIdString); static QString escapeUriParam(const QString ¶m); QSharedDataPointer d; friend class QContactManager; friend class QContactManagerEngine; Q_CONTACTS_EXPORT friend uint qHash(const QContactId &key); #ifndef QT_NO_DEBUG_STREAM Q_CONTACTS_EXPORT friend QDebug operator<<(QDebug dbg, const QContactId &id); #endif }; Q_CONTACTS_EXPORT uint qHash(const QContactId &key); #ifndef QT_NO_DEBUG_STREAM Q_CONTACTS_EXPORT QDebug operator<<(QDebug dbg, const QContactId &id); #endif #ifndef QT_NO_DATASTREAM Q_CONTACTS_EXPORT QDataStream& operator<<(QDataStream &out, const QContactId &id); Q_CONTACTS_EXPORT QDataStream& operator>>(QDataStream &in, QContactId &id); #endif QT_END_NAMESPACE_CONTACTS QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(QTCONTACTS_PREPEND_NAMESPACE(QContactId), Q_MOVABLE_TYPE); QT_END_NAMESPACE Q_DECLARE_METATYPE(QTCONTACTS_PREPEND_NAMESPACE(QContactId)) #endif // QCONTACTID_H src/contacts/qcontactmanager.cpp000066400000000000000000001216361233466112000173450ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactmanager.h" #include #include "qcontact_p.h" #include "qcontactfilter.h" #include "qcontactmanager_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactManager \brief The QContactManager class provides an interface which allows clients with access to contact information stored in a particular backend. \inmodule QtContacts \ingroup contacts-main This class provides an abstraction of a datastore or aggregation of datastores which contains contact information. It provides methods to retrieve and manipulate contact information and contact relationship information. It also provides metadata and error information reporting. The functions provided by QContactManager are purely synchronous; to access the same functionality in an asynchronous manner, clients should use the use-case-specific classes derived from QContactAbstractRequest. Some functionality provided by QContactManager directly is not accessible using the asynchronous API. See the \l{Qt Contacts Synchronous API}{synchronous} and \l{Qt Contacts Asynchronous API}{asynchronous} API information in the \l{Qt Contacts C++ API} documentation. */ /*! \fn QContactManager::dataChanged() This signal is emitted by the manager if its internal state changes, and it is unable to determine the changes which occurred, or if the manager considers the changes to be radical enough to require clients to reload all data. If this signal is emitted, no other signals will be emitted for the associated changes. */ /*! \fn QContactManager::contactsAdded(const QList& contactIds) This signal is emitted at some point once the contacts identified by \a contactIds have been added to a datastore managed by this manager. This signal must not be emitted if the dataChanged() signal was previously emitted for these changes. */ /*! \fn QContactManager::contactsChanged(const QList& contactIds) This signal is emitted at some point once the contacts identified by \a contactIds have been modified in a datastore managed by this manager. This signal must not be emitted if the dataChanged() signal was previously emitted for these changes. */ /*! \fn QContactManager::contactsRemoved(const QList& contactIds) This signal is emitted at some point once the contacts identified by \a contactIds have been removed from a datastore managed by this manager. This signal must not be emitted if the dataChanged() signal was previously emitted for these changes. */ /*! \fn QContactManager::relationshipsAdded(const QList& affectedContactIds) This signal is emitted at some point after relationships have been added to the manager which involve the contacts identified by \a affectedContactIds. This signal must not be emitted if the dataChanged() signal was previously emitted for these changes. */ /*! \fn QContactManager::relationshipsRemoved(const QList& affectedContactIds) This signal is emitted at some point after relationships have eben removed from the manager which involve the contacts identified by \a affectedContactIds. This signal must not be emitted if the dataChanged() signal was previously emitted for these changes. */ /*! \fn QContactManager::selfContactIdChanged(const QContactId& oldId, const QContactId& newId) This signal is emitted at some point after the id of the self-contact is changed from \a oldId to \a newId in the manager. If the \a newId is the invalid, then the self contact was deleted or no self contact exists. This signal must not be emitted if the dataChanged() signal was previously emitted for this change. */ #define makestr(x) (#x) #define makename(x) makestr(x) /*! Returns a list of available manager ids that can be used when constructing a QContactManager. If an empty id is specified to the constructor, the first value in this list will be used instead. The QTCONTACTS_MANAGER_OVERRIDE environment variable may be set to override the default engine. */ QStringList QContactManager::availableManagers() { QStringList ret; QContactManagerData::loadFactoriesMetadata(); ret = QContactManagerData::m_managerNames; // Unless overridden, the default must be 'invalid' so that malicious plugins // do not become the default selection without explicit selection ret.prepend(QStringLiteral("invalid")); // bump memory to the end of the list if (ret.removeAll(QStringLiteral("memory"))) ret.append(QStringLiteral("memory")); #if defined(Q_CONTACTS_DEFAULT_ENGINE) // now swizzle the default engine to pole position const QString defaultManagerName = QString::fromLatin1(makename(Q_CONTACTS_DEFAULT_ENGINE)); if (ret.removeAll(defaultManagerName)) { ret.prepend(defaultManagerName); } #endif // and prefer the override engine if specified in the environment const QString overrideManagerName = QString::fromLatin1(qgetenv("QTCONTACTS_MANAGER_OVERRIDE")); if (!overrideManagerName.isEmpty() && ret.contains(overrideManagerName)) { ret.removeAll(overrideManagerName); ret.prepend(overrideManagerName); } return ret; } /*! Splits the given \a uri into the manager, store, and parameters that it describes, and places the information into the memory addressed by \a pManagerId and \a pParams respectively. Returns true if \a uri could be split successfully, otherwise returns false */ bool QContactManager::parseUri(const QString &uri, QString *pManagerId, QMap *pParams) { // Format: qtcontacts::=&= // 1) parameters are currently a qstringlist.. should they be a map? // 2) is the uri going to be escaped? my guess would be "probably not" // 3) hence, do we assume that the prefix, managerid and storeid cannot contain `:' // 4) similarly, that neither keys nor values can contain `=' or `&' QStringList colonSplit = uri.split(QLatin1Char(':')); QString prefix = colonSplit.value(0); if (prefix != QStringLiteral("qtcontacts")) return false; QString managerName = colonSplit.value(1); if (managerName.trimmed().isEmpty()) return false; QString firstParts = prefix + QLatin1Char(':') + managerName + QLatin1Char(':'); QString paramString = uri.mid(firstParts.length()); QMap outParams; // Now we have to decode each parameter if (!paramString.isEmpty()) { QStringList params = paramString.split(QRegExp(QStringLiteral("&(?!(amp;|equ;))")), QString::KeepEmptyParts); // If we have an empty string for paramstring, we get one entry in params, // so skip that case. for(int i = 0; i < params.count(); i++) { /* This should be something like "foo&bar&equ;=grob&" */ QStringList paramChunk = params.value(i).split(QStringLiteral("="), QString::KeepEmptyParts); if (paramChunk.count() != 2) return false; QString arg = paramChunk.value(0); QString param = paramChunk.value(1); arg.replace(QStringLiteral("&equ;"), QStringLiteral("=")); arg.replace(QStringLiteral("&"), QStringLiteral("&")); param.replace(QStringLiteral("&equ;"), QStringLiteral("=")); param.replace(QStringLiteral("&"), QStringLiteral("&")); if (arg.isEmpty()) return false; outParams.insert(arg, param); } } if (pParams) *pParams = outParams; if (pManagerId) *pManagerId = managerName; return true; } /*! Returns a URI that completely describes a manager implementation, datastore, and the parameters with which to instantiate the manager, from the given \a managerName, \a params and an optional \a implementationVersion. This function is generally useful only if you intend to construct a manager with the \l fromUri() function, or wish to construct a contact id manually (for synchronization or other purposes). Most clients will not need to use this function. */ QString QContactManager::buildUri(const QString &managerName, const QMap ¶ms, int implementationVersion) { QString ret(QStringLiteral("qtcontacts:%1:%2")); // we have to escape each param QStringList escapedParams; QStringList keys = params.keys(); for (int i=0; i < keys.size(); i++) { QString key = keys.at(i); QString arg = params.value(key); arg = QContactId::escapeUriParam(arg); key = QContactId::escapeUriParam(key); key = key + QLatin1Char('=') + arg; escapedParams.append(key); } if (implementationVersion != -1) { QString versionString = QString(QStringLiteral(QTCONTACTS_IMPLEMENTATION_VERSION_NAME)); versionString += QString::fromLatin1("="); versionString += QString::number(implementationVersion); escapedParams.append(versionString); } return ret.arg(managerName, escapedParams.join(QStringLiteral("&"))); } /*! Constructs a QContactManager whose implementation version, manager name and specific parameters are specified in the given \a managerUri, and whose parent object is \a parent. */ QContactManager* QContactManager::fromUri(const QString &managerUri, QObject *parent) { if (managerUri.isEmpty()) { return new QContactManager(QString(), QMap(), parent); } else { QString id; QMap parameters; if (parseUri(managerUri, &id, ¶meters)) { return new QContactManager(id, parameters, parent); } else { // invalid return new QContactManager(QStringLiteral("invalid"), QMap(), parent); } } } /*! Constructs a QContactManager whose parent QObject is \a parent. The default implementation for the platform will be created. */ QContactManager::QContactManager(QObject *parent) : QObject(parent), d(new QContactManagerData) { createEngine(QString(), QMap()); } /*! Constructs a QContactManager whose implementation is identified by \a managerName with the given \a parameters. The \a parent QObject will be used as the parent of this QContactManager. If an empty \a managerName is specified, the default implementation for the platform will be used. */ QContactManager::QContactManager(const QString &managerName, const QMap ¶meters, QObject *parent) : QObject(parent), d(new QContactManagerData) { createEngine(managerName, parameters); } void QContactManager::createEngine(const QString &managerName, const QMap ¶meters) { d->createEngine(managerName, parameters); QContactManagerData::m_aliveEngines.insert(this); } /*! Constructs a QContactManager whose backend has the name \a managerName and version \a implementationVersion, where the manager is constructed with the provided \a parameters. The \a parent QObject will be used as the parent of this QContactManager. If an empty \a managerName is specified, the default implementation for the platform will be instantiated. If the specified implementation version is not available, the manager with the name \a managerName with the default implementation version is instantiated. */ QContactManager::QContactManager(const QString &managerName, int implementationVersion, const QMap ¶meters, QObject *parent) : QObject(parent), d(new QContactManagerData) { QMap params = parameters; params[QString(QStringLiteral(QTCONTACTS_IMPLEMENTATION_VERSION_NAME))] = QString::number(implementationVersion); createEngine(managerName, params); } /*! Frees the memory used by the QContactManager */ QContactManager::~QContactManager() { QContactManagerData::m_aliveEngines.remove(this); delete d; } /*! \fn QContactManager::ParameterSignalSources() The string constant for the parameter key which holds the value for signal sources. If a manager supports suppressing change signals depending on the value given for this construction parameter, clients can request that signals be suppressed if the changes which might cause a signal to be emitted do not match particular criteria. If the parameter (or value given for the parameter) is not supported by the manager, the manager may still be constructed, however the parameter will not be reported to the client if the client calls managerParameters() subsequent to manager construction. The default (assumed) value for this parameter, if this parameter is not given, is that the client wants to be notified of all changes to the data, regardless of the source of the change. */ /*! \fn QContactManager::ParameterSignalDefinitions() The string constant for the parameter key which holds the names of detail definitions. If a manager supports suppressing change signals depending on the value given for this construction parameter, clients can request that signals be suppressed if the changes which might otherwise cause a signal to be emitted, involve details whose definition name is not contained in the given list. That is, if a detail in a contact is changed, but that detail's definition name is not listed in the value for this parameter, the manager will not emit a change signal for that change. If this parameter is not specified at construction time, changes to any detail of a contact will cause a change signal to be emitted. The value of this parameter should be a comma (,) separated list of definition names. Any commas which might be part of a definition name must be escaped with a single backslash (\) character prior to concatenation. Any backslash character which might be part of a definition name must also be escaped with a backslash. If the parameter (or value given for the parameter) is not supported by the manager, the manager may still be constructed, however the parameter will not be reported to the client if the client calls managerParameters() subsequent to manager construction. */ /*! \fn QContactManager::ParameterValueOnlyOtherManagers() This value tells the manager to only emit signals for changes which are made in other manager instances. That is, the client wishes to receive change signals when another client (or background service) changes the data as it is stored in the backend, but does not wish to be notified of changes (or side effects) which it has caused itself. */ /*! \fn QContactManager::ParameterValueOnlyOtherProcesses() This value tells the manager to only emit signals for changes which are made in other processes. That is, the client wishes to receive change signals when a client (or background service) in another process changes the data as it is stored in the backend, but does not wish to be notified of changes (or side effects) which were caused in the current client's process, even if those changes were made in a different manager instance to this one. */ /*! \enum QContactManager::Error This enum specifies an error that occurred during the most recent operation: \value NoError The most recent operation was successful \value DoesNotExistError The most recent operation failed because the requested contact does not exist \value AlreadyExistsError The most recent operation failed because the specified contact already exists \value InvalidDetailError The most recent operation failed because the specified contact contains details which do not conform to their definition \value InvalidRelationshipError The most recent operation failed because the specified relationship is circular or references an invalid local contact \value InvalidContactTypeError The most recent operation failed because the contact type specified was not valid for the operation \value LockedError The most recent operation failed because the datastore specified is currently locked \value DetailAccessError The most recent operation failed because a detail was modified or removed and its access method does not allow that \value PermissionsError The most recent operation failed because the caller does not have permission to perform the operation \value OutOfMemoryError The most recent operation failed due to running out of memory \value VersionMismatchError The most recent operation failed because the backend of the manager is not of the required version \value LimitReachedError The most recent operation failed because the limit for that type of object has been reached \value NotSupportedError The most recent operation failed because the requested operation is not supported in the specified store \value BadArgumentError The most recent operation failed because one or more of the parameters to the operation were invalid \value TimeoutError The most recent operation failed because it took longer than expected. It may be possible to try again. \value UnspecifiedError The most recent operation failed for an undocumented reason \value InvalidStorageLocationError The most recent operation failed due to requested storage location is unavailable or invalid. \value MissingPlatformRequirementsError The most recent operation failed due to all storage locations are unavailable. */ /*! Return the error code of the most recent operation. For batch operations, if the error code is not equal to \c QContactManager::NoError, detailed per-input errors may be retrieved by calling \l errorMap(). \sa errorMap() */ QContactManager::Error QContactManager::error() const { return d->m_lastError; } /*! Returns per-input error codes for the most recent operation. This function only returns meaningful information if the most recent operation was a batch operation. Each key in the map is the index of the element in the input list for which the error (whose error code is stored in the value for that key in the map) occurred during the batch operation. \sa error(), contacts(), saveContacts(), removeContacts(), saveRelationships(), removeRelationships() */ QMap QContactManager::errorMap() const { return d->m_lastErrorMap; } /*! Return the list of contact ids, sorted according to the given list of \a sortOrders */ QList QContactManager::contactIds(const QList &sortOrders) const { QContactManagerSyncOpErrorHolder h(this); return d->m_engine->contactIds(QContactFilter(), sortOrders, &h.error); } /*! Returns a list of contact ids that match the given \a filter, sorted according to the given list of \a sortOrders. Depending on the backend, this filtering operation may involve retrieving all the contacts. */ QList QContactManager::contactIds(const QContactFilter &filter, const QList &sortOrders) const { QContactManagerSyncOpErrorHolder h(this); return d->m_engine->contactIds(filter, sortOrders, &h.error); } /*! Returns the list of contacts stored in the manager sorted according to the given list of \a sortOrders. The \a fetchHint parameter describes the optimization hints that a manager may take. If the \a fetchHint is the default constructed hint, all existing details, relationships and action preferences in the matching contact will be returned. If a client makes changes to an contact which has been retrieved with a fetch hint, they should save it back using a partial save, masked by the same set of detail names in order to avoid information loss. \sa QContactFetchHint */ QList QContactManager::contacts(const QList &sortOrders, const QContactFetchHint &fetchHint) const { QContactManagerSyncOpErrorHolder h(this); return d->m_engine->contacts(QContactFilter(), sortOrders, fetchHint, &h.error); } /*! Returns a list of contacts that match the given \a filter, sorted according to the given list of \a sortOrders. Depending on the manager implementation, this filtering operation might be slow and involve retrieving all the contacts and testing them against the supplied filter - see the \l isFilterSupported() function. The \a fetchHint parameter describes the optimization hints that a manager may take. If the \a fetchHint is the default constructed hint, all existing details, relationships and action preferences in the matching contact will be returned. If a client makes changes to an contact which has been retrieved with a fetch hint, they should save it back using a partial save, masked by the same set of detail names in order to avoid information loss. \sa QContactFetchHint */ QList QContactManager::contacts(const QContactFilter &filter, const QList& sortOrders, const QContactFetchHint &fetchHint) const { QContactManagerSyncOpErrorHolder h(this); return d->m_engine->contacts(filter, sortOrders, fetchHint, &h.error); } /*! Returns the contact in the database identified by \a contactId. If the contact does not exist, an empty, default constructed QContact will be returned, and the error returned by \l error() will be \c QContactManager::DoesNotExistError. The \a fetchHint parameter describes the optimization hints that a manager may take. If the \a fetchHint is the default constructed hint, all existing details, relationships and action preferences in the matching contact will be returned. If a client makes changes to an contact which has been retrieved with a fetch hint, they should save it back using a partial save, masked by the same set of detail names in order to avoid information loss. \sa QContactFetchHint */ QContact QContactManager::contact(const QContactId &contactId, const QContactFetchHint &fetchHint) const { QContactManagerSyncOpErrorHolder h(this); return d->m_engine->contact(contactId, fetchHint, &h.error); } /*! Returns a list of contacts given a list of ids (\a contactIds). Returns the list of contacts with the ids given by \a contactIds. There is a one-to-one correspondence between the returned contacts and the supplied \a contactIds. If there is an invalid id in \a contactIds, then an empty QContact will take its place in the returned list. The \a errorMap parameter can be supplied to store per-input errors in. In all cases, calling \l errorMap() will return the per-input errors for the latest batch function. The \a fetchHint parameter describes the optimization hints that a manager may take. If the \a fetchHint is the default constructed hint, all existing details, relationships and action preferences in the matching contact will be returned. If a client makes changes to an contact which has been retrieved with a fetch hint, they should save it back using a partial save, masked by the same set of detail names in order to avoid information loss. \sa QContactFetchHint */ QList QContactManager::contacts(const QList &contactIds, const QContactFetchHint &fetchHint, QMap *errorMap) const { QContactManagerSyncOpErrorHolder h(this, errorMap); return d->m_engine->contacts(contactIds, fetchHint, &h.errorMap, &h.error); } /*! Adds the given \a contact to the database if \a contact has the (default constructed) null id. If the manager URI of the id of the \a contact is neither empty nor equal to the URI of this manager, or id of the \a contact is not null but does not exist in the manager, the operation will fail and calling error() will return \c QContactManager::DoesNotExistError. Alternatively, the function will update the existing contact in the database if \a contact has a id which is not null and currently exists in the database. If the \a contact contains one or more details whose types are not supported by the used engine, the operation will fail and calling error() will return \c QContactManager::UnsupportedError. If the \a contact has had its relationships reordered, the manager will check to make sure that every relationship that the contact is currently involved in is included in the reordered list, and that no relationships which either do not involve the contact, or have not been saved in the manager are included in the list. If these conditions are not met, the function will return \c false and calling error() will return \c QContactManager::InvalidRelationshipError. Returns false on failure, or true on success. On successful save of an contact with a null id, its id will be set to a new, non-null id. The manager is not required to fetch updated details of the contact on save, and as such, clients should fetch a contact if they want the most up-to-date information by calling \l QContactManager::contact(). \sa managerUri() */ bool QContactManager::saveContact(QContact *contact) { QContactManagerSyncOpErrorHolder h(this); if (contact) { return d->m_engine->saveContact(contact, &h.error); } else { h.error = QContactManager::BadArgumentError; return false; } } /*! Remove the contact identified by \a contactId from the database, and also removes any relationships in which the contact was involved. Returns true if the contact was removed successfully, otherwise returns false. */ bool QContactManager::removeContact(const QContactId &contactId) { QContactManagerSyncOpErrorHolder h(this); return d->m_engine->removeContact(contactId, &h.error); } /*! Adds the list of contacts given by \a contacts list to the database. Returns true if the contacts were saved successfully, otherwise false. The \a errorMap parameter can be supplied to store per-input errors in. In all cases, calling \l errorMap() will return the per-input errors for the latest batch function. The \l QContactManager::error() function will only return \c QContactManager::NoError if all contacts were saved successfully. For each newly saved contact that was successful, the id of the contact in the \a contacts list will be updated with the new value. \sa QContactManager::saveContact() */ bool QContactManager::saveContacts(QList *contacts, QMap *errorMap) { QContactManagerSyncOpErrorHolder h(this, errorMap); if (contacts) { return d->m_engine->saveContacts(contacts, &h.errorMap, &h.error); } else { h.error = QContactManager::BadArgumentError; return false; } } /*! Adds the list of contacts given by \a contacts list to the database. Returns true if the contacts were saved successfully, otherwise false. This function accepts a \a typeMask, which specifies which details of the contacts should be updated. Details with types not included in the typeMask will not be updated or added. The \a errorMap parameter can be supplied to store per-input errors in. In all cases, calling \l errorMap() will return the per-input errors for the latest batch function. The \l QContactManager::error() function will only return \c QContactManager::NoError if all contacts were saved successfully. For each newly saved contact that was successful, the id of the contact in the \a contacts list will be updated with the new value. \sa QContactManager::saveContact() */ bool QContactManager::saveContacts(QList *contacts, const QList &typeMask, QMap *errorMap) { QContactManagerSyncOpErrorHolder h(this, errorMap); if (contacts) { return d->m_engine->saveContacts(contacts, typeMask, &h.errorMap, &h.error); } else { h.error = QContactManager::BadArgumentError; return false; } } /*! Remove every contact whose id is contained in the list of contacts ids \a contactIds. Returns true if all contacts were removed successfully, otherwise false. Any contact that was removed successfully will have the relationships in which it was involved removed also. The \a errorMap parameter can be supplied to store per-input errors in. In all cases, calling \l errorMap() will return the per-input errors for the latest batch function. The \l QContactManager::error() function will only return \c QContactManager::NoError if all contacts were removed successfully. If the given list of contact ids \a contactIds is empty, the function will return false and calling error() will return \c QContactManager::BadArgumentError. If the list is non-empty and contains ids which do not identify a valid contact in the manager, the function will remove any contacts which are identified by ids in the \a contactIds list, insert \c QContactManager::DoesNotExist entries into the \a errorMap for the indices of invalid ids in the \a contactIds list, return false, and set the overall operation error to \c QContactManager::DoesNotExistError. \sa QContactManager::removeContact() */ bool QContactManager::removeContacts(const QList &contactIds, QMap *errorMap) { QContactManagerSyncOpErrorHolder h(this, errorMap); if (!contactIds.isEmpty()) { return d->m_engine->removeContacts(contactIds, &h.errorMap, &h.error); } else { h.error = QContactManager::BadArgumentError; return false; } } /*! Sets the id of the "self" contact to the given \a contactId. Returns true if the "self" contact id was set successfully. If the given \a contactId does not identify a contact stored in this manager, the error will be set to \c QContactManager::DoesNotExistError and the function will return false; if the backend does not support the concept of a "self" contact then the error will be set to \c QContactManager::NotSupportedError and the function will return false. */ bool QContactManager::setSelfContactId(const QContactId &contactId) { QContactManagerSyncOpErrorHolder h(this); return d->m_engine->setSelfContactId(contactId, &h.error); } /*! Returns the id of the "self" contact which has previously been set. If no "self" contact has been set, or if the self contact was removed from the manager after being set, or if the backend does not support the concept of a "self" contact, an invalid id will be returned and the error will be set to \c QContactManager::DoesNotExistError. */ QContactId QContactManager::selfContactId() const { QContactManagerSyncOpErrorHolder h(this); return d->m_engine->selfContactId(&h.error); } /*! Returns a list of relationships in which the contact \a participant participates in the given \a role. If \a participant is empty, \a role is ignored and all relationships are returned. */ QList QContactManager::relationships(const QContact &participant, QContactRelationship::Role role) const { QContactManagerSyncOpErrorHolder h(this); return d->m_engine->relationships(QString(), participant, role, &h.error); } /*! Returns a list of relationships of the given \a relationshipType in which the contact identified by the given \a participant participates in the given \a role. If \a participant is empty, \a role is ignored and all relationships of the given \a relationshipType are returned. If \a relationshipType is empty, relationships of any type are returned. */ QList QContactManager::relationships(const QString &relationshipType, const QContact &participant, QContactRelationship::Role role) const { QContactManagerSyncOpErrorHolder h(this); return d->m_engine->relationships(relationshipType, participant, role, &h.error); } /*! Saves the given \a relationship in the database. If the relationship already exists in the database, this function will return \c false and the error will be set to \c QContactManager::AlreadyExistsError. If the relationship is saved successfully, this function will return \c true and error will be set to \c QContactManager::NoError. Note that relationships cannot be updated directly using this function; in order to update a relationship, you must remove the old relationship, make the required modifications, and then save it. The given relationship is invalid if it is circular (the first contact is the second contact), or if it references a non-existent local contact (either the first or second contact). If the given \a relationship is invalid, the function will return \c false and the error will be set to \c QContactManager::InvalidRelationshipError. If the given \a relationship could not be saved in the database (due to backend limitations) the function will return \c false and error will be set to \c QContactManager::NotSupportedError. */ bool QContactManager::saveRelationship(QContactRelationship *relationship) { QContactManagerSyncOpErrorHolder h(this); if (relationship) { return d->m_engine->saveRelationship(relationship, &h.error); } else { h.error = QContactManager::BadArgumentError; return false; } } /*! Saves the given \a relationships in the database and returns true if the operation was successful. The \a errorMap parameter can be supplied to store per-input errors in. In all cases, calling \l errorMap() will return the per-input errors for the latest batch function. */ bool QContactManager::saveRelationships(QList *relationships, QMap *errorMap) { QContactManagerSyncOpErrorHolder h(this, errorMap); if (relationships) { return d->m_engine->saveRelationships(relationships, &h.errorMap, &h.error); } else { h.error = QContactManager::BadArgumentError; return false; } } /*! Removes the given \a relationship from the manager. If the relationship exists in the manager, the relationship will be removed, the error will be set to \c QContactManager::NoError and this function will return true. If no such relationship exists in the manager, the error will be set to \c QContactManager::DoesNotExistError and this function will return false. */ bool QContactManager::removeRelationship(const QContactRelationship &relationship) { QContactManagerSyncOpErrorHolder h(this); return d->m_engine->removeRelationship(relationship, &h.error); } /*! Removes the given \a relationships from the database and returns true if the operation was successful. The \a errorMap parameter can be supplied to store per-input errors in. In all cases, calling \l errorMap() will return the per-input errors for the latest batch function. */ bool QContactManager::removeRelationships(const QList &relationships, QMap *errorMap) { QContactManagerSyncOpErrorHolder h(this, errorMap); return d->m_engine->removeRelationships(relationships, &h.errorMap, &h.error); } /*! Returns the list of data types supported by the manager */ QList QContactManager::supportedDataTypes() const { return d->m_engine->supportedDataTypes(); } /*! Returns true if the given \a filter is supported natively by the manager, and false if the filter behaviour would be emulated. Note: In some cases, the behaviour of an unsupported filter cannot be emulated. For example, a filter that requests contacts that have changed since a given time depends on having that information available. In these cases, the filter will fail. */ bool QContactManager::isFilterSupported(const QContactFilter &filter) const { return d->m_engine->isFilterSupported(filter); } /*! Returns true if the manager supports the relationship type specified in \a relationshipType for contacts whose type is the given \a contactType. Note that some managers may support the relationship type for a contact in a limited manner (for example, only as the first contact in the relationship, or only as the second contact in the relationship). In this case, it will still return true. It will only return false if the relationship is entirely unsupported for the given type of contact. */ bool QContactManager::isRelationshipTypeSupported(const QString& relationshipType, QContactType::TypeValues contactType) const { return d->m_engine->isRelationshipTypeSupported(relationshipType, contactType); } /*! Returns the list of contact types which are supported by this manager. This is a convenience function, equivalent to retrieving the allowable values for the \c QContactType::FieldType field of the QContactType detail which is valid in this manager. */ QList QContactManager::supportedContactTypes() const { return d->m_engine->supportedContactTypes(); } /*! Returns the list of contact detail types which are supported by this manager. The returned list can be used by clients to identify incompatibilities between contact objects to be saved and the actual subset of detail types supported by this manager. */ QList QContactManager::supportedContactDetailTypes() const { return d->m_engine->supportedContactDetailTypes(); } /*! Returns the engine backend implementation version number */ int QContactManager::managerVersion() const { return d->m_engine->managerVersion(); } /*! Returns the manager name for this QContactManager */ QString QContactManager::managerName() const { return d->m_engine->managerName(); } /*! Return the parameters relevant to the creation of this QContactManager */ QMap QContactManager::managerParameters() const { QMap params = d->m_engine->managerParameters(); params.remove(QString::fromLatin1(QTCONTACTS_VERSION_NAME)); params.remove(QString::fromLatin1(QTCONTACTS_IMPLEMENTATION_VERSION_NAME)); return params; } /*! Return the uri describing this QContactManager, consisting of the manager name and any parameters. */ QString QContactManager::managerUri() const { return d->m_engine->managerUri(); } /*! \internal Returns the signal that corresponds to \a proxySignal in the meta-object of the \a sourceObject. */ static QMetaMethod proxyToSourceSignal(const QMetaMethod &proxySignal, QObject *sourceObject) { if (!proxySignal.isValid()) return proxySignal; Q_ASSERT(proxySignal.methodType() == QMetaMethod::Signal); Q_ASSERT(sourceObject != 0); const QMetaObject *sourceMeta = sourceObject->metaObject(); int sourceIndex = sourceMeta->indexOfSignal(proxySignal.methodSignature()); Q_ASSERT(sourceIndex != -1); return sourceMeta->method(sourceIndex); } /*! \internal When someone connects to this manager, connect the corresponding signal from the engine, if we haven't before. If we have, just increment a count. This allows lazy evaluation on the engine side (e.g. setting up dbus watchers) and prevents unnecessary work. */ void QContactManager::connectNotify(const QMetaMethod &signal) { /* For most signals we just connect from the engine to ourselves, since we just proxy, but we should connect only once */ QMetaMethod sourceSignal = proxyToSourceSignal(signal, d->m_engine); connect(d->m_engine, sourceSignal, this, signal, Qt::UniqueConnection); } /*! \internal When someone disconnects, disconnect from the engine too if there are no more users of that signal. */ void QContactManager::disconnectNotify(const QMetaMethod &signal) { if (!isSignalConnected(signal)) { QMetaMethod sourceSignal = proxyToSourceSignal(signal, d->m_engine); disconnect(d->m_engine, sourceSignal, this, signal); } } #include "moc_qcontactmanager.cpp" QT_END_NAMESPACE_CONTACTS src/contacts/qcontactmanager.h000066400000000000000000000216631233466112000170110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTMANAGER_H #define QCONTACTMANAGER_H #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactAction; class QContactFilter; class QContactManagerData; class Q_CONTACTS_EXPORT QContactManager : public QObject { Q_OBJECT public: #ifdef Q_QDOC // qdoc's parser fails to recognise the default map argument explicit QContactManager(const QString& managerName = QString(), const QMap& parameters = 0, QObject* parent = 0); QContactManager(const QString& managerName, int implementationVersion, const QMap& parameters = 0, QObject* parent = 0); #else explicit QContactManager(const QString& managerName = QString(), const QMap& parameters = (QMap()), QObject* parent = 0); QContactManager(const QString& managerName, int implementationVersion, const QMap& parameters = (QMap()), QObject* parent = 0); #endif explicit QContactManager(QObject* parent); inline static const QString ParameterSignalSources() {return QStringLiteral("SignalSources");}; inline static const QString ParameterSignalDefinitions() {return QStringLiteral("SignalDefinitions");}; inline static const QString ParameterValueOnlyOtherProcesses() {return QStringLiteral("OnlyOtherProcesses");}; inline static const QString ParameterValueOnlyOtherManagers() {return QStringLiteral("OnlyOtherManagers");}; static QContactManager* fromUri(const QString& uri, QObject* parent = 0); ~QContactManager(); // dtor QString managerName() const; // e.g. "memory" QMap managerParameters() const; // e.g. "filename=private.db" QString managerUri() const; // managerName + managerParameters int managerVersion() const; static bool parseUri(const QString& uri, QString* managerName, QMap* params); static QString buildUri(const QString& managerName, const QMap& params, int implementationVersion = -1); enum Error { NoError = 0, DoesNotExistError, AlreadyExistsError, InvalidDetailError, InvalidRelationshipError, LockedError, DetailAccessError, PermissionsError, OutOfMemoryError, NotSupportedError, BadArgumentError, UnspecifiedError, VersionMismatchError, LimitReachedError, InvalidContactTypeError, TimeoutError, InvalidStorageLocationError, MissingPlatformRequirementsError }; /* Error reporting */ QContactManager::Error error() const; QMap errorMap() const; /* Contacts - Accessors and Mutators */ QList contactIds(const QList& sortOrders = QList()) const; QList contactIds(const QContactFilter& filter, const QList& sortOrders = QList()) const; QList contacts(const QList& sortOrders = QList(), const QContactFetchHint& fetchHint = QContactFetchHint()) const; QList contacts(const QContactFilter& filter, const QList& sortOrders = QList(), const QContactFetchHint& fetchHint = QContactFetchHint()) const; QList contacts(const QList& contactIds, const QContactFetchHint& fetchHint = QContactFetchHint(), QMap* errorMap = 0) const; QContact contact(const QContactId& contactId, const QContactFetchHint& fetchHint = QContactFetchHint()) const; // retrieve a contact bool saveContact(QContact* contact); // note: MODIFIES contact (sets the contactId) bool removeContact(const QContactId& contactId); // remove the contact from the persistent store bool saveContacts(QList* contacts, QMap* errorMap = 0); // batch API - save. bool saveContacts(QList* contacts, const QList& typeMask, QMap* errorMap = 0); // Partial save bool removeContacts(const QList& contactIds, QMap* errorMap = 0); // batch API - remove. /* "Self" contact id (MyCard) */ bool setSelfContactId(const QContactId& contactId); QContactId selfContactId() const; /* Relationships */ QList relationships(const QContact& participant, QContactRelationship::Role role = QContactRelationship::Either) const; QList relationships(const QString& relationshipType = QString(), const QContact& participant = QContact(), QContactRelationship::Role role = QContactRelationship::Either) const; bool saveRelationship(QContactRelationship* relationship); bool saveRelationships(QList* relationships, QMap* errorMap = 0); bool removeRelationship(const QContactRelationship& relationship); bool removeRelationships(const QList& relationships, QMap* errorMap = 0); /* Capabilities reporting */ bool isRelationshipTypeSupported(const QString& relationshipType, QContactType::TypeValues contactType = QContactType::TypeContact) const; QList supportedDataTypes() const; bool isFilterSupported(const QContactFilter& filter) const; QList supportedContactTypes() const; QList supportedContactDetailTypes() const; /* return a list of available backends for which a QContactManager can be constructed. */ static QStringList availableManagers(); Q_SIGNALS: void dataChanged(); void contactsAdded(const QList& contactIds); void contactsChanged(const QList& contactIds); void contactsRemoved(const QList& contactIds); void relationshipsAdded(const QList& affectedContactIds); void relationshipsRemoved(const QList& affectedContactIds); void selfContactIdChanged(const QContactId& oldId, const QContactId& newId); // need both? or just new? protected: void connectNotify(const QMetaMethod &signal); void disconnectNotify(const QMetaMethod &signal); private: friend class QContactManagerData; void createEngine(const QString& managerName, const QMap& parameters); Q_DISABLE_COPY(QContactManager) Q_PRIVATE_SLOT(d, void _q_contactsUpdated(const QList& ids)) Q_PRIVATE_SLOT(d, void _q_contactsDeleted(const QList& ids)) // private data pointer QContactManagerData* d; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTMANAGER_H src/contacts/qcontactmanager_p.cpp000066400000000000000000000321531233466112000176570ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactmanager.h" #include "qcontactmanager_p.h" #if !defined(QT_NO_DEBUG) #include #endif #include #include #include #include #include #include #include "qcontact_p.h" #include "qcontactaction.h" #include "qcontactactiondescriptor.h" #include "qcontactmanagerenginefactory.h" #include "qcontactinvalidbackend_p.h" #include "qcontactspluginsearch_p.h" QT_BEGIN_NAMESPACE_CONTACTS /* Shared QContactManager stuff here, default engine stuff below */ QHash QContactManagerData::m_engines; QSet QContactManagerData::m_aliveEngines; QList QContactManagerData::m_actionManagers; bool QContactManagerData::m_discoveredStatic; QList QContactManagerData::m_pluginPaths; QList QContactManagerData::m_metaData; QStringList QContactManagerData::m_managerNames; #ifndef QT_NO_LIBRARY Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, (QT_CONTACT_MANAGER_ENGINE_INTERFACE, QLatin1String("/contacts"))) #endif static void qContactsCleanEngines() { // This is complicated by needing to remove any engines before we unload factories // guard pointers as one engine could be parent of another manager and cause doubledelete QList > aliveManagers; foreach(QContactManager* manager, QContactManagerData::m_aliveEngines) { aliveManagers << QPointer(manager); } foreach(QPointer manager, aliveManagers) { if (!manager) { // deleting engine of one manager, could cause deleting next manager in list (aggregation case) continue; } // We don't delete the managers here, we just kill their engines // and replace it with an invalid engine (for safety :/) QContactManagerData* d = QContactManagerData::managerData(manager.data()); delete d->m_engine; d->m_engine = new QContactInvalidEngine(); } QList factories = QContactManagerData::m_engines.values(); for (int i=0; i < factories.count(); i++) { delete factories.at(i); } QContactManagerData::m_engines.clear(); QContactManagerData::m_actionManagers.clear(); QContactManagerData::m_aliveEngines.clear(); QContactManagerData::m_pluginPaths.clear(); QContactManagerData::m_metaData.clear(); QContactManagerData::m_managerNames.clear(); } static int parameterValue(const QMap ¶meters, const char *key, int defaultValue) { if (parameters.contains(QString::fromLatin1(key))) { bool ok; int version = parameters.value(QString::fromLatin1(key)).toInt(&ok); if (ok) return version; } return defaultValue; } void QContactManagerData::createEngine(const QString &managerName, const QMap ¶meters) { m_engine = 0; QString builtManagerName = managerName.isEmpty() ? QContactManager::availableManagers().value(0) : managerName; int implementationVersion = parameterValue(parameters, QTCONTACTS_IMPLEMENTATION_VERSION_NAME, -1); bool found = false; bool loadedDynamic = false; /* First check static factories */ loadStaticFactories(); /* See if we got a fast hit */ QList factories = m_engines.values(builtManagerName); m_lastError = QContactManager::NoError; while (!found) { foreach (QContactManagerEngineFactory* f, factories) { QList versions = f->supportedImplementationVersions(); if (implementationVersion == -1 ||//no given implementation version required versions.isEmpty() || //the manager engine factory does not report any version versions.contains(implementationVersion)) { m_engine = f->engine(parameters, &m_lastError); if (!m_engine) { qWarning("Creation of %s engine failed.", qPrintable(managerName)); } else { found = true; break; } } } // Break if found or if this is the second time through if (loadedDynamic || found) break; // otherwise load dynamic factories and reloop loadFactoriesMetadata(); if (!m_metaData.isEmpty()) { QFactoryLoader *l = loader(); foreach (const QJsonObject &metaDataObject, m_metaData) { if (metaDataObject.value(QStringLiteral("MetaData")).toObject().value(QStringLiteral("Keys")).toArray().at(0).toString() == builtManagerName) { QContactManagerEngineFactory *managerFactory = qobject_cast(l->instance(m_metaData.indexOf(metaDataObject))); QContactActionManagerPlugin *actionFactory = qobject_cast(l->instance(m_metaData.indexOf(metaDataObject))); m_engines.insertMulti(builtManagerName, managerFactory); m_actionManagers.append(actionFactory); } } } factories = m_engines.values(builtManagerName); loadedDynamic = true; } // XXX remove this // the engine factory could lie to us, so check the real implementation version if (m_engine && (implementationVersion != -1 && m_engine->managerVersion() != implementationVersion)) { m_lastError = QContactManager::VersionMismatchError; m_engine = 0; } if (!m_engine) { if (m_lastError == QContactManager::NoError) m_lastError = QContactManager::DoesNotExistError; m_engine = new QContactInvalidEngine(); } } /* Caller takes ownership of the id */ QContactEngineId* QContactManagerData::createEngineContactId(const QString &managerName, const QMap& parameters, const QString &engineIdString) { loadFactoriesMetadata(); QContactManagerEngineFactory *engineFactory = m_engines.value(managerName); return engineFactory ? engineFactory->createContactEngineId(parameters, engineIdString) : NULL; } void QContactManagerData::loadStaticFactories() { if (!m_discoveredStatic) { #if !defined QT_NO_DEBUG const bool showDebug = qgetenv("QT_DEBUG_PLUGINS").toInt() > 0; #endif m_discoveredStatic = true; /* Clean stuff up at the end */ qAddPostRoutine(qContactsCleanEngines); /* Loop over all the static plugins */ QObjectList staticPlugins = QPluginLoader::staticInstances(); for (int i=0; i < staticPlugins.count(); i++ ){ QContactManagerEngineFactory *f = qobject_cast(staticPlugins.at(i)); if (f) { QString name = f->managerName(); #if !defined QT_NO_DEBUG if (showDebug) qDebug() << "Static: found an engine plugin" << f << "with name" << name; #endif if (name != QStringLiteral("invalid") && !name.isEmpty()) { // we also need to ensure that we haven't already loaded this factory. if (m_engines.keys().contains(name)) { qWarning("Static contacts plugin %s has the same name as a currently loaded plugin; ignored", qPrintable(name)); } else { m_engines.insertMulti(name, f); } } else { qWarning("Static contacts plugin with reserved name %s ignored", qPrintable(name)); } } } } } /* Plugin loader */ void QContactManagerData::loadFactoriesMetadata() { #if !defined QT_NO_DEBUG const bool showDebug = qgetenv("QT_DEBUG_PLUGINS").toInt() > 0; #endif // Always do this.. loadStaticFactories(); QFactoryLoader *l = loader(); QList metaData = l->metaData(); m_metaData = metaData; if (m_metaData != m_pluginPaths) { m_pluginPaths = m_metaData; QString currentManagerName; /* Now discover the dynamic plugins */ foreach (const QJsonObject &metaDataObject, metaData) { currentManagerName = metaDataObject.value(QStringLiteral("MetaData")).toObject().value(QStringLiteral("Keys")).toArray().at(0).toString(); #if !defined QT_NO_DEBUG if (showDebug) qDebug() << "Loading metadata of plugin " << currentManagerName; #endif if (!currentManagerName.isEmpty()) m_managerNames << currentManagerName; } } } // Observer stuff void QContactManagerData::registerObserver(QContactManager *manager, QContactObserver *observer) { if (!manager) return; QContactManagerData* d = QContactManagerData::get(manager); d->m_observerForContact.insert(observer->contactId(), observer); // If this is the first observer, connect to the engine too if (d->m_observerForContact.size() == 1) { // This takes advantage of the manager connectNotify code QObject::connect(manager, SIGNAL(contactsChanged(QList)), manager, SLOT(_q_contactsUpdated(QList))); QObject::connect(manager, SIGNAL(contactsRemoved(QList)), manager, SLOT(_q_contactsDeleted(QList))); } } void QContactManagerData::unregisterObserver(QContactManager *manager, QContactObserver *observer) { Q_ASSERT(manager); QContactManagerData* d = QContactManagerData::get(manager); QContactId key = d->m_observerForContact.key(observer); if (!key.isNull()) { d->m_observerForContact.remove(key, observer); // If there are now no more observers, disconnect from the engine if (d->m_observerForContact.size() == 0) { // This takes advantage of the manager disconnectNotify code QObject::disconnect(manager, SIGNAL(contactsChanged(QList)), manager, SLOT(_q_contactsUpdated(QList))); QObject::disconnect(manager, SIGNAL(contactsRemoved(QList)), manager, SLOT(_q_contactsDeleted(QList))); } } } void QContactManagerData::_q_contactsUpdated(const QList &ids) { foreach (const QContactId &id, ids) { QList observers = m_observerForContact.values(id); foreach (QContactObserver* observer, observers) { QMetaObject::invokeMethod(observer, "contactChanged"); } } } void QContactManagerData::_q_contactsDeleted(const QList &ids) { foreach (const QContactId &id, ids) { QList observers = m_observerForContact.values(id); foreach (QContactObserver* observer, observers) { QMetaObject::invokeMethod(observer, "contactRemoved"); } } } // trampolines for private classes QContactManagerData* QContactManagerData::get(const QContactManager *manager) { return manager->d; } QContactManagerEngine* QContactManagerData::engine(const QContactManager *manager) { if (manager) return manager->d->m_engine; return 0; } QT_END_NAMESPACE_CONTACTS src/contacts/qcontactmanager_p.h000066400000000000000000000127171233466112000173300ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTMANAGER_P_H #define QCONTACTMANAGER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactManagerEngineFactory; /* Data and stuff that is shared amongst all backends */ class QContactManagerData { public: QContactManagerData() : m_engine(0), m_lastError(QContactManager::NoError) { } ~QContactManagerData() { delete m_engine; } void createEngine(const QString &managerName, const QMap ¶meters); static QContactManagerData* get(const QContactManager *manager); static QContactManagerEngine* engine(const QContactManager *manager); static QContactEngineId* createEngineContactId(const QString &managerName, const QMap ¶meters, const QString &engineIdString); QContactManagerEngine* m_engine; QContactManager::Error m_lastError; QMap m_lastErrorMap; /* Manager plugins */ static QHash m_engines; static QSet m_aliveEngines; static QContactManagerData* managerData(QContactManager *manager) {return manager->d;} static QContactManagerData* managerData(const QContactManager *manager) {return manager->d;} // laziness to avoid const_cast static QList m_actionManagers; static bool m_discoveredStatic; static QList m_pluginPaths; static QList m_metaData; static QStringList m_managerNames; static void loadFactoriesMetadata(); static void loadStaticFactories(); // Observer stuff static void registerObserver(QContactManager *m, QContactObserver *observer); static void unregisterObserver(QContactManager *m, QContactObserver *observer); void _q_contactsUpdated(const QList &ids); void _q_contactsDeleted(const QList &ids); QMultiHash m_observerForContact; private: Q_DISABLE_COPY(QContactManagerData) }; /* Helper to hold the error state of a synchronous operation - when destructed, updates the manager's last error variables to the result of this operation. This means that during callbacks the error state can't be modified behind the engines back. and it's more conceptually correct. */ class QContactManagerSyncOpErrorHolder { public: QContactManagerSyncOpErrorHolder(const QContactManager *m, QMap *pUserError = 0) : error(QContactManager::NoError), data(QContactManagerData::managerData(m)), userError(pUserError) { } ~QContactManagerSyncOpErrorHolder() { data->m_lastError = error; data->m_lastErrorMap = errorMap; if (userError) *userError = errorMap; } QContactManager::Error error; QContactManagerData* data; QMap errorMap; QMap *userError; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTMANAGER_P_H src/contacts/qcontactmanagerengine.cpp000066400000000000000000002563141233466112000205350ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactmanagerengine.h" #include #include #include #include "qcontact_p.h" #include "qcontactdetail_p.h" #include "qcontactdetails.h" #include "qcontactfilters.h" #include "qcontactabstractrequest_p.h" #include "qcontactaction.h" #include "qcontactactiondescriptor.h" #include "qcontactactionmanager_p.h" #include "qcontactrequests_p.h" #include "qcontactsortorder.h" QT_BEGIN_NAMESPACE_CONTACTS static bool validateActionFilter(const QContactFilter& filter); /*! \class QContactManagerEngine \brief The QContactManagerEngine class provides the interface for implementations of the contact manager backend functionality. \inmodule QtContacts \ingroup contacts-backends Instances of this class are usually provided by a \l QContactManagerEngineFactory, which is loaded from a plugin. The default implementation of this interface provides a basic level of functionality for some functions so that specific engines can simply implement the functionality that is supported by the specific contacts engine that is being adapted. More information on writing a contacts engine plugin is available in the \l{Qt Contacts Manager Engines} documentation. \sa QContactManager, QContactManagerEngineFactory */ /*! \fn QContactManagerEngine::QContactManagerEngine() A default, empty constructor. */ /*! \fn QContactManagerEngine::dataChanged() This signal is emitted some time after changes occur to the data managed by this engine, and the engine is unable to determine which changes occurred, or if the engine considers the changes to be radical enough to require clients to reload all data. If this signal is emitted, no other signals may be emitted for the associated changes. As it is possible that other processes (or other devices) may have caused the changes, the timing can not be determined. \sa contactsAdded(), contactsChanged(), contactsRemoved() */ /*! \fn QContactManagerEngine::contactsAdded(const QList& contactIds); This signal is emitted some time after a set of contacts has been added to this engine where the \l dataChanged() signal was not emitted for those changes. As it is possible that other processes (or other devices) may have added the contacts, the timing cannot be determined. The list of ids of contacts added is given by \a contactIds. There may be one or more ids in the list. \sa dataChanged() */ /*! \fn QContactManagerEngine::contactsChanged(const QList& contactIds); This signal is emitted some time after a set of contacts has been modified in this engine where the \l dataChanged() signal was not emitted for those changes. As it is possible that other processes (or other devices) may have modified the contacts, the timing cannot be determined. The list of ids of changed contacts is given by \a contactIds. There may be one or more ids in the list. \sa dataChanged() */ /*! \fn QContactManagerEngine::contactsRemoved(const QList& contactIds); This signal is emitted some time after a set of contacts has been removed from this engine where the \l dataChanged() signal was not emitted for those changes. As it is possible that other processes (or other devices) may have removed the contacts, the timing cannot be determined. The list of ids of removed contacts is given by \a contactIds. There may be one or more ids in the list. \sa dataChanged() */ /*! \fn QContactManagerEngine::relationshipsAdded(const QList& affectedContactIds); This signal is emitted some time after a set of contacts has been added to this engine where the \l dataChanged() signal was not emitted for those changes. As it is possible that other processes (or other devices) may have added the contacts, the timing cannot be determined. The list of ids of affected contacts is given by \a affectedContactIds. There may be one or more ids in the list. \sa dataChanged() */ /*! \fn QContactManagerEngine::relationshipsRemoved(const QList& affectedContactIds); This signal is emitted some time after a set of relationships has been removed from this engine where the \l dataChanged() signal was not emitted for those changes. As it is possible that other processes (or other devices) may have removed the relationships, the timing cannot be determined. The list of ids of affected contacts is given by \a affectedContactIds. There may be one or more ids in the list. \sa dataChanged() */ /*! \fn QContactManagerEngine::selfContactIdChanged(const QContactId& oldId, const QContactId& newId) This signal is emitted at some point after the id of the self-contact is changed from \a oldId to \a newId in the manager. If the \a newId is the invalid, null id, then the self contact was deleted or no self contact exists. This signal must not be emitted if the dataChanged() signal was previously emitted for this change. As it is possible that other processes (or other devices) may have removed or changed the self contact, the timing cannot be determined. \sa dataChanged() */ /*! Returns the manager name for this QContactManagerEngine */ QString QContactManagerEngine::managerName() const { return QString(QStringLiteral("base")); } /*! Returns the parameters with which this engine was constructed. Note that the engine may have discarded unused or invalid parameters at the time of construction, and these will not be returned. */ QMap QContactManagerEngine::managerParameters() const { return QMap(); // default implementation requires no parameters. } /*! Returns the unique URI of this manager, which is built from the manager name and the parameters used to construct it. */ QString QContactManagerEngine::managerUri() const { return QContactManager::buildUri(managerName(), managerParameters()); } /*! Returns a list of contact ids that match the given \a filter, sorted according to the given list of \a sortOrders. Depending on the backend, this filtering operation may involve retrieving all the contacts. Any error which occurs will be saved in \a error. */ QList QContactManagerEngine::contactIds(const QContactFilter& filter, const QList& sortOrders, QContactManager::Error* error) const { Q_UNUSED(filter); Q_UNUSED(sortOrders); *error = QContactManager::NotSupportedError; return QList(); } /*! Returns the list of contacts which match the given \a filter stored in the manager sorted according to the given list of \a sortOrders. Any operation error which occurs will be saved in \a error. The \a fetchHint parameter describes the optimization hints that a manager may take. If the \a fetchHint is the default constructed hint, all existing details, relationships and action preferences in the matching contacts will be returned. If a non-default fetch hint is supplied, and the client wishes to make changes to the contacts, they should ensure that only a detail type hint is supplied and that when saving it back, a type mask should be used which corresponds to the detail type hint. This is to ensure that no data is lost by overwriting an existing contact with a restricted version of it. \sa QContactFetchHint */ QList QContactManagerEngine::contacts(const QContactFilter& filter, const QList& sortOrders, const QContactFetchHint& fetchHint, QContactManager::Error* error) const { Q_UNUSED(filter); Q_UNUSED(sortOrders); Q_UNUSED(fetchHint); *error = QContactManager::NotSupportedError; return QList(); } /*! Returns the contact in the database identified by \a contactId. If the contact does not exist, an empty, default constructed QContact will be returned, and the \a error will be set to \c QContactManager::DoesNotExistError. Any operation error which occurs will be saved in \a error. The \a fetchHint parameter describes the optimization hints that a manager may take. If the \a fetchHint is the default constructed hint, all existing details, relationships and action preferences in the matching contact will be returned. If a client makes changes to an contact which has been retrieved with a fetch hint, they should save it back using a partial save, masked by the same set of detail names in order to avoid information loss. \sa QContactFetchHint */ QContact QContactManagerEngine::contact(const QContactId& contactId, const QContactFetchHint& fetchHint, QContactManager::Error* error) const { Q_UNUSED(contactId); Q_UNUSED(fetchHint); *error = QContactManager::NotSupportedError; return QContact(); } /*! Sets the id of the "self" contact to the given \a contactId. Returns true if the "self" contact id was set successfully. If the given \a contactId does not identify a contact stored in this manager, the \a error will be set to \c QContactManager::DoesNotExistError and the function will return false; if the backend does not support the concept of a "self" contact, the \a error will be set to \c QContactManager::NotSupportedError and the function will return false. */ bool QContactManagerEngine::setSelfContactId(const QContactId& contactId, QContactManager::Error* error) { Q_UNUSED(contactId); *error = QContactManager::NotSupportedError; return false; } /*! Returns the id of the "self" contact which has previously been set. If no "self" contact has been set, or if the self contact was removed from the manager after being set, or if the backend does not support the concept of a "self" contact, the null id will be returned and the \a error will be set to \c QContactManager::DoesNotExistError. */ QContactId QContactManagerEngine::selfContactId(QContactManager::Error* error) const { *error = QContactManager::DoesNotExistError; return QContactId(); } /*! Returns a list of relationships of the given \a relationshipType in which the contact identified by the given \a participant participates in the given \a role. If \a participant is empty, \a role is ignored and all relationships of the given \a relationshipType are returned. If \a relationshipType is empty, relationships of any type are returned. If no relationships of the given \a relationshipType in which the contact identified by the given \a participant is involved in the given \a role exists, \a error is set to QContactManager::DoesNotExistError. */ QList QContactManagerEngine::relationships(const QString& relationshipType, const QContact& participant, QContactRelationship::Role role, QContactManager::Error* error) const { Q_UNUSED(relationshipType); Q_UNUSED(participant); Q_UNUSED(role); *error = QContactManager::NotSupportedError; return QList(); } /*! Saves the given \a relationships in the database and returns true if the operation was successful. For any relationship which was unable to be saved, an entry into the \a errorMap will be created, with the key being the index into the input relationships list, and the value being the error which occurred for that index. The supplied \a errorMap parameter may be null, if the client does not desire detailed error information. If supplied, it will be empty upon entry to this function. The overall operation error will be saved in \a error. */ bool QContactManagerEngine::saveRelationships(QList* relationships, QMap* errorMap, QContactManager::Error* error) { Q_UNUSED(relationships); Q_UNUSED(errorMap); *error = QContactManager::NotSupportedError; return false; } /*! Saves the given \a relationship in the database. If the relationship already exists in the database, this function will return \c false and the \a error will be set to \c QContactManager::AlreadyExistsError. If the relationship is saved successfully, this function will return \c true and \a error will be set to \c QContactManager::NoError. Note that relationships cannot be updated directly using this function; in order to update a relationship, you must remove the old relationship, make the required modifications, and then save it. The given relationship is invalid if it is circular (the first contact is the second contact), or if it references a non-existent local contact (either the first or second contact). If the given \a relationship is invalid, the function will return \c false and the \a error will be set to \c QContactManager::InvalidRelationshipError. The default implementation of this function converts the argument into a call to saveRelationships. */ bool QContactManagerEngine::saveRelationship(QContactRelationship *relationship, QContactManager::Error *error) { // Convert to a list op if (relationship) { QList list; list.append(*relationship); QMap errors; bool ret = saveRelationships(&list, &errors, error); if (errors.count() > 0) *error = errors.begin().value(); *relationship = list.value(0); return ret; } else { *error = QContactManager::BadArgumentError; return false; } } /*! Removes the given \a relationship from the manager. If the relationship exists in the manager, the relationship will be removed, the \a error will be set to \c QContactManager::NoError and this function will return true. If no such relationship exists in the manager, the \a error will be set to \c QContactManager::DoesNotExistError and this function will return false. The default implementation of this function converts the argument into a call to removeRelationships */ bool QContactManagerEngine::removeRelationship(const QContactRelationship& relationship, QContactManager::Error* error) { // Convert to a list op QList list; list.append(relationship); QMap errors; bool ret = removeRelationships(list, &errors, error); if (errors.count() > 0) *error = errors.begin().value(); return ret; } /*! Removes the given \a relationships from the database and returns true if the operation was successful. For any relationship which was unable to be removed, an entry into the \a errorMap will be created, with the key being the index into the input relationships list, and the value being the error which occurred for that index. The supplied \a errorMap parameter may be null, if the client does not desire detailed error information. If supplied, it will be empty upon entry to this function. The overall operation error will be saved in \a error. */ bool QContactManagerEngine::removeRelationships(const QList& relationships, QMap* errorMap, QContactManager::Error* error) { Q_UNUSED(relationships); Q_UNUSED(errorMap); *error = QContactManager::NotSupportedError; return false; } /*! Given an input \a filter, returns the canonical version of the filter. Some of the following transformations may be applied: \list \li Any QContactActionFilters are transformed into the corresponding QContactFilters returned by matching actions \li Any QContactInvalidFilters contained in a union filter will be removed \li Any default QContactFilters contained in an intersection filter will be removed \li Any QContactIntersectionFilters with a QContactInvalidFilter contained will be replaced with a QContactInvalidFilter \li Any QContactUnionFilters with a default QContactFilter contained will be replaced with a default QContactFilter \li An empty QContactIntersectionFilter will be replaced with a QContactDefaultFilter \li An empty QContactUnionFilter will be replaced with a QContactInvalidFilter \li An empty QContactIdFilter will be replaced with a QContactInvalidFilter \li An intersection or union filter with a single entry will be replaced by that entry \li A QContactDetailFilter or QContactDetailRangeFilter with no detail type will be replaced with a QContactInvalidFilter \li A QContactDetailRangeFilter with no range specified will be converted to a QContactDetailFilter \endlist */ QContactFilter QContactManagerEngine::canonicalizedFilter(const QContactFilter &filter) { switch(filter.type()) { case QContactFilter::ActionFilter: { // Find any matching actions, and do a union filter on their filter objects QContactActionFilter af(filter); QList descriptors = QContactActionManager::instance()->actionDescriptors(af.actionName()); QList filters; for (int j = 0; j < descriptors.count(); j++) { // Action filters are not allowed to return action filters, at all // it's too annoying to check for recursion QContactFilter d = descriptors.at(j).contactFilter(); if (!validateActionFilter(d)) continue; filters.append(d); } if (filters.count() == 0) return QContactInvalidFilter(); if (filters.count() == 1) return filters.first(); QContactUnionFilter f; f.setFilters(filters); return canonicalizedFilter(f); } // unreachable case QContactFilter::IntersectionFilter: { QContactIntersectionFilter f(filter); QList filters = f.filters(); QList::iterator it = filters.begin(); // XXX in theory we can remove duplicates in a set filter while (it != filters.end()) { QContactFilter canon = canonicalizedFilter(*it); if (canon.type() == QContactFilter::DefaultFilter) { it = filters.erase(it); } else if (canon.type() == QContactFilter::InvalidFilter) { return QContactInvalidFilter(); } else { *it = canon; ++it; } } if (filters.count() == 0) return QContactFilter(); if (filters.count() == 1) return filters.first(); f.setFilters(filters); return f; } // unreachable case QContactFilter::UnionFilter: { QContactUnionFilter f(filter); QList filters = f.filters(); QList::iterator it = filters.begin(); // XXX in theory we can remove duplicates in a set filter while (it != filters.end()) { QContactFilter canon = canonicalizedFilter(*it); if (canon.type() == QContactFilter::InvalidFilter) { it = filters.erase(it); } else if (canon.type() == QContactFilter::DefaultFilter) { return QContactFilter(); } else { *it = canon; ++it; } } if (filters.count() == 0) return QContactInvalidFilter(); if (filters.count() == 1) return filters.first(); f.setFilters(filters); return f; } // unreachable case QContactFilter::IdFilter: { QContactIdFilter f(filter); if (f.ids().count() == 0) return QContactInvalidFilter(); } break; // fall through to return at end case QContactFilter::ContactDetailRangeFilter: { QContactDetailRangeFilter f(filter); if (f.detailType() == QContactDetail::TypeUndefined) return QContactInvalidFilter(); if (f.minValue() == f.maxValue() && f.rangeFlags() == (QContactDetailRangeFilter::ExcludeLower | QContactDetailRangeFilter::ExcludeUpper)) return QContactInvalidFilter(); if ((f.minValue().isNull() && f.maxValue().isNull()) || (f.minValue() == f.maxValue())) { QContactDetailFilter df; df.setDetailType(f.detailType(), f.detailField()); df.setMatchFlags(f.matchFlags()); df.setValue(f.minValue()); return df; } } break; // fall through to return at end case QContactFilter::ContactDetailFilter: { QContactDetailFilter f(filter); if (f.detailType() == QContactDetail::TypeUndefined) return QContactInvalidFilter(); } break; // fall through to return at end default: break; // fall through to return at end } return filter; } /*! Returns a whether the supplied \a filter can be implemented natively by this engine. If not, the base class implementation will emulate the functionality. */ bool QContactManagerEngine::isFilterSupported(const QContactFilter& filter) const { Q_UNUSED(filter); return false; } /*! Returns the list of data types supported by this engine. */ QList QContactManagerEngine::supportedDataTypes() const { return QList(); } /*! Returns true if the manager supports the relationship type specified in \a relationshipType for contacts whose type is the given \a contactType. Note that some managers may support the relationship type for a contact in a limited manner (for example, only as the first contact in the relationship, or only as the second contact in the relationship). In this case, it will still return true. It will only return false if the relationship is entirely unsupported for the given type of contact. */ bool QContactManagerEngine::isRelationshipTypeSupported(const QString& relationshipType, QContactType::TypeValues contactType) const { Q_UNUSED(relationshipType); Q_UNUSED(contactType); return false; } /*! Returns the list of contact types which are supported by this engine. This is a convenience function, equivalent to retrieving the allowable values for the \c QContactType::FieldType field of the QContactType detail which is valid in this engine. */ QList QContactManagerEngine::supportedContactTypes() const { QList list; list << QContactType::TypeContact << QContactType::TypeGroup; return list; } /*! \fn int QContactManagerEngine::managerVersion() const Returns the engine backend implementation version number */ /*! Returns the list of contact detail types which are supported by this engine. */ QList QContactManagerEngine::supportedContactDetailTypes() const { QList supportedDetails; supportedDetails << QContactAddress::Type << QContactAnniversary::Type << QContactAvatar::Type << QContactBirthday::Type << QContactDisplayLabel::Type << QContactEmailAddress::Type << QContactExtendedDetail::Type << QContactFamily::Type << QContactFavorite::Type << QContactGender::Type << QContactGeoLocation::Type << QContactGlobalPresence::Type << QContactGuid::Type << QContactHobby::Type << QContactName::Type << QContactNickname::Type << QContactNote::Type << QContactOnlineAccount::Type << QContactOrganization::Type << QContactPhoneNumber::Type << QContactPresence::Type << QContactRingtone::Type << QContactSyncTarget::Type << QContactTag::Type << QContactTimestamp::Type << QContactType::Type << QContactUrl::Type << QContactVersion::Type; return supportedDetails; } /*! Checks that the given contact \a contact does not have a type which is not supported. It also checks if the details of the given \a contact are valid or not. Note that this function is unable to ensure that all the details of \a contact are supported by a certain back-end. It also cannot check that the access constraints (such as CreateOnly and ReadOnly) are observed; backend specific code must be written if you wish to enforce these constraints. Returns true if the \a contact has a valid type, otherwise returns false. Any errors encountered during this operation should be stored to \a error. */ bool QContactManagerEngine::validateContact(const QContact &contact, QContactManager::Error *error) const { if (!supportedContactTypes().contains(contact.type())) { *error = QContactManager::InvalidContactTypeError; return false; } if ( (!contact.id().isNull()) && (contact.id().managerUri() != this->managerUri())) { *error = QContactManager::DoesNotExistError; return false; } QList contactDetailList = contact.details(); for (int i=0; i list; list.append(*contact); QMap errors; bool ret = saveContacts(&list, &errors, error); if (errors.count() > 0) *error = errors.begin().value(); *contact = list.value(0); return ret; } else { *error = QContactManager::BadArgumentError; return false; } } /*! Remove the contact identified by \a contactId from the database, and also removes any relationships in which the contact was involved. After the contact has been removed it can not be updated or re-created with the same contact id anymore. Returns true if the contact was removed successfully, otherwise returns false. Any error which occurs will be saved in \a error. The default implementation will convert this into a call to removeContacts. */ bool QContactManagerEngine::removeContact(const QContactId& contactId, QContactManager::Error* error) { // Convert to a list op QList list; list.append(contactId); QMap errors; bool ret = removeContacts(list, &errors, error); if (errors.count() > 0) *error = errors.begin().value(); return ret; } /*! Adds the list of contacts given by \a contacts list to the database. Returns true if the contacts were saved successfully, otherwise false. The manager might populate \a errorMap (the map of indices of the \a contacts list to the error which occurred when saving the contact at that index) for every index for which the contact could not be saved, if it is able. The supplied \a errorMap parameter may be null, if the client does not desire detailed error information. If supplied, it will be empty upon entry to this function. The \l QContactManager::error() function will only return \c QContactManager::NoError if all contacts were saved successfully. For each newly saved contact that was successful, the id of the contact in the \a contacts list will be updated with the new value. If a failure occurs when saving a new contact, the id will be cleared. Any errors encountered during this operation should be stored to \a error. \sa QContactManager::saveContact() */ bool QContactManagerEngine::saveContacts(QList* contacts, QMap* errorMap, QContactManager::Error* error) { Q_UNUSED(contacts); Q_UNUSED(errorMap); *error = QContactManager::NotSupportedError; return false; } /*! Remove every contact whose id is contained in the list of contacts ids \a contactIds. Returns true if all contacts were removed successfully, otherwise false. Any contact that was removed successfully will have the relationships in which it was involved removed also. The manager might populate \a errorMap (the map of indices of the \a contactIds list to the error which occurred when saving the contact at that index) for every index for which the contact could not be removed, if it is able. The supplied \a errorMap parameter may be null, if the client does not desire detailed error information. If supplied, it will be empty upon entry to this function. The \l QContactManager::error() function will only return \c QContactManager::NoError if all contacts were removed successfully. If the list contains ids which do not identify a valid contact in the manager, the function will remove any contacts which are identified by ids in the \a contactIds list, insert \c QContactManager::DoesNotExist entries into the \a errorMap for the indices of invalid ids in the \a contactIds list, return false, and set the overall operation error to \c QContactManager::DoesNotExistError. Any errors encountered during this operation should be stored to \a error. \sa QContactManager::removeContact() */ bool QContactManagerEngine::removeContacts(const QList& contactIds, QMap* errorMap, QContactManager::Error* error) { Q_UNUSED(contactIds); Q_UNUSED(errorMap); *error = QContactManager::NotSupportedError; return false; } /* This implements the string comparison behaviour required for compareVariant, amongst others */ static inline int compareStrings(const QString& left, const QString& right, Qt::CaseSensitivity sensitivity) { if (sensitivity == Qt::CaseSensitive) { return left.localeAwareCompare(right); } else { return left.toCaseFolded().localeAwareCompare(right.toCaseFolded()); } } /*! Compares \a first against \a second. If the types are strings (QVariant::String), the \a sensitivity argument controls case sensitivity when comparing. Also, when comparing strings, a locale aware comparison is used, and if the sensitivity is CaseSensitive, strings that are identical under a case insensitive sort are then sorted case sensitively within that context. For example: aaron Bob Aaron aAron Carol would sort as: aaron aAron Aaron Bob Carol Returns: <0 if \a first is less than \a second 0 if \a first is equal to \a second >0 if \a first is greater than \a second. The results are undefined if the variants are different types, or cannot be compared. */ int QContactManagerEngine::compareVariant(const QVariant& first, const QVariant& second, Qt::CaseSensitivity sensitivity) { switch(first.type()) { case QVariant::Int: { const int a = first.toInt(); const int b = second.toInt(); return (a < b) ? -1 : ((a == b) ? 0 : 1); } case QVariant::LongLong: { const qlonglong a = first.toLongLong(); const qlonglong b = second.toLongLong(); return (a < b) ? -1 : ((a == b) ? 0 : 1); } case QVariant::Bool: case QVariant::UInt: { const uint a = first.toUInt(); const uint b = second.toUInt(); return (a < b) ? -1 : ((a == b) ? 0 : 1); } case QVariant::ULongLong: { const qulonglong a = first.toULongLong(); const qulonglong b = second.toULongLong(); return (a < b) ? -1 : ((a == b) ? 0 : 1); } case QVariant::Char: // Needs to do proper string comparison case QVariant::String: return compareStrings(first.toString(), second.toString(), sensitivity); case QVariant::Double: { const double a = first.toDouble(); const double b = second.toDouble(); return (a < b) ? -1 : ((a == b) ? 0 : 1); } case QVariant::DateTime: { const QDateTime a = first.toDateTime(); const QDateTime b = second.toDateTime(); return (a < b) ? -1 : ((a == b) ? 0 : 1); } case QVariant::Date: { const QDate a = first.toDate(); const QDate b = second.toDate(); return (a < b) ? -1 : ((a == b) ? 0 : 1); } case QVariant::Time: { const QTime a = first.toTime(); const QTime b = second.toTime(); return (a < b) ? -1 : ((a == b) ? 0 : 1); } case QVariant::StringList: { // We don't actually sort on these, I hope // {} < {"aa"} < {"aa","bb"} < {"aa", "cc"} < {"bb"} int i; const QStringList a = first.toStringList(); const QStringList b = second.toStringList(); for (i = 0; i < a.size(); i++) { if (b.size() <= i) return 1; // first is longer int memberComp = compareStrings(a.at(i), b.at(i), sensitivity); if (memberComp != 0) return memberComp; // this element is the same, so loop again } // Either a.size() < b.size() and they are equal all // the way, or a == b if (a.size() < b.size()) return -1; // a is less than b; return 0; // they are equal } default: return 0; } } /*! Returns true if the supplied contact \a contact matches the supplied filter \a filter. This function will test each condition in the filter, possibly recursing. */ bool QContactManagerEngine::testFilter(const QContactFilter &filter, const QContact &contact) { switch(filter.type()) { case QContactFilter::InvalidFilter: return false; case QContactFilter::DefaultFilter: return true; case QContactFilter::IdFilter: { const QContactIdFilter idf(filter); if (idf.ids().contains(contact.id())) return true; } // Fall through to end break; case QContactFilter::ContactDetailFilter: { const QContactDetailFilter cdf(filter); if (cdf.detailType() == QContactDetail::TypeUndefined) return false; /* See if this contact has one of these details in it */ const QList& details = contact.details(cdf.detailType()); if (details.count() == 0) return false; /* can't match */ /* See if we need to check the values */ if (cdf.detailField() == -1) return true; /* just testing for the presence of a detail of the specified type */ /* Now figure out what tests we are doing */ const bool valueTest = cdf.value().isValid(); const bool presenceTest = !valueTest; /* See if we need to test any values at all */ if (presenceTest) { for(int j=0; j < details.count(); j++) { const QContactDetail& detail = details.at(j); /* Check that the field is present and has a non-empty value */ if (detail.values().contains(cdf.detailField()) && !detail.value(cdf.detailField()).isNull()) return true; } return false; } /* Case sensitivity, for those parts that use it */ Qt::CaseSensitivity cs = (cdf.matchFlags() & QContactFilter::MatchCaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive; /* See what flags are requested, since we're looking at a value */ if (cdf.matchFlags() & QContactFilter::MatchPhoneNumber) { /* Doing phone number filtering. We hand roll an implementation here, backends will obviously want to override this. */ QString input = cdf.value().toString(); /* preprocess the input - ignore any non-digits (doesn't perform ITU-T collation */ QString preprocessedInput; for (int i = 0; i < input.size(); i++) { QChar current = input.at(i).toLower(); // XXX NOTE: we ignore characters like '+', 'p', 'w', '*' and '#' which may be important. if (current.isDigit()) { preprocessedInput.append(current); } } /* Look at every detail in the set of details and compare */ for (int j = 0; j < details.count(); j++) { const QContactDetail& detail = details.at(j); const QString& valueString = detail.value(cdf.detailField()).toString(); QString preprocessedValueString; for (int i = 0; i < valueString.size(); i++) { QChar current = valueString.at(i).toLower(); // note: we ignore characters like '+', 'p', 'w', '*' and '#' which may be important. if (current.isDigit()) { preprocessedValueString.append(current); } } // if the matchflags input don't require a particular criteria to pass, we assume that it has passed. // the "default" match strategy is an "endsWith" strategy. bool me = (cdf.matchFlags() & 7) == QContactFilter::MatchExactly; bool mc = (cdf.matchFlags() & 7) == QContactFilter::MatchContains; bool msw = (cdf.matchFlags() & 7) == QContactFilter::MatchStartsWith; bool mew = (cdf.matchFlags() & 7) == QContactFilter::MatchEndsWith; bool mer = (me ? preprocessedValueString == preprocessedInput : true); bool mcr = (mc ? preprocessedValueString.contains(preprocessedInput) : true); bool mswr = (msw ? preprocessedValueString.startsWith(preprocessedInput) : true); bool mewr = (mew ? preprocessedValueString.endsWith(preprocessedInput) : true); if (mewr && mswr && mcr && mer) { return true; // this detail meets all of the criteria which were required, and hence must match. } // fallback case: default MatchPhoneNumber compares the rightmost 7 digits, ignoring other matchflags. if (preprocessedValueString.right(7) == preprocessedInput.right(7)) { return true; } } } else if (cdf.matchFlags() & QContactFilter::MatchKeypadCollation) { // XXX TODO: not sure about the filtering semantics for MatchKeypadCollation. QString input = cdf.value().toString(); /* Look at every detail in the set of details and compare */ for (int j = 0; j < details.count(); j++) { const QContactDetail& detail = details.at(j); const QString& valueString = detail.value(cdf.detailField()).toString().toLower(); // preprocess the valueString QString preprocessedValue; for (int i = 0; i < valueString.size(); i++) { // we use ITU-T keypad collation by default. QChar currentValueChar = valueString.at(i); if (currentValueChar == QLatin1Char('a') || currentValueChar == QLatin1Char('b') || currentValueChar == QLatin1Char('c')) preprocessedValue.append(QLatin1Char('2')); else if (currentValueChar == QLatin1Char('d') || currentValueChar == QLatin1Char('e') || currentValueChar == QLatin1Char('f')) preprocessedValue.append(QLatin1Char('3')); else if (currentValueChar == QLatin1Char('g') || currentValueChar == QLatin1Char('h') || currentValueChar == QLatin1Char('i')) preprocessedValue.append(QLatin1Char('4')); else if (currentValueChar == QLatin1Char('j') || currentValueChar == QLatin1Char('k') || currentValueChar == QLatin1Char('l')) preprocessedValue.append(QLatin1Char('5')); else if (currentValueChar == QLatin1Char('m') || currentValueChar == QLatin1Char('n') || currentValueChar == QLatin1Char('o')) preprocessedValue.append(QLatin1Char('6')); else if (currentValueChar == QLatin1Char('p') || currentValueChar == QLatin1Char('q') || currentValueChar == QLatin1Char('r') || currentValueChar == QLatin1Char('s')) preprocessedValue.append(QLatin1Char('7')); else if (currentValueChar == QLatin1Char('t') || currentValueChar == QLatin1Char('u') || currentValueChar == QLatin1Char('v')) preprocessedValue.append(QLatin1Char('8')); else if (currentValueChar == QLatin1Char('w') || currentValueChar == QLatin1Char('x') || currentValueChar == QLatin1Char('y') || currentValueChar == QLatin1Char('z')) preprocessedValue.append(QLatin1Char('9')); else preprocessedValue.append(currentValueChar); } bool me = (cdf.matchFlags() & 7) == QContactFilter::MatchExactly; bool mc = (cdf.matchFlags() & 7) == QContactFilter::MatchContains; bool msw = (cdf.matchFlags() & 7) == QContactFilter::MatchStartsWith; bool mew = (cdf.matchFlags() & 7) == QContactFilter::MatchEndsWith; bool mer = (me ? preprocessedValue == input : true); bool mcr = (mc ? preprocessedValue.contains(input) : true); bool mswr = (msw ? preprocessedValue.startsWith(input) : true); bool mewr = (mew ? preprocessedValue.endsWith(input) : true); if (mewr && mswr && mcr && mer) { return true; // this detail meets all of the criteria which were required, and hence must match. } } } else if (cdf.matchFlags() & (QContactFilter::MatchEndsWith | QContactFilter::MatchStartsWith | QContactFilter::MatchContains | QContactFilter::MatchFixedString)) { /* We're strictly doing string comparisons here */ bool matchStarts = (cdf.matchFlags() & 7) == QContactFilter::MatchStartsWith; bool matchEnds = (cdf.matchFlags() & 7) == QContactFilter::MatchEndsWith; bool matchContains = (cdf.matchFlags() & 7) == QContactFilter::MatchContains; /* Value equality test */ for(int j=0; j < details.count(); j++) { const QContactDetail& detail = details.at(j); const QString& var = detail.value(cdf.detailField()).toString(); const QString& needle = cdf.value().toString(); if (matchStarts && var.startsWith(needle, cs)) return true; if (matchEnds && var.endsWith(needle, cs)) return true; if (matchContains && var.contains(needle, cs)) return true; if (compareStrings(var, needle, cs) == 0) return true; } return false; } else { /* Nope, testing the values as a variant */ /* Value equality test */ for(int j = 0; j < details.count(); j++) { const QContactDetail& detail = details.at(j); const QVariant& var = detail.value(cdf.detailField()); if (!var.isNull() && compareVariant(var, cdf.value(), cs) == 0) return true; } } } break; case QContactFilter::ContactDetailRangeFilter: { /* The only supported flags are: MatchExactly, MatchFixedString, MatchCaseSensitive */ const QContactDetailRangeFilter cdf(filter); if (cdf.detailType() == QContactDetail::TypeUndefined) return false; /* we do not know which field to check */ /* See if this contact has one of these details in it */ const QList& details = contact.details(cdf.detailType()); if (details.count() == 0) return false; /* can't match */ /* Check for a detail presence test */ if (cdf.detailField() == -1) return true; /* See if this is a field presence test */ if (!cdf.minValue().isValid() && !cdf.maxValue().isValid()) { for(int j=0; j < details.count(); j++) { const QContactDetail& detail = details.at(j); if (detail.values().contains(cdf.detailField())) return true; } return false; } /* open or closed interval testing support */ const int minComp = cdf.rangeFlags() & QContactDetailRangeFilter::ExcludeLower ? 1 : 0; const int maxComp = cdf.rangeFlags() & QContactDetailRangeFilter::IncludeUpper ? 1 : 0; /* Case sensitivity, for those parts that use it */ Qt::CaseSensitivity cs = (cdf.matchFlags() & QContactFilter::MatchCaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive; /* See what flags are requested, since we're looking at a value */ if (cdf.matchFlags() & QContactFilter::MatchFixedString) { /* We're strictly doing string comparisons here */ QString minVal = cdf.minValue().toString(); QString maxVal = cdf.maxValue().toString(); const bool testMin = !minVal.isEmpty(); const bool testMax = !maxVal.isEmpty(); for(int j=0; j < details.count(); j++) { const QContactDetail& detail = details.at(j); // The detail has to have a field of this type in order to be compared. if (!detail.value(cdf.detailField()).isValid()) continue; const QString& var = detail.value(cdf.detailField()).toString(); if (testMin && compareStrings(var, minVal, cs) < minComp) continue; if (testMax && compareStrings(var, maxVal, cs) >= maxComp) continue; return true; } // Fall through to end } else { const bool testMin = cdf.minValue().isValid(); const bool testMax = cdf.maxValue().isValid(); /* Nope, testing the values as a variant */ for(int j=0; j < details.count(); j++) { const QContactDetail& detail = details.at(j); const QVariant& var = detail.value(cdf.detailField()); // The detail has to have a field of this type in order to be compared. if (!var.isValid()) continue; if (testMin && compareVariant(var, cdf.minValue(), cs) < minComp) continue; if (testMax && compareVariant(var, cdf.maxValue(), cs) >= maxComp) continue; return true; } // Fall through to end } } break; case QContactFilter::RelationshipFilter: { // matches any contact that plays the specified role in a relationship // of the specified type with the specified other participant. const QContactRelationshipFilter rf(filter); // first, retrieve contact uris QContact relatedContact = rf.relatedContact(); QContactId relatedId = relatedContact.id(); QContactId contactId = contact.id(); if (relatedId == contactId) { return false; } // get the relationships in which this contact is involved. QList allRelationships; allRelationships = contact.relationships(); // now check to see if we have a match. foreach (const QContactRelationship& rel, allRelationships) { // perform the matching. if (rf.relatedContactRole() == QContactRelationship::Second) { // this is the role of the related contact; ie, to match, contact must be the first in the relationship. if ((rf.relationshipType().isEmpty() || rel.relationshipType() == rf.relationshipType()) && (rel.first().id() == contactId) && (relatedId.isNull() || relatedId == rel.second().id())) { return true; } } else if (rf.relatedContactRole() == QContactRelationship::First) { // this is the role of the related contact; ie, to match, contact must be the second in the relationship. if ((rf.relationshipType().isEmpty() || rel.relationshipType() == rf.relationshipType()) && (rel.second().id() == contactId) && (relatedId.isNull() || relatedId == rel.first().id())) { return true; } } else { // QContactRelationship::Either if ((rf.relationshipType().isEmpty() || rel.relationshipType() == rf.relationshipType()) && (relatedId.isNull() || (relatedId == rel.first().id() || relatedId == rel.second().id()))) { return true; } } } // if not found by now, it doesn't match the filter. return false; } //break; // unreachable. case QContactFilter::ChangeLogFilter: { QContactChangeLogFilter ccf(filter); // See what we can do... QContactTimestamp ts = contact.detail(QContactTimestamp::Type); // See if timestamps are even supported if (ts.isEmpty()) break; if (ccf.eventType() == QContactChangeLogFilter::EventAdded) return ccf.since() <= ts.created(); if (ccf.eventType() == QContactChangeLogFilter::EventChanged) return ccf.since() <= ts.lastModified(); // You can't emulate a removed.. // Fall through to end } break; case QContactFilter::ActionFilter: { // Find any matching actions, and do a union filter on their filter objects QContactActionFilter af(filter); QList descriptors = QContactActionManager::instance()->actionDescriptors(af.actionName()); // There's a small wrinkle if there's a value specified in the action filter // we have to adjust any contained QContactDetailFilters to have that value // or test if a QContactDetailRangeFilter contains this value already for (int j = 0; j < descriptors.count(); j++) { // Action filters are not allowed to return action filters, at all // it's too annoying to check for recursion QContactFilter d = descriptors.at(j).contactFilter(); if (!validateActionFilter(d)) return false; // Check for values etc... if (testFilter(d, contact)) return true; } // Fall through to end } break; case QContactFilter::IntersectionFilter: { /* XXX In theory we could reorder the terms to put the native tests first */ const QContactIntersectionFilter bf(filter); const QList& terms = bf.filters(); if (terms.count() > 0) { for(int j = 0; j < terms.count(); j++) { if (!testFilter(terms.at(j), contact)) { return false; } } return true; } // Fall through to end } break; case QContactFilter::UnionFilter: { /* XXX In theory we could reorder the terms to put the native tests first */ const QContactUnionFilter bf(filter); const QList& terms = bf.filters(); if (terms.count() > 0) { for(int j = 0; j < terms.count(); j++) { if (testFilter(terms.at(j), contact)) { return true; } } return false; } // Fall through to end } break; } return false; } /*! Given a QContactFilter \a filter retrieved from a QContactAction, check that it is valid and cannot cause infinite recursion. In particular, a filter from a QContactAction cannot contain any instances of a QContactActionFilter. Returns true if \a filter seems ok, or false otherwise. */ bool validateActionFilter(const QContactFilter& filter) { QList toVerify; toVerify << filter; while(toVerify.count() > 0) { QContactFilter f = toVerify.takeFirst(); if (f.type() == QContactFilter::ActionFilter) return false; if (f.type() == QContactFilter::IntersectionFilter) toVerify.append(QContactIntersectionFilter(f).filters()); if (f.type() == QContactFilter::UnionFilter) toVerify.append(QContactUnionFilter(f).filters()); } return true; } /*! Sets the cached relationships in the given \a contact to \a relationships */ void QContactManagerEngine::setContactRelationships(QContact* contact, const QList& relationships) { contact->d->m_relationshipsCache = relationships; } /*! Returns the engine ID from the given \a contactId. The caller does not take ownership of the pointer, and should not delete returned id or undefined behavior may occur. */ const QContactEngineId *QContactManagerEngine::engineId(const QContactId &contactId) { return contactId.d.data(); } /*! Compares two contacts (\a a and \a b) using the given list of \a sortOrders. Returns a negative number if \a a should appear before \a b according to the sort order, a positive number if \a a should appear after \a b according to the sort order, and zero if the two are unable to be sorted. */ int QContactManagerEngine::compareContact(const QContact& a, const QContact& b, const QList& sortOrders) { foreach(const QContactSortOrder& sortOrder, sortOrders) { if (!sortOrder.isValid()) break; const QContactDetail::DetailType detailType = sortOrder.detailType(); const int detailField = sortOrder.detailField(); const QList aDetails = a.details(detailType); const QList bDetails = b.details(detailType); if (aDetails.isEmpty() && bDetails.isEmpty()) continue; // use next sort criteria. // See if we need to check the values if (detailField == -1) { // just testing for the presence of a detail of the specified definition if (aDetails.size() == bDetails.size()) continue; // use next sort criteria. if (aDetails.isEmpty()) return sortOrder.blankPolicy() == QContactSortOrder::BlanksFirst ? -1 : 1; if (bDetails.isEmpty()) return sortOrder.blankPolicy() == QContactSortOrder::BlanksFirst ? 1 : -1; return 0; } // obtain the values which this sort order concerns const QVariant aVal = !aDetails.isEmpty() ? aDetails.first().value(detailField) : QVariant(); const QVariant bVal = !bDetails.isEmpty() ? bDetails.first().value(detailField) : QVariant(); bool aIsNull = false; bool bIsNull = false; // treat empty strings as null qvariants. if ((aVal.type() == QVariant::String && aVal.toString().isEmpty()) || aVal.isNull()) { aIsNull = true; } if ((bVal.type() == QVariant::String && bVal.toString().isEmpty()) || bVal.isNull()) { bIsNull = true; } // early exit error checking if (aIsNull && bIsNull) continue; // use next sort criteria. if (aIsNull) return (sortOrder.blankPolicy() == QContactSortOrder::BlanksFirst ? -1 : 1); if (bIsNull) return (sortOrder.blankPolicy() == QContactSortOrder::BlanksFirst ? 1 : -1); // real comparison int comparison = compareVariant(aVal, bVal, sortOrder.caseSensitivity()) * (sortOrder.direction() == Qt::AscendingOrder ? 1 : -1); if (comparison == 0) continue; return comparison; } return 0; // or according to id? return (a.id() < b.id() ? -1 : 1); } /* A functor that returns true iff a is less than b, according to the sortOrders passed in to the * ctor. The sortOrders pointer passed in must remain valid for the lifetime of the functor. */ class ContactLessThan { public: ContactLessThan(const QList* sortOrders) : mSortOrders(sortOrders) {} bool operator()(const QContact& a, const QContact& b) const { return QContactManagerEngine::compareContact(a, b, *mSortOrders) < 0; } private: const QList* mSortOrders; }; /*! Performs insertion sort of the contact \a toAdd into the \a sorted list, according to the provided \a sortOrders list. The first QContactSortOrder in the list has the highest priority: if the contact \a toAdd is deemed equal to another in the \a sorted list according to the first QContactSortOrder, the second QContactSortOrder in the list is used (and so on until either the contact is inserted or there are no more sort order objects in the list). If a contact is equal to another contact according to all sort orders, it is inserted after the previously-added contact. */ void QContactManagerEngine::addSorted(QList* sorted, const QContact& toAdd, const QList& sortOrders) { if (sortOrders.count() > 0) { ContactLessThan lessThan(&sortOrders); QList::iterator it(std::upper_bound(sorted->begin(), sorted->end(), toAdd, lessThan)); sorted->insert(it, toAdd); } else { // no sort order? just add it to the end sorted->append(toAdd); } } /*! Sorts the given list of contacts \a cs according to the provided \a sortOrders */ QList QContactManagerEngine::sortContacts(const QList& cs, const QList& sortOrders) { QList sortedIds; QList sortedContacts = cs; if (!sortOrders.isEmpty()) { ContactLessThan lessThan(&sortOrders); std::stable_sort(sortedContacts.begin(), sortedContacts.end(), lessThan); } foreach(const QContact& c, sortedContacts) { sortedIds.append(c.id()); } return sortedIds; } /*! Notifies the manager engine that the given request \a req is in the process of being destroyed. The request pointer \a req is still valid during this function call, but before returning from this call the engine should ensure that it no longer holds any references to the \a req pointer (for example, in a queue in another thread) because directly following this call the request will be deleted and this pointer will become invalid. In a multithreaded engine, this may mean blocking the calling thread while other threads clean up. If a request is still in progress at this point, it is undefined what will happen to the operation requested, but in general it should either be fully completed or fully aborted. In any case, the client has signalled that they do not care about the outcome (by deleting the request). */ void QContactManagerEngine::requestDestroyed(QContactAbstractRequest* req) { Q_UNUSED(req); } /*! Asks the manager engine to begin the given request \a req which is currently in a (re)startable state. Returns true if the request was started successfully, else returns false. \sa QContactAbstractRequest::start() */ bool QContactManagerEngine::startRequest(QContactAbstractRequest* req) { Q_UNUSED(req); return false; } /*! Asks the manager engine to cancel the given request \a req which was previously started and is currently in a cancellable state. Returns true if cancellation of the request was started successfully, otherwise returns false. \sa startRequest(), QContactAbstractRequest::cancel() */ bool QContactManagerEngine::cancelRequest(QContactAbstractRequest* req) { Q_UNUSED(req); return false; } /*! Blocks until the manager engine has completed the given request \a req which was previously started, or until \a msecs milliseconds have passed. Returns true if the request was completed, and false if the request was not in the \c QContactAbstractRequest::Active state or no progress could be reported. \sa startRequest() */ bool QContactManagerEngine::waitForRequestFinished(QContactAbstractRequest* req, int msecs) { Q_UNUSED(req); Q_UNUSED(msecs); return false; } /*! Updates the given asynchronous request \a req by setting the new \a state of the request. If the new state is different, the stateChanged() signal will be emitted by the request. */ void QContactManagerEngine::updateRequestState(QContactAbstractRequest* req, QContactAbstractRequest::State state) { Q_ASSERT(req); QMutexLocker ml(&req->d_ptr->m_mutex); bool emitState = req->d_ptr->m_state != state; req->d_ptr->m_state = state; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QContactAbstractRequest::State, state)); Q_ASSERT(guard); } /*! Updates the given QContactIdFetchRequest \a req with the latest results \a result, and operation error \a error. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QContactManagerEngine::updateContactIdFetchRequest(QContactIdFetchRequest* req, const QList& result, QContactManager::Error error, QContactAbstractRequest::State newState) { Q_ASSERT(req); QContactIdFetchRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_ids = result; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QContactAbstractRequest::State, newState)); Q_ASSERT(guard); } /*! Updates the given QContactFetchRequest \a req with the latest results \a result, and operation error \a error. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QContactManagerEngine::updateContactFetchRequest(QContactFetchRequest* req, const QList& result, QContactManager::Error error, QContactAbstractRequest::State newState) { Q_ASSERT(req); QContactFetchRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_contacts = result; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QContactAbstractRequest::State, newState)); Q_ASSERT(guard); } /*! Updates the given QContactRemoveRequest \a req with the operation error \a error, and map of input index to individual error \a errorMap. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QContactManagerEngine::updateContactRemoveRequest(QContactRemoveRequest* req, QContactManager::Error error, const QMap& errorMap, QContactAbstractRequest::State newState) { Q_ASSERT(req); QContactRemoveRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_errors = errorMap; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QContactAbstractRequest::State, newState)); Q_ASSERT(guard); } /*! Updates the given QContactSaveRequest \a req with the latest results \a result, operation error \a error, and map of input index to individual error \a errorMap. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QContactManagerEngine::updateContactSaveRequest(QContactSaveRequest* req, const QList& result, QContactManager::Error error, const QMap& errorMap, QContactAbstractRequest::State newState) { Q_ASSERT(req); QContactSaveRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_contacts = result; rd->m_errors = errorMap; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QContactAbstractRequest::State, newState)); Q_ASSERT(guard); } /*! Updates the given QContactRelationshipSaveRequest \a req with the latest results \a result, operation error \a error, and map of input index to individual error \a errorMap. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QContactManagerEngine::updateRelationshipSaveRequest(QContactRelationshipSaveRequest* req, const QList& result, QContactManager::Error error, const QMap& errorMap, QContactAbstractRequest::State newState) { Q_ASSERT(req); QContactRelationshipSaveRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_relationships = result; rd->m_errors = errorMap; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QContactAbstractRequest::State, newState)); Q_ASSERT(guard); } /*! Updates the given QContactRelationshipRemoveRequest \a req with the operation error \a error, and map of input index to individual error \a errorMap. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QContactManagerEngine::updateRelationshipRemoveRequest(QContactRelationshipRemoveRequest* req, QContactManager::Error error, const QMap& errorMap, QContactAbstractRequest::State newState) { Q_ASSERT(req); QContactRelationshipRemoveRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_errors = errorMap; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QContactAbstractRequest::State, newState)); Q_ASSERT(guard); } /*! Updates the given QContactRelationshipFetchRequest \a req with the latest results \a result, and operation error \a error. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QContactManagerEngine::updateRelationshipFetchRequest(QContactRelationshipFetchRequest* req, const QList& result, QContactManager::Error error, QContactAbstractRequest::State newState) { Q_ASSERT(req); QContactRelationshipFetchRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_relationships = result; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QContactAbstractRequest::State, newState)); Q_ASSERT(guard); } /*! For each contact in \a contacts, either add it to the database or update an existing one. This function accepts a \a typeMask, which specifies which details of the contacts should be updated. Details with types not included in the typeMask will not be updated or added. The manager should populate \a errorMap (the map of indices of the \a contacts list to the error which occurred when saving the contact at that index) for every index for which the contact could not be saved, if it is able. The supplied \a errorMap parameter may be null, if the client does not desire detailed error information. If supplied, it will be empty upon entry to this function. The \l QContactManager::error() function will only return \c QContactManager::NoError if all contacts were saved successfully. For each newly saved contact that was successful, the id of the contact in the \a contacts list will be updated with the new value. If a failure occurs when saving a new contact, the id will be cleared. Any errors encountered during this operation should be stored to \a error. */ bool QContactManagerEngine::saveContacts(QList *contacts, const QList &typeMask, QMap *errorMap, QContactManager::Error *error) { // TODO should the default implementation do the right thing, or return false? if (typeMask.isEmpty()) { // Non partial, just pass it on return saveContacts(contacts, errorMap, error); } else { // Partial contact save. // Basically // Need to: // 1) fetch existing contacts // 2) strip out details in typeMask for existing contacts // 3) copy the details from the passed in list for existing contacts // 4) for any new contacts, copy the masked details to a blank contact // 5) save the modified ones // 6) update the id of any new contacts // 7) transfer any errors from saving to errorMap QList existingContactIds; // Error conditions: // 1) bad id passed in (can't fetch) // 2) bad fetch (can't save partial update) // 3) bad save error // all of which needs to be returned in the error map QHash existingIdMap; // contacts index to existingContacts index // Try to figure out which of our arguments are new contacts for(int i = 0; i < contacts->count(); i++) { // See if there's a contactId that's not from this manager const QContact c = contacts->at(i); if (c.id().managerUri() == managerUri()) { existingIdMap.insert(i, existingContactIds.count()); existingContactIds.append(c.id()); } else if (!c.id().isNull()) { // Hmm, error (wrong manager) errorMap->insert(i, QContactManager::DoesNotExistError); } // else new contact } // Now fetch the existing contacts QMap fetchErrors; QContactManager::Error fetchError = QContactManager::NoError; QList existingContacts = this->contacts(existingContactIds, QContactFetchHint(), &fetchErrors, &fetchError); // Prepare the list to save QList contactsToSave; QList savedToOriginalMap; // contactsToSave index to contacts index QSet mask = typeMask.toSet(); for (int i = 0; i < contacts->count(); i++) { // See if this is an existing contact or a new one const int fetchedIdx = existingIdMap.value(i, -1); QContact contactToSave; if (fetchedIdx >= 0) { // See if we had an error if (fetchErrors[fetchedIdx] != QContactManager::NoError) { errorMap->insert(i, fetchErrors[fetchedIdx]); continue; } // Existing contact we should have fetched contactToSave = existingContacts.at(fetchedIdx); QSharedDataPointer& cd = QContactData::contactData(contactToSave); cd->removeOnly(mask); } else if (errorMap->contains(i)) { // A bad argument. Leave it out of the contactsToSave list continue; } // else new contact // Now copy in the details from the arguments const QContact& c = contacts->at(i); foreach (QContactDetail::DetailType type, mask) { QList details = c.details(type); foreach(QContactDetail detail, details) { contactToSave.saveDetail(&detail); } } savedToOriginalMap.append(i); contactsToSave.append(contactToSave); } // Now save them QMap saveErrors; QContactManager::Error saveError = QContactManager::NoError; saveContacts(&contactsToSave, &saveErrors, &saveError); // Now update the passed in arguments, where necessary // Update IDs of the contacts list for (int i = 0; i < contactsToSave.count(); i++) { (*contacts)[savedToOriginalMap[i]].setId(contactsToSave[i].id()); } // Populate the errorMap with the errorMap of the attempted save QMap::iterator it(saveErrors.begin()); while (it != saveErrors.end()) { if (it.value() != QContactManager::NoError) { errorMap->insert(savedToOriginalMap[it.key()], it.value()); } it++; } return errorMap->isEmpty(); } } /*! Returns the list of contacts with the ids given by \a contactIds. There is a one-to-one correspondence between the returned contacts and the supplied \a contactIds. If there is an invalid id in \a contactIds, then an empty QContact will take its place in the returned list and an entry will be inserted into \a errorMap. The overall operation error will be saved in \a error. The \a fetchHint parameter describes the optimization hints that a manager may take. If the \a fetchHint is the default constructed hint, all existing details, relationships and action preferences in the matching contacts will be returned. If a non-default fetch hint is supplied, and the client wishes to make changes to the contacts, they should ensure that only a detail type hint is supplied and that when saving it back, a type mask should be used which corresponds to the detail type hint. This is to ensure that no data is lost by overwriting an existing contact with a restricted version of it. \sa QContactFetchHint */ QList QContactManagerEngine::contacts(const QList &contactIds, const QContactFetchHint &fetchHint, QMap *errorMap, QContactManager::Error *error) const { QContactIdFilter lif; lif.setIds(contactIds); QList unsorted = contacts(lif, QContactSortOrder(), fetchHint, error); // Build an index into the results QHash idMap; // value is index into unsorted if (*error == QContactManager::NoError) { for (int i = 0; i < unsorted.size(); i++) { idMap.insert(unsorted[i].id(), i); } } // Build up the results and errors QList results; for (int i = 0; i < contactIds.count(); i++) { QContactId id(contactIds[i]); if (!idMap.contains(id)) { if (errorMap) errorMap->insert(i, QContactManager::DoesNotExistError); if (*error == QContactManager::NoError) *error = QContactManager::DoesNotExistError; results.append(QContact()); } else { results.append(unsorted[idMap[id]]); } } return results; } /*! Updates the given QContactFetchByIdRequest \a req with the latest results \a result, and operation error \a error, and map of input index to individual error \a errorMap. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QContactManagerEngine::updateContactFetchByIdRequest(QContactFetchByIdRequest *req, const QList &result, QContactManager::Error error, const QMap &errorMap, QContactAbstractRequest::State newState) { Q_ASSERT(req); QContactFetchByIdRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_contacts = result; rd->m_errors = errorMap; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QContactAbstractRequest::State, newState)); Q_ASSERT(guard); } #include "moc_qcontactmanagerengine.cpp" QT_END_NAMESPACE_CONTACTS src/contacts/qcontactmanagerengine.h000066400000000000000000000227111233466112000201720ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTMANAGERENGINE_H #define QCONTACTMANAGERENGINE_H #include #include #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class Q_CONTACTS_EXPORT QContactManagerEngine : public QObject { Q_OBJECT public: QContactManagerEngine() {} /* URI reporting */ virtual QString managerName() const = 0; // e.g. "memory" virtual QMap managerParameters() const; // e.g. "filename=private.db" virtual int managerVersion() const = 0; /* Default and only implementation of this */ QString managerUri() const; /* Filtering */ virtual QList contactIds(const QContactFilter &filter, const QList &sortOrders, QContactManager::Error *error) const; virtual QList contacts(const QContactFilter &filter, const QList& sortOrders, const QContactFetchHint &fetchHint, QContactManager::Error *error) const; virtual QList contacts(const QList &contactIds, const QContactFetchHint& fetchHint, QMap *errorMap, QContactManager::Error *error) const; virtual QContact contact(const QContactId &contactId, const QContactFetchHint &fetchHint, QContactManager::Error *error) const; virtual bool saveContact(QContact *contact, QContactManager::Error *error); virtual bool removeContact(const QContactId &contactId, QContactManager::Error *error); virtual bool saveRelationship(QContactRelationship *relationship, QContactManager::Error *error); virtual bool removeRelationship(const QContactRelationship &relationship, QContactManager::Error *error); virtual bool saveContacts(QList *contacts, QMap *errorMap, QContactManager::Error *error); virtual bool saveContacts(QList *contacts, const QList &typeMask, QMap *errorMap, QContactManager::Error *error); virtual bool removeContacts(const QList &contactIds, QMap *errorMap, QContactManager::Error *error); /* "Self" contact id (MyCard) */ virtual bool setSelfContactId(const QContactId &contactId, QContactManager::Error *error); virtual QContactId selfContactId(QContactManager::Error *error) const; /* Relationships between contacts */ virtual QList relationships(const QString &relationshipType, const QContact& participant, QContactRelationship::Role role, QContactManager::Error *error) const; virtual bool saveRelationships(QList *relationships, QMap* errorMap, QContactManager::Error *error); virtual bool removeRelationships(const QList &relationships, QMap *errorMap, QContactManager::Error *error); /* Validation for saving */ virtual bool validateContact(const QContact &contact, QContactManager::Error *error) const; /* Asynchronous Request Support */ virtual void requestDestroyed(QContactAbstractRequest *req); virtual bool startRequest(QContactAbstractRequest *req); virtual bool cancelRequest(QContactAbstractRequest *req); virtual bool waitForRequestFinished(QContactAbstractRequest *req, int msecs); /* Capabilities reporting */ virtual bool isRelationshipTypeSupported(const QString &relationshipType, QContactType::TypeValues contactType) const; virtual bool isFilterSupported(const QContactFilter &filter) const; virtual QList supportedDataTypes() const; virtual QList supportedContactTypes() const; virtual QList supportedContactDetailTypes() const; Q_SIGNALS: void dataChanged(); void contactsAdded(const QList &contactIds); void contactsChanged(const QList &contactIds); void contactsRemoved(const QList &contactIds); void relationshipsAdded(const QList &affectedContactIds); void relationshipsRemoved(const QList &affectedContactIds); void selfContactIdChanged(const QContactId &oldId, const QContactId &newId); public: // Async update functions static void updateRequestState(QContactAbstractRequest *req, QContactAbstractRequest::State state); static void updateContactIdFetchRequest(QContactIdFetchRequest *req, const QList& result, QContactManager::Error error, QContactAbstractRequest::State); static void updateContactFetchRequest(QContactFetchRequest *req, const QList &result, QContactManager::Error error, QContactAbstractRequest::State); static void updateContactFetchByIdRequest(QContactFetchByIdRequest *req, const QList& result, QContactManager::Error error, const QMap &errorMap, QContactAbstractRequest::State); static void updateContactRemoveRequest(QContactRemoveRequest *req, QContactManager::Error error, const QMap &errorMap, QContactAbstractRequest::State); static void updateContactSaveRequest(QContactSaveRequest *req, const QList &result, QContactManager::Error error, const QMap &errorMap, QContactAbstractRequest::State); static void updateRelationshipSaveRequest(QContactRelationshipSaveRequest *req, const QList &result, QContactManager::Error error, const QMap &errorMap, QContactAbstractRequest::State); static void updateRelationshipRemoveRequest(QContactRelationshipRemoveRequest *req, QContactManager::Error error, const QMap &errorMap, QContactAbstractRequest::State); static void updateRelationshipFetchRequest(QContactRelationshipFetchRequest *req, const QList &result, QContactManager::Error error, QContactAbstractRequest::State); // Other protected area update functions static void setDetailAccessConstraints(QContactDetail *detail, QContactDetail::AccessConstraints constraints); static void setContactRelationships(QContact *contact, const QList &relationships); /* Helper functions */ static const QContactEngineId *engineId(const QContactId &contactId); static int compareContact(const QContact &a, const QContact &b, const QList &sortOrders); static void addSorted(QList* sorted, const QContact &toAdd, const QList &sortOrders); static int compareVariant(const QVariant &first, const QVariant &second, Qt::CaseSensitivity sensitivity); static bool testFilter(const QContactFilter& filter, const QContact &contact); static QList sortContacts(const QList &contacts, const QList &sortOrders); static QContactFilter canonicalizedFilter(const QContactFilter &filter); private: /* QContactChangeSet is a utility class used to emit the appropriate signals */ friend class QContactChangeSet; }; QT_END_NAMESPACE_CONTACTS QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(QtContacts::QContactManagerEngine, "org.qt-project.Qt.QContactManagerEngine") QT_END_NAMESPACE #endif // QCONTACTMANAGERENGINE_H src/contacts/qcontactmanagerenginefactory.cpp000066400000000000000000000105651233466112000221210ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactmanagerenginefactory.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactManagerEngineFactory \brief The QContactManagerEngineFactory class provides the interface for plugins that implement QContactManagerEngine functionality. \inmodule QtContacts \ingroup contacts-backends This class provides a simple interface for the creation of manager engine instances. Each factory has a specific id associated with it, which forms the \c managerName parameter when creating \l QContactManager objects. More information on writing a contacts engine plugin is available in the \l{Qt Contacts Manager Engines} documentation. \sa QContactManager, QContactManagerEngine */ /*! A default, empty destructor. */ QContactManagerEngineFactory::~QContactManagerEngineFactory() { } /*! \internal */ QStringList QContactManagerEngineFactory::keys() const { return QStringList() << managerName(); } /*! \fn QContactManagerEngineFactory::engine(const QMap& parameters, QContactManager::Error* error) This function is called by the QContactManager implementation to create an instance of the engine provided by this factory. The \a parameters supplied can be ignored or interpreted as desired. If a supplied parameter results in an unfulfillable request, or some other error occurs, this function may return a null pointer, and the client developer will get an invalid QContactManager in return. Any error should be stored in the supplied \a error reference. */ /*! \fn QContactManagerEngineFactory::createContactEngineId(const QMap ¶meters, const QString &engineIdString) const This function should return an engine-specific Contact ID, according to the given \a parameters and the \a engineIdString. */ /*! \fn QContactManagerEngineFactory::managerName() const This function should return a unique string that identifies the engines provided by this factory. Typically this would be of the form "org.qt-project.Qt.SampleContactEngine", with the appropriate domain and engine name substituted. */ /*! \fn QContactManagerEngineFactory::supportedImplementationVersions() const This function should return a list of versions of the engine which this factory can instantiate. */ QList QContactManagerEngineFactory::supportedImplementationVersions() const { return QList(); } #include "moc_qcontactmanagerenginefactory.cpp" QT_END_NAMESPACE_CONTACTS src/contacts/qcontactmanagerenginefactory.h000066400000000000000000000073001233466112000215570ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTMANAGERENGINEFACTORY_H #define QCONTACTMANAGERENGINEFACTORY_H #include #include #include QT_BEGIN_NAMESPACE_CONTACTS /* Backend plugin API interface, creates engines for us */ class QContactEngineId; class QContactManagerEngine; struct Q_CONTACTS_EXPORT QContactManagerEngineFactoryInterface : public QFactoryInterface { virtual QContactManagerEngine *engine(const QMap ¶meters, QContactManager::Error *error) = 0; virtual QString managerName() const = 0; virtual QContactEngineId *createContactEngineId(const QMap ¶meters, const QString &engineIdString) const = 0; }; QT_END_NAMESPACE_CONTACTS QT_BEGIN_NAMESPACE #define QT_CONTACT_MANAGER_ENGINE_INTERFACE "org.qt-project.Qt.QContactManagerEngineFactoryInterface" Q_DECLARE_INTERFACE(QtContacts::QContactManagerEngineFactoryInterface, QT_CONTACT_MANAGER_ENGINE_INTERFACE) QT_END_NAMESPACE QT_BEGIN_NAMESPACE_CONTACTS class Q_CONTACTS_EXPORT QContactManagerEngineFactory : public QObject, public QContactManagerEngineFactoryInterface { Q_OBJECT Q_INTERFACES(QtContacts::QContactManagerEngineFactoryInterface:QFactoryInterface) public: // engine factory functions virtual QList supportedImplementationVersions() const; virtual ~QContactManagerEngineFactory(); virtual QContactManagerEngine* engine(const QMap ¶meters, QContactManager::Error *error) = 0; virtual QContactEngineId* createContactEngineId(const QMap ¶meters, const QString &engineIdString) const = 0; virtual QString managerName() const = 0; virtual QStringList keys() const; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTMANAGERENGINEFACTORY_H src/contacts/qcontactmanagerenginev2wrapper_p.cpp000066400000000000000000000425601233466112000227210ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactmanagerenginev2wrapper_p.h" #include #include "qcontact_p.h" #include "qcontactidfilter.h" #include "qcontactabstractrequest_p.h" #include "qcontactfetchbyidrequest.h" QT_BEGIN_NAMESPACE_CONTACTS QContactManagerEngineV2Wrapper::QContactManagerEngineV2Wrapper(QContactManagerEngine *wrappee) : m_engine(wrappee) { Q_ASSERT(wrappee); } QContactManagerEngineV2Wrapper::~QContactManagerEngineV2Wrapper() { delete m_engine; } void QContactManagerEngineV2Wrapper::requestDestroyed(QContactAbstractRequest* req) { RequestController* controller = m_controllerForRequest.value(req); if (controller) { // If we own it, just delete the controller (and ignore any subrequests' signals from now on) delete controller; m_controllerForRequest.insert(req, 0); } else { m_engine->requestDestroyed(req); } } bool QContactManagerEngineV2Wrapper::startRequest(QContactAbstractRequest* req) { if (req && req->type() == QContactAbstractRequest::ContactSaveRequest && !static_cast(req)->typeMask().isEmpty()) { RequestController* controller; controller = new PartialSaveRequestController(m_engine, this); controller->setRequest(req); connect(controller, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(requestStateChanged(QContactAbstractRequest::State)), Qt::QueuedConnection); m_controllerForRequest.insert(req, controller); if (controller->start()) { updateRequestState(req, QContactAbstractRequest::ActiveState); return true; } else { return false; } } // Otherwise, pass it on return m_engine->startRequest(req); } /* A slot connected to the stateChanged signal of a controller object. It is signaled by the controller to indicate that one of the client's requests has an updated state. */ void QContactManagerEngineV2Wrapper::requestStateChanged(QContactAbstractRequest::State state) { RequestController* controller = qobject_cast(sender()); Q_ASSERT(controller); QContactAbstractRequest* request = controller->request(); if (state == QContactAbstractRequest::FinishedState) { controller->deleteLater(); if (request) { // It's possible the request was deleted by the sender. // Keep the key in m_controllerForRequest but point it to null to indicate a defunct // controller m_controllerForRequest.insert(request, 0); } } else { updateRequestState(request, state); } } /* \reimp */ bool QContactManagerEngineV2Wrapper::cancelRequest(QContactAbstractRequest* req) { if (m_controllerForRequest.contains(req)) { RequestController* controller = m_controllerForRequest.value(req); if (controller) { // If we own it and it hasn't already been deleted, // just delete the controller (and ignore any subrequests' signals from now on) delete controller; m_controllerForRequest.insert(req, 0); } return true; } else { return m_engine->cancelRequest(req); } } /* \reimp */ bool QContactManagerEngineV2Wrapper::waitForRequestFinished(QContactAbstractRequest* req, int msecs) { if (m_controllerForRequest.contains(req)) { RequestController* controller = m_controllerForRequest.value(req); if (controller) { if (controller->waitForFinished(msecs)) { updateRequestState(req, QContactAbstractRequest::FinishedState); return true; } else { return false; } } else { // A request with a null controller means it has already finished return true; } } else { return m_engine->waitForRequestFinished(req, msecs); } } /* A static helper to twiddle with \a request's privates, setting its engine to \a engine. */ void QContactManagerEngineV2Wrapper::setEngineOfRequest(QContactAbstractRequest* request, QContactManagerEngine* engine) { request->d_ptr->m_engine = engine; } // General RequestController stuff /* A single RequestController is associated with a single client QContactAbstractRequest. It manages the sub-requests that need to be sent to the real engine and controls the asynchronous flow between start() and one or more handleUpdatedSubRequest() calls that might follow. waitForFinished() can also be called on a controller, which synchronously performs the rest of the necessary sub-requests. */ /* A slot connected the stateChanged signal of a sub request */ void RequestController::handleUpdatedSubRequest(QContactAbstractRequest::State state) { QObject* caller = sender(); QContactAbstractRequest* subRequest = qobject_cast(caller); if (subRequest) { if (state == QContactAbstractRequest::FinishedState) { // It's possibly already finished if waitForFinished has previously been called if (!isFinished()) handleFinishedSubRequest(subRequest); } else { // XXX maybe Canceled should be handled } } } /* This function handles the rest of the program flow in a synchronous way. */ bool RequestController::waitForFinished(int msecs) { // If the current request is active, it must be a ContactFetchRequest. We just need to // wait for it to finish, then finalize the post-processing. if (m_currentSubRequest.isNull()) { return false; } while (!isFinished()) { if (!m_currentSubRequest->waitForFinished(msecs)) return false; handleFinishedSubRequest(m_currentSubRequest.data()); } return true; } // FetchByIdRequestController stuff /* A FetchByIdRequestController is a RequestController for QContactFetchByIdRequests */ bool FetchByIdRequestController::start() { // Our strategy is to translate it to a ContactFetchRequest. Later when it finishes, we can // fiddle with the results to get it in the right format. Q_ASSERT(m_request); QContactFetchByIdRequest* originalRequest = static_cast(m_request.data()); QContactFetchRequest* qcfr = new QContactFetchRequest; QContactIdFilter lif; lif.setIds(originalRequest->contactIds()); qcfr->setFilter(lif); qcfr->setFetchHint(originalRequest->fetchHint()); // normally, you'd set the manager, but in this case, we only have a bare engine: QContactManagerEngineV2Wrapper::setEngineOfRequest(qcfr, m_engine); m_currentSubRequest.reset(qcfr); connect(qcfr, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(handleUpdatedSubRequest(QContactAbstractRequest::State)), Qt::QueuedConnection); return qcfr->start(); } /* One of our subrequests has finished. Go to the next step. */ void FetchByIdRequestController::handleFinishedSubRequest(QContactAbstractRequest* subReq) { // For a FetchByIdRequest, we know that the only subrequest is a QContactFetchRequest. // The next step is simply to take the results and reformat it. // Take the results: QContactFetchRequest* qcfr = qobject_cast(subReq); QList contacts = qcfr->contacts(); QContactManager::Error error = qcfr->error(); // Build an index into the results QHash idMap; // value is index into unsorted if (error == QContactManager::NoError) { for (int i = 0; i < contacts.size(); i++) { idMap.insert(contacts[i].id(), i); } } // Find the order in which the results should be presented QContactFetchByIdRequest* request = static_cast(m_request.data()); QList ids(request->contactIds()); // Build up the results and errors QList results; QMap errorMap; for (int i = 0; i < ids.count(); i++) { QContactId id(ids[i]); if (!idMap.contains(id)) { errorMap.insert(i, QContactManager::DoesNotExistError); if (error == QContactManager::NoError) error = QContactManager::DoesNotExistError; results.append(QContact()); } else { results.append(contacts[idMap[id]]); } } // Update the request object QContactManagerEngineV2Wrapper::updateContactFetchByIdRequest( request, results, error, errorMap, QContactAbstractRequest::FinishedState); finish(); } // PartialSaveRequestController stuff /* A PartialSaveRequestController is a RequestController for QContactSaveRequests with definition mask */ bool PartialSaveRequestController::start() { // The first step is to fetch the existing contacts. QList existingContactIds; QList newContactIndices; // First, remove the contacts that aren't from this manager QList contacts(request()->contacts()); // Try to figure out which of our arguments are new contacts for(int i = 0; i < contacts.count(); i++) { // See if there's a contactId that's not from this manager const QContact c = contacts.at(i); if (c.id().managerUri() == m_engine->managerUri()) { m_existingIdMap.insert(i, existingContactIds.count()); existingContactIds.append(c.id()); } else if (!c.id().isNull()) { // Hmm, error (wrong manager) m_errorMap.insert(i, QContactManager::DoesNotExistError); } else { newContactIndices.append(i); } } if (!existingContactIds.isEmpty()) { // Now do the fetch and wait for the result in handleFinishedSubRequest QContactFetchByIdRequest* cfbir = new QContactFetchByIdRequest; cfbir->setIds(existingContactIds); // normally, you'd set the manager, but in this case, we only have a bare engine: QContactManagerEngineV2Wrapper::setEngineOfRequest(cfbir, m_v2wrapper); m_currentSubRequest.reset(cfbir); connect(cfbir, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(handleUpdatedSubRequest(QContactAbstractRequest::State)), Qt::QueuedConnection); return cfbir->start(); } else { // Special case for the case where every contact is new - we don't need to bother fetching // any existing contacts - just prune them down to the mask and do the save request. QList contactsToSave; QSet mask(request()->typeMask().toSet()); for (int i = 0; i < newContactIndices.count(); i++) { QContact contactToSave; partiallyCopyDetails(&contactToSave, contacts[newContactIndices[i]], mask); m_savedToOriginalMap.append(i); contactsToSave.append(contactToSave); } QContactSaveRequest* csr = new QContactSaveRequest; csr->setContacts(contactsToSave); // normally, you'd set the manager, but in this case, we only have a bare engine: QContactManagerEngineV2Wrapper::setEngineOfRequest(csr, m_engine); m_currentSubRequest.reset(csr); connect(csr, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(handleUpdatedSubRequest(QContactAbstractRequest::State)), Qt::QueuedConnection); return csr->start(); } } /* One of our subrequests has finished. Go to the next step. */ void PartialSaveRequestController::handleFinishedSubRequest(QContactAbstractRequest* subReq) { if (subReq->type() == QContactAbstractRequest::ContactFetchByIdRequest) { QContactFetchByIdRequest* cfbir = qobject_cast(subReq); QList contactsToSave; QMap fetchErrors(cfbir->errorMap()); QList existingContacts(cfbir->contacts()); QList contacts(request()->contacts()); QSet mask(request()->typeMask().toSet()); for (int i = 0; i < contacts.count(); i++) { // See if this is an existing contact or a new one const int fetchedIdx = m_existingIdMap.value(i, -1); QContact contactToSave; if (fetchedIdx >= 0) { // See if we had an error if (fetchErrors[fetchedIdx] != QContactManager::NoError) { m_errorMap.insert(i, fetchErrors[fetchedIdx]); continue; } // Existing contact we should have fetched contactToSave = existingContacts.at(fetchedIdx); QSharedDataPointer& cd = QContactData::contactData(contactToSave); cd->removeOnly(mask); } else if (m_errorMap.contains(i)) { // A bad argument. Leave it out of the contactsToSave list continue; } // else new contact partiallyCopyDetails(&contactToSave, contacts.at(i), mask); m_savedToOriginalMap.append(i); contactsToSave.append(contactToSave); } // Now do the fetch and wait for the result QContactSaveRequest* csr = new QContactSaveRequest; csr->setContacts(contactsToSave); // normally, you'd set the manager, but in this case, we only have a bare engine: QContactManagerEngineV2Wrapper::setEngineOfRequest(csr, m_engine); m_currentSubRequest.reset(csr); connect(csr, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(handleUpdatedSubRequest(QContactAbstractRequest::State)), Qt::QueuedConnection); csr->start(); // TODO what if this fails? } else if (subReq->type() == QContactAbstractRequest::ContactSaveRequest) { QContactSaveRequest* csr = qobject_cast(subReq); QList savedContacts(csr->contacts()); QMap saveErrors(csr->errorMap()); QList contacts(request()->contacts()); for (int i = 0; i < savedContacts.count(); i++) { contacts[m_savedToOriginalMap[i]].setId(savedContacts[i].id()); } // Populate the m_errorMap with the m_errorMap of the attempted save QMap::iterator it(saveErrors.begin()); QContactManager::Error error = QContactManager::NoError; while (it != saveErrors.end()) { error = it.value(); m_errorMap.insert(m_savedToOriginalMap[it.key()], it.value()); it++; } // Update the request object QContactManagerEngine::updateContactSaveRequest( request(), contacts, error, m_errorMap, QContactAbstractRequest::FinishedState); finish(); } else { Q_ASSERT(false); } } /* Copy details specified in \a mask from contact \a from to contact \a to */ void PartialSaveRequestController::partiallyCopyDetails(QContact* to, const QContact& from, const QSet& mask) { foreach (QContactDetail::DetailType type, mask) { QList details = from.details(type); foreach(QContactDetail detail, details) { to->saveDetail(&detail); } } } #include "moc_qcontactmanagerenginev2wrapper_p.cpp" QT_END_NAMESPACE_CONTACTS src/contacts/qcontactobserver.cpp000066400000000000000000000070571233466112000175620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactobserver.h" #include #include "qcontactmanager.h" #include "qcontactmanager_p.h" QT_BEGIN_NAMESPACE_CONTACTS class QContactObserverPrivate { public: QContactId m_contactId; QPointer m_manager; }; /*! \class QContactObserver \brief The QContactObserver class is a simple class that emits a signal when a single particular contact is updated or deleted. \inmodule QtContacts \ingroup contacts-main */ /*! Constructs a QContactObserver to observe the contact in \a manager with the given \a contactId and \a parent object. */ QContactObserver::QContactObserver(QContactManager* manager, QContactId contactId, QObject* parent) : QObject(parent), d(new QContactObserverPrivate) { d->m_contactId = contactId; d->m_manager = manager; QContactManagerData::registerObserver(manager, this); } /*! Destroys this observer. */ QContactObserver::~QContactObserver() { if (!d->m_manager.isNull()) { QContactManagerData::unregisterObserver(d->m_manager.data(), this); } delete d; } /*! Returns the contact id of the contact that this object observes. */ QContactId QContactObserver::contactId() const { return d->m_contactId; } /*! \fn QContactObserver::contactChanged() This signal is emitted when the observed contact is changed in the manager. */ /*! \fn QContactObserver::contactRemoved() This signal is emitted when the observed contact is removed from the manager. */ #include "moc_qcontactobserver.cpp" QT_END_NAMESPACE_CONTACTS src/contacts/qcontactobserver.h000066400000000000000000000050641233466112000172230ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTOBSERVER_H #define QCONTACTOBSERVER_H #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactManager; class QContactObserverPrivate; class Q_CONTACTS_EXPORT QContactObserver : public QObject { Q_OBJECT public: QContactObserver(QContactManager* manager, QContactId contactId, QObject* parent = 0); ~QContactObserver(); QContactId contactId() const; Q_SIGNALS: void contactChanged(); void contactRemoved(); private: Q_DISABLE_COPY(QContactObserver) QContactObserverPrivate* d; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTOBSERVER_H src/contacts/qcontactrelationship.cpp000066400000000000000000000210171233466112000204240ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactrelationship.h" #include "qcontactrelationship_p.h" #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactRelationship \brief The QContactRelationship class describes a one-to-one relationship between a locally-stored contact and another (possibly remote) contact. \inmodule QtContacts \ingroup contacts-main Each relationship is uniquely identified by the combination of the first contact, second contact, and the relationship type. A relationship should not contain a second contact which is the same as the first contact. Any local contacts which are referenced in the relationship (that is, any source contact, or any second contact whose manager URI is left empty or whose manager URI references the manager that stores the source contact, and in which the relationship will be saved) should exist. If any of these requirements are not met, validation of the relationship may fail when attempting to save the relationship in a QContactManager. \sa QContactRelationshipFilter */ /*! \enum QContactRelationship::Role Describes the roles that a contact may take in a relationship. \value First The contact is the first contact in the relationship \value Second The contact is the second contact in the relationship \value Either The contact is either the first or second contact in the relationship */ /*! * \fn QContactRelationship::HasMember() * The relationship type which identifies the first contact as being a group which includes the second contact */ /*! * \fn QContactRelationship::Aggregates() * The relationship type which identifies the first contact as aggregating the second contact into a metacontact */ /*! * \fn QContactRelationship::IsSameAs() * The relationship type which identifies the first contact as being the same contact as the second contact */ /*! * \fn QContactRelationship::HasAssistant() * The relationship type which identifies the second contact as being the assistant of the first contact */ /*! * \fn QContactRelationship::HasManager() * The relationship type which identifies the second contact as being the manager of the first contact */ /*! * \fn QContactRelationship::HasSpouse() * The relationship type which identifies the second contact as being the spouse of the first contact */ /*! * Constructs a new relationship */ QContactRelationship::QContactRelationship() : d(new QContactRelationshipPrivate) { } /*! * Frees the memory in use by the relationship */ QContactRelationship::~QContactRelationship() { } /*! * Creates a copy of the \a other relationship */ QContactRelationship::QContactRelationship(const QContactRelationship& other) : d(other.d) { } /*! * Assigns this relationship to be equal to \a other */ QContactRelationship& QContactRelationship::operator=(const QContactRelationship& other) { d = other.d; return *this; } /*! * Returns true if this relationship is equal to the \a other relationship, otherwise returns false. */ bool QContactRelationship::operator==(const QContactRelationship &other) const { if (d.constData()->m_first != other.d.constData()->m_first) return false; if (d.constData()->m_second != other.d.constData()->m_second) return false; if (d.constData()->m_relationshipType != other.d.constData()->m_relationshipType) return false; return true; } /*! * Returns the hash value for \a key. */ uint qHash(const QContactRelationship &key) { return qHash(key.first()) + qHash(key.second()) + QT_PREPEND_NAMESPACE(qHash)(key.relationshipType()); } #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QContactRelationship& rel) { dbg.nospace() << "QContactRelationship(" << rel.first() << ' ' << rel.relationshipType() << ' ' << rel.second() << ')'; return dbg.maybeSpace(); } #endif #ifndef QT_NO_DATASTREAM /*! * Writes \a rel to the stream \a out. */ QDataStream& operator<<(QDataStream& out, const QContactRelationship& rel) { quint8 formatVersion = 1; // Version of QDataStream format for QContactRelationship return out << formatVersion << rel.first() << rel.relationshipType() << rel.second(); } /*! * Reads a contact relationship from stream \a in into \a rel. */ QDataStream& operator>>(QDataStream& in, QContactRelationship& rel) { rel = QContactRelationship(); quint8 formatVersion; in >> formatVersion; if (formatVersion == 1) { QContact first; QString relationshipType; QContact second; in >> first >> relationshipType >> second; rel.setFirst(first); rel.setRelationshipType(relationshipType); rel.setSecond(second); } else { in.setStatus(QDataStream::ReadCorruptData); } return in; } #endif /*! * \fn QContactRelationship::operator!=(const QContactRelationship& other) const * Returns true if this relationship is not equal to \a other, otherwise returns false. */ /*! * Returns the locally-stored contact which has a relationship of the given type with the second contact * \sa relationshipType(), second(), setFirst() */ QContact QContactRelationship::first() const { return d.constData()->m_first; } /*! * Returns the contact with which the first contact has a relationship of the given type * \sa relationshipType(), first() */ QContact QContactRelationship::second() const { return d.constData()->m_second; } /*! * Returns the type of relationship which the source contact has with the destination contacts * \sa setRelationshipType() */ QString QContactRelationship::relationshipType() const { return d.constData()->m_relationshipType; } /*! * Sets the first contact in the relationship to \a firstContact. This contact * must be stored in the manager in which the relationship is stored, and has * a relationship of the specified type with the second contact. * \sa first() */ void QContactRelationship::setFirst(const QContact& firstContact) { d->m_first = firstContact; } /*! * Sets the second contact in the relationship to \a secondContact. The first contact * has a relationship of the specified type with this contact. * \sa second() */ void QContactRelationship::setSecond(const QContact& secondContact) { d->m_second = secondContact; } /*! * Sets the type of relationship that the source contact has with the destination contacts * to \a relationshipType. * \sa relationshipType() */ void QContactRelationship::setRelationshipType(const QString& relationshipType) { d->m_relationshipType = relationshipType; } QT_END_NAMESPACE_CONTACTS src/contacts/qcontactrelationship.h000066400000000000000000000076051233466112000201000ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTRELATIONSHIP_H #define QCONTACTRELATIONSHIP_H #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContact; class QContactRelationshipPrivate; class Q_CONTACTS_EXPORT QContactRelationship { public: inline static const QString HasMember() {return QStringLiteral("HasMember");} inline static const QString Aggregates() {return QStringLiteral("Aggregates");} inline static const QString IsSameAs() {return QStringLiteral("IsSameAs");} inline static const QString HasAssistant() {return QStringLiteral("HasAssistant");} inline static const QString HasManager() {return QStringLiteral("HasManager");} inline static const QString HasSpouse() {return QStringLiteral("HasSpouse");} QContactRelationship(); ~QContactRelationship(); QContactRelationship(const QContactRelationship& other); QContactRelationship& operator=(const QContactRelationship& other); bool operator==(const QContactRelationship &other) const; bool operator!=(const QContactRelationship &other) const { return !(*this==other); } QContact first() const; QContact second() const; QString relationshipType() const; void setFirst(const QContact& firstContact); void setSecond(const QContact& secondContact); void setRelationshipType(const QString& relationshipType); enum Role { First = 0, Second, Either }; private: QSharedDataPointer d; }; Q_CONTACTS_EXPORT uint qHash(const QContactRelationship& key); #ifndef QT_NO_DEBUG_STREAM Q_CONTACTS_EXPORT QDebug operator<<(QDebug dbg, const QContactRelationship& rel); #endif #ifndef QT_NO_DATASTREAM Q_CONTACTS_EXPORT QDataStream& operator<<(QDataStream& out, const QContactRelationship& rel); Q_CONTACTS_EXPORT QDataStream& operator>>(QDataStream& in, QContactRelationship& rel); #endif QT_END_NAMESPACE_CONTACTS #endif // QCONTACTRELATIONSHIP_H src/contacts/qcontactrelationship_p.h000066400000000000000000000057131233466112000204150ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTRELATIONSHIP_P_H #define QCONTACTRELATIONSHIP_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactRelationshipPrivate : public QSharedData { public: QContactRelationshipPrivate() : QSharedData() { } QContactRelationshipPrivate(const QContactRelationshipPrivate& other) : QSharedData(other), m_first(other.m_first), m_second(other.m_second), m_relationshipType(other.m_relationshipType) { } ~QContactRelationshipPrivate() { } QContact m_first; QContact m_second; QString m_relationshipType; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTRELATIONSHIP_P_H src/contacts/qcontacts.h000066400000000000000000000073231233466112000156360ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QTCONTACTS_H #define QTCONTACTS_H // this file includes all of the public header files // provided by the Qt Contacts API #include // global exports #include // manager #include // manager backend #include // manage backend instantiator #include // contact #include // contact identifier #include // contact observer #include // contact detail #include // leaf detail classes #include // backend optimization hint class #include // contact filter #include // leaf filter classes #include // contact sorting #include // actions #include // action descriptors #include // action factory #include // action target (contact + detail(s)) #include // asynchronous request #include // request leaf classes #include // contact relationships QT_BEGIN_NAMESPACE_CONTACTS QT_END_NAMESPACE_CONTACTS #endif // QTCONTACTS_H src/contacts/qcontactsglobal.h000066400000000000000000000060071233466112000170150ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QTCONTACTSGLOBAL_H #define QTCONTACTSGLOBAL_H #include #if defined(QT_NAMESPACE) # define QTCONTACTS_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::QtContacts::name # define QT_BEGIN_NAMESPACE_CONTACTS namespace QT_NAMESPACE { namespace QtContacts { # define QT_END_NAMESPACE_CONTACTS } } # define QTCONTACTS_USE_NAMESPACE using namespace QT_NAMESPACE; using namespace QtContacts; #else # define QTCONTACTS_PREPEND_NAMESPACE(name) ::QtContacts::name # define QT_BEGIN_NAMESPACE_CONTACTS namespace QtContacts { # define QT_END_NAMESPACE_CONTACTS } # define QTCONTACTS_USE_NAMESPACE using namespace QtContacts; #endif #ifndef QT_STATIC # if defined(QT_BUILD_CONTACTS_LIB) # define Q_CONTACTS_EXPORT Q_DECL_EXPORT # else # define Q_CONTACTS_EXPORT Q_DECL_IMPORT # endif #else # define Q_CONTACTS_EXPORT #endif #define QTCONTACTS_VERSION_NAME "org.qt-project.Qt.contacts.api.version" #define QTCONTACTS_IMPLEMENTATION_VERSION_NAME "org.qt-project.Qt.contacts.implementation.version" QT_BEGIN_NAMESPACE_CONTACTS QT_END_NAMESPACE_CONTACTS #endif // QTCONTACTSGLOBAL_H src/contacts/qcontactsortorder.cpp000066400000000000000000000212141233466112000177450ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactsortorder.h" #include "qcontactsortorder_p.h" #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactSortOrder \brief The QContactSortOrder class defines how a list of contacts should be ordered according to some criteria \inmodule QtContacts */ /*! * \enum QContactSortOrder::BlankPolicy * Enumerates the ways in which the sort order interprets blanks when sorting contacts * \value BlanksFirst Considers blank values to evaluate to less than all other values in comparisons * \value BlanksLast Considers blank values to evaluate to greater than all other values in comparisons */ /*! * \fn QContactSortOrder::operator QList() const * Constructs a new list of sort orders containing only the current sort order */ /*! * \fn QContactSortOrder::operator!=(const QContactSortOrder& other) const * Returns true if this sort order is not identical to the \a other sort order * \sa operator==() */ /*! * Constructs a new sort order */ QContactSortOrder::QContactSortOrder() : d(new QContactSortOrderPrivate()) { } /*! * Frees any memory in use by this sort order */ QContactSortOrder::~QContactSortOrder() { } /*! * Constructs a copy of the \a other sort order */ QContactSortOrder::QContactSortOrder(const QContactSortOrder& other) : d(other.d) { } /*! * Assigns this sort order to be equal to \a other */ QContactSortOrder& QContactSortOrder::operator=(const QContactSortOrder& other) { if (this != &other) { d = other.d; } return *this; } /*! * Returns true if the sort order is able to be used to sort a list of contacts; otherwise, returns false */ bool QContactSortOrder::isValid() const { return d->m_type != QContactDetail::TypeUndefined; } /*! * Returns true if this sort order is identical to the \a other sort order * \sa operator!=() */ bool QContactSortOrder::operator ==(const QContactSortOrder& other) const { if (d.constData()->m_blankPolicy == other.d.constData()->m_blankPolicy && d.constData()->m_direction == other.d.constData()->m_direction && d.constData()->m_sensitivity == other.d.constData()->m_sensitivity && d.constData()->m_type == other.d.constData()->m_type && d.constData()->m_field == other.d.constData()->m_field) return true; return false; } #ifndef QT_NO_DATASTREAM /*! * Writes \a sortOrder to the stream \a out. */ QDataStream& operator<<(QDataStream& out, const QContactSortOrder& sortOrder) { quint8 formatVersion = 1; // Version of QDataStream format for QContactSortOrder return out << formatVersion << static_cast(sortOrder.detailType()) << sortOrder.detailField() << static_cast(sortOrder.blankPolicy()) << static_cast(sortOrder.direction()) << static_cast(sortOrder.caseSensitivity()); } /*! * Reads a sort order from stream \a in into \a sortOrder. */ QDataStream& operator>>(QDataStream& in, QContactSortOrder& sortOrder) { sortOrder = QContactSortOrder(); quint8 formatVersion; in >> formatVersion; if (formatVersion == 1) { quint32 type; int field; quint32 blankPolicy; quint32 direction; quint32 caseSensitivity; in >> type >> field >> blankPolicy >> direction >> caseSensitivity; sortOrder.setDetailType(QContactDetail::DetailType(type), field); sortOrder.setBlankPolicy(QContactSortOrder::BlankPolicy(blankPolicy)); sortOrder.setDirection(Qt::SortOrder(direction)); sortOrder.setCaseSensitivity(Qt::CaseSensitivity(caseSensitivity)); } else { in.setStatus(QDataStream::ReadCorruptData); } return in; } #endif #ifndef QT_NO_DEBUG_STREAM /*! Outputs \a sortOrder to the debug stream \a dbg */ QDebug operator<<(QDebug dbg, const QContactSortOrder& sortOrder) { dbg.nospace() << "QContactSortOrder(" << "detailType=" << static_cast(sortOrder.detailType()) << "," << "detailField=" << sortOrder.detailField() << "," << "blankPolicy=" << static_cast(sortOrder.blankPolicy()) << "," << "direction=" << static_cast(sortOrder.direction()) << "," << "caseSensitivity=" << static_cast(sortOrder.caseSensitivity()) << ")"; return dbg.maybeSpace(); } #endif /*! * Sets the type of the details which will be inspected to perform sorting to \a type * and the name of those details' fields which contains the value which contacts will be sorted by to \a field * * If \a field is not specified, or equal to -1, the contact with a detail of the specified type * would appear before or after the contact that lacks a detail of the specified type, * according to blankPolicy(). * * \sa detailType(), detailField() */ void QContactSortOrder::setDetailType(QContactDetail::DetailType type, int field) { d->m_type = type; d->m_field = field; } /*! * Sets the sort order's policy on blank values with respect to sorting to \a blankPolicy * \sa blankPolicy() */ void QContactSortOrder::setBlankPolicy(BlankPolicy blankPolicy) { d->m_blankPolicy = blankPolicy; } /*! * Sets the sort order direction to \a direction * \sa direction() */ void QContactSortOrder::setDirection(Qt::SortOrder direction) { d->m_direction = direction; } /*! * Returns the type of the details which will be inspected to perform sorting. * Note that if a contact has multiple details of the definition, the result of the sorting * is undefined. * \sa setDetailType() */ QContactDetail::DetailType QContactSortOrder::detailType() const { return d.constData()->m_type; } /*! * Returns the detail field which the sorting order will be based on. * \sa setDetailType() */ int QContactSortOrder::detailField() const { return d.constData()->m_field; } /*! * Returns the blank policy of the sort order * \sa setBlankPolicy() */ QContactSortOrder::BlankPolicy QContactSortOrder::blankPolicy() const { return d.constData()->m_blankPolicy; } /*! * Returns the direction of the sort order * \sa setDirection() */ Qt::SortOrder QContactSortOrder::direction() const { return d.constData()->m_direction; } /*! * Returns the case sensitivity of the sort order * \sa setCaseSensitivity() */ Qt::CaseSensitivity QContactSortOrder::caseSensitivity() const { return d.constData()->m_sensitivity; } /*! * Sets the case sensitivity of the sort order to \a sensitivity * \sa caseSensitivity() */ void QContactSortOrder::setCaseSensitivity(Qt::CaseSensitivity sensitivity) { d->m_sensitivity = sensitivity; } QT_END_NAMESPACE_CONTACTS src/contacts/qcontactsortorder.h000066400000000000000000000074031233466112000174160ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTSORTORDER_H #define QCONTACTSORTORDER_H #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactSortOrderPrivate; class Q_CONTACTS_EXPORT QContactSortOrder { public: QContactSortOrder(); ~QContactSortOrder(); QContactSortOrder(const QContactSortOrder& other); QContactSortOrder& operator=(const QContactSortOrder& other); enum BlankPolicy { BlanksFirst, BlanksLast, }; /* Mutators */ void setDetailType(QContactDetail::DetailType type, int field = -1); void setBlankPolicy(BlankPolicy blankPolicy); void setDirection(Qt::SortOrder direction); void setCaseSensitivity(Qt::CaseSensitivity sensitivity); /* Accessors */ QContactDetail::DetailType detailType() const; int detailField() const; BlankPolicy blankPolicy() const; Qt::SortOrder direction() const; Qt::CaseSensitivity caseSensitivity() const; bool isValid() const; bool operator==(const QContactSortOrder& other) const; bool operator!=(const QContactSortOrder& other) const {return !operator==(other);} /* Convenience cast */ operator QList() const {return QList() << *this;} private: QSharedDataPointer d; }; #ifndef QT_NO_DATASTREAM Q_CONTACTS_EXPORT QDataStream& operator<<(QDataStream& out, const QContactSortOrder& sortOrder); Q_CONTACTS_EXPORT QDataStream& operator>>(QDataStream& in, QContactSortOrder& sortOrder); #endif #ifndef QT_NO_DEBUG_STREAM Q_CONTACTS_EXPORT QDebug operator<<(QDebug dbg, const QContactSortOrder& sortOrder); #endif QT_END_NAMESPACE_CONTACTS QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(QTCONTACTS_PREPEND_NAMESPACE(QContactSortOrder), Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif // QCONTACTSORTORDER_H src/contacts/qcontactsortorder_p.h000066400000000000000000000060001233466112000177250ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTSORTORDER_P_H #define QCONTACTSORTORDER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactSortOrderPrivate : public QSharedData { public: QContactSortOrderPrivate() : QSharedData() , m_blankPolicy(QContactSortOrder::BlanksLast) , m_direction(Qt::AscendingOrder) , m_sensitivity(Qt::CaseSensitive) , m_type(QContactDetail::TypeUndefined) , m_field(-1) { } ~QContactSortOrderPrivate() { } QContactSortOrder::BlankPolicy m_blankPolicy; Qt::SortOrder m_direction; Qt::CaseSensitivity m_sensitivity; QContactDetail::DetailType m_type; int m_field; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTSORTORDER_P_H src/contacts/qcontactspluginsearch_p.h000066400000000000000000000122361233466112000205610ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTSPLUGINSEARCH_H #define QCONTACTSPLUGINSEARCH_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #if !defined QT_NO_DEBUG #include #endif #include #include #include QT_BEGIN_NAMESPACE_CONTACTS #define CHECKDIR(dir) (dir).exists() inline QStringList mobilityPlugins(const QString& plugintype) { #if !defined QT_NO_DEBUG const bool showDebug = qgetenv("QT_DEBUG_PLUGINS").toInt() > 0; #endif QStringList paths = QCoreApplication::libraryPaths(); #ifdef QTM_PLUGIN_PATH paths << QLatin1String(QTM_PLUGIN_PATH); #endif #if !defined QT_NO_DEBUG if (showDebug) qDebug() << "Plugin paths:" << paths; #endif // Temp variable to avoid multiple identical paths // (we don't convert the list to set first, because that loses the order) QSet processed; /* The list of discovered plugins */ QStringList plugins; /* Enumerate our plugin paths */ for (int i=0; i < paths.count(); i++) { if (processed.contains(paths.at(i))) continue; processed.insert(paths.at(i)); QDir pluginsDir(paths.at(i)); if (!CHECKDIR(pluginsDir)) continue; #if defined(Q_OS_WIN) if (pluginsDir.dirName().toLower() == QLatin1String("debug") || pluginsDir.dirName().toLower() == QLatin1String("release")) pluginsDir.cdUp(); #elif defined(Q_OS_MAC) if (pluginsDir.dirName() == QLatin1String("MacOS")) { pluginsDir.cdUp(); pluginsDir.cdUp(); pluginsDir.cdUp(); } #endif QString subdir(QStringLiteral("plugins/")); subdir += plugintype; if (pluginsDir.path().endsWith(QStringLiteral("/plugins")) || pluginsDir.path().endsWith(QStringLiteral("/plugins/"))) subdir = plugintype; if (CHECKDIR(QDir(pluginsDir.filePath(subdir)))) { pluginsDir.cd(subdir); QStringList files = pluginsDir.entryList(QDir::Files); #if !defined QT_NO_DEBUG if (showDebug) qDebug() << "Looking for " << plugintype << " plugins in" << pluginsDir.path() << files; #endif for (int j=0; j < files.count(); j++) { plugins << pluginsDir.absoluteFilePath(files.at(j)); } } } /* Add application path + plugintype */ QDir appldir(QCoreApplication::applicationDirPath()); if(appldir.cd(plugintype)){ if (!processed.contains(appldir.absolutePath())){ processed.insert(appldir.absolutePath()); QStringList files = appldir.entryList(QDir::Files); #if !defined QT_NO_DEBUG if (showDebug) qDebug() << "Looking for " << plugintype << " plugins in" << appldir.path() << files; #endif for (int j=0; j < files.count(); j++) { plugins << appldir.absoluteFilePath(files.at(j)); } } } return plugins; } QT_END_NAMESPACE_CONTACTS #endif // QCONTACTSPLUGINSEARCH_H src/contacts/requests/000077500000000000000000000000001233466112000153345ustar00rootroot00000000000000src/contacts/requests/qcontactfetchbyidrequest.cpp000066400000000000000000000133101233466112000231450ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactfetchbyidrequest.h" #include "qcontactrequests_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactFetchByIdRequest \brief The QContactFetchByIdRequest class allows a client to asynchronously request contacts from a contacts store manager, given a list of contact IDs. The contacts fetched by the backend should have a one-to-one correspondence to the IDs passed into this class. That is, the nth contact in the returned list should have an ID which is equal to the nth ID in the list of IDs. Any invalid ID should correspond to an empty QContact. For a QContactFetchByIdRequest, the resultsAvailable() signal will be emitted when the resultant contacts (which may be retrieved by calling contacts()), are updated, as well as if the overall operation error (which may be retrieved by calling error()) is updated. Please see the class documentation of QContactAbstractRequest for more information about the usage of request classes and ownership semantics. \inmodule QtContacts \ingroup contacts-requests */ /*! Constructs a new contact fetch request whose parent is the specified \a parent */ QContactFetchByIdRequest::QContactFetchByIdRequest(QObject* parent) : QContactAbstractRequest(new QContactFetchByIdRequestPrivate, parent) { } /*! Frees any memory used by this request */ QContactFetchByIdRequest::~QContactFetchByIdRequest() { } /*! Sets the list of ids of the contacts that the backend should retrieve to \a ids. */ void QContactFetchByIdRequest::setIds(const QList& ids) { Q_D(QContactFetchByIdRequest); QMutexLocker ml(&d->m_mutex); d->m_contactIds = ids; } /*! Sets the fetch hint which may be used by the backend to optimize contact retrieval to \a fetchHint. A client should not make changes to a contact which has been retrieved using a fetch hint other than the default fetch hint. Doing so will result in information loss when saving the contact back to the manager (as the "new" restricted contact will replace the previously saved contact in the backend). \sa QContactFetchHint */ void QContactFetchByIdRequest::setFetchHint(const QContactFetchHint &fetchHint) { Q_D(QContactFetchByIdRequest); QMutexLocker ml(&d->m_mutex); d->m_fetchHint = fetchHint; } /*! Returns the list of ids of the contacts that the backend should retrieve. */ QList QContactFetchByIdRequest::contactIds() const { Q_D(const QContactFetchByIdRequest); QMutexLocker ml(&d->m_mutex); return d->m_contactIds; } /*! Returns the fetch hint which may be used by the backend to optimize contact retrieval. A client should not make changes to a contact which has been retrieved using a fetch hint other than the default fetch hint. Doing so will result in information loss when saving the contact back to the manager (as the "new" restricted contact will replace the previously saved contact in the backend). \sa QContactFetchHint */ QContactFetchHint QContactFetchByIdRequest::fetchHint() const { Q_D(const QContactFetchByIdRequest); QMutexLocker ml(&d->m_mutex); return d->m_fetchHint; } /*! Returns the list of contacts retrieved by this request */ QList QContactFetchByIdRequest::contacts() const { Q_D(const QContactFetchByIdRequest); QMutexLocker ml(&d->m_mutex); return d->m_contacts; } /*! Returns the map of input definition list indices to errors which occurred. In case of global errors like QContactManager::TimeoutError affecting to all items in the input definitions the error map may be empty. */ QMap QContactFetchByIdRequest::errorMap() const { Q_D(const QContactFetchByIdRequest); QMutexLocker ml(&d->m_mutex); return d->m_errors; } #include "moc_qcontactfetchbyidrequest.cpp" QT_END_NAMESPACE_CONTACTS src/contacts/requests/qcontactfetchbyidrequest.h000066400000000000000000000057711233466112000226260ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTFETCHBYIDREQUEST_H #define QCONTACTFETCHBYIDREQUEST_H #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactFetchHint; class QContactFetchByIdRequestPrivate; class Q_CONTACTS_EXPORT QContactFetchByIdRequest : public QContactAbstractRequest { Q_OBJECT public: QContactFetchByIdRequest(QObject* parent = 0); ~QContactFetchByIdRequest(); /* Selection, restriction and sorting */ void setIds(const QList& ids); void setFetchHint(const QContactFetchHint& fetchHint); QList contactIds() const; QContactFetchHint fetchHint() const; /* Results */ QList contacts() const; QMap errorMap() const; private: Q_DISABLE_COPY(QContactFetchByIdRequest) friend class QContactManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QContactFetchByIdRequest) }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTFETCHBYIDREQUEST_H src/contacts/requests/qcontactfetchrequest.cpp000066400000000000000000000146651233466112000223130ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactfetchrequest.h" #include "qcontactrequests_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactFetchRequest \brief The QContactFetchRequest class allows a client to asynchronously request contacts from a contacts store manager. For a QContactFetchRequest, the resultsAvailable() signal will be emitted when the resultant contacts (which may be retrieved by calling contacts()), are updated, as well as if the overall operation error (which may be retrieved by calling error()) is updated. Please see the class documentation of QContactAbstractRequest for more information about the usage of request classes and ownership semantics. \inmodule QtContacts \ingroup contacts-requests */ /*! Constructs a new contact fetch request whose parent is the specified \a parent */ QContactFetchRequest::QContactFetchRequest(QObject* parent) : QContactAbstractRequest(new QContactFetchRequestPrivate, parent) { } /*! Frees any memory used by this request */ QContactFetchRequest::~QContactFetchRequest() { } /*! Sets the storage location from where the contacts will be retrieved. \a storageLocations is a flag so it is possible to define multiple locations in it. However, some backend implementations may accept only one flag to be set. In case multiple flags are set fetching is done from the default storage location. \sa QContactAbstractRequest::StorageLocation \sa QContactAbstractRequest::StorageLocations */ void QContactFetchRequest::setStorageLocations(QContactAbstractRequest::StorageLocations storageLocations) { Q_D(QContactFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_storageLocations = storageLocations; } /*! Returns the storage locations from where the contacts will be retrieved. \sa QContactAbstractRequest::StorageLocation \sa QContactAbstractRequest::StorageLocations */ QContactAbstractRequest::StorageLocations QContactFetchRequest::storageLocations() const { Q_D(const QContactFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_storageLocations; } /*! Sets the contact filter used to determine which contacts will be retrieved to \a filter. */ void QContactFetchRequest::setFilter(const QContactFilter& filter) { Q_D(QContactFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_filter = filter; } /*! Sets the sort order of the result to \a sorting. Only has an effect if called prior to calling \c start() */ void QContactFetchRequest::setSorting(const QList& sorting) { Q_D(QContactFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_sorting = sorting; } /*! Sets the fetch hint which may be used by the backend to optimize contact retrieval to \a fetchHint. A client should not make changes to a contact which has been retrieved using a fetch hint other than the default fetch hint. Doing so will result in information loss when saving the contact back to the manager (as the "new" restricted contact will replace the previously saved contact in the backend). \sa QContactFetchHint */ void QContactFetchRequest::setFetchHint(const QContactFetchHint &fetchHint) { Q_D(QContactFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_fetchHint = fetchHint; } /*! Returns the filter that will be used to select contacts to be returned */ QContactFilter QContactFetchRequest::filter() const { Q_D(const QContactFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_filter; } /*! Returns the sort ordering that will be used sort the results of this request */ QList QContactFetchRequest::sorting() const { Q_D(const QContactFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_sorting; } /*! Returns the fetch hint which may be used by the backend to optimize contact retrieval. A client should not make changes to a contact which has been retrieved using a fetch hint other than the default fetch hint. Doing so will result in information loss when saving the contact back to the manager (as the "new" restricted contact will replace the previously saved contact in the backend). \sa QContactFetchHint */ QContactFetchHint QContactFetchRequest::fetchHint() const { Q_D(const QContactFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_fetchHint; } /*! Returns the list of contacts retrieved by this request */ QList QContactFetchRequest::contacts() const { Q_D(const QContactFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_contacts; } #include "moc_qcontactfetchrequest.cpp" QT_END_NAMESPACE_CONTACTS src/contacts/requests/qcontactfetchrequest.h000066400000000000000000000063261233466112000217530ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTFETCHREQUEST_H #define QCONTACTFETCHREQUEST_H #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactFetchHint; class QContactFilter; class QContactFetchRequestPrivate; class Q_CONTACTS_EXPORT QContactFetchRequest : public QContactAbstractRequest { Q_OBJECT public: QContactFetchRequest(QObject* parent = 0); ~QContactFetchRequest(); /* Selection, restriction and sorting */ void setFilter(const QContactFilter& filter); void setSorting(const QList& sorting); void setFetchHint(const QContactFetchHint& fetchHint); QContactFilter filter() const; QList sorting() const; QContactFetchHint fetchHint() const; /* Results */ QList contacts() const; // Storage location setter and getter void setStorageLocations(QContactAbstractRequest::StorageLocations storageLocations); QContactAbstractRequest::StorageLocations storageLocations() const; private: Q_DISABLE_COPY(QContactFetchRequest) friend class QContactManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QContactFetchRequest) }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTFETCHREQUEST_H src/contacts/requests/qcontactidfetchrequest.cpp000066400000000000000000000123261233466112000226200ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactidfetchrequest.h" #include "qcontactrequests_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactIdFetchRequest \brief The QContactIdFetchRequest class allows a client to asynchronously request a list of contact ids from a contacts store manager. For a QContactIdFetchRequest, the resultsAvailable() signal will be emitted when the resultant contact ids (which may be retrieved by calling ids()), are updated, as well as if the overall operation error (which may be retrieved by calling error()) is updated. Please see the class documentation of QContactAbstractRequest for more information about the usage of request classes and ownership semantics. \inmodule QtContacts \ingroup contacts-requests */ /*! Constructs a new contact id fetch request whose parent is the specified \a parent */ QContactIdFetchRequest::QContactIdFetchRequest(QObject* parent) : QContactAbstractRequest(new QContactIdFetchRequestPrivate, parent) { } /*! Frees any memory used by this request */ QContactIdFetchRequest::~QContactIdFetchRequest() { } /*! Sets the storage location from where the contact ids will be retrieved. \a storageLocations is a flag so it is possible to define multiple locations in it. \sa QContactAbstractRequest::StorageLocation \sa QContactAbstractRequest::StorageLocations */ void QContactIdFetchRequest::setStorageLocations(QContactAbstractRequest::StorageLocations storageLocations) { Q_D(QContactIdFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_storageLocations = storageLocations; } /*! Returns the storage locations from where the contact ids will be retrieved. \sa QContactAbstractRequest::StorageLocation \sa QContactAbstractRequest::StorageLocations */ QContactAbstractRequest::StorageLocations QContactIdFetchRequest::storageLocations() const { Q_D(const QContactIdFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_storageLocations; } /*! Sets the filter which will be used to select the contacts whose ids will be returned to \a filter */ void QContactIdFetchRequest::setFilter(const QContactFilter& filter) { Q_D(QContactIdFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_filter = filter; } /*! Sets the future sort ordering of the result of the request to \a sorting. This function only has effect on the result if called prior to calling \c start() */ void QContactIdFetchRequest::setSorting(const QList& sorting) { Q_D(QContactIdFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_sorting = sorting; } /*! Returns the filter which will be used to select the contacts whose ids will be returned */ QContactFilter QContactIdFetchRequest::filter() const { Q_D(const QContactIdFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_filter; } /*! Returns the sort ordering which will be used to sort the result */ QList QContactIdFetchRequest::sorting() const { Q_D(const QContactIdFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_sorting; } /*! Returns the list of ids of contacts which matched the request */ QList QContactIdFetchRequest::ids() const { Q_D(const QContactIdFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_ids; } #include "moc_qcontactidfetchrequest.cpp" QT_END_NAMESPACE_CONTACTS src/contacts/requests/qcontactidfetchrequest.h000066400000000000000000000061521233466112000222650ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTIDFETCHREQUEST_H #define QCONTACTIDFETCHREQUEST_H #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactFilter; class QContactIdFetchRequestPrivate; class Q_CONTACTS_EXPORT QContactIdFetchRequest : public QContactAbstractRequest { Q_OBJECT public: QContactIdFetchRequest(QObject* parent = 0); ~QContactIdFetchRequest(); /* Selection, restriction and sorting */ void setFilter(const QContactFilter& filter); void setSorting(const QList& sorting); QContactFilter filter() const; QList sorting() const; /* Results */ QList ids() const; // Storage location setter and getter void setStorageLocations(QContactAbstractRequest::StorageLocations storageLocations); QContactAbstractRequest::StorageLocations storageLocations() const; private: Q_DISABLE_COPY(QContactIdFetchRequest) friend class QContactManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QContactIdFetchRequest) }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTIDFETCHREQUEST_H src/contacts/requests/qcontactrelationshipfetchrequest.cpp000066400000000000000000000126501233466112000247250ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactrelationshipfetchrequest.h" #include "qcontactrequests_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactRelationshipFetchRequest \brief The QContactRelationshipFetchRequest class allows a client to asynchronously request relationships from a contacts store manager. For a QContactRelationshipFetchRequest, the resultsAvailable() signal will be emitted when the resultant relationships (which may be retrieved by calling relationships()), are updated, as well as if the overall operation error (which may be retrieved by calling error()) is updated. Please see the class documentation of QContactAbstractRequest for more information about the usage of request classes and ownership semantics. \inmodule QtContacts \ingroup contacts-requests */ /*! Constructs a new relationship fetch request whose parent is the specified \a parent */ QContactRelationshipFetchRequest::QContactRelationshipFetchRequest(QObject* parent) : QContactAbstractRequest(new QContactRelationshipFetchRequestPrivate, parent) { } /*! Frees any memory used by this request */ QContactRelationshipFetchRequest::~QContactRelationshipFetchRequest() { } /*! Sets the source contact criterion of the fetch request to \a firstContact. If \a firstContact is empty, or the first contact is not set, the request will fetch relationships involving any first contact. */ void QContactRelationshipFetchRequest::setFirst(const QContact& firstContact) { Q_D(QContactRelationshipFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_first = firstContact; } /*! Returns the source contact criterion of the fetch request */ QContact QContactRelationshipFetchRequest::first() const { Q_D(const QContactRelationshipFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_first; } /*! Sets the relationship type criterion of the fetch request to \a relationshipType. If \a relationshipType is empty, or the relationship type is not set, the request will fetch relationships of any type. */ void QContactRelationshipFetchRequest::setRelationshipType(const QString& relationshipType) { Q_D(QContactRelationshipFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_relationshipType = relationshipType; } /*! Returns the relationship type criterion of the fetch request */ QString QContactRelationshipFetchRequest::relationshipType() const { Q_D(const QContactRelationshipFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_relationshipType; } /*! Sets the destination contact criterion of the fetch request to \a secondContact. If \a secondContact is the default-constructed empty contact or the second contact is not set, the request will fetch relationships involving any second contact. */ void QContactRelationshipFetchRequest::setSecond(const QContact& secondContact) { Q_D(QContactRelationshipFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_second = secondContact; } /*! Returns the destination contact criterion of the fetch request */ QContact QContactRelationshipFetchRequest::second() const { Q_D(const QContactRelationshipFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_second; } /*! Returns the list of relationships that was the result of the request */ QList QContactRelationshipFetchRequest::relationships() const { Q_D(const QContactRelationshipFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_relationships; } #include "moc_qcontactrelationshipfetchrequest.cpp" QT_END_NAMESPACE_CONTACTS src/contacts/requests/qcontactrelationshipfetchrequest.h000066400000000000000000000060171233466112000243720ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTRELATIONSHIPFETCHREQUEST_H #define QCONTACTRELATIONSHIPFETCHREQUEST_H #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContact; class QContactRelationshipFetchRequestPrivate; class Q_CONTACTS_EXPORT QContactRelationshipFetchRequest : public QContactAbstractRequest { Q_OBJECT public: QContactRelationshipFetchRequest(QObject* parent = 0); ~QContactRelationshipFetchRequest(); /* Selection */ void setFirst(const QContact& firstContact); QContact first() const; void setRelationshipType(const QString& relationshipType); QString relationshipType() const; void setSecond(const QContact& secondContact); QContact second() const; /* Results */ QList relationships() const; private: Q_DISABLE_COPY(QContactRelationshipFetchRequest) friend class QContactManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QContactRelationshipFetchRequest) }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTRELATIONSHIPFETCHREQUEST_H src/contacts/requests/qcontactrelationshipremoverequest.cpp000066400000000000000000000107011233466112000251240ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactrelationshipremoverequest.h" #include "qcontactrequests_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactRelationshipRemoveRequest \brief The QContactRelationshipRemoveRequest class allows a client to asynchronously request that certain relationships be removed from a contacts store. For a QContactRelationshipRemoveRequest, the resultsUpdated() signal will be emitted when the individual item errors (which may be retrieved by calling errorMap()) are updated, or if the overall operation error (which may be retrieved by calling error()) is updated. Please see the class documentation of QContactAbstractRequest for more information about the usage of request classes and ownership semantics. \inmodule QtContacts \ingroup contacts-requests */ /*! Constructs a new relationship remove request whose parent is the specified \a parent */ QContactRelationshipRemoveRequest::QContactRelationshipRemoveRequest(QObject* parent) : QContactAbstractRequest(new QContactRelationshipRemoveRequestPrivate, parent) { } /*! Frees any memory used by this request */ QContactRelationshipRemoveRequest::~QContactRelationshipRemoveRequest() { } /*! Sets the relationship which will be removed to \a relationship. Equivalent to calling: \code setRelationships(QList() << relationship); \endcode */ void QContactRelationshipRemoveRequest::setRelationship(const QContactRelationship& relationship) { Q_D(QContactRelationshipRemoveRequest); QMutexLocker ml(&d->m_mutex); d->m_relationships.clear(); d->m_relationships.append(relationship); } /*! Sets the list of relationships which will be removed to \a relationships */ void QContactRelationshipRemoveRequest::setRelationships(const QList& relationships) { Q_D(QContactRelationshipRemoveRequest); QMutexLocker ml(&d->m_mutex); d->m_relationships = relationships; } /*! Returns the list of relationships which will be removed */ QList QContactRelationshipRemoveRequest::relationships() const { Q_D(const QContactRelationshipRemoveRequest); QMutexLocker ml(&d->m_mutex); return d->m_relationships; } /*! Returns the map of input contact list indices to errors which occurred */ QMap QContactRelationshipRemoveRequest::errorMap() const { Q_D(const QContactRelationshipRemoveRequest); QMutexLocker ml(&d->m_mutex); return d->m_errors; } #include "moc_qcontactrelationshipremoverequest.cpp" QT_END_NAMESPACE_CONTACTS src/contacts/requests/qcontactrelationshipremoverequest.h000066400000000000000000000057451233466112000246050ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTRELATIONSHIPREMOVEREQUEST_H #define QCONTACTRELATIONSHIPREMOVEREQUEST_H #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactRelationshipRemoveRequestPrivate; class Q_CONTACTS_EXPORT QContactRelationshipRemoveRequest : public QContactAbstractRequest { Q_OBJECT public: QContactRelationshipRemoveRequest(QObject* parent = 0); ~QContactRelationshipRemoveRequest(); /* Selection */ void setRelationship(const QContactRelationship& relationship); void setRelationships(const QList& relationships); QList relationships() const; /* Results */ QMap errorMap() const; private: Q_DISABLE_COPY(QContactRelationshipRemoveRequest) friend class QContactManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QContactRelationshipRemoveRequest) }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTRELATIONSHIPREMOVEREQUEST_H src/contacts/requests/qcontactrelationshipsaverequest.cpp000066400000000000000000000112141233466112000245650ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactrelationshipsaverequest.h" #include "qcontactrequests_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactRelationshipSaveRequest \brief The QContactRelationshipSaveRequest class allows a client to asynchronously request that certain groups be saved to a contacts store. For a QContactRelationshipSaveRequest, the resultsAvailable() signal will be emitted when either the individual item errors (which may be retrieved by calling errorMap()), or the resultant relationships (which may be retrieved by calling relationships()), are updated, as well as if the overall operation error (which may be retrieved by calling error()) is updated. Please see the class documentation of QContactAbstractRequest for more information about the usage of request classes and ownership semantics. \inmodule QtContacts \ingroup contacts-requests */ /*! Constructs a new relationship save request whose parent is the specified \a parent */ QContactRelationshipSaveRequest::QContactRelationshipSaveRequest(QObject* parent) : QContactAbstractRequest(new QContactRelationshipSaveRequestPrivate, parent) { } /*! Frees any memory used by this request */ QContactRelationshipSaveRequest::~QContactRelationshipSaveRequest() { } /*! Sets the relationship to save to be \a contactRelationship. Equivalent to calling: \code setRelationships(QList() << contactRelationships); \endcode */ void QContactRelationshipSaveRequest::setRelationship(const QContactRelationship& contactRelationship) { Q_D(QContactRelationshipSaveRequest); QMutexLocker ml(&d->m_mutex); d->m_relationships.clear(); d->m_relationships.append(contactRelationship); } /*! Sets the relationships to save to be \a contactRelationships */ void QContactRelationshipSaveRequest::setRelationships(const QList& contactRelationships) { Q_D(QContactRelationshipSaveRequest); QMutexLocker ml(&d->m_mutex); d->m_relationships = contactRelationships; } /*! Returns the list of relationships that will be saved if called prior to calling \c start(), otherwise returns the list of relationships as they were saved in the contacts store */ QList QContactRelationshipSaveRequest::relationships() const { Q_D(const QContactRelationshipSaveRequest); QMutexLocker ml(&d->m_mutex); return d->m_relationships; } /*! Returns the map of input relationship list indices to errors which occurred */ QMap QContactRelationshipSaveRequest::errorMap() const { Q_D(const QContactRelationshipSaveRequest); QMutexLocker ml(&d->m_mutex); return d->m_errors; } #include "moc_qcontactrelationshipsaverequest.cpp" QT_END_NAMESPACE_CONTACTS src/contacts/requests/qcontactrelationshipsaverequest.h000066400000000000000000000057411233466112000242420ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTRELATIONSHIPSAVEREQUEST_H #define QCONTACTRELATIONSHIPSAVEREQUEST_H #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactRelationshipSaveRequestPrivate; class Q_CONTACTS_EXPORT QContactRelationshipSaveRequest : public QContactAbstractRequest { Q_OBJECT public: QContactRelationshipSaveRequest(QObject* parent = 0); ~QContactRelationshipSaveRequest(); /* Selection */ void setRelationship(const QContactRelationship& contactRelationship); void setRelationships(const QList& contactRelationships); /* Results */ QList relationships() const; QMap errorMap() const; private: Q_DISABLE_COPY(QContactRelationshipSaveRequest) friend class QContactManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QContactRelationshipSaveRequest) }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTRELATIONSHIPSAVEREQUEST_H src/contacts/requests/qcontactremoverequest.cpp000066400000000000000000000102221233466112000225000ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactremoverequest.h" #include "qcontactrequests_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactRemoveRequest \brief The QContactRemoveRequest class allows a client to asynchronously request that certain contacts be removed from a contacts store. For a QContactRemoveRequest, the resultsUpdated() signal will be emitted when the individual item errors (which may be retrieved by calling errorMap()) are updated, or if the overall operation error (which may be retrieved by calling error()) is updated. Please see the class documentation of QContactAbstractRequest for more information about the usage of request classes and ownership semantics. \inmodule QtContacts \ingroup contacts-requests */ /*! Constructs a new contact remove request whose parent is the specified \a parent */ QContactRemoveRequest::QContactRemoveRequest(QObject* parent) : QContactAbstractRequest(new QContactRemoveRequestPrivate, parent) { } /*! Frees any memory used by this request */ QContactRemoveRequest::~QContactRemoveRequest() { } /*! Sets the id of the contact which will be removed to \a contactId. Equivalent to calling: \code setContactIds(QList() << contactIds); \endcode */ void QContactRemoveRequest::setContactId(const QContactId& contactId) { Q_D(QContactRemoveRequest); QMutexLocker ml(&d->m_mutex); d->m_contactIds.clear(); d->m_contactIds.append(contactId); } /*! Sets the list of ids of contacts which will be removed to \a contactIds */ void QContactRemoveRequest::setContactIds(const QList& contactIds) { Q_D(QContactRemoveRequest); QMutexLocker ml(&d->m_mutex); d->m_contactIds = contactIds; } /*! Returns the list of ids of contacts which will be removed */ QList QContactRemoveRequest::contactIds() const { Q_D(const QContactRemoveRequest); QMutexLocker ml(&d->m_mutex); return d->m_contactIds; } /*! Returns the map of input contact list indices to errors which occurred */ QMap QContactRemoveRequest::errorMap() const { Q_D(const QContactRemoveRequest); QMutexLocker ml(&d->m_mutex); return d->m_errors; } #include "moc_qcontactremoverequest.cpp" QT_END_NAMESPACE_CONTACTS src/contacts/requests/qcontactremoverequest.h000066400000000000000000000055021233466112000221520ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTREMOVEREQUEST_H #define QCONTACTREMOVEREQUEST_H #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactRemoveRequestPrivate; class Q_CONTACTS_EXPORT QContactRemoveRequest : public QContactAbstractRequest { Q_OBJECT public: QContactRemoveRequest(QObject* parent = 0); ~QContactRemoveRequest(); /* Selection */ void setContactId(const QContactId& contactId); void setContactIds(const QList& contactIds); QList contactIds() const; /* Results */ QMap errorMap() const; private: Q_DISABLE_COPY(QContactRemoveRequest) friend class QContactManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QContactRemoveRequest) }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTREMOVEREQUEST_H src/contacts/requests/qcontactrequests.h000066400000000000000000000050771233466112000211260ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTREQUESTS_H #define QCONTACTREQUESTS_H // this file includes all of the asynchronous request // leaf classes that are included in the public API #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS QT_END_NAMESPACE_CONTACTS #endif // QCONTACTREQUESTS_H src/contacts/requests/qcontactrequests_p.h000066400000000000000000000227361233466112000214460ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTREQUESTS_P_H #define QCONTACTREQUESTS_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #ifndef QT_NO_DEBUG_STREAM #include #endif #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactSaveRequestPrivate : public QContactAbstractRequestPrivate { public: QContactSaveRequestPrivate() : QContactAbstractRequestPrivate(QContactAbstractRequest::ContactSaveRequest), m_storageLocation(QContactAbstractRequest::UserDataStorage) { } ~QContactSaveRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QContactSaveRequest("; dbg.nospace() << "contacts=" << m_contacts << "," << "typeMask=" << m_typeMask << "," << "errorMap=" << m_errors << "," << "storageLocation=" << m_storageLocation; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif QList m_contacts; QMap m_errors; QList m_typeMask; QContactAbstractRequest::StorageLocation m_storageLocation; }; class QContactFetchRequestPrivate : public QContactAbstractRequestPrivate { public: QContactFetchRequestPrivate() : QContactAbstractRequestPrivate(QContactAbstractRequest::ContactFetchRequest), m_storageLocations(QContactAbstractRequest::UserDataStorage) { } ~QContactFetchRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QContactFetchRequest(" << "filter=" << m_filter << "," << "sorting=" << m_sorting << "," << "fetchHint=" << m_fetchHint << "," << "storageLocations=" << m_storageLocations; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif QContactFilter m_filter; QList m_sorting; QContactFetchHint m_fetchHint; QList m_contacts; QContactAbstractRequest::StorageLocations m_storageLocations; }; class QContactFetchByIdRequestPrivate : public QContactAbstractRequestPrivate { public: QContactFetchByIdRequestPrivate() : QContactAbstractRequestPrivate(QContactAbstractRequest::ContactFetchByIdRequest) { } ~QContactFetchByIdRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QContactFetchByIdRequest(\n" << "* contactIds=" << m_contactIds << ",\n" << "* contacts=" << m_contacts << ",\n" << "* fetchHint=" << m_fetchHint << ",\n" << "* errorMap=" << m_errors; dbg.nospace() << "\n)"; return dbg.maybeSpace(); } #endif QList m_contactIds; QContactFetchHint m_fetchHint; QList m_contacts; QMap m_errors; }; class QContactRemoveRequestPrivate : public QContactAbstractRequestPrivate { public: QContactRemoveRequestPrivate() : QContactAbstractRequestPrivate(QContactAbstractRequest::ContactRemoveRequest) { } ~QContactRemoveRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QContactRemoveRequest(" << "contactIds=" << m_contactIds << "," << "errorMap=" << m_errors; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif QList m_contactIds; QMap m_errors; }; class QContactIdFetchRequestPrivate : public QContactAbstractRequestPrivate { public: QContactIdFetchRequestPrivate() : QContactAbstractRequestPrivate(QContactAbstractRequest::ContactIdFetchRequest), m_storageLocations(QContactAbstractRequest::UserDataStorage) { } ~QContactIdFetchRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QContactIdFetchRequest(" << "filter=" << m_filter << "," << "sorting=" << m_sorting << "," << "ids=" << m_ids << "," << "storageLocations=" << m_storageLocations; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif QContactFilter m_filter; QList m_sorting; QList m_ids; QContactAbstractRequest::StorageLocations m_storageLocations; }; class QContactRelationshipFetchRequestPrivate : public QContactAbstractRequestPrivate { public: QContactRelationshipFetchRequestPrivate() : QContactAbstractRequestPrivate(QContactAbstractRequest::RelationshipFetchRequest) { } ~QContactRelationshipFetchRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QContactRelationshipFetchRequest(" << "first=" << m_first << "," << "second=" << m_second << "," << "relationshipType=" << m_relationshipType << "," << "relationships=" << m_relationships; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif // selection criteria QContact m_first; QContact m_second; QString m_relationshipType; // results QList m_relationships; }; class QContactRelationshipSaveRequestPrivate : public QContactAbstractRequestPrivate { public: QContactRelationshipSaveRequestPrivate() : QContactAbstractRequestPrivate(QContactAbstractRequest::RelationshipSaveRequest) { } ~QContactRelationshipSaveRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QContactRelationshipSaveRequest(" << "relationships=" << m_relationships << "," << "errorMap=" << m_errors; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif QList m_relationships; QMap m_errors; }; class QContactRelationshipRemoveRequestPrivate : public QContactAbstractRequestPrivate { public: QContactRelationshipRemoveRequestPrivate() : QContactAbstractRequestPrivate(QContactAbstractRequest::RelationshipRemoveRequest) { } ~QContactRelationshipRemoveRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QContactRelationshipRemoveRequest(" << "relationships=" << m_relationships << "," << "errorMap=" << m_errors; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif QList m_relationships; QMap m_errors; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTREQUESTS_P_H src/contacts/requests/qcontactsaverequest.cpp000066400000000000000000000160561233466112000221540ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactsaverequest.h" #include "qcontactrequests_p.h" QT_BEGIN_NAMESPACE_CONTACTS /*! \class QContactSaveRequest \brief The QContactSaveRequest class allows a client to asynchronously request that certain contacts be saved to a contacts store. For a QContactSaveRequest, the resultsAvailable() signal will be emitted when either the individual item errors (which may be retrieved by calling errorMap()), or the resultant contacts (which may be retrieved by calling contacts()), are updated, as well as if the overall operation error (which may be retrieved by calling error()) is updated. Please see the class documentation of QContactAbstractRequest for more information about the usage of request classes and ownership semantics. \inmodule QtContacts \ingroup contacts-requests */ /*! Constructs a new contact save request whose parent is the specified \a parent */ QContactSaveRequest::QContactSaveRequest(QObject* parent) : QContactAbstractRequest(new QContactSaveRequestPrivate, parent) { } /*! Frees any memory used by this request */ QContactSaveRequest::~QContactSaveRequest() { } /*! Sets the \a storageLocation where the new contacts will be saved to. This is ignored for contacts which have already been saved. Instead, those contacts are saved back to the storage location where they were originally stored. \sa QContactAbstractRequest::StorageLocation \sa storageLocation() */ void QContactSaveRequest::setStorageLocation(QContactAbstractRequest::StorageLocation storageLocation) { Q_D(QContactSaveRequest); QMutexLocker ml(&d->m_mutex); d->m_storageLocation = storageLocation; } /*! Returns the storage location where the contacts will be saved to. This is ignored for contacts which have already been saved. Instead, those contacts are saved back to the storage location where they were originally stored. \sa QContactAbstractRequest::StorageLocation \sa setStorageLocation() */ QContactAbstractRequest::StorageLocation QContactSaveRequest::storageLocation() const { Q_D(const QContactSaveRequest); QMutexLocker ml(&d->m_mutex); return d->m_storageLocation; } /*! Sets the contact to be saved to \a contact. Equivalent to calling: \code setContacts(QList() << contact); \endcode */ void QContactSaveRequest::setContact(const QContact& contact) { Q_D(QContactSaveRequest); QMutexLocker ml(&d->m_mutex); d->m_contacts.clear(); d->m_contacts.append(contact); } /*! Sets the list of contacts to be saved to \a contacts */ void QContactSaveRequest::setContacts(const QList& contacts) { Q_D(QContactSaveRequest); QMutexLocker ml(&d->m_mutex); d->m_contacts = contacts; } /*! Returns the list of contacts which will be saved if called prior to calling \c start(), otherwise returns the list of contacts with their ids set appropriately (successfully saved new contacts will have an id assigned). */ QList QContactSaveRequest::contacts() const { Q_D(const QContactSaveRequest); QMutexLocker ml(&d->m_mutex); return d->m_contacts; } /*! Returns the map of input contact list indices to errors which occurred */ QMap QContactSaveRequest::errorMap() const { Q_D(const QContactSaveRequest); QMutexLocker ml(&d->m_mutex); return d->m_errors; } /*! Set the list of definitions to restrict saving to \a typeMask. This allows you to perform partial save (and remove) operations on existing contacts. If \a typeMask is empty (the default), no restrictions will apply, and the passed in contacts will be saved as is. Otherwise, only details whose types are in the typeMask will be saved. If a type is present in the list, but there are no corresponding details in the contact passed into this request, any existing details in the manager for that contact will be removed. This is useful if you've used a fetch hint to fetch a partial contact from a manager so that you can save changes to the details you actually fetched without removing the details you didn't. Additionally, when performing synchronization operations with other managers that don't support the full range of details, you can restrict the update operation to only those details so that you don't lose the extra details that are supported in this manager. \note Some managers do not support partial updates natively, in which case the QtContacts framework will emulate the functionality (fetching the whole contact, applying the new restricted details, and saving the contact back). */ void QContactSaveRequest::setTypeMask(const QList &typeMask) { Q_D(QContactSaveRequest); QMutexLocker ml(&d->m_mutex); d->m_typeMask = typeMask; } /*! Returns the list of definitions that this request will operate on. If the list is empty, the request will operate on all details. */ QList QContactSaveRequest::typeMask() const { Q_D(const QContactSaveRequest); QMutexLocker ml(&d->m_mutex); return d->m_typeMask; } #include "moc_qcontactsaverequest.cpp" QT_END_NAMESPACE_CONTACTS src/contacts/requests/qcontactsaverequest.h000066400000000000000000000062171233466112000216170ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTSAVEREQUEST_H #define QCONTACTSAVEREQUEST_H #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactSaveRequestPrivate; class Q_CONTACTS_EXPORT QContactSaveRequest : public QContactAbstractRequest { Q_OBJECT public: QContactSaveRequest(QObject* parent = 0); ~QContactSaveRequest(); /* Selection */ void setContact(const QContact& contact); void setContacts(const QList& contacts); void setTypeMask(const QList &typeMask); QList typeMask() const; /* Results */ QList contacts() const; QMap errorMap() const; // Storage location setter and getter void setStorageLocation(QContactAbstractRequest::StorageLocation storageLocation); QContactAbstractRequest::StorageLocation storageLocation() const; private: Q_DISABLE_COPY(QContactSaveRequest) friend class QContactManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QContactSaveRequest) }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTSAVEREQUEST_H src/contacts/requests/requests.pri000066400000000000000000000015251233466112000177260ustar00rootroot00000000000000INCLUDEPATH += requests PUBLIC_HEADERS += \ requests/qcontactfetchrequest.h \ requests/qcontactfetchbyidrequest.h \ requests/qcontactidfetchrequest.h \ requests/qcontactsaverequest.h \ requests/qcontactremoverequest.h \ requests/qcontactrelationshipfetchrequest.h \ requests/qcontactrelationshipremoverequest.h \ requests/qcontactrelationshipsaverequest.h \ requests/qcontactrequests.h PRIVATE_HEADERS += \ requests/qcontactrequests_p.h SOURCES += \ requests/qcontactfetchrequest.cpp \ requests/qcontactfetchbyidrequest.cpp \ requests/qcontactidfetchrequest.cpp \ requests/qcontactsaverequest.cpp \ requests/qcontactremoverequest.cpp \ requests/qcontactrelationshipfetchrequest.cpp \ requests/qcontactrelationshipremoverequest.cpp \ requests/qcontactrelationshipsaverequest.cpp src/imports/000077500000000000000000000000001233466112000133405ustar00rootroot00000000000000src/imports/contacts/000077500000000000000000000000001233466112000151565ustar00rootroot00000000000000src/imports/contacts/contacts.json000066400000000000000000000000031233466112000176600ustar00rootroot00000000000000{} src/imports/contacts/contacts.pro000066400000000000000000000015751233466112000175260ustar00rootroot00000000000000QT += qml contacts versit include(details/details.pri) include(filters/filters.pri) HEADERS += qdeclarativecontactmodel_p.h \ qdeclarativecontact_p.h \ qdeclarativecontactdetail_p.h \ qdeclarativecontactfilter_p.h \ qdeclarativecontactsortorder_p.h \ qdeclarativecontactfetchhint_p.h \ qdeclarativecontactrelationship_p.h \ qdeclarativecontactrelationshipmodel_p.h \ SOURCES += plugin.cpp \ qdeclarativecontactmodel.cpp \ qdeclarativecontact.cpp \ qdeclarativecontactdetail.cpp \ qdeclarativecontactfilter.cpp \ qdeclarativecontactsortorder.cpp \ qdeclarativecontactfetchhint.cpp \ qdeclarativecontactrelationship.cpp \ qdeclarativecontactrelationshipmodel.cpp \ RESOURCES += contacts.qrc OTHER_FILES += contacts.json DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 load(qml_plugin) src/imports/contacts/contacts.qrc000066400000000000000000000001321233466112000174770ustar00rootroot00000000000000 default.svg src/imports/contacts/default.svg000066400000000000000000000111141233466112000173210ustar00rootroot00000000000000 src/imports/contacts/details/000077500000000000000000000000001233466112000166035ustar00rootroot00000000000000src/imports/contacts/details/details.pri000066400000000000000000000026361233466112000207530ustar00rootroot00000000000000INCLUDEPATH += details \ ./ HEADERS += \ details/qdeclarativecontactaddress_p.h \ details/qdeclarativecontactanniversary_p.h \ details/qdeclarativecontactavatar_p.h \ details/qdeclarativecontactbirthday_p.h \ details/qdeclarativecontactdetails_p.h \ details/qdeclarativecontactdisplaylabel_p.h \ details/qdeclarativecontactemailaddress_p.h \ details/qdeclarativecontactfamily_p.h \ details/qdeclarativecontactfavorite_p.h \ details/qdeclarativecontactgender_p.h \ details/qdeclarativecontactgeolocation_p.h \ details/qdeclarativecontactglobalpresence_p.h \ details/qdeclarativecontactguid_p.h \ details/qdeclarativecontactname_p.h \ details/qdeclarativecontactnickname_p.h \ details/qdeclarativecontactnote_p.h \ details/qdeclarativecontactonlineaccount_p.h \ details/qdeclarativecontactorganization_p.h \ details/qdeclarativecontactphonenumber_p.h \ details/qdeclarativecontactpresence_p.h \ details/qdeclarativecontactringtone_p.h \ details/qdeclarativecontactsynctarget_p.h \ details/qdeclarativecontacttag_p.h \ details/qdeclarativecontacttimestamp_p.h \ details/qdeclarativecontacttype_p.h \ details/qdeclarativecontacturl_p.h \ details/qdeclarativecontactversion_p.h \ details/qdeclarativecontacthobby_p.h \ details/qdeclarativecontactextendeddetail_p.h SOURCES += \ details/qdeclarativecontactmoc_p.cpp src/imports/contacts/details/qdeclarativecontactaddress_p.h000066400000000000000000000142461233466112000246700ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTADDRESS_H #define QDECLARATIVECONTACTADDRESS_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactAddress : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString street READ street WRITE setStreet NOTIFY valueChanged) Q_PROPERTY(QString locality READ locality WRITE setLocality NOTIFY valueChanged) Q_PROPERTY(QString region READ region WRITE setRegion NOTIFY valueChanged) Q_PROPERTY(QString postcode READ postcode WRITE setPostcode NOTIFY valueChanged) Q_PROPERTY(QString country READ country WRITE setCountry NOTIFY valueChanged) Q_PROPERTY(QList subTypes READ subTypes WRITE setSubTypes NOTIFY valueChanged) Q_PROPERTY(QString postOfficeBox READ postOfficeBox WRITE setPostOfficeBox NOTIFY valueChanged) Q_ENUMS(FieldType) Q_ENUMS(AddressSubType) public: enum FieldType { Street = QContactAddress::FieldStreet, Locality = QContactAddress::FieldLocality, Region = QContactAddress::FieldRegion, Postcode = QContactAddress::FieldPostcode, Country = QContactAddress::FieldCountry, SubTypes = QContactAddress::FieldSubTypes, PostOfficeBox = QContactAddress::FieldPostOfficeBox }; enum AddressSubType { Parcel = QContactAddress::SubTypeParcel, Postal = QContactAddress::SubTypePostal, Domestic = QContactAddress::SubTypeDomestic, International = QContactAddress::SubTypeInternational }; QDeclarativeContactAddress(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactAddress()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Address; } void setStreet(const QString& v) { if (!readOnly() && v != street()) { detail().setValue(QContactAddress::FieldStreet, v); emit valueChanged(); } } QString street() const {return detail().value(QContactAddress::FieldStreet).toString();} void setLocality(const QString& v) { if (!readOnly() && v != locality()) { detail().setValue(QContactAddress::FieldLocality, v); emit valueChanged(); } } QString locality() const {return detail().value(QContactAddress::FieldLocality).toString();} void setRegion(const QString& v) { if (!readOnly() && v != region()) { detail().setValue(QContactAddress::FieldRegion, v); emit valueChanged(); } } QString region() const {return detail().value(QContactAddress::FieldRegion).toString();} void setPostcode(const QString& v) { if (!readOnly() && v != postcode()) { detail().setValue(QContactAddress::FieldPostcode, v); emit valueChanged(); } } QString postcode() const {return detail().value(QContactAddress::FieldPostcode).toString();} void setCountry(const QString& v) { if (!readOnly() && v != country()) { detail().setValue(QContactAddress::FieldCountry, v); emit valueChanged(); } } QString country() const {return detail().value(QContactAddress::FieldCountry).toString();} void setPostOfficeBox(const QString& v) { if (!readOnly() && v != postOfficeBox()) { detail().setValue(QContactAddress::FieldPostOfficeBox, v); emit valueChanged(); } } QString postOfficeBox() const {return detail().value(QContactAddress::FieldPostOfficeBox).toString();} void setSubTypes(const QList& subTypes) { QList oldList = detail().value< QList >(QContactAddress::FieldSubTypes); if (!readOnly() && subTypes.toSet() != oldList.toSet()) { detail().setValue(QContactAddress::FieldSubTypes, QVariant::fromValue(subTypes)); emit valueChanged(); } } QList subTypes() const { return detail().value< QList >(QContactAddress::FieldSubTypes); } signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactAddress) #endif // QDECLARATIVECONTACTADDRESS_H src/imports/contacts/details/qdeclarativecontactanniversary_p.h000066400000000000000000000125501233466112000256000ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTANNIVERSARY_H #define QDECLARATIVECONTACTANNIVERSARY_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactAnniversary : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString calendarId READ calendarId WRITE setCalendarId NOTIFY valueChanged) Q_PROPERTY(QDate originalDate READ originalDate WRITE setOriginalDate NOTIFY valueChanged) Q_PROPERTY(QDateTime originalDateTime READ originalDateTime WRITE setOriginalDateTime NOTIFY valueChanged) Q_PROPERTY(QString event READ event WRITE setEvent NOTIFY valueChanged) Q_PROPERTY(AnniversarySubType subType READ subType WRITE setSubType NOTIFY valueChanged) Q_ENUMS(FieldType) Q_ENUMS(AnniversarySubType) public: enum FieldType { CalendarId = QContactAnniversary::FieldCalendarId, OriginalDate = QContactAnniversary::FieldOriginalDate, Event = QContactAnniversary::FieldEvent, SubType = QContactAnniversary::FieldSubType }; enum AnniversarySubType { Wedding = QContactAnniversary::SubTypeWedding, Engagement = QContactAnniversary::SubTypeEngagement, House = QContactAnniversary::SubTypeHouse, Employment = QContactAnniversary::SubTypeEmployment, Memorial = QContactAnniversary::SubTypeMemorial }; QDeclarativeContactAnniversary(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactAnniversary()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Anniversary; } void setOriginalDate(const QDate& v) { if (!readOnly() && v != originalDate()) { detail().setValue(QContactAnniversary::FieldOriginalDate, v); emit valueChanged(); } } QDate originalDate() const {return detail().value(QContactAnniversary::FieldOriginalDate);} void setOriginalDateTime(const QDateTime& v) { if (!readOnly() && v != originalDateTime()) { detail().setValue(QContactAnniversary::FieldOriginalDate, v); emit valueChanged(); } } QDateTime originalDateTime() const {return detail().value(QContactAnniversary::FieldOriginalDate);} void setCalendarId(const QString& v) { if (!readOnly() && v != calendarId()) { detail().setValue(QContactAnniversary::FieldCalendarId, v); emit valueChanged(); } } QString calendarId() const {return detail().value(QContactAnniversary::FieldCalendarId).toString();} void setEvent(const QString& v) { if (!readOnly() && v != event()) { detail().setValue(QContactAnniversary::FieldEvent, v); emit valueChanged(); } } QString event() const {return detail().value(QContactAnniversary::FieldEvent).toString();} void setSubType(AnniversarySubType v) { if (!readOnly() && v != subType()) { detail().setValue(QContactAnniversary::FieldSubType, v); } } AnniversarySubType subType() const { return static_cast(detail().value(QContactAnniversary::FieldSubType)); } signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactAnniversary) #endif // QDECLARATIVECONTACTANNIVERSARY_H src/imports/contacts/details/qdeclarativecontactavatar_p.h000066400000000000000000000070351233466112000245170ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTAVATAR_H #define QDECLARATIVECONTACTAVATAR_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactAvatar : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QUrl imageUrl READ imageUrl WRITE setImageUrl NOTIFY valueChanged) Q_PROPERTY(QUrl videoUrl READ videoUrl WRITE setVideoUrl NOTIFY valueChanged) Q_ENUMS(FieldType) public: enum FieldType { ImageUrl = QContactAvatar::FieldImageUrl, VideoUrl = QContactAvatar::FieldVideoUrl }; QDeclarativeContactAvatar(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactAvatar()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } void setImageUrl(const QUrl& v) { if (!readOnly() && v != imageUrl()) { detail().setValue(QContactAvatar::FieldImageUrl, v); emit valueChanged(); } } QUrl imageUrl() const {return detail().value(QContactAvatar::FieldImageUrl);} void setVideoUrl(const QUrl& v) { if (!readOnly() && v != videoUrl()) { detail().setValue(QContactAvatar::FieldVideoUrl, v); emit valueChanged(); } } QUrl videoUrl() const {return detail().value(QContactAvatar::FieldVideoUrl);} DetailType detailType() const { return QDeclarativeContactDetail::Avatar; } signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactAvatar) #endif // QDECLARATIVECONTACTAVATAR_H src/imports/contacts/details/qdeclarativecontactbirthday_p.h000066400000000000000000000062421233466112000250460ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTBIRTHDAY_H #define QDECLARATIVECONTACTBIRTHDAY_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactBirthday : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QDateTime birthday READ birthday WRITE setBirthday NOTIFY valueChanged) Q_ENUMS(FieldType) public: enum FieldType { Birthday = QContactBirthday::FieldBirthday }; QDeclarativeContactBirthday(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactBirthday()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Birthday; } void setBirthday(const QDateTime& v) { if (!readOnly() && v != birthday()) { detail().setValue(QContactBirthday::FieldBirthday, v); emit valueChanged(); } } QDateTime birthday() const {return detail().value(QContactBirthday::FieldBirthday);} signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactBirthday) #endif // QDECLARATIVECONTACTBIRTHDAY_H src/imports/contacts/details/qdeclarativecontactdetails_p.h000066400000000000000000000064721233466112000246720ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTDETAILS_H #define QDECLARATIVECONTACTDETAILS_H // this file includes all of the leaf detail classes // provided by the Qt Contacts API. #include "qdeclarativecontactaddress_p.h" #include "qdeclarativecontactanniversary_p.h" #include "qdeclarativecontactavatar_p.h" #include "qdeclarativecontactbirthday_p.h" #include "qdeclarativecontactdisplaylabel_p.h" #include "qdeclarativecontactemailaddress_p.h" #include "qdeclarativecontactextendeddetail_p.h" #include "qdeclarativecontactfamily_p.h" #include "qdeclarativecontactfavorite_p.h" #include "qdeclarativecontactgender_p.h" #include "qdeclarativecontactgeolocation_p.h" #include "qdeclarativecontactglobalpresence_p.h" #include "qdeclarativecontactguid_p.h" #include "qdeclarativecontactname_p.h" #include "qdeclarativecontactnickname_p.h" #include "qdeclarativecontactnote_p.h" #include "qdeclarativecontactonlineaccount_p.h" #include "qdeclarativecontactorganization_p.h" #include "qdeclarativecontactphonenumber_p.h" #include "qdeclarativecontactpresence_p.h" #include "qdeclarativecontactringtone_p.h" #include "qdeclarativecontactsynctarget_p.h" #include "qdeclarativecontacttag_p.h" #include "qdeclarativecontacttimestamp_p.h" #include "qdeclarativecontacttype_p.h" #include "qdeclarativecontacturl_p.h" #include "qdeclarativecontactversion_p.h" #include "qdeclarativecontacthobby_p.h" #endif // QDECLARATIVECONTACTDETAILS_H src/imports/contacts/details/qdeclarativecontactdisplaylabel_p.h000066400000000000000000000063321233466112000257050ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTDISPLAYLABEL_H #define QDECLARATIVECONTACTDISPLAYLABEL_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactDisplayLabel : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY valueChanged) Q_ENUMS(FieldType) Q_CLASSINFO("DefaultProperty", "label") public: enum FieldType { Label = QContactDisplayLabel::FieldLabel }; QDeclarativeContactDisplayLabel(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactDisplayLabel()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::DisplayLabel; } void setLabel(const QString &v) { if (!readOnly() && v != label()) { detail().setValue(QContactDisplayLabel::FieldLabel, v); emit valueChanged(); } } QString label() const {return detail().value(QContactDisplayLabel::FieldLabel).toString();} signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactDisplayLabel) #endif // QDECLARATIVECONTACTDISPLAYLABEL_H src/imports/contacts/details/qdeclarativecontactemailaddress_p.h000066400000000000000000000063541233466112000257010ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTEMAILADDRESS_H #define QDECLARATIVECONTACTEMAILADDRESS_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactEmailAddress : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString emailAddress READ emailAddress WRITE setEmailAddress NOTIFY valueChanged) Q_ENUMS(FieldType) public: enum FieldType { EmailAddress = QContactEmailAddress::FieldEmailAddress }; QDeclarativeContactEmailAddress(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactEmailAddress()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Email; } void setEmailAddress(const QString& v) { if (!readOnly() && v != emailAddress()) { detail().setValue(QContactEmailAddress::FieldEmailAddress, v); emit valueChanged(); } } QString emailAddress() const {return detail().value(QContactEmailAddress::FieldEmailAddress).toString();} signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactEmailAddress) #endif // QDECLARATIVECONTACTEMAILADDRESS_H src/imports/contacts/details/qdeclarativecontactextendeddetail_p.h000066400000000000000000000072751233466112000262320ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTEXTENDEDDETAIL_P_H #define QDECLARATIVECONTACTEXTENDEDDETAIL_P_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactExtendedDetail : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY valueChanged) Q_PROPERTY(QVariant data READ data WRITE setData NOTIFY valueChanged) Q_ENUMS(FieldType) public: enum FieldType { Name = QContactExtendedDetail::FieldName, Data = QContactExtendedDetail::FieldData }; QDeclarativeContactExtendedDetail (QObject *parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactExtendedDetail()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::ExtendedDetail; } void setName(const QString &newDetailName) { if (newDetailName != name() && !readOnly()) { detail().setValue(QContactExtendedDetail::FieldName, newDetailName); emit valueChanged(); } } QString name() const { return detail().value(QContactExtendedDetail::FieldName).toString(); } void setData(const QVariant &newData) { if (newData != data() && !readOnly()) { detail().setValue(QContactExtendedDetail::FieldData, newData); emit valueChanged(); } } QVariant data() const { return detail().value(QContactExtendedDetail::FieldData); } signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactExtendedDetail) #endif // QDECLARATIVECONTACTEXTENDEDDETAIL_P_H src/imports/contacts/details/qdeclarativecontactfamily_p.h000066400000000000000000000070771233466112000245300ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTFAMILY_H #define QDECLARATIVECONTACTFAMILY_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactFamily : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString spouse READ spouse WRITE setSpouse NOTIFY valueChanged) Q_PROPERTY(QStringList children READ children WRITE setChildren NOTIFY valueChanged) Q_ENUMS(FieldType) public: enum FieldType { Spouse = QContactFamily::FieldSpouse, Children = QContactFamily::FieldChildren }; QDeclarativeContactFamily(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactFamily()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Family; } void setSpouse(const QString& v) { if (!readOnly() && v != spouse()) { detail().setValue(QContactFamily::FieldSpouse, v); emit valueChanged(); } } QString spouse() const {return detail().value(QContactFamily::FieldSpouse).toString();} void setChildren(const QStringList& v) { if (!readOnly() && v.toSet() != children().toSet()) { detail().setValue(QContactFamily::FieldChildren, v); emit valueChanged(); } } QStringList children() const {return detail().value(QContactFamily::FieldChildren);} signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactFamily) #endif // QDECLARATIVECONTACTFAMILY_H src/imports/contacts/details/qdeclarativecontactfavorite_p.h000066400000000000000000000070771233466112000250660ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTFAVORITE_H #define QDECLARATIVECONTACTFAVORITE_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactFavorite : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(bool favorite READ isFavorite WRITE setFavorite NOTIFY valueChanged) Q_PROPERTY(int index READ index WRITE setIndex NOTIFY valueChanged) Q_ENUMS(FieldType) Q_CLASSINFO("DefaultProperty", "index") public: enum FieldType { Favorite = QContactFavorite::FieldFavorite, Index = QContactFavorite::FieldIndex }; QDeclarativeContactFavorite(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactFavorite()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Favorite; } void setFavorite(bool v) { if (!readOnly() && v != isFavorite()) { detail().setValue(QContactFavorite::FieldFavorite, v); emit valueChanged(); } } bool isFavorite() const {return detail().value(QContactFavorite::FieldFavorite).toBool();} void setIndex(int v) { if (!readOnly() && v != index()) { detail().setValue(QContactFavorite::FieldIndex, v); emit valueChanged(); } } int index() const {return detail().value(QContactFavorite::FieldIndex).toInt();} signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactFavorite) #endif // QDECLARATIVECONTACTFAVORITE_H src/imports/contacts/details/qdeclarativecontactgender_p.h000066400000000000000000000072461233466112000245110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTGENDER_H #define QDECLARATIVECONTACTGENDER_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactGender : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(GenderType gender READ gender WRITE setGender NOTIFY valueChanged) Q_ENUMS(FieldType) Q_ENUMS(GenderType) Q_CLASSINFO("DefaultProperty", "gender") public: enum FieldType { Gender = QContactGender::FieldGender }; enum GenderType { Male = QContactGender::GenderMale, Female = QContactGender::GenderFemale, Unspecified = QContactGender::GenderUnspecified }; QDeclarativeContactGender(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactGender()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Gender; } void setGender(const GenderType v) { if (!readOnly() && v != gender()) { switch (v) { case Male: case Female: detail().setValue(QContactGender::FieldGender, v); break; default: detail().setValue(QContactGender::FieldGender, Unspecified); } emit valueChanged(); } } GenderType gender() const { if (detail().value(QContactGender::FieldGender) == QContactGender::GenderMale) { return Male; } return Female; } signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactGender) #endif // QDECLARATIVECONTACTGENDER_H src/imports/contacts/details/qdeclarativecontactgeolocation_p.h000066400000000000000000000154671233466112000255540ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTGEOLOCATION_H #define QDECLARATIVECONTACTGEOLOCATION_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactGeoLocation : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY valueChanged) Q_PROPERTY(double latitude READ latitude WRITE setLatitude NOTIFY valueChanged) Q_PROPERTY(double longitude READ longitude WRITE setLongitude NOTIFY valueChanged) Q_PROPERTY(double accuracy READ accuracy WRITE setAccuracy NOTIFY valueChanged) Q_PROPERTY(double altitude READ altitude WRITE setAltitude NOTIFY valueChanged) Q_PROPERTY(double altitudeAccuracy READ altitudeAccuracy WRITE setAltitudeAccuracy NOTIFY valueChanged) Q_PROPERTY(double heading READ heading WRITE setHeading NOTIFY valueChanged) Q_PROPERTY(double speed READ speed WRITE setSpeed NOTIFY valueChanged) Q_PROPERTY(QDateTime timestamp READ timestamp WRITE setTimestamp NOTIFY valueChanged) Q_CLASSINFO("DefaultProperty", "label") Q_ENUMS(FieldType) public: enum FieldType { Label = QContactGeoLocation::FieldLabel, Latitude = QContactGeoLocation::FieldLatitude, Longitude = QContactGeoLocation::FieldLongitude, Accuracy = QContactGeoLocation::FieldAccuracy, Altitude = QContactGeoLocation::FieldAltitude, AltitudeAccuracy = QContactGeoLocation::FieldAltitudeAccuracy, Heading = QContactGeoLocation::FieldHeading, Speed = QContactGeoLocation::FieldSpeed, Timestamp = QContactGeoLocation::FieldTimestamp }; QDeclarativeContactGeoLocation(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactGeoLocation()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Geolocation; } void setLabel(const QString& v) { if (!readOnly() && v != label()) { detail().setValue(QContactGeoLocation::FieldLabel, v); emit valueChanged(); } } QString label() const {return detail().value(QContactGeoLocation::FieldLabel).toString();} void setLatitude(double v) { if (!readOnly() && !qFuzzyCompare(v, latitude())) { detail().setValue(QContactGeoLocation::FieldLatitude, v); emit valueChanged(); } } double latitude() const {return detail().value(QContactGeoLocation::FieldLatitude).toDouble();} void setLongitude(double v) { if (!readOnly() && !qFuzzyCompare(v, longitude())) { detail().setValue(QContactGeoLocation::FieldLongitude, v); emit valueChanged(); } } double longitude() const {return detail().value(QContactGeoLocation::FieldLongitude).toDouble();} void setAccuracy(double v) { if (!readOnly() && !qFuzzyCompare(v, accuracy())) { detail().setValue(QContactGeoLocation::FieldAccuracy, v); emit valueChanged(); } } double accuracy() const {return detail().value(QContactGeoLocation::FieldAccuracy).toDouble();} void setAltitude(double v) { if (!readOnly() && !qFuzzyCompare(v, altitude())) { detail().setValue(QContactGeoLocation::FieldAltitude, v); emit valueChanged(); } } double altitude() const {return detail().value(QContactGeoLocation::FieldAltitude).toDouble();} void setAltitudeAccuracy(double v) { if (!readOnly() && !qFuzzyCompare(v, altitudeAccuracy())) { detail().setValue(QContactGeoLocation::FieldAltitudeAccuracy, v); emit valueChanged(); } } double altitudeAccuracy() const {return detail().value(QContactGeoLocation::FieldAltitudeAccuracy).toDouble();} void setHeading(double v) { if (!readOnly() && v != heading()) { detail().setValue(QContactGeoLocation::FieldHeading, v); emit valueChanged(); } } double heading() const {return detail().value(QContactGeoLocation::FieldHeading).toDouble();} void setSpeed(double v) { if (!readOnly() && !qFuzzyCompare(v, speed())) { detail().setValue(QContactGeoLocation::FieldSpeed, v); emit valueChanged(); } } double speed() const {return detail().value(QContactGeoLocation::FieldSpeed).toDouble();} void setTimestamp(const QDateTime& v) { if (!readOnly() && v != timestamp()) { detail().setValue(QContactGeoLocation::FieldTimestamp, v); emit valueChanged(); } } QDateTime timestamp() const {return detail().value(QContactGeoLocation::FieldTimestamp).toDateTime();} signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactGeoLocation) #endif // QDECLARATIVECONTACTGEOLOCATION_H src/imports/contacts/details/qdeclarativecontactglobalpresence_p.h000066400000000000000000000140111233466112000262160ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTGLOBALPRESENCE_H #define QDECLARATIVECONTACTGLOBALPRESENCE_H #include #include "qdeclarativecontactdetail_p.h" #include "qdeclarativecontactpresence_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactGlobalPresence : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QDateTime timestamp READ timestamp WRITE setTimestamp NOTIFY valueChanged) Q_PROPERTY(QString nickname READ nickname WRITE setNickname NOTIFY valueChanged) Q_PROPERTY(QDeclarativeContactPresence::PresenceStateType state READ presenceState WRITE setPresenceState NOTIFY valueChanged) Q_PROPERTY(QString stateText READ presenceStateText WRITE setPresenceStateText NOTIFY valueChanged) Q_PROPERTY(QUrl imageUrl READ presenceStateImageUrl WRITE setPresenceStateImageUrl NOTIFY valueChanged) Q_PROPERTY(QString customMessage READ customMessage WRITE setCustomMessage NOTIFY valueChanged) Q_CLASSINFO("DefaultProperty", "state") Q_ENUMS(FieldType) public: enum FieldType { Timestamp = QContactGlobalPresence::FieldTimestamp, Nickname = QContactGlobalPresence::FieldNickname, State = QContactGlobalPresence::FieldPresenceState, StateText = QContactGlobalPresence::FieldPresenceStateText, ImageUrl = QContactGlobalPresence::FieldPresenceStateImageUrl, CustomMessage = QContactGlobalPresence::FieldCustomMessage }; QDeclarativeContactGlobalPresence(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactGlobalPresence()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::GlobalPresence; } void setTimestamp(const QDateTime& v) { if (!readOnly() && v != timestamp()) { detail().setValue(QContactGlobalPresence::FieldTimestamp, v); emit valueChanged(); } } QDateTime timestamp() const {return detail().value(QContactGlobalPresence::FieldTimestamp);} void setNickname(const QString& v) { if (!readOnly() && v != nickname()) { detail().setValue(QContactGlobalPresence::FieldNickname, v); emit valueChanged(); } } QString nickname() const {return detail().value(QContactGlobalPresence::FieldNickname).toString();} void setPresenceState(QDeclarativeContactPresence::PresenceStateType v) { if (!readOnly() && v != presenceState()) { detail().setValue(QContactGlobalPresence::FieldPresenceState, static_cast(v)); emit valueChanged(); } } QDeclarativeContactPresence::PresenceStateType presenceState() const { return static_cast(detail().value(QContactGlobalPresence::FieldPresenceState)); } void setPresenceStateText(const QString& v) { if (!readOnly() && v != presenceStateText()) { detail().setValue(QContactGlobalPresence::FieldPresenceStateText, v); emit valueChanged(); } } QString presenceStateText() const {return detail().value(QContactGlobalPresence::FieldPresenceStateText).toString();} void setPresenceStateImageUrl(const QUrl& v) { if (!readOnly() && v != presenceStateImageUrl()) { detail().setValue(QContactGlobalPresence::FieldPresenceStateImageUrl, v); emit valueChanged(); } } QUrl presenceStateImageUrl() const {return detail().value(QContactGlobalPresence::FieldPresenceStateImageUrl);} void setCustomMessage(const QString& v) { if (!readOnly() && v != customMessage()) { detail().setValue(QContactGlobalPresence::FieldCustomMessage, v); emit valueChanged(); } } QString customMessage() const {return detail().value(QContactGlobalPresence::FieldCustomMessage).toString();} signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactGlobalPresence) #endif // QDECLARATIVECONTACTGLOBALPRESENCE_H src/imports/contacts/details/qdeclarativecontactguid_p.h000066400000000000000000000061551233466112000241730ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTGUID_H #define QDECLARATIVECONTACTGUID_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactGuid : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString guid READ guid WRITE setGuid NOTIFY valueChanged) Q_ENUMS(FieldType) Q_CLASSINFO("DefaultProperty", "guid") public: enum FieldType { Guid = QContactGuid::FieldGuid }; QDeclarativeContactGuid(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactGuid()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Guid; } void setGuid(const QString& v) { if (!readOnly() && v != guid()) { detail().setValue(QContactGuid::FieldGuid, v); emit valueChanged(); } } QString guid() const {return detail().value(QContactGuid::FieldGuid).toString();} signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactGuid) #endif // QDECLARATIVECONTACTGUID_H src/imports/contacts/details/qdeclarativecontacthobby_p.h000066400000000000000000000062041233466112000243410ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTHOBBY_H #define QDECLARATIVECONTACTHOBBY_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactHobby : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString hobby READ hobby WRITE setHobby NOTIFY valueChanged) Q_ENUMS(FieldType) Q_CLASSINFO("DefaultProperty", "hobby") public: enum FieldType { Hobby = QContactHobby::FieldHobby }; QDeclarativeContactHobby(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactHobby()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Hobby; } void setHobby(const QString& v) { if (!readOnly() && v != hobby()) { detail().setValue(QContactHobby::FieldHobby, v); emit valueChanged(); } } QString hobby() const {return detail().value(QContactHobby::FieldHobby).toString();} signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactHobby) #endif // QDECLARATIVECONTACTHOBBY_H src/imports/contacts/details/qdeclarativecontactmoc_p.cpp000066400000000000000000000065411233466112000243530ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPIM module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativecontactdetails_p.h" QT_BEGIN_NAMESPACE #include "moc_qdeclarativecontactaddress_p.cpp" #include "moc_qdeclarativecontactanniversary_p.cpp" #include "moc_qdeclarativecontactavatar_p.cpp" #include "moc_qdeclarativecontactbirthday_p.cpp" #include "moc_qdeclarativecontactdisplaylabel_p.cpp" #include "moc_qdeclarativecontactemailaddress_p.cpp" #include "moc_qdeclarativecontactextendeddetail_p.cpp" #include "moc_qdeclarativecontactfamily_p.cpp" #include "moc_qdeclarativecontactfavorite_p.cpp" #include "moc_qdeclarativecontactgender_p.cpp" #include "moc_qdeclarativecontactgeolocation_p.cpp" #include "moc_qdeclarativecontactglobalpresence_p.cpp" #include "moc_qdeclarativecontactguid_p.cpp" #include "moc_qdeclarativecontacthobby_p.cpp" #include "moc_qdeclarativecontactname_p.cpp" #include "moc_qdeclarativecontactnickname_p.cpp" #include "moc_qdeclarativecontactnote_p.cpp" #include "moc_qdeclarativecontactonlineaccount_p.cpp" #include "moc_qdeclarativecontactorganization_p.cpp" #include "moc_qdeclarativecontactphonenumber_p.cpp" #include "moc_qdeclarativecontactpresence_p.cpp" #include "moc_qdeclarativecontactringtone_p.cpp" #include "moc_qdeclarativecontactsynctarget_p.cpp" #include "moc_qdeclarativecontacttag_p.cpp" #include "moc_qdeclarativecontacttype_p.cpp" #include "moc_qdeclarativecontacttimestamp_p.cpp" #include "moc_qdeclarativecontacturl_p.cpp" #include "moc_qdeclarativecontactversion_p.cpp" QT_END_NAMESPACE src/imports/contacts/details/qdeclarativecontactname_p.h000066400000000000000000000115151233466112000241570ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTNAME_H #define QDECLARATIVECONTACTNAME_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactName : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString prefix READ prefix WRITE setPrefix NOTIFY valueChanged) Q_PROPERTY(QString firstName READ firstName WRITE setFirstName NOTIFY valueChanged) Q_PROPERTY(QString middleName READ middleName WRITE setMiddleName NOTIFY valueChanged) Q_PROPERTY(QString lastName READ lastName WRITE setLastName NOTIFY valueChanged) Q_PROPERTY(QString suffix READ suffix WRITE setSuffix NOTIFY valueChanged) Q_ENUMS(FieldType) Q_CLASSINFO("DefaultProperty", "firstName") public: enum FieldType { Prefix = QContactName::FieldPrefix, FirstName = QContactName::FieldFirstName, MiddleName = QContactName::FieldMiddleName, LastName = QContactName::FieldLastName, Suffix = QContactName::FieldSuffix, }; QDeclarativeContactName(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactName()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Name; } QString prefix() const {return detail().value(QContactName::FieldPrefix).toString();} QString firstName() const {return detail().value(QContactName::FieldFirstName).toString();} QString middleName() const {return detail().value(QContactName::FieldMiddleName).toString();} QString lastName() const {return detail().value(QContactName::FieldLastName).toString();} QString suffix() const {return detail().value(QContactName::FieldSuffix).toString();} void setPrefix(const QString& v) { if (!readOnly() && v != prefix()) { detail().setValue(QContactName::FieldPrefix, v); emit valueChanged(); } } void setFirstName(const QString& v) { if (!readOnly() && v != firstName()) { detail().setValue(QContactName::FieldFirstName, v); emit valueChanged(); } } void setMiddleName(const QString& v) { if (!readOnly() && v != middleName()) { detail().setValue(QContactName::FieldMiddleName, v); emit valueChanged(); } } void setLastName(const QString& v) { if (!readOnly() && v != lastName()) { detail().setValue(QContactName::FieldLastName, v); emit valueChanged(); } } void setSuffix(const QString& v) { if (!readOnly() && v != suffix()) { detail().setValue(QContactName::FieldSuffix, v); emit valueChanged(); } } signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactName) #endif // QDECLARATIVECONTACTNAME_H src/imports/contacts/details/qdeclarativecontactnickname_p.h000066400000000000000000000063111233466112000250220ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTNICKNAME_H #define QDECLARATIVECONTACTNICKNAME_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactNickname : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString nickname READ nickname WRITE setNickname NOTIFY valueChanged) Q_ENUMS(FieldType) Q_CLASSINFO("DefaultProperty", "nickname") public: enum FieldType { NickName = QContactNickname::FieldNickname }; QDeclarativeContactNickname(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactNickname()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::NickName; } void setNickname(const QString& v) { if (!readOnly() && v != nickname()) { detail().setValue(QContactNickname::FieldNickname, v); emit valueChanged(); } } QString nickname() const {return detail().value(QContactNickname::FieldNickname).toString();} signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactNickname) #endif // QDECLARATIVECONTACTNICKNAME_H src/imports/contacts/details/qdeclarativecontactnote_p.h000066400000000000000000000061561233466112000242110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTNOTE_H #define QDECLARATIVECONTACTNOTE_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactNote : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString note READ note WRITE setNote NOTIFY valueChanged) Q_ENUMS(FieldType) Q_CLASSINFO("DefaultProperty", "note") public: enum FieldType { Note = QContactNote::FieldNote }; QDeclarativeContactNote(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactNote()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Note; } void setNote(const QString& v) { if (!readOnly() && v != note()) { detail().setValue(QContactNote::FieldNote, v); emit valueChanged(); } } QString note() const {return detail().value(QContactNote::FieldNote).toString();} signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactNote) #endif // QDECLARATIVECONTACTNOTE_H src/imports/contacts/details/qdeclarativecontactonlineaccount_p.h000066400000000000000000000144651233466112000261070ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTONLINEACCOUNT_H #define QDECLARATIVECONTACTONLINEACCOUNT_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactOnlineAccount : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString accountUri READ accountUri WRITE setAccountUri NOTIFY valueChanged) Q_PROPERTY(QString serviceProvider READ serviceProvider WRITE setServiceProvider NOTIFY valueChanged) Q_PROPERTY(QStringList capabilities READ capabilities WRITE setCapabilities NOTIFY valueChanged) Q_PROPERTY(QList subTypes READ subTypes WRITE setSubTypes NOTIFY valueChanged) Q_PROPERTY(OnlineAccountProtocol protocol READ protocol WRITE setProtocol NOTIFY valueChanged) Q_ENUMS(FieldType) Q_ENUMS(OnlineAccountSubType) Q_ENUMS(OnlineAccountProtocol) Q_CLASSINFO("DefaultProperty", "accountUri") public: enum FieldType { AccountUri = QContactOnlineAccount::FieldAccountUri, ServiceProvider = QContactOnlineAccount::FieldServiceProvider, Protocol = QContactOnlineAccount::FieldProtocol, Capabilities = QContactOnlineAccount::FieldCapabilities, SubTypes = QContactOnlineAccount::FieldSubTypes }; enum OnlineAccountSubType { Sip = QContactOnlineAccount::SubTypeSip, SipVoip = QContactOnlineAccount::SubTypeSipVoip, Impp = QContactOnlineAccount::SubTypeImpp, VideoShare= QContactOnlineAccount::SubTypeVideoShare }; enum OnlineAccountProtocol { Unknown = QContactOnlineAccount::ProtocolUnknown, Aim = QContactOnlineAccount::ProtocolAim, Icq = QContactOnlineAccount::ProtocolIcq, Irc = QContactOnlineAccount::ProtocolIrc, Jabber = QContactOnlineAccount::ProtocolJabber, Msn = QContactOnlineAccount::ProtocolMsn, Qq = QContactOnlineAccount::ProtocolQq, Skype = QContactOnlineAccount::ProtocolSkype, Yahoo = QContactOnlineAccount::ProtocolYahoo }; QDeclarativeContactOnlineAccount(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactOnlineAccount()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::OnlineAccount; } void setAccountUri(const QString& v) { if (!readOnly() && v != accountUri()) { detail().setValue(QContactOnlineAccount::FieldAccountUri, v); emit valueChanged(); } } QString accountUri() const {return detail().value(QContactOnlineAccount::FieldAccountUri).toString();} void setServiceProvider(const QString& v) { if (!readOnly() && v != serviceProvider()) { detail().setValue(QContactOnlineAccount::FieldServiceProvider, v); emit valueChanged(); } } QString serviceProvider() const {return detail().value(QContactOnlineAccount::FieldServiceProvider).toString();} void setCapabilities(const QStringList& v) { if (!readOnly() && v.toSet() != capabilities().toSet()) { detail().setValue(QContactOnlineAccount::FieldCapabilities, v); emit valueChanged(); } } QStringList capabilities() const {return detail().value(QContactOnlineAccount::FieldCapabilities);} void setSubTypes(const QList& subTypes) { QList oldList = detail().value< QList >(QContactOnlineAccount::FieldSubTypes); if (!readOnly() && subTypes.toSet() != oldList.toSet()) { detail().setValue(QContactOnlineAccount::FieldSubTypes, QVariant::fromValue(subTypes)); emit valueChanged(); } } QList subTypes() const { return detail().value< QList >(QContactOnlineAccount::FieldSubTypes); } void setProtocol(OnlineAccountProtocol v) { if (!readOnly() && v != protocol()) { detail().setValue(QContactOnlineAccount::FieldProtocol, static_cast(v)); emit valueChanged(); } } OnlineAccountProtocol protocol() const { return static_cast(detail().value(QContactOnlineAccount::FieldProtocol)); } signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactOnlineAccount) #endif // QDECLARATIVECONTACTONLINEACCOUNT_H src/imports/contacts/details/qdeclarativecontactorganization_p.h000066400000000000000000000135771233466112000257550ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTORGANIZATION_H #define QDECLARATIVECONTACTORGANIZATION_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactOrganization : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY valueChanged) Q_PROPERTY(QUrl logoUrl READ logoUrl WRITE setLogoUrl NOTIFY valueChanged) Q_PROPERTY(QStringList department READ department WRITE setDepartment NOTIFY valueChanged) Q_PROPERTY(QString location READ location WRITE setLocation NOTIFY valueChanged) Q_PROPERTY(QString role READ role WRITE setRole NOTIFY valueChanged) Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY valueChanged) Q_PROPERTY(QString assistantName READ assistantName WRITE setAssistantName NOTIFY valueChanged) Q_CLASSINFO("DefaultProperty", "name") Q_ENUMS(FieldType) public: enum FieldType { Name = QContactOrganization::FieldName, LogoUrl = QContactOrganization::FieldLogoUrl, Department = QContactOrganization::FieldDepartment, Location = QContactOrganization::FieldLocation, Role = QContactOrganization::FieldRole, Title = QContactOrganization::FieldTitle, AssistantName = QContactOrganization::FieldAssistantName }; QDeclarativeContactOrganization(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactOrganization()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Organization; } void setName(const QString& v) { if (!readOnly() && v != name()) { detail().setValue(QContactOrganization::FieldName, v); emit valueChanged(); } } QString name() const {return detail().value(QContactOrganization::FieldName).toString();} void setLogoUrl(const QUrl& v) { if (!readOnly() && v != logoUrl()) { detail().setValue(QContactOrganization::FieldLogoUrl, v); emit valueChanged(); } } QUrl logoUrl() const {return detail().value(QContactOrganization::FieldLogoUrl).toString();} void setDepartment(const QStringList& v) { if (!readOnly() && v.toSet() != department().toSet()) { detail().setValue(QContactOrganization::FieldDepartment, v); emit valueChanged(); } } QStringList department() const {return detail().value(QContactOrganization::FieldDepartment);} void setLocation(const QString& v) { if (!readOnly() && v != location()) { detail().setValue(QContactOrganization::FieldLocation, v); emit valueChanged(); } } QString location() const {return detail().value(QContactOrganization::FieldLocation).toString();} void setRole(const QString& v) { if (!readOnly() && v != role()) { detail().setValue(QContactOrganization::FieldRole, v); emit valueChanged(); } } QString role() const {return detail().value(QContactOrganization::FieldRole).toString();} void setTitle(const QString& v) { if (!readOnly() && v != title()) { detail().setValue(QContactOrganization::FieldTitle, v); emit valueChanged(); } } QString title() const {return detail().value(QContactOrganization::FieldTitle).toString();} void setAssistantName(const QString& v) { if (!readOnly() && v != assistantName()) { detail().setValue(QContactOrganization::FieldAssistantName, v); emit valueChanged(); } } QString assistantName() const {return detail().value(QContactOrganization::FieldAssistantName).toString();} signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactOrganization) #endif // QDECLARATIVECONTACTORGANIZATION_H src/imports/contacts/details/qdeclarativecontactphonenumber_p.h000066400000000000000000000111001233466112000255470ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTPHONENUMBER_H #define QDECLARATIVECONTACTPHONENUMBER_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactPhoneNumber : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString number READ number WRITE setNumber NOTIFY valueChanged) Q_PROPERTY(QList subTypes READ subTypes WRITE setSubTypes NOTIFY valueChanged) Q_ENUMS(FieldType) Q_ENUMS(PhoneNumberSubType) Q_CLASSINFO("DefaultProperty", "number") public: enum FieldType { Number = QContactPhoneNumber::FieldNumber, SubTypes = QContactPhoneNumber::FieldSubTypes }; enum PhoneNumberSubType { Landline = QContactPhoneNumber::SubTypeLandline, Mobile = QContactPhoneNumber::SubTypeMobile, Fax = QContactPhoneNumber::SubTypeFax, Pager = QContactPhoneNumber::SubTypePager, Voice = QContactPhoneNumber::SubTypeVoice, Modem = QContactPhoneNumber::SubTypeModem, Video = QContactPhoneNumber::SubTypeVideo, Car = QContactPhoneNumber::SubTypeCar, BulletinBoardSystem = QContactPhoneNumber::SubTypeBulletinBoardSystem, MessagingCapable = QContactPhoneNumber::SubTypeMessagingCapable, Assistant = QContactPhoneNumber::SubTypeAssistant, DtmfMenu = QContactPhoneNumber::SubTypeDtmfMenu }; QDeclarativeContactPhoneNumber(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactPhoneNumber()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::PhoneNumber; } void setNumber(const QString& v) { if (!readOnly() && v != number()) { detail().setValue(QContactPhoneNumber::FieldNumber, v); emit valueChanged(); } } QString number() const {return detail().value(QContactPhoneNumber::FieldNumber).toString();} void setSubTypes(const QList& subTypes) { QList oldList = detail().value< QList >(QContactPhoneNumber::FieldSubTypes); if (!readOnly() && subTypes.toSet() != oldList.toSet()) { detail().setValue(QContactPhoneNumber::FieldSubTypes, QVariant::fromValue(subTypes)); emit valueChanged(); } } QList subTypes() const { return detail().value< QList >(QContactPhoneNumber::FieldSubTypes); } signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactPhoneNumber) #endif // QDECLARATIVECONTACTPHONENUMBER_H src/imports/contacts/details/qdeclarativecontactpresence_p.h000066400000000000000000000142001233466112000250350ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTPRESENCE_H #define QDECLARATIVECONTACTPRESENCE_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactPresence : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QDateTime timestamp READ timestamp WRITE setTimestamp NOTIFY valueChanged) Q_PROPERTY(QString nickname READ nickname WRITE setNickname NOTIFY valueChanged) Q_PROPERTY(PresenceStateType state READ presenceState WRITE setPresenceState NOTIFY valueChanged) Q_PROPERTY(QString stateText READ presenceStateText WRITE setPresenceStateText NOTIFY valueChanged) Q_PROPERTY(QUrl imageUrl READ presenceStateImageUrl WRITE setPresenceStateImageUrl NOTIFY valueChanged) Q_PROPERTY(QString customMessage READ customMessage WRITE setCustomMessage NOTIFY valueChanged) Q_ENUMS(FieldType) Q_ENUMS(PresenceStateType) Q_CLASSINFO("DefaultProperty", "state") public: enum FieldType { Timestamp = QContactPresence::FieldTimestamp, Nickname = QContactPresence::FieldNickname, State = QContactPresence::FieldPresenceState, StateText = QContactPresence::FieldPresenceStateText, ImageUrl = QContactPresence::FieldPresenceStateImageUrl, CustomMessage = QContactPresence::FieldCustomMessage }; enum PresenceStateType { Unknown = QContactPresence::PresenceUnknown, Available = QContactPresence::PresenceAvailable, Hidden = QContactPresence::PresenceHidden, Busy = QContactPresence::PresenceBusy, Away = QContactPresence::PresenceAway, ExtendedAway = QContactPresence::PresenceExtendedAway, Offline = QContactPresence::PresenceOffline }; QDeclarativeContactPresence(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactPresence()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Presence; } void setTimestamp(const QDateTime& v) { if (!readOnly() && v != timestamp()) { detail().setValue(QContactPresence::FieldTimestamp, v); emit valueChanged(); } } QDateTime timestamp() const {return detail().value(QContactPresence::FieldTimestamp);} void setNickname(const QString& v) { if (!readOnly() && v != nickname()) { detail().setValue(QContactPresence::FieldNickname, v); emit valueChanged(); } } QString nickname() const {return detail().value(QContactPresence::FieldNickname).toString();} void setPresenceState(PresenceStateType v) { if (!readOnly() && v != presenceState()) { detail().setValue(QContactPresence::FieldPresenceState, static_cast(v)); emit valueChanged(); } } PresenceStateType presenceState() const { return static_cast(detail().value(QContactPresence::FieldPresenceState)); } void setPresenceStateText(const QString& v) { if (!readOnly() && v != presenceStateText()) { detail().setValue(QContactPresence::FieldPresenceStateText, v); emit valueChanged(); } } QString presenceStateText() const {return detail().value(QContactPresence::FieldPresenceStateText).toString();} void setPresenceStateImageUrl(const QUrl& v) { if (!readOnly() && v != presenceStateImageUrl()) { detail().setValue(QContactPresence::FieldPresenceStateImageUrl, v); emit valueChanged(); } } QUrl presenceStateImageUrl() const {return detail().value(QContactPresence::FieldPresenceStateImageUrl);} void setCustomMessage(const QString& v) { if (!readOnly() && v != customMessage()) { detail().setValue(QContactPresence::FieldCustomMessage, v); emit valueChanged(); } } QString customMessage() const {return detail().value(QContactPresence::FieldCustomMessage).toString();} signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactPresence) #endif // QDECLARATIVECONTACTPRESENCE_H src/imports/contacts/details/qdeclarativecontactringtone_p.h000066400000000000000000000104671233466112000250710ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTRINGTONE_H #define QDECLARATIVECONTACTRINGTONE_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactRingtone : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QUrl audioRingtoneUrl READ audioRingtoneUrl WRITE setAudioRingtoneUrl NOTIFY valueChanged) Q_PROPERTY(QUrl videoRingtoneUrl READ videoRingtoneUrl WRITE setVideoRingtoneUrl NOTIFY valueChanged) Q_PROPERTY(QUrl vibrationRingtoneUrl READ vibrationRingtoneUrl WRITE setVibrationRingtoneUrl NOTIFY valueChanged) Q_ENUMS(FieldType) Q_CLASSINFO("DefaultProperty", "audioRingtoneUrl") public: enum FieldType { AudioRingtoneUrl = QContactRingtone::FieldAudioRingtoneUrl, VideoRingtoneUrl = QContactRingtone::FieldVideoRingtoneUrl, VibrationRingtoneUrl = QContactRingtone::FieldVibrationRingtoneUrl }; QDeclarativeContactRingtone(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactRingtone()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Ringtone; } void setAudioRingtoneUrl(const QUrl& v) { if (!readOnly() && v != audioRingtoneUrl()) { detail().setValue(QContactRingtone::FieldAudioRingtoneUrl, v); emit valueChanged(); } } QUrl audioRingtoneUrl() const {return detail().value(QContactRingtone::FieldAudioRingtoneUrl);} void setVideoRingtoneUrl(const QUrl& v) { if (!readOnly() && v != videoRingtoneUrl()) { detail().setValue(QContactRingtone::FieldVideoRingtoneUrl, v); emit valueChanged(); } } QUrl videoRingtoneUrl() const {return detail().value(QContactRingtone::FieldVideoRingtoneUrl);} void setVibrationRingtoneUrl(const QUrl& v) { if (!readOnly() && v != vibrationRingtoneUrl()) { detail().setValue(QContactRingtone::FieldVibrationRingtoneUrl, v); emit valueChanged(); } } QUrl vibrationRingtoneUrl() const {return detail().value(QContactRingtone::FieldVibrationRingtoneUrl);} signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactRingtone) #endif // QDECLARATIVECONTACTRINGTONE_H src/imports/contacts/details/qdeclarativecontactsynctarget_p.h000066400000000000000000000063711233466112000254260ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTSYNCTARGET_H #define QDECLARATIVECONTACTSYNCTARGET_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactSyncTarget : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString syncTarget READ syncTarget WRITE setSyncTarget NOTIFY valueChanged) Q_ENUMS(FieldType) Q_CLASSINFO("DefaultProperty", "syncTarget") public: enum FieldType { SyncTarget = QContactSyncTarget::FieldSyncTarget }; QDeclarativeContactSyncTarget(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactSyncTarget()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::SyncTarget; } void setSyncTarget(const QString& v) { if (!readOnly() && v != syncTarget()) { detail().setValue(QContactSyncTarget::FieldSyncTarget, v); emit valueChanged(); } } QString syncTarget() const {return detail().value(QContactSyncTarget::FieldSyncTarget).toString();} signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactSyncTarget) #endif // QDECLARATIVECONTACTSYNCTARGET_H src/imports/contacts/details/qdeclarativecontacttag_p.h000066400000000000000000000061271233466112000240150ustar00rootroot00000000000000 /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTTAG_H #define QDECLARATIVECONTACTTAG_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactTag : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString tag READ tag WRITE setTag NOTIFY valueChanged) Q_ENUMS(FieldType) Q_CLASSINFO("DefaultProperty", "tag") public: enum FieldType { Tag = QContactTag::FieldTag }; QDeclarativeContactTag(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactTag()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Tag; } void setTag(const QString& v) { if (!readOnly() && v != tag()) { detail().setValue(QContactTag::FieldTag, v); emit valueChanged(); } } QString tag() const {return detail().value(QContactTag::FieldTag).toString();} signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactTag) #endif // QDECLARATIVECONTACTTAG_H src/imports/contacts/details/qdeclarativecontacttimestamp_p.h000066400000000000000000000073711233466112000252470ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTTIMESTAMP_H #define QDECLARATIVECONTACTTIMESTAMP_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactTimestamp : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QDateTime lastModified READ lastModified WRITE setLastModified NOTIFY valueChanged) Q_PROPERTY(QDateTime created READ created WRITE setCreated NOTIFY valueChanged) Q_ENUMS(FieldType) Q_CLASSINFO("DefaultProperty", "lastModified") public: enum FieldType { LastModified = QContactTimestamp::FieldModificationTimestamp, Created = QContactTimestamp::FieldCreationTimestamp }; DetailType detailType() const { return QDeclarativeContactDetail::Timestamp; } QDeclarativeContactTimestamp(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactTimestamp()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } void setLastModified(const QDateTime& v) { if (!readOnly() && v != lastModified()) { detail().setValue(QContactTimestamp::FieldModificationTimestamp, v); emit valueChanged(); } } QDateTime lastModified() const {return detail().value(QContactTimestamp::FieldModificationTimestamp);} void setCreated(const QDateTime& v) { if (!readOnly() && v != created()) { detail().setValue(QContactTimestamp::FieldCreationTimestamp, v); emit valueChanged(); } } QDateTime created() const {return detail().value(QContactTimestamp::FieldCreationTimestamp);} signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactTimestamp) #endif // QDECLARATIVECONTACTTIMESTAMP_H src/imports/contacts/details/qdeclarativecontacttype_p.h000066400000000000000000000070211233466112000242150ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTTYPE_H #define QDECLARATIVECONTACTTYPE_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactType : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(ContactType type READ type WRITE setType NOTIFY valueChanged) Q_ENUMS(FieldType) Q_ENUMS(ContactType) Q_CLASSINFO("DefaultProperty", "type") public: enum FieldType { TypeField = QContactType::FieldType }; enum ContactType { Unspecified = 0, Contact = QContactType::TypeContact, Group = QContactType::TypeGroup }; DetailType detailType() const { return QDeclarativeContactDetail::Type; } QDeclarativeContactType(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactType()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } void setType(ContactType newType) { if (!readOnly() && newType!=type()) { switch (newType) { case Contact: case Group: detail().setValue(QContactType::FieldType, newType); break; default: detail().setValue(QContactType::FieldType, Unspecified); } } } ContactType type() const { return static_cast(detail().value(QContactType::FieldType).toInt()); } signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactType) #endif // QDECLARATIVECONTACTTYPE_H src/imports/contacts/details/qdeclarativecontacturl_p.h000066400000000000000000000101011233466112000240270ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTURL_H #define QDECLARATIVECONTACTURL_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactUrl : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY valueChanged) Q_PROPERTY(UrlSubType subType READ subType WRITE setSubType NOTIFY valueChanged) Q_ENUMS(FieldType) Q_ENUMS(UrlSubType) public: enum FieldType { Url = QContactUrl::FieldUrl, SubType = QContactUrl::FieldSubType }; enum UrlSubType { Unknown = 0, HomePage = QContactUrl::SubTypeHomePage, Blog = QContactUrl::SubTypeBlog, Favourite = QContactUrl::SubTypeFavourite }; QDeclarativeContactUrl(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactUrl()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Url; } void setUrl(const QString& v) { if (!readOnly() && v != url()) { detail().setValue(QContactUrl::FieldUrl, v); emit valueChanged(); } } QString url() const {return detail().value(QContactUrl::FieldUrl).toString();} void setSubType(const UrlSubType& v) { switch (v) { case HomePage: case Blog: case Favourite: if (v != detail().value(QContactUrl::FieldSubType)) { detail().setValue(QContactUrl::FieldSubType, v); emit valueChanged(); } break; default: if (detail().value(QContactUrl::FieldSubType) != Unknown) { detail().setValue(QContactUrl::FieldSubType, Unknown); emit valueChanged(); } } } UrlSubType subType() const { return static_cast(detail().value(QContactUrl::FieldSubType).toInt()); } signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactUrl) #endif // QDECLARATIVECONTACTURL_H src/imports/contacts/details/qdeclarativecontactversion_p.h000066400000000000000000000075671233466112000247400ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTVERSION_H #define QDECLARATIVECONTACTVERSION_H #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactVersion : public QDeclarativeContactDetail { Q_OBJECT Q_PROPERTY(int sequenceNumber READ sequenceNumber WRITE setSequenceNumber NOTIFY valueChanged) Q_PROPERTY(QString extendedVersion READ extendedVersion WRITE setExtendedVersion NOTIFY valueChanged) Q_ENUMS(FieldType) public: enum FieldType { SequenceNumber = QContactVersion::FieldSequenceNumber, ExtendedVersion = QContactVersion::FieldExtendedVersion }; QDeclarativeContactVersion(QObject* parent = 0) :QDeclarativeContactDetail(parent) { setDetail(QContactVersion()); connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); } DetailType detailType() const { return QDeclarativeContactDetail::Version; } void setSequenceNumber(int newSequenceNumber) { if (!readOnly() && newSequenceNumber != sequenceNumber()) { detail().setValue(QContactVersion::FieldSequenceNumber, newSequenceNumber); emit valueChanged(); } } int sequenceNumber() const {return detail().value(QContactVersion::FieldSequenceNumber).toInt();} void setExtendedVersion(const QString &newExtendedVersion) { if (extendedVersion() != newExtendedVersion) { detail().setValue(QContactVersion::FieldExtendedVersion, newExtendedVersion); emit valueChanged(); } } QString extendedVersion() const { QByteArray version = detail().value(QContactVersion::FieldExtendedVersion).toByteArray(); return QString::fromLatin1(version.constData(), version.length()); } signals: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactVersion) #endif // QDECLARATIVECONTACTVERSION_H src/imports/contacts/filters/000077500000000000000000000000001233466112000166265ustar00rootroot00000000000000src/imports/contacts/filters/filters.pri000066400000000000000000000011361233466112000210130ustar00rootroot00000000000000INCLUDEPATH += filters HEADERS += \ filters/qdeclarativecontactactionfilter_p.h \ filters/qdeclarativecontactchangelogfilter_p.h \ filters/qdeclarativecontactdetailfilter_p.h \ filters/qdeclarativecontactdetailrangefilter_p.h \ filters/qdeclarativecontactidfilter_p.h \ filters/qdeclarativecontactinvalidfilter_p.h \ filters/qdeclarativecontactintersectionfilter_p.h \ filters/qdeclarativecontactrelationshipfilter_p.h \ filters/qdeclarativecontactunionfilter_p.h \ filters/qdeclarativecontactfilters_p.h SOURCES += \ filters/qdeclarativecontactfiltermoc.cpp src/imports/contacts/filters/qdeclarativecontactactionfilter_p.h000066400000000000000000000057671233466112000257610ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTACTIONFILTER_H #define QDECLARATIVECONTACTACTIONFILTER_H #include #include "qdeclarativecontactfilter_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactActionFilter : public QDeclarativeContactFilter { Q_OBJECT Q_PROPERTY(QString actionName READ actionName WRITE setActionName NOTIFY valueChanged) public: QDeclarativeContactActionFilter(QObject* parent=0) :QDeclarativeContactFilter(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(filterChanged())); } void setActionName(const QString& action) { if (action != d.actionName()) { d.setActionName(action); emit valueChanged(); } } QString actionName() const { return d.actionName(); } QContactFilter filter() const { return d; } signals: void valueChanged(); private: QContactActionFilter d; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactActionFilter) #endif // QDECLARATIVECONTACTACTIONFILTER_H src/imports/contacts/filters/qdeclarativecontactchangelogfilter_p.h000066400000000000000000000071501233466112000264170ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTCHANGELOGFILTER_H #define QDECLARATIVECONTACTCHANGELOGFILTER_H #include #include "qdeclarativecontactfilter_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactChangeLogFilter: public QDeclarativeContactFilter { Q_OBJECT Q_PROPERTY(QDateTime since READ since WRITE setSince NOTIFY valueChanged) Q_PROPERTY(EventType eventType READ eventType WRITE setEventType NOTIFY valueChanged) Q_ENUMS(EventType) public: enum EventType { EventAdded = QContactChangeLogFilter::EventAdded, EventChanged = QContactChangeLogFilter::EventChanged, EventRemoved = QContactChangeLogFilter::EventRemoved }; QDeclarativeContactChangeLogFilter(QObject* parent = 0) :QDeclarativeContactFilter(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(filterChanged())); } void setEventType(EventType type) { if (type != eventType()) { d.setEventType(static_cast(type)); emit valueChanged(); } } EventType eventType() const { return static_cast(d.eventType()); } void setSince(const QDateTime& since) { if (since != d.since()) { d.setSince(since); emit valueChanged(); } } QDateTime since() const { return d.since(); } QContactFilter filter() const { return d; } signals: void valueChanged(); private: QContactChangeLogFilter d; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactChangeLogFilter) #endif // QDECLARATIVECONTACTCHANGELOGFILTER_H src/imports/contacts/filters/qdeclarativecontactdetailfilter_p.h000066400000000000000000000106761233466112000257410ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTDETAILFILTER_H #define QDECLARATIVECONTACTDETAILFILTER_H #include #include "qdeclarativecontactdetail_p.h" #include "qdeclarativecontactfilter_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactDetailFilter : public QDeclarativeContactFilter { Q_OBJECT Q_PROPERTY(QDeclarativeContactDetail::DetailType detail READ detail WRITE setDetail NOTIFY valueChanged) Q_PROPERTY(int field READ field WRITE setField NOTIFY valueChanged) Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) Q_PROPERTY(MatchFlags matchFlags READ matchFlags WRITE setMatchFlags NOTIFY valueChanged) public: QDeclarativeContactDetailFilter(QObject* parent = 0) : QDeclarativeContactFilter(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(filterChanged())); } void setDetail(QDeclarativeContactDetail::DetailType detail) { if (detail != static_cast(d.detailType())) { d.setDetailType(static_cast(detail), d.detailField()); emit valueChanged(); } } QDeclarativeContactDetail::DetailType detail() const { return static_cast(d.detailType()); } void setField(int field) { if (field != d.detailField()) { d.setDetailType(d.detailType(), field); emit valueChanged(); } } int field() const { return d.detailField(); } QDeclarativeContactFilter::MatchFlags matchFlags() const { QDeclarativeContactFilter::MatchFlags flags; flags = ~flags & (int)d.matchFlags(); return flags; } void setMatchFlags(QDeclarativeContactFilter::MatchFlags flags) { QContactFilter::MatchFlags newFlags; newFlags = ~newFlags & (int)flags; if (newFlags != d.matchFlags()) { d.setMatchFlags(newFlags); emit valueChanged(); } } QVariant value() const { return d.value(); } void setValue(const QVariant& value) { if (value != d.value()) { d.setValue(value); emit valueChanged(); } } QContactFilter filter() const { return d; } signals: void valueChanged(); private: QContactDetailFilter d; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactDetailFilter) #endif // QDECLARATIVECONTACTDETAILFILTER_H src/imports/contacts/filters/qdeclarativecontactdetailrangefilter_p.h000066400000000000000000000135211233466112000267460ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTDETAILRANGEFILTER_H #define QDECLARATIVECONTACTDETAILRANGEFILTER_H #include #include "qdeclarativecontact_p.h" #include "qdeclarativecontactfilter_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactDetailRangeFilter : public QDeclarativeContactFilter { Q_OBJECT Q_PROPERTY(QDeclarativeContactDetail::DetailType detail READ detail WRITE setDetail NOTIFY valueChanged) Q_PROPERTY(int field READ field WRITE setField NOTIFY valueChanged) Q_PROPERTY(QVariant min READ minValue WRITE setMinValue NOTIFY valueChanged) Q_PROPERTY(QVariant max READ maxValue WRITE setMaxValue NOTIFY valueChanged) Q_PROPERTY(MatchFlags matchFlags READ matchFlags WRITE setMatchFlags NOTIFY valueChanged) Q_PROPERTY(RangeFlags rangeFlags READ rangeFlags WRITE setRangeFlags NOTIFY valueChanged) Q_FLAGS(RangeFlags) public: enum RangeFlag { IncludeLower = QContactDetailRangeFilter::IncludeLower, IncludeUpper = QContactDetailRangeFilter::IncludeUpper, ExcludeLower = QContactDetailRangeFilter::ExcludeLower, ExcludeUpper = QContactDetailRangeFilter::ExcludeUpper }; Q_DECLARE_FLAGS(RangeFlags, RangeFlag) QDeclarativeContactDetailRangeFilter(QObject* parent = 0) : QDeclarativeContactFilter(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(filterChanged())); } void setDetail(QDeclarativeContactDetail::DetailType detail) { if (detail != static_cast(d.detailType())) { d.setDetailType(static_cast(detail), d.detailField()); emit valueChanged(); } } QDeclarativeContactDetail::DetailType detail() const { return static_cast(d.detailType()); } void setField(int field) { if (field != d.detailField()) { d.setDetailType(d.detailType(), field); emit valueChanged(); } } int field() const { return d.detailField(); } QDeclarativeContactFilter::MatchFlags matchFlags() const { QDeclarativeContactFilter::MatchFlags flags; flags = ~flags & (int)d.matchFlags(); return flags; } void setMatchFlags(QDeclarativeContactFilter::MatchFlags flags) { QContactFilter::MatchFlags newFlags; newFlags = ~newFlags & (int)flags; if (newFlags != d.matchFlags()) { d.setMatchFlags(newFlags); emit valueChanged(); } } QDeclarativeContactDetailRangeFilter::RangeFlags rangeFlags() const { QDeclarativeContactDetailRangeFilter::RangeFlags flags; flags = ~flags & (int)d.rangeFlags(); return flags; } void setRangeFlags(QDeclarativeContactDetailRangeFilter::RangeFlags flags) { QContactDetailRangeFilter::RangeFlags newFlags; newFlags = ~newFlags & (int)flags; if (newFlags != d.rangeFlags()) { d.setRange(d.minValue(), d.maxValue(), newFlags); emit valueChanged(); } } QVariant minValue() const { return d.minValue(); } void setMinValue(const QVariant& value) { if (value != d.minValue()) { d.setRange(value, d.maxValue(), d.rangeFlags()); emit valueChanged(); } } QVariant maxValue() const { return d.maxValue(); } void setMaxValue(const QVariant& value) { if (value != d.maxValue()) { d.setRange(d.minValue(), value, d.rangeFlags()); emit valueChanged(); } } QContactFilter filter() const { return d; } signals: void valueChanged(); private: QContactDetailRangeFilter d; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactDetailRangeFilter) #endif // QDECLARATIVECONTACTDETAILRANGEFILTER_H src/imports/contacts/filters/qdeclarativecontactfiltermoc.cpp000066400000000000000000000047561233466112000252730ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPIM module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativecontactfilters_p.h" QT_BEGIN_NAMESPACE #include "moc_qdeclarativecontactactionfilter_p.cpp" #include "moc_qdeclarativecontactchangelogfilter_p.cpp" #include "moc_qdeclarativecontactdetailfilter_p.cpp" #include "moc_qdeclarativecontactdetailrangefilter_p.cpp" #include "moc_qdeclarativecontactintersectionfilter_p.cpp" #include "moc_qdeclarativecontactinvalidfilter_p.cpp" #include "moc_qdeclarativecontactidfilter_p.cpp" #include "moc_qdeclarativecontactrelationshipfilter_p.cpp" #include "moc_qdeclarativecontactunionfilter_p.cpp" QT_END_NAMESPACE src/imports/contacts/filters/qdeclarativecontactfilters_p.h000066400000000000000000000050711233466112000247320ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTFILTERS_H #define QDECLARATIVECONTACTFILTERS_H // this file includes all of the leaf filter classes // provided by the Qt Contacts API. #include "qdeclarativecontactactionfilter_p.h" #include "qdeclarativecontactchangelogfilter_p.h" #include "qdeclarativecontactdetailfilter_p.h" #include "qdeclarativecontactdetailrangefilter_p.h" #include "qdeclarativecontactidfilter_p.h" #include "qdeclarativecontactintersectionfilter_p.h" #include "qdeclarativecontactinvalidfilter_p.h" #include "qdeclarativecontactrelationshipfilter_p.h" #include "qdeclarativecontactunionfilter_p.h" #endif // QDECLARATIVECONTACTFILTERS_H src/imports/contacts/filters/qdeclarativecontactidfilter_p.h000066400000000000000000000067121233466112000250670ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTIDFILTER_H #define QDECLARATIVECONTACTIDFILTER_H #include #include "qdeclarativecontactfilter_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactIdFilter : public QDeclarativeContactFilter { Q_OBJECT Q_PROPERTY(QStringList ids READ ids WRITE setIds NOTIFY valueChanged) Q_CLASSINFO("DefaultProperty", "ids") public: QDeclarativeContactIdFilter(QObject *parent = 0) :QDeclarativeContactFilter(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(filterChanged())); } QStringList ids() const { return m_ids; } void setIds(const QStringList &ids) { foreach (const QString &id, ids) { if (!m_ids.contains(id)) { m_ids = ids; emit valueChanged(); return; } } foreach (const QString &id, m_ids) { if (!ids.contains(id)) { m_ids = ids; emit valueChanged(); } } } QContactFilter filter() const { QContactIdFilter f; QList ids; foreach (const QString &id, m_ids) { QContactId contactId = QContactId::fromString(id); ids << contactId; } f.setIds(ids); return f; } signals: void valueChanged(); private: QStringList m_ids; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactIdFilter) #endif // QDECLARATIVECONTACTIDFILTER_H src/imports/contacts/filters/qdeclarativecontactintersectionfilter_p.h000066400000000000000000000054471233466112000272050ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTINTERSECTIONFILTER_H #define QDECLARATIVECONTACTINTERSECTIONFILTER_H #include #include "qdeclarativecontactfilter_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactIntersectionFilter : public QDeclarativeContactCompoundFilter { Q_OBJECT public: QDeclarativeContactIntersectionFilter(QObject* parent = 0) :QDeclarativeContactCompoundFilter(parent) { } QContactFilter filter() const { QList filters; foreach (QDeclarativeContactFilter* f, m_filters) { filters << f->filter(); } QContactIntersectionFilter f; f.setFilters(filters); return f; } }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactIntersectionFilter) #endif // QDECLARATIVECONTACTINTERSECTIONFILTER_H src/imports/contacts/filters/qdeclarativecontactinvalidfilter_p.h000066400000000000000000000050611233466112000261150ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTINVALIDFILTER_H #define QDECLARATIVECONTACTINVALIDFILTER_H #include #include "qdeclarativecontactfilter_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactInvalidFilter : public QDeclarativeContactFilter { Q_OBJECT public: QDeclarativeContactInvalidFilter(QObject* parent=0) :QDeclarativeContactFilter(parent) { } QContactFilter filter() const { return QContactInvalidFilter(); } }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactInvalidFilter) #endif // QDECLARATIVECONTACTINVALIDFILTER_H src/imports/contacts/filters/qdeclarativecontactrelationshipfilter_p.h000066400000000000000000000136761233466112000272030ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTRELATIONSHIPFILTER_H #define QDECLARATIVECONTACTRELATIONSHIPFILTER_H #include #include "qdeclarativecontact_p.h" #include "qdeclarativecontactfilter_p.h" #include "qdeclarativecontactrelationship_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactRelationshipFilter : public QDeclarativeContactFilter { Q_OBJECT Q_PROPERTY(QVariant relationshipType READ relationshipType WRITE setRelationshipType NOTIFY valueChanged) Q_PROPERTY(QDeclarativeContact* relatedContact READ relatedContact WRITE setRelatedContact NOTIFY valueChanged) Q_PROPERTY(QDeclarativeContactRelationship::RelationshipRole relatedContactRole READ relatedContactRole WRITE setRelatedContactRole NOTIFY valueChanged) public: QDeclarativeContactRelationshipFilter(QObject* parent = 0) :QDeclarativeContactFilter(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(filterChanged())); } QVariant relationshipType() const { return d.relationshipType(); } void setRelationshipType(const QVariant& v) { QString rt; if (v.type() != QVariant::String) { switch (v.toInt()) { case QDeclarativeContactRelationship::HasMember: rt = QContactRelationship::HasMember(); break; case QDeclarativeContactRelationship::Aggregates: rt = QContactRelationship::Aggregates(); break; case QDeclarativeContactRelationship::IsSameAs: rt = QContactRelationship::IsSameAs(); break; case QDeclarativeContactRelationship::HasAssistant: rt = QContactRelationship::HasAssistant(); break; case QDeclarativeContactRelationship::HasManager: rt = QContactRelationship::HasManager(); break; case QDeclarativeContactRelationship::HasSpouse: rt = QContactRelationship::HasSpouse(); break; default: break; } } else { rt = v.toString(); } if (rt != relationshipType()) { d.setRelationshipType(rt); emit valueChanged(); } } QDeclarativeContact* relatedContact() const { QDeclarativeContact *v = new QDeclarativeContact(); v->setContact(d.relatedContact()); return v; } void setRelatedContact(const QDeclarativeContact* v) { if (v->contact() != d.relatedContact()) { d.setRelatedContact(v->contact()); emit valueChanged(); } } QDeclarativeContactRelationship::RelationshipRole relatedContactRole() const { switch (d.relatedContactRole()) { case QContactRelationship::First: return QDeclarativeContactRelationship::First; case QContactRelationship::Second: return QDeclarativeContactRelationship::Second; default: break; } return QDeclarativeContactRelationship::Either; } void setRelatedContactRole(QDeclarativeContactRelationship::RelationshipRole v) { if (v != relatedContactRole()) { switch (v) { case QDeclarativeContactRelationship::First: d.setRelatedContactRole(QContactRelationship::First); break; case QDeclarativeContactRelationship::Second: d.setRelatedContactRole(QContactRelationship::Second); break; case QDeclarativeContactRelationship::Either: d.setRelatedContactRole(QContactRelationship::Either); break; } emit valueChanged(); } } QContactFilter filter() const { return d; } signals: void valueChanged(); private: QContactRelationshipFilter d; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactRelationshipFilter) #endif // QDECLARATIVECONTACTRELATIONSHIPFILTER_H src/imports/contacts/filters/qdeclarativecontactunionfilter_p.h000066400000000000000000000053561233466112000256260ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTUNIONFILTER_H #define QDECLARATIVECONTACTUNIONFILTER_H #include #include "qdeclarativecontactfilter_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactUnionFilter : public QDeclarativeContactCompoundFilter { Q_OBJECT public: QDeclarativeContactUnionFilter(QObject* parent = 0) :QDeclarativeContactCompoundFilter(parent) { } QContactFilter filter() const { QList filters; foreach (QDeclarativeContactFilter* f, m_filters) { filters << f->filter(); } QContactUnionFilter f; f.setFilters(filters); return f; } }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactUnionFilter) #endif // QDECLARATIVECONTACTUNIONFILTER_H src/imports/contacts/plugin.cpp000066400000000000000000000162131233466112000171630ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include "qdeclarativecontact_p.h" #include "qdeclarativecontactdetail_p.h" #include "qdeclarativecontactfetchhint_p.h" #include "qdeclarativecontactfilter_p.h" #include "qdeclarativecontactfilters_p.h" #include "qdeclarativecontactmodel_p.h" #include "qdeclarativecontactrelationship_p.h" #include "qdeclarativecontactrelationshipmodel_p.h" #include "qdeclarativecontactsortorder_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QContactQmlPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface" FILE "contacts.json") public: void registerTypes(const char *uri) { Q_ASSERT(uri == QStringLiteral("QtContacts")); int major = 5; int minor = 0; qRegisterMetaType("QContactAbstractRequest::State"); qRegisterMetaType("QContactId"); qRegisterMetaType >("QList"); qmlRegisterType(uri, major, minor, "ContactModel"); qmlRegisterType(uri, major, minor, "Contact"); qmlRegisterType(uri, major, minor, "FetchHint"); qmlRegisterType(uri, major, minor, "RelationshipModel"); qmlRegisterType(uri, major, minor, "Relationship"); qmlRegisterType(uri, major, minor, "SortOrder"); //details qmlRegisterType(uri, major, minor, "ContactDetail"); qmlRegisterType(uri, major, minor, "Address"); qmlRegisterType(uri, major, minor, "Anniversary"); qmlRegisterType(uri, major, minor, "Avatar"); qmlRegisterType(uri, major, minor, "Birthday"); qmlRegisterType(uri, major, minor, "DisplayLabel"); qmlRegisterType(uri, major, minor, "EmailAddress"); qmlRegisterType(uri, major, minor, "Family"); qmlRegisterType(uri, major, minor, "Favorite"); qmlRegisterType(uri, major, minor, "Gender"); qmlRegisterType(uri, major, minor, "Location"); qmlRegisterType(uri, major, minor, "GlobalPresence"); qmlRegisterType(uri, major, minor, "Guid"); qmlRegisterType(uri, major, minor, "Name"); qmlRegisterType(uri, major, minor, "Nickname"); qmlRegisterType(uri, major, minor, "Note"); qmlRegisterType(uri, major, minor, "OnlineAccount"); qmlRegisterType(uri, major, minor, "Organization"); qmlRegisterType(uri, major, minor, "PhoneNumber"); qmlRegisterType(uri, major, minor, "Presence"); qmlRegisterType(uri, major, minor, "Ringtone"); qmlRegisterType(uri, major, minor, "SyncTarget"); qmlRegisterType(uri, major, minor, "Tag"); qmlRegisterType(uri, major, minor, "Timestamp"); qmlRegisterType(uri, major, minor, "Type"); qmlRegisterType(uri, major, minor, "Url"); qmlRegisterType(uri, major, minor, "Version"); qmlRegisterType(uri, major, minor, "Hobby"); qmlRegisterType(uri, major, minor, "ExtendedDetail"); //filters qmlRegisterType(uri, major, minor, "Filter"); qmlRegisterType(uri, major, minor, "ActionFilter"); qmlRegisterType(uri, major, minor, "ChangeLogFilter"); qmlRegisterType(uri, major, minor, "DetailFilter"); qmlRegisterType(uri, major, minor, "DetailRangeFilter"); qmlRegisterType(uri, major, minor, "IdFilter"); qmlRegisterType(uri, major, minor, "RelationshipFilter"); qmlRegisterType(uri, major, minor, "IntersectionFilter"); qmlRegisterType(uri, major, minor, "UnionFilter"); qmlRegisterType(uri, major, minor, "InvalidFilter"); qmlRegisterType(); } void initializeEngine(QQmlEngine *engine, const char *uri) { Q_UNUSED(engine) Q_UNUSED(uri) } }; #include "plugin.moc" QT_END_NAMESPACE src/imports/contacts/plugins.qmltypes000066400000000000000000001200341233466112000204370ustar00rootroot00000000000000import QtQuick.tooling 1.1 // This file describes the plugin-supplied types contained in the library. // It is used for QML tooling purposes only. // // This file was auto-generated with the command 'qmlplugindump -notrelocatable QtContacts 5.0'. Module { Component { name: "QtContacts::QDeclarativeContact" defaultProperty: "contactDetails" prototype: "QObject" exports: ["QtContacts/Contact 5.0"] exportMetaObjectRevisions: [0] Property { name: "modified"; type: "bool"; isReadonly: true } Property { name: "type"; type: "QDeclarativeContactType::ContactType"; isReadonly: true } Property { name: "contactId"; type: "string"; isReadonly: true } Property { name: "manager"; type: "string"; isReadonly: true } Property { name: "contactDetails" type: "QDeclarativeContactDetail" isList: true isReadonly: true } Property { name: "address" type: "QDeclarativeContactAddress" isReadonly: true isPointer: true } Property { name: "addresses"; type: "QDeclarativeContactAddress"; isList: true; isReadonly: true } Property { name: "anniversary" type: "QDeclarativeContactAnniversary" isReadonly: true isPointer: true } Property { name: "avatar"; type: "QDeclarativeContactAvatar"; isReadonly: true; isPointer: true } Property { name: "birthday" type: "QDeclarativeContactBirthday" isReadonly: true isPointer: true } Property { name: "displayLabel" type: "QDeclarativeContactDisplayLabel" isReadonly: true isPointer: true } Property { name: "email" type: "QDeclarativeContactEmailAddress" isReadonly: true isPointer: true } Property { name: "emails" type: "QDeclarativeContactEmailAddress" isList: true isReadonly: true } Property { name: "extendedDetail" type: "QDeclarativeContactExtendedDetail" isReadonly: true isPointer: true } Property { name: "extendedDetails" type: "QDeclarativeContactExtendedDetail" isList: true isReadonly: true } Property { name: "family"; type: "QDeclarativeContactFamily"; isReadonly: true; isPointer: true } Property { name: "favorite" type: "QDeclarativeContactFavorite" isReadonly: true isPointer: true } Property { name: "gender"; type: "QDeclarativeContactGender"; isReadonly: true; isPointer: true } Property { name: "geolocation" type: "QDeclarativeContactGeoLocation" isReadonly: true isPointer: true } Property { name: "globalPresence" type: "QDeclarativeContactGlobalPresence" isReadonly: true isPointer: true } Property { name: "guid"; type: "QDeclarativeContactGuid"; isReadonly: true; isPointer: true } Property { name: "hobby"; type: "QDeclarativeContactHobby"; isReadonly: true; isPointer: true } Property { name: "name"; type: "QDeclarativeContactName"; isReadonly: true; isPointer: true } Property { name: "nickname" type: "QDeclarativeContactNickname" isReadonly: true isPointer: true } Property { name: "note"; type: "QDeclarativeContactNote"; isReadonly: true; isPointer: true } Property { name: "onlineAccount" type: "QDeclarativeContactOnlineAccount" isReadonly: true isPointer: true } Property { name: "organization" type: "QDeclarativeContactOrganization" isReadonly: true isPointer: true } Property { name: "organizations" type: "QDeclarativeContactOrganization" isList: true isReadonly: true } Property { name: "phoneNumber" type: "QDeclarativeContactPhoneNumber" isReadonly: true isPointer: true } Property { name: "phoneNumbers" type: "QDeclarativeContactPhoneNumber" isList: true isReadonly: true } Property { name: "presence" type: "QDeclarativeContactPresence" isReadonly: true isPointer: true } Property { name: "ringtone" type: "QDeclarativeContactRingtone" isReadonly: true isPointer: true } Property { name: "syncTarget" type: "QDeclarativeContactSyncTarget" isReadonly: true isPointer: true } Property { name: "tag"; type: "QDeclarativeContactTag"; isReadonly: true; isPointer: true } Property { name: "timestamp" type: "QDeclarativeContactTimestamp" isReadonly: true isPointer: true } Property { name: "url"; type: "QDeclarativeContactUrl"; isReadonly: true; isPointer: true } Property { name: "urls"; type: "QDeclarativeContactUrl"; isList: true; isReadonly: true } Property { name: "version" type: "QDeclarativeContactVersion" isReadonly: true isPointer: true } Signal { name: "contactChanged" } Method { name: "clearDetails" } Method { name: "save" } Method { name: "detail" type: "QDeclarativeContactDetail*" Parameter { name: "type"; type: "int" } } Method { name: "details" type: "QVariantList" Parameter { name: "type"; type: "int" } } Method { name: "removeDetail" type: "bool" Parameter { name: "detail"; type: "QDeclarativeContactDetail"; isPointer: true } } Method { name: "addDetail" type: "bool" Parameter { name: "detail"; type: "QDeclarativeContactDetail"; isPointer: true } } } Component { name: "QtContacts::QDeclarativeContactActionFilter" prototype: "QtContacts::QDeclarativeContactFilter" exports: ["QtContacts/ActionFilter 5.0"] exportMetaObjectRevisions: [0] Property { name: "actionName"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactAddress" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Address 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Street": 0, "Locality": 1, "Region": 2, "Postcode": 3, "Country": 4, "SubTypes": 5, "PostOfficeBox": 6 } } Enum { name: "AddressSubType" values: { "Parcel": 0, "Postal": 1, "Domestic": 2, "International": 3 } } Property { name: "street"; type: "string" } Property { name: "locality"; type: "string" } Property { name: "region"; type: "string" } Property { name: "postcode"; type: "string" } Property { name: "country"; type: "string" } Property { name: "subTypes"; type: "QList" } Property { name: "postOfficeBox"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactAnniversary" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Anniversary 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "CalendarId": 0, "OriginalDate": 1, "Event": 2, "SubType": 3 } } Enum { name: "AnniversarySubType" values: { "Wedding": 0, "Engagement": 1, "House": 2, "Employment": 3, "Memorial": 4 } } Property { name: "calendarId"; type: "string" } Property { name: "originalDate"; type: "QDate" } Property { name: "originalDateTime"; type: "QDateTime" } Property { name: "event"; type: "string" } Property { name: "subType"; type: "AnniversarySubType" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactAvatar" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Avatar 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "ImageUrl": 0, "VideoUrl": 1 } } Property { name: "imageUrl"; type: "QUrl" } Property { name: "videoUrl"; type: "QUrl" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactBirthday" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Birthday 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Birthday": 0 } } Property { name: "birthday"; type: "QDateTime" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactChangeLogFilter" prototype: "QtContacts::QDeclarativeContactFilter" exports: ["QtContacts/ChangeLogFilter 5.0"] exportMetaObjectRevisions: [0] Enum { name: "EventType" values: { "EventAdded": 0, "EventChanged": 1, "EventRemoved": 2 } } Property { name: "since"; type: "QDateTime" } Property { name: "eventType"; type: "EventType" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactCompoundFilter" defaultProperty: "filters" prototype: "QtContacts::QDeclarativeContactFilter" Property { name: "filters"; type: "QDeclarativeContactFilter"; isList: true; isReadonly: true } } Component { name: "QtContacts::QDeclarativeContactDetail" prototype: "QObject" exports: ["QtContacts/ContactDetail 5.0"] exportMetaObjectRevisions: [0] Enum { name: "DetailType" values: { "Address": 1, "Anniversary": 2, "Avatar": 3, "Birthday": 4, "DisplayLabel": 5, "Email": 6, "ExtendedDetail": 7, "Family": 8, "Favorite": 9, "Gender": 10, "Geolocation": 11, "GlobalPresence": 12, "Guid": 13, "Hobby": 14, "Name": 15, "NickName": 16, "Note": 17, "OnlineAccount": 18, "Organization": 19, "PhoneNumber": 20, "Presence": 21, "Ringtone": 22, "SyncTarget": 23, "Tag": 24, "Timestamp": 25, "Type": 26, "Url": 27, "Version": 28, "Unknown": 0 } } Enum { name: "ContextField" values: { "FieldContext": 5000, "ContextHome": 0, "ContextWork": 1, "ContextOther": 2 } } Property { name: "type"; type: "DetailType"; isReadonly: true } Property { name: "contexts"; type: "QList" } Property { name: "detailUri"; type: "string" } Property { name: "linkedDetailUris"; type: "QStringList" } Property { name: "fields"; type: "QList"; isReadonly: true } Property { name: "readOnly"; type: "bool"; isReadonly: true } Property { name: "removable"; type: "bool"; isReadonly: true } Signal { name: "detailChanged" } Method { name: "value" type: "QVariant" Parameter { name: "field"; type: "int" } } Method { name: "setValue" type: "bool" Parameter { name: "field"; type: "int" } Parameter { name: "value"; type: "QVariant" } } Method { name: "removeValue" type: "bool" Parameter { name: "field"; type: "int" } } } Component { name: "QtContacts::QDeclarativeContactDetailFilter" prototype: "QtContacts::QDeclarativeContactFilter" exports: ["QtContacts/DetailFilter 5.0"] exportMetaObjectRevisions: [0] Property { name: "detail"; type: "QDeclarativeContactDetail::DetailType" } Property { name: "field"; type: "int" } Property { name: "value"; type: "QVariant" } Property { name: "matchFlags"; type: "MatchFlags" } } Component { name: "QtContacts::QDeclarativeContactDetailRangeFilter" prototype: "QtContacts::QDeclarativeContactFilter" exports: ["QtContacts/DetailRangeFilter 5.0"] exportMetaObjectRevisions: [0] Enum { name: "RangeFlags" values: { "IncludeLower": 0, "IncludeUpper": 1, "ExcludeLower": 2, "ExcludeUpper": 0 } } Property { name: "detail"; type: "QDeclarativeContactDetail::DetailType" } Property { name: "field"; type: "int" } Property { name: "min"; type: "QVariant" } Property { name: "max"; type: "QVariant" } Property { name: "matchFlags"; type: "MatchFlags" } Property { name: "rangeFlags"; type: "RangeFlags" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactDisplayLabel" defaultProperty: "label" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/DisplayLabel 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Label": 0 } } Property { name: "label"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactEmailAddress" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/EmailAddress 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "EmailAddress": 0 } } Property { name: "emailAddress"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactExtendedDetail" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/ExtendedDetail 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Name": 0, "Data": 1 } } Property { name: "name"; type: "string" } Property { name: "data"; type: "QVariant" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactFamily" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Family 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Spouse": 0, "Children": 1 } } Property { name: "spouse"; type: "string" } Property { name: "children"; type: "QStringList" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactFavorite" defaultProperty: "index" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Favorite 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Favorite": 0, "Index": 1 } } Property { name: "favorite"; type: "bool" } Property { name: "index"; type: "int" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactFetchHint" prototype: "QObject" exports: ["QtContacts/FetchHint 5.0"] exportMetaObjectRevisions: [0] Enum { name: "OptimizationHints" values: { "AllRequired": 0, "NoRelationships": 1, "NoActionPreferences": 2, "NoBinaryBlobs": 4 } } Property { name: "detailTypesHint"; type: "QList" } Property { name: "relationshipTypesHint"; type: "QStringList" } Property { name: "optimizationHints"; type: "OptimizationHints" } Property { name: "imageWidth"; type: "int" } Property { name: "imageHeight"; type: "int" } Signal { name: "fetchHintChanged" } } Component { name: "QtContacts::QDeclarativeContactFilter" prototype: "QObject" exports: ["QtContacts/Filter 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FilterType" values: { "InvalidFilter": 0, "DetailFilter": 1, "DetailRangeFilter": 2, "ChangeLogFilter": 3, "ActionFilter": 4, "RelationshipFilter": 5, "IntersectionFilter": 6, "UnionFilter": 7, "IdFilter": 8, "DefaultFilter": 9 } } Enum { name: "MatchFlags" values: { "MatchExactly": 0, "MatchContains": 1, "MatchStartsWith": 2, "MatchEndsWith": 4, "MatchFixedString": 8, "MatchCaseSensitive": 16, "MatchPhoneNumber": 1024, "MatchKeypadCollation": 2048 } } Property { name: "type"; type: "FilterType"; isReadonly: true } Signal { name: "filterChanged" } } Component { name: "QtContacts::QDeclarativeContactGender" defaultProperty: "gender" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Gender 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Gender": 0 } } Enum { name: "GenderType" values: { "Male": 1, "Female": 2, "Unspecified": 3 } } Property { name: "gender"; type: "GenderType" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactGeoLocation" defaultProperty: "label" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Location 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Label": 0, "Latitude": 1, "Longitude": 2, "Accuracy": 3, "Altitude": 4, "AltitudeAccuracy": 5, "Heading": 6, "Speed": 7, "Timestamp": 8 } } Property { name: "label"; type: "string" } Property { name: "latitude"; type: "double" } Property { name: "longitude"; type: "double" } Property { name: "accuracy"; type: "double" } Property { name: "altitude"; type: "double" } Property { name: "altitudeAccuracy"; type: "double" } Property { name: "heading"; type: "double" } Property { name: "speed"; type: "double" } Property { name: "timestamp"; type: "QDateTime" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactGlobalPresence" defaultProperty: "state" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/GlobalPresence 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Timestamp": 0, "Nickname": 1, "State": 2, "StateText": 3, "ImageUrl": 4, "CustomMessage": 5 } } Property { name: "timestamp"; type: "QDateTime" } Property { name: "nickname"; type: "string" } Property { name: "state"; type: "QDeclarativeContactPresence::PresenceStateType" } Property { name: "stateText"; type: "string" } Property { name: "imageUrl"; type: "QUrl" } Property { name: "customMessage"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactGuid" defaultProperty: "guid" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Guid 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Guid": 0 } } Property { name: "guid"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactHobby" defaultProperty: "hobby" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Hobby 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Hobby": 0 } } Property { name: "hobby"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactIdFilter" defaultProperty: "ids" prototype: "QtContacts::QDeclarativeContactFilter" exports: ["QtContacts/IdFilter 5.0"] exportMetaObjectRevisions: [0] Property { name: "ids"; type: "QStringList" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactIntersectionFilter" defaultProperty: "filters" prototype: "QtContacts::QDeclarativeContactCompoundFilter" exports: ["QtContacts/IntersectionFilter 5.0"] exportMetaObjectRevisions: [0] } Component { name: "QtContacts::QDeclarativeContactModel" prototype: "QAbstractListModel" exports: ["QtContacts/ContactModel 5.0"] exportMetaObjectRevisions: [0] Enum { name: "ExportError" values: { "ExportNoError": 0, "ExportUnspecifiedError": 1, "ExportIOError": 2, "ExportOutOfMemoryError": 3, "ExportNotReadyError": 4 } } Enum { name: "ImportError" values: { "ImportNoError": 0, "ImportUnspecifiedError": 1, "ImportIOError": 2, "ImportOutOfMemoryError": 3, "ImportNotReadyError": 4, "ImportParseError": 5 } } Enum { name: "StorageLocation" values: { "UserDataStorage": 1, "SystemStorage": 2 } } Property { name: "manager"; type: "string" } Property { name: "storageLocations"; type: "int" } Property { name: "availableManagers"; type: "QStringList"; isReadonly: true } Property { name: "error"; type: "string"; isReadonly: true } Property { name: "autoUpdate"; type: "bool" } Property { name: "filter"; type: "QDeclarativeContactFilter"; isPointer: true } Property { name: "fetchHint"; type: "QDeclarativeContactFetchHint"; isPointer: true } Property { name: "contacts"; type: "QDeclarativeContact"; isList: true; isReadonly: true } Property { name: "sortOrders" type: "QDeclarativeContactSortOrder" isList: true isReadonly: true } Signal { name: "exportCompleted" Parameter { name: "error"; type: "ExportError" } Parameter { name: "url"; type: "QUrl" } } Signal { name: "importCompleted" Parameter { name: "error"; type: "ImportError" } Parameter { name: "url"; type: "QUrl" } } Signal { name: "contactsFetched" Parameter { name: "requestId"; type: "int" } Parameter { name: "fetchedContacts"; type: "QVariantList" } } Method { name: "update" } Method { name: "removeContact" Parameter { name: "id"; type: "string" } } Method { name: "removeContacts" Parameter { name: "ids"; type: "QStringList" } } Method { name: "saveContact" Parameter { name: "dc"; type: "QDeclarativeContact"; isPointer: true } Parameter { name: "storageLocation"; type: "StorageLocation" } } Method { name: "saveContact" Parameter { name: "dc"; type: "QDeclarativeContact"; isPointer: true } } Method { name: "fetchContacts" type: "int" Parameter { name: "contactIds"; type: "QStringList" } } Method { name: "importContacts" Parameter { name: "url"; type: "QUrl" } Parameter { name: "profiles"; type: "QStringList" } } Method { name: "importContacts" Parameter { name: "url"; type: "QUrl" } } Method { name: "exportContacts" Parameter { name: "url"; type: "QUrl" } Parameter { name: "profiles"; type: "QStringList" } Parameter { name: "declarativeContacts"; type: "QVariantList" } } Method { name: "exportContacts" Parameter { name: "url"; type: "QUrl" } Parameter { name: "profiles"; type: "QStringList" } } Method { name: "exportContacts" Parameter { name: "url"; type: "QUrl" } } } Component { name: "QtContacts::QDeclarativeContactName" defaultProperty: "firstName" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Name 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Prefix": 0, "FirstName": 1, "MiddleName": 2, "LastName": 3, "Suffix": 4 } } Property { name: "prefix"; type: "string" } Property { name: "firstName"; type: "string" } Property { name: "middleName"; type: "string" } Property { name: "lastName"; type: "string" } Property { name: "suffix"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactNickname" defaultProperty: "nickname" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Nickname 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "NickName": 0 } } Property { name: "nickname"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactNote" defaultProperty: "note" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Note 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Note": 0 } } Property { name: "note"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactOnlineAccount" defaultProperty: "accountUri" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/OnlineAccount 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "AccountUri": 0, "ServiceProvider": 1, "Capabilities": 3, "SubTypes": 4 } } Enum { name: "OnlineAccountSubType" values: { "Sip": 0, "SipVoip": 1, "Impp": 2, "VideoShare": 3 } } Property { name: "accountUri"; type: "string" } Property { name: "serviceProvider"; type: "string" } Property { name: "capabilities"; type: "QStringList" } Property { name: "subTypes"; type: "QList" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactOrganization" defaultProperty: "name" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Organization 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Name": 0, "LogoUrl": 1, "Department": 2, "Location": 3, "Role": 4, "Title": 5, "AssistantName": 6 } } Property { name: "name"; type: "string" } Property { name: "logoUrl"; type: "QUrl" } Property { name: "department"; type: "QStringList" } Property { name: "location"; type: "string" } Property { name: "role"; type: "string" } Property { name: "title"; type: "string" } Property { name: "assistantName"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactPhoneNumber" defaultProperty: "number" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/PhoneNumber 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Number": 0, "SubTypes": 1 } } Enum { name: "PhoneNumberSubType" values: { "Landline": 0, "Mobile": 1, "Fax": 2, "Pager": 3, "Voice": 4, "Modem": 5, "Video": 6, "Car": 7, "BulletinBoardSystem": 8, "MessagingCapable": 9, "Assistant": 10, "DtmfMenu": 11 } } Property { name: "number"; type: "string" } Property { name: "subTypes"; type: "QList" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactPresence" defaultProperty: "state" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Presence 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Timestamp": 0, "Nickname": 1, "State": 2, "StateText": 3, "ImageUrl": 4, "CustomMessage": 5 } } Enum { name: "PresenceStateType" values: { "Unknown": 0, "Available": 1, "Hidden": 2, "Busy": 3, "Away": 4, "ExtendedAway": 5, "Offline": 6 } } Property { name: "timestamp"; type: "QDateTime" } Property { name: "nickname"; type: "string" } Property { name: "state"; type: "PresenceStateType" } Property { name: "stateText"; type: "string" } Property { name: "imageUrl"; type: "QUrl" } Property { name: "customMessage"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactRelationship" prototype: "QObject" exports: ["QtContacts/Relationship 5.0"] exportMetaObjectRevisions: [0] Enum { name: "RelationshipRole" values: { "First": 0, "Second": 1, "Either": 2 } } Enum { name: "RelationshipType" values: { "Unknown": 0, "HasMember": 1, "Aggregates": 2, "IsSameAs": 3, "HasAssistant": 4, "HasManager": 5, "HasSpouse": 6 } } Property { name: "first"; type: "QDeclarativeContact"; isPointer: true } Property { name: "second"; type: "QDeclarativeContact"; isPointer: true } Property { name: "type"; type: "QVariant" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactRelationshipFilter" prototype: "QtContacts::QDeclarativeContactFilter" exports: ["QtContacts/RelationshipFilter 5.0"] exportMetaObjectRevisions: [0] Property { name: "relationshipType"; type: "QVariant" } Property { name: "relatedContact"; type: "QDeclarativeContact"; isPointer: true } Property { name: "relatedContactRole" type: "QDeclarativeContactRelationship::RelationshipRole" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactRelationshipModel" prototype: "QAbstractListModel" exports: ["QtContacts/RelationshipModel 5.0"] exportMetaObjectRevisions: [0] Property { name: "manager"; type: "string" } Property { name: "autoUpdate"; type: "bool" } Property { name: "participant"; type: "QDeclarativeContact"; isPointer: true } Property { name: "relationshipType"; type: "QVariant" } Property { name: "role"; type: "QDeclarativeContactRelationship::RelationshipRole" } Property { name: "relationships" type: "QDeclarativeContactRelationship" isList: true isReadonly: true } Property { name: "error"; type: "string"; isReadonly: true } Method { name: "removeRelationship" Parameter { name: "dcr"; type: "QDeclarativeContactRelationship"; isPointer: true } } Method { name: "addRelationship" Parameter { name: "dcr"; type: "QDeclarativeContactRelationship"; isPointer: true } } } Component { name: "QtContacts::QDeclarativeContactRingtone" defaultProperty: "audioRingtoneUrl" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Ringtone 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "AudioRingtoneUrl": 0, "VideoRingtoneUrl": 1, "VibrationRingtoneUrl": 2 } } Property { name: "audioRingtoneUrl"; type: "QUrl" } Property { name: "videoRingtoneUrl"; type: "QUrl" } Property { name: "vibrationRingtoneUrl"; type: "QUrl" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactSortOrder" prototype: "QObject" exports: ["QtContacts/SortOrder 5.0"] exportMetaObjectRevisions: [0] Enum { name: "BlankPolicy" values: { "BlanksFirst": 0, "BlanksLast": 1 } } Property { name: "detail"; type: "int" } Property { name: "field"; type: "int" } Property { name: "direction"; type: "Qt::SortOrder" } Property { name: "blankPolicy"; type: "BlankPolicy" } Property { name: "caseSensitivity"; type: "Qt::CaseSensitivity" } Signal { name: "sortOrderChanged" } } Component { name: "QtContacts::QDeclarativeContactSyncTarget" defaultProperty: "syncTarget" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/SyncTarget 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "SyncTarget": 0 } } Property { name: "syncTarget"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactTag" defaultProperty: "tag" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Tag 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Tag": 0 } } Property { name: "tag"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactTimestamp" defaultProperty: "lastModified" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Timestamp 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "LastModified": 0, "Created": 1 } } Property { name: "lastModified"; type: "QDateTime" } Property { name: "created"; type: "QDateTime" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactType" defaultProperty: "type" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Type 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "TypeField": 0 } } Enum { name: "ContactType" values: { "Unspecified": 0, "Contact": 0, "Group": 1 } } Property { name: "type"; type: "ContactType" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactUnionFilter" defaultProperty: "filters" prototype: "QtContacts::QDeclarativeContactCompoundFilter" exports: ["QtContacts/UnionFilter 5.0"] exportMetaObjectRevisions: [0] } Component { name: "QtContacts::QDeclarativeContactUrl" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Url 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "Url": 0, "SubType": 1 } } Enum { name: "UrlSubType" values: { "Unknown": 0, "HomePage": 0, "Blog": 1, "Favourite": 2 } } Property { name: "url"; type: "string" } Property { name: "subType"; type: "UrlSubType" } Signal { name: "valueChanged" } } Component { name: "QtContacts::QDeclarativeContactVersion" prototype: "QtContacts::QDeclarativeContactDetail" exports: ["QtContacts/Version 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FieldType" values: { "SequenceNumber": 0, "ExtendedVersion": 1 } } Property { name: "sequenceNumber"; type: "int" } Property { name: "extendedVersion"; type: "string" } Signal { name: "valueChanged" } } } src/imports/contacts/qdeclarativecontact.cpp000066400000000000000000000677301233466112000217170ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativecontact_p.h" #include #include #include #include #include "qdeclarativecontactmodel_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE /*! \qmltype Contact \instantiates QDeclarativeContact \brief The Contact element represents an addressbook contact. \ingroup qml-contacts-main \inqmlmodule QtContacts 5.0 The Contact element is part of the \b{QtContacts} module. A Contact object has a collection of details (like a name, phone numbers and email addresses). Each detail (which can have multiple fields) is stored in an appropriate subclass of ContactDetail, and the Contact allows retrieving these details in various ways. If some of the contact details are not unique details, all of this type of detail values can be accessed by the property with the name in plural. For example, if there are 3 phone numbers stored in a contact, they can be accessed by contact.phoneNumbers property, which holds a list of all PhoneNumber details. If a contact does not contain a detail of particular type, the value of the corresponding singular property (e.g. phoneNumber) in undefined and the plural (e.g. phoneNumbers) is empty. The list of properties which support multiple detail instances depends on the contact engine implementations. \sa QContact */ // call-back function templates for list properties template static void list_property_append(QQmlListProperty *, T *) { } template static int list_property_count(QQmlListProperty *property) { QDeclarativeContact *object = qobject_cast(property->object); if (object) return object->details(detailType).size(); else return 0; } template static T *list_property_at(QQmlListProperty *property, int index) { QDeclarativeContact *object = qobject_cast(property->object); if (object) return qobject_cast(qvariant_cast(object->details(detailType).at(index))); else return 0; } template static void list_property_clear(QQmlListProperty *) { } QDeclarativeContact::QDeclarativeContact(QObject *parent) :QObject(parent) , m_modified(false) { connect(this, SIGNAL(contactChanged()), SLOT(setModified())); } QDeclarativeContact::~QDeclarativeContact() { clearDetails(); } void QDeclarativeContact::setContact(const QContact& contact) { m_id = contact.id(); foreach (QDeclarativeContactDetail *detail, m_details) delete detail; m_details.clear(); m_preferredDetails.clear(); QList details(contact.details()); foreach (const QContactDetail &detail, details) { QDeclarativeContactDetail *contactDetail = QDeclarativeContactDetailFactory::createContactDetail(static_cast(detail.type())); contactDetail->setParent(this); contactDetail->setDetail(detail); connect(contactDetail, SIGNAL(detailChanged()), this, SIGNAL(contactChanged())); m_details.append(contactDetail); } QMap prefDetails(contact.preferredDetails()); QMap::const_iterator it = prefDetails.begin(); while (it != prefDetails.end()) { m_preferredDetails.insert(it.key(), it.value().key()); it++; } m_modified = false; emit contactChanged(); } QContact QDeclarativeContact::contact() const { QContact contact; contact.setId(m_id); foreach (QDeclarativeContactDetail *detail, m_details) contact.saveDetail(&detail->detail()); QVariantMap prefDetails = preferredDetails(); QVariantMap::const_iterator it = prefDetails.begin(); while (it != prefDetails.end()) { contact.setPreferredDetail(it.key(), it.value().value()->detail()); it++; } return contact; } /*! \qmlproperty bool Contact::modified This property holds the dirty flag of the Contact object. If the Contact has been changed, returns true, otherwise returns false. */ bool QDeclarativeContact::modified() const { return m_modified; } void QDeclarativeContact::setModified() { m_modified = true; } /*! \qmlproperty enumeration Contact::type This property holds type of the Contact, the value can be one of: \list \li Contact.Contact \li Contact.Group \endlist */ QDeclarativeContactType::ContactType QDeclarativeContact::type() const { foreach (QDeclarativeContactDetail *detail, m_details) { if (QDeclarativeContactDetail::Type == detail->detailType()) return static_cast(detail)->type(); } return QDeclarativeContactType::Contact; } /*! \qmlmethod Contact::removeDetail(detail) Removes the given contact \a detail from the contact, returns true if successful, otherwise returns false. */ bool QDeclarativeContact::removeDetail(QDeclarativeContactDetail* detail) { if (detail) { if (!detail->removable()) return false; int key = detail->detail().key(); int i = 0; foreach (QDeclarativeContactDetail *contactDetail, m_details) { if (key == contactDetail->detail().key()) { removePreferredDetail(detail); delete contactDetail; m_details.removeAt(i); emit contactChanged(); return true; } ++i; } } return false; } void QDeclarativeContact::removePreferredDetail(QDeclarativeContactDetail* detail) { QMap cpy = m_preferredDetails; QMap::const_iterator it = cpy.begin(); while (it != cpy.end()) { if (it.value() == detail->detail().key()) { m_preferredDetails.remove(it.key()); break; } it++; } } /*! \qmlmethod Contact::addDetail(detail) Adds the given contact \a detail to the contact, returns true if successful, otherwise returns false. Note: If the \a detail has been added into the same contact before, this operation will be ignored, so if you want to add a \a detail multiple times, the \a detail should be copied before calling this function. */ bool QDeclarativeContact::addDetail(QDeclarativeContactDetail* detail) { if (!detail || m_details.contains(detail)) return false; QDeclarativeContactDetail *contactDetail = QDeclarativeContactDetailFactory::createContactDetail(detail->detailType()); contactDetail->setParent(this); contactDetail->setDetail(detail->detail()); connect(contactDetail, SIGNAL(detailChanged()), this, SIGNAL(contactChanged())); m_details.append(contactDetail); m_modified = true; emit contactChanged(); return true; } /*! \qmlmethod Contact::setPreferredDetail(actionName, detail) Set a particular detail (\a preferredDetail) as the preferred detail for any actions with the given \a actionName. The \a preferredDetail object must exist in this object, and the \a actionName cannot be empty. Returns true if the preference could be recorded, and false otherwise. \sa preferredDetail() */ bool QDeclarativeContact::setPreferredDetail(const QString& actionName, QDeclarativeContactDetail* detail) { if (actionName.isEmpty() || !detail || !m_details.contains(detail)) return false; if (m_preferredDetails.contains(actionName) && m_preferredDetails[actionName] == detail->detail().key()) return false; m_preferredDetails.insert(actionName, detail->detail().key()); m_modified = true; emit contactChanged(); return true; } /*! \qmlmethod Contact::isPreferredDetail(actionName, detail) Returns true if the given \a detail is a preferred detail for the given \a actionName, or for any action if the \a actionName is empty. \sa preferredDetail() */ bool QDeclarativeContact::isPreferredDetail(const QString& actionName, QDeclarativeContactDetail* detail) const { if (actionName.isEmpty() || !detail || !m_details.contains(detail)) return false; if (!m_preferredDetails.contains(actionName)) return false; return (m_preferredDetails[actionName] == detail->detail().key()); } /*! \qmlmethod Contact::preferredDetail(actionName, detail) Returns the preferred detail for a given \a actionName. If the \a actionName is empty, or there is no preference recorded for the supplied \a actionName, returns null. \sa preferredDetails() */ QDeclarativeContactDetail* QDeclarativeContact::preferredDetail(const QString& actionName) const { int id = m_preferredDetails.value(actionName, -1); if (id == -1) return 0; foreach (QDeclarativeContactDetail* detail, m_details) { if (detail->detail().key() == id) return detail; } return 0; } /*! \qmlproperty map Contact::preferredDetails This property holds the recorded detail preferences for action names. Each entry in the map has the action name as the key, and the corresponding preferred detail as the value. */ QVariantMap QDeclarativeContact::preferredDetails() const { QVariantMap result; QMap::const_iterator it = m_preferredDetails.begin(); while (it != m_preferredDetails.end()) { result.insert(it.key(), QVariant::fromValue(preferredDetail(it.key()))); it++; } return result; } /*! \qmlproperty list Contact::contactDetails This property holds the list of all the details that the contact has. */ QQmlListProperty QDeclarativeContact::contactDetails() { return QQmlListProperty(this, 0, &QDeclarativeContact::_q_detail_append, &QDeclarativeContact::_q_detail_count, &QDeclarativeContact::_q_detail_at, &QDeclarativeContact::_q_detail_clear); } /*! \qmlproperty int Contact::contactId This property holds the id of the Contact object. This property is read only. */ QString QDeclarativeContact::contactId() const { return m_id.toString(); } /*! \qmlproperty string Contact::manager This property holds the manager name which the Contact object comes from. */ QString QDeclarativeContact::manager() const { return m_id.managerUri(); } /*! \qmlmethod QDeclarativeContactDetail* QDeclarativeContact::detail(int type) Returns contactDetail object which detail name or detail type is \a name. */ QDeclarativeContactDetail* QDeclarativeContact::detail(int type) { foreach (QDeclarativeContactDetail *detail, m_details) { if (type == detail->detailType()) { return detail; } } return 0; } /*! \qmlmethod QVariantList QDeclarativeContact::details(int type) Returns a list of ContactDetail objects which detail name or detail type is \a name. */ QVariantList QDeclarativeContact::details(int type) { QVariantList list; foreach (QDeclarativeContactDetail *detail, m_details) { if (type == detail->detailType()) { list.append(QVariant::fromValue((QObject*)detail)); } } return list; } /*! \qmlmethod Contact::clearDetails() Remove all detail objects in this contact. */ void QDeclarativeContact::clearDetails() { if (m_details.isEmpty()) return; foreach (QDeclarativeContactDetail *detail, m_details) delete detail; m_details.clear(); m_modified = true; emit contactChanged(); } /*! \qmlmethod Contact::save() Saves this Contact if the contact has been modified. \sa Contact::modified */ void QDeclarativeContact::save() { if (modified()) { QDeclarativeContactModel* model = qobject_cast(parent()); if (model) { model->saveContact(this); m_modified = false; } } } // convenient access to most frequently used details /*! \qmlproperty Address Contact::address This property holds the address detail of the Contact object. In case a contact has several addresses then the first one is returned. */ QDeclarativeContactAddress* QDeclarativeContact::address() { return getDetail(QDeclarativeContactDetail::Address); } /*! \qmlproperty list
Contact::addresses This property holds the address details of the Contact object. */ QQmlListProperty QDeclarativeContact::addresses() { return QQmlListProperty( this, 0, &list_property_append, &list_property_count, &list_property_at, &list_property_clear); } /*! \qmlproperty Anniversary Contact::anniversary This property holds the anniversary detail of the Contact object. */ QDeclarativeContactAnniversary* QDeclarativeContact::anniversary() { return getDetail(QDeclarativeContactDetail::Anniversary); } /*! \qmlproperty Avatar Contact::avatar This property holds the avatar detail of the Contact object. */ QDeclarativeContactAvatar* QDeclarativeContact::avatar() { return getDetail(QDeclarativeContactDetail::Avatar); } /*! \qmlproperty Birthday Contact::birthday This property holds the birthday detail of the Contact object. */ QDeclarativeContactBirthday* QDeclarativeContact::birthday() { return getDetail(QDeclarativeContactDetail::Birthday); } /*! \qmlproperty DisplayLabel Contact::displayLabel This property holds the displayLabel detail of the Contact object. display label is the one which gets displayed when a contact is created as per versit doc specs this is a "FN" property */ QDeclarativeContactDisplayLabel* QDeclarativeContact::displayLabel() { return getDetail(QDeclarativeContactDetail::DisplayLabel); } /*! \qmlproperty EmailAddress Contact::email This property holds the email address detail of the Contact object. In case a contact has several email addresses then the first one is returned. */ QDeclarativeContactEmailAddress* QDeclarativeContact::email() { return getDetail(QDeclarativeContactDetail::Email); } /*! \qmlproperty list Contact::emails This property holds the email address details of the Contact object. */ QQmlListProperty QDeclarativeContact::emails() { return QQmlListProperty( this, 0, &list_property_append, &list_property_count, &list_property_at, &list_property_clear); } /*! \qmlproperty ExtendedDetail Contact::extendedDetail This property holds the extended detail of the Contact object. In case a contact has several extended details then the first one is returned. */ QDeclarativeContactExtendedDetail* QDeclarativeContact::extendedDetail() { return getDetail(QDeclarativeContactDetail::ExtendedDetail); } /*! \qmlproperty list Contact::extendedDetails This property holds the extended details of the Contact object. */ QQmlListProperty QDeclarativeContact::extendedDetails() { return QQmlListProperty( this, 0, &list_property_append, &list_property_count, &list_property_at, &list_property_clear); } /*! \qmlproperty Family Contact::family This property holds the family detail of the Contact object. */ QDeclarativeContactFamily* QDeclarativeContact::family() { return getDetail(QDeclarativeContactDetail::Family); } /*! \qmlproperty Favorite Contact::favorite This property holds the favorite detail of the Contact object. */ QDeclarativeContactFavorite* QDeclarativeContact::favorite() { return getDetail(QDeclarativeContactDetail::Favorite); } /*! \qmlproperty Gender Contact::gender This property holds the gender detail of the Contact object. */ QDeclarativeContactGender* QDeclarativeContact::gender() { return getDetail(QDeclarativeContactDetail::Gender); } /*! \qmlproperty GeoLocation Contact::geolocation This property holds the geolocation detail of the Contact object. */ QDeclarativeContactGeoLocation* QDeclarativeContact::geolocation() { return getDetail(QDeclarativeContactDetail::Geolocation); } /*! \qmlproperty GlobalPresence Contact::globalPresence This property holds the globalPresence detail of the Contact object. */ QDeclarativeContactGlobalPresence* QDeclarativeContact::globalPresence() { return getDetail(QDeclarativeContactDetail::GlobalPresence); } /*! \qmlproperty Guid Contact::guid This property holds the guid detail of the Contact object. */ QDeclarativeContactGuid* QDeclarativeContact::guid() { return getDetail(QDeclarativeContactDetail::Guid); } /*! \qmlproperty Hobby Contact::hobby This property holds the hobby detail of the Contact object. */ QDeclarativeContactHobby* QDeclarativeContact::hobby() { return getDetail(QDeclarativeContactDetail::Hobby); } /*! \qmlproperty Name Contact::name This property holds the name detail of the Contact object. */ QDeclarativeContactName* QDeclarativeContact::name() { return getDetail(QDeclarativeContactDetail::Name); } /*! \qmlproperty Nickname Contact::nickname This property holds the nickname detail of the Contact object. */ QDeclarativeContactNickname* QDeclarativeContact::nickname() { return getDetail(QDeclarativeContactDetail::NickName); } /*! \qmlproperty Note Contact::note This property holds the note detail of the Contact object. */ QDeclarativeContactNote* QDeclarativeContact::note() { return getDetail(QDeclarativeContactDetail::Note); } /*! \qmlproperty OnlineAccount Contact::onlineAccount This property holds the onlineAccount detail of the Contact object. In case a contact has several accounts then the first one is returned. */ QDeclarativeContactOnlineAccount* QDeclarativeContact::onlineAccount() { return getDetail(QDeclarativeContactDetail::OnlineAccount); } /*! \qmlproperty Organization Contact::organization This property holds the organization detail of the Contact object. */ QDeclarativeContactOrganization* QDeclarativeContact::organization() { return getDetail(QDeclarativeContactDetail::Organization); } /*! \qmlproperty list Contact::organizations This property holds the organization details of the Contact object. */ QQmlListProperty QDeclarativeContact::organizations() { return QQmlListProperty( this, 0, &list_property_append, &list_property_count, &list_property_at, &list_property_clear); } /*! \qmlproperty PhoneNumber Contact::phoneNumber This property holds the phone number detail of the Contact object. In case a contact has several numbers then the first one is returned. */ QDeclarativeContactPhoneNumber* QDeclarativeContact::phoneNumber() { return getDetail(QDeclarativeContactDetail::PhoneNumber); } /*! \qmlproperty list Contact::phoneNumbers This property holds the phone number details of the Contact object. */ QQmlListProperty QDeclarativeContact::phoneNumbers() { return QQmlListProperty( this, 0, &list_property_append, &list_property_count, &list_property_at, &list_property_clear); } /*! \qmlproperty Presence Contact::presence This property holds the presence detail of the Contact object. */ QDeclarativeContactPresence* QDeclarativeContact::presence() { return getDetail(QDeclarativeContactDetail::Presence); } /*! \qmlproperty Ringtone Contact::ringtone This property holds the ringtone detail of the Contact object. */ QDeclarativeContactRingtone* QDeclarativeContact::ringtone() { return getDetail(QDeclarativeContactDetail::Ringtone); } /*! \qmlproperty SyncTarget Contact::syncTarget This property holds the syncTarget detail of the Contact object. */ QDeclarativeContactSyncTarget* QDeclarativeContact::syncTarget() { return getDetail(QDeclarativeContactDetail::SyncTarget); } /*! \qmlproperty Tag Contact::tag This property holds the tag detail of the Contact object. */ QDeclarativeContactTag* QDeclarativeContact::tag() { return getDetail(QDeclarativeContactDetail::Tag); } /*! \qmlproperty Timestamp Contact::timestamp This property holds the timestamp detail of the Contact object. */ QDeclarativeContactTimestamp* QDeclarativeContact::timestamp() { return getDetail(QDeclarativeContactDetail::Timestamp); } /*! \qmlproperty Url Contact::url This property holds the url detail of the Contact object. */ QDeclarativeContactUrl* QDeclarativeContact::url() { return getDetail(QDeclarativeContactDetail::Url); } /*! \qmlproperty list Contact::urls This property holds the url details of the Contact object. */ QQmlListProperty QDeclarativeContact::urls() { return QQmlListProperty( this, 0, &list_property_append, &list_property_count, &list_property_at, &list_property_clear); } /*! \qmlproperty Version Contact::version This property holds the version detail of the Contact object. */ QDeclarativeContactVersion* QDeclarativeContact::version() { return getDetail(QDeclarativeContactDetail::Version); } // call-back functions for list property /*! \internal */ void QDeclarativeContact::_q_detail_append(QQmlListProperty *property, QDeclarativeContactDetail *value) { QDeclarativeContact *object = qobject_cast(property->object); if (object) { object->m_details.append(value); value->connect(value, SIGNAL(valueChanged()), SIGNAL(detailChanged()), Qt::UniqueConnection); value->connect(value, SIGNAL(detailChanged()), object, SIGNAL(contactChanged()), Qt::UniqueConnection); } } /*! \internal */ QDeclarativeContactDetail *QDeclarativeContact::_q_detail_at(QQmlListProperty *property, int index) { QDeclarativeContact *object = qobject_cast(property->object); if (object) return object->m_details.at(index); else return 0; } /*! \internal */ void QDeclarativeContact::_q_detail_clear(QQmlListProperty *property) { QDeclarativeContact *object = qobject_cast(property->object); if (object) { foreach (QDeclarativeContactDetail *obj, object->m_details) delete obj; object->m_details.clear(); } } /*! \internal */ int QDeclarativeContact::_q_detail_count(QQmlListProperty *property) { QDeclarativeContact *object = qobject_cast(property->object); if (object) return object->m_details.size(); else return 0; } #include "moc_qdeclarativecontact_p.cpp" QT_END_NAMESPACE src/imports/contacts/qdeclarativecontact_p.h000066400000000000000000000242401233466112000216700ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACT_P_H #define QDECLARATIVECONTACT_P_H #include #include #include #include "qdeclarativecontactdetails_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContact : public QObject { Q_OBJECT // basic information Q_PROPERTY (bool modified READ modified) Q_PROPERTY (QDeclarativeContactType::ContactType type READ type NOTIFY contactChanged) Q_PROPERTY (QString contactId READ contactId NOTIFY contactIdChanged) Q_PROPERTY (QString manager READ manager NOTIFY managerChanged) Q_PROPERTY (QQmlListProperty contactDetails READ contactDetails NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactAddress* address READ address NOTIFY contactChanged) Q_PROPERTY (QQmlListProperty addresses READ addresses NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactAnniversary* anniversary READ anniversary NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactAvatar* avatar READ avatar NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactBirthday* birthday READ birthday NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactDisplayLabel* displayLabel READ displayLabel NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactEmailAddress* email READ email NOTIFY contactChanged) Q_PROPERTY (QQmlListProperty emails READ emails NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactExtendedDetail* extendedDetail READ extendedDetail NOTIFY contactChanged) Q_PROPERTY (QQmlListProperty extendedDetails READ extendedDetails NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactFamily* family READ family NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactFavorite* favorite READ favorite NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactGender* gender READ gender NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactGeoLocation* geolocation READ geolocation NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactGlobalPresence* globalPresence READ globalPresence NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactGuid* guid READ guid NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactHobby* hobby READ hobby NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactName* name READ name NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactNickname* nickname READ nickname NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactNote* note READ note NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactOnlineAccount* onlineAccount READ onlineAccount NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactOrganization* organization READ organization NOTIFY contactChanged) Q_PROPERTY (QQmlListProperty organizations READ organizations NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactPhoneNumber* phoneNumber READ phoneNumber NOTIFY contactChanged) Q_PROPERTY (QQmlListProperty phoneNumbers READ phoneNumbers NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactPresence* presence READ presence NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactRingtone* ringtone READ ringtone NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactSyncTarget* syncTarget READ syncTarget NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactTag* tag READ tag NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactTimestamp* timestamp READ timestamp NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactUrl* url READ url NOTIFY contactChanged) Q_PROPERTY (QQmlListProperty urls READ urls NOTIFY contactChanged) Q_PROPERTY (QDeclarativeContactVersion* version READ version NOTIFY contactChanged) Q_PROPERTY (QVariantMap preferredDetails READ preferredDetails NOTIFY contactChanged) Q_CLASSINFO("DefaultProperty", "contactDetails") public: explicit QDeclarativeContact(QObject *parent = 0); ~QDeclarativeContact(); void setContact(const QContact& c); QContact contact() const; bool modified() const; QDeclarativeContactType::ContactType type() const; QString contactId() const; QString manager() const; QQmlListProperty contactDetails(); // use int instead of QDeclarativeContactDetail::ContactType as a work-around for QTBUG-20639 Q_INVOKABLE QDeclarativeContactDetail* detail(int type); Q_INVOKABLE QVariantList details(int type); Q_INVOKABLE bool removeDetail(QDeclarativeContactDetail* detail); Q_INVOKABLE bool addDetail(QDeclarativeContactDetail* detail); Q_INVOKABLE bool setPreferredDetail(const QString& actionName, QDeclarativeContactDetail* detail); Q_INVOKABLE bool isPreferredDetail(const QString& actionName, QDeclarativeContactDetail* detail) const; Q_INVOKABLE QDeclarativeContactDetail* preferredDetail(const QString& actionName) const; QVariantMap preferredDetails() const; QDeclarativeContactAddress* address(); QQmlListProperty addresses(); QDeclarativeContactAnniversary* anniversary(); QDeclarativeContactAvatar* avatar(); QDeclarativeContactBirthday* birthday(); QDeclarativeContactDisplayLabel* displayLabel(); QDeclarativeContactEmailAddress* email(); QQmlListProperty emails(); QDeclarativeContactExtendedDetail* extendedDetail(); QQmlListProperty extendedDetails(); QDeclarativeContactFamily* family(); QDeclarativeContactFavorite* favorite(); QDeclarativeContactGender* gender(); QDeclarativeContactGeoLocation* geolocation(); QDeclarativeContactGlobalPresence* globalPresence(); QDeclarativeContactGuid* guid(); QDeclarativeContactHobby* hobby(); QDeclarativeContactName* name(); QDeclarativeContactNickname* nickname(); QDeclarativeContactNote* note(); QDeclarativeContactOnlineAccount* onlineAccount(); QDeclarativeContactOrganization* organization(); QQmlListProperty organizations(); QDeclarativeContactPhoneNumber* phoneNumber(); QQmlListProperty phoneNumbers(); QDeclarativeContactPresence* presence(); QDeclarativeContactRingtone* ringtone(); QDeclarativeContactSyncTarget* syncTarget(); QDeclarativeContactTag* tag(); QDeclarativeContactTimestamp* timestamp(); QDeclarativeContactUrl* url(); QQmlListProperty urls(); QDeclarativeContactVersion* version(); protected: bool m_modified; QContactId m_id; // always create a copy of the detail for QML // however, seems the garbage collection can't delete all of them (QTBUG-20377) QList m_details; QMap m_preferredDetails; public slots: void clearDetails(); void save(); Q_SIGNALS: void contactIdChanged(); void managerChanged(); void contactChanged(); private slots: void setModified(); private: Q_DISABLE_COPY(QDeclarativeContact) template T* getDetail(const QDeclarativeContactDetail::DetailType &type) { foreach (QDeclarativeContactDetail *detail, m_details) { if (type == detail->detailType()) { T* tempDetail = static_cast(detail); return tempDetail; } } T* detail = new T; if (detail) { m_details.append(detail); emit contactChanged(); return detail; } return 0; } void removePreferredDetail(QDeclarativeContactDetail *detail); // call-back functions for list property static void _q_detail_append(QQmlListProperty *property, QDeclarativeContactDetail *value); static QDeclarativeContactDetail *_q_detail_at(QQmlListProperty *property, int index); static void _q_detail_clear(QQmlListProperty *property); static int _q_detail_count(QQmlListProperty *property); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContact) #endif // QDECLARATIVECONTACT_P_H src/imports/contacts/qdeclarativecontactdetail.cpp000066400000000000000000001120211233466112000230620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativecontactdetail_p.h" #include "qdeclarativecontactdetails_p.h" #include "qdeclarativecontact_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE /* ==================== QDeclarativeContactDetail ======================= */ /*! \qmltype ContactDetail \instantiates QDeclarativeContactDetail \brief The ContactDetail element represents a single, complete detail about a contact. \ingroup qml-contacts-main \inqmlmodule QtContacts 5.0 \sa QContactDetail The ContactDetail element is part of the \b{QtContacts} module. */ QDeclarativeContactDetail::QDeclarativeContactDetail(QObject* parent) :QObject(parent) { QDeclarativeContact* c = qobject_cast(parent); if (c) connect(this, SIGNAL(detailChanged()), c, SIGNAL(contactChanged())); } QDeclarativeContactDetail::~QDeclarativeContactDetail() { } QContactDetail& QDeclarativeContactDetail::detail() { return m_detail; } const QContactDetail& QDeclarativeContactDetail::detail() const { return m_detail; } void QDeclarativeContactDetail::setDetail(const QContactDetail& detail) { m_detail = detail; emit detailChanged(); } /*! \qmlproperty list ContactDetail::contexts This property holds one or more contexts that this detail is associated with. */ QList QDeclarativeContactDetail::contexts() const { return m_detail.contexts(); } void QDeclarativeContactDetail::setContexts(const QList& contexts) { m_detail.setContexts(contexts); } /*! \qmlproperty bool ContactDetail::readOnly This property indicates whether or not this detail is writable. This property is read only. */ bool QDeclarativeContactDetail::readOnly() const { return m_detail.accessConstraints().testFlag(QContactDetail::ReadOnly); } /*! \qmlproperty bool ContactDetail::removable This property indicates whether or not this detail is removale. This property is read only. */ bool QDeclarativeContactDetail::removable() const { return !m_detail.accessConstraints().testFlag(QContactDetail::Irremovable); } /*! \qmlproperty string ContactDetail::detailUri This property holds the unique URI of the detail if one exists. */ QString QDeclarativeContactDetail::detailUri() const { return m_detail.detailUri(); } void QDeclarativeContactDetail::setDetailUri(const QString& detailUri) { m_detail.setDetailUri(detailUri); } /*! \qmlproperty list ContactDetail::linkedDetailUris This property holds a list of detail URIs to which this detail is linked. */ QStringList QDeclarativeContactDetail::linkedDetailUris() const { return m_detail.linkedDetailUris(); } void QDeclarativeContactDetail::setLinkedDetailUris(const QStringList& linkedDetailUris) { m_detail.setLinkedDetailUris(linkedDetailUris); } /*! \qmlproperty enumeration ContactDetail::type This property holds the type of the detail. \list \li ContactDetail.Address \li ContactDetail.Anniversary \li ContactDetail.Avatar \li ContactDetail.Birthday \li ContactDetail.DisplayLabel \li ContactDetail.Email \li ContactDetail.ExtendedDetail \li ContactDetail.Family \li ContactDetail.Favorite \li ContactDetail.Gender \li ContactDetail.Geolocation \li ContactDetail.GlobalPresence \li ContactDetail.Guid \li ContactDetail.Hobby \li ContactDetail.Name \li ContactDetail.NickName \li ContactDetail.Note \li ContactDetail.OnlineAccount \li ContactDetail.Organization \li ContactDetail.PhoneNumber \li ContactDetail.Presence \li ContactDetail.Ringtone \li ContactDetail.SyncTarget \li ContactDetail.Tag \li ContactDetail.Timestamp \li ContactDetail.Url \li ContactDetail.Version \li ContactDetail.Unknown \endlist This property is read only. */ QDeclarativeContactDetail::DetailType QDeclarativeContactDetail::detailType() const { return Unknown; } /*! \qmlproperty list ContactDetail::fields This property holds the list of all fields which this detail supports. This property is read only. */ QList QDeclarativeContactDetail::fields() const { return m_detail.values().keys(); } QVariant QDeclarativeContactDetail::value(int field) const { return m_detail.value(field); } bool QDeclarativeContactDetail::setValue(int field, const QVariant& v) { bool changed = false; if (value(field) != v) changed = m_detail.setValue(field, v); if (changed) emit detailChanged(); return changed; } /*! \qmlmethod bool Detail::removeValue(field) Removes the value stored in this detail for the given \a field. Returns true if a value was stored for the given field and the operation succeeded, and false otherwise. */ bool QDeclarativeContactDetail::removeValue(int field) { bool ok = m_detail.removeValue(field); if (ok) emit detailChanged(); return ok; } QDeclarativeContactDetail *QDeclarativeContactDetailFactory::createContactDetail(QDeclarativeContactDetail::DetailType type) { QDeclarativeContactDetail *contactDetail; if (type == QDeclarativeContactDetail::Address) contactDetail = new QDeclarativeContactAddress; else if (type == QDeclarativeContactDetail::Anniversary) contactDetail = new QDeclarativeContactAnniversary; else if (type == QDeclarativeContactDetail::Avatar) contactDetail = new QDeclarativeContactAvatar; else if (type == QDeclarativeContactDetail::Birthday) contactDetail = new QDeclarativeContactBirthday; else if (type == QDeclarativeContactDetail::DisplayLabel) contactDetail = new QDeclarativeContactDisplayLabel; else if (type == QDeclarativeContactDetail::Email) contactDetail = new QDeclarativeContactEmailAddress; else if (type == QDeclarativeContactDetail::ExtendedDetail) contactDetail = new QDeclarativeContactExtendedDetail; else if (type == QDeclarativeContactDetail::Family) contactDetail = new QDeclarativeContactFamily; else if (type == QDeclarativeContactDetail::Favorite) contactDetail = new QDeclarativeContactFavorite; else if (type == QDeclarativeContactDetail::Gender) contactDetail = new QDeclarativeContactGender; else if (type == QDeclarativeContactDetail::Geolocation) contactDetail = new QDeclarativeContactGeoLocation; else if (type == QDeclarativeContactDetail::GlobalPresence) contactDetail = new QDeclarativeContactGlobalPresence; else if (type == QDeclarativeContactDetail::Guid) contactDetail = new QDeclarativeContactGuid; else if (type == QDeclarativeContactDetail::Hobby) contactDetail = new QDeclarativeContactHobby; else if (type == QDeclarativeContactDetail::Name) contactDetail = new QDeclarativeContactName; else if (type == QDeclarativeContactDetail::NickName) contactDetail = new QDeclarativeContactNickname; else if (type == QDeclarativeContactDetail::Note) contactDetail = new QDeclarativeContactNote; else if (type == QDeclarativeContactDetail::OnlineAccount) contactDetail = new QDeclarativeContactOnlineAccount; else if (type == QDeclarativeContactDetail::Organization) contactDetail = new QDeclarativeContactOrganization; else if (type == QDeclarativeContactDetail::PhoneNumber) contactDetail = new QDeclarativeContactPhoneNumber; else if (type == QDeclarativeContactDetail::Presence) contactDetail = new QDeclarativeContactPresence; else if (type == QDeclarativeContactDetail::Ringtone) contactDetail = new QDeclarativeContactRingtone; else if (type == QDeclarativeContactDetail::SyncTarget) contactDetail = new QDeclarativeContactSyncTarget; else if (type == QDeclarativeContactDetail::Tag) contactDetail = new QDeclarativeContactTag; else if (type == QDeclarativeContactDetail::Timestamp) contactDetail = new QDeclarativeContactTimestamp; else if (type == QDeclarativeContactDetail::Type) contactDetail = new QDeclarativeContactType; else if (type == QDeclarativeContactDetail::Url) contactDetail = new QDeclarativeContactUrl; else if (type == QDeclarativeContactDetail::Version) contactDetail = new QDeclarativeContactVersion; else contactDetail = new QDeclarativeContactDetail; return contactDetail; } /* ==================== QDeclarativeContactAddress ======================= */ /*! \qmltype Address \instantiates QDeclarativeContactAddress \brief The Address element contains an address of a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 The fields in the Address element are based on the segments of the ADR property of a Versit vCard file. Address element contains the following field types: \list \li Address.Street \li Address.Locality \li Address.Region \li Address.PostCode \li Address.Country \li Address.SubTypes \li Address.PostOfficeBox \endlist Versit \reg is a trademark of the Internet Mail Consortium. This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string Address::street This property holds the street number and street name of the address. */ /*! \qmlproperty string Address::locality This property holds the name of the city, town or suburb of the address. */ /*! \qmlproperty string Address::region This property holds the name or identifier of the state, province or region of the address. */ /*! \qmlproperty string Address::postcode This property holds the postal code for the address. */ /*! \qmlproperty string Address::country This property holds the name of the country of the address. */ /*! \qmlproperty list Address::subTypes This property stores the sub types of the address. \list \li Address.Parcel - An address for parcel delivery. \li Address.Postal - An address for postal delivery. \li Address.Domestic - An address for domestic mail delivery. \li Address.International - An address for international mail delivery. \endlist */ /*! \qmlproperty string Address::postOfficeBox This property holds the post office box identifier of the mailing address. */ /* ==================== QDeclarativeContactAnniversary ======================= */ /*! \qmltype Anniversary \instantiates QDeclarativeContactAnniversary \brief The Anniversary element contains an anniversary of a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Anniversary element contains the following field types: \list \li Anniversary.CalendarId \li Anniversary.OriginalDate \li Anniversary.Event \li Anniversary.SubType \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string Anniversary::calendarId This property holds the id of the calendar event. */ /*! \qmlproperty date Anniversary::originalDate This property holds the original anniversary date value. This property is either a date, or a date and time. */ /*! \qmlproperty string Anniversary::event This property holds the name of the event value. */ /*! \qmlproperty enumeration Anniversary::subType This property holds the sub type of an Anniversary. \list \li Unknown - Unknown sub type (default). \li Wedding - A wedding anniversary. \li Engagement - An engagement anniversary. \li House - A new residence anniversary. \li Employment - A start of employment anniversary. \li Memorial - An event of sentimental significance. \endlist */ /* ==================== QDeclarativeContactAvatar ======================= */ /*! \qmltype Avatar \instantiates QDeclarativeContactAvatar \brief The Avatar element contains avatar URLs of a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Avatar element contains the following field types: \list \li Avatar.ImageUrl \li Avatar.VideoUrl \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string Avatar::imageUrl This property holds the URL of the avatar image. */ /*! \qmlproperty string Avatar::videoUrl This property holds the URL of a video avatar. */ /* ==================== QDeclarativeContactBirthday ======================= */ /*! \qmltype Birthday \instantiates QDeclarativeContactBirthday \brief The Birthday element contains a birthday of a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Birthday element contains the following field types: \list \li Birthday.Birthday \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty date Birthday::birthday This property holds the birthday date. The property value is either a date, or a date and time. */ /* ==================== QDeclarativeContactDisplayLabel ======================= */ /*! \qmltype DisplayLabel \instantiates QDeclarativeContactDisplayLabel \brief The DisplayLabel element contains a label that can be used by clients when displaying a contact, for example in a list. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 DisplayLabel element contains the following field types: \list \li DisplayLabel.Label \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string DisplayLabel::label This property holds the value of the display label. */ /* ==================== QDeclarativeContactEmailAddress ======================= */ /*! \qmltype EmailAddress \instantiates QDeclarativeContactEmailAddress \brief The EmailAddress element contains an email address of a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 EmailAddress element contains the following field types: \list \li EmailAddress.EmailAddress \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string EmailAddress::emailAddress This property holds the email address value. */ /* ==================== QDeclarativeContactExtendedDetail ======================= */ /*! \qmltype ExtendedDetail \instantiates QDeclarativeContactExtendedDetail \brief The ExtendedDetail element contains an extended detail of a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Note for the jsondb manager backend: it converts extended detail data of type date, time or javascript Date to a string expressed in local timezone in ISO 8601 format without timezone specified. Javascript function Date.parse() cannot be used to parse this string, since it interprets the string without timezone specified being in UTC. It is recommended to convert dates to strings before storing them to extended details. For example, javascript functions Date.toISOString() and Date.parse() can be used for the conversion. ExtendedDetail element contains the following field types: \list \li ExtendedDetail.Name \li ExtendedDetail.Data \endlist This element is part of the \b{QtContacts} module. \sa QContactExtendedDetail */ /*! \qmlproperty string ExtendedDetail::name This property holds the name of the extended detail. */ /*! \qmlproperty variant ExtendedDetail::data This property holds the data of the extended detail. */ /* ==================== QDeclarativeContactFamily ======================= */ /*! \qmltype Family \instantiates QDeclarativeContactFamily \brief The Family element contains names of family members of a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Family element contains the following field types: \list \li Family.Spouse \li Family.Children \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string Family::spouse This property holds the name of a spouse. */ /*! \qmlproperty list Family::children This property holds the the names of children. */ /* ==================== QDeclarativeContactFavorite ======================= */ /*! \qmltype Favorite \instantiates QDeclarativeContactFavorite \brief The Favorite element indicates if a contact is a favorite contact as well as the position it should appear in an ordered list of favorites. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Favorite element contains the following field types: \list \li Favorite.Favorite \li Favorite.Index \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty bool Favorite::favorite This property holds the value that indicates whether a contact is a favorite. */ /*! \qmlproperty int Favorite::index This property holds the index of the favorite contact (which determines the order they appear). */ /* ==================== QDeclarativeContactGender ======================= */ /*! \qmltype Gender \instantiates QDeclarativeContactGender \brief The Gender element contains the gender of a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Gender element contains the following field types: \list \li Gender.Gender \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty enumeration Gender::gender This property holds the value of the gender. \list \li Gender.Male \li Gender.Female \endlist */ /* ==================== QDeclarativeContactGeoLocation ======================= */ /*! \qmltype GeoLocation \instantiates QDeclarativeContactGeoLocation \brief The GeoLocation element contains a global location coordinate associated with a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 GeoLocation element contains the following field types: \list \li GeoLocation.Label \li GeoLocation.Latitude \li GeoLocation.Longitude \li GeoLocation.Accuracy \li GeoLocation.Altitude \li GeoLocation.AltitudeAccuracy \li GeoLocation.Heading \li GeoLocation.Speed \li GeoLocation.Timestamp \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string GeoLocation::label This property holds the location label. */ /*! \qmlproperty double GeoLocation::latitude This property holds the value of the latitude. */ /*! \qmlproperty double GeoLocation::longitude This property holds the value of the longitude. */ /*! \qmlproperty double GeoLocation::accuracy This property holds the value of the location (latitude/longitude) accuracy. */ /*! \qmlproperty double GeoLocation::altitude This property holds the value of the altitude. */ /*! \qmlproperty double GeoLocation::altitudeAccuracy This property holds the value of the accuracy of the altitude. */ /*! \qmlproperty double GeoLocation::heading This property holds the value of the heading. */ /*! \qmlproperty double GeoLocation::speed This property holds the value of the speed. */ /*! \qmlproperty date GeoLocation::timestamp This property holds the value of the timestamp of the location information. */ /* ==================== QDeclarativeContactGlobalPresence ======================= */ /*! \qmltype GlobalPresence \instantiates QDeclarativeContactGlobalPresence \brief The GlobalPresence element provides aggregated presence information for a contact, synthesized or supplied by the backend. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 GlobalPresence element contains the following field types: \list \li GlobalPresence.Timestamp \li GlobalPresence.Nickname \li GlobalPresence.State \li GlobalPresence.StateText \li GlobalPresence.ImageUrl \li GlobalPresence.CustomMessage \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty date GlobalPresence::timestamp This property holds the timestamp value of the GlobalPresence. */ /*! \qmlproperty string GlobalPresence::nickname This property holds the nickname value of the GlobalPresence. */ /*! \qmlproperty enumeration GlobalPresence::state This property holds the presence state enumeration value. \list \li Presence.Unknown - Signifies that the presence state of the contact is not currently known (default). \li Presence.Available - Signifies that the contact is available. \li Presence.Hidden - Signifies that the contact is hidden. \li Presence.Busy - Signifies that the contact is busy. \li Presence.Away - Signifies that the contact is away. \li Presence.ExtendedAway - Signifies that the contact is away for an extended period of time. \li Presence.Offline - Signifies that the contact is offline. \endlist */ /*! \qmlproperty string GlobalPresence::stateText This property holds the text corresponding to the current presence state. */ /*! \qmlproperty url GlobalPresence::imageUrl This property holds the last-known status image url of the contact for the online account about which this detail stores presence information. */ /*! \qmlproperty string GlobalPresence::customMessage This property holds the custom status message from the contact for the online account about which this detail stores presence information. */ /* ==================== QDeclarativeContactGuid ======================= */ /*! \qmltype Guid \instantiates QDeclarativeContactGuid \brief The Guid element contains a globally unique Id of a contact, for use in synchronization with other datastores. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Guid element contains the following field types: \list \li Guid.Guid \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string Guid::guid This property holds the value of the GUID. */ /* ==================== QDeclarativeContactHobby ======================= */ /*! \qmltype Hobby \instantiates QDeclarativeContactHobby \brief The Hobby element contains a hobby of the contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Hobby element contains the following field types: \list \li Hobby.Hobby \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string Hobby::hobby This property holds the name of the hobby. */ /* ==================== QDeclarativeContactName ======================= */ /*! \qmltype Name \instantiates QDeclarativeContactName \brief The Name element contains a name of a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Name element contains the following field types: \list \li Name.Prefix \li Name.FirstName \li Name.MiddleName \li Name.LastName \li Name.Suffix \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string Name::prefix This property holds the prefix name part of the name. */ /*! \qmlproperty string Name::firstName This property holds the first name part of the name. */ /*! \qmlproperty string Name::middleName This property holds the middle name part of the name. */ /*! \qmlproperty string Name::lastName This property holds the last name part of the name. */ /*! \qmlproperty string Name::suffix This property holds the suffix part of the name. */ /* ==================== QDeclarativeContactNickname ======================= */ /*! \qmltype Nickname \instantiates QDeclarativeContactNickname \brief The Nickname element contains a nickname of a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Nickname element contains the following field types: \list \li Nickname.Nickname \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string Nickname::nickname This property holds the value of the nickname. */ /* ==================== QDeclarativeContactNote ======================= */ /*! \qmltype Note \instantiates QDeclarativeContactNote \brief The Note element contains a note associated with a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Note element contains the following field types: \list \li Note.Note \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string Note::note This property holds the value of the note. */ /* ==================== QDeclarativeContactOnlineAccount ======================= */ /*! \qmltype OnlineAccount \instantiates QDeclarativeContactOnlineAccount \brief The OnlineAccount element contains a note associated with a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 OnlineAccount element contains the following field types: \list \li OnlineAccount.AccountUri - the account uri value. \li OnlineAccount.ServiceProvider - the account service provider name. \li OnlineAccount.Protocol - the account protocol value. \li OnlineAccount.Capabilities - the account capabilities value. \li OnlineAccount.SubTypes - the sub types of an online account. \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string OnlineAccount::accountUri This property holds the value of the account uri. */ /*! \qmlproperty string OnlineAccount::serviceProvider This property holds the value of the account service provider name. */ /*! \qmlproperty list OnlineAccount::capabilities This property holds the value of the account capabilities. */ /*! \qmlproperty list OnlineAccount::subTypes This property holds the value of the sub types of an online account. \list \li OnlineAccount.Unknown (default) \li OnlineAccount.Sip - indicating this online account supports SIP. \li OnlineAccount.SipVoip - indicating this online account supports SIP based VOIP. \li OnlineAccount.Impp - indicating this online account supports IMPP. \li OnlineAccount.VideoShare - indicating this online account supports VideoShare. \endlist */ /*! \qmlproperty enumeration OnlineAccount::protocol This property holds the protocol enumeration value. \list \li OnlineAccount.Unknown - indicates this online account is for one unsupported protocol. \li OnlineAccount.Aim - indicates this online account is for the AIM protocol. \li OnlineAccount.Icq - indicates this online account is for the ICQ protocol. \li OnlineAccount.Irc - indicates this online account is for the IRC protocol. \li OnlineAccount.Jabber - indicates this online account is for the jabber protocol. \li OnlineAccount.Msn - indicates this online account is for the MSN protocol. \li OnlineAccount.Qq - indicates this online account is for the QQ protocol. \li OnlineAccount.Skype - indicates this online account is for the Skype protocol. \li OnlineAccount.Yahoo - indicates this online account is for the Yahoo protocol. \endlist */ /* ==================== QDeclarativeContactOrganization ======================= */ /*! \qmltype Organization \instantiates QDeclarativeContactOrganization \brief The Organization element provides details about an organization that the contact is either a part of, or stands for. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Organization element contains the following field types: \list \li Organization.Name \li Organization.LogoUrl \li Organization.Department \li Organization.Location \li Organization.Role \li Organization.Title \li Organization.AssistantName \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string Organization::name This property holds the value of the organization name. */ /*! \qmlproperty url Organization::logoUrl This property holds the URL of the organization logo image. */ /*! \qmlproperty list Organization::department This property holds the value of the department name. */ /*! \qmlproperty string Organization::location This property holds the value of the location of the organization. */ /*! \qmlproperty string Organization::role This property holds the value of the contact's role in the organization. */ /*! \qmlproperty string Organization::title This property holds the value of the contact's title in the organization. */ /*! \qmlproperty string Organization::assistantName This property holds the value of the name of the contact's assistant. */ /* ==================== QDeclarativeContactPhoneNumber ======================= */ /*! \qmltype PhoneNumber \instantiates QDeclarativeContactPhoneNumber \brief The PhoneNumber element provides a phone number of a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 PhoneNumber element contains the following field types: \list \li PhoneNumber.Number \li PhoneNumber.SubTypes \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string PhoneNumber::number This property holds the value of the phone number. */ /*! \qmlproperty list PhoneNumber::subTypes This property holds the sub types of a PhoneNumber. \list \li PhoneNumber.Unknown - indicating this phone number type is unknown(default). \li PhoneNumber.Landline - indicating this phone number is a landline number. \li PhoneNumber.Mobile - ndicating this phone number is a mobile (cellular) number. \li PhoneNumber.Fax - indicating this phone number is a fax number. \li PhoneNumber.Pager - indicating this phone number is a pager number. \li PhoneNumber.Voice - indicating this phone number supports voice transmission. \li PhoneNumber.Modem - indicating this phone number supports data transmission. \li PhoneNumber.Video - indicating this phone number supports video transmission. \li PhoneNumber.Car - indicating this phone number is a car phone. \li PhoneNumber.BulletinBoardSystem - indicating this phone number is a bulletin board system. \li PhoneNumber.MessagingCapable - indicating this phone number supports messaging services. \li PhoneNumber.Assistant - indicating this phone number is the number of an assistant. \li PhoneNumber.DtmfMenu - indicating this phone number supports DTMF-controlled voice menu navigation. \endlist */ /* ==================== QDeclarativeContactPresence ======================= */ /*! \qmltype Presence \instantiates QDeclarativeContactPresence \brief The Presence element provides presence information for an online account of a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Presence element contains the following field types: \list \li Presence.Timestamp \li Presence.Nickname \li Presence.State \li Presence.StateText \li Presence.ImageUrl \li Presence.CustomMessage \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty date Presence::timestamp This property holds the timestamp value of the Presence. */ /*! \qmlproperty string Presence::nickname This property holds the nickname value of the Presence. */ /*! \qmlproperty enumeration Presence::state This property holds the presence state enumeration value. \list \li Presence.Unknown - Signifies that the presence state of the contact is not currently known (default). \li Presence.Available - Signifies that the contact is available. \li Presence.Hidden - Signifies that the contact is hidden. \li Presence.Busy - Signifies that the contact is busy. \li Presence.Away - Signifies that the contact is away. \li Presence.ExtendedAway - Signifies that the contact is away for an extended period of time. \li Presence.Offline - Signifies that the contact is offline. \endlist */ /*! \qmlproperty string Presence::stateText This property holds the text corresponding to the current presence state. */ /*! \qmlproperty url Presence::imageUrl This property holds the last-known status image url of the contact for the online account about which this detail stores presence information. */ /*! \qmlproperty string Presence::customMessage This property holds the custom status message from the contact for the online account about which this detail stores presence information. */ /* ==================== QDeclarativeContactRingtone ======================= */ /*! \qmltype Ringtone \instantiates QDeclarativeContactRingtone \brief The Ringtone element provides a ringtone associated with a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Ringtone element contains the following field types: \list \li Ringtone.AudioRingtoneUrl \li Ringtone.VideoRingtoneUrl \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty url Ringtone::audioRingtoneUrl This property holds the value of the URL for an audio ringtone. */ /*! \qmlproperty url Ringtone::videoRingtoneUrl This property holds the value of the URL for a video ringtone. */ // Not in use (note the missing ! below) /* \qmlproperty url Ringtone::vibrationRingtoneUrl This property holds the value of the URL for a vibration ringtone. */ /* ==================== QDeclarativeContactSyncTarget ======================= */ /*! \qmltype SyncTarget \instantiates QDeclarativeContactSyncTarget \brief The SyncTarget element provides a sync target for a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 SyncTarget element contains the following field types: \list \li SyncTarget.SyncTarget \endlist \sa QContactSyncTarget This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string SyncTarget::syncTarget This property holds the sync target value. */ /* ==================== QDeclarativeContactTag ======================= */ /*! \qmltype Tag \instantiates QDeclarativeContactTag \brief The Tag element provides a contains a tag associated with a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Tag element contains the following field types: \list \li Tag.Tag \endlist \sa QContactTag This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string Tag::tag This property holds the value of the tag. */ /* ==================== QDeclarativeContactTimestamp ======================= */ /*! \qmltype Timestamp \instantiates QDeclarativeContactTimestamp \brief The Timestamp element contains the creation and last-modified timestamp associated with the contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Timestamp element contains the following field types: \list \li Timestamp.LastModified \li Timestamp.Created \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty date Timestamp::lastModified This property holds the value of the last modified timestamp. */ /*! \qmlproperty date Timestamp::created This property holds the value of the timestamp a contact was created. */ /* ==================== QDeclarativeContactUrl ======================= */ /*! \qmltype Url \instantiates QDeclarativeContactUrl \brief The Url element contains a url associated with a contact. \ingroup qml-contacts-details \inqmlmodule QtContacts 5.0 Url element contains the following field types: \list \li Url.Url \li Url.SubType \endlist This element is part of the \b{QtContacts} module. */ /*! \qmlproperty string Url::url This property holds the value of the URL. */ /*! \qmlproperty enumeration Url::subType This property holds the sub type of a QContactUrl. \list \li Url.Unknown - indicating this url type is unknown (default). \li Url.HomePage - indicating this url is a contact's home page. \li Url.Favourite - indicating this url is one of the contact's favourite URLs (or bookmarks). \endlist */ #include "moc_qdeclarativecontactdetail_p.cpp" QT_END_NAMESPACE src/imports/contacts/qdeclarativecontactdetail_p.h000066400000000000000000000131431233466112000230530ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTDETAIL_P_H #define QDECLARATIVECONTACTDETAIL_P_H #include #include #include #include QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactDetail : public QObject { Q_OBJECT Q_PROPERTY(DetailType type READ detailType NOTIFY detailChanged) Q_PROPERTY(QList contexts READ contexts WRITE setContexts NOTIFY detailChanged) Q_PROPERTY(QString detailUri READ detailUri WRITE setDetailUri NOTIFY detailChanged) Q_PROPERTY(QStringList linkedDetailUris READ linkedDetailUris WRITE setLinkedDetailUris NOTIFY detailChanged) Q_PROPERTY(QList fields READ fields NOTIFY detailChanged) Q_PROPERTY(bool readOnly READ readOnly NOTIFY detailChanged) Q_PROPERTY(bool removable READ removable NOTIFY detailChanged) Q_ENUMS(DetailType) Q_ENUMS(ContextField) public: QDeclarativeContactDetail(QObject* parent = 0); enum DetailType { Address = QContactDetail::TypeAddress, Anniversary = QContactDetail::TypeAnniversary, Avatar = QContactDetail::TypeAvatar, Birthday = QContactDetail::TypeBirthday, DisplayLabel = QContactDetail::TypeDisplayLabel, Email = QContactDetail::TypeEmailAddress, ExtendedDetail = QContactDetail::TypeExtendedDetail, Family = QContactDetail::TypeFamily, Favorite = QContactDetail::TypeFavorite, Gender = QContactDetail::TypeGender, Geolocation = QContactDetail::TypeGeoLocation, GlobalPresence = QContactDetail::TypeGlobalPresence, Guid = QContactDetail::TypeGuid, Hobby = QContactDetail::TypeHobby, Name = QContactDetail::TypeName, NickName = QContactDetail::TypeNickname, Note = QContactDetail::TypeNote, OnlineAccount = QContactDetail::TypeOnlineAccount, Organization = QContactDetail::TypeOrganization, PhoneNumber = QContactDetail::TypePhoneNumber, Presence = QContactDetail::TypePresence, Ringtone = QContactDetail::TypeRingtone, SyncTarget = QContactDetail::TypeSyncTarget, Tag = QContactDetail::TypeTag, Timestamp = QContactDetail::TypeTimestamp, Type = QContactDetail::TypeType, Url = QContactDetail::TypeUrl, Version = QContactDetail::TypeVersion, Unknown = QContactDetail::TypeUndefined }; enum ContextField { FieldContext = QContactDetail::FieldContext, ContextHome = QContactDetail::ContextHome, ContextWork = QContactDetail::ContextWork, ContextOther = QContactDetail::ContextOther }; ~QDeclarativeContactDetail(); // QML functions Q_INVOKABLE QVariant value(int field) const; Q_INVOKABLE bool setValue(int field, const QVariant& value); Q_INVOKABLE bool removeValue(int field); QContactDetail& detail(); const QContactDetail& detail() const; void setDetail(const QContactDetail& detail); bool readOnly() const; bool removable() const; QList contexts() const; void setContexts(const QList& context); QString detailUri() const; void setDetailUri(const QString& detailUri); QStringList linkedDetailUris() const; void setLinkedDetailUris(const QStringList& linkedDetailUris); virtual DetailType detailType() const; QList fields() const; Q_SIGNALS: void detailChanged(); protected: QContactDetail m_detail; }; class QDeclarativeContactDetailFactory { public: static QDeclarativeContactDetail *createContactDetail(const QDeclarativeContactDetail::DetailType type); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactDetail) #endif // QDECLARATIVECONTACTDETAIL_P_H src/imports/contacts/qdeclarativecontactfetchhint.cpp000066400000000000000000000152261233466112000236050ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativecontactfetchhint_p.h" #include QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE /*! \qmltype FetchHint \instantiates QDeclarativeContactFetchHint \brief The FetchHint element provides hints to the manager about which contact information needs to be retrieved in an asynchronous fetch request or a synchronous function call. \ingroup qml-contacts-main \inqmlmodule QtContacts 5.0 This element is part of the \b{QtContacts} module. \sa QContactFetchHint */ QDeclarativeContactFetchHint::QDeclarativeContactFetchHint(QObject* parent) :QObject(parent) { } /*! \qmlproperty list FetchHint::detailTypesHint This property holds a list of contact detail types the manager should (at a minimum) retrieve when fetching contacts. */ QList QDeclarativeContactFetchHint::detailTypesHint() const { QList savedList; foreach (const QContactDetail::DetailType &detailTypeHint, m_fetchHint.detailTypesHint()) { savedList << static_cast(detailTypeHint); } return savedList; } void QDeclarativeContactFetchHint::setDetailTypesHint(const QList &detailTypes) { if (detailTypes.toSet() != detailTypesHint().toSet()) { QList convertedDetailTypes; foreach (const int detailType, detailTypes) { convertedDetailTypes << static_cast(detailType); } m_fetchHint.setDetailTypesHint(convertedDetailTypes); emit fetchHintChanged(); } } /*! \qmlproperty list FetchHint::relationshipTypesHint This property holds a list of relationship types that the manager should (at a minimum) retrieve when fetching contacts. */ QStringList QDeclarativeContactFetchHint::relationshipTypesHint() const { return m_fetchHint.relationshipTypesHint(); } void QDeclarativeContactFetchHint::setRelationshipTypesHint(const QStringList& relationshipTypes) { if (relationshipTypes.toSet() != m_fetchHint.relationshipTypesHint().toSet()) { m_fetchHint.setRelationshipTypesHint(relationshipTypes); emit fetchHintChanged(); } } /*! \qmlproperty int FetchHint::imageWidth This property holds the preferred pixel width for any images returned by the manager for a given request. This hint may be ignored by the manager. */ int QDeclarativeContactFetchHint::preferredImageWidth() const { return m_fetchHint.preferredImageSize().width(); } void QDeclarativeContactFetchHint::setPreferredImageWidth(int w) { if (m_fetchHint.preferredImageSize().width() != w) { QSize s = m_fetchHint.preferredImageSize(); s.setWidth(w); m_fetchHint.setPreferredImageSize(s); emit fetchHintChanged(); } } /*! \qmlproperty int FetchHint::imageHeight This property holds the preferred pixel height for any images returned by the manager for a given request. This hint may be ignored by the manager. */ int QDeclarativeContactFetchHint::preferredImageHeight() const { return m_fetchHint.preferredImageSize().height(); } void QDeclarativeContactFetchHint::setPreferredImageHeight(int h) { if (m_fetchHint.preferredImageSize().height() != h) { QSize s = m_fetchHint.preferredImageSize(); s.setHeight(h); m_fetchHint.setPreferredImageSize(s); emit fetchHintChanged(); } } /*! \qmlproperty FetchHint::OptimizationHints FetchHint::optimizationHints This property holds the optimization hint flags specified by the client. These hints may be ignored by the backend, in which case it will return the full set of information accessible in a contact, including relationships, action preferences, and binary blobs. The value of the flags can be: \list \li FetchHint.AllRequired - (default). \li FetchHint.NoRelationships \li FetchHint.NoActionPreferences \li FetchHint.NoBinaryBlobs \endlist */ QDeclarativeContactFetchHint::OptimizationHints QDeclarativeContactFetchHint::optimizationHints() const { QDeclarativeContactFetchHint::OptimizationHints hints; hints = ~hints & (int)m_fetchHint.optimizationHints(); return hints; } void QDeclarativeContactFetchHint::setOptimizationHints(QDeclarativeContactFetchHint::OptimizationHints hints) { QContactFetchHint::OptimizationHints newHints; newHints = ~newHints & (int)hints; if (newHints != m_fetchHint.optimizationHints()) { m_fetchHint.setOptimizationHints(newHints); emit fetchHintChanged(); } } QContactFetchHint QDeclarativeContactFetchHint::fetchHint() const { return m_fetchHint; } void QDeclarativeContactFetchHint::setFetchHint(const QContactFetchHint& fetchHint) { m_fetchHint = fetchHint; emit fetchHintChanged(); } #include "moc_qdeclarativecontactfetchhint_p.cpp" QT_END_NAMESPACE src/imports/contacts/qdeclarativecontactfetchhint_p.h000066400000000000000000000100321233466112000235570ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTFETCHHINT_H #define QDECLARATIVECONTACTFETCHHINT_H #include #include #include #include #include QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactFetchHint : public QObject { Q_OBJECT Q_PROPERTY(QList detailTypesHint READ detailTypesHint WRITE setDetailTypesHint NOTIFY fetchHintChanged ) Q_PROPERTY(QStringList relationshipTypesHint READ relationshipTypesHint WRITE setRelationshipTypesHint NOTIFY fetchHintChanged ) Q_PROPERTY(OptimizationHints optimizationHints READ optimizationHints WRITE setOptimizationHints NOTIFY fetchHintChanged ) Q_PROPERTY(int imageWidth READ preferredImageWidth WRITE setPreferredImageWidth NOTIFY fetchHintChanged ) Q_PROPERTY(int imageHeight READ preferredImageHeight WRITE setPreferredImageHeight NOTIFY fetchHintChanged ) Q_FLAGS(OptimizationHints) public: enum OptimizationHint { AllRequired = QContactFetchHint::AllRequired, NoRelationships = QContactFetchHint::NoRelationships, NoActionPreferences = QContactFetchHint::NoActionPreferences, NoBinaryBlobs = QContactFetchHint::NoBinaryBlobs }; Q_DECLARE_FLAGS(OptimizationHints, OptimizationHint) QDeclarativeContactFetchHint(QObject* parent = 0); QList detailTypesHint() const; void setDetailTypesHint(const QList &detailTypes); QStringList relationshipTypesHint() const; void setRelationshipTypesHint(const QStringList& relationshipTypes); int preferredImageWidth() const; void setPreferredImageWidth(int w); int preferredImageHeight() const; void setPreferredImageHeight(int h); OptimizationHints optimizationHints() const; void setOptimizationHints(OptimizationHints hints); QContactFetchHint fetchHint() const; void setFetchHint(const QContactFetchHint& fetchHint); signals: void fetchHintChanged(); private: QContactFetchHint m_fetchHint; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactFetchHint) #endif // QDECLARATIVECONTACTFETCHHINT_H src/imports/contacts/qdeclarativecontactfilter.cpp000066400000000000000000000332071233466112000231150ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativecontactfilter_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE /*! \qmltype Filter \instantiates QDeclarativeContactFilter \brief The Filter element is used as a property of ContactModel, to allow selection of contacts which have certain details or properties. \ingroup qml-contacts-main \inqmlmodule QtContacts 5.0 This element is part of the \b{QtContacts} module. \sa QContactFilter */ /*! \qmlproperty enumeration Filter::type This property holds the type value of this filter. It can be one of: \list \li Filter.DefaultFilter - A filter which matches everything (default). \li Filter.InvalidFilter - An invalid filter which matches nothing. \li Filter.DetailFilter - A filter which matches contacts containing one or more details of a particular definition with a particular value. \li Filter.DetailRangeFilter - A filter which matches contacts containing one or more details of a particular definition whose values are within a particular range. \li Filter.ChangeLogFilter - A filter which matches contacts whose timestamps have been updated since some particular date and time. \li Filter.ActionFilter - A filter which matches contacts for which a particular action is available, or which contain a detail with a particular value for which a particular action is available. \li Filter.RelationshipFilter - A filter which matches contacts which participate in a particular type of relationship, or relationship with a specified contact. \li Filter.IntersectionFilter - A filter which matches all contacts that are matched by all filters it includes. \li Filter.UnionFilter - A filter which matches any contact that is matched by any of the filters it includes. \li Filter.IdFilter - A filter which matches any contact whose local id is contained in a particular list of contact local ids. \endlist */ /*! \qmltype ActionFilter \instantiates QDeclarativeContactActionFilter \brief The ActionFilter element provides a filter based around an action availability criterion. \ingroup qml-contacts-filters \inqmlmodule QtContacts 5.0 This element is part of the \b{QtContacts} module. \sa QContactActionFilter */ /*! \qmlproperty string ActionFilter::actionName This property holds the action name criterion of the filter. */ /*! \qmltype ChangeLogFilter \instantiates QDeclarativeContactChangeLogFilter \brief The ChangeLogFilter element provides a filter based around a contact timestamp criterion. \ingroup qml-contacts-filters \inqmlmodule QtContacts 5.0 This element is part of the \b{QtContacts} module. \sa QContactChangeLogFilter */ /*! \qmlproperty date ChangeLogFilter::since This property holds the date and time lower-bound criterion of the filter.The value can be one of: \list \li ChangeLogFilter.EventAdded \li ChangeLogFilter.EventChanged \li ChangeLogFilter.EventRemoved \endlist */ /*! \qmlproperty enumeration ChangeLogFilter::eventType This property holds the type of change that this filter will match against. */ /*! \qmltype DetailFilter \instantiates QDeclarativeContactDetailFilter \brief The DetailFilter element provides a filter based around a detail value criterion. \ingroup qml-contacts-filters \inqmlmodule QtContacts 5.0 This element is part of the \b{QtContacts} module. \sa QContactDetailFilter */ /*! \qmlproperty enumeration DetailFilter::detail This property holds the detail type of which details will be matched to. \sa ContactDetail::type */ /*! \qmlproperty int DetailFilter::field This property holds the detail field type of which detail fields will be matched to. Detail field types are enumeration values defined in each detail elements. \sa Address \sa Anniversary \sa Avatar \sa Birthday \sa DisplayLabel \sa EmailAddress \sa Family \sa Favorite \sa Gender \sa GeoLocation \sa GlobalPresence \sa Guid \sa Name \sa Nickname \sa Note \sa OnlineAccount \sa Organization \sa PhoneNumber \sa Presence \sa Ringtone \sa SyncTarget \sa Tag \sa Timestamp \sa Url \sa Hobby */ /*! \qmlproperty variant DetailFilter::value This property holds the value criterion of the detail filter. */ /*! \qmlproperty enumeration DetailFilter::matchFlags This property holds the semantics of the value matching criterion. The valid match flags include: \list \li MatchExactly - Performs QVariant-based matching (default). \li MatchContains - The search term is contained in the item. \li MatchStartsWith - The search term matches the start of the item. \li MatchEndsWith - The search term matches the end of the item. \li MatchFixedString - Performs string-based matching. String-based comparisons are case-insensitive unless the \c MatchCaseSensitive flag is also specified. \li MatchCaseSensitive - The search is case sensitive. \endlist */ /*! \qmltype DetailRangeFilter \instantiates QDeclarativeContactDetailRangeFilter \brief The DetailRangeFilter element provides a filter based around a detail value range criterion. \ingroup qml-contacts-filters \inqmlmodule QtContacts 5.0 This element is part of the \b{QtContacts} module. \sa QContactDetailRangeFilter */ /*! \qmlproperty enumeration DetailRangeFilter::detail This property holds the detail type of which details will be matched to. \sa ContactDetail::type \sa DetailFilter::detail */ /*! \qmlproperty int DetailRangeFilter::field This property holds the detail field type of which detail fields will be matched to. Detail field types are enumeration values defined in each detail elements. \sa DetailFilter::field */ /*! \qmlproperty variant DetailRangeFilter::min This property holds the lower bound of the value range criterion. By default, there is no lower bound. */ /*! \qmlproperty variant DetailRangeFilter::max This property holds the upper bound of the value range criterion. By default, there is no upper bound. */ /*! \qmlproperty enumeration DetailRangeFilter::matchFlags This property holds the match flags of the criterion, which define semantics such as case sensitivity, and exact matching. \sa DetailFilter::matchFlags */ /*! \qmlproperty enumeration DetailRangeFilter::rangeFlags This property holds a set of flags which defines the boundary condition semantics of the value range criterion.The valid range flags include: \list \li DetailRangeFilter.IncludeLower \li DetailRangeFilter.IncludeUpper \li DetailRangeFilter.ExcludeLower \li DetailRangeFilter.ExcludeUpper \endlist */ /*! \qmltype IntersectionFilter \instantiates QDeclarativeContactIntersectionFilter \brief The IntersectionFilter element provides a filter which intersects the results of other filters. \ingroup qml-contacts-filters \inqmlmodule QtContacts 5.0 This element is part of the \b{QtContacts} module. \sa QContactIntersectionFilter */ /*! \qmlproperty list IntersectionFilter::filters This property holds the list of filters which form the intersection filter. */ /*! \qmltype UnionFilter \instantiates QDeclarativeContactUnionFilter \brief The UnionFilter element provides a filter which unions the results of other filters. \ingroup qml-contacts-filters \inqmlmodule QtContacts 5.0 This element is part of the \b{QtContacts} module. \sa QContactUnionFilter */ /*! \qmlproperty list UnionFilter::filters This property holds the list of filters which form the union filter. */ /*! \qmltype IdFilter \instantiates QDeclarativeContactIdFilter \brief The IdFilter element provides a filter based around a list of contact ids. \ingroup qml-contacts-filters \inqmlmodule QtContacts 5.0 This element is part of the \b{QtContacts} module. \sa QContactIdFilter */ /*! \qmlproperty list IdFilter::ids This property holds the list of ids of contacts which match this filter. */ /*! \qmltype RelationshipFilter \instantiates QDeclarativeContactRelationshipFilter \brief The RelationshipFilter element provides a filter based around relationship criteria. \ingroup qml-contacts-filters \inqmlmodule QtContacts 5.0 This element is part of the \b{QtContacts} module. \sa Relationship \sa RelationshipModel \sa QContactRelationshipFilter \sa QContactRelationship */ /*! \qmlproperty variant RelationshipFilter::relationshipType This property holds the type of relationship that a contact must have in order to match the filter. \sa Relationship::type */ /*! \qmlproperty int RelationshipFilter::relatedContactId This property holds the id of the contact with whom the tested contact must have a relationship in order for the tested contact to match this filter */ /*! \qmlproperty enumeration RelationshipFilter::relatedContactRole This property holds the role in the relationship with the tested contact that the related contact must play in order for the tested contact to match this filter. The role can be one of: \list \li Relationship.First - The contact is the first contact in the relationship. \li Relationship.Second - The contact is the second contact in the relationship. \li Relationship.Either - The contact is either the first or second contact in the relationship (default). \endlist */ /*! \qmltype InvalidFilter \instantiates QDeclarativeContactInvalidFilter \brief the InvalidFilter element provides a filter which will never match any contacts. \ingroup qml-contacts-filters \inqmlmodule QtContacts 5.0 This element is part of the \b{QtContacts} module. \sa QContactInvalidFilter */ QQmlListProperty QDeclarativeContactCompoundFilter::filters() { return QQmlListProperty(this, 0, // opaque data parameter filters_append, filters_count, filters_at, filters_clear); } void QDeclarativeContactCompoundFilter::filters_append(QQmlListProperty* prop, QDeclarativeContactFilter* filter) { QDeclarativeContactCompoundFilter* compoundFilter = static_cast(prop->object); compoundFilter->m_filters.append(filter); QObject::connect(filter, SIGNAL(filterChanged()), compoundFilter, SIGNAL(filterChanged()), Qt::UniqueConnection); emit compoundFilter->filterChanged(); } int QDeclarativeContactCompoundFilter::filters_count(QQmlListProperty* prop) { // The 'prop' is in a sense 'this' for this static function (as given in filters() function) return static_cast(prop->object)->m_filters.count(); } QDeclarativeContactFilter* QDeclarativeContactCompoundFilter::filters_at(QQmlListProperty* prop, int index) { return static_cast(prop->object)->m_filters.at(index); } void QDeclarativeContactCompoundFilter::filters_clear(QQmlListProperty* prop) { QDeclarativeContactCompoundFilter* compoundFilter = static_cast(prop->object); if (!compoundFilter->m_filters.isEmpty()) { foreach (QDeclarativeContactFilter* filter, compoundFilter->m_filters) { QObject::disconnect(filter, SIGNAL(filterChanged()), compoundFilter, SIGNAL(filterChanged())); } compoundFilter->m_filters.clear(); emit compoundFilter->filterChanged(); } } #include "moc_qdeclarativecontactfilter_p.cpp" QT_END_NAMESPACE src/imports/contacts/qdeclarativecontactfilter_p.h000066400000000000000000000121271233466112000230770ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTFILTER_P_H #define QDECLARATIVECONTACTFILTER_P_H #include #include QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactFilter : public QObject { Q_OBJECT Q_PROPERTY(FilterType type READ type NOTIFY filterChanged) Q_ENUMS(FilterType) Q_FLAGS(MatchFlags) public: QDeclarativeContactFilter(QObject *parent=0) :QObject(parent) { //for grouped filter: intersect /union filters if (parent && qobject_cast(parent)) { connect(this, SIGNAL(filterChanged()), parent, SIGNAL(filterChanged())); } } enum FilterType { InvalidFilter = QContactFilter::InvalidFilter, DetailFilter = QContactFilter::ContactDetailFilter, DetailRangeFilter = QContactFilter::ContactDetailRangeFilter, ChangeLogFilter = QContactFilter::ChangeLogFilter, ActionFilter = QContactFilter::ActionFilter, RelationshipFilter = QContactFilter::RelationshipFilter, IntersectionFilter = QContactFilter::IntersectionFilter, UnionFilter = QContactFilter::UnionFilter, IdFilter = QContactFilter::IdFilter, DefaultFilter = QContactFilter::DefaultFilter }; FilterType type() const { return static_cast(filter().type()); } enum MatchFlag { MatchExactly = QContactFilter::MatchExactly, MatchContains = QContactFilter::MatchContains, MatchStartsWith = QContactFilter::MatchStartsWith, MatchEndsWith = QContactFilter::MatchEndsWith, MatchFixedString = QContactFilter::MatchFixedString, MatchCaseSensitive = QContactFilter::MatchCaseSensitive, MatchPhoneNumber = QContactFilter::MatchPhoneNumber, MatchKeypadCollation = QContactFilter::MatchKeypadCollation }; Q_DECLARE_FLAGS(MatchFlags, MatchFlag) virtual QContactFilter filter() const { return QContactFilter(); } signals: void filterChanged(); }; class QDeclarativeContactCompoundFilter : public QDeclarativeContactFilter { Q_OBJECT Q_PROPERTY(QQmlListProperty filters READ filters) Q_CLASSINFO("DefaultProperty", "filters") public: explicit QDeclarativeContactCompoundFilter(QObject* parent = 0) : QDeclarativeContactFilter(parent){} virtual ~QDeclarativeContactCompoundFilter() {} // 'READ' accessor for the filters, basically this is also a 'WRITE' accessor // as per QQmlListProperty's design. QQmlListProperty filters(); static void filters_append(QQmlListProperty* prop, QDeclarativeContactFilter* filter); static int filters_count(QQmlListProperty* prop); static QDeclarativeContactFilter* filters_at(QQmlListProperty* prop, int index); static void filters_clear(QQmlListProperty* prop); protected: QList m_filters; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactFilter) QML_DECLARE_TYPE(QDeclarativeContactCompoundFilter) #endif // QDECLARATIVECONTACTFILTER_P_H src/imports/contacts/qdeclarativecontactmodel.cpp000066400000000000000000001374431233466112000227370ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativecontactmodel_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QTCONTACTS_USE_NAMESPACE QTVERSIT_USE_NAMESPACE QT_BEGIN_NAMESPACE /*! \qmltype ContactModel \instantiates QDeclarativeContactModel \brief The ContactModel element provides access to contacts from the contacts store. \ingroup qml-contacts-main \inqmlmodule QtContacts 5.0 This element is part of the \b{QtContacts} module. ContactModel provides a model of contacts from the contacts store. The contents of the model can be specified with \l filter, \l sortOrders and \l fetchHint properties. Whether the model is automatically updated when the store or \l contacts changes, can be controlled with \l ContactModel::autoUpdate property. There are two ways of accessing the contact data: via model by using views and delegates, or alternatively via \l contacts list property. Of the two, the model access is preferred. Direct list access (i.e. non-model) is not guaranteed to be in order set by \l sortOrder. At the moment the model roles provided by ContactModel are display, decoration and \c contact. Through the \c contact role can access any data provided by the Contact element. \sa RelationshipModel, Contact, {QContactManager} */ class QDeclarativeContactModelPrivate { public: QDeclarativeContactModelPrivate() :m_manager(0), m_storageLocations(QContactAbstractRequest::UserDataStorage), m_fetchHint(0), m_filter(0), m_error(QContactManager::NoError), m_autoUpdate(true), m_componentCompleted(false), m_progressiveLoading(true) { } ~QDeclarativeContactModelPrivate() { if (m_manager) delete m_manager; } QList m_contacts; QMap m_contactMap; QMap m_contactFetchedMap; QContactManager* m_manager; QContactAbstractRequest::StorageLocations m_storageLocations; QDeclarativeContactFetchHint* m_fetchHint; QList m_sortOrders; QDeclarativeContactFilter* m_filter; QVersitReader m_reader; QVersitWriter m_writer; QStringList m_importProfiles; QContactManager::Error m_error; bool m_autoUpdate; bool m_componentCompleted; QUrl m_lastExportUrl; QUrl m_lastImportUrl; QAtomicInt m_lastRequestId; QHash m_requestIdHash; QList m_pendingRequests; QList m_pendingContacts; bool m_progressiveLoading; }; QDeclarativeContactModel::QDeclarativeContactModel(QObject *parent) : QAbstractListModel(parent), d(new QDeclarativeContactModelPrivate) { QHash roleNames; roleNames = QAbstractItemModel::roleNames(); roleNames.insert(ContactRole, "contact"); setRoleNames(roleNames); connect(this, SIGNAL(managerChanged()), SLOT(doUpdate())); connect(this, SIGNAL(storageLocationsChanged()), SLOT(doUpdate())); connect(this, SIGNAL(filterChanged()), SLOT(doUpdate())); connect(this, SIGNAL(fetchHintChanged()), SLOT(doUpdate())); connect(this, SIGNAL(sortOrdersChanged()), SLOT(doUpdate())); //import vcard connect(&d->m_reader, SIGNAL(stateChanged(QVersitReader::State)), this, SLOT(startImport(QVersitReader::State))); connect(&d->m_writer, SIGNAL(stateChanged(QVersitWriter::State)), this, SLOT(contactsExported(QVersitWriter::State))); } QDeclarativeContactModel::~QDeclarativeContactModel() { } /*! \qmlproperty string ContactModel::manager This property holds the manager uri of the contact backend engine. */ QString QDeclarativeContactModel::manager() const { if (d->m_manager) return d->m_manager->managerName(); return QString(); } void QDeclarativeContactModel::setManager(const QString& managerName) { if (d->m_manager && (managerName == d->m_manager->managerName() || managerName == d->m_manager->managerUri())) return; if (d->m_manager) delete d->m_manager; d->m_manager = new QContactManager(managerName); connect(d->m_manager, SIGNAL(dataChanged()), this, SLOT(update())); connect(d->m_manager, SIGNAL(contactsAdded(QList)), this, SLOT(onContactsAdded(QList))); connect(d->m_manager, SIGNAL(contactsRemoved(QList)), this, SLOT(onContactsRemoved(QList))); connect(d->m_manager, SIGNAL(contactsChanged(QList)), this, SLOT(onContactsChanged(QList))); if (d->m_error != QContactManager::NoError) { d->m_error = QContactManager::NoError; emit errorChanged(); } emit managerChanged(); } /*! \qmlproperty enumeration ContactModel::StorageLocation Defines the different storage locations for saving contacts and model population purposes. \list \li ContactModel::UserDataStorage A storage location where user data is stored. \li ContactModel::SystemStorage A storage location where system files are stored. \endlist Depending on the backend implementation, the access rights for different storage locations might vary. \sa ContactModel::storageLocations \sa ContactModel::saveContact */ /*! \qmlsignal ContactModel::storageLocationsChanged() This signal is emitted, when \l ContactModel::storageLocations property changes. \sa ContactModel::storageLocations */ /*! \qmlproperty int ContactModel::storageLocations This property indicates which storage location is used to populate the model. Only one storage location can be used for each model. Storage location is a backend specific feature. Some backends support it and some might just ignore it. If backend is having some specific requirements and they're not met, backend returns StorageLocationsNotExistingError. \sa ContactModel::StorageLocation \sa ContactModel::saveContact */ int QDeclarativeContactModel::storageLocations() const { return d->m_storageLocations; } void QDeclarativeContactModel::setStorageLocations(int storageLocations) { QContactAbstractRequest::StorageLocations newStorageLocation = 0; // only one storage location for a model is supported switch (storageLocations) { case UserDataStorage: newStorageLocation = QContactAbstractRequest::UserDataStorage; break; case SystemStorage: newStorageLocation = QContactAbstractRequest::SystemStorage; break; case (UserDataStorage | SystemStorage): qWarning() << Q_FUNC_INFO << "Model does not support multiple storage locations"; updateError(QContactManager::NotSupportedError); return; default: qWarning() << Q_FUNC_INFO << "Unknown storage location"; updateError(QContactManager::BadArgumentError); return; } if (d->m_storageLocations != newStorageLocation) { d->m_storageLocations = newStorageLocation; emit storageLocationsChanged(); } } void QDeclarativeContactModel::componentComplete() { if (!d->m_manager) setManager(QString()); d->m_componentCompleted = true; if (d->m_autoUpdate) update(); } /*! \qmlproperty bool ContactModel::autoUpdate This property indicates whether or not the contact model should be updated automatically, default value is true. */ void QDeclarativeContactModel::setAutoUpdate(bool autoUpdate) { if (autoUpdate == d->m_autoUpdate) return; d->m_autoUpdate = autoUpdate; emit autoUpdateChanged(); } bool QDeclarativeContactModel::autoUpdate() const { return d->m_autoUpdate; } void QDeclarativeContactModel::update() { if (!d->m_componentCompleted) return; QMetaObject::invokeMethod(this, "fetchAgain", Qt::QueuedConnection); } void QDeclarativeContactModel::doUpdate() { if (d->m_autoUpdate) update(); } /*! \qmlproperty string ContactModel::error This property holds the latest error code returned by the contact manager. This property is read only. */ QString QDeclarativeContactModel::error() const { if (d->m_manager) { switch (d->m_error) { case QContactManager::DoesNotExistError: return QStringLiteral("DoesNotExist"); case QContactManager::AlreadyExistsError: return QStringLiteral("AlreadyExists"); case QContactManager::InvalidDetailError: return QStringLiteral("InvalidDetail"); case QContactManager::InvalidRelationshipError: return QStringLiteral("InvalidRelationship"); case QContactManager::LockedError: return QStringLiteral("LockedError"); case QContactManager::DetailAccessError: return QStringLiteral("DetailAccessError"); case QContactManager::PermissionsError: return QStringLiteral("PermissionsError"); case QContactManager::OutOfMemoryError: return QStringLiteral("OutOfMemory"); case QContactManager::NotSupportedError: return QStringLiteral("NotSupported"); case QContactManager::BadArgumentError: return QStringLiteral("BadArgument"); case QContactManager::UnspecifiedError: return QStringLiteral("UnspecifiedError"); case QContactManager::VersionMismatchError: return QStringLiteral("VersionMismatch"); case QContactManager::LimitReachedError: return QStringLiteral("LimitReached"); case QContactManager::InvalidContactTypeError: return QStringLiteral("InvalidContactType"); default: break; } } return QStringLiteral("NoError"); } /*! \qmlproperty list ContactModel::availableManagers This property holds the list of available manager names. This property is read only. */ QStringList QDeclarativeContactModel::availableManagers() const { return QContactManager::availableManagers(); } static QString urlToLocalFileName(const QUrl& url) { if (!url.isValid()) { return url.toString(); } else if (url.scheme() == "qrc") { return url.toString().remove(0, 5).prepend(':'); } else { return url.toLocalFile(); } } /*! \qmlmethod void ContactModel::importContacts(url url, list profiles) Import contacts from a vcard by the given \a url and optional \a profiles. Only one import operation can be active at a time. Supported profiles are: \list \li "Sync" Imports contacts in sync mode, currently, this is the same as passing in an empty list, and is generally what you want. \li "Backup" imports contacts in backup mode, use this mode if the vCard was generated by exporting in backup mode. \endlist \sa QVersitContactHandlerFactory \sa QVersitContactHandlerFactory::ProfileSync() \sa QVersitContactHandlerFactory::ProfileBackup() */ void QDeclarativeContactModel::importContacts(const QUrl& url, const QStringList& profiles) { // Reader is capable of handling only one request at the time. ImportError importError = ImportNotReadyError; if (d->m_reader.state() != QVersitReader::ActiveState) { d->m_importProfiles = profiles; //TODO: need to allow download vcard from network QFile* file = new QFile(urlToLocalFileName(url)); bool ok = file->open(QIODevice::ReadOnly); if (ok) { d->m_reader.setDevice(file); if (d->m_reader.startReading()) { d->m_lastImportUrl = url; return; } importError = QDeclarativeContactModel::ImportError(d->m_reader.error()); } else { importError = ImportIOError; } } emit importCompleted(importError, url); } /*! \qmlmethod void ContactModel::exportContacts(url url, list profiles, list declarativeContacts) Export all contacts of this model into a vcard file to the given \a url by optional \a profiles. The optional \a declarativeContacts list can be used to export an arbitrary list of QDeclarativeContact objects not necessarily belonging to the data set of this model. At the moment only the local file url is supported in export method. Also, only one export operation can be active at a time. Supported profiles are: \list \li "Sync" exports contacts in sync mode, currently, this is the same as passing in an empty list, and is generally what you want. \li "Backup" exports contacts in backup mode, this will add non-standard properties to the generated vCard to try to save every detail of the contacts. Only use this if the vCard is going to be imported using the backup profile. #include "moc_qdeclarativecontactmodel_p.cpp" \endlist \sa QVersitContactHandlerFactory \sa QVersitContactHandlerFactory::ProfileSync() \sa QVersitContactHandlerFactory::ProfileBackup() */ void QDeclarativeContactModel::exportContacts(const QUrl& url, const QStringList& profiles, const QVariantList &declarativeContacts) { // Writer is capable of handling only one request at the time. ExportError exportError = ExportNotReadyError; if (d->m_writer.state() != QVersitWriter::ActiveState) { QString profile = profiles.isEmpty()? QString() : profiles.at(0); //only one profile string supported now. QVersitContactExporter exporter(profile); QList contacts; if (declarativeContacts.isEmpty()) { foreach (QDeclarativeContact* dc, d->m_contacts) { contacts.append(dc->contact()); } } else { foreach (const QVariant &contactVariant, declarativeContacts) { QObject *rawObject = contactVariant.value(); QDeclarativeContact *dc = qobject_cast(rawObject); if (dc) { contacts.append(dc->contact()); } } } exporter.exportContacts(contacts, QVersitDocument::VCard30Type); QList documents = exporter.documents(); QFile* file = new QFile(urlToLocalFileName(url)); bool ok = file->open(QIODevice::WriteOnly); if (ok) { d->m_writer.setDevice(file); if (d->m_writer.startWriting(documents)) { d->m_lastExportUrl = url; return; } exportError = QDeclarativeContactModel::ExportError(d->m_writer.error()); } else { exportError = ExportIOError; } } emit exportCompleted(exportError, url); } void QDeclarativeContactModel::contactsExported(QVersitWriter::State state) { if (state == QVersitWriter::FinishedState || state == QVersitWriter::CanceledState) { delete d->m_writer.device(); d->m_writer.setDevice(0); emit exportCompleted(QDeclarativeContactModel::ExportError(d->m_writer.error()), d->m_lastExportUrl); } } void QDeclarativeContactModel::onFetchedContactDestroyed(QObject *obj) { QContactId id = d->m_contactFetchedMap.key(static_cast(obj)); if (!id.isNull()) d->m_contactFetchedMap.remove(id); } int QDeclarativeContactModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return d->m_contacts.count(); } /*! \qmlproperty Filter ContactModel::filter This property holds the filter instance used by the contact model. \sa Filter */ QDeclarativeContactFilter* QDeclarativeContactModel::filter() const { return d->m_filter; } void QDeclarativeContactModel::setFilter(QDeclarativeContactFilter* filter) { if (d->m_filter != filter) { if (d->m_filter) disconnect(d->m_filter, SIGNAL(filterChanged()), this, SIGNAL(filterChanged())); d->m_filter = filter; if (d->m_filter) connect(d->m_filter, SIGNAL(filterChanged()), SIGNAL(filterChanged()), Qt::UniqueConnection); emit filterChanged(); } } /*! \qmlproperty FetchHint ContactModel::fetchHint This property holds the fetch hint instance used by the contact model. \sa FetchHint */ QDeclarativeContactFetchHint* QDeclarativeContactModel::fetchHint() const { return d->m_fetchHint; } void QDeclarativeContactModel::setFetchHint(QDeclarativeContactFetchHint* fetchHint) { if (d->m_fetchHint != fetchHint) { if (d->m_fetchHint) disconnect(d->m_fetchHint, SIGNAL(fetchHintChanged()), this, SIGNAL(fetchHintChanged())); d->m_fetchHint = fetchHint; if (d->m_fetchHint) connect(d->m_fetchHint, SIGNAL(fetchHintChanged()), SIGNAL(fetchHintChanged()), Qt::UniqueConnection); emit fetchHintChanged(); } } /*! \qmlproperty list ContactModel::contacts This property holds the list of contacts. \sa Contact */ QQmlListProperty QDeclarativeContactModel::contacts() { return QQmlListProperty(this, 0, contacts_append, contacts_count, contacts_at, contacts_clear); } void QDeclarativeContactModel::contacts_append(QQmlListProperty* prop, QDeclarativeContact* contact) { Q_UNUSED(prop); Q_UNUSED(contact); qWarning() << Q_FUNC_INFO << "appending contacts is not currently supported"; } int QDeclarativeContactModel::contacts_count(QQmlListProperty* prop) { return static_cast(prop->object)->d->m_contacts.count(); } QDeclarativeContact* QDeclarativeContactModel::contacts_at(QQmlListProperty* prop, int index) { return static_cast(prop->object)->d->m_contacts.at(index); } void QDeclarativeContactModel::contacts_clear(QQmlListProperty* prop) { QDeclarativeContactModel* model = static_cast(prop->object); model->clearContacts(); emit model->contactsChanged(); } /*! \qmlproperty list ContactModel::sortOrders This property holds a list of sort orders used by the contacts model. \sa SortOrder */ QQmlListProperty QDeclarativeContactModel::sortOrders() { return QQmlListProperty(this, 0, sortOrder_append, sortOrder_count, sortOrder_at, sortOrder_clear); } void QDeclarativeContactModel::startImport(QVersitReader::State state) { if (state == QVersitReader::FinishedState || state == QVersitReader::CanceledState) { QVersitContactImporter importer(d->m_importProfiles); importer.importDocuments(d->m_reader.results()); QList contacts = importer.contacts(); delete d->m_reader.device(); d->m_reader.setDevice(0); if (d->m_manager) { if (!d->m_manager->saveContacts(&contacts)) { if (d->m_error != d->m_manager->error()) { d->m_error = d->m_manager->error(); emit errorChanged(); } } } emit importCompleted(QDeclarativeContactModel::ImportError(d->m_reader.error()), d->m_lastImportUrl); } } /*! \qmlsignal ContactModel::contactsFetched(int requestId, list fetchedContacts) This signal is emitted, when a contact fetch request is finished. \sa ContactModel::fetchContacts */ /*! \qmlmethod int ContactModel::fetchContacts(list contactIds) Starts a request to fetch contacts by the given \a contactIds, and returns the unique ID of this request. -1 is returned if the request can't be started. Note that the contacts fetched won't be added to the model, but can be accessed through the contactsFetched signal handler. \sa ContactModel::contactsFetched */ int QDeclarativeContactModel::fetchContacts(const QStringList &contactIds) { if (contactIds.isEmpty()) return -1; QContactFetchByIdRequest *fetchRequest = new QContactFetchByIdRequest(this); connect(fetchRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(onFetchContactsRequestStateChanged(QContactAbstractRequest::State))); fetchRequest->setManager(d->m_manager); QList ids; foreach (const QString &contactId, contactIds) ids.append(QContactId::fromString(contactId)); fetchRequest->setIds(ids); int requestId = d->m_lastRequestId.fetchAndAddOrdered(1); d->m_requestIdHash.insert(fetchRequest, requestId); if (fetchRequest->start()) { return requestId; } else { d->m_requestIdHash.remove(fetchRequest); return -1; } } /*! \internal */ void QDeclarativeContactModel::onFetchContactsRequestStateChanged(QContactAbstractRequest::State state) { if (state != QContactAbstractRequest::FinishedState) return; QContactFetchByIdRequest *request = qobject_cast(sender()); Q_ASSERT(request); checkError(request); const int requestId = d->m_requestIdHash.value(request, -1); if (requestId == -1) qWarning() << Q_FUNC_INFO << "transaction not found from the request hash"; else d->m_requestIdHash.remove(request); QVariantList list; if (request->error() == QContactManager::NoError) { QList contacts(request->contacts()); foreach (const QContact &contact, contacts) { // if the contact was already fetched update the contact QDeclarativeContact *declarativeContact = d->m_contactFetchedMap.value(contact.id(), 0); if (!declarativeContact) { declarativeContact = new QDeclarativeContact(this); // Transfer the ownership to QML // The model will destroy the contact if it get removed from the backend, otherwise the QML side need to destroy it. QQmlEngine::setObjectOwnership(declarativeContact, QQmlEngine::JavaScriptOwnership); // keep track of contact destruction to remove it from the list if QML destroys it connect(declarativeContact, SIGNAL(destroyed(QObject*)), SLOT(onFetchedContactDestroyed(QObject*))); // we need keep track of the contact to update it if the contact get update on the backend. or destroy it // if the contact get removed from the backend d->m_contactFetchedMap[contact.id()] = declarativeContact; } declarativeContact->setContact(contact); list.append(QVariant::fromValue(declarativeContact)); } } emit contactsFetched(requestId, list); request->deleteLater(); } void QDeclarativeContactModel::clearContacts() { qDeleteAll(d->m_contacts); d->m_contacts.clear(); d->m_contactMap.clear(); qDeleteAll(d->m_contactFetchedMap.values()); d->m_contactFetchedMap.clear(); } void QDeclarativeContactModel::fetchAgain() { QList sortOrders; foreach (QDeclarativeContactSortOrder* so, d->m_sortOrders) { sortOrders.append(so->sortOrder()); } QContactFetchRequest* fetchRequest = new QContactFetchRequest(this); fetchRequest->setStorageLocations(d->m_storageLocations); fetchRequest->setManager(d->m_manager); fetchRequest->setSorting(sortOrders); if (d->m_filter){ fetchRequest->setFilter(d->m_filter->filter()); } else { fetchRequest->setFilter(QContactFilter()); } fetchRequest->setFetchHint(d->m_fetchHint ? d->m_fetchHint->fetchHint() : QContactFetchHint()); connect(fetchRequest, SIGNAL(resultsAvailable()), this, SLOT(requestUpdated())); connect(fetchRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(fetchRequestStateChanged(QContactAbstractRequest::State))); // cancel all previous requests foreach (QContactFetchRequest *req, d->m_pendingRequests) { req->cancel(); req->deleteLater(); } d->m_pendingContacts.clear(); d->m_pendingRequests.clear(); d->m_pendingRequests.append(fetchRequest); // if we have no contacts yet, we can display results as soon as they arrive // but if we are updating the model after a sort or filter change, we have to // wait for all contacts before processing the update d->m_progressiveLoading = d->m_contacts.isEmpty(); fetchRequest->start(); } void QDeclarativeContactModel::requestUpdated() { QContactFetchRequest* req = qobject_cast(QObject::sender()); Q_ASSERT(req); if (req) { QList contacts = req->contacts(); // if we are starting from scratch, we can show contact results as they arrive if (d->m_progressiveLoading) { QList dcs; foreach (const QContact &c, contacts) { if (d->m_contactMap.contains(c.id())) { QDeclarativeContact* dc = d->m_contactMap.value(c.id()); dc->setContact(c); } else { QDeclarativeContact* dc = new QDeclarativeContact(this); if (dc) { d->m_contactMap.insert(c.id(), dc); dc->setContact(c); dcs.append(dc); } } } if (dcs.count() > 0) { beginInsertRows(QModelIndex(), d->m_contacts.count(), d->m_contacts.count() + dcs.count() - 1); // At this point we need to relay on the backend and assume that the partial results are following the fetch sorting property d->m_contacts += dcs; endInsertRows(); emit contactsChanged(); } } else { d->m_pendingContacts << contacts; } checkError(req); } } void QDeclarativeContactModel::fetchRequestStateChanged(QContactAbstractRequest::State newState) { if (newState != QContactAbstractRequest::FinishedState) return; QContactFetchRequest* req = qobject_cast(QObject::sender()); Q_ASSERT(req); if (req) { // if we were not processing contacts as soon as they arrive, we need to process them here. if (!d->m_progressiveLoading) { // start by removing the contacts that don't belong to this result set anymore for (int i = d->m_contacts.count()-1; i >= 0; --i) { QDeclarativeContact *contact = d->m_contacts[i]; if (!d->m_pendingContacts.contains(contact->contact())) { beginRemoveRows(QModelIndex(), i, i); d->m_contacts.removeAt(i); d->m_contactMap.remove(contact->contact().id()); endRemoveRows(); } } // now insert new contacts and move existing ones to their final positions int count = d->m_pendingContacts.count(); for (int i = 0; i < count; ++i) { QContact c = d->m_pendingContacts[i]; if (!d->m_contactMap.contains(c.id())) { QDeclarativeContact* dc = new QDeclarativeContact(this); dc->setContact(c); beginInsertRows(QModelIndex(), i, i); d->m_contacts.insert(i, dc); d->m_contactMap.insert(c.id(),dc); endInsertRows(); } else { QDeclarativeContact *contact = d->m_contactMap[c.id()]; int pos = d->m_contacts.indexOf(contact); if (pos != i) { beginMoveRows(QModelIndex(), pos, pos, QModelIndex(), i); d->m_contacts.move(pos, i); endMoveRows(); } } } emit contactsChanged(); } // and now clear the pending contact list as the model is up-to-date d->m_pendingContacts.clear(); d->m_pendingRequests.removeOne(req); req->deleteLater(); } } /*! \qmlmethod ContactModel::saveContact(Contact contact, StorageLocation storageLocation = UserDataStorage) Save the given \a contact into the contacts backend. The location for storing the contact can be defined with \a storageLocation for new contacts. When the contact is updated, ie saved again, \a storageLocation is ignored and the contact is saved to the same location as it were before. Once saved successfully, the dirty flags of this contact will be reset. \sa Contact::modified */ void QDeclarativeContactModel::saveContact(QDeclarativeContact* dc, StorageLocation storageLocation) { if (dc) { QContactSaveRequest* req = new QContactSaveRequest(this); req->setManager(d->m_manager); req->setContact(dc->contact()); req->setStorageLocation(QContactAbstractRequest::StorageLocation(storageLocation)); if (dc->contact().id().isNull()) { // if the contact id is empty this means that this contact is a new contact // we need to keep trace of this declarative contact to update with the // new Id as soon as this request finish QPointer pContact = dc; req->setProperty("DeclarativeContact", QVariant::fromValue(pContact)); } connect(req,SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(onRequestStateChanged(QContactAbstractRequest::State))); req->start(); } } void QDeclarativeContactModel::onRequestStateChanged(QContactAbstractRequest::State newState) { if (newState != QContactAbstractRequest::FinishedState) { return; } QContactAbstractRequest *request = qobject_cast(sender()); Q_ASSERT(request); if ((request->type() == QContactSaveRequest::ContactSaveRequest) && (request->error() == QContactManager::NoError)) { QVariant vContact = request->property("DeclarativeContact"); if (vContact.isValid()) { QPointer pContact = vContact.value >(); // Update contact info. // this is necessary to make sure that the declarative contact get the new contact ID otherwise // the contact Id will be empty QList contacts = qobject_cast(request)->contacts(); if (pContact && contacts.length() == 1) { pContact->setContact(contacts[0]); } } } checkError(request); request->deleteLater(); } void QDeclarativeContactModel::checkError(const QContactAbstractRequest *request) { if (request) { updateError(request->error()); } } void QDeclarativeContactModel::updateError(QContactManager::Error error) { if (d->m_error != error) { d->m_error = error; emit errorChanged(); } } void QDeclarativeContactModel::onContactsAdded(const QList& ids) { if (d->m_autoUpdate && !ids.isEmpty()) { QList contactsIdsForThisModel = extractContactIdsInStorageLocationFromThisModel(ids); if (contactsIdsForThisModel.isEmpty()) return; QContactFetchRequest *fetchRequest = createContactFetchRequest(contactsIdsForThisModel); connect(fetchRequest,SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(onContactsAddedFetchRequestStateChanged(QContactAbstractRequest::State))); fetchRequest->start(); } } QList QDeclarativeContactModel::extractContactIdsInStorageLocationFromThisModel(const QList &ids) { QList idsForThisModel; foreach (const QContactId &id, ids) { if (d->m_storageLocations & extractStorageLocation(id)) idsForThisModel.append(id); } return idsForThisModel; } QContactAbstractRequest::StorageLocations QDeclarativeContactModel::extractStorageLocation(const QContactId &id) { const QContactEngineId *engineId = QContactManagerEngine::engineId(id); return engineId->storageLocation(); } /*! \qmlmethod ContactModel::removeContact(string contactId) Remove the contact from the contacts store by given \a contactId. After removing a contact it is not possible to save it again. \sa Contact::contactId */ void QDeclarativeContactModel::removeContact(QString id) { QList ids; ids << id; removeContacts(ids); } /*! \qmlmethod ContactModel::removeContacts(list contactIds) Remove the list of contacts from the contacts store by given \a contactIds. \sa Contact::contactId */ void QDeclarativeContactModel::removeContacts(const QStringList &ids) { QContactRemoveRequest* req = new QContactRemoveRequest(this); QList contactIdsAsList; req->setManager(d->m_manager); foreach (const QString& id, ids) { QContactId contactId = QContactId::fromString(id); if (!contactId.isNull()) contactIdsAsList.append(contactId); } req->setContactIds(contactIdsAsList); connect(req,SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(onRequestStateChanged(QContactAbstractRequest::State))); req->start(); } void QDeclarativeContactModel::onContactsRemoved(const QList &ids) { if (!d->m_autoUpdate) return; bool emitSignal = false; foreach (const QContactId &id, ids) { // delete the contact from fetched map if necessary QDeclarativeContact* contact = d->m_contactFetchedMap.take(id); if (contact) contact->deleteLater(); if (d->m_contactMap.contains(id)) { int row = 0; //TODO:need a fast lookup for (; row < d->m_contacts.count(); row++) { if (d->m_contacts.at(row)->contactId() == id.toString()) break; } if (row < d->m_contacts.count()) { beginRemoveRows(QModelIndex(), row, row); contact = d->m_contacts.takeAt(row); contact->deleteLater(); d->m_contactMap.remove(id); endRemoveRows(); emitSignal = true; } } } if (emitSignal) emit contactsChanged(); } void QDeclarativeContactModel::onContactsChanged(const QList &ids) { if (d->m_autoUpdate && !ids.isEmpty()) { QList contactsIdsForThisModel = extractContactIdsInStorageLocationFromThisModel(ids); if (contactsIdsForThisModel.isEmpty()) return; QContactFetchRequest *fetchRequest = createContactFetchRequest(contactsIdsForThisModel); connect(fetchRequest, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(onContactsChangedFetchRequestStateChanged(QContactAbstractRequest::State))); fetchRequest->start(); } // If any contact in the fetchedList has changed we need to update it. // We need a different query because feched contacts could not be part of the model. // // For example: if the model contains a filter if (!ids.isEmpty()) { QStringList pendingFetch; foreach (const QContactId &id, ids) { QDeclarativeContact* dc = d->m_contactFetchedMap.value(id); if (dc) pendingFetch << dc->contactId(); } if (!pendingFetch.isEmpty()) fetchContacts(pendingFetch); } } QContactFetchRequest *QDeclarativeContactModel::createContactFetchRequest(const QList &ids) { QContactFetchRequest *fetchRequest = new QContactFetchRequest(this); fetchRequest->setManager(d->m_manager); fetchRequest->setFetchHint(d->m_fetchHint ? d->m_fetchHint->fetchHint() : QContactFetchHint()); // model supports only one storage location at the moment fetchRequest->setStorageLocations(QFlags(d->m_storageLocations)); QContactIdFilter idFilter; idFilter.setIds(ids); if (d->m_filter) { QContactIntersectionFilter filter; filter.append(idFilter); // result handling assumes that id filter is the first filter filter.append(d->m_filter->filter()); fetchRequest->setFilter(filter); } else fetchRequest->setFilter(idFilter); return fetchRequest; } QVariant QDeclarativeContactModel::data(const QModelIndex &index, int role) const { //Check if QList itme's index is valid before access it, index should be between 0 and count - 1 if (index.row() < 0 || index.row() >= d->m_contacts.count()) { return QVariant(); } QDeclarativeContact* dc = d->m_contacts.value(index.row()); Q_ASSERT(dc); QContact c = dc->contact(); switch(role) { case Qt::DisplayRole: return c.detail(QContactDetail::TypeDisplayLabel).value(QContactDisplayLabel::FieldLabel); case Qt::DecorationRole: return QPixmap(); case ContactRole: return QVariant::fromValue(dc); } return QVariant(); } void QDeclarativeContactModel::sortOrder_append(QQmlListProperty *p, QDeclarativeContactSortOrder *sortOrder) { QDeclarativeContactModel* model = qobject_cast(p->object); if (model && sortOrder) { QObject::connect(sortOrder, SIGNAL(sortOrderChanged()), model, SIGNAL(sortOrdersChanged())); model->d->m_sortOrders.append(sortOrder); emit model->sortOrdersChanged(); } } int QDeclarativeContactModel::sortOrder_count(QQmlListProperty *p) { QDeclarativeContactModel* model = qobject_cast(p->object); if (model) return model->d->m_sortOrders.size(); return 0; } QDeclarativeContactSortOrder * QDeclarativeContactModel::sortOrder_at(QQmlListProperty *p, int idx) { QDeclarativeContactModel* model = qobject_cast(p->object); QDeclarativeContactSortOrder* sortOrder = 0; if (model) { int i = 0; foreach (QDeclarativeContactSortOrder* s, model->d->m_sortOrders) { if (i == idx) { sortOrder = s; break; } else { i++; } } } return sortOrder; } void QDeclarativeContactModel::sortOrder_clear(QQmlListProperty *p) { QDeclarativeContactModel* model = qobject_cast(p->object); if (model) { model->d->m_sortOrders.clear(); emit model->sortOrdersChanged(); } } /*! \internal It's invoked by the fetch request from onContactsAdded(). */ void QDeclarativeContactModel::onContactsAddedFetchRequestStateChanged(QContactAbstractRequest::State state) { if (state != QContactAbstractRequest::FinishedState) return; QContactFetchRequest *request = qobject_cast(sender()); Q_ASSERT(request); checkError(request); if (request->error() == QContactManager::NoError) { QList fetchedContacts(request->contacts()); bool contactsAdded = false; foreach (const QContact &c,fetchedContacts) { if (d->m_contactMap.contains(c.id())) { qWarning() <setContact(c); int index = contactIndex(dc); beginInsertRows(QModelIndex(), index, index); d->m_contacts.insert(index, dc); d->m_contactMap.insert(c.id(), dc); if (!contactsAdded) contactsAdded = true; endInsertRows(); } if (contactsAdded) emit contactsChanged(); } request->deleteLater(); } static bool contactListDoesNotContainContactWithId(const QList &contactList, const QContactId &contactId) { foreach (const QContact &contact, contactList) { if (contact.id() == contactId) return false; } return true; } /*! \internal It's invoked by the fetch request from onContactsChanged(). */ void QDeclarativeContactModel::onContactsChangedFetchRequestStateChanged(QContactAbstractRequest::State state) { if (state != QContactAbstractRequest::FinishedState) return; QContactFetchRequest *request = qobject_cast(sender()); Q_ASSERT(request); checkError(request); bool contactsUpdated = false; if (request->error() == QContactManager::NoError || request->error() == QContactManager::DoesNotExistError) { QList fetchedContacts(request->contacts()); QList requestedContactIds; //read requested contacts ids from the filter if (request->filter().type() == QContactFilter::IdFilter) { QContactIdFilter idFilter(request->filter()); requestedContactIds = idFilter.ids(); } else { QContactIntersectionFilter intersectionFilter(request->filter()); QContactIdFilter idFilter(intersectionFilter.filters().at(0)); // assuming that id filter is the first filter requestedContactIds = idFilter.ids(); } //handle updated contacts which needs removal from model //all contacts requested but not received are removed foreach (const QContactId &id, requestedContactIds) { if (contactListDoesNotContainContactWithId(fetchedContacts, id)) { for (int i=0;im_contacts.size();++i) { if (d->m_contacts.at(i)->contactId() == id.toString()) { beginRemoveRows(QModelIndex(), i, i); // Remove and delete contact object QDeclarativeContact* dc = d->m_contacts.takeAt(i); dc->deleteLater(); d->m_contactMap.remove(id); endRemoveRows(); contactsUpdated = true; } } } } foreach (const QContact &fetchedContact, fetchedContacts) { QString contactIdString(fetchedContact.id().toString()); bool fetchedContactFound = false; for (int i = 0; i < d->m_contacts.size(); ++i) { //handle updated contacts which should be updated in the model if (d->m_contacts.at(i)->contactId() == contactIdString) { QDeclarativeContact* dc = d->m_contacts.at(i); dc->setContact(fetchedContact); // Since the contact can change the position due the sort order we need take care of it // First we need to remove it from previous position and notify the model about that beginRemoveRows(QModelIndex(), i, i); d->m_contactMap.remove(fetchedContact.id()); d->m_contacts.removeAt(i); endRemoveRows(); // Calculate the new position int index = contactIndex(dc); // Notify the model about the new item position beginInsertRows(QModelIndex(), index, index); d->m_contacts.insert(index, dc); d->m_contactMap.insert(fetchedContact.id(),dc); if (!contactsUpdated) contactsUpdated = true; endInsertRows(); fetchedContactFound = true; break; } } //handle updated contacts which needs to be added in the model if (!fetchedContactFound) { QDeclarativeContact* dc = new QDeclarativeContact(this); dc->setContact(fetchedContact); int index = contactIndex(dc); beginInsertRows(QModelIndex(), index, index); d->m_contacts.insert(index, dc); d->m_contactMap.insert(fetchedContact.id(),dc); contactsUpdated = true; endInsertRows(); } } } if (contactsUpdated) emit contactsChanged(); request->deleteLater(); } int QDeclarativeContactModel::contactIndex(const QDeclarativeContact* contact) { if (d->m_sortOrders.count() > 0) { QList mSortOrders; foreach (QDeclarativeContactSortOrder *sortOrder, d->m_sortOrders) mSortOrders.append(sortOrder->sortOrder()); for (int i = 0; i < d->m_contacts.size(); i++) { // check to see if the new contact should be inserted here int comparison = QContactManagerEngine::compareContact(d->m_contacts.at(i)->contact(), contact->contact(), mSortOrders); //if the contacts are equal or cannot be compared //we return the current position.The default case is if the new contact //should appear before the compared contact in m_contacts if (comparison >= 0) return i; } } return d->m_contacts.size(); } #include "moc_qdeclarativecontactmodel_p.cpp" QT_END_NAMESPACE src/imports/contacts/qdeclarativecontactmodel_p.h000066400000000000000000000214151233466112000227120ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTMODEL_P_H #define QDECLARATIVECONTACTMODEL_P_H #include #include #include #include #include #include #include "qdeclarativecontact_p.h" #include "qdeclarativecontactfetchhint_p.h" #include "qdeclarativecontactfilter_p.h" #include "qdeclarativecontactsortorder_p.h" QTCONTACTS_USE_NAMESPACE QTVERSIT_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactModelPrivate; class QDeclarativeContactModel : public QAbstractListModel, public QQmlParserStatus { Q_OBJECT Q_PROPERTY(QString manager READ manager WRITE setManager NOTIFY managerChanged) Q_PROPERTY(int storageLocations READ storageLocations WRITE setStorageLocations NOTIFY storageLocationsChanged) Q_PROPERTY(QStringList availableManagers READ availableManagers) Q_PROPERTY(QString error READ error NOTIFY errorChanged) Q_PROPERTY(bool autoUpdate READ autoUpdate WRITE setAutoUpdate NOTIFY autoUpdateChanged) Q_PROPERTY(QDeclarativeContactFilter* filter READ filter WRITE setFilter NOTIFY filterChanged) Q_PROPERTY(QDeclarativeContactFetchHint* fetchHint READ fetchHint WRITE setFetchHint NOTIFY fetchHintChanged) Q_PROPERTY(QQmlListProperty contacts READ contacts NOTIFY contactsChanged) Q_PROPERTY(QQmlListProperty sortOrders READ sortOrders NOTIFY sortOrdersChanged) Q_ENUMS(ExportError) Q_ENUMS(ImportError) Q_ENUMS(StorageLocation) Q_INTERFACES(QQmlParserStatus) public: explicit QDeclarativeContactModel(QObject *parent = 0); ~QDeclarativeContactModel(); enum { ContactRole = Qt::UserRole + 500 }; enum ExportError { ExportNoError = QVersitWriter::NoError, ExportUnspecifiedError = QVersitWriter::UnspecifiedError, ExportIOError = QVersitWriter::IOError, ExportOutOfMemoryError = QVersitWriter::OutOfMemoryError, ExportNotReadyError = QVersitWriter::NotReadyError }; enum ImportError { ImportNoError = QVersitReader::NoError, ImportUnspecifiedError = QVersitReader::UnspecifiedError, ImportIOError = QVersitReader::IOError, ImportOutOfMemoryError = QVersitReader::OutOfMemoryError, ImportNotReadyError = QVersitReader::NotReadyError, ImportParseError = QVersitReader::ParseError }; enum StorageLocation { UserDataStorage = QContactAbstractRequest::UserDataStorage, SystemStorage = QContactAbstractRequest::SystemStorage }; QString manager() const; void setManager(const QString& manager); int storageLocations() const; void setStorageLocations(int storageLocations); QStringList availableManagers() const; QString error() const; QDeclarativeContactFilter* filter() const; void setFilter(QDeclarativeContactFilter* filter); QDeclarativeContactFetchHint* fetchHint() const; void setFetchHint(QDeclarativeContactFetchHint* fetchHint); // From QQmlParserStatus virtual void classBegin() {} virtual void componentComplete(); // From QAbstractListModel int rowCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; bool autoUpdate() const; void setAutoUpdate(bool autoUpdate); QQmlListProperty contacts() ; static void contacts_append(QQmlListProperty* prop, QDeclarativeContact* contact); static int contacts_count(QQmlListProperty* prop); static QDeclarativeContact* contacts_at(QQmlListProperty* prop, int index); static void contacts_clear(QQmlListProperty* prop); static void sortOrder_append(QQmlListProperty *p, QDeclarativeContactSortOrder *sortOrder); static int sortOrder_count(QQmlListProperty *p); static QDeclarativeContactSortOrder * sortOrder_at(QQmlListProperty *p, int idx); static void sortOrder_clear(QQmlListProperty *p); QQmlListProperty sortOrders() ; Q_INVOKABLE void removeContact(QString id); Q_INVOKABLE void removeContacts(const QStringList& ids); Q_INVOKABLE void saveContact(QDeclarativeContact* dc, StorageLocation storageLocation = UserDataStorage); Q_INVOKABLE int fetchContacts(const QStringList& contactIds); Q_INVOKABLE void importContacts(const QUrl& url, const QStringList& profiles = QStringList()); Q_INVOKABLE void exportContacts(const QUrl& url, const QStringList& profiles = QStringList(), const QVariantList &declarativeContacts = QVariantList()); signals: void managerChanged(); void storageLocationsChanged(); void filterChanged(); void errorChanged(); void fetchHintChanged(); void contactsChanged(); void sortOrdersChanged(); void autoUpdateChanged(); void exportCompleted(ExportError error, QUrl url); void importCompleted(ImportError error, QUrl url); void contactsFetched(int requestId, const QVariantList &fetchedContacts); public slots: void update(); private slots: void clearContacts(); void fetchAgain(); void requestUpdated(); void fetchRequestStateChanged(QContactAbstractRequest::State newState); void doUpdate(); void onRequestStateChanged(QContactAbstractRequest::State newState); void onContactsAdded(const QList& ids); void onContactsRemoved(const QList& ids); void onContactsChanged(const QList& ids); void startImport(QVersitReader::State state); void contactsExported(QVersitWriter::State state); void onFetchedContactDestroyed(QObject *obj); // handle fetch request from onContactsAdded() void onContactsAddedFetchRequestStateChanged(QContactAbstractRequest::State state); // handle fetch request from onContactsChanged() void onContactsChangedFetchRequestStateChanged(QContactAbstractRequest::State state); // handle fetch request from fetchContacts() void onFetchContactsRequestStateChanged(QContactAbstractRequest::State state); private: QList extractContactIdsInStorageLocationFromThisModel(const QList &ids); static QContactAbstractRequest::StorageLocations extractStorageLocation(const QContactId &id); QContactFetchRequest *createContactFetchRequest(const QList &ids); void checkError(const QContactAbstractRequest *request); void updateError(QContactManager::Error error); int contactIndex(const QDeclarativeContact* contact); private: QScopedPointer d; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactModel) #endif // QDECLARATIVECONTACTMODEL_P_H src/imports/contacts/qdeclarativecontactrelationship.cpp000066400000000000000000000133541233466112000243320ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativecontactrelationship_p.h" #include QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE /*! \qmltype Relationship \instantiates QDeclarativeContactRelationship \brief The Relationship element describes a one-to-one relationship between a locally-stored contact and another (possibly remote) contact. \ingroup qml-contacts-main \inqmlmodule QtContacts 5.0 This element is part of the \b{QtContacts} module. \sa QContactRelationship \sa RelationshipModel */ QDeclarativeContactRelationship::QDeclarativeContactRelationship(QObject* parent) :QObject(parent) { } /*! \qmlproperty int Relationship::first This property holds the locally-stored contact which has a relationship of the given type with the second contact. */ QDeclarativeContact* QDeclarativeContactRelationship::first() const { QDeclarativeContact *v = new QDeclarativeContact(); v->setContact(m_relationship.first()); return v; } /*! \qmlproperty int Relationship::second This property holds the contact with which the first contact has a relationship of the given type. */ QDeclarativeContact* QDeclarativeContactRelationship::second() const { QDeclarativeContact *v = new QDeclarativeContact(); v->setContact(m_relationship.second()); return v; } /*! \qmlproperty string Relationship::type This property holds the type of relationship which the source contact has with the destination contacts. The value for this property can be one of: \list \li HasMember \li Aggregates \li IsSameAs \li HasAssistant \li HasManager \li HasSpouse \endlist or any other customized relationship type string. */ QVariant QDeclarativeContactRelationship::relationshipType() const { return m_relationship.relationshipType(); } void QDeclarativeContactRelationship::setFirst(QDeclarativeContact* firstContact) { if (firstContact) m_relationship.setFirst(firstContact->contact()); } void QDeclarativeContactRelationship::setSecond(QDeclarativeContact* secondContact) { if (secondContact) m_relationship.setSecond(secondContact->contact()); } void QDeclarativeContactRelationship::setRelationshipType(const QVariant& relationshipType) { if (relationshipType.type() == QVariant::Int) { switch (relationshipType.toInt()) { case QDeclarativeContactRelationship::HasMember: m_relationship.setRelationshipType(QContactRelationship::HasMember()); break; case QDeclarativeContactRelationship::Aggregates: m_relationship.setRelationshipType(QContactRelationship::Aggregates()); break; case QDeclarativeContactRelationship::IsSameAs: m_relationship.setRelationshipType(QContactRelationship::IsSameAs()); break; case QDeclarativeContactRelationship::HasAssistant: m_relationship.setRelationshipType(QContactRelationship::HasAssistant()); break; case QDeclarativeContactRelationship::HasManager: m_relationship.setRelationshipType(QContactRelationship::HasManager()); break; case QDeclarativeContactRelationship::HasSpouse: m_relationship.setRelationshipType(QContactRelationship::HasSpouse()); break; default: //unknown type qmlInfo(this) << tr("unknown relationship type:") << relationshipType; break; } } else { m_relationship.setRelationshipType(relationshipType.toString()); } } QContactRelationship QDeclarativeContactRelationship::relationship() const { return m_relationship; } void QDeclarativeContactRelationship::setRelationship(const QContactRelationship& relationship) { m_relationship = relationship; emit valueChanged(); } #include "moc_qdeclarativecontactrelationship_p.cpp" QT_END_NAMESPACE src/imports/contacts/qdeclarativecontactrelationship_p.h000066400000000000000000000071031233466112000243110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTRELATIONSHIP_P_H #define QDECLARATIVECONTACTRELATIONSHIP_P_H #include #include #include "qdeclarativecontact_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactRelationship : public QObject { Q_OBJECT Q_PROPERTY(QDeclarativeContact* first READ first WRITE setFirst NOTIFY valueChanged) Q_PROPERTY(QDeclarativeContact* second READ second WRITE setSecond NOTIFY valueChanged) Q_PROPERTY(QVariant type READ relationshipType WRITE setRelationshipType NOTIFY valueChanged) Q_ENUMS(RelationshipRole) Q_ENUMS(RelationshipType) public: enum RelationshipRole { First = QContactRelationship::First, Second = QContactRelationship::Second, Either = QContactRelationship::Either }; enum RelationshipType { Unknown = 0, HasMember, Aggregates, IsSameAs, HasAssistant, HasManager, HasSpouse }; QDeclarativeContactRelationship(QObject* parent = 0); QDeclarativeContact* first() const; QDeclarativeContact* second() const; QVariant relationshipType() const; void setFirst( QDeclarativeContact* firstContact); void setSecond( QDeclarativeContact* secondContact); void setRelationshipType(const QVariant& relationshipType); QContactRelationship relationship() const; void setRelationship(const QContactRelationship& relationship); signals: void valueChanged(); private: QContactRelationship m_relationship; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactRelationship) #endif // QDECLARATIVECONTACTRELATIONSHIP_P_H src/imports/contacts/qdeclarativecontactrelationshipmodel.cpp000066400000000000000000000364351233466112000253600ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativecontactrelationshipmodel_p.h" #include #include #include #include QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE /*! \qmltype RelationshipModel \instantiates QDeclarativeContactRelationshipModel \brief The RelationshipModel provides a model of contact relationships from the contacts store. \ingroup qml-contacts-main \inqmlmodule QtContacts 5.0 This element is part of the \b{QtContacts} module. The contents of the model can be specified with \l participantId, \l role and \l relationshipType properties. Whether the model is automatically updated when the store or filter changes, can be controlled with \l RelationshipModel::autoUpdate property. There are two ways of accessing the relationship data: through model by using views and delegates, or alternatively via \l relationships list property. At the moment only data role provided by the model is \c relationship (\l Relationship). Through that one can access any data provided by the Relationship element. \sa Relationship, {QContactRelationship} */ class QDeclarativeContactRelationshipModelPrivate { public: QDeclarativeContactRelationshipModelPrivate() : m_manager(0) , m_participant(0) , m_role(QDeclarativeContactRelationship::Either) { } ~QDeclarativeContactRelationshipModelPrivate() { delete m_manager; } QContactManager *m_manager; QDeclarativeContactRelationship m_relationshipTypeHolder; QDeclarativeContact* m_participant; QDeclarativeContactRelationship::RelationshipRole m_role; QList m_relationships; QList m_declarativeRelationships; }; QDeclarativeContactRelationshipModel::QDeclarativeContactRelationshipModel(QObject *parent) : QAbstractListModel(parent) , d(new QDeclarativeContactRelationshipModelPrivate) { QHash roleNames; roleNames = QAbstractItemModel::roleNames(); roleNames.insert(RelationshipRole, "relationship"); setRoleNames(roleNames); connect(this, SIGNAL(managerChanged()), SLOT(fetchAgain())); connect(this, SIGNAL(participantChanged()), SLOT(fetchAgain())); connect(this, SIGNAL(relationshipTypeChanged()), SLOT(fetchAgain())); connect(this, SIGNAL(roleChanged()), SLOT(fetchAgain())); } QDeclarativeContactRelationshipModel::~QDeclarativeContactRelationshipModel() { delete d; } /*! \qmlproperty string RelationshipModel::manager This property holds the manager uri of the contact backend engine. */ QString QDeclarativeContactRelationshipModel::manager() const { if (d->m_manager) return d->m_manager->managerName(); return QString(); } /*! \qmlproperty string RelationshipModel::error This property holds the latest error code returned by the contact manager. This property is read only. */ QString QDeclarativeContactRelationshipModel::error() const { switch (d->m_manager->error()) { case QContactManager::DoesNotExistError: return QStringLiteral("DoesNotExist"); case QContactManager::AlreadyExistsError: return QStringLiteral("AlreadyExists"); case QContactManager::InvalidDetailError: return QStringLiteral("InvalidDetail"); case QContactManager::InvalidRelationshipError: return QStringLiteral("InvalidRelationship"); case QContactManager::LockedError: return QStringLiteral("LockedError"); case QContactManager::DetailAccessError: return QStringLiteral("DetailAccessError"); case QContactManager::PermissionsError: return QStringLiteral("PermissionsError"); case QContactManager::OutOfMemoryError: return QStringLiteral("OutOfMemory"); case QContactManager::NotSupportedError: return QStringLiteral("NotSupported"); case QContactManager::BadArgumentError: return QStringLiteral("BadArgument"); case QContactManager::UnspecifiedError: return QStringLiteral("UnspecifiedError"); case QContactManager::VersionMismatchError: return QStringLiteral("VersionMismatch"); case QContactManager::LimitReachedError: return QStringLiteral("LimitReached"); case QContactManager::InvalidContactTypeError: return QStringLiteral("InvalidContactType"); default: break; } return QStringLiteral("NoError"); } void QDeclarativeContactRelationshipModel::setManager(const QString& manager) { if (d->m_manager == 0 || manager != d->m_manager->managerName() ) { d->m_manager = new QContactManager(manager,QMap(), this); connect(d->m_manager,SIGNAL(relationshipsAdded(QList)), this, SLOT(fetchAgain())); connect(d->m_manager,SIGNAL(relationshipsRemoved(QList)), this, SLOT(fetchAgain())); emit managerChanged(); } } /*! \qmlproperty int RelationshipModel::participantId This property holds the participant which the list of relationships returned by RelationshipModel should contain. \sa RelationshipFilter::relatedContactId \sa RelationshipModel::role */ QDeclarativeContact* QDeclarativeContactRelationshipModel::participant() const { return d->m_participant; } void QDeclarativeContactRelationshipModel::setParticipant(QDeclarativeContact* participant) { if (d->m_participant != participant) { d->m_participant = participant; emit participantChanged(); } } /*! \qmlproperty variant RelationshipModel::relationshipType This property holds the relationship type which the list of relationships returned by RelationshipModel should contain. \sa Relationship::type */ QVariant QDeclarativeContactRelationshipModel::relationshipType() const { return d->m_relationshipTypeHolder.relationshipType(); } void QDeclarativeContactRelationshipModel::setRelationshipType(const QVariant& type) { if (type != relationshipType()) { d->m_relationshipTypeHolder.setRelationshipType(type); emit relationshipTypeChanged(); } } /*! \qmlproperty enumeration RelationshipModel::role This property holds the relationship role which the list of relationships returned by RelationshipModel should contain. \sa RelationshipFilter::relatedContactRole */ QDeclarativeContactRelationship::RelationshipRole QDeclarativeContactRelationshipModel::role() const { return d->m_role; } void QDeclarativeContactRelationshipModel::setRole(QDeclarativeContactRelationship::RelationshipRole role) { if (d->m_role != role) { d->m_role = role; emit roleChanged(); } } /*! \qmlproperty bool RelationshipModel::autoUpdate This property indicates whether or not the relationship model should be updated automatically, default value is true. */ bool QDeclarativeContactRelationshipModel::autoUpdate() const { //TODO return true; } void QDeclarativeContactRelationshipModel::setAutoUpdate(bool autoUpdate) { Q_UNUSED(autoUpdate); //TODO } /*! \qmlproperty list RelationshipModel::relationships This property holds a list of relationships. \sa Relationship */ QQmlListProperty QDeclarativeContactRelationshipModel::relationships() { return QQmlListProperty(this, d->m_declarativeRelationships); } int QDeclarativeContactRelationshipModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return d->m_declarativeRelationships.count(); } QVariant QDeclarativeContactRelationshipModel::data(const QModelIndex &index, int role) const { QDeclarativeContactRelationship* dcr = d->m_declarativeRelationships.value(index.row()); if (role == RelationshipRole) { return QVariant::fromValue(dcr); } else if (role == Qt::DisplayRole) { return QString(QStringLiteral("%1 %2 %3")).arg(dcr->relationship().first().id().toString()).arg(dcr->relationship().relationshipType()).arg(dcr->relationship().second().id().toString()); } return QVariant(); } void QDeclarativeContactRelationshipModel::fetchAgain() { if (d->m_manager) { QContactRelationshipFetchRequest* req = new QContactRelationshipFetchRequest(this); req->setManager(d->m_manager); if (d->m_participant) { QContact contact (d->m_participant->contact()); if (d->m_role == QDeclarativeContactRelationship::First || d->m_role == QDeclarativeContactRelationship::Either) req->setFirst(contact); if (d->m_role == QDeclarativeContactRelationship::Second || d->m_role == QDeclarativeContactRelationship::Either) req->setSecond(contact); req->setRelationshipType(d->m_relationshipTypeHolder.relationship().relationshipType()); connect(req,SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(requestUpdated())); req->start(); } } } /*! \qmlmethod RelationshipModel::addRelationship(relationship) Addes the given \a relationship to the backend store. */ void QDeclarativeContactRelationshipModel::addRelationship(QDeclarativeContactRelationship* dcr) { if (dcr) { QContactRelationship cr = dcr->relationship(); QContactRelationshipSaveRequest* req = new QContactRelationshipSaveRequest(this); req->setManager(d->m_manager); req->setRelationship(cr); connect(req, SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(relationshipsSaved())); req->start(); } } /*! \qmlmethod RelationshipModel::removeRelationship(relationship) Removes the given \a relationship from the backend store. */ void QDeclarativeContactRelationshipModel::removeRelationship(QDeclarativeContactRelationship* dcr) { if (dcr) { QContactRelationship cr = dcr->relationship(); QContactRelationshipRemoveRequest* req = new QContactRelationshipRemoveRequest(this); req->setManager(d->m_manager); req->setRelationship(cr); connect(req,SIGNAL(stateChanged(QContactAbstractRequest::State)), this, SLOT(relationshipsRemoved())); req->start(); } } void QDeclarativeContactRelationshipModel::requestUpdated() { QContactRelationshipFetchRequest* req = qobject_cast(sender()); Q_ASSERT(req); if (req->isFinished() && req->error() == QContactManager::NoError) { QList relationships = req->relationships(); reset(); beginInsertRows(QModelIndex(), 0, relationships.count()); foreach(QDeclarativeContactRelationship* dcr, d->m_declarativeRelationships) { dcr->deleteLater(); } d->m_declarativeRelationships.clear(); d->m_relationships.clear(); foreach (const QContactRelationship& cr, relationships) { QDeclarativeContactRelationship* dcr = new QDeclarativeContactRelationship(this); dcr->setRelationship(cr); d->m_declarativeRelationships.append(dcr); d->m_relationships.append(cr); } endInsertRows(); req->deleteLater(); emit relationshipsChanged(); } } void QDeclarativeContactRelationshipModel::relationshipsSaved() { QContactRelationshipSaveRequest* req = qobject_cast(sender()); Q_ASSERT(req); if (req->isFinished()) { QList rs = req->relationships(); QList errorIds = req->errorMap().keys(); for( int i = 0; i < rs.count(); i++) { if (!errorIds.contains(i)) { //saved QContactRelationship r = rs.at(i); if (!d->m_relationships.contains(r)) { //new relationship saved QDeclarativeContactRelationship* dcr = new QDeclarativeContactRelationship(this); dcr->setRelationship(r); beginInsertRows(QModelIndex(), d->m_declarativeRelationships.count(), d->m_declarativeRelationships.count()); d->m_declarativeRelationships.append(dcr); d->m_relationships.append(r); endInsertRows(); } } } req->deleteLater(); } } void QDeclarativeContactRelationshipModel::relationshipsRemoved() { QContactRelationshipRemoveRequest* req = qobject_cast(sender()); Q_ASSERT(req); if (req->isFinished()) { QList rs = req->relationships(); QList errorIds = req->errorMap().keys(); for( int i = 0; i < rs.count(); i++) { if (!errorIds.contains(i)) { int row = 0; QContactRelationship r = rs.at(i); for (; row < d->m_relationships.count(); row++) { if (d->m_relationships.at(row) == r) break; } if (row < d->m_relationships.count()) { beginRemoveRows(QModelIndex(), row, row); d->m_declarativeRelationships.removeAt(row); d->m_relationships.removeAt(row); endRemoveRows(); } else { //impossible? qmlInfo(this) << tr("this relationship '") << row << tr("' was already removed!"); } } } req->deleteLater(); } } #include "moc_qdeclarativecontactrelationshipmodel_p.cpp" QT_END_NAMESPACE src/imports/contacts/qdeclarativecontactrelationshipmodel_p.h000066400000000000000000000107721233466112000253400ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTRELATIONSHIPMODEL_P_H #define QDECLARATIVECONTACTRELATIONSHIPMODEL_P_H #include #include #include "qdeclarativecontactrelationship_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactRelationshipModelPrivate; class QDeclarativeContactRelationshipModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QString manager READ manager WRITE setManager NOTIFY managerChanged) Q_PROPERTY(bool autoUpdate READ autoUpdate WRITE setAutoUpdate NOTIFY autoUpdateChanged) Q_PROPERTY(QDeclarativeContact* participant READ participant WRITE setParticipant NOTIFY participantChanged) Q_PROPERTY(QVariant relationshipType READ relationshipType WRITE setRelationshipType NOTIFY relationshipTypeChanged) Q_PROPERTY(QDeclarativeContactRelationship::RelationshipRole role READ role WRITE setRole NOTIFY roleChanged) Q_PROPERTY(QQmlListProperty relationships READ relationships NOTIFY relationshipsChanged) Q_PROPERTY(QString error READ error) public: QDeclarativeContactRelationshipModel(QObject *parent = 0); ~QDeclarativeContactRelationshipModel(); enum { RelationshipRole = Qt::UserRole + 500 }; QString manager() const; void setManager(const QString& manager); bool autoUpdate() const; void setAutoUpdate(bool autoUpdate); QString error() const; QDeclarativeContact* participant() const; void setParticipant(QDeclarativeContact* participant); QVariant relationshipType() const; void setRelationshipType(const QVariant& type); QDeclarativeContactRelationship::RelationshipRole role() const; void setRole(QDeclarativeContactRelationship::RelationshipRole role); QQmlListProperty relationships(); int rowCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; Q_INVOKABLE void removeRelationship(QDeclarativeContactRelationship* dcr); Q_INVOKABLE void addRelationship(QDeclarativeContactRelationship* dcr); signals: void managerChanged(); void participantChanged(); void relationshipTypeChanged(); void roleChanged(); void relationshipsChanged(); void autoUpdateChanged(); private slots: void fetchAgain(); void requestUpdated(); void relationshipsSaved(); void relationshipsRemoved(); private: QDeclarativeContactRelationshipModelPrivate* d; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactRelationshipModel) #endif // QDECLARATIVECONTACTRELATIONSHIPMODEL_P_H src/imports/contacts/qdeclarativecontactsortorder.cpp000066400000000000000000000136531233466112000236560ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativecontactsortorder_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE /*! \qmltype SortOrder \instantiates QDeclarativeContactSortOrder \brief The SortOrder element defines how a list of contacts should be ordered according to some criteria. \ingroup qml-contacts-main \inqmlmodule QtContacts 5.0 This element is part of the \b{QtContacts} module. \sa QContactSortOrder \sa ContactModel */ QDeclarativeContactSortOrder::QDeclarativeContactSortOrder(QObject* parent) :QObject(parent) { } /*! \qmlproperty enumeration SortOrder::detail This property holds the detail type of the details which will be inspected to perform sorting. \sa ContactDetail::type */ void QDeclarativeContactSortOrder::setDetail(const int detailType) { if (detailType != static_cast(m_sortOrder.detailType())) { m_sortOrder.setDetailType(static_cast(detailType), m_sortOrder.detailField()); emit sortOrderChanged(); } } int QDeclarativeContactSortOrder::detail() const { return static_cast(m_sortOrder.detailType()); } /*! \qmlproperty int SortOrder::field This property holds the detail field type of the details which will be inspected to perform sorting. For each detail elements, there are predefined field types. */ void QDeclarativeContactSortOrder::setField(const int fieldType) { if (fieldType != m_sortOrder.detailField()) { m_sortOrder.setDetailType(m_sortOrder.detailType(), fieldType); emit sortOrderChanged(); } } int QDeclarativeContactSortOrder::field() const { return m_sortOrder.detailField(); } /*! \qmlproperty enumeration SortOrder::blankPolicy This property enumerates the ways in which the sort order interprets blanks when sorting contacts. \list \li SortOrder.BlanksFirst - Considers blank values to evaluate to less than all other values in comparisons. \li SortOrder.BlanksLast - Considers blank values to evaluate to greater than all other values in comparisons. \endlist */ QDeclarativeContactSortOrder::BlankPolicy QDeclarativeContactSortOrder::blankPolicy() const { return static_cast(m_sortOrder.blankPolicy()); } void QDeclarativeContactSortOrder::setBlankPolicy(QDeclarativeContactSortOrder::BlankPolicy blankPolicy) { if (blankPolicy != static_cast(m_sortOrder.blankPolicy())) { m_sortOrder.setBlankPolicy(static_cast(blankPolicy)); emit sortOrderChanged(); } } /*! \qmlproperty enumeration SortOrder::direction This property holds the direction of the sort order, the value can be one of: \list \li Qt.AscendingOrder - (default) \li Qt.DescendingOrder \endlist */ Qt::SortOrder QDeclarativeContactSortOrder::direction() const { return m_sortOrder.direction(); } void QDeclarativeContactSortOrder::setDirection(Qt::SortOrder direction) { if (direction != m_sortOrder.direction()) { m_sortOrder.setDirection(direction); emit sortOrderChanged(); } } /*! \qmlproperty enumeration SortOrder::caseSensitivity This property holds the case sensitivity of the sort order, the value can be one of: \list \li Qt.CaseInsensitive \li Qt.CaseSensitive - (default) \endlist */ Qt::CaseSensitivity QDeclarativeContactSortOrder::caseSensitivity() const { return m_sortOrder.caseSensitivity(); } void QDeclarativeContactSortOrder::setCaseSensitivity(Qt::CaseSensitivity sensitivity) { if (sensitivity != m_sortOrder.caseSensitivity()) { m_sortOrder.setCaseSensitivity(sensitivity); emit sortOrderChanged(); } } QContactSortOrder QDeclarativeContactSortOrder::sortOrder() { return m_sortOrder; } void QDeclarativeContactSortOrder::setSortOrder(const QContactSortOrder& sortOrder) { m_sortOrder = sortOrder; emit sortOrderChanged(); } #include "moc_qdeclarativecontactsortorder_p.cpp" QT_END_NAMESPACE src/imports/contacts/qdeclarativecontactsortorder_p.h000066400000000000000000000071441233466112000236400ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVECONTACTSORTORDER_P_H #define QDECLARATIVECONTACTSORTORDER_P_H #include #include #include "qdeclarativecontactdetail_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeContactSortOrder : public QObject { Q_OBJECT Q_PROPERTY(int detail READ detail WRITE setDetail NOTIFY sortOrderChanged) Q_PROPERTY(int field READ field WRITE setField NOTIFY sortOrderChanged) Q_PROPERTY(Qt::SortOrder direction READ direction WRITE setDirection NOTIFY sortOrderChanged) Q_PROPERTY(BlankPolicy blankPolicy READ blankPolicy WRITE setBlankPolicy NOTIFY sortOrderChanged) Q_PROPERTY(Qt::CaseSensitivity caseSensitivity READ caseSensitivity WRITE setCaseSensitivity NOTIFY sortOrderChanged) Q_ENUMS(BlankPolicy) public: enum BlankPolicy { BlanksFirst = QContactSortOrder::BlanksFirst, BlanksLast = QContactSortOrder::BlanksLast }; QDeclarativeContactSortOrder(QObject* parent = 0); void setDetail(const int detailType); int detail() const; void setField(const int fieldType); int field() const; BlankPolicy blankPolicy() const; void setBlankPolicy(BlankPolicy blankPolicy); Qt::SortOrder direction() const; void setDirection(Qt::SortOrder direction); Qt::CaseSensitivity caseSensitivity() const; void setCaseSensitivity(Qt::CaseSensitivity sensitivity); QContactSortOrder sortOrder(); void setSortOrder(const QContactSortOrder& sortOrder); signals: void sortOrderChanged(); private: QContactSortOrder m_sortOrder; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeContactSortOrder) #endif // QDECLARATIVECONTACTSORTORDER_P_H src/imports/contacts/qdeclarativeglobal_p.h000066400000000000000000000072741233466112000215050ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVEGLOBAL_H #define QDECLARATIVEGLOBAL_H #include QT_BEGIN_NAMESPACE QT_MODULE(Declarative) #define DEFINE_BOOL_CONFIG_OPTION(name, var) \ static bool name() \ { \ static enum { Yes, No, Unknown } status = Unknown; \ if (status == Unknown) { \ QByteArray v = qgetenv(#var); \ bool value = !v.isEmpty() && v != "0" && v != "false"; \ if (value) status = Yes; \ else status = No; \ } \ return status == Yes; \ } struct QQmlGraphics_DerivedObject : public QObject { void setParent_noEvent(QObject *parent) { bool sce = d_ptr->sendChildEvents; d_ptr->sendChildEvents = false; setParent(parent); d_ptr->sendChildEvents = sce; } }; /*! Returns true if the case of \a fileName is equivalent to the file case of \a fileName on disk, and false otherwise. This is used to ensure that the behavior of QML on a case-insensitive file system is the same as on a case-sensitive file system. This function performs a "best effort" attempt to determine the real case of the file. It may have false positives (say the case is correct when it isn't), but it should never have a false negative (say the case is incorrect when it is correct). */ bool QQml_isFileCaseCorrect(const QString &fileName); /*! Makes the \a object a child of \a parent. Note that when using this method, neither \a parent nor the object's previous parent (if it had one) will receive ChildRemoved or ChildAdded events. */ inline void QQml_setParent_noEvent(QObject *object, QObject *parent) { static_cast(object)->setParent_noEvent(parent); } QT_END_NAMESPACE #endif // QDECLARATIVEGLOBAL_H src/imports/contacts/qmldir000066400000000000000000000001101233466112000163610ustar00rootroot00000000000000module QtContacts plugin declarative_contacts typeinfo plugins.qmltypes src/imports/imports.pro000066400000000000000000000000611233466112000155540ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS += contacts organizer src/imports/organizer/000077500000000000000000000000001233466112000153405ustar00rootroot00000000000000src/imports/organizer/organizer.json000066400000000000000000000000031233466112000202240ustar00rootroot00000000000000{} src/imports/organizer/organizer.pro000066400000000000000000000016241233466112000200650ustar00rootroot00000000000000QT += qml organizer versit versitorganizer # Input HEADERS += qdeclarativeorganizermodel_p.h \ qdeclarativeorganizeritem_p.h \ qdeclarativeorganizeritemdetail_p.h \ qdeclarativeorganizeritemfilter_p.h \ qdeclarativeorganizerrecurrencerule_p.h \ qdeclarativeorganizercollection_p.h \ qdeclarativeorganizeritemsortorder_p.h \ qdeclarativeorganizeritemfetchhint_p.h SOURCES += plugin.cpp \ qdeclarativeorganizeritem.cpp \ qdeclarativeorganizeritemdetail.cpp \ qdeclarativeorganizermodel.cpp \ qdeclarativeorganizeritemfilter.cpp \ qdeclarativeorganizercollection.cpp \ qdeclarativeorganizeritemsortorder.cpp \ qdeclarativeorganizerrecurrencerule.cpp \ qdeclarativeorganizeritemfetchhint.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 load(qml_plugin) src/imports/organizer/plugin.cpp000066400000000000000000000167431233466112000173550ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include "qdeclarativeorganizercollection_p.h" #include "qdeclarativeorganizeritem_p.h" #include "qdeclarativeorganizeritemdetail_p.h" #include "qdeclarativeorganizeritemfetchhint_p.h" #include "qdeclarativeorganizeritemfilter_p.h" #include "qdeclarativeorganizeritemsortorder_p.h" #include "qdeclarativeorganizermodel_p.h" #include "qdeclarativeorganizerrecurrencerule_p.h" QT_BEGIN_NAMESPACE class QOrganizerQmlPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface" FILE "organizer.json") public: void registerTypes(const char *uri) { Q_ASSERT(uri == QStringLiteral("QtOrganizer")); int major = 5; int minor = 0; qRegisterMetaType("QOrganizerAbstractRequest::State"); qRegisterMetaType("QOrganizerItemId"); qRegisterMetaType >("QList"); qRegisterMetaType("QOrganizerCollectionId"); qRegisterMetaType >("QList"); qmlRegisterType(uri, major, minor, "OrganizerModel"); qmlRegisterType(uri, major, minor, "RecurrenceRule"); qmlRegisterType(uri, major, minor, "FetchHint"); qmlRegisterType(uri, major, minor, "SortOrder"); qmlRegisterType(uri, major, minor, "Collection"); //items qmlRegisterType(uri, major, minor, "OrganizerItem"); qmlRegisterType(uri, major, minor, "Event"); qmlRegisterType(uri, major, minor, "EventOccurrence"); qmlRegisterType(uri, major, minor, "Journal"); qmlRegisterType(uri, major, minor, "Note"); qmlRegisterType(uri, major, minor, "Todo"); qmlRegisterType(uri, major, minor, "TodoOccurrence"); //details qmlRegisterType(uri, major, minor, "Detail"); qmlRegisterType(uri, major, minor, "EventTime"); qmlRegisterType(uri, major, minor, "Comment"); qmlRegisterType(uri, major, minor, "Description"); qmlRegisterType(uri, major, minor, "DisplayLabel"); qmlRegisterType(uri, major, minor, "Guid"); qmlRegisterType(uri, major, minor, "Location"); qmlRegisterType(uri, major, minor, "Parent"); qmlRegisterType(uri, major, minor, "Priority"); qmlRegisterType(uri, major, minor, "Recurrence"); qmlRegisterType(uri, major, minor, "Tag"); qmlRegisterType(uri, major, minor, "Timestamp"); qmlRegisterType(uri, major, minor, "Type"); qmlRegisterType(uri, major, minor, "JournalTime"); qmlRegisterType(uri, major, minor, "TodoProgress"); qmlRegisterType(uri, major, minor, "TodoTime"); qmlRegisterType(uri, major, minor, "ExtendedDetail"); qmlRegisterType(uri, major, minor, "EventAttendee"); qmlRegisterType(uri, major, minor, "EventRsvp"); qmlRegisterType(uri, major, minor, "Classification"); qmlRegisterType(uri, major, minor, "Version"); qmlRegisterType(uri, major, minor, "Reminder"); qmlRegisterType(uri, major, minor, "AudibleReminder"); qmlRegisterType(uri, major, minor, "EmailReminder"); qmlRegisterType(uri, major, minor, "VisualReminder"); //filters qmlRegisterType(uri, major, minor, "Filter"); qmlRegisterType(uri, major, minor, "CollectionFilter"); qmlRegisterType(uri, major, minor, "DetailFilter"); qmlRegisterType(uri, major, minor, "DetailFieldFilter"); qmlRegisterType(uri, major, minor, "DetailRangeFilter"); qmlRegisterType(uri, major, minor, "IdFilter"); qmlRegisterType(uri, major, minor, "IntersectionFilter"); qmlRegisterType(uri, major, minor, "UnionFilter"); qmlRegisterType(uri, major, minor, "InvalidFilter"); qmlRegisterType(); } }; #include "plugin.moc" QT_END_NAMESPACE src/imports/organizer/plugins.qmltypes000066400000000000000000001237171233466112000206340ustar00rootroot00000000000000import QtQuick.tooling 1.1 // This file describes the plugin-supplied types contained in the library. // It is used for QML tooling purposes only. // // This file was auto-generated with the command 'qmlplugindump -notrelocatable QtOrganizer 5.0'. Module { Component { name: "QtOrganizer::QDeclarativeOrganizerCollection" prototype: "QObject" exports: ["QtOrganizer/Collection 5.0"] exportMetaObjectRevisions: [0] Enum { name: "MetaDataKey" values: { "KeyName": 0, "KeyDescription": 1, "KeyColor": 2, "KeyImage": 3, "KeyExtended": 4 } } Property { name: "collectionId"; type: "string" } Property { name: "name"; type: "string" } Property { name: "description"; type: "string" } Property { name: "color"; type: "QColor" } Property { name: "image"; type: "QUrl" } Signal { name: "valueChanged" } Method { name: "setMetaData" Parameter { name: "key"; type: "QOrganizerCollection::MetaDataKey" } Parameter { name: "value"; type: "QVariant" } } Method { name: "metaData" type: "QVariant" Parameter { name: "key"; type: "QOrganizerCollection::MetaDataKey" } } Method { name: "setExtendedMetaData" Parameter { name: "key"; type: "string" } Parameter { name: "value"; type: "QVariant" } } Method { name: "extendedMetaData" type: "QVariant" Parameter { name: "key"; type: "string" } } } Component { name: "QtOrganizer::QDeclarativeOrganizerEvent" defaultProperty: "itemDetails" prototype: "QtOrganizer::QDeclarativeOrganizerItem" exports: ["QtOrganizer/Event 5.0"] exportMetaObjectRevisions: [0] Property { name: "allDay"; type: "bool" } Property { name: "startDateTime"; type: "QDateTime" } Property { name: "endDateTime"; type: "QDateTime" } Property { name: "priority"; type: "QDeclarativeOrganizerItemPriority::Priority" } Property { name: "recurrence" type: "QDeclarativeOrganizerItemRecurrence" isReadonly: true isPointer: true } Property { name: "location"; type: "string" } Property { name: "attendees" type: "QDeclarativeOrganizerEventAttendee" isList: true isReadonly: true } Signal { name: "valueChanged" } Method { name: "setDetail" Parameter { name: "detail"; type: "QDeclarativeOrganizerItemDetail"; isPointer: true } } Method { name: "removeDetail" Parameter { name: "detail"; type: "QDeclarativeOrganizerItemDetail"; isPointer: true } } Method { name: "clearDetails" } } Component { name: "QtOrganizer::QDeclarativeOrganizerEventAttendee" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/EventAttendee 5.0"] exportMetaObjectRevisions: [0] Enum { name: "EventAttendeeField" values: { "FieldName": 1901, "FieldEmailAddress": 1902, "FieldAddendeeId": 1903, "FieldParticipationStatus": 1904, "FieldParticipationRole": 1905 } } Enum { name: "ParticipationStatus" values: { "StatusUnknown": 0, "StatusAccepted": 1, "StatusDeclined": 2, "StatusTentative": 3, "StatusDelegated": 4, "StatusInProcess": 5, "StatusCompleted": 6 } } Enum { name: "ParticipationRole" values: { "RoleUnknown": 0, "RoleOrganizer": 1, "RoleChairperson": 2, "RoleHost": 3, "RoleRequiredParticipant": 4, "RoleOptionalParticipant": 5, "RoleNonParticipant": 6 } } Property { name: "name"; type: "string" } Property { name: "emailAddress"; type: "string" } Property { name: "attendeeId"; type: "string" } Property { name: "participationStatus"; type: "ParticipationStatus" } Property { name: "participationRole"; type: "ParticipationRole" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerEventOccurrence" defaultProperty: "itemDetails" prototype: "QtOrganizer::QDeclarativeOrganizerItem" exports: ["QtOrganizer/EventOccurrence 5.0"] exportMetaObjectRevisions: [0] Property { name: "allDay"; type: "bool" } Property { name: "originalDate"; type: "QDateTime" } Property { name: "startDateTime"; type: "QDateTime" } Property { name: "endDateTime"; type: "QDateTime" } Property { name: "priority"; type: "QDeclarativeOrganizerItemPriority::Priority" } Property { name: "location"; type: "string" } Property { name: "parentId"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerEventRsvp" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/EventRsvp 5.0"] exportMetaObjectRevisions: [0] Enum { name: "EventRsvpField" values: { "FieldParticipationStatus": 2001, "FieldParticipationRole": 2002, "FieldResponseRequirement": 2003, "FieldResponseDeadline": 2004, "FieldResponseDate": 2005, "FieldOrganizerName": 2006, "FieldOrganizerEmail": 2007 } } Enum { name: "ResponseRequirement" values: { "ResponseNotRequired": 0, "ResponseRequired": 1 } } Property { name: "participationStatus" type: "QDeclarativeOrganizerEventAttendee::ParticipationStatus" } Property { name: "participationRole" type: "QDeclarativeOrganizerEventAttendee::ParticipationRole" } Property { name: "responseRequirement"; type: "ResponseRequirement" } Property { name: "responseDeadline"; type: "QDateTime" } Property { name: "responseDate"; type: "QDateTime" } Property { name: "organizerName"; type: "string" } Property { name: "organizerEmail"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerEventTime" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/EventTime 5.0"] exportMetaObjectRevisions: [0] Enum { name: "EventTimeField" values: { "FieldStartDateTime": 2101, "FieldEndDateTime": 2102, "FieldAllDay": 2103 } } Property { name: "allDay"; type: "bool" } Property { name: "startDateTime"; type: "QDateTime" } Property { name: "endDateTime"; type: "QDateTime" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItem" defaultProperty: "itemDetails" prototype: "QObject" exports: ["QtOrganizer/OrganizerItem 5.0"] exportMetaObjectRevisions: [0] Property { name: "modified"; type: "bool"; isReadonly: true } Property { name: "itemType"; type: "QDeclarativeOrganizerItemType::ItemType"; isReadonly: true } Property { name: "itemDetails" type: "QDeclarativeOrganizerItemDetail" isList: true isReadonly: true } Property { name: "itemId"; type: "string"; isReadonly: true } Property { name: "manager"; type: "string"; isReadonly: true } Property { name: "collectionId"; type: "string" } Property { name: "description"; type: "string" } Property { name: "displayLabel"; type: "string" } Property { name: "guid"; type: "string" } Signal { name: "itemChanged" } Method { name: "detail" type: "QDeclarativeOrganizerItemDetail*" Parameter { name: "type"; type: "int" } } Method { name: "details" type: "QVariantList" Parameter { name: "type"; type: "int" } } Method { name: "setDetail" Parameter { name: "detail"; type: "QDeclarativeOrganizerItemDetail"; isPointer: true } } Method { name: "removeDetail" Parameter { name: "detail"; type: "QDeclarativeOrganizerItemDetail"; isPointer: true } } Method { name: "clearDetails" } Method { name: "save" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemAudibleReminder" prototype: "QtOrganizer::QDeclarativeOrganizerItemReminder" exports: ["QtOrganizer/AudibleReminder 5.0"] exportMetaObjectRevisions: [0] Enum { name: "AudibleReminderField" values: { "FieldDataUrl": 1501 } } Property { name: "dataUrl"; type: "QUrl" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemClassification" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/Classification 5.0"] exportMetaObjectRevisions: [0] Enum { name: "Field" values: { "FieldClassification": 101 } } Enum { name: "AccessClassification" values: { "AccessPublic": 0, "AccessConfidential": 1, "AccessPrivate": 2 } } Property { name: "classification"; type: "AccessClassification" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemCollectionFilter" prototype: "QtOrganizer::QDeclarativeOrganizerItemFilter" exports: ["QtOrganizer/CollectionFilter 5.0"] exportMetaObjectRevisions: [0] Property { name: "ids"; type: "QStringList" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemComment" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/Comment 5.0"] exportMetaObjectRevisions: [0] Enum { name: "CommentField" values: { "FieldComment": 201 } } Property { name: "comment"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemCompoundFilter" defaultProperty: "filters" prototype: "QtOrganizer::QDeclarativeOrganizerItemFilter" Property { name: "filters" type: "QDeclarativeOrganizerItemFilter" isList: true isReadonly: true } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemDescription" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/Description 5.0"] exportMetaObjectRevisions: [0] Enum { name: "DescriptionField" values: { "FieldDescription": 301 } } Property { name: "description"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemDetail" prototype: "QObject" exports: ["QtOrganizer/Detail 5.0"] exportMetaObjectRevisions: [0] Enum { name: "DetailType" values: { "Undefined": 0, "Classification": 100, "Comment": 200, "Description": 300, "DisplayLabel": 400, "ItemType": 500, "Guid": 600, "Location": 700, "Parent": 800, "Priority": 900, "Recurrence": 1000, "Tag": 1100, "Timestamp": 1200, "Version": 1300, "Reminder": 1400, "AudibleReminder": 1500, "EmailReminder": 1600, "VisualReminder": 1700, "ExtendedDetail": 1800, "EventAttendee": 1900, "EventRsvp": 2000, "EventTime": 2100, "JournalTime": 2200, "TodoTime": 2400, "TodoProgress": 2300 } } Property { name: "type"; type: "DetailType"; isReadonly: true } Signal { name: "detailChanged" } Method { name: "value" type: "QVariant" Parameter { name: "key"; type: "int" } } Method { name: "setValue" type: "bool" Parameter { name: "key"; type: "int" } Parameter { name: "value"; type: "QVariant" } } Method { name: "removeValue" type: "bool" Parameter { name: "key"; type: "int" } } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemDetailFieldFilter" prototype: "QtOrganizer::QDeclarativeOrganizerItemFilter" exports: ["QtOrganizer/DetailFieldFilter 5.0"] exportMetaObjectRevisions: [0] Property { name: "value"; type: "QVariant" } Property { name: "matchFlags"; type: "QDeclarativeOrganizerItemFilter::MatchFlags" } Property { name: "field"; type: "int" } Property { name: "detail"; type: "QDeclarativeOrganizerItemDetail::DetailType" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemDetailFilter" prototype: "QtOrganizer::QDeclarativeOrganizerItemFilter" exports: ["QtOrganizer/DetailFilter 5.0"] exportMetaObjectRevisions: [0] Property { name: "detail"; type: "QDeclarativeOrganizerItemDetail"; isPointer: true } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemDetailRangeFilter" prototype: "QtOrganizer::QDeclarativeOrganizerItemFilter" exports: ["QtOrganizer/DetailRangeFilter 5.0"] exportMetaObjectRevisions: [0] Enum { name: "RangeFlag" values: { "IncludeLower": 0, "IncludeUpper": 1, "ExcludeLower": 2, "ExcludeUpper": 0 } } Enum { name: "RangeFlags" values: { "IncludeLower": 0, "IncludeUpper": 1, "ExcludeLower": 2, "ExcludeUpper": 0 } } Property { name: "min"; type: "QVariant" } Property { name: "max"; type: "QVariant" } Property { name: "matchFlags"; type: "QDeclarativeOrganizerItemFilter::MatchFlags" } Property { name: "rangeFlags"; type: "RangeFlags" } Property { name: "detail"; type: "QDeclarativeOrganizerItemDetail::DetailType" } Property { name: "field"; type: "int" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemDisplayLabel" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/DisplayLabel 5.0"] exportMetaObjectRevisions: [0] Enum { name: "DisplayLabelField" values: { "FieldLabel": 401 } } Property { name: "label"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemEmailReminder" prototype: "QtOrganizer::QDeclarativeOrganizerItemReminder" exports: ["QtOrganizer/EmailReminder 5.0"] exportMetaObjectRevisions: [0] Enum { name: "EmailReminderField" values: { "FieldSubject": 1601, "FieldBody": 1602, "FieldRecipients": 1604, "FieldAttachments": 1603 } } Property { name: "body"; type: "string" } Property { name: "subject"; type: "string" } Property { name: "recipients"; type: "QStringList" } Property { name: "attachments"; type: "QVariantList" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemExtendedDetail" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/ExtendedDetail 5.0"] exportMetaObjectRevisions: [0] Enum { name: "ExtendedDetailField" values: { "FieldName": 1801, "FieldData": 1802 } } Property { name: "name"; type: "string" } Property { name: "data"; type: "QVariant" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemFetchHint" prototype: "QObject" exports: ["QtOrganizer/FetchHint 5.0"] exportMetaObjectRevisions: [0] Enum { name: "OptimizationHint" values: { "AllRequired": 0, "NoActionPreferences": 2, "NoBinaryBlobs": 4 } } Enum { name: "OptimizationHints" values: { "AllRequired": 0, "NoActionPreferences": 2, "NoBinaryBlobs": 4 } } Property { name: "optimizationHints"; type: "OptimizationHints" } Signal { name: "fetchHintChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemFilter" prototype: "QObject" exports: ["QtOrganizer/Filter 5.0"] exportMetaObjectRevisions: [0] Enum { name: "FilterType" values: { "DefaultFilter": 8, "InvalidFilter": 0, "IntersectionFilter": 4, "UnionFilter": 5, "CollectionFilter": 7, "DetailFilter": 1, "DetailFieldFilter": 2, "DetailRangeFilter": 3, "IdFilter": 6 } } Enum { name: "MatchFlags" values: { "MatchExactly": 0, "MatchContains": 1, "MatchStartsWith": 2, "MatchEndsWith": 3, "MatchFixedString": 8, "MatchCaseSensitive": 16 } } Property { name: "type"; type: "FilterType"; isReadonly: true } Signal { name: "filterChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemGuid" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/Guid 5.0"] exportMetaObjectRevisions: [0] Enum { name: "GuidField" values: { "FieldGuid": 601 } } Property { name: "guid"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemIdFilter" prototype: "QtOrganizer::QDeclarativeOrganizerItemFilter" exports: ["QtOrganizer/IdFilter 5.0"] exportMetaObjectRevisions: [0] Property { name: "ids"; type: "QStringList" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemIntersectionFilter" defaultProperty: "filters" prototype: "QtOrganizer::QDeclarativeOrganizerItemCompoundFilter" exports: ["QtOrganizer/IntersectionFilter 5.0"] exportMetaObjectRevisions: [0] } Component { name: "QtOrganizer::QDeclarativeOrganizerItemInvalidFilter" prototype: "QtOrganizer::QDeclarativeOrganizerItemFilter" exports: ["QtOrganizer/InvalidFilter 5.0"] exportMetaObjectRevisions: [0] } Component { name: "QtOrganizer::QDeclarativeOrganizerItemLocation" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/Location 5.0"] exportMetaObjectRevisions: [0] Enum { name: "LocationField" values: { "FieldLabel": 703, "FieldLatitude": 701, "FieldLongitude": 702 } } Property { name: "latitude"; type: "double" } Property { name: "longitude"; type: "double" } Property { name: "label"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemParent" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/Parent 5.0"] exportMetaObjectRevisions: [0] Enum { name: "ParentField" values: { "FieldParentId": 801, "FieldOriginalDate": 802 } } Property { name: "originalDate"; type: "QDateTime" } Property { name: "parentId"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemPriority" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/Priority 5.0"] exportMetaObjectRevisions: [0] Enum { name: "PriorityField" values: { "FieldPriority": 901 } } Enum { name: "Priority" values: { "Unknown": 0, "Highest": 1, "ExtremelyHigh": 2, "VeryHigh": 3, "High": 4, "Medium": 5, "Low": 6, "VeryLow": 7, "ExtremelyLow": 8, "Lowest": 9 } } Property { name: "priority"; type: "Priority" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemRecurrence" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/Recurrence 5.0"] exportMetaObjectRevisions: [0] Enum { name: "RecurrenceField" values: { "FieldRecurrenceRules": 1001, "FieldExceptionRules": 1002, "FieldRecurrenceDates": 1003, "FieldExceptionDates": 1004 } } Property { name: "recurrenceRules" type: "QDeclarativeOrganizerRecurrenceRule" isList: true isReadonly: true } Property { name: "exceptionRules" type: "QDeclarativeOrganizerRecurrenceRule" isList: true isReadonly: true } Property { name: "recurrenceDates"; type: "QVariantList" } Property { name: "exceptionDates"; type: "QVariantList" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemReminder" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/Reminder 5.0"] exportMetaObjectRevisions: [0] Enum { name: "ReminderField" values: { "FieldRepetitionCount": 1402, "FieldRepetitionDelay": 1403, "FieldSecondsBeforeStart": 1401 } } Enum { name: "ReminderType" values: { "NoReminder": 0, "VisualReminder": 1, "AudibleReminder": 2, "EmailReminder": 3 } } Property { name: "reminderType"; type: "ReminderType"; isReadonly: true } Property { name: "repetitionCount"; type: "int" } Property { name: "repetitionDelay"; type: "int" } Property { name: "secondsBeforeStart"; type: "int" } Signal { name: "reminderChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemSortOrder" prototype: "QObject" exports: ["QtOrganizer/SortOrder 5.0"] exportMetaObjectRevisions: [0] Enum { name: "BlankPolicy" values: { "BlanksFirst": 0, "BlanksLast": 1 } } Property { name: "detail"; type: "QDeclarativeOrganizerItemDetail::DetailType" } Property { name: "field"; type: "int" } Property { name: "blankPolicy"; type: "BlankPolicy" } Property { name: "direction"; type: "Qt::SortOrder" } Property { name: "sensitivity"; type: "Qt::CaseSensitivity" } Signal { name: "sortOrderChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemTag" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/Tag 5.0"] exportMetaObjectRevisions: [0] Enum { name: "TagField" values: { "FieldTag": 1101 } } Property { name: "tag"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemTimestamp" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/Timestamp 5.0"] exportMetaObjectRevisions: [0] Enum { name: "TimestampField" values: { "FieldCreated": 1201, "FieldLastModified": 1202 } } Property { name: "created"; type: "QDateTime" } Property { name: "lastModified"; type: "QDateTime" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemType" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/Type 5.0"] exportMetaObjectRevisions: [0] Enum { name: "ItemTypeField" values: { "FieldType": 501 } } Enum { name: "ItemType" values: { "Undefined": 501, "Event": 502, "EventOccurrence": 503, "Todo": 504, "TodoOccurrence": 505, "Journal": 506, "Note": 507 } } Property { name: "itemType"; type: "ItemType" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemUnionFilter" defaultProperty: "filters" prototype: "QtOrganizer::QDeclarativeOrganizerItemCompoundFilter" exports: ["QtOrganizer/UnionFilter 5.0"] exportMetaObjectRevisions: [0] } Component { name: "QtOrganizer::QDeclarativeOrganizerItemVersion" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/Version 5.0"] exportMetaObjectRevisions: [0] Enum { name: "Field" values: { "FieldVersion": 1301, "FieldExtendedVersion": 1302 } } Property { name: "version"; type: "int" } Property { name: "extendedVersion"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerItemVisualReminder" prototype: "QtOrganizer::QDeclarativeOrganizerItemReminder" exports: ["QtOrganizer/VisualReminder 5.0"] exportMetaObjectRevisions: [0] Enum { name: "VisualReminderField" values: { "FieldDataUrl": 1702, "FieldMessage": 1701 } } Property { name: "message"; type: "string" } Property { name: "dataUrl"; type: "QUrl" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerJournal" defaultProperty: "itemDetails" prototype: "QtOrganizer::QDeclarativeOrganizerItem" exports: ["QtOrganizer/Journal 5.0"] exportMetaObjectRevisions: [0] Property { name: "dateTime"; type: "QDateTime" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerJournalTime" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/JournalTime 5.0"] exportMetaObjectRevisions: [0] Enum { name: "JournalTimeField" values: { "FieldEntryDateTime": 2201 } } Property { name: "entryDateTime"; type: "QDateTime" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerModel" prototype: "QAbstractListModel" exports: ["QtOrganizer/OrganizerModel 5.0"] exportMetaObjectRevisions: [0] Enum { name: "ExportError" values: { "ExportNoError": 0, "ExportUnspecifiedError": 1, "ExportIOError": 2, "ExportOutOfMemoryError": 3, "ExportNotReadyError": 4 } } Enum { name: "ImportError" values: { "ImportNoError": 0, "ImportUnspecifiedError": 1, "ImportIOError": 2, "ImportOutOfMemoryError": 3, "ImportNotReadyError": 4, "ImportParseError": 5 } } Property { name: "manager"; type: "string" } Property { name: "managerName"; type: "string"; isReadonly: true } Property { name: "availableManagers"; type: "QStringList"; isReadonly: true } Property { name: "autoUpdate"; type: "bool" } Property { name: "startPeriod"; type: "QDateTime" } Property { name: "endPeriod"; type: "QDateTime" } Property { name: "filter"; type: "QDeclarativeOrganizerItemFilter"; isPointer: true } Property { name: "fetchHint"; type: "QDeclarativeOrganizerItemFetchHint"; isPointer: true } Property { name: "sortOrders" type: "QDeclarativeOrganizerItemSortOrder" isList: true isReadonly: true } Property { name: "items"; type: "QDeclarativeOrganizerItem"; isList: true; isReadonly: true } Property { name: "collections" type: "QDeclarativeOrganizerCollection" isList: true isReadonly: true } Property { name: "error"; type: "string"; isReadonly: true } Property { name: "itemCount"; type: "int"; isReadonly: true } Signal { name: "modelChanged" } Signal { name: "itemsFetched" Parameter { name: "requestId"; type: "int" } Parameter { name: "fetchedItems"; type: "QVariantList" } } Signal { name: "exportCompleted" Parameter { name: "error"; type: "ExportError" } Parameter { name: "url"; type: "QUrl" } } Signal { name: "importCompleted" Parameter { name: "error"; type: "ImportError" } Parameter { name: "url"; type: "QUrl" } } Method { name: "update" } Method { name: "updateItems" } Method { name: "updateCollections" } Method { name: "cancelUpdate" } Method { name: "removeItem" Parameter { name: "id"; type: "string" } } Method { name: "removeItem" Parameter { name: "item"; type: "QDeclarativeOrganizerItem"; isPointer: true } } Method { name: "removeItems" Parameter { name: "ids"; type: "QStringList" } } Method { name: "removeItems" Parameter { name: "items"; type: "QList" } } Method { name: "saveItem" Parameter { name: "item"; type: "QDeclarativeOrganizerItem"; isPointer: true } } Method { name: "fetchItems" type: "int" Parameter { name: "itemIds"; type: "QStringList" } } Method { name: "fetchItems" type: "int" Parameter { name: "start"; type: "QDateTime" } Parameter { name: "end"; type: "QDateTime" } Parameter { name: "filter"; type: "QDeclarativeOrganizerItemFilter"; isPointer: true } Parameter { name: "maxCount"; type: "int" } Parameter { name: "sortOrders"; type: "QVariantList" } Parameter { name: "fetchHint"; type: "QDeclarativeOrganizerItemFetchHint"; isPointer: true } } Method { name: "fetchItems" type: "int" Parameter { name: "start"; type: "QDateTime" } Parameter { name: "end"; type: "QDateTime" } Parameter { name: "filter"; type: "QDeclarativeOrganizerItemFilter"; isPointer: true } Parameter { name: "maxCount"; type: "int" } Parameter { name: "sortOrders"; type: "QVariantList" } } Method { name: "fetchItems" type: "int" Parameter { name: "start"; type: "QDateTime" } Parameter { name: "end"; type: "QDateTime" } Parameter { name: "filter"; type: "QDeclarativeOrganizerItemFilter"; isPointer: true } Parameter { name: "maxCount"; type: "int" } } Method { name: "fetchItems" type: "int" Parameter { name: "start"; type: "QDateTime" } Parameter { name: "end"; type: "QDateTime" } Parameter { name: "filter"; type: "QDeclarativeOrganizerItemFilter"; isPointer: true } } Method { name: "fetchItems" type: "int" Parameter { name: "start"; type: "QDateTime" } Parameter { name: "end"; type: "QDateTime" } } Method { name: "removeCollection" Parameter { name: "collectionId"; type: "string" } } Method { name: "saveCollection" Parameter { name: "collection"; type: "QDeclarativeOrganizerCollection"; isPointer: true } } Method { name: "fetchCollections" } Method { name: "containsItems" type: "QList" Parameter { name: "start"; type: "QDateTime" } Parameter { name: "end"; type: "QDateTime" } Parameter { name: "interval"; type: "int" } } Method { name: "containsItems" type: "bool" Parameter { name: "start"; type: "QDateTime" } Parameter { name: "end"; type: "QDateTime" } } Method { name: "containsItems" type: "bool" Parameter { name: "start"; type: "QDateTime" } } Method { name: "itemsByTimePeriod" type: "QVariantList" Parameter { name: "start"; type: "QDateTime" } Parameter { name: "end"; type: "QDateTime" } } Method { name: "itemsByTimePeriod" type: "QVariantList" Parameter { name: "start"; type: "QDateTime" } } Method { name: "itemsByTimePeriod"; type: "QVariantList" } Method { name: "item" type: "QDeclarativeOrganizerItem*" Parameter { name: "id"; type: "string" } } Method { name: "itemIds" type: "QStringList" Parameter { name: "start"; type: "QDateTime" } Parameter { name: "end"; type: "QDateTime" } } Method { name: "itemIds" type: "QStringList" Parameter { name: "start"; type: "QDateTime" } } Method { name: "itemIds"; type: "QStringList" } Method { name: "defaultCollection"; type: "QDeclarativeOrganizerCollection*" } Method { name: "collection" type: "QDeclarativeOrganizerCollection*" Parameter { name: "collectionId"; type: "string" } } Method { name: "importItems" Parameter { name: "url"; type: "QUrl" } Parameter { name: "profiles"; type: "QStringList" } } Method { name: "importItems" Parameter { name: "url"; type: "QUrl" } } Method { name: "exportItems" Parameter { name: "url"; type: "QUrl" } Parameter { name: "profiles"; type: "QStringList" } } Method { name: "exportItems" Parameter { name: "url"; type: "QUrl" } } } Component { name: "QtOrganizer::QDeclarativeOrganizerNote" defaultProperty: "itemDetails" prototype: "QtOrganizer::QDeclarativeOrganizerItem" exports: ["QtOrganizer/Note 5.0"] exportMetaObjectRevisions: [0] Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerRecurrenceRule" prototype: "QObject" exports: ["QtOrganizer/RecurrenceRule 5.0"] exportMetaObjectRevisions: [0] Enum { name: "Frequency" values: { "Invalid": 0, "Daily": 1, "Weekly": 2, "Monthly": 3, "Yearly": 4 } } Enum { name: "Month" values: { "January": 1, "February": 2, "March": 3, "April": 4, "May": 5, "June": 6, "July": 7, "August": 8, "September": 9, "October": 10, "November": 11, "December": 12 } } Property { name: "frequency"; type: "Frequency" } Property { name: "limit"; type: "QVariant" } Property { name: "interval"; type: "int" } Property { name: "daysOfWeek"; type: "QVariantList" } Property { name: "daysOfMonth"; type: "QVariantList" } Property { name: "daysOfYear"; type: "QVariantList" } Property { name: "monthsOfYear"; type: "QVariantList" } Property { name: "positions"; type: "QVariantList" } Property { name: "firstDayOfWeek"; type: "Qt::DayOfWeek" } Signal { name: "recurrenceRuleChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerTodo" defaultProperty: "itemDetails" prototype: "QtOrganizer::QDeclarativeOrganizerItem" exports: ["QtOrganizer/Todo 5.0"] exportMetaObjectRevisions: [0] Property { name: "allDay"; type: "bool" } Property { name: "percentageComplete"; type: "int" } Property { name: "startDateTime"; type: "QDateTime" } Property { name: "dueDateTime"; type: "QDateTime" } Property { name: "finishedDateTime"; type: "QDateTime" } Property { name: "priority"; type: "QDeclarativeOrganizerItemPriority::Priority" } Property { name: "status"; type: "QDeclarativeOrganizerTodoProgress::StatusType" } Property { name: "recurrence" type: "QDeclarativeOrganizerItemRecurrence" isReadonly: true isPointer: true } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerTodoOccurrence" defaultProperty: "itemDetails" prototype: "QtOrganizer::QDeclarativeOrganizerItem" exports: ["QtOrganizer/TodoOccurrence 5.0"] exportMetaObjectRevisions: [0] Property { name: "allDay"; type: "bool" } Property { name: "percentageComplete"; type: "int" } Property { name: "originalDate"; type: "QDateTime" } Property { name: "startDateTime"; type: "QDateTime" } Property { name: "dueDateTime"; type: "QDateTime" } Property { name: "finishedDateTime"; type: "QDateTime" } Property { name: "priority"; type: "QDeclarativeOrganizerItemPriority::Priority" } Property { name: "status"; type: "QDeclarativeOrganizerTodoProgress::StatusType" } Property { name: "parentId"; type: "string" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerTodoProgress" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/TodoProgress 5.0"] exportMetaObjectRevisions: [0] Enum { name: "TodoProgressField" values: { "FieldStatus": 2303, "FieldPercentageComplete": 2302, "FieldFinishedDateTime": 2301 } } Enum { name: "StatusType" values: { "NotStarted": 0, "InProgress": 1, "Complete": 2 } } Property { name: "percentageComplete"; type: "int" } Property { name: "finishedDateTime"; type: "QDateTime" } Property { name: "status"; type: "StatusType" } Signal { name: "valueChanged" } } Component { name: "QtOrganizer::QDeclarativeOrganizerTodoTime" prototype: "QtOrganizer::QDeclarativeOrganizerItemDetail" exports: ["QtOrganizer/TodoTime 5.0"] exportMetaObjectRevisions: [0] Enum { name: "TodoTimeField" values: { "FieldStartDateTime": 2401, "FieldDueDateTime": 2402, "FieldAllDay": 2403 } } Property { name: "allDay"; type: "bool" } Property { name: "startDateTime"; type: "QDateTime" } Property { name: "dueDateTime"; type: "QDateTime" } Signal { name: "valueChanged" } } } src/imports/organizer/qdeclarativeorganizercollection.cpp000066400000000000000000000151711233466112000245120ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativeorganizercollection_p.h" QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE /*! \qmltype Collection \instantiates QDeclarativeOrganizerCollection \brief The Collection element represents a collection of items in an organizer manager. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-main */ /*! \internal */ QDeclarativeOrganizerCollection::QDeclarativeOrganizerCollection(QObject *parent) : QObject(parent) { } /*! \qmlproperty string Collection::collectionId This property holds the ID of the collection. */ QString QDeclarativeOrganizerCollection::id() const { return d.id().toString(); } void QDeclarativeOrganizerCollection::setId(const QString &id) { if (d.id().toString() != id) { d.setId(QOrganizerCollectionId::fromString(id)); emit valueChanged(); } } /*! \qmlproperty string Collection::name This property holds the name meta data of a collection. */ QString QDeclarativeOrganizerCollection::name() const { return metaData(QOrganizerCollection::KeyName).toString(); } void QDeclarativeOrganizerCollection::setName(const QString &name) { setMetaData(QOrganizerCollection::KeyName, name); } /*! \qmlproperty string Collection::description This property holds the description meta data of a collection. */ QString QDeclarativeOrganizerCollection::description() const { return metaData(QOrganizerCollection::KeyDescription).toString(); } void QDeclarativeOrganizerCollection::setDescription(const QString &description) { setMetaData(QOrganizerCollection::KeyDescription, description); } /*! \qmlproperty color Collection::secondaryColor This property holds the secondary color meta data of a collection. */ QColor QDeclarativeOrganizerCollection::secondaryColor() const { return metaData(QOrganizerCollection::KeySecondaryColor).value(); } void QDeclarativeOrganizerCollection::setSecondaryColor(const QColor &secondaryColor) { setMetaData(QOrganizerCollection::KeySecondaryColor, secondaryColor); } /*! \qmlproperty color Collection::color This property holds the color meta data of a collection. */ QColor QDeclarativeOrganizerCollection::color() const { return metaData(QOrganizerCollection::KeyColor).value(); } void QDeclarativeOrganizerCollection::setColor(const QColor &color) { setMetaData(QOrganizerCollection::KeyColor, color); } /*! \qmlproperty url Collection::image This property holds the image url meta data of a collection. */ QUrl QDeclarativeOrganizerCollection::image() const { return QUrl(metaData(QOrganizerCollection::KeyImage).toString()); } void QDeclarativeOrganizerCollection::setImage(const QUrl &url) { setMetaData(QOrganizerCollection::KeyImage, url); } /*! \qmlmethod Collection::setMetaData(key, value) Sets the meta data of the collection for the given \a key to the given \a value. Possible keys include: \list \li Collection.KeyName \li Collection.KeyDescription \li Collection.KeyColor \li Collection.KeySecondaryColor \li Collection.KeyImage \li Collection.KeyExtended \endlist */ void QDeclarativeOrganizerCollection::setMetaData(QOrganizerCollection::MetaDataKey key, const QVariant &value) { if (metaData(key) != value) { d.setMetaData(key, value); emit valueChanged(); } } /*! \qmlmethod var Collection::metaData(key) Returns the meta data stored in this collection for the given \a key. Possible keys include: \list \li Collection.KeyName \li Collection.KeyDescription \li Collection.KeyColor \li Collection.KeyImage \li Collection.KeyExtended \endlist */ QVariant QDeclarativeOrganizerCollection::metaData(QOrganizerCollection::MetaDataKey key) const { return d.metaData(key); } /*! \qmlmethod Collection::setExtendedMetaData(key, value) Sets the value of the extended metadata with the given \a key to \a value. */ void QDeclarativeOrganizerCollection::setExtendedMetaData(const QString &key, const QVariant &value) { if (extendedMetaData(key) != value) { d.setExtendedMetaData(key, value); emit valueChanged(); } } /*! \qmlmethod var Collection::extendedMetaData(key) Returns the value of extended metadata with the given \a key. */ QVariant QDeclarativeOrganizerCollection::extendedMetaData(const QString &key) const { return d.extendedMetaData(key); } /*! \internal */ QOrganizerCollection QDeclarativeOrganizerCollection::collection() const { return d; } /*! \internal */ void QDeclarativeOrganizerCollection::setCollection(const QOrganizerCollection &collection) { d = collection; } #include "moc_qdeclarativeorganizercollection_p.cpp" QT_END_NAMESPACE src/imports/organizer/qdeclarativeorganizercollection_p.h000066400000000000000000000103751233466112000244770ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVEORGANIZERCOLLECTION_H #define QDECLARATIVEORGANIZERCOLLECTION_H #include #include #include #include QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeOrganizerCollection : public QObject { Q_OBJECT Q_ENUMS(MetaDataKey) Q_PROPERTY(QString collectionId READ id WRITE setId NOTIFY valueChanged) Q_PROPERTY(QString name READ name WRITE setName NOTIFY valueChanged) Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY valueChanged) Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY valueChanged) Q_PROPERTY(QColor secondaryColor READ secondaryColor WRITE setSecondaryColor NOTIFY valueChanged) Q_PROPERTY(QUrl image READ image WRITE setImage NOTIFY valueChanged) public: enum MetaDataKey { KeyName = QOrganizerCollection::KeyName, KeyDescription = QOrganizerCollection::KeyDescription, KeyColor = QOrganizerCollection::KeyColor, KeySecondaryColor = QOrganizerCollection::KeySecondaryColor, KeyImage = QOrganizerCollection::KeyImage, KeyExtended = QOrganizerCollection::KeyExtended }; QDeclarativeOrganizerCollection(QObject *parent = 0); QString id() const; void setId(const QString &id); QString name() const; void setName(const QString &name); QString description() const; void setDescription(const QString &description); QColor color() const; void setColor(const QColor &color); QColor secondaryColor() const; void setSecondaryColor(const QColor &secondaryColor); QUrl image() const; void setImage(const QUrl &url); Q_INVOKABLE void setMetaData(QOrganizerCollection::MetaDataKey key, const QVariant &value); Q_INVOKABLE QVariant metaData(QOrganizerCollection::MetaDataKey key) const; Q_INVOKABLE void setExtendedMetaData(const QString &key, const QVariant &value); Q_INVOKABLE QVariant extendedMetaData(const QString &key) const; // used by model QOrganizerCollection collection() const; void setCollection(const QOrganizerCollection & collection); Q_SIGNALS: void valueChanged(); private: QOrganizerCollection d; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeOrganizerCollection) #endif // QDECLARATIVEORGANIZERCOLLECTION_H src/imports/organizer/qdeclarativeorganizeritem.cpp000066400000000000000000002016551233466112000233210ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativeorganizeritem_p.h" #include #include "qdeclarativeorganizermodel_p.h" QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE /*! \qmltype OrganizerItem \instantiates QDeclarativeOrganizerItem \brief The OrganizerItem element represents the in-memory version of a organizer item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-main A OrganizerItem has a number of mandatory details. Different subclasses of OrganizerItem (i.e., Event, EventOccurrence, Journal, Todo, TodoOccurrence, Note) may have more mandatory details. Most frequently-used details can also be accessed through convenient properties, e.g. displayLabel, while all details can be accessed through detail(), details(), saveDetail(), among others. \sa Event, EventOccurrence, Journal, Todo, TodoOccurrence, Note, {QOrganizerManager}, {QOrganizerItem} */ /*! \qmlsignal OrganizerItem::onItemChanged() This signal is emitted, when any of the OrganizerItem's or child element's (like Event, Todo etc) properties have been changed. */ /*! \internal */ QDeclarativeOrganizerItem::QDeclarativeOrganizerItem(QObject *parent) : QObject(parent) , m_modified(false) { } /*! \internal */ QDeclarativeOrganizerItem::~QDeclarativeOrganizerItem() { clearDetails(); } // basic information /*! \qmlproperty bool OrganizerItem::modified This property holds the dirty flag of the OrganizerItem object. \sa save */ bool QDeclarativeOrganizerItem::modified() const { return m_modified; } /*! \qmlproperty enum OrganizerItem::itemType This property holds the type of the OrganizerItem object. */ QDeclarativeOrganizerItemType::ItemType QDeclarativeOrganizerItem::itemType() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::ItemType == detail->type()) return static_cast(detail)->itemType(); } return QDeclarativeOrganizerItemType::Undefined; } /*! \qmlproperty list OrganizerItem::itemDetails This property holds the details of the OrganizerItem object. */ QQmlListProperty QDeclarativeOrganizerItem::itemDetails() { return QQmlListProperty(this, 0, &QDeclarativeOrganizerItem::_q_detail_append, &QDeclarativeOrganizerItem::_q_detail_count, &QDeclarativeOrganizerItem::_q_detail_at, &QDeclarativeOrganizerItem::_q_detail_clear); } /*! \qmlproperty string OrganizerItem::itemId This property holds the id of the OrganizerItem object. */ QString QDeclarativeOrganizerItem::itemId() const { return m_id.toString(); } /*! \qmlproperty string OrganizerItem::manager This property holds the manager uri which the \l OrganizerItem object comes from. */ QString QDeclarativeOrganizerItem::manager() const { return m_id.managerUri(); } /*! \qmlproperty string OrganizerItem::collectionId This property holds the id of collection where the item belongs to. */ QString QDeclarativeOrganizerItem::collectionId() const { return m_collectionId.toString(); } void QDeclarativeOrganizerItem::setCollectionId(const QString &collectionId) { QOrganizerCollectionId newCollectionId(QOrganizerCollectionId::fromString(collectionId)); // in case invalid collectionId-string, fromString() will return default collectionId-string // instead of the intended collectionId-string if (newCollectionId.toString() == collectionId && m_collectionId.toString() != collectionId) { m_collectionId = newCollectionId; m_modified = true; emit itemChanged(); } } // convenient access to most frequently used details /*! \qmlproperty string OrganizerItem::description This property holds the description text of the organizer item. */ QString QDeclarativeOrganizerItem::description() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Description == detail->type()) return static_cast(detail)->description(); } return QString::null; } void QDeclarativeOrganizerItem::setDescription(const QString &description) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Description == detail->type()) { QDeclarativeOrganizerItemDescription *desc = static_cast(detail); if (desc->description() != description) { desc->setDescription(description); m_modified = true; emit itemChanged(); } return; } } QDeclarativeOrganizerItemDescription *desc = new QDeclarativeOrganizerItemDescription(this); desc->setDescription(description); m_details.append(desc); m_modified = true; emit itemChanged(); } /*! \qmlproperty string OrganizerItem::displayLabel This property holds the display label of the organizer item. */ QString QDeclarativeOrganizerItem::displayLabel() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::DisplayLabel == detail->type()) return static_cast(detail)->label(); } return QString::null; } void QDeclarativeOrganizerItem::setDisplayLabel(const QString &label) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::DisplayLabel == detail->type()) { QDeclarativeOrganizerItemDisplayLabel *displayLabel = static_cast(detail); if (displayLabel->label() != label) { displayLabel->setLabel(label); m_modified = true; emit itemChanged(); } return; } } QDeclarativeOrganizerItemDisplayLabel *displayLabel = new QDeclarativeOrganizerItemDisplayLabel(this); displayLabel->setLabel(label); m_details.append(displayLabel); m_modified = true; emit itemChanged(); } /*! \qmlproperty string OrganizerItem::guid This property holds the GUID string of the organizer item. */ QString QDeclarativeOrganizerItem::guid() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Guid == detail->type()) return static_cast(detail)->guid(); } return QString::null; } void QDeclarativeOrganizerItem::setGuid(const QString &guid) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Guid == detail->type()) { QDeclarativeOrganizerItemGuid *itemGuid = static_cast(detail); if (itemGuid->guid() != guid) { itemGuid->setGuid(guid); m_modified = true; emit itemChanged(); } return; } } QDeclarativeOrganizerItemGuid *itemGuid = new QDeclarativeOrganizerItemGuid(this); itemGuid->setGuid(guid); m_details.append(itemGuid); m_modified = true; emit itemChanged(); } // functions /*! \qmlmethod Detail OrganizerItem::detail(type) Returns the first detail stored in the organizer item with the given \a type. \sa Detail::type */ QDeclarativeOrganizerItemDetail *QDeclarativeOrganizerItem::detail(int type) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (type == detail->type()) { QDeclarativeOrganizerItemDetail *itemDetail = QDeclarativeOrganizerItemDetailFactory::createItemDetail(detail->type()); QQmlEngine::setObjectOwnership(itemDetail, QQmlEngine::JavaScriptOwnership); itemDetail->setDetail(detail->detail()); return itemDetail; } } return 0; } /*! \qmlmethod list OrganizerItem::details(type) Returns all the details stored in the organizer item with the given \a type. \sa Detail::type */ QVariantList QDeclarativeOrganizerItem::details(int type) { QVariantList list; foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (type == detail->type()) { QDeclarativeOrganizerItemDetail *itemDetail = QDeclarativeOrganizerItemDetailFactory::createItemDetail(detail->type()); QQmlEngine::setObjectOwnership(itemDetail, QQmlEngine::JavaScriptOwnership); itemDetail->setDetail(detail->detail()); list.append(QVariant::fromValue((QObject*)itemDetail)); } } return list; } /*! \qmlmethod void OrganizerItem::setDetail(detail) Saves the given \a detail in the organizer item, and sets its id. */ void QDeclarativeOrganizerItem::setDetail(QDeclarativeOrganizerItemDetail *detail) { if (_q_setDetail(detail)) emit itemChanged(); } /*! \qmlmethod void OrganizerItem::removeDetail(detail) Removes given \a detail from the organizer item. */ void QDeclarativeOrganizerItem::removeDetail(QDeclarativeOrganizerItemDetail *detail) { if (_q_removeDetail(detail)) emit itemChanged(); } /*! \qmlmethod OrganizerItem::clearDetails() Removes all details from the organizer item. \sa removeDetail */ void QDeclarativeOrganizerItem::clearDetails() { if (_q_clearDetails()) emit itemChanged(); } /*! \qmlmethod OrganizerItem::save() Saves this OrganizerItem if the item has been modified. \sa modified */ void QDeclarativeOrganizerItem::save() { if (m_modified) { QDeclarativeOrganizerModel *model = qobject_cast(parent()); if (model) { model->saveItem(this); m_modified = false; } } } // non-QML APIs, used by model only /*! \internal */ void QDeclarativeOrganizerItem::setItem(const QOrganizerItem &item) { m_id = item.id(); m_collectionId = item.collectionId(); foreach (QDeclarativeOrganizerItemDetail *detail, m_details) delete detail; m_details.clear(); QList details(item.details()); foreach (const QOrganizerItemDetail &detail, details) { QDeclarativeOrganizerItemDetail *itemDetail = QDeclarativeOrganizerItemDetailFactory::createItemDetail(static_cast(detail.type())); itemDetail->setDetail(detail); m_details.append(itemDetail); } m_modified = false; emit itemChanged(); } /*! \internal */ QOrganizerItem QDeclarativeOrganizerItem::item() const { QOrganizerItem item; item.setId(m_id); item.setCollectionId(m_collectionId); foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { QOrganizerItemDetail itemDetail = detail->detail(); item.saveDetail(&itemDetail); } return item; } /*! \internal */ bool QDeclarativeOrganizerItem::generatedOccurrence() const { QDeclarativeOrganizerItemType::ItemType type = itemType(); return (m_id.isNull() && (type == QDeclarativeOrganizerItemType::EventOccurrence || type == QDeclarativeOrganizerItemType::TodoOccurrence)); } /*! \internal */ QDateTime QDeclarativeOrganizerItem::itemStartTime() const { switch (itemType()) { case QDeclarativeOrganizerItemType::Event: return static_cast(this)->startDateTime(); case QDeclarativeOrganizerItemType::EventOccurrence: return static_cast(this)->startDateTime(); case QDeclarativeOrganizerItemType::Todo: return static_cast(this)->startDateTime(); case QDeclarativeOrganizerItemType::TodoOccurrence: return static_cast(this)->startDateTime(); case QDeclarativeOrganizerItemType::Journal: return static_cast(this)->dateTime(); case QDeclarativeOrganizerItemType::Note: default: break; } return QDateTime(); } /*! \internal */ QDateTime QDeclarativeOrganizerItem::itemEndTime() const { switch (itemType()) { case QDeclarativeOrganizerItemType::Event: return static_cast(this)->endDateTime(); case QDeclarativeOrganizerItemType::EventOccurrence: return static_cast(this)->endDateTime(); case QDeclarativeOrganizerItemType::Todo: return static_cast(this)->dueDateTime(); case QDeclarativeOrganizerItemType::TodoOccurrence: return static_cast(this)->dueDateTime(); case QDeclarativeOrganizerItemType::Journal: //there is no end time for journal item, make it 30mins later for display purpose return static_cast(this)->dateTime().addSecs(60*30); case QDeclarativeOrganizerItemType::Note: default: break; } return QDateTime(); } // call-back functions for list property /*! \internal */ void QDeclarativeOrganizerItem::_q_detail_append(QQmlListProperty *property, QDeclarativeOrganizerItemDetail *value) { QDeclarativeOrganizerItem *object = qobject_cast(property->object); if (object) object->m_details.append(value); } /*! \internal */ QDeclarativeOrganizerItemDetail *QDeclarativeOrganizerItem::_q_detail_at(QQmlListProperty *property, int index) { QDeclarativeOrganizerItem *object = qobject_cast(property->object); if (object) return object->m_details.at(index); else return 0; } /*! \internal */ void QDeclarativeOrganizerItem::_q_detail_clear(QQmlListProperty *property) { QDeclarativeOrganizerItem *object = qobject_cast(property->object); if (object) { foreach (QDeclarativeOrganizerItemDetail *obj, object->m_details) delete obj; object->m_details.clear(); } } /*! \internal */ int QDeclarativeOrganizerItem::_q_detail_count(QQmlListProperty *property) { QDeclarativeOrganizerItem *object = qobject_cast(property->object); if (object) return object->m_details.size(); else return 0; } /*! \internal */ bool QDeclarativeOrganizerItem::_q_removeDetail(QDeclarativeOrganizerItemDetail *detail) { if (!detail) return false; int key = detail->detail().key(); int i = 0; bool removed = false; foreach (QDeclarativeOrganizerItemDetail *itemDetail, m_details) { if (key == itemDetail->detail().key()) { delete itemDetail; m_details.removeAt(i); removed = true; } ++i; } return removed; } /*! \internal */ bool QDeclarativeOrganizerItem::_q_setDetail(QDeclarativeOrganizerItemDetail *detail) { if (!detail) return false; bool found(false); int key = detail->detail().key(); foreach (QDeclarativeOrganizerItemDetail *itemDetail, m_details) { if (key == itemDetail->detail().key()) { itemDetail->setDetail(detail->detail()); found = true; } } if (!found) { QDeclarativeOrganizerItemDetail *itemDetail = QDeclarativeOrganizerItemDetailFactory::createItemDetail(detail->type()); itemDetail->setDetail(detail->detail()); m_details.append(itemDetail); } m_modified = true; return true; } /*! \internal */ bool QDeclarativeOrganizerItem::_q_clearDetails() { bool ret = false; if (!m_details.empty()) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) delete detail; m_details.clear(); m_modified = true; ret = true; } return ret; } /*! \qmltype Event \instantiates QDeclarativeOrganizerEvent \brief The Event element provides an event in time which may reoccur. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-items \sa OrganizerItem, EventOccurrence, Journal, Todo, TodoOccurrence, Note, {QOrganizerEvent} */ /*! \qmlsignal Event::onItemChanged() \sa OrganizerItem::onItemChanged */ /*! \internal */ QDeclarativeOrganizerEvent::QDeclarativeOrganizerEvent(QObject *parent) : QDeclarativeOrganizerItem(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(itemChanged())); setItem(QOrganizerEvent()); } /*! \qmlmethod void Event::setDetail(detail) Saves the given \a detail in the organizer event, and sets its id. */ void QDeclarativeOrganizerEvent::setDetail(QDeclarativeOrganizerItemDetail *detail) { if (_q_setDetail(detail)) emit valueChanged(); } /*! \qmlmethod void Event::removeDetail(detail) Removes given \a detail from the organizer event. */ void QDeclarativeOrganizerEvent::removeDetail(QDeclarativeOrganizerItemDetail *detail) { if (_q_removeDetail(detail)) emit valueChanged(); } /*! \qmlmethod Event::clearDetails() Removes all details from the organizer event. \sa removeDetail() */ void QDeclarativeOrganizerEvent::clearDetails() { if (_q_clearDetails()) emit valueChanged(); } /*! \qmlproperty list Event::attendees This property holds the attendees list of the event. */ QQmlListProperty QDeclarativeOrganizerEvent::attendees() { return QQmlListProperty(this, 0, &QDeclarativeOrganizerEvent::_q_attendee_append, &QDeclarativeOrganizerEvent::_q_attendee_count, &QDeclarativeOrganizerEvent::_q_attendee_at, &QDeclarativeOrganizerEvent::_q_attendee_clear); } // call-back functions for list property /*! \internal */ void QDeclarativeOrganizerEvent::_q_attendee_append(QQmlListProperty *property, QDeclarativeOrganizerEventAttendee *value) { QDeclarativeOrganizerEvent *object = qobject_cast(property->object); if (object) object->setDetail(value); } /*! \internal */ QDeclarativeOrganizerEventAttendee *QDeclarativeOrganizerEvent::_q_attendee_at(QQmlListProperty *property, int index) { QDeclarativeOrganizerEvent *object = qobject_cast(property->object); QDeclarativeOrganizerEventAttendee *ret = 0; int i = 0; foreach (QDeclarativeOrganizerItemDetail *detail, object->m_details) { if (QDeclarativeOrganizerItemDetail::EventAttendee == detail->type()) { if (i == index) { ret = qobject_cast(detail); break; } else { ++i; } } } return ret; } /*! \internal */ void QDeclarativeOrganizerEvent::_q_attendee_clear(QQmlListProperty *property) { QDeclarativeOrganizerEvent *object = qobject_cast(property->object); if (object) { int i = 0; bool removed = false; foreach (QDeclarativeOrganizerItemDetail *obj, object->m_details) { if (obj->type() == QDeclarativeOrganizerItemDetail::EventAttendee) { delete obj; object->m_details.removeAt(i); removed = true; } else {// Index should not increase if some thing is removed ++i; } } if (removed) emit object->valueChanged(); } } /*! \internal */ int QDeclarativeOrganizerEvent::_q_attendee_count(QQmlListProperty *property) { QDeclarativeOrganizerEvent *object = qobject_cast(property->object); int ret = 0; if (object) { foreach (QDeclarativeOrganizerItemDetail *detail, object->m_details) { if (QDeclarativeOrganizerItemDetail::EventAttendee == detail->type()) ++ret; } } return ret; } /*! \qmlproperty bool Event::allDay This property indicates whether the time-of-day component of the event's start date-time or end date-time is insignificant. If allDay is true, the time-of-day component is considered insignificant, and the event will be an all-day item. */ void QDeclarativeOrganizerEvent::setAllDay(bool allDay) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::EventTime == detail->type()) { QDeclarativeOrganizerEventTime *eventTime = static_cast(detail); if (eventTime->isAllDay() != allDay) { eventTime->setAllDay(allDay); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerEventTime *eventTime = new QDeclarativeOrganizerEventTime(this); eventTime->setAllDay(allDay); m_details.append(eventTime); m_modified = true; emit valueChanged(); } bool QDeclarativeOrganizerEvent::isAllDay() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::EventTime == detail->type()) return static_cast(detail)->isAllDay(); } return false; } /*! \qmlproperty date Event::startDateTime This property holds the start date time of the event. */ void QDeclarativeOrganizerEvent::setStartDateTime(const QDateTime &datetime) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::EventTime == detail->type()) { QDeclarativeOrganizerEventTime *eventTime = static_cast(detail); if (eventTime->startDateTime() != datetime) { eventTime->setStartDateTime(datetime); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerEventTime *eventTime = new QDeclarativeOrganizerEventTime(this); eventTime->setStartDateTime(datetime); m_details.append(eventTime); m_modified = true; emit valueChanged(); } QDateTime QDeclarativeOrganizerEvent::startDateTime() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::EventTime == detail->type()) return static_cast(detail)->startDateTime(); } return QDateTime(); } /*! \qmlproperty date Event::endDateTime This property holds the end date time of the event. */ void QDeclarativeOrganizerEvent::setEndDateTime(const QDateTime& datetime) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::EventTime == detail->type()) { QDeclarativeOrganizerEventTime *eventTime = static_cast(detail); if (eventTime->endDateTime() != datetime) { eventTime->setEndDateTime(datetime); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerEventTime *eventTime = new QDeclarativeOrganizerEventTime(this); eventTime->setEndDateTime(datetime); m_details.append(eventTime); m_modified = true; emit valueChanged(); } QDateTime QDeclarativeOrganizerEvent::endDateTime() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::EventTime == detail->type()) return static_cast(detail)->endDateTime(); } return QDateTime(); } /*! \qmlproperty enumeration Event::priority This property holds the priority of the event. The value can be one of: \list \li Priority.Unknown \li Priority.Highest \li Priority.ExtremelyHigh \li Priority.VeryHigh \li Priority.High \li Priority.Medium \li Priority.Low \li Priority.VeryLow \li Priority.ExtremelyLow \li Priority.Lowest \endlist */ void QDeclarativeOrganizerEvent::setPriority(QDeclarativeOrganizerItemPriority::Priority priority) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Priority == detail->type()) { QDeclarativeOrganizerItemPriority *itemPriority = static_cast(detail); if (itemPriority->priority() != priority) { itemPriority->setPriority(priority); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerItemPriority *itemPriority = new QDeclarativeOrganizerItemPriority(this); itemPriority->setPriority(priority); m_details.append(itemPriority); m_modified = true; emit valueChanged(); } QDeclarativeOrganizerItemPriority::Priority QDeclarativeOrganizerEvent::priority() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Priority == detail->type()) return static_cast(detail)->priority(); } return QDeclarativeOrganizerItemPriority::Unknown; } /*! \qmlproperty string Event::location This property holds the label of the location at which the event occurs. */ void QDeclarativeOrganizerEvent::setLocation(const QString &location) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Location == detail->type()) { QDeclarativeOrganizerItemLocation *itemLocation = static_cast(detail); if (itemLocation->label() != location) { itemLocation->setLabel(location); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerItemLocation *itemLocation = new QDeclarativeOrganizerItemLocation(this); itemLocation->setLabel(location); m_details.append(itemLocation); m_modified = true; emit valueChanged(); } QString QDeclarativeOrganizerEvent::location() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Location == detail->type()) return static_cast(detail)->label(); } return QString::null; } /*! \qmlproperty Recurrence Event::recurrence This property holds the recurrence element of the event item. */ QDeclarativeOrganizerItemRecurrence *QDeclarativeOrganizerEvent::recurrence() { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Recurrence == detail->type()) return static_cast(detail); } QDeclarativeOrganizerItemRecurrence *detail = new QDeclarativeOrganizerItemRecurrence; m_details.append(detail); m_modified = true; emit valueChanged(); return detail; } /*! \qmltype EventOccurrence \instantiates QDeclarativeOrganizerEventOccurrence \brief The EventOccurrence element provides an occurrence of an event. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-items \sa OrganizerItem, Event, Journal, Todo, TodoOccurrence, Note, {QOrganizerEventOccurrence} */ /*! \qmlsignal EventOccurrence::onItemChanged() \sa OrganizerItem::onItemChanged */ /*! \internal */ QDeclarativeOrganizerEventOccurrence::QDeclarativeOrganizerEventOccurrence(QObject *parent) : QDeclarativeOrganizerItem(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(itemChanged())); setItem (QOrganizerEventOccurrence()); } /*! \qmlproperty date EventOccurrence::originalDate This property holds the date at which the occurrence was originally going to occur. */ void QDeclarativeOrganizerEventOccurrence::setOriginalDate(const QDateTime &date) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Parent == detail->type()) { QDeclarativeOrganizerItemParent *parent = static_cast(detail); if (parent->originalDate() != date) { parent->setOriginalDate(date); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerItemParent *parent = new QDeclarativeOrganizerItemParent(this); parent->setOriginalDate(date); m_details.append(parent); m_modified = true; emit valueChanged(); } QDateTime QDeclarativeOrganizerEventOccurrence::originalDate() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Parent == detail->type()) return static_cast(detail)->originalDate(); } return QDateTime(); } /*! \qmlproperty date EventOccurrence::startDateTime This property holds the start date time of the event occurrence. */ void QDeclarativeOrganizerEventOccurrence::setStartDateTime(const QDateTime &datetime) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::EventTime == detail->type()) { QDeclarativeOrganizerEventTime *eventTime = static_cast(detail); if (eventTime->startDateTime() != datetime) { eventTime->setStartDateTime(datetime); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerEventTime *eventTime = new QDeclarativeOrganizerEventTime(this); eventTime->setStartDateTime(datetime); m_details.append(eventTime); m_modified = true; emit valueChanged(); } QDateTime QDeclarativeOrganizerEventOccurrence::startDateTime() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::EventTime == detail->type()) return static_cast(detail)->startDateTime(); } return QDateTime(); } /*! \qmlproperty date EventOccurrence::endDateTime This property holds the date time at which the event occurrence ends. */ void QDeclarativeOrganizerEventOccurrence::setEndDateTime(const QDateTime &datetime) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::EventTime == detail->type()) { QDeclarativeOrganizerEventTime *eventTime = static_cast(detail); if (eventTime->endDateTime() != datetime) { eventTime->setEndDateTime(datetime); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerEventTime *eventTime = new QDeclarativeOrganizerEventTime(this); eventTime->setEndDateTime(datetime); m_details.append(eventTime); m_modified = true; emit valueChanged(); } QDateTime QDeclarativeOrganizerEventOccurrence::endDateTime() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::EventTime == detail->type()) return static_cast(detail)->endDateTime(); } return QDateTime(); } /*! \qmlproperty enumeration EventOccurrence::priority This property holds the priority of the event occurrence. The value can be one of: \list \li Priority.Unknown \li Priority.Highest \li Priority.ExtremelyHigh \li Priority.VeryHigh \li Priority.High \li Priority.Medium \li Priority.Low \li Priority.VeryLow \li Priority.ExtremelyLow \li Priority.Lowest \endlist */ void QDeclarativeOrganizerEventOccurrence::setPriority(QDeclarativeOrganizerItemPriority::Priority priority) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Priority == detail->type()) { QDeclarativeOrganizerItemPriority *itemPriority = static_cast(detail); if (itemPriority->priority() != priority) { itemPriority->setPriority(priority); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerItemPriority *itemPriority = new QDeclarativeOrganizerItemPriority(this); itemPriority->setPriority(priority); m_details.append(itemPriority); m_modified = true; emit valueChanged(); } QDeclarativeOrganizerItemPriority::Priority QDeclarativeOrganizerEventOccurrence::priority() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Priority == detail->type()) return static_cast(detail)->priority(); } return QDeclarativeOrganizerItemPriority::Unknown; } /*! \qmlproperty string EventOccurrence::location This property holds the label of the location at which the event occurrence is held. */ void QDeclarativeOrganizerEventOccurrence::setLocation(const QString &location) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Location == detail->type()) { QDeclarativeOrganizerItemLocation *itemLocation = static_cast(detail); if (itemLocation->label() != location) { itemLocation->setLabel(location); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerItemLocation *itemLocation = new QDeclarativeOrganizerItemLocation(this); itemLocation->setLabel(location); m_details.append(itemLocation); m_modified = true; emit valueChanged(); } QString QDeclarativeOrganizerEventOccurrence::location() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Location == detail->type()) return static_cast(detail)->label(); } return QString::null; } /*! \qmlproperty int EventOccurrence::parentId This property holds the id of the event which is this occurrence's parent. */ void QDeclarativeOrganizerEventOccurrence::setParentId(const QString &parentId) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Parent == detail->type()) { QDeclarativeOrganizerItemParent *parent = static_cast(detail); if (parent->parentId() != parentId) { parent->setParentId(parentId); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerItemParent *parent = new QDeclarativeOrganizerItemParent(this); parent->setParentId(parentId); m_details.append(parent); m_modified = true; emit valueChanged(); } QString QDeclarativeOrganizerEventOccurrence::parentId() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Parent == detail->type()) return static_cast(detail)->parentId(); } return QString::null; } /*! \qmlproperty bool EventOccurrence::allDay This property indicates whether the time-of-day component of the event occurrence's start date-time or end date-time is insignificant. If allDay is true, the time-of-day component is considered insignificant, and the event occurrence will be an all-day item. */ void QDeclarativeOrganizerEventOccurrence::setAllDay(bool allDay) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::EventTime == detail->type()) { QDeclarativeOrganizerEventTime *eventTime = static_cast(detail); if (eventTime->isAllDay() != allDay) { eventTime->setAllDay(allDay); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerEventTime *eventTime = new QDeclarativeOrganizerEventTime(this); eventTime->setAllDay(allDay); m_details.append(eventTime); m_modified = true; emit valueChanged(); } bool QDeclarativeOrganizerEventOccurrence::isAllDay() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::EventTime == detail->type()) return static_cast(detail)->isAllDay(); } return false; } /*! \qmltype Journal \instantiates QDeclarativeOrganizerJournal \brief The Journal element provides a journal which is associated with a particular point in time. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-items \sa OrganizerItem, Event, EventOccurrence, Todo, TodoOccurrence, Note, {QOrganizerJournal} */ /*! \qmlsignal Journal::onItemChanged() \sa OrganizerItem::onItemChanged */ /*! \internal */ QDeclarativeOrganizerJournal::QDeclarativeOrganizerJournal(QObject *parent) : QDeclarativeOrganizerItem(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(itemChanged())); setItem (QOrganizerJournal()); } /*! \qmlproperty date Journal::dateTime This property holds the date time associated with this journal. */ void QDeclarativeOrganizerJournal::setDateTime(const QDateTime &dateTime) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::JournalTime == detail->type()) { QDeclarativeOrganizerJournalTime *journalTime = static_cast(detail); if (journalTime->entryDateTime() != dateTime) { journalTime->setEntryDateTime(dateTime); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerJournalTime *journalTime = new QDeclarativeOrganizerJournalTime(this); journalTime->setEntryDateTime(dateTime); m_details.append(journalTime); m_modified = true; emit valueChanged(); } QDateTime QDeclarativeOrganizerJournal::dateTime() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::JournalTime == detail->type()) return static_cast(detail)->entryDateTime(); } return QDateTime(); } /*! \qmltype Note \instantiates QDeclarativeOrganizerNote \brief The Note element provides a note which is not associated with any particular point in time. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-items \sa OrganizerItem, Event, EventOccurrence, Journal, Todo, TodoOccurrence, {QOrganizerNote} */ /*! \qmlsignal Note::onItemChanged() \sa OrganizerItem::onItemChanged */ /*! \internal */ QDeclarativeOrganizerNote::QDeclarativeOrganizerNote(QObject *parent) : QDeclarativeOrganizerItem(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(itemChanged())); setItem(QOrganizerNote()); } /*! \qmltype Todo \instantiates QDeclarativeOrganizerTodo \brief The Todo element provides a task which should be completed. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-items \sa OrganizerItem, Event, EventOccurrence, Journal, TodoOccurrence, Note, {QOrganizerTodo} */ /*! \qmlsignal Todo::onItemChanged() \sa OrganizerItem::onItemChanged */ /*! \internal */ QDeclarativeOrganizerTodo::QDeclarativeOrganizerTodo(QObject *parent) : QDeclarativeOrganizerItem(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(itemChanged())); setItem(QOrganizerTodo()); } /*! \qmlproperty bool Todo::allDay This property indicates whether the time-of-day component of the Todo's start date-time or due date-time is insignificant. If allDay is true, the time-of-day component is considered insignificant, and the todo will be an all-day item. */ void QDeclarativeOrganizerTodo::setAllDay(bool allDay) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoTime == detail->type()) { QDeclarativeOrganizerTodoTime *todoTime = static_cast(detail); if (todoTime->isAllDay() != allDay) { todoTime->setAllDay(allDay); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerTodoTime *todoTime = new QDeclarativeOrganizerTodoTime(this); todoTime->setAllDay(allDay); m_details.append(todoTime); m_modified = true; emit valueChanged(); } bool QDeclarativeOrganizerTodo::isAllDay() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoTime == detail->type()) return static_cast(detail)->isAllDay(); } return false; } /*! \qmlproperty int Todo::percentageComplete This property holds the percentage of progress completed on the task described by the todo item. */ void QDeclarativeOrganizerTodo::setPercentageComplete(int percentageComplete) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoProgress == detail->type()) { QDeclarativeOrganizerTodoProgress *todoProgress = static_cast(detail); if (todoProgress->percentageComplete() != percentageComplete) { todoProgress->setPercentageComplete(percentageComplete); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerTodoProgress *todoProgress = new QDeclarativeOrganizerTodoProgress(this); todoProgress->setPercentageComplete(percentageComplete); m_details.append(todoProgress); m_modified = true; emit valueChanged(); } int QDeclarativeOrganizerTodo::percentageComplete() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoProgress == detail->type()) return static_cast(detail)->percentageComplete(); } return 0; } /*! \qmlproperty date Todo::startDateTime This property holds the date time at which the task should be started. */ void QDeclarativeOrganizerTodo::setStartDateTime(const QDateTime &dateTime) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoTime == detail->type()) { QDeclarativeOrganizerTodoTime *todoTime = static_cast(detail); if (todoTime->startDateTime() != dateTime) { todoTime->setStartDateTime(dateTime); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerTodoTime *todoTime = new QDeclarativeOrganizerTodoTime(this); todoTime->setStartDateTime(dateTime); m_details.append(todoTime); m_modified = true; emit valueChanged(); } QDateTime QDeclarativeOrganizerTodo::startDateTime() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoTime == detail->type()) return static_cast(detail)->startDateTime(); } return QDateTime(); } /*! \qmlproperty date Todo::dueDateTime This property holds the date time by which the task should be completed. */ void QDeclarativeOrganizerTodo::setDueDateTime(const QDateTime &dateTime) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoTime == detail->type()) { QDeclarativeOrganizerTodoTime *todoTime = static_cast(detail); if (todoTime->dueDateTime() != dateTime) { todoTime->setDueDateTime(dateTime); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerTodoTime *todoTime = new QDeclarativeOrganizerTodoTime(this); todoTime->setDueDateTime(dateTime); m_details.append(todoTime); m_modified = true; emit valueChanged(); } QDateTime QDeclarativeOrganizerTodo::dueDateTime() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoTime == detail->type()) return static_cast(detail)->dueDateTime(); } return QDateTime(); } /*! \qmlproperty date Todo::finishedDateTime This property holds the date and time at which the task was completed, if known. */ void QDeclarativeOrganizerTodo::setFinishedDateTime(const QDateTime &dateTime) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoProgress == detail->type()) { QDeclarativeOrganizerTodoProgress *todoProgress = static_cast(detail); if (todoProgress->finishedDateTime() != dateTime) { todoProgress->setFinishedDateTime(dateTime); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerTodoProgress *todoProgress = new QDeclarativeOrganizerTodoProgress(this); todoProgress->setFinishedDateTime(dateTime); m_details.append(todoProgress); m_modified = true; emit valueChanged(); } QDateTime QDeclarativeOrganizerTodo::finishedDateTime() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoProgress == detail->type()) return static_cast(detail)->finishedDateTime(); } return QDateTime(); } /*! \qmlproperty enumeration Todo::priority This property holds the priority of the todo item. The value can be one of: \list \li Priority.Unknown \li Priority.Highest \li Priority.ExtremelyHigh \li Priority.VeryHigh \li Priority.High \li Priority.Medium \li Priority.Low \li Priority.VeryLow \li Priority.ExtremelyLow \li Priority.Lowest \endlist */ void QDeclarativeOrganizerTodo::setPriority(QDeclarativeOrganizerItemPriority::Priority priority) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Priority == detail->type()) { QDeclarativeOrganizerItemPriority *itemPriority = static_cast(detail); if (itemPriority->priority() != priority) { itemPriority->setPriority(priority); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerItemPriority *itemPriority = new QDeclarativeOrganizerItemPriority(this); itemPriority->setPriority(priority); m_details.append(itemPriority); m_modified = true; emit valueChanged(); } QDeclarativeOrganizerItemPriority::Priority QDeclarativeOrganizerTodo::priority() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Priority == detail->type()) return static_cast(detail)->priority(); } return QDeclarativeOrganizerItemPriority::Unknown; } /*! \qmlproperty enumeration Todo::status This property holds the progress status of the task described by the todo. The value can be one of: \list \li TodoProgress.NotStarted \li TodoProgress.InProgress \li TodoProgress.Complete \endlist */ void QDeclarativeOrganizerTodo::setStatus(QDeclarativeOrganizerTodoProgress::StatusType status) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoProgress == detail->type()) { QDeclarativeOrganizerTodoProgress *todoProgress = static_cast(detail); if (todoProgress->status() != status) { todoProgress->setStatus(status); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerTodoProgress *todoProgress = new QDeclarativeOrganizerTodoProgress(this); todoProgress->setStatus(status); m_details.append(todoProgress); m_modified = true; emit valueChanged(); } QDeclarativeOrganizerTodoProgress::StatusType QDeclarativeOrganizerTodo::status() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoProgress == detail->type()) return static_cast(detail)->status(); } return QDeclarativeOrganizerTodoProgress::NotStarted; } /*! \qmlproperty Recurrence Todo::recurrence This property holds the recurrence element of the todo item. */ QDeclarativeOrganizerItemRecurrence* QDeclarativeOrganizerTodo::recurrence() { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Recurrence == detail->type()) return static_cast(detail); } QDeclarativeOrganizerItemRecurrence *detail = new QDeclarativeOrganizerItemRecurrence; m_details.append(detail); m_modified = true; emit valueChanged(); return detail; } /*! \qmltype TodoOccurrence \instantiates QDeclarativeOrganizerTodoOccurrence \brief The TodoOccurrence element provides an occurrence of an event. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-items \sa OrganizerItem, Event, EventOccurrence, Journal, Todo, Note, {QOrganizerTodoOccurrence} */ /*! \qmlsignal TodoOccurrence::onItemChanged() \sa OrganizerItem::onItemChanged */ QDeclarativeOrganizerTodoOccurrence::QDeclarativeOrganizerTodoOccurrence(QObject *parent) : QDeclarativeOrganizerItem(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(itemChanged())); setItem(QOrganizerTodoOccurrence()); } /*! \qmlproperty bool TodoOccurrence::allDay This property indicates whether the time-of-day component of the todo occurrence's start date-time or due date-time is insignificant. If allDay is true, the time-of-day component is considered insignificant, and the todo occurrence will be an all-day item. */ void QDeclarativeOrganizerTodoOccurrence::setAllDay(bool allDay) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoTime == detail->type()) { QDeclarativeOrganizerTodoTime *todoTime = static_cast(detail); if (todoTime->isAllDay() != allDay) { todoTime->setAllDay(allDay); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerTodoTime *todoTime = new QDeclarativeOrganizerTodoTime(this); todoTime->setAllDay(allDay); m_details.append(todoTime); m_modified = true; emit valueChanged(); } bool QDeclarativeOrganizerTodoOccurrence::isAllDay() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoTime == detail->type()) return static_cast(detail)->isAllDay(); } return false; } /*! \qmlproperty int TodoOccurrence::percentageComplete This property holds the percentage of progress completed on the task described by the todo item. */ void QDeclarativeOrganizerTodoOccurrence::setPercentageComplete(int percentageComplete) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoProgress == detail->type()) { QDeclarativeOrganizerTodoProgress *todoProgress = static_cast(detail); if (todoProgress->percentageComplete() != percentageComplete) { todoProgress->setPercentageComplete(percentageComplete); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerTodoProgress *todoProgress = new QDeclarativeOrganizerTodoProgress(this); todoProgress->setPercentageComplete(percentageComplete); m_details.append(todoProgress); m_modified = true; emit valueChanged(); } int QDeclarativeOrganizerTodoOccurrence::percentageComplete() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoProgress == detail->type()) return static_cast(detail)->percentageComplete(); } return 0; } /*! \qmlproperty date TodoOccurrence::originalDate This property holds the date at which the occurrence was originally going to occur. */ void QDeclarativeOrganizerTodoOccurrence::setOriginalDate(const QDateTime &date) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Parent == detail->type()) { QDeclarativeOrganizerItemParent *parent = static_cast(detail); if (parent->originalDate() != date) { parent->setOriginalDate(date); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerItemParent *parent = new QDeclarativeOrganizerItemParent(this); parent->setOriginalDate(date); m_details.append(parent); m_modified = true; emit valueChanged(); } QDateTime QDeclarativeOrganizerTodoOccurrence::originalDate() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Parent == detail->type()) return static_cast(detail)->originalDate(); } return QDateTime(); } /*! \qmlproperty date TodoOccurrence::startDateTime This property holds the date time at which the task should be started. */ void QDeclarativeOrganizerTodoOccurrence::setStartDateTime(const QDateTime &dateTime) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoTime == detail->type()) { QDeclarativeOrganizerTodoTime *todoTime = static_cast(detail); if (todoTime->startDateTime() != dateTime) { todoTime->setStartDateTime(dateTime); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerTodoTime *todoTime = new QDeclarativeOrganizerTodoTime(this); todoTime->setStartDateTime(dateTime); m_details.append(todoTime); m_modified = true; emit valueChanged(); } QDateTime QDeclarativeOrganizerTodoOccurrence::startDateTime() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoTime == detail->type()) return static_cast(detail)->startDateTime(); } return QDateTime(); } /*! \qmlproperty date TodoOccurrence::dueDateTime This property holds the date time by which the task should be completed. */ void QDeclarativeOrganizerTodoOccurrence::setDueDateTime(const QDateTime &dateTime) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoTime == detail->type()) { QDeclarativeOrganizerTodoTime *todoTime = static_cast(detail); if (todoTime->dueDateTime() != dateTime) { todoTime->setDueDateTime(dateTime); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerTodoTime *todoTime = new QDeclarativeOrganizerTodoTime(this); todoTime->setDueDateTime(dateTime); m_details.append(todoTime); m_modified = true; emit valueChanged(); } QDateTime QDeclarativeOrganizerTodoOccurrence::dueDateTime() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoTime == detail->type()) return static_cast(detail)->dueDateTime(); } return QDateTime(); } /*! \qmlproperty date TodoOccurrence::finishedDateTime This property holds the date and time at which the task was completed, if known. */ void QDeclarativeOrganizerTodoOccurrence::setFinishedDateTime(const QDateTime &dateTime) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoProgress == detail->type()) { QDeclarativeOrganizerTodoProgress *todoProgress = static_cast(detail); if (todoProgress->finishedDateTime() != dateTime) { todoProgress->setFinishedDateTime(dateTime); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerTodoProgress *todoProgress = new QDeclarativeOrganizerTodoProgress(this); todoProgress->setFinishedDateTime(dateTime); m_details.append(todoProgress); m_modified = true; emit valueChanged(); } QDateTime QDeclarativeOrganizerTodoOccurrence::finishedDateTime() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoProgress == detail->type()) return static_cast(detail)->finishedDateTime(); } return QDateTime(); } /*! \qmlproperty enumeration TodoOccurrence::priority This property holds the priority of the todo occurrence. The value can be one of: \list \li Priority.Unknown \li Priority.Highest \li Priority.ExtremelyHigh \li Priority.VeryHigh \li Priority.High \li Priority.Medium \li Priority.Low \li Priority.VeryLow \li Priority.ExtremelyLow \li Priority.Lowest \endlist */ void QDeclarativeOrganizerTodoOccurrence::setPriority(QDeclarativeOrganizerItemPriority::Priority priority) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Priority == detail->type()) { QDeclarativeOrganizerItemPriority *itemPriority = static_cast(detail); if (itemPriority->priority() != priority) { itemPriority->setPriority(priority); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerItemPriority *itemPriority = new QDeclarativeOrganizerItemPriority(this); itemPriority->setPriority(priority); m_details.append(itemPriority); m_modified = true; emit valueChanged(); } QDeclarativeOrganizerItemPriority::Priority QDeclarativeOrganizerTodoOccurrence::priority() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Priority == detail->type()) return static_cast(detail)->priority(); } return QDeclarativeOrganizerItemPriority::Unknown; } /*! \qmlproperty enumeration TodoOccurrence::status This property holds the progress status of the task described by the todo occurrence. The value can be one of: \list \li TodoProgress.NotStarted \li TodoProgress.InProgress \li TodoProgress.Complete \endlist */ void QDeclarativeOrganizerTodoOccurrence::setStatus(QDeclarativeOrganizerTodoProgress::StatusType status) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoProgress == detail->type()) { QDeclarativeOrganizerTodoProgress *todoProgress = static_cast(detail); if (todoProgress->status() != status) { todoProgress->setStatus(status); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerTodoProgress *todoProgress = new QDeclarativeOrganizerTodoProgress(this); todoProgress->setStatus(status); m_details.append(todoProgress); m_modified = true; emit valueChanged(); } QDeclarativeOrganizerTodoProgress::StatusType QDeclarativeOrganizerTodoOccurrence::status() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::TodoProgress == detail->type()) return static_cast(detail)->status(); } return QDeclarativeOrganizerTodoProgress::NotStarted; } /*! \qmlproperty int TodoOccurrence::parentId This property holds the id of the todo which is this occurrence's parent. */ void QDeclarativeOrganizerTodoOccurrence::setParentId(const QString &parentId) { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Parent == detail->type()) { QDeclarativeOrganizerItemParent *parent = static_cast(detail); if (parent->parentId() != parentId) { parent->setParentId(parentId); m_modified = true; emit valueChanged(); } return; } } QDeclarativeOrganizerItemParent *parent = new QDeclarativeOrganizerItemParent(this); parent->setParentId(parentId); m_details.append(parent); m_modified = true; emit valueChanged(); } QString QDeclarativeOrganizerTodoOccurrence::parentId() const { foreach (QDeclarativeOrganizerItemDetail *detail, m_details) { if (QDeclarativeOrganizerItemDetail::Parent == detail->type()) return static_cast(detail)->parentId(); } return QString::null; } #include "moc_qdeclarativeorganizeritem_p.cpp" QT_END_NAMESPACE src/imports/organizer/qdeclarativeorganizeritem_p.h000066400000000000000000000336771233466112000233140ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVEORGANIZERITEM_H #define QDECLARATIVEORGANIZERITEM_H #include #include #include "qdeclarativeorganizeritemdetail_p.h" QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeOrganizerItem : public QObject { Q_OBJECT // basic information Q_PROPERTY(bool modified READ modified NOTIFY itemChanged) Q_PROPERTY(QDeclarativeOrganizerItemType::ItemType itemType READ itemType NOTIFY itemChanged) Q_PROPERTY(QQmlListProperty itemDetails READ itemDetails NOTIFY itemChanged) Q_PROPERTY(QString itemId READ itemId NOTIFY itemChanged) Q_PROPERTY(QString manager READ manager NOTIFY itemChanged) Q_PROPERTY(QString collectionId READ collectionId WRITE setCollectionId NOTIFY itemChanged) // convenient access to most frequently used details Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY itemChanged) Q_PROPERTY(QString displayLabel READ displayLabel WRITE setDisplayLabel NOTIFY itemChanged) Q_PROPERTY(QString guid READ guid WRITE setGuid NOTIFY itemChanged) Q_CLASSINFO("DefaultProperty", "itemDetails") public: explicit QDeclarativeOrganizerItem(QObject *parent = 0); ~QDeclarativeOrganizerItem(); // basic information bool modified() const; QDeclarativeOrganizerItemType::ItemType itemType() const; QQmlListProperty itemDetails(); QString itemId() const; QString manager() const; QString collectionId() const; void setCollectionId(const QString& collectionId); // convenient access to most frequently used details QString displayLabel() const; void setDisplayLabel(const QString &label); QString description() const; void setDescription(const QString &description); QString guid() const; void setGuid(const QString& guid); // functions // use int instead of QDeclarativeOrganizerItemDetail::ItemDetailType as a work-around for QTBUG-20639 Q_INVOKABLE QDeclarativeOrganizerItemDetail *detail(int type); Q_INVOKABLE QVariantList details(int type); Q_INVOKABLE virtual void setDetail(QDeclarativeOrganizerItemDetail *detail); Q_INVOKABLE virtual void removeDetail(QDeclarativeOrganizerItemDetail *detail); Q_INVOKABLE virtual void clearDetails(); // non-QML APIs, used internal only bool _q_setDetail(QDeclarativeOrganizerItemDetail *detail); bool _q_removeDetail(QDeclarativeOrganizerItemDetail *detail); bool _q_clearDetails(); Q_INVOKABLE void save(); // non-QML APIs, used by model only void setItem(const QOrganizerItem &item); QOrganizerItem item() const; bool generatedOccurrence() const; QDateTime itemStartTime() const; QDateTime itemEndTime() const; Q_SIGNALS: void itemChanged(); protected: bool m_modified; QOrganizerItemId m_id; QOrganizerCollectionId m_collectionId; // always create a copy of the detail for QML // however, seems the garbage collection can't delete all of them (QTBUG-20377) QList m_details; private: Q_DISABLE_COPY(QDeclarativeOrganizerItem) // call-back functions for list property static void _q_detail_append(QQmlListProperty *property, QDeclarativeOrganizerItemDetail *value); static QDeclarativeOrganizerItemDetail *_q_detail_at(QQmlListProperty *property, int index); static void _q_detail_clear(QQmlListProperty *property); static int _q_detail_count(QQmlListProperty *property); }; class QDeclarativeOrganizerEvent : public QDeclarativeOrganizerItem { Q_OBJECT Q_PROPERTY(bool allDay READ isAllDay WRITE setAllDay NOTIFY valueChanged) Q_PROPERTY(QDateTime startDateTime READ startDateTime WRITE setStartDateTime NOTIFY valueChanged) Q_PROPERTY(QDateTime endDateTime READ endDateTime WRITE setEndDateTime NOTIFY valueChanged) Q_PROPERTY(QDeclarativeOrganizerItemPriority::Priority priority READ priority WRITE setPriority NOTIFY valueChanged) Q_PROPERTY(QDeclarativeOrganizerItemRecurrence *recurrence READ recurrence NOTIFY valueChanged) Q_PROPERTY(QString location READ location WRITE setLocation NOTIFY valueChanged) Q_PROPERTY(QQmlListProperty attendees READ attendees NOTIFY valueChanged) public: explicit QDeclarativeOrganizerEvent(QObject *parent = 0); QQmlListProperty attendees(); Q_INVOKABLE virtual void setDetail(QDeclarativeOrganizerItemDetail *detail); Q_INVOKABLE virtual void removeDetail(QDeclarativeOrganizerItemDetail *detail); Q_INVOKABLE virtual void clearDetails(); void setAllDay(bool isAllDay); bool isAllDay() const; void setStartDateTime(const QDateTime &startDateTime); QDateTime startDateTime() const; void setEndDateTime(const QDateTime &endDateTime); QDateTime endDateTime() const; void setPriority(QDeclarativeOrganizerItemPriority::Priority priority); QDeclarativeOrganizerItemPriority::Priority priority() const; void setLocation(const QString &location); QString location() const; QDeclarativeOrganizerItemRecurrence *recurrence(); Q_SIGNALS: void valueChanged(); private: // call-back functions for attendees list property static void _q_attendee_append(QQmlListProperty *property, QDeclarativeOrganizerEventAttendee *value); static QDeclarativeOrganizerEventAttendee *_q_attendee_at(QQmlListProperty *property, int index); static void _q_attendee_clear(QQmlListProperty *property); static int _q_attendee_count(QQmlListProperty *property); }; class QDeclarativeOrganizerEventOccurrence : public QDeclarativeOrganizerItem { Q_OBJECT Q_PROPERTY(bool allDay READ isAllDay WRITE setAllDay NOTIFY valueChanged) Q_PROPERTY(QDateTime originalDate READ originalDate WRITE setOriginalDate NOTIFY valueChanged) Q_PROPERTY(QDateTime startDateTime READ startDateTime WRITE setStartDateTime NOTIFY valueChanged) Q_PROPERTY(QDateTime endDateTime READ endDateTime WRITE setEndDateTime NOTIFY valueChanged) Q_PROPERTY(QDeclarativeOrganizerItemPriority::Priority priority READ priority WRITE setPriority NOTIFY valueChanged) Q_PROPERTY(QString location READ location WRITE setLocation NOTIFY valueChanged) Q_PROPERTY(QString parentId READ parentId WRITE setParentId NOTIFY valueChanged) public: explicit QDeclarativeOrganizerEventOccurrence(QObject *parent = 0); void setAllDay(bool isAllDay); bool isAllDay() const; void setOriginalDate(const QDateTime &date); QDateTime originalDate() const; void setStartDateTime(const QDateTime &startDateTime); QDateTime startDateTime() const; void setEndDateTime(const QDateTime &endDateTime); QDateTime endDateTime() const; void setPriority(QDeclarativeOrganizerItemPriority::Priority priority); QDeclarativeOrganizerItemPriority::Priority priority() const; void setLocation(const QString &location); QString location() const; void setParentId(const QString &parentId); QString parentId() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerJournal : public QDeclarativeOrganizerItem { Q_OBJECT Q_PROPERTY(QDateTime dateTime READ dateTime WRITE setDateTime NOTIFY valueChanged) public: explicit QDeclarativeOrganizerJournal(QObject *parent = 0); void setDateTime(const QDateTime &dateTime); QDateTime dateTime() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerNote : public QDeclarativeOrganizerItem { Q_OBJECT public: explicit QDeclarativeOrganizerNote(QObject *parent = 0); Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerTodo : public QDeclarativeOrganizerItem { Q_OBJECT Q_PROPERTY(bool allDay READ isAllDay WRITE setAllDay NOTIFY valueChanged) Q_PROPERTY(int percentageComplete READ percentageComplete WRITE setPercentageComplete NOTIFY valueChanged) Q_PROPERTY(QDateTime startDateTime READ startDateTime WRITE setStartDateTime NOTIFY valueChanged) Q_PROPERTY(QDateTime dueDateTime READ dueDateTime WRITE setDueDateTime NOTIFY valueChanged) Q_PROPERTY(QDateTime finishedDateTime READ finishedDateTime WRITE setFinishedDateTime NOTIFY valueChanged) Q_PROPERTY(QDeclarativeOrganizerItemPriority::Priority priority READ priority WRITE setPriority NOTIFY valueChanged) Q_PROPERTY(QDeclarativeOrganizerTodoProgress::StatusType status READ status WRITE setStatus NOTIFY valueChanged) Q_PROPERTY(QDeclarativeOrganizerItemRecurrence* recurrence READ recurrence NOTIFY valueChanged) public: explicit QDeclarativeOrganizerTodo(QObject *parent = 0); void setAllDay(bool isAllDay); bool isAllDay() const; void setPercentageComplete(int percentageComplete); int percentageComplete() const; void setStartDateTime(const QDateTime &startDateTime); QDateTime startDateTime() const; void setDueDateTime(const QDateTime &dueDateTime); QDateTime dueDateTime() const; void setFinishedDateTime(const QDateTime &finishedDateTime); QDateTime finishedDateTime() const; void setPriority(QDeclarativeOrganizerItemPriority::Priority priority); QDeclarativeOrganizerItemPriority::Priority priority() const; void setStatus(QDeclarativeOrganizerTodoProgress::StatusType status); QDeclarativeOrganizerTodoProgress::StatusType status() const; QDeclarativeOrganizerItemRecurrence *recurrence(); Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerTodoOccurrence : public QDeclarativeOrganizerItem { Q_OBJECT Q_PROPERTY(bool allDay READ isAllDay WRITE setAllDay NOTIFY valueChanged) Q_PROPERTY(int percentageComplete READ percentageComplete WRITE setPercentageComplete NOTIFY valueChanged) Q_PROPERTY(QDateTime originalDate READ originalDate WRITE setOriginalDate NOTIFY valueChanged) Q_PROPERTY(QDateTime startDateTime READ startDateTime WRITE setStartDateTime NOTIFY valueChanged) Q_PROPERTY(QDateTime dueDateTime READ dueDateTime WRITE setDueDateTime NOTIFY valueChanged) Q_PROPERTY(QDateTime finishedDateTime READ finishedDateTime WRITE setFinishedDateTime NOTIFY valueChanged) Q_PROPERTY(QDeclarativeOrganizerItemPriority::Priority priority READ priority WRITE setPriority NOTIFY valueChanged) Q_PROPERTY(QDeclarativeOrganizerTodoProgress::StatusType status READ status WRITE setStatus NOTIFY valueChanged) Q_PROPERTY(QString parentId READ parentId WRITE setParentId NOTIFY valueChanged) public: explicit QDeclarativeOrganizerTodoOccurrence(QObject *parent = 0); void setAllDay(bool isAllDay); bool isAllDay() const; void setPercentageComplete(int percentageComplete); int percentageComplete() const; void setOriginalDate(const QDateTime &date); QDateTime originalDate() const; void setStartDateTime(const QDateTime &startDateTime); QDateTime startDateTime() const; void setDueDateTime(const QDateTime &dueDateTime); QDateTime dueDateTime() const; void setFinishedDateTime(const QDateTime &finishedDateTime); QDateTime finishedDateTime() const; void setPriority(QDeclarativeOrganizerItemPriority::Priority priority); QDeclarativeOrganizerItemPriority::Priority priority() const; void setStatus(QDeclarativeOrganizerTodoProgress::StatusType status); QDeclarativeOrganizerTodoProgress::StatusType status() const; QString parentId() const; void setParentId(const QString &parentId); Q_SIGNALS: void valueChanged(); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeOrganizerItem) QML_DECLARE_TYPE(QDeclarativeOrganizerEvent) QML_DECLARE_TYPE(QDeclarativeOrganizerEventOccurrence) QML_DECLARE_TYPE(QDeclarativeOrganizerJournal) QML_DECLARE_TYPE(QDeclarativeOrganizerNote) QML_DECLARE_TYPE(QDeclarativeOrganizerTodo) QML_DECLARE_TYPE(QDeclarativeOrganizerTodoOccurrence) #endif // QDECLARATIVEORGANIZERITEM_H src/imports/organizer/qdeclarativeorganizeritemdetail.cpp000066400000000000000000002156501233466112000245040ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativeorganizeritemdetail_p.h" #include #include #include QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE /*! \qmltype Detail \instantiates QDeclarativeOrganizerItemDetail \brief The Detail element represents a single, complete detail about a organizer item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-main \sa QOrganizerItemDetail */ /*! \qmlsignal Detail::onDetailChanged() This signal is emitted, when any of the Details's or child element's (like EventTime, DisplayLabel etc) properties have been changed. */ /*! \internal */ QDeclarativeOrganizerItemDetail::QDeclarativeOrganizerItemDetail(QObject *parent) : QObject(parent) { } /*! \internal */ QDeclarativeOrganizerItemDetail::~QDeclarativeOrganizerItemDetail() { } /*! \qmlproperty enumeration Detail::type This property holds the type of the detail and is read only. It can be one of: \list \li Detail.Undefined \li Detail.Classification \li Detail.Comment \li Detail.Description \li Detail.DisplayLabel \li Detail.ItemType \li Detail.Guid \li Detail.Location \li Detail.Parent \li Detail.Priority \li Detail.Recurrence \li Detail.Tag \li Detail.Timestamp \li Detail.Version \li Detail.Reminder \li Detail.AudibleReminder \li Detail.EmailReminder \li Detail.VisualReminder \li Detail.ExtendedDetail \li Detail.EventAttendee \li Detail.EventRsvp \li Detail.EventTime \li Detail.JournalTime \li Detail.TodoTime \li Detail.TodoProgress \endlist \sa Classification, Comment, Description, DisplayLabel, ItemType, Guid, Location, Parent, Priority, Recurrence, Tag, Timestamp \sa Version, Reminder, AudibleReminder, EmailReminder, VisualReminder, ExtendedDetail, EventAttendee, EventRsvp, EventTime \sa JournalTime, TodoTime TodoProgress */ QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemDetail::type() const { return Undefined; } /*! \qmlmethod variant Detail::value(field) Returns the value stored in this detail for the given \a field, or an empty variant if not available. */ QVariant QDeclarativeOrganizerItemDetail::value(int field) const { return m_detail.value(field); } /*! \qmlmethod bool Detail::setValue(field, value) Inserts \a value into the detail for the given \a key if value is valid. If value is invalid, removes the field with the given key from the detail. Returns true if the given value was set for the key (if the value was valid), or if the given key was removed from detail (if the value was invalid), otherwise returns false if the key was unable to be removed (and the value was invalid). */ bool QDeclarativeOrganizerItemDetail::setValue(int field, const QVariant &value) { bool ok = m_detail.setValue(field, value); if (ok) emit detailChanged(); return ok; } /*! \qmlmethod bool Detail::removeValue(field) Removes the value stored in this detail for the given \a field. Returns true if a value was stored for the given key and the operation succeeded, and false otherwise. */ bool QDeclarativeOrganizerItemDetail::removeValue(int field) { bool ok = m_detail.removeValue(field); if (ok) emit detailChanged(); return ok; } // non-QML APIs /*! \internal */ QOrganizerItemDetail QDeclarativeOrganizerItemDetail::detail() const { return m_detail; } /*! \internal */ void QDeclarativeOrganizerItemDetail::setDetail(const QOrganizerItemDetail &detail) { m_detail = detail; emit detailChanged(); } /*! \qmltype EventTime \instantiates QDeclarativeOrganizerEventTime \brief The EventTime element contains the start and end dates and times of a recurring event series, or occurrence of an event. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li EventTime.FieldStartDateTime \li EventTime.FieldEndDateTime \li EventTime.FieldAllDay \endlist \sa QOrganizerEventTime */ /*! \qmlsignal EventTime::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerEventTime::QDeclarativeOrganizerEventTime(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerEventTime()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerEventTime::type() const { return QDeclarativeOrganizerItemDetail::EventTime; } /*! \qmlproperty date EventTime::allDay This property holds whether the time is significant in the start datetime. */ void QDeclarativeOrganizerEventTime::setAllDay(bool allDay) { if (allDay != isAllDay()) { m_detail.setValue(QOrganizerEventTime::FieldAllDay, allDay); emit valueChanged(); } } bool QDeclarativeOrganizerEventTime::isAllDay() { return m_detail.value(QOrganizerEventTime::FieldAllDay); } /*! \qmlproperty date EventTime::startDateTime This property holds the start date and time value of the event. */ void QDeclarativeOrganizerEventTime::setStartDateTime(const QDateTime &datetime) { if (datetime != startDateTime()) { m_detail.setValue(QOrganizerEventTime::FieldStartDateTime, datetime.toUTC()); emit valueChanged(); } } QDateTime QDeclarativeOrganizerEventTime::startDateTime() const { return m_detail.value(QOrganizerEventTime::FieldStartDateTime).toLocalTime(); } /*! \qmlproperty date EventTime::endDateTime This property holds the end date and time value of the event. */ void QDeclarativeOrganizerEventTime::setEndDateTime(const QDateTime &datetime) { if (datetime != endDateTime()) { m_detail.setValue(QOrganizerEventTime::FieldEndDateTime, datetime.toUTC()); emit valueChanged(); } } QDateTime QDeclarativeOrganizerEventTime::endDateTime() const { return m_detail.value(QOrganizerEventTime::FieldEndDateTime).toLocalTime(); } /*! \qmltype Comment \instantiates QDeclarativeOrganizerItemComment \brief The Comment element contains the comment text of an organizer item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li Comment.FieldComment \endlist \sa QOrganizerItemComment */ /*! \qmlsignal Comment::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemComment::QDeclarativeOrganizerItemComment(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerItemComment()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemComment::type() const { return QDeclarativeOrganizerItemDetail::Comment; } /*! \qmlproperty string Comment::comment This property holds the text of the comment. */ void QDeclarativeOrganizerItemComment::setComment(const QString &newComment) { if (newComment != comment()) { m_detail.setValue(QOrganizerItemComment::FieldComment, newComment); emit valueChanged(); } } QString QDeclarativeOrganizerItemComment::comment() const { return m_detail.value(QOrganizerItemComment::FieldComment).toString(); } /*! \qmltype Description \instantiates QDeclarativeOrganizerItemDescription \brief The Description element contains the description text of an organizer item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li Description.FieldDescription \endlist \sa QOrganizerItemDescription */ /*! \qmlsignal Description::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemDescription::QDeclarativeOrganizerItemDescription(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerItemDescription()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemDescription::type() const { return QDeclarativeOrganizerItemDetail::Description; } /*! \qmlproperty string Description::description This property holds the text of the description. */ void QDeclarativeOrganizerItemDescription::setDescription(const QString &desc) { if (desc != description()) { m_detail.setValue(QOrganizerItemDescription::FieldDescription, desc); emit valueChanged(); } } QString QDeclarativeOrganizerItemDescription::description() const { return m_detail.value(QOrganizerItemDescription::FieldDescription).toString(); } /*! \qmltype DisplayLabel \instantiates QDeclarativeOrganizerItemDisplayLabel \brief The DisplayLabel element contains the display label of an organizer item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li DisplayLabel.FieldLabel \endlist \sa QOrganizerItemDisplayLabel */ /*! \qmlsignal DisplayLabel::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemDisplayLabel::QDeclarativeOrganizerItemDisplayLabel(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerItemDisplayLabel()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemDisplayLabel::type() const { return QDeclarativeOrganizerItemDetail::DisplayLabel; } /*! \qmlproperty string DisplayLabel::label This property holds the display label text. */ void QDeclarativeOrganizerItemDisplayLabel::setLabel(const QString &newLabel) { if (newLabel != label()) { m_detail.setValue(QOrganizerItemDisplayLabel::FieldLabel, newLabel); emit valueChanged(); } } QString QDeclarativeOrganizerItemDisplayLabel::label() const { return m_detail.value(QOrganizerItemDisplayLabel::FieldLabel).toString(); } /*! \qmltype Guid \instantiates QDeclarativeOrganizerItemGuid \brief The Guid element contains the GUID string of an organizer item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li Guid.FieldGuid \endlist \sa QOrganizerItemGuid */ /*! \qmlsignal Guid::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemGuid::QDeclarativeOrganizerItemGuid(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerItemGuid()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemGuid::type() const { return QDeclarativeOrganizerItemDetail::Guid; } /*! \qmlproperty string Guid::guid This property holds the GUID string. */ void QDeclarativeOrganizerItemGuid::setGuid(const QString &newGuid) { if (newGuid != guid()) { m_detail.setValue(QOrganizerItemGuid::FieldGuid, newGuid); emit valueChanged(); } } QString QDeclarativeOrganizerItemGuid::guid() const { return m_detail.value(QOrganizerItemGuid::FieldGuid).toString(); } /*! \qmltype Location \instantiates QDeclarativeOrganizerItemLocation \brief The Location element contains information about a location which is related to the organizer item in some manner. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li Location.FieldLabel \li Location.FieldLatitude \li Location.FieldLongitude \endlist \sa QOrganizerItemLocation */ /*! \qmlsignal Location::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemLocation::QDeclarativeOrganizerItemLocation(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerItemLocation()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemLocation::type() const { return QDeclarativeOrganizerItemDetail::Location; } /*! \qmlproperty double Location::latitude This property holds the location latitude value. */ void QDeclarativeOrganizerItemLocation::setLatitude(double newLatitude) { if (!qFuzzyCompare(newLatitude, latitude())) { m_detail.setValue(QOrganizerItemLocation::FieldLatitude, newLatitude); emit valueChanged(); } } double QDeclarativeOrganizerItemLocation::latitude() const { return m_detail.value(QOrganizerItemLocation::FieldLatitude); } /*! \qmlproperty double Location::longitude This property holds the location longitude value . */ void QDeclarativeOrganizerItemLocation::setLongitude(double newLongitude) { if (!qFuzzyCompare(newLongitude, longitude())) { m_detail.setValue(QOrganizerItemLocation::FieldLongitude, newLongitude); emit valueChanged(); } } double QDeclarativeOrganizerItemLocation::longitude() const { return m_detail.value(QOrganizerItemLocation::FieldLongitude); } /*! \qmlproperty string Location::label This property holds the location label value. */ void QDeclarativeOrganizerItemLocation::setLabel(const QString &newLabel) { if (newLabel != label()) { m_detail.setValue(QOrganizerItemLocation::FieldLabel, newLabel); emit valueChanged(); } } QString QDeclarativeOrganizerItemLocation::label() const { return m_detail.value(QOrganizerItemLocation::FieldLabel).toString(); } /*! \qmltype Parent \instantiates QDeclarativeOrganizerItemParent \brief The Parent element contains information about the event or todo that generated this item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li Parent.FieldParentId \li Parent.FieldOriginalDate \endlist \sa QOrganizerItemParent */ /*! \qmlsignal Parent::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemParent::QDeclarativeOrganizerItemParent(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerItemParent()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemParent::type() const { return QDeclarativeOrganizerItemDetail::Parent; } /*! \qmlmethod variant Parent::value(field) \sa Detail::value */ QVariant QDeclarativeOrganizerItemParent::value(int field) const { switch (field) { case FieldParentId: { QString id = parentId(); return id.isNull() ? QVariant() : id; } case FieldOriginalDate: { QDateTime date = originalDate(); return date.isValid() ? date : QVariant(); } default: { return QVariant(); } } } /*! \qmlmethod bool Parent::setValue(field, value) \sa Detail::setValue */ bool QDeclarativeOrganizerItemParent::setValue(int field, const QVariant &value) { switch (field) { case FieldParentId: { if (value.canConvert()) { setParentId(value.toString()); return true; } break; } case FieldOriginalDate: { if (value.canConvert()) { setOriginalDate(value.toDateTime()); return true; } break; } default: { return false; } } return false; } /*! \qmlproperty date Parent::originalDate This property holds the original date of this instance origin item. */ void QDeclarativeOrganizerItemParent::setOriginalDate(const QDateTime &date) { if (date != originalDate()) { m_detail.setValue(QOrganizerItemParent::FieldOriginalDate, date.date()); emit valueChanged(); } } QDateTime QDeclarativeOrganizerItemParent::originalDate() const { QDateTime retDateTime(m_detail.value(QOrganizerItemParent::FieldOriginalDate).toDate(), QTime(0, 0, 0, 0), Qt::UTC); return retDateTime; } /*! \qmlproperty string Parent::parentId This property holds the organizer item id of the parent recurrent event or todo. */ void QDeclarativeOrganizerItemParent::setParentId(const QString &newParentId) { if (newParentId != parentId()) { m_detail.setValue(QOrganizerItemParent::FieldParentId, QVariant::fromValue(QOrganizerItemId::fromString(newParentId))); emit valueChanged(); } } QString QDeclarativeOrganizerItemParent::parentId() const { return m_detail.value(QOrganizerItemParent::FieldParentId).value().toString(); } /*! \qmltype Priority \instantiates QDeclarativeOrganizerItemPriority \brief The Priority element contains the priority of the organizer item, which may be used to resolve scheduling conflicts. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li Priority.FieldPriority \endlist \sa QOrganizerItemPriority */ /*! \qmlsignal Priority::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemPriority::QDeclarativeOrganizerItemPriority(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerItemPriority()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemPriority::type() const { return QDeclarativeOrganizerItemDetail::Priority; } /*! \qmlproperty enumeration Priority::priority This property holds the priority associated with an organizer item. The value can be one of: \list \li Priority.Unknown \li Priority.Highest \li Priority.ExtremelyHigh \li Priority.VeryHigh \li Priority.High \li Priority.Medium \li Priority.Low \li Priority.VeryLow \li Priority.ExtremelyLow \li Priority.Lowest \endlist */ void QDeclarativeOrganizerItemPriority::setPriority(QDeclarativeOrganizerItemPriority::Priority newPriority) { if (newPriority != priority()) { m_detail.setValue(QOrganizerItemPriority::FieldPriority, static_cast(newPriority)); emit valueChanged(); } } QDeclarativeOrganizerItemPriority::Priority QDeclarativeOrganizerItemPriority::priority() const { return static_cast(m_detail.value(QOrganizerItemPriority::FieldPriority)); } /*! \qmltype Recurrence \instantiates QDeclarativeOrganizerItemRecurrence \brief The Recurrence element contains a list of rules and dates on which the recurrent item occurs, and a list of rules and dates on which exceptions occur. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li Recurrence.FieldRecurrenceRules \li Recurrence.FieldExceptionRules \li Recurrence.FieldRecurrenceDates \li Recurrence.FieldExceptionDates \endlist */ /*! \qmlsignal Recurrence::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemRecurrence::QDeclarativeOrganizerItemRecurrence(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerItemRecurrence()); connect(this, SIGNAL(recurrenceRulesChanged()), SLOT(_saveRecurrenceRules())); connect(this, SIGNAL(exceptionRulesChanged()), SLOT(_saveExceptionRules())); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemRecurrence::type() const { return QDeclarativeOrganizerItemDetail::Recurrence; } /*! \qmlmethod variant Recurrence::value(field) \sa Detail::value */ QVariant QDeclarativeOrganizerItemRecurrence::value(int field) const { switch (field) { case FieldRecurrenceDates: { QVariantList rdates = recurrenceDates(); return rdates; } case FieldExceptionDates: { QVariantList edates = exceptionDates(); return edates; } default: { // TODO: proper handling of FieldRecurrenceRules and FieldExceptionRules --> conversion // from QSet to QVariantList return QVariant(); } } } /*! \qmlmethod bool Recurrence::setValue(field, value) \sa Detail::setValue */ bool QDeclarativeOrganizerItemRecurrence::setValue(int field, const QVariant &value) { switch (field) { case FieldRecurrenceDates: { if (value.canConvert()) { setRecurrenceDates(value.toList()); return true; } break; } case FieldExceptionDates: { if (value.canConvert()) { setExceptionDates(value.toList()); return true; } break; } default: { // TODO: proper handling of FieldRecurrenceRules and FieldExceptionRules --> conversion // from QVariantList to QSet return false; } } return false; } /*! \qmlproperty list Recurrence::recurrenceRules This property holds the list of recurrence rules. \sa RecurrenceRule */ QQmlListProperty QDeclarativeOrganizerItemRecurrence::recurrenceRules() { QSet ruleSet = m_detail.value(QOrganizerItemRecurrence::FieldRecurrenceRules).value< QSet >(); if (m_recurrenceRules.isEmpty() && !ruleSet.isEmpty()) { foreach (QOrganizerRecurrenceRule rule, ruleSet) { QDeclarativeOrganizerRecurrenceRule* drule = new QDeclarativeOrganizerRecurrenceRule(this); drule->setRule(rule); connect(drule, SIGNAL(recurrenceRuleChanged()), this, SLOT(_saveRecurrenceRules())); m_recurrenceRules.append(drule); } } return QQmlListProperty(this, &m_recurrenceRules, rrule_append, rule_count, rule_at, rrule_clear); } /*! \qmlproperty list Recurrence::exceptionRules This property holds the list of exception rules. \sa RecurrenceRule */ QQmlListProperty QDeclarativeOrganizerItemRecurrence::exceptionRules() { QSet ruleSet = m_detail.value(QOrganizerItemRecurrence::FieldExceptionRules).value< QSet >(); if (m_exceptionRules.isEmpty() && !ruleSet.isEmpty()) { foreach (QOrganizerRecurrenceRule rule, ruleSet) { QDeclarativeOrganizerRecurrenceRule* drule = new QDeclarativeOrganizerRecurrenceRule(this); drule->setRule(rule); connect(drule, SIGNAL(recurrenceRuleChanged()), this, SLOT(_saveExceptionRules())); m_exceptionRules.append(drule); } } return QQmlListProperty(this, &m_exceptionRules, xrule_append, rule_count, rule_at, xrule_clear); } /*! \qmlproperty list Recurrence::recurrenceDates This property holds the list of recurrence dates. */ void QDeclarativeOrganizerItemRecurrence::setRecurrenceDates(const QVariantList &dates) { if (dates != recurrenceDates()) { QSet dateSet; QVariant dateSetVariant; foreach (QVariant date, dates) { if (date.canConvert(QVariant::DateTime)) dateSet.insert(date.toDateTime().toUTC().date()); } dateSetVariant.setValue(dateSet); m_detail.setValue(QOrganizerItemRecurrence::FieldRecurrenceDates, dateSetVariant); emit valueChanged(); } } QVariantList QDeclarativeOrganizerItemRecurrence::recurrenceDates() const { QVariant dateSetVariant = m_detail.value(QOrganizerItemRecurrence::FieldRecurrenceDates); QSet dateSet = dateSetVariant.value >(); QVariantList dates; foreach (QDate date, dateSet) { QDateTime dateTime(date, QTime(0, 0, 0, 0), Qt::UTC); dates.append(QVariant(dateTime)); } return dates; } /*! \qmlproperty list Recurrence::exceptionDates This property holds the list of exception dates. */ void QDeclarativeOrganizerItemRecurrence::setExceptionDates(const QVariantList& dates) { if (dates != exceptionDates()) { QSet dateSet; QVariant dateSetVariant; foreach (QVariant date, dates) { if (date.canConvert(QVariant::DateTime)) dateSet.insert(date.toDateTime().toUTC().date()); } dateSetVariant.setValue(dateSet); m_detail.setValue(QOrganizerItemRecurrence::FieldExceptionDates, dateSetVariant); emit valueChanged(); } } QVariantList QDeclarativeOrganizerItemRecurrence::exceptionDates() const { QVariant dateSetVariant = m_detail.value(QOrganizerItemRecurrence::FieldExceptionDates); QSet dateSet = dateSetVariant.value >(); QVariantList dates; foreach (QDate date, dateSet) { QDateTime dateTime(date, QTime(0, 0, 0, 0), Qt::UTC); dates.append(QVariant(dateTime)); } return dates; } void QDeclarativeOrganizerItemRecurrence::_saveRecurrenceRules() { QSet rules; foreach (const QDeclarativeOrganizerRecurrenceRule *r, m_recurrenceRules) rules << r->rule(); m_detail.setValue(QOrganizerItemRecurrence::FieldRecurrenceRules, QVariant::fromValue(rules)); emit valueChanged(); } void QDeclarativeOrganizerItemRecurrence::_saveExceptionRules() { QSet rules; foreach (const QDeclarativeOrganizerRecurrenceRule *r, m_exceptionRules) rules << r->rule(); m_detail.setValue(QOrganizerItemRecurrence::FieldExceptionRules, QVariant::fromValue(rules)); emit valueChanged(); } void QDeclarativeOrganizerItemRecurrence::rrule_append(QQmlListProperty *p, QDeclarativeOrganizerRecurrenceRule *item) { QDeclarativeOrganizerItemRecurrence* recurrence = qobject_cast(p->object); connect(item, SIGNAL(recurrenceRuleChanged()), recurrence, SLOT(_saveRecurrenceRules())); static_cast *>(p->data)->append(item); emit recurrence->recurrenceRulesChanged(); } void QDeclarativeOrganizerItemRecurrence::xrule_append(QQmlListProperty *p, QDeclarativeOrganizerRecurrenceRule *item) { QDeclarativeOrganizerItemRecurrence* recurrence = qobject_cast(p->object); connect(item, SIGNAL(recurrenceRuleChanged()), recurrence, SLOT(_saveExceptionRules())); static_cast *>(p->data)->append(item); emit recurrence->exceptionRulesChanged(); } int QDeclarativeOrganizerItemRecurrence::rule_count(QQmlListProperty *p) { return static_cast*>(p->data)->count(); } QDeclarativeOrganizerRecurrenceRule* QDeclarativeOrganizerItemRecurrence::rule_at(QQmlListProperty *p, int idx) { return static_cast*>(p->data)->at(idx); } void QDeclarativeOrganizerItemRecurrence::rrule_clear(QQmlListProperty *p) { static_cast*>(p->data)->clear(); emit qobject_cast(p->object)->recurrenceRulesChanged(); } void QDeclarativeOrganizerItemRecurrence::xrule_clear(QQmlListProperty *p) { static_cast*>(p->data)->clear(); emit qobject_cast(p->object)->exceptionRulesChanged(); } /*! \qmltype Tag \instantiates QDeclarativeOrganizerItemTag \brief The Tag element contains the tag string of an organizer item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li Tag.FieldTag \endlist \sa QOrganizerItemTag */ /*! \qmlsignal Tag::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemTag::QDeclarativeOrganizerItemTag(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerItemTag()); } QDeclarativeOrganizerItemTag::DetailType QDeclarativeOrganizerItemTag::type() const { return QDeclarativeOrganizerItemDetail::Tag; } /*! \qmlproperty string Tag::tag This property holds the tag string. */ void QDeclarativeOrganizerItemTag::setTag(const QString &newTag) { if (newTag != tag()) { m_detail.setValue(QOrganizerItemTag::FieldTag, newTag); emit valueChanged(); } } QString QDeclarativeOrganizerItemTag::tag() const { return m_detail.value(QOrganizerItemTag::FieldTag).toString(); } /*! \qmltype Timestamp \instantiates QDeclarativeOrganizerItemTimestamp \brief The Timestamp element contains the created and last modified timestamp of an organizer item's creating date and time. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li Timestamp.FieldCreated \li Timestamp.FieldLastModified \endlist \sa QOrganizerItemTimestamp */ /*! \qmlsignal Timestamp::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemTimestamp::QDeclarativeOrganizerItemTimestamp(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerItemTimestamp()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemTimestamp::type() const { return QDeclarativeOrganizerItemDetail::Timestamp; } /*! \qmlproperty date Timestamp::created This property holds the value of the item's creation date and time. */ void QDeclarativeOrganizerItemTimestamp::setCreated(const QDateTime ×tamp) { if (timestamp != created()) { m_detail.setValue(QOrganizerItemTimestamp::FieldCreated, timestamp.toUTC()); emit valueChanged(); } } QDateTime QDeclarativeOrganizerItemTimestamp::created() const { return m_detail.value(QOrganizerItemTimestamp::FieldCreated).toLocalTime(); } /*! \qmlproperty date Timestamp::lastModified This property holds the value of the item's last modified date and time. */ void QDeclarativeOrganizerItemTimestamp::setLastModified(const QDateTime ×tamp) { if (timestamp != lastModified()) { m_detail.setValue(QOrganizerItemTimestamp::FieldLastModified, timestamp.toUTC()); emit valueChanged(); } } QDateTime QDeclarativeOrganizerItemTimestamp::lastModified() const { return m_detail.value(QOrganizerItemTimestamp::FieldLastModified).toLocalTime(); } /*! \qmltype ItemType \instantiates QDeclarativeOrganizerItemType \brief The ItemType element contains the type of an organizer item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li ItemType.FieldType \endlist \sa QOrganizerItemType */ /*! \qmlsignal ItemType::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemType::QDeclarativeOrganizerItemType(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerItemType()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemType::type() const { return QDeclarativeOrganizerItemDetail::ItemType; } /*! \qmlproperty enum ItemType::itemType This property holds the type of the item. The value can be one of: \list \li ItemType.Event \li ItemType.EventOccurrence \li ItemType.Todo \li ItemType.TodoOccurrence \li ItemType.Note \li ItemType.Journal \li ItemType.Customized \endlist */ void QDeclarativeOrganizerItemType::setItemType(ItemType newType) { if (newType != itemType()) { m_detail.setValue(QOrganizerItemType::FieldType, static_cast(newType)); emit valueChanged(); } } QDeclarativeOrganizerItemType::ItemType QDeclarativeOrganizerItemType::itemType() const { return static_cast(m_detail.value(QOrganizerItemType::FieldType).toInt()); } /*! \qmltype JournalTime \instantiates QDeclarativeOrganizerJournalTime \brief The JournalTime element contains the entry date and time of a journal item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li JournalTime.FieldEntryDateTime \endlist \sa QOrganizerJournalTime */ /*! \qmlsignal JournalTime::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerJournalTime::QDeclarativeOrganizerJournalTime(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerJournalTime()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerJournalTime::type() const { return QDeclarativeOrganizerItemDetail::JournalTime; } /*! \qmlproperty date JournalTime::entryDateTime This property holds the entry date and time value of the journal. */ void QDeclarativeOrganizerJournalTime::setEntryDateTime(const QDateTime &datetime) { if (datetime != entryDateTime()) { m_detail.setValue(QOrganizerJournalTime::FieldEntryDateTime, datetime.toUTC()); emit valueChanged(); } } QDateTime QDeclarativeOrganizerJournalTime::entryDateTime() const { return m_detail.value(QOrganizerJournalTime::FieldEntryDateTime).toLocalTime(); } /*! \qmltype TodoProgress \instantiates QDeclarativeOrganizerTodoProgress \brief The TodoProgress element contains information about the progress of a todo item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li TodoProgress.FieldStatus \li TodoProgress.FieldPercentage \li TodoProgress.FieldFinishedDateTime \endlist \sa QOrganizerTodoProgress */ /*! \qmlsignal TodoProgress::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerTodoProgress::QDeclarativeOrganizerTodoProgress(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerTodoProgress()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerTodoProgress::type() const { return QDeclarativeOrganizerItemDetail::TodoProgress; } /*! \qmlproperty int TodoProgress::percentageComplete This property holds the value which contains the current completion percentage of the todo item. */ void QDeclarativeOrganizerTodoProgress::setPercentageComplete(int newPercentageComplete) { if (newPercentageComplete != percentageComplete()) { if (newPercentageComplete >=0 && newPercentageComplete <= 100) { m_detail.setValue(QOrganizerTodoProgress::FieldPercentageComplete, newPercentageComplete); emit valueChanged(); } } } int QDeclarativeOrganizerTodoProgress::percentageComplete() const { return m_detail.value(QOrganizerTodoProgress::FieldPercentageComplete); } /*! \qmlproperty date TodoProgress::finishedDateTime This property holds the date time value which contains the date and time at which the todo item was completed. */ void QDeclarativeOrganizerTodoProgress::setFinishedDateTime(const QDateTime &datetime) { if (datetime != finishedDateTime()) { m_detail.setValue(QOrganizerTodoProgress::FieldFinishedDateTime, datetime.toUTC()); emit valueChanged(); } } QDateTime QDeclarativeOrganizerTodoProgress::finishedDateTime() const { return m_detail.value(QOrganizerTodoProgress::FieldFinishedDateTime).toLocalTime(); } /*! \qmlproperty enumeration TodoProgress::status This property holds the value which describes the current completion status of the todo item. The value can be one of: \list \li TodoProgress.NotStarted \li TodoProgress.InProgress \li TodoProgress.Complete \endlist */ void QDeclarativeOrganizerTodoProgress::setStatus(QDeclarativeOrganizerTodoProgress::StatusType newStatus) { if (newStatus != status()) { m_detail.setValue(QOrganizerTodoProgress::FieldStatus, (int) newStatus); emit valueChanged(); } } QDeclarativeOrganizerTodoProgress::StatusType QDeclarativeOrganizerTodoProgress::status() const { return (StatusType) m_detail.value(QOrganizerTodoProgress::FieldStatus); } /*! \qmltype TodoTime \instantiates QDeclarativeOrganizerTodoTime \brief The TodoTime element contains the start and due dates and times of a recurring todo series, or occurrence of an todo item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li TodoTime.FieldStartDateTime \li TodoTime.FieldDueDateTime \li TodoTime.FieldAllDay \endlist \sa QOrganizerTodoTime */ /*! \qmlsignal TodoTime::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerTodoTime::QDeclarativeOrganizerTodoTime(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerTodoTime()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerTodoTime::type() const { return QDeclarativeOrganizerItemDetail::TodoTime; } /*! \qmlproperty date TodoTime::allDay This property holds whether the time is significant in the start datetime. */ void QDeclarativeOrganizerTodoTime::setAllDay(bool allDay) { if (allDay != isAllDay()) { m_detail.setValue(QOrganizerTodoTime::FieldAllDay, allDay); emit valueChanged(); } } bool QDeclarativeOrganizerTodoTime::isAllDay() { return m_detail.value(QOrganizerTodoTime::FieldAllDay); } /*! \qmlproperty date TodoTime::startDateTime This property holds the start date and time value of the todo item. */ void QDeclarativeOrganizerTodoTime::setStartDateTime(const QDateTime &datetime) { if (datetime != startDateTime()) { m_detail.setValue(QOrganizerTodoTime::FieldStartDateTime, datetime.toUTC()); emit valueChanged(); } } QDateTime QDeclarativeOrganizerTodoTime::startDateTime() const { return m_detail.value(QOrganizerTodoTime::FieldStartDateTime).toLocalTime(); } /*! \qmlproperty date TodoTime::dueDateTime This property holds the end date and time value of the todo item. */ void QDeclarativeOrganizerTodoTime::setDueDateTime(const QDateTime &dateTime) { if (dateTime != dueDateTime()) { m_detail.setValue(QOrganizerTodoTime::FieldDueDateTime, dateTime.toUTC()); emit valueChanged(); } } QDateTime QDeclarativeOrganizerTodoTime::dueDateTime() const { return m_detail.value(QOrganizerTodoTime::FieldDueDateTime).toLocalTime(); } /*! \qmltype Reminder \instantiates QDeclarativeOrganizerItemReminder \brief The Reminder element contains information about when and how the user wants to reminded of the item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li Reminder.FieldRepetitionCount \li Reminder.FieldRepetitionDelay \li Reminder.FieldSecondsBeforeStart \endlist \sa QOrganizerItemReminder */ /*! \qmlsignal Reminder::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemReminder::QDeclarativeOrganizerItemReminder(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(reminderChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerItemReminder()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemReminder::type() const { return QDeclarativeOrganizerItemDetail::Reminder; } /*! \qmlproperty enumeration Reminder::reminderType This property holds the reminder type of this reminder for an organizer item. The value can be one of: \list \li Reminder.NoReminder \li Reminder.VisualReminder \li Reminder.AudibleReminder \li Reminder.EmailReminder \endlist */ QDeclarativeOrganizerItemReminder::ReminderType QDeclarativeOrganizerItemReminder::reminderType() const { if (m_detail.type() == QOrganizerItemDetail::TypeAudibleReminder) return QDeclarativeOrganizerItemReminder::AudibleReminder; else if (m_detail.type() == QOrganizerItemDetail::TypeEmailReminder) return QDeclarativeOrganizerItemReminder::EmailReminder; else if (m_detail.type() == QOrganizerItemDetail::TypeVisualReminder) return QDeclarativeOrganizerItemReminder::VisualReminder; else return QDeclarativeOrganizerItemReminder::NoReminder; } /*! \qmlproperty int Reminder::repetitionCount This property holds the number of times the user should be reminded of the item. */ void QDeclarativeOrganizerItemReminder::setRepetitionCount(int count) { if (count != repetitionCount()) { m_detail.setValue(QOrganizerItemReminder::FieldRepetitionCount, count); emit reminderChanged(); } } int QDeclarativeOrganizerItemReminder::repetitionCount() const { return m_detail.value(QOrganizerItemReminder::FieldRepetitionCount); } /*! \qmlproperty int Reminder::repetitionDelay This property holds the delay (in seconds) between each repetition of the reminder. */ void QDeclarativeOrganizerItemReminder::setRepetitionDelay(int delaySeconds) { if (delaySeconds != repetitionDelay()) { m_detail.setValue(QOrganizerItemReminder::FieldRepetitionDelay, delaySeconds); emit reminderChanged(); } } int QDeclarativeOrganizerItemReminder::repetitionDelay() const { return m_detail.value(QOrganizerItemReminder::FieldRepetitionDelay); } /*! \qmlproperty int Reminder::secondsBeforeStart This property holds the number of seconds prior to the activation of the item at which the user wants to be reminded of the item. */ void QDeclarativeOrganizerItemReminder::setSecondsBeforeStart(int seconds) { if (seconds != secondsBeforeStart() || !m_detail.hasValue(QOrganizerItemReminder::FieldSecondsBeforeStart)) { m_detail.setValue(QOrganizerItemReminder::FieldSecondsBeforeStart, seconds); emit reminderChanged(); } } int QDeclarativeOrganizerItemReminder::secondsBeforeStart() const { return m_detail.value(QOrganizerItemReminder::FieldSecondsBeforeStart); } /*! \qmltype AudibleReminder \instantiates QDeclarativeOrganizerItemAudibleReminder \brief The AudibleReminder element contains information about an audible reminder of an item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details \inherits Reminder The following fields are supported: \list \li AudibleReminder.FieldRepetitionCount \li AudibleReminder.FieldRepetitionDelay \li AudibleReminder.FieldSecondsBeforeStart \li AudibleReminder.FieldDataUrl \endlist \sa Reminder QOrganizerItemAudibleReminder */ /*! \qmlsignal AudibleReminder::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemAudibleReminder::QDeclarativeOrganizerItemAudibleReminder(QObject *parent) : QDeclarativeOrganizerItemReminder(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(reminderChanged())); setDetail(QOrganizerItemAudibleReminder()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemAudibleReminder::type() const { return QDeclarativeOrganizerItemDetail::AudibleReminder; } /*! \qmlproperty url AudibleReminder::dataUrl This property holds the url of the audible data to play. */ void QDeclarativeOrganizerItemAudibleReminder::setDataUrl(const QUrl &url) { if (url != dataUrl()) { m_detail.setValue(QOrganizerItemAudibleReminder::FieldDataUrl, url); emit valueChanged(); } } QUrl QDeclarativeOrganizerItemAudibleReminder::dataUrl() const { return m_detail.value(QOrganizerItemAudibleReminder::FieldDataUrl); } /*! \qmltype EmailReminder \instantiates QDeclarativeOrganizerItemEmailReminder \brief The EmailReminder element contains information about an email reminder of an item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details \inherits Reminder The following fields are supported: \list \li EmailReminder.FieldRepetitionCount \li EmailReminder.FieldRepetitionDelay \li EmailReminder.FieldSecondsBeforeStart \li EmailReminder.FieldSubject \li EmailReminder.FieldBody \li EmailReminder.FieldRecipients \li EmailReminder.FieldAttachments \endlist \sa Reminder QOrganizerItemEmailReminder */ /*! \qmlsignal EmailReminder::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemEmailReminder::QDeclarativeOrganizerItemEmailReminder(QObject *parent) : QDeclarativeOrganizerItemReminder(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(reminderChanged())); setDetail(QOrganizerItemEmailReminder()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemEmailReminder::type() const { return QDeclarativeOrganizerItemDetail::EmailReminder; } /*! \qmlproperty string EmailReminder::body This property holds the body of the email. */ void QDeclarativeOrganizerItemEmailReminder::setBody(const QString &newBody) { if (newBody != body()) { m_detail.setValue(QOrganizerItemEmailReminder::FieldBody, newBody); emit valueChanged(); } } QString QDeclarativeOrganizerItemEmailReminder::body() const { return m_detail.value(QOrganizerItemEmailReminder::FieldBody).toString(); } /*! \qmlproperty string EmailReminder::subject This property holds the subject of the email. */ void QDeclarativeOrganizerItemEmailReminder::setSubject(const QString &newSubject) { if (newSubject != subject()) { m_detail.setValue(QOrganizerItemEmailReminder::FieldSubject, newSubject); emit valueChanged(); } } QString QDeclarativeOrganizerItemEmailReminder::subject() const { return m_detail.value(QOrganizerItemEmailReminder::FieldSubject).toString(); } /*! \qmlproperty list EmailReminder::recipients This property holds the list of recipients that the user wishes to be sent an email as part of the reminder. */ void QDeclarativeOrganizerItemEmailReminder::setRecipients(const QStringList &newRecipients) { if (newRecipients != recipients()) { m_detail.setValue(QOrganizerItemEmailReminder::FieldRecipients, newRecipients); emit valueChanged(); } } QStringList QDeclarativeOrganizerItemEmailReminder::recipients() const { return m_detail.value(QOrganizerItemEmailReminder::FieldRecipients); } /*! \qmlproperty list EmailReminder::attachments This property holds the attachments of the email. */ void QDeclarativeOrganizerItemEmailReminder::setAttachments(const QVariantList &newAttachments) { if (newAttachments != attachments()) { m_detail.setValue(QOrganizerItemEmailReminder::FieldAttachments, newAttachments); emit valueChanged(); } } QVariantList QDeclarativeOrganizerItemEmailReminder::attachments() { return m_detail.value(QOrganizerItemEmailReminder::FieldAttachments); } /*! \qmltype VisualReminder \instantiates QDeclarativeOrganizerItemVisualReminder \brief The VisualReminder element contains information about a visual reminder of an item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details \inherits Reminder The following fields are supported: \list \li VisualReminder.FieldRepetitionCount \li VisualReminder.FieldRepetitionDelay \li VisualReminder.FieldSecondsBeforeStart \li VisualReminder.FieldDataUrl \li VisualReminder.FieldMessage \endlist \sa Reminder QOrganizerItemVisualReminder */ /*! \qmlsignal VisualReminder::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemVisualReminder::QDeclarativeOrganizerItemVisualReminder(QObject *parent) : QDeclarativeOrganizerItemReminder(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(reminderChanged())); setDetail(QOrganizerItemVisualReminder()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemVisualReminder::type() const { return QDeclarativeOrganizerItemDetail::VisualReminder; } /*! \qmlproperty string VisualReminder::message This property holds the message which the user wishes to be displayed as part of the reminder. */ void QDeclarativeOrganizerItemVisualReminder::setMessage(const QString &msg) { if (msg != message()) { m_detail.setValue(QOrganizerItemVisualReminder::FieldMessage, msg); emit valueChanged(); } } QString QDeclarativeOrganizerItemVisualReminder::message() const { return m_detail.value(QOrganizerItemVisualReminder::FieldMessage); } /*! \qmlproperty url VisualReminder::dataUrl This property holds the url of the visual data which the user wishes to be displayed as part of the reminder. */ void QDeclarativeOrganizerItemVisualReminder::setDataUrl(const QUrl &url) { if (url != dataUrl()) { m_detail.setValue(QOrganizerItemVisualReminder::FieldDataUrl, url); emit valueChanged(); } } QUrl QDeclarativeOrganizerItemVisualReminder::dataUrl() const { return m_detail.value(QOrganizerItemVisualReminder::FieldDataUrl); } /*! \qmltype ExtendedDetail \instantiates QDeclarativeOrganizeritemExtendedDetail \brief The ExtendedDetail element contains a extended detail of an organizer item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li ExtendedDetail.FieldName \li ExtendedDetail.FieldData \endlist \sa QOrganizerItemExtendedDetail */ /*! \qmlsignal ExtendedDetail::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemExtendedDetail::QDeclarativeOrganizerItemExtendedDetail(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerItemExtendedDetail()); } QDeclarativeOrganizerItemExtendedDetail::DetailType QDeclarativeOrganizerItemExtendedDetail::type() const { return QDeclarativeOrganizerItemDetail::ExtendedDetail; } /*! \qmlproperty string ExtendedDetail::name This property holds the name of the extended detail. */ void QDeclarativeOrganizerItemExtendedDetail::setName(const QString &newDetailName) { if (newDetailName != name()) { m_detail.setValue(QOrganizerItemExtendedDetail::FieldName, newDetailName); emit valueChanged(); } } QString QDeclarativeOrganizerItemExtendedDetail::name() const { return m_detail.value(QOrganizerItemExtendedDetail::FieldName).toString(); } /*! \qmlproperty variant ExtendedDetail::data This property holds the data of the extended detail. */ void QDeclarativeOrganizerItemExtendedDetail::setData(const QVariant &newData) { if (newData != data()) { setValue(QOrganizerItemExtendedDetail::FieldData, newData); emit valueChanged(); } } QVariant QDeclarativeOrganizerItemExtendedDetail::data() const { return m_detail.value(QOrganizerItemExtendedDetail::FieldData); } /*! \qmltype EventAttendee \instantiates QDeclarativeOrganizerEventAttendee \brief The EventAttendee element contains information about an attendee of an event. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The following fields are supported: \list \li EventAttendee.FieldName \li EventAttendee.FieldEmailAddress \li EventAttendee.FieldAddendeeId \li EventAttendee.FieldParticipationStatus \li EventAttendee.FieldParticipationRole \endlist \sa QOrganizerEventAttendee */ /*! \qmlsignal EventAttendee::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerEventAttendee::QDeclarativeOrganizerEventAttendee(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerEventAttendee()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerEventAttendee::type() const { return QDeclarativeOrganizerItemDetail::EventAttendee; } /*! \qmlproperty variant EventAttendee::name This property holds the name of the attendee. */ void QDeclarativeOrganizerEventAttendee::setName(const QString &newName) { if (name() != newName) { m_detail.setValue(QOrganizerEventAttendee::FieldName, newName); emit valueChanged(); } } QString QDeclarativeOrganizerEventAttendee::name() const { return m_detail.value(QOrganizerEventAttendee::FieldName).toString(); } /*! \qmlproperty variant EventAttendee::emailAddress This property holds the email address of the attendee. */ void QDeclarativeOrganizerEventAttendee::setEmailAddress(const QString &newEmailAddress) { if (emailAddress() != newEmailAddress) { m_detail.setValue(QOrganizerEventAttendee::FieldEmailAddress, newEmailAddress); emit valueChanged(); } } QString QDeclarativeOrganizerEventAttendee::emailAddress() const { return m_detail.value(QOrganizerEventAttendee::FieldEmailAddress).toString(); } /*! \qmlproperty variant EventAttendee::participationStatus This property holds the participation status of the attendee of the event. The value can be one of: \list \li EventAttendee.StatusUnknown \li EventAttendee.StatusAccepted \li EventAttendee.StatusDeclined \li EventAttendee.StatusTentative \li EventAttendee.StatusDelegated \li EventAttendee.StatusInProcess \li EventAttendee.StatusCompleted \endlist */ void QDeclarativeOrganizerEventAttendee::setParticipationStatus(ParticipationStatus status) { if (participationStatus() != status) { m_detail.setValue(QOrganizerEventAttendee::FieldParticipationStatus, status); emit valueChanged(); } } QDeclarativeOrganizerEventAttendee::ParticipationStatus QDeclarativeOrganizerEventAttendee::participationStatus() const { return static_cast(m_detail.value(QOrganizerEventAttendee::FieldParticipationStatus).toInt()); } /*! \qmlproperty variant EventAttendee::participationRole This property holds the participation role of the attendee of the event.The value can be one of: \list \li EventAttendee.RoleUnknown \li EventAttendee.RoleOrganizer \li EventAttendee.RoleChairperson \li EventAttendee.RoleHost \li EventAttendee.RoleRequiredParticipant \li EventAttendee.RoleOptionalParticipant \li EventAttendee.RoleNonParticipant \endlist */ void QDeclarativeOrganizerEventAttendee::setParticipationRole(ParticipationRole role) { if (participationRole() != role) { m_detail.setValue(QOrganizerEventAttendee::FieldParticipationRole, role); emit valueChanged(); } } QDeclarativeOrganizerEventAttendee::ParticipationRole QDeclarativeOrganizerEventAttendee::participationRole() const { return static_cast(m_detail.value(QOrganizerEventAttendee::FieldParticipationRole).toInt()); } /*! \qmlproperty variant EventAttendee::attendeeId This property holds the unique identifier of the attendee. */ void QDeclarativeOrganizerEventAttendee::setAttendeeId(const QString &newAttendeeId) { if (attendeeId() != newAttendeeId) { m_detail.setValue(QOrganizerEventAttendee::FieldAttendeeId, newAttendeeId); emit valueChanged(); } } QString QDeclarativeOrganizerEventAttendee::attendeeId() const { return m_detail.value(QOrganizerEventAttendee::FieldAttendeeId).toString(); } /*! \qmltype EventRsvp \instantiates QDeclarativeOrganizerEventRsvp \brief The EventRsvp element contains Rsvp-information of an event. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details EventRsvp detail contains user specific information about calendar event like participation status and role, information about response dates and information about organizer of the event. See more details from the properties themselves and the QOrganizerEventRsvp. \sa QOrganizerEventRsvp */ /*! \qmlsignal EventRsvp::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerEventRsvp::QDeclarativeOrganizerEventRsvp(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerEventRsvp()); } QDeclarativeOrganizerEventRsvp::DetailType QDeclarativeOrganizerEventRsvp::type() const { return QDeclarativeOrganizerItemDetail::EventRsvp; } /*! \qmlmethod variant EventRsvp::value(field) \sa Detail::value */ QVariant QDeclarativeOrganizerEventRsvp::value(int field) const { switch (field) { case FieldResponseDeadline: { QDateTime date = responseDeadline(); return date.isValid() ? date : QVariant(); } case FieldResponseDate: { QDateTime date = responseDate(); return date.isValid() ? date : QVariant(); } default: { return m_detail.value(field); } } } /*! \qmlmethod bool EventRsvp::setValue(field, value) \sa Detail::setValue */ bool QDeclarativeOrganizerEventRsvp::setValue(int field, const QVariant &value) { switch (field) { case FieldResponseDeadline: { if (value.canConvert()) { setResponseDeadline(value.toDateTime()); return true; } break; } case FieldResponseDate: { if (value.canConvert()) { setResponseDate(value.toDateTime()); return true; } break; } default: { if (m_detail.setValue(field, value)) return true; } } return false; } /*! \qmlproperty variant EventRsvp::participationStatus This property holds the calendar user's participation status related to the event. See EventAttendee::participationStatus for more details. \sa EventAttendee::participationStatus */ void QDeclarativeOrganizerEventRsvp::setParticipationStatus(QDeclarativeOrganizerEventAttendee::ParticipationStatus status) { if (participationStatus() != status) { m_detail.setValue(QOrganizerEventRsvp::FieldParticipationStatus, status); emit valueChanged(); } } QDeclarativeOrganizerEventAttendee::ParticipationStatus QDeclarativeOrganizerEventRsvp::participationStatus() const { return static_cast(m_detail.value(QOrganizerEventRsvp::FieldParticipationStatus).toInt()); } /*! \qmlproperty variant EventRsvp::participationRole This property holds the calendar user's participation role related to the event. See EventAttendee::participationRole for more details. \sa EventAttendee::participationRole */ void QDeclarativeOrganizerEventRsvp::setParticipationRole(QDeclarativeOrganizerEventAttendee::ParticipationRole role) { if (participationRole() != role) { m_detail.setValue(QOrganizerEventRsvp::FieldParticipationRole, role); emit valueChanged(); } } QDeclarativeOrganizerEventAttendee::ParticipationRole QDeclarativeOrganizerEventRsvp::participationRole() const { return static_cast(m_detail.value(QOrganizerEventRsvp::FieldParticipationRole).toInt()); } /*! \qmlproperty variant EventRsvp::responseRequirement This property holds the response requirement of the event. The value can be one of: \list \li EventRsvp.ResponseNotRequired \li EventRsvp.ResponseRequired \endlist */ void QDeclarativeOrganizerEventRsvp::setResponseRequirement(ResponseRequirement requirement) { if (responseRequirement() != requirement) { m_detail.setValue(QOrganizerEventRsvp::FieldResponseRequirement, requirement); emit valueChanged(); } } QDeclarativeOrganizerEventRsvp::ResponseRequirement QDeclarativeOrganizerEventRsvp::responseRequirement() const { return static_cast(m_detail.value(QOrganizerEventRsvp::FieldResponseRequirement).toInt()); } /*! \qmlproperty variant EventRsvp::responseDeadline This property holds the last date for responding the event. */ void QDeclarativeOrganizerEventRsvp::setResponseDeadline(const QDateTime &date) { if (responseDeadline() != date) { m_detail.setValue(QOrganizerEventRsvp::FieldResponseDeadline, date.toUTC().date()); emit valueChanged(); } } QDateTime QDeclarativeOrganizerEventRsvp::responseDeadline() const { QDateTime retDateTime(m_detail.value(QOrganizerEventRsvp::FieldResponseDeadline), QTime(0, 0, 0, 0), Qt::UTC); return retDateTime; } /*! \qmlproperty variant EventRsvp::responseDate This property holds the date when user responded to the event. */ void QDeclarativeOrganizerEventRsvp::setResponseDate(const QDateTime &date) { if (responseDate() != date) { m_detail.setValue(QOrganizerEventRsvp::FieldResponseDate, date.toUTC().date()); emit valueChanged(); } } QDateTime QDeclarativeOrganizerEventRsvp::responseDate() const { QDateTime retDateTime(m_detail.value(QOrganizerEventRsvp::FieldResponseDate), QTime(0, 0, 0, 0), Qt::UTC); return retDateTime; } /*! \qmlproperty variant EventRsvp::organizerName This property holds organizer's name of the event. */ void QDeclarativeOrganizerEventRsvp::setOrganizerName(const QString &name) { if (organizerName() != name) { m_detail.setValue(QOrganizerEventRsvp::FieldOrganizerName, name); emit valueChanged(); } } QString QDeclarativeOrganizerEventRsvp::organizerName() const { return m_detail.value(QOrganizerEventRsvp::FieldOrganizerName).toString(); } /*! \qmlproperty variant EventRsvp::organizerEmail This property holds organizer's email of the event. */ void QDeclarativeOrganizerEventRsvp::setOrganizerEmail(const QString &email) { if (organizerEmail() != email) { m_detail.setValue(QOrganizerEventRsvp::FieldOrganizerEmail, email); emit valueChanged(); } } QString QDeclarativeOrganizerEventRsvp::organizerEmail() const { return m_detail.value(QOrganizerEventRsvp::FieldOrganizerEmail).toString(); } /*! \qmltype Classification \instantiates QDeclarativeOrganizerItemClassification \brief The Classification element contains classification-information of an item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details The Classification detail contains classification related information. This can be used as a part of security model for the organizer. \sa QOrganizerItemClassification */ /*! \qmlsignal Classification::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemClassification::QDeclarativeOrganizerItemClassification(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerItemClassification()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemClassification::type() const { return QDeclarativeOrganizerItemDetail::Classification; } /*! \qmlproperty enumeration Classification::classification This property holds the calendar item's classification related information. The value can be one of: \list \li Classification.AccessPublic \li Classification.AccessConfidential \li Classification.AccessPrivate \endlist */ void QDeclarativeOrganizerItemClassification::setClassification(AccessClassification newClassification) { if (classification() != newClassification) { m_detail.setValue(QOrganizerItemClassification::FieldClassification, newClassification); emit valueChanged(); } } QDeclarativeOrganizerItemClassification::AccessClassification QDeclarativeOrganizerItemClassification::classification() const { return static_cast(m_detail.value(QOrganizerItemClassification::FieldClassification).toInt()); } /*! \qmltype Version \instantiates QDeclarativeOrganizerItemVersion \brief The Version element contains versioning information of an organizer item. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-details \sa QOrganizerItemVersion */ /*! \qmlsignal Version::onDetailChanged() \sa Detail::onDetailChanged */ QDeclarativeOrganizerItemVersion::QDeclarativeOrganizerItemVersion(QObject *parent) : QDeclarativeOrganizerItemDetail(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(detailChanged())); setDetail(QOrganizerItemVersion()); } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemVersion::type() const { return QDeclarativeOrganizerItemDetail::Version; } /*! \qmlproperty int Version::version This property holds the integer version of an organizer item, which can be used as the sequence number as per iCalendar spec. */ void QDeclarativeOrganizerItemVersion::setVersion(int newVersion) { if (version() != newVersion) { m_detail.setValue(QOrganizerItemVersion::FieldVersion, newVersion); emit valueChanged(); } } int QDeclarativeOrganizerItemVersion::version() const { return m_detail.value(QOrganizerItemVersion::FieldVersion).toInt(); } /*! \qmlproperty string Version::extendedVersion This property holds the extended version of an organizer item, which can be used to represent the version stored in the back-end. */ void QDeclarativeOrganizerItemVersion::setExtendedVersion(const QString &newExtendedVersion) { if (extendedVersion() != newExtendedVersion) { m_detail.setValue(QOrganizerItemVersion::FieldExtendedVersion, newExtendedVersion); emit valueChanged(); } } QString QDeclarativeOrganizerItemVersion::extendedVersion() const { QByteArray version = m_detail.value(QOrganizerItemVersion::FieldExtendedVersion).toByteArray(); return QString::fromLatin1(version.constData(), version.length()); } QDeclarativeOrganizerItemDetail *QDeclarativeOrganizerItemDetailFactory::createItemDetail(QDeclarativeOrganizerItemDetail::DetailType type) { QDeclarativeOrganizerItemDetail *itemDetail; if (type == QDeclarativeOrganizerItemDetail::EventTime) itemDetail = new QDeclarativeOrganizerEventTime; else if (type == QDeclarativeOrganizerItemDetail::AudibleReminder) itemDetail = new QDeclarativeOrganizerItemAudibleReminder; else if (type == QDeclarativeOrganizerItemDetail::Comment) itemDetail = new QDeclarativeOrganizerItemComment; else if (type == QDeclarativeOrganizerItemDetail::Description) itemDetail = new QDeclarativeOrganizerItemDescription; else if (type == QDeclarativeOrganizerItemDetail::DisplayLabel) itemDetail = new QDeclarativeOrganizerItemDisplayLabel; else if (type == QDeclarativeOrganizerItemDetail::EmailReminder) itemDetail = new QDeclarativeOrganizerItemEmailReminder; else if (type == QDeclarativeOrganizerItemDetail::Guid) itemDetail = new QDeclarativeOrganizerItemGuid; else if (type == QDeclarativeOrganizerItemDetail::Location) itemDetail = new QDeclarativeOrganizerItemLocation; else if (type == QDeclarativeOrganizerItemDetail::Parent) itemDetail = new QDeclarativeOrganizerItemParent; else if (type == QDeclarativeOrganizerItemDetail::Priority) itemDetail = new QDeclarativeOrganizerItemPriority; else if (type == QDeclarativeOrganizerItemDetail::Recurrence) itemDetail = new QDeclarativeOrganizerItemRecurrence; else if (type == QDeclarativeOrganizerItemDetail::Reminder) itemDetail = new QDeclarativeOrganizerItemReminder; else if (type == QDeclarativeOrganizerItemDetail::Tag) itemDetail = new QDeclarativeOrganizerItemTag; else if (type == QDeclarativeOrganizerItemDetail::Timestamp) itemDetail = new QDeclarativeOrganizerItemTimestamp; else if (type == QDeclarativeOrganizerItemDetail::ItemType) itemDetail = new QDeclarativeOrganizerItemType; else if (type == QDeclarativeOrganizerItemDetail::VisualReminder) itemDetail = new QDeclarativeOrganizerItemVisualReminder; else if (type == QDeclarativeOrganizerItemDetail::JournalTime) itemDetail = new QDeclarativeOrganizerJournalTime; else if (type == QDeclarativeOrganizerItemDetail::TodoProgress) itemDetail = new QDeclarativeOrganizerTodoProgress; else if (type == QDeclarativeOrganizerItemDetail::TodoTime) itemDetail = new QDeclarativeOrganizerTodoTime; else if (type == QDeclarativeOrganizerItemDetail::ExtendedDetail) itemDetail = new QDeclarativeOrganizerItemExtendedDetail; else if (type == QDeclarativeOrganizerItemDetail::EventAttendee) itemDetail = new QDeclarativeOrganizerEventAttendee; else if (type == QDeclarativeOrganizerItemDetail::EventRsvp) itemDetail = new QDeclarativeOrganizerEventRsvp; else if (type == QDeclarativeOrganizerItemDetail::Classification) itemDetail = new QDeclarativeOrganizerItemClassification; else if (type == QDeclarativeOrganizerItemDetail::Version) itemDetail = new QDeclarativeOrganizerItemVersion; else itemDetail = new QDeclarativeOrganizerItemDetail; return itemDetail; } #include "moc_qdeclarativeorganizeritemdetail_p.cpp" QT_END_NAMESPACE src/imports/organizer/qdeclarativeorganizeritemdetail_p.h000066400000000000000000000762701233466112000244730ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVEORGANIZERITEMDETAIL_H #define QDECLARATIVEORGANIZERITEMDETAIL_H #include #include #include "qdeclarativeorganizerrecurrencerule_p.h" QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeOrganizerItemDetail : public QObject { Q_OBJECT Q_ENUMS(DetailType) Q_PROPERTY(DetailType type READ type) public: enum DetailType { Undefined = QOrganizerItemDetail::TypeUndefined, Classification = QOrganizerItemDetail::TypeClassification, Comment = QOrganizerItemDetail::TypeComment, Description = QOrganizerItemDetail::TypeDescription, DisplayLabel = QOrganizerItemDetail::TypeDisplayLabel, ItemType = QOrganizerItemDetail::TypeItemType, Guid = QOrganizerItemDetail::TypeGuid, Location = QOrganizerItemDetail::TypeLocation, Parent = QOrganizerItemDetail::TypeParent, Priority = QOrganizerItemDetail::TypePriority, Recurrence = QOrganizerItemDetail::TypeRecurrence, Tag = QOrganizerItemDetail::TypeTag, Timestamp = QOrganizerItemDetail::TypeTimestamp, Version = QOrganizerItemDetail::TypeVersion, Reminder = QOrganizerItemDetail::TypeReminder, AudibleReminder = QOrganizerItemDetail::TypeAudibleReminder, EmailReminder = QOrganizerItemDetail::TypeEmailReminder, VisualReminder = QOrganizerItemDetail::TypeVisualReminder, ExtendedDetail = QOrganizerItemDetail::TypeExtendedDetail, EventAttendee = QOrganizerItemDetail::TypeEventAttendee, EventRsvp = QOrganizerItemDetail::TypeEventRsvp, EventTime = QOrganizerItemDetail::TypeEventTime, JournalTime = QOrganizerItemDetail::TypeJournalTime, TodoTime = QOrganizerItemDetail::TypeTodoTime, TodoProgress = QOrganizerItemDetail::TypeTodoProgress }; explicit QDeclarativeOrganizerItemDetail(QObject *parent = 0); ~QDeclarativeOrganizerItemDetail(); virtual DetailType type() const; // QML functions Q_INVOKABLE virtual QVariant value(int key) const; Q_INVOKABLE virtual bool setValue(int key, const QVariant& value); Q_INVOKABLE bool removeValue(int key); // non-QML APIs QOrganizerItemDetail detail() const; void setDetail(const QOrganizerItemDetail &detail); Q_SIGNALS: void detailChanged(); protected: QOrganizerItemDetail m_detail; private: Q_DISABLE_COPY(QDeclarativeOrganizerItemDetail) }; class QDeclarativeOrganizerEventTime : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(EventTimeField) Q_PROPERTY(bool allDay READ isAllDay WRITE setAllDay NOTIFY valueChanged) Q_PROPERTY(QDateTime startDateTime READ startDateTime WRITE setStartDateTime NOTIFY valueChanged) Q_PROPERTY(QDateTime endDateTime READ endDateTime WRITE setEndDateTime NOTIFY valueChanged) public: enum EventTimeField { FieldStartDateTime = QOrganizerEventTime::FieldStartDateTime, FieldEndDateTime = QOrganizerEventTime::FieldEndDateTime, FieldAllDay = QOrganizerEventTime::FieldAllDay }; QDeclarativeOrganizerEventTime(QObject *parent = 0); virtual DetailType type() const; void setAllDay(bool isAllDay); bool isAllDay(); void setStartDateTime(const QDateTime &datetime); QDateTime startDateTime() const; void setEndDateTime(const QDateTime &datetime); QDateTime endDateTime() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerItemComment : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(CommentField) Q_PROPERTY(QString comment READ comment WRITE setComment NOTIFY valueChanged) public: enum CommentField { FieldComment = QOrganizerItemComment::FieldComment }; QDeclarativeOrganizerItemComment(QObject *parent = 0); virtual DetailType type() const; void setComment(const QString &newComment); QString comment() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerItemDescription : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(DescriptionField) Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY valueChanged) public: enum DescriptionField { FieldDescription = QOrganizerItemDescription::FieldDescription }; QDeclarativeOrganizerItemDescription(QObject *parent = 0); virtual DetailType type() const; void setDescription(const QString &desc); QString description() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerItemDisplayLabel : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(DisplayLabelField) Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY valueChanged) public: enum DisplayLabelField { FieldLabel = QOrganizerItemDisplayLabel::FieldLabel }; QDeclarativeOrganizerItemDisplayLabel(QObject *parent = 0); virtual DetailType type() const; void setLabel(const QString &newLabel); QString label() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerItemGuid : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(GuidField) Q_PROPERTY(QString guid READ guid WRITE setGuid NOTIFY valueChanged) public: enum GuidField { FieldGuid = QOrganizerItemGuid::FieldGuid }; QDeclarativeOrganizerItemGuid(QObject *parent = 0); virtual DetailType type() const; void setGuid(const QString &newGuid); QString guid() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerItemLocation : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(LocationField) Q_PROPERTY(double latitude READ latitude WRITE setLatitude NOTIFY valueChanged) Q_PROPERTY(double longitude READ longitude WRITE setLongitude NOTIFY valueChanged) Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY valueChanged) public: enum LocationField { FieldLabel = QOrganizerItemLocation::FieldLabel, FieldLatitude = QOrganizerItemLocation::FieldLatitude, FieldLongitude = QOrganizerItemLocation::FieldLongitude }; QDeclarativeOrganizerItemLocation(QObject *parent = 0); virtual DetailType type() const; void setLatitude(double newLatitude); double latitude() const; void setLongitude(double newLongitude); double longitude() const; void setLabel(const QString &newLabel); QString label() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerItemParent : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(ParentField) Q_PROPERTY(QDateTime originalDate READ originalDate WRITE setOriginalDate NOTIFY valueChanged) Q_PROPERTY(QString parentId READ parentId WRITE setParentId NOTIFY valueChanged) public: enum ParentField { FieldParentId = QOrganizerItemParent::FieldParentId, FieldOriginalDate = QOrganizerItemParent::FieldOriginalDate }; QDeclarativeOrganizerItemParent(QObject *parent = 0); virtual DetailType type() const; virtual QVariant value(int field) const; virtual bool setValue(int key, const QVariant& value); void setOriginalDate(const QDateTime &date); QDateTime originalDate() const; void setParentId(const QString &newParentId); QString parentId() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerItemPriority : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(PriorityField) Q_ENUMS(Priority) Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY valueChanged) public: enum PriorityField { FieldPriority = QOrganizerItemPriority::FieldPriority }; enum Priority { Unknown = QOrganizerItemPriority::UnknownPriority, Highest = QOrganizerItemPriority::HighestPriority, ExtremelyHigh = QOrganizerItemPriority::ExtremelyHighPriority, VeryHigh = QOrganizerItemPriority::VeryHighPriority, High = QOrganizerItemPriority::HighPriority, Medium = QOrganizerItemPriority::MediumPriority, Low = QOrganizerItemPriority::LowPriority, VeryLow = QOrganizerItemPriority::VeryLowPriority, ExtremelyLow = QOrganizerItemPriority::ExtremelyLowPriority, Lowest = QOrganizerItemPriority::LowestPriority }; QDeclarativeOrganizerItemPriority(QObject *parent = 0); virtual DetailType type() const; void setPriority(Priority newPriority); Priority priority() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerItemRecurrence : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(RecurrenceField) Q_PROPERTY(QQmlListProperty recurrenceRules READ recurrenceRules NOTIFY recurrenceRulesChanged) Q_PROPERTY(QQmlListProperty exceptionRules READ exceptionRules NOTIFY exceptionRulesChanged) Q_PROPERTY(QVariantList recurrenceDates READ recurrenceDates WRITE setRecurrenceDates NOTIFY valueChanged) Q_PROPERTY(QVariantList exceptionDates READ exceptionDates WRITE setExceptionDates NOTIFY valueChanged) public: enum RecurrenceField { FieldRecurrenceRules = QOrganizerItemRecurrence::FieldRecurrenceRules, FieldExceptionRules = QOrganizerItemRecurrence::FieldExceptionRules, FieldRecurrenceDates = QOrganizerItemRecurrence::FieldRecurrenceDates, FieldExceptionDates = QOrganizerItemRecurrence::FieldExceptionDates }; QDeclarativeOrganizerItemRecurrence(QObject *parent = 0); virtual DetailType type() const; virtual QVariant value(int field) const; virtual bool setValue(int key, const QVariant& value); QQmlListProperty recurrenceRules(); QQmlListProperty exceptionRules(); void setRecurrenceDates(const QVariantList &dates); QVariantList recurrenceDates() const; void setExceptionDates(const QVariantList &dates); QVariantList exceptionDates() const; Q_SIGNALS: void recurrenceRulesChanged(); void exceptionRulesChanged(); void valueChanged(); private Q_SLOTS: void _saveRecurrenceRules(); void _saveExceptionRules(); private: static void rrule_append(QQmlListProperty *p, QDeclarativeOrganizerRecurrenceRule *item); static void xrule_append(QQmlListProperty *p, QDeclarativeOrganizerRecurrenceRule *item); static int rule_count(QQmlListProperty *p); static QDeclarativeOrganizerRecurrenceRule *rule_at(QQmlListProperty *p, int idx); static void rrule_clear(QQmlListProperty *p); static void xrule_clear(QQmlListProperty *p); QList m_recurrenceRules; QList m_exceptionRules; }; class QDeclarativeOrganizerItemTag : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(TagField) Q_PROPERTY(QString tag READ tag WRITE setTag NOTIFY valueChanged) public: enum TagField { FieldTag = QOrganizerItemTag::FieldTag }; QDeclarativeOrganizerItemTag(QObject *parent = 0); virtual DetailType type() const; void setTag(const QString &newTag); QString tag() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerItemTimestamp : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(TimestampField) Q_PROPERTY(QDateTime created READ created WRITE setCreated NOTIFY valueChanged) Q_PROPERTY(QDateTime lastModified READ lastModified WRITE setLastModified NOTIFY valueChanged) public: enum TimestampField { FieldCreated = QOrganizerItemTimestamp::FieldCreated, FieldLastModified = QOrganizerItemTimestamp::FieldLastModified }; QDeclarativeOrganizerItemTimestamp(QObject *parent = 0); virtual DetailType type() const; void setCreated(const QDateTime ×tamp); QDateTime created() const; void setLastModified(const QDateTime ×tamp); QDateTime lastModified() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerItemType : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(ItemTypeField) Q_ENUMS(ItemType) Q_PROPERTY(ItemType itemType READ itemType WRITE setItemType NOTIFY valueChanged) public: enum ItemTypeField { FieldType = QOrganizerItemType::FieldType }; enum ItemType { Undefined = QOrganizerItemType::TypeUndefined, Event = QOrganizerItemType::TypeEvent, EventOccurrence = QOrganizerItemType::TypeEventOccurrence, Todo = QOrganizerItemType::TypeTodo, TodoOccurrence = QOrganizerItemType::TypeTodoOccurrence, Journal = QOrganizerItemType::TypeJournal, Note = QOrganizerItemType::TypeNote }; QDeclarativeOrganizerItemType(QObject *parent = 0); virtual DetailType type() const; void setItemType(ItemType newType); ItemType itemType() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerJournalTime : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(JournalTimeField) Q_PROPERTY(QDateTime entryDateTime READ entryDateTime WRITE setEntryDateTime NOTIFY valueChanged) public: enum JournalTimeField { FieldEntryDateTime = QOrganizerJournalTime::FieldEntryDateTime }; QDeclarativeOrganizerJournalTime(QObject *parent = 0); virtual DetailType type() const; void setEntryDateTime(const QDateTime &datetime); QDateTime entryDateTime() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerTodoProgress : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(TodoProgressField) Q_ENUMS(StatusType) Q_PROPERTY(int percentageComplete READ percentageComplete WRITE setPercentageComplete NOTIFY valueChanged) Q_PROPERTY(QDateTime finishedDateTime READ finishedDateTime WRITE setFinishedDateTime NOTIFY valueChanged) Q_PROPERTY(StatusType status READ status WRITE setStatus NOTIFY valueChanged) public: enum TodoProgressField { FieldStatus = QOrganizerTodoProgress::FieldStatus, FieldPercentageComplete = QOrganizerTodoProgress::FieldPercentageComplete, FieldFinishedDateTime = QOrganizerTodoProgress::FieldFinishedDateTime }; enum StatusType { NotStarted = QOrganizerTodoProgress::StatusNotStarted, InProgress = QOrganizerTodoProgress::StatusInProgress, Complete = QOrganizerTodoProgress::StatusComplete }; QDeclarativeOrganizerTodoProgress(QObject *parent = 0); virtual DetailType type() const; void setPercentageComplete(int percentageComplete); int percentageComplete() const; void setFinishedDateTime(const QDateTime &datetime); QDateTime finishedDateTime() const; void setStatus(StatusType newStatus); StatusType status() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerTodoTime : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(TodoTimeField) Q_PROPERTY(bool allDay READ isAllDay WRITE setAllDay NOTIFY valueChanged) Q_PROPERTY(QDateTime startDateTime READ startDateTime WRITE setStartDateTime NOTIFY valueChanged) Q_PROPERTY(QDateTime dueDateTime READ dueDateTime WRITE setDueDateTime NOTIFY valueChanged) public: enum TodoTimeField { FieldStartDateTime = QOrganizerTodoTime::FieldStartDateTime, FieldDueDateTime = QOrganizerTodoTime::FieldDueDateTime, FieldAllDay = QOrganizerTodoTime::FieldAllDay }; QDeclarativeOrganizerTodoTime(QObject *parent = 0); virtual DetailType type() const; void setAllDay(bool isAllDay); bool isAllDay(); void setStartDateTime(const QDateTime &datetime); QDateTime startDateTime() const; void setDueDateTime(const QDateTime &dateTime); QDateTime dueDateTime() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerItemReminder : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(ReminderField) Q_ENUMS(ReminderType) Q_PROPERTY(ReminderType reminderType READ reminderType NOTIFY reminderChanged) Q_PROPERTY(int repetitionCount READ repetitionCount WRITE setRepetitionCount NOTIFY reminderChanged) Q_PROPERTY(int repetitionDelay READ repetitionDelay WRITE setRepetitionDelay NOTIFY reminderChanged) Q_PROPERTY(int secondsBeforeStart READ secondsBeforeStart WRITE setSecondsBeforeStart NOTIFY reminderChanged) public: enum ReminderField { FieldRepetitionCount = QOrganizerItemReminder::FieldRepetitionCount, FieldRepetitionDelay = QOrganizerItemReminder::FieldRepetitionDelay, FieldSecondsBeforeStart = QOrganizerItemReminder::FieldSecondsBeforeStart }; enum ReminderType { NoReminder = QOrganizerItemReminder::NoReminder, VisualReminder = QOrganizerItemReminder::VisualReminder, AudibleReminder = QOrganizerItemReminder::AudibleReminder, EmailReminder = QOrganizerItemReminder::EmailReminder }; QDeclarativeOrganizerItemReminder(QObject *parent = 0); virtual DetailType type() const; ReminderType reminderType() const; void setRepetitionCount(int count); int repetitionCount() const; void setRepetitionDelay(int delaySeconds); int repetitionDelay() const; void setSecondsBeforeStart(int seconds); int secondsBeforeStart() const; Q_SIGNALS: void reminderChanged(); }; class QDeclarativeOrganizerItemAudibleReminder : public QDeclarativeOrganizerItemReminder { Q_OBJECT Q_ENUMS(AudibleReminderField) Q_PROPERTY(QUrl dataUrl READ dataUrl WRITE setDataUrl NOTIFY valueChanged) public: enum AudibleReminderField { FieldDataUrl = QOrganizerItemAudibleReminder::FieldDataUrl }; QDeclarativeOrganizerItemAudibleReminder(QObject *parent = 0); virtual DetailType type() const; void setDataUrl(const QUrl &url); QUrl dataUrl() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerItemEmailReminder : public QDeclarativeOrganizerItemReminder { Q_OBJECT Q_ENUMS(EmailReminderField) Q_PROPERTY(QString body READ body WRITE setBody NOTIFY valueChanged) Q_PROPERTY(QString subject READ subject WRITE setSubject NOTIFY valueChanged) Q_PROPERTY(QStringList recipients READ recipients WRITE setRecipients NOTIFY valueChanged) Q_PROPERTY(QVariantList attachments READ attachments WRITE setAttachments NOTIFY valueChanged) public: enum EmailReminderField { FieldSubject = QOrganizerItemEmailReminder::FieldSubject, FieldBody = QOrganizerItemEmailReminder::FieldBody, FieldRecipients = QOrganizerItemEmailReminder::FieldRecipients, FieldAttachments = QOrganizerItemEmailReminder::FieldAttachments }; QDeclarativeOrganizerItemEmailReminder(QObject *parent = 0); virtual DetailType type() const; void setBody(const QString &newBody); QString body() const; void setSubject(const QString &newSubject); QString subject() const; void setRecipients(const QStringList &newRecipients); QStringList recipients() const; void setAttachments(const QVariantList &newAttachments); QVariantList attachments(); Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerItemVisualReminder : public QDeclarativeOrganizerItemReminder { Q_OBJECT Q_ENUMS(VisualReminderField) Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY valueChanged) Q_PROPERTY(QUrl dataUrl READ dataUrl WRITE setDataUrl NOTIFY valueChanged) public: enum VisualReminderField { FieldDataUrl = QOrganizerItemVisualReminder::FieldDataUrl, FieldMessage = QOrganizerItemVisualReminder::FieldMessage }; QDeclarativeOrganizerItemVisualReminder(QObject *parent = 0); virtual DetailType type() const; void setMessage(const QString &msg); QString message() const; void setDataUrl(const QUrl &url); QUrl dataUrl() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerItemExtendedDetail : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(ExtendedDetailField) Q_PROPERTY(QString name READ name WRITE setName NOTIFY valueChanged) Q_PROPERTY(QVariant data READ data WRITE setData NOTIFY valueChanged) public: enum ExtendedDetailField { FieldName = QOrganizerItemExtendedDetail::FieldName, FieldData = QOrganizerItemExtendedDetail::FieldData }; QDeclarativeOrganizerItemExtendedDetail(QObject *parent = 0); virtual DetailType type() const; void setName(const QString &newDetailName); QString name() const; void setData(const QVariant &data); QVariant data() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerEventAttendee : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(EventAttendeeField) Q_ENUMS(ParticipationStatus) Q_ENUMS(ParticipationRole) Q_PROPERTY(QString name READ name WRITE setName NOTIFY valueChanged) Q_PROPERTY(QString emailAddress READ emailAddress WRITE setEmailAddress NOTIFY valueChanged) Q_PROPERTY(QString attendeeId READ attendeeId WRITE setAttendeeId NOTIFY valueChanged) Q_PROPERTY(ParticipationStatus participationStatus READ participationStatus WRITE setParticipationStatus NOTIFY valueChanged) Q_PROPERTY(ParticipationRole participationRole READ participationRole WRITE setParticipationRole NOTIFY valueChanged) public: enum EventAttendeeField { FieldName = QOrganizerEventAttendee::FieldName, FieldEmailAddress = QOrganizerEventAttendee::FieldEmailAddress, FieldAddendeeId = QOrganizerEventAttendee::FieldAttendeeId, FieldParticipationStatus = QOrganizerEventAttendee::FieldParticipationStatus, FieldParticipationRole = QOrganizerEventAttendee::FieldParticipationRole }; enum ParticipationStatus { StatusUnknown = QOrganizerEventAttendee::StatusUnknown, StatusAccepted = QOrganizerEventAttendee::StatusAccepted, StatusDeclined = QOrganizerEventAttendee::StatusDeclined, StatusTentative = QOrganizerEventAttendee::StatusTentative, StatusDelegated = QOrganizerEventAttendee::StatusDelegated, StatusInProcess = QOrganizerEventAttendee::StatusInProcess, StatusCompleted = QOrganizerEventAttendee::StatusCompleted }; enum ParticipationRole { RoleUnknown = QOrganizerEventAttendee::RoleUnknown, RoleOrganizer = QOrganizerEventAttendee::RoleOrganizer, RoleChairperson = QOrganizerEventAttendee::RoleChairperson, RoleHost = QOrganizerEventAttendee::RoleHost, RoleRequiredParticipant = QOrganizerEventAttendee::RoleRequiredParticipant, RoleOptionalParticipant = QOrganizerEventAttendee::RoleOptionalParticipant, RoleNonParticipant = QOrganizerEventAttendee::RoleNonParticipant }; QDeclarativeOrganizerEventAttendee(QObject *parent = 0); virtual DetailType type() const; void setName(const QString &newName); QString name() const; void setEmailAddress(const QString &newEmailAddress); QString emailAddress() const; void setParticipationStatus(ParticipationStatus status); ParticipationStatus participationStatus() const; void setParticipationRole(ParticipationRole role); ParticipationRole participationRole() const; void setAttendeeId(const QString &newAttendeeId); QString attendeeId() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerEventRsvp : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_PROPERTY(QDeclarativeOrganizerEventAttendee::ParticipationStatus participationStatus READ participationStatus WRITE setParticipationStatus NOTIFY valueChanged) Q_PROPERTY(QDeclarativeOrganizerEventAttendee::ParticipationRole participationRole READ participationRole WRITE setParticipationRole NOTIFY valueChanged) Q_PROPERTY(ResponseRequirement responseRequirement READ responseRequirement WRITE setResponseRequirement NOTIFY valueChanged) Q_PROPERTY(QDateTime responseDeadline READ responseDeadline WRITE setResponseDeadline NOTIFY valueChanged) Q_PROPERTY(QDateTime responseDate READ responseDate WRITE setResponseDate NOTIFY valueChanged) Q_PROPERTY(QString organizerName READ organizerName WRITE setOrganizerName NOTIFY valueChanged) Q_PROPERTY(QString organizerEmail READ organizerEmail WRITE setOrganizerEmail NOTIFY valueChanged) Q_ENUMS(EventRsvpField) Q_ENUMS(ResponseRequirement) public: enum EventRsvpField { FieldParticipationStatus = QOrganizerEventRsvp::FieldParticipationStatus, FieldParticipationRole = QOrganizerEventRsvp::FieldParticipationRole, FieldResponseRequirement = QOrganizerEventRsvp::FieldResponseRequirement, FieldResponseDeadline = QOrganizerEventRsvp::FieldResponseDeadline, FieldResponseDate = QOrganizerEventRsvp::FieldResponseDate, FieldOrganizerName = QOrganizerEventRsvp::FieldOrganizerName, FieldOrganizerEmail = QOrganizerEventRsvp::FieldOrganizerEmail }; enum ResponseRequirement { ResponseNotRequired = QOrganizerEventRsvp::ResponseNotRequired, ResponseRequired = QOrganizerEventRsvp::ResponseRequired }; QDeclarativeOrganizerEventRsvp(QObject *parent = 0); virtual DetailType type() const; virtual QVariant value(int field) const; virtual bool setValue(int key, const QVariant& value); void setParticipationStatus(QDeclarativeOrganizerEventAttendee::ParticipationStatus status); QDeclarativeOrganizerEventAttendee::ParticipationStatus participationStatus() const; void setParticipationRole(QDeclarativeOrganizerEventAttendee::ParticipationRole role); QDeclarativeOrganizerEventAttendee::ParticipationRole participationRole() const; void setResponseRequirement(ResponseRequirement requirement); ResponseRequirement responseRequirement() const; void setResponseDeadline(const QDateTime &date); QDateTime responseDeadline() const; void setResponseDate(const QDateTime &date); QDateTime responseDate() const; void setOrganizerName(const QString &name); QString organizerName() const; void setOrganizerEmail(const QString &email); QString organizerEmail() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerItemClassification : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_PROPERTY(AccessClassification classification READ classification WRITE setClassification NOTIFY valueChanged) Q_ENUMS(Field) Q_ENUMS(AccessClassification) public: enum Field { FieldClassification = QOrganizerItemClassification::FieldClassification }; enum AccessClassification { AccessPublic = QOrganizerItemClassification::AccessPublic, AccessConfidential = QOrganizerItemClassification::AccessConfidential, AccessPrivate = QOrganizerItemClassification::AccessPrivate }; QDeclarativeOrganizerItemClassification(QObject *parent = 0); virtual DetailType type() const; void setClassification(AccessClassification newClassification); AccessClassification classification() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerItemVersion : public QDeclarativeOrganizerItemDetail { Q_OBJECT Q_ENUMS(Field) Q_PROPERTY(int version READ version WRITE setVersion NOTIFY valueChanged) Q_PROPERTY(QString extendedVersion READ extendedVersion WRITE setExtendedVersion NOTIFY valueChanged) public: enum Field { FieldVersion = QOrganizerItemVersion::FieldVersion, FieldExtendedVersion = QOrganizerItemVersion::FieldExtendedVersion }; QDeclarativeOrganizerItemVersion(QObject *parent = 0); virtual DetailType type() const; void setVersion(int newVersion); int version() const; void setExtendedVersion(const QString &newExtendedVersion); QString extendedVersion() const; Q_SIGNALS: void valueChanged(); }; class QDeclarativeOrganizerItemDetailFactory { public: static QDeclarativeOrganizerItemDetail *createItemDetail(QDeclarativeOrganizerItemDetail::DetailType type); }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeOrganizerItemDetail) QML_DECLARE_TYPE(QDeclarativeOrganizerEventTime) QML_DECLARE_TYPE(QDeclarativeOrganizerItemComment) QML_DECLARE_TYPE(QDeclarativeOrganizerItemDescription) QML_DECLARE_TYPE(QDeclarativeOrganizerItemDisplayLabel) QML_DECLARE_TYPE(QDeclarativeOrganizerItemGuid) QML_DECLARE_TYPE(QDeclarativeOrganizerItemLocation) QML_DECLARE_TYPE(QDeclarativeOrganizerItemParent) QML_DECLARE_TYPE(QDeclarativeOrganizerItemPriority) QML_DECLARE_TYPE(QDeclarativeOrganizerItemRecurrence) QML_DECLARE_TYPE(QDeclarativeOrganizerItemTag) QML_DECLARE_TYPE(QDeclarativeOrganizerItemTimestamp) QML_DECLARE_TYPE(QDeclarativeOrganizerItemType) QML_DECLARE_TYPE(QDeclarativeOrganizerJournalTime) QML_DECLARE_TYPE(QDeclarativeOrganizerTodoProgress) QML_DECLARE_TYPE(QDeclarativeOrganizerTodoTime) QML_DECLARE_TYPE(QDeclarativeOrganizerItemReminder) QML_DECLARE_TYPE(QDeclarativeOrganizerItemAudibleReminder) QML_DECLARE_TYPE(QDeclarativeOrganizerItemEmailReminder) QML_DECLARE_TYPE(QDeclarativeOrganizerItemVisualReminder) QML_DECLARE_TYPE(QDeclarativeOrganizerItemExtendedDetail) QML_DECLARE_TYPE(QDeclarativeOrganizerEventAttendee) QML_DECLARE_TYPE(QDeclarativeOrganizerEventRsvp) QML_DECLARE_TYPE(QDeclarativeOrganizerItemClassification) QML_DECLARE_TYPE(QDeclarativeOrganizerItemVersion) #endif // QDECLARATIVEORGANIZERITEMDETAIL_H src/imports/organizer/qdeclarativeorganizeritemfetchhint.cpp000066400000000000000000000101751233466112000252110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativeorganizeritemfetchhint_p.h" QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE /*! \qmltype FetchHint \instantiates QDeclarativeOrganizerItemFetchHint \brief The FetchHint element provides hints to the manager about which organizer item information needs to be retrieved in an asynchronous fetch request or a synchronous function call. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-filters */ /*! \qmlsignal FetchHint::onFetchHintChanged() This signal is emitted, when any of the FetchHint's properties have been changed. */ /*! \internal */ QDeclarativeOrganizerItemFetchHint::QDeclarativeOrganizerItemFetchHint(QObject *parent) : QObject(parent) { } /*! \qmlproperty enumeration FetchHint::optimizationHints This property holds the optimization hint flags specified by the client. \list \li AllRequired Tells the backend that all information is required. \li NoActionPreferences Tells the backend that the client does not require retrieved organizer items to include a cache of action preferences. \li NoBinaryBlobs Tells the backend that the client does not require retrieved organizer items to include binary blobs such as thumbnail images. \endlist */ QDeclarativeOrganizerItemFetchHint::OptimizationHints QDeclarativeOrganizerItemFetchHint::optimizationHints() const { OptimizationHints hints; hints = ~hints & (int)d.optimizationHints(); return hints; } void QDeclarativeOrganizerItemFetchHint::setOptimizationHints(OptimizationHints hints) { if (hints != d.optimizationHints()) { QOrganizerItemFetchHint::OptimizationHints newHints; newHints = ~newHints & (int)hints; d.setOptimizationHints(newHints); emit fetchHintChanged(); } } /*! \internal */ QOrganizerItemFetchHint QDeclarativeOrganizerItemFetchHint::fetchHint() const { return d; } /*! \internal */ void QDeclarativeOrganizerItemFetchHint::setFetchHint(const QOrganizerItemFetchHint &fetchHint) { d = fetchHint; emit fetchHintChanged(); } #include "moc_qdeclarativeorganizeritemfetchhint_p.cpp" QT_END_NAMESPACE src/imports/organizer/qdeclarativeorganizeritemfetchhint_p.h000066400000000000000000000062431233466112000251760ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVEORGANIZERITEMFETCHHINT_H #define QDECLARATIVEORGANIZERITEMFETCHHINT_H #include #include QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeOrganizerItemFetchHint : public QObject { Q_OBJECT Q_PROPERTY(OptimizationHints optimizationHints READ optimizationHints WRITE setOptimizationHints NOTIFY fetchHintChanged) Q_ENUMS(OptimizationHint) Q_FLAGS(OptimizationHints) public: QDeclarativeOrganizerItemFetchHint(QObject *parent = 0); enum OptimizationHint { AllRequired = QOrganizerItemFetchHint::AllRequired, NoActionPreferences = QOrganizerItemFetchHint::NoActionPreferences, NoBinaryBlobs = QOrganizerItemFetchHint::NoBinaryBlobs }; Q_DECLARE_FLAGS(OptimizationHints, OptimizationHint) OptimizationHints optimizationHints() const; void setOptimizationHints(OptimizationHints hints); QOrganizerItemFetchHint fetchHint() const; void setFetchHint(const QOrganizerItemFetchHint &fetchHint); Q_SIGNALS: void fetchHintChanged(); private: QOrganizerItemFetchHint d; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeOrganizerItemFetchHint) #endif // QDECLARATIVEORGANIZERITEMFETCHHINT_H src/imports/organizer/qdeclarativeorganizeritemfilter.cpp000066400000000000000000000601631233466112000245240ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativeorganizeritemfilter_p.h" QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE /*! \qmltype Filter \instantiates QDeclarativeOrganizerItemFilter \brief The Filter element is used to filter items made available through a backend. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-main \sa QOrganizerItemFilter */ /*! \qmlsignal QtOrganizer5::Filter::onFilterChanged() This signal is emitted, when any of the Filter's or child element's (like DetailFilter, CollectionFilter etc) properties have been changed. */ /*! \internal */ QDeclarativeOrganizerItemFilter::QDeclarativeOrganizerItemFilter(QObject *parent) : QObject(parent) { //for grouped filter: intersect /union filters if (parent && qobject_cast(parent)) connect(this, SIGNAL(filterChanged()), parent, SIGNAL(filterChanged())); } /*! \qmlproperty enumeration Filter::type This property holds the type value of this filter. It can be one of: \list \li Filter.DefaultFilter A filter which matches everything (default). \li Filter.InvalidFilter An invalid filter which matches nothing. \li Filter.IntersectionFilter A filter which matches all organizer items that are matched by all filters it includes. \li Filter.UnionFilter A filter which matches any organizer item that is matched by any of the filters it includes. \li Filter.CollectionFilter A filter which matches any organizer item that is matched by collection. \li Filter.DetailFilter A filter which matches organizer items containing exactly one given detail. \li Filter.DetailFieldFilter A filter which matches organizer items containing one or more details of a particular type with a particular field having a particular value. \li Filter.DetailRangeFilter A filter which matches organizer items containing one or more details of a particular type whose values are within a particular range. \li Filter.IdFilter A filter which matches any organizer item whose ID is contained in a particular list of organizer item IDs. \endlist */ QDeclarativeOrganizerItemFilter::FilterType QDeclarativeOrganizerItemFilter::type() const { return static_cast(filter().type()); } /*! \internal */ QOrganizerItemFilter QDeclarativeOrganizerItemFilter::filter() const { return QOrganizerItemFilter(); } /*! \qmltype InvalidFilter \instantiates QDeclarativeOrganizerItemInvalidFilter \brief the InvalidFilter element provides a filter which will never match any organizer items. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-filters \sa QOrganizerItemInvalidFilter */ /*! \qmlsignal InvalidFilter::onFilterChanged() \sa QtOrganizer5::Filter::onFilterChanged */ /*! \internal */ QDeclarativeOrganizerItemInvalidFilter::QDeclarativeOrganizerItemInvalidFilter(QObject *parent) : QDeclarativeOrganizerItemFilter(parent) { } /*! \internal */ QOrganizerItemFilter QDeclarativeOrganizerItemInvalidFilter::filter() const { return QOrganizerItemInvalidFilter(); } /*! \internal */ QDeclarativeOrganizerItemCompoundFilter::QDeclarativeOrganizerItemCompoundFilter(QObject *parent) : QDeclarativeOrganizerItemFilter(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(filterChanged())); } /*! \internal */ QDeclarativeOrganizerItemCompoundFilter::~QDeclarativeOrganizerItemCompoundFilter() { } /*! \internal */ QQmlListProperty QDeclarativeOrganizerItemCompoundFilter::filters() { return QQmlListProperty(this, 0, filters_append, filters_count, filters_at, filters_clear); } /*! \internal */ void QDeclarativeOrganizerItemCompoundFilter::filters_append(QQmlListProperty *prop, QDeclarativeOrganizerItemFilter *filter) { QDeclarativeOrganizerItemCompoundFilter *compoundFilter = static_cast(prop->object); compoundFilter->m_filters.append(filter); QObject::connect(filter, SIGNAL(filterChanged()), compoundFilter, SIGNAL(valueChanged())); emit compoundFilter->valueChanged(); } /*! \internal */ int QDeclarativeOrganizerItemCompoundFilter::filters_count(QQmlListProperty *prop) { return static_cast(prop->object)->m_filters.count(); } /*! \internal */ QDeclarativeOrganizerItemFilter *QDeclarativeOrganizerItemCompoundFilter::filters_at(QQmlListProperty *prop, int index) { return static_cast(prop->object)->m_filters.at(index); } /*! \internal */ void QDeclarativeOrganizerItemCompoundFilter::filters_clear(QQmlListProperty *prop) { QDeclarativeOrganizerItemCompoundFilter *filter = static_cast(prop->object); if (!filter->m_filters.isEmpty()) { filter->m_filters.clear(); emit filter->valueChanged(); } } /*! \qmltype IntersectionFilter \instantiates QDeclarativeOrganizerItemIntersectionFilter \brief The IntersectionFilter element provides a filter which intersects the results of other filters. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-filters \sa QOrganizerItemIntersectionFilter */ /*! \qmlsignal InvalidFilter::onFilterChanged() \sa QtOrganizer5::Filter::onFilterChanged */ /*! \internal */ QDeclarativeOrganizerItemIntersectionFilter::QDeclarativeOrganizerItemIntersectionFilter(QObject *parent) : QDeclarativeOrganizerItemCompoundFilter(parent) { } /*! \qmlproperty list IntersectionFilter::filters This property holds the list of filters which form the intersection filter. */ /*! \internal */ QOrganizerItemFilter QDeclarativeOrganizerItemIntersectionFilter::filter() const { QList filters; foreach (const QDeclarativeOrganizerItemFilter *filter, m_filters) filters << filter->filter(); QOrganizerItemIntersectionFilter f; f.setFilters(filters); return f; } /*! \qmltype UnionFilter \instantiates QDeclarativeOrganizerItemUnionFilter \brief The UnionFilter element provides a filter which unions the results of other filters. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-filters \sa QOrganizerItemUnionFilter */ /*! \qmlsignal UnionFilter::onFilterChanged() \sa QtOrganizer5::Filter::onFilterChanged */ /*! \internal */ QDeclarativeOrganizerItemUnionFilter::QDeclarativeOrganizerItemUnionFilter(QObject *parent) : QDeclarativeOrganizerItemCompoundFilter(parent) { } /*! \qmlproperty list UnionFilter::filters This property holds the list of filters which form the union filter. */ /*! \internal */ QOrganizerItemFilter QDeclarativeOrganizerItemUnionFilter::filter() const { QList filters; foreach (const QDeclarativeOrganizerItemFilter *filter, m_filters) filters << filter->filter(); QOrganizerItemUnionFilter f; f.setFilters(filters); return f; } /*! \qmltype CollectionFilter \instantiates QDeclarativeOrganizerItemCollectionFilter \brief The CollectionFilter element provides a filter based around the collection one organizer item belongs to. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-filters \sa Collection */ /*! \qmlsignal CollectionFilter::onFilterChanged() \sa QtOrganizer5::Filter::onFilterChanged */ /*! \internal */ QDeclarativeOrganizerItemCollectionFilter::QDeclarativeOrganizerItemCollectionFilter(QObject *parent) : QDeclarativeOrganizerItemFilter(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(filterChanged())); } /*! \qmlproperty list CollectionFilter::ids This property holds the list of IDs of organizer collections which the items should belong to. */ QStringList QDeclarativeOrganizerItemCollectionFilter::ids() const { return m_ids; } void QDeclarativeOrganizerItemCollectionFilter::setIds(const QStringList &ids) { foreach (const QString &id, ids) { if (!m_ids.contains(id)) { m_ids = ids; emit valueChanged(); return; } } foreach (const QString &id, m_ids) { if (!ids.contains(id)) { m_ids = ids; emit valueChanged(); return; } } } /*! \internal */ QOrganizerItemFilter QDeclarativeOrganizerItemCollectionFilter::filter() const { QOrganizerItemCollectionFilter f; QSet ids; foreach (const QVariant &id, m_ids) { QOrganizerCollectionId cId = QOrganizerCollectionId::fromString(id.toString()); if (!cId.isNull()) ids << cId; } f.setCollectionIds(ids); return f; } /*! \qmltype DetailFilter \instantiates QDeclarativeOrganizerItemDetailFilter \brief The DetailFilter element provides a filter based around a detail value criterion. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-filters Simple example how to utilize DetailFilter element together with OrganizerModel and ListView elements: \code Rectangle { height: 400; width: 400; OrganizerModel{ id: organizer startPeriod: "2009-01-01" endPeriod: "2012-12-31" filter: todoFilter } Type { id: typeDetailToMatch type: Type.Todo } DetailFilter { id: todoFilter detail: typeDetailToMatch } ListView { width: parent.width; height: parent.height; model: organizer.items delegate: Text {text: displayLabel} } } \endcode \sa QOrganizerItemDetailFilter */ /*! \qmlsignal DetailFilter::onFilterChanged() \sa QtOrganizer5::Filter::onFilterChanged */ /*! \internal */ QDeclarativeOrganizerItemDetailFilter::QDeclarativeOrganizerItemDetailFilter(QObject *parent) : QDeclarativeOrganizerItemFilter(parent) , m_detail(0) { connect(this, SIGNAL(valueChanged()), SIGNAL(filterChanged())); } /*! \qmlproperty Detail DetailFilter::detail This property holds the detail instance used by this filter for matching. */ QDeclarativeOrganizerItemDetail *QDeclarativeOrganizerItemDetailFilter::detail() const { return m_detail; } void QDeclarativeOrganizerItemDetailFilter::setDetail(QDeclarativeOrganizerItemDetail *detail) { if (m_detail != detail) { m_detail = detail; if (m_detail && m_detail->detail() != d.detail()) { d.setDetail(m_detail->detail()); emit valueChanged(); } } } /*! \internal */ QOrganizerItemFilter QDeclarativeOrganizerItemDetailFilter::filter() const { return d; } /*! \qmltype DetailFieldFilter \instantiates QDeclarativeOrganizerItemDetailFieldFilter \brief The DetailFieldFilter element provides a filter based around a detail value criterion. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-filters Simple example how to utilize DetailFieldFilter element together with OrganizerModel and ListView elements: \code Rectangle { height: 400; width: 400; OrganizerModel{ id: organizer startPeriod: "2009-01-01" endPeriod: "2012-12-31" filter: todoFilter } DetailFieldFilter { id: todoFilter detail: Detail.Type field: Type.FieldType value: Type.Todo } ListView { width: parent.width; height: parent.height; model: organizer.items delegate: Text {text: displayLabel} } } \endcode \sa QOrganizerItemDetailFieldFilter */ /*! \qmlsignal DetailFieldFilter::onFilterChanged() \sa QtOrganizer5::Filter::onFilterChanged */ /*! \internal */ QDeclarativeOrganizerItemDetailFieldFilter::QDeclarativeOrganizerItemDetailFieldFilter(QObject *parent) : QDeclarativeOrganizerItemFilter(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(filterChanged())); } /*! \qmlproperty enum DetailFieldFilter::detail This property holds the detail type of which the detail filter will be matched to. The value shuold be the enumeration value of Detail::type. */ QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemDetailFieldFilter::detail() const { return static_cast(d.detailType()); } void QDeclarativeOrganizerItemDetailFieldFilter::setDetail(QDeclarativeOrganizerItemDetail::DetailType detail) { if (detail != static_cast(d.detailType())) { d.setDetail(static_cast(detail), d.detailField()); emit valueChanged(); } } /*! \qmlproperty enum DetailFieldFilter::field This property holds the detail field type of which the detail field filter will be matched to. The value should be the filld enumeration value defined in each detail element. \sa EventTime, JournalTime, TodoTime, TodoProgress, Reminder, AudibleReminder, VisualReminder, EmailReminder, Comment, Description, DisplayLabel, Guid, Location, Parent, Priority, Recurrence, Timestamp, ItemType, Tag */ int QDeclarativeOrganizerItemDetailFieldFilter::field() const { return d.detailField(); } void QDeclarativeOrganizerItemDetailFieldFilter::setField(int field) { if (field != d.detailField()) { d.setDetail(d.detailType(), field); emit valueChanged(); } } /*! \qmlproperty variant DetailFieldFilter::value This property holds the value criterion of the detail field filter. */ QVariant QDeclarativeOrganizerItemDetailFieldFilter::value() const { return d.value(); } void QDeclarativeOrganizerItemDetailFieldFilter::setValue(const QVariant &newValue) { if (newValue != value()) { if (QVariant::DateTime == newValue.type()) { // handling dates and datetimes internally as UTC d.setValue(newValue.toDateTime().toUTC()); } else { d.setValue(newValue); } emit valueChanged(); } } /*! \qmlproperty enumeration DetailFieldFilter::matchFlags This property holds the semantics of the value matching criterion. The valid match flags include: \list \li MatchExactly - Performs QVariant-based matching (default). \li MatchContains - The search term is contained in the item. \li MatchStartsWith - The search term matches the start of the item. \li MatchEndsWith - The search term matches the end of the item. \li MatchFixedString - Performs string-based matching. String-based comparisons are case-insensitive unless the \c MatchCaseSensitive flag is also specified. \li MatchCaseSensitive - The search is case sensitive. \endlist */ QDeclarativeOrganizerItemFilter::MatchFlags QDeclarativeOrganizerItemDetailFieldFilter::matchFlags() const { QDeclarativeOrganizerItemFilter::MatchFlags newFlags; newFlags = ~newFlags & (int)d.matchFlags(); return newFlags; } void QDeclarativeOrganizerItemDetailFieldFilter::setMatchFlags(QDeclarativeOrganizerItemFilter::MatchFlags flags) { QOrganizerItemFilter::MatchFlags newFlags; newFlags = ~newFlags & (int)flags; if (newFlags != d.matchFlags()) { d.setMatchFlags(newFlags); emit valueChanged(); } } /*! \internal */ QOrganizerItemFilter QDeclarativeOrganizerItemDetailFieldFilter::filter() const { return d; } /*! \qmltype DetailRangeFilter \instantiates QDeclarativeOrganizerItemDetailRangeFilter \brief The DetailRangeFilter element provides a filter based around a detail value range criterion. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-filters \sa QOrganizerItemDetailRangeFilter */ /*! \qmlsignal DetailRangeFilter::onFilterChanged() \sa QtOrganizer5::Filter::onFilterChanged */ /*! \internal */ QDeclarativeOrganizerItemDetailRangeFilter::QDeclarativeOrganizerItemDetailRangeFilter(QObject *parent) : QDeclarativeOrganizerItemFilter(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(filterChanged())); } /*! \qmlproperty enum DetailRangeFilter::detail This property holds the detail type of which the detail filter will be matched to. The value shuold be the enumeration value of Detail::type. */ QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemDetailRangeFilter::detail() const { return static_cast(d.detailType()); } void QDeclarativeOrganizerItemDetailRangeFilter::setDetail(QDeclarativeOrganizerItemDetail::DetailType detail) { if (detail != static_cast(d.detailType())) { d.setDetail(static_cast(detail), d.detailField()); emit valueChanged(); } } /*! \qmlproperty enum DetailRangeFilter::field This property holds the detail field type of which the detail filter will be matched to. The value should be the filld enumeration value defined in each detail element. \sa EventTime, JournalTime, TodoTime, TodoProgress, Reminder, AudibleReminder, VisualReminder, EmailReminder, Comment, Description, DisplayLabel, Guid, Location, Parent, Priority, Recurrence, Timestamp, ItemType, Tag */ int QDeclarativeOrganizerItemDetailRangeFilter::field() const { return d.detailField(); } void QDeclarativeOrganizerItemDetailRangeFilter::setField(int field) { if (field != d.detailField()) { d.setDetail(d.detailType(), field); emit valueChanged(); } } /*! \qmlproperty variant DetailRangeFilter::min This property holds the lower bound of the value range criterion. By default, there is no lower bound. */ void QDeclarativeOrganizerItemDetailRangeFilter::setMinValue(const QVariant &value) { if (value != d.minValue()) { d.setRange(value, d.maxValue(), d.rangeFlags()); emit valueChanged(); } } QVariant QDeclarativeOrganizerItemDetailRangeFilter::minValue() const { return d.minValue(); } /*! \qmlproperty variant DetailRangeFilter::max This property holds the upper bound of the value range criterion. By default, there is no upper bound. */ void QDeclarativeOrganizerItemDetailRangeFilter::setMaxValue(const QVariant &value) { if (value != d.maxValue()) { d.setRange(d.minValue(), value, d.rangeFlags()); emit valueChanged(); } } QVariant QDeclarativeOrganizerItemDetailRangeFilter::maxValue() const { return d.maxValue(); } /*! \qmlproperty enumeration DetailRangeFilter::matchFlags This property holds the match flags of the criterion, which define semantics such as case sensitivity, and exact matching. \sa DetailFieldFilter::matchFlags */ void QDeclarativeOrganizerItemDetailRangeFilter::setMatchFlags(QDeclarativeOrganizerItemFilter::MatchFlags flags) { QOrganizerItemFilter::MatchFlags newFlags; newFlags = ~newFlags & (int)flags; if (newFlags != d.matchFlags()) { d.setMatchFlags(newFlags); emit valueChanged(); } } QDeclarativeOrganizerItemFilter::MatchFlags QDeclarativeOrganizerItemDetailRangeFilter::matchFlags() const { QDeclarativeOrganizerItemFilter::MatchFlags newFlags; newFlags = ~newFlags & (int)d.matchFlags(); return newFlags; } /*! \qmlproperty enumeration DetailRangeFilter::rangeFlags This property holds a set of flags which defines the boundary condition semantics of the value range criterion. The valid range flags include: \list \li DetailRangeFilter.IncludeLower \li DetailRangeFilter.IncludeUpper \li DetailRangeFilter.ExcludeLower \li DetailRangeFilter.ExcludeUpper \endlist */ void QDeclarativeOrganizerItemDetailRangeFilter::setRangeFlags(RangeFlags flags) { QOrganizerItemDetailRangeFilter::RangeFlags newFlags; newFlags = ~newFlags & (int)flags; if (newFlags != d.rangeFlags()) { d.setRange(d.minValue(), d.maxValue(), newFlags); emit valueChanged(); } } QDeclarativeOrganizerItemDetailRangeFilter::RangeFlags QDeclarativeOrganizerItemDetailRangeFilter::rangeFlags() const { QDeclarativeOrganizerItemDetailRangeFilter::RangeFlags newFlags; newFlags = ~newFlags & (int)d.rangeFlags(); return newFlags; } /*! \internal */ QOrganizerItemFilter QDeclarativeOrganizerItemDetailRangeFilter::filter() const { return d; } /*! \qmltype IdFilter \instantiates QDeclarativeOrganizerItemIdFilter \brief The IdFilter element provides a filter based around a list of organizer item IDs. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-filters \sa {QOrganizerItemIdFilter} */ /*! \qmlsignal IdFilter::onFilterChanged() \sa QtOrganizer5::Filter::onFilterChanged */ /*! \internal */ QDeclarativeOrganizerItemIdFilter::QDeclarativeOrganizerItemIdFilter(QObject *parent) : QDeclarativeOrganizerItemFilter(parent) { connect(this, SIGNAL(valueChanged()), SIGNAL(filterChanged())); } /*! \qmlproperty list IdFilter::ids This property holds the list of IDs of organizer items which match this filter. */ QStringList QDeclarativeOrganizerItemIdFilter::ids() const { return m_ids; } void QDeclarativeOrganizerItemIdFilter::setIds(const QStringList &ids) { foreach (const QString& id, ids) { if (!m_ids.contains(id)) { m_ids = ids; emit valueChanged(); return; } } foreach (const QString& id, m_ids) { if (!ids.contains(id)) { m_ids = ids; emit valueChanged(); } } } /*! \internal */ QOrganizerItemFilter QDeclarativeOrganizerItemIdFilter::filter() const { QOrganizerItemIdFilter f; QList ids; foreach (const QString& id, m_ids) { QOrganizerItemId itemId = QOrganizerItemId::fromString(id); if (!itemId.isNull()) ids << itemId; } f.setIds(ids); return f; } #include "moc_qdeclarativeorganizeritemfilter_p.cpp" QT_END_NAMESPACE src/imports/organizer/qdeclarativeorganizeritemfilter_p.h000066400000000000000000000243351233466112000245110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVEORGANIZERITEMFILTER_P_H #define QDECLARATIVEORGANIZERITEMFILTER_P_H #include #include #include "qdeclarativeorganizeritemdetail_p.h" QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeOrganizerItemFilter : public QObject { Q_OBJECT Q_ENUMS(FilterType) Q_FLAGS(MatchFlags) Q_PROPERTY(FilterType type READ type NOTIFY filterChanged) public: QDeclarativeOrganizerItemFilter(QObject *parent = 0); enum FilterType { DefaultFilter = QOrganizerItemFilter::DefaultFilter, InvalidFilter = QOrganizerItemFilter::InvalidFilter, IntersectionFilter = QOrganizerItemFilter::IntersectionFilter, UnionFilter = QOrganizerItemFilter::UnionFilter, CollectionFilter = QOrganizerItemFilter::CollectionFilter, DetailFilter = QOrganizerItemFilter::DetailFilter, DetailFieldFilter = QOrganizerItemFilter::DetailFieldFilter, DetailRangeFilter = QOrganizerItemFilter::DetailRangeFilter, IdFilter = QOrganizerItemFilter::IdFilter }; FilterType type() const; enum MatchFlag { MatchExactly = QOrganizerItemFilter::MatchExactly, MatchContains = QOrganizerItemFilter::MatchContains, MatchStartsWith = QOrganizerItemFilter::MatchStartsWith, MatchEndsWith = QOrganizerItemFilter::MatchEndsWith, MatchFixedString = QOrganizerItemFilter::MatchFixedString, MatchCaseSensitive = QOrganizerItemFilter::MatchCaseSensitive }; Q_DECLARE_FLAGS(MatchFlags, MatchFlag) // used by model virtual QOrganizerItemFilter filter() const; Q_SIGNALS: void filterChanged(); }; class QDeclarativeOrganizerItemInvalidFilter : public QDeclarativeOrganizerItemFilter { Q_OBJECT public: QDeclarativeOrganizerItemInvalidFilter(QObject *parent = 0); // used by model QOrganizerItemFilter filter() const; }; class QDeclarativeOrganizerItemCompoundFilter : public QDeclarativeOrganizerItemFilter { Q_OBJECT Q_PROPERTY(QQmlListProperty filters READ filters NOTIFY valueChanged) Q_CLASSINFO("DefaultProperty", "filters") public: explicit QDeclarativeOrganizerItemCompoundFilter(QObject *parent = 0); virtual ~QDeclarativeOrganizerItemCompoundFilter(); QQmlListProperty filters(); static void filters_append(QQmlListProperty *prop, QDeclarativeOrganizerItemFilter *filter); static int filters_count(QQmlListProperty *prop); static QDeclarativeOrganizerItemFilter *filters_at(QQmlListProperty *prop, int index); static void filters_clear(QQmlListProperty *prop); Q_SIGNALS: void valueChanged(); protected: QList m_filters; }; class QDeclarativeOrganizerItemIntersectionFilter : public QDeclarativeOrganizerItemCompoundFilter { Q_OBJECT public: QDeclarativeOrganizerItemIntersectionFilter(QObject *parent = 0); // used by model QOrganizerItemFilter filter() const; }; class QDeclarativeOrganizerItemUnionFilter : public QDeclarativeOrganizerItemCompoundFilter { Q_OBJECT public: QDeclarativeOrganizerItemUnionFilter(QObject *parent = 0); // used by model QOrganizerItemFilter filter() const; }; class QDeclarativeOrganizerItemCollectionFilter : public QDeclarativeOrganizerItemFilter { Q_OBJECT Q_PROPERTY(QStringList ids READ ids WRITE setIds NOTIFY valueChanged) public: QDeclarativeOrganizerItemCollectionFilter(QObject *parent = 0); QStringList ids() const; void setIds(const QStringList &ids); // used by model QOrganizerItemFilter filter() const; Q_SIGNALS: void valueChanged(); private: QStringList m_ids; }; class QDeclarativeOrganizerItemDetailFilter : public QDeclarativeOrganizerItemFilter { Q_OBJECT Q_PROPERTY(QDeclarativeOrganizerItemDetail *detail READ detail WRITE setDetail NOTIFY valueChanged) public: QDeclarativeOrganizerItemDetailFilter(QObject *parent = 0); QDeclarativeOrganizerItemDetail *detail() const; void setDetail(QDeclarativeOrganizerItemDetail *detail); // used by model QOrganizerItemFilter filter() const; signals: void valueChanged(); private: QDeclarativeOrganizerItemDetail *m_detail; QOrganizerItemDetailFilter d; }; class QDeclarativeOrganizerItemDetailFieldFilter : public QDeclarativeOrganizerItemFilter { Q_OBJECT Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) Q_PROPERTY(MatchFlags matchFlags READ matchFlags WRITE setMatchFlags NOTIFY valueChanged) Q_PROPERTY(int field READ field WRITE setField NOTIFY valueChanged) Q_PROPERTY(QDeclarativeOrganizerItemDetail::DetailType detail READ detail WRITE setDetail NOTIFY valueChanged) public: QDeclarativeOrganizerItemDetailFieldFilter(QObject *parent = 0); QDeclarativeOrganizerItemDetail::DetailType detail() const; void setDetail(QDeclarativeOrganizerItemDetail::DetailType detail); int field() const; void setField(int field); void setValue(const QVariant &value); QVariant value() const; QDeclarativeOrganizerItemFilter::MatchFlags matchFlags() const; void setMatchFlags(QDeclarativeOrganizerItemFilter::MatchFlags flags); // used by model QOrganizerItemFilter filter() const; signals: void valueChanged(); private: QOrganizerItemDetailFieldFilter d; }; class QDeclarativeOrganizerItemDetailRangeFilter : public QDeclarativeOrganizerItemFilter { Q_OBJECT Q_FLAGS(RangeFlags) Q_PROPERTY(QVariant min READ minValue WRITE setMinValue NOTIFY valueChanged) Q_PROPERTY(QVariant max READ maxValue WRITE setMaxValue NOTIFY valueChanged) Q_PROPERTY(MatchFlags matchFlags READ matchFlags WRITE setMatchFlags NOTIFY valueChanged) Q_PROPERTY(RangeFlags rangeFlags READ rangeFlags WRITE setRangeFlags NOTIFY valueChanged) Q_PROPERTY(QDeclarativeOrganizerItemDetail::DetailType detail READ detail WRITE setDetail NOTIFY valueChanged) Q_PROPERTY(int field READ field WRITE setField NOTIFY valueChanged) public: enum RangeFlag { IncludeLower = QOrganizerItemDetailRangeFilter::IncludeLower, IncludeUpper = QOrganizerItemDetailRangeFilter::IncludeUpper, ExcludeLower = QOrganizerItemDetailRangeFilter::ExcludeLower, ExcludeUpper = QOrganizerItemDetailRangeFilter::ExcludeUpper }; Q_DECLARE_FLAGS(RangeFlags, RangeFlag) QDeclarativeOrganizerItemDetailRangeFilter(QObject *parent = 0); QDeclarativeOrganizerItemDetail::DetailType detail() const; void setDetail(QDeclarativeOrganizerItemDetail::DetailType detail); int field() const; void setField(int field); QDeclarativeOrganizerItemFilter::MatchFlags matchFlags() const; void setMatchFlags(QDeclarativeOrganizerItemFilter::MatchFlags flags); RangeFlags rangeFlags() const; void setRangeFlags(RangeFlags flags); QVariant minValue() const; void setMinValue(const QVariant &value); QVariant maxValue() const; void setMaxValue(const QVariant &value); // used by model QOrganizerItemFilter filter() const; signals: void valueChanged(); private: QOrganizerItemDetailRangeFilter d; }; class QDeclarativeOrganizerItemIdFilter : public QDeclarativeOrganizerItemFilter { Q_OBJECT Q_PROPERTY(QStringList ids READ ids WRITE setIds NOTIFY valueChanged) public: QDeclarativeOrganizerItemIdFilter(QObject *parent = 0); QStringList ids() const; void setIds(const QStringList &ids); // used by model QOrganizerItemFilter filter() const; Q_SIGNALS: void valueChanged(); private: QStringList m_ids; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeOrganizerItemFilter) QML_DECLARE_TYPE(QDeclarativeOrganizerItemInvalidFilter) QML_DECLARE_TYPE(QDeclarativeOrganizerItemCompoundFilter) QML_DECLARE_TYPE(QDeclarativeOrganizerItemIntersectionFilter) QML_DECLARE_TYPE(QDeclarativeOrganizerItemUnionFilter) QML_DECLARE_TYPE(QDeclarativeOrganizerItemCollectionFilter) QML_DECLARE_TYPE(QDeclarativeOrganizerItemDetailFilter) QML_DECLARE_TYPE(QDeclarativeOrganizerItemDetailFieldFilter) QML_DECLARE_TYPE(QDeclarativeOrganizerItemDetailRangeFilter) QML_DECLARE_TYPE(QDeclarativeOrganizerItemIdFilter) #endif // QDECLARATIVEORGANIZERITEMFILTER_P_H src/imports/organizer/qdeclarativeorganizeritemsortorder.cpp000066400000000000000000000144361233466112000252640ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativeorganizeritemsortorder_p.h" QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE /*! \qmltype SortOrder \instantiates QDeclarativeOrganizerItemSortOrder \brief The SortOrder element defines how a list of organizer item should be ordered according to some criteria. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-filters */ QDeclarativeOrganizerItemSortOrder::QDeclarativeOrganizerItemSortOrder(QObject *parent) : QObject(parent) { } /*! \qmlsignal SortOrder::onSortOrderChanged() This signal is emitted, when any of the SortOrder's properties have been changed. */ /*! \qmlproperty string SortOrder::detail This property holds the detail type of which the sorting will be performed to. The value should be the enumeration value of Detail::type. */ void QDeclarativeOrganizerItemSortOrder::setDetail(QDeclarativeOrganizerItemDetail::DetailType detail) { if (detail != static_cast(d.detailType())) { d.setDetail(static_cast(detail), d.detailField()); emit sortOrderChanged(); } } QDeclarativeOrganizerItemDetail::DetailType QDeclarativeOrganizerItemSortOrder::detail() const { return static_cast(d.detailType()); } /*! \qmlproperty string SortOrder::field This property holds the detail field type of which the sorting will be performed to. The value should be the filld enumeration value defined in each detail element. \sa EventTime, JournalTime, TodoTime, TodoProgress, Reminder, AudibleReminder, VisualReminder, EmailReminder, Comment, Description, DisplayLabel, Guid, Location, Parent, Priority, Recurrence, Timestamp, ItemType, Tag */ void QDeclarativeOrganizerItemSortOrder::setField(int field) { if (field != d.detailField()) { d.setDetail(d.detailType(), field); emit sortOrderChanged(); } } int QDeclarativeOrganizerItemSortOrder::field() const { return d.detailField(); } /*! \qmlproperty enumeration SortOrder::blankPolicy This property enumerates the ways in which the sort order interprets blanks when sorting organizer. \list \li SortOrder.BlanksFirst Considers blank values to evaluate to less than all other values in comparisons. \li SortOrder.BlanksLast Considers blank values to evaluate to greater than all other values in comparisons. \endlist */ void QDeclarativeOrganizerItemSortOrder::setBlankPolicy(BlankPolicy policy) { if (policy != blankPolicy()) { d.setBlankPolicy(static_cast(policy)); emit sortOrderChanged(); } } QDeclarativeOrganizerItemSortOrder::BlankPolicy QDeclarativeOrganizerItemSortOrder::blankPolicy() const { return static_cast(d.blankPolicy()); } /*! \qmlproperty enumeration SortOrder::direction This property holds the direction of the sort order, the value can be one of: \list \li Qt.AscendingOrder The items will be sorted by the ascending order (default). \li Qt.DescendingOrder The items will be sorted by the descending order. \endlist */ void QDeclarativeOrganizerItemSortOrder::setDirection(Qt::SortOrder newDirection) { if (newDirection != direction()) { d.setDirection(newDirection); emit sortOrderChanged(); } } Qt::SortOrder QDeclarativeOrganizerItemSortOrder::direction() const { return d.direction(); } /*! \qmlproperty enumeration SortOrder::caseSensitivity This property holds the case sensitivity of the sort order, the value can be one of: \list \li Qt.CaseInsensitive Sets the case sensitivity of the sort order to insensitivity. \li Qt.CaseSensitive Sets the case sensitivity of the sort order to sensitivity (default). \endlist */ void QDeclarativeOrganizerItemSortOrder::setCaseSensitivity(Qt::CaseSensitivity newSensitivity) { if (newSensitivity != caseSensitivity()) { d.setCaseSensitivity(newSensitivity); emit sortOrderChanged(); } } Qt::CaseSensitivity QDeclarativeOrganizerItemSortOrder::caseSensitivity() const { return d.caseSensitivity(); } /*! \internal */ QOrganizerItemSortOrder QDeclarativeOrganizerItemSortOrder::sortOrder() { return d; } #include "moc_qdeclarativeorganizeritemsortorder_p.cpp" QT_END_NAMESPACE src/imports/organizer/qdeclarativeorganizeritemsortorder_p.h000066400000000000000000000073541233466112000252510ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVEORGANIZERITEMSORTORDER_H #define QDECLARATIVEORGANIZERITEMSORTORDER_H #include #include #include "qdeclarativeorganizeritemdetail_p.h" QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeOrganizerItemSortOrder : public QObject { Q_OBJECT Q_PROPERTY(QDeclarativeOrganizerItemDetail::DetailType detail READ detail WRITE setDetail NOTIFY sortOrderChanged) Q_PROPERTY(int field READ field WRITE setField NOTIFY sortOrderChanged) Q_PROPERTY(BlankPolicy blankPolicy READ blankPolicy WRITE setBlankPolicy NOTIFY sortOrderChanged) Q_PROPERTY(Qt::SortOrder direction READ direction WRITE setDirection NOTIFY sortOrderChanged) Q_PROPERTY(Qt::CaseSensitivity sensitivity READ caseSensitivity WRITE setCaseSensitivity NOTIFY sortOrderChanged) Q_ENUMS(BlankPolicy) public: enum BlankPolicy { BlanksFirst = QOrganizerItemSortOrder::BlanksFirst, BlanksLast = QOrganizerItemSortOrder::BlanksLast }; QDeclarativeOrganizerItemSortOrder(QObject *parent = 0); void setDetail(QDeclarativeOrganizerItemDetail::DetailType detail); QDeclarativeOrganizerItemDetail::DetailType detail() const; void setField(int field); int field() const; void setBlankPolicy(BlankPolicy policy); BlankPolicy blankPolicy() const; void setDirection(Qt::SortOrder newDirection); Qt::SortOrder direction() const; void setCaseSensitivity(Qt::CaseSensitivity newSensitivity); Qt::CaseSensitivity caseSensitivity() const; // used by organizer model QOrganizerItemSortOrder sortOrder(); Q_SIGNALS: void sortOrderChanged(); private: QOrganizerItemSortOrder d; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeOrganizerItemSortOrder) #endif // QDECLARATIVEORGANIZERITEMSORTORDER_H src/imports/organizer/qdeclarativeorganizermodel.cpp000066400000000000000000002107641233466112000234640ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativeorganizermodel_p.h" #include #include #include #include #include #include #include #include #include #include "qdeclarativeorganizercollection_p.h" QTORGANIZER_USE_NAMESPACE QTVERSITORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE // TODO: // - Improve handling of itemsModified signal. Instead of fetching all items each time the // signal is received, only modified items should be fetched. Item based fetching allows easier // use of any item id based caches backends might have. // - Full update is not needed every time some model property changes. Collections should // be updated only if collections have been changed while autoUpdate is off. // - Changing the time period is by far the most common use case and should be optimized. // If new time period overlaps with the old, no need to fetch all the items in new time period. // - All changes happening during autoUpdate is off should be monitored. If only timePeriod changes // there is no need for full update after switching autoUpdate on. static QString urlToLocalFileName(const QUrl& url) { if (!url.isValid()) { return url.toString(); } else if (url.scheme() == "qrc") { return url.toString().remove(0, 5).prepend(':'); } else { return url.toLocalFile(); } } class QDeclarativeOrganizerModelPrivate { public: QDeclarativeOrganizerModelPrivate() :m_manager(0), m_fetchHint(0), m_filter(0), m_fetchRequest(0), m_occurrenceFetchRequest(0), m_reader(0), m_writer(0), m_startPeriod(QDateTime::currentDateTime()), m_endPeriod(QDateTime::currentDateTime()), m_error(QOrganizerManager::NoError), m_autoUpdate(true), m_updatePendingFlag(QDeclarativeOrganizerModelPrivate::NonePending), m_componentCompleted(false), m_initialUpdate(false), m_lastRequestId(0) { } ~QDeclarativeOrganizerModelPrivate() { if (m_manager) delete m_manager; delete m_reader; delete m_writer; } QList m_items; QHash m_itemIdHash; QOrganizerManager* m_manager; QDeclarativeOrganizerItemFetchHint* m_fetchHint; QList m_sortOrders; QList m_declarativeSortOrders; QDeclarativeOrganizerItemFilter* m_filter; QOrganizerItemFetchRequest* m_fetchRequest; QSet m_addedItemIds; QMap > m_notifiedItems; QOrganizerItemOccurrenceFetchRequest* m_occurrenceFetchRequest; QStringList m_importProfiles; QVersitReader *m_reader; QVersitWriter *m_writer; QDateTime m_startPeriod; QDateTime m_endPeriod; QList m_collections; QOrganizerManager::Error m_error; bool m_autoUpdate; enum UpdateTypePending { NonePending = 0x0, UpdatingItemsPending = 0x1, UpdatingCollectionsPending = 0x2 }; int m_updatePendingFlag; bool m_componentCompleted; bool m_initialUpdate; QAtomicInt m_lastRequestId; QHash m_requestIdHash; QUrl m_lastExportUrl; QUrl m_lastImportUrl; }; /*! \qmltype OrganizerModel \instantiates QDeclarativeOrganizerModel \brief The OrganizerModel element provides access to organizer items from the organizer store. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-main OrganizerModel provides a model of organizer items from the organizer store. The contents of the model can be specified with \l filter, \l sortOrders and \l fetchHint properties. Whether the model is automatically updated when the store or \l{Qt Organizer Overview} {C++ organizer} item changes, can be controlled with \l OrganizerModel::autoUpdate property. There are two ways of accessing the organizer item data: via the model by using views and delegates, or alternatively via \l items list property. Of the two, the model access is preferred. Direct list access (i.e. non-model) is not guaranteed to be in order set by \l sortOrder. At the moment the model roles provided by OrganizerModel are \c display and \c item. Through the \c item role can access any data provided by the OrganizerItem element. \note Both the \c startPeriod and \c endPeriod are set by default to the current time (when the OrganizerModel was created). In most cases, both (or at least one) of the startPeriod and endPeriod should be set; otherwise, the OrganizerModel will contain zero items because the \c startPeriod and \c endPeriod are the same value. For example, if only \c endPeriod is provided, the OrganizerModel will contain all items from now (the time of the OrganizerModel's creation) to the \c endPeriod time. \sa OrganizerItem, {QOrganizerManager} */ /*! \qmlsignal OrganizerModel::onModelChanged() This signal is emitted, when there are changes in items contained by \l OrganizerModel's data model. Items have either been added, removed or modified. This signal is also always emitted during OrganizerModel construction when data model is ready for use, even in cases when data model is not having any items in it. */ QDeclarativeOrganizerModel::QDeclarativeOrganizerModel(QObject *parent) : QAbstractListModel(parent), d_ptr(new QDeclarativeOrganizerModelPrivate) { QHash roleNames; roleNames = QAbstractItemModel::roleNames(); roleNames.insert(OrganizerItemRole, "item"); setRoleNames(roleNames); connect(this, SIGNAL(managerChanged()), SLOT(doUpdate())); connect(this, SIGNAL(filterChanged()), SLOT(doUpdateItems())); connect(this, SIGNAL(fetchHintChanged()), SLOT(doUpdateItems())); connect(this, SIGNAL(sortOrdersChanged()), SLOT(doUpdateItems())); connect(this, SIGNAL(startPeriodChanged()), SLOT(doUpdateItems())); connect(this, SIGNAL(endPeriodChanged()), SLOT(doUpdateItems())); } QDeclarativeOrganizerModel::~QDeclarativeOrganizerModel() { } /*! \qmlproperty string OrganizerModel::manager This property holds the manager name or manager uri of the organizer backend engine. The manager uri format: qtorganizer::=&=. For example, memory organizer engine has an optional id parameter, if user want to share the same memory engine with multiple OrganizerModel instances, the manager property should declared like this: \code model : OrganizerModel { manager:"qtorganizer:memory:id=organizer1 } \endcode instead of just the manager name: \code model : OrganizerModel { manager:"memory" } \endcode \sa QOrganizerManager::fromUri() */ QString QDeclarativeOrganizerModel::manager() const { Q_D(const QDeclarativeOrganizerModel); if (d->m_manager) return d->m_manager->managerUri(); return QString(); } /*! \qmlproperty string OrganizerModel::managerName This property holds the manager name of the organizer backend engine. This property is read only. \sa QOrganizerManager::fromUri() */ QString QDeclarativeOrganizerModel::managerName() const { Q_D(const QDeclarativeOrganizerModel); if (d->m_manager) return d->m_manager->managerName(); return QString(); } /*! \qmlproperty list OrganizerModel::availableManagers This property holds the list of available manager names. This property is read only. */ QStringList QDeclarativeOrganizerModel::availableManagers() const { return QOrganizerManager::availableManagers(); } /*! \qmlproperty bool OrganizerModel::autoUpdate This property indicates whether or not the organizer model should be updated automatically, default value is true. \sa OrganizerModel::update() */ void QDeclarativeOrganizerModel::setAutoUpdate(bool autoUpdate) { Q_D(QDeclarativeOrganizerModel); if (autoUpdate == d->m_autoUpdate) return; d->m_autoUpdate = autoUpdate; emit autoUpdateChanged(); } bool QDeclarativeOrganizerModel::autoUpdate() const { Q_D(const QDeclarativeOrganizerModel); return d->m_autoUpdate; } /*! \qmlmethod OrganizerModel::update() Manually update the organizer model content including both items and collections. \sa OrganizerModel::updateItems \sa OrganizerModel::updateCollections \sa OrganizerModel::autoUpdate */ void QDeclarativeOrganizerModel::update() { Q_D(QDeclarativeOrganizerModel); if (!d->m_componentCompleted || d->m_updatePendingFlag) return; // Disallow possible duplicate request triggering d->m_updatePendingFlag = (QDeclarativeOrganizerModelPrivate::UpdatingItemsPending | QDeclarativeOrganizerModelPrivate::UpdatingCollectionsPending); QMetaObject::invokeMethod(this, "fetchCollections", Qt::QueuedConnection); } void QDeclarativeOrganizerModel::doUpdate() { Q_D(QDeclarativeOrganizerModel); if (d->m_autoUpdate) update(); } void QDeclarativeOrganizerModel::doUpdateItems() { Q_D(QDeclarativeOrganizerModel); if (d->m_autoUpdate) updateItems(); } /*! \qmlmethod OrganizerModel::updateItems() Manually update the organizer model items. \sa OrganizerModel::update \sa OrganizerModel::updateCollections \sa OrganizerModel::autoUpdate */ void QDeclarativeOrganizerModel::updateItems() { Q_D(QDeclarativeOrganizerModel); if (!d->m_componentCompleted || d->m_updatePendingFlag) return; d->m_updatePendingFlag = QDeclarativeOrganizerModelPrivate::UpdatingItemsPending;// Disallow possible duplicate request triggering QMetaObject::invokeMethod(this, "fetchAgain", Qt::QueuedConnection); } /*! \qmlmethod OrganizerModel::updateCollections() Manually update the organizer model collections. \sa OrganizerModel::update \sa OrganizerModel::updateItems \sa OrganizerModel::autoUpdate */ void QDeclarativeOrganizerModel::updateCollections() { Q_D(QDeclarativeOrganizerModel); if (!d->m_componentCompleted || d->m_updatePendingFlag) return; d->m_updatePendingFlag = QDeclarativeOrganizerModelPrivate::UpdatingCollectionsPending;// Disallow possible duplicate request triggering QMetaObject::invokeMethod(this, "fetchCollections", Qt::QueuedConnection); } /*! \qmlmethod OrganizerModel::cancelUpdate() Cancel the running organizer model content update request. \sa OrganizerModel::autoUpdate, OrganizerModel::update */ void QDeclarativeOrganizerModel::cancelUpdate() { Q_D(QDeclarativeOrganizerModel); if (d->m_fetchRequest) { d->m_fetchRequest->cancel(); d->m_fetchRequest->deleteLater(); d->m_fetchRequest = 0; d->m_updatePendingFlag = QDeclarativeOrganizerModelPrivate::NonePending; } } /*! \qmlproperty date OrganizerModel::startPeriod This property holds the start date and time period used by the organizer model to fetch organizer items. The default value is the datetime of OrganizerModel creation. */ QDateTime QDeclarativeOrganizerModel::startPeriod() const { Q_D(const QDeclarativeOrganizerModel); return d->m_startPeriod; } void QDeclarativeOrganizerModel::setStartPeriod(const QDateTime& start) { Q_D(QDeclarativeOrganizerModel); if (start != d->m_startPeriod) { d->m_startPeriod = start; emit startPeriodChanged(); } } /*! \qmlproperty date OrganizerModel::endPeriod This property holds the end date and time period used by the organizer model to fetch organizer items. The default value is the datetime of OrganizerModel creation. */ QDateTime QDeclarativeOrganizerModel::endPeriod() const { Q_D(const QDeclarativeOrganizerModel); return d->m_endPeriod; } void QDeclarativeOrganizerModel::setEndPeriod(const QDateTime& end) { Q_D(QDeclarativeOrganizerModel); if (end != d->m_endPeriod) { d->m_endPeriod = end; emit endPeriodChanged(); } } /*! \qmlproperty enumeration OrganizerModel::ImportError Defines the errors cases for \l OrganizerModel::importItems() -function. \list \li OrganizerModel::ImportNoError Completed succesfully, no error. \li OrganizerModel::ImportUnspecifiedError Unspecified error. \li OrganizerModel::ImportIOError Input/output error. \li OrganizerModel::ImportOutOfMemoryError Out of memory error. \li OrganizerModel::ImportNotReadyError Not ready for importing. Only one import operation can be active at a time. \li OrganizerModel::ImportParseError Error during parsing. \endlist */ /*! \qmlsignal OrganizerModel::onImportCompleted() This signal is emitted, when \l OrganizerModel::importItems() completes. The success of operation can be seen on \a error which is defined in \l OrganizerModel::ImportError. \a url indicates the file, which was imported. If the operation was succesful, items are now imported to backend. If \l OrganizerModel::autoUpdate is enabled, \l OrganizerModel::modelChanged will be emitted when imported items are also visible on \l OrganizerModel's data model. */ /*! \qmlmethod OrganizerModel::importItems(url url, list profiles) Import organizer items from a vcalendar by the given \a url and optional \a profiles. Only one import operation can be active at a time. */ void QDeclarativeOrganizerModel::importItems(const QUrl& url, const QStringList &profiles) { Q_D(QDeclarativeOrganizerModel); ImportError importError = ImportNotReadyError; // Reader is capable of handling only one request at the time. if (!d->m_reader || (d->m_reader->state() != QVersitReader::ActiveState)) { d->m_importProfiles = profiles; //TODO: need to allow download vcard from network QFile *file = new QFile(urlToLocalFileName(url)); if (file->open(QIODevice::ReadOnly)) { if (!d->m_reader) { d->m_reader = new QVersitReader; connect(d->m_reader, SIGNAL(stateChanged(QVersitReader::State)), this, SLOT(startImport(QVersitReader::State))); } d->m_reader->setDevice(file); if (d->m_reader->startReading()) { d->m_lastImportUrl = url; return; } importError = QDeclarativeOrganizerModel::ImportError(d->m_reader->error()); } else { importError = ImportIOError; } } // If cannot startReading because already running then report the import error now emit importCompleted(importError, url); } /*! \qmlsignal OrganizerModel::onExportCompleted() This signal is emitted, when \l OrganizerModel::exportItems() completes. The success of operation can be seen on \a error which is defined in \l OrganizerModel::ExportError. \a url indicates the file, which was exported. */ /*! \qmlmethod OrganizerModel::exportItems(url url, list profiles) Export organizer items into a vcalendar file to the given \a url by optional \a profiles. At the moment only the local file url is supported in export method. */ void QDeclarativeOrganizerModel::exportItems(const QUrl &url, const QStringList &profiles) { Q_D(QDeclarativeOrganizerModel); ExportError exportError = ExportNotReadyError; // Writer is capable of handling only one request at the time. if (!d->m_writer || (d->m_writer->state() != QVersitWriter::ActiveState)) { QString profile = profiles.isEmpty() ? QString() : profiles.at(0); QVersitOrganizerExporter exporter(profile); QList items; foreach (QDeclarativeOrganizerItem *di, d->m_items) items.append(di->item()); exporter.exportItems(items, QVersitDocument::ICalendar20Type); QVersitDocument document = exporter.document(); QFile *file = new QFile(urlToLocalFileName(url)); if (file->open(QIODevice::ReadWrite)) { if (!d->m_writer) { d->m_writer = new QVersitWriter; connect(d->m_writer, SIGNAL(stateChanged(QVersitWriter::State)), this, SLOT(itemsExported(QVersitWriter::State))); } d->m_writer->setDevice(file); if (d->m_writer->startWriting(document)) { d->m_lastExportUrl = url; return; } exportError = QDeclarativeOrganizerModel::ExportError(d->m_writer->error()); } else { exportError = ExportIOError; } } emit exportCompleted(exportError, url); } void QDeclarativeOrganizerModel::itemsExported(QVersitWriter::State state) { Q_D(QDeclarativeOrganizerModel); if (state == QVersitWriter::FinishedState || state == QVersitWriter::CanceledState) { emit exportCompleted(QDeclarativeOrganizerModel::ExportError(d->m_writer->error()), d->m_lastExportUrl); delete d->m_writer->device(); d->m_writer->setDevice(0); } } int QDeclarativeOrganizerModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); Q_D(const QDeclarativeOrganizerModel); return d->m_items.count(); } void QDeclarativeOrganizerModel::setManager(const QString& managerName) { Q_D(QDeclarativeOrganizerModel); if (d->m_manager && (managerName == d->m_manager->managerName() || managerName == d->m_manager->managerUri())) return; if (d->m_manager) { cancelUpdate(); d->m_updatePendingFlag = QDeclarativeOrganizerModelPrivate::NonePending; delete d->m_manager; } if (managerName.startsWith("qtorganizer:")) { d->m_manager = QOrganizerManager::fromUri(managerName, this); } else { d->m_manager = new QOrganizerManager(managerName, QMap(), this); } connect(d->m_manager, SIGNAL(dataChanged()), this, SLOT(doUpdate())); connect(d->m_manager, SIGNAL(itemsModified(QList >)), this, SLOT(onItemsModified(QList >))); connect(d->m_manager, SIGNAL(collectionsAdded(QList)), this, SLOT(fetchCollections())); connect(d->m_manager, SIGNAL(collectionsChanged(QList)), this, SLOT(fetchCollections())); connect(d->m_manager, SIGNAL(collectionsRemoved(QList)), this, SLOT(fetchCollections())); const QOrganizerManager::Error managerError = d->m_manager->error(); if (QOrganizerManager::NoError != managerError && d->m_error != managerError) { d->m_error = managerError; emit errorChanged(); } else if (QOrganizerManager::NoError != d->m_error) { d->m_error = QOrganizerManager::NoError; emit errorChanged(); } emit managerChanged(); } void QDeclarativeOrganizerModel::componentComplete() { Q_D(QDeclarativeOrganizerModel); d->m_componentCompleted = true; if (!d->m_manager) setManager(QString()); if (d->m_autoUpdate) { d->m_initialUpdate = true; update(); } else { emit modelChanged(); } } /*! \qmlproperty Filter OrganizerModel::filter This property holds the filter instance used by the organizer model. Set filter property to 'null', when you want to reset it to default value. \sa Filter */ QDeclarativeOrganizerItemFilter* QDeclarativeOrganizerModel::filter() const { Q_D(const QDeclarativeOrganizerModel); return d->m_filter; } void QDeclarativeOrganizerModel::setFilter(QDeclarativeOrganizerItemFilter* filter) { Q_D(QDeclarativeOrganizerModel); if (filter != d->m_filter) { if (d->m_filter) disconnect(d->m_filter, SIGNAL(filterChanged()), this, SIGNAL(filterChanged())); d->m_filter = filter; if (d->m_filter) connect(d->m_filter, SIGNAL(filterChanged()), this, SIGNAL(filterChanged()), Qt::UniqueConnection); emit filterChanged(); } } /*! \qmlproperty FetchHint OrganizerModel::fetchHint This property holds the fetch hint instance used by the organizer model. \sa FetchHint */ QDeclarativeOrganizerItemFetchHint* QDeclarativeOrganizerModel::fetchHint() const { Q_D(const QDeclarativeOrganizerModel); return d->m_fetchHint; } void QDeclarativeOrganizerModel::setFetchHint(QDeclarativeOrganizerItemFetchHint* fetchHint) { Q_D(QDeclarativeOrganizerModel); if (fetchHint != d->m_fetchHint) { if (d->m_fetchHint) disconnect(d->m_fetchHint, SIGNAL(fetchHintChanged()), this, SIGNAL(fetchHintChanged())); d->m_fetchHint = fetchHint; if (d->m_fetchHint) connect(d->m_fetchHint, SIGNAL(fetchHintChanged()), this, SIGNAL(fetchHintChanged()), Qt::UniqueConnection); emit fetchHintChanged(); } } /*! \qmlproperty int OrganizerModel::itemCount This property holds the size of organizer items the OrganizerModel currently holds. This property is read only. */ int QDeclarativeOrganizerModel::itemCount() const { Q_D(const QDeclarativeOrganizerModel); return d->m_items.size(); } /*! \qmlproperty string OrganizerModel::error This property holds the latest error code returned by the organizer manager. This property is read only. */ QString QDeclarativeOrganizerModel::error() const { Q_D(const QDeclarativeOrganizerModel); if (d->m_manager) { switch (d->m_error) { case QOrganizerManager::DoesNotExistError: return QStringLiteral("DoesNotExist"); case QOrganizerManager::AlreadyExistsError: return QStringLiteral("AlreadyExists"); case QOrganizerManager::InvalidDetailError: return QStringLiteral("InvalidDetail"); case QOrganizerManager::InvalidCollectionError: return QStringLiteral("InvalidCollection"); case QOrganizerManager::LockedError: return QStringLiteral("LockedError"); case QOrganizerManager::DetailAccessError: return QStringLiteral("DetailAccessError"); case QOrganizerManager::PermissionsError: return QStringLiteral("PermissionsError"); case QOrganizerManager::OutOfMemoryError: return QStringLiteral("OutOfMemory"); case QOrganizerManager::NotSupportedError: return QStringLiteral("NotSupported"); case QOrganizerManager::BadArgumentError: return QStringLiteral("BadArgument"); case QOrganizerManager::UnspecifiedError: return QStringLiteral("UnspecifiedError"); case QOrganizerManager::LimitReachedError: return QStringLiteral("LimitReached"); case QOrganizerManager::InvalidItemTypeError: return QStringLiteral("InvalidItemType"); case QOrganizerManager::InvalidOccurrenceError: return QStringLiteral("InvalidOccurrence"); default: break; } } return QStringLiteral("NoError"); } /*! \qmlproperty list OrganizerModel::sortOrders This property holds a list of sort orders used by the organizer model. \sa SortOrder */ QQmlListProperty QDeclarativeOrganizerModel::sortOrders() { return QQmlListProperty(this, 0, sortOrder_append, sortOrder_count, sortOrder_at, sortOrder_clear); } void QDeclarativeOrganizerModel::startImport(QVersitReader::State state) { Q_D(QDeclarativeOrganizerModel); if (state == QVersitReader::FinishedState || state == QVersitReader::CanceledState) { if (!d->m_reader->results().isEmpty()) { QVersitOrganizerImporter importer; importer.importDocument(d->m_reader->results().at(0)); QList items = importer.items(); delete d->m_reader->device(); d->m_reader->setDevice(0); if (d->m_manager && !d->m_manager->saveItems(&items) && d->m_error != d->m_manager->error()) { d->m_error = d->m_manager->error(); emit errorChanged(); } } emit importCompleted(QDeclarativeOrganizerModel::ImportError(d->m_reader->error()), d->m_lastImportUrl); } } bool QDeclarativeOrganizerModel::itemHasRecurrence(const QOrganizerItem& oi) const { if (oi.type() == QOrganizerItemType::TypeEvent || oi.type() == QOrganizerItemType::TypeTodo) { QOrganizerItemRecurrence recur = oi.detail(QOrganizerItemDetail::TypeRecurrence); return !recur.recurrenceDates().isEmpty() || !recur.recurrenceRules().isEmpty(); } return false; } QDeclarativeOrganizerItem* QDeclarativeOrganizerModel::createItem(const QOrganizerItem& item) { QDeclarativeOrganizerItem* di; if (item.type() == QOrganizerItemType::TypeEvent) di = new QDeclarativeOrganizerEvent(this); else if (item.type() == QOrganizerItemType::TypeEventOccurrence) di = new QDeclarativeOrganizerEventOccurrence(this); else if (item.type() == QOrganizerItemType::TypeTodo) di = new QDeclarativeOrganizerTodo(this); else if (item.type() == QOrganizerItemType::TypeTodoOccurrence) di = new QDeclarativeOrganizerTodoOccurrence(this); else if (item.type() == QOrganizerItemType::TypeJournal) di = new QDeclarativeOrganizerJournal(this); else if (item.type() == QOrganizerItemType::TypeNote) di = new QDeclarativeOrganizerNote(this); else di = new QDeclarativeOrganizerItem(this); di->setItem(item); return di; } void QDeclarativeOrganizerModel::checkError(const QOrganizerAbstractRequest *request) { Q_D(QDeclarativeOrganizerModel); if (request && d->m_error != request->error()) { d->m_error = request->error(); emit errorChanged(); } } /*! \qmlsignal OrganizerModel::onItemsFetched(int requestId, list fetchedItems) This handler is called when request of the given \a requestId is finished with the \a fetchedItems. \sa fetchItems */ /*! \qmlmethod int OrganizerModel::fetchItems(date start, date end, Filter filter, int maxCount, list sortOrders, FetchHint fetchHint) This method will start a request to fetch items between the given \a start and \a end dates. Optionally a \a sort order, \a filter, \a fetchHint and \a maxCount can be specified to narrow the search. If nothing is set for these optional paramenters then defaults are applied, essentially any sort order, default filter, default storage location and all items. The unique ID of this request will be returned. If the request can't be started -1 is returned. The end date must be greater than the start date for this method to start a fetch request. Note that the items fetched won't be added to the model, but can be accessed through the onItemsFetched handler. No properties in the model are updated at all. \sa onItemsFetched */ int QDeclarativeOrganizerModel::fetchItems(const QDateTime &start, const QDateTime &end, QDeclarativeOrganizerItemFilter *filter, int maxCount, const QVariantList &sortOrders, QDeclarativeOrganizerItemFetchHint *fetchHint) { Q_D(QDeclarativeOrganizerModel); if (!start.isValid() || !end.isValid() || !(end > start)) { return -1; } // Parameter validation left to fetch request method. QOrganizerItemFetchRequest *fetchRequest = new QOrganizerItemFetchRequest(this); connect(fetchRequest, SIGNAL(stateChanged(QOrganizerAbstractRequest::State)), this, SLOT(onFetchItemsRequestStateChanged(QOrganizerAbstractRequest::State))); QList sList; QVariantList::const_iterator it = sortOrders.begin(); while (it != sortOrders.end()) { if ((*it).canConvert()) { QDeclarativeOrganizerItemSortOrder *sortOrderItem = (*it).value(); sList << sortOrderItem->sortOrder(); } ++it; } const QOrganizerItemFilter &fetchFilter = filter->filter(); fetchRequest->setFilter( fetchFilter ); const QOrganizerItemFetchHint &hint = fetchHint->fetchHint(); fetchRequest->setManager(d->m_manager); fetchRequest->setStartDate(start); fetchRequest->setEndDate(end); fetchRequest->setSorting(sList); fetchRequest->setMaxCount(maxCount); fetchRequest->setFetchHint(hint); int requestId = d->m_lastRequestId.fetchAndAddOrdered(1); d->m_requestIdHash.insert(fetchRequest, requestId); if (fetchRequest->start()) { return requestId; } else { d->m_requestIdHash.remove(fetchRequest); return -1; } } /*! \qmlmethod int OrganizerModel::fetchItems(stringlist itemIds) Starts a request to fetch items by the given \a itemIds, and returns the unique ID of this request. -1 is returned if the request can't be started. Note that the items fetched won't be added to the model, but can be accessed through the onItemsFetched handler. \sa onItemsFetched */ int QDeclarativeOrganizerModel::fetchItems(const QStringList &itemIds) { Q_D(QDeclarativeOrganizerModel); if (itemIds.isEmpty()) return -1; QOrganizerItemFetchByIdRequest *fetchRequest = new QOrganizerItemFetchByIdRequest(this); connect(fetchRequest, SIGNAL(stateChanged(QOrganizerAbstractRequest::State)), this, SLOT(onFetchItemsRequestStateChanged(QOrganizerAbstractRequest::State))); fetchRequest->setManager(d->m_manager); QList ids; foreach (const QString &itemId, itemIds) ids.append(QOrganizerItemId::fromString(itemId)); fetchRequest->setIds(ids); int requestId = d->m_lastRequestId.fetchAndAddOrdered(1); d->m_requestIdHash.insert(fetchRequest, requestId); if (fetchRequest->start()) { return requestId; } else { d->m_requestIdHash.remove(fetchRequest); return -1; } } /*! \internal */ void QDeclarativeOrganizerModel::onFetchItemsRequestStateChanged(QOrganizerAbstractRequest::State state) { Q_D(QDeclarativeOrganizerModel); QOrganizerAbstractRequest *request=qobject_cast(sender()); if (state != QOrganizerAbstractRequest::FinishedState || !request) return; QOrganizerItemFetchRequest *itemFetchRequest = qobject_cast(request); QOrganizerItemFetchByIdRequest *itemByIdFetchRequest = qobject_cast(request); if (!itemFetchRequest && !itemByIdFetchRequest) return; checkError(request); const int requestId = d->m_requestIdHash.value(request, -1); if (requestId == -1) qWarning() << Q_FUNC_INFO << "transaction not found from the request hash"; else d->m_requestIdHash.remove(request); QVariantList list; if (request->error() == QOrganizerManager::NoError) { const QList &items((!itemFetchRequest) ? itemByIdFetchRequest->items():itemFetchRequest->items()); QDeclarativeOrganizerItem *declarativeItem(0); foreach (const QOrganizerItem &item, items) { switch (item.type()) { case QOrganizerItemType::TypeEvent: declarativeItem = new QDeclarativeOrganizerEvent(this); break; case QOrganizerItemType::TypeEventOccurrence: declarativeItem = new QDeclarativeOrganizerEventOccurrence(this); break; case QOrganizerItemType::TypeTodo: declarativeItem = new QDeclarativeOrganizerTodo(this); break; case QOrganizerItemType::TypeTodoOccurrence: declarativeItem = new QDeclarativeOrganizerTodoOccurrence(this); break; default: declarativeItem = new QDeclarativeOrganizerItem(this); break; } declarativeItem->setItem(item); list.append(QVariant::fromValue((QObject *)declarativeItem)); } } emit itemsFetched(requestId, list); request->deleteLater(); } /*! \qmlmethod list OrganizerModel::containsItems(date start, date end, int interval) Returns a list of booleans telling if there is any item falling in the given time range. For example, if the \a start time is 2011-12-08 14:00:00, the \a end time is 2011-12-08 20:00:00, and the \a interval is 3600 (seconds), a list of size 6 is returned, telling if there is any item falling in the range of 14:00:00 to 15:00:00, 15:00:00 to 16:00:00, ..., 19:00:00 to 20:00:00. */ QList QDeclarativeOrganizerModel::containsItems(const QDateTime &start, const QDateTime &end, int interval) { Q_D(QDeclarativeOrganizerModel); if (!(start.isValid() && end.isValid() && start < end && interval > 0)) return QList(); int i(0); int count = qCeil(start.secsTo(end) / static_cast(interval)); QVector occupiedTimeSlots(count, false); QVector dateTime(count + 1); dateTime[0] = start; for (i = 1; i < count; ++i) dateTime[i] = dateTime.at(i - 1).addSecs(interval); dateTime[count] = end; QDateTime startTime; QDateTime endTime; bool itemStartFound; foreach (QDeclarativeOrganizerItem *item, d->m_items) { startTime = item->itemStartTime(); endTime = item->itemEndTime(); // check if item is occurring between start and end if (!((!startTime.isNull() && startTime >= start && startTime < end) || (!endTime.isNull() && endTime > start && endTime <= end) || (!startTime.isNull() && !endTime.isNull() && startTime <= start && endTime >= end))) continue; itemStartFound = (!startTime.isNull() && startTime <= start); for (i = 0; i < count; ++i) { if (!endTime.isNull() && endTime > dateTime.at(i) && endTime <= dateTime.at(i + 1)) { // item end time found, no need to check more time slots occupiedTimeSlots[i] = true; break; } if (occupiedTimeSlots.at(i)) continue; if (itemStartFound) { occupiedTimeSlots[i] = true; } else if (!startTime.isNull() && startTime < dateTime.at(i + 1)) { if (startTime >= dateTime.at(i)) occupiedTimeSlots[i] = true; if (endTime.isNull()) break; itemStartFound = true; } } } return occupiedTimeSlots.toList(); } /*! \qmlmethod bool OrganizerModel::containsItems(date start, date end) Returns true if there is at least one OrganizerItem between the given date range. Both the \a start and \a end parameters are optional, if no \a end parameter, returns true if there are item(s) after \a start, if neither start nor end date time provided, returns true if items in the current model is not empty, otherwise return false. \sa itemIds() */ bool QDeclarativeOrganizerModel::containsItems(const QDateTime &start, const QDateTime &end) { return !itemIds(start, end).isEmpty(); } /*! \qmlmethod list OrganizerModel::itemsByTimePeriod(date start, date end) Returns the list of organizer items between the given \a start and \a end period. */ QVariantList QDeclarativeOrganizerModel::itemsByTimePeriod(const QDateTime &start, const QDateTime &end) { Q_D(QDeclarativeOrganizerModel); QVariantList list; if (start.isValid() && end.isValid()) { QDateTime startTime; QDateTime endTime; foreach (QDeclarativeOrganizerItem *item, d->m_items) { startTime = item->itemStartTime(); endTime = item->itemEndTime(); if ((startTime.isValid() && startTime <= start && endTime >= end) || (startTime >= start && startTime <= end) || (endTime >= start && endTime <= end)) { list.append(QVariant::fromValue((QObject *)item)); } } } else if (start.isValid()) { foreach (QDeclarativeOrganizerItem *item, d->m_items) { if (item->itemEndTime() >= start) list.append(QVariant::fromValue((QObject *)item)); } } else if (end.isValid()) { foreach (QDeclarativeOrganizerItem *item, d->m_items) { if (item->itemStartTime() <= end) list.append(QVariant::fromValue((QObject *)item)); } } else { foreach (QDeclarativeOrganizerItem *item, d->m_items) list.append(QVariant::fromValue((QObject *)item)); } return list; } /*! \qmlmethod OrganizerItem OrganizerModel::item(string itemId) Returns the OrganizerItem object with the given \a itemId. */ QDeclarativeOrganizerItem *QDeclarativeOrganizerModel::item(const QString &itemId) { Q_D(QDeclarativeOrganizerModel); if (itemId.isEmpty()) return 0; return d->m_itemIdHash.value(itemId, 0); } /*! \qmlmethod list OrganizerModel::itemIds(date start, date end) Returns the list of organizer item ids between the given date range \a start and \a end, excluding generated occurrences. Both the \a start and \a end parameters are optional, if no \a end parameter, returns all item ids from \a start, if neither start nor end date time provided, returns all item ids in the current model. \sa containsItems() */ QStringList QDeclarativeOrganizerModel::itemIds(const QDateTime &start, const QDateTime &end) { Q_D(QDeclarativeOrganizerModel); //TODO: quick search this QStringList ids; if (!end.isNull()) { // both start date and end date are valid foreach (QDeclarativeOrganizerItem* item, d->m_items) { if (item->generatedOccurrence()) continue; if ( (item->itemStartTime() >= start && item->itemStartTime() <= end) || (item->itemEndTime() >= start && item->itemEndTime() <= end) || (item->itemEndTime() > end && item->itemStartTime() < start)) ids << item->itemId(); } } else if (!start.isNull()) { // only a valid start date is valid foreach (QDeclarativeOrganizerItem* item, d->m_items) { if (!item->generatedOccurrence() && item->itemStartTime() >= start) ids << item->itemId(); } } else { // neither start nor end date is valid foreach (QDeclarativeOrganizerItem* item, d->m_items) { if (!item->generatedOccurrence()) ids << item->itemId(); } } return ids; } void QDeclarativeOrganizerModel::fetchAgain() { Q_D(QDeclarativeOrganizerModel); cancelUpdate(); d->m_fetchRequest = new QOrganizerItemFetchRequest(this); d->m_fetchRequest->setManager(d->m_manager); d->m_fetchRequest->setSorting(d->m_sortOrders); d->m_fetchRequest->setStartDate(d->m_startPeriod); d->m_fetchRequest->setEndDate(d->m_endPeriod); if (d->m_filter){ d->m_fetchRequest->setFilter(d->m_filter->filter()); } else { d->m_fetchRequest->setFilter(QOrganizerItemFilter()); } d->m_fetchRequest->setFetchHint(d->m_fetchHint ? d->m_fetchHint->fetchHint() : QOrganizerItemFetchHint()); connect(d->m_fetchRequest, SIGNAL(stateChanged(QOrganizerAbstractRequest::State)), this, SLOT(requestUpdated())); d->m_fetchRequest->start(); } /* This slot function is connected with item fetch requests and item occurrence fetch requests, so the QObject::sender() must be checked for the right sender type. During update() function, the fetchAgain() will be invoked, inside fetchAgain(), a QOrganizerItemFetchRequest object is created and started, when this fetch request finished, this requestUpdate() slot will be invoked for the first time. Then check each of the organizer items returned by the item fetch request, if the item is a recurrence item, a QOrganizerItemOccurrenceFetchRequest object will be created and started. When each of these occurrence fetch requests finishes, this requestUpdated() slot will be invoked again and insert the returned occurrence items into the d->m_items list. */ void QDeclarativeOrganizerModel::requestUpdated() { Q_D(QDeclarativeOrganizerModel); QList items; QOrganizerItemFetchRequest* ifr = qobject_cast(QObject::sender()); Q_ASSERT(ifr); if (ifr->isFinished()) { items = ifr->items(); checkError(ifr); ifr->deleteLater(); d->m_fetchRequest = 0; d->m_updatePendingFlag &= ~QDeclarativeOrganizerModelPrivate::UpdatingItemsPending; } else { return; } if (!items.isEmpty() || !d->m_items.isEmpty() || d->m_initialUpdate) { // full update: first go through new items and check if they // existed earlier. if they did, use the existing declarative wrapper. // otherwise create new declarative item. // for occurrences new declarative item is always created. QList newList; QHash newItemIdHash; QHash::iterator iterator; QOrganizerItem item; QString idString; QDeclarativeOrganizerItem *declarativeItem; d->m_initialUpdate = false; int i; for (i = 0; i < items.size(); i++) { item = items[i]; idString = item.id().toString(); if (item.id().isNull()) { // this is occurrence declarativeItem = createItem(item); } else { iterator = d->m_itemIdHash.find(idString); if (iterator != d->m_itemIdHash.end()) { declarativeItem = iterator.value(); declarativeItem->setItem(item); } else { declarativeItem = createItem(item); } newItemIdHash.insert(idString, declarativeItem); } newList.append(declarativeItem); } // go through old items and delete items, which are not part of the // new item set. delete also all old occurrences. for (i = 0; i < d->m_items.size(); i++) { // FIXME: avoid unnecessary usage of item getter which copies all details... if (d->m_items[i]->item().id().isNull()) { d->m_items[i]->deleteLater(); } else { iterator = newItemIdHash.find(d->m_items[i]->itemId()); if (iterator == newItemIdHash.end()) d->m_items[i]->deleteLater(); } } beginResetModel(); d->m_items = newList; endResetModel(); d->m_itemIdHash = newItemIdHash; emit modelChanged(); } } /*! \qmlmethod OrganizerModel::saveItem(OrganizerItem item) Saves asynchronously the given \a item into the organizer backend. */ void QDeclarativeOrganizerModel::saveItem(QDeclarativeOrganizerItem* di) { Q_D(QDeclarativeOrganizerModel); if (di) { QOrganizerItem item = di->item(); QOrganizerItemSaveRequest* req = new QOrganizerItemSaveRequest(this); req->setManager(d->m_manager); req->setItem(item); connect(req, SIGNAL(stateChanged(QOrganizerAbstractRequest::State)), this, SLOT(onRequestStateChanged(QOrganizerAbstractRequest::State))); req->start(); } } /*! \qmlmethod OrganizerModel::removeItem(string itemId) Removes the organizer item with the given \a itemId from the backend. */ void QDeclarativeOrganizerModel::removeItem(const QString& id) { QList ids; ids << id; removeItems(ids); } /*! \qmlmethod OrganizerModel::removeItem(OrganizerItem item) Removes the given organizer \a item from the backend. */ void QDeclarativeOrganizerModel::removeItem(QDeclarativeOrganizerItem *item) { Q_D(QDeclarativeOrganizerModel); QOrganizerItemRemoveRequest* req = new QOrganizerItemRemoveRequest(this); req->setManager(d->m_manager); req->setItem(item->item()); connect(req, SIGNAL(stateChanged(QOrganizerAbstractRequest::State)), this, SLOT(onRequestStateChanged(QOrganizerAbstractRequest::State))); req->start(); } /*! \qmlmethod OrganizerModel::removeItems(list itemId) Removes asynchronously the organizer items with the given \a ids from the backend. */ void QDeclarativeOrganizerModel::removeItems(const QStringList& ids) { Q_D(QDeclarativeOrganizerModel); QOrganizerItemRemoveByIdRequest* req = new QOrganizerItemRemoveByIdRequest(this); req->setManager(d->m_manager); QList oids; // FIXME: no special format for occurrence ids foreach (const QString& id, ids) { if (id.startsWith(QString("qtorganizer:occurrence"))) { qmlInfo(this) << tr("Can't remove an occurrence item, please modify the parent item's recurrence rule instead!"); continue; } QOrganizerItemId itemId = QOrganizerItemId::fromString(id); if (!itemId.isNull()) { oids.append(itemId); } } req->setItemIds(oids); connect(req, SIGNAL(stateChanged(QOrganizerAbstractRequest::State)), this, SLOT(onRequestStateChanged(QOrganizerAbstractRequest::State))); req->start(); } /*! \qmlmethod OrganizerModel::removeItems(list items) Removes asynchronously the organizer items in the given \a items list from the backend. */ void QDeclarativeOrganizerModel::removeItems(const QList &items) { Q_D(QDeclarativeOrganizerModel); QOrganizerItemRemoveRequest* req = new QOrganizerItemRemoveRequest(this); req->setManager(d->m_manager); QList ois; for (int i = 0; i < items.size(); i++) { ois.append(items[i].item()); } req->setItems(ois); connect(req, SIGNAL(stateChanged(QOrganizerAbstractRequest::State)), this, SLOT(onRequestStateChanged(QOrganizerAbstractRequest::State))); req->start(); } /*! \internal */ void QDeclarativeOrganizerModel::onRequestStateChanged(QOrganizerAbstractRequest::State newState) { if (newState != QOrganizerAbstractRequest::FinishedState) return; QOrganizerAbstractRequest *request = qobject_cast(sender()); Q_ASSERT(request); checkError(request); request->deleteLater(); } void QDeclarativeOrganizerModel::removeItemsFromModel(const QList &itemIds) { Q_D(QDeclarativeOrganizerModel); bool emitSignal = false; bool itemIdFound = false; foreach (const QString &itemId, itemIds) { itemIdFound = false; // generated occurrences are not in m_itemIdHash if (d->m_itemIdHash.remove(itemId) > 0) itemIdFound = true; for (int i = d->m_items.count() - 1; i >= 0; i--) { if (itemIdFound) { if (d->m_items.at(i)->itemId() == itemId) { beginRemoveRows(QModelIndex(), i, i); d->m_items.removeAt(i); endRemoveRows(); emitSignal = true; break; } } else if (d->m_items.at(i)->generatedOccurrence()) { QDeclarativeOrganizerItemDetail *parentDetail = d->m_items.at(i)->detail(QDeclarativeOrganizerItemDetail::Parent); if (parentDetail->value(QDeclarativeOrganizerItemParent::FieldParentId).toString() == itemId) { beginRemoveRows(QModelIndex(), i, i); d->m_items.removeAt(i); endRemoveRows(); emitSignal = true; } } } } if (emitSignal) emit modelChanged(); } /*! \internal It's invoked upon the QOrganizerManager::itemsModified() signal. */ void QDeclarativeOrganizerModel::onItemsModified(const QList > &itemIds) { Q_D(QDeclarativeOrganizerModel); if (!d->m_autoUpdate) return; QSet addedAndChangedItems; QList removedItems; for (int i = itemIds.size() - 1; i >= 0; i--) { if (itemIds[i].second == QOrganizerManager::Remove) { // check that item has not been added after removing it if (!addedAndChangedItems.contains(itemIds[i].first)) removedItems.append(itemIds[i].first.toString()); } else { addedAndChangedItems.insert(itemIds[i].first); } } if (!removedItems.isEmpty()) removeItemsFromModel(removedItems); if (!addedAndChangedItems.isEmpty()) { // FIXME; to be optimized with fetching only the modified items // from the storage locations modified items are on QOrganizerItemFetchRequest *fetchRequest = new QOrganizerItemFetchRequest(this); connect(fetchRequest, SIGNAL(stateChanged(QOrganizerAbstractRequest::State)), this, SLOT(onItemsModifiedFetchRequestStateChanged(QOrganizerAbstractRequest::State))); fetchRequest->setManager(d->m_manager); fetchRequest->setStartDate(d->m_startPeriod); fetchRequest->setEndDate(d->m_endPeriod); fetchRequest->setFilter(d->m_filter ? d->m_filter->filter() : QOrganizerItemFilter()); fetchRequest->setSorting(d->m_sortOrders); fetchRequest->setFetchHint(d->m_fetchHint ? d->m_fetchHint->fetchHint() : QOrganizerItemFetchHint()); d->m_notifiedItems.insert(fetchRequest, addedAndChangedItems); fetchRequest->start(); } } /*! \internal It's invoked by the fetch request from onItemsModified(). */ void QDeclarativeOrganizerModel::onItemsModifiedFetchRequestStateChanged(QOrganizerAbstractRequest::State state) { // NOTE: this function assumes the sorting algorithm gives always the same result with // same data. E.g. items which have the identical sorting key must be sorted too. Q_D(QDeclarativeOrganizerModel); if (state != QOrganizerAbstractRequest::FinishedState) return; QOrganizerItemFetchRequest *request = qobject_cast(sender()); Q_ASSERT(request); checkError(request); QSet notifiedItems = d->m_notifiedItems.value(request); if (notifiedItems.isEmpty()) return; if (request->error() == QOrganizerManager::NoError) { bool emitSignal = false; QList fetchedItems = request->items(); QOrganizerItem oldItem; QOrganizerItem newItem; QOrganizerItemParent oldParentDetail; QOrganizerItemParent newParentDetail; QDeclarativeOrganizerItem *declarativeItem; QSet removedIds; QSet addedIds; int oldInd = 0; int newInd = 0; while (newInd < fetchedItems.size()) { bool addNewItem = false; bool oldItemExists = false; newItem = fetchedItems[newInd]; if (oldInd < d->m_items.size()) { // quick check if items are same in old and new event lists // FIXME: avoid unnecessary usage of item getter which copies all details oldItem = d->m_items[oldInd]->item(); oldItemExists = true; if (!newItem.id().isNull() && !oldItem.id().isNull() && newItem.id() == oldItem.id()) { if (notifiedItems.contains(newItem.id())) { d->m_items[oldInd]->setItem(newItem); const QModelIndex idx = index(oldInd, 0); emit dataChanged(idx, idx); emitSignal = true; } newInd++; oldInd++; continue; } } // check should we remove old item if (oldItemExists) { if (oldItem.id().isNull()) { // this is generated occurrence oldParentDetail = oldItem.detail(QOrganizerItemDetail::TypeParent); if (notifiedItems.contains(oldParentDetail.parentId())) { beginRemoveRows(QModelIndex(), oldInd, oldInd); d->m_items.takeAt(oldInd)->deleteLater(); endRemoveRows(); emitSignal = true; continue; } } else if (notifiedItems.contains(oldItem.id())) { // if notifiedItems contains the oldItem id, it means the item has been // changed and we should reuse the declarative part and only remove // rows from abstract list model // it might also mean that oldItem has been changed so that it does not belong to // the model anymore (e.g. changing fron normal item to recurring item) beginRemoveRows(QModelIndex(), oldInd, oldInd); d->m_items.removeAt(oldInd); endRemoveRows(); removedIds.insert(oldItem.id()); emitSignal = true; continue; } else if (notifiedItems.contains(newItem.id())) { // if newItem is a notified item and does not correspond to an oldItem // then find a correspondent item by id in the old items list // and remove it from the hash and list for (int removeInd = oldInd + 1; removeInd < d->m_items.size(); ++removeInd) { if (newItem.id() == d->m_items[removeInd]->item().id()) { beginRemoveRows(QModelIndex(), removeInd, removeInd); d->m_itemIdHash.remove(d->m_items[removeInd]->itemId()); d->m_items.takeAt(removeInd)->deleteLater(); endRemoveRows(); emitSignal = true; break; } } } } // check should we add the new item if (newItem.id().isNull() && (newItem.type() == QOrganizerItemType::TypeEventOccurrence || newItem.type() == QOrganizerItemType::TypeTodoOccurrence)) { // this is occurrence (generated or exception) newParentDetail = newItem.detail(QOrganizerItemDetail::TypeParent); if (notifiedItems.contains(newParentDetail.parentId())) { declarativeItem = createItem(newItem); addNewItem = true; } } else if (notifiedItems.contains(newItem.id())) { QHash::const_iterator iterator = d->m_itemIdHash.find(newItem.id().toString()); if (iterator == d->m_itemIdHash.end()) { declarativeItem = createItem(newItem); d->m_itemIdHash.insert(declarativeItem->itemId(), declarativeItem); } else { declarativeItem = d->m_itemIdHash.value(newItem.id().toString()); addedIds.insert(newItem.id()); } addNewItem = true; } if (addNewItem) { beginInsertRows(QModelIndex(), oldInd, oldInd); d->m_items.insert(oldInd, declarativeItem); endInsertRows(); emitSignal = true; } oldInd++; newInd++; } // remove the rest of the old items if (oldInd <= d->m_items.size() - 1) { beginRemoveRows(QModelIndex(), oldInd, d->m_items.size() - 1); while (oldInd < d->m_items.size()) { d->m_itemIdHash.remove(d->m_items[oldInd]->itemId()); d->m_items.takeAt(oldInd)->deleteLater(); emitSignal = true; oldInd++; } endRemoveRows(); } // remove items which were changed so that they are no longer part of the model // they have been removed from the model earlier, but need to still be removed from the hash // and deleted removedIds.subtract(addedIds); foreach (const QOrganizerItemId &id, removedIds) { QDeclarativeOrganizerItem *changedItem = d->m_itemIdHash.take(id.toString()); if (changedItem) { changedItem->deleteLater(); emitSignal = true; } } if (emitSignal) emit modelChanged(); } d->m_notifiedItems.remove(request); request->deleteLater(); } /*! \qmlmethod OrganizerModel::fetchCollections() Fetch asynchronously a list of organizer collections from the organizer backend. */ void QDeclarativeOrganizerModel::fetchCollections() { Q_D(QDeclarativeOrganizerModel); // fetchCollections() is used for both direct calls and // signals from model. For signal from model, check also the // autoupdate-flag. if (sender() == d->m_manager && !d->m_autoUpdate) { return; } QOrganizerCollectionFetchRequest* req = new QOrganizerCollectionFetchRequest(this); req->setManager(d->m_manager); connect(req,SIGNAL(stateChanged(QOrganizerAbstractRequest::State)), this, SLOT(collectionsFetched())); req->start(); } void QDeclarativeOrganizerModel::collectionsFetched() { Q_D(QDeclarativeOrganizerModel); QOrganizerCollectionFetchRequest* req = qobject_cast(QObject::sender()); Q_ASSERT(req); if (req->isFinished() && QOrganizerManager::NoError == req->error()) { d->m_updatePendingFlag &= ~QDeclarativeOrganizerModelPrivate::UpdatingCollectionsPending; // prepare tables QHash collections; foreach (const QOrganizerCollection& collection, req->collections()) { collections.insert(collection.id().toString(), &collection); } QHash declCollections; foreach(QDeclarativeOrganizerCollection* declCollection, d->m_collections) { declCollections.insert(declCollection->collection().id().toString(), declCollection); } // go tables through QHashIterator collIterator(collections); while (collIterator.hasNext()) { collIterator.next(); if (declCollections.contains(collIterator.key())) { // collection on both sides, update the declarative collection declCollections.value(collIterator.key())->setCollection(*collections.value(collIterator.key())); } else { // new collection, add it to declarative collection list QDeclarativeOrganizerCollection* declCollection = new QDeclarativeOrganizerCollection(this); declCollection->setCollection(*collections.value(collIterator.key())); d->m_collections.append(declCollection); } } QHashIterator declCollIterator(declCollections); while (declCollIterator.hasNext()) { declCollIterator.next(); if (!collections.contains(declCollIterator.key())) { // collection deleted on the backend side, delete from declarative collection list QDeclarativeOrganizerCollection* toBeDeletedColl = declCollections.value(declCollIterator.key()); d->m_collections.removeOne(toBeDeletedColl); toBeDeletedColl->deleteLater(); } } emit collectionsChanged(); if (d->m_updatePendingFlag & QDeclarativeOrganizerModelPrivate::UpdatingItemsPending) QMetaObject::invokeMethod(this, "fetchAgain", Qt::QueuedConnection); req->deleteLater(); } checkError(req); } /*! \qmlmethod OrganizerModel::saveCollection(Collection collection) Saves asynchronously the given \a collection into the organizer backend. */ void QDeclarativeOrganizerModel::saveCollection(QDeclarativeOrganizerCollection* declColl) { Q_D(QDeclarativeOrganizerModel); if (declColl) { QOrganizerCollection collection = declColl->collection(); QOrganizerCollectionSaveRequest* req = new QOrganizerCollectionSaveRequest(this); req->setManager(d->m_manager); req->setCollection(collection); connect(req, SIGNAL(stateChanged(QOrganizerAbstractRequest::State)), this, SLOT(onRequestStateChanged(QOrganizerAbstractRequest::State))); req->start(); } } /*! \qmlmethod OrganizerModel::removeCollection(string collectionId) Removes asynchronously the organizer collection with the given \a collectionId from the backend. */ void QDeclarativeOrganizerModel::removeCollection(const QString &collectionId) { Q_D(QDeclarativeOrganizerModel); QOrganizerCollectionRemoveRequest* req = new QOrganizerCollectionRemoveRequest(this); req->setManager(d->m_manager); req->setCollectionId(QOrganizerCollectionId::fromString(collectionId)); connect(req, SIGNAL(stateChanged(QOrganizerAbstractRequest::State)), this, SLOT(onRequestStateChanged(QOrganizerAbstractRequest::State))); req->start(); } /*! \qmlmethod Collection OrganizerModel::defaultCollection() Returns the default Collection object. */ QDeclarativeOrganizerCollection* QDeclarativeOrganizerModel::defaultCollection() { Q_D(QDeclarativeOrganizerModel); return collection(d->m_manager->defaultCollection().id().toString()); } /*! \qmlmethod Collection OrganizerModel::collection(string collectionId) Returns the Collection object which collection id is the given \a collectionId and null if collection id is not found. */ QDeclarativeOrganizerCollection* QDeclarativeOrganizerModel::collection(const QString &collectionId) { Q_D(QDeclarativeOrganizerModel); foreach (QDeclarativeOrganizerCollection* collection, d->m_collections) { if (collection->id() == collectionId) return collection; } return 0; } QVariant QDeclarativeOrganizerModel::data(const QModelIndex &index, int role) const { Q_D(const QDeclarativeOrganizerModel); //Check if QList itme's index is valid before access it, index should be between 0 and count - 1 if (index.row() < 0 || index.row() >= d->m_items.count()) { return QVariant(); } QDeclarativeOrganizerItem* di = d->m_items.at(index.row()); Q_ASSERT(di); QOrganizerItem item = di->item(); switch(role) { case Qt::DisplayRole: return item.displayLabel(); case Qt::DecorationRole: //return pixmap for this item type case OrganizerItemRole: return QVariant::fromValue(di); } return QVariant(); } /*! \qmlproperty list OrganizerModel::items This property holds a list of organizer items in the organizer model. \sa OrganizerItem */ QQmlListProperty QDeclarativeOrganizerModel::items() { return QQmlListProperty(this, 0, item_count, item_at); } /*! \qmlproperty list OrganizerModel::collections This property holds a list of collections in the organizer model. \sa Collection */ QQmlListProperty QDeclarativeOrganizerModel::collections() { return QQmlListProperty(this, 0, collection_count, collection_at); } int QDeclarativeOrganizerModel::item_count(QQmlListProperty *p) { QDeclarativeOrganizerModel* model = qobject_cast(p->object); if (model) return model->d_ptr->m_items.count(); return 0; } QDeclarativeOrganizerItem * QDeclarativeOrganizerModel::item_at(QQmlListProperty *p, int idx) { QDeclarativeOrganizerModel* model = qobject_cast(p->object); if (model && idx >= 0 && idx < model->d_ptr->m_items.size()) return model->d_ptr->m_items.at(idx); return 0; } void QDeclarativeOrganizerModel::sortOrder_append(QQmlListProperty *p, QDeclarativeOrganizerItemSortOrder *sortOrder) { QDeclarativeOrganizerModel* model = qobject_cast(p->object); if (model && sortOrder) { QObject::connect(sortOrder, SIGNAL(sortOrderChanged()), model, SIGNAL(sortOrdersChanged())); model->d_ptr->m_declarativeSortOrders.append(sortOrder); model->d_ptr->m_sortOrders.append(sortOrder->sortOrder()); emit model->sortOrdersChanged(); } } int QDeclarativeOrganizerModel::sortOrder_count(QQmlListProperty *p) { QDeclarativeOrganizerModel* model = qobject_cast(p->object); if (model) return model->d_ptr->m_declarativeSortOrders.size(); return 0; } QDeclarativeOrganizerItemSortOrder * QDeclarativeOrganizerModel::sortOrder_at(QQmlListProperty *p, int idx) { QDeclarativeOrganizerModel* model = qobject_cast(p->object); QDeclarativeOrganizerItemSortOrder* sortOrder = 0; if (model) { int i = 0; foreach (QDeclarativeOrganizerItemSortOrder* s, model->d_ptr->m_declarativeSortOrders) { if (i == idx) { sortOrder = s; break; } else { i++; } } } return sortOrder; } void QDeclarativeOrganizerModel::sortOrder_clear(QQmlListProperty *p) { QDeclarativeOrganizerModel* model = qobject_cast(p->object); if (model) { model->d_ptr->m_sortOrders.clear(); model->d_ptr->m_declarativeSortOrders.clear(); emit model->sortOrdersChanged(); } } int QDeclarativeOrganizerModel::collection_count(QQmlListProperty *p) { QDeclarativeOrganizerModel* model = qobject_cast(p->object); return model ? model->d_ptr->m_collections.count() : 0; } QDeclarativeOrganizerCollection* QDeclarativeOrganizerModel::collection_at(QQmlListProperty *p, int idx) { QDeclarativeOrganizerModel* model = qobject_cast(p->object); QDeclarativeOrganizerCollection* collection = 0; if (model) { if (!model->d_ptr->m_collections.isEmpty() && idx >= 0 && idx < model->d_ptr->m_collections.count()) collection = model->d_ptr->m_collections.at(idx); } return collection; } #include "moc_qdeclarativeorganizermodel_p.cpp" QT_END_NAMESPACE src/imports/organizer/qdeclarativeorganizermodel_p.h000066400000000000000000000252541233466112000234460ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVEORGANIZERMODEL_H #define QDECLARATIVEORGANIZERMODEL_H #include #include #include #include #include #include #include #include "qdeclarativeorganizercollection_p.h" #include "qdeclarativeorganizeritem_p.h" #include "qdeclarativeorganizeritemfetchhint_p.h" #include "qdeclarativeorganizeritemfilter_p.h" #include "qdeclarativeorganizeritemsortorder_p.h" QTORGANIZER_USE_NAMESPACE QTVERSIT_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeOrganizerModelPrivate; class QDeclarativeOrganizerModel : public QAbstractListModel, public QQmlParserStatus { Q_OBJECT Q_PROPERTY(QString manager READ manager WRITE setManager NOTIFY managerChanged) Q_PROPERTY(QString managerName READ managerName NOTIFY managerChanged) Q_PROPERTY(QStringList availableManagers READ availableManagers) Q_PROPERTY(bool autoUpdate READ autoUpdate WRITE setAutoUpdate NOTIFY autoUpdateChanged) Q_PROPERTY(QDateTime startPeriod READ startPeriod WRITE setStartPeriod NOTIFY startPeriodChanged) Q_PROPERTY(QDateTime endPeriod READ endPeriod WRITE setEndPeriod NOTIFY endPeriodChanged) Q_PROPERTY(QDeclarativeOrganizerItemFilter* filter READ filter WRITE setFilter NOTIFY filterChanged) Q_PROPERTY(QDeclarativeOrganizerItemFetchHint* fetchHint READ fetchHint WRITE setFetchHint NOTIFY fetchHintChanged) Q_PROPERTY(QQmlListProperty sortOrders READ sortOrders NOTIFY sortOrdersChanged) Q_PROPERTY(QQmlListProperty items READ items NOTIFY modelChanged) Q_PROPERTY(QQmlListProperty collections READ collections NOTIFY collectionsChanged) Q_PROPERTY(QString error READ error NOTIFY errorChanged) Q_PROPERTY(int itemCount READ itemCount NOTIFY modelChanged) Q_ENUMS(ExportError) Q_ENUMS(ImportError) Q_INTERFACES(QQmlParserStatus) public: enum { OrganizerItemRole = Qt::UserRole + 500 }; enum ExportError { ExportNoError = QVersitWriter::NoError, ExportUnspecifiedError = QVersitWriter::UnspecifiedError, ExportIOError = QVersitWriter::IOError, ExportOutOfMemoryError = QVersitWriter::OutOfMemoryError, ExportNotReadyError = QVersitWriter::NotReadyError }; enum ImportError { ImportNoError = QVersitReader::NoError, ImportUnspecifiedError = QVersitReader::UnspecifiedError, ImportIOError = QVersitReader::IOError, ImportOutOfMemoryError = QVersitReader::OutOfMemoryError, ImportNotReadyError = QVersitReader::NotReadyError, ImportParseError = QVersitReader::ParseError }; explicit QDeclarativeOrganizerModel(QObject *parent = 0); explicit QDeclarativeOrganizerModel(QOrganizerManager* manager, const QDateTime& start, const QDateTime& end, QObject *parent = 0); ~QDeclarativeOrganizerModel(); QString error() const; int itemCount() const; QString manager() const; void setManager(const QString& managerUri); QString managerName() const; QStringList availableManagers() const; QDateTime startPeriod() const; void setStartPeriod(const QDateTime& start); QDateTime endPeriod() const; void setEndPeriod(const QDateTime& end); // From QQmlParserStatus virtual void classBegin() {} virtual void componentComplete(); int rowCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; QDeclarativeOrganizerItemFilter* filter() const; QDeclarativeOrganizerItemFetchHint* fetchHint() const; QQmlListProperty items() ; QQmlListProperty sortOrders() ; QQmlListProperty collections(); Q_INVOKABLE void removeItem(const QString& id); Q_INVOKABLE void removeItem(QDeclarativeOrganizerItem *item); Q_INVOKABLE void removeItems(const QStringList& ids); Q_INVOKABLE void removeItems(const QList &items); Q_INVOKABLE void saveItem(QDeclarativeOrganizerItem* item); Q_INVOKABLE int fetchItems(const QStringList &itemIds); Q_INVOKABLE int fetchItems(const QDateTime &start, const QDateTime &end, QDeclarativeOrganizerItemFilter *filter = new QDeclarativeOrganizerItemFilter(), int maxCount = -1, const QVariantList &sortOrders = QVariantList(), QDeclarativeOrganizerItemFetchHint *fetchHint = new QDeclarativeOrganizerItemFetchHint()); Q_INVOKABLE void removeCollection(const QString& collectionId); Q_INVOKABLE void saveCollection(QDeclarativeOrganizerCollection* collection); // FIXME : Naming indicates fetch from database Q_INVOKABLE void fetchCollections(); Q_INVOKABLE QList containsItems(const QDateTime &start, const QDateTime &end, int interval); Q_INVOKABLE bool containsItems(const QDateTime &start, const QDateTime &end = QDateTime()); Q_INVOKABLE QVariantList itemsByTimePeriod(const QDateTime &start = QDateTime(), const QDateTime &end = QDateTime()); Q_INVOKABLE QDeclarativeOrganizerItem* item(const QString& id); Q_INVOKABLE QStringList itemIds(const QDateTime &start = QDateTime(), const QDateTime &end = QDateTime()); Q_INVOKABLE QDeclarativeOrganizerCollection* defaultCollection(); Q_INVOKABLE QDeclarativeOrganizerCollection* collection(const QString& collectionId); Q_INVOKABLE void importItems(const QUrl& url, const QStringList& profiles = QStringList()); Q_INVOKABLE void exportItems(const QUrl& url, const QStringList& profiles = QStringList()); bool autoUpdate() const; void setAutoUpdate(bool autoUpdate); void setFilter(QDeclarativeOrganizerItemFilter* filter); void setFetchHint(QDeclarativeOrganizerItemFetchHint* fetchHint); signals: void managerChanged(); void availableManagersChanged(); void filterChanged(); void fetchHintChanged(); void modelChanged(); void sortOrdersChanged(); void errorChanged(); void startPeriodChanged(); void endPeriodChanged(); void autoUpdateChanged(); void collectionsChanged(); void itemsFetched(int requestId, const QVariantList &fetchedItems); void exportCompleted(ExportError error, QUrl url); void importCompleted(ImportError error, QUrl url); public slots: void update(); void updateItems(); void updateCollections(); void cancelUpdate(); private slots: void doUpdate(); void doUpdateItems(); void fetchAgain(); void requestUpdated(); // handle request from saveItem(), removeItem(), saveCollection(), and removeCollection() void onRequestStateChanged(QOrganizerAbstractRequest::State newState); // handle fetch request from fetchItems() void onFetchItemsRequestStateChanged(QOrganizerAbstractRequest::State state); // handle signals from organizer manager void onItemsModified(const QList > &itemIds); // handle fetch request from onItemsModified() void onItemsModifiedFetchRequestStateChanged(QOrganizerAbstractRequest::State state); void collectionsFetched(); void startImport(QVersitReader::State state); void itemsExported(QVersitWriter::State state); private: void removeItemsFromModel(const QList& ids); bool itemHasRecurrence(const QOrganizerItem& oi) const; QDeclarativeOrganizerItem* createItem(const QOrganizerItem& item); void checkError(const QOrganizerAbstractRequest *request); static int item_count(QQmlListProperty *p); static QDeclarativeOrganizerItem * item_at(QQmlListProperty *p, int idx); static void sortOrder_append(QQmlListProperty *p, QDeclarativeOrganizerItemSortOrder *sortOrder); static int sortOrder_count(QQmlListProperty *p); static QDeclarativeOrganizerItemSortOrder * sortOrder_at(QQmlListProperty *p, int idx); static void sortOrder_clear(QQmlListProperty *p); static int collection_count(QQmlListProperty *p); static QDeclarativeOrganizerCollection* collection_at(QQmlListProperty *p, int idx); QScopedPointer d_ptr; Q_DECLARE_PRIVATE(QDeclarativeOrganizerModel) }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeOrganizerModel) #endif // QDECLARATIVEORGANIZERMODEL_H src/imports/organizer/qdeclarativeorganizerrecurrencerule.cpp000066400000000000000000000251201233466112000253770ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativeorganizerrecurrencerule_p.h" #include QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE /*! \qmltype RecurrenceRule \instantiates QDeclarativeOrganizerRecurrenceRule \brief The RecurrenceRule element represents a rule by which a organizer item repeats. \inqmlmodule QtOrganizer 5.0 \ingroup qml-organizer-main */ /*! \qmlsignal RecurrenceRule::onRecurrenceRuleChanged() This signal is emitted, when any of the RecurrenceRule's properties have been changed. */ /*! \internal */ QDeclarativeOrganizerRecurrenceRule::QDeclarativeOrganizerRecurrenceRule(QObject *parent) : QObject(parent) { } /*! \qmlproperty enumeration RecurrenceRule::frequency This property holds the frequency with which the item recurs, the value can be one of: \list \li RecurrenceRule.Invalid - (default). \li RecurrenceRule.Daily \li RecurrenceRule.Weekly \li RecurrenceRule.Monthly \li RecurrenceRule.Yearly \endlist */ void QDeclarativeOrganizerRecurrenceRule::setFrequency(Frequency freq) { if (freq != frequency()) { m_rule.setFrequency(static_cast(freq)); emit recurrenceRuleChanged(); } } QDeclarativeOrganizerRecurrenceRule::Frequency QDeclarativeOrganizerRecurrenceRule::frequency() const { return static_cast(m_rule.frequency()); } /*! \qmlproperty variant RecurrenceRule::limit This property holds the limit condition of the recurrence rule, the value can be a limit date and time or a limit count. The default is no limit. */ void QDeclarativeOrganizerRecurrenceRule::setLimit(const QVariant &value) { if (!value.isValid()) { if (m_rule.limitType() != QOrganizerRecurrenceRule::NoLimit) { m_rule.clearLimit(); emit recurrenceRuleChanged(); } } else if (value.type() == QVariant::DateTime) { QDate v = value.toDateTime().toUTC().date(); if (v != m_rule.limitDate()) { m_rule.setLimit(v); emit recurrenceRuleChanged(); } } else if (value.type() == QVariant::Date) { QDate v = value.value(); if (v != m_rule.limitDate()) { m_rule.setLimit(v); emit recurrenceRuleChanged(); } } else if ((value.type() == QVariant::Int) || (value.type() == QVariant::Double)) { int v = value.value(); if (v != m_rule.limitCount()) { m_rule.setLimit(v); emit recurrenceRuleChanged(); } } else { // TODO throw an error event qmlInfo(this) << tr("Invalid recurrence rule limit; value ,") << value << tr(", did not match one of the types: date, integer or double"); } } QVariant QDeclarativeOrganizerRecurrenceRule::limit() const { if (m_rule.limitType() == QOrganizerRecurrenceRule::CountLimit) return QVariant::fromValue(m_rule.limitCount()); else if (m_rule.limitType() == QOrganizerRecurrenceRule::DateLimit) return QVariant::fromValue(m_rule.limitDate()); //NoLimit return QVariant(); } /*! \qmlproperty int RecurrenceRule::interval This property holds the interval of recurrence. The default interval is 1. */ void QDeclarativeOrganizerRecurrenceRule::setInterval(int interval) { if (interval != m_rule.interval()) { m_rule.setInterval(interval); emit recurrenceRuleChanged(); } } int QDeclarativeOrganizerRecurrenceRule::interval() const { return m_rule.interval(); } /*! \qmlproperty list RecurrenceRule::daysOfWeek This property holds a list of the days of week that the item should recur on. */ void QDeclarativeOrganizerRecurrenceRule::setDaysOfWeek(const QVariantList &days) { QSet saved; foreach (const QVariant &day, days) saved << static_cast(day.value()); if (saved != m_rule.daysOfWeek()) { m_rule.setDaysOfWeek(saved); emit recurrenceRuleChanged(); } } QVariantList QDeclarativeOrganizerRecurrenceRule::daysOfWeek() const { QVariantList days; foreach (Qt::DayOfWeek day, m_rule.daysOfWeek()) days << day; return days; } /*! \qmlproperty list RecurrenceRule::daysOfMonth This property holds a list of the days of the month that the item should recur on. */ void QDeclarativeOrganizerRecurrenceRule::setDaysOfMonth(const QVariantList &days) { QSet saved; foreach (const QVariant &day, days) saved << day.value(); if (saved != m_rule.daysOfMonth()) { m_rule.setDaysOfMonth(saved); emit recurrenceRuleChanged(); } } QVariantList QDeclarativeOrganizerRecurrenceRule::daysOfMonth() const { QVariantList days; foreach (int day, m_rule.daysOfMonth()) days << day; return days; } /*! \qmlproperty list RecurrenceRule::daysOfYear This property holds a list of the days of the year that the item should recur on. */ void QDeclarativeOrganizerRecurrenceRule::setDaysOfYear(const QVariantList &days) { QSet saved; foreach (const QVariant &day, days) saved << day.value(); if (saved != m_rule.daysOfYear()) { m_rule.setDaysOfYear(saved); emit recurrenceRuleChanged(); } } QVariantList QDeclarativeOrganizerRecurrenceRule::daysOfYear() const { QVariantList days; foreach (int day, m_rule.daysOfYear()) days << day; return days; } /*! \qmlproperty list RecurrenceRule::monthsOfYear This property holds a list of the months that the item should recur on, the list element value can be one of: \list \li RecurrenceRule.January \li RecurrenceRule.February \li RecurrenceRule.March \li RecurrenceRule.April \li RecurrenceRule.May \li RecurrenceRule.June \li RecurrenceRule.July \li RecurrenceRule.August \li RecurrenceRule.September \li RecurrenceRule.October \li RecurrenceRule.November \li RecurrenceRule.December \endlist */ void QDeclarativeOrganizerRecurrenceRule::setMonthsOfYear(const QVariantList &months) { QSet saved; foreach (const QVariant &day, months) saved << static_cast(day.value()); if (saved != m_rule.monthsOfYear()) { m_rule.setMonthsOfYear(saved); emit recurrenceRuleChanged(); } } QVariantList QDeclarativeOrganizerRecurrenceRule::monthsOfYear() const { QVariantList ms; foreach (int m, m_rule.monthsOfYear()) ms << m; return ms; } /*! \qmlproperty list RecurrenceRule::weeksOfYear This property holds a list of the weeks of the year that the item should recur on. */ void QDeclarativeOrganizerRecurrenceRule::setWeeksOfYear(const QVariantList &weeks) { QSet saved; foreach (const QVariant &week, weeks) saved << week.value(); if (saved != m_rule.weeksOfYear()) { m_rule.setWeeksOfYear(saved); emit recurrenceRuleChanged(); } } QVariantList QDeclarativeOrganizerRecurrenceRule::weeksOfYear() const { QVariantList weeks; foreach (int week, m_rule.weeksOfYear()) weeks << week; return weeks; } /*! \qmlproperty list RecurrenceRule::positions This property holds the position-list of the recurrence rule. */ void QDeclarativeOrganizerRecurrenceRule::setPositions(const QVariantList &pos) { if (pos != positions()) { QSet saved; foreach (const QVariant &p, pos) saved << p.value(); m_rule.setPositions(saved); emit recurrenceRuleChanged(); } } QVariantList QDeclarativeOrganizerRecurrenceRule::positions() const { QVariantList pos; foreach (int p, m_rule.positions()) pos << p; return pos; } /*! \qmlproperty enumeration RecurrenceRule::firstDayOfWeek This property holds the day that the week starts on. If not set, this is Monday. The value can be one of: \list \li Qt.Monday \li Qt.Tuesday \li Qt.Wednesday \li Qt.Thursday \li Qt.Friday \li Qt.Saturday \li Qt.Sunday \endlist */ void QDeclarativeOrganizerRecurrenceRule::setFirstDayOfWeek(Qt::DayOfWeek day) { if (day != firstDayOfWeek()) { m_rule.setFirstDayOfWeek(day); emit recurrenceRuleChanged(); } } Qt::DayOfWeek QDeclarativeOrganizerRecurrenceRule::firstDayOfWeek() const { return m_rule.firstDayOfWeek(); } /*! \internal */ QOrganizerRecurrenceRule QDeclarativeOrganizerRecurrenceRule::rule() const { return m_rule; } /*! \internal */ void QDeclarativeOrganizerRecurrenceRule::setRule(const QOrganizerRecurrenceRule &rule) { m_rule = rule; } #include "moc_qdeclarativeorganizerrecurrencerule_p.cpp" QT_END_NAMESPACE src/imports/organizer/qdeclarativeorganizerrecurrencerule_p.h000066400000000000000000000124561233466112000253730ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QDECLARATIVEORGANIZERITEMRECURRENCERULE_H #define QDECLARATIVEORGANIZERITEMRECURRENCERULE_H #include #include QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE class QDeclarativeOrganizerRecurrenceRule : public QObject { Q_OBJECT Q_ENUMS(Frequency) Q_ENUMS(Month) Q_PROPERTY(Frequency frequency READ frequency WRITE setFrequency NOTIFY recurrenceRuleChanged) Q_PROPERTY(QVariant limit READ limit WRITE setLimit NOTIFY recurrenceRuleChanged) Q_PROPERTY(int interval READ interval WRITE setInterval NOTIFY recurrenceRuleChanged) Q_PROPERTY(QVariantList daysOfWeek READ daysOfWeek WRITE setDaysOfWeek NOTIFY recurrenceRuleChanged) Q_PROPERTY(QVariantList daysOfMonth READ daysOfMonth WRITE setDaysOfMonth NOTIFY recurrenceRuleChanged) Q_PROPERTY(QVariantList daysOfYear READ daysOfYear WRITE setDaysOfYear NOTIFY recurrenceRuleChanged) Q_PROPERTY(QVariantList monthsOfYear READ monthsOfYear WRITE setMonthsOfYear NOTIFY recurrenceRuleChanged) Q_PROPERTY(QVariantList positions READ positions WRITE setPositions NOTIFY recurrenceRuleChanged) Q_PROPERTY(Qt::DayOfWeek firstDayOfWeek READ firstDayOfWeek WRITE setFirstDayOfWeek NOTIFY recurrenceRuleChanged) public: enum Frequency { Invalid = QOrganizerRecurrenceRule::Invalid, Daily = QOrganizerRecurrenceRule::Daily, Weekly = QOrganizerRecurrenceRule::Weekly, Monthly = QOrganizerRecurrenceRule::Monthly, Yearly = QOrganizerRecurrenceRule::Yearly }; enum Month { January = QOrganizerRecurrenceRule::January, February = QOrganizerRecurrenceRule::February, March = QOrganizerRecurrenceRule::March, April = QOrganizerRecurrenceRule::April, May = QOrganizerRecurrenceRule::May, June = QOrganizerRecurrenceRule::June, July = QOrganizerRecurrenceRule::July, August = QOrganizerRecurrenceRule::August, September = QOrganizerRecurrenceRule::September, October = QOrganizerRecurrenceRule::October, November = QOrganizerRecurrenceRule::November, December = QOrganizerRecurrenceRule::December }; QDeclarativeOrganizerRecurrenceRule(QObject *parent = 0); void setFrequency(Frequency freq); Frequency frequency() const; void setLimit(const QVariant &value); QVariant limit() const; void setInterval(int interval); int interval() const; void setDaysOfWeek(const QVariantList &days); QVariantList daysOfWeek() const; void setDaysOfMonth(const QVariantList &days); QVariantList daysOfMonth() const; void setDaysOfYear(const QVariantList &days); QVariantList daysOfYear() const; void setMonthsOfYear(const QVariantList &months); QVariantList monthsOfYear() const; void setWeeksOfYear(const QVariantList &weeks); QVariantList weeksOfYear() const; void setPositions(const QVariantList &pos); QVariantList positions() const; void setFirstDayOfWeek(Qt::DayOfWeek day); Qt::DayOfWeek firstDayOfWeek() const; // used by recurrence detail QOrganizerRecurrenceRule rule() const; void setRule(const QOrganizerRecurrenceRule &rule); Q_SIGNALS: void recurrenceRuleChanged(); private: QOrganizerRecurrenceRule m_rule; }; QT_END_NAMESPACE QML_DECLARE_TYPE(QDeclarativeOrganizerRecurrenceRule) #endif // QDECLARATIVEORGANIZERITEMRECURRENCERULE_H src/imports/organizer/qmldir000066400000000000000000000001121233466112000165450ustar00rootroot00000000000000module QtOrganizer plugin declarative_organizer typeinfo plugins.qmltypes src/organizer/000077500000000000000000000000001233466112000136435ustar00rootroot00000000000000src/organizer/details/000077500000000000000000000000001233466112000152705ustar00rootroot00000000000000src/organizer/details/details.pri000066400000000000000000000020751233466112000174350ustar00rootroot00000000000000INCLUDEPATH += details \ ./ PUBLIC_HEADERS += \ details/qorganizereventtime.h \ details/qorganizeritemaudiblereminder.h \ details/qorganizeritemcomment.h \ details/qorganizeritemdescription.h \ details/qorganizeritemdisplaylabel.h \ details/qorganizeritememailreminder.h \ details/qorganizeritemguid.h \ details/qorganizeritemlocation.h \ details/qorganizeritemparent.h \ details/qorganizeritempriority.h \ details/qorganizeritemrecurrence.h \ details/qorganizeritemreminder.h \ details/qorganizeritemtag.h \ details/qorganizeritemtimestamp.h \ details/qorganizeritemtype.h \ details/qorganizeritemversion.h \ details/qorganizeritemvisualreminder.h \ details/qorganizeritemdetails.h \ details/qorganizerjournaltime.h \ details/qorganizertodoprogress.h \ details/qorganizertodotime.h \ details/qorganizeritemextendeddetail.h \ details/qorganizereventattendee.h \ details/qorganizereventrsvp.h \ details/qorganizeritemclassification.h SOURCES += details/qorganizeritemdetails.cpp src/organizer/details/qorganizereventattendee.h000066400000000000000000000066721233466112000224110ustar00rootroot00000000000000 /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZEREVENTATTENDEE_H #define QORGANIZEREVENTATTENDEE_H #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerEventAttendee : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerEventAttendee, QOrganizerItemDetail::TypeEventAttendee) #endif enum EventAttendeeField { FieldName = TypeEventAttendee + 1, FieldEmailAddress, FieldAttendeeId, FieldParticipationStatus, FieldParticipationRole }; enum ParticipationStatus { StatusUnknown = 0, StatusAccepted, StatusDeclined, StatusTentative, StatusDelegated, StatusInProcess, StatusCompleted }; enum ParticipationRole { RoleUnknown = 0, RoleOrganizer, RoleChairperson, RoleHost, RoleRequiredParticipant, RoleOptionalParticipant, RoleNonParticipant }; void setName(const QString &name); QString name() const; void setEmailAddress(const QString &emailAddress); QString emailAddress() const; void setParticipationStatus(ParticipationStatus status); ParticipationStatus participationStatus() const; void setParticipationRole(ParticipationRole role); ParticipationRole participationRole() const; void setAttendeeId(const QString &attendeeId); QString attendeeId() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZEREVENTATTENDEE_H src/organizer/details/qorganizereventrsvp.h000066400000000000000000000070571233466112000216100ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZEREVENTRSVP_H #define QORGANIZEREVENTRSVP_H #include #include QT_FORWARD_DECLARE_CLASS(QDate) QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerEventRsvp : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerEventRsvp, QOrganizerItemDetail::TypeEventRsvp) #endif enum EventRsvpField { FieldParticipationStatus = TypeEventRsvp + 1, FieldParticipationRole, FieldResponseRequirement, FieldResponseDeadline, FieldResponseDate, FieldOrganizerName, FieldOrganizerEmail }; enum ResponseRequirement { ResponseNotRequired = 0, ResponseRequired = 1 }; void setParticipationStatus(QOrganizerEventAttendee::ParticipationStatus status); QOrganizerEventAttendee::ParticipationStatus participationStatus() const; void setParticipationRole(QOrganizerEventAttendee::ParticipationRole role); QOrganizerEventAttendee::ParticipationRole participationRole() const; void setResponseRequirement(ResponseRequirement responseRequirement); ResponseRequirement responseRequirement() const; void setResponseDeadline(const QDate &date); QDate responseDeadline() const; void setResponseDate(const QDate &date); QDate responseDate() const; void setOrganizerName(const QString &name); QString organizerName() const; void setOrganizerEmail(const QString &email); QString organizerEmail() const; }; QT_END_NAMESPACE_ORGANIZER #endif// QORGANIZEREVENTRSVP_H src/organizer/details/qorganizereventtime.h000066400000000000000000000053751233466112000215550ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZEREVENTTIME_H #define QORGANIZEREVENTTIME_H #include QT_FORWARD_DECLARE_CLASS(QDateTime) QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerEventTime : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerEventTime, QOrganizerItemDetail::TypeEventTime) #endif enum EventTimeField { FieldStartDateTime = TypeEventTime + 1, FieldEndDateTime, FieldAllDay }; void setStartDateTime(const QDateTime &startDateTime); QDateTime startDateTime() const; void setEndDateTime(const QDateTime &endDateTime); QDateTime endDateTime() const; void setAllDay(bool isAllDay); bool isAllDay() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZEREVENTTIME_H src/organizer/details/qorganizeritemaudiblereminder.h000066400000000000000000000051361233466112000235620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMAUDIBLEREMINDER_H #define QORGANIZERITEMAUDIBLEREMINDER_H #include QT_FORWARD_DECLARE_CLASS(QUrl) QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemAudibleReminder : public QOrganizerItemReminder { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_REMINDER_DETAIL(QOrganizerItemAudibleReminder, QOrganizerItemDetail::TypeAudibleReminder) #endif enum AudibleReminderField { FieldDataUrl = TypeAudibleReminder + 1 }; void setDataUrl(const QUrl &dataUrl); QUrl dataUrl() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMAUDIBLEREMINDER_H src/organizer/details/qorganizeritemclassification.h000066400000000000000000000052731233466112000234240ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Pim module. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMCLASSIFICATION_H #define QORGANIZERITEMCLASSIFICATION_H #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemClassification : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerItemClassification, QOrganizerItemDetail::TypeClassification) #endif enum ClassificationField { FieldClassification = TypeClassification + 1 }; enum AccessClassification { AccessPublic = 0, AccessConfidential, AccessPrivate }; void setClassification(AccessClassification classification); AccessClassification classification() const; }; QT_END_NAMESPACE_ORGANIZER #endif// QORGANIZERITEMCLASSIFICATION_H src/organizer/details/qorganizeritemcomment.h000066400000000000000000000047671233466112000221020ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMCOMMENT_H #define QORGANIZERITEMCOMMENT_H #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemComment : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerItemComment, QOrganizerItemDetail::TypeComment) #endif enum CommentField { FieldComment = TypeComment + 1 }; void setComment(const QString &comment); QString comment() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMCOMMENT_H src/organizer/details/qorganizeritemdescription.h000066400000000000000000000050471233466112000227530ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMDESCRIPTION_H #define QORGANIZERITEMDESCRIPTION_H #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemDescription : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerItemDescription, QOrganizerItemDetail::TypeDescription) #endif enum DescriptionField { FieldDescription = TypeDescription + 1 }; void setDescription(const QString &description); QString description() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMDESCRIPTION_H src/organizer/details/qorganizeritemdetails.cpp000066400000000000000000001357571233466112000224240ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemdetails.h" #include #include QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemDescription \brief The QOrganizerItemDescription class contains some arbitrary information which is relevant to the organizer item. \inmodule QtOrganizer \ingroup organizer-details */ /*! \enum QOrganizerItemDescription::DescriptionField This enumeration defines the fields supported by QOrganizerItemDescription. \value FieldDescription The value stored is a description. */ /*! Sets a description associated with an organizer item to \a description. */ void QOrganizerItemDescription::setDescription(const QString &description) { setValue(FieldDescription, description); } /*! Returns a string for a description associated with an organizer item. */ QString QOrganizerItemDescription::description() const { return value(FieldDescription).toString(); } /*! \class QOrganizerItemDisplayLabel \brief The QOrganizerItemDisplayLabel class contains the backend-synthesized display label of the organizer item. \inmodule QtOrganizer \ingroup organizer-details */ /*! Sets the display label of the organizer item to \a label. */ void QOrganizerItemDisplayLabel::setLabel(const QString &label) { setValue(FieldLabel, label); } /*! Returns the display label of the organizer item. */ QString QOrganizerItemDisplayLabel::label() const { return value(FieldLabel).toString(); } /*! \enum QOrganizerItemDisplayLabel::DisplayLabelField This enumeration defines the fields supported by QOrganizerItemDisplayLabel. \value FieldLabel The value stored is a description label. */ /*! \class QOrganizerEventAttendee \brief The QOrganizerEventAttendee class contains information about an attendee of an event \inmodule QtOrganizer \ingroup organizer-details Attendee details contain information such as the display label (name) of an attendee, their role in the event, and their participation status. */ /*! \enum QOrganizerEventAttendee::EventAttendeeField This enumeration defines the fields supported by QOrganizerEventAttendee. \value FieldName The value stored describes the name of the attendee. \value FieldEmailAddress The value stored describes the Email address of the attendee. \value FieldAttendeeId The value stored describes the ID of the attendee. It can be e.g. a serialized contact ID, vCard UID, or any other platform specific ID. \value FieldParticipationStatus The value stored describes the participation status of the attendee. \value FieldParticipationRole The value stored describes the participation role of the attendee. */ /*! \enum QOrganizerEventAttendee::ParticipationStatus \value StatusUnknown The status of the attendee is unknown or they have yet to respond. \value StatusAccepted The attendee has responded that they will be attending the event. \value StatusDeclined The attendee has responded that they will not be attending the event. \value StatusTentative The attendee has responded that they may be attending the event. \value StatusDelegated The attendee has delegated attendance at the event to another person. \value StatusInProcess The attendee is currently attending the event. \value StatusCompleted The attendee attended the event. */ /*! \enum QOrganizerEventAttendee::ParticipationRole \value RoleUnknown The role of the attendee is unknown or they have yet to respond. \value RoleOrganizer The attendee is the organizer of the event. \value RoleChairperson The attendee is the chairperson of the event. \value RoleHost The attendee is the host of the event. \value RoleRequiredParticipant The attendee is a required participant of the event. \value RoleOptionalParticipant The attendee is an optional participant of the event. \value RoleNonParticipant The attendee is not participating in the event (value included for informational purposes only, as per iCalendar specification). */ /*! Sets the name (or title or other label) of the attendee to \a name. */ void QOrganizerEventAttendee::setName(const QString &name) { setValue(FieldName, name); } /*! Returns the name (or title or other label) of the attendee. */ QString QOrganizerEventAttendee::name() const { return value(FieldName).toString(); } /*! Sets the email address of the attendee to \a emailAddress. */ void QOrganizerEventAttendee::setEmailAddress(const QString &emailAddress) { setValue(FieldEmailAddress, emailAddress); } /*! Returns the email address of the attendee. */ QString QOrganizerEventAttendee::emailAddress() const { return value(FieldEmailAddress).toString(); } /*! Sets the unique identifier of the attendee to \a attendeeId. */ void QOrganizerEventAttendee::setAttendeeId(const QString &attendeeId) { setValue(FieldAttendeeId, attendeeId); } /*! Returns the unique identifier of the attendee. The format of the identifier is platform specific and may be a serialized id, a vCard UID, or something else. */ QString QOrganizerEventAttendee::attendeeId() const { return value(FieldAttendeeId).toString(); } /*! Sets the participation status of the attendee in the event to \a status. */ void QOrganizerEventAttendee::setParticipationStatus(ParticipationStatus status) { setValue(FieldParticipationStatus, status); } /*! Returns the participation status of the attendee in the event. */ QOrganizerEventAttendee::ParticipationStatus QOrganizerEventAttendee::participationStatus() const { return static_cast(value(FieldParticipationStatus).toInt()); } /*! Sets the role of the attendee in the event to \a role. */ void QOrganizerEventAttendee::setParticipationRole(ParticipationRole role) { setValue(FieldParticipationRole, role); } /*! Returns the participation role of the attendee in the event. */ QOrganizerEventAttendee::ParticipationRole QOrganizerEventAttendee::participationRole() const { return static_cast(value(FieldParticipationRole).toInt()); } /*! \class QOrganizerEventTime \brief The QOrganizerEventTime class contains the start and end dates and times of a recurring event series, or occurrence of an event. \inmodule QtOrganizer \ingroup organizer-details */ /*! \enum QOrganizerEventTime::EventTimeField This enumeration defines the fields supported by QOrganizerEventTime. \value FieldEndDateTime The value stored describes the end date time of the event. \value FieldStartDateTime The value stored describes the start date time of the event. \value FieldAllDay The value stored describes if the event is an all day event. */ /*! Returns the event time's start date and time as QDateTime. For all-day events, the time part is meaningless. \sa QOrganizerEventTime::setStartDateTime() */ QDateTime QOrganizerEventTime::startDateTime() const { return value(FieldStartDateTime).toDateTime(); } /*! Sets the event time's start date and time to \a startDateTime. For all-day events, the time part can be set to any valid value. \sa QOrganizerEventTime::startDateTime() */ void QOrganizerEventTime::setStartDateTime(const QDateTime &startDateTime) { setValue(FieldStartDateTime, startDateTime); } /*! Sets the event time's due date and time to \a endDateTime. For all-day events, the time part can be set to any valid value, and the date is to be interpreted as the inclusive end date. \sa QOrganizerEventTime::endDateTime() */ void QOrganizerEventTime::setEndDateTime(const QDateTime &endDateTime) { setValue(FieldEndDateTime, endDateTime); } /*! Returns the event time's due date and time as QDateTime. For all-day events, the time part is meaningless, and the date is to be interpreted as the inclusive end date. \sa QOrganizerEventTime::setEndDateTime() */ QDateTime QOrganizerEventTime::endDateTime() const { return value(FieldEndDateTime).toDateTime(); } /*! Sets the all-day status of the event to \a isAllDay. If the event is an all-day event, no time is considered to be specified for the event, even if a start or end date time set for the event has a time component. */ void QOrganizerEventTime::setAllDay(bool isAllDay) { setValue(FieldAllDay, isAllDay); } /*! Returns true if the event is an all-day event, or false otherwise. */ bool QOrganizerEventTime::isAllDay() const { return value(FieldAllDay).toBool(); } /*! \class QOrganizerItemGuid \brief The QOrganizerItemGuid class contains the globally unique identifier of the organizer item, which can be used for synchronization purposes. \inmodule QtOrganizer \ingroup organizer-details */ /*! \enum QOrganizerItemGuid::GuidField This enumeration defines the fields supported by QOrganizerItemGuid. \value FieldGuid The value stored is the global unique identifier of the item. */ /*! Returns the globally unique identifier which is stored in this detail. */ QString QOrganizerItemGuid::guid() const { return value(FieldGuid).toString(); } /*! Sets the globally unique identifier which is stored in this detail to \a guid. */ void QOrganizerItemGuid::setGuid(const QString &guid) { setValue(FieldGuid, guid); } /*! \class QOrganizerItemParent \brief The QOrganizerItemParent class contains information about the event or todo that generated this item. \inmodule QtOrganizer \ingroup organizer-details */ /*! \enum QOrganizerItemParent::ParentField This enumeration defines the fields supported by QOrganizerItemParent. \value FieldParentId The value stored describes the ID of the item's parent item. \value FieldOriginalDate The value stored describes the original date of this instance or exception according to the recurrent series of the parent item is stored. */ /*! Returns the ID of the item instance origin's parent item. */ QOrganizerItemId QOrganizerItemParent::parentId() const { return value(FieldParentId).value(); } /*! Sets the parent ID of this instance origin item to \a parentId. */ void QOrganizerItemParent::setParentId(const QOrganizerItemId &parentId) { setValue(FieldParentId, QVariant::fromValue(parentId)); } /*! Returns the original date of this instance origin item. */ QDate QOrganizerItemParent::originalDate() const { return value(FieldOriginalDate).toDate(); } /*! Sets the origin date to \a date. */ void QOrganizerItemParent::setOriginalDate(const QDate &date) { setValue(FieldOriginalDate, date); } /*! \class QOrganizerJournalTime \brief The QOrganizerJournalTime class contains information about the date and time for which a journal entry has been created. \inmodule QtOrganizer \ingroup organizer-details */ /*! \enum QOrganizerJournalTime::JournalTimeField This enumeration defines the fields supported by QOrganizerJournalTime. \value FieldEntryDateTime The value stored describes the date time of the journal entry. */ /*! Returns the journal entry date and time as QDateTime. */ QDateTime QOrganizerJournalTime::entryDateTime() const { return value(FieldEntryDateTime).toDateTime(); } /*! Sets the journal entry date and time to \a entryDateTime. */ void QOrganizerJournalTime::setEntryDateTime(const QDateTime &entryDateTime) { setValue(FieldEntryDateTime, entryDateTime); } /*! \class QOrganizerItemLocation \brief The QOrganizerItemLocation class contains information about a location which is related to the organizer item in some manner. \inmodule QtOrganizer \ingroup organizer-details */ /*! \enum QOrganizerItemLocation::LocationField This enumeration defines the fields supported by QOrganizerItemLocation. \value FieldLatitude The value stored describes the latitude of the location. \value FieldLongitude The value stored describes the longitude of the location. \value FieldLabel The value stored is a label for the location. */ /*! Returns the latitude value of the location's geocoordinates. */ double QOrganizerItemLocation::latitude() const { return value(FieldLatitude).toDouble(); } /*! Sets the latitude value of the location's geocoordinates to \a latitude. The equator has a latitude of 0, the North pole has a latitude of 90, and the South pole has a latitude of -90. Values out of the range will be ignored. */ void QOrganizerItemLocation::setLatitude(double latitude) { if (latitude >= -90 && latitude <= 90) setValue(FieldLatitude, latitude); } /*! Returns the longitude value of the location's geocoordinates. */ double QOrganizerItemLocation::longitude() const { return value(FieldLongitude).toDouble(); } /*! Sets the longitude value of the location's geocoordinates to \a longitude. The Prime Meridian has a longitude of 0, ranging to 180 eastward and -180 westward. Values out of the range will be ignored. */ void QOrganizerItemLocation::setLongitude(double longitude) { if (longitude >= -180 && longitude <= 180) setValue(FieldLongitude, longitude); } /*! Returns the human-readable label of the location. */ QString QOrganizerItemLocation::label() const { return value(FieldLabel).toString(); } /*! Sets the human-readable label of the location to \a label. */ void QOrganizerItemLocation::setLabel(const QString &label) { setValue(FieldLabel, label); } /*! \class QOrganizerItemComment \brief The QOrganizerItemComment class contains some arbitrary information which is relevant to the organizer item. \inmodule QtOrganizer \ingroup organizer-details */ /*! \enum QOrganizerItemComment::CommentField This enumeration defines the fields supported by QOrganizerItemComment. \value FieldComment The value is a comment of the item. */ /*! Sets a comment associated with an organizer item to \a comment. */ void QOrganizerItemComment::setComment(const QString &comment) { setValue(FieldComment, comment); } /*! Returns a string for a comment associated with an organizer item. */ QString QOrganizerItemComment::comment() const { return value(FieldComment).toString(); } /*! \class QOrganizerItemPriority \brief The QOrganizerItemPriority class contains the priority of the organizer item, which may be used to resolve scheduling conflicts. \inmodule QtOrganizer \ingroup organizer-details */ /*! \enum QOrganizerItemPriority::Priority \value UnknownPriority \value HighestPriority \value ExtremelyHighPriority \value VeryHighPriority \value HighPriority \value MediumPriority \value LowPriority \value VeryLowPriority \value ExtremelyLowPriority \value LowestPriority */ /*! \enum QOrganizerItemPriority::PriorityField This enumeration defines the fields supported by QOrganizerItemPriority. \value FieldPriority The value describes the priority of the item. */ /*! Sets the priority associated with an organizer item to \a priority. */ void QOrganizerItemPriority::setPriority(Priority priority) { setValue(FieldPriority, priority); } /*! Returns the priority associated with an organizer item. */ QOrganizerItemPriority::Priority QOrganizerItemPriority::priority() const { return static_cast(value(FieldPriority).toInt()); } /*! \class QOrganizerItemRecurrence \brief The QOrganizerItemRecurrence class contains a list of rules and dates on which the recurrent item occurs, and a list of rules and dates on which exceptions occur. \inmodule QtOrganizer \ingroup organizer-details */ /*! Returns true if the \a other recurrence detail is equal to this detail; otherwise, false. Since the data types stored in this detail are custom data types, the base class operator==() doesn't know how to perform the comparison without calling this function. However, it means that if (in the future) a backend were to extend the detail with more fields, this operator== would no longer work; it'd have to be updated to compare the other fields also. */ bool QOrganizerItemRecurrence::operator==(const QOrganizerItemRecurrence &other) const { return recurrenceRules() == other.recurrenceRules() && exceptionRules() == other.exceptionRules() && recurrenceDates() == other.recurrenceDates() && exceptionDates() == other.exceptionDates(); } /*! \fn QOrganizerItemRecurrence::operator!=(const QOrganizerItemRecurrence &other) const Returns true if the \a other recurrence detail is not equal to this detail; otherwise, false. */ /*! \enum QOrganizerItemRecurrence::RecurrenceField This enumeration defines the fields supported by QOrganizerItemRecurrence. \value FieldRecurrenceRules The value stored describes the rules for when an item should recur. \value FieldRecurrenceDates The value stored describes the dates for when an item should recur. \value FieldExceptionRules The value stored describes the rules for when an item should not recur. \value FieldExceptionDates The value stored describes the dates for when an item should not recur. */ /*! Returns the set of recurrence dates. */ QSet QOrganizerItemRecurrence::recurrenceDates() const { return value(FieldRecurrenceDates).value >(); } /*! Sets the set of recurrence dates to \a rdates. */ void QOrganizerItemRecurrence::setRecurrenceDates(const QSet &rdates) { setValue(FieldRecurrenceDates, QVariant::fromValue(rdates)); } /*! Returns the set of exception rules. */ QSet QOrganizerItemRecurrence::exceptionRules() const { return value(FieldExceptionRules).value >(); } /*! Sets the set of exception rules to \a xrules. */ void QOrganizerItemRecurrence::setExceptionRules(const QSet &xrules) { setValue(FieldExceptionRules, QVariant::fromValue(xrules)); } /*! Returns the set of recurrence rules. */ QSet QOrganizerItemRecurrence::recurrenceRules() const { return value(FieldRecurrenceRules).value >(); } /*! Sets the set of recurrence rules to \a rrules. */ void QOrganizerItemRecurrence::setRecurrenceRules(const QSet &rrules) { setValue(FieldRecurrenceRules, QVariant::fromValue(rrules)); } /*! Returns the set of exception dates. */ QSet QOrganizerItemRecurrence::exceptionDates() const { return value(FieldExceptionDates).value >(); } /*! Sets the set of exception dates to \a xdates. */ void QOrganizerItemRecurrence::setExceptionDates(const QSet &xdates) { setValue(FieldExceptionDates, QVariant::fromValue(xdates)); } /*! \class QOrganizerItemReminder \brief The QOrganizerItemReminder class contains information about when and how the user wants to reminded of the item \inmodule QtOrganizer \ingroup organizer-details Note that the Organizer API does not enforce that the user is reminded of the item; rather, it simply allows clients to store and manipulate data which might be used by the platform to implement alarms and reminders. */ /*! \enum QOrganizerItemReminder::ReminderType This enumeration defines the type of the reminder. \value NoReminder This reminder is entirely unobtrusive \value AudibleReminder This reminder has an audible element \value VisualReminder This reminder has a visual element \value EmailReminder This reminder has a email element */ /*! \enum QOrganizerItemReminder::ReminderField This enumeration defines the fields supported by QOrganizerItemReminder. \value FieldSecondsBeforeStart The value stored describes the time in seconds prior to the item's start time, when the reminder should be triggered. \value FieldRepetitionCount The value stored describes the number of repetitions of the reminder. \value FieldRepetitionDelay The value stored describes the delays in seconds between repetitions of the reminder. */ /*! Returns the reminder type of this reminder for an organizer item. */ QOrganizerItemReminder::ReminderType QOrganizerItemReminder::reminderType() const { if (type() == QOrganizerItemDetail::TypeAudibleReminder) return QOrganizerItemReminder::AudibleReminder; else if (type() == QOrganizerItemDetail::TypeEmailReminder) return QOrganizerItemReminder::EmailReminder; else if (type() == QOrganizerItemDetail::TypeVisualReminder) return QOrganizerItemReminder::VisualReminder; return QOrganizerItemReminder::NoReminder; } /*! Sets the number of seconds prior to the activation of the item at which the user wants to be reminded of the item to \a seconds. The exact datetime of activation of the item depends on the type of item: for a QOrganizerTodo or QOrganizerTodoOccurrence it is the due date time; for a QOrganizerEvent or QOrganizerEventOccurrence it is the start date time. The value must be non-negative, and negative values will be ignored. */ void QOrganizerItemReminder::setSecondsBeforeStart(int seconds) { if (seconds >= 0) setValue(FieldSecondsBeforeStart, seconds); } /*! Returns the number of seconds prior to the activation of the item at which the user wants to be reminded of the item. The exact datetime of activation of the item depends on the type of item: for a QOrganizerTodo or QOrganizerTodoOccurrence it is the due date time; for a QOrganizerEvent or QOrganizerEventOccurrence it is the start date time. */ int QOrganizerItemReminder::secondsBeforeStart() const { return value(FieldSecondsBeforeStart).toInt(); } /*! Returns the number of times the user should be reminded of the item. \sa repetitionDelay() */ int QOrganizerItemReminder::repetitionCount() const { return value(FieldRepetitionCount).toInt(); } /*! Returns the delay (in seconds) between each repetition of the reminder. \sa repetitionCount() */ int QOrganizerItemReminder::repetitionDelay() const { return value(FieldRepetitionDelay).toInt(); } /*! Sets the number of repetitions of the reminderto \a count, and the delay (in seconds) between each repetition of the reminder to \a delaySeconds. Both \a count and \a delaySeconds must be positive numbers, otherwise both will be ignored. \sa repetitionCount(), repetitionDelay() */ void QOrganizerItemReminder::setRepetition(int count, int delaySeconds) { if (count > 0 && delaySeconds >= 0) { setValue(FieldRepetitionCount, count); setValue(FieldRepetitionDelay, delaySeconds); } } /*! \macro Q_DECLARE_CUSTOM_ORGANIZER_REMINDER_DETAIL \relates QOrganizerItemReminder Macro for simplifying declaring custom (leaf) reminder detail classes. The first argument is the name of the class, and the second argument is a Latin-1 string literal naming the detail type, and the third argument is the reminder type of the leaf reminder detail class. If you are creating a convenience class for a type of QOrganizerItemReminder, you should use this macro when declaring your class to ensure that it interoperates with other organizer item functionality. */ /*! \class QOrganizerItemAudibleReminder \brief The QOrganizerItemAudibleReminder class contains information about an audible reminder of an item. \inmodule QtOrganizer \ingroup organizer-details An audible reminder is a reminder which alerts the user about the item, with sound. Note that the Organizer API does not enforce that the sound data is played, or that any other sort of reminder occurs; rather, it simply allows clients to store and manipulate data which might be used by the platform to implement alarms and reminders. */ /*! \enum QOrganizerItemAudibleReminder::AudibleReminderField This enumeration defines the fields supported by QOrganizerItemAudibleReminder. \value FieldDataUrl The value stored describes URL of the sound to be played when the reminder is triggered. */ /*! Sets the url of the audible data which should be played to \a dataUrl. */ void QOrganizerItemAudibleReminder::setDataUrl(const QUrl &dataUrl) { setValue(FieldDataUrl, dataUrl); } /*! Returns the url of the audible data which should be played. */ QUrl QOrganizerItemAudibleReminder::dataUrl() const { return value(FieldDataUrl).toUrl(); } /*! \class QOrganizerItemEmailReminder \brief The QOrganizerItemEmailReminder class contains information about an email reminder of an item. \inmodule QtOrganizer \ingroup organizer-details An email reminder is a reminder which alerts the user (or other users) about the item, by sending an email. Note that the Organizer API does not enforce that the email is sent, or that any other sort of reminder occurs; rather, it simply allows clients to store and manipulate data which might be used by the platform to implement alarms and reminders. */ /*! \enum QOrganizerItemEmailReminder::EmailReminderField This enumeration defines the fields supported by QOrganizerItemEmailReminder. \value FieldSubject The value stored describes the subject of the Email, which the user wishes to be sent as a reminder. \value FieldBody The value stored describes the body of the Email, which the user wishes to be sent as a reminder. \value FieldAttachments The value stored describes the attachments of the Email, which the user wishes to be sent as a reminder. \value FieldRecipients The value stored describes the recipients of the Email, which the user wishes to be sent as a reminder. */ /*! Sets the contents of the email reminder to be the given \a subject, \a body and \a attachments. */ void QOrganizerItemEmailReminder::setContents(const QString &subject, const QString &body, const QVariantList &attachments) { setValue(FieldSubject, subject); setValue(FieldBody, body); setValue(FieldAttachments, attachments); } /*! Returns the subject of the email. */ QString QOrganizerItemEmailReminder::subject() const { return value(FieldSubject).toString(); } /*! Returns the body of the email. */ QString QOrganizerItemEmailReminder::body() const { return value(FieldBody).toString(); } /*! Returns the attachments of the email. */ QVariantList QOrganizerItemEmailReminder::attachments() const { return value(FieldAttachments).toList(); } /*! Sets the list of recipients that the user wishes to be sent an email as part of the reminder to \a recipients. */ void QOrganizerItemEmailReminder::setRecipients(const QStringList &recipients) { setValue(FieldRecipients, recipients); } /*! Returns the list of recipients that the user wishes to be sent an email as part of the reminder. */ QStringList QOrganizerItemEmailReminder::recipients() const { return value(FieldRecipients).toStringList(); } /*! \class QOrganizerItemVisualReminder \brief The QOrganizerItemVisualReminder class contains information about a visual reminder of an item. \inmodule QtOrganizer \ingroup organizer-details A visual reminder is a reminder which alerts the user about the item, with a message, image or video. Note that the Organizer API does not enforce that the visual data is displayed, or that any other sort of reminder occurs; rather, it simply allows clients to store and manipulate data which might be used by the platform to implement alarms and reminders. */ /*! \enum QOrganizerItemVisualReminder::VisualReminderField This enumeration defines the fields supported by QOrganizerItemVisualReminder. \value FieldMessage The value stored describes the message to be shown when the reminder is triggered. \value FieldDataUrl The value stored describes URL of the video to be played when the reminder is triggered. */ /*! Sets the message which the user wishes to be displayed as part of the reminder to \a message. */ void QOrganizerItemVisualReminder::setMessage(const QString &message) { setValue(FieldMessage, message); } /*! Returns the message which the user wishes to be displayed as part of the reminder. */ QString QOrganizerItemVisualReminder::message() const { return value(FieldMessage).toString(); } /*! Sets the url of the visual data which the user wishes to be displayed as part of the reminder to \a dataUrl. */ void QOrganizerItemVisualReminder::setDataUrl(const QUrl &dataUrl) { setValue(FieldDataUrl, dataUrl); } /*! Returns the url of the visual data which the user wishes to be displayed as part of the reminder. */ QUrl QOrganizerItemVisualReminder::dataUrl() const { return value(FieldDataUrl).toUrl(); } /*! \class QOrganizerItemTag \brief The QOrganizerItemTag class contains some arbitrary tag which is relevant to the organizer item. \inmodule QtOrganizer \ingroup organizer-details */ /*! \enum QOrganizerItemTag::TagField This enumeration defines the fields supported by QOrganizerItemTag. \value FieldTag The value stored is a tag of the item. */ /*! Sets a tag associated with an organizer item to \a tag. */ void QOrganizerItemTag::setTag(const QString &tag) { setValue(FieldTag, tag); } /*! Returns the tag associated with an organizer item which is stored in this detail. */ QString QOrganizerItemTag::tag() const { return value(FieldTag).toString(); } /*! \class QOrganizerItemTimestamp \brief The QOrganizerItemTimestamp class contains the creation and last-modified timestamp associated with the organizer item. \inmodule QtOrganizer \ingroup organizer-details */ /*! \enum QOrganizerItemTimestamp::TimestampField This enumeration defines the fields supported by QOrganizerItemTimestamp. \value FieldCreated The value stored describes the time the item is created. \value FieldLastModified The value stored describes the last time the item is modified. */ /*! Returns the creation timestamp saved in this detail. */ QDateTime QOrganizerItemTimestamp::created() const { return value(FieldCreated).toDateTime(); } /*! Returns the last-modified timestamp saved in this detail. */ QDateTime QOrganizerItemTimestamp::lastModified() const { return value(FieldLastModified).toDateTime(); } /*! Sets the creation timestamp saved in this detail to \a timestamp. */ void QOrganizerItemTimestamp::setCreated(const QDateTime ×tamp) { setValue(FieldCreated, timestamp); } /*! Sets the last-modified timestamp saved in this detail to \a timestamp. */ void QOrganizerItemTimestamp::setLastModified(const QDateTime ×tamp) { setValue(FieldLastModified, timestamp); } /*! \class QOrganizerTodoProgress \brief The QOrganizerTodoProgress class contains information about the progress of a todo item. \inmodule QtOrganizer \ingroup organizer-details */ /*! \enum QOrganizerTodoProgress::TodoProgressField This enumeration defines the fields supported by QOrganizerTodoProgress. \value FieldStatus The value stored describes the status of the TODO item. \value FieldPercentageComplete The value stored describes the current completion percentage of the TODO item. \value FieldFinishedDateTime The value stored describes the date time at which this TODO item is finished. */ /*! \enum QOrganizerTodoProgress::Status Enumerates the various possible types of todo item status \value StatusNotStarted The todo item hasn't been started yet \value StatusInProgress The todo item is current in progress \value StatusComplete The todo item has finished */ /*! Returns the todo progress item's current status as QOrganizerTodoProgress::Status. */ QOrganizerTodoProgress::Status QOrganizerTodoProgress::status() const { return static_cast(value(FieldStatus).toInt()); } /*! Sets the todo progress item's current status to \a status. */ void QOrganizerTodoProgress::setStatus(Status status) { setValue(FieldStatus, status); } /*! Returns the todo progress item's finished date and timeas QDateTime. */ QDateTime QOrganizerTodoProgress::finishedDateTime() const { return value(FieldFinishedDateTime).toDateTime(); } /*! Sets the todo progress item's finished date and time to \a finishedDateTime. */ void QOrganizerTodoProgress::setFinishedDateTime(const QDateTime &finishedDateTime) { setValue(FieldFinishedDateTime, finishedDateTime); } /*! Returns the todo progress item's completion percentage. */ int QOrganizerTodoProgress::percentageComplete() const { return value(FieldPercentageComplete).toInt(); } /*! Sets the todo progress item's completion percentage to \a percentage. The \a percentage must be between 0 and 100, and values out of the range will be ignored. */ void QOrganizerTodoProgress::setPercentageComplete(int percentage) { if (percentage >=0 && percentage <= 100) setValue(FieldPercentageComplete, percentage); } /*! \class QOrganizerTodoTime \brief The QOrganizerTodoTime class contains information about the time range of a todo item. \inmodule QtOrganizer \ingroup organizer-details */ /*! \enum QOrganizerTodoTime::TodoTimeField This enumeration defines the fields supported by QOrganizerTodoTime. \value FieldStartDateTime The value stored describes the time when the TODO item should be started. \value FieldDueDateTime The value stored describes the time when the TODO item should be finished. \value FieldAllDay The value stored describes if it is an all day TODO item. */ /*! Returns the todo time's start date and time as QDateTime. For all-day tasks, the time part is meaningless. */ QDateTime QOrganizerTodoTime::startDateTime() const { return value(FieldStartDateTime).toDateTime(); } /*! Sets the todo time's start date and time to \a startDateTime. For all-day tasks, the time part can be set to any valid value. */ void QOrganizerTodoTime::setStartDateTime(const QDateTime &startDateTime) { setValue(FieldStartDateTime, startDateTime); } /*! Returns the todo time's due date and time as QDateTime. For all-day tasks, the time part is meaningless. */ QDateTime QOrganizerTodoTime::dueDateTime() const { return value(FieldDueDateTime).toDateTime(); } /*! Sets the todo time's due date and time to \a dueDateTime. For all-day tasks, the time part can be set to any valid value. */ void QOrganizerTodoTime::setDueDateTime(const QDateTime &dueDateTime) { setValue(FieldDueDateTime, dueDateTime); } /*! Sets the all-day status of the TODO to \a isAllDay. If the tasks is an all-day TODO, no time is considered to be specified for the todo, even if the start date time set for the todo has a time component. */ void QOrganizerTodoTime::setAllDay(bool isAllDay) { setValue(FieldAllDay, isAllDay); } /*! Returns true if the todo is an all-day TODO, or false otherwise. */ bool QOrganizerTodoTime::isAllDay() const { return value(FieldAllDay).toBool(); } /*! \class QOrganizerItemType \brief The QOrganizerItemType class describes the type of the organizer item. \inmodule QtOrganizer \ingroup organizer-details */ /*! \enum QOrganizerItemType::ItemType This enumeration describes the type of the organizer item. \value TypeUndefined This item is of an unknown type. \value TypeEvent This item is an event. \value TypeEventOccurrence This item is an event occurrence. \value TypeTodo This item is a TODO. \value TypeTodoOccurrence This item is a TODO occurrence. \value TypeJournal This item is a journal. \value TypeNote This item is a note. */ /*! \enum QOrganizerItemType::ItemTypeField This enumeration defines the fields supported by QOrganizerItemType. \value FieldType The value stored describes the type of the item. */ /*! Returns the organizer item type value stored in this detail. */ QOrganizerItemType::ItemType QOrganizerItemType::type() const { return static_cast(value(FieldType).toInt()); } /*! Sets the type of the organizer item to be the give \a type. */ void QOrganizerItemType::setType(QOrganizerItemType::ItemType type) { setValue(FieldType, type); } /*! \class QOrganizerEventRsvp \brief The QOrganizerEventRsvp class contains RSVP information for an event, applicable to the user of the calendar \inmodule QtOrganizer \ingroup organizer-details RSVP detail contain information such as the role of the calendar user in the event, the participation status of the calendar user in the event, the date by which the user is requested to respond to the invitation, the date at which the user did respond to the invitation, the name of the organizer of the event, and the contact details of the organizer of the event. */ /*! \enum QOrganizerEventRsvp::EventRsvpField This enumeration defines the fields supported by QOrganizerEventRsvp. \value FieldParticipationStatus The value stored describes the pariticipation status of the user for this event. \value FieldParticipationRole The value stored describes the pariticipation role of the user for this event. \value FieldResponseRequirement The value stored describes the if the user is required to respond this event invitation. \value FieldResponseDeadline The value stored describes when the user should respond to this event invitation. \value FieldResponseDate The value stored describes when the user responds to this event invitation. \value FieldOrganizerName The value stored describes the organizer's name of this event. \value FieldOrganizerEmail The value stored describes the organizer's Email of this event. */ /*! Sets the participation status of the user of the calendar in the event to \a status. */ void QOrganizerEventRsvp::setParticipationStatus(QOrganizerEventAttendee::ParticipationStatus status) { setValue(FieldParticipationStatus, status); } /*! Returns the participation status of the user of the calendar in the event. */ QOrganizerEventAttendee::ParticipationStatus QOrganizerEventRsvp::participationStatus() const { return static_cast(value(FieldParticipationStatus).toInt()); } /*! Sets the role of the user of the calendar in the event to \a role. */ void QOrganizerEventRsvp::setParticipationRole(QOrganizerEventAttendee::ParticipationRole role) { setValue(FieldParticipationRole, role); } /*! Returns the participation role of the user of the calendar in the event. */ QOrganizerEventAttendee::ParticipationRole QOrganizerEventRsvp::participationRole() const { return static_cast(value(FieldParticipationRole).toInt()); } /*! \enum QOrganizerEventRsvp::ResponseRequirement \value ResponseNotRequired The organizer does not require the calendar user to respond to the invitation \value ResponseRequired The organizer requires the calendar user to respond to the invitation */ /*! Sets the response requirement for the invitation to \a responseRequirement. */ void QOrganizerEventRsvp::setResponseRequirement(ResponseRequirement responseRequirement) { setValue(FieldResponseRequirement, responseRequirement); } /*! Returns the response requirement of the invitation. */ QOrganizerEventRsvp::ResponseRequirement QOrganizerEventRsvp::responseRequirement() const { return static_cast(value(FieldResponseRequirement).toInt()); } /*! Sets the date by which the user was requested to have responded to the invitation to the event to \a date. */ void QOrganizerEventRsvp::setResponseDeadline(const QDate &date) { setValue(FieldResponseDeadline, date); } /*! Returns the date by which the user was requested to have responded to the invitation to the event. */ QDate QOrganizerEventRsvp::responseDeadline() const { return value(FieldResponseDeadline).toDate(); } /*! Sets the date at which the user responded to the invitation to the event to \a date. */ void QOrganizerEventRsvp::setResponseDate(const QDate &date) { setValue(FieldResponseDate, date); } /*! Returns the date at which user responded to the invitation to the event. */ QDate QOrganizerEventRsvp::responseDate() const { return value(FieldResponseDate).toDate(); } /*! Sets the name of the organizer of the event (who sent the invitation) to \a name. */ void QOrganizerEventRsvp::setOrganizerName(const QString &name) { setValue(FieldOrganizerName, name); } /*! Returns the name of the organizer of the event. */ QString QOrganizerEventRsvp::organizerName() const { return value(FieldOrganizerName).toString(); } /*! Sets the email address of the organizer of the event (who sent the invitation) to \a email. */ void QOrganizerEventRsvp::setOrganizerEmail(const QString &email) { setValue(FieldOrganizerEmail, email); } /*! Returns the email address of the organizer of the event. */ QString QOrganizerEventRsvp::organizerEmail() const { return value(FieldOrganizerEmail).toString(); } /*! \class QOrganizerItemClassification \brief The QOrganizerItemClassification class is for defining the classification of an organizer item. \inmodule QtOrganizer \ingroup organizer-details This can be used as a part of security model for the organizer. */ /*! \enum QOrganizerItemClassification::ClassificationField This enumeration defines the fields supported by QOrganizerItemClassification. \value FieldClassification The value stored describes the classification of an item. */ /*! \enum QOrganizerItemClassification::AccessClassification \value AccessPublic The item can be accessed by everybody \value AccessConfidential The access to the item is restricted \value AccessPrivate Only private access allowed for the item */ /*! Sets the classification of the item \a classification. */ void QOrganizerItemClassification::setClassification(AccessClassification classification) { setValue(FieldClassification, classification); } /*! Returns classification of the item. */ QOrganizerItemClassification::AccessClassification QOrganizerItemClassification::classification() const { return static_cast(value(FieldClassification).toInt()); } /*! \class QOrganizerItemExtendedDetail \brief The QOrganizerItemExtendedDetail class provides the possibility to save extended details to the organizer item. \inmodule QtOrganizer \ingroup organizer-details Different back-end engines may or may not support extended details for different item types. Even if supported, they may accept different QVariant types as the data. */ /*! \enum QOrganizerItemExtendedDetail::ExtendedDetailField This enumeration defines the fields supported by QOrganizerItemExtendedDetail. \value FieldName The value stored describes the name of this extended detail. \value FieldData The value stored describes the data stored in this extended detail. */ /*! Sets the \a name of this extended detail. */ void QOrganizerItemExtendedDetail::setName(const QString &name) { setValue(FieldName, name); } /*! Gets the name of this extended detail. */ QString QOrganizerItemExtendedDetail::name() const { return value(FieldName).toString(); } /*! Sets the \a data of the extended detail. */ void QOrganizerItemExtendedDetail::setData(const QVariant &data) { setValue(FieldData, data); } /*! Gets the data of this extended detail. */ QVariant QOrganizerItemExtendedDetail::data() const { return value(FieldData); } /*! \class QOrganizerItemVersion \brief The QOrganizerItemVersion class provides the versioning information of an organizer item. \inmodule QtOrganizer \ingroup organizer-details */ /*! \enum QOrganizerItemVersion::VersionField This enumeration defines the fields supported by QOrganizerItemVersion. \value FieldVersion The value stored describes the integer version of an organizer item. It can be used as the sequence number as per iCalendar spec. \value FieldExtendedVersion The value stored describes the extended version of an organizer item. It can be used to represent the version stored in the back-end. */ /*! Sets the integer \a version. The \a version must be a positive number, otherwise ignored. */ void QOrganizerItemVersion::setVersion(int version) { if (version > 0) setValue(FieldVersion, version); } /*! Gets the integer version. */ int QOrganizerItemVersion::version() const { return value(FieldVersion).toInt(); } /*! Sets the \a extendedVersion. */ void QOrganizerItemVersion::setExtendedVersion(const QByteArray &extendedVersion) { setValue(FieldExtendedVersion, extendedVersion); } /*! Gets the extended version. */ QByteArray QOrganizerItemVersion::extendedVersion() const { return value(FieldExtendedVersion).toByteArray(); } QT_END_NAMESPACE_ORGANIZER src/organizer/details/qorganizeritemdetails.h000066400000000000000000000063421233466112000220540ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMDETAILS_H #define QORGANIZERITEMDETAILS_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 QT_BEGIN_NAMESPACE_ORGANIZER QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMDETAILS_H src/organizer/details/qorganizeritemdisplaylabel.h000066400000000000000000000050271233466112000230730ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMDISPLAYLABEL_H #define QORGANIZERITEMDISPLAYLABEL_H #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemDisplayLabel : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerItemDisplayLabel, QOrganizerItemDetail::TypeDisplayLabel) #endif enum DisplayLabelField { FieldLabel = TypeDisplayLabel + 1 }; void setLabel(const QString &label); QString label() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMDISPLAYLABEL_H src/organizer/details/qorganizeritememailreminder.h000066400000000000000000000055151233466112000232450ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMEMAILREMINDER_H #define QORGANIZERITEMEMAILREMINDER_H #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemEmailReminder : public QOrganizerItemReminder { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_REMINDER_DETAIL(QOrganizerItemEmailReminder, QOrganizerItemDetail::TypeEmailReminder) #endif enum EmailReminderField { FieldSubject = TypeEmailReminder + 1, FieldBody, FieldAttachments, FieldRecipients }; void setContents(const QString &subject, const QString &body, const QVariantList &attachments); QString subject() const; QString body() const; QVariantList attachments() const; void setRecipients(const QStringList &recipients); QStringList recipients() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMEMAILREMINDER_H src/organizer/details/qorganizeritemextendeddetail.h000066400000000000000000000051721233466112000234120ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMEXTENDEDDETAIL_H #define QORGANIZERITEMEXTENDEDDETAIL_H #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemExtendedDetail : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerItemExtendedDetail, QOrganizerItemDetail::TypeExtendedDetail) #endif enum ExtendedDetailField { FieldName = TypeExtendedDetail + 1, FieldData }; void setName(const QString &name); QString name() const; void setData(const QVariant &data); QVariant data() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMEXTENDEDDETAIL_H src/organizer/details/qorganizeritemguid.h000066400000000000000000000047231233466112000213600ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMGUID_H #define QORGANIZERITEMGUID_H #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemGuid : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerItemGuid, QOrganizerItemDetail::TypeGuid) #endif enum GuidField { FieldGuid = TypeGuid + 1 }; void setGuid(const QString &guid); QString guid() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMGUID_H src/organizer/details/qorganizeritemlocation.h000066400000000000000000000052631233466112000222400ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMLOCATION_H #define QORGANIZERITEMLOCATION_H #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemLocation : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerItemLocation, QOrganizerItemDetail::TypeLocation) #endif enum LocationField { FieldLatitude = TypeLocation + 1, FieldLongitude, FieldLabel }; void setLatitude(double latitude); double latitude() const; void setLongitude(double longitude); double longitude() const; void setLabel(const QString &label); QString label() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMLOCATION_H src/organizer/details/qorganizeritemparent.h000066400000000000000000000053221233466112000217150ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMPARENT_H #define QORGANIZERITEMPARENT_H #include #include QT_FORWARD_DECLARE_CLASS(QDate) QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemId; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemParent : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerItemParent, QOrganizerItemDetail::TypeParent) #endif enum ParentField { FieldParentId = TypeParent + 1, FieldOriginalDate }; void setParentId(const QOrganizerItemId &parentId); QOrganizerItemId parentId() const; void setOriginalDate(const QDate &date); QDate originalDate() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMPARENT_H src/organizer/details/qorganizeritempriority.h000066400000000000000000000055641233466112000223150ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMPRIORITY_H #define QORGANIZERITEMPRIORITY_H #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemPriority : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerItemPriority, QOrganizerItemDetail::TypePriority) #endif enum PriorityField { FieldPriority = TypePriority + 1 }; // time vs impact priority? greater granularity? enum Priority { UnknownPriority = 0, HighestPriority = 1, ExtremelyHighPriority = 2, VeryHighPriority = 3, HighPriority = 4, MediumPriority = 5, LowPriority = 6, VeryLowPriority = 7, ExtremelyLowPriority = 8, LowestPriority = 9 }; void setPriority(Priority priority); Priority priority() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMPRIORITY_H src/organizer/details/qorganizeritemrecurrence.h000066400000000000000000000064271233466112000225700ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMRECURRENCE_H #define QORGANIZERITEMRECURRENCE_H #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemRecurrence : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerItemRecurrence, QOrganizerItemDetail::TypeRecurrence) #endif enum RecurrenceField { FieldRecurrenceRules = TypeRecurrence + 1, FieldExceptionRules, FieldRecurrenceDates, FieldExceptionDates }; void setRecurrenceRules(const QSet &rrules); QSet recurrenceRules() const; void setRecurrenceDates(const QSet &rdates); QSet recurrenceDates() const; void setExceptionRules(const QSet &xrules); QSet exceptionRules() const; void setExceptionDates(const QSet &xdates); QSet exceptionDates() const; bool operator==(const QOrganizerItemRecurrence &other) const; bool operator!=(const QOrganizerItemRecurrence &other) const {return !(other == *this);} }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMRECURRENCE_H src/organizer/details/qorganizeritemreminder.h000066400000000000000000000070641233466112000222360ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMREMINDER_H #define QORGANIZERITEMREMINDER_H #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemReminder : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerItemReminder, QOrganizerItemDetail::TypeReminder) #endif enum ReminderField { FieldSecondsBeforeStart = TypeReminder + 1, FieldRepetitionCount, FieldRepetitionDelay }; enum ReminderType { NoReminder = 0, VisualReminder, AudibleReminder, EmailReminder // other types of reminders? //ProcedureReminder, //TactileReminder, }; ReminderType reminderType() const; void setSecondsBeforeStart(int seconds); int secondsBeforeStart() const; void setRepetition(int count, int delaySeconds); int repetitionDelay() const; int repetitionCount() const; protected: /*! \internal */ QOrganizerItemReminder(DetailType detailType) : QOrganizerItemDetail(detailType) {} /*! \internal */ QOrganizerItemReminder(const QOrganizerItemDetail &detail, DetailType detailType) : QOrganizerItemDetail(detail, detailType) {} }; #define Q_DECLARE_CUSTOM_ORGANIZER_REMINDER_DETAIL(className, reminderType) \ className() : QOrganizerItemReminder(reminderType) {} \ className(const QOrganizerItemDetail &field) : QOrganizerItemReminder(field, reminderType) {} \ className& operator=(const QOrganizerItemDetail &other) {assign(other, reminderType); return *this;} QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMREMINDER_H src/organizer/details/qorganizeritemtag.h000066400000000000000000000047071233466112000212050ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMTAG_H #define QORGANIZERITEMTAG_H #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemTag : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerItemTag, QOrganizerItemDetail::TypeTag) #endif enum TagField { FieldTag = TypeTag + 1 }; void setTag(const QString &tag); QString tag() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMTAG_H src/organizer/details/qorganizeritemtimestamp.h000066400000000000000000000052501233466112000224270ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMTIMESTAMP_H #define QORGANIZERITEMTIMESTAMP_H #include QT_FORWARD_DECLARE_CLASS(QDateTime) QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemTimestamp : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerItemTimestamp, QOrganizerItemDetail::TypeTimestamp) #endif enum TimestampField { FieldCreated = TypeTimestamp + 1, FieldLastModified }; void setCreated(const QDateTime ×tamp); QDateTime created() const; void setLastModified(const QDateTime ×tamp); QDateTime lastModified() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMTIMESTAMP_H src/organizer/details/qorganizeritemtype.h000066400000000000000000000052271233466112000214110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMTYPE_H #define QORGANIZERITEMTYPE_H #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemType : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerItemType, QOrganizerItemDetail::TypeItemType) #endif enum ItemType { TypeUndefined = TypeItemType + 1, TypeEvent, TypeEventOccurrence, TypeTodo, TypeTodoOccurrence, TypeJournal, TypeNote }; enum ItemTypeField { FieldType = 501 }; void setType(ItemType type); ItemType type() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMTYPE_H src/organizer/details/qorganizeritemversion.h000066400000000000000000000051361233466112000221140ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Organizer module. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMVERSION_H #define QORGANIZERITEMVERSION_H #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemVersion : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerItemVersion, QOrganizerItemDetail::TypeVersion) #endif enum VersionField { FieldVersion = TypeVersion + 1, FieldExtendedVersion }; void setVersion(int version); int version() const; void setExtendedVersion(const QByteArray &extendedVersion); QByteArray extendedVersion() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMVERSION_H src/organizer/details/qorganizeritemvisualreminder.h000066400000000000000000000052671233466112000234650ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMVISUALREMINDER_H #define QORGANIZERITEMVISUALREMINDER_H #include QT_FORWARD_DECLARE_CLASS(QUrl) QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemVisualReminder : public QOrganizerItemReminder { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_REMINDER_DETAIL(QOrganizerItemVisualReminder, QOrganizerItemDetail::TypeVisualReminder) #endif enum VisualReminderField { FieldMessage = TypeVisualReminder + 1, FieldDataUrl }; void setMessage(const QString &message); QString message() const; void setDataUrl(const QUrl &dataUrl); QUrl dataUrl() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMVISUALREMINDER_H src/organizer/details/qorganizerjournaltime.h000066400000000000000000000051041233466112000220740ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERJOURNALTIME_H #define QORGANIZERJOURNALTIME_H #include QT_FORWARD_DECLARE_CLASS(QDateTime) QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerJournalTime : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerJournalTime, QOrganizerItemDetail::TypeJournalTime) #endif enum JournalTimeField { FieldEntryDateTime = TypeJournalTime + 1 }; void setEntryDateTime(const QDateTime &entryDateTime); QDateTime entryDateTime() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERJOURNALTIME_H src/organizer/details/qorganizertodoprogress.h000066400000000000000000000056741233466112000223110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERTODOPROGRESS_H #define QORGANIZERTODOPROGRESS_H #include QT_FORWARD_DECLARE_CLASS(QDateTime) QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerTodoProgress : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerTodoProgress, QOrganizerItemDetail::TypeTodoProgress) #endif enum TodoProgressField { FieldFinishedDateTime = TypeTodoProgress + 1, FieldPercentageComplete, FieldStatus }; enum Status { // what about: waited/blocked, and deferred? StatusNotStarted, StatusInProgress, StatusComplete }; void setFinishedDateTime(const QDateTime &finishedDateTime); QDateTime finishedDateTime() const; void setPercentageComplete(int percentage); int percentageComplete() const; void setStatus(Status status); Status status() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERTODOPROGRESS_H src/organizer/details/qorganizertodotime.h000066400000000000000000000053651233466112000214000ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERTODOTIME_H #define QORGANIZERTODOTIME_H #include QT_FORWARD_DECLARE_CLASS(QDateTime) QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerTodoTime : public QOrganizerItemDetail { public: #ifndef Q_QDOC Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(QOrganizerTodoTime, QOrganizerItemDetail::TypeTodoTime) #endif enum TodoTimeField { FieldStartDateTime = TypeTodoTime + 1, FieldDueDateTime, FieldAllDay }; void setStartDateTime(const QDateTime &startDateTime); QDateTime startDateTime() const; void setDueDateTime(const QDateTime &dueDateTime); QDateTime dueDateTime() const; void setAllDay(bool isAllDay); bool isAllDay() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERTODOTIME_H src/organizer/doc/000077500000000000000000000000001233466112000144105ustar00rootroot00000000000000src/organizer/doc/images/000077500000000000000000000000001233466112000156555ustar00rootroot00000000000000src/organizer/doc/images/qmlorganizerlistview-main.png000066400000000000000000001142221233466112000236100ustar00rootroot00000000000000PNG  IHDRRlsBIT|dtEXtSoftwaregnome-screenshot> IDATxwxTٞ;I7E XAԫ(`ظbU,ElXiһ{ {f%@@ЈbQ1o%3ضr)cp6tq?wy{k;(Hye k|0)4kӉ=;Gp6/E܌'c\֫NI ]MwrOFN"I!2| cL{EdǙ|&O <+Rr2 p1G%(N#5%&tꘄB2RsD%Q,v3)w'`#gu|v̅dfQX\LَKHD,qq& e9'ȯp2拪"écU{@R(k/pp"Ɠ0̹Y*U PЮbӏKى'nlY(Bh ,!h*Dz)jmޕ\\twU[M <_],W0!gY ',;NV^1n=&$hD]@. Y[:0= P D'̋]UEdgep7gYiq|Q tNy^ǎe_F:<}",2P?Z* @HW^MU Y §oE>]'%9VE ~׉3 03B!,uxgLO P#TnR&" xTaqz[Fw>-"N-oE%먗żUnw&z>1Ol4WLFTկGZd18N%7OD_PPl|k>> QqHWxn"W+TZx7=3GU%\g&2ND^o!xLXmbBN4}hH+Ŕ!?x#g}.59r׈׆ REEjWd?+n-58W4&67E^ϒ*} h,F~B}1w9zE%<":Qo,˝5^6XxBW%&>YV]. aD1e0ϔ Ejm{"K؄N}ʢ(Z#{.SbDZ%mͭq/jEM}}8Mڛ& -szk{U*YB~OQJ(:%6eVSP@luhaD'!BC_/ָqE 6Q>c(9}:Xhm@(j,QyqK8/pB+^|xXjXUuӽu(]qCZhsf $<u"qO])oO%I7%3v[ί݃T£}}5mIs$ -P_](N?H_ T?Eɯyy\&pWD]E3o]5c(zm9iX~ bl(n;eX}񸸑npWPRVvԭ"zx֮x]V*ҟ֐gw3ugn.ܗ;XO>/MPkWc̈7kp]{Mt0W4C[{nUS<]WᎶAh#u| IL]-~)硑tHcU(9BO}s/џg>ݛu 7mET&``Dj?Byޚ`oǀޭ=9?.;ȯs籹"&<]NFxp` ?9ؠ.ݗd?LmNfW*?x{ Jqh8g8br#&ۉʆ ހVuq_EEUpb98\A>]I|YؕZ:z(:7Szٮ=S>bA8(-% asaRQv"2;ԾGMh=w_FtK((.uPaĚy~468;0'LncoIsѣS4TwjД5[!5Jqx[ԄưxW<l8sw]$9[C#Zԋ-+U{~,Ym[&"x&Sm`4"P,Zsg"}^ELBm'U@"c,OC[A.`PT:L^&fn.+Y0[4rM;C|XRG^mD>u7?n+y{ͬ]}ET:\g!BURUP|Z 5cB%X.0'E;gcf 7*Ooqػ%Gsp(ܵ3)Afp[sS.pQLIEV5p.y~65ne#Hȗ(^u@䯙q kNۮsV#!{$TŝT#- ,)OO.*OƢ8A*o1bu&tb.-G =oaO&0i܍nݺйC[Z4N *ܿs銓 㧆 Jl|U'(t.ZPtxua m݁I1`2N Τ1]sH ۄVO_}UCV=꒟dn*Ӗdv'pN+UN dKC,^^taPD:{q~ی¯rgkG6dQD 잌qX,X@Ev:ȸ@$is=ܮZ!RQj [ϥ#X:_|͏Kֲu,6)ړs#ޅ8V7W,Ф4SHŎC OlQA(rjFKXŭBkĨ{YUN꺒qY(,čTgj.ǴnyF!(۞wʥ_Y-|3wQ!#_GsnTj= ~3^> ScKC2Dж_ZzKV. pxR֝Pp&, A xa o /+ؕWyzZY?ʾYx>civ PX KF*d:R/:b*:}6 ^$hq䧑eS #&)eq*zEӳd^`*?w&œxWa:{*c0q ayϮ@폿~|WV}֏򝿲l[6Xdd DY Sh9V`| EUc6xw=ϴ:WsΛ&+j&h[rmCin4f$lC/:7!{Vt:oZ^G>;ףuBT/SkiU4Ƣ(~Y^%Ol6E&K{xy?j0ZtJc`nik`,{Xh7dyx4Gќ=`jMѨ69Qu6db [Rf꒖J(hciݥq&SlK5$,EuyofSg9\`yzAE^|8GH{$v=*<^h1{1[*)=̚9Sxx 4a.ͯדZ<=8/yp\R&ϼ GÇ+Oqk *$lūSf2YCg?zlf ?OaiչWHp;W~ˬ9 RE}i~ʧ1IN7>_DZ2Aea:;Ei?^!)aw8p8y b8V?&mq"]o`jxg2\ӹ9~ZGٵq vebHbؤxe|b=Hl LN/,(oY9m=w0$+-S}Z ~G1{ˋi5_@UTX",RMzg[xlLo" =_$>7oAmb*+%9i6x߿_TO涵9G)t$>3'$)5ǰ`|wTEޭa X.ѸX ފ *&^nM'\: l>ۙLi ԢHga15dN扄pp跥_^LkM|e߶!`!94;WE8IcLA I{woLG_E?h? P1D"+_ջNPVb'-Ϸ_=M?L9O֯_`c,#*l,egx?4bЈBjƅW˗/[n |u8b"0rrҏr$=&S#h/$f~z{wg I$IM$IF%I$G^YH$Iඐs s7nQ%'pv6*ݥ|qu8fJ /,JCLRAh$I]OYyT!>15_ki[<oX'Gj~dmJ%Ȍd~4AG$/tH_~f؇Lxs)`lNПoaVS,pV?7vU'EKuC/z'\b%IBgn %TlVϘ[_?8S o1>+x잞I8PB[>9A:[ O]}5G{`5 h1_8dI$zgCC/6|x WmXL (afE˙?~D^9v]qQnM'N?ݒgm8tU_3?yczYa*(2Rǜiجɽ~ayck?\+ S_r-:'uS3,d X.X?I$]Q^Wl L=X$W/$m<Ҩ L%{mЖb̯q>CڂʃRD QEfjfIߗψɴkzq{3")ĽϤwlCiþ|n>o$I^4P;GPj}2mH+ gu1-D*Fhz;:Qnv7n*p;Fva% 89&<Ǝroy]u$I{9=gxи9UѼ }5^) z)j[4aП@ģ0&H`ԧǾEZmN$I "wSEW\x. 6ɱF}yoKaȞ^YʍK.\nPTj._$Iș`QǑqL6GI7ȕKLJ{SiC0dxSF!_&>-BFY^B~ 較  !4̗ Yty9nsjvsX:%63iU`/".||D'㫻zK$]fZOԻf0ag=|&5($tLU;1]:M_SxoĻJJ-4 Z67Ӆwxexnzˆ50;rp[:]hsdQ! y`S_`l+,$Ij8pۅ0R 6d ꨤ*< T$L 8=^a({9LnBqa C Aq\JيSI`/jK>e. /"pp̓.+:- =xgI?Kr&V$IuV$I $IRd$I%$IT/,$Iz`!I$K I$^7**\j=FF=?J IDAT قKk9;݀RrAUV*j#^^uRQ]eۤ?,…Bjpc,ҩˈVv$I 4+9[~_dW6a0`#7,I=t(޽_snD/wp|2SlIԿLRM/PT:~a܋~}:8]~8K|OP w}&D8KpT>bC#5oDAYr?/b햝<7xz`$=s1*|h~{zZlK$9*[2ޞ2{[⋙o1dTf NTd`ڜm9]-.Z-ܸ _y﫝^x_<eKYuUe"~]~*8{|n{27f}ΧSn#pSLxoYUȖ Q|?ˎc\ؑGD8kA֚x8/?B=/ȕ5$I4Y{^؞nDnfm X-e/{)9goAnI~ZGqx7[+?m_0}Y1NS,!)Mayh=u}RRc%.KtכNMP;==^(JO]CLޑ4Ѝ>}׻cszDh_͏/`r<u[+-(91~$u> ^5 c/W:Uy8oY wc4?_K硅_,Qxb@xiRjߑ *mM^wbޫKuW7B/r7zI:q$61oXdcqVw5Q$'ENѵ%Q&p'dskGeO=ʇp"f };<>O -;{Dۧ>cwߚJИMlZ5wun .*.)BZnR&Aם;EO6+N=LX`csG/"+OPPYnj4ې>16s03ƱTD4#Gȭ(( :]eGS+Hh@_L qH+:$I@w-ON+g3pcx݌?>h8 om{W0Ydcmڕ~GxG ԙ1)FAc1 я6.ʚb5V:\w3^xF8!f#:EDWP "ʫطs$ǧw'˶{hG׏sGKoT@Uxsi"c?C,V3n&é5: ] YᨢRѤ;,Lyk-IT j/{.xN&7[/+^0m$rs7xz/G-r z\j|u_R̕?c̯7`4cދ0iF,?ݛЋub0c dM'0 B[i\XJ.&${]dKf+ym*w @4ztOޙ p98u^hܞ֢ӹq\SN6Fno.Iҟsz [&$ ! ofk(sҢ ܅fJ⟙ xuҍPIj splj=Zd6Eɮ`!,l_]AH5oML`iSģ n ǖg慷y_,ƓGCF^N`D&V.c Q 37dc0\$IQXȷr$se9ٱ ֈH:t&dfPPTF͉өhP+vd 5zO EdfRPT`׼*t]J}Èb+)9֭GI 1ZNUg5xҸ{ذ]*| DUYDur_Lke YGSɩ|]ӫo76R|v6)=QX`  +)K̒EGh׻=QFa08YX\kI6Ogv!6tSK,9_RZ^$IRJw~"HLi-:u"ڥĊ>e QY|2hݼha1w1yƽSrkѣ_/ѵ߭bn3lSd/{ZTs7-Z5o)Z~LpTihi$!.;{:{8QCtn (Y$=[?{HII"))Y4mK~ktU?m/z5o)$l/Zt->V,JX3MDWAXSl#۱-ExDOL' V8.a>@pS+Ѥ}7ѥE{lq7wF4 ":<\D&ľbB8wO ͚w:4͇L %IQp |J+&8"w2*X8N^wh~2CX7s5 ³ƈVNqQ.->AAyT [,v( ֋hB}t{&7r˫'UjO|}LjmdUXKhaƓ+X8]U1IgM5mRj;:L :U(!DU_"b8YG%TE?PoSICO`d,uY$ȴ$IRA$IRd$I%$IT/,$Iz`!I$K I$^ynmR/sQ[<8kECdyvUq-p:{ΔðdT]n7B=Ly-ܗbBsHTVcė''2 ^.jf[}mBէx 5G$ 'ѭg#ޟAY|YK03[Wo!،A#vaɗsf]if?hGTz $'շe#3Ly$]^'[2AY~vnζmضmbCPUo};utm}6Yq~}a&;x܎䕅b;ƂɷLlWR2SLy$]N'elU*I "{WƟf7.ȝ3qkTDkW}ivKLy̔'IsUc3ux<ړs* # Dxka**2iNţ1V=®xd$ILNd&L^,$&kCc0a:\'e<)Ot d-tӫgb7Es6~uf %G6hvldd1aɬw2_ZLy2S$IFln}jh=Y3 Pb*gRh AP{pLxӌSm?}1?ӯ`+(I&NA{&A+&݆0uZ ^; /Zfi/$ք>J0{yi\;'M9E>xkyָ"pdl/8]kuQL5kBIlYn!b {x{*;GRȏ?grPOz) @%$I:"fQ_Dv6h+sȵ C^NAA9"/V4a譔;apWR_LXe\v1d<)OFfʓ$I%w$I%$IT/,$Iz`!I$l6 ##*$IԀ<==INN&66z-黡VXcO=OA7^/WJ$lVҏ`ӚEXl/5{=uFSVW7uN?: vK9 @}[BI$Uv;_͜C@ٱ}sLkn ȱyeZ,{oûEOeqG'FwsFV%8sx!IRtt{3gڙDu;|UN?室0"HӜMƑd;DQL2?10E׸~z'H",d݀&:!ʆ!Pio]ꭓYy75F_77m!m%IjQhVZE[C΍?Ԃ}5)<܃4;jUx0Q`^˂^cYN)qe|a{#R-߁OC[)nOTvmF~U.Sꡠψ(3cNI!:*t^>gQQhP4:u&$I2:ݙ[gm4pѽN ;khrQ.~Y}!&s?+$5GZ "mG"[PwE:D !uKZʡ8z<ʋdSڜGQ%tYgu_m;H@eO;Faq9c<6A-FJEvcU\f5cx:fV[^1m:+WIp@>!I$] $IRd$I%$IT/,$Iz`!I$K I$^2XH$IB$IUEm//,"L<-0 PsU:|_MTU̡E(InKL@][8(Kľf܊ ƀw aфT8gqd?X!D6nKpXRH`/{2"[$XS;{;vL= [ ˆrvX˄+o4G):;sh 0oÙ{7neתM8 U`!e.)/C(>GnH;R|l$I=a/TSnѢ&/[BVTF|cLJ̸t6"W^|bɤPEʄ; [E>YG"sbw)h:ԗy0r|5W?iC[CY{|^}eѵxp*zrw`\4 JCa/&/(%6QDE;Yxa"7YX1Gx?I"~O`5ފ@ƶ?\:wh7!?̒{iqW'}[3乹>̢)oRY s/&b y VЩhd$]%TLx5(:o|t`=[S1xυy|׽œZCزwqT>\GL~.MhJR9 t5 wڇ|uErc7S$,ȫ:))PR?^((ZKXa 跜wNN$ba6yowOrd/;z$stZ܉ ֔SE`h2i=FOj:4#GI!O(0%SNz$sY9~8᭚lT^&ݜ(%w$WdSBKinlOT? Cq4To2OQYʚd4>gBhSa iK( (.3c}V 9qTnQ太 Bsc=2iv}GB)K܊b*mn}{)%.@¦ı ߒv0SIř9vsDӑۚbT+ X`s鑀U@T_(B7iҙC ~* 2{$I@ܨ:at\-Qy3_1Y8,vi:M= ꯈQ&vSu[6/"Ŧ?;<*{d&@B (XPQAŎbvQDDD*JD@Bz~~$Cpyxd!s>*3a釿 N V|0xH{ESe;ilTXA~ԬzE[pk5ඤEm[ɗ_]C_YFt'9sBy[Wشf-\O%oW,XFl|~{PTYBMJ IDAT&ogE͡,fFbr:<8*HJk`; oˁ}{/AcmBtr{Z4ϤmȡMӎ]tck8X%8 \ )oYXH ஥$s+2rt۱3kXw@bǮDUc rb8,6H)Il8PhGc}(Ϯ P!NSM["5pVH5lݙA6IkDU?su_W;vtuWc6C=iAfcoVse1ڇ4Iulmٵ>DIX/BHqzFH%<"]3zX}je B\ b߾}t܅b!S YfJudf7cȐIr\$''|e!.iR,n7zt,`0puR0%KBa0ر# .`СM&z E $IrG.4 ݻwgW_q`0g& qɑb!rG @g2jh #z h2Ks_,ҢjN4$6}d(J)Cٳ0Fm:h4I =_Sd;~ O3Rғ=۽M>ߗc~wʜMat4.Äi|bsQ+v1]5T##׋Wň˻u*m?b|!ɷ5׿nj7!wªd7덫p_>68}Mi) {2T2/U><5n}Fs.'8BӡjIOOg cmxwiN/u qցqGhV =>^nJssч@]Kdb Ecs=rr:h/D'Iy;\V^رc0aSŠ+`Ab01x8ns`"͵䬚w~2 l*xYoBFHR$]\.\uTW0|png\78@Qy҆ڡ%݌%(җ֓g`?ݟ1qE/}7U}q1VldM!XMr\$)ҕͨQjOy ӦM'{f7Э/^/J\zꇡt+چ0Fbj`Ky fYJEcZңeӺD['xW14>-3ź (I;%)Rr, nc 1i$&MkMe߮tOUJJ#)=n';FVP(DžKkpdLDh<;sL$)ܺ+i|؄Oc=Fzz:;#U)sdB?~i򏡉~9etl^HރU}騤)+l0꘤me{ןMR媢h{}R5uIyRSaPY4)[Ɓ2RQqhIy&)o,[jHʻꬓEDk$)j5~!I4V ^zvVXɶѮE>S!NM<ŶRrao?ؼfrV`({oj3z 4//̝ZOkdl5 on|'?jz>) 6U'Iywh4$$˂VEJ\b %etwiN'cĥE y/zҲcW17dOA]roR#,xjsl_):xJ QwHϕ~"S.\z5Gt9AQX.{2K ˈVi[ ?Ep;'ǓNnixjݲ%):D !ŦP cʹ4 \gmhx$бoZ13E0>PYvbP /2TgWhmS|O݈3y{ YNL^X vl7Xa+`&qDKM'rccᰪ8c!EuX/c+9+i, {C`'}ŘwY? HdzG_憾 F4`Ը&j4U لSxUPX\AbBJ}sRAQH p K9%v-zPCo?Ӟl ԁ10I{YA֞͵a eR4~ SYZDAA1n/"f=A顝8PEDp!YPꇗWۨWߠnnRzgTNIuطԺ_rS԰ M1jCUjl SX]|cSY+q6:v|MjƈIuUujR%&uT}S-YN-pDq~խE0~Z{P6sԲkUVm՘7~VvUUQ*jq_V#ՀƨzQe _+vwJ1cK㤪0@ JSXM͈w`~zey6 UUDaUCE~UJOeI Yyg]m'(_ytSa{pd>@7#jCEeq)4礌tm%ctREo?Μ) c+#/;2hG8(&ĆHxP|ǟҌ9ꭥr\w]֏֣ٷoe2d>9K)Zڝ'un>y!SzJK`7&-&|Iv~Œp萙O'{?ŤY[p[ ؊ 0C-Me3{iWɆM5O?gr| ~xxt|˼qt ;C!`tw.%>zpM9='SgE068>腗o~@;FETݟBI5rw:Z|gByB?1b çy7vJ$CV=^9TaTm!Y9v"18+)tQ~K^-j7,t.][NM< ษ^+aS.+)/Nw*ؓwUǯ5##ط#+8ϤPbĄ5 WwQ}.yæ-݈05ދЄDž}8e?-Gc9mxGk6(fDb$'{Ve,II>t#u?㗪`U 1s/m~{${Z3]MlǚҞ}l &Pm?tؔ-xӇm͛[Gll ~ֿɇiSʯMCLV-U3X7Vo*g'+ 都c.[ss 7}5#Jq?6b,X#SS@!sf$@vy|heQ=%TDWI Mǖ5sP3Z6se3NxMY`ZunM.5^gO 1Eqnd: b ]*٭kDŽV&gyثK)Ԧ'4m07?pc$4fSХI kdsvIPd:u|1ݮ+m8v^QY&"B哗G^^>tH FrTt;?:@u*~#6_p_MrV{֎?߲,LaR!q4)΀lY{\8=ptrAg@8qNܸ\4zß[ɮ<Q.N^B\`*QBFIB()B!%B!DX!h !ҫ"Ȣq`9]^!LѽhHHR$ !%v/_=p onBUWe ޝc b*'>ً+G=[ ?u+OgDo^|t$e'Y5vEYSNIA7r1K?~R=s!d"@юm8O&%"Ι=o#u(e[1 ^؝p'bwq51ts 9:+>E޹O}7q#m`!άPy=^~XތլpVRXۢ(/FHdT1Wnj*a:f$IR:rI3x#G2xgU-Thwk|g/"VǓOeUm ޖHZƖꏪʶs 6*;'IyBs[e·xz-u;[#[rP-Me:w>!ˇvdd>{y:EW͂[wOded}Wlo]îп5s!IR IyB?Xh1G46M}n5؋[cJ%:浆%T6zO[Ѕ rkPo+ XO/xOOM[{weKœ;a}Y5!ؐUsp~>\Tx~O Ɛ> x_O9|݇IbO3\&|+?0:Mym1r嘖͍t1c/dž|iӸym3zF)$5%-g}-փ,=;VC>u3kb[6G\vaY{PUX5c b6M:1zkoύw(K3=Ȥ l8KͫOgˣ@jd|Qc^ IDATAU"(3"ÉKoCXΏ|2;:O]BM;9) IʓxnF3?&O y{wsRK@l dxJF)$4 hfn}'sX+w~Ɣ~"Wl;tU{wo?`WK!5A:TNnqb`tjiwљ0FUK\]<1fTړ|Jʄ;ɪB@H8'ga;Sޟ{Dӆ fɏs/"tX3>5ф3ັ,{2fN]CWB.a}؂Z3扌!{)5|104]w^Nl;l?!dHm0NcfLۋ+>}ٷU!Σ q?+pp푗d9!: c-K^[@̕cS)=T+иٻK>>^LxnEl{_`*t>Mi)S;gC%ҭ0q|w揵"3T'=LMI7/,COBK@-}zdc:?v&>sM d$9)xJY̪Lk|3 <{_?n>5%tmZNۦ,}w[{NavhߣBWz =O #`h%@-Z3`Pn]3Jϋ~%[hf7Ѕ泷U q9SMjCH;r"=+Y!$X3V̦#W<6(3PJchh'НC6Ue's U{|6Weu p BqUܛZb?Z6y*)-` $6)N^ +7nhsqt/tenU8͉Rqک}Bqa-U۱ϱy: aqYuhkp{ޚ9yt'>CX[.ђfI\* /`~HЅг8o(9ܴAјM~GS Mׇb\ٷ|3m rliΰI2ū_`76 w n!EU7mFvqƎO24wBϙ0[ЃC4y^wK s>jjIw^ -Fo4hLfl҅s5(v~"v yMLƒZ ,Fy2Fg/8c}ꬦZzFi^9E85&|‰ נUO(!>ծJJq 7Ս+eWp GWS@As`(A=KMF.̉?`Y !y"B!% !h !b!QR,B4JBFIB()B!udrRYY[gb>/Ev:qk OVUIGj=u˅ˣ@Eӡi Wm%n~YMe zN\ :Nue n7l#"8+z̏Y9Ķ ;[Ga۾o{cH/bh)nJ,=ҝ˹n)8|ZY^^~/,lff0ud;t#OBVW'?\h:"k܂9slc'Ulͮ+=W|'/O'h|fNŷ[NIw2}F\oyWa.u9h=w-9[`kM$@ !g.{|Jƽ5'GypCQx<&T+-/噫|?y2o#O1nl,:ze"6ڞ7&zn&ƱǙYBUml(߮Lvj~ϗm>xo͑+ٰo"݆dD$ ̥$4uuoZc AYF.s^g)oM;>NJ9:1rţ3̓GCy^=M`pV< 4N?>m¸Ww{^wڛa=HӃrPk%w Mѫ)FqbC75ղ{ȣO0[L5M2lm>Q14S-y,XdiҧU(f,7nν +!h-q$ˏ>߶/Wݵ7xDֹ=muRqm1Scƚ|\(%fXVIʰьߙfAޥ'=iUm;?`KؚJFS6jNfθkaCThLDĐ6e} }7?7p记h23NE/yr@'Fv #{RyۉM Vt=Y4L-vSDl Iͪٳ/4yܽq%L͠TB^qCyxz3g+'Sl瞱KlNZrfHHGwfEV`kٍ53e­$ $ӹx@[_7-od:9E9*.tN~m N5♉OMƧݤɏ %lۏ7YI\ C~JMU#wme޴W9NHh, s :OVG=o6%]ߏ}NKe't/6%?k>jdʌGX-׹mю[coV3UU@#9,xu2՛^/cbwj)V-Zb`9c` iJckPF4JE--߹,\Ǿ*(uEFl1ba)!++r.\. zAYJ(,.Ϙ\61ՅY̡ʍCdTLD~b/{( kJo mj$sTuCvim{+7t'4+Xb%zBBCj..¦ !{w\KxcU6U%ٷn|cj,|rtk:]TQXs%Po y8\X'ؘZJG nь_ ܶr3s(vc "I]0LrKh-4nB ܵ%V`?! Vo !6OR#VEFN).s1d q*QBFIB()B!%B!DX!h !ң<\s)z ?2k?jy9?fb;8$IR\+4hvV8!09L$XclVf:@܎¶&p`ŇMS kRu6*g?k'6G v`&ZAH`yl?3/,r:9&t8[-ߺաLA z d? 7 I/lxˌZ:^;4Oʜ&!hK ޙz|=%;xy^ғ<=+AM< ~ݛr?=2Cl&{]v'xxN-$dC(sY%?K} !Dc4Q >.Bc:!AxA9*(,pB\Tgp>00 ̞j pC{HR^IB 'QBFIB()B!%B!DX!h !ץ9p"Bc0v Iq8O,̧NB H3_WWqbת^6ux,'ƪcj qs6Ջݥ&NɞL|z*@i_:ۻ !guVޒ$W89q;yRn5]m[9 ej~4;m`m[<܊pUdm[[rƑޱ!fض0mkaAk'U":&wlS*Bt uR,MV!W:c'SZ׳ &/Ć%1!hJ8#A<; KSٷ=CFxj55S~٭'Һj˾[áll? [!JMˋ!?]m:MI_Yׄb}FWU s[M!@H C]TT@:( l8+ N"%TCH~H"0u?ឳe] Avw)X4dȒJW x #|oۏM!* .NBQwDߔ÷K_ 4V|=ʎ,&@"Ky?j_'÷'n43fs4;]̓}$lP1PۀwcueUI.gߪEd(C ̼WW1%Bl+qcoAF6 +Izfԭۨ4h/gj"L)GHͩ,x'900RrWф|1oBR%@gX#ݲuNYØnwl%avo]1U*yOAh_z!ԃ^;%rP ?  ; ]+0Ԕrh͊MN8Y^Z,t &ϼln:8z_ug۰7m_?Hjt_A.YHO˖iz8½-v6YK|\1\$jtV:k}ȍEJuRD2LJtlh0`c4qqֳcHZk2<^-݈g$iAr 찳CoojP[RYEf&i~_n:]Hua.EʹGN|F AڕZXkd-X^*c+O1Sj7bGasgr_bә3 Ly Ɖ*rd>|.|[V_ܺUyo,O<130wWM4\4QE:᪮!;u[2y0KLo о$XdF_^Թk Z2iW Ex{hW.1Q[ƁI(Vǻs7z{ 4v>.M].;P9p7ti*g/ɦ32g'[KTke֮S8W:w)G9y6zw0Z BiUA6^ BDA$ &,A6`! I AM"X mBAhPdR2h:ߙm5K+ )l$ܼ|Khrjsv-?`4mNtd(φ"R$s NnaV1TSyGQԈW0R2<&@f5V403J5ͧ /RDhoC%gfϑ7dݽAhj ⟻וUs!G]J;Q~%^/F EfW:湻6a0,ۋ-u xx HI_44Y|; o6`D]6KDJW_;47锿,yqi"Ajfyl}=:2׾Iof3a{l۷$CJ+5;SA۹ƣXU!FN5IDAT} ?O]}z_mdf w@MUkPU呕[F}ę㩨Vtĝ2ɿP\,Qh8 vzҜ*$9eWq"@<-4$Z=`Ь~@ygEG3鏥`jn(JGix0l eY$;O=o11 %8~& :v%]k̍edJ'B*;~:lyUD؇k6팥J`.<!{}m_,p{b1S6e,^pm_x).TgyyR,*TVlOCߴ_?#$Qj)PBe-jd=..P{0[QQhņ} svjkC}ARѼdctTGM͈l *ϳ|l,p0x+mxx7[ 6`*V)8$,c7 ïRe$@4X91lC|7AA4]K3qȾ]m9v\8_U'XӼKGřTGsrED"R-ңQђ\ ) ˜l=*..[ώb/omque|XKC xIj:%M=>Akb̙2fXby=KSDHI5f ?i z`(xsDΦg9*=NO$)fuӆp~zr)8Ûcۙ0QnmREK*v+Z"w3⩩%}sL]a㘰W5sh\My%oTwS5'7A|IDbM35%]ӵ~QQMiqM 4VSf傳MqvN4Q`! %.%8α9#L\sVHRK9YV!2Ri6|Gsl4Q[PIg)uDN+ * jYIF{h͇ZPdڋ>H' gc3:5#fBS)HOYXވƹL~;DNTk8:n}3wl6$]OB$f3m( U%IߗVEjY_̸{'sqW|' /(i39>\,j93|`6ssKMA@QG7IAtwD#ЀF[F#-X8}րO 4,spI <.pBvozw,aךI &h€Sw;,n[Xqlȡh@i" mP7Rlv ,8<$hQ M-YM5g0౑ ㄹ2J0#ZahdILCq69*;t?ջPP r囷]{\F~i psb*\DXKH`='\0vb(- UF@ UG`6*Hj${rw/e>.όYvE`f s 1ҽ'W&޻LUbJO/9Vȝ誳9OA!(AڠzvN{>{L쇭G\OyEdEe>_1]>eb01 >ْ ` #5r5 8s2?Wt;t7C%Wɛw *)>b7º{U*XIs];Wϗc5c!.#m@lϪ$H"[6e4zu#Y9nW9PDMQɧEjOakzFN6j<- fC>ͧi[ [8PAX;X~aj8C y9U'Qq R9v i;\kP9;g@w?b $CU~(G$B-]B ``D2\Jt!ؐζ;8v^EHj:'(r~3kPq}[؛QU`?Gm9rbC%z`:%z0n K;. A"Ҫ mgC mBAh BDA$ &,A6`a6a4)mU$ P+OCq]5pyX V7Ȕ'2 pS~Zfq"m=;ohH^8$l68w؛` MU{|b˖B~,i>hKd%~83A>XݬLy;yck:p?-_.絏G'hԘ͖7Lͩ,[p;8L6Vl"оJ _s;qco1yAh6;áA֓ kK|UIBL])NWq1r>;jAa[2qqrkѺ{"כ.]1ԄIeBdA. M ȭmҢU)>)ofBD}x^@g:N2=0lK)5Ubvog1=tȔw-)OGKPII&f-7 (4V:0|<DOx $MIG^ϯbѤX&l: kg-մD<@dwi  #![{G_$t]#vnڦ4\Ut%z`C}/CT])rEJ_sYg2NZӐ3x鄌Νg̳;I2c~WMp7fobHqe (`;2uF^|a!|)NXOTOgd,%PZkދ{n}cj/7K?+J`n KɏIKK#));we?Ĵi:t(wq$)TWW3sL/_v}vbbbڻpˑBRijըژv Ĩ}޸ƝAcvy IA\ IFsZNըX` ^Æ cǎ] Aiii@L$i^yUY*cfzK2swJ)ٛ}KMsOK.z&_Co}G߿'y-ovǬWqO3ۢEڻpQ'%%wc$6<'G515\@B Y&H2*Iiه ɨ /?oǶRtS{OӿyPdbh-PDF^* ,#)&_ I}ƌD7kpJHH 77. 2ٳgwCd|$B_A>˃Hۇ߲3q7 kbc|.q`lG2SD(Lvв>;-]$,~g>9A .Y `Iؑt3Ys"˄vW5F#GJ)_ĠLԇ/ u`wuG1?>r>}x4<#5i#"uɡ{w 2wĢs!Dp HT8ų/$?c$GaT ;6cx )ړYcBa+yt dj37SoҠ!#l)N &oPqSAuI%݇0AuAJ 3޸u&)\*ClqA*I$m973EWmbV0YMy,toGw,a= $ {U&2RӨ؃} v#ý<ڛvkԡ4*L #2U)Fj63ouSihnmЕ*\\5"beuw7$1)?|΋IL9ޟECG)4rYt:ęW،uxZ㉏g7x mxLp6ZPg) ?Jvw";aK5`jjajDOٸi5ʟN:w"%''+QQQ]Geg` Φ"N-]pcd4Ad 4Nhq'?+ HgFH')pǺ ^#iq K. 448tOo<4ٕF@BeKXx(*jp)jM(Ʃ#!ˆ·NvNqzJ:\:cwUTOV+tv9͹+ƪ$z0z㨪;UHícg 9ddWa4NtvS(ʢQARYw Ο?T\\[lib-ER}%·_z=o6ӦMkBB CiIENDB`src/organizer/doc/qtorganizer.qdocconf000066400000000000000000000040211233466112000204700ustar00rootroot00000000000000include($QT_INSTALL_DOCS/global/qt-html-templates-offline.qdocconf) include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) project = QtOrganizer description = Qt Organizer Reference Documentation url = http://qt-project.org/doc/qt-5.0/qtorganizer-index.html version = 5.0.0 qhp.projects = QtOrganizer qhp.QtOrganizer.file = qtorganizer.qhp qhp.QtOrganizer.namespace = org.qt-project.qtorganizer.500 qhp.QtOrganizer.virtualFolder = qdoc qhp.QtOrganizer.indexTitle = Qt Organizer Reference Documentation qhp.QtOrganizer.indexRoot = qhp.QtOrganizer.filterAttributes = qtorganizer 5.0.0 qtrefdoc qhp.QtOrganizer.customFilters.Qt.name = QtOrganizer 5.0.0 qhp.QtOrganizer.customFilters.Qt.filterAttributes = qtorganizer 5.0.0 qhp.QtOrganizer.subprojects = classes overviews examples qhp.QtOrganizer.subprojects.classes.title = Classes qhp.QtOrganizer.subprojects.classes.indexTitle = Qt Organizer's Classes qhp.QtOrganizer.subprojects.classes.selectors = class fake:headerfile qhp.QtOrganizer.subprojects.classes.sortPages = true qhp.QtOrganizer.subprojects.overviews.title = Overviews qhp.QtOrganizer.subprojects.overviews.indexTitle = All Overviews and HOWTOs qhp.QtOrganizer.subprojects.overviews.selectors = fake:page,group,module qhp.QtOrganizer.subprojects.examples.title = Qt Organizer Examples qhp.QtOrganizer.subprojects.examples.indexTitle = Qt Organizer Examples qhp.QtOrganizer.subprojects.examples.selectors = fake:example tagfile = ../../../doc/qtorganizer/qtorganizer.tags headerdirs += .. \ ../../versitorganizer \ ../../plugins/organizer \ ../../imports/organizer sourcedirs += .. \ ../../versitorganizer \ ../../plugins/organizer \ ../../imports/organizer exampledirs += ../../../examples/organizer \ snippets/ imagedirs += images depends += qtversit qtbase qtcontacts src/organizer/doc/snippets/000077500000000000000000000000001233466112000162555ustar00rootroot00000000000000src/organizer/doc/snippets/doc_src_qtorganizer.cpp000066400000000000000000000041661233466112000230310ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //! [include] #include //! [include] //! [namespace] QT_BEGIN_NAMESPACE_ORGANIZER QT_END_NAMESPACE_ORGANIZER QTORGANIZER_USE_NAMESPACE //! [namespace] src/organizer/doc/snippets/doc_src_qtorganizer.pro000066400000000000000000000040501233466112000230370ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #! [organizer project modification] QT += organizer #! [organizer project modification] src/organizer/doc/snippets/moduleimports.qml000066400000000000000000000041501233466112000216730ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //! [Organizer import] import QtOrganizer 5.0 //! [Organizer import] //! [Contacts import] import QtContacts 5.0 //! [Contacts import] Rectangle { } src/organizer/doc/snippets/qmlorganizerbasiclist/000077500000000000000000000000001233466112000226655ustar00rootroot00000000000000src/organizer/doc/snippets/qmlorganizerbasiclist/content/000077500000000000000000000000001233466112000243375ustar00rootroot00000000000000src/organizer/doc/snippets/qmlorganizerbasiclist/content/organizer_ical_test.ics000066400000000000000000000041731233466112000310730ustar00rootroot00000000000000BEGIN:VCALENDAR VERSION:2.0 CALSCALE:GREGORIAN METHOD:PUBLISH BEGIN:VTIMEZONE BEGIN:STANDARD TZOFFSETFROM:+1000 TZOFFSETTO:+1000 TZNAME:EST DTSTART:19700101T000000 END:STANDARD END:VTIMEZONE BEGIN:VEVENT DTSTART:20101208T220000Z DTEND:20101209T070000Z DTSTAMP:20101208T051153Z UID:Event CREATED:20101208T050327Z DESCRIPTION:starts 2010-12-09 8AM finishes 5PM LAST-MODIFIED:20101208T050327Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 2 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART:20101207T220000Z DTEND:20101208T030000Z DTSTAMP:20101208T051153Z UID:Event2 CREATED:20101208T050202Z DESCRIPTION:starts 2010-12-08 8AM finishes 1PM LAST-MODIFIED:20101208T050202Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 1 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART:20101216T000000Z DTEND:20101216T060000Z DTSTAMP:20101208T051153Z UID:Event1 CREATED:20101208T050550Z DESCRIPTION:starts after Event 3 and finishes 4PM LAST-MODIFIED:20101208T050550Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 4 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART:20101216T060000Z DTEND:20101216T070000Z DTSTAMP:20101208T051153Z UID:Event4 CREATED:20101208T050643Z DESCRIPTION:start after end of Event 4 finishing at 5PM LAST-MODIFIED:20101208T050643Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 5 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART:20101215T220000Z DTEND:20101216T000000Z DTSTAMP:20101208T051153Z UID:Event5 CREATED:20101208T050446Z DESCRIPTION:starts 2010-12-15 at 8AM finishes 10AM LAST-MODIFIED:20101208T051055Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 3 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART:20110108T010000Z DTEND:20110108T020000Z DTSTAMP:20101208T051153Z UID:Event3 CREATED:20101208T050752Z DESCRIPTION:start a month from 2010-12-08 at 11AM finish at 2PM LAST-MODIFIED:20101208T050752Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 6 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTAMP:20101208T051153Z UID:Event6 CREATED:20101208T050926Z DESCRIPTION:starts 2010-12-10 at 11AM finishing 1PM\, repeating for 4 weeks LAST-MODIFIED:20101208T050926Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 7 TRANSP:OPAQUE END:VEVENT END:VCALENDAR src/organizer/doc/snippets/qmlorganizerbasiclist/qmlorganizerbasiclist.qml000066400000000000000000000046221233466112000300140ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ //! [Basic list Complete Snippet] import QtQuick 2.0 import QtOrganizer 5.0 Rectangle { id : organizerSample width: 200 height: 400 Rectangle { id:organizerEventView anchors.fill: parent color: "white" //! [Organizer Model] OrganizerModel { id: organizer startPeriod: "1970-01-01" endPeriod:'2012-12-31' autoUpdate:true //! [Manager Choice] // manager:"qtorganizer:jsondb:id=qml" manager:"qtorganizer:memory:id=qml" //! [Manager Choice] Component.onCompleted : { if (managerName == "memory") { organizer.importItems(Qt.resolvedUrl("content/organizer_ical_test.ics")); console.log("Memory backend : import Items executed."); } } } //! [Organizer Model] //! [List View] ListView { id: calendar anchors.fill: parent clip: true delegate: Text { text: model.item.displayLabel } model: organizer } //! [List View] } } //! [Basic list Complete Snippet] src/organizer/doc/snippets/qtorganizerdocsample/000077500000000000000000000000001233466112000225125ustar00rootroot00000000000000src/organizer/doc/snippets/qtorganizerdocsample/qtorganizerdocsample.cpp000066400000000000000000000236131233466112000274600ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include QTORGANIZER_USE_NAMESPACE static void snippets(); static void dumpItems(QOrganizerManager* manager); static void dumpItem(const QOrganizerItem& item); int main(int, char**) { snippets(); return 0; } void snippets() { //! [Instantiating the default manager for the platform] QOrganizerManager defaultManager; //! [Instantiating the default manager for the platform] //! [Instantiating a specific manager] QOrganizerManager specificManager("KCal"); //! [Instantiating a specific manager] // XXX TODO: use rrule instead of rdates. QDateTime startDateTime = QDateTime::currentDateTime(); QDate firstOccDate = startDateTime.date().addDays(7); QDate secondOccDate = startDateTime.date().addDays(14); QDate thirdOccDate = startDateTime.date().addDays(21); QDateTime endDateTime = startDateTime.addDays(28); QSet rDates; rDates << firstOccDate << secondOccDate << thirdOccDate; //! [Creating a recurrent event] QOrganizerEvent marshmallowMeeting; marshmallowMeeting.setRecurrenceDates(rDates); marshmallowMeeting.setPriority(QOrganizerItemPriority::HighPriority); marshmallowMeeting.setLocation("Meeting Room 8"); marshmallowMeeting.setDescription("A meeting every wednesday to discuss the vitally important topic of marshmallows"); marshmallowMeeting.setDisplayLabel("Marshmallow Conference"); if (!defaultManager.saveItem(&marshmallowMeeting)) qDebug() << "Failed to save the recurrent event; error:" << defaultManager.error(); //! [Creating a recurrent event] //! [Retrieving occurrences of a particular recurrent event within a time period] QList instances = defaultManager.itemOccurrences(marshmallowMeeting, startDateTime, endDateTime); //! [Retrieving occurrences of a particular recurrent event within a time period] qDebug() << "dumping retrieved instances:"; foreach (const QOrganizerItem& currInst, instances) { dumpItem(currInst); qDebug() << "...................."; } //! [Retrieving the next 5 occurrences of a particular recurrent event] instances = defaultManager.itemOccurrences(marshmallowMeeting, QDateTime::currentDateTime(), QDateTime(), 5); //! [Retrieving the next 5 occurrences of a particular recurrent event] //! [Retrieving the next 10 occurrences of any item (Agenda View)] instances = defaultManager.items(QDateTime::currentDateTime(), QDateTime()); instances = instances.mid(0, 10); //! [Retrieving the next 10 occurrences of any item (Agenda View)] //! [Creating a non-recurrent entry] // a default constructed journal will have it's date/time set to the current date/time. QOrganizerJournal journal; journal.setDescription("The conference went well. We all agree that marshmallows are awesome, "\ "but we were unable to reach any agreement as to how we could possibly "\ "increase our intake of marshmallows. Several action points were assigned "\ "to various members of the group; I have been tasked with finding a good "\ "recipe that combines both marshmallows and chocolate, by next Wednesday."); defaultManager.saveItem(&journal); //! [Creating a non-recurrent entry] //! [Editing a non-recurrent entry] journal.addComment("Serves: 8. Ingredients: 500g Milk Chocolate, 500g Marshmallows."\ " Step 1: Put the marshmallows into 8 separate bowls."\ " Step 2: Melt the chocolate."\ " Step 3: Pour the chocolate over the marshmallows in the bowls."\ " Step 4: Put the bowls into the refrigerator for 20 minutes; serve chilled."); if (!defaultManager.saveItem(&journal)) qDebug() << "Unable to save updated journal! Error:" << defaultManager.error(); //! [Editing a non-recurrent entry] //! [Removing an entry] defaultManager.removeItem(journal.id()); //! [Removing an entry] //! [Retrieving entries for a time period] QList entries = defaultManager.items(QDateTime(QDate(2010, 1, 1), QTime(0, 0, 0)), QDateTime(QDate(2010, 1, 31), QTime(23, 59, 59))); //! [Retrieving entries for a time period] //! [Retrieving entries with a filter] entries = defaultManager.items(QOrganizerItemLocation::match("Meeting Room 8")); //! [Retrieving entries with a filter] //! [Downcasting items] QList items = defaultManager.items(); foreach (QOrganizerItem item, entries) { if (item.type() == QOrganizerItemType::TypeEvent) { QOrganizerEvent event(item); qDebug() << "Event:" << event.startDateTime() << ", " << event.displayLabel(); } else if (item.type() == QOrganizerItemType::TypeEventOccurrence) { QOrganizerEventOccurrence event(item); qDebug() << "Event:" << event.startDateTime() << ", " << event.displayLabel(); } else if (item.type() == QOrganizerItemType::TypeTodo) { // process todos } else if (item.type() == QOrganizerItemType::TypeTodoOccurrence) { // process recurring todos } else if (item.type() == QOrganizerItemType::TypeJournal) { // process journals } else if (item.type() == QOrganizerItemType::TypeNote) { // process notes } } //! [Downcasting items] //! [Creating an exception to a particular recurrent event] QOrganizerEventOccurrence nextMarshmallowMeeting = defaultManager.itemOccurrences(marshmallowMeeting).value(0); nextMarshmallowMeeting.setStartDateTime(QDateTime::fromString("13.05.2010 18:00:00", "dd.MM.yy hh:mm:ss")); nextMarshmallowMeeting.setEndDateTime(QDateTime::fromString("13.05.2010 20:00:00", "dd.MM.yy hh:mm:ss")); nextMarshmallowMeeting.addComment("The next meeting will go for an hour longer (starting one "\ "hour earlier than usual), since we have scheduled one hour"\ "to taste the results of the recipe that I will be presenting "\ "at the meeting."); defaultManager.saveItem(&nextMarshmallowMeeting); //! [Creating an exception to a particular recurrent event] //! [Getting a list of collections] QList collections = defaultManager.collections(); //! [Getting a list of collections] QOrganizerCollection collection = collections.first(); //! [Saving an item to a collection] marshmallowMeeting.setCollectionId(collection.id()); defaultManager.saveItem(&marshmallowMeeting); //! [Saving an item to a collection] //! [Retrieving items in a collection] QOrganizerItemCollectionFilter collectionFilter; collectionFilter.setCollectionId(collection.id()); items = defaultManager.items(collectionFilter); //! [Retrieving items in a collection] dumpItems(&defaultManager); } void dumpItems(QOrganizerManager* manager) { QList items = manager->items(); qDebug() << "dumping" << items.count() << "items:"; qDebug() << "============================="; for (int i = 0; i < items.size(); ++i) { QOrganizerItem curr = items.at(i); dumpItem(curr); if (i < (items.size() - 1)) { qDebug() << "--------------"; } } qDebug() << "============================="; } void dumpItem(const QOrganizerItem& item) { qDebug() << "item:" << item.displayLabel() << ", id:" << item.id(); QList dets = item.details(); foreach (const QOrganizerItemDetail det, dets) { qDebug() << " new" << det.definitionName() << "detail:"; QVariantMap values = det.variantValues(); QStringList keys = values.keys(); foreach (const QString& key, keys) { qDebug() << " " << key << "=" << values.value(key); } } } src/organizer/doc/snippets/qtorganizerdocsample/qtorganizerdocsample.pro000066400000000000000000000012121233466112000274650ustar00rootroot00000000000000###################################################################### # # Simple example of how to use the contacts API # ###################################################################### TEMPLATE = app TARGET = qtorganizerdocsample include(../../../../features/basic_examples_setup.pri) INCLUDEPATH += ../../../../src/global \ ../../../../src/organizer \ ../../../../src/organizer/details \ ../../../../src/organizer/requests \ ../../../../src/organizer/items \ ../../../../src/organizer/filters CONFIG += console QT = organizer SOURCES += qtorganizerdocsample.cpp src/organizer/doc/src/000077500000000000000000000000001233466112000151775ustar00rootroot00000000000000src/organizer/doc/src/icalsupport.qdoc000066400000000000000000000115521233466112000204200ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page icalsupport.html \target icalsupport \title Supported iCalendar Features \section1 Calendar Components The ical specification allows a number of components to be supported within each calendar. The following are supported with the QOrganizer Versit implementation. \table \header \li Versit Component \li QOrganizer Component \row \li VEVENT \li QOrganizerEvent, QOrganizerEventOccurrence \row \li VTODO \li QOrganizerTodo, QOrganizerTodoOccurrence \row \li VJOURNAL \li QOrganizerJournal \row \li VALARM \li QOrganizerItemAudibleReminder, QOrganizerItemEmailReminder, QOrganizerItemVisualReminder \endtable \section1 Properties The following table lists the iCalendar properties that the \l{Qt Versit C++ API} supports for the Organizer calendar components. \table \header \li Versit Property \li QOrganizerDetail \row \li DTSTART \li QOrganizerEventTime \row \li DTEND \li QOrganizerEventTime \row \li RECURRENCE-ID \li QOrganizerItemParent \row \li UID \li QOrganizerItemId \row \li PRIORITY \li QOrganizerItemPriority \row \li DUE \li QOrganizerTodoTime \row \li CREATED \li QOrganizerTodoTime, QOrganizerItemTimestamp \row \li LAST-MODIFIED \li QOrganizerTodoTime, QOrganizerItemTimestamp \row \li RRULE \li QOrganizerItemRecurrence \row \li EXRULE \li QOrganizerRecurrenceRule \row \li EXDATE \li QOrganizerRecurrenceRule \row \li INTERVAL \li QOrganizerRecurrenceRule \row \li FREQ \li QOrganizerRecurrenceRule \row \li RDATE \li QOrganizerRecurrenceRule \row \li DATE \li QOrganizerEventTime \row \li COMMENT \li QOrganizerItemComment \row \li DUE \li QOrganizerTodoTime \row \li CATEGORIES \li QOrganizerItemTag \row \li X-QTPROJECT-EXTENDED-DETAIL \li QOrganizerItemExtendedDetail \endtable \section1 Parameters The Versit module supports the following vCard parameter: \list \li ENCODING (for base64 or quoted-printable values) \li CHARSET (text character set for a specific property) \li TYPE (see below) \endlist The following table lists the values that are supported for the TYPE parameter: \table \header \li Value of TYPE parameter \li Value of QOrganizerDetail Context/SubType \row \li DAILY \li QOrganizerRecurrenceRule::Daily \row \li WEEKLY \li QOrganizerRecurrenceRule::Weekly \row \li MONTHLY \li QOrganizerRecurrenceRule::Monthly \row \li YEARLY \li QOrganizerRecurrenceRule::Yearly \row \li COUNT \li QOrganizerRecurrenceRule::CountLimit \row \li UNTIL \li QOrganizerRecurrenceRule::DateLimit \row \li INTERVAL \li QOrganizerRecurrenceRule::Frequency \row \li BYDAY \li Qt::DayOfWeek \row \li BYMONTHDAY \li see \l{QOrganizerRecurrenceRule::setDaysOfMonth()} \row \li BYYEARDAY \li see \l{QOrganizerRecurrenceRule::setDaysOfYear()} \row \li BYWEEKNO \li see \l{QOrganizerRecurrenceRule::setWeeksOfYear()} \row \li BYMONTH \li QOrganizerRecurrenceRule::Month \row \li BYSETPOS \li see \l{QOrganizerRecurrenceRule::setPositions()} \row \li WKST \li Qt::DayOfWeek \row \li STATUS \li QOrganizerTodoProgress::FieldStatus \row \li IN-PROCESS \li QOrganizerTodoProgress::StatusInProgress \row \li COMPLETED \li QOrganizerTodoProgress::StatusComplete \row \li NEEDS-ACTION \li QOrganizerTodoProgress::StatusNotStarted \row \li PERCENT-COMPLETE \li QOrganizerTodoProgress::FieldPercentageComplete \endtable */ src/organizer/doc/src/organizer-index.qdoc000066400000000000000000000067771233466112000211750ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page qtorganizer-index.html \title Qt Organizer \brief Qt Organizer enables clients to fetch, update, or remove calendar, scheduling and personal data from an organizer item manager. Qt Organizer provides clients with the ability to access calendar, schedule, and personal data in a platform-independent and datastore-agnostic manner. This is achieved by defining generic personal information data abstractions which can sufficiently describe calendar and scheduling data stored in the native calendaring system of a platform. Through the plugin architecture, Qt Organizer can be used as a front-end API for any calendaring system, such as an online calendar. \section1 Getting Started To include the definitions of the module's classes, use the following directive: \snippet doc_src_qtorganizer.cpp include To use the C++ library in your application, add the following configuration option to your \c .pro file: \snippet doc_src_qtorganizer.pro organizer project modification To use the classes of the module in your application, add the following import statement to your \c .qml file: \snippet moduleimports.qml Organizer import \section1 Related Information \section2 Guides \list \li \l{Qt Organizer Overview} \li \l{Qt Organizer API Advanced Usage} \li \l{Qt Organizer Asynchronous API} \li \l{Qt Organizer Synchronous API} \li \l{Qt Organizer Manager Engines} \li \l{Qt Organizer C++ API} - overview of the C++ API \li \l{Qt Organizer QML API} - overview of the QML API \endlist \section2 Reference \list \li \l{Qt Organizer C++ Classes} - list of C++ classes \li \l{Qt Organizer QML Types} - list of QML types \endlist \section2 Examples \list \li \l{Qt Quick Organizer List View Example} - Import event lists in iCalendar format, select a backend for volatile memory or persistent storage, as well as list, add, edit, and remove events. \li \l{calendardemo}{Calendar Demo} -specify some simple events with simple recurrence options and view the events. \li \l{ToDo Example} \endlist */ src/organizer/doc/src/organizer.qdoc000066400000000000000000000261001233466112000200460ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page organizer.html \title Qt Organizer Overview \brief An API enabling clients to request calendar, schedule and personal data from local or remote backends. \ingroup qtpim-c++ The Qt Organizer API enables a client to request calendar, schedule and personal data from local or remote backends. \tableofcontents With the Qt Organizer API typical use cases are: \list \li Access a list of calendar events from the calendar database supported by the selected backend. \li Import iCalendar events into the selected calendar database. \li Share events by exporting iCalendar content. \endlist The Qt Organizer provides both a \l{Qt Organizer Synchronous API}{synchronous} and an \l{Qt Organizer Asynchronous API}{asynchronous} API. Note that for clarity, the short examples on this page demonstrate the synchronous API. While these code snippets might be useful for non-GUI applications, it is highly recommended that the asynchronous API is used for GUI applications. A full list of classes can be found in \l{Qt Organizer C++ API}. \section1 Instantiating Organizer Manager Organizer information is stored in datastores whose functionality is exposed via the QOrganizerManager class. Most users of the API will want to use the default manager for the platform, which provides access to the system address book. Instantiating a manager by using the default constructor will result in the default manager for that platform being instantiated: \snippet qtorganizerdocsample/qtorganizerdocsample.cpp Instantiating the default manager for the platform \section1 Creating New Items You can create a new item simply by instantiating one and saving it in a manager. \snippet qtorganizerdocsample/qtorganizerdocsample.cpp Creating a non-recurrent entry \section2 Retrieving Items You can request all items from the manager that occur in a given time range. \snippet qtorganizerdocsample/qtorganizerdocsample.cpp Retrieving entries for a time period It is also possible to filter the items on the value of a detail. \snippet qtorganizerdocsample/qtorganizerdocsample.cpp Retrieving entries with a filter The above code will retrieve both items that have been saved to the manager and items which are generated based on \l {recurrences}{recurrence rules}. Given a recurring item, it is possible to retrieve a list of items that it generates; that is, to get a list of the upcoming occurrences of a single recurring item. This can be done using QOrganizerManager::itemOccurrences(): You can also retrieve a particular existing item from a manager, by directly requesting the item with a particular (previously known) id. The synchronous API provides the QOrganizerManager::item() function to retrieve a single item by its id. With the asynchronous API, this can be done using a QOrganizerItemFetchRequest and a QOrganizerItemIdFilter. \section1 Updating Existing Items You can update a previously saved item retrieving the item, modifying it, and saving it back to the manager. The manager uses the id of the item to match up the provided item with the one in the database. \snippet qtorganizerdocsample/qtorganizerdocsample.cpp Editing a non-recurrent entry \section1 Removing Items You can remove an item from the manager by using its id. \snippet qtorganizerdocsample/qtorganizerdocsample.cpp Removing an entry \section1 The Organizer Item Model \section2 Items A \l QOrganizerItem represents an event, todo, journal or note. Each item stored in a manager is identified by a \l QOrganizerItemId. The id is the means by which the manager can: \list \li Determine whether a \l{QOrganizerManager::saveItem()}{save} operation should make a new item or update an existing one. (If an item has a null id, it should be saved as a new item) \li Match an item to an existing one for updating. \li Link between items (for example, in QOrganizerItemParent). \endlist The QOrganizerItem class provides a generic interface for accessing events, todos, journals and notes. To actually access specific fields of an item, convenience subclasses of QOrganizerItem are offered. These are QOrganizerEvent, QOrganizerTodo, QOrganizerJournal and QOrganizerNote. Additionally, QOrganizerEventOccurrence and QOrganizerTodoOccurrence can be used for manipulating occurrences of event or todos (see the \l{recurrences}{Recurring Items} section). Here is an example of how to retrieve details specific to an item: \snippet qtorganizerdocsample/qtorganizerdocsample.cpp Downcasting items \target recurrences \section2 Recurring Items A recurring item is an item that occurs more than once; for example, a meeting that occurs every week for the next 10 weeks. A recurring item is created by creating a QOrganizerEvent or QOrganizerTodo and setting a QOrganizerRecurrenceRule on it to specify the rules for when it should recur. When QOrganizerManager::items() is called, recurring items are \e not returned. Rather, they expanded into multiple QOrganizerEventOccurrence and QOrganizerTodoOccurrence items. Each generated occurrence item has a null id. You can make an exception for an occurrence by taking a generated item occurrence from the manager, making the necessary modifications, and resaving it. When the manager is then queried with QOrganizerManager::items(), it will return the list of occurrences as before, but with the modifications in place. The modified item will be given a non-null id, and replaces the generated one in the list. Here is an example of changing a single occurrence of an item: \snippet qtorganizerdocsample/qtorganizerdocsample.cpp Creating an exception to a particular recurrent event You can also query the manager for a list of unexpanded items by calling QOrganizerManager::itemsForExport(). The list of returned items will contain all items that have been saved to the manager with a call to QOrganizerManager::saveItem() That is, recurring events will be returned as is, and event occurrences will not appear unless they are exceptions (ie. have a non-null id). Fetching the list in this way can be useful for transfering items to other managers or for exporting to iCalendar with QtVersit. \section2 Collections Every item stored in a manager belongs to exactly one \l{QOrganizerCollection}{collection}. A collection can have properties such as a name, a "color", a specified icon, a description, and so on. Collections may be added or removed if the manager supports those operations, or modified. There will always be at least one collection in a manager, and the manager will always have a default collection into which items are saved if no other collection is specified. Some managers will allow users to create collections (for example, a "football fixtures" collection) while others may have built-in collections (for example, "work" and "home" collections). A list of all collections can be retrieved from a manager with one function call: \snippet qtorganizerdocsample/qtorganizerdocsample.cpp Getting a list of collections To save an item to a collection, set the collection ID on the item object. If the collection id is the null id, the item will be saved in the collection in which it is currently saved (if the item already exists) or into the manager's default collection (if it is a new item). \snippet qtorganizerdocsample/qtorganizerdocsample.cpp Saving an item to a collection To retrieve all items in a collection, a QOrganizerItemCollectionFilter should be used. \snippet qtorganizerdocsample/qtorganizerdocsample.cpp Retrieving items in a collection \section2 All-day Events Events and Todos can be specified as all-day or multi-day by setting the AllDay field to \tt{true} (using QOrganizerEvent::setAllDay()). When this field is set to true, it means that the time portion of the StartDateTime and EndDateTime should be ignored. An event or todo marked as all-day should be considered to start and end roughly on its given start and end dates (inclusive), but without specifying exact times. For example, a birthday could be specified as an all-day QOrganizerEvent where the StartDateTime and EndDateTime have the same value. \section1 Using Asynchronous and Synchronous API The asynchronous API provides a way to access or modify the organizer item information managed by a particular backend via non-blocking, asynchronous requests. It is recommended for most applications that the asynchronous API be used where possible. The asynchronous API is offered through various classes derived from the QOrganizerAbstractRequest class, including QOrganizerItemIdFetchRequest, QOrganizerItemFetchRequest, QOrganizerItemFetchForExportRequest, QOrganizerItemSaveRequest, QOrganizerItemRemoveRequest, QOrganizerItemOccurrenceFetchRequest, QOrganizerCollectionFetchRequest, QOrganizerCollectionRemoveRequest, and QOrganizerCollectionSaveRequest. The asynchronous API allows manipulation of \l{QOrganizerItem}{items}, and \l{QOrganizerCollection}{collections}, but does not provide manager capability information reporting. \sa {Qt Organizer Asynchronous API} The synchronous API provides the simplest way to access or modify the organizer item information managed by a particular backend. It has the disadvantage that calls block the current thread of execution until completion and is therefore most suitable only for applications which interact with local, high-speed datastores, or for applications which do not require a responsive user interface. The synchronous API is offered through the QOrganizerManager class, and includes manipulation of \l{QOrganizerItem}{items}, and \l{QOrganizerCollection}{collections}. \sa {Qt Organizer Synchronous API} \sa {Qt Organizer API Advanced Usage} It is possible for third party developers to implement a manager engine plugin from which clients may request data. For more information on this topic (for example, if you intend to implement a manager backend) please see \l{Qt Organizer Manager Engines}. */ src/organizer/doc/src/organizeradvanced.qdoc000066400000000000000000000221511233466112000215360ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page organizeradvanced.html \title Qt Organizer API Advanced Usage \section1 Introduction This section provides some detailed information on more advanced topics regarding the Qt Organizer API. \section2 The Detail Model While QOrganizerItem and its subclasses provide methods for data access and manipulation which should be sufficient for most purposes, it is actually a generic container that can hold arbitrary data in the form of \e details. In fact, functions for manipulating items, such as QOrganizerItem::displayLabel() or QOrganizerEvent::setRecurrenceRule() are merely convenience functions that perform operations on the underlying details of an item. A QOrganizerItem consists of nothing more than the details it contains, as well as an id and the id of its collection. A QOrganizerItemDetail is a single, cohesive unit of information that is stored in an item. A detail may have specific meta-data associated with it, as well as access constraints which may apply to the detail (e.g., read-only, irremovable, etc). A list of all standard details defined by this API are listed \l{QOrganizerItemDetail Leaf Classes}{here}. Some details are read-only (such as the modification timestamp of an item) or irremovable (like the type of an item), but most are freely modifiable by clients. The QOrganizerItem::details(), QOrganizerItem::detail(), QOrganizerItem::saveDetail() and QOrganizerItem::removeDetail() functions can be used to manipulate these details. It is important to note that details are implicitly shared objects with particular semantics surrounding saving, removal and modification. \section2 Optimizing Item Retrieval Clients can inform the manager that they do not require certain details from an item, which can allow a manager to optimize item retrieval. In this way, the client can inform the manager that they are not interested in any binary blob data (e.g., images) in retrieved items. These restrictions can be specified by providing a QOrganizerItemFetchHint as an argument to the retrieval operation. Note that if the item already exists in the database, it will be completely replaced. This means that clients should not save any item which was retrieved with a non-empty fetchHint defined, or data loss may occur. \section1 The Organizer Manager and Manager Engines \section2 The Organizer Manager Access to organizer items is provided by implementations of the Qt Organizer \l{QOrganizerManager}{manager} API. Each manager may support different capabilities (for example, the ability to store certain datatypes, the ability to natively filter on different details or details of different definitions, the provision of locking mechanisms, the provision of changelog information, etc) which are reported by the manager on request. The manager therefore provides access to detail definitions and collections of organizer items stored in different datastores, in a platform and datastore independent manner. The QOrganizerManager is in fact a client-facing interface through to a platform-specific manager engine (which is implemented as a Qt plugin). While clients never interact directly with the manager engine, they may need to be aware of limitations of individual engines, or differences between engines. The API offered through QOrganizerManager allows clients to retrieve this information for the engine which provides the functionality exposed through a particular QOrganizerManager. \section2 Storage Considerations A QOrganizerManagerEngine may provide an aggregated view of multiple physical datastores, some of which may be remote datastores. Clients of the API are aware only that the data is managed by a QOrganizerManagerEngine with a particular URI. It is possible that multiple different engines will have overlap in the datastores which they aggregate, and in that case the way in which those engines were implemented will determine whether operations are thread-safe or not. Since the data may physically be stored in a remote datastore, any operations may be dominated by the return-trip-time of communications with the remote datastore. As such, \b{it is recommended that clients use the asynchronous client API to access organizer information from any QOrganizerManager.} \section2 Provided Engines The Qt Organizer module includes several backends already, some of which are designed to interface with the default calendar on their particular platform. \section3 In-Memory Example Engine The in-memory engine identifies itself as the \c memory engine. The in-memory engine supports the default schema, and provides almost all functionality available through the Qt Organizer API; however, all data is stored in-memory and is not persisted in any way. \section1 Manager Settings and Configuration Users of the items API can define which backend they wish to access if a manager for that backend is available. The list of available managers can be queried programmatically at run-time, and the capabilities of different managers can be ascertained by inspecting a QOrganizerManager instance. Furthermore, some managers can be constructed with parameters which affect the operation of the backend. \section2 Querying a Manager for Capabilities Different managers will support different capabilities and details. Clients can use the meta data reporting functions of QOrganizerManager to determine what the capabilities of the manager they have instantiated might be. \section2 Loading the Manager for a Specific Backend The client can choose to load a manager for a specific backend. While the engine could be found and retrieved using a more advanced plugin framework (such as the Qt Service Framework), this code assumes that the client has prior knowledge of the backend in question: \code QOrganizerManager specificManager("memory"); \endcode Clients may wish to use this feature of the API if they wish to store or retrieve item information to a particular manager (for example, one that interfaces with a particular online service). \section2 Loading a Manager with Specific Parameters The client can load a manager with specific parameters defined. The parameters which are available are backend specific, and so the client has to know which parameters are valid for a particular backend, and what argument it takes. \section2 Meta Data API The QOrganizerManager class provides a static function QOrganizerManager::availableManagers() which allows clients of the API to determine (at run time) which plugins (managers) are available for use. Clients of the API also need to be able to determine (at run time) what the capabilities of a given plugin (organizer item manager) are. The QOrganizerManager class provides API to query the capabilities of a given manager with the following synchronous functions: \list \li \l{QOrganizerManager::supportedFilters()}{supportedFilters()} \li \l{QOrganizerManager::supportedItemDetails()}{supportedItemDetails()} \li \l{QOrganizerManager::supportedItemTypes()}{supportedItemTypes()} \endlist A given manager is identified by its URI. The URI consists of the manager's name, any relevant parameters which were used during instantiation of the manager, and the version of the manager. While the name of the manager identifies the plugin which provides the functionality, you cannot guarantee that the data available through one manager will be available through another with the same name (for example, if one parameter tells the plugin to store and retrieve organizer information from a particular online service or local file). The synchronous API offered to allow run-time querying of a manager's metadata includes: \list \li \l{QOrganizerManager::managerName()}{managerName} \li \l{QOrganizerManager::managerParameters()}{managerParameters} \li \l{QOrganizerManager::managerUri()}{managerUri} \li \l{QOrganizerManager::parseUri()}{parseUri} \li \l{QOrganizerManager::buildUri()}{buildUri} \endlist The functionality that the above functions provide is only available through synchronous API. */ src/organizer/doc/src/organizerasync.qdoc000066400000000000000000000113521233466112000211070ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page organizerasync.html \title Qt Organizer Asynchronous API \tableofcontents The Qt Organizer Asynchronous API enables a client to asynchronously fetch, update, or remove calendar, scheduling and personal data from an organizer item manager. Use of the asynchronous API offers the programmer greater flexibility when requesting information from remote or slow local datastores. The asynchronous API is available through classes derived from the QOrganizerAbstractRequest class that enable clients to: \list \li Manipulate organizer items \li Manipulate item collections \endlist \section1 Manipulating Organizer Items The most common type of operation that clients will perform involves retrieval or modification of organizer items. For in-depth information about item manipulation, see \l{Qt Organizer C++ API}. The asynchronous API supports the following operations: \list \li Fetch item ids \li Fetch persistent items (for export) \li Fetch items (including generated occurrences) in a time period \li Fetch the occurrences of a specific item \li Save items (create or update) \li Remove items \endlist These operations are supported via the following classes: \list \li QOrganizerItemIdFetchRequest \li QOrganizerItemFetchForExportRequest \li QOrganizerItemFetchRequest \li QOrganizerItemOccurrenceFetchRequest \li QOrganizerItemSaveRequest \li QOrganizerItemRemoveRequest \endlist \section1 Manipulating Item Collections Each item is saved in a collection in a manager. Each collection has various properties which, if the manager supports such operations, may be modified by clients. For in-depth information about collections, see \l{Qt Organizer C++ API}. The asynchronous API supports the following operations: \list \li Fetch collections (that is, the object which defines the properties of a collection) \li Save collections (create or update) \li Remove collections \endlist These operations are supported via the following classes: \list \li QOrganizerCollectionFetchRequest \li QOrganizerCollectionSaveRequest \li QOrganizerCollectionRemoveRequest \endlist \section1 Performing Asynchronous Operations All asynchronous operations are performed in a similar way: \list \li A request of the desired type (which is derived from QOrganizerAbstractRequest) is created \li Certain criteria are set which determine the intent of the request \li QOrganizerAbstractRequest::stateChanged() signal of the request is connected to a slot which deals with the results. \li The request is started. \endlist \note To receive the results of the request as they become available, rather than only the final set of results once the request changes state (to \c FinishedState, for example), the client should instead connect the QOrganizerAbstractRequest::resultsAvailable() signal to the slot which deals with the results. \section2 Reporting Errors When a asynchronous operation fails, clients need to be able to retrieve error information associated with that asynchronous operation. It is possible to retrieve this error information by calling the QOrganizerAbstractRequest::error() function on the corresponding request. For some asynchronous operations (for example, batch save or remove operations) it is possible that multiple errors may occur during the operation. In those cases, clients can call the errorMap() function on the request object to retrieve a map of input index to error. See, for instance, QOrganizerItemSaveRequest::errorMap(). */ src/organizer/doc/src/organizerclasses.qdoc000066400000000000000000000104431233466112000214270ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \module QtOrganizer \title Qt Organizer C++ Classes \ingroup modules \brief The QtOrganizer module allows access to calendar event information. The \l{Qt Organizer} API enables a client to request calendar, schedule and personal data from local or remote backends. To include the definitions of the module's classes, use the following directive: \snippet doc_src_qtorganizer.cpp include To link against the module, add this line to your \l qmake \c .pro file: \snippet doc_src_qtorganizer.pro organizer project modification */ /*! \title Qt Organizer C++ API \page organizerclasses.html The Organizer API allows access to calendar event information. For more details, see the \l {Qt Organizer Overview}. The following are the classes involved in this API. The \l{Qt Organizer C++ Classes} page has a list of all classes in Qt Organizer. \section2 Main Classes \annotatedlist organizer-main \section2 QOrganizerItemDetail Leaf Classes Several subclasses of \l{QOrganizerItemDetail} are provided as part of the Organizer API. They are general in design but are intended to fulfill specific use-cases. Please note that certain backends may choose not to support one or more of these subclasses as they appear here; they may offer their own which provide similar functionality. \annotatedlist organizer-details \section2 Asynchronous Requests You may use either the \l{Qt Organizer Synchronous API}{synchronous} or \l{Qt Organizer Asynchronous API}{asynchronous} API to access functionality provided by a manager backend. The asynchronous API is offered through subclasses of the \l{QOrganizerAbstractRequest} class: \annotatedlist organizer-requests \section2 Organizer Item Selection And Sorting You may select an organizer item by specifying a unique item id, or by supplying a \l{QOrganizerItemFilter} which matches the item or items they wish to select. The various derivatives of \l{QOrganizerItemFilter} allow for fine-grained and flexible selection of organizer data according to various criteria: \annotatedlist organizer-filters A client can also request that the results of such a selection be sorted, by passing a \l{QOrganizerItemSortOrder} (or list of sort orders) to the manager. \section2 Implementing Backends A backend implementor must implement the following interfaces: \annotatedlist organizer-backends For more information on this topic, see please see the documentation on \l{Qt Organizer Manager Engines}{implementing manager engines}. \section2 Synchronization and Serialization The organizer API is used by the \l {Qt Versit C++ API}* module. It allows serialization of a QOrganizerItem into an iCalendar document, and vice versa. [*] Versit \reg is a trademark of the Internet Mail Consortium. \section1 QML Types Qt Organizer includes QML types that provide organizer functionality for QML applications. The \l{Qt Organizer QML API} page contains more details about the QML support. For a list of QML types, the \l{Qt Organizer QML Types} page lists the types in Qt Organizer */ src/organizer/doc/src/organizerengines.qdoc000066400000000000000000000262031233466112000214230ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page organizerengines.html \title Qt Organizer Manager Engines \tableofcontents The QOrganizerManager interface provided to clients to allow access to organizer information depends on an implementation of QOrganizerManagerEngine existing. This engine provides the methods which are called by the manager. An engine is identified by its URI, which is the name reported to clients through the QOrganizerManager::managerUri() function. The URI of a manager is built by combining its name, version and relevant construction parameters. \section1 Implementing Organizer Manager Engines Some developers may wish to provide implementations of QOrganizerManagerEngine for use by clients. The engine that they provide may aggregate multiple datastores, or access a remote datastore, or provide some other functionality to clients. An engine is distributed as a Qt Plugin, and will be detected automatically by the plugin loading code in the QOrganizerManager, so long as the plugin is located in the correct path ($QT_PLUGINS_DIR/organizer/). \section2 Manager Engine The functionality exposed by the QOrganizerManager class may be implemented by \l{QOrganizerManagerEngine}{engine} plugins which interface directly to a platform-specific backend or provide their own data storage backend. As such, the terms \e manager, \e plugin, and \e backend are used interchangeably in this documentation to refer to any engine plugin which implements the functionality exposed by the QOrganizerManager interface. The plugin architecture allows dynamic loading of different manager engines at runtime. A manager backend may be implemented by subclassing \l{QOrganizerManagerEngine}, and providing a \l{QOrganizerManagerEngineFactory} which can instantiate it when required. \section2 Engine-Specific Ids Each engine interfaces with a particular datastore, and that datastore may have its own particular way of identifying items stored in it. The Qt Organizer API allows engine implementers to define their own id format. Engine implementers must implement their own id classes derived from \l QOrganizerItemEngineId and \l QOrganizerCollectionEngineId respectively. For an example of how to implement these classes, see the \e skeleton example plugin. \section2 Which Functions Do I Need To Implement Different engines provide different functionality and support different features. Depending on the feature set of the engine, it will need to implement a particular subset of the API. The default implementation for most functions will set the error to \c QOrganizerManager::NotSupportedError and return the value which indicates that an error has occurred. \section3 Mandatory Functions All engines must implement the following functions: \list \li QOrganizerManagerEngine::managerName() \li QOrganizerManagerEngine::managerVersion() \li QOrganizerManagerEngine::supportedFilters() \li QOrganizerManagerEngine::supportedItemDetails() \li QOrganizerManagerEngine::supportedItemTypes() \li QOrganizerManagerEngine::itemIds() \li QOrganizerManagerEngine::items() \li QOrganizerManagerEngine::itemsForExport() \li QOrganizerManagerEngine::defaultCollection() \li QOrganizerManagerEngine::collections() \endlist Every engine implementation must also come with an implementation of QOrganizerManagerEngineFactory for that engine. Note that you do not need to implement filtering and sorting natively in an engine; the default implementation offers the following static functions to perform filtering and sorting respectively, in memory: \list \li QOrganizerManagerEngine::testFilter() \li QOrganizerManagerEngine::sortItems() \endlist However, engine implementors should be aware that the default implementation is naive and will have greatly reduced performance compared to a native implementation (e.g., SQL queries, if the calendar or personal data exposed by the engine implementation is stored in an SQL database). Similarly, any QOrganizerItemFetchHint parameter may be ignored by an engine implementation, but if it does so it must return all information available for the item. All engines must also implement the following functions to implement asynchronous requests: \list \li QOrganizerManagerEngine::requestDestroyed() \li QOrganizerManagerEngine::startRequest() \li QOrganizerManagerEngine::cancelRequest() \li QOrganizerManagerEngine::waitForRequestFinished() \endlist If the engine does not support asynchronous requests, it should always return false in the last three of those functions, and do nothing in the first. If the engine does support asynchronous requests, it must ensure that all information required to perform the request is saved in the engine within QOrganizerManagerEngine::startRequest(), as the client owns the request object and may delete it at any time. In general, engine implementors should be aware of this ownership semantic, and never attempt an unsafe operation on a request pointer. It is recommended that all engine implementations support asynchronous requests, even if they use a "dummy" implementation which services the request synchronously during startRequest, and then emit the appropriate signals from the request via a zero-millisecond timeout timer. \section3 Optional Functionality The rest of the virtual functions are optional, and should be implemented only if the engine supports the operations. If the engine can be constructed with different parameters, which affects the operation of the engine (for example, a parameter might define which file to read schedule or calendar information from, or it might be an access token to prove that the client has the access rights to read organizer information from the engine, etc), it must report which parameters it was constructed with via the \list \li QOrganizerManagerEngine::managerParameters() \endlist function. If the engine supports native filtering of any kind, it must report to clients which filters are supported natively by implementing: \list \li QOrganizerManagerEngine::supportedFilters() \endlist If the engine supports saving or removing organizer item information, as well as retrieval, it must implement: \list \li QOrganizerManagerEngine::saveItems() \li QOrganizerManagerEngine::removeItems() \endlist It may also choose to implement the "single item" functions: \list \li QOrganizerManagerEngine::saveItem() \li QOrganizerManagerEngine::removeItem() \endlist If it does not, the default implementation of those functions will use the batch (plural) versions of those functions to implement the required behavior. If the engine supports addition, modification and removal of collections, it must implement: \list \li QOrganizerManagerEngine::saveCollection() \li QOrganizerManagerEngine::removeCollection() \endlist \section2 Which Signals Do I Need To Emit An engine implementation must emit the appropriate signals for the subset of functionality that it supports. If the engine supports reading or saving items, it must emit the: \list \li QOrganizerManagerEngine::itemsAdded() \li QOrganizerManagerEngine::itemsChanged() \li QOrganizerManagerEngine::itemsRemoved() \endlist signals as appropriate. Alternatively, it can emit the QOrganizerManager::dataChanged() signal instead. Similarly, if the engine supports reading or saving collections, it must emit the: \list \li QOrganizerManagerEngine::collectionsAdded() \li QOrganizerManagerEngine::collectionsChanged() \li QOrganizerManagerEngine::collectionsRemoved() \endlist signals as appropriate. Alternatively, it can emit the QOrganizerManager::dataChanged() signal instead. Note that the collectionsChanged() signal should be emitted if the meta data of a collection is updated, not if the client saves an item in the collection. That is, the collection-related signals are for collection meta-data, not the contents of the collection. \section2 Other Considerations There are several other considerations that engine writers must be aware of: \list \li Most batch functions take an error map as a parameter. This parameter cannot be null as it exists in the private implementation of QOrganizerManager, so engines need not check the pointer before attempting to dereference it. \li Every function takes a mandatory \c QOrganizerManager::Error pointer argument. This argument is also never null, since it exists in the private implementation of QOrganizerManager. Testing this argument for null is, therefore, superfluous. \li The single-item functions for item retrieval, removal and save already have a default implementation which merely wraps the batch retrieval, removal or save function appropriately. This default implementation may not be as performant as a hand-rolled function. Engine implementations MUST implement the batch functions for each area of functionality supported by the engine. \li Most clients will prefer to use the asynchronous API to access information from the engine. It is therefore suggested that asynchronous requests be serviced, even if it is implemented in a similar manner to the (provided) memory engine's naive implementation. \endlist \section2 Example Implementation There are several implementations of QOrganizerManagerEngine available in the QtPim source code repository. In particular, the "memory" engine provides an implementation of an in-memory, anonymous datastore which supports almost every feature in the API, and therefore is useful for demonstration purposes. Be aware, however, that the implementation of all functionality in the "memory" engine is naive and not performant, and should not be copied in any real engine implementation (e.g., to perform filtering, it reads all items from the (in-memory) database, and checks one by one for matches; a real engine, on the other hand, might perform a database query to return the results directly, rather than performing n-reads). The "skeleton" engine provides a useful template for engine implementors, and it is suggested that it is used as a starting point for anyone who wishes to implement a QOrganizerManagerEngine. */ src/organizer/doc/src/organizersync.qdoc000066400000000000000000000115471233466112000207540ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page organizersync.html \title Qt Organizer Synchronous API \tableofcontents The Qt Organizer Synchronous API provides the simplest way to access or modify the organizer item information managed by a particular backend. It has the disadvantage that calls block the current thread of execution until completion and is therefore most suitable only for applications which interact with local, high-speed datastores, or for applications which do not require a responsive user interface. Most operations which may be performed using the synchronous API may also be performed using the asynchronous API. It is recommended for most applications that the asynchronous API be used where possible. The synchronous API offered by the Organizer Items module is available through the QOrganizerManager class. It has the following main use cases: \list \li Manipulating Organizer Items \li Manipulating Item Collections \endlist \section1 Manipulating Organizer Items The most common type of operation that clients perform involves retrieval or modification of organizer items. For in-depth information about item manipulation, see \l{Qt Organizer C++ API}. The QOrganizerManager class provides API for accessing the IDs of items which are stored in the manager: \list \li QOrganizerManager::itemIds() \endlist The synchronous, singular item manipulation functions offered by the QOrganizerManager class are: \list \li QOrganizerManager::item() \li QOrganizerManager::saveItem() \li QOrganizerManager::removeItem() \endlist The synchronous, batch item manipulation functions offered by the QOrganizerManager class are: \list \li QOrganizerManager::items() which returns persistent and generated items within a given time period \li QOrganizerManager::itemsForExport() which returns only persistent items \li QOrganizerManager::itemOccurrences() which returns the occurrences of a specific recurring item \li QOrganizerManager::saveItems() which may be used to update a recurring item, or save an exceptional occurrence \li QOrganizerManager::removeItems() which may be used to remove a recurring item or exceptional occurrence \endlist \section1 Manipulating Item Collections Every item is saved in a collection in a manager. Each collection has various properties which, if the manager supports such operations, may be modified by clients. For in-depth information about collections, see \l{Qt Organizer C++ API}. The synchronous API offers the following functions to manipulate collections: \list \li QOrganizerManager::defaultCollection() returns the default collection of the manager \li QOrganizerManager::collections() returns all collections in the manager \li QOrganizerManager::saveCollection() updates an existing collection or adds a new collection \li QOrganizerManager::removeCollection() removes an existing collection (and deletes any items it contains) \endlist It also offers a convenience function which returns a collection with a given collection id. \section1 Reporting Errors When a synchronous operation fails, clients need to be able to retrieve error information associated with that synchronous operation. You can call the QOrganizerManager::error() function directly after a failing synchronous operation to determine why it failed. For some synchronous operations (for example, batch save or remove operations) it is possible that multiple errors may occur during the operation. In those cases, clients can call QOrganizerManager::errorMap() to retrieve a map of input index to error, which will be filled by the function as required. The QOrganizerManager::error() function will report the overall operation error. */ src/organizer/doc/src/plugins/000077500000000000000000000000001233466112000166605ustar00rootroot00000000000000src/organizer/doc/src/plugins/qml-organizer.qdoc000066400000000000000000000120671233466112000223250ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \qmlmodule QtOrganizer 5.0 \title Qt Organizer QML Types \ingroup qmlmodules \brief Provides QML types for handling calendar, schedule, and personal data The \l{Qt Organizer} module enables a client to request calendar, schedule and personal data from local or remote backends. These QML types deliver these capabilities in an easy to use form. The \l{Qt Organizer QML API} page provides more information about the API. To use the types, simply add the following to the QML file: \snippet moduleimports.qml Organizer import */ /*! \page qml-organizer.html \title Qt Organizer QML API \brief A QML plugin for the Organizer API \section1 Overview To be able to use this component the QML \e {import} statement needs to reference the module before it can used. Simply add the following to the QML file: \snippet moduleimports.qml Organizer import The Organizer API enables a client to request calendar, schedule and personal data from local or remote backends. The QML Organizer API delivers these capabilities in an easy to use form. The following describes the API. You can also study the \l{Qt Personal Information Management Examples}{Examples} or try the \l{Qt Personal Information Management Tutorials}{Tutorials} to help get started quickly. \section2 Organizer Items The \l {OrganizerItem} type provides a generic interface for accessing events, todos, journals and notes. To access specific fields of an item, the \l {Event}, \l {Todo}, \l {Journal} and \l {Note} QML types provide convenience. Additionally, \l {EventOccurrence} and \l {TodoOccurrence} can be used for manipulating occurrences of events or todos. \section2 Recurring Items A recurring item is an item that occurs more than once; for example, a meeting that occurs every week for the next 10 weeks. A recurring item is created by creating an Event or Todo and setting a \l {RecurrenceRule} on it to specify the rules for when it should recur. See \l {Recurrence} and \l {RecurrenceRule} for detail references. \section2 Collections Every item stored in an organizer store belongs to exactly one collection. A collection can have properties such as a name, a "color", a specified icon, a description, and so on. Collections may be added, modified, or removed if the backend store supports those operations. There will always be at least one (default) collection in an organizer manager, into which items are saved if no other collection is specified. \section2 Organizer Item Details Several sub types of \l {Detail} are provided as part of the API. They are general in design but are intended to fulfill specific use-cases. Please note that certain backends may choose not to support one or more of these QML types as they appear here. \section2 Organizer Filters The Organizer QML plugin supplies filters to search for organizer items with particular values for various properties in the organizer item, such as IDs, collections, and details, etc. \section1 Reference documentation \section2 Main Classes \annotatedlist qml-organizer-main \section2 Item Classes \annotatedlist qml-organizer-items \section2 Detail Leaf Classes \annotatedlist qml-organizer-details \section2 Item Selection And Sorting \annotatedlist qml-organizer-filters \section2 Examples The following sample applications show examples of API usage: \list \li \l{qmlorganizerlistview}{Qt Quick Organizer List view} \endlist \section2 Tutorials The following tutorials are useful to work through the use of the Qt Quick Organizer API:: \list \li \l{Organizer Qt Quick ListView Tutorial}{Qt Quick Organizer List view Tutorial} \endlist */ src/organizer/doc/src/tutorials/000077500000000000000000000000001233466112000172255ustar00rootroot00000000000000src/organizer/doc/src/tutorials/qmlorganizerlistviewtutorial.qdoc000066400000000000000000000076541233466112000261760ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page tutorials-qmlorganizerListView.html \contentspage Organizer Qt Quick ListView Tutorial \startpage Organizer Qt Quick ListView Tutorial \title Organizer Qt Quick ListView Tutorial \brief An introduction to the Organizer Qt Quick API, showing how to develop a simple appplication using ListView and the Organizer Model to show events. In this tutorial, you will learn about the basic components of the Organizer API, including \list \li Using the Organizer Model and the Qt Quick ListView to display Events \li Delete Events stored in your supported calendar backend \li Add your own new Events stored in your supported calendar backend \li Edit existing title, start and end dates for your events retrieved. \endlist We will be developing the simple Qt Quick Events ListView application shown below: \image qmlorganizerlistview-main.png Tutorial contents: \list 1 \li \l{Part 1 - The Organizer Model and using listViews} \li \l{Part 2 - Adding New Events} \li \l{Part 3 - Deleting Events} \li \l{Part 4 - Editing selected Events} \li \l{Part 5 - Future steps} \endlist */ /*! \page tutorials-qmlorganizerListView-part1.html \previouspage {Organizer Qt Quick ListView Tutorial} \contentspage {Organizer Qt Quick ListView Tutorial} {Contents} \nextpage Part 2 - Adding New Events \startpage Organizer Qt Quick ListView Tutorial \title Part 1 - The Organizer Model and using ListViews */ /*! \page tutorials-qmlorganizerListView-part2.html \previouspage Part 1 - The Organizer Model and using ListViews \contentspage {Organizer Qt Quick ListView Tutorial} {Contents} \nextpage Part 3 - Deleting Events \startpage Organizer Qt Quick ListView Tutorial \title Part 2 - Adding New Events */ /*! \page tutorials-qmlorganizerListView-part3.html \previouspage Part 2 - Adding New Events \contentspage {Organizer Qt Quick ListView Tutorial} {Contents} \nextpage Part 4 - Editing selected Events \startpage Organizer Qt Quick ListView Tutorial \title Part 3 - Deleting Events */ /*! \page tutorials-qmlorganizerListView-part4.html \previouspage Part 3 - Deleting Events \contentspage {Organizer Qt Quick ListView Tutorial} {Contents} \nextpage Part 5 - Future Steps \startpage Organizer Qt Quick ListView Tutorial \title Part 4 - Editing selected Events */ /*! \page tutorials-qmlorganizerListView-part5.html \previouspage Part 4 - Editing selected Events \contentspage {Organizer Qt Quick ListView Tutorial} {Contents} \startpage Organizer Qt Quick ListView Tutorial \title Part 5 - Future Steps */ src/organizer/filters/000077500000000000000000000000001233466112000153135ustar00rootroot00000000000000src/organizer/filters/filters.pri000066400000000000000000000022431233466112000175000ustar00rootroot00000000000000INCLUDEPATH += filters \ ./ PUBLIC_HEADERS += \ filters/qorganizeritemfilters.h \ filters/qorganizeritemdetailfilter.h \ filters/qorganizeritemdetailfieldfilter.h \ filters/qorganizeritemdetailrangefilter.h \ filters/qorganizeritemintersectionfilter.h \ filters/qorganizeriteminvalidfilter.h \ filters/qorganizeritemidfilter.h \ filters/qorganizeritemcollectionfilter.h \ filters/qorganizeritemunionfilter.h PRIVATE_HEADERS += \ filters/qorganizeritemdetailfilter_p.h \ filters/qorganizeritemdetailfieldfilter_p.h \ filters/qorganizeritemdetailrangefilter_p.h \ filters/qorganizeritemintersectionfilter_p.h \ filters/qorganizeritemidfilter_p.h \ filters/qorganizeritemcollectionfilter_p.h \ filters/qorganizeritemunionfilter_p.h SOURCES += \ filters/qorganizeritemdetailfilter.cpp \ filters/qorganizeritemdetailfieldfilter.cpp \ filters/qorganizeritemdetailrangefilter.cpp \ filters/qorganizeritemintersectionfilter.cpp \ filters/qorganizeriteminvalidfilter.cpp \ filters/qorganizeritemidfilter.cpp \ filters/qorganizeritemcollectionfilter.cpp \ filters/qorganizeritemunionfilter.cpp src/organizer/filters/qorganizeritemcollectionfilter.cpp000066400000000000000000000072031233466112000243430ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemcollectionfilter.h" #include "qorganizeritemcollectionfilter_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemCollectionFilter \brief The QOrganizerItemCollectionFilter class provides a filter based around the collection one organizer item belongs to. \inmodule QtOrganizer \ingroup organizeritems-filters It may be used to select organizer items belonging to certain collections. */ Q_IMPLEMENT_ORGANIZERITEMFILTER_PRIVATE(QOrganizerItemCollectionFilter) /*! \fn QOrganizerItemCollectionFilter::QOrganizerItemCollectionFilter(const QOrganizerItemFilter &other) Constructs a copy of \a other if possible, otherwise constructs a new organizeritem collection filter. */ /*! Constructs a new organizeritem collection filter. */ QOrganizerItemCollectionFilter::QOrganizerItemCollectionFilter() : QOrganizerItemFilter(new QOrganizerItemCollectionFilterPrivate) { } /*! Sets the \a id of the collection, which the organizer items should belong to. */ void QOrganizerItemCollectionFilter::setCollectionId(const QOrganizerCollectionId &id) { Q_D(QOrganizerItemCollectionFilter); d->m_ids.clear(); d->m_ids.insert(id); } /*! Sets the list of collection \a ids, which the organizer items should belong to. */ void QOrganizerItemCollectionFilter::setCollectionIds(const QSet &ids) { Q_D(QOrganizerItemCollectionFilter); d->m_ids = ids; } /*! Returns the list of collection IDs of organizeritems should belong to. */ QSet QOrganizerItemCollectionFilter::collectionIds() const { Q_D(const QOrganizerItemCollectionFilter); return d->m_ids; } QT_END_NAMESPACE_ORGANIZER src/organizer/filters/qorganizeritemcollectionfilter.h000066400000000000000000000054051233466112000240120ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMCOLLECTIONFILTER_H #define QORGANIZERITEMCOLLECTIONFILTER_H #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemCollectionFilterPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemCollectionFilter : public QOrganizerItemFilter { public: QOrganizerItemCollectionFilter(); QOrganizerItemCollectionFilter(const QOrganizerItemFilter &other); void setCollectionId(const QOrganizerCollectionId &id); void setCollectionIds(const QSet &ids); QSet collectionIds() const; private: Q_DECLARE_ORGANIZERITEMFILTER_PRIVATE(QOrganizerItemCollectionFilter) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMCOLLECTIONFILTER_H src/organizer/filters/qorganizeritemcollectionfilter_p.h000066400000000000000000000076701233466112000243370ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMCOLLECTIONFILTER_P_H #define QORGANIZERITEMCOLLECTIONFILTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemCollectionFilterPrivate : public QOrganizerItemFilterPrivate { public: QOrganizerItemCollectionFilterPrivate() : QOrganizerItemFilterPrivate() { } QOrganizerItemCollectionFilterPrivate(const QOrganizerItemCollectionFilterPrivate &other) : QOrganizerItemFilterPrivate(other), m_ids(other.m_ids) { } virtual bool compare(const QOrganizerItemFilterPrivate *other) const { const QOrganizerItemCollectionFilterPrivate *od = static_cast(other); if (od) return m_ids == od->m_ids; return false; } #ifndef QT_NO_DATASTREAM QDataStream &outputToStream(QDataStream &stream, quint8 formatVersion) const { if (formatVersion == 1) stream << m_ids; return stream; } QDataStream &inputFromStream(QDataStream &stream, quint8 formatVersion) { if (formatVersion == 1) stream >> m_ids; return stream; } #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM QDebug &debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerItemCollectionFilter(collectionIds="; dbg.nospace() << m_ids; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif // QT_NO_DEBUG_STREAM Q_IMPLEMENT_ORGANIZERITEMFILTER_VIRTUALCTORS(QOrganizerItemCollectionFilter, QOrganizerItemFilter::CollectionFilter) QSet m_ids; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMCOLLECTIONFILTER_P_H src/organizer/filters/qorganizeritemdetailfieldfilter.cpp000066400000000000000000000122031233466112000244520ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemdetailfieldfilter.h" #include "qorganizeritemdetailfieldfilter_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemDetailFieldFilter \brief The QOrganizerItemDetailFieldFilter class provides a filter based around a detail value criterion. \inmodule QtOrganizer \ingroup organizer-filters It may be used to select organizeritems which contain a detail of a particular type and a particular value. */ Q_IMPLEMENT_ORGANIZERITEMFILTER_PRIVATE(QOrganizerItemDetailFieldFilter) /*! \fn QOrganizerItemDetailFieldFilter::QOrganizerItemDetailFieldFilter(const QOrganizerItemFilter &other) Constructs a copy of \a other if possible, otherwise constructs a new detail filter. */ /*! Constructs a new detail filter. */ QOrganizerItemDetailFieldFilter::QOrganizerItemDetailFieldFilter() : QOrganizerItemFilter(new QOrganizerItemDetailFieldFilterPrivate) { } /*! Sets the type of detail which will be matched to \a detailType, and the field of the detail which will contain the value criterion to \a field. If \a detailType is QOrganizerItemDetail::TypeUndefined, the detail filter will match no organizer items. If \a field is not specified, or equal to -1, the detail filter acts like a "detail exists" filter; if any detail of the specified type is present in the organizer item, that organizer item will match the filter, regardless of what values might be stored in that detail. \sa detailType(), detailField() */ void QOrganizerItemDetailFieldFilter::setDetail(QOrganizerItemDetail::DetailType detailType, int field) { Q_D(QOrganizerItemDetailFieldFilter); d->m_detailType = detailType; d->m_detailField = field; } /*! Sets the value criterion of the filter to \a value. \sa value() */ void QOrganizerItemDetailFieldFilter::setValue(const QVariant &value) { Q_D(QOrganizerItemDetailFieldFilter); d->m_exactValue = value; } /*! Sets the semantics of the value matching criterion to those defined in \a flags. \sa matchFlags() */ void QOrganizerItemDetailFieldFilter::setMatchFlags(QOrganizerItemFilter::MatchFlags flags) { Q_D(QOrganizerItemDetailFieldFilter); d->m_flags = flags; } /*! Returns the semantics of the value matching criterion. \sa setMatchFlags() */ QOrganizerItemFilter::MatchFlags QOrganizerItemDetailFieldFilter::matchFlags() const { Q_D(const QOrganizerItemDetailFieldFilter); return d->m_flags; } /*! Returns the type of the detail which will be inspected for matching values. \sa setDetail() */ QOrganizerItemDetail::DetailType QOrganizerItemDetailFieldFilter::detailType() const { Q_D(const QOrganizerItemDetailFieldFilter); return d->m_detailType; } /*! Returns the detail field containing the value which will be matched against the value criterion. \sa setDetail() */ int QOrganizerItemDetailFieldFilter::detailField() const { Q_D(const QOrganizerItemDetailFieldFilter); return d->m_detailField; } /*! Returns the value criterion of the detail filter. \sa setValue() */ QVariant QOrganizerItemDetailFieldFilter::value() const { Q_D(const QOrganizerItemDetailFieldFilter); return d->m_exactValue; } QT_END_NAMESPACE_ORGANIZER src/organizer/filters/qorganizeritemdetailfieldfilter.h000066400000000000000000000056411233466112000241270ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMDETAILFIELDFILTER_H #define QORGANIZERITEMDETAILFIELDFILTER_H #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemDetailFieldFilterPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemDetailFieldFilter : public QOrganizerItemFilter { public: QOrganizerItemDetailFieldFilter(); QOrganizerItemDetailFieldFilter(const QOrganizerItemFilter &other); void setDetail(QOrganizerItemDetail::DetailType detailType, int field = -1); QOrganizerItemDetail::DetailType detailType() const; int detailField() const; void setMatchFlags(QOrganizerItemFilter::MatchFlags flags); QOrganizerItemFilter::MatchFlags matchFlags() const; void setValue(const QVariant &value); QVariant value() const; private: Q_DECLARE_ORGANIZERITEMFILTER_PRIVATE(QOrganizerItemDetailFieldFilter) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMDETAILFIELDFILTER_H src/organizer/filters/qorganizeritemdetailfieldfilter_p.h000066400000000000000000000121311233466112000244360ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMDETAILFIELDFILTER_P_H #define QORGANIZERITEMDETAILFIELDFILTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemDetailFieldFilterPrivate : public QOrganizerItemFilterPrivate { public: QOrganizerItemDetailFieldFilterPrivate() : QOrganizerItemFilterPrivate(), m_detailType(QOrganizerItemDetail::TypeUndefined), m_detailField(-1), m_flags(0) { } QOrganizerItemDetailFieldFilterPrivate(const QOrganizerItemDetailFieldFilterPrivate& other) : QOrganizerItemFilterPrivate(other), m_detailType(other.m_detailType), m_detailField(other.m_detailField), m_exactValue(other.m_exactValue), m_flags(other.m_flags) { } virtual bool compare(const QOrganizerItemFilterPrivate *other) const { const QOrganizerItemDetailFieldFilterPrivate *od = static_cast(other); if (od) { return (m_detailType == od->m_detailType) && (m_detailField == od->m_detailField) && (m_exactValue == od->m_exactValue) && (m_flags == od->m_flags); } return false; } #ifndef QT_NO_DATASTREAM QDataStream &outputToStream(QDataStream &stream, quint8 formatVersion) const { if (formatVersion == 1) stream << m_detailType << m_detailField << m_exactValue << static_cast(m_flags); return stream; } QDataStream &inputFromStream(QDataStream &stream, quint8 formatVersion) { if (formatVersion == 1) { quint32 flags; quint32 defId; stream >> defId >> m_detailField >> m_exactValue >> flags; m_detailType = static_cast(defId); m_flags = static_cast(flags); } return stream; } #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM QDebug &debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerItemDetailFieldFilter("; dbg.nospace() << "detailType="; dbg.nospace() << m_detailType; dbg.nospace() << ","; dbg.nospace() << "detailField="; dbg.nospace() << m_detailField; dbg.nospace() << ","; dbg.nospace() << "value="; dbg.nospace() << m_exactValue; dbg.nospace() << ","; dbg.nospace() << "matchFlags="; dbg.nospace() << static_cast(m_flags); dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif // QT_NO_DEBUG_STREAM Q_IMPLEMENT_ORGANIZERITEMFILTER_VIRTUALCTORS(QOrganizerItemDetailFieldFilter, QOrganizerItemFilter::DetailFieldFilter) QOrganizerItemDetail::DetailType m_detailType; int m_detailField; QVariant m_exactValue; QOrganizerItemFilter::MatchFlags m_flags; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMDETAILFIELDFILTER_P_H src/organizer/filters/qorganizeritemdetailfilter.cpp000066400000000000000000000064131233466112000234540ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemdetailfilter.h" #include "qorganizeritemdetailfilter_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemDetailFilter \brief The QOrganizerItemDetailFilter class provides a filter based around a detail value criterion. \inmodule QtOrganizer \ingroup organizer-filters It may be used to select organizeritems which contain a detail identical to that used by this filter for matching. */ Q_IMPLEMENT_ORGANIZERITEMFILTER_PRIVATE(QOrganizerItemDetailFilter) /*! \fn QOrganizerItemDetailFilter::QOrganizerItemDetailFilter(const QOrganizerItemFilter &other) Constructs a copy of \a other if possible, otherwise constructs a new detail filter. */ /*! Constructs a new detail filter. */ QOrganizerItemDetailFilter::QOrganizerItemDetailFilter() : QOrganizerItemFilter(new QOrganizerItemDetailFilterPrivate) { } /*! Sets the detail that will be matched to \a detail. \sa detail() */ void QOrganizerItemDetailFilter::setDetail(const QOrganizerItemDetail &detail) { Q_D(QOrganizerItemDetailFilter); d->m_detail = detail; } /*! Returns the detail which will be matched by this filter. \sa setDetail() */ QOrganizerItemDetail QOrganizerItemDetailFilter::detail() const { Q_D(const QOrganizerItemDetailFilter); return d->m_detail; } QT_END_NAMESPACE_ORGANIZER src/organizer/filters/qorganizeritemdetailfilter.h000066400000000000000000000051621233466112000231210ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMDETAILFILTER_H #define QORGANIZERITEMDETAILFILTER_H #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemDetailFilterPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemDetailFilter : public QOrganizerItemFilter { public: QOrganizerItemDetailFilter(); QOrganizerItemDetailFilter(const QOrganizerItemFilter &other); void setDetail(const QOrganizerItemDetail &detail); QOrganizerItemDetail detail() const; private: Q_DECLARE_ORGANIZERITEMFILTER_PRIVATE(QOrganizerItemDetailFilter) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMDETAILFILTER_H src/organizer/filters/qorganizeritemdetailfilter_p.h000066400000000000000000000121431233466112000234350ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMDETAILFILTER_P_H #define QORGANIZERITEMDETAILFILTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemDetailFilterPrivate : public QOrganizerItemFilterPrivate { public: QOrganizerItemDetailFilterPrivate() : QOrganizerItemFilterPrivate(), m_detailType(QOrganizerItemDetail::TypeUndefined), m_detailFields(QList()), m_flags(0) { } QOrganizerItemDetailFilterPrivate(const QOrganizerItemDetailFilterPrivate& other) : QOrganizerItemFilterPrivate(other), m_detailType(other.m_detailType), m_detailFields(other.m_detailFields), m_exactValues(other.m_exactValues), m_flags(other.m_flags) { } virtual bool compare(const QOrganizerItemFilterPrivate *other) const { const QOrganizerItemDetailFilterPrivate *od = static_cast(other); if (od) { return (m_detailType == od->m_detailType) && (m_detailFields == od->m_detailFields) && (m_exactValues == od->m_exactValues) && (m_flags == od->m_flags); } return false; } #ifndef QT_NO_DATASTREAM QDataStream &outputToStream(QDataStream &stream, quint8 formatVersion) const { if (formatVersion == 1) stream << m_detailType << m_detailFields << m_exactValues << static_cast(m_flags); return stream; } QDataStream &inputFromStream(QDataStream &stream, quint8 formatVersion) { if (formatVersion == 1) { quint32 flags; quint32 defId; stream >> defId >> m_detailFields >> m_exactValues >> flags; m_detailType = static_cast(defId); m_flags = static_cast(flags); } return stream; } #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM QDebug &debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerItemDetailFilter("; dbg.nospace() << "detailType="; dbg.nospace() << m_detailType; dbg.nospace() << ","; dbg.nospace() << "detailFields="; dbg.nospace() << m_detailFields; dbg.nospace() << ","; dbg.nospace() << "values="; dbg.nospace() << m_exactValues; dbg.nospace() << ","; dbg.nospace() << "matchFlags="; dbg.nospace() << static_cast(m_flags); dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif // QT_NO_DEBUG_STREAM Q_IMPLEMENT_ORGANIZERITEMFILTER_VIRTUALCTORS(QOrganizerItemDetailFilter, QOrganizerItemFilter::DetailFilter) QOrganizerItemDetail m_detail; QOrganizerItemDetail::DetailType m_detailType; QList m_detailFields; QVariantList m_exactValues; QOrganizerItemFilter::MatchFlags m_flags; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMDETAILFILTER_P_H src/organizer/filters/qorganizeritemdetailrangefilter.cpp000066400000000000000000000154721233466112000244760ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemdetailrangefilter.h" #include "qorganizeritemdetailrangefilter_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemDetailRangeFilter \brief The QOrganizerItemDetailRangeFilter class provides a filter based around a detail value range criterion. \inmodule QtOrganizer \ingroup organizer-filters It may be used to select organizer items which contain a detail of a particular type, whose value falls in a particular range. */ Q_IMPLEMENT_ORGANIZERITEMFILTER_PRIVATE(QOrganizerItemDetailRangeFilter) /*! \fn QOrganizerItemDetailRangeFilter::QOrganizerItemDetailRangeFilter(const QOrganizerItemFilter &other) Constructs a copy of \a other if possible, otherwise constructs a new detail range filter. */ /*! \enum QOrganizerItemDetailRangeFilter::RangeFlag This enumeration describes the semantics of the boundary conditions of the detail range filter. \value IncludeLower \value IncludeUpper \value ExcludeLower \value ExcludeUpper */ /*! Constructs a new detail range filter. */ QOrganizerItemDetailRangeFilter::QOrganizerItemDetailRangeFilter() : QOrganizerItemFilter(new QOrganizerItemDetailRangeFilterPrivate) { } /*! Sets the value range criterion of the filter to within \a min and \a max, with boundary conditions specified in the given \a flags. \sa minValue(), maxValue(), rangeFlags() */ void QOrganizerItemDetailRangeFilter::setRange(const QVariant &min, const QVariant &max, RangeFlags flags) { Q_D(QOrganizerItemDetailRangeFilter); d->m_minValue = min; d->m_maxValue = max; d->m_rangeflags = flags; } /*! Sets the match flags of the filter criterion to \a flags. Not all flags are supported by a range filter. The supported flags include: \list \li QOrganizerItemFilter::MatchExactly \li QOrganizerItemFilter::MatchFixedString \li QOrganizerItemFilter::MatchCaseSensitive \endlist Unsupported flags will be ignored. \sa matchFlags() */ void QOrganizerItemDetailRangeFilter::setMatchFlags(QOrganizerItemFilter::MatchFlags flags) { Q_D(QOrganizerItemDetailRangeFilter); flags &= (QOrganizerItemFilter::MatchExactly | QOrganizerItemFilter::MatchFixedString | QOrganizerItemFilter::MatchCaseSensitive); d->m_flags = flags; } /*! Sets the type of detail which will be matched to \a detailType, and the field of the detail which will contain the value criterion to \a field. \sa detailType(), detailField() */ /*! Sets the type of detail which will be matched to \a detailType, and the field of the detail which will contain the value criterion to \a field. If \a detailType is QOrganizerItemDetail::TypeUndefined, the detail filter will match no organizer items. If \a field is not specified, or equal to -1, the detail filter acts like a "detail exists" filter; if any detail of the specified type is present in the organizer item, that organizer item will match the filter, regardless of what values might be stored in that detail. \sa detailType(), detailField() */ void QOrganizerItemDetailRangeFilter::setDetail(QOrganizerItemDetail::DetailType detailType, int field) { Q_D(QOrganizerItemDetailRangeFilter); d->m_detailType = detailType; d->m_detailField = field; } /*! Returns the match flags of the criterion, which define semantics such as case sensitivity, prefix matching, exact matching, etc. \sa setMatchFlags() */ QOrganizerItemFilter::MatchFlags QOrganizerItemDetailRangeFilter::matchFlags() const { Q_D(const QOrganizerItemDetailRangeFilter); return d->m_flags; } /*! Returns the type of the detail which will be inspected for matching values. \sa setDetail() */ QOrganizerItemDetail::DetailType QOrganizerItemDetailRangeFilter::detailType() const { Q_D(const QOrganizerItemDetailRangeFilter); return d->m_detailType; } /*! Returns the detail field containing the value which will be matched against the value criterion. \sa setDetail() */ int QOrganizerItemDetailRangeFilter::detailField() const { Q_D(const QOrganizerItemDetailRangeFilter); return d->m_detailField; } /*! Returns the lower bound of the value range criterion. \sa setRange() */ QVariant QOrganizerItemDetailRangeFilter::minValue() const { Q_D(const QOrganizerItemDetailRangeFilter); return d->m_minValue; } /*! Returns the upper bound of the value range criterion. \sa setRange() */ QVariant QOrganizerItemDetailRangeFilter::maxValue() const { Q_D(const QOrganizerItemDetailRangeFilter); return d->m_maxValue; } /*! Returns a set of flags which defines the boundary condition semantics of the value range criterion. \sa setRange() */ QOrganizerItemDetailRangeFilter::RangeFlags QOrganizerItemDetailRangeFilter::rangeFlags() const { Q_D(const QOrganizerItemDetailRangeFilter); return d->m_rangeflags; } QT_END_NAMESPACE_ORGANIZER src/organizer/filters/qorganizeritemdetailrangefilter.h000066400000000000000000000064111233466112000241340ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMDETAILRANGEFILTER_H #define QORGANIZERITEMDETAILRANGEFILTER_H #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemDetailRangeFilterPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemDetailRangeFilter : public QOrganizerItemFilter { public: QOrganizerItemDetailRangeFilter(); QOrganizerItemDetailRangeFilter(const QOrganizerItemFilter &other); enum RangeFlag { IncludeLower = 0, IncludeUpper = 1, ExcludeLower = 2, ExcludeUpper = 0 }; Q_DECLARE_FLAGS(RangeFlags, RangeFlag) void setDetail(QOrganizerItemDetail::DetailType detailType, int field = -1); QOrganizerItemDetail::DetailType detailType() const; int detailField() const; void setMatchFlags(QOrganizerItemFilter::MatchFlags flags); QOrganizerItemFilter::MatchFlags matchFlags() const; void setRange(const QVariant &min, const QVariant &max, RangeFlags flags = 0); QVariant minValue() const; QVariant maxValue() const; RangeFlags rangeFlags() const; private: Q_DECLARE_ORGANIZERITEMFILTER_PRIVATE(QOrganizerItemDetailRangeFilter) }; Q_DECLARE_OPERATORS_FOR_FLAGS(QOrganizerItemDetailRangeFilter::RangeFlags) QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMDETAILRANGEFILTER_H src/organizer/filters/qorganizeritemdetailrangefilter_p.h000066400000000000000000000134761233466112000244640ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMDETAILRANGEFILTER_P_H #define QORGANIZERITEMDETAILRANGEFILTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemDetailRangeFilterPrivate : public QOrganizerItemFilterPrivate { public: QOrganizerItemDetailRangeFilterPrivate() : QOrganizerItemFilterPrivate(), m_detailType(QOrganizerItemDetail::TypeUndefined), m_detailField(-1), m_flags(0), m_rangeflags(0) { } QOrganizerItemDetailRangeFilterPrivate(const QOrganizerItemDetailRangeFilterPrivate& other) : QOrganizerItemFilterPrivate(other), m_detailType(other.m_detailType), m_detailField(other.m_detailField), m_minValue(other.m_minValue), m_maxValue(other.m_maxValue), m_flags(other.m_flags), m_rangeflags(other.m_rangeflags) { } virtual bool compare(const QOrganizerItemFilterPrivate *other) const { const QOrganizerItemDetailRangeFilterPrivate *od = static_cast(other); if (od) { return (m_detailType == od->m_detailType) && (m_detailField == od->m_detailField) && (m_minValue == od->m_minValue) && (m_maxValue == od->m_maxValue) && (m_flags == od->m_flags) && (m_rangeflags == od->m_rangeflags); } return false; } #ifndef QT_NO_DATASTREAM QDataStream &outputToStream(QDataStream &stream, quint8 formatVersion) const { if (formatVersion == 1) { stream << m_detailType << m_detailField << m_minValue << m_maxValue << static_cast(m_flags) << static_cast(m_rangeflags); } return stream; } QDataStream &inputFromStream(QDataStream &stream, quint8 formatVersion) { if (formatVersion == 1) { quint32 flags; quint32 rangeFlags; quint32 defId; stream >> defId >> m_detailField >> m_minValue >> m_maxValue >> flags >> rangeFlags; m_detailType = static_cast(defId); m_flags = static_cast(flags); m_rangeflags = static_cast(rangeFlags); } return stream; } #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM QDebug &debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerItemDetailRangeFilter("; dbg.nospace() << "detailType="; dbg.nospace() << m_detailType; dbg.nospace() << ","; dbg.nospace() << "detailField="; dbg.nospace() << m_detailField; dbg.nospace() << ","; dbg.nospace() << "minValue="; dbg.nospace() << m_minValue; dbg.nospace() << ","; dbg.nospace() << "maxValue="; dbg.nospace() << m_maxValue; dbg.nospace() << ","; dbg.nospace() << "matchFlags="; dbg.nospace() << static_cast(m_flags); dbg.nospace() << ","; dbg.nospace() << "rangeFlags="; dbg.nospace() << static_cast(m_rangeflags); dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif // QT_NO_DEBUG_STREAM Q_IMPLEMENT_ORGANIZERITEMFILTER_VIRTUALCTORS(QOrganizerItemDetailRangeFilter, QOrganizerItemFilter::DetailRangeFilter) QOrganizerItemDetail::DetailType m_detailType; int m_detailField; QVariant m_minValue; QVariant m_maxValue; QOrganizerItemFilter::MatchFlags m_flags; QOrganizerItemDetailRangeFilter::RangeFlags m_rangeflags; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMDETAILRANGEFILTER_P_H src/organizer/filters/qorganizeritemfilters.h000066400000000000000000000051361233466112000221220ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMFILTERS_H #define QORGANIZERITEMFILTERS_H // this file includes all of the leaf filter classes // provided by the Qt Organizer API. #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMFILTERS_H src/organizer/filters/qorganizeritemidfilter.cpp000066400000000000000000000101201233466112000225740ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemidfilter.h" #include "qorganizeritemidfilter_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemIdFilter \brief The QOrganizerItemIdFilter class provides a filter based around a list of organizer item IDs. \inmodule QtOrganizer \ingroup organizer-filters It may be used to select organizer items whose IDs are contained in the given list. Note: a QOrganizerItemIdFilter will not be preserved if streamed to a QDataStream. */ Q_IMPLEMENT_ORGANIZERITEMFILTER_PRIVATE(QOrganizerItemIdFilter) /*! \fn QOrganizerItemIdFilter::QOrganizerItemIdFilter(const QOrganizerItemFilter &other) Constructs a copy of \a other if possible, otherwise constructs a new organizer item ID filter. */ /*! Constructs a new organizer item ID filter. */ QOrganizerItemIdFilter::QOrganizerItemIdFilter() : QOrganizerItemFilter(new QOrganizerItemIdFilterPrivate) { } /*! Sets the list which contains the IDs of possible matching organizer items to \a ids. \sa ids() */ void QOrganizerItemIdFilter::setIds(const QList &ids) { Q_D(QOrganizerItemIdFilter); d->m_ids = ids; } /*! Inserts the \a id into the list which contains the IDs of possible matching items. \sa setIds() */ void QOrganizerItemIdFilter::insert(const QOrganizerItemId &id) { Q_D(QOrganizerItemIdFilter); if (!d->m_ids.contains(id)) d->m_ids.append(id); } /*! Removes the id \a id from the list which contains the IDs of possible matching items. \sa clear() */ void QOrganizerItemIdFilter::remove(const QOrganizerItemId &id) { Q_D(QOrganizerItemIdFilter); d->m_ids.removeAll(id); } /*! Clears the list which contains the IDs of possible matching items. Note that an item ID filter with an empty list will match no items. \sa setIds() */ void QOrganizerItemIdFilter::clear() { Q_D(QOrganizerItemIdFilter); d->m_ids.clear(); } /*! Returns the list of IDs of organizer items which match this filter. \sa setIds() */ QList QOrganizerItemIdFilter::ids() const { Q_D(const QOrganizerItemIdFilter); return d->m_ids; } QT_END_NAMESPACE_ORGANIZER src/organizer/filters/qorganizeritemidfilter.h000066400000000000000000000053231233466112000222520ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMIDFILTER_H #define QORGANIZERITEMIDFILTER_H #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemIdFilterPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemIdFilter : public QOrganizerItemFilter { public: QOrganizerItemIdFilter(); QOrganizerItemIdFilter(const QOrganizerItemFilter &other); void setIds(const QList &ids); void insert(const QOrganizerItemId &id); void remove(const QOrganizerItemId &id); void clear(); QList ids() const; private: Q_DECLARE_ORGANIZERITEMFILTER_PRIVATE(QOrganizerItemIdFilter) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMIDFILTER_H src/organizer/filters/qorganizeritemidfilter_p.h000066400000000000000000000075361233466112000226010ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMIDFILTER_P_H #define QORGANIZERITEMIDFILTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemIdFilterPrivate : public QOrganizerItemFilterPrivate { public: QOrganizerItemIdFilterPrivate() : QOrganizerItemFilterPrivate() { } QOrganizerItemIdFilterPrivate(const QOrganizerItemIdFilterPrivate &other) : QOrganizerItemFilterPrivate(other), m_ids(other.m_ids) { } virtual bool compare(const QOrganizerItemFilterPrivate *other) const { const QOrganizerItemIdFilterPrivate *od = static_cast(other); if (od) return m_ids == od->m_ids; return false; } #ifndef QT_NO_DATASTREAM QDataStream &outputToStream(QDataStream &stream, quint8 formatVersion) const { if (formatVersion == 1) stream << m_ids; return stream; } QDataStream &inputFromStream(QDataStream &stream, quint8 formatVersion) { if (formatVersion == 1) stream >> m_ids; return stream; } #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM QDebug &debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerItemIdFilter("; dbg.nospace() << "ids="; dbg.nospace() << m_ids; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif // QT_NO_DEBUG_STREAM Q_IMPLEMENT_ORGANIZERITEMFILTER_VIRTUALCTORS(QOrganizerItemIdFilter, QOrganizerItemFilter::IdFilter) QList m_ids; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMIDFILTER_P_H src/organizer/filters/qorganizeritemintersectionfilter.cpp000066400000000000000000000114621233466112000247200ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemintersectionfilter.h" #include "qorganizeritemintersectionfilter_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemIntersectionFilter \brief The QOrganizerItemIntersectionFilter class provides a filter which intersects the results of other filters. \inmodule QtOrganizer \ingroup organizer-filters It may be used to select organizer items which match all of the filters in the intersection filter. */ Q_IMPLEMENT_ORGANIZERITEMFILTER_PRIVATE(QOrganizerItemIntersectionFilter) /*! \fn QOrganizerItemIntersectionFilter::QOrganizerItemIntersectionFilter(const QOrganizerItemFilter &other) Constructs a copy of \a other if possible, otherwise constructs a new intersection filter. */ /*! Constructs a new intersection filter. */ QOrganizerItemIntersectionFilter::QOrganizerItemIntersectionFilter() : QOrganizerItemFilter(new QOrganizerItemIntersectionFilterPrivate) { } /*! Sets the filters whose criteria will be intersected to \a filters. \sa filters(), clear() */ void QOrganizerItemIntersectionFilter::setFilters(const QList &filters) { Q_D(QOrganizerItemIntersectionFilter); d->m_filters = filters; } /*! Clears the list of filters. Note that an empty intersection filter will match no items. \sa filters(), setFilters() */ void QOrganizerItemIntersectionFilter::clear() { Q_D(QOrganizerItemIntersectionFilter); d->m_filters.clear(); } /*! Prepends the given \a filter to the list of intersected filters. \sa append(), filters() */ void QOrganizerItemIntersectionFilter::prepend(const QOrganizerItemFilter &filter) { Q_D(QOrganizerItemIntersectionFilter); d->m_filters.prepend(filter); } /*! Appends the given \a filter to the list of intersected filters. \sa operator<<(), prepend(), filters() */ void QOrganizerItemIntersectionFilter::append(const QOrganizerItemFilter &filter) { Q_D(QOrganizerItemIntersectionFilter); d->m_filters.append(filter); } /*! Removes the given \a filter from the intersection list. \sa filters(), append(), prepend(), clear() */ void QOrganizerItemIntersectionFilter::remove(const QOrganizerItemFilter &filter) { Q_D(QOrganizerItemIntersectionFilter); d->m_filters.removeAll(filter); } /*! Appends the given \a filter to the list of intersected filters. \sa append() */ QOrganizerItemIntersectionFilter &QOrganizerItemIntersectionFilter::operator<<(const QOrganizerItemFilter &filter) { Q_D(QOrganizerItemIntersectionFilter); d->m_filters << filter; return *this; } /*! Returns the list of filters which form the intersection filter. \sa setFilters(), prepend(), append(), remove() */ QList QOrganizerItemIntersectionFilter::filters() const { Q_D(const QOrganizerItemIntersectionFilter); return d->m_filters; } QT_END_NAMESPACE_ORGANIZER src/organizer/filters/qorganizeritemintersectionfilter.h000066400000000000000000000056551233466112000243740ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMINTERSECTIONFILTER_H #define QORGANIZERITEMINTERSECTIONFILTER_H #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemIntersectionFilterPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemIntersectionFilter : public QOrganizerItemFilter { public: QOrganizerItemIntersectionFilter(); QOrganizerItemIntersectionFilter(const QOrganizerItemFilter &other); void setFilters(const QList &filters); void prepend(const QOrganizerItemFilter & filter); void append(const QOrganizerItemFilter & filter); void remove(const QOrganizerItemFilter & filter); void clear(); QOrganizerItemIntersectionFilter &operator<<(const QOrganizerItemFilter &filter); QList filters() const; private: Q_DECLARE_ORGANIZERITEMFILTER_PRIVATE(QOrganizerItemIntersectionFilter) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMINTERSECTIONFILTER_H src/organizer/filters/qorganizeritemintersectionfilter_p.h000066400000000000000000000100061233466112000246750ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMINTERSECTIONFILTER_P_H #define QORGANIZERITEMINTERSECTIONFILTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemIntersectionFilterPrivate : public QOrganizerItemFilterPrivate { public: QOrganizerItemIntersectionFilterPrivate() : QOrganizerItemFilterPrivate() { } QOrganizerItemIntersectionFilterPrivate(const QOrganizerItemIntersectionFilterPrivate &other) : QOrganizerItemFilterPrivate(other), m_filters(other.m_filters) { } virtual bool compare(const QOrganizerItemFilterPrivate *other) const { const QOrganizerItemIntersectionFilterPrivate *od = static_cast(other); if (od) return m_filters == od->m_filters; return false; } #ifndef QT_NO_DATASTREAM QDataStream &outputToStream(QDataStream &stream, quint8 formatVersion) const { if (formatVersion == 1) stream << m_filters; return stream; } QDataStream &inputFromStream(QDataStream &stream, quint8 formatVersion) { if (formatVersion == 1) stream >> m_filters; return stream; } #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM QDebug &debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerItemIntersectionFilter("; dbg.nospace() << "filters="; dbg.nospace() << m_filters; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif // QT_NO_DEBUG_STREAM Q_IMPLEMENT_ORGANIZERITEMFILTER_VIRTUALCTORS(QOrganizerItemIntersectionFilter, QOrganizerItemFilter::IntersectionFilter) QList m_filters; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMINTERSECTIONFILTER_P_H src/organizer/filters/qorganizeriteminvalidfilter.cpp000066400000000000000000000075071233466112000236450ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeriteminvalidfilter.h" #include "qorganizeritemfilter_p.h" /*! \class QOrganizerItemInvalidFilter \brief The QOrganizerItemInvalidFilter class matches no organizeritems. \inmodule QtOrganizer \ingroup organizer-filters This class provides a filter which will never match any organizer items. */ QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemInvalidFilterPrivate : public QOrganizerItemFilterPrivate { public: QOrganizerItemInvalidFilterPrivate() : QOrganizerItemFilterPrivate() { } bool compare(const QOrganizerItemFilterPrivate *) const { return true; } #ifndef QT_NO_DATASTREAM QDataStream &outputToStream(QDataStream &stream, quint8 formatVersion) const { Q_UNUSED(formatVersion) return stream; } QDataStream &inputFromStream(QDataStream &stream, quint8 formatVersion) { Q_UNUSED(formatVersion) return stream; } #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM QDebug &debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerItemInvalidFilter()"; return dbg.maybeSpace() ; } #endif // QT_NO_DEBUG_STREAM QOrganizerItemFilterPrivate *clone() const { return new QOrganizerItemInvalidFilterPrivate(); } QOrganizerItemFilter::FilterType type() const { return QOrganizerItemFilter::InvalidFilter; } QList m_filters; }; /*! Constructs a new invalid filter. */ QOrganizerItemInvalidFilter::QOrganizerItemInvalidFilter() : QOrganizerItemFilter(new QOrganizerItemInvalidFilterPrivate) { } /*! Constructs a new invalid filter, ignoring the \a other filter. */ QOrganizerItemInvalidFilter::QOrganizerItemInvalidFilter(const QOrganizerItemFilter &other) : QOrganizerItemFilter(new QOrganizerItemInvalidFilterPrivate) { Q_UNUSED(other); } QT_END_NAMESPACE_ORGANIZER src/organizer/filters/qorganizeriteminvalidfilter.h000066400000000000000000000046311233466112000233050ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMINVALIDFILTER_H #define QORGANIZERITEMINVALIDFILTER_H #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemInvalidFilterPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemInvalidFilter : public QOrganizerItemFilter { public: QOrganizerItemInvalidFilter(); QOrganizerItemInvalidFilter(const QOrganizerItemFilter &other); }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMINVALIDFILTER_H src/organizer/filters/qorganizeritemunionfilter.cpp000066400000000000000000000110561233466112000233410ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemunionfilter.h" #include "qorganizeritemunionfilter_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemUnionFilter \brief The QOrganizerItemUnionFilter class provides a filter which unions the results of other filters. \inmodule QtOrganizer \ingroup organizer-filters It may be used to select organizer items which match any of the filters in the union. */ Q_IMPLEMENT_ORGANIZERITEMFILTER_PRIVATE(QOrganizerItemUnionFilter) /*! \fn QOrganizerItemUnionFilter::QOrganizerItemUnionFilter(const QOrganizerItemFilter &other) Constructs a copy of \a other if possible, otherwise constructs a new union filter. */ /*! Constructs a new intersection filter. */ QOrganizerItemUnionFilter::QOrganizerItemUnionFilter() : QOrganizerItemFilter(new QOrganizerItemUnionFilterPrivate) { } /*! Sets the filters whose criteria will be unioned to \a filters. \sa filters() */ void QOrganizerItemUnionFilter::setFilters(const QList &filters) { Q_D(QOrganizerItemUnionFilter); d->m_filters = filters; } /*! Prepends the given \a filter to the list of unioned filters. \sa append(), filters() */ void QOrganizerItemUnionFilter::prepend(const QOrganizerItemFilter &filter) { Q_D(QOrganizerItemUnionFilter); d->m_filters.prepend(filter); } /*! Appends the given \a filter to the list of unioned filters. \sa operator<<(), prepend(), filters() */ void QOrganizerItemUnionFilter::append(const QOrganizerItemFilter &filter) { Q_D(QOrganizerItemUnionFilter); d->m_filters.append(filter); } /*! Removes the given \a filter from the union list. \sa filters(), append(), prepend(), clear() */ void QOrganizerItemUnionFilter::remove(const QOrganizerItemFilter &filter) { Q_D(QOrganizerItemUnionFilter); d->m_filters.removeAll(filter); } /*! Clears the list of filters. Note that an empty union filter will match no items. \sa filters(), remove() */ void QOrganizerItemUnionFilter::clear() { Q_D(QOrganizerItemUnionFilter); d->m_filters.clear(); } /*! Appends the given \a filter to the list of unioned filters. \sa append() */ QOrganizerItemUnionFilter &QOrganizerItemUnionFilter::operator<<(const QOrganizerItemFilter &filter) { Q_D(QOrganizerItemUnionFilter); d->m_filters << filter; return *this; } /*! Returns the list of filters which form the union filter. \sa setFilters(), prepend(), append(), remove() */ QList QOrganizerItemUnionFilter::filters() const { Q_D(const QOrganizerItemUnionFilter); return d->m_filters; } QT_END_NAMESPACE_ORGANIZER src/organizer/filters/qorganizeritemunionfilter.h000066400000000000000000000055541233466112000230140ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMUNIONFILTER_H #define QORGANIZERITEMUNIONFILTER_H #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemUnionFilterPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemUnionFilter : public QOrganizerItemFilter { public: QOrganizerItemUnionFilter(); QOrganizerItemUnionFilter(const QOrganizerItemFilter &other); void setFilters(const QList &filters); void prepend(const QOrganizerItemFilter &filter); void append(const QOrganizerItemFilter &filter); void remove(const QOrganizerItemFilter &filter); void clear(); QOrganizerItemUnionFilter &operator<<(const QOrganizerItemFilter &filter); QList filters() const; private: Q_DECLARE_ORGANIZERITEMFILTER_PRIVATE(QOrganizerItemUnionFilter) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMUNIONFILTER_H src/organizer/filters/qorganizeritemunionfilter_p.h000066400000000000000000000076531233466112000233350ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMUNIONFILTER_P_H #define QORGANIZERITEMUNIONFILTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemUnionFilterPrivate : public QOrganizerItemFilterPrivate { public: QOrganizerItemUnionFilterPrivate() : QOrganizerItemFilterPrivate() { } QOrganizerItemUnionFilterPrivate(const QOrganizerItemUnionFilterPrivate &other) : QOrganizerItemFilterPrivate(other), m_filters(other.m_filters) { } virtual bool compare(const QOrganizerItemFilterPrivate *other) const { const QOrganizerItemUnionFilterPrivate *od = static_cast(other); if (od) return m_filters == od->m_filters; return false; } #ifndef QT_NO_DATASTREAM QDataStream &outputToStream(QDataStream &stream, quint8 formatVersion) const { if (formatVersion == 1) stream << m_filters; return stream; } QDataStream &inputFromStream(QDataStream &stream, quint8 formatVersion) { if (formatVersion == 1) stream >> m_filters; return stream; } #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM QDebug &debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerItemUnionFilter("; dbg.nospace() << "filters="; dbg.nospace() << m_filters; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif // QT_NO_DEBUG_STREAM Q_IMPLEMENT_ORGANIZERITEMFILTER_VIRTUALCTORS(QOrganizerItemUnionFilter, QOrganizerItemFilter::UnionFilter) QList m_filters; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMUNIONFILTER_P_H src/organizer/items/000077500000000000000000000000001233466112000147645ustar00rootroot00000000000000src/organizer/items/items.pri000066400000000000000000000007571233466112000166320ustar00rootroot00000000000000INCLUDEPATH += items \ ./ PUBLIC_HEADERS += items/qorganizeritems.h \ items/qorganizerevent.h \ items/qorganizereventoccurrence.h \ items/qorganizerjournal.h \ items/qorganizernote.h \ items/qorganizertodo.h \ items/qorganizertodooccurrence.h SOURCES += \ items/qorganizerevent.cpp \ items/qorganizereventoccurrence.cpp \ items/qorganizerjournal.cpp \ items/qorganizernote.cpp \ items/qorganizertodo.cpp \ items/qorganizertodooccurrence.cpp src/organizer/items/qorganizerevent.cpp000066400000000000000000000253271233466112000207240ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizerevent.h" #include "qorganizereventtime.h" #include "qorganizeritemlocation.h" #include "qorganizeritemrecurrence.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerEvent \brief The QOrganizerEvent class provides an event in time which may reoccur \inmodule QtOrganizer \ingroup organizer-items A QOrganizerEvent is an item which occurs at a particular point in time and may be associated with a location or have other details. It may have a set of recurrence rules or dates on which the event occurs associated with it, and also exceptions to those recurrences. */ /*! Sets the start date time of the event to \a startDateTime (for recurring events, this applies to the first instance). For all-day events, the time part can be set to any valid value. */ void QOrganizerEvent::setStartDateTime(const QDateTime &startDateTime) { QOrganizerEventTime etr = detail(QOrganizerItemDetail::TypeEventTime); etr.setStartDateTime(startDateTime); saveDetail(&etr); } /*! Returns the date time at which the event starts (for recurring events, this applies to the first instance). For all-day events, the time part is meaningless. */ QDateTime QOrganizerEvent::startDateTime() const { QOrganizerEventTime etr = detail(QOrganizerItemDetail::TypeEventTime); return etr.startDateTime(); } /*! Sets the end date time of the event to \a endDateTime (for recurring events, this applies to the first instance). For all-day events, the time part can be set to any valid value, and the date is to be interpreted as the inclusive end date. */ void QOrganizerEvent::setEndDateTime(const QDateTime &endDateTime) { QOrganizerEventTime etr = detail(QOrganizerItemDetail::TypeEventTime); etr.setEndDateTime(endDateTime); saveDetail(&etr); } /*! Returns the date time at which the event ends (for recurring events, this applies to the first instance). For all-day events, the time part is meaningless, and the date is to be interpreted as the inclusive end date. */ QDateTime QOrganizerEvent::endDateTime() const { QOrganizerEventTime etr = detail(QOrganizerItemDetail::TypeEventTime); return etr.endDateTime(); } /*! Sets whether the time-of-day component of the event's start date-time or end date-time is insignificant (eg. this is generally set to true for a birthday). If \a isAllDay is true, the time-of-day component is considered insignificant, and the event will be an all-day item. */ void QOrganizerEvent::setAllDay(bool isAllDay) { QOrganizerEventTime etr = detail(QOrganizerItemDetail::TypeEventTime); etr.setAllDay(isAllDay); saveDetail(&etr); } /*! Returns true if and only if the time component of the start date/time or end date/time are insignificant. */ bool QOrganizerEvent::isAllDay() const { QOrganizerEventTime etr = detail(QOrganizerItemDetail::TypeEventTime); return etr.isAllDay(); } /*! Sets the list of dates \a rdates to be dates on which the event occurs. */ void QOrganizerEvent::setRecurrenceDates(const QSet &rdates) { QOrganizerItemRecurrence rec = detail(QOrganizerItemDetail::TypeRecurrence); rec.setRecurrenceDates(rdates); saveDetail(&rec); } /*! Sets a single date \a rdate to be the date on which the event occurs. \sa setRecurrenceDates() */ void QOrganizerEvent::setRecurrenceDate(const QDate &rdate) { setRecurrenceDates(QSet() << rdate); } /*! Returns the list of dates which have been explicitly set as dates on which the event occurs. */ QSet QOrganizerEvent::recurrenceDates() const { QOrganizerItemRecurrence rec = detail(QOrganizerItemDetail::TypeRecurrence); return rec.recurrenceDates(); } /*! Sets the list of recurrence rules \a rrules to be the rules which define when the event occurs, other than those dates specified explicitly via setRecurrenceDates(). */ void QOrganizerEvent::setRecurrenceRules(const QSet &rrules) { QOrganizerItemRecurrence rec = detail(QOrganizerItemDetail::TypeRecurrence); rec.setRecurrenceRules(rrules); saveDetail(&rec); } /*! Sets a single recurrence rule \a rrule to be the rule which define when the event occurs, other than those dates specified explicitly via setRecurrenceDates(). \sa setRecurrenceRules() */ void QOrganizerEvent::setRecurrenceRule(const QOrganizerRecurrenceRule &rrule) { setRecurrenceRules(QSet() << rrule); } /*! Returns the list of recurrence rules which define when the event occurs. */ QSet QOrganizerEvent::recurrenceRules() const { QOrganizerItemRecurrence rec = detail(QOrganizerItemDetail::TypeRecurrence); return rec.recurrenceRules(); } /*! Returns a recurrence rule which has been explicitly set for the event. Note: if more than one rule exists, the order of the rules is undefined, so any one could be returned. */ QOrganizerRecurrenceRule QOrganizerEvent::recurrenceRule() const { QSet rrules = recurrenceRules(); if (!rrules.isEmpty()) return *rrules.begin(); return QOrganizerRecurrenceRule(); } /*! Sets the given list of dates \a exdates to be dates on which the event explicitly does not occur, even if the recurrence rules suggest that the event should occur on those dates. Any previously specified exception dates will be cleared when this function is called. */ void QOrganizerEvent::setExceptionDates(const QSet &exdates) { QOrganizerItemRecurrence rec = detail(QOrganizerItemDetail::TypeRecurrence); rec.setExceptionDates(exdates); saveDetail(&rec); } /*! Sets the given single date \a exdate to be the date on which the event explicitly does not occur, event if the recurrence rules suggest that the event should occur on this date. Any previously specified exception dates will be cleared when this function is called. \sa setExceptionDates() */ void QOrganizerEvent::setExceptionDate(const QDate &exdate) { setExceptionDates(QSet() << exdate); } /*! Returns the list of dates on which the event explicitly does not occur despite the recurrence rules for the event */ QSet QOrganizerEvent::exceptionDates() const { QOrganizerItemRecurrence rec = detail(QOrganizerItemDetail::TypeRecurrence); return rec.exceptionDates(); } /*! Sets the given list of recurrence rules \a exrules to be the rules which define when the event does not occur. Any previously specified exception rules will be cleared when this function is called. */ void QOrganizerEvent::setExceptionRules(const QSet &exrules) { QOrganizerItemRecurrence rec = detail(QOrganizerItemDetail::TypeRecurrence); rec.setExceptionRules(exrules); saveDetail(&rec); } /*! Sets the given single recurrence rules \a xrule to be the rule which defines when the event does not occur. Any previously specified exception rules will be cleared when this function is called. \sa setExceptionRules() */ void QOrganizerEvent::setExceptionRule(const QOrganizerRecurrenceRule &xrule) { setExceptionRules(QSet() << xrule); } /*! Returns an exception rule which has been explicitly set for the event. Note: if more than one exception rule exists, the order of the rules is undefined, so any one could be returned. */ QOrganizerRecurrenceRule QOrganizerEvent::exceptionRule() const { QSet exrules = exceptionRules(); if (!exrules.isEmpty()) return *exrules.begin(); return QOrganizerRecurrenceRule(); } /*! Returns the list of exception rules for the event. */ QSet QOrganizerEvent::exceptionRules() const { QOrganizerItemRecurrence rec = detail(QOrganizerItemDetail::TypeRecurrence); return rec.exceptionRules(); } /*! Sets the priority of this event to \a priority. */ void QOrganizerEvent::setPriority(QOrganizerItemPriority::Priority priority) { QOrganizerItemPriority pd = detail(QOrganizerItemDetail::TypePriority); pd.setPriority(priority); saveDetail(&pd); } /*! Returns the priority of the event. */ QOrganizerItemPriority::Priority QOrganizerEvent::priority() const { QOrganizerItemPriority pd = detail(QOrganizerItemDetail::TypePriority); return pd.priority(); } /*! Returns the label of the location at which the event occurs. */ QString QOrganizerEvent::location() const { QOrganizerItemLocation ld = detail(QOrganizerItemDetail::TypeLocation); return ld.label(); } /*! Sets the label of the location at which the event occurs to \a label. */ void QOrganizerEvent::setLocation(const QString &label) { QOrganizerItemLocation ld = detail(QOrganizerItemDetail::TypeLocation); ld.setLabel(label); saveDetail(&ld); } QT_END_NAMESPACE_ORGANIZER src/organizer/items/qorganizerevent.h000066400000000000000000000072301233466112000203620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZEREVENT_H #define QORGANIZEREVENT_H #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerEvent : public QOrganizerItem { public: Q_DECLARE_CUSTOM_ORGANIZER_ITEM(QOrganizerEvent, QOrganizerItemType::TypeEvent) void setStartDateTime(const QDateTime &startDateTime); QDateTime startDateTime() const; void setEndDateTime(const QDateTime &endDateTime); QDateTime endDateTime() const; void setAllDay(bool isAllDay); bool isAllDay() const; void setRecurrenceDate(const QDate &rdate); void setRecurrenceDates(const QSet &rdates); QSet recurrenceDates() const; void setRecurrenceRule(const QOrganizerRecurrenceRule &rrule); void setRecurrenceRules(const QSet &rrules); QOrganizerRecurrenceRule recurrenceRule() const; QSet recurrenceRules() const; void setExceptionDate(const QDate &exdate); void setExceptionDates(const QSet &exdates); QSet exceptionDates() const; void setExceptionRule(const QOrganizerRecurrenceRule &xrule); void setExceptionRules(const QSet &exrules); QOrganizerRecurrenceRule exceptionRule() const; QSet exceptionRules() const; void setPriority(QOrganizerItemPriority::Priority priority); QOrganizerItemPriority::Priority priority() const; QString location() const; void setLocation(const QString &label); }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZEREVENT_H src/organizer/items/qorganizereventoccurrence.cpp000066400000000000000000000147531233466112000227760ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizereventoccurrence.h" #include #include "qorganizereventtime.h" #include "qorganizeritemlocation.h" #include "qorganizeritemparent.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerEventOccurrence \brief The QOrganizerEventOccurrence class provides an occurrence of an event. \inmodule QtOrganizer \ingroup organizer-items An event occurrence is where the occurrence differs from the generating event in some way. An occurrence which is retrieved from a manager may not actually be persisted in that manager (for example, it may be generated automatically from the recurrence rule of the parent event stored in the manager), in which case it will have a zero-id and differ from the parent event only in its start date. Alternatively, it may be persisted in the manager (that is, the client has saved the occurrence previously) where it is stored as an exception to its parent event. */ /*! Sets the start date time of the event occurrence to \a startDateTime. For all-day events, the time part is meaningless. */ void QOrganizerEventOccurrence::setStartDateTime(const QDateTime &startDateTime) { QOrganizerEventTime etr = detail(QOrganizerItemDetail::TypeEventTime); etr.setStartDateTime(startDateTime); saveDetail(&etr); } /*! Returns the date time at which the event occurrence begins. For all-day events, the time part can be set to any valid value. */ QDateTime QOrganizerEventOccurrence::startDateTime() const { QOrganizerEventTime etr = detail(QOrganizerItemDetail::TypeEventTime); return etr.startDateTime(); } /*! Sets the end date time of the event occurrence to \a endDateTime. For all-day events, the time part can be set to any valid value, and the date is to be interpreted as the inclusive end date. */ void QOrganizerEventOccurrence::setEndDateTime(const QDateTime &endDateTime) { QOrganizerEventTime etr = detail(QOrganizerItemDetail::TypeEventTime); etr.setEndDateTime(endDateTime); saveDetail(&etr); } /*! Returns the date time at which the event occurrence ends. For all-day events, the time part is meaningless, and the date is to be interpreted as the inclusive end date. */ QDateTime QOrganizerEventOccurrence::endDateTime() const { QOrganizerEventTime etr = detail(QOrganizerItemDetail::TypeEventTime); return etr.endDateTime(); } /*! Sets the event occurrence's parent to be the event identified by the given \a parentId. */ void QOrganizerEventOccurrence::setParentId(const QOrganizerItemId &parentId) { QOrganizerItemParent origin = detail(QOrganizerItemDetail::TypeParent); origin.setParentId(parentId); saveDetail(&origin); } /*! Returns the id of the event which is this occurrence's parent. */ QOrganizerItemId QOrganizerEventOccurrence::parentId() const { QOrganizerItemParent origin = detail(QOrganizerItemDetail::TypeParent); return origin.parentId(); } /*! Sets the date at which this occurrence was originally going to occur, to the given \a date. */ void QOrganizerEventOccurrence::setOriginalDate(const QDate &date) { QOrganizerItemParent origin = detail(QOrganizerItemDetail::TypeParent); origin.setOriginalDate(date); saveDetail(&origin); } /*! Returns the date at which the occurrence was originally going to occur. */ QDate QOrganizerEventOccurrence::originalDate() const { QOrganizerItemParent origin = detail(QOrganizerItemDetail::TypeParent); return origin.originalDate(); } /*! Sets the priority of the event occurrence to \a priority. */ void QOrganizerEventOccurrence::setPriority(QOrganizerItemPriority::Priority priority) { QOrganizerItemPriority pd = detail(QOrganizerItemDetail::TypePriority); pd.setPriority(priority); saveDetail(&pd); } /*! Returns the priority of the event occurrence. */ QOrganizerItemPriority::Priority QOrganizerEventOccurrence::priority() const { QOrganizerItemPriority pd = detail(QOrganizerItemDetail::TypePriority); return pd.priority(); } /*! Returns the label of the location at which the event occurrence is held. */ QString QOrganizerEventOccurrence::location() const { QOrganizerItemLocation ld = detail(QOrganizerItemDetail::TypeLocation); return ld.label(); } /*! Sets the label of the location at which the event occurrence is held to \a label. */ void QOrganizerEventOccurrence::setLocation(const QString &label) { QOrganizerItemLocation ld = detail(QOrganizerItemDetail::TypeLocation); ld.setLabel(label); saveDetail(&ld); } QT_END_NAMESPACE_ORGANIZER src/organizer/items/qorganizereventoccurrence.h000066400000000000000000000057761233466112000224500ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZEREVENTOCCURRENCE_H #define QORGANIZEREVENTOCCURRENCE_H #include #include QT_FORWARD_DECLARE_CLASS(QDate) QT_FORWARD_DECLARE_CLASS(QDateTime) QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerEventOccurrence : public QOrganizerItem { public: Q_DECLARE_CUSTOM_ORGANIZER_ITEM(QOrganizerEventOccurrence, QOrganizerItemType::TypeEventOccurrence) void setStartDateTime(const QDateTime &startDateTime); QDateTime startDateTime() const; void setEndDateTime(const QDateTime &endDateTime); QDateTime endDateTime() const; void setParentId(const QOrganizerItemId &parentId); QOrganizerItemId parentId() const; void setOriginalDate(const QDate &date); QDate originalDate() const; void setPriority(QOrganizerItemPriority::Priority priority); QOrganizerItemPriority::Priority priority() const; QString location() const; void setLocation(const QString &label); }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZEREVENTOCCURRENCE_H src/organizer/items/qorganizeritems.h000066400000000000000000000046571233466112000203740ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMS_H #define QORGANIZERITEMS_H // this file includes all of the organizer item // leaf classes that are included in the public API #include #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMS_H src/organizer/items/qorganizerjournal.cpp000066400000000000000000000055771233466112000212620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizerjournal.h" #include #include "qorganizerjournaltime.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerJournal \brief The QOrganizerJournal class supplies a journal which is associated with a particular point in time \inmodule QtOrganizer \ingroup organizer-items A journal consists of personal data which is associated with a particular point in time. */ /*! Sets the date time which this journal is associated with to \a dateTime. */ void QOrganizerJournal::setDateTime(const QDateTime &dateTime) { QOrganizerJournalTime jtr = detail(QOrganizerItemDetail::TypeJournalTime); jtr.setEntryDateTime(dateTime); saveDetail(&jtr); } /*! Returns the date time associated with this journal. */ QDateTime QOrganizerJournal::dateTime() const { QOrganizerJournalTime jtr = detail(QOrganizerItemDetail::TypeJournalTime); return jtr.entryDateTime(); } QT_END_NAMESPACE_ORGANIZER src/organizer/items/qorganizerjournal.h000066400000000000000000000046421233466112000207170ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERJOURNAL_H #define QORGANIZERJOURNAL_H #include QT_FORWARD_DECLARE_CLASS(QDateTime) QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerJournal : public QOrganizerItem { public: Q_DECLARE_CUSTOM_ORGANIZER_ITEM(QOrganizerJournal, QOrganizerItemType::TypeJournal) void setDateTime(const QDateTime &dateTime); QDateTime dateTime() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERJOURNAL_H src/organizer/items/qorganizernote.cpp000066400000000000000000000046121233466112000205420ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizernote.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerNote \brief The QOrganizerNote class provides a note which is not associated with any particular point in time \inmodule QtOrganizer \ingroup organizer-items A note is a convenience facade for a QOrganizerItem that has its type set to TypeNote. It contains information which is not associated with a particular point in time. */ QT_END_NAMESPACE_ORGANIZER src/organizer/items/qorganizernote.h000066400000000000000000000044311233466112000202060ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERNOTE_H #define QORGANIZERNOTE_H #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerNote : public QOrganizerItem { public: Q_DECLARE_CUSTOM_ORGANIZER_ITEM(QOrganizerNote, QOrganizerItemType::TypeNote) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERNOTE_H src/organizer/items/qorganizertodo.cpp000066400000000000000000000265221233466112000205460ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizertodo.h" #include "qorganizertodotime.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerTodo \brief The QOrganizerTodo class provides a task which should be completed. \inmodule QtOrganizer \ingroup organizer-items A todo is an item which contains information about a task which has to be completed. It might be associated with a particular point in time (for example, water the garden tomorrow evening) or it might have no specific temporal association (for example, climb Mount Everest someday). A todo can reoccur (for example, water the garden every evening) or it can occur only once. Todos can be used to schedule agenda items or tasks in a meaningful manner. */ /*! Sets the date time at which the task should be started to \a startDateTime (for recurring tasks, this applies to the first instance). For all-day tasks, the time part can be set to any valid value. */ void QOrganizerTodo::setStartDateTime(const QDateTime &startDateTime) { QOrganizerTodoTime ttr = detail(QOrganizerItemDetail::TypeTodoTime); ttr.setStartDateTime(startDateTime); saveDetail(&ttr); } /*! Returns the date time at which the task should be started (for recurring tasks, this applies to the first instance). For all-day tasks, the time part is meaningless. */ QDateTime QOrganizerTodo::startDateTime() const { QOrganizerTodoTime ttr = detail(QOrganizerItemDetail::TypeTodoTime); return ttr.startDateTime(); } /*! Sets the date time by which the task should be completed to \a dueDateTime (for recurring tasks, this applies to the first instance). For all-day tasks, the time part can be set to any valid value. */ void QOrganizerTodo::setDueDateTime(const QDateTime &dueDateTime) { QOrganizerTodoTime ttr = detail(QOrganizerItemDetail::TypeTodoTime); ttr.setDueDateTime(dueDateTime); saveDetail(&ttr); } /*! Returns the date time by which the task should be completed (for recurring tasks, this applies to the first instance). For all-day tasks, the time part is meaningless. */ QDateTime QOrganizerTodo::dueDateTime() const { QOrganizerTodoTime ttr = detail(QOrganizerItemDetail::TypeTodoTime); return ttr.dueDateTime(); } /*! Sets whether the time-of-day component of the todo's start date-time or end date-time is insignificant (e.g. this is generally set to true for a birthday). If \a isAllDay is true, the time-of-day component is considered insignificant, and the todo will be an all-day item. */ void QOrganizerTodo::setAllDay(bool isAllDay) { QOrganizerTodoTime ttr = detail(QOrganizerItemDetail::TypeTodoTime); ttr.setAllDay(isAllDay); saveDetail(&ttr); } /*! Returns true if and only if the time component of the start date/time or end date/time are insignificant. */ bool QOrganizerTodo::isAllDay() const { QOrganizerTodoTime ttr = detail(QOrganizerItemDetail::TypeTodoTime); return ttr.isAllDay(); } /*! Sets the dates on which the todo reoccurs to \a rdates. */ void QOrganizerTodo::setRecurrenceDates(const QSet &rdates) { QOrganizerItemRecurrence rec = detail(QOrganizerItemDetail::TypeRecurrence); rec.setRecurrenceDates(rdates); saveDetail(&rec); } /*! Returns the dates on which the todo reoccurs, which have been explicitly set by calling \l setRecurrenceDates(). */ QSet QOrganizerTodo::recurrenceDates() const { QOrganizerItemRecurrence rec = detail(QOrganizerItemDetail::TypeRecurrence); return rec.recurrenceDates(); } /*! Clears the set of recurrence rules which define when the todo occurs, and replaces it will the single recurrence rule \a rrule. */ void QOrganizerTodo::setRecurrenceRule(const QOrganizerRecurrenceRule &rrule) { setRecurrenceRules(QSet() << rrule); } /*! Sets the recurrence rules which define when the todo occurs to \a rrules. */ void QOrganizerTodo::setRecurrenceRules(const QSet &rrules) { QOrganizerItemRecurrence rec = detail(QOrganizerItemDetail::TypeRecurrence); rec.setRecurrenceRules(rrules); saveDetail(&rec); } /*! Returns a recurrence rule which defines when the todo occurs. If more than one recurrence rule has been set in the todo, one will be returned at random. */ QOrganizerRecurrenceRule QOrganizerTodo::recurrenceRule() const { QSet rrules = recurrenceRules(); if (!rrules.isEmpty()) return *rrules.begin(); return QOrganizerRecurrenceRule(); } /*! Returns the list of recurrence rules which define when the todo occurs. */ QSet QOrganizerTodo::recurrenceRules() const { QOrganizerItemRecurrence rec = detail(QOrganizerItemDetail::TypeRecurrence); return rec.recurrenceRules(); } /*! Sets the dates on which the todo does not occur despite the date fulfilling the recurrence rules of the todo, to \a exdates */ void QOrganizerTodo::setExceptionDates(const QSet &exdates) { QOrganizerItemRecurrence rec = detail(QOrganizerItemDetail::TypeRecurrence); rec.setExceptionDates(exdates); saveDetail(&rec); } /*! Returns the dates on which the todo does not occur, where it otherwise would occur as described by the recurrence rules. */ QSet QOrganizerTodo::exceptionDates() const { QOrganizerItemRecurrence rec = detail(QOrganizerItemDetail::TypeRecurrence); return rec.exceptionDates(); } /*! Clears the set of recurrence rules which describe the dates on which the todo does not occur, where it otherwise would occur as described by the recurrence rules, and inserts into the cleared list the single exception rule \a exrule. */ void QOrganizerTodo::setExceptionRule(const QOrganizerRecurrenceRule &exrule) { setExceptionRules(QSet() << exrule); } /*! Sets the recurrence rules which describe the dates on which the todo does not occur, where it otherwise would occur as described by the recurrence rules set with \l setRecurrenceRules(), to \a exrules. */ void QOrganizerTodo::setExceptionRules(const QSet &exrules) { QOrganizerItemRecurrence rec = detail(QOrganizerItemDetail::TypeRecurrence); rec.setExceptionRules(exrules); saveDetail(&rec); } /*! Returns a recurrence rule which describe the dates on which the todo does not occur, where it otherwise would occur as described by the recurrence rules. If more than one exception rule was set for the todo item, one of those exception rules will be returned at random. */ QOrganizerRecurrenceRule QOrganizerTodo::exceptionRule() const { QSet exrules = exceptionRules(); if (!exrules.isEmpty()) return *exrules.begin(); return QOrganizerRecurrenceRule(); } /*! Returns the recurrence rules which describe the dates on which the todo does not occur, where it otherwise would occur as described by the recurrence rules set the \l setRecurrenceRules(). */ QSet QOrganizerTodo::exceptionRules() const { QOrganizerItemRecurrence rec = detail(QOrganizerItemDetail::TypeRecurrence); return rec.exceptionRules(); } /*! Sets the priority of the todo to \a priority. */ void QOrganizerTodo::setPriority(QOrganizerItemPriority::Priority priority) { QOrganizerItemPriority pd = detail(QOrganizerItemDetail::TypePriority); pd.setPriority(priority); saveDetail(&pd); } /*! Returns the priority of the task. */ QOrganizerItemPriority::Priority QOrganizerTodo::priority() const { QOrganizerItemPriority pd = detail(QOrganizerItemDetail::TypePriority); return pd.priority(); } /*! Sets the percentage of progress completed on the task described by the todo item to \a percentage. Note that the \a percentage must be between 0 and 100, otherwise ignored. */ void QOrganizerTodo::setProgressPercentage(int percentage) { if (percentage >= 0 && percentage <= 100) { QOrganizerTodoProgress tp = detail(QOrganizerItemDetail::TypeTodoProgress); tp.setPercentageComplete(percentage); saveDetail(&tp); } } /*! Returns the percentage of progress completed on the task described by the todo. */ int QOrganizerTodo::progressPercentage() const { QOrganizerTodoProgress tp = detail(QOrganizerItemDetail::TypeTodoProgress); return tp.percentageComplete(); } /*! Sets the progress status of the task to \a status. */ void QOrganizerTodo::setStatus(QOrganizerTodoProgress::Status status) { QOrganizerTodoProgress tp = detail(QOrganizerItemDetail::TypeTodoProgress); tp.setStatus(status); saveDetail(&tp); } /*! Returns the progress status of the task described by the todo. */ QOrganizerTodoProgress::Status QOrganizerTodo::status() const { QOrganizerTodoProgress tp = detail(QOrganizerItemDetail::TypeTodoProgress); return tp.status(); } /*! Sets the date and time at which the task was completed to \a finishedDateTime. */ void QOrganizerTodo::setFinishedDateTime(const QDateTime &finishedDateTime) { QOrganizerTodoProgress tp = detail(QOrganizerItemDetail::TypeTodoProgress); tp.setFinishedDateTime(finishedDateTime); saveDetail(&tp); } /*! Returns the date and time at which the task was completed. */ QDateTime QOrganizerTodo::finishedDateTime() const { QOrganizerTodoProgress tp = detail(QOrganizerItemDetail::TypeTodoProgress); return tp.finishedDateTime(); } QT_END_NAMESPACE_ORGANIZER src/organizer/items/qorganizertodo.h000066400000000000000000000075041233466112000202120ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERTODO_H #define QORGANIZERTODO_H #include #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerTodo : public QOrganizerItem { public: Q_DECLARE_CUSTOM_ORGANIZER_ITEM(QOrganizerTodo, QOrganizerItemType::TypeTodo) void setStartDateTime(const QDateTime &dueDateTime); QDateTime startDateTime() const; void setDueDateTime(const QDateTime &dueDateTime); QDateTime dueDateTime() const; void setAllDay(bool isAllDay); bool isAllDay() const; void setRecurrenceDates(const QSet &rdates); QSet recurrenceDates() const; void setRecurrenceRule(const QOrganizerRecurrenceRule &rrule); void setRecurrenceRules(const QSet &rrules); QSet recurrenceRules() const; QOrganizerRecurrenceRule recurrenceRule() const; void setExceptionDates(const QSet &exdates); QSet exceptionDates() const; void setExceptionRule(const QOrganizerRecurrenceRule &exrule); void setExceptionRules(const QSet &exrules); QSet exceptionRules() const; QOrganizerRecurrenceRule exceptionRule() const; void setPriority(QOrganizerItemPriority::Priority priority); QOrganizerItemPriority::Priority priority() const; void setProgressPercentage(int percentage); int progressPercentage() const; void setStatus(QOrganizerTodoProgress::Status status); QOrganizerTodoProgress::Status status() const; void setFinishedDateTime(const QDateTime &finishedDateTime); QDateTime finishedDateTime() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERTODO_H src/organizer/items/qorganizertodooccurrence.cpp000066400000000000000000000173701233466112000226200ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizertodooccurrence.h" #include #include "qorganizeritemid.h" #include "qorganizeritemparent.h" #include "qorganizertodotime.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerTodoOccurrence \brief The QOrganizerTodoOccurrence class provides an occurrence of a task which should be completed \inmodule QtOrganizer \ingroup organizer-items A todo occurrence is a specific instance of a todo item. An occurrence which is retrieved from a manager may not actually be persisted in that manager (for example, it may be generated automatically from the recurrence rule of the parent todo stored in the manager), in which case it will have a zero-id and differ from the parent todo only in its start date. Alternatively, it may be persisted in the manager (that is, the client has saved the occurrence previously) where it is stored as an exception to its parent todo. */ /*! Sets the date time at which the task should be started to \a startDateTime. For all-day tasks, the time part can be set to any valid value. */ void QOrganizerTodoOccurrence::setStartDateTime(const QDateTime &startDateTime) { QOrganizerTodoTime ttr = detail(QOrganizerItemDetail::TypeTodoTime); ttr.setStartDateTime(startDateTime); saveDetail(&ttr); } /*! Returns the date time at which the task should be started. For all-day tasks, the time part is meaningless. */ QDateTime QOrganizerTodoOccurrence::startDateTime() const { QOrganizerTodoTime ttr = detail(QOrganizerItemDetail::TypeTodoTime); return ttr.startDateTime(); } /*! Sets the date time by which the task should be completed to \a dueDateTime. For all-day tasks, the time part can be set to any valid value. */ void QOrganizerTodoOccurrence::setDueDateTime(const QDateTime &dueDateTime) { QOrganizerTodoTime ttr = detail(QOrganizerItemDetail::TypeTodoTime); ttr.setDueDateTime(dueDateTime); saveDetail(&ttr); } /*! Returns the date time by which the task should be completed. For all-day tasks, the time part is meaningless. */ QDateTime QOrganizerTodoOccurrence::dueDateTime() const { QOrganizerTodoTime ttr = detail(QOrganizerItemDetail::TypeTodoTime); return ttr.dueDateTime(); } /*! Sets the todo occurrence's parent to be the todo identified by the given \a parentId. */ void QOrganizerTodoOccurrence::setParentId(const QOrganizerItemId &parentId) { QOrganizerItemParent origin = detail(QOrganizerItemDetail::TypeParent); origin.setParentId(parentId); saveDetail(&origin); } /*! Returns the id of the todo which is this occurrence's parent. */ QOrganizerItemId QOrganizerTodoOccurrence::parentId() const { QOrganizerItemParent origin = detail(QOrganizerItemDetail::TypeParent); return origin.parentId(); } /*! Sets the date at which this occurrence was originally going to occur, to the given \a date. */ void QOrganizerTodoOccurrence::setOriginalDate(const QDate &date) { QOrganizerItemParent origin = detail(QOrganizerItemDetail::TypeParent); origin.setOriginalDate(date); saveDetail(&origin); } /*! Returns the date at which the occurrence was originally going to occur. */ QDate QOrganizerTodoOccurrence::originalDate() const { QOrganizerItemParent origin = detail(QOrganizerItemDetail::TypeParent); return origin.originalDate(); } /*! Sets the priority of the todo occurrence to \a priority. */ void QOrganizerTodoOccurrence::setPriority(QOrganizerItemPriority::Priority priority) { QOrganizerItemPriority pd = detail(QOrganizerItemDetail::TypePriority); pd.setPriority(priority); saveDetail(&pd); } /*! Returns the priority of the todo occurrence.. */ QOrganizerItemPriority::Priority QOrganizerTodoOccurrence::priority() const { QOrganizerItemPriority pd = detail(QOrganizerItemDetail::TypePriority); return pd.priority(); } /*! Sets the percentage of progress completed on the task described by the todo occurrence item to \a percentage. Note that the given \a percentage must be between 0 and 100, otherwise ignored. */ void QOrganizerTodoOccurrence::setProgressPercentage(int percentage) { if (percentage >= 0 && percentage <= 100) { QOrganizerTodoProgress tp = detail(QOrganizerItemDetail::TypeTodoProgress); tp.setPercentageComplete(percentage); saveDetail(&tp); } } /*! Returns the percentage of progress completed on the task described by the todo occurrence. */ int QOrganizerTodoOccurrence::progressPercentage() const { QOrganizerTodoProgress tp = detail(QOrganizerItemDetail::TypeTodoProgress); return tp.percentageComplete(); } /*! Sets the progress status of the todo occurrence to \a status. */ void QOrganizerTodoOccurrence::setStatus(QOrganizerTodoProgress::Status status) { QOrganizerTodoProgress tp = detail(QOrganizerItemDetail::TypeTodoProgress); tp.setStatus(status); saveDetail(&tp); } /*! Returns the progress status of the task described by the todo occurrence. */ QOrganizerTodoProgress::Status QOrganizerTodoOccurrence::status() const { QOrganizerTodoProgress tp = detail(QOrganizerItemDetail::TypeTodoProgress); return tp.status(); } /*! Sets the date and time at which the task described by the todo occurrence was completed to \a finishedDateTime. */ void QOrganizerTodoOccurrence::setFinishedDateTime(const QDateTime &finishedDateTime) { QOrganizerTodoProgress tp = detail(QOrganizerItemDetail::TypeTodoProgress); tp.setFinishedDateTime(finishedDateTime); saveDetail(&tp); } /*! Returns the date and time at which the task described by the todo occurrence was completed. */ QDateTime QOrganizerTodoOccurrence::finishedDateTime() const { QOrganizerTodoProgress tp = detail(QOrganizerItemDetail::TypeTodoProgress); return tp.finishedDateTime(); } QT_END_NAMESPACE_ORGANIZER src/organizer/items/qorganizertodooccurrence.h000066400000000000000000000064441233466112000222650ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERTODOOCCURRENCE_H #define QORGANIZERTODOOCCURRENCE_H #include #include #include QT_FORWARD_DECLARE_CLASS(QDate) QT_FORWARD_DECLARE_CLASS(QDateTime) QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemId; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerTodoOccurrence : public QOrganizerItem { public: Q_DECLARE_CUSTOM_ORGANIZER_ITEM(QOrganizerTodoOccurrence, QOrganizerItemType::TypeTodoOccurrence) void setStartDateTime(const QDateTime &startDateTime); QDateTime startDateTime() const; void setDueDateTime(const QDateTime &dueDateTime); QDateTime dueDateTime() const; void setParentId(const QOrganizerItemId &parentId); QOrganizerItemId parentId() const; void setOriginalDate(const QDate &date); QDate originalDate() const; void setPriority(QOrganizerItemPriority::Priority priority); QOrganizerItemPriority::Priority priority() const; void setProgressPercentage(int percentage); int progressPercentage() const; void setStatus(QOrganizerTodoProgress::Status status); QOrganizerTodoProgress::Status status() const; void setFinishedDateTime(const QDateTime &finishedDateTime); QDateTime finishedDateTime() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERTODOOCCURRENCE_H src/organizer/organizer.pro000066400000000000000000000041761233466112000163750ustar00rootroot00000000000000TARGET = QtOrganizer QT = core-private MODULE_PLUGIN_TYPES = \ organizer QMAKE_DOCS = $$PWD/doc/qtorganizer.qdocconf include(details/details.pri) include(items/items.pri) include(requests/requests.pri) include(filters/filters.pri) PUBLIC_HEADERS += \ qorganizercollection.h \ qorganizercollectionchangeset.h \ qorganizercollectionengineid.h \ qorganizercollectionid.h \ qorganizerabstractrequest.h \ qorganizeritemchangeset.h \ qorganizeritemdetail.h \ qorganizeritemfetchhint.h \ qorganizeritemfilter.h \ qorganizeritem.h \ qorganizeritemid.h \ qorganizeritemengineid.h \ qorganizeritemobserver.h \ qorganizermanager.h \ qorganizermanagerengine.h \ qorganizermanagerenginefactory.h \ qorganizerrecurrencerule.h \ qorganizeritemsortorder.h \ qorganizerglobal.h \ qorganizer.h PRIVATE_HEADERS += \ qorganizercollection_p.h \ qorganizercollectionchangeset_p.h \ qorganizerabstractrequest_p.h \ qorganizeritemchangeset_p.h \ qorganizeritem_p.h \ qorganizeritemid_p.h \ qorganizeritemdetail_p.h \ qorganizeritemfilter_p.h \ qorganizeritemfetchhint_p.h \ qorganizermanager_p.h \ qorganizerrecurrencerule_p.h \ qorganizeritemsortorder_p.h SOURCES += \ qorganizercollection.cpp \ qorganizercollectionchangeset.cpp \ qorganizercollectionengineid.cpp \ qorganizercollectionid.cpp \ qorganizerabstractrequest.cpp \ qorganizeritemchangeset.cpp \ qorganizeritem.cpp \ qorganizeritemdetail.cpp \ qorganizeritemfetchhint.cpp \ qorganizeritemfilter.cpp \ qorganizeritemid.cpp \ qorganizeritemengineid.cpp \ qorganizeritemobserver.cpp \ qorganizermanager.cpp \ qorganizermanagerengine.cpp \ qorganizermanagerenginefactory.cpp \ qorganizerrecurrencerule.cpp \ qorganizeritemsortorder.cpp \ qorganizermanager_p.cpp qtHaveModule(jsondb) { isEmpty(ORGANIZER_DEFAULT_ENGINE): ORGANIZER_DEFAULT_ENGINE=jsondb } !isEmpty(ORGANIZER_DEFAULT_ENGINE): DEFINES += Q_ORGANIZER_DEFAULT_ENGINE=$$ORGANIZER_DEFAULT_ENGINE HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS load(qt_module) src/organizer/qorganizer.h000066400000000000000000000103121233466112000161720ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QTORGANIZER_H #define QTORGANIZER_H // this file includes all of the public header files // provided by the Qt Organizer API #include // global exports #include // asynchronous request #include // organizer item detail #include // fetch hint class #include // organizer item filter #include // organizer item #include // organizer item identifier #include // organizer item observer #include // manager #include // manager backend #include // manage backend instantiator #include // a single recurrence rule #include // organizer item sorting #include // collection of items #include // collection identifier #include // engine-specific collection changeset #include // engine-specific item changeset #include // engine-specific collection id #include // engine specific item id #include // item derived classes #include // detail derived classes #include // request derived classes #include // detail derived classes QT_BEGIN_NAMESPACE_ORGANIZER QT_END_NAMESPACE_ORGANIZER #endif // QTORGANIZER_H src/organizer/qorganizerabstractrequest.cpp000066400000000000000000000255101233466112000216700ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizerabstractrequest.h" #include "qorganizerabstractrequest_p.h" #ifndef QT_NO_DEBUG_STREAM #include #endif QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerAbstractRequest \brief The QOrganizerAbstractRequest class provides a mechanism for asynchronous requests to be made of a manager if it supports them. \inmodule QtOrganizer \ingroup organizer-main It allows a client to asynchronously request some functionality of a particular QOrganizerManager. Instances of the class will emit signals when the state of the request changes, or when more results become available. Clients should not attempt to create instances of this class directly, but should instead use the use-case-specific classes derived from this class. After creating any sort of request, the client retains ownership and must delete the request to avoid leaking memory. The client may either do this directly (if not within a slot connected to a signal emitted by the request) or by using the deleteLater() slot to schedule the request for deletion when control returns to the event loop. */ /*! \fn QOrganizerAbstractRequest::stateChanged(QOrganizerAbstractRequest::State newState) This signal is emitted when the state of the request is changed. The new state of the request will be contained in \a newState. */ /*! \fn QOrganizerAbstractRequest::resultsAvailable() This signal is emitted when new results are available. Results can include the operation error which may be accessed via error(), or derived-class-specific results which are accessible through the derived class API. \sa error() */ /*! \enum QOrganizerAbstractRequest::RequestType Enumerates the various possible types of asynchronous requests. \value InvalidRequest An invalid request. \value ItemOccurrenceFetchRequest A request to fetch a list of occurrences of an organizer item. \value ItemFetchRequest A request to fetch a list of organizer items. \value ItemFetchForExportRequest A request to fetch a list of persisted organizer items and exceptions. \value ItemIdFetchRequest A request to fetch a list of organizer item IDs. \value ItemFetchByIdRequest A request to fetch a list of organizer items by the given IDs. \value ItemRemoveRequest A request to remove a list of organizer items. \value ItemRemoveByIdRequest A request to remove a list of organizer items by the given IDs. \value ItemSaveRequest A request to save a list of organizer items. \value CollectionFetchRequest A request to fetch a collection. \value CollectionRemoveRequest A request to remove a collection. \value CollectionSaveRequest A request to save a collection. \value ItemFetchByIdRequest A request to fetch an organizer item by ID. \value ItemRemoveByIdRequest A request to remove an organizer item by ID. */ /*! \enum QOrganizerAbstractRequest::State Enumerates the various states that a request may be in at any given time. \value InactiveState Operation not yet started. \value ActiveState Operation started, not yet finished. \value CanceledState Operation is finished due to cancellation. \value FinishedState Operation successfully completed. */ /*! \internal \fn QOrganizerAbstractRequest::QOrganizerAbstractRequest(QObject *parent) Constructs a new, invalid asynchronous request with the specified \a parent. */ /*! \internal Constructs a new request from the given request data \a other with the given parent \a parent. */ QOrganizerAbstractRequest::QOrganizerAbstractRequest(QOrganizerAbstractRequestPrivate *other, QObject *parent) : QObject(parent), d_ptr(other) { } /*! Cleans up the memory used by this request. */ QOrganizerAbstractRequest::~QOrganizerAbstractRequest() { d_ptr->m_mutex.lock(); QOrganizerManagerEngine *engine = QOrganizerManagerData::engine(d_ptr->m_manager); d_ptr->m_mutex.unlock(); if (engine) engine->requestDestroyed(this); delete d_ptr; d_ptr = 0; } /*! \fn bool QOrganizerAbstractRequest::isInactive() const Returns true if the request is in the \l QOrganizerAbstractRequest::InactiveState state; returns false otherwise. \sa state(), isActive(), isCanceled(), isFinished() */ /*! \fn bool QOrganizerAbstractRequest::isActive() const Returns true if the request is in the \l QOrganizerAbstractRequest::ActiveState state; returns false otherwise. \sa state(), isInactive(), isCanceled(), isFinished() */ /*! \fn bool QOrganizerAbstractRequest::isFinished() const Returns true if the request is in the \l QOrganizerAbstractRequest::FinishedState; returns false otherwise. \sa state(), isActive(), isInactive(), isCanceled() */ /*! \fn bool QOrganizerAbstractRequest::isCanceled() const Returns true if the request is in the \l QOrganizerAbstractRequest::CanceledState; returns false otherwise. \sa state(), isActive(), isInactive(), isFinished() */ /*! Returns the overall error of the most recent asynchronous operation. */ QOrganizerManager::Error QOrganizerAbstractRequest::error() const { QMutexLocker ml(&d_ptr->m_mutex); return d_ptr->m_error; } /*! Returns the type of this asynchronous request. */ QOrganizerAbstractRequest::RequestType QOrganizerAbstractRequest::type() const { return d_ptr->m_type; } /*! Returns the current state of the request. */ QOrganizerAbstractRequest::State QOrganizerAbstractRequest::state() const { QMutexLocker ml(&d_ptr->m_mutex); return d_ptr->m_state; } /*! Returns a pointer to the manager of which this request instance requests operations. */ QOrganizerManager *QOrganizerAbstractRequest::manager() const { QMutexLocker ml(&d_ptr->m_mutex); return d_ptr->m_manager; } /*! Sets the manager of which this request instance requests operations to \a manager. Note that if the current request is in active state, the manager can not be changed. */ void QOrganizerAbstractRequest::setManager(QOrganizerManager *manager) { QMutexLocker ml(&d_ptr->m_mutex); if (d_ptr->m_state == QOrganizerAbstractRequest::ActiveState && d_ptr->m_manager) return; d_ptr->m_manager = manager; d_ptr->m_engine = QOrganizerManagerData::engine(d_ptr->m_manager); } /*! Attempts to start the request. Returns false if the request is in the QOrganizerAbstractRequest::Active state, or if the request was unable to be performed by the manager engine; otherwise returns true. */ bool QOrganizerAbstractRequest::start() { QMutexLocker ml(&d_ptr->m_mutex); if (d_ptr->m_engine && d_ptr->m_state != QOrganizerAbstractRequest::ActiveState) { ml.unlock(); return d_ptr->m_engine->startRequest(this); } return false; } /*! Attempts to cancel the request. Returns false if the request is not in the QOrganizerAbstractRequest::Active state, or if the request is unable to be cancelled by the manager engine; otherwise returns true. */ bool QOrganizerAbstractRequest::cancel() { QMutexLocker ml(&d_ptr->m_mutex); if (d_ptr->m_engine && d_ptr->m_state == QOrganizerAbstractRequest::ActiveState) { ml.unlock(); return d_ptr->m_engine->cancelRequest(this); } return false; } /*! Blocks until the request has been completed by the manager engine, or until \a msecs milliseconds has elapsed. If \a msecs is zero or negative, this function will block until the request is complete, regardless of how long it takes. Returns true if the request was cancelled or completed successfully within the given period, otherwise false. Some backends are unable to support this operation safely, and will return false immediately. */ bool QOrganizerAbstractRequest::waitForFinished(int msecs) { QMutexLocker ml(&d_ptr->m_mutex); if (d_ptr->m_engine) { switch (d_ptr->m_state) { case QOrganizerAbstractRequest::ActiveState: ml.unlock(); return d_ptr->m_engine->waitForRequestFinished(this, msecs); case QOrganizerAbstractRequest::CanceledState: case QOrganizerAbstractRequest::FinishedState: return true; default: return false; } } return false; } #ifndef QT_NO_DEBUG_STREAM /*! Outputs \a request to the debug stream \a dbg */ QDebug operator<<(QDebug dbg, const QOrganizerAbstractRequest &request) { dbg.nospace() << "QOrganizerAbstractRequest("; Q_ASSERT(request.d_ptr); if (request.d_ptr->m_type != QOrganizerAbstractRequest::InvalidRequest) { QMutexLocker locker(&request.d_ptr->m_mutex); request.d_ptr->debugStreamOut(dbg); } else { dbg.nospace() << "(null)"; } dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif #include "moc_qorganizerabstractrequest.cpp" QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizerabstractrequest.h000066400000000000000000000103531233466112000213340ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERABSTRACTREQUEST_H #define QORGANIZERABSTRACTREQUEST_H #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManagerEngine; class QOrganizerAbstractRequestPrivate; class Q_ORGANIZER_EXPORT QOrganizerAbstractRequest : public QObject { Q_OBJECT public: ~QOrganizerAbstractRequest(); enum State { InactiveState = 0, ActiveState, CanceledState, FinishedState }; State state() const; inline bool isInactive() const { return state() == QOrganizerAbstractRequest::InactiveState; } inline bool isActive() const { return state() == QOrganizerAbstractRequest::ActiveState; } inline bool isCanceled() const { return state() == QOrganizerAbstractRequest::CanceledState; } inline bool isFinished() const { return state() == QOrganizerAbstractRequest::FinishedState; } QOrganizerManager::Error error() const; enum RequestType { InvalidRequest = 0, ItemOccurrenceFetchRequest, ItemFetchRequest, ItemFetchForExportRequest, ItemIdFetchRequest, ItemFetchByIdRequest, ItemRemoveRequest, ItemRemoveByIdRequest, ItemSaveRequest, CollectionFetchRequest, CollectionRemoveRequest, CollectionSaveRequest }; RequestType type() const; QOrganizerManager* manager() const; void setManager(QOrganizerManager *manager); public Q_SLOTS: bool start(); bool cancel(); bool waitForFinished(int msecs = 0); Q_SIGNALS: void stateChanged(QOrganizerAbstractRequest::State newState); void resultsAvailable(); protected: QOrganizerAbstractRequest(QOrganizerAbstractRequestPrivate *other, QObject *parent = 0); QOrganizerAbstractRequestPrivate *d_ptr; private: QOrganizerAbstractRequest(QObject *parent = 0) : QObject(parent), d_ptr(0) {} Q_DISABLE_COPY(QOrganizerAbstractRequest) friend class QOrganizerManagerEngine; friend class QOrganizerAbstractRequestPrivate; #ifndef QT_NO_DEBUG_STREAM friend Q_ORGANIZER_EXPORT QDebug operator<<(QDebug dbg, const QOrganizerAbstractRequest &request); #endif }; #ifndef QT_NO_DEBUG_STREAM Q_ORGANIZER_EXPORT QDebug operator<<(QDebug dbg, const QOrganizerAbstractRequest &request); #endif QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERABSTRACTREQUEST_H src/organizer/qorganizerabstractrequest_p.h000066400000000000000000000064471233466112000216640ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERABSTRACTREQUEST_P_H #define QORGANIZERABSTRACTREQUEST_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerAbstractRequestPrivate { public: QOrganizerAbstractRequestPrivate(QOrganizerAbstractRequest::RequestType type = QOrganizerAbstractRequest::InvalidRequest) : m_type(type) , m_error(QOrganizerManager::NoError) , m_state(QOrganizerAbstractRequest::InactiveState) , m_manager(0) { } virtual ~QOrganizerAbstractRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM virtual QDebug &debugStreamOut(QDebug &dbg) const = 0; #endif const QOrganizerAbstractRequest::RequestType m_type; QOrganizerManager::Error m_error; QOrganizerAbstractRequest::State m_state; QPointer m_manager; QPointer m_engine; mutable QMutex m_mutex; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERABSTRACTREQUEST_P_H src/organizer/qorganizercollection.cpp000066400000000000000000000240311233466112000206040ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizercollection.h" #include "qorganizercollection_p.h" #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerCollection \brief The QOrganizerCollection class represents a collection of items in a manager. \inmodule QtOrganizer \ingroup organizer-main A collection has an ID and optionally some metadata, and contains zero or more items. Each different manager will have different requirements before a collection may be saved in it. Some managers do not allow collections to be saved at all, while others may require a collection to have some minimal amount of meta data defined in it prior to save. For example, most managers require a valid value for the QOrganizerCollection::KeyName meta data key to be set prior to save. Every QOrganizerItem is contained within a collection when stored in a manager. To save an item in a collection, the client should call QOrganizerItem::setCollectionId() on the item, passing in the ID of the destination collection as the argument, and then save the item in the manager. To move an item from one collection to another, the client must fetch the item from the manager, set the collection ID in the item to the ID of the collection to which the client wishes the item to be moved, and then resave the item in the manager. That is, the collection which an item is part of is treated as a property of the item. */ /*! \enum QOrganizerCollection::MetaDataKey This enumeration describes the key of the organizer collection metadata. \value KeyName This metadata describes the name of the collection. \value KeyDescription This metadata gives a description of the collection. \value KeyColor This metadata describes the color of the collection. \value KeySecondaryColor This metadata describes the secondary color of the collection. \value KeyImage This metadata describes the image of the collection. \value KeyExtended This is an extened metadata, which is stored as a QVariantMap. */ /*! Constructs a new collection. */ QOrganizerCollection::QOrganizerCollection() : d(new QOrganizerCollectionData) { } /*! Cleans up any memory in use by the collection. */ QOrganizerCollection::~QOrganizerCollection() { } /*! Constructs a new copy of the \a other collection. */ QOrganizerCollection::QOrganizerCollection(const QOrganizerCollection &other) : d(other.d) { } /*! Assigns this collection to be equal to the \a other collection. */ QOrganizerCollection &QOrganizerCollection::operator=(const QOrganizerCollection &other) { d = other.d; return *this; } /*! Returns true if the collection is the same as that of the \a other collection, false if either the ID or any of the stored metadata are not the same. */ bool QOrganizerCollection::operator==(const QOrganizerCollection &other) const { if (d == other.d) return true; if (d->m_id != other.d->m_id || d->m_metaData.size() != other.d->m_metaData.size()) { return false; } QMap::const_iterator i = d->m_metaData.constBegin(); while (i != d->m_metaData.constEnd()) { if (i.value() != other.d->m_metaData.value(i.key())) return false; ++i; } return true; } /*! \fn QOrganizerCollection::operator!=(const QOrganizerCollection &other) const Returns true if the collection is not the same as the \a other collection. */ /*! Returns the ID of the collection. */ QOrganizerCollectionId QOrganizerCollection::id() const { return d->m_id; } /*! Sets the ID of the collection to \a id. If the ID is set to a null (default-constructed) ID, saving the collection will cause the manager to save the collection as a new collection. */ void QOrganizerCollection::setId(const QOrganizerCollectionId &id) { d->m_id = id; } /*! Sets the metadata of the collection to be \a metaData. */ void QOrganizerCollection::setMetaData(const QMap &metaData) { d->m_metaData = metaData; } /*! Returns the meta data of the collection. */ QMap QOrganizerCollection::metaData() const { return d->m_metaData; } /*! Sets the meta data of the collection for the given \a key to the given \a value. */ void QOrganizerCollection::setMetaData(MetaDataKey key, const QVariant &value) { d->m_metaData.insert(key, value); } /*! Sets the value of the extended metadata with the given \a key to \a value. */ void QOrganizerCollection::setExtendedMetaData(const QString &key, const QVariant &value) { QVariantMap variantMap = d->m_metaData.value(QOrganizerCollection::KeyExtended).toMap(); variantMap.insert(key, value); d->m_metaData.insert(QOrganizerCollection::KeyExtended, variantMap); } /*! Returns the value of extended metadata with the given \a key. */ QVariant QOrganizerCollection::extendedMetaData(const QString &key) const { return d->m_metaData.value(QOrganizerCollection::KeyExtended).toMap().value(key); } /*! Returns the meta data of the collection for the given \a key. */ QVariant QOrganizerCollection::metaData(MetaDataKey key) const { return d->m_metaData.value(key); } /*! \relates QOrganizerCollection Returns the hash value for \a key. */ Q_ORGANIZER_EXPORT uint qHash(const QOrganizerCollection &key) { uint hash = qHash(key.id()); QMap::const_iterator i = key.d->m_metaData.constBegin(); while (i != key.d->m_metaData.constEnd()) { if (i.key() == QOrganizerCollection::KeyExtended) { QVariantMap variantMap = i.value().toMap(); QVariantMap::const_iterator j = variantMap.constBegin(); while (j != variantMap.constEnd()) { hash += QT_PREPEND_NAMESPACE(qHash)(j.key()) + QT_PREPEND_NAMESPACE(qHash)(j.value().toString()); ++j; } } else { hash += QT_PREPEND_NAMESPACE(qHash)(i.key()) + QT_PREPEND_NAMESPACE(qHash)(i.value().toString()); } ++i; } return hash; } #ifndef QT_NO_DEBUG_STREAM /*! \relates QOrganizerCollection Streams the \a collection to the given debug stream \a dbg, and returns the stream. */ QDebug operator<<(QDebug dbg, const QOrganizerCollection& collection) { dbg.nospace() << "QOrganizerCollection(id=" << collection.id(); QMap metaData = collection.metaData(); QMap::const_iterator i = metaData.constBegin(); while (i != metaData.constEnd()) { dbg.nospace() << ", " << i.key() << '=' << i.value(); ++i; } dbg.nospace() << ')'; return dbg.maybeSpace(); } #endif // QT_NO_DEBUG_STREAM #ifndef QT_NO_DATASTREAM /*! \relates QOrganizerCollection Writes \a collection to the stream \a out. */ QDataStream &operator<<(QDataStream &out, const QOrganizerCollection &collection) { quint8 formatVersion = 1; return out << formatVersion << collection.id().toString() << collection.metaData(); } /*! \relates QOrganizerCollection Reads an organizer collection from stream \a in into \a collection. */ QDataStream &operator>>(QDataStream &in, QOrganizerCollection &collection) { quint8 formatVersion; in >> formatVersion; if (formatVersion == 1) { QString idString; QMap values; in >> idString >> values; collection = QOrganizerCollection(); collection.setId(QOrganizerCollectionId::fromString(idString)); QMap::const_iterator i = values.constBegin(); while (i != values.constEnd()) { collection.setMetaData(static_cast(i.key()), i.value()); ++i; } } else { in.setStatus(QDataStream::ReadCorruptData); } return in; } #endif // QT_NO_DATASTREAM QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizercollection.h000066400000000000000000000102711233466112000202520ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERCOLLECTION_H #define QORGANIZERCOLLECTION_H #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManagerEngine; class QOrganizerCollectionData; class Q_ORGANIZER_EXPORT QOrganizerCollection { public: enum MetaDataKey { KeyName = 0, KeyDescription, KeyColor, KeySecondaryColor, KeyImage, KeyExtended }; QOrganizerCollection(); ~QOrganizerCollection(); QOrganizerCollection(const QOrganizerCollection &other); QOrganizerCollection &operator=(const QOrganizerCollection &other); bool operator==(const QOrganizerCollection &other) const; bool operator!=(const QOrganizerCollection &other) const {return !(other == *this);} QOrganizerCollectionId id() const; void setId(const QOrganizerCollectionId &id); void setMetaData(MetaDataKey key, const QVariant &value); QVariant metaData(MetaDataKey key) const; void setMetaData(const QMap &metaData); QMap metaData() const; void setExtendedMetaData(const QString &key, const QVariant &value); QVariant extendedMetaData(const QString &key) const; private: friend Q_ORGANIZER_EXPORT uint qHash(const QOrganizerCollection &key); friend class QOrganizerManagerEngine; QSharedDataPointer d; }; Q_ORGANIZER_EXPORT uint qHash(const QOrganizerCollection &key); #ifndef QT_NO_DEBUG_STREAM Q_ORGANIZER_EXPORT QDebug operator<<(QDebug dbg, const QOrganizerCollection &collection); #endif // QT_NO_DEBUG_STREAM #ifndef QT_NO_DATASTREAM Q_ORGANIZER_EXPORT QDataStream &operator<<(QDataStream &out, const QOrganizerCollection &collection); Q_ORGANIZER_EXPORT QDataStream &operator>>(QDataStream &in, QOrganizerCollection &collection); #endif // QT_NO_DATASTREAM QT_END_NAMESPACE_ORGANIZER QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(QTORGANIZER_PREPEND_NAMESPACE(QOrganizerCollection), Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif // QORGANIZERCOLLECTION_H src/organizer/qorganizercollection_p.h000066400000000000000000000056661233466112000206050ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERCOLLECTION_P_H #define QORGANIZERCOLLECTION_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerCollectionData : public QSharedData { public: QOrganizerCollectionData() : QSharedData() { } QOrganizerCollectionData(const QOrganizerCollectionData &other) : QSharedData(other), m_metaData(other.m_metaData), m_id(other.m_id) { } ~QOrganizerCollectionData() { } QMap m_metaData; QOrganizerCollectionId m_id; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERCOLLECTION_P_H src/organizer/qorganizercollectionchangeset.cpp000066400000000000000000000225441233466112000224750ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizercollectionchangeset.h" #include "qorganizercollectionchangeset_p.h" #include "qorganizermanagerengine.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerCollectionChangeSet \brief The QOrganizerCollectionChangeSet class provides a simple API to simplify the emission of state-change signals for collections from QOrganizerManagerEngine implementations. \inmodule QtOrganizer \ingroup organizer-backends This class should only be used by backend developers. */ /*! Constructs a new change set. */ QOrganizerCollectionChangeSet::QOrganizerCollectionChangeSet() : d(new QOrganizerCollectionChangeSetData) { } /*! Constructs a copy of the \a other change set. */ QOrganizerCollectionChangeSet::QOrganizerCollectionChangeSet(const QOrganizerCollectionChangeSet &other) : d(other.d) { } /*! Frees the memory used by this change set. */ QOrganizerCollectionChangeSet::~QOrganizerCollectionChangeSet() { } /*! Assigns this change set to be equal to \a other. */ QOrganizerCollectionChangeSet &QOrganizerCollectionChangeSet::operator=(const QOrganizerCollectionChangeSet &other) { d = other.d; return *this; } /*! Sets the data changed flag to \a dataChanged. If this is set to true prior to calling emitSignals(), only the QOrganizerManagerEngine::dataChanged() signal will be emitted; otherwise, the appropriate finer-grained signals will be emitted. */ void QOrganizerCollectionChangeSet::setDataChanged(bool dataChanged) { d->m_dataChanged = dataChanged; } /*! Returns the value of the data changed flag. */ bool QOrganizerCollectionChangeSet::dataChanged() const { return d->m_dataChanged; } /*! Returns the set of IDs of collections which have been added to the database. */ QSet QOrganizerCollectionChangeSet::addedCollections() const { return d->m_addedCollections; } /*! Inserts the given \a collectionId into the set of IDs of collections which have been added to the database. */ void QOrganizerCollectionChangeSet::insertAddedCollection(const QOrganizerCollectionId &collectionId) { d->m_addedCollections.insert(collectionId); d->m_modifiedCollections.append(QPair(collectionId, QOrganizerManager::Add)); } /*! Inserts each of the given \a collectionIds into the set of IDs of collections which have been added to the database. */ void QOrganizerCollectionChangeSet::insertAddedCollections(const QList &collectionIds) { foreach (const QOrganizerCollectionId &id, collectionIds) { d->m_addedCollections.insert(id); d->m_modifiedCollections.append(QPair(id, QOrganizerManager::Add)); } } /*! Clears the set of IDs of collections which have been added to the database. */ void QOrganizerCollectionChangeSet::clearAddedCollections() { d->m_addedCollections.clear(); } /*! Returns the set of IDs of collections which have been changed in the database. */ QSet QOrganizerCollectionChangeSet::changedCollections() const { return d->m_changedCollections; } /*! Inserts the given \a collectionId into the set of IDs of collections which have been changed in the database. */ void QOrganizerCollectionChangeSet::insertChangedCollection(const QOrganizerCollectionId &collectionId) { d->m_changedCollections.insert(collectionId); d->m_modifiedCollections.append(QPair(collectionId, QOrganizerManager::Change)); } /*! Inserts each of the given \a collectionIds into the set of IDs of collections which have been changed in the database. */ void QOrganizerCollectionChangeSet::insertChangedCollections(const QList &collectionIds) { foreach (const QOrganizerCollectionId &id, collectionIds) { d->m_changedCollections.insert(id); d->m_modifiedCollections.append(QPair(id, QOrganizerManager::Change)); } } /*! Clears the set of IDs of collections which have been changed in the database. */ void QOrganizerCollectionChangeSet::clearChangedCollections() { d->m_changedCollections.clear(); } /*! Returns the set of IDs of collections which have been removed from the database. */ QSet QOrganizerCollectionChangeSet::removedCollections() const { return d->m_removedCollections; } /*! Inserts the given \a collectionId into the set of IDs of collections which have been removed from the database. */ void QOrganizerCollectionChangeSet::insertRemovedCollection(const QOrganizerCollectionId &collectionId) { d->m_removedCollections.insert(collectionId); d->m_modifiedCollections.append(QPair(collectionId, QOrganizerManager::Remove)); } /*! Inserts each of the given \a collectionIds into the set of IDs of collections which have been removed from the database. */ void QOrganizerCollectionChangeSet::insertRemovedCollections(const QList &collectionIds) { foreach (const QOrganizerCollectionId &id, collectionIds) { d->m_removedCollections.insert(id); d->m_modifiedCollections.append(QPair(id, QOrganizerManager::Remove)); } } /*! Clears the set of ids of collections which have been removed from the database. */ void QOrganizerCollectionChangeSet::clearRemovedCollections() { d->m_removedCollections.clear(); } /*! Returns the list of ids of organizer collections which have been added, changed or removed from the database. The list includes information about which database operation was done. The ids and operations are ordered so that the first operation is first in the list. */ QList > QOrganizerCollectionChangeSet::modifiedCollections() const { return d->m_modifiedCollections; } /*! Clears the list of ids of organizer collections which have been added, changed or removed from the database */ void QOrganizerCollectionChangeSet::clearModifiedCollections() { d->m_modifiedCollections.clear(); } /*! Clears all flags and sets of IDs in this change set. */ void QOrganizerCollectionChangeSet::clearAll() { d->m_dataChanged = false; d->m_addedCollections.clear(); d->m_changedCollections.clear(); d->m_removedCollections.clear(); d->m_modifiedCollections.clear(); } /*! Emits the appropriate signals from the given \a engine given the state of the change set. Note that the flags and sets of IDs are not cleared after signals are emitted. */ void QOrganizerCollectionChangeSet::emitSignals(QOrganizerManagerEngine *engine) const { if (!engine) return; if (d->m_dataChanged) { emit engine->dataChanged(); } else { if (!d->m_addedCollections.isEmpty()) emit engine->collectionsAdded(d->m_addedCollections.toList()); if (!d->m_changedCollections.isEmpty()) emit engine->collectionsChanged(d->m_changedCollections.toList()); if (!d->m_removedCollections.isEmpty()) emit engine->collectionsRemoved(d->m_removedCollections.toList()); if (!d->m_modifiedCollections.isEmpty()) emit engine->collectionsModified(d->m_modifiedCollections); } } QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizercollectionchangeset.h000066400000000000000000000074331233466112000221420ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERCOLLECTIONCHANGESET_H #define QORGANIZERCOLLECTIONCHANGESET_H #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManagerEngine; class QOrganizerCollectionChangeSetData; class Q_ORGANIZER_EXPORT QOrganizerCollectionChangeSet { public: QOrganizerCollectionChangeSet(); QOrganizerCollectionChangeSet(const QOrganizerCollectionChangeSet &other); ~QOrganizerCollectionChangeSet(); QOrganizerCollectionChangeSet &operator=(const QOrganizerCollectionChangeSet &other); void setDataChanged(bool dataChanged); bool dataChanged() const; QSet addedCollections() const; void insertAddedCollection(const QOrganizerCollectionId &collectionId); void insertAddedCollections(const QList &collectionIds); void clearAddedCollections(); QSet changedCollections() const; void insertChangedCollection(const QOrganizerCollectionId &collectionId); void insertChangedCollections(const QList &collectionIds); void clearChangedCollections(); QSet removedCollections() const; void insertRemovedCollection(const QOrganizerCollectionId &collectionId); void insertRemovedCollections(const QList &collectionIds); void clearRemovedCollections(); QList > modifiedCollections() const; void clearModifiedCollections(); void clearAll(); void emitSignals(QOrganizerManagerEngine *engine) const; private: QSharedDataPointer d; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERCOLLECTIONCHANGESET_H src/organizer/qorganizercollectionchangeset_p.h000066400000000000000000000066061233466112000224620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERCOLLECTIONCHANGESET_P_H #define QORGANIZERCOLLECTIONCHANGESET_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerCollectionChangeSetData : public QSharedData { public: QOrganizerCollectionChangeSetData() : QSharedData(), m_dataChanged(false) { } QOrganizerCollectionChangeSetData(const QOrganizerCollectionChangeSetData& other) : QSharedData(other), m_dataChanged(other.m_dataChanged), m_addedCollections(other.m_addedCollections), m_changedCollections(other.m_changedCollections), m_removedCollections(other.m_removedCollections), m_modifiedCollections(other.m_modifiedCollections) { } ~QOrganizerCollectionChangeSetData() { } bool m_dataChanged; QSet m_addedCollections; QSet m_changedCollections; QSet m_removedCollections; QList > m_modifiedCollections; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERCOLLECTIONCHANGESET_P_H src/organizer/qorganizercollectionengineid.cpp000066400000000000000000000107641233466112000223170ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizercollectionengineid.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerCollectionEngineId \brief The QOrganizerCollectionEngineId class uniquely identifies an item within a particular engine plugin. \inmodule QtOrganizer \ingroup organizer-backends Clients of the Organizer API should never use this class, while every engine implementor must implement a class derived from QOrganizerCollectionEngineId. This class is provided so that engine implementors can implement their own collection ID class (which may contain arbitrary data, and which may implement the required functions in an arbitrary manner). */ /*! Cleans up any memory in use by this engine collection ID. */ QOrganizerCollectionEngineId::~QOrganizerCollectionEngineId() { } /*! \fn QOrganizerCollectionEngineId::isEqualTo(const QOrganizerCollectionEngineId *other) const Returns true if this ID is equal to the \a other; false otherwise. Note that when implementing this function, you do not have to check that the type is the same, since the function which calls this function (in QOrganizerCollectionId) does that check for you. */ /*! \fn QOrganizerCollectionEngineId::isLessThan(const QOrganizerCollectionEngineId *other) const Returns true if this id is less than the \a other; false otherwise. Note that when implementing this function, you do not have to check that the type is the same, since the function which calls this function (in QOrganizerCollectionId) does that check for you. */ /*! \fn QOrganizerCollectionEngineId::managerUri() const Returns the manager URI of the constructed manager which created the ID. If the collection which the ID identifies has not been deleted, the ID should still be valid in the manager identified by the manager URI returned by this function. */ /*! \fn QOrganizerCollectionEngineId::toString() const Serializes the id to a string. It contains all of the information required to identify a particular collection in the manager which created the ID, formatted according to the serialization format of the manager. */ /*! \fn QOrganizerCollectionEngineId::clone() const Returns a deep-copy clone of this ID. The caller takes ownership of the returned engine collection ID. */ /*! \fn QOrganizerCollectionEngineId::debugStreamOut(QDebug &dbg) const Streams this ID out to the debug stream \a dbg. */ /*! \fn QOrganizerCollectionEngineId::hash() const Returns the hash value of this ID. */ QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizercollectionengineid.h000066400000000000000000000060151233466112000217560ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERCOLLECTIONENGINEID_H #define QORGANIZERCOLLECTIONENGINEID_H #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class Q_ORGANIZER_EXPORT QOrganizerCollectionEngineId : public QSharedData { public: virtual ~QOrganizerCollectionEngineId(); virtual bool isEqualTo(const QOrganizerCollectionEngineId *other) const = 0; virtual bool isLessThan(const QOrganizerCollectionEngineId *other) const = 0; virtual QString managerUri() const = 0; virtual QOrganizerCollectionEngineId *clone() const = 0; virtual QString toString() const = 0; #ifndef QT_NO_DEBUG_STREAM // NOTE: on platforms where Qt is built without debug streams enabled, vtable will differ! virtual QDebug &debugStreamOut(QDebug &dbg) const = 0; #endif // QT_NO_DEBUG_STREAM virtual uint hash() const = 0; }; QT_END_NAMESPACE_ORGANIZER QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(QTORGANIZER_PREPEND_NAMESPACE(QOrganizerCollectionEngineId), Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif // QORGANIZERCOLLECTIONENGINEID_H src/organizer/qorganizercollectionid.cpp000066400000000000000000000205371233466112000211300ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizercollectionid.h" #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif #include #include "qorganizercollectionengineid.h" #include "qorganizermanager_p.h" #include "qorganizeritemid_p.h" #if !defined(Q_CC_MWERKS) QT_BEGIN_NAMESPACE template<> QTORGANIZER_PREPEND_NAMESPACE(QOrganizerCollectionEngineId) *QSharedDataPointer::clone() { return d ? d->clone() : 0; } QT_END_NAMESPACE #endif // Q_CC_MWERKS QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerCollectionId \brief The QOrganizerCollectionId class provides information that uniquely identifies a collection in a particular manager. \inmodule QtOrganizer \ingroup organizer-main It consists of a manager URI which identifies the manager which manages the collection, and the ID of the collection in that manager. */ /*! Constructs a new, null collection ID. */ QOrganizerCollectionId::QOrganizerCollectionId() : d(0) { } /*! Cleans up the memory in use by the collection ID. */ QOrganizerCollectionId::~QOrganizerCollectionId() { } /*! Constructs a manager-unique ID which wraps the given engine-unique \a engineId. This ID takes ownership of the \a engineId and will delete it when this ID is destructed. Engine implementors must not delete the \a engineId, otherwise undefined behaviour will occur. */ QOrganizerCollectionId::QOrganizerCollectionId(QOrganizerCollectionEngineId *engineId) : d(engineId) { } /*! Constructs a new collection ID as a copy of \a other. */ QOrganizerCollectionId::QOrganizerCollectionId(const QOrganizerCollectionId &other) : d(other.d) { } /*! Assigns the collection ID to be equal to \a other. */ QOrganizerCollectionId &QOrganizerCollectionId::operator=(const QOrganizerCollectionId &other) { d = other.d; return *this; } /*! Returns true if it has the same manager URI and ID as \a other. */ bool QOrganizerCollectionId::operator==(const QOrganizerCollectionId &other) const { if (d == other.d) return true; if (d && other.d) return d->managerUri() == other.d->managerUri() && d->isEqualTo(other.d); return false; } /*! Returns true if either the manager URI or ID of it is different to that of \a other. */ bool QOrganizerCollectionId::operator!=(const QOrganizerCollectionId &other) const { return !(*this == other); } /*! Returns true if this ID is less than the \a other. This ID will be considered less than the \a other if the manager URI of this ID is alphabetically less than the manager URI of the \a other. If both IDs have the same manager URI, this ID will be considered less than the \a other if the engine ID is less than the engine ID of the \a other. The invalid, empty ID consists of an empty manager URI and a null engine ID, and hence will be less than any non-invalid ID. This operator is provided primarily to allow use of a QOrganizerCollectionId as a key in a QMap. */ bool QOrganizerCollectionId::operator<(const QOrganizerCollectionId &other) const { // a null id is always less than a non-null id. if (d == 0 && other.d != 0) return true; if (d && other.d) { // ensure they're of the same type (and therefore comparable) if (d->managerUri() == other.d->managerUri()) return d->isLessThan(other.d); // not the same type? just compare the manager uri. return d->managerUri() < other.d->managerUri(); } return false; } /*! Returns true if the engine ID part of this ID is null (default constructed); otherwise, returns false. */ bool QOrganizerCollectionId::isNull() const { return (d == 0); } /*! \relates QOrganizerCollectionId Returns the hash value for \a key. */ uint qHash(const QOrganizerCollectionId &key) { if (key.d) return key.d->hash(); return 0; } #ifndef QT_NO_DEBUG_STREAM /*! \relates QOrganizerCollectionId Outputs \a id to the debug stream \a dbg. */ QDebug operator<<(QDebug dbg, const QOrganizerCollectionId &id) { dbg.nospace() << "QOrganizerCollectionId("; if (id.isNull()) dbg.nospace() << "(null))"; else id.d->debugStreamOut(dbg) << ")"; return dbg.maybeSpace(); } #endif // QT_NO_DEBUG_STREAM #ifndef QT_NO_DATASTREAM /*! \relates QOrganizerCollectionId Streams \a collectionId to the data stream \a out. */ QDataStream &operator<<(QDataStream &out, const QOrganizerCollectionId &collectionId) { out << (collectionId.toString()); return out; } /*! \relates QOrganizerCollectionId Streams \a collectionId in from the data stream \a in. */ QDataStream &operator>>(QDataStream &in, QOrganizerCollectionId &collectionId) { QString idString; in >> idString; collectionId = QOrganizerCollectionId::fromString(idString); return in; } #endif // QT_NO_DATASTREAM /*! Returns the URI of the manager which contains the collection identified by this ID. */ QString QOrganizerCollectionId::managerUri() const { return d ? d->managerUri() : QString::null; } /*! Serializes the ID to a string. The format of the string will be: "qtorganizer:managerName:constructionParams:serializedEngineLocalItemId". */ QString QOrganizerCollectionId::toString() const { QString mgrName; QMap params; QString engineId; if (d) { QOrganizerManager::parseUri(d->managerUri(), &mgrName, ¶ms); engineId = d->toString(); } // having extracted the params the name, we now need to build a new string. return buildIdString(mgrName, params, engineId); } /*! Deserializes the given \a idString. Returns a default-constructed (null) collection ID if the given \a idString is not a valid, serialized collection ID, or if the manager engine from which the ID refers to could not be found. */ QOrganizerCollectionId QOrganizerCollectionId::fromString(const QString &idString) { QString managerName; QMap params; QString engineIdString; if (!parseIdString(idString, &managerName, ¶ms, &engineIdString)) return QOrganizerCollectionId(); // invalid idString given. QOrganizerCollectionEngineId *engineId = QOrganizerManagerData::createEngineCollectionId(managerName, params, engineIdString); return QOrganizerCollectionId(engineId); } QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizercollectionid.h000066400000000000000000000076511233466112000205770ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERCOLLECTIONID_H #define QORGANIZERCOLLECTIONID_H #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManagerEngine; class QOrganizerCollectionEngineId; class Q_ORGANIZER_EXPORT QOrganizerCollectionId { public: QOrganizerCollectionId(); explicit QOrganizerCollectionId(QOrganizerCollectionEngineId *engineId); QOrganizerCollectionId(const QOrganizerCollectionId &other); ~QOrganizerCollectionId(); QOrganizerCollectionId &operator=(const QOrganizerCollectionId &other); bool operator==(const QOrganizerCollectionId &other) const; bool operator!=(const QOrganizerCollectionId &other) const; bool operator<(const QOrganizerCollectionId &other) const; bool isNull() const; QString managerUri() const; QString toString() const; static QOrganizerCollectionId fromString(const QString &idString); private: QSharedDataPointer d; #ifndef QT_NO_DEBUG_STREAM Q_ORGANIZER_EXPORT friend QDebug operator<<(QDebug dbg, const QOrganizerCollectionId &id); #endif // QT_NO_DEBUG_STREAM Q_ORGANIZER_EXPORT friend uint qHash(const QOrganizerCollectionId &key); friend class QOrganizerManagerEngine; }; Q_ORGANIZER_EXPORT uint qHash(const QOrganizerCollectionId &key); #ifndef QT_NO_DATASTREAM Q_ORGANIZER_EXPORT QDataStream &operator<<(QDataStream &out, const QOrganizerCollectionId &collectionId); Q_ORGANIZER_EXPORT QDataStream &operator>>(QDataStream &in, QOrganizerCollectionId &collectionId); #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM Q_ORGANIZER_EXPORT QDebug operator<<(QDebug dbg, const QOrganizerCollectionId &id); #endif // QT_NO_DEBUG_STREAM QT_END_NAMESPACE_ORGANIZER QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(QTORGANIZER_PREPEND_NAMESPACE(QOrganizerCollectionId), Q_MOVABLE_TYPE); QT_END_NAMESPACE Q_DECLARE_METATYPE(QTORGANIZER_PREPEND_NAMESPACE(QOrganizerCollectionId)) #endif // QORGANIZERCOLLECTIONID_H src/organizer/qorganizerglobal.h000066400000000000000000000057351233466112000173700ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QTORGANIZERGLOBAL_H #define QTORGANIZERGLOBAL_H #include #if defined(QT_NAMESPACE) # define QTORGANIZER_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::QtOrganizer::name # define QT_BEGIN_NAMESPACE_ORGANIZER namespace QT_NAMESPACE { namespace QtOrganizer { # define QT_END_NAMESPACE_ORGANIZER } } # define QTORGANIZER_USE_NAMESPACE using namespace QT_NAMESPACE; using namespace QtOrganizer; #else # define QTORGANIZER_PREPEND_NAMESPACE(name) ::QtOrganizer::name # define QT_BEGIN_NAMESPACE_ORGANIZER namespace QtOrganizer { # define QT_END_NAMESPACE_ORGANIZER } # define QTORGANIZER_USE_NAMESPACE using namespace QtOrganizer; #endif #ifndef QT_STATIC # if defined(QT_BUILD_ORGANIZER_LIB) # define Q_ORGANIZER_EXPORT Q_DECL_EXPORT # else # define Q_ORGANIZER_EXPORT Q_DECL_IMPORT # endif #else # define Q_ORGANIZER_EXPORT #endif #define QTORGANIZER_BACKEND_VERSION QString(QStringLiteral("org.qt-project.Qt.organizer.backendVersion")) QT_BEGIN_NAMESPACE_ORGANIZER QT_END_NAMESPACE_ORGANIZER #endif // QTORGANIZERGLOBAL_H src/organizer/qorganizeritem.cpp000066400000000000000000000612311233466112000174120ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritem.h" #include "qorganizeritem_p.h" #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif #include "qorganizeritemdetail_p.h" #include "qorganizeritemdetails.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \macro Q_DECLARE_CUSTOM_ORGANIZER_ITEM \relates QOrganizerItem Macro for simplifying declaring convenience leaf classes for QOrganizerItem. The first argument is the name of the class, and the second argument is the item type. */ /*! \class QOrganizerItem \brief The QOrganizerItem class is the base class of an event, todo, note, or journal entry. \inmodule QtOrganizer \ingroup organizer-main A QOrganizerItem object has an id and a collection of details (like a start date and location), as well as a collection id which identifies which QOrganizerCollection the item is part of in a manager. Each detail (which can have multiple fields) is stored in an appropriate subclass of QOrganizerItemDetail, and the QOrganizerItem allows retrieving these details in various ways. QOrganizerItemExtendedDetail is supposed to be used to store user specific details that are not pre-defined in the detal leaf classes. Most clients will want to use the convenient subclasses of QOrganizerItem (i.e., QOrganizerEvent (and QOrganizerEventOccurence), QOrganizerTodo (and QOrganizerTodoOccurence), QOrganizerJournal and QOrganizerNote) instead of manipulating instances of QOrganizerItem directly. A QOrganizerItem instance represents the in-memory version of an organizer item. It is possible for the contents of a QOrganizerItem to change independently of the contents that are stored persistently in a QOrganizerManager. A QOrganizerItem has an id associated with it when it is first retrieved from a QOrganizerManager, or after it has been first saved, and this allows clients to track changes using the signals in QOrganizerManager. When saved in a manager, every item is placed into a QOrganizerCollection in that manager, according to the collection id set in the item prior to save (or the default collection if no collection id was set in the item). Different QOrganizerManagers may require an item to have certain details saved in it before it can be stored in that manager. By default, every item must have a QOrganizerItemType detail which identifies the type of the item. Different subclasses of QOrganizerItem (i.e., QOrganizerEvent (and QOrganizerEventOccurence), QOrganizerTodo (and QOrganizerTodoOccurence), QOrganizerJournal and QOrganizerNote) may have other mandatory details, depending on the manager. \sa QOrganizerManager, QOrganizerItemDetail */ /*! \fn QOrganizerItem::operator!=(const QOrganizerItem &other) const Returns true if this organizer item's id or details are different to those of the \a other organizer item. */ /*! Construct an empty organizer item. The organizer item will have an empty item ID, and an empty collection ID. It's of type \l QOrganizerItemType::TypeUndefined. */ QOrganizerItem::QOrganizerItem() : d(new QOrganizerItemData) { QOrganizerItemType organizeritemType; organizeritemType.setType(QOrganizerItemType::TypeUndefined); d->m_details.append(organizeritemType); } /*! Constructs an item that is a copy of \a other. */ QOrganizerItem::QOrganizerItem(const QOrganizerItem &other) : d(other.d) { } /*! \internal Constructs a new, empty item of the given type \a type. */ QOrganizerItem::QOrganizerItem(QOrganizerItemType::ItemType type) : d(new QOrganizerItemData) { QOrganizerItemType organizeritemType; organizeritemType.setType(type); d->m_details.append(organizeritemType); } /*! \internal Constructs an item that is a copy of \a other if \a other is of the expected type identified by \a expectedType, else constructs a new, empty item of the type identified by the \a expectedType. The \a expectedType pointer must be valid for the lifetime of the program. */ QOrganizerItem::QOrganizerItem(const QOrganizerItem &other, QOrganizerItemType::ItemType expectedType) { if (other.type() == expectedType) { d = other.d; } else { d = new QOrganizerItemData; setType(expectedType); } } /*! \internal Assigns this item to \a other if the type of \a other is that identified by the given \a expectedType, else assigns this item to be a new, empty item of the type identified by the given \a expectedType */ QOrganizerItem &QOrganizerItem::assign(const QOrganizerItem &other, QOrganizerItemType::ItemType expectedType) { if (this != &other) { if (other.type() == expectedType) { d = other.d; } else { d = new QOrganizerItemData; setType(expectedType); } } return *this; } /*! Returns true if this QOrganizerItem is empty, false if not. Note that the type detail of the organizer item is irrelevant. */ bool QOrganizerItem::isEmpty() const { return (d->m_details.count() == 1); } /*! Removes all details of the organizer item, and resets the type to be \l QOrganizerItemType::TypeUndefined. */ void QOrganizerItem::clearDetails() { d->m_details.clear(); QOrganizerItemType organizeritemType; organizeritemType.setType(QOrganizerItemType::TypeUndefined); d->m_details.append(organizeritemType); } /*! Replace the contents of this organizer item with the \a other. */ QOrganizerItem &QOrganizerItem::operator=(const QOrganizerItem &other) { d = other.d; return *this; } /*! Frees the memory used by this item. */ QOrganizerItem::~QOrganizerItem() { } /*! Returns the QOrganizerItemId that identifies this organizer item. This may have been set when the organizer item was retrieved from a particular manager, or when the organizer item was first saved in a manager. The QOrganizerItemId is only valid within a specific manager. See \l QOrganizerManager::saveItem() for more information. */ QOrganizerItemId QOrganizerItem::id() const { return d->m_id; } /*! Returns the id of the collection which this item is part of, in the manager in which the item has been saved, if the item has previously been saved in a manager. If the item has not previously been saved in a manager, this function will return the id of the collection into which the client wishes the item to be saved when \l QOrganizerManager::saveItem() is called, which is set by calling \l setId(); otherwise, returns a null id. An item always belongs to exactly one collection in a particular manager after it has been saved in the manager. If the item has previously been saved in the manager, in a particular collection, and the client sets the collection id of the item to the id of a different collection within that manager and then resaves the item, the item will be moved from its original collection into the specified collection if the move operation is supported by the manager; otherwise, the \l QOrganizerManager::saveItem() operation will fail and calling \l QOrganizerManager::error() will return \c QOrganizerManager::NotSupportedError. */ QOrganizerCollectionId QOrganizerItem::collectionId() const { return d->m_collectionId; } /*! Sets the id of the collection into which the client wishes the item to be saved to the given \a collectionId. If the given \a collectionId is the null collection id, the client is specifying that the item should be saved into the collection in which the item is already saved (if the item has previously been saved in the manager, without having been removed since), or into the default collection of the manager (if the item has not previously been saved in the manager, or has been removed since the last time it was saved). If the item has previously been saved in a particular manager, and the given \a collectionId is the id of a different collection than the one which the item is currently a part of in that manager, saving the item with \l QOrganizerManager::saveItem() will move the item from its original collection to the collection whose id is \a collectionId, if \a collectionId identifies a valid collection and the operation is supported by the manager. */ void QOrganizerItem::setCollectionId(const QOrganizerCollectionId &collectionId) { d->m_collectionId = collectionId; } /*! Sets the id of this organizer item to \a id. Note that this only affects this object, not any corresponding structures stored by a QOrganizerManager. If you change the id of a organizer item and save the organizer item in a manager, the previously existing organizer item will still exist. You can do this to create copies (possibly modified) of an existing organizer item, or to save a organizer item in a different manager. \sa QOrganizerManager::saveItem() */ void QOrganizerItem::setId(const QOrganizerItemId &id) { d->m_id = id; // TODO - reset collection id? } /*! Returns the first detail stored in the organizer item with the given \a detailType. If the given \a detailType is TypeUndefined, it returns the first detail found. */ QOrganizerItemDetail QOrganizerItem::detail(QOrganizerItemDetail::DetailType detailType) const { if (detailType == QOrganizerItemDetail::TypeUndefined) return d->m_details.first(); for (int i = 0; i < d->m_details.size(); i++) { const QOrganizerItemDetail &existing = d->m_details.at(i); if (existing.d->m_detailType == detailType) return existing; } return QOrganizerItemDetail(); } /*! Returns a list of details with the given \a detailType. If the given \a detailType is of TypeUndefined, it returns all the details. */ QList QOrganizerItem::details(QOrganizerItemDetail::DetailType detailType) const { if (detailType == QOrganizerItemDetail::TypeUndefined) return d->m_details; QList sublist; for (int i = 0; i < d->m_details.size(); i++) { const QOrganizerItemDetail &existing = d->m_details.at(i); if (existing.d->m_detailType == detailType) sublist.append(existing); } return sublist; } /*! Saves the given \a detail in the list of stored details, and sets the detail's id. If another detail of the same type and id has been previously saved in this organizer item, that detail is overwritten. Otherwise, a new id is generated and set in the detail, and the detail is added to the organizer item. If \a detail is a QOrganizerItemType, the existing organizer item type will be overwritten with \a detail. There is never more than one organizer item type in a organizer item. Returns true if the detail was saved successfully, otherwise returns false. Note that the caller retains ownership of the detail. */ bool QOrganizerItem::saveDetail(QOrganizerItemDetail *detail) { if (!detail) return false; // we only allow one instance of these details per item if (detail->d.constData()->m_detailType == QOrganizerItemDetail::TypeItemType || detail->d.constData()->m_detailType == QOrganizerItemDetail::TypeDescription || detail->d.constData()->m_detailType == QOrganizerItemDetail::TypeDisplayLabel || detail->d.constData()->m_detailType == QOrganizerItemDetail::TypeClassification || detail->d.constData()->m_detailType == QOrganizerItemDetail::TypeVersion) { for (int i = 0; i < d.constData()->m_details.size(); i++) { if (detail->d.constData()->m_detailType == d.constData()->m_details.at(i).d.constData()->m_detailType) { d->m_details.replace(i, *detail); return true; } } // doesn't already exist; append it. d->m_details.append(*detail); return true; } // try to find the "old version" of this field // ie, the one with the same type and id, but different value or attributes. for (int i = 0; i < d.constData()->m_details.size(); i++) { const QOrganizerItemDetail& curr = d.constData()->m_details.at(i); if (detail->d.constData()->m_detailType == curr.d.constData()->m_detailType && detail->d.constData()->m_id == curr.d.constData()->m_id) { // update the detail constraints of the supplied detail // Found the old version. Replace it with this one. d->m_details[i] = *detail; return true; } } // this is a new detail! add it to the organizer item. d->m_details.append(*detail); return true; } /*! Removes the \a detail from the organizer item. The detail in the organizer item which has the same key as that of the given \a detail will be removed if it exists. Only the key is used for comparison - that is, the information in the detail may be different. Returns true if the detail was removed successfully, false if an error occurred. Note that the caller retains ownership of the detail. */ bool QOrganizerItem::removeDetail(QOrganizerItemDetail *detail) { if (!detail) return false; // find the detail stored in the organizer item which has the same key as the detail argument int removeIndex = -1; for (int i = 0; i < d.constData()->m_details.size(); i++) { if (d.constData()->m_details.at(i).key() == detail->key()) { removeIndex = i; break; } } // make sure the detail exists (in some form) in the organizer item. if (removeIndex < 0) return false; // Type -detail is specific case which cannot be deleted if (QOrganizerItemDetail::TypeItemType == detail->d.constData()->m_detailType) return false; if (!d.constData()->m_details.contains(*detail)) return false; // then remove the detail. d->m_details.removeAt(removeIndex); return true; } /*! Returns true if this organizer item is equal to the \a other organizer item, false if either the id, collection id or stored details are not the same. */ bool QOrganizerItem::operator==(const QOrganizerItem &other) const { if (d == other.d) return true; if (other.d->m_id != d->m_id || other.d->m_collectionId != d->m_collectionId || d->m_details.size() != other.d->m_details.size()) { return false; } QList searchList(d->m_details); foreach (const QOrganizerItemDetail &detail, other.d->m_details) { if (!searchList.removeOne(detail)) return false; } return true; } /*! \relates QOrganizerItem Returns the hash value for \a key. */ uint qHash(const QOrganizerItem &key) { uint hash = qHash(key.id()); hash += qHash(key.collectionId()); foreach (const QOrganizerItemDetail &detail, key.details()) hash += qHash(detail); return hash; } #ifndef QT_NO_DEBUG_STREAM /*! \relates QOrganizerItem Streams the \a item to the given debug stream \a dbg, and returns the stream. */ QDebug operator<<(QDebug dbg, const QOrganizerItem &item) { dbg.nospace() << "QOrganizerItem(" << item.id() << ") in collection(" << item.collectionId() << ")"; foreach (const QOrganizerItemDetail& detail, item.details()) dbg.space() << '\n' << detail; return dbg.maybeSpace(); } #endif // QT_NO_DEBUG_STREAM #ifndef QT_NO_DATASTREAM /*! \relates QOrganizerItem Writes \a item to the stream \a out. */ QDataStream &operator<<(QDataStream &out, const QOrganizerItem &item) { quint8 formatVersion = 1; // Version of QDataStream format for QOrganizerItem out << formatVersion << item.id().toString() << item.collectionId().toString() << item.details(); return out; } /*! \relates QOrganizerItem Reads an item from stream \a in into \a item. */ QDataStream &operator>>(QDataStream &in, QOrganizerItem &item) { quint8 formatVersion; in >> formatVersion; if (formatVersion == 1) { item = QOrganizerItem(); QString itemIdString; QString collectionIdString; QList details; in >> itemIdString >> collectionIdString >> details; item.setId(QOrganizerItemId::fromString(itemIdString)); item.setCollectionId(QOrganizerCollectionId::fromString(collectionIdString)); item.d->m_details = details; } else { in.setStatus(QDataStream::ReadCorruptData); } return in; } #endif // QT_NO_DATASTREAM /*! Returns the type of the organizer item. */ QOrganizerItemType::ItemType QOrganizerItem::type() const { // type is always the first detail QOrganizerItemType type = static_cast(d->m_details.at(0)); return type.type(); } /*! Sets the type of the organizer item to the given \a type. */ void QOrganizerItem::setType(QOrganizerItemType::ItemType type) { if (d->m_details.isEmpty()) { QOrganizerItemType organizeritemType; organizeritemType.setType(type); d->m_details.append(organizeritemType); } else { // type is always the first detail d->m_details.first().setValue(QOrganizerItemType::FieldType, type); } } /*! Returns the display label of the item. */ QString QOrganizerItem::displayLabel() const { QOrganizerItemDisplayLabel dl = detail(QOrganizerItemDetail::TypeDisplayLabel); return dl.label(); } /*! Sets the display label of the item to \a label. */ void QOrganizerItem::setDisplayLabel(const QString &label) { QOrganizerItemDisplayLabel dl = detail(QOrganizerItemDetail::TypeDisplayLabel); dl.setLabel(label); saveDetail(&dl); } /*! Returns the human-readable description of the item. */ QString QOrganizerItem::description() const { QOrganizerItemDescription descr = detail(QOrganizerItemDetail::TypeDescription); return descr.description(); } /*! Sets the human-readable description of the item to \a description. */ void QOrganizerItem::setDescription(const QString &description) { QOrganizerItemDescription descr = detail(QOrganizerItemDetail::TypeDescription); descr.setDescription(description); saveDetail(&descr); } /*! Returns the list of comments of this item. */ QStringList QOrganizerItem::comments() const { QStringList commentList; for (int i = 0; i < d->m_details.size(); ++i) { const QOrganizerItemDetail &detail = d->m_details.at(i); if (detail.d->m_detailType == QOrganizerItemDetail::TypeComment) commentList.append(detail.d->m_values.value(QOrganizerItemComment::FieldComment).toString()); } return commentList; } /*! Removes all comments of this item. */ void QOrganizerItem::clearComments() { d->removeOnly(QOrganizerItemDetail::TypeComment); } /*! Sets the list of comments associated with the item to \a comments. */ void QOrganizerItem::setComments(const QStringList &comments) { d->removeOnly(QOrganizerItemDetail::TypeComment); foreach (const QString &comment, comments) addComment(comment); } /*! Adds the \a comment to this item */ void QOrganizerItem::addComment(const QString &comment) { QOrganizerItemComment detail; detail.setComment(comment); saveDetail(&detail); } /*! Returns the list of tags for this item. */ QStringList QOrganizerItem::tags() const { QStringList tagList; for (int i = 0; i < d->m_details.size(); ++i) { const QOrganizerItemDetail &detail = d->m_details.at(i); if (detail.d->m_detailType == QOrganizerItemDetail::TypeTag) tagList.append(detail.d->m_values.value(QOrganizerItemTag::FieldTag).toString()); } return tagList; } /*! Removes all tags associated with the item. */ void QOrganizerItem::clearTags() { d->removeOnly(QOrganizerItemDetail::TypeTag); } /*! Adds the \a tag to this item. */ void QOrganizerItem::addTag(const QString &tag) { QOrganizerItemTag tagDetail; tagDetail.setTag(tag); saveDetail(&tagDetail); } /*! Sets the list of tags associated with the item to \a tags. */ void QOrganizerItem::setTags(const QStringList &tags) { d->removeOnly(QOrganizerItemDetail::TypeTag); foreach (const QString &tag, tags) addTag(tag); } /*! Returns the globally unique identifier which identifies this item, which is used for synchronization purposes. */ QString QOrganizerItem::guid() const { QOrganizerItemGuid guid = detail(QOrganizerItemDetail::TypeGuid); return guid.guid(); } /*! Sets the item's globally unique identifier to \a guid. */ void QOrganizerItem::setGuid(const QString &guid) { QOrganizerItemGuid guidDetail = detail(QOrganizerItemDetail::TypeGuid); guidDetail.setGuid(guid); saveDetail(&guidDetail); } /*! Returns the data of the extended detail with the given \a name. */ QVariant QOrganizerItem::data(const QString &name) const { for (int i = 0; i < d->m_details.size(); ++i) { const QOrganizerItemDetail &detail = d->m_details.at(i); if (detail.d->m_detailType == QOrganizerItemDetail::TypeExtendedDetail && detail.d->m_values.value(QOrganizerItemExtendedDetail::FieldName).toString() == name) { return detail.d->m_values.value(QOrganizerItemExtendedDetail::FieldData); } } return QVariant(); } /*! Sets the \a data of a extended detail with the given \a name. */ void QOrganizerItem::setData(const QString &name, const QVariant &data) { for (int i = 0; i < d->m_details.size(); ++i) { const QOrganizerItemDetail &detail = d->m_details.at(i); if (detail.d->m_detailType == QOrganizerItemDetail::TypeExtendedDetail && detail.d->m_values.value(QOrganizerItemExtendedDetail::FieldName).toString() == name) { QOrganizerItemDetail newDetail = d->m_details.at(i); newDetail.d->m_values.insert(QOrganizerItemExtendedDetail::FieldData, data); saveDetail(&newDetail); return; } } QOrganizerItemExtendedDetail newDetail; newDetail.setName(name); newDetail.setData(data); saveDetail(&newDetail); } /*! \internal */ void QOrganizerItemData::removeOnly(QOrganizerItemDetail::DetailType detailType) { QList::iterator dit = m_details.begin(); while (dit != m_details.end()) { // XXX this doesn't check type or display label if (dit->type() == detailType) dit = m_details.erase(dit); else ++dit; } } /*! \internal */ void QOrganizerItemData::removeOnly(const QSet &detailTypes) { QList::iterator dit = m_details.begin(); while (dit != m_details.end()) { // XXX this doesn't check type or display label if (detailTypes.contains(dit->type())) dit = m_details.erase(dit); else ++dit; } } QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizeritem.h000066400000000000000000000130551233466112000170600ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEM_H #define QORGANIZERITEM_H #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemData; class Q_ORGANIZER_EXPORT QOrganizerItem { public: QOrganizerItem(); QOrganizerItem(const QOrganizerItem &other); ~QOrganizerItem(); QOrganizerItem &operator=(const QOrganizerItem &other); bool operator==(const QOrganizerItem &other) const; bool operator!=(const QOrganizerItem &other) const {return !(other == *this);} QOrganizerItemId id() const; void setId(const QOrganizerItemId &id); QOrganizerCollectionId collectionId() const; void setCollectionId(const QOrganizerCollectionId &collectionId); bool isEmpty() const; void clearDetails(); QOrganizerItemDetail detail(QOrganizerItemDetail::DetailType detailType = QOrganizerItemDetail::TypeUndefined) const; QList details(QOrganizerItemDetail::DetailType detailType = QOrganizerItemDetail::TypeUndefined) const; bool saveDetail(QOrganizerItemDetail *detail); bool removeDetail(QOrganizerItemDetail *detail); QOrganizerItemType::ItemType type() const; void setType(QOrganizerItemType::ItemType type); QString displayLabel() const; void setDisplayLabel(const QString &label); QString description() const; void setDescription(const QString &description); QStringList comments() const; void clearComments(); void setComments(const QStringList &comments); void addComment(const QString &comment); QStringList tags() const; void clearTags(); void addTag(const QString &tag); void setTags(const QStringList &tags); QString guid() const; void setGuid(const QString &guid); QVariant data(const QString &name) const; void setData(const QString &name, const QVariant &data); protected: explicit QOrganizerItem(QOrganizerItemType::ItemType type); QOrganizerItem(const QOrganizerItem &other, QOrganizerItemType::ItemType expectedType); QOrganizerItem &assign(const QOrganizerItem &other, QOrganizerItemType::ItemType expectedType); protected: friend class QOrganizerItemData; friend class QOrganizerManager; friend class QOrganizerManagerData; friend class QOrganizerManagerEngine; #ifndef QT_NO_DATASTREAM Q_ORGANIZER_EXPORT friend QDataStream &operator<<(QDataStream &out, const QOrganizerItem &item); Q_ORGANIZER_EXPORT friend QDataStream &operator>>(QDataStream &in, QOrganizerItem &item); #endif // QT_NO_DATASTREAM QSharedDataPointer d; }; Q_ORGANIZER_EXPORT uint qHash(const QOrganizerItem &key); #ifndef QT_NO_DATASTREAM Q_ORGANIZER_EXPORT QDataStream &operator<<(QDataStream &out, const QOrganizerItem &item); Q_ORGANIZER_EXPORT QDataStream &operator>>(QDataStream &in, QOrganizerItem &item); #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM Q_ORGANIZER_EXPORT QDebug operator<<(QDebug dbg, const QOrganizerItem &item); #endif // QT_NO_DEBUG_STREAM #define Q_DECLARE_CUSTOM_ORGANIZER_ITEM(className, typeConstant) \ className() : QOrganizerItem(typeConstant) {} \ className(const QOrganizerItem &other) : QOrganizerItem(other, typeConstant) {} \ className& operator=(const QOrganizerItem &other) {assign(other, typeConstant); return *this;} QT_END_NAMESPACE_ORGANIZER QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(QTORGANIZER_PREPEND_NAMESPACE(QOrganizerItem), Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif // QORGANIZERITEM_H src/organizer/qorganizeritem_p.h000066400000000000000000000064111233466112000173750ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEM_P_H #define QORGANIZERITEM_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemData : public QSharedData { public: QOrganizerItemData() : QSharedData() { } QOrganizerItemData(const QOrganizerItemData &other) : QSharedData(other) , m_id(other.m_id) , m_collectionId(other.m_collectionId) , m_details(other.m_details) { } ~QOrganizerItemData() {} void removeOnly(QOrganizerItemDetail::DetailType detailType); void removeOnly(const QSet &detailTypes); // Trampoline static QSharedDataPointer &itemData(QOrganizerItem &item) {return item.d;} QOrganizerItemId m_id; QOrganizerCollectionId m_collectionId; QList m_details; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEM_P_H src/organizer/qorganizeritemchangeset.cpp000066400000000000000000000212321233466112000212710ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemchangeset.h" #include "qorganizeritemchangeset_p.h" #include "qorganizermanagerengine.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemChangeSet \brief The QOrganizerItemChangeSet class provides a simple API to simplify the emission of state-change signals for items from QOrganizerManagerEngine implementations. \inmodule QtOrganizer \ingroup organizer-backends This class should only be used by backend developers. */ /*! Constructs a new change set. */ QOrganizerItemChangeSet::QOrganizerItemChangeSet() : d(new QOrganizerItemChangeSetData) { } /*! Constructs a copy of the \a other change set. */ QOrganizerItemChangeSet::QOrganizerItemChangeSet(const QOrganizerItemChangeSet &other) : d(other.d) { } /*! Frees the memory used by this change set. */ QOrganizerItemChangeSet::~QOrganizerItemChangeSet() { } /*! Assigns this change set to be equal to \a other. */ QOrganizerItemChangeSet &QOrganizerItemChangeSet::operator=(const QOrganizerItemChangeSet &other) { d = other.d; return *this; } /*! Sets the data changed flag to \a dataChanged. If this is set to true prior to calling emitSignals(), only the QOrganizerManagerEngine::dataChanged() signal will be emitted; otherwise, the appropriate finer-grained signals will be emitted. */ void QOrganizerItemChangeSet::setDataChanged(bool dataChanged) { d->m_dataChanged = dataChanged; } /*! Returns the value of the data changed flag. */ bool QOrganizerItemChangeSet::dataChanged() const { return d->m_dataChanged; } /*! Returns the set of IDs of organizer items which have been added to the database. */ QSet QOrganizerItemChangeSet::addedItems() const { return d->m_addedItems; } /*! Inserts the given \a itemId into the set of ids of organizer items which have been added to the database. */ void QOrganizerItemChangeSet::insertAddedItem(const QOrganizerItemId &itemId) { d->m_addedItems.insert(itemId); d->m_modifiedItems.append(QPair(itemId, QOrganizerManager::Add)); } /*! Inserts each of the given \a itemIds into the set of IDs of organizer items which have been added to the database. */ void QOrganizerItemChangeSet::insertAddedItems(const QList &itemIds) { foreach (const QOrganizerItemId &id, itemIds) { d->m_addedItems.insert(id); d->m_modifiedItems.append(QPair(id, QOrganizerManager::Add)); } } /*! Clears the set of IDs of organizer items which have been added to the database. */ void QOrganizerItemChangeSet::clearAddedItems() { d->m_addedItems.clear(); } /*! Returns the set of IDs of organizer items which have been changed in the database. */ QSet QOrganizerItemChangeSet::changedItems() const { return d->m_changedItems; } /*! Inserts the given \a itemId into the set of IDs of organizer items which have been changed in the database. */ void QOrganizerItemChangeSet::insertChangedItem(const QOrganizerItemId &itemId) { d->m_changedItems.insert(itemId); d->m_modifiedItems.append(QPair(itemId, QOrganizerManager::Change)); } /*! Inserts each of the given \a itemIds into the set of IDs of organizer items which have been changed in the database. */ void QOrganizerItemChangeSet::insertChangedItems(const QList &itemIds) { foreach (const QOrganizerItemId &id, itemIds) { d->m_changedItems.insert(id); d->m_modifiedItems.append(QPair(id, QOrganizerManager::Change)); } } /*! Clears the set of IDs of organizer items which have been changed in the database. */ void QOrganizerItemChangeSet::clearChangedItems() { d->m_changedItems.clear(); } /*! Returns the set of IDs of organizer items which have been removed from the database. */ QSet QOrganizerItemChangeSet::removedItems() const { return d->m_removedItems; } /*! Inserts the given \a itemId into the set of IDs of organizer items which have been removed from the database. */ void QOrganizerItemChangeSet::insertRemovedItem(const QOrganizerItemId &itemId) { d->m_removedItems.insert(itemId); d->m_modifiedItems.append(QPair(itemId, QOrganizerManager::Remove)); } /*! Inserts each of the given \a itemIds into the set of IDs of organizer items which have been removed from the database. */ void QOrganizerItemChangeSet::insertRemovedItems(const QList &itemIds) { foreach (const QOrganizerItemId &id, itemIds) { d->m_removedItems.insert(id); d->m_modifiedItems.append(QPair(id, QOrganizerManager::Remove)); } } /*! Clears the set of IDs of organizer items which have been removed from the database. */ void QOrganizerItemChangeSet::clearRemovedItems() { d->m_removedItems.clear(); } /*! Returns the list of ids of organizer items which have been added, changed or removed from the database. The list includes information about which database operation was done. The ids and operations are ordered so that the first operation is first in the list. */ QList > QOrganizerItemChangeSet::modifiedItems() const { return d->m_modifiedItems; } /*! Clears the list of ids of organizer items which have been added, changed or removed from the database */ void QOrganizerItemChangeSet::clearModifiedItems() { d->m_modifiedItems.clear(); } /*! Clears all flags and sets of IDs in this change set. */ void QOrganizerItemChangeSet::clearAll() { d->m_dataChanged = false; d->m_addedItems.clear(); d->m_changedItems.clear(); d->m_removedItems.clear(); d->m_modifiedItems.clear(); } /*! Emits the appropriate signals from the given \a engine given the state of the change set. Note that the flags and sets of IDs are not cleared after signals are emitted. */ void QOrganizerItemChangeSet::emitSignals(QOrganizerManagerEngine *engine) const { if (!engine) return; if (d->m_dataChanged) { emit engine->dataChanged(); } else { if (!d->m_addedItems.isEmpty()) emit engine->itemsAdded(d->m_addedItems.toList()); if (!d->m_changedItems.isEmpty()) emit engine->itemsChanged(d->m_changedItems.toList()); if (!d->m_removedItems.isEmpty()) emit engine->itemsRemoved(d->m_removedItems.toList()); if (!d->m_modifiedItems.isEmpty()) emit engine->itemsModified(d->m_modifiedItems); } } QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizeritemchangeset.h000066400000000000000000000070311233466112000207370ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMCHANGESET_H #define QORGANIZERITEMCHANGESET_H #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManagerEngine; class QOrganizerItemChangeSetData; class Q_ORGANIZER_EXPORT QOrganizerItemChangeSet { public: QOrganizerItemChangeSet(); QOrganizerItemChangeSet(const QOrganizerItemChangeSet &other); ~QOrganizerItemChangeSet(); QOrganizerItemChangeSet &operator=(const QOrganizerItemChangeSet &other); void setDataChanged(bool dataChanged); bool dataChanged() const; QSet addedItems() const; void insertAddedItem(const QOrganizerItemId &itemId); void insertAddedItems(const QList &itemIds); void clearAddedItems(); QSet changedItems() const; void insertChangedItem(const QOrganizerItemId &itemId); void insertChangedItems(const QList &itemIds); void clearChangedItems(); QSet removedItems() const; void insertRemovedItem(const QOrganizerItemId &itemId); void insertRemovedItems(const QList &itemIds); void clearRemovedItems(); QList > modifiedItems() const; void clearModifiedItems(); void clearAll(); void emitSignals(QOrganizerManagerEngine *engine) const; private: QSharedDataPointer d; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMCHANGESET_H src/organizer/qorganizeritemchangeset_p.h000066400000000000000000000063601233466112000212620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMCHANGESET_P_H #define QORGANIZERITEMCHANGESET_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemChangeSetData : public QSharedData { public: QOrganizerItemChangeSetData() : QSharedData(), m_dataChanged(false) { } QOrganizerItemChangeSetData(const QOrganizerItemChangeSetData& other) : QSharedData(other), m_dataChanged(other.m_dataChanged), m_addedItems(other.m_addedItems), m_changedItems(other.m_changedItems), m_removedItems(other.m_removedItems), m_modifiedItems(other.m_modifiedItems) { } ~QOrganizerItemChangeSetData() { } bool m_dataChanged; QSet m_addedItems; QSet m_changedItems; QSet m_removedItems; QList > m_modifiedItems; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMCHANGESET_P_H src/organizer/qorganizeritemdetail.cpp000066400000000000000000000344561233466112000206060ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemdetail.h" #include "qorganizeritemdetail_p.h" #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif #include "qorganizeritemrecurrence.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemDetail \brief The QOrganizerItemDetail class represents a single, complete detail about an organizer item. \inmodule QtOrganizer \ingroup organizer-main All of the information for an organizer item is stored in one or more QOrganizerItemDetail objects. A detail is a group of logically related bits of data - for example, a QOrganizerItemTimestamp is a single detail that has multiple fields (timestamp of creation, timestamp of last update, etc). Different organizer managers may support different details for different item types, e.g. certain manager may not support the timestamp, while others do. In general, QOrganizerItemDetail and the built in subclasses (like \l QOrganizerEventTime) provide convenience and standardized access to values. For example, \l QOrganizerEventTime provides a convenient API for manipulating a QOrganizerItemDetail to describe the start and end time of an event. Subclasses also provide constants for the names of fields (like \l QOrganizerEventTime::FieldStartDateTime). Typically the constants for field names start with \c Field, and the constants for predefined values of a field start with the name of that field (e.g. \c TypeEvent is a predefined constant for \c FieldType). QOrganizerItemDetail objects act like type checked values. In general, you can assign them to and fro and have reasonable behaviour, like the following example. \code QOrganizerItemDescription description; description.setDescription("Some descriptive text"); // description.value(QOrganizerItemDescription::FieldDescription) == "Some descriptive text"; // description.type() == QOrganizerItemDetail::TypeDescription QOrganizerItemDetail detail = description; // detail.value(QOrganizerItemDescription::FieldDescription) == "Some descriptive text"; // detail.type() == QOrganizerItemDetail::TypeDescription QOrganizerItemDescription otherDescription = detail; // otherDescription.description() == "Some descriptive text"; // otherDescription.type() == QOrganizerItemDetail::TypeDescription QOrganizerItemDisplayLabel label = detail; // label is now a default constructed QOrganizerItemDisplayLabel // label.value(QOrganizerItemDescription::FieldDescription) is empty // label.type() == QOrganizerItemDetail::TypeDisplayLabel QOrganizerItemDisplayLabel otherLabel = description; // otherLabel is now a default constructed QOrganizerItemDisplayLabel // otherLabel.value(QOrganizerItemDescription::FieldDescription) is empty // otherLabel.type() == QOrganizerItemDetail::TypeDisplayLabel \endcode \sa QOrganizerItem, QOrganizerItemDetailFilter, QOrganizerItemDetailFieldFilter, QOrganizerItemDetailRangeFilter */ /*! \enum QOrganizerItemDetail::DetailType This enumeration describes the type of the organizer item detail. \value TypeUndefined This detail is of type undefined. \value TypeClassification This detail is a classification. \value TypeComment This detail is a comment \value TypeDescription This detail is a description. \value TypeDisplayLabel This detail is a display label. \value TypeItemType This detail is an item type. \value TypeGuid This detail is a GUID. \value TypeLocation This detail is a location. \value TypeParent This detail is a parent. Should not be used in parent items. \value TypePriority This detail is a priority. \value TypeRecurrence This detail is a recurrence. Should not be used in occurrences. \value TypeTag This detail is a tag. \value TypeTimestamp This detail is a timestamp. \value TypeVersion This detail is a version. \value TypeReminder This detail is a reminder. Should not be directly used. \value TypeAudibleReminder This detail is an audible reminder. \value TypeEmailReminder This detail is an email reminder. \value TypeVisualReminder This detail is a visual reminder. \value TypeExtendedDetail This detail is an extended detail. \value TypeEventAttendee This detail is an event attendee. \value TypeEventRsvp This detail is an event RSVP. \value TypeEventTime This detail is an event time. \value TypeJournalTime This detail is a journal time. \value TypeTodoTime This detail is a TODO time. \value TypeTodoProgress This detail is a TODO progress. */ /*! \internal \macro Q_DECLARE_CUSTOM_ORGANIZER_DETAIL \relates QOrganizerItemDetail Macro for simplifying declaring leaf detail classes. The first argument is the name of the class, and the second argument is the detail definition name. If you are creating a leaf detail class for a type of QOrganizerItemDetail, you should use this macro when declaring your class to ensure that it interoperates with other organizer item functionality. */ /*! \fn bool QOrganizerItemDetail::operator!=(const QOrganizerItemDetail &other) const Returns true if the values or id of this detail is different to those of the \a other detail */ /*! Constructs a new, empty detail of the \a detailType. */ QOrganizerItemDetail::QOrganizerItemDetail(DetailType detailType) : d(new QOrganizerItemDetailPrivate(detailType)) { } /*! Constructs a detail that is a copy of \a other. */ QOrganizerItemDetail::QOrganizerItemDetail(const QOrganizerItemDetail &other) : d(other.d) { } /*! \internal Constructs a detail that is a copy of \a other if \a other is of the type identified by \a expectedDetailType, else constructs a new, empty detail of the type identified by the \a expectedDetailType. */ QOrganizerItemDetail::QOrganizerItemDetail(const QOrganizerItemDetail &other, DetailType expectedDetailType) { if (other.d->m_detailType == expectedDetailType) d = other.d; else d = new QOrganizerItemDetailPrivate(expectedDetailType); } /*! Assigns this detail to \a other. */ QOrganizerItemDetail &QOrganizerItemDetail::operator=(const QOrganizerItemDetail &other) { d = other.d; return *this; } /*! \internal Assigns this detail to \a other if the type of \a other is that identified by the given \a expectedDetailType, else assigns this detail to be a new, empty detail of the type identified by the given \a expectedDetailType. */ QOrganizerItemDetail &QOrganizerItemDetail::assign(const QOrganizerItemDetail &other, DetailType expectedDetailType) { if (d != other.d) { if (other.d->m_detailType == expectedDetailType) d = other.d; else d = new QOrganizerItemDetailPrivate(expectedDetailType); } return *this; } /*! Frees the memory used by this detail. */ QOrganizerItemDetail::~QOrganizerItemDetail() { } /*! Returns the detail type. */ QOrganizerItemDetail::DetailType QOrganizerItemDetail::type() const { return d->m_detailType; } /*! Compares this detail to \a other. Returns true if the type and values of \a other are equal to those of this detail. The keys of each detail are not considered during the comparison, in order to allow details from different organizer items to be compared according to their values. */ bool QOrganizerItemDetail::operator==(const QOrganizerItemDetail &other) const { if (d == other.d) return true; // note: id is auto-generated and should not be compared here if (d->m_detailType != other.d->m_detailType) return false; // QVariant doesn't support == on QOrganizerItemRecurrence - do it manually if (d->m_detailType == QOrganizerItemDetail::TypeRecurrence) return static_cast(*this) == static_cast(other); return d->m_values == other.d->m_values; } /*! \relates QOrganizerItemDetail Returns the hash value for \a key. */ Q_ORGANIZER_EXPORT uint qHash(const QOrganizerItemDetail &key) { uint hash = QT_PREPEND_NAMESPACE(qHash)(key.d->m_detailType); QMap::const_iterator it = key.d->m_values.constBegin(); while (it != key.d->m_values.constEnd()) { hash += QT_PREPEND_NAMESPACE(qHash)(it.key()) + QT_PREPEND_NAMESPACE(qHash)(it.value().toString()); ++it; } return hash; } #ifndef QT_NO_DEBUG_STREAM /*! \relates QOrganizerItemDetail Streams the \a detail to the given debug stream \a dbg, and returns the stream. */ Q_ORGANIZER_EXPORT QDebug operator<<(QDebug dbg, const QOrganizerItemDetail &detail) { dbg.nospace() << "QOrganizerItemDetail(name=" << detail.type() << ", key=" << detail.key(); QMap fields = detail.values(); QMap::const_iterator it; for (it = fields.constBegin(); it != fields.constEnd(); ++it) dbg.nospace() << ", " << it.key() << '=' << it.value(); dbg.nospace() << ')'; return dbg.maybeSpace(); } #endif // QT_NO_DEBUG_STREAM #ifndef QT_NO_DATASTREAM /*! \relates QOrganizerItemDetail Writes \a detail to the stream \a out. */ Q_ORGANIZER_EXPORT QDataStream &operator<<(QDataStream &out, const QOrganizerItemDetail &detail) { quint8 formatVersion = 1; // Version of QDataStream format for QOrganizerItemDetail return out << formatVersion << detail.type() << detail.values(); } /*! \relates QOrganizerItemDetail Reads an organizer item detail from stream \a in into \a detail. */ Q_ORGANIZER_EXPORT QDataStream &operator>>(QDataStream &in, QOrganizerItemDetail &detail) { quint8 formatVersion; in >> formatVersion; if (formatVersion == 1) { quint32 detailType; QMap values; in >> detailType >> values; detail = QOrganizerItemDetail(static_cast(detailType)); QMapIterator it(values); while (it.hasNext()) { it.next(); detail.setValue(it.key(), it.value()); } } else { in.setStatus(QDataStream::ReadCorruptData); } return in; } #endif // QT_NO_DATASTREAM /*! Returns true if no values are contained in this detail. */ bool QOrganizerItemDetail::isEmpty() const { return (d->m_values.isEmpty()); } /*! Returns the key of this detail. */ int QOrganizerItemDetail::key() const { return d->m_id; } /*! Causes the implicitly-shared detail to be detached from any other copies, and generates a new key for it. This ensures that calling QOrganizerItem::saveDetail() will result in a new detail being saved, rather than another detail being updated. */ void QOrganizerItemDetail::resetKey() { d->m_id = QOrganizerItemDetailPrivate::lastDetailKey().fetchAndAddOrdered(1); } /*! Returns the value stored in this detail for the given \a field. An invalid QVariant is returned if the value of \a field is not set. */ QVariant QOrganizerItemDetail::value(int field) const { return d->m_values.value(field); } /*! Returns true if the value of the given \a field has been set, or false otherwise. */ bool QOrganizerItemDetail::hasValue(int field) const { return d->m_values.contains(field); } /*! Sets the value of the given \a field to be \a value. If the given \a value is invalid or null, removes the given \a field from the detail. Returns true on success, or false otherwise. */ bool QOrganizerItemDetail::setValue(int field, const QVariant &value) { if (!value.isValid() || value.isNull()) return removeValue(field); d->m_values.insert(field, value); return true; } /*! Removes the value stored in this detail for the given \a field. Returns true if a value was stored for the given \a field and the removing succeeds, or false otherwise. */ bool QOrganizerItemDetail::removeValue(int field) { return d->m_values.remove(field); } /*! Returns the values stored in this detail as a field-to-value map. */ QMap QOrganizerItemDetail::values() const { return d->m_values; } /*! \fn template T QOrganizerItemDetail::value(int field) const Returns the value of the template type associated with the given \a field. */ QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizeritemdetail.h000066400000000000000000000124641233466112000202460ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMDETAIL_H #define QORGANIZERITEMDETAIL_H #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemDetailPrivate; class Q_ORGANIZER_EXPORT QOrganizerItemDetail { public: enum DetailType { TypeUndefined = 0, TypeClassification = 100, TypeComment = 200, TypeDescription = 300, TypeDisplayLabel = 400, TypeItemType = 500, TypeGuid = 600, TypeLocation = 700, TypeParent = 800, TypePriority = 900, TypeRecurrence = 1000, TypeTag = 1100, TypeTimestamp = 1200, TypeVersion = 1300, TypeReminder = 1400, TypeAudibleReminder = 1500, TypeEmailReminder = 1600, TypeVisualReminder = 1700, TypeExtendedDetail = 1800, TypeEventAttendee = 1900, TypeEventRsvp = 2000, TypeEventTime = 2100, TypeJournalTime = 2200, TypeTodoProgress = 2300, TypeTodoTime = 2400 }; QOrganizerItemDetail(DetailType detailType = TypeUndefined); QOrganizerItemDetail(const QOrganizerItemDetail &other); ~QOrganizerItemDetail(); QOrganizerItemDetail &operator=(const QOrganizerItemDetail &other); bool operator==(const QOrganizerItemDetail &other) const; bool operator!=(const QOrganizerItemDetail &other) const {return !(other == *this);} DetailType type() const; bool isEmpty() const; int key() const; void resetKey(); bool setValue(int field, const QVariant &value); bool removeValue(int field); bool hasValue(int field) const; QMap values() const; QVariant value(int field) const; template T value(int field) const { return value(field).value(); } protected: QOrganizerItemDetail(const QOrganizerItemDetail &other, DetailType expectedDetailType); QOrganizerItemDetail &assign(const QOrganizerItemDetail &other, DetailType expectedDetailType); private: friend Q_ORGANIZER_EXPORT uint qHash(const QOrganizerItemDetail &key); friend class QOrganizerItem; friend class QOrganizerItemDetailPrivate; #ifndef QT_NO_DATASTREAM friend Q_ORGANIZER_EXPORT QDataStream &operator>>(QDataStream &in, QOrganizerItemDetail &detail); #endif // QT_NO_DATASTREAM QSharedDataPointer d; }; Q_ORGANIZER_EXPORT uint qHash(const QOrganizerItemDetail &key); #ifndef QT_NO_DATASTREAM Q_ORGANIZER_EXPORT QDataStream &operator<<(QDataStream &out, const QOrganizerItemDetail &detail); Q_ORGANIZER_EXPORT QDataStream &operator>>(QDataStream &in, QOrganizerItemDetail &detail); #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM Q_ORGANIZER_EXPORT QDebug operator<<(QDebug dbg, const QOrganizerItemDetail &detail); #endif // QT_NO_DEBUG_STREAM #define Q_DECLARE_CUSTOM_ORGANIZER_DETAIL(className, detailType) \ className() : QOrganizerItemDetail(detailType) {} \ className(const QOrganizerItemDetail &other) : QOrganizerItemDetail(other, detailType) {} \ className &operator=(const QOrganizerItemDetail &other) {assign(other, detailType); return *this;} QT_END_NAMESPACE_ORGANIZER QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(QTORGANIZER_PREPEND_NAMESPACE(QOrganizerItemDetail), Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif // QORGANIZERITEMDETAIL_H src/organizer/qorganizeritemdetail_p.h000066400000000000000000000062771233466112000205720ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMDETAIL_P_H #define QORGANIZERITEMDETAIL_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemDetailPrivate : public QSharedData { public: QOrganizerItemDetailPrivate(QOrganizerItemDetail::DetailType detailType) : QSharedData() , m_id(lastDetailKey().fetchAndAddOrdered(1)) , m_detailType(detailType) { } QOrganizerItemDetailPrivate(const QOrganizerItemDetailPrivate &other) : QSharedData(other) , m_id(other.m_id) , m_detailType(other.m_detailType) , m_values(other.m_values) { } ~QOrganizerItemDetailPrivate() { } int m_id; // internal, unique id. QOrganizerItemDetail::DetailType m_detailType; QMap m_values; static QAtomicInt &lastDetailKey() { static QAtomicInt counter(0); return counter; } }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMDETAIL_P_H src/organizer/qorganizeritemengineid.cpp000066400000000000000000000104761233466112000211220ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemengineid.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemEngineId \relates QOrganizerItemId \brief The QOrganizerItemEngineId class uniquely identifies an item within a particular engine plugin. \inmodule QtOrganizer \ingroup organizer-backends Clients of the Organizer API should never use this class. Every engine implementor must implement a class derived from QOrganizerItemEngineId. This class is provided so that engine implementors can implement their own id class (which may contain arbitrary data, and which may implement the required functions in an arbitrary manner). */ /*! \fn QOrganizerItemEngineId::~QOrganizerItemEngineId() Cleans up any memory in use by this engine item id. */ /*! \fn QOrganizerItemEngineId::isEqualTo(const QOrganizerItemEngineId* other) const Returns true if this id is equal to the \a other id; false otherwise. Note that when implementing this function, you do not have to check that the type is the same, since the function which calls this function (in QOrganizerItemId) does that check for you. */ /*! \fn QOrganizerItemEngineId::isLessThan(const QOrganizerItemEngineId* other) const Returns true if this id is less than the \a other id; false otherwise. Note that when implementing this function, you do not have to check that the type is the same, since the function which calls this function (in QOrganizerItemId) does that check for you. */ /*! \fn QOrganizerItemEngineId::managerUri() const Returns the manager URI of the constructed manager which created the id. If the item which the id identifies has not been deleted, the id should still be valid in the manager identified by the manager URI returned by this function. */ /*! \fn QOrganizerItemEngineId::toString() const Serializes the id to a string. It contains all of the information required to identify a particular item in the manager which created the id, formatted according to the serialization format of the manager. */ /*! \fn QOrganizerItemEngineId::clone() const Returns a deep-copy clone of this id. The caller takes ownership of the returned engine item id. */ /*! \fn QOrganizerItemEngineId::debugStreamOut(QDebug& dbg) const Streams this id out to the debug stream \a dbg. */ /*! \fn QOrganizerItemEngineId::hash() const Returns the hash value of this id. */ QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizeritemengineid.h000066400000000000000000000053431233466112000205640ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMENGINEID_H #define QORGANIZERITEMENGINEID_H #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class Q_ORGANIZER_EXPORT QOrganizerItemEngineId: public QSharedData { public: virtual ~QOrganizerItemEngineId() {} virtual bool isEqualTo(const QOrganizerItemEngineId *other) const = 0; virtual bool isLessThan(const QOrganizerItemEngineId *other) const = 0; virtual QString managerUri() const = 0; virtual QOrganizerItemEngineId *clone() const = 0; virtual QString toString() const = 0; #ifndef QT_NO_DEBUG_STREAM virtual QDebug &debugStreamOut(QDebug &dbg) const = 0; #endif virtual uint hash() const = 0; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMENGINEID_H src/organizer/qorganizeritemfetchhint.cpp000066400000000000000000000175461233466112000213210ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemfetchhint_p.h" #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemFetchHint \brief The QOrganizerItemFetchHint class provides hints to the manager about which organizer item information needs to be retrieved. \inmodule QtOrganizer \ingroup organizer-filters All of the hints may be ignored at the discretion of the manager, however if a manager is able to optimize retrieval of organizer items due to hints, it may do so. If a manager ignores a hint, it must retrieve the full set of data that the hint refers to. The fetch hint contains: \list \li a list of detail definition names which the client is interested in (empty if interested in all detail definitions) \li some optimization flags which allow the client to tell the backend if they are not interested in binary blobs (images etc). \endlist Important note: if certain organizer item is retrieved with fetch hint set, normal saving will result in the loss of information that is not retrieved. Partial save should be used to avoid information loss. */ /*! \enum QOrganizerItemFetchHint::OptimizationHint This enum defines flags which may be set to inform the backend that the client does not require certain information. \value AllRequired Tells the backend that all information is required. \value NoActionPreferences Tells the backend that the client does not require retrieved organizer items to include a cache of action preferences. \value NoBinaryBlobs Tells the backend that the client does not require retrieved organizer items to include binary blobs such as thumbnail images. */ /*! Constructs a new organizer item fetch hint which requests that the backend fetch all information. */ QOrganizerItemFetchHint::QOrganizerItemFetchHint() : d(new QOrganizerItemFetchHintPrivate) { } /*! Constructs a new organizer item fetch hint as a copy of \a other. */ QOrganizerItemFetchHint::QOrganizerItemFetchHint(const QOrganizerItemFetchHint &other) : d(other.d) { } /*! Frees any memory in use by the fetch hint. */ QOrganizerItemFetchHint::~QOrganizerItemFetchHint() { } /*! Assigns this fetch hint to the \a other. */ QOrganizerItemFetchHint& QOrganizerItemFetchHint::operator=(const QOrganizerItemFetchHint &other) { d = other.d; return *this; } /*! Returns true if this fetch hint is the same as that of the \a other fetch hint; false otherwise. \sa operator!=() */ bool QOrganizerItemFetchHint::operator==(const QOrganizerItemFetchHint &other) const { if (d == other.d) return true; return d->m_optimizationHints == other.d->m_optimizationHints && d->m_detailTypesHint == other.d->m_detailTypesHint; } /*! \fn QOrganizerItemFetchHint::operator!=(const QOrganizerItemFetchHint &other) const Returns true if this fetch hint is not the same as that of the \a other fetch hint. \sa operator==() */ /*! Returns the list of detail types that identify details which should be retrieved by the manager when fetching items. \sa setDetailTypesHint() */ QList QOrganizerItemFetchHint::detailTypesHint() const { return d->m_detailTypesHint; } /*! Sets the list of detail types to \a detailTypes that identify details which should be retrieved' by the manager when fetching items. \sa detailTypesHint() */ void QOrganizerItemFetchHint::setDetailTypesHint(const QList &detailTypes) { d->m_detailTypesHint = detailTypes; } /*! Returns the optimization hint flags specified by the client. \sa setOptimizationHints() */ QOrganizerItemFetchHint::OptimizationHints QOrganizerItemFetchHint::optimizationHints() const { return d->m_optimizationHints; } /*! Sets the optimization hint flags specified by the client to \a hints. \sa optimizationHints() */ void QOrganizerItemFetchHint::setOptimizationHints(OptimizationHints hints) { d->m_optimizationHints = hints; } #ifndef QT_NO_DATASTREAM /*! \relates QOrganizerItemFetchHint Streams \a hint to the data stream \a out. */ QDataStream &operator<<(QDataStream &out, const QOrganizerItemFetchHint &hint) { quint8 formatVersion = 1; return out << formatVersion << hint.detailTypesHint() << static_cast(hint.optimizationHints()); } /*! \relates QOrganizerItemFetchHint Streams \a hint in from the data stream \a in. */ QDataStream &operator>>(QDataStream &in, QOrganizerItemFetchHint &hint) { quint8 formatVersion; in >> formatVersion; if (formatVersion == 1) { QList detailTypesHint; quint32 optimizations; in >> detailTypesHint >> optimizations; QList types; foreach (quint32 detailType, detailTypesHint) types.append(static_cast(detailType)); hint.setDetailTypesHint(types); hint.setOptimizationHints(static_cast(optimizations)); } else { in.setStatus(QDataStream::ReadCorruptData); } return in; } #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM /*! \relates QOrganizerItemFetchHint Outputs \a hint to the debug stream \a dbg. */ QDebug operator<<(QDebug dbg, const QOrganizerItemFetchHint &hint) { dbg.nospace() << "QOrganizerItemFetchHint("; dbg.nospace() << "detailDefinitionsHint="; dbg.nospace() << hint.detailTypesHint(); dbg.nospace() << ","; dbg.nospace() << "optimizationHints="; dbg.nospace() << static_cast(hint.optimizationHints()); dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif // QT_NO_DEBUG_STREAM QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizeritemfetchhint.h000066400000000000000000000072341233466112000207570ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMFETCHHINT_H #define QORGANIZERITEMFETCHHINT_H #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemFetchHintPrivate; class Q_ORGANIZER_EXPORT QOrganizerItemFetchHint { public: QOrganizerItemFetchHint(); QOrganizerItemFetchHint(const QOrganizerItemFetchHint &other); ~QOrganizerItemFetchHint(); QOrganizerItemFetchHint &operator=(const QOrganizerItemFetchHint &other); bool operator==(const QOrganizerItemFetchHint &other) const; inline bool operator!=(const QOrganizerItemFetchHint &other) const { return !(other == *this); } QList detailTypesHint() const; void setDetailTypesHint(const QList &detailTypes); enum OptimizationHint { AllRequired = 0x0, NoActionPreferences = 0x2, NoBinaryBlobs = 0x4 // any other optimization hints? }; Q_DECLARE_FLAGS(OptimizationHints, OptimizationHint) OptimizationHints optimizationHints() const; void setOptimizationHints(OptimizationHints hints); private: QSharedDataPointer d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QOrganizerItemFetchHint::OptimizationHints) #ifndef QT_NO_DATASTREAM Q_ORGANIZER_EXPORT QDataStream &operator<<(QDataStream &out, const QOrganizerItemFetchHint &hint); Q_ORGANIZER_EXPORT QDataStream &operator>>(QDataStream &in, QOrganizerItemFetchHint &hint); #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM Q_ORGANIZER_EXPORT QDebug operator<<(QDebug dbg, const QOrganizerItemFetchHint &hint); #endif // QT_NO_DEBUG_STREAM QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMFETCHHINT_H src/organizer/qorganizeritemfetchhint_p.h000066400000000000000000000061231233466112000212720ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMFETCHHINT_P_H #define QORGANIZERITEMFETCHHINT_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemFetchHintPrivate : public QSharedData { public: QOrganizerItemFetchHintPrivate() : QSharedData(), m_optimizationHints(QOrganizerItemFetchHint::AllRequired) { } QOrganizerItemFetchHintPrivate(const QOrganizerItemFetchHintPrivate &other) : QSharedData(other), m_detailTypesHint(other.m_detailTypesHint), m_optimizationHints(other.m_optimizationHints) { } ~QOrganizerItemFetchHintPrivate() { } QList m_detailTypesHint; QOrganizerItemFetchHint::OptimizationHints m_optimizationHints; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMFETCHHINT_P_H src/organizer/qorganizeritemfilter.cpp000066400000000000000000000247631233466112000206310ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemfilter.h" #include "qorganizeritemfilter_p.h" #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif #include "qorganizeritemfilters.h" /*! \class QOrganizerItemFilter \brief The QOrganizerItemFilter class is used to filter items made available through a backend. \inmodule QtOrganizer \ingroup organizer-main This class is used as a parameter to various functions offered by QOrganizerManager and QOrganizerAbstractRequest, to allow filtering of items which have certain details or properties. */ /*! \enum QOrganizerItemFilter::FilterType This enumeration describes the type of the filter. \value InvalidFilter An invalid filter which matches nothing. \value DetailFilter A filter which matches items containing a detail identical to that used by the filter for matching. \value DetailFieldFilter A filter which matches items containing a detail of a particular type, having a given value for a particular field. \value DetailRangeFilter A filter which matches items containing a detail of a particular type, whose values are within a particular range. \value IntersectionFilter A filter which matches all items that are matched by all filters it includes. \value UnionFilter A filter which matches any organizer item that is matched by any of the filters it includes. \value IdFilter A filter which matches any organizer item whose ID is contained in a particular list of organizer item IDs. \value CollectionFilter A filter which matches any items whose collection ID is contained in a particular list of collection IDs. \value DefaultFilter A filter which matches everything. */ /*! \enum QOrganizerItemFilter::MatchFlag This enumeration describes the semantics of matching followed by the filter. \value MatchExactly Performs QVariant-based matching. \value MatchContains The search term is contained in the item. \value MatchStartsWith The search term matches the start of the item. \value MatchEndsWith The search term matches the end of the item. \value MatchFixedString Performs string-based matching. String-based comparisons are case-insensitive unless the \c MatchCaseSensitive flag is also specified. \value MatchCaseSensitive The search is case sensitive. */ /*! \fn QOrganizerItemFilter::operator!=(const QOrganizerItemFilter &other) const Returns true if this filter is not identical to the \a other filter. \sa operator==() */ #if !defined(Q_CC_MWERKS) template<> QTORGANIZER_PREPEND_NAMESPACE(QOrganizerItemFilterPrivate) *QSharedDataPointer::clone() { return d->clone(); } #endif // Q_CC_MWERKS QT_BEGIN_NAMESPACE_ORGANIZER /*! Constructs an empty filter of type DefaultFilter. */ QOrganizerItemFilter::QOrganizerItemFilter() : d_ptr(0) { } /*! Constructs a new copy of \a other. */ QOrganizerItemFilter::QOrganizerItemFilter(const QOrganizerItemFilter &other) : d_ptr(other.d_ptr) { } /*! Assigns this filter to be \a other. */ QOrganizerItemFilter &QOrganizerItemFilter::operator=(const QOrganizerItemFilter &other) { d_ptr = other.d_ptr; return *this; } /*! Cleans up the memory used by this filter. */ QOrganizerItemFilter::~QOrganizerItemFilter() { } /*! Returns the type of the filter. */ QOrganizerItemFilter::FilterType QOrganizerItemFilter::type() const { if (!d_ptr) return QOrganizerItemFilter::DefaultFilter; return d_ptr->type(); } /*! Returns true if this filter is identical to the \a other filter. \sa operator!=() */ bool QOrganizerItemFilter::operator==(const QOrganizerItemFilter &other) const { if (d_ptr == other.d_ptr) return true; if (d_ptr && other.d_ptr) return d_ptr->type() == other.d_ptr->type() && d_ptr->compare(other.d_ptr); return false; } #ifndef QT_NO_DATASTREAM /*! \relates QOrganizerItemFilter Writes \a filter to the stream \a out. A QOrganizerItemIdFilter will not be preserved if streamed to a QDataStream. */ QDataStream &operator<<(QDataStream &out, const QOrganizerItemFilter &filter) { quint8 formatVersion = 1; out << formatVersion << static_cast(filter.type()); if (filter.d_ptr) filter.d_ptr->outputToStream(out, formatVersion); return out; } /*! \relates QOrganizerItemFilter Reads an organizer item filter from stream \a in into \a filter. A QOrganizerItemIdFilter will not be preserved if streamed from a QDataStream. */ QDataStream &operator>>(QDataStream &in, QOrganizerItemFilter &filter) { quint8 formatVersion; in >> formatVersion; if (formatVersion == 1) { quint32 type; in >> type; switch (type) { case QOrganizerItemFilter::InvalidFilter: filter = QOrganizerItemInvalidFilter(); break; case QOrganizerItemFilter::DetailFilter: filter = QOrganizerItemDetailFilter(); case QOrganizerItemFilter::DetailFieldFilter: filter = QOrganizerItemDetailFieldFilter(); break; case QOrganizerItemFilter::DetailRangeFilter: filter = QOrganizerItemDetailRangeFilter(); break; case QOrganizerItemFilter::IntersectionFilter: filter = QOrganizerItemIntersectionFilter(); break; case QOrganizerItemFilter::UnionFilter: filter = QOrganizerItemUnionFilter(); break; case QOrganizerItemFilter::IdFilter: filter = QOrganizerItemIdFilter(); break; case QOrganizerItemFilter::DefaultFilter: filter = QOrganizerItemFilter(); break; } if (filter.d_ptr) filter.d_ptr->inputFromStream(in, formatVersion); } else { in.setStatus(QDataStream::ReadCorruptData); } return in; } #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM /*! \relates QOrganizerItemFilter Outputs \a filter to the debug stream \a dbg. */ QDebug operator<<(QDebug dbg, const QOrganizerItemFilter &filter) { dbg.nospace() << "QOrganizerItemFilter("; if (filter.d_ptr) filter.d_ptr->debugStreamOut(dbg); else dbg.nospace() << "(null)"; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif // QT_NO_DEBUG_STREAM /*! \internal Constructs a new filter from the given data pointer \a d. */ QOrganizerItemFilter::QOrganizerItemFilter(QOrganizerItemFilterPrivate *d) : d_ptr(d) { } /*! \relates QOrganizerItemFilter Returns a filter which is the intersection of the \a left and \a right filters. \sa QOrganizerItemIntersectionFilter */ const QOrganizerItemFilter operator&(const QOrganizerItemFilter &left, const QOrganizerItemFilter &right) { // XXX TODO: empty intersection/union operations are not well defined yet. //if (left.type() == QOrganizerItemFilter::Intersection) { // QOrganizerItemIntersectionFilter bf(left); // /* we can just add the right to this one */ // bf.append(right); // return bf; //} //if (right.type() == QOrganizerItemFilter::Intersection) { // QOrganizerItemIntersectionFilter bf(right); // /* we can prepend the left to this one */ // bf.prepend(left); // return bf; //} /* usual fallback case */ QOrganizerItemIntersectionFilter nif; nif << left << right; return nif; } /*! \relates QOrganizerItemFilter Returns a filter which is the union of the \a left and \a right filters. \sa QOrganizerItemUnionFilter */ const QOrganizerItemFilter operator|(const QOrganizerItemFilter &left, const QOrganizerItemFilter &right) { if (left.type() == QOrganizerItemFilter::UnionFilter) { QOrganizerItemUnionFilter bf(left); /* we can just add the right to this one */ bf.append(right); return bf; } if (right.type() == QOrganizerItemFilter::UnionFilter) { QOrganizerItemUnionFilter bf(right); /* we can prepend the left to this one */ bf.prepend(left); return bf; } /* usual fallback case */ QOrganizerItemUnionFilter nif; nif << left << right; return nif; } QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizeritemfilter.h000066400000000000000000000116511233466112000202660ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMFILTER_H #define QORGANIZERITEMFILTER_H #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemFilterPrivate; class Q_ORGANIZER_EXPORT QOrganizerItemFilter { public: enum FilterType { InvalidFilter = 0, DetailFilter, DetailFieldFilter, DetailRangeFilter, IntersectionFilter, UnionFilter, IdFilter, CollectionFilter, DefaultFilter }; QOrganizerItemFilter(); QOrganizerItemFilter(const QOrganizerItemFilter &other); ~QOrganizerItemFilter(); QOrganizerItemFilter &operator=(const QOrganizerItemFilter &other); FilterType type() const; // Qt::MatchFlags don't quite match here enum MatchFlag { MatchExactly = Qt::MatchExactly, // 0 MatchContains = Qt::MatchContains, // 1 MatchStartsWith = Qt::MatchStartsWith, // 2 MatchEndsWith = Qt::MatchEndsWith, // 3 MatchFixedString = Qt::MatchFixedString, // 8 MatchCaseSensitive = Qt::MatchCaseSensitive // 16 }; Q_DECLARE_FLAGS(MatchFlags, MatchFlag) bool operator==(const QOrganizerItemFilter &other) const; bool operator!=(const QOrganizerItemFilter &other) const { return !operator==(other); } protected: explicit QOrganizerItemFilter(QOrganizerItemFilterPrivate *d); QSharedDataPointer d_ptr; protected: friend class QOrganizerItemFilterPrivate; #ifndef QT_NO_DATASTREAM Q_ORGANIZER_EXPORT friend QDataStream &operator<<(QDataStream &out, const QOrganizerItemFilter &filter); Q_ORGANIZER_EXPORT friend QDataStream &operator>>(QDataStream &in, QOrganizerItemFilter &filter); #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM Q_ORGANIZER_EXPORT friend QDebug operator<<(QDebug dbg, const QOrganizerItemFilter &filter); #endif // QT_NO_DEBUG_STREAM }; #define Q_DECLARE_ORGANIZERITEMFILTER_PRIVATE(Class) \ inline Class##Private* d_func(); \ inline const Class##Private* d_func() const; \ friend class Class##Private; const Q_ORGANIZER_EXPORT QOrganizerItemFilter operator&(const QOrganizerItemFilter &left, const QOrganizerItemFilter &right); const Q_ORGANIZER_EXPORT QOrganizerItemFilter operator|(const QOrganizerItemFilter &left, const QOrganizerItemFilter &right); #ifndef QT_NO_DATASTREAM Q_ORGANIZER_EXPORT QDataStream &operator<<(QDataStream &out, const QOrganizerItemFilter &filter); Q_ORGANIZER_EXPORT QDataStream &operator>>(QDataStream &in, QOrganizerItemFilter &filter); #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM Q_ORGANIZER_EXPORT QDebug operator<<(QDebug dbg, const QOrganizerItemFilter &filter); #endif // QT_NO_DEBUG_STREAM QT_END_NAMESPACE_ORGANIZER QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(QTORGANIZER_PREPEND_NAMESPACE(QOrganizerItemFilter), Q_MOVABLE_TYPE); Q_DECLARE_OPERATORS_FOR_FLAGS(QTORGANIZER_PREPEND_NAMESPACE(QOrganizerItemFilter::MatchFlags)) QT_END_NAMESPACE #endif // QORGANIZERITEMFILTER_H src/organizer/qorganizeritemfilter_p.h000066400000000000000000000117771233466112000206160ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMFILTER_P_H #define QORGANIZERITEMFILTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif #include #include #include #include /* Boiler plate code */ #define Q_IMPLEMENT_ORGANIZERITEMFILTER_PRIVATE(Class) \ Class##Private* Class::d_func() { return reinterpret_cast(d_ptr.data()); } \ const Class##Private* Class::d_func() const { return reinterpret_cast(d_ptr.constData()); } \ Class::Class(const QOrganizerItemFilter& other) : QOrganizerItemFilter() { Class##Private::copyIfPossible(d_ptr, other); } #define Q_IMPLEMENT_ORGANIZERITEMFILTER_VIRTUALCTORS(Class, Type) \ QOrganizerItemFilterPrivate* clone() const { return new Class##Private(*this); } \ virtual QOrganizerItemFilter::FilterType type() const {return Type;} \ static void copyIfPossible(QSharedDataPointer& d_ptr, const QOrganizerItemFilter& other) \ { \ if (other.type() == Type) \ d_ptr = extract_d(other); \ else \ d_ptr = new Class##Private; \ } QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemFilterPrivate : public QSharedData { public: QOrganizerItemFilterPrivate() { } virtual ~QOrganizerItemFilterPrivate() { } virtual bool compare(const QOrganizerItemFilterPrivate *other) const = 0; #ifndef QT_NO_DATASTREAM virtual QDataStream &outputToStream(QDataStream &stream, quint8 formatVersion) const = 0; virtual QDataStream &inputFromStream(QDataStream &stream, quint8 formatVersion) = 0; #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM virtual QDebug &debugStreamOut(QDebug &dbg) const = 0; #endif // QT_NO_DEBUG_STREAM virtual QOrganizerItemFilterPrivate *clone() const = 0; virtual QOrganizerItemFilter::FilterType type() const = 0; /* Helper functions for C++ protection rules */ static const QSharedDataPointer &extract_d(const QOrganizerItemFilter &other) { return other.d_ptr; } }; QT_END_NAMESPACE_ORGANIZER QT_BEGIN_NAMESPACE #if defined(Q_CC_MWERKS) // This results in multiple symbol definition errors on all other compilers // but not having a definition here results in an attempt to use the unspecialized // clone (which fails because of the pure virtuals above) template<> QTORGANIZER_PREPEND_NAMESPACE(QOrganizerItemFilterPrivate) *QSharedDataPointer::clone() { return d->clone(); } #else template<> QTORGANIZER_PREPEND_NAMESPACE(QOrganizerItemFilterPrivate) *QSharedDataPointer::clone(); #endif QT_END_NAMESPACE #endif // QORGANIZERITEMFILTER_P_H src/organizer/qorganizeritemid.cpp000066400000000000000000000276321233466112000177360ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemid.h" #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif #include #include "qorganizeritemengineid.h" #include "qorganizermanager_p.h" #if !defined(Q_CC_MWERKS) QT_BEGIN_NAMESPACE template<> QTORGANIZER_PREPEND_NAMESPACE(QOrganizerItemEngineId) *QSharedDataPointer::clone() { return d ? d->clone() : 0; } QT_END_NAMESPACE #endif QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemId \brief The QOrganizerItemId class provides information that uniquely identifies an organizer item in a particular manager. \inmodule QtOrganizer \ingroup organizer-main It consists of a manager URI which identifies the manager which contains the organizer item, and the engine specific ID of the organizer item in that manager. A "null" QOrganizerItemId has an empty manager URI. */ /*! Constructs a new organizer item ID. */ QOrganizerItemId::QOrganizerItemId() : d(0) { } /*! Constructs a manager-unique ID which wraps the given engine-unique item ID \a engineItemId. This item ID takes ownership of the engine ID and will delete it when the item ID goes out of scope. Engine implementors must not delete the \a engineItemId or undefined behaviour will occur. */ QOrganizerItemId::QOrganizerItemId(QOrganizerItemEngineId *engineItemId) : d(engineItemId) { } /*! Cleans up the memory in use by the organizer item ID. */ QOrganizerItemId::~QOrganizerItemId() { } /*! Constructs a new organizer item ID as a copy of \a other. */ QOrganizerItemId::QOrganizerItemId(const QOrganizerItemId &other) : d(other.d) { } /*! Assigns the organizer item ID to be equal to \a other. */ QOrganizerItemId &QOrganizerItemId::operator=(const QOrganizerItemId &other) { d = other.d; return *this; } /*! Returns true if the organizer item ID has the same manager URI and ID as \a other. */ bool QOrganizerItemId::operator==(const QOrganizerItemId &other) const { if (d == other.d) return true; if (d && other.d) return d->managerUri() == other.d->managerUri() && d->isEqualTo(other.d); return false; } /*! Returns true if either the manager URI or ID is different to that of \a other. */ bool QOrganizerItemId::operator!=(const QOrganizerItemId &other) const { return !(*this == other); } /*! Returns true if this ID is less than the \a other ID. This ID will be considered less than the \a other iID if the manager URI of this ID is alphabetically less than the manager URI of the \a other ID. If both IDs have the same manager URI, this ID will be considered less than the \a other ID if the engine ID of this ID is less than the engine ID of the \a other ID. The invalid, empty ID consists of an empty manager URI and a null engine ID, and hence will be less than any non-invalid ID. This operator is provided primarily to allow use of a QOrganizerItemId as a key in a QMap. */ bool QOrganizerItemId::operator<(const QOrganizerItemId &other) const { if (d == 0 && other.d != 0) return true; if (d && other.d) { if (d->managerUri() == other.d->managerUri()) return d->isLessThan(other.d); return d->managerUri() < other.d->managerUri(); } return false; } /*! \relates QOrganizerItemId Returns the hash value for \a key. */ Q_ORGANIZER_EXPORT uint qHash(const QOrganizerItemId &key) { if (key.d) return key.d->hash(); return 0; } #ifndef QT_NO_DEBUG_STREAM /*! \relates QOrganizerItemId Outputs \a id to the debug stream \a dbg. */ Q_ORGANIZER_EXPORT QDebug operator<<(QDebug dbg, const QOrganizerItemId &id) { dbg.nospace() << "QOrganizerItemId("; if (id.isNull()) dbg.nospace() << "(null))"; else id.d->debugStreamOut(dbg) << ")"; return dbg.maybeSpace(); } #endif #ifndef QT_NO_DATASTREAM /*! \relates QOrganizerItemId Streams \a itemId to the data stream \a out. */ Q_ORGANIZER_EXPORT QDataStream &operator<<(QDataStream &out, const QOrganizerItemId &itemId) { out << (itemId.toString()); return out; } /*! \relates QOrganizerItemId Streams \a itemId in from the data stream \a in. */ Q_ORGANIZER_EXPORT QDataStream &operator>>(QDataStream &in, QOrganizerItemId &itemId) { QString idString; in >> idString; itemId = QOrganizerItemId::fromString(idString); return in; } #endif /*! Returns true if the engine ID part is a null (default constructed) engine ID; otherwise, returns false. */ bool QOrganizerItemId::isNull() const { return d == 0; } /*! Returns the URI of the manager which contains the organizer item identified by this ID. */ QString QOrganizerItemId::managerUri() const { return d ? d->managerUri() : QString(); } /*! \internal Escape the id string */ QString escapeIdString(const QString &string) { QString rich; const int len = string.length(); rich.reserve(int(len * 1.5)); for (int i = 0; i < len; ++i) { if (string.at(i) == QLatin1Char(':')) rich += QStringLiteral(":"); else if (string.at(i) == QLatin1Char('=')) rich += QStringLiteral("&equ;"); else if (string.at(i) == QLatin1Char('&')) rich += QStringLiteral("&"); else rich += string.at(i); } rich.squeeze(); return rich; } /*! \internal Builds a string from the given \a managerName, \a params and \a engineIdString. */ QString buildIdString(const QString &managerName, const QMap ¶ms, const QString &engineIdString) { // the constructed id string will be of the form: "qtcontacts:managerName:param1=value1¶m2=value2: QString ret(QStringLiteral("qtorganizer:%1:%2:%3")); // we have to escape each param QStringList escapedParams; QString arg; foreach (const QString &key, params.keys()) { arg = params.value(key); escapedParams.append(escapeIdString(key) + QLatin1Char('=') + escapeIdString(arg)); } // and we escape the engine id string. QString escapedEngineId = escapeIdString(engineIdString); return ret.arg(managerName, escapedParams.join(QStringLiteral("&")), escapedEngineId); } /*! \internal Parses the individual components of the given \a idString and fills the \a managerName, \a params and \a engineIdString. Returns true if the parts could be parsed successfully, false otherwise. */ bool parseIdString(const QString &idString, QString *managerName, QMap *params, QString *engineIdString) { QStringList colonSplit = idString.split(QLatin1Char(':')); QString prefix = colonSplit.value(0); if (prefix != QStringLiteral("qtorganizer") || colonSplit.size() != 4) return false; // invalid serialized string. we cannot continue. QString mgrName = colonSplit.value(1); QString paramString = colonSplit.value(2); QString engIdString = colonSplit.value(3); // Now we have to decode each parameter QMap outParams; if (!paramString.isEmpty()) { QStringList params = paramString.split(QRegExp(QStringLiteral("&(?!(amp;|equ;))")), QString::KeepEmptyParts); // If we have an empty string for paramstring, we get one entry in params, // so skip that case. for(int i = 0; i < params.count(); i++) { /* This should be something like "foo&bar&equ;=grob&" */ QStringList paramChunk = params.value(i).split(QStringLiteral("="), QString::KeepEmptyParts); if (paramChunk.count() != 2) return false; QString arg = paramChunk.value(0); QString param = paramChunk.value(1); arg.replace(QStringLiteral(":"), QStringLiteral(":")); arg.replace(QStringLiteral("&equ;"), QStringLiteral("=")); arg.replace(QStringLiteral("&"), QStringLiteral("&")); param.replace(QStringLiteral(":"), QStringLiteral(":")); param.replace(QStringLiteral("&equ;"), QStringLiteral("=")); param.replace(QStringLiteral("&"), QStringLiteral("&")); if (arg.isEmpty()) return false; outParams.insert(arg, param); } } // and unescape the engine id string. engIdString.replace(QStringLiteral(":"), QStringLiteral(":")); engIdString.replace(QStringLiteral("&"), QStringLiteral("&")); engIdString.replace(QStringLiteral("&equ;"), QStringLiteral("=")); // now fill the return values. if (managerName) *managerName = mgrName; if (params) *params = outParams; if (engineIdString) *engineIdString = engIdString; // and return. return true; } /*! Serializes the ID to a string. The format of the string will be: "qtorganizer:managerName:constructionParams:serializedEngineLocalItemId" */ QString QOrganizerItemId::toString() const { QString mgrName; QMap params; QString engineId; if (d) { QOrganizerManager::parseUri(d->managerUri(), &mgrName, ¶ms); engineId = d->toString(); } // having extracted the params the name, we now need to build a new string. return buildIdString(mgrName, params, engineId); } /*! Deserializes the given \a idString. Returns a default-constructed (null) item ID if the given \a idString is not a valid, serialized item ID, or if the manager engine from which the ID came could not be found. */ QOrganizerItemId QOrganizerItemId::fromString(const QString &idString) { QString managerName; QMap params; QString engineIdString; if (!parseIdString(idString, &managerName, ¶ms, &engineIdString)) return QOrganizerItemId(); // invalid idString given. QOrganizerItemEngineId *engineId = QOrganizerManagerData::createEngineItemId(managerName, params, engineIdString); return QOrganizerItemId(engineId); } QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizeritemid.h000066400000000000000000000073111233466112000173730ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMID_H #define QORGANIZERITEMID_H #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManagerEngine; class QOrganizerItemEngineId; class Q_ORGANIZER_EXPORT QOrganizerItemId { public: QOrganizerItemId(); QOrganizerItemId(const QOrganizerItemId &other); explicit QOrganizerItemId(QOrganizerItemEngineId *engineId); ~QOrganizerItemId(); QOrganizerItemId &operator=(const QOrganizerItemId &other); bool operator==(const QOrganizerItemId &other) const; bool operator!=(const QOrganizerItemId &other) const; bool operator<(const QOrganizerItemId &other) const; bool isNull() const; QString managerUri() const; QString toString() const; static QOrganizerItemId fromString(const QString &idString); private: QSharedDataPointer d; #ifndef QT_NO_DEBUG_STREAM friend Q_ORGANIZER_EXPORT QDebug operator<<(QDebug dbg, const QOrganizerItemId &itemId); #endif friend Q_ORGANIZER_EXPORT uint qHash(const QOrganizerItemId &key); friend class QOrganizerManagerEngine; }; Q_ORGANIZER_EXPORT uint qHash(const QOrganizerItemId &key); #ifndef QT_NO_DATASTREAM Q_ORGANIZER_EXPORT QDataStream &operator<<(QDataStream &out, const QOrganizerItemId &itemId); Q_ORGANIZER_EXPORT QDataStream &operator>>(QDataStream &in, QOrganizerItemId &itemId); #endif #ifndef QT_NO_DEBUG_STREAM Q_ORGANIZER_EXPORT QDebug operator<<(QDebug dbg, const QOrganizerItemId &itemId); #endif QT_END_NAMESPACE_ORGANIZER QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(QTORGANIZER_PREPEND_NAMESPACE(QOrganizerItemId), Q_MOVABLE_TYPE); QT_END_NAMESPACE Q_DECLARE_METATYPE(QTORGANIZER_PREPEND_NAMESPACE(QOrganizerItemId)); #endif // QORGANIZERITEMID_H src/organizer/qorganizeritemid_p.h000066400000000000000000000051711233466112000177140ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMID_P_H #define QORGANIZERITEMID_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_ORGANIZER extern QString buildIdString(const QString &managerName, const QMap ¶ms, const QString &engineIdString); extern bool parseIdString(const QString &idString, QString *managerName, QMap *params, QString *engineIdString); QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMID_P_H src/organizer/qorganizeritemobserver.cpp000066400000000000000000000072021233466112000211600ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemobserver.h" #include #include "qorganizermanager.h" #include "qorganizermanager_p.h" QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemObserverPrivate { public: QOrganizerItemId m_id; QPointer m_manager; QOrganizerManagerData *m_managerPrivate; }; /*! \class QOrganizerItemObserver \brief The QOrganizerItemObserver class is a simple class that emits a signal when a single particular item is updated or deleted. \inmodule QtOrganizer \ingroup organizer-main */ /*! Constructs a QOrganizerItemObserver to observe the item in \a manager with the given \a itemId and a \a parent object. */ QOrganizerItemObserver::QOrganizerItemObserver(QOrganizerManager *manager, const QOrganizerItemId &itemId, QObject *parent) : QObject(parent), d(new QOrganizerItemObserverPrivate) { d->m_id = itemId; d->m_manager = manager; d->m_managerPrivate = QOrganizerManagerData::get(manager); d->m_managerPrivate->registerObserver(this); } /*! Destroys this observer. */ QOrganizerItemObserver::~QOrganizerItemObserver() { if (d->m_manager.data()) d->m_managerPrivate->unregisterObserver(this); delete d; } /*! Returns the ID of the item that this object observes. */ QOrganizerItemId QOrganizerItemObserver::itemId() const { return d->m_id; } /*! \fn void QOrganizerItemObserver::itemChanged() This signal is emitted when the observed item is changed in the manager. */ /*! \fn void QOrganizerItemObserver::itemRemoved() This signal is emitted when the observed item is removed from the manager. */ #include "moc_qorganizeritemobserver.cpp" QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizeritemobserver.h000066400000000000000000000052021233466112000206230ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMOBSERVER_H #define QORGANIZERITEMOBSERVER_H #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManager; class QOrganizerItemObserverPrivate; class Q_ORGANIZER_EXPORT QOrganizerItemObserver : public QObject { Q_OBJECT public: QOrganizerItemObserver(QOrganizerManager *manager, const QOrganizerItemId &itemId, QObject *parent = 0); ~QOrganizerItemObserver(); QOrganizerItemId itemId() const; Q_SIGNALS: void itemChanged(); void itemRemoved(); private: Q_DISABLE_COPY(QOrganizerItemObserver) QOrganizerItemObserverPrivate *d; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMOBSERVER_H src/organizer/qorganizeritemsortorder.cpp000066400000000000000000000220451233466112000213560ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemsortorder.h" #include "qorganizeritemsortorder_p.h" #ifndef QT_NO_DATASTREAM #include #endif #ifndef QT_NO_DEBUG_STREAM #include #endif QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemSortOrder \brief The QOrganizerItemSortOrder class defines how a list of organizer items should be ordered according to some criteria. \inmodule QtOrganizer \ingroup organizer-filters */ /*! \enum QOrganizerItemSortOrder::BlankPolicy Enumerates the ways in which the sort order interprets blanks when sorting organizer items. \value BlanksFirst Considers blank values to evaluate to less than all other values in comparisons. \value BlanksLast Considers blank values to evaluate to greater than all other values in comparisons. */ /*! \fn QOrganizerItemSortOrder::operator QList() const Constructs a new list of sort orders containing only the current sort order. */ /*! \fn QOrganizerItemSortOrder::operator!=(const QOrganizerItemSortOrder &other) const Returns true if this sort order is not identical to the \a other sort order. \sa operator==() */ /*! Constructs a new sort order */ QOrganizerItemSortOrder::QOrganizerItemSortOrder() : d(new QOrganizerItemSortOrderPrivate()) { } /*! Frees any memory in use by this sort order */ QOrganizerItemSortOrder::~QOrganizerItemSortOrder() { } /*! Constructs a copy of the \a other sort order. */ QOrganizerItemSortOrder::QOrganizerItemSortOrder(const QOrganizerItemSortOrder &other) : d(other.d) { } /*! Assigns this sort order to be equal to \a other. */ QOrganizerItemSortOrder &QOrganizerItemSortOrder::operator=(const QOrganizerItemSortOrder &other) { d = other.d; return *this; } /*! Returns true if the sort order is able to be used to sort a list of organizer items; otherwise, returns false. */ bool QOrganizerItemSortOrder::isValid() const { return d->m_detailType != QOrganizerItemDetail::TypeUndefined; } /*! Returns true if this sort order is identical to the \a other sort order. \sa operator!=() */ bool QOrganizerItemSortOrder::operator ==(const QOrganizerItemSortOrder &other) const { if (d == other.d) return true; return d->m_blankPolicy == other.d->m_blankPolicy && d->m_direction == other.d->m_direction && d->m_sensitivity == other.d->m_sensitivity && d->m_detailType == other.d->m_detailType && d->m_detailField == other.d->m_detailField; } #ifndef QT_NO_DATASTREAM /*! \relates QOrganizerItemSortOrder Writes \a sortOrder to the stream \a out. */ QDataStream &operator<<(QDataStream &out, const QOrganizerItemSortOrder &sortOrder) { quint8 formatVersion = 1; // Version of QDataStream format for QOrganizerItemSortOrder return out << formatVersion << sortOrder.detailType() << sortOrder.detailField() << static_cast(sortOrder.blankPolicy()) << static_cast(sortOrder.direction()) << static_cast(sortOrder.caseSensitivity()); } /*! \relates QOrganizerItemSortOrder Reads a sort order from stream \a in into \a sortOrder. */ QDataStream &operator>>(QDataStream &in, QOrganizerItemSortOrder &sortOrder) { quint8 formatVersion; in >> formatVersion; if (formatVersion == 1) { quint32 detailType; int fieldName; quint32 blankPolicy; quint32 direction; quint32 caseSensitivity; in >> detailType >> fieldName >> blankPolicy >> direction >> caseSensitivity; sortOrder.setDetail(static_cast(detailType), fieldName); sortOrder.setBlankPolicy(static_cast(blankPolicy)); sortOrder.setDirection(static_cast(direction)); sortOrder.setCaseSensitivity(static_cast(caseSensitivity)); } else { in.setStatus(QDataStream::ReadCorruptData); } return in; } #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM /*! \relates QOrganizerItemSortOrder Outputs \a sortOrder to the debug stream \a dbg. */ QDebug operator<<(QDebug dbg, const QOrganizerItemSortOrder &sortOrder) { dbg.nospace() << "QOrganizerItemSortOrder("; dbg.nospace() << "detailType="; dbg.nospace() << sortOrder.detailType(); dbg.nospace() << ","; dbg.nospace() << "detailField="; dbg.nospace() << sortOrder.detailField(); dbg.nospace() << ","; dbg.nospace() << "blankPolicy="; dbg.nospace() << static_cast(sortOrder.blankPolicy()); dbg.nospace() << ","; dbg.nospace() << "direction="; dbg.nospace() << static_cast(sortOrder.direction()); dbg.nospace() << ","; dbg.nospace() << "caseSensitivity="; dbg.nospace() << static_cast(sortOrder.caseSensitivity()); dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif // QT_NO_DEBUG_STREAM /*! Sets the type of detail which will be inspected for sorting to \a detailType, and the field of the detail to \a field. If \a field is not specified, or equal to -1, the organizer item with a detail of the specified type would appear before or after the organizer item that lacks a detail of the specified type, according to blankPolicy(). \sa detailType(), detailField() */ void QOrganizerItemSortOrder::setDetail(QOrganizerItemDetail::DetailType detailType, int field) { d->m_detailType = detailType; d->m_detailField = field; } /*! Sets the sort order's policy on blank values with respect to sorting to \a blankPolicy. \sa blankPolicy() */ void QOrganizerItemSortOrder::setBlankPolicy(BlankPolicy blankPolicy) { d->m_blankPolicy = blankPolicy; } /*! Sets the sort order direction to \a direction. \sa direction() */ void QOrganizerItemSortOrder::setDirection(Qt::SortOrder direction) { d->m_direction = direction; } /*! Returns the type of the detail which will be inspected to perform sorting. \sa setDetail() */ QOrganizerItemDetail::DetailType QOrganizerItemSortOrder::detailType() const { return d->m_detailType; } /*! Returns the detail field which will be inspected to perform sorting. \sa setDetail() */ int QOrganizerItemSortOrder::detailField() const { return d->m_detailField; } /*! Returns the blank policy of the sort order. \sa setBlankPolicy() */ QOrganizerItemSortOrder::BlankPolicy QOrganizerItemSortOrder::blankPolicy() const { return d->m_blankPolicy; } /*! Returns the direction of the sort order. \sa setDirection() */ Qt::SortOrder QOrganizerItemSortOrder::direction() const { return d->m_direction; } /*! Returns the case sensitivity of the sort order. \sa setCaseSensitivity() */ Qt::CaseSensitivity QOrganizerItemSortOrder::caseSensitivity() const { return d->m_sensitivity; } /*! Sets the case sensitivity of the sort order to \a sensitivity. \sa caseSensitivity() */ void QOrganizerItemSortOrder::setCaseSensitivity(Qt::CaseSensitivity sensitivity) { d->m_sensitivity = sensitivity; } QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizeritemsortorder.h000066400000000000000000000076741233466112000210360ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMSORTORDER_H #define QORGANIZERITEMSORTORDER_H #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemSortOrderPrivate; class Q_ORGANIZER_EXPORT QOrganizerItemSortOrder { public: QOrganizerItemSortOrder(); ~QOrganizerItemSortOrder(); QOrganizerItemSortOrder(const QOrganizerItemSortOrder &other); QOrganizerItemSortOrder &operator=(const QOrganizerItemSortOrder &other); enum BlankPolicy { BlanksFirst, BlanksLast }; // mutators void setDetail(QOrganizerItemDetail::DetailType detailType, int field = -1); void setBlankPolicy(BlankPolicy blankPolicy); void setDirection(Qt::SortOrder direction); void setCaseSensitivity(Qt::CaseSensitivity sensitivity); // accessors QOrganizerItemDetail::DetailType detailType() const; int detailField() const; BlankPolicy blankPolicy() const; Qt::SortOrder direction() const; Qt::CaseSensitivity caseSensitivity() const; bool isValid() const; bool operator==(const QOrganizerItemSortOrder &other) const; bool operator!=(const QOrganizerItemSortOrder &other) const { return !operator==(other); } // convenience cast operator QList() const { return QList() << *this; } private: QSharedDataPointer d; }; #ifndef QT_NO_DATASTREAM Q_ORGANIZER_EXPORT QDataStream &operator<<(QDataStream &out, const QOrganizerItemSortOrder &sortOrder); Q_ORGANIZER_EXPORT QDataStream &operator>>(QDataStream &in, QOrganizerItemSortOrder &sortOrder); #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM Q_ORGANIZER_EXPORT QDebug operator<<(QDebug dbg, const QOrganizerItemSortOrder &sortOrder); #endif // QT_NO_DEBUG_STREAM QT_END_NAMESPACE_ORGANIZER QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(QTORGANIZER_PREPEND_NAMESPACE(QOrganizerItemSortOrder), Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif // QORGANIZERITEMSORTORDER_H src/organizer/qorganizeritemsortorder_p.h000066400000000000000000000061151233466112000213420ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMSORTORDER_P_H #define QORGANIZERITEMSORTORDER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemSortOrderPrivate : public QSharedData { public: QOrganizerItemSortOrderPrivate() : QSharedData() , m_blankPolicy(QOrganizerItemSortOrder::BlanksLast) , m_direction(Qt::AscendingOrder) , m_sensitivity(Qt::CaseSensitive) , m_detailType(QOrganizerItemDetail::TypeUndefined) , m_detailField(-1) { } ~QOrganizerItemSortOrderPrivate() { } QOrganizerItemSortOrder::BlankPolicy m_blankPolicy; Qt::SortOrder m_direction; Qt::CaseSensitivity m_sensitivity; QOrganizerItemDetail::DetailType m_detailType; int m_detailField; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMSORTORDER_P_H src/organizer/qorganizermanager.cpp000066400000000000000000001123641233466112000200720ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizermanager.h" #include "qorganizermanager_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerManager \brief The QOrganizerManager class provides an interface which allows clients with access to organizer item and collection information stored in a particular backend. \inmodule QtOrganizer \ingroup organizer-main This class provides synchronous methods to retrieve and manipulate organizer item information, collection information, as well as functionality reporting and error information reporting. Clients can also use the use-case-specific classes derived from QOrganizerAbstractRequest to retrieve and manipulate organizer items and collections in an asynchronous manner. However, certain functionality (e.g. backend functionality reporting) can not be accessed using the asynchronous APIs. See the \l{Qt Organizer Synchronous API}{synchronous} and \l{Qt Organizer Asynchronous API}{asynchronous} API information from the \l{Qt Organizer Overview}{organizer module} API documentation for more details. When constructing a QOrganizerManager instance, certain parameters can be given to provide more control, e.g. to specify the version of the backend it wants to construct. Note that the parameters returned when calling managerParameters() are not necessarily the same as the ones passed in, since certain parameters might be discarded or added by the backend. */ /*! \enum QOrganizerManager::Operation This enumeration describes the operation that has been done to the item or collection modified. \value Add The item / collection has been added. \value Change The item / collection has been changed. \value Remove The item / collection has been removed. */ /*! \fn QOrganizerManager::itemsModified(const QList > &itemIds); This signal is emitted at some point once the items have been modified in a datastore managed by this manager. In the \a itemIds list, the item ID tells which item has been modified with the corresponding operation. This signal will not be emitted if the dataChanged() signal was previously emitted for these changes. \sa dataChanged() */ /*! \fn QOrganizerManager::collectionsModified(const QList > &collectionIds); This signal is emitted at some point once the collections have been modified in a datastore managed by this manager. In the \a collectionIds list, the collection ID tells which item has been modified with the corresponding operation. This signal will not be emitted if the dataChanged() signal was previously emitted for these changes. \sa dataChanged() */ /*! \macro QTORGANIZER_BACKEND_VERSION \relates QOrganizerManager This macro tells the parameter is to specify the version the backend that the customer wants to create. If the specified version is not available, a backend with the given name and a default version will be created for this manager. */ /*! \fn QOrganizerManager::dataChanged() This signal is emitted by the manager if its internal state changes, and it is unable to determine the changes which occurred, or if the manager considers the changes to be radical enough to require clients to reload all data. If this signal is emitted, no other signals will be emitted for the associated changes. \sa itemsAdded(), itemsChanged(), itemsRemoved() */ /*! \fn QOrganizerManager::itemsAdded(const QList &itemIds) This signal is emitted at some point once the items identified by \a itemIds have been added to a datastore managed by this manager. This signal will not be emitted if the dataChanged() signal was previously emitted for these changes. \sa dataChanged() */ /*! \fn QOrganizerManager::itemsChanged(const QList &itemIds) This signal is emitted at some point once the items identified by \a itemIds have been modified in a datastore managed by this manager. This signal will not be emitted if the dataChanged() signal was previously emitted for these changes. \sa dataChanged() */ /*! \fn QOrganizerManager::itemsRemoved(const QList &itemIds) This signal is emitted at some point once the items identified by \a itemIds have been removed from a datastore managed by this manager. This signal will not be emitted if the dataChanged() signal was previously emitted for these changes. \sa dataChanged() */ /*! \fn QOrganizerManager::collectionsAdded(const QList &collectionIds) This signal is emitted at some point once the collections identified by \a collectionIds have been added to a datastore managed by this manager. This signal will not be emitted if the dataChanged() signal was previously emitted for these changes. \sa dataChanged() */ /*! \fn QOrganizerManager::collectionsChanged(const QList &collectionIds) This signal is emitted at some point once the metadata for the collections identified by \a collectionIds have been modified in a datastore managed by this manager. This signal will not be emitted if items in the collections have been added, modified, or removed. This signal will not be emitted if the dataChanged() signal was previously emitted for these changes. \sa dataChanged() */ /*! \fn QOrganizerManager::collectionsRemoved(const QList &collectionIds) This signal is emitted at some point once the collections identified by \a collectionIds have been removed from a datastore managed by this manager. This signal will not be emitted if the dataChanged() signal was previously emitted for these changes. \sa dataChanged() */ #define makestr(x) (#x) #define makename(x) makestr(x) /*! Returns a list of available manager names that can be used to construct a QOrganizerManager. */ QStringList QOrganizerManager::availableManagers() { QStringList ret; ret << QStringLiteral("invalid"); QOrganizerManagerData::loadFactories(); ret.append(QOrganizerManagerData::m_engines.keys()); // now swizzle the default engine to pole position #if defined(Q_ORGANIZER_DEFAULT_ENGINE) if (ret.removeAll(QLatin1String(makename(Q_ORGANIZER_DEFAULT_ENGINE)))) ret.prepend(QLatin1String(makename(Q_ORGANIZER_DEFAULT_ENGINE))); #endif return ret; } /*! Splits the given \a uri into the manager name and parameters that it describes, and places the information into the memory addressed by \a pManagerName and \a pParams respectively. Returns true if \a uri could be split successfully, otherwise returns false. */ bool QOrganizerManager::parseUri(const QString &uri, QString *pManagerName, QMap *pParams) { // Format: qtorganizer::=&= // 1) parameters are currently a qstringlist.. should they be a map? // 2) is the uri going to be escaped? my guess would be "probably not" // 3) hence, do we assume that the prefix, managerid and storeid cannot contain `:' // 4) similarly, that neither keys nor values can contain `=' or `&' QStringList colonSplit = uri.split(QLatin1Char(':')); QString prefix = colonSplit.value(0); if (prefix != QStringLiteral("qtorganizer")) return false; QString managerName = colonSplit.value(1); if (managerName.trimmed().isEmpty()) return false; QString firstParts = prefix + QLatin1Char(':') + managerName + QLatin1Char(':'); QString paramString = uri.mid(firstParts.length()); QMap outParams; // Now we have to decode each parameter if (!paramString.isEmpty()) { QStringList params = paramString.split(QRegExp(QStringLiteral("&(?!(amp;|equ;))")), QString::KeepEmptyParts); // If we have an empty string for paramstring, we get one entry in params, // so skip that case. for (int i = 0; i < params.count(); ++i) { /* This should be something like "foo&bar&equ;=grob&" */ QStringList paramChunk = params.value(i).split(QStringLiteral("="), QString::KeepEmptyParts); if (paramChunk.count() != 2) return false; QString arg = paramChunk.value(0); QString param = paramChunk.value(1); arg.replace(QStringLiteral("&equ;"), QStringLiteral("=")); arg.replace(QStringLiteral("&"), QStringLiteral("&")); param.replace(QStringLiteral("&equ;"), QStringLiteral("=")); param.replace(QStringLiteral("&"), QStringLiteral("&")); if (arg.isEmpty()) return false; outParams.insert(arg, param); } } if (pParams) *pParams = outParams; if (pManagerName) *pManagerName = managerName; return true; } /*! Returns a URI that describes a manager name, parameters, and version with which to instantiate a manager object, from the given \a managerName and \a params. */ QString QOrganizerManager::buildUri(const QString &managerName, const QMap ¶ms) { QString ret(QStringLiteral("qtorganizer:%1:%2")); // we have to escape each param QStringList escapedParams; QStringList keys = params.keys(); for (int i=0; i < keys.size(); ++i) { QString key = keys.at(i); QString arg = params.value(key); arg = arg.replace(QLatin1Char('&'), QStringLiteral("&")); arg = arg.replace(QLatin1Char('='), QStringLiteral("&equ;")); key = key.replace(QLatin1Char('&'), QStringLiteral("&")); key = key.replace(QLatin1Char('='), QStringLiteral("&equ;")); key = key + QLatin1Char('=') + arg; escapedParams.append(key); } return ret.arg(managerName, escapedParams.join(QStringLiteral("&"))); } /*! Constructs a QOrganizerManager whose name, parameters, and version are specified in the given \a uri, and whose parent object is \a parent. */ QOrganizerManager *QOrganizerManager::fromUri(const QString &uri, QObject *parent) { if (uri.isEmpty()) { return new QOrganizerManager(QString(), QMap(), parent); } else { QString id; QMap parameters; if (parseUri(uri, &id, ¶meters)) return new QOrganizerManager(id, parameters, parent); else return new QOrganizerManager(QStringLiteral("invalid"), QMap(), parent); } } /*! Constructs a QOrganizerManager whose parent object is \a parent. The default backend, i.e. the first one returned by the availableManagers() function, for the platform will be created. */ QOrganizerManager::QOrganizerManager(QObject *parent) : QObject(parent), d(new QOrganizerManagerData) { createEngine(QString(), QMap()); } /*! Constructs a QOrganizerManager whose backend is identified by \a managerName with the given \a parameters, and the parent object is \a parent. The default backend, i.e. the first one returned by the availableManagers() function, for the platform will be created, if the \a managerName is empty. If the backend identified by \a managerName does not exist, an invalid backend is created. */ QOrganizerManager::QOrganizerManager(const QString &managerName, const QMap ¶meters, QObject *parent) : QObject(parent), d(new QOrganizerManagerData) { createEngine(managerName, parameters); } void QOrganizerManager::createEngine(const QString &managerName, const QMap ¶meters) { d->createEngine(managerName, parameters); connect(d->m_engine, SIGNAL(dataChanged()), this, SIGNAL(dataChanged())); connect(d->m_engine, SIGNAL(itemsAdded(QList)), this, SIGNAL(itemsAdded(QList))); connect(d->m_engine, SIGNAL(itemsChanged(QList)), this, SIGNAL(itemsChanged(QList))); connect(d->m_engine, SIGNAL(itemsRemoved(QList)), this, SIGNAL(itemsRemoved(QList))); connect(d->m_engine, SIGNAL(itemsModified(QList >)), this, SIGNAL(itemsModified(QList >))); connect(d->m_engine, SIGNAL(collectionsAdded(QList)), this, SIGNAL(collectionsAdded(QList))); connect(d->m_engine, SIGNAL(collectionsChanged(QList)), this, SIGNAL(collectionsChanged(QList))); connect(d->m_engine, SIGNAL(collectionsRemoved(QList)), this, SIGNAL(collectionsRemoved(QList))); connect(d->m_engine, SIGNAL(collectionsModified(QList >)), this, SIGNAL(collectionsModified(QList >))); connect(d->m_engine, SIGNAL(itemsChanged(QList)), this, SLOT(_q_itemsUpdated(QList))); connect(d->m_engine, SIGNAL(itemsRemoved(QList)), this, SLOT(_q_itemsDeleted(QList))); } /*! Frees the memory used by the QOrganizerManager. */ QOrganizerManager::~QOrganizerManager() { delete d; } /*! \enum QOrganizerManager::Error This enum specifies an error that occurred during the most recent operation: \value NoError The most recent operation was successful \value DoesNotExistError The most recent operation failed because the requested organizer item or detail definition does not exist \value AlreadyExistsError The most recent operation failed because the specified organizer item or detail definition already exists \value InvalidDetailError The most recent operation failed because the specified organizer detail definition already exists \value LockedError The most recent operation failed because the datastore specified is currently locked \value DetailAccessError The most recent operation failed because a detail was modified or removed and its access method does not allow that \value PermissionsError The most recent operation failed because the caller does not have permission to perform the operation \value OutOfMemoryError The most recent operation failed due to running out of memory \value NotSupportedError The most recent operation failed because the requested operation is not supported in the specified store \value BadArgumentError The most recent operation failed because one or more of the parameters to the operation were invalid \value UnspecifiedError The most recent operation failed for an undocumented reason \value LimitReachedError The most recent operation failed because the limit for that type of object has been reached \value InvalidItemTypeError The most recent operation failed because the item given was of an invalid type for the operation \value InvalidCollectionError The most recent operation failed because the collection is invalid \value InvalidOccurrenceError The most recent operation failed because it was an attempt to save an occurrence without a correct InstanceOrigin detail \value TimeoutError The most recent operation failed because it took longer than expected. It may be possible to try again. Backend specific documentation might have more details on exact error cases. */ /*! Return the error code of the most recent operation. \sa errorMap() */ QOrganizerManager::Error QOrganizerManager::error() const { return d->m_lastError; } /*! Returns per-input error codes for the most recent operation. This function only returns meaningful information if the most recent operation was a batch operation. Each key in the map is the index of the element in the input list for which the error (whose error code is stored in the value for that key in the map) occurred during the batch operation. \sa error(), saveItems(), removeItems() */ QMap QOrganizerManager::errorMap() const { return d->m_lastErrorMap; } /*! Returns a list of a maximum of \a maxCount organizer item instances which are occurrences of the given \a parentItem recurring item, which occur between the given \a startDateTime and the given \a endDateTime date, inclusive. A default-constructed (invalid) \a startDateTime specifies an open start date time (matches anything which occurs up until the \a endDateTime), and a default-constructed (invalid) \a endDateTime specifies an open end date time (matches anything which occurs after the \a startDateTime). If both the \a startDateTime and \a endDateTime are invalid, this function will return the IDs of all items. Note that backends will decide how many occurrences are returned if \a maxCount is negative. The \a fetchHint allows clients to specify which pieces of information they are interested or not interested in, to allow backends to optimise data retrieval if possible. Note that it is simply a hint; backends can ignore the \a fetchHint and return the full item. If a client makes changes to an organizer item which has been retrieved with a fetch hint, they should save it back using a partial save, masked by the same set of detail types in order to avoid information loss. */ QList QOrganizerManager::itemOccurrences(const QOrganizerItem &parentItem,const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QOrganizerItemFetchHint &fetchHint) { QOrganizerManagerSyncOpErrorHolder h(this); return d->m_engine->itemOccurrences(parentItem, startDateTime, endDateTime, maxCount, fetchHint, &h.error); } /*! Returns a list of item IDs of persisted organizer items that match the given \a filter, sorted according to the given list of \a sortOrders, for any item which occurs (or has an occurrence which occurs) in the range specified by the given \a startDateTime and \a endDateTime, inclusive. A default-constructed (invalid) \a startDateTime specifies an open start date time (matches anything which occurs up until the \a endDateTime), and a default-constructed (invalid) \a endDateTime specifies an open end date time (matches anything which occurs after the \a startDateTime). If both the \a startDateTime and \a endDateTime are invalid, this function will return the IDs of all items which match the \a filter criteria. Note that certain backends may ignore the given \a filter. */ QList QOrganizerManager::itemIds(const QDateTime &startDateTime, const QDateTime &endDateTime, const QOrganizerItemFilter &filter, const QList &sortOrders) { QOrganizerManagerSyncOpErrorHolder h(this); return d->m_engine->itemIds(filter, startDateTime, endDateTime, sortOrders, &h.error); } /*! Returns a list of a maximum of \a maxCount organizer items and occurrences that match the given \a filter, which occur in the range specified by the given \a startDateTime and \a endDateTime, inclusive, and sorted according to the given list of \a sortOrders. A default-constructed (invalid) \a startDateTime specifies an open start date time (matches anything which occurs up until the \a endDateTime), and a default-constructed (invalid) \a endDateTime specifies an open end date time (matches anything which occurs after the \a startDateTime). If both the \a startDateTime and \a endDateTime are invalid, this function will return the IDs of all items which match the \a filter criteria. If no sort order is provided, the list is returned sorted by date. Note that certain backends may ignore the given \a filter. Note that backends will decide how many items are returned if \a maxCount is negative. The \a fetchHint allows clients to specify which pieces of information they are interested or not interested in, to allow backends to optimise data retrieval if possible. Note that it is simply a hint; backends can ignore the \a fetchHint and return the full item. If a client makes changes to an organizer item which has been retrieved with a fetch hint, they should save it back using a partial save, masked by the same set of detail types in order to avoid information loss. */ QList QOrganizerManager::items(const QDateTime &startDateTime, const QDateTime &endDateTime, const QOrganizerItemFilter &filter, int maxCount, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint) { QOrganizerManagerSyncOpErrorHolder h(this); return d->m_engine->items(filter, startDateTime, endDateTime, maxCount, sortOrders, fetchHint, &h.error); } /*! Returns a list of organizer items that match the given \a filter, sorted according to the given list of \a sortOrders, for any item which occurs (or has an occurrence which occurs) in the range specified by the given \a startDateTime and \a endDateTime, inclusive. A default-constructed (invalid) \a startDateTime specifies an open start date time (matches anything which occurs up until the \a endDateTime), and a default-constructed (invalid) \a endDateTime specifies an open end date time (matches anything which occurs after the \a startDateTime). If both the \a startDateTime and \a endDateTime are invalid, this function will return the IDs of all items which match the \a filter criteria. This function will only return parent items and persisted exceptions which match the specified criteria; not generated occurrences. Note that certain backends may ignore the given \a filter. The \a fetchHint allows clients to specify which pieces of information they are interested or not interested in, to allow backends to optimise data retrieval if possible. Note that it is simply a hint; backends can ignore the \a fetchHint and return the full item. If a client makes changes to an organizer item which has been retrieved with a fetch hint, they should save it back using a partial save, masked by the same set of detail types in order to avoid information loss. */ QList QOrganizerManager::itemsForExport(const QDateTime &startDateTime, const QDateTime &endDateTime, const QOrganizerItemFilter &filter, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint) { QOrganizerManagerSyncOpErrorHolder h(this); return d->m_engine->itemsForExport(startDateTime, endDateTime, filter, sortOrders, fetchHint, &h.error); } /*! Returns the organizer items in the database identified by \a itemIds. The items fetched by the backend should have a one-to-one correspondence to the IDs passed into this class. That is, the nth item in the returned list should have an ID which is equal to the nth ID in the list of IDs. Any invalid ID should correspond to an empty QOrganizerItem. The \a fetchHint allows clients to specify which pieces of information they are interested or not interested in, to allow backends to optimise data retrieval if possible. Note that it is simply a hint; backends can ignore the \a fetchHint and return the full item. If a client makes changes to an organizer item which has been retrieved with a fetch hint, they should save it back using a partial save, masked by the same set of detail types in order to avoid information loss. Calling \l errorMap() will return the per-input errors for the fetch. The \l QOrganizerManager::error() function will only return \c QOrganizerManager::NoError if all organizer items are fetched successfully. */ QList QOrganizerManager::items(const QList &itemIds, const QOrganizerItemFetchHint &fetchHint) { QOrganizerManagerSyncOpErrorHolder h(this); return d->m_engine->items(itemIds, fetchHint, &h.errorMap, &h.error); } /*! Returns the organizer item in the database identified by \a itemId, or an empty item if the specified item does not exist. The \a fetchHint allows clients to specify which pieces of information they are interested or not interested in, to allow backends to optimise data retrieval if possible. Note that it is simply a hint; backends can ignore the \a fetchHint and return the full item. If a client makes changes to an organizer item which has been retrieved with a fetch hint, they should save it back using a partial save, masked by the same set of detail types in order to avoid information loss. */ QOrganizerItem QOrganizerManager::item(const QOrganizerItemId& itemId, const QOrganizerItemFetchHint& fetchHint) { QList tmp = items(QList() << itemId, fetchHint); if (tmp.size() > 0) return tmp.at(0); return QOrganizerItem(); } /*! Saves the given \a item to the backend, and returns true on success or false otherwise. A new organizer item will be created in the backend store if the item ID of it is null. Otherwise, an existing item with the same ID will be updated. If the given item ID does not exist in the backend, it will result a QOrganizerManager::DoesNotExistError error. If the collection ID of the item is null, it will be saved to the default collection. If the given collection ID doesn't exist, the saving will fail. If the \a detailMask is empty, only the details currently existing in the item will be saved. Otherwise, only details masked by the \a detailMask will be saved or updated, others are kept untouched. It's useful to avoid information loss if the items are retrieved with a fetch hint. Note that upon successful saving, the backend may update the item, e.g. item ID for newly saved items, GUID, timestamp, version, etc. \sa saveItems() */ bool QOrganizerManager::saveItem(QOrganizerItem *item, const QList &detailMask) { QList items; items.append(*item); bool rtn = saveItems(&items, detailMask); *item = items.at(0); return rtn; } /*! Removes all the item whose ID is \a itemId, and all its occurrences. Returns true if the item and occurrences are successfully removed, or false otherwise. \sa removeItems() */ bool QOrganizerManager::removeItem(const QOrganizerItemId& itemId) { return removeItems(QList() << itemId); } /*! Remove the organizer \a item from the database. If \a item is a generated occurrence, the start date of the occurrence is added to its parent item's exception date list. Returns true if the organizer item was removed successfully, otherwise returns false. \sa removeItems() */ bool QOrganizerManager::removeItem(const QOrganizerItem *item) { QList list; list.append(*item); return removeItems(&list); } /*! Saves the given list of \a items to the backend, and returns true on success or false otherwise. A new organizer item will be created in the backend store if the item ID of it is null. Otherwise, an existing item with the same ID will be updated. If the given item ID does not exist in the backend, it will result a QOrganizerManager::DoesNotExistError error. If the collection ID of the item is null, it will be saved to the default collection. If the given collection ID doesn't exist, the saving will fail. If the \a detailMask is empty, only the details currently existing in the item will be saved. Otherwise, only details masked by the \a detailMask will be saved or updated, others are kept untouched. It's useful to avoid information loss if the items are retrieved with a fetch hint. Note that upon successful saving, the backend may update the item, e.g. item ID for newly saved items, GUID, timestamp, version, etc. Calling errorMap() will return the per-input errors for the operation. The error() function will only return QOrganizerManager::NoError if all organizer items were saved successfully. \sa saveItem() */ bool QOrganizerManager::saveItems(QList* items, const QList &detailMask) { QOrganizerManagerSyncOpErrorHolder h(this); if (!items) { h.error = QOrganizerManager::BadArgumentError; return false; } return d->m_engine->saveItems(items, detailMask, &h.errorMap, &h.error); } /*! Removes all the items whose ID is contained in the given list of \a itemIds, and all the occurrences whose parent ID is containd in the \a itemIds. Returns true if all the items and occurrences are successfully removed, or false otherwise. Calling errorMap() will return the per-input errors for the operation. The error() function will only return QOrganizerManager::NoError if all organizer items were saved successfully. \sa removeItem() */ bool QOrganizerManager::removeItems(const QList &itemIds) { QOrganizerManagerSyncOpErrorHolder h(this); if (itemIds.isEmpty()) { h.error = QOrganizerManager::BadArgumentError; return false; } return d->m_engine->removeItems(itemIds, &h.errorMap, &h.error); } /*! Removes every organizer item which is contained in the list of organizer items \a items. If the list contains generated occurrences the start date of the occurrence is added to its parent item's exception date list. Returns true if all organizer items were removed successfully, otherwise false. Calling \l errorMap() will return the per-input errors for the latest batch function. The \l QOrganizerManager::error() function will only return \c QOrganizerManager::NoError if all organizer items were removed successfully. If the given list of organizer \a items is empty, the function will return false and calling error() will return \c QOrganizerManager::BadArgumentError. If the list is non-empty and contains items which are not a valid organizer item in the manager, the function will remove any valid organizer items in the \a items list, insert \c QOrganizerManager::DoesNotExist entries into the error map for the indices of invalid id in the \a items list, return false, and set the overall operation error to \c QOrganizerManager::DoesNotExistError. \sa QOrganizerManager::removeItem() */ bool QOrganizerManager::removeItems(const QList *items) { QOrganizerManagerSyncOpErrorHolder h(this); if (items->isEmpty()) { h.error = QOrganizerManager::BadArgumentError; return false; } return d->m_engine->removeItems(items, &h.errorMap, &h.error); } /*! Returns the default collection managed by this manager. There is always only one default collection for each backend. */ QOrganizerCollection QOrganizerManager::defaultCollection() { QOrganizerManagerSyncOpErrorHolder h(this); return d->m_engine->defaultCollection(&h.error); } /*! Returns the collection identified by the given \a collectionId which is managed by this manager. */ QOrganizerCollection QOrganizerManager::collection(const QOrganizerCollectionId& collectionId) { QOrganizerManagerSyncOpErrorHolder h(this); return d->m_engine->collection(collectionId, &h.error); } /*! Returns a list of all of the collections managed by this manager. */ QList QOrganizerManager::collections() { QOrganizerManagerSyncOpErrorHolder h(this); return d->m_engine->collections(&h.error); } /*! Saves the given \a collection to the backend, and returns true on success or false otherwise. Note that certain backends may not allow modification nor adding new collections, in such cases the operation will fail and result a QOrganizerManager::PermissionsError error. A new collection will be created in the backend store if the collection ID of it is null. Otherwise, an existing collection with the same ID will be updated. If the given collection ID does not exist in the backend, it will result a QOrganizerManager::DoesNotExistError error. Note that upon successful saving, the backend may update the collection, e.g. collection ID for newly saved collections. */ bool QOrganizerManager::saveCollection(QOrganizerCollection* collection) { QOrganizerManagerSyncOpErrorHolder h(this); if (collection) { return d->m_engine->saveCollection(collection, &h.error); } else { h.error = QOrganizerManager::BadArgumentError; return false; } } /*! Removes the collection identified by the given \a collectionId (and all items in the collection) from the manager. Returns true on success, false on failure. If the given \a collectionId does not exist, the operation will fail and QOrganizerManager::DoesNotExistError will be returned when calling error(). If the given \a collectionId refers to the default collection, the operation will fail and QOrganizerManager::PermissionsError will be returned when calling error(). */ bool QOrganizerManager::removeCollection(const QOrganizerCollectionId &collectionId) { QOrganizerManagerSyncOpErrorHolder h(this); return d->m_engine->removeCollection(collectionId, &h.error); } /*! Returns the list of supported filters by this manager. */ QList QOrganizerManager::supportedFilters() const { return d->m_engine->supportedFilters(); } /*! Returns the list of details that are supported by this manager for the given \a itemType. */ QList QOrganizerManager::supportedItemDetails(QOrganizerItemType::ItemType itemType) const { return d->m_engine->supportedItemDetails(itemType); } /*! Returns the list of organizer item types supported by this manager. */ QList QOrganizerManager::supportedItemTypes() const { return d->m_engine->supportedItemTypes(); } /*! Returns the manager name for the backend. */ QString QOrganizerManager::managerName() const { return d->m_engine->managerName(); } /*! Return the parameters used by the backend. Note that if certain paramters are invalid, or discarded by the backend, they will not be returned. Also, the backend might add certain parameters when being constructed. */ QMap QOrganizerManager::managerParameters() const { return d->m_engine->managerParameters(); } /*! Returns the version of the backend. The version is stored as part of the parameters, with the key QTORGANIZER_BACKEND_VERSION. */ int QOrganizerManager::managerVersion() const { return managerParameters().value(QTORGANIZER_BACKEND_VERSION).toInt(); } /*! Return the URI describing the backend. */ QString QOrganizerManager::managerUri() const { return d->m_engine->managerUri(); } /*! Return a list of QOrganizerItemId extracted from the \a items. */ QList QOrganizerManager::extractIds(const QList &items) { QList ids; ids.reserve(items.count()); foreach (const QOrganizerItem &item, items) ids.append(item.id()); return ids; } #include "moc_qorganizermanager.cpp" QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizermanager.h000066400000000000000000000203601233466112000175310ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERMANAGER_H #define QORGANIZERMANAGER_H #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManagerData; class Q_ORGANIZER_EXPORT QOrganizerManager : public QObject { Q_OBJECT public: #if Q_QDOC // qdoc's parser fails to recognise the default map argument explicit QOrganizerManager(const QString &managerName, const QMap ¶meters = 0, QObject *parent = 0); #else explicit QOrganizerManager(const QString &managerName, const QMap ¶meters = (QMap()), QObject *parent = 0); #endif explicit QOrganizerManager(QObject *parent = 0); ~QOrganizerManager(); static QOrganizerManager *fromUri(const QString &uri, QObject *parent = 0); static QStringList availableManagers(); QString managerName() const; QMap managerParameters() const; int managerVersion() const; QString managerUri() const; static bool parseUri(const QString &uri, QString *managerName, QMap *params); static QString buildUri(const QString &managerName, const QMap ¶ms); // error reporting enum Error { NoError = 0, DoesNotExistError, AlreadyExistsError, InvalidDetailError, LockedError, DetailAccessError, PermissionsError, OutOfMemoryError, NotSupportedError, BadArgumentError, UnspecifiedError, LimitReachedError, InvalidItemTypeError, InvalidCollectionError, InvalidOccurrenceError, TimeoutError }; enum Operation { Add, Change, Remove }; /* Error reporting */ QOrganizerManager::Error error() const; QMap errorMap() const; // items QOrganizerItem item(const QOrganizerItemId &itemId, const QOrganizerItemFetchHint &fetchHint = QOrganizerItemFetchHint()); QList items(const QList &itemIds, const QOrganizerItemFetchHint &fetchHint = QOrganizerItemFetchHint()); QList items(const QDateTime &startDateTime = QDateTime(), const QDateTime &endDateTime = QDateTime(), const QOrganizerItemFilter &filter = QOrganizerItemFilter(), int maxCount = -1, const QList &sortOrders = QList(), const QOrganizerItemFetchHint &fetchHint = QOrganizerItemFetchHint()); QList itemIds(const QDateTime &startDateTime = QDateTime(), const QDateTime &endDateTime = QDateTime(), const QOrganizerItemFilter &filter = QOrganizerItemFilter(), const QList &sortOrders = QList()); QList itemOccurrences(const QOrganizerItem &parentItem, const QDateTime &startDateTime = QDateTime(), const QDateTime &endDateTime = QDateTime(), int maxCount = -1, const QOrganizerItemFetchHint &fetchHint = QOrganizerItemFetchHint()); QList itemsForExport(const QDateTime &startDateTime = QDateTime(), const QDateTime &endDateTime = QDateTime(), const QOrganizerItemFilter &filter = QOrganizerItemFilter(), const QList &sortOrders = QList(), const QOrganizerItemFetchHint &fetchHint = QOrganizerItemFetchHint()); bool saveItem(QOrganizerItem *item, const QList &detailMask = QList()); bool saveItems(QList *items, const QList &detailMask = QList()); bool removeItem(const QOrganizerItemId &itemId); bool removeItem(const QOrganizerItem *item); bool removeItems(const QList& itemIds); bool removeItems(const QList *items); // collections QOrganizerCollection defaultCollection(); QOrganizerCollection collection(const QOrganizerCollectionId& collectionId); QList collections(); bool saveCollection(QOrganizerCollection* collection); bool removeCollection(const QOrganizerCollectionId& collectionId); // functionality reporting QList supportedFilters() const; QList supportedItemDetails(QOrganizerItemType::ItemType itemType) const; QList supportedItemTypes() const; // helper static QList extractIds(const QList &items); Q_SIGNALS: void dataChanged(); void itemsAdded(const QList &itemIds); void itemsChanged(const QList &itemIds); void itemsRemoved(const QList &itemIds); void itemsModified(const QList > &itemIds); void collectionsAdded(const QList &collectionIds); void collectionsChanged(const QList &collectionIds); void collectionsRemoved(const QList &collectionIds); void collectionsModified(const QList > &collectionIds); private: friend class QOrganizerManagerData; Q_DISABLE_COPY(QOrganizerManager) QOrganizerManagerData *d; void createEngine(const QString &managerName, const QMap ¶meters); Q_PRIVATE_SLOT(d, void _q_itemsUpdated(const QList &ids)) Q_PRIVATE_SLOT(d, void _q_itemsDeleted(const QList &ids)) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERMANAGER_H src/organizer/qorganizermanager_p.cpp000066400000000000000000000227331233466112000204110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizermanager_p.h" #include #if !defined(QT_NO_DEBUG) #include #endif #include #include #include "qorganizeritemobserver.h" #include "qorganizermanagerenginefactory.h" QT_BEGIN_NAMESPACE_ORGANIZER QHash QOrganizerManagerData::m_engines; bool QOrganizerManagerData::m_discovered; bool QOrganizerManagerData::m_discoveredStatic; QStringList QOrganizerManagerData::m_pluginPaths; #ifndef QT_NO_LIBRARY Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, (QT_ORGANIZER_MANAGER_ENGINE_INTERFACE, QLatin1String("/organizer"))) #endif static void qOrganizerItemsCleanEngines() { QOrganizerManagerData::m_discovered = false; QList factories = QOrganizerManagerData::m_engines.values(); for (int i=0; i < factories.count(); i++) delete factories.at(i); QOrganizerManagerData::m_engines.clear(); } void QOrganizerManagerData::createEngine(const QString &managerName, const QMap ¶meters) { m_engine = 0; QString builtManagerName = managerName.isEmpty() ? QOrganizerManager::availableManagers().value(0) : managerName; bool found = false; bool loadedDynamic = false; /* First check static factories */ loadStaticFactories(); /* See if we got a fast hit */ QList factories = m_engines.values(builtManagerName); m_lastError = QOrganizerManager::NoError; while (!found) { foreach (QOrganizerManagerEngineFactory *f, factories) { m_engine = f->engine(parameters, &m_lastError); if (m_engine) { found = true; break; } } // Break if found or if this is the second time through if (loadedDynamic || found) break; // otherwise load dynamic factories and reloop loadFactories(); factories = m_engines.values(builtManagerName); loadedDynamic = true; } if (!m_engine) { if (m_lastError == QOrganizerManager::NoError) m_lastError = QOrganizerManager::DoesNotExistError; m_engine = new QOrganizerManagerEngine(); } } void QOrganizerManagerData::loadStaticFactories() { if (!m_discoveredStatic) { #if !defined QT_NO_DEBUG const bool showDebug = qgetenv("QT_DEBUG_PLUGINS").toInt() > 0; #endif m_discoveredStatic = true; /* Clean stuff up at the end */ qAddPostRoutine(qOrganizerItemsCleanEngines); /* Loop over all the static plugins */ QObjectList staticPlugins = QPluginLoader::staticInstances(); for (int i = 0; i < staticPlugins.count(); i++ ){ QOrganizerManagerEngineFactory *f = qobject_cast(staticPlugins.at(i)); if (f) { QString name = f->managerName(); #if !defined QT_NO_DEBUG if (showDebug) qDebug() << "Static: found an engine plugin" << f << "with name" << name; #endif if (name != QStringLiteral("invalid") && !name.isEmpty()) { // we also need to ensure that we haven't already loaded this factory. if (m_engines.keys().contains(name)) qWarning("Static organizeritems plugin %s has the same name as a currently loaded plugin; ignored", qPrintable(name)); else m_engines.insertMulti(name, f); } else { qWarning("Static organizeritems plugin with reserved name %s ignored", qPrintable(name)); } } } } } /* Plugin loader */ void QOrganizerManagerData::loadFactories() { #if !defined QT_NO_DEBUG const bool showDebug = qgetenv("QT_DEBUG_PLUGINS").toInt() > 0; #endif // Always do this.. loadStaticFactories(); QFactoryLoader *l = loader(); const QStringList keys = l->keyMap().values(); if (!m_discovered || keys != m_pluginPaths) { m_discovered = true; m_pluginPaths = keys; for (int i = 0; i < keys.size(); ++i) { QOrganizerManagerEngineFactory *f = qobject_cast(l->instance(i)); if (f) { const QString name = f->managerName(); #if !defined QT_NO_DEBUG if (showDebug) qDebug() << "Dynamic: found a organizer engine plugin" << f << "with name" << name; #endif if (name != QStringLiteral("invalid") && !name.isEmpty()) { // we also need to ensure that we haven't already loaded this factory. if (m_engines.keys().contains(name)) qWarning("Organizer plugin %s has the same name as currently loaded plugin %s; ignored", qPrintable(m_pluginPaths.at(i)), qPrintable(name)); else m_engines.insertMulti(name, f); } else { qWarning("Organizer plugin %s with reserved name %s ignored", qPrintable(m_pluginPaths.at(i)), qPrintable(name)); } } #if !defined QT_NO_DEBUG if (showDebug && !f) { qDebug() << "Unknown plugin!"; if (const QObject *instance = l->instance(i)) qDebug() << "[qobject:" << instance << "]"; } #endif } } } QOrganizerItemEngineId *QOrganizerManagerData::createEngineItemId(const QString &managerName, const QMap ¶meters, const QString &engineIdString) { // caller takes ownership of the ID loadFactories(); QOrganizerManagerEngineFactory *engineFactory = m_engines.value(managerName); return engineFactory ? engineFactory->createItemEngineId(parameters, engineIdString) : NULL; } QOrganizerCollectionEngineId *QOrganizerManagerData::createEngineCollectionId(const QString &managerName, const QMap ¶meters, const QString &engineIdString) { // caller takes ownership of the ID loadFactories(); QOrganizerManagerEngineFactory *engineFactory = m_engines.value(managerName); return engineFactory ? engineFactory->createCollectionEngineId(parameters, engineIdString) : NULL; } void QOrganizerManagerData::registerObserver(QOrganizerItemObserver *observer) { m_observerForItem.insert(observer->itemId(), observer); } void QOrganizerManagerData::unregisterObserver(QOrganizerItemObserver *observer) { QOrganizerItemId key = m_observerForItem.key(observer); if (!key.isNull()) m_observerForItem.remove(key, observer); } void QOrganizerManagerData::_q_itemsUpdated(const QList &ids) { foreach (QOrganizerItemId id, ids) { QList observers = m_observerForItem.values(id); foreach (QOrganizerItemObserver *observer, observers) QMetaObject::invokeMethod(observer, "itemChanged"); } } void QOrganizerManagerData::_q_itemsDeleted(const QList &ids) { foreach (QOrganizerItemId id, ids) { QList observers = m_observerForItem.values(id); foreach (QOrganizerItemObserver *observer, observers) QMetaObject::invokeMethod(observer, "itemRemoved"); } } QOrganizerManagerData *QOrganizerManagerData::get(const QOrganizerManager *manager) { return manager->d; } QOrganizerManagerEngine *QOrganizerManagerData::engine(const QOrganizerManager *manager) { if (manager) return manager->d->m_engine; return 0; } QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizermanager_p.h000066400000000000000000000123221233466112000200470ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTMANAGER_P_H #define QCONTACTMANAGER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerCollectionEngineId; class QOrganizerItemObserver; class QOrganizerManagerEngineFactory; class QOrganizerItemEngineId; class QOrganizerManagerData { public: QOrganizerManagerData() : m_engine(0), m_lastError(QOrganizerManager::NoError) { } ~QOrganizerManagerData() { delete m_engine; } void createEngine(const QString &managerName, const QMap ¶meters); static QOrganizerManagerData *get(const QOrganizerManager *manager); static QOrganizerManagerEngine *engine(const QOrganizerManager *manager); static QOrganizerItemEngineId *createEngineItemId(const QString &managerName, const QMap ¶meters, const QString &engineIdString); static QOrganizerCollectionEngineId *createEngineCollectionId(const QString &managerName, const QMap ¶meters, const QString &engineIdString); QOrganizerManagerEngine *m_engine; QOrganizerManager::Error m_lastError; QMap m_lastErrorMap; // manager plugins static QHash m_engines; static bool m_discovered; static bool m_discoveredStatic; static QStringList m_pluginPaths; static void loadFactories(); static void loadStaticFactories(); // observer stuff void registerObserver(QOrganizerItemObserver *observer); void unregisterObserver(QOrganizerItemObserver *observer); void _q_itemsUpdated(const QList &ids); void _q_itemsDeleted(const QList &ids); QMultiHash m_observerForItem; // helpers static QOrganizerManagerData *managerData(const QOrganizerManager *m) { return m->d; } }; /*! \internal Helper to hold the error state of a synchronous operation - when destructed, updates the manager's last error variables to the result of this operation. This means that during callbacks the error state can't be modified behind the engines back. and it's more conceptually correct. */ class QOrganizerManagerSyncOpErrorHolder { public: QOrganizerManagerSyncOpErrorHolder(const QOrganizerManager *m, QMap *pUserError = 0) : error(QOrganizerManager::NoError), data(QOrganizerManagerData::managerData(m)), userError(pUserError) { } ~QOrganizerManagerSyncOpErrorHolder() { data->m_lastError = error; data->m_lastErrorMap = errorMap; if (userError) *userError = errorMap; } QOrganizerManager::Error error; QOrganizerManagerData *data; QMap errorMap; QMap *userError; }; QT_END_NAMESPACE_ORGANIZER #endif // QCONTACTMANAGER_P_H src/organizer/qorganizermanagerengine.cpp000066400000000000000000002657021233466112000212650ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizermanagerengine.h" #include "qorganizeritems.h" #include "qorganizeritemdetails.h" #include "qorganizeritemfilters.h" #include "qorganizeritemrequests.h" #include "qorganizeritemrequests_p.h" #include QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerManagerEngine \brief The QOrganizerManagerEngine class provides the interface to implement functionalities of organizer managers. \inmodule QtOrganizer \ingroup organizer-backends This class should only be used by backend developers. Instances of this class are provided to QOrganizerManager by a QOrganizerManagerEngineFactory instance, which is loaded from a plugin. The default implementation of this interface provides a backend doing nothing, so that backend developers only need to reimplement the functionalities needed. More information on writing a organizer engine plugin is available in the \l{Qt Organizer Manager Engines} documentation. \sa QOrganizerManager, QOrganizerManagerEngineFactory */ /*! \fn QOrganizerManagerEngine::dataChanged() This signal should be emitted if the internal state of the plugin changes, and it is unable to determine the changes which occurred, or if it considers the changes to be radical enough to require clients to reload all data. If this signal is emitted, no other signals will be emitted for the associated changes. \sa itemsAdded(), itemsChanged(), itemsRemoved() */ /*! \fn QOrganizerManagerEngine::itemsAdded(const QList &itemIds); This signal should be emitted at some point once the items identified by \a itemIds have been added to the backend. This signal should not be emitted if the dataChanged() signal was previously emitted for these changes. \sa dataChanged() */ /*! \fn QOrganizerManagerEngine::itemsChanged(const QList &itemIds); This signal should be emitted at some point once the items identified by \a itemIds have been modified in the backend. This signal should not be emitted if the dataChanged() signal was previously emitted for these changes. \sa dataChanged() */ /*! \fn QOrganizerManagerEngine::itemsRemoved(const QList &itemIds); This signal should be emitted at some point once the items identified by \a itemIds have been removed from the backend. This signal should not be emitted if the dataChanged() signal was previously emitted for these changes. \sa dataChanged() */ /*! \fn QOrganizerManagerEngine::itemsModified(const QList > &itemIds) This signal should be emitted at some point once the items identified by \a itemIds have been modified in the backend. This signal should not be emitted if the dataChanged() signal was previously emitted for these changes. \sa dataChanged() */ /*! \fn QOrganizerManagerEngine::collectionsAdded(const QList &collectionIds) This signal should be emitted at some point once the collections identified by \a collectionIds have been added to the backend. This signal should not be emitted if the dataChanged() signal was previously emitted for these changes. \sa dataChanged() */ /*! \fn QOrganizerManagerEngine::collectionsChanged(const QList &collectionIds) This signal should be emitted at some point once the collections identified by \a collectionIds have been changed in the backend. This signal should not be emitted if items in the collections have been added, modified, or removed. This signal should not be emitted if the dataChanged() signal was previously emitted for these changes. \sa dataChanged() */ /*! \fn QOrganizerManagerEngine::collectionsRemoved(const QList &collectionIds) This signal should be emitted at some point once the collections identified by \a collectionIds have been removed from the backend. This signal should not be emitted if the dataChanged() signal was previously emitted for these changes. \sa dataChanged() */ /*! \fn QOrganizerManagerEngine::collectionsModified(const QList > &collectionIds) This signal should be emitted at some point once the collections identified by \a collectionIds have been modified in the backend. This signal should not be emitted if the dataChanged() signal was previously emitted for these changes. \sa dataChanged() */ /*! Constructs an empty QOrganizerManagerEngine with the given \a parent. */ QOrganizerManagerEngine::QOrganizerManagerEngine(QObject *parent) : QObject(parent) { } /*! This function should be reimplemented to return the name of this backend. The default implementation returns the name "invalid". */ QString QOrganizerManagerEngine::managerName() const { return QString(QStringLiteral("invalid")); } /*! This function should be reimplemented to return the parameters used in when constructing this backend. The default implementation returns an empty QMap. If certain paramters are invalid, or discarded by the backend, they should not be returned. */ QMap QOrganizerManagerEngine::managerParameters() const { return QMap(); } /*! Returns the unique URI of this manager, which is built from the manager name and the parameters used to construct it. */ QString QOrganizerManagerEngine::managerUri() const { return QOrganizerManager::buildUri(managerName(), managerParameters()); } /*! This function should be reimplemented to support synchronous calls to fetch occurrences of the given parent item. This function is supposed to return a list of a maximum of \a maxCount organizer item instances which are occurrences of the given \a parentItem recurring item, which occur between the given \a startDateTime and the given \a endDateTime date, inclusive. Any error which occurs should be saved in \a error. A default-constructed (invalid) \a startDateTime specifies an open start date time (matches anything which occurs up until the \a endDateTime), and a default-constructed (invalid) \a endDateTime specifies an open end date time (matches anything which occurs after the \a startDateTime). If both the \a startDateTime and \a endDateTime are invalid, this function will return the IDs of all items. It's up to the backend to decide how many occurrences are returned if the given \a maxCount is negative. It's up to the backend to decide if fetch hint is supported. If supported, only the details defined by \a fetchHint will be fetched. */ QList QOrganizerManagerEngine::itemOccurrences(const QOrganizerItem &parentItem, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error) { Q_UNUSED(parentItem); Q_UNUSED(startDateTime); Q_UNUSED(endDateTime); Q_UNUSED(maxCount); Q_UNUSED(fetchHint); *error = QOrganizerManager::NotSupportedError; return QList(); } /*! This function should be reimplemented to support synchronous calls to fetch organizer item IDs. This function is supposed to return a list of item IDs of persisted organizer items that match the given \a filter, sorted according to the given list of \a sortOrders, for any item which occurs (or has an occurrence which occurs) in the range specified by the given \a startDateTime and \a endDateTime, inclusive. Any error which occurs should be saved in \a error. A default-constructed (invalid) \a startDateTime specifies an open start date time (matches anything which occurs up until the \a endDateTime), and a default-constructed (invalid) \a endDateTime specifies an open end date time (matches anything which occurs after the \a startDateTime). If both the \a startDateTime and \a endDateTime are invalid, this function will return the IDs of all items which match the \a filter criteria. It's up to the backend to decide how filters are supported. */ QList QOrganizerManagerEngine::itemIds(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, const QList &sortOrders, QOrganizerManager::Error *error) { Q_UNUSED(startDateTime) Q_UNUSED(endDateTime) Q_UNUSED(filter) Q_UNUSED(sortOrders) *error = QOrganizerManager::NotSupportedError; return QList(); } /*! This function should be reimplemented to support synchronous calls to fetch organizer items. This function is supposed to return a list of a maximum of \a maxCount organizer items and occurrences that match the given \a filter, which occur in the range specified by the given \a startDateTime and \a endDateTime, inclusive, and sorted according to the given list of \a sortOrders. Any operation error which occurs should be saved in \a error. A default-constructed (invalid) \a startDateTime specifies an open start date time (matches anything which occurs up until the \a endDateTime), and a default-constructed (invalid) \a endDateTime specifies an open end date time (matches anything which occurs after the \a startDateTime). If both the \a startDateTime and \a endDateTime are invalid, this function will return the IDs of all items which match the \a filter criteria. If no sort order is provided, the list is returned sorted by date. It's up to the backend to decide how many items should be returned if \a maxCount is negative. It's up to the backend to decide if filter and fetch hint are supported. */ QList QOrganizerManagerEngine::items(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error) { Q_UNUSED(filter) Q_UNUSED(startDateTime) Q_UNUSED(endDateTime) Q_UNUSED(maxCount) Q_UNUSED(sortOrders) Q_UNUSED(fetchHint) *error = QOrganizerManager::NotSupportedError; return QList(); } /*! This function should be reimplemented to support synchronous calls to fetch organizer items for export. This function is supposed to return a list of organizer items that match the given \a filter, sorted according to the given list of \a sortOrders, for any item which occurs (or has an occurrence which occurs) in the range specified by the given \a startDateTime and \a endDateTime, inclusive. Any operation error which occurs should be saved in \a error. Note that event occurrences and TODO occurrences should only be returned when they represent an exceptional occurrence (i.e. the client has specifically saved the occurrence in the backend). A default-constructed (invalid) \a startDateTime specifies an open start date time (matches anything which occurs up until the \a endDateTime), and a default-constructed (invalid) \a endDateTime specifies an open end date time (matches anything which occurs after the \a startDateTime). If both the \a startDateTime and \a endDateTime are invalid, this function will return the IDs of all items which match the \a filter criteria. It's up to the backend to decide if filter and fetch hint are supported. If the fetch hint is supported, only the details defined by \a fetchHint will be fetched. */ QList QOrganizerManagerEngine::itemsForExport(const QDateTime &startDateTime, const QDateTime &endDateTime, const QOrganizerItemFilter &filter, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error) { Q_UNUSED(startDateTime) Q_UNUSED(endDateTime) Q_UNUSED(filter) Q_UNUSED(sortOrders) Q_UNUSED(fetchHint) *error = QOrganizerManager::NotSupportedError; return QList(); } /*! This function should be reimplemented to support synchronous calls to fetch organizer items by their IDs \a itemIds. The items fetched by the backend should have a one-to-one correspondence to the IDs passed into this class. That is, the nth item in the returned list should have an ID which is equal to the nth ID in the list of IDs. Any invalid ID should correspond to an empty QOrganizerItem. It's up to the backend to decide if fetch hint is supported. If supported, only the details defined by \a fetchHint will be fetched. Any operation error which occurs should be saved in \a error. And the per-input errors should be stored in \a errorMap. */ QList QOrganizerManagerEngine::items(const QList &itemIds, const QOrganizerItemFetchHint &fetchHint, QMap *errorMap, QOrganizerManager::Error *error) { Q_UNUSED(itemIds) Q_UNUSED(fetchHint) Q_UNUSED(errorMap) *error = QOrganizerManager::NotSupportedError; return QList(); } /*! This function should be reimplemented to return the list of filters supported by this backend. The default implementation returns an empty list. */ QList QOrganizerManagerEngine::supportedFilters() const { return QList(); } /*! This function should be reimplemented to return the list of details supported by this backend for the given \a itemType. The default implementation returns an empty list. */ QList QOrganizerManagerEngine::supportedItemDetails(QOrganizerItemType::ItemType itemType) const { Q_UNUSED(itemType) return QList(); } /*! This function should be reimplemented to return the list of item types supported by this backend. The default implementation returns an empty list. */ QList QOrganizerManagerEngine::supportedItemTypes() const { return QList(); } /*! This function should be reimplemented to support synchronous calls to save organizer items. This function is supposed to save the given list of \a items to the backend, and returns true on success or false otherwise. A new organizer item will be created in the backend store if the item ID of it is null. Otherwise, an existing item with the same ID will be updated. If the given item ID does not exist in the backend, it will result a QOrganizerManager::DoesNotExistError error. If the collection ID of the item is null, it will be saved to the default collection. If the given collection ID doesn't exist, the saving will fail and \a error will be set to QOrganizerManager::InvalidCollectionError. If the \a detailMask is empty, only the details currently existing in the item will be saved. Otherwise, only details masked by the \a detailMask will be saved or updated, others are kept untouched. It's useful to avoid information loss if the items are retrieved with a fetch hint. Note that upon successful saving, the backend may update the item, e.g. item ID for newly saved items, GUID, timestamp, version, etc. Any error which occurs should be saved in \a error, and per-input errors for the operation should be stored in \a errorMap. */ bool QOrganizerManagerEngine::saveItems(QList *items, const QList &detailMask, QMap *errorMap, QOrganizerManager::Error *error) { Q_UNUSED(items) Q_UNUSED(detailMask) Q_UNUSED(errorMap) *error = QOrganizerManager::NotSupportedError; return false; } /*! This function should be reimplemented to support synchronous calls to remove organizer items. This function is supposed to remove all the items whose ID is contained in the given list of \a itemIds, and all the occurrences whose parent ID is containd in the \a itemIds. If the list contains ids which do not identify a valid item in the manager \a error will be set to \c QOrganizerManager::DoesNotExist. Returns true if all the items and occurrences are successfully removed, or false otherwise. Any error which occurs should be saved in \a error, and per-input errors for the operation should be stored in \a errorMap. */ bool QOrganizerManagerEngine::removeItems(const QList &itemIds, QMap *errorMap, QOrganizerManager::Error *error) { Q_UNUSED(itemIds) Q_UNUSED(errorMap) *error = QOrganizerManager::NotSupportedError; return false; } /*! This function should be reimplemented to support synchronous calls to remove organizer items. This function is supposed to remove all the items in the given list of \a items, and all the occurrences whose parent is containd in the \a items. If item in the list is a generated occurrence, an exception date is added to the parent item. If the list contains ids which do not identify a valid item in the manager \a error will be set to \c QOrganizerManager::DoesNotExist. Returns true if all the items and occurrences are successfully removed, or false otherwise. Any error which occurs should be saved in \a error, and per-input errors for the operation should be stored in \a errorMap. */ bool QOrganizerManagerEngine::removeItems(const QList *items, QMap *errorMap, QOrganizerManager::Error *error) { Q_UNUSED(items) Q_UNUSED(errorMap) *error = QOrganizerManager::NotSupportedError; return false; } /*! This function should be reimplemented to support synchronous calls to fetch the default collection. Any errors encountered during this operation should be stored to \a error. */ QOrganizerCollection QOrganizerManagerEngine::defaultCollection(QOrganizerManager::Error* error) { *error = QOrganizerManager::NotSupportedError; return QOrganizerCollection(); } /*! This function should be reimplemented to support synchronous calls to fetch a collection based on its ID. Any errors encountered during this operation should be stored to \a error. If the given \a collectionId does not specify a valid collection, \a error will be set to \c QOrganizerManager::DoesNotExistError. */ QOrganizerCollection QOrganizerManagerEngine::collection(const QOrganizerCollectionId& collectionId, QOrganizerManager::Error* error) { Q_UNUSED(collectionId); *error = QOrganizerManager::NotSupportedError; return QOrganizerCollection(); } /*! This function should be reimplemented to support synchronous calls to fetch all the collections managed by this backend. Any errors encountered during this operation should be stored to \a error. */ QList QOrganizerManagerEngine::collections(QOrganizerManager::Error* error) { *error = QOrganizerManager::NotSupportedError; return QList(); } /*! This function should be reimplemented to support synchronous calls to save a collection. This function is supposed to save the given \a collection to the backend, and returns true on success or false otherwise. Any errors encountered during this operation should be stored to \a error. A new collection will be created in the backend store if the collection ID of it is null. Otherwise, an existing collection with the same ID will be updated. If the given collection ID does not exist in the backend, it will result a QOrganizerManager::DoesNotExistError error. Note that upon successful saving, the backend may update the collection, e.g. collection ID for newly saved collections. */ bool QOrganizerManagerEngine::saveCollection(QOrganizerCollection* collection, QOrganizerManager::Error* error) { Q_UNUSED(collection); *error = QOrganizerManager::NotSupportedError; return false; } /*! This function should be reimplemented to support synchronous calls to remove a collection. This function is supposed to remove the collection identified by the given \a collectionId, and all items in the collection. Returns true on success, or false otherwise. Any errors encountered during this operation should be stored to \a error. Note that removing the default collection should not be allowed and should result a QOrganizerManager::PermissionsError error. */ bool QOrganizerManagerEngine::removeCollection(const QOrganizerCollectionId& collectionId, QOrganizerManager::Error* error) { Q_UNUSED(collectionId); *error = QOrganizerManager::NotSupportedError; return false; } /*! Given an input \a filter, returns the canonical version of the filter. Some of the following transformations may be applied: \list \li Any QOrganizerItemInvalidFilters contained in a union filter will be removed \li Any default QOrganizerItemFilters contained in an intersection filter will be removed \li Any QOrganizerItemIntersectionFilters with a QOrganizerItemInvalidFilter contained will be replaced with a QOrganizerItemInvalidFilter \li Any QOrganizerItemUnionFilters with a default QOrganizerItemFilter contained will be replaced with a default QOrganizerItemFilter \li An empty QOrganizerItemIntersectionFilter will be replaced with a QOrganizerItemDefaultFilter \li An empty QOrganizerItemUnionFilter will be replaced with a QOrganizerItemInvalidFilter \li An empty QOrganizerItemIdFilter will be replaced with a QOrganizerItemInvalidFilter \li An intersection or union filter with a single entry will be replaced by that entry \li A QOrganizerItemDetailFieldFilter or QOrganizerItemDetailRangeFilter with no definition name will be replaced with a QOrganizerItemInvalidFilter \li A QOrganizerItemDetailRangeFilter with no range specified will be converted to a QOrganizerItemDetailFieldFilter \endlist */ QOrganizerItemFilter QOrganizerManagerEngine::canonicalizedFilter(const QOrganizerItemFilter &filter) { switch (filter.type()) { case QOrganizerItemFilter::IntersectionFilter: { QOrganizerItemIntersectionFilter f(filter); QList filters = f.filters(); QList::iterator it = filters.begin(); // XXX in theory we can remove duplicates in a set filter while (it != filters.end()) { QOrganizerItemFilter canon = canonicalizedFilter(*it); if (canon.type() == QOrganizerItemFilter::DefaultFilter) { it = filters.erase(it); } else if (canon.type() == QOrganizerItemFilter::InvalidFilter) { return QOrganizerItemInvalidFilter(); } else { *it = canon; ++it; } } if (filters.count() == 0) return QOrganizerItemFilter(); if (filters.count() == 1) return filters.first(); f.setFilters(filters); return f; } // unreachable case QOrganizerItemFilter::UnionFilter: { QOrganizerItemUnionFilter f(filter); QList filters = f.filters(); QList::iterator it = filters.begin(); // XXX in theory we can remove duplicates in a set filter while (it != filters.end()) { QOrganizerItemFilter canon = canonicalizedFilter(*it); if (canon.type() == QOrganizerItemFilter::InvalidFilter) { it = filters.erase(it); } else if (canon.type() == QOrganizerItemFilter::DefaultFilter) { return QOrganizerItemFilter(); } else { *it = canon; ++it; } } if (filters.count() == 0) return QOrganizerItemInvalidFilter(); if (filters.count() == 1) return filters.first(); f.setFilters(filters); return f; } // unreachable case QOrganizerItemFilter::IdFilter: { QOrganizerItemIdFilter f(filter); if (f.ids().count() == 0) return QOrganizerItemInvalidFilter(); } break; // fall through to return at end case QOrganizerItemFilter::DetailRangeFilter: { QOrganizerItemDetailRangeFilter f(filter); if (f.detailType() == QOrganizerItemDetail::TypeUndefined) return QOrganizerItemInvalidFilter(); if (f.minValue() == f.maxValue() && f.rangeFlags() == (QOrganizerItemDetailRangeFilter::ExcludeLower | QOrganizerItemDetailRangeFilter::ExcludeUpper)) return QOrganizerItemInvalidFilter(); if ((f.minValue().isNull() && f.maxValue().isNull()) || (f.minValue() == f.maxValue())) { QOrganizerItemDetailFieldFilter df; df.setDetail(f.detailType(), f.detailField()); df.setMatchFlags(f.matchFlags()); df.setValue(f.minValue()); return df; } } break; // fall through to return at end case QOrganizerItemFilter::DetailFieldFilter: { QOrganizerItemDetailFieldFilter f(filter); if (f.detailType() == QOrganizerItemDetail::TypeUndefined) return QOrganizerItemInvalidFilter(); } break; // fall through to return at end default: break; // fall through to return at end } return filter; } /*! Compares \a first against \a second. If the types are strings (QVariant::String), the \a sensitivity argument controls case sensitivity when comparing. Returns: <0 if \a first is less than \a second 0 if \a first is equal to \a second >0 if \a first is greater than \a second. The results are undefined if the variants are different types, or cannot be compared. */ int QOrganizerManagerEngine::compareVariant(const QVariant& first, const QVariant& second, Qt::CaseSensitivity sensitivity) { switch(first.type()) { case QVariant::Int: return first.toInt() - second.toInt(); case QVariant::LongLong: return first.toLongLong() - second.toLongLong(); case QVariant::Bool: case QVariant::Char: case QVariant::UInt: return first.toUInt() - second.toUInt(); case QVariant::ULongLong: return first.toULongLong() - second.toULongLong(); case QVariant::String: return first.toString().compare(second.toString(), sensitivity); case QVariant::Double: { const double a = first.toDouble(); const double b = second.toDouble(); return (a < b) ? -1 : ((a == b) ? 0 : 1); } case QVariant::DateTime: { const QDateTime a = first.toDateTime(); const QDateTime b = second.toDateTime(); return (a < b) ? -1 : ((a == b) ? 0 : 1); } case QVariant::Date: return first.toDate().toJulianDay() - second.toDate().toJulianDay(); case QVariant::Time: { const QTime a = first.toTime(); const QTime b = second.toTime(); return (a < b) ? -1 : ((a == b) ? 0 : 1); } default: return 0; } } /*! Returns true if the supplied item \a item matches the supplied filter \a filter. This function will test each condition in the filter, possibly recursing. */ bool QOrganizerManagerEngine::testFilter(const QOrganizerItemFilter &filter, const QOrganizerItem &item) { switch(filter.type()) { case QOrganizerItemFilter::InvalidFilter: return false; case QOrganizerItemFilter::DefaultFilter: return true; case QOrganizerItemFilter::IdFilter: { const QOrganizerItemIdFilter idf(filter); if (idf.ids().contains(item.id())) return true; } // Fall through to end break; case QOrganizerItemFilter::DetailFilter: { const QOrganizerItemDetailFilter cdf(filter); QOrganizerItemDetail matchingDetail = cdf.detail(); if ( (matchingDetail.isEmpty()) || (matchingDetail.type() == QOrganizerItemDetail::TypeUndefined) ) return false; /* See if this organizer item has one of these details in it */ const QList& details = item.details(cdf.detail().type()); if (details.count() == 0) return false; /* can't match */ /* Value equality test */ for (int j=0; j < details.count(); j++) { if (details.at(j) == matchingDetail) return true; } return false; } // Fall through to end break; case QOrganizerItemFilter::DetailFieldFilter: { const QOrganizerItemDetailFieldFilter cdf(filter); if (cdf.detailType() == QOrganizerItemDetail::TypeUndefined) return false; /* See if this organizer item has one of these details in it */ const QList& details = item.details(cdf.detailType()); if (details.count() == 0) return false; /* can't match */ /* See if we need to check the values */ if (cdf.detailField() == -1) return true; /* just testing for the presence of a detail of the specified definition */ /* Now figure out what tests we are doing */ const bool valueTest = cdf.value().isValid(); const bool presenceTest = !valueTest; /* See if we need to test any values at all */ if (presenceTest) { for(int j=0; j < details.count(); j++) { const QOrganizerItemDetail& detail = details.at(j); /* Check that the field is present and has a non-empty value */ if (detail.values().contains(cdf.detailField()) && !detail.value(cdf.detailField()).isNull()) return true; } return false; } /* Case sensitivity, for those parts that use it */ Qt::CaseSensitivity cs = (cdf.matchFlags() & QOrganizerItemFilter::MatchCaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive; /* See what flags are requested, since we're looking at a value */ if (cdf.matchFlags() & (QOrganizerItemFilter::MatchEndsWith | QOrganizerItemFilter::MatchStartsWith | QOrganizerItemFilter::MatchContains | QOrganizerItemFilter::MatchFixedString)) { /* We're strictly doing string comparisons here */ bool matchStarts = (cdf.matchFlags() & 7) == QOrganizerItemFilter::MatchStartsWith; bool matchEnds = (cdf.matchFlags() & 7) == QOrganizerItemFilter::MatchEndsWith; bool matchContains = (cdf.matchFlags() & 7) == QOrganizerItemFilter::MatchContains; /* Value equality test */ for(int j=0; j < details.count(); j++) { const QOrganizerItemDetail& detail = details.at(j); const QString& var = detail.value(cdf.detailField()).toString(); const QString& needle = cdf.value().toString(); if (matchStarts && var.startsWith(needle, cs)) return true; if (matchEnds && var.endsWith(needle, cs)) return true; if (matchContains && var.contains(needle, cs)) return true; if (QString::compare(var, needle, cs) == 0) return true; } return false; } else { /* Nope, testing the values as a variant */ /* Value equality test */ for(int j = 0; j < details.count(); j++) { const QOrganizerItemDetail& detail = details.at(j); const QVariant& var = detail.value(cdf.detailField()); if (!var.isNull() && compareVariant(var, cdf.value(), cs) == 0) return true; } } } break; case QOrganizerItemFilter::DetailRangeFilter: { const QOrganizerItemDetailRangeFilter cdf(filter); if (cdf.detailType() == QOrganizerItemDetail::TypeUndefined) return false; /* we do not know which field to check */ /* See if this organizer item has one of these details in it */ const QList& details = item.details(cdf.detailType()); if (details.count() == 0) return false; /* can't match */ /* Check for a detail presence test */ if (cdf.detailField() == -1) return true; /* See if this is a field presence test */ if (!cdf.minValue().isValid() && !cdf.maxValue().isValid()) { for(int j=0; j < details.count(); j++) { const QOrganizerItemDetail& detail = details.at(j); if (detail.values().contains(cdf.detailField())) return true; } return false; } /* open or closed interval testing support */ const int minComp = cdf.rangeFlags() & QOrganizerItemDetailRangeFilter::ExcludeLower ? 1 : 0; const int maxComp = cdf.rangeFlags() & QOrganizerItemDetailRangeFilter::IncludeUpper ? 1 : 0; const bool testMin = cdf.minValue().isValid(); const bool testMax = cdf.maxValue().isValid(); /* At this point we know that at least of testMin & testMax is true */ /* Case sensitivity, for those parts that use it */ Qt::CaseSensitivity cs = (cdf.matchFlags() & QOrganizerItemFilter::MatchCaseSensitive) ? Qt::CaseSensitive : Qt::CaseInsensitive; /* See what flags are requested, since we're looking at a value */ if (cdf.matchFlags() & (QOrganizerItemFilter::MatchEndsWith | QOrganizerItemFilter::MatchStartsWith | QOrganizerItemFilter::MatchContains | QOrganizerItemFilter::MatchFixedString)) { /* We're strictly doing string comparisons here */ //bool matchStarts = (cdf.matchFlags() & 7) == QOrganizerItemFilter::MatchStartsWith; bool matchEnds = (cdf.matchFlags() & 7) == QOrganizerItemFilter::MatchEndsWith; bool matchContains = (cdf.matchFlags() & 7) == QOrganizerItemFilter::MatchContains; /* Min/Max and contains do not make sense */ if (matchContains) return false; QString minVal = cdf.minValue().toString(); QString maxVal = cdf.maxValue().toString(); /* Starts with is the normal compare case, endsWith is a bit trickier */ for(int j=0; j < details.count(); j++) { const QOrganizerItemDetail& detail = details.at(j); const QString& var = detail.value(cdf.detailField()).toString(); if (!matchEnds) { // MatchStarts or MatchFixedString if (testMin && QString::compare(var, minVal, cs) < minComp) continue; if (testMax && QString::compare(var, maxVal, cs) >= maxComp) continue; return true; } else { /* Have to test the length of min & max */ // using refs means the parameter order is backwards, so negate the result of compare if (testMin && -QString::compare(minVal, var.rightRef(minVal.length()), cs) < minComp) continue; if (testMax && -QString::compare(maxVal, var.rightRef(maxVal.length()), cs) >= maxComp) continue; return true; } } // Fall through to end } else { /* Nope, testing the values as a variant */ for(int j=0; j < details.count(); j++) { const QOrganizerItemDetail& detail = details.at(j); const QVariant& var = detail.value(cdf.detailField()); if (testMin && compareVariant(var, cdf.minValue(), cs) < minComp) continue; if (testMax && compareVariant(var, cdf.maxValue(), cs) >= maxComp) continue; return true; } // Fall through to end } } break; case QOrganizerItemFilter::IntersectionFilter: { /* XXX In theory we could reorder the terms to put the native tests first */ const QOrganizerItemIntersectionFilter bf(filter); const QList& terms = bf.filters(); if (terms.count() > 0) { for(int j = 0; j < terms.count(); j++) { if (!testFilter(terms.at(j), item)) { return false; } } return true; } // Fall through to end } break; case QOrganizerItemFilter::UnionFilter: { /* XXX In theory we could reorder the terms to put the native tests first */ const QOrganizerItemUnionFilter bf(filter); const QList& terms = bf.filters(); if (terms.count() > 0) { for(int j = 0; j < terms.count(); j++) { if (testFilter(terms.at(j), item)) { return true; } } return false; } // Fall through to end } break; case QOrganizerItemFilter::CollectionFilter: { const QOrganizerItemCollectionFilter cf(filter); const QSet& ids = cf.collectionIds(); if (ids.contains(item.collectionId())) return true; return false; } } return false; } /*! Returns true if the given \a item (or an occurrence of the item) occurs within the range specified by the \a startPeriod and the \a endPeriod, inclusive. A default-constructed \a startPeriod signifies that the lower bound of the range is infinitely small (i.e., will match anything up to the \a endPeriod) and a default-constructed \a endPeriod signifies that the upper bound of the range is infinitely large (i.e., will match anything which occurs after the \a startPeriod). */ bool QOrganizerManagerEngine::isItemBetweenDates(const QOrganizerItem& item, const QDateTime& startPeriod, const QDateTime& endPeriod) { if (startPeriod.isNull() && endPeriod.isNull()) return true; QDateTime itemDateStart; QDateTime itemDateEnd; if (item.type() == QOrganizerItemType::TypeEvent || item.type() == QOrganizerItemType::TypeEventOccurrence) { QOrganizerEventTime etr = item.detail(QOrganizerItemDetail::TypeEventTime); itemDateStart = etr.startDateTime(); itemDateEnd = etr.endDateTime(); } else if (item.type() == QOrganizerItemType::TypeTodo || item.type() == QOrganizerItemType::TypeTodoOccurrence) { QOrganizerTodoTime ttr = item.detail(QOrganizerItemDetail::TypeTodoTime); itemDateStart = ttr.startDateTime(); itemDateEnd = ttr.dueDateTime(); } else if (item.type() == QOrganizerItemType::TypeJournal) { QOrganizerJournal journal = item; itemDateStart = itemDateEnd = journal.dateTime(); } else if (item.type() == QOrganizerItemType::TypeNote) { //for note, there is no such start/end datetime so we always return false return false; } // if period start date is not given, check that item is starting or ending before period end if (startPeriod.isNull()) // endPeriod must be non-null because of initial test return (!itemDateStart.isNull() && itemDateStart <= endPeriod) || (!itemDateEnd.isNull() && itemDateEnd <= endPeriod); // if period end date is not given, check that item is starting or ending after the period start if (endPeriod.isNull()) // startPeriod must be non-null because of initial test return (!itemDateEnd.isNull() && itemDateEnd >= startPeriod) || (!itemDateStart.isNull() && itemDateStart >= startPeriod); // Both startPeriod and endPeriod are not null // check if item start date is between the period start and end date if (!itemDateStart.isNull() && itemDateStart >= startPeriod && itemDateStart <= endPeriod) return true; // check if item end date is between the period start and end date if (!itemDateEnd.isNull() && itemDateEnd >= startPeriod && itemDateEnd <= endPeriod) return true; // check if item interval is including the period interval if (!itemDateStart.isNull() && !itemDateEnd.isNull() && itemDateStart <= startPeriod && itemDateEnd >= endPeriod) return true; return false; } /*! \internal Returns the date associated with \a item that can be used for the purpose of date-sorting the item. */ QDateTime getDateForSorting(const QOrganizerItem& item) { QDateTime retn; { QOrganizerEventTime detail = item.detail(QOrganizerItemDetail::TypeEventTime); if (!detail.isEmpty()) { retn = detail.startDateTime(); if (!retn.isValid()) retn = detail.endDateTime(); if (retn.isValid() && detail.isAllDay()) { // set it to a millisecond before the given date to have it sorted correctly retn.setTime(QTime(23, 59, 59, 999)); retn.addDays(-1); } return retn; } } { QOrganizerTodoTime detail = item.detail(QOrganizerItemDetail::TypeTodoTime); if (!detail.isEmpty()) { retn = detail.startDateTime(); if (!retn.isValid()) retn = detail.dueDateTime(); if (retn.isValid() && detail.isAllDay()) { // set it to a millisecond before the given date to have it sorted correctly retn.setTime(QTime(23, 59, 59, 999)); retn.addDays(-1); } return retn; } } // If it's a note, this will just return null, as expected return item.detail(QOrganizerItemDetail::TypeJournalTime).value(QOrganizerJournalTime::FieldEntryDateTime).toDateTime(); } /*! Returns true if and only if \a a is temporally less than \a b. Items with an earlier date are temporally less than items with a later date, or items with no date. All day items are temporally less than non-all day items on the same date. For events and todos, the start date is used, or if null, the end date is used. This function defines a total ordering suitable for use in a sort function. */ bool QOrganizerManagerEngine::itemLessThan(const QOrganizerItem& a, const QOrganizerItem& b) { QDateTime date1 = getDateForSorting(a); if (!date1.isValid()) { return false; } else { QDateTime date2 = getDateForSorting(b); if (!date2.isValid()) return true; else return date1 < date2; } } /*! Compares two organizer items (\a a and \a b) using the given list of \a sortOrders. Returns a negative number if \a a should appear before \a b according to the sort order, a positive number if \a a should appear after \a b according to the sort order, and zero if the two are unable to be sorted. */ int QOrganizerManagerEngine::compareItem(const QOrganizerItem& a, const QOrganizerItem& b, const QList& sortOrders) { foreach (const QOrganizerItemSortOrder &sortOrder, sortOrders) { if (!sortOrder.isValid()) break; const QOrganizerItemDetail::DetailType detailType = sortOrder.detailType(); const int detailField = sortOrder.detailField(); const QList aDetails = a.details(detailType); const QList bDetails = b.details(detailType); if (aDetails.isEmpty() && bDetails.isEmpty()) continue; // use next sort criteria. // See if we need to check the values if (detailField == -1) { // just testing for the presence of a detail of the specified definition if (aDetails.size() == bDetails.size()) continue; // use next sort criteria. if (aDetails.isEmpty()) return sortOrder.blankPolicy() == QOrganizerItemSortOrder::BlanksFirst ? -1 : 1; if (bDetails.isEmpty()) return sortOrder.blankPolicy() == QOrganizerItemSortOrder::BlanksFirst ? 1 : -1; return 0; } // obtain the values which this sort order concerns const QVariant aVal = !aDetails.isEmpty() ? aDetails.first().value(detailField) : QVariant(); const QVariant bVal = !bDetails.isEmpty() ? bDetails.first().value(detailField) : QVariant(); bool aIsNull = false; bool bIsNull = false; // treat empty strings as null qvariants. if ((aVal.type() == QVariant::String && aVal.toString().isEmpty()) || aVal.isNull()) { aIsNull = true; } if ((bVal.type() == QVariant::String && bVal.toString().isEmpty()) || bVal.isNull()) { bIsNull = true; } // early exit error checking if (aIsNull && bIsNull) continue; // use next sort criteria. if (aIsNull) return (sortOrder.blankPolicy() == QOrganizerItemSortOrder::BlanksFirst ? -1 : 1); if (bIsNull) return (sortOrder.blankPolicy() == QOrganizerItemSortOrder::BlanksFirst ? 1 : -1); // real comparison int comparison = compareVariant(aVal, bVal, sortOrder.caseSensitivity()) * (sortOrder.direction() == Qt::AscendingOrder ? 1 : -1); if (comparison == 0) continue; return comparison; } return 0; // or according to id? return (a.id() < b.id() ? -1 : 1); } /*! A functor that returns true iff \a a is less than \a b, according to \a sortOrders passed in to the ctor. */ class OrganizerItemLessThan { const QList &m_sortOrders; public: inline OrganizerItemLessThan(const QList &sortOrders) : m_sortOrders(sortOrders) {} inline bool operator()(const QOrganizerItem &a, const QOrganizerItem &b) const { return QOrganizerManagerEngine::compareItem(a, b, m_sortOrders) < 0; } }; /*! Insert \a toAdd to the \a sorted list, according to the provided \a sortOrders. The index where \a toAdd is inserted is returned. The first one in the \a sortOrders list has the highest priority. */ int QOrganizerManagerEngine::addSorted(QList *sorted, const QOrganizerItem &toAdd, const QList &sortOrders) { QList::iterator it; if (sortOrders.count() > 0) it = std::upper_bound(sorted->begin(), sorted->end(), toAdd, OrganizerItemLessThan(sortOrders)); else it = sorted->end(); // no sort order? just add it to the end it = sorted->insert(it, toAdd); return it - sorted->begin(); } /*! Insert \a toAdd to the \a defaultSorted map. If \a toAdd does not have valid start or end date, returns false and does not insert \a toAdd to \a defaultSorted map. This function provides default sorting, which should be used for sorting fetch results, if no sort order was defined for the fetch. The default sorting algorithm is to sort based on start time of an item. If start time does not exist, end time or due time is used instead. For allday events, time 00:00 is used for sorting purposes. Items with no start or end time are last in the sorting order. This function sorts items using QMultiMap, where QDateTime is used as a key. To get a sorted list of items, QMultiMap::values function should be called and items without start and end date added to the end of the list. */ bool QOrganizerManagerEngine::addDefaultSorted(QMultiMap *defaultSorted, const QOrganizerItem &toAdd) { QDateTime sortTime; if (toAdd.type() == QOrganizerItemType::TypeEvent || toAdd.type() == QOrganizerItemType::TypeEventOccurrence) { QOrganizerEventTime eventTime = toAdd.detail(QOrganizerItemDetail::TypeEventTime); // both start and end times are mandatory for an event in jsondb schema, so all this checking might redundant if (eventTime.startDateTime().isValid()) sortTime = eventTime.startDateTime(); else if (eventTime.endDateTime().isValid()) sortTime = eventTime.endDateTime(); if (eventTime.isAllDay() && sortTime.isValid()) sortTime.setTime(QTime(0, 0, 0)); } else if (toAdd.type() == QOrganizerItemType::TypeTodo || toAdd.type() == QOrganizerItemType::TypeTodoOccurrence) { QOrganizerTodoTime todoTime = toAdd.detail(QOrganizerItemDetail::TypeTodoTime); if (todoTime.startDateTime().isValid()) sortTime = todoTime.startDateTime(); else if (todoTime.dueDateTime().isValid()) sortTime = todoTime.dueDateTime(); if (todoTime.isAllDay() && sortTime.isValid()) sortTime.setTime(QTime(0, 0, 0)); } if (sortTime.isValid()) { // FIXME: sorting of events with exactly the same key defaultSorted->insert(sortTime, toAdd); return true; } else { return false; } } /*! Generates a new occurrence for \a parentItem. All \a parentItem details, except for \l QOrganizerItemType and \l QOrganizerItemRecurrence copied to the occurrence. Occurrence start date is set to the date given in \a rdate. End date is modified accordingly. Occurrence's \l QOrganizerItemParent detail contains the id of \a parentItem and the original date given in \a rdate. */ QOrganizerItem QOrganizerManagerEngine::generateOccurrence(const QOrganizerItem &parentItem, const QDateTime &rdate) { QOrganizerItem instanceItem; if (parentItem.type() == QOrganizerItemType::TypeEvent) { instanceItem = QOrganizerEventOccurrence(); } else { instanceItem = QOrganizerTodoOccurrence(); } instanceItem.setCollectionId(parentItem.collectionId()); // XXX TODO: something better than this linear search... // Grab all details from the parent item except the recurrence information, and event/todo time range QList allDetails = parentItem.details(); QList occDetails; foreach (const QOrganizerItemDetail &detail, allDetails) { if (detail.type() != QOrganizerItemDetail::TypeRecurrence && detail.type() != QOrganizerItemDetail::TypeEventTime && detail.type() != QOrganizerItemDetail::TypeTodoTime) { occDetails.append(detail); } } // add the detail which identifies exactly which instance this item is. QOrganizerItemParent parentDetail; parentDetail.setParentId(parentItem.id()); parentDetail.setOriginalDate(rdate.date()); occDetails.append(parentDetail); // save those details in the instance. foreach (const QOrganizerItemDetail &detail, occDetails) { // copy every detail except the type if (detail.type() != QOrganizerItemDetail::TypeItemType) { QOrganizerItemDetail modifiable = detail; instanceItem.saveDetail(&modifiable); } } // and update the time range in the instance based on the current instance date if (parentItem.type() == QOrganizerItemType::TypeEvent) { QOrganizerEventTime etr = parentItem.detail(QOrganizerItemDetail::TypeEventTime); if (!etr.isEmpty()) { int eventDayCount = 0; if (etr.startDateTime().isValid() && etr.endDateTime().isValid()) eventDayCount = etr.startDateTime().daysTo(etr.endDateTime()); QDateTime temp = etr.startDateTime(); temp.setDate(rdate.date()); etr.setStartDateTime(temp); temp = etr.endDateTime(); QDate endDate = rdate.addDays(eventDayCount).date(); temp.setDate(endDate); etr.setEndDateTime(temp); instanceItem.saveDetail(&etr); } } // for todo's if (parentItem.type() == QOrganizerItemType::TypeTodo) { QOrganizerTodoTime ttr = parentItem.detail(QOrganizerItemDetail::TypeTodoTime); if (!ttr.isEmpty()) { int todoDayCount = 0; if (ttr.startDateTime().isValid() && ttr.dueDateTime().isValid()) todoDayCount = ttr.startDateTime().daysTo(ttr.dueDateTime()); QDateTime temp = ttr.startDateTime(); temp.setDate(rdate.date()); ttr.setStartDateTime(temp); temp = ttr.dueDateTime(); QDate endDate = rdate.addDays(todoDayCount).date(); temp.setDate(endDate); ttr.setDueDateTime(temp); instanceItem.saveDetail(&ttr); } } return instanceItem; } /*! Generates all start times for recurrence \a rrule during the given time period. The time period is defined by \a periodStart and \a periodEnd. \a initialDateTime is the start time of the event, which defines the first start time for \a rrule. \a maxCount can be used to limit the amount of generated start times. */ QList QOrganizerManagerEngine::generateDateTimes(const QDateTime &initialDateTime, QOrganizerRecurrenceRule rrule, const QDateTime &periodStart, const QDateTime &periodEnd, int maxCount) { QList retn; if (periodEnd.isValid() || maxCount <= 0) maxCount = INT_MAX; // count of returned items is unlimited QDateTime realPeriodEnd(periodEnd); if (rrule.limitType() == QOrganizerRecurrenceRule::DateLimit && rrule.limitDate() < realPeriodEnd.date()) { realPeriodEnd.setDate(rrule.limitDate()); realPeriodEnd.setTime(QTime(23,59,59,999)); // the last instant of the limit date, since it's prior to the periodEnd. } QDate nextDate; if (rrule.limitType() == QOrganizerRecurrenceRule::CountLimit) nextDate = initialDateTime.date(); else nextDate = periodStart.date(); inferMissingCriteria(&rrule, initialDateTime.date()); int countLimitDates = 0; bool periodEndReached = false; while (!periodEndReached && nextDate <= realPeriodEnd.date() && retn.size() < maxCount) { if (rrule.limitType() == QOrganizerRecurrenceRule::CountLimit && countLimitDates >= rrule.limitCount()) break; // reached limit count defined in the recurrence rule // Skip nextDate if it is not the right multiple of intervals away from initialDateTime. if (inMultipleOfInterval(nextDate, initialDateTime.date(), rrule.frequency(), rrule.interval(), rrule.firstDayOfWeek())) { // Calculate the inclusive start and inclusive end of nextDate's week/month/year QDate subPeriodStart(firstDateInPeriod(nextDate, rrule.frequency(), rrule.firstDayOfWeek())); QDate subPeriodEnd(firstDateInNextPeriod(nextDate, rrule.frequency(), rrule.firstDayOfWeek()).addDays(-1)); // Compute matchesInPeriod to be the set of dates in the current week/month/year that match the rrule QList matchesInPeriod(filterByPosition( matchingDates(subPeriodStart, subPeriodEnd, rrule), rrule.positions())); // A final filter over the dates list before adding it to the returned list foreach (const QDate &match, matchesInPeriod) { nextDate = match; if (match < initialDateTime.date()) continue; if (match > realPeriodEnd.date() || retn.size() >= maxCount) break; QDateTime generatedDateTime(initialDateTime); generatedDateTime.setDate(match); countLimitDates++; if (generatedDateTime >= periodStart && generatedDateTime <= realPeriodEnd) { retn.append(generatedDateTime); } else if (generatedDateTime > realPeriodEnd) { // We've gone past the end of the period. Ensure we break both the foreach and // the while loop periodEndReached = true; break; } if (rrule.limitType() == QOrganizerRecurrenceRule::CountLimit && countLimitDates >= rrule.limitCount()) break; // reached limit count defined in the recurrence rule } } nextDate = firstDateInNextPeriod(nextDate, rrule.frequency(), rrule.firstDayOfWeek()); } return retn; } /*! Determines if \a rrule is underspecified and if so, fills in missing information based on \a initialDate. */ void QOrganizerManagerEngine::inferMissingCriteria(QOrganizerRecurrenceRule *rrule, const QDate &initialDate) { switch (rrule->frequency()) { case QOrganizerRecurrenceRule::Weekly: if (rrule->daysOfWeek().isEmpty()) { // derive day of week QSet days; days << static_cast(initialDate.dayOfWeek()); rrule->setDaysOfWeek(days); } break; case QOrganizerRecurrenceRule::Monthly: if (rrule->daysOfWeek().isEmpty() && rrule->daysOfMonth().isEmpty()) { // derive day of month QSet days; days << initialDate.day(); rrule->setDaysOfMonth(days); } break; case QOrganizerRecurrenceRule::Yearly: if (rrule->monthsOfYear().isEmpty() && rrule->weeksOfYear().isEmpty() && rrule->daysOfYear().isEmpty() && rrule->daysOfMonth().isEmpty() && rrule->daysOfWeek().isEmpty()) { // derive day of month and month of year QSet daysOfMonth; daysOfMonth << initialDate.day(); rrule->setDaysOfMonth(daysOfMonth); QSet months; months << static_cast(initialDate.month()); rrule->setMonthsOfYear(months); } else if (!rrule->monthsOfYear().isEmpty() && rrule->weeksOfYear().isEmpty() && rrule->daysOfYear().isEmpty() && rrule->daysOfMonth().isEmpty() && rrule->daysOfWeek().isEmpty()) { // derive day of month QSet daysOfMonth; daysOfMonth << initialDate.day(); rrule->setDaysOfMonth(daysOfMonth); } else if (!rrule->weeksOfYear().isEmpty() && rrule->daysOfYear().isEmpty() && rrule->daysOfMonth().isEmpty() && rrule->daysOfWeek().isEmpty()) { // derive day of week QSet days; days << static_cast(initialDate.dayOfWeek()); rrule->setDaysOfWeek(days); } break; case QOrganizerRecurrenceRule::Daily: break; case QOrganizerRecurrenceRule::Invalid: Q_ASSERT(false); } } /*! Returns true if the calendar period (specified by \a frequency) of \a date is an \a interval multiple of periods ahead of the calendar period of \a initialDate. For Weekly frequencies, \a firstDayOfWeek is used to determine when the week boundary is. eg. If \a frequency is Monthly and \a interval is 3, then true is returned iff \a date is in the same month as \a initialDate, in a month 3 months ahead, 6 months ahead, etc. */ bool QOrganizerManagerEngine::inMultipleOfInterval(const QDate &date, const QDate &initialDate, QOrganizerRecurrenceRule::Frequency frequency, int interval, Qt::DayOfWeek firstDayOfWeek) { Q_ASSERT(date >= initialDate); switch (frequency) { case QOrganizerRecurrenceRule::Yearly: { uint yearsDelta = date.year() - initialDate.year(); return (yearsDelta % interval == 0); } case QOrganizerRecurrenceRule::Monthly: { uint monthsDelta = date.month() - initialDate.month() + (12 * (date.year() - initialDate.year())); return (monthsDelta % interval == 0); } case QOrganizerRecurrenceRule::Weekly: { // we need to adjust for the week start specified by the client if the interval is greater than 1 // ie, every time we hit the day specified, we increment the week count. uint weekCount = 0; QDate tempDate = initialDate; while (tempDate < date) { tempDate = tempDate.addDays(1); if (static_cast(tempDate.dayOfWeek()) == firstDayOfWeek) { weekCount += 1; } } return (weekCount % interval == 0); } case QOrganizerRecurrenceRule::Daily: { uint daysDelta = initialDate.daysTo(date); return (daysDelta % interval == 0); } case QOrganizerRecurrenceRule::Invalid: Q_ASSERT(false); } return true; } /*! Returns the date which is the first date of the calendar period that \a date resides in. eg. if the \a frequency is Monthly, then this returns the first day of \a date's month. If the \a frequency is Weekly, then it returns the first day of \a date's week, considering the week to start on \a firstDayOfWeek */ QDate QOrganizerManagerEngine::firstDateInPeriod(const QDate &date, QOrganizerRecurrenceRule::Frequency frequency, Qt::DayOfWeek firstDayOfWeek) { QDate retn(date); switch (frequency) { case QOrganizerRecurrenceRule::Yearly: retn.setDate(date.year(), 1, 1); return retn; case QOrganizerRecurrenceRule::Monthly: retn.setDate(date.year(), date.month(), 1); return retn; case QOrganizerRecurrenceRule::Weekly: while (retn.dayOfWeek() != firstDayOfWeek) { retn = retn.addDays(-1); } return retn; case QOrganizerRecurrenceRule::Daily: return retn; default: Q_ASSERT(false); return retn; } } /*! Returns the date which is the first date of the next calendar period after \a date specified by \a frequency. eg. if \a frequency is Monthly, then this returns the first day of the next month. If \a frequency is Weekly, then it returns the first \a firstDayOfWeek after \a date. */ QDate QOrganizerManagerEngine::firstDateInNextPeriod(const QDate &date, QOrganizerRecurrenceRule::Frequency frequency, Qt::DayOfWeek firstDayOfWeek) { QDate retn(date); switch (frequency) { case QOrganizerRecurrenceRule::Yearly: retn.setDate(date.year()+1, 1, 1); return retn; case QOrganizerRecurrenceRule::Monthly: { int newMonth = date.month() + 1; int newYear = date.year() + (newMonth==13 ? 1 : 0); retn.setDate(newYear, newMonth==13 ? 1 : newMonth, 1); } return retn; case QOrganizerRecurrenceRule::Weekly: do { retn = retn.addDays(1); } while (retn.dayOfWeek() != firstDayOfWeek); return retn; case QOrganizerRecurrenceRule::Daily: retn = retn.addDays(1); return retn; case QOrganizerRecurrenceRule::Invalid: Q_ASSERT(false); } return retn; } /*! Returns a list of dates between \a periodStart (inclusive) and \a periodEnd (inclusive) which match the \a rrule. Only daysOfWeek, daysOfMonth, daysOfYear, weeksOfYear and months from the \a rrule are matched. */ QList QOrganizerManagerEngine::matchingDates(const QDate &periodStart, const QDate &periodEnd, const QOrganizerRecurrenceRule &rrule) { QList retn; QSet daysOfWeek = rrule.daysOfWeek(); QSet daysOfMonth = rrule.daysOfMonth(); QSet daysOfYear = rrule.daysOfYear(); QSet weeksOfYear = rrule.weeksOfYear(); QSet monthsOfYear = rrule.monthsOfYear(); QDate tempDate = periodStart; while (tempDate <= periodEnd) { if ((monthsOfYear.isEmpty() || monthsOfYear.contains(static_cast(tempDate.month()))) && (weeksOfYear.isEmpty() || weeksOfYear.contains(tempDate.weekNumber())) && (daysOfYear.isEmpty() || daysOfYear.contains(tempDate.dayOfYear())) && (daysOfMonth.isEmpty() || daysOfMonth.contains(tempDate.day())) && (daysOfWeek.isEmpty() || daysOfWeek.contains(static_cast(tempDate.dayOfWeek())))) { retn.append(tempDate); } tempDate = tempDate.addDays(1); } return retn; } /*! Returns a list of dates from \a dates which are at the indices specified by \a positions. For positive values in \a positions, the values represent a 1-based index into \a dates. For negative values, they represent indices counting from the end of \a dates (eg. -1 means the last value of \a dates). */ QList QOrganizerManagerEngine::filterByPosition(const QList &dates, const QSet positions) { if (positions.isEmpty()) { return dates; } QList retn; foreach (int i, positions) { if (i >= 1 && i <= dates.size()) { // positions is 1-indexed, but the QList is 0-indexed retn.append(dates[i-1]); } else if (i <= -1 && i >= -dates.size()) { // for negative values, count from the end of the list retn.append(dates[dates.size() + i]); } } return retn; } /*! Returns true if the given organizer item \a oi has any recurrence. */ bool QOrganizerManagerEngine::itemHasReccurence(const QOrganizerItem& oi) { if (oi.type() == QOrganizerItemType::TypeEvent || oi.type() == QOrganizerItemType::TypeTodo) { QOrganizerItemRecurrence recur = oi.detail(QOrganizerItemDetail::TypeRecurrence); return !recur.recurrenceDates().isEmpty() || !recur.recurrenceRules().isEmpty(); } return false; } /*! Returns the engine ID from the given item \a id. The caller does not take ownership of the pointer, and should not delete returned id or undefined behavior may occur. */ const QOrganizerItemEngineId *QOrganizerManagerEngine::engineItemId(const QOrganizerItemId &id) { return id.d.data(); } /*! Returns the engine ID from the given collection \a id. The caller does not take ownership of the pointer, and should not delete returned id or undefined behavior may occur. */ const QOrganizerCollectionEngineId* QOrganizerManagerEngine::engineCollectionId(const QOrganizerCollectionId& id) { return id.d.data(); } /*! This function is called when the given \a request has been destroyed by the client. When this function is called, it means for the backend: \list \li The client doesn't care about the request any more. The engine can still complete it, but completion is not required. \li It can't reliably access any properties of the request pointer any more. The pointer will be invalid once this function returns. \endlist Note that since the \a request may run in another thread, this function should be blocked until the worker thread gets fully notified. */ void QOrganizerManagerEngine::requestDestroyed(QOrganizerAbstractRequest *request) { Q_UNUSED(request); } /*! This function is called when the client tries to start the given asynchronous \a request. Returns true if the request is started successfully, or false otherwise. Note that the request is supposed to run in an asynchronous manner that this function should return as soon as possible. Therefore, it the operation would last sometime, a worker thread should be used to queue and process the request. In such cases, backend should be aware that the request may be deleted by the client, and requestDestroyed() function will be called. */ bool QOrganizerManagerEngine::startRequest(QOrganizerAbstractRequest* request) { Q_UNUSED(request); return false; } /*! This function is called when the client tries to cancel the given asynchronous \a request. Returns true if the request is calcelled successfully, or false otherwise. */ bool QOrganizerManagerEngine::cancelRequest(QOrganizerAbstractRequest* request) { Q_UNUSED(request); return false; } /*! This function is called when the client wants to be blocked until the given \a request is completed, or until \a msecs milliseconds have passed. Returns true when the request is completed, or false otherwise. */ bool QOrganizerManagerEngine::waitForRequestFinished(QOrganizerAbstractRequest* request, int msecs) { Q_UNUSED(request); Q_UNUSED(msecs); return false; } /*! Updates the given asynchronous request \a req by setting the new \a state of the request. If the new state is different, the stateChanged() signal will be emitted by the request. */ void QOrganizerManagerEngine::updateRequestState(QOrganizerAbstractRequest* req, QOrganizerAbstractRequest::State state) { Q_ASSERT(req); QMutexLocker ml(&req->d_ptr->m_mutex); bool emitState = req->d_ptr->m_state != state; req->d_ptr->m_state = state; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QOrganizerAbstractRequest::State, state)); Q_ASSERT(guard); } /*! Updates the given QOrganizerItemOccurrenceFetchRequest \a req with the latest results \a result, and operation error \a error. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QOrganizerManagerEngine::updateItemOccurrenceFetchRequest(QOrganizerItemOccurrenceFetchRequest* req, const QList& result, QOrganizerManager::Error error, QOrganizerAbstractRequest::State newState) { Q_ASSERT(req); QOrganizerItemOccurrenceFetchRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_organizeritems = result; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QOrganizerAbstractRequest::State, newState)); Q_ASSERT(guard); } /*! Updates the given QOrganizerItemIdFetchRequest \a req with the latest results \a result, and operation error \a error. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QOrganizerManagerEngine::updateItemIdFetchRequest(QOrganizerItemIdFetchRequest* req, const QList& result, QOrganizerManager::Error error, QOrganizerAbstractRequest::State newState) { Q_ASSERT(req); QOrganizerItemIdFetchRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_ids = result; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QOrganizerAbstractRequest::State, newState)); Q_ASSERT(guard); } /*! Updates the given QOrganizerItemFetchByIdRequest \a req with the latest results \a result, and operation error \a error, and map of input index to individual error \a errorMap. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QOrganizerManagerEngine::updateItemFetchByIdRequest(QOrganizerItemFetchByIdRequest *req, const QList &result, QOrganizerManager::Error error, const QMap &errorMap, QOrganizerAbstractRequest::State newState) { Q_ASSERT(req); QOrganizerItemFetchByIdRequestPrivate *rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_items = result; rd->m_errors = errorMap; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QOrganizerAbstractRequest::State, newState)); Q_ASSERT(guard); } /*! Updates the given QOrganizerItemFetchRequest \a req with the latest results \a result, and operation error \a error. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QOrganizerManagerEngine::updateItemFetchRequest(QOrganizerItemFetchRequest* req, const QList& result, QOrganizerManager::Error error, QOrganizerAbstractRequest::State newState) { Q_ASSERT(req); QOrganizerItemFetchRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_organizeritems = result; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QOrganizerAbstractRequest::State, newState)); Q_ASSERT(guard); } /*! Updates the given QOrganizerItemFetchForExportRequest \a req with the latest results \a result, and operation error \a error. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QOrganizerManagerEngine::updateItemFetchForExportRequest(QOrganizerItemFetchForExportRequest* req, const QList& result, QOrganizerManager::Error error, QOrganizerAbstractRequest::State newState) { Q_ASSERT(req); QOrganizerItemFetchForExportRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_organizeritems = result; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QOrganizerAbstractRequest::State, newState)); Q_ASSERT(guard); } /*! Updates the given QOrganizerItemRemoveRequest \a req with the operation error \a error, and map of input index to individual error \a errorMap. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QOrganizerManagerEngine::updateItemRemoveRequest(QOrganizerItemRemoveRequest* req, QOrganizerManager::Error error, const QMap& errorMap, QOrganizerAbstractRequest::State newState) { Q_ASSERT(req); QOrganizerItemRemoveRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_errors = errorMap; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QOrganizerAbstractRequest::State, newState)); Q_ASSERT(guard); } /*! Updates the given QOrganizerItemRemoveByIdRequest \a req with the operation error \a error, and map of input index to individual error \a errorMap. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QOrganizerManagerEngine::updateItemRemoveByIdRequest(QOrganizerItemRemoveByIdRequest* req, QOrganizerManager::Error error, const QMap& errorMap, QOrganizerAbstractRequest::State newState) { Q_ASSERT(req); QOrganizerItemRemoveByIdRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_errors = errorMap; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QOrganizerAbstractRequest::State, newState)); Q_ASSERT(guard); } /*! Updates the given QOrganizerItemSaveRequest \a req with the latest results \a result, operation error \a error, and map of input index to individual error \a errorMap. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QOrganizerManagerEngine::updateItemSaveRequest(QOrganizerItemSaveRequest* req, const QList& result, QOrganizerManager::Error error, const QMap& errorMap, QOrganizerAbstractRequest::State newState) { Q_ASSERT(req); QOrganizerItemSaveRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_organizeritems = result; rd->m_errors = errorMap; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QOrganizerAbstractRequest::State, newState)); Q_ASSERT(guard); } /*! Updates the given QOrganizerCollectionFetchRequest \a req with the latest results \a result and an operation error \a error. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QOrganizerManagerEngine::updateCollectionFetchRequest(QOrganizerCollectionFetchRequest* req, const QList& result, QOrganizerManager::Error error, QOrganizerAbstractRequest::State newState) { Q_ASSERT(req); QOrganizerCollectionFetchRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_collections = result; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QOrganizerAbstractRequest::State, newState)); Q_ASSERT(guard); } /*! Updates the given QOrganizerCollectionRemoveRequest \a req with the operation error \a error, and map of input index to individual error \a errorMap. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QOrganizerManagerEngine::updateCollectionRemoveRequest(QOrganizerCollectionRemoveRequest* req, QOrganizerManager::Error error, const QMap& errorMap, QOrganizerAbstractRequest::State newState) { Q_ASSERT(req); QOrganizerCollectionRemoveRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_errors = errorMap; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QOrganizerAbstractRequest::State, newState)); Q_ASSERT(guard); } /*! Updates the given QOrganizerCollectionSaveRequest \a req with the latest results \a result, operation error \a error, and map of input index to individual error \a errorMap. In addition, the state of the request will be changed to \a newState. It then causes the request to emit its resultsAvailable() signal to notify clients of the request progress. If the new request state is different from the previous state, the stateChanged() signal will also be emitted from the request. */ void QOrganizerManagerEngine::updateCollectionSaveRequest(QOrganizerCollectionSaveRequest* req, const QList& result, QOrganizerManager::Error error, const QMap& errorMap, QOrganizerAbstractRequest::State newState) { Q_ASSERT(req); QOrganizerCollectionSaveRequestPrivate* rd = static_cast(req->d_ptr); QMutexLocker ml(&rd->m_mutex); bool emitState = rd->m_state != newState; rd->m_collections = result; rd->m_errors = errorMap; rd->m_error = error; rd->m_state = newState; ml.unlock(); #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) QPointer guard(req); #endif Qt::ConnectionType connectionType = Qt::DirectConnection; #ifdef QT_NO_THREAD if (req->thread() != QThread::currentThread()) connectionType = Qt::BlockingQueuedConnection; #endif QMetaObject::invokeMethod(req, "resultsAvailable", connectionType); Q_ASSERT(guard); if (emitState) QMetaObject::invokeMethod(req, "stateChanged", connectionType, Q_ARG(QOrganizerAbstractRequest::State, newState)); Q_ASSERT(guard); } #include "moc_qorganizermanagerengine.cpp" QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizermanagerengine.h000066400000000000000000000304041233466112000207170ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERMANAGERENGINE_H #define QORGANIZERMANAGERENGINE_H #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerCollectionFetchRequest; class QOrganizerCollectionRemoveRequest; class QOrganizerCollectionSaveRequest; class QOrganizerItemIdFetchRequest; class QOrganizerItemFetchByIdRequest; class QOrganizerItemFetchRequest; class QOrganizerItemOccurrenceFetchRequest; class QOrganizerItemRemoveRequest; class QOrganizerItemRemoveByIdRequest; class QOrganizerItemSaveRequest; class QOrganizerItemFetchForExportRequest; class Q_ORGANIZER_EXPORT QOrganizerManagerEngine : public QObject { Q_OBJECT public: QOrganizerManagerEngine(QObject *parent = 0); virtual QString managerName() const; virtual QMap managerParameters() const; QString managerUri() const; // items virtual QList items(const QList &itemIds, const QOrganizerItemFetchHint &fetchHint, QMap *errorMap, QOrganizerManager::Error *error); virtual QList items(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error); virtual QList itemIds(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, const QList &sortOrders, QOrganizerManager::Error *error); virtual QList itemOccurrences(const QOrganizerItem &parentItem, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error); virtual QList itemsForExport(const QDateTime &startDateTime, const QDateTime &endDateTime, const QOrganizerItemFilter &filter, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error); virtual bool saveItems(QList *items, const QList &detailMask, QMap *errorMap, QOrganizerManager::Error *error); virtual bool removeItems(const QList &itemIds, QMap *errorMap, QOrganizerManager::Error *error); virtual bool removeItems(const QList *items, QMap *errorMap, QOrganizerManager::Error *error); // collections virtual QOrganizerCollection defaultCollection(QOrganizerManager::Error *error); virtual QOrganizerCollection collection(const QOrganizerCollectionId &collectionId, QOrganizerManager::Error *error); virtual QList collections(QOrganizerManager::Error *error); virtual bool saveCollection(QOrganizerCollection *collection, QOrganizerManager::Error *error); virtual bool removeCollection(const QOrganizerCollectionId &collectionId, QOrganizerManager::Error *error); // asynchronous request support virtual void requestDestroyed(QOrganizerAbstractRequest *request); virtual bool startRequest(QOrganizerAbstractRequest *request); virtual bool cancelRequest(QOrganizerAbstractRequest *request); virtual bool waitForRequestFinished(QOrganizerAbstractRequest *request, int msecs); static void updateRequestState(QOrganizerAbstractRequest *request, QOrganizerAbstractRequest::State state); static void updateItemOccurrenceFetchRequest(QOrganizerItemOccurrenceFetchRequest *request, const QList &result, QOrganizerManager::Error error, QOrganizerAbstractRequest::State newState); static void updateItemIdFetchRequest(QOrganizerItemIdFetchRequest *request, const QList &result, QOrganizerManager::Error error, QOrganizerAbstractRequest::State newState); static void updateItemFetchByIdRequest(QOrganizerItemFetchByIdRequest *request, const QList &result, QOrganizerManager::Error error, const QMap &errorMap, QOrganizerAbstractRequest::State); static void updateItemFetchRequest(QOrganizerItemFetchRequest *request, const QList &result, QOrganizerManager::Error error, QOrganizerAbstractRequest::State newState); static void updateItemFetchForExportRequest(QOrganizerItemFetchForExportRequest *request, const QList &result, QOrganizerManager::Error error, QOrganizerAbstractRequest::State newState); static void updateItemRemoveRequest(QOrganizerItemRemoveRequest *request, QOrganizerManager::Error error, const QMap &errorMap, QOrganizerAbstractRequest::State newState); static void updateItemRemoveByIdRequest(QOrganizerItemRemoveByIdRequest *request, QOrganizerManager::Error error, const QMap &errorMap, QOrganizerAbstractRequest::State newState); static void updateItemSaveRequest(QOrganizerItemSaveRequest *request, const QList &result, QOrganizerManager::Error error, const QMap &errorMap, QOrganizerAbstractRequest::State newState); static void updateCollectionFetchRequest(QOrganizerCollectionFetchRequest *request, const QList &result, QOrganizerManager::Error error, QOrganizerAbstractRequest::State newState); static void updateCollectionRemoveRequest(QOrganizerCollectionRemoveRequest *request, QOrganizerManager::Error error, const QMap &errorMap, QOrganizerAbstractRequest::State newState); static void updateCollectionSaveRequest(QOrganizerCollectionSaveRequest *request, const QList &result, QOrganizerManager::Error error, const QMap &errorMap, QOrganizerAbstractRequest::State newState); // functionality reporting virtual QList supportedFilters() const; virtual QList supportedItemDetails(QOrganizerItemType::ItemType itemType) const; virtual QList supportedItemTypes() const; // helper static const QOrganizerItemEngineId *engineItemId(const QOrganizerItemId &itemId); static const QOrganizerCollectionEngineId *engineCollectionId(const QOrganizerCollectionId &collectionId); static int addSorted(QList *sorted, const QOrganizerItem &toAdd, const QList &sortOrders); static bool addDefaultSorted(QMultiMap *defaultSorted, const QOrganizerItem &toAdd); static int compareItem(const QOrganizerItem &a, const QOrganizerItem &b, const QList &sortOrders); static int compareVariant(const QVariant &a, const QVariant &b, Qt::CaseSensitivity sensitivity); static bool isItemBetweenDates(const QOrganizerItem &item, const QDateTime &startPeriod, const QDateTime &endPeriod); static bool itemLessThan(const QOrganizerItem &a, const QOrganizerItem &b); static bool testFilter(const QOrganizerItemFilter &filter, const QOrganizerItem &item); static QOrganizerItemFilter canonicalizedFilter(const QOrganizerItemFilter &filter); // recurrence help static QOrganizerItem generateOccurrence(const QOrganizerItem &parentItem, const QDateTime &rdate); static QList generateDateTimes(const QDateTime &initialDateTime, QOrganizerRecurrenceRule rrule, const QDateTime &periodStart, const QDateTime &periodEnd, int maxCount); static void inferMissingCriteria(QOrganizerRecurrenceRule *rrule, const QDate &initialDate); static bool inMultipleOfInterval(const QDate &date, const QDate &initialDate, QOrganizerRecurrenceRule::Frequency frequency, int interval, Qt::DayOfWeek firstDayOfWeek); static QDate firstDateInPeriod(const QDate &date, QOrganizerRecurrenceRule::Frequency frequency, Qt::DayOfWeek firstDayOfWeek); static QDate firstDateInNextPeriod(const QDate &date, QOrganizerRecurrenceRule::Frequency frequency, Qt::DayOfWeek firstDayOfWeek); static QList matchingDates(const QDate &periodStart, const QDate &periodEnd, const QOrganizerRecurrenceRule &rrule); static QList filterByPosition(const QList &dates, const QSet positions); static bool itemHasReccurence(const QOrganizerItem &oi); Q_SIGNALS: void dataChanged(); void itemsAdded(const QList &itemIds); void itemsChanged(const QList &itemIds); void itemsRemoved(const QList &itemIds); void itemsModified(const QList > &itemIds); void collectionsAdded(const QList &collectionIds); void collectionsChanged(const QList &collectionIds); void collectionsRemoved(const QList &collectionIds); void collectionsModified(const QList > &collectionIds); private: Q_DISABLE_COPY(QOrganizerManagerEngine) friend class QOrganizerItemChangeSet; friend class QOrganizerCollectionChangeSet; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERMANAGERENGINE_H src/organizer/qorganizermanagerenginefactory.cpp000066400000000000000000000104631233466112000226450ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizermanagerenginefactory.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerManagerEngineFactory \brief The QOrganizerManagerEngineFactory class provides the interface to implement the creation of organizer manager engine instances. \inmodule QtOrganizer \ingroup organizer-backends This class should only be used by backend developers. All the functions are only called internally by QOrganizerManager, and should not be called by others. More information on writing a organizer engine plugin is available in the \l{Qt Organizer Manager Engines} documentation. \sa QOrganizerManager, QOrganizerManagerEngine */ /*! A default, empty destructor. */ QOrganizerManagerEngineFactory::~QOrganizerManagerEngineFactory() { } /*! \fn QOrganizerManagerEngineFactory::engine(const QMap ¶meters, QOrganizerManager::Error *error) This function should return an instance of the engine provided by this factory. The \a parameters supplied can be ignored or interpreted as desired. If a supplied parameter results in an unfulfillable request, or some other error occurs, this function may return a null pointer, and the client developer will get an invalid QOrganizerManager in return. Any error should be saved in \a error. */ /*! \fn QOrganizerManagerEngineFactory::managerName() const This function should return a unique string that identifies the engines provided by this factory. Typically this would be of the form "org.qt-project.Qt.SampleOrganizerEngine", with the appropriate domain and engine name substituted. */ /*! \fn QOrganizerManagerEngineFactory::createItemEngineId(const QMap ¶meters, const QString &engineIdString) const This function should return an engine-specific item ID, according to the given \a parameters and the \a engineIdString. */ /*! \fn QOrganizerManagerEngineFactory::createCollectionEngineId(const QMap ¶meters, const QString &engineIdString) const This function should return an engine-specific collection ID, according to the given \a parameters and the \a engineIdString. */ /*! \internal */ QStringList QOrganizerManagerEngineFactory::keys() const { return QStringList() << managerName(); } #include "moc_qorganizermanagerenginefactory.cpp" QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizermanagerenginefactory.h000066400000000000000000000075101233466112000223110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERMANAGERENGINEFACTORY_H #define QORGANIZERMANAGERENGINEFACTORY_H #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER struct Q_ORGANIZER_EXPORT QOrganizerManagerEngineFactoryInterface : public QFactoryInterface { virtual QOrganizerManagerEngine *engine(const QMap ¶meters, QOrganizerManager::Error *error) = 0; virtual QString managerName() const = 0; virtual QOrganizerItemEngineId *createItemEngineId(const QMap ¶meters, const QString &engineIdString) const = 0; virtual QOrganizerCollectionEngineId *createCollectionEngineId(const QMap ¶meters, const QString &engineIdString) const = 0; }; QT_END_NAMESPACE_ORGANIZER QT_BEGIN_NAMESPACE #define QT_ORGANIZER_MANAGER_ENGINE_INTERFACE "org.qt-project.Qt.QOrganizerManagerEngineFactoryInterface" Q_DECLARE_INTERFACE(QtOrganizer::QOrganizerManagerEngineFactoryInterface, QT_ORGANIZER_MANAGER_ENGINE_INTERFACE) QT_END_NAMESPACE QT_BEGIN_NAMESPACE_ORGANIZER class Q_ORGANIZER_EXPORT QOrganizerManagerEngineFactory : public QObject, public QOrganizerManagerEngineFactoryInterface { Q_OBJECT Q_INTERFACES(QtOrganizer::QOrganizerManagerEngineFactoryInterface:QFactoryInterface) public: virtual ~QOrganizerManagerEngineFactory(); virtual QOrganizerManagerEngine *engine(const QMap ¶meters, QOrganizerManager::Error *error) = 0; virtual QString managerName() const = 0; virtual QOrganizerItemEngineId *createItemEngineId(const QMap ¶meters, const QString &engineIdString) const = 0; virtual QOrganizerCollectionEngineId *createCollectionEngineId(const QMap ¶meters, const QString &engineIdString) const = 0; QStringList keys() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERMANAGERENGINEFACTORY_H src/organizer/qorganizerrecurrencerule.cpp000066400000000000000000000467601233466112000215130ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizerrecurrencerule.h" #include "qorganizerrecurrencerule_p.h" #ifndef QT_NO_DEBUG_STREAM #include #endif QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerRecurrenceRule \brief The QOrganizerRecurrenceRule class describes the a rule by which a QOrganizerItem repeats. \inmodule QtOrganizer \ingroup organizer-main This class is a mapping of a subset of the iCalendar RRULE property value, and each field in this class corresponds to a fragment of iCalendar's RRULE. This class supports the same fragments as those supported by RRULE, except for describing recurrences on a higher frequency than Daily. That is, this class doesn't support hourly, minutely or secondly recurrences, nor does it support specifying which hour, minute or second of a day to recur on. These types of rules are unsupported because most calendaring backends don't support them, and it simplifies recurrences by enforcing that there can be at most one occurrence of an item per day. The general rules for interaction between the fields when generating the occurence dates is as follows: \list \li When a criterion takes a list, the items in the list are unioned together. \list \li e.g. with [dayOfWeek == Tuesday,Thursday], the event occurs if it is Tuesday or Thursday. \endlist \li Frequency and specific criteria interact in a more complicated fashion. For each criterion on a larger timespan than the frequency, the dates matching the criterion are intersected with the dates resulting from the frequency. \list \li e.g. [frequency = Daily, month = January] means every day in January. For each criterion on a shorter timespan than the frequency, the criterion is unioned. \li e.g. [frequency = Weekly, dayOfWeek = Wednesday,Friday] means every Wednesday and Friday of every week. \endlist This makes the frequency field superfluous in many cases when other criteria are present. e.g. all of the below mean the same thing: \list \li [frequency = Daily, dayOfWeek = Monday,Tuesday] \li [frequency = Weekly, dayOfWeek = Monday,Tuesday] \li [frequency = Monthly, dayOfWeek = Monday,Tuesday] \li [frequency = Yearly, dayOfWeek = Monday,Tuesday] \endlist However, the frequency field may start affecting the result differently when other fields are added like interval and positions. \li For the purpose of calculating occurrence dates, information not contained in the rule is in some cases derived from the startDateTime field of the event that the detail is associated with. There are three cases where such derivation is necessary. \list \li Case 1: frequency == Weekly. If dayOfWeek is not specified, derive it from the week day that the startDateTime occurs on. \li Case 2: frequency == Monthly. If neither dayOfWeek or dayOfMonth is specified, dayOfMonth should be derived from the startDateTime \li Case 3: frequency == Yearly. If none of monthOfYear, weekOfYear, dayOfYear, dayOfMonth or dayOfWeek are specified, derive monthOfYear and dayOfMonth. If monthOfYear is specified but not weekOfYear, dayOfYear, dayOfMonth or dayOfWeek, then derive dayOfMonth. If weekOfYear is specified but not dayOfYear, dayOfWeek or dayOfMonth, derive dayOfWeek from the startDateTime. For any cases not covered here, do not derive any of the fields. \endlist \endlist A recurrence rule may be limited by either count or date, or it may be unlimited. If limited by count, the series generated by the rule will have at most \c count occurrences. If limited by date, the series generated by the rule may have occurrences up to (and including) the limit \c date. See \l setLimit() for more information on this topic. */ /*! \enum QOrganizerRecurrenceRule::Frequency This enumeration describes how often an item recurs. \value Invalid The entire recurrence rule is invalid. \value Daily The item recurs every day. \value Weekly The item recurs every week. \value Monthly The item recurs every month. \value Yearly The item recurs every year. */ /*! \enum QOrganizerRecurrenceRule::Month This enumeration describes which month an item recurs on. \value January \value February \value March \value April \value May \value June \value July \value August \value September \value October \value November \value December */ /*! \enum QOrganizerRecurrenceRule::LimitType This enumeration describes the limitation of this recurrence rule. \value NoLimit The recurrence rule has no limit specified. \value CountLimit The recurrence rule specifies a certain count of repetitions in the series. \value DateLimit The recurrence rule specifies that the series ends after a particular date. */ /*! Constructs a QOrganizerRecurrenceRule object describing a weekly recurrence. */ QOrganizerRecurrenceRule::QOrganizerRecurrenceRule() : d(new QOrganizerRecurrenceRulePrivate) { } /*! Destroys the QOrganizerRecurrenceRule object. */ QOrganizerRecurrenceRule::~QOrganizerRecurrenceRule() { } /*! Constructs a QOrganizerRecurrenceRule object as a copy of \a other. */ QOrganizerRecurrenceRule::QOrganizerRecurrenceRule(const QOrganizerRecurrenceRule &other) : d(other.d) { } /*! Assigns this detail to be equal to \a other. */ QOrganizerRecurrenceRule &QOrganizerRecurrenceRule::operator=(const QOrganizerRecurrenceRule &other) { d = other.d; return *this; } /*! Returns true if this recurrence rule is equal to the \a other; otherwise returns false. \sa operator!=() */ bool QOrganizerRecurrenceRule::operator==(const QOrganizerRecurrenceRule &other) const { if (d == other.d) return true; return d->firstDayOfWeek == other.d->firstDayOfWeek && d->frequency == other.d->frequency && d->interval == other.d->interval && d->limitCount == other.d->limitCount && d->limitDate == other.d->limitDate && d->limitType == other.d->limitType && d->positions == other.d->positions && d->daysOfMonth == other.d->daysOfMonth && d->daysOfWeek == other.d->daysOfWeek && d->daysOfYear == other.d->daysOfYear && d->monthsOfYear == other.d->monthsOfYear && d->weeksOfYear == other.d->weeksOfYear; } /*! \fn bool QOrganizerRecurrenceRule::operator!=(const QOrganizerRecurrenceRule &other) const Returns true if this recurrence rule is not equal to the \a other; otherwise returns false. \sa operator==() */ /*! Sets the frequency with which the item recurs to \a freq. This corresponds to the FREQ fragment in iCalendar's RRULE. */ void QOrganizerRecurrenceRule::setFrequency(Frequency freq) { d->frequency = freq; } /*! Returns the frequency with which the item recurs. The default frequency is Invalid. */ QOrganizerRecurrenceRule::Frequency QOrganizerRecurrenceRule::frequency() const { return d->frequency; } /*! Sets the "count" condition of the recurrence rule to \a count. If an end-date was previously set, it is removed as count and endDate are mutually exclusive. The "count" condition is the maximum number of times the item should recur. Calling clearLimit() or setting this to a negative value removes the count condition. This corresponds to the COUNT fragment in iCalendar's RRULE. */ void QOrganizerRecurrenceRule::setLimit(int count) { if (count < 0) { clearLimit(); } else { d->limitType = QOrganizerRecurrenceRule::CountLimit; d->limitCount = count; d->limitDate = QDate(); } } /*! Sets the end-date condition of the recurrence rule to \a date. If a "count" condition was previously set, it is removed as count and endDate are mutually exclusive. The end-date condition is the date after which the item should not recur. Calling clearLimit() or setting this to an invalid date removes the end-date condition. This corresponds to the UNTIL fragment in iCalendar's RRULE. */ void QOrganizerRecurrenceRule::setLimit(const QDate &date) { if (!date.isValid()) { clearLimit(); } else { d->limitType = QOrganizerRecurrenceRule::DateLimit; d->limitDate = date; d->limitCount = -1; } } /*! Clear any recurrence rule limitation conditions. */ void QOrganizerRecurrenceRule::clearLimit() { d->limitType = QOrganizerRecurrenceRule::NoLimit; d->limitCount = -1; d->limitDate = QDate(); } /*! Returns the type of limitation specified by the recurrence rule. The default limit type is NoLimit (i.e. unlimited). */ QOrganizerRecurrenceRule::LimitType QOrganizerRecurrenceRule::limitType() const { return d->limitType; } /*! Returns the "count" condition specified by the recurrence rule. -1 is returned if the "count" condition is not set or an end-date condition is currently set. */ int QOrganizerRecurrenceRule::limitCount() const { if (d->limitType == QOrganizerRecurrenceRule::CountLimit) return d->limitCount; return -1; } /*! Returns the end-date condition specified by the recurrence rule. An invalid date is returned if the end-date condition is not set or a "count" condition is currently set. */ QDate QOrganizerRecurrenceRule::limitDate() const { if (d->limitType == QOrganizerRecurrenceRule::DateLimit) return d->limitDate; return QDate(); } /*! Sets the interval, between cycles of length given by frequency(), in which the item should recur to \a interval. For example, if the frequency() is QOrganizerRecurrenceRule::Daily and the interval is set to 2, the item should recur every second day. This corresponds to the INTERVAL fragment in iCalendar's RRULE. */ void QOrganizerRecurrenceRule::setInterval(int interval) { if (interval > 0) d->interval = interval; } /*! Returns the interval of recurrence. The default interval is 1. */ int QOrganizerRecurrenceRule::interval() const { return d->interval; } /*! Sets the days of week on which the item should recur to \a days. This corresponds to the BYDAY fragment in iCalendar's RRULE. */ void QOrganizerRecurrenceRule::setDaysOfWeek(const QSet &days) { d->daysOfWeek = days; } /*! Returns a set of the days of week that the item should recur on, or an empty set if not specified. */ QSet QOrganizerRecurrenceRule::daysOfWeek() const { return d->daysOfWeek; } /*! Sets the days of the month on which the item should recur to \a days. Negative values in the set represent the number of days from the end of the month. e.g. 1 represents the first day of the month and -1 represents the last day of the month. This corresponds to the BYMONTHDAY fragment in iCalendar's RRULE. */ void QOrganizerRecurrenceRule::setDaysOfMonth(const QSet &days) { d->daysOfMonth = days; } /*! Returns a set of the days of the month that the item should recur on, or an empty set if not specified. */ QSet QOrganizerRecurrenceRule::daysOfMonth() const { return d->daysOfMonth; } /*! Sets the days of the year on which the item should recur to \a days. Negative values in the set represent the number of days from the end of the year. e.g. 1 represents the first day of the year and -1 represents the last day of the year. This corresponds to the BYYEARDAY fragment in iCalendar's RRULE. */ void QOrganizerRecurrenceRule::setDaysOfYear(const QSet &days) { d->daysOfYear = days; } /*! Returns a set of the days of the year that the item should recur on, or an empty set if not specified. */ QSet QOrganizerRecurrenceRule::daysOfYear() const { return d->daysOfYear; } /*! Sets the months on which the item should recur to \a months. This corresponds to the BYMONTHDAY fragment in iCalendar's RRULE. */ void QOrganizerRecurrenceRule::setMonthsOfYear(const QSet &months) { d->monthsOfYear = months; } /*! Returns a set of the months of the year that the item should recur on, or an empty set if not specified. */ QSet QOrganizerRecurrenceRule::monthsOfYear() const { return d->monthsOfYear; } /*! Sets the weeks of the year on which the item should recur to \a weeks. Negative values in the set represent the number of weeks from the end of the year. e.g. 1 represents the first week of the year and -1 represents the last week of the year. This corresponds to the BYWEEK fragment in iCalendar's RRULE. */ void QOrganizerRecurrenceRule::setWeeksOfYear(const QSet &weeks) { d->weeksOfYear = weeks; } /*! Returns a set of the weeks of the year that the item should recur on, or an empty set if not specified. */ QSet QOrganizerRecurrenceRule::weeksOfYear() const { return d->weeksOfYear; } /*! Sets the set of positions that the item should recur on to \a pos. This specifies that the item should only recur on the nth occurrence within the set of events otherwise specified by the rule, for the values of n in \a pos. Negative values in the list represnet a position counting from the end of the set. For example, if frequency() == Monthly and months() is the list Monday, Tuesday, Wednesday, Thursday, Friday, and positions() == -1, this specifies that the item should recur on the last weekday of each month. This corresponds to the BYSETPOS fragment in iCalendar's RRULE. */ void QOrganizerRecurrenceRule::setPositions(const QSet &pos) { d->positions = pos; } /*! Returns the position-set of the recurrence rule, or an empty set if not specified. */ QSet QOrganizerRecurrenceRule::positions() const { return d->positions; } /*! Sets the day that the week starts on to \a day, for the purposes of calculating recurrences. This is significant when the frequency is Weekly and the interval is greater than 1, or when weekOfYear is set. See the iCalendar spec for examples of its significance. If not set, Monday is the first day of a week. This corresponds to the BYWKST fragment in iCalendar's RRULE. */ void QOrganizerRecurrenceRule::setFirstDayOfWeek(Qt::DayOfWeek day) { d->firstDayOfWeek = day; } /*! Returns the day that the week starts on. */ Qt::DayOfWeek QOrganizerRecurrenceRule::firstDayOfWeek() const { return d->firstDayOfWeek; } /*! \relates QOrganizerRecurrenceRule Returns the hash value for \a key. */ uint qHash(const QOrganizerRecurrenceRule &key) { uint hash(0); static const unsigned int prime1 = 11; static const unsigned int prime2 = 31; static const unsigned int prime3 = 47; foreach (int day, key.daysOfMonth()) hash += day; hash *= prime1; foreach (Qt::DayOfWeek day, key.daysOfWeek()) hash += day; hash *= prime2; foreach (int day, key.daysOfYear()) hash += day; hash *= prime3; foreach (QOrganizerRecurrenceRule::Month month, key.monthsOfYear()) hash += month; hash *= prime1; foreach (int week, key.weeksOfYear()) hash += week; hash *= prime2; foreach (int pos, key.positions()) hash += pos; hash *= prime3; hash += static_cast(key.firstDayOfWeek()) + static_cast(key.frequency()) + key.interval() + key.limitCount() + qHash(key.limitDate()) + static_cast(key.limitType()); return hash * prime1; } #ifndef QT_NO_DEBUG_STREAM /*! \relates QOrganizerRecurrenceRule Outputs \a rule to the debug stream \a dbg. */ QDebug operator<<(QDebug dbg, const QOrganizerRecurrenceRule &rule) { dbg.nospace() << "QOrganizerRecurrenceRule(frequency="; dbg.nospace() << rule.frequency(); dbg.nospace() << ","; dbg.nospace() << "interval="; dbg.nospace() << rule.interval(); dbg.nospace() << ","; switch (rule.limitType()) { case QOrganizerRecurrenceRule::CountLimit: dbg.nospace() << "limitCount="; dbg.nospace() << rule.limitCount(); break; case QOrganizerRecurrenceRule::DateLimit: dbg.nospace() << "limitDate="; dbg.nospace() << rule.limitDate().toString(); break; case QOrganizerRecurrenceRule::NoLimit: dbg.nospace() << "no limit"; break; default: break; } dbg.nospace() << ",daysOfWeek=\""; foreach (Qt::DayOfWeek day, rule.daysOfWeek()) { dbg.nospace() << static_cast(day); dbg.space(); } dbg.nospace() << "\""; dbg.nospace() << ",daysOfMonth=\""; foreach (int day, rule.daysOfMonth()) { dbg.nospace() << day; dbg.space(); } dbg.nospace() << "\""; dbg.nospace() << ",daysOfYear=\""; foreach (int day, rule.daysOfYear()) { dbg.nospace() << day; dbg.space(); } dbg.nospace() << "\""; dbg.nospace() << ",monthsOfYear=\""; foreach (QOrganizerRecurrenceRule::Month month, rule.monthsOfYear()) { dbg.nospace() << static_cast(month); dbg.space(); } dbg.nospace() << "\""; dbg.nospace() << ",positions=\""; foreach (int position, rule.positions()) { dbg.nospace() << position; dbg.space(); } dbg.nospace() << "\","; dbg.nospace() << "firstDayOfWeek="; dbg.nospace() << static_cast(rule.firstDayOfWeek()); dbg.nospace() << ')'; return dbg.maybeSpace(); } #endif // QT_NO_DEBUG_STREAM QT_END_NAMESPACE_ORGANIZER src/organizer/qorganizerrecurrencerule.h000066400000000000000000000113441233466112000211460ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERRECURRENCERULE_H #define QORGANIZERRECURRENCERULE_H #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerRecurrenceRulePrivate; class Q_ORGANIZER_EXPORT QOrganizerRecurrenceRule { public: enum Frequency { Invalid = 0, Daily, Weekly, Monthly, Yearly }; enum Month { January = 1, February, March, April, May, June, July, August, September, October, November, December }; enum LimitType { NoLimit = 0, CountLimit, DateLimit }; QOrganizerRecurrenceRule(); QOrganizerRecurrenceRule(const QOrganizerRecurrenceRule &other); ~QOrganizerRecurrenceRule(); QOrganizerRecurrenceRule &operator=(const QOrganizerRecurrenceRule &other); bool operator==(const QOrganizerRecurrenceRule &other) const; inline bool operator!=(const QOrganizerRecurrenceRule &other) const { return !operator==(other); } void setFrequency(Frequency freq); Frequency frequency() const; void setLimit(int count); void setLimit(const QDate &date); void clearLimit(); LimitType limitType() const; int limitCount() const; QDate limitDate() const; void setInterval(int interval); int interval() const; void setDaysOfWeek(const QSet &days); QSet daysOfWeek() const; void setDaysOfMonth(const QSet &days); QSet daysOfMonth() const; void setDaysOfYear(const QSet &days); QSet daysOfYear() const; void setMonthsOfYear(const QSet &months); QSet monthsOfYear() const; void setWeeksOfYear(const QSet &weeks); QSet weeksOfYear() const; void setFirstDayOfWeek(Qt::DayOfWeek day); Qt::DayOfWeek firstDayOfWeek() const; void setPositions(const QSet &pos); QSet positions() const; private: QSharedDataPointer d; }; //hash functions Q_ORGANIZER_EXPORT uint qHash(const QOrganizerRecurrenceRule &rule); #ifndef QT_NO_DEBUG_STREAM Q_ORGANIZER_EXPORT QDebug operator<<(QDebug dbg, const QOrganizerRecurrenceRule &rule); #endif // QT_NO_DEBUG_STREAM inline uint qHash(QOrganizerRecurrenceRule::Month month) { return static_cast(month); } QT_END_NAMESPACE_ORGANIZER QT_BEGIN_NAMESPACE Q_DECLARE_TYPEINFO(QTORGANIZER_PREPEND_NAMESPACE(QOrganizerRecurrenceRule), Q_MOVABLE_TYPE); QT_END_NAMESPACE Q_DECLARE_METATYPE(QTORGANIZER_PREPEND_NAMESPACE(QOrganizerRecurrenceRule)) Q_DECLARE_METATYPE(QSet) Q_DECLARE_METATYPE(QSet) #endif // QORGANIZERRECURRENCERULE_H src/organizer/qorganizerrecurrencerule_p.h000066400000000000000000000074011233466112000214640ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERRECURRENCERULE_P_H #define QORGANIZERRECURRENCERULE_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerRecurrenceRulePrivate : public QSharedData { public: QOrganizerRecurrenceRulePrivate() : QSharedData(), frequency(QOrganizerRecurrenceRule::Invalid), limitCount(-1), limitType(QOrganizerRecurrenceRule::NoLimit), interval(1), firstDayOfWeek(Qt::Monday) { } QOrganizerRecurrenceRulePrivate(const QOrganizerRecurrenceRulePrivate &other) : QSharedData(other), frequency(other.frequency), limitCount(other.limitCount), limitDate(other.limitDate), limitType(other.limitType), interval(other.interval), daysOfWeek(other.daysOfWeek), daysOfMonth(other.daysOfMonth), daysOfYear(other.daysOfYear), monthsOfYear(other.monthsOfYear), weeksOfYear(other.weeksOfYear), positions(other.positions), firstDayOfWeek(other.firstDayOfWeek) { } ~QOrganizerRecurrenceRulePrivate() { } QOrganizerRecurrenceRule::Frequency frequency; int limitCount; QDate limitDate; QOrganizerRecurrenceRule::LimitType limitType; int interval; QSet daysOfWeek; QSet daysOfMonth; QSet daysOfYear; QSet monthsOfYear; QSet weeksOfYear; QSet positions; Qt::DayOfWeek firstDayOfWeek; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERRECURRENCERULE_P_H src/organizer/requests/000077500000000000000000000000001233466112000155165ustar00rootroot00000000000000src/organizer/requests/qorganizercollectionfetchrequest.cpp000066400000000000000000000061201233466112000251010ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizercollectionfetchrequest.h" #include "qorganizeritemrequests_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerCollectionFetchRequest \brief The QOrganizerCollectionFetchRequest class allows a client to asynchronously fetch collections from a backend. \inmodule QtOrganizer \ingroup organizeritems-requests This request will fetch all the collections stored in the given backend. */ /*! Constructs a new organizeritem fetch request whose parent is the specified \a parent. */ QOrganizerCollectionFetchRequest::QOrganizerCollectionFetchRequest(QObject *parent) : QOrganizerAbstractRequest(new QOrganizerCollectionFetchRequestPrivate, parent) { } /*! Frees memory in use by this request. */ QOrganizerCollectionFetchRequest::~QOrganizerCollectionFetchRequest() { } /*! Returns the collections retrieved by this request. */ QList QOrganizerCollectionFetchRequest::collections() const { Q_D(const QOrganizerCollectionFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_collections; } #include "moc_qorganizercollectionfetchrequest.cpp" QT_END_NAMESPACE_ORGANIZER src/organizer/requests/qorganizercollectionfetchrequest.h000066400000000000000000000053601233466112000245530ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERCOLLECTIONFETCHREQUEST_H #define QORGANIZERCOLLECTIONFETCHREQUEST_H #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerCollectionFetchRequestPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerCollectionFetchRequest : public QOrganizerAbstractRequest { Q_OBJECT public: QOrganizerCollectionFetchRequest(QObject *parent = 0); ~QOrganizerCollectionFetchRequest(); QList collections() const; private: Q_DISABLE_COPY(QOrganizerCollectionFetchRequest) friend class QOrganizerManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QOrganizerCollectionFetchRequest) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERCOLLECTIONFETCHREQUEST_H src/organizer/requests/qorganizercollectionremoverequest.cpp000066400000000000000000000077551233466112000253240ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizercollectionremoverequest.h" #include "qorganizeritemrequests_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerCollectionRemoveRequest \brief The QOrganizerCollectionRemoveRequest class allows a client to asynchronously remove collections from a backend. \inmodule QtOrganizer \ingroup organizeritems-requests */ /*! Constructs a new collection remove request whose parent is the specified \a parent. */ QOrganizerCollectionRemoveRequest::QOrganizerCollectionRemoveRequest(QObject *parent) : QOrganizerAbstractRequest(new QOrganizerCollectionRemoveRequestPrivate, parent) { } /*! Frees memory in use by this request. */ QOrganizerCollectionRemoveRequest::~QOrganizerCollectionRemoveRequest() { } /*! Sets the ID of collection which will be removed by this request to \a collectionId. */ void QOrganizerCollectionRemoveRequest::setCollectionId(const QOrganizerCollectionId &collectionId) { Q_D(QOrganizerCollectionRemoveRequest); QMutexLocker ml(&d->m_mutex); d->m_collectionIds.clear(); d->m_collectionIds.append(collectionId); } /*! Sets the list of IDs of collections which will be removed by this request to \a collectionIds. */ void QOrganizerCollectionRemoveRequest::setCollectionIds(const QList &collectionIds) { Q_D(QOrganizerCollectionRemoveRequest); QMutexLocker ml(&d->m_mutex); d->m_collectionIds = collectionIds; } /*! Returns the list of IDs of collections which will be removed by this request. */ QList QOrganizerCollectionRemoveRequest::collectionIds() const { Q_D(const QOrganizerCollectionRemoveRequest); QMutexLocker ml(&d->m_mutex); return d->m_collectionIds; } /*! Returns any errors which occurred during the request. */ QMap QOrganizerCollectionRemoveRequest::errorMap() const { Q_D(const QOrganizerCollectionRemoveRequest); QMutexLocker ml(&d->m_mutex); return d->m_errors; } #include "moc_qorganizercollectionremoverequest.cpp" QT_END_NAMESPACE_ORGANIZER src/organizer/requests/qorganizercollectionremoverequest.h000066400000000000000000000057501233466112000247620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERCOLLECTIONREMOVEREQUEST_H #define QORGANIZERCOLLECTIONREMOVEREQUEST_H #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerCollectionRemoveRequestPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerCollectionRemoveRequest : public QOrganizerAbstractRequest { Q_OBJECT public: QOrganizerCollectionRemoveRequest(QObject *parent = 0); ~QOrganizerCollectionRemoveRequest(); void setCollectionId(const QOrganizerCollectionId &collectionId); void setCollectionIds(const QList &collectionIds); QList collectionIds() const; QMap errorMap() const; private: Q_DISABLE_COPY(QOrganizerCollectionRemoveRequest) friend class QOrganizerManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QOrganizerCollectionRemoveRequest) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERCOLLECTIONREMOVEREQUEST_H src/organizer/requests/qorganizercollectionsaverequest.cpp000066400000000000000000000077521233466112000247620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizercollectionsaverequest.h" #include "qorganizeritemrequests_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerCollectionSaveRequest \brief The QOrganizerCollectionSaveRequest class allows a client to asynchronously save collections to a backend. \inmodule QtOrganizer \ingroup organizeritems-requests */ /*! Constructs a new collection save request whose parent is the specified \a parent. */ QOrganizerCollectionSaveRequest::QOrganizerCollectionSaveRequest(QObject *parent) : QOrganizerAbstractRequest(new QOrganizerCollectionSaveRequestPrivate, parent) { } /*! Frees memory in use by this request. */ QOrganizerCollectionSaveRequest::~QOrganizerCollectionSaveRequest() { } /*! Sets the collection which will be saved to \a collection. */ void QOrganizerCollectionSaveRequest::setCollection(const QOrganizerCollection &collection) { Q_D(QOrganizerCollectionSaveRequest); QMutexLocker ml(&d->m_mutex); d->m_collections.clear(); d->m_collections.append(collection); } /*! Sets the list of collections which will be saved to \a collections. */ void QOrganizerCollectionSaveRequest::setCollections(const QList &collections) { Q_D(QOrganizerCollectionSaveRequest); QMutexLocker ml(&d->m_mutex); d->m_collections = collections; } /*! Returns the collections which will be saved by this request if called prior to calling start(), otherwise returns the (possibly updated) collections which have been saved. */ QList QOrganizerCollectionSaveRequest::collections() const { Q_D(const QOrganizerCollectionSaveRequest); QMutexLocker ml(&d->m_mutex); return d->m_collections; } /*! Returns the map of input definition list indices to errors which occurred. */ QMap QOrganizerCollectionSaveRequest::errorMap() const { Q_D(const QOrganizerCollectionSaveRequest); QMutexLocker ml(&d->m_mutex); return d->m_errors; } #include "moc_qorganizercollectionsaverequest.cpp" QT_END_NAMESPACE_ORGANIZER src/organizer/requests/qorganizercollectionsaverequest.h000066400000000000000000000057041233466112000244220ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERCOLLECTIONSAVEREQUEST_H #define QORGANIZERCOLLECTIONSAVEREQUEST_H #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerCollectionSaveRequestPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerCollectionSaveRequest : public QOrganizerAbstractRequest { Q_OBJECT public: QOrganizerCollectionSaveRequest(QObject *parent = 0); ~QOrganizerCollectionSaveRequest(); void setCollection(const QOrganizerCollection &collection); void setCollections(const QList &collections); QList collections() const; QMap errorMap() const; private: Q_DISABLE_COPY(QOrganizerCollectionSaveRequest) friend class QOrganizerManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QOrganizerCollectionSaveRequest) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERCOLLECTIONSAVEREQUEST_H src/organizer/requests/qorganizeritemfetchbyidrequest.cpp000066400000000000000000000123551233466112000245630ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemfetchbyidrequest.h" #include "qorganizeritemrequests_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemFetchByIdRequest \brief The QOrganizerItemFetchByIdRequest class allows a client to asynchronously fetch items from a backend, given a list of item IDs. \inmodule QtOrganizer \ingroup organizer-requests The items fetched by the backend should have a one-to-one correspondence to the IDs passed into this class. That is, the nth item in the returned list should have an ID which is equal to the nth ID in the list of IDs. Any invalid ID should correspond to an empty QOrganizerItem. */ /*! Constructs a new item fetch by ID request whose parent is the specified \a parent. */ QOrganizerItemFetchByIdRequest::QOrganizerItemFetchByIdRequest(QObject *parent) : QOrganizerAbstractRequest(new QOrganizerItemFetchByIdRequestPrivate, parent) { } /*! Frees any memory used by this request. */ QOrganizerItemFetchByIdRequest::~QOrganizerItemFetchByIdRequest() { } /*! Sets the list of IDs of the items that the backend should retrieve to \a ids. */ void QOrganizerItemFetchByIdRequest::setIds(const QList &ids) { Q_D(QOrganizerItemFetchByIdRequest); QMutexLocker ml(&d->m_mutex); d->m_ids = ids; } /*! Sets the fetch hint which may be used by the backend to optimize item retrieval to \a fetchHint. A client should not make changes to a item which has been retrieved using a fetch hint other than the default fetch hint. Doing so will result in information loss when saving the item back to the manager (as the "new" restricted item will replace the previously saved item in the backend). */ void QOrganizerItemFetchByIdRequest::setFetchHint(const QOrganizerItemFetchHint &fetchHint) { Q_D(QOrganizerItemFetchByIdRequest); QMutexLocker ml(&d->m_mutex); d->m_fetchHint = fetchHint; } /*! Returns the list of IDs of the items that the backend should retrieve. */ QList QOrganizerItemFetchByIdRequest::ids() const { Q_D(const QOrganizerItemFetchByIdRequest); QMutexLocker ml(&d->m_mutex); return d->m_ids; } /*! Returns the fetch hint which may be used by the backend to optimize item retrieval. A client should not make changes to a item which has been retrieved using a fetch hint other than the default fetch hint. Doing so will result in information loss when saving the item back to the manager (as the "new" restricted item will replace the previously saved item in the backend). */ QOrganizerItemFetchHint QOrganizerItemFetchByIdRequest::fetchHint() const { Q_D(const QOrganizerItemFetchByIdRequest); QMutexLocker ml(&d->m_mutex); return d->m_fetchHint; } /*! Returns the list of items retrieved by this request. */ QList QOrganizerItemFetchByIdRequest::items() const { Q_D(const QOrganizerItemFetchByIdRequest); QMutexLocker ml(&d->m_mutex); return d->m_items; } /*! Returns the map of input definition list indices to errors which occurred. */ QMap QOrganizerItemFetchByIdRequest::errorMap() const { Q_D(const QOrganizerItemFetchByIdRequest); QMutexLocker ml(&d->m_mutex); return d->m_errors; } #include "moc_qorganizeritemfetchbyidrequest.cpp" QT_END_NAMESPACE_ORGANIZER src/organizer/requests/qorganizeritemfetchbyidrequest.h000066400000000000000000000060711233466112000242260ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMFETCHBYIDREQUEST_H #define QORGANIZERITEMFETCHBYIDREQUEST_H #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemFetchHint; class QOrganizerItemFetchByIdRequestPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemFetchByIdRequest : public QOrganizerAbstractRequest { Q_OBJECT public: QOrganizerItemFetchByIdRequest(QObject *parent = 0); ~QOrganizerItemFetchByIdRequest(); void setIds(const QList &ids); QList ids() const; void setFetchHint(const QOrganizerItemFetchHint &fetchHint); QOrganizerItemFetchHint fetchHint() const; QList items() const; QMap errorMap() const; private: Q_DISABLE_COPY(QOrganizerItemFetchByIdRequest) friend class QOrganizerManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QOrganizerItemFetchByIdRequest) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMFETCHBYIDREQUEST_H src/organizer/requests/qorganizeritemfetchforexportrequest.cpp000066400000000000000000000155731233466112000256710ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemfetchforexportrequest.h" #include "qorganizeritemrequests_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemFetchForExportRequest \brief The QOrganizerItemFetchForExportRequest class allows a client to asynchronously fetch organizer items for export from a backend. \inmodule QtOrganizer \ingroup organizer-requests This request will only fetch parent items and persisted exceptions which match the specified criteria, and no generated occurrences will be fetched. */ /*! Constructs a new organizer item fetch for export request whose parent is the specified \a parent. */ QOrganizerItemFetchForExportRequest::QOrganizerItemFetchForExportRequest(QObject *parent) : QOrganizerAbstractRequest(new QOrganizerItemFetchForExportRequestPrivate, parent) { } /*! Frees memory in use by this request. */ QOrganizerItemFetchForExportRequest::~QOrganizerItemFetchForExportRequest() { } /*! Sets the organizer item filter used to determine which organizer items will be retrieved to \a filter. */ void QOrganizerItemFetchForExportRequest::setFilter(const QOrganizerItemFilter &filter) { Q_D(QOrganizerItemFetchForExportRequest); QMutexLocker ml(&d->m_mutex); d->m_filter = filter; } /*! Sets the sort order of the result to \a sorting. */ void QOrganizerItemFetchForExportRequest::setSorting(const QList &sorting) { Q_D(QOrganizerItemFetchForExportRequest); QMutexLocker ml(&d->m_mutex); d->m_sorting = sorting; } /*! Sets the fetch hint which may be used by the backend to optimize item retrieval to \a fetchHint. A client should not make changes to a item which has been retrieved using a fetch hint other than the default fetch hint. Doing so will result in information loss when saving the item back to the manager (as the "new" restricted item will replace the previously saved item in the backend). */ void QOrganizerItemFetchForExportRequest::setFetchHint(const QOrganizerItemFetchHint &fetchHint) { Q_D(QOrganizerItemFetchForExportRequest); QMutexLocker ml(&d->m_mutex); d->m_fetchHint = fetchHint; } /*! Sets the start period of the request to \a date. A default-constructed (invalid) start date time specifies an open start date time (matches anything which occurs up until the end date time). */ void QOrganizerItemFetchForExportRequest::setStartDate(const QDateTime &date) { Q_D(QOrganizerItemFetchForExportRequest); QMutexLocker ml(&d->m_mutex); d->m_startDate = date; } /*! Sets the end period of the request to \a date. A default-constructed (invalid) end date time specifies an open end date time (matches anything which occurs after the start date time). */ void QOrganizerItemFetchForExportRequest::setEndDate(const QDateTime &date) { Q_D(QOrganizerItemFetchForExportRequest); QMutexLocker ml(&d->m_mutex); d->m_endDate = date; } /*! Returns the filter that will be used to select organizer items to be returned. */ QOrganizerItemFilter QOrganizerItemFetchForExportRequest::filter() const { Q_D(const QOrganizerItemFetchForExportRequest); QMutexLocker ml(&d->m_mutex); return d->m_filter; } /*! Returns the sort ordering that will be used to sort the results of this request. */ QList QOrganizerItemFetchForExportRequest::sorting() const { Q_D(const QOrganizerItemFetchForExportRequest); QMutexLocker ml(&d->m_mutex); return d->m_sorting; } /*! Returns the fetch hint which may be used by the backend to optimize item retrieval. A client should not make changes to a item which has been retrieved using a fetch hint other than the default fetch hint. Doing so will result in information loss when saving the item back to the manager (as the "new" restricted item will replace the previously saved item in the backend). */ QOrganizerItemFetchHint QOrganizerItemFetchForExportRequest::fetchHint() const { Q_D(const QOrganizerItemFetchForExportRequest); QMutexLocker ml(&d->m_mutex); return d->m_fetchHint; } /*! Returns the date-time which is the lower bound for the range in which items will be returned. */ QDateTime QOrganizerItemFetchForExportRequest::startDate() const { Q_D(const QOrganizerItemFetchForExportRequest); QMutexLocker ml(&d->m_mutex); return d->m_startDate; } /*! Returns the date-time which is the upper bound for the range in which items will be returned. */ QDateTime QOrganizerItemFetchForExportRequest::endDate() const { Q_D(const QOrganizerItemFetchForExportRequest); QMutexLocker ml(&d->m_mutex); return d->m_endDate; } /*! Returns the list of organizer items retrieved by this request. */ QList QOrganizerItemFetchForExportRequest::items() const { Q_D(const QOrganizerItemFetchForExportRequest); QMutexLocker ml(&d->m_mutex); return d->m_organizeritems; } #include "moc_qorganizeritemfetchforexportrequest.cpp" QT_END_NAMESPACE_ORGANIZER src/organizer/requests/qorganizeritemfetchforexportrequest.h000066400000000000000000000065631233466112000253350ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMFETCHFOREXPORTREQUEST_H #define QORGANIZERITEMFETCHFOREXPORTREQUEST_H #include #include #include #include QT_FORWARD_DECLARE_CLASS(QDateTime) QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemFetchHint; class QOrganizerItemFilter; class QOrganizerItemFetchForExportRequestPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemFetchForExportRequest : public QOrganizerAbstractRequest { Q_OBJECT public: QOrganizerItemFetchForExportRequest(QObject *parent = 0); ~QOrganizerItemFetchForExportRequest(); void setFilter(const QOrganizerItemFilter &filter); QOrganizerItemFilter filter() const; void setSorting(const QList &sorting); QList sorting() const; void setFetchHint(const QOrganizerItemFetchHint &fetchHint); QOrganizerItemFetchHint fetchHint() const; void setStartDate(const QDateTime &date); QDateTime startDate() const; void setEndDate(const QDateTime &date); QDateTime endDate() const; QList items() const; private: Q_DISABLE_COPY(QOrganizerItemFetchForExportRequest) friend class QOrganizerManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QOrganizerItemFetchForExportRequest) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMFETCHFOREXPORTREQUEST_H src/organizer/requests/qorganizeritemfetchrequest.cpp000066400000000000000000000157701233466112000237170ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemfetchrequest.h" #include "qorganizeritemrequests_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemFetchRequest \brief The QOrganizerItemFetchRequest class allows a client to asynchronously fetch organizer items from a backend. \inmodule QtOrganizer \ingroup organizer-requests This request will fetch all the items and occurrences matching the specified criteria. */ /*! Constructs a new organizer item fetch request whose parent is the specified \a parent. */ QOrganizerItemFetchRequest::QOrganizerItemFetchRequest(QObject *parent) : QOrganizerAbstractRequest(new QOrganizerItemFetchRequestPrivate, parent) { } /*! Frees memory in use by this request. */ QOrganizerItemFetchRequest::~QOrganizerItemFetchRequest() { } /*! Sets the organizer item filter used to determine which organizer items will be retrieved to \a filter. */ void QOrganizerItemFetchRequest::setFilter(const QOrganizerItemFilter &filter) { Q_D(QOrganizerItemFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_filter = filter; } /*! Sets the sort order of the result to \a sorting. */ void QOrganizerItemFetchRequest::setSorting(const QList &sorting) { Q_D(QOrganizerItemFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_sorting = sorting; } /*! Sets the fetch hint which may be used by the backend to optimize item retrieval to \a fetchHint. A client should not make changes to a item which has been retrieved using a fetch hint other than the default fetch hint. Doing so will result in information loss when saving the item back to the manager (as the "new" restricted item will replace the previously saved item in the backend). */ void QOrganizerItemFetchRequest::setFetchHint(const QOrganizerItemFetchHint &fetchHint) { Q_D(QOrganizerItemFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_fetchHint = fetchHint; } /*! Sets the start period of the request to \a date. A default-constructed (invalid) start date time specifies an open start date time (matches anything which occurs up until the end date time). */ void QOrganizerItemFetchRequest::setStartDate(const QDateTime &date) { Q_D(QOrganizerItemFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_startDate = date; } /*! Sets the end period of the request to \a date. A default-constructed (invalid) end date time specifies an open end date time (matches anything which occurs after the start date time). */ void QOrganizerItemFetchRequest::setEndDate(const QDateTime &date) { Q_D(QOrganizerItemFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_endDate = date; } /*! Sets the maximum number of items to be fetched to \a maxCount. Note that backends will decide how many items are fetched if \a maxCount is negative. */ void QOrganizerItemFetchRequest::setMaxCount(int maxCount) { Q_D(QOrganizerItemFetchRequest); d->m_maxCount = maxCount; } /*! Returns the filter that will be used to select organizer items to be returned. */ QOrganizerItemFilter QOrganizerItemFetchRequest::filter() const { Q_D(const QOrganizerItemFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_filter; } /*! Returns the sort ordering that will be used sort the results of this request. */ QList QOrganizerItemFetchRequest::sorting() const { Q_D(const QOrganizerItemFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_sorting; } /*! Returns the fetch hint which may be used by the backend to optimize item retrieval. A client should not make changes to a item which has been retrieved using a fetch hint other than the default fetch hint. Doing so will result in information loss when saving the item back to the manager (as the "new" restricted item will replace the previously saved item in the backend). */ QOrganizerItemFetchHint QOrganizerItemFetchRequest::fetchHint() const { Q_D(const QOrganizerItemFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_fetchHint; } /*! Returns the date-time which is the lower bound for the range in which items will be returned. */ QDateTime QOrganizerItemFetchRequest::startDate() const { Q_D(const QOrganizerItemFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_startDate; } /*! Returns the date-time which is the upper bound for the range in which items will be returned. */ QDateTime QOrganizerItemFetchRequest::endDate() const { Q_D(const QOrganizerItemFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_endDate; } /*! Returns the maximum number of items to return for the request. */ int QOrganizerItemFetchRequest::maxCount() const { Q_D(const QOrganizerItemFetchRequest); return d->m_maxCount; } /*! Returns the list of organizer items retrieved by this request. */ QList QOrganizerItemFetchRequest::items() const { Q_D(const QOrganizerItemFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_organizeritems; } #include "moc_qorganizeritemfetchrequest.cpp" QT_END_NAMESPACE_ORGANIZER src/organizer/requests/qorganizeritemfetchrequest.h000066400000000000000000000065411233466112000233600ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMFETCHREQUEST_H #define QORGANIZERITEMFETCHREQUEST_H #include #include #include #include QT_FORWARD_DECLARE_CLASS(QDateTime) QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemFetchHint; class QOrganizerItemFilter; class QOrganizerItemFetchRequestPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemFetchRequest : public QOrganizerAbstractRequest { Q_OBJECT public: QOrganizerItemFetchRequest(QObject *parent = 0); ~QOrganizerItemFetchRequest(); void setFilter(const QOrganizerItemFilter &filter); QOrganizerItemFilter filter() const; void setSorting(const QList &sorting); QList sorting() const; void setFetchHint(const QOrganizerItemFetchHint &fetchHint); QOrganizerItemFetchHint fetchHint() const; void setStartDate(const QDateTime &date); QDateTime startDate() const; void setEndDate(const QDateTime &date); QDateTime endDate() const; void setMaxCount(int maxCount); int maxCount() const; QList items() const; private: Q_DISABLE_COPY(QOrganizerItemFetchRequest) friend class QOrganizerManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QOrganizerItemFetchRequest) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMFETCHREQUEST_H src/organizer/requests/qorganizeritemidfetchrequest.cpp000066400000000000000000000125451233466112000242310ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemidfetchrequest.h" #include "qorganizeritemrequests_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemIdFetchRequest \brief The QOrganizerItemIdFetchRequest class allows a client to asynchronously fetch organizer item IDs from a backend. \inmodule QtOrganizer \ingroup organizer-requests */ /*! Constructs a new organizer item ID fetch request whose parent is the specified \a parent. */ QOrganizerItemIdFetchRequest::QOrganizerItemIdFetchRequest(QObject *parent) : QOrganizerAbstractRequest(new QOrganizerItemIdFetchRequestPrivate, parent) { } /*! Frees memory in use by this request. */ QOrganizerItemIdFetchRequest::~QOrganizerItemIdFetchRequest() { } /*! Sets the filter which will be used to select the organizer items whose IDs will be returned to \a filter. */ void QOrganizerItemIdFetchRequest::setFilter(const QOrganizerItemFilter &filter) { Q_D(QOrganizerItemIdFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_filter = filter; } /*! Sets the future sort ordering of the result of the request to \a sorting. */ void QOrganizerItemIdFetchRequest::setSorting(const QList &sorting) { Q_D(QOrganizerItemIdFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_sorting = sorting; } /*! Sets the start period of the request to \a date. A default-constructed (invalid) start date time specifies an open start date time (matches anything which occurs up until the end date time). */ void QOrganizerItemIdFetchRequest::setStartDate(const QDateTime &date) { Q_D(QOrganizerItemIdFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_startDate = date; } /*! Sets the end period of the request to \a date. A default-constructed (invalid) end date time specifies an open end date time (matches anything which occurs after the start date time). */ void QOrganizerItemIdFetchRequest::setEndDate(const QDateTime &date) { Q_D(QOrganizerItemIdFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_endDate = date; } /*! Returns the filter which will be used to select the organizer items whose IDs will be returned. */ QOrganizerItemFilter QOrganizerItemIdFetchRequest::filter() const { Q_D(const QOrganizerItemIdFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_filter; } /*! Returns the sort ordering which will be used to sort the result. */ QList QOrganizerItemIdFetchRequest::sorting() const { Q_D(const QOrganizerItemIdFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_sorting; } /*! Returns the date-time which is the lower bound for the range in which items will be returned. */ QDateTime QOrganizerItemIdFetchRequest::startDate() const { Q_D(const QOrganizerItemIdFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_startDate; } /*! Returns the date-time which is the upper bound for the range in which items will be returned. */ QDateTime QOrganizerItemIdFetchRequest::endDate() const { Q_D(const QOrganizerItemIdFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_endDate; } /*! Returns the list of IDs of organizer items retrieved by this request. */ QList QOrganizerItemIdFetchRequest::itemIds() const { Q_D(const QOrganizerItemIdFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_ids; } #include "moc_qorganizeritemidfetchrequest.cpp" QT_END_NAMESPACE_ORGANIZER src/organizer/requests/qorganizeritemidfetchrequest.h000066400000000000000000000062521233466112000236740ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMIDFETCHREQUEST_H #define QORGANIZERITEMIDFETCHREQUEST_H #include #include #include #include QT_FORWARD_DECLARE_CLASS(QDateTime) QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemFilter; class QOrganizerItemIdFetchRequestPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemIdFetchRequest : public QOrganizerAbstractRequest { Q_OBJECT public: QOrganizerItemIdFetchRequest(QObject *parent = 0); ~QOrganizerItemIdFetchRequest(); void setFilter(const QOrganizerItemFilter &filter); QOrganizerItemFilter filter() const; void setSorting(const QList &sorting); QList sorting() const; void setStartDate(const QDateTime &date); QDateTime startDate() const; void setEndDate(const QDateTime &date); QDateTime endDate() const; QList itemIds() const; private: Q_DISABLE_COPY(QOrganizerItemIdFetchRequest) friend class QOrganizerManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QOrganizerItemIdFetchRequest) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMIDFETCHREQUEST_H src/organizer/requests/qorganizeritemoccurrencefetchrequest.cpp000066400000000000000000000156771233466112000257760ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemoccurrencefetchrequest.h" #include "qorganizeritemrequests_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemOccurrenceFetchRequest \brief The QOrganizerItemOccurrenceFetchRequest class allows a client to asynchronously fetch occurrences generated by a recurring item. \inmodule QtOrganizer \ingroup organizer-requests This request will fetch both generated occurrences and persisted occurrences, which match the specified criteria, of the given parent item. */ /*! Constructs a new organizer item occurrence fetch request whose parent is the specified \a parent. */ QOrganizerItemOccurrenceFetchRequest::QOrganizerItemOccurrenceFetchRequest(QObject *parent) : QOrganizerAbstractRequest(new QOrganizerItemOccurrenceFetchRequestPrivate, parent) { } /*! Frees memory in use by this request. */ QOrganizerItemOccurrenceFetchRequest::~QOrganizerItemOccurrenceFetchRequest() { } /*! Sets the parent item, whose occurrences are to be fetched to \a item. */ void QOrganizerItemOccurrenceFetchRequest::setParentItem(const QOrganizerItem &item) { Q_D(QOrganizerItemOccurrenceFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_generator = item; } /*! Sets the start period of the request to \a date. A default-constructed (invalid) start date time specifies an open start date time (matches anything which occurs up until the end date time). */ void QOrganizerItemOccurrenceFetchRequest::setStartDate(const QDateTime &date) { Q_D(QOrganizerItemOccurrenceFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_startDate = date; } /*! Sets the end period of the request to \a date. A default-constructed (invalid) end date time specifies an open end date time (matches anything which occurs after the start date time). */ void QOrganizerItemOccurrenceFetchRequest::setEndDate(const QDateTime &date) { Q_D(QOrganizerItemOccurrenceFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_endDate = date; } /*! Sets the maximum number of occurrences to fetch to \a maxCount. Note that backends will decide how many items are fetched if \a maxCount is negative. */ void QOrganizerItemOccurrenceFetchRequest::setMaxOccurrences(int maxCount) { Q_D(QOrganizerItemOccurrenceFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_maxOccurrences = maxCount; } /*! Sets the fetch hint which may be used by the backend to optimize item retrieval to \a fetchHint. A client should not make changes to a item which has been retrieved using a fetch hint other than the default fetch hint. Doing so will result in information loss when saving the item back to the manager (as the "new" restricted item will replace the previously saved item in the backend). */ void QOrganizerItemOccurrenceFetchRequest::setFetchHint(const QOrganizerItemFetchHint &fetchHint) { Q_D(QOrganizerItemOccurrenceFetchRequest); QMutexLocker ml(&d->m_mutex); d->m_fetchHint = fetchHint; } /*! Returns the parent item, whose occurrences are to be fetched. */ QOrganizerItem QOrganizerItemOccurrenceFetchRequest::parentItem() const { Q_D(const QOrganizerItemOccurrenceFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_generator; } /*! Returns the date-time which is the lower bound for the range in which occurrences will be returned. */ QDateTime QOrganizerItemOccurrenceFetchRequest::startDate() const { Q_D(const QOrganizerItemOccurrenceFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_startDate; } /*! Returns the date-time which is the upper bound for the range in which occurrences will be returned. */ QDateTime QOrganizerItemOccurrenceFetchRequest::endDate() const { Q_D(const QOrganizerItemOccurrenceFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_endDate; } /*! Returns the maximum number of occurrences to return for the request. */ int QOrganizerItemOccurrenceFetchRequest::maxOccurrences() const { Q_D(const QOrganizerItemOccurrenceFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_maxOccurrences; } /*! Returns the list of organizer item occurrences retrieved by this request. */ QList QOrganizerItemOccurrenceFetchRequest::itemOccurrences() const { Q_D(const QOrganizerItemOccurrenceFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_organizeritems; } /*! Returns the fetch hint which may be used by the backend to optimize item retrieval. A client should not make changes to a item which has been retrieved using a fetch hint other than the default fetch hint. Doing so will result in information loss when saving the item back to the manager (as the "new" restricted item will replace the previously saved item in the backend). */ QOrganizerItemFetchHint QOrganizerItemOccurrenceFetchRequest::fetchHint() const { Q_D(const QOrganizerItemOccurrenceFetchRequest); QMutexLocker ml(&d->m_mutex); return d->m_fetchHint; } #include "moc_qorganizeritemoccurrencefetchrequest.cpp" QT_END_NAMESPACE_ORGANIZER src/organizer/requests/qorganizeritemoccurrencefetchrequest.h000066400000000000000000000064001233466112000254230ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMOCCURRENCEFETCHREQUEST_H #define QORGANIZERITEMOCCURRENCEFETCHREQUEST_H #include #include #include QT_FORWARD_DECLARE_CLASS(QDateTime) QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemFetchHint; class QOrganizerItemOccurrenceFetchRequestPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemOccurrenceFetchRequest : public QOrganizerAbstractRequest { Q_OBJECT public: QOrganizerItemOccurrenceFetchRequest(QObject *parent = 0); ~QOrganizerItemOccurrenceFetchRequest(); void setParentItem(const QOrganizerItem &item); QOrganizerItem parentItem() const; void setStartDate(const QDateTime &date); QDateTime startDate() const; void setEndDate(const QDateTime &date); QDateTime endDate() const; void setMaxOccurrences(int maxCount); int maxOccurrences() const; void setFetchHint(const QOrganizerItemFetchHint &hint); QOrganizerItemFetchHint fetchHint() const; QList itemOccurrences() const; private: Q_DISABLE_COPY(QOrganizerItemOccurrenceFetchRequest) friend class QOrganizerManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QOrganizerItemOccurrenceFetchRequest) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMOCCURRENCEFETCHREQUEST_H src/organizer/requests/qorganizeritemremovebyidrequest.cpp000066400000000000000000000105501233466112000247620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemremovebyidrequest.h" #include "qorganizeritemrequests_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemRemoveByIdRequest \brief The QOrganizerItemRemoveByIdRequest class allows a client to asynchronously request that certain organizer items be removed from a organizer items store. \inmodule QtOrganizer For a QOrganizerItemRemoveByIdRequest, the resultsUpdated() signal will be emitted when the individual item errors (which may be retrieved by calling errorMap()) are updated, or if the overall operation error (which may be retrieved by calling error()) is updated. \ingroup organizer-requests */ /*! Constructs a new organizer item remove request whose parent is the specified \a parent */ QOrganizerItemRemoveByIdRequest::QOrganizerItemRemoveByIdRequest(QObject* parent) : QOrganizerAbstractRequest(new QOrganizerItemRemoveByIdRequestPrivate, parent) { } /*! Frees memory in use by this request */ QOrganizerItemRemoveByIdRequest::~QOrganizerItemRemoveByIdRequest() { } /*! Sets the id of the organizer item which will be removed to \a organizeritemId. Equivalent to calling: \code setOrganizerItemIds(QList() << organizeritemIds); \endcode */ void QOrganizerItemRemoveByIdRequest::setItemId(const QOrganizerItemId& organizeritemId) { Q_D(QOrganizerItemRemoveByIdRequest); QMutexLocker ml(&d->m_mutex); d->m_organizeritemIds.clear(); d->m_organizeritemIds.append(organizeritemId); } /*! Sets the list of ids of organizer items which will be removed to \a organizeritemIds */ void QOrganizerItemRemoveByIdRequest::setItemIds(const QList& organizeritemIds) { Q_D(QOrganizerItemRemoveByIdRequest); QMutexLocker ml(&d->m_mutex); d->m_organizeritemIds = organizeritemIds; } /*! Returns the list of ids of organizer items which will be removed */ QList QOrganizerItemRemoveByIdRequest::itemIds() const { Q_D(const QOrganizerItemRemoveByIdRequest); QMutexLocker ml(&d->m_mutex); return d->m_organizeritemIds; } /*! Returns the map of input organizer item list indices to errors which occurred */ QMap QOrganizerItemRemoveByIdRequest::errorMap() const { Q_D(const QOrganizerItemRemoveByIdRequest); QMutexLocker ml(&d->m_mutex); return d->m_errors; } #include "moc_qorganizeritemremovebyidrequest.cpp" QT_END_NAMESPACE_ORGANIZER src/organizer/requests/qorganizeritemremovebyidrequest.h000066400000000000000000000057301233466112000244330ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMREMOVEBYIDREQUEST_H #define QORGANIZERITEMREMOVEBYIDREQUEST_H #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemRemoveByIdRequestPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemRemoveByIdRequest : public QOrganizerAbstractRequest { Q_OBJECT public: QOrganizerItemRemoveByIdRequest(QObject* parent = 0); ~QOrganizerItemRemoveByIdRequest(); /* Selection */ void setItemId(const QOrganizerItemId& organizeritemId); void setItemIds(const QList& organizeritemIds); QList itemIds() const; /* Results */ QMap errorMap() const; private: Q_DISABLE_COPY(QOrganizerItemRemoveByIdRequest) friend class QOrganizerManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QOrganizerItemRemoveByIdRequest) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMREMOVEBYIDREQUEST_H src/organizer/requests/qorganizeritemremoverequest.cpp000066400000000000000000000077751233466112000241310ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemremoverequest.h" #include "qorganizeritemrequests_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemRemoveRequest \brief The QOrganizerItemRemoveRequest class allows a client to asynchronously request that certain organizer items be removed from a backend. \inmodule QtOrganizer \ingroup organizer-requests This request will remove the items and all the occurrences (both generated and persisted) of the given items. */ /*! Constructs a new organizer item remove request whose parent is the specified \a parent. */ QOrganizerItemRemoveRequest::QOrganizerItemRemoveRequest(QObject *parent) : QOrganizerAbstractRequest(new QOrganizerItemRemoveRequestPrivate, parent) { } /*! Frees memory in use by this request. */ QOrganizerItemRemoveRequest::~QOrganizerItemRemoveRequest() { } /*! Sets the organizer item which will be removed to \a item. Equivalent to calling: \code setOrganizerItems(QList() << item); \endcode */ void QOrganizerItemRemoveRequest::setItem(const QOrganizerItem &item) { Q_D(QOrganizerItemRemoveRequest); QMutexLocker ml(&d->m_mutex); d->m_organizeritems.clear(); d->m_organizeritems.append(item); } /*! Sets the organizer items which will be removed to \a items */ void QOrganizerItemRemoveRequest::setItems(const QList &items) { Q_D(QOrganizerItemRemoveRequest); QMutexLocker ml(&d->m_mutex); d->m_organizeritems = items; } /*! Returns the list of IDs of organizer items which will be removed. */ QList QOrganizerItemRemoveRequest::items() const { Q_D(const QOrganizerItemRemoveRequest); QMutexLocker ml(&d->m_mutex); return d->m_organizeritems; } /*! Returns the map of input organizer item list indices to errors which occurred. */ QMap QOrganizerItemRemoveRequest::errorMap() const { Q_D(const QOrganizerItemRemoveRequest); QMutexLocker ml(&d->m_mutex); return d->m_errors; } #include "moc_qorganizeritemremoverequest.cpp" QT_END_NAMESPACE_ORGANIZER src/organizer/requests/qorganizeritemremoverequest.h000066400000000000000000000055511233466112000235640ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMREMOVEREQUEST_H #define QORGANIZERITEMREMOVEREQUEST_H #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemRemoveRequestPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemRemoveRequest : public QOrganizerAbstractRequest { Q_OBJECT public: QOrganizerItemRemoveRequest(QObject *parent = 0); ~QOrganizerItemRemoveRequest(); void setItem(const QOrganizerItem &item); void setItems(const QList &items); QList items() const; QMap errorMap() const; private: Q_DISABLE_COPY(QOrganizerItemRemoveRequest) friend class QOrganizerManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QOrganizerItemRemoveRequest) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMREMOVEREQUEST_H src/organizer/requests/qorganizeritemrequests.h000066400000000000000000000053021233466112000225230ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMREQUESTS_H #define QORGANIZERITEMREQUESTS_H #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMREQUESTS_H src/organizer/requests/qorganizeritemrequests_p.h000066400000000000000000000337511233466112000230530ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMREQUESTS_P_H #define QORGANIZERITEMREQUESTS_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #ifndef QT_NO_DEBUG_STREAM #include #endif #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemSaveRequestPrivate : public QOrganizerAbstractRequestPrivate { public: QOrganizerItemSaveRequestPrivate() : QOrganizerAbstractRequestPrivate(QOrganizerAbstractRequest::ItemSaveRequest) { } ~QOrganizerItemSaveRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerItemSaveRequest(\n"; dbg.nospace() << "* items="; dbg.nospace() << m_organizeritems; dbg.nospace() << ",\n"; dbg.nospace() << "* definitionMask="; dbg.nospace() << m_detailMask; dbg.nospace() << ",\n"; dbg.nospace() << "* errorMap="; dbg.nospace() << m_errors; dbg.nospace() << "\n)"; return dbg.maybeSpace(); } #endif QList m_organizeritems; QMap m_errors; QList m_detailMask; }; class QOrganizerItemFetchRequestPrivate : public QOrganizerAbstractRequestPrivate { public: QOrganizerItemFetchRequestPrivate() : QOrganizerAbstractRequestPrivate(QOrganizerAbstractRequest::ItemFetchRequest) , m_maxCount(-1) { } ~QOrganizerItemFetchRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerItemFetchRequest(\n"; dbg.nospace() << "* items="; dbg.nospace() << m_organizeritems; dbg.nospace() << ",\n"; dbg.nospace() << "* filter="; dbg.nospace() << m_filter; dbg.nospace() << ",\n"; dbg.nospace() << "* sorting="; dbg.nospace() << m_sorting; dbg.nospace() << ",\n"; dbg.nospace() << "* startDate="; dbg.nospace() << m_startDate; dbg.nospace() << ",\n"; dbg.nospace() << "* endDate="; dbg.nospace() << m_endDate; dbg.nospace() << ",\n"; dbg.nospace() << "* fetchHint="; dbg.nospace() << m_fetchHint; dbg.nospace() << ",\n"; dbg.nospace() << "* maxCount="; dbg.nospace() << m_maxCount; dbg.nospace() << "\n)"; return dbg.maybeSpace(); } #endif QOrganizerItemFilter m_filter; QList m_sorting; QOrganizerItemFetchHint m_fetchHint; QList m_organizeritems; QDateTime m_startDate; QDateTime m_endDate; int m_maxCount; }; class QOrganizerItemFetchForExportRequestPrivate : public QOrganizerAbstractRequestPrivate { public: QOrganizerItemFetchForExportRequestPrivate() : QOrganizerAbstractRequestPrivate(QOrganizerAbstractRequest::ItemFetchForExportRequest) { } ~QOrganizerItemFetchForExportRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerItemFetchForExportRequest(\n"; dbg.nospace() << "* items="; dbg.nospace() << m_organizeritems; dbg.nospace() << ",\n"; dbg.nospace() << "* filter="; dbg.nospace() << m_filter; dbg.nospace() << ",\n"; dbg.nospace() << "* sorting="; dbg.nospace() << m_sorting; dbg.nospace() << ",\n"; dbg.nospace() << "* startDate="; dbg.nospace() << m_startDate; dbg.nospace() << ",\n"; dbg.nospace() << "* endDate="; dbg.nospace() << m_endDate; dbg.nospace() << ",\n"; dbg.nospace() << "* fetchHint="; dbg.nospace() << m_fetchHint; dbg.nospace() << "\n)"; return dbg.maybeSpace(); } #endif QOrganizerItemFilter m_filter; QList m_sorting; QOrganizerItemFetchHint m_fetchHint; QList m_organizeritems; QDateTime m_startDate; QDateTime m_endDate; }; class QOrganizerItemFetchByIdRequestPrivate : public QOrganizerAbstractRequestPrivate { public: QOrganizerItemFetchByIdRequestPrivate() : QOrganizerAbstractRequestPrivate(QOrganizerAbstractRequest::ItemFetchByIdRequest) { } ~QOrganizerItemFetchByIdRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerItemFetchByIdRequest(\n"; dbg.nospace() << "* items="; dbg.nospace() << m_items; dbg.nospace() << ",\n"; dbg.nospace() << "* ids="; dbg.nospace() << m_ids; dbg.nospace() << ",\n"; dbg.nospace() << "* fetchHint="; dbg.nospace() << m_fetchHint; dbg.nospace() << ",\n"; dbg.nospace() << "* errorMap="; dbg.nospace() << m_errors; dbg.nospace() << "\n)"; return dbg.maybeSpace(); } #endif QList m_ids; QOrganizerItemFetchHint m_fetchHint; QList m_items; QMap m_errors; }; class QOrganizerItemOccurrenceFetchRequestPrivate : public QOrganizerAbstractRequestPrivate { public: QOrganizerItemOccurrenceFetchRequestPrivate() : QOrganizerAbstractRequestPrivate(QOrganizerAbstractRequest::ItemOccurrenceFetchRequest) , m_maxOccurrences(-1) { } ~QOrganizerItemOccurrenceFetchRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerItemOccurrenceFetchRequest(\n"; dbg.nospace() << "* itemOccurrences="; dbg.nospace() << m_organizeritems; dbg.nospace() << ",\n"; dbg.nospace() << "* parentItem="; dbg.nospace() << m_generator; dbg.nospace() << ",\n"; dbg.nospace() << "* startDate="; dbg.nospace() << m_startDate; dbg.nospace() << ",\n"; dbg.nospace() << "* endDate="; dbg.nospace() << m_endDate; dbg.nospace() << ",\n"; dbg.nospace() << "* fetchHint="; dbg.nospace() << m_fetchHint; dbg.nospace() << ",\n"; dbg.nospace() << "* maxOccurrences="; dbg.nospace() << m_maxOccurrences; dbg.nospace() << "\n)"; return dbg.maybeSpace(); } #endif QOrganizerItem m_generator; QDateTime m_startDate; QDateTime m_endDate; int m_maxOccurrences; QOrganizerItemFetchHint m_fetchHint; QList m_organizeritems; }; class QOrganizerItemRemoveRequestPrivate : public QOrganizerAbstractRequestPrivate { public: QOrganizerItemRemoveRequestPrivate() : QOrganizerAbstractRequestPrivate(QOrganizerAbstractRequest::ItemRemoveRequest) { } ~QOrganizerItemRemoveRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerItemRemoveRequest("; dbg.nospace() << "items="; dbg.nospace() << m_organizeritems; dbg.nospace() << ","; dbg.nospace() << "errorMap="; dbg.nospace() << m_errors; dbg.nospace() << "\n)"; return dbg.maybeSpace(); } #endif QList m_organizeritems; QMap m_errors; }; class QOrganizerItemRemoveByIdRequestPrivate : public QOrganizerAbstractRequestPrivate { public: QOrganizerItemRemoveByIdRequestPrivate() : QOrganizerAbstractRequestPrivate(QOrganizerAbstractRequest::ItemRemoveByIdRequest) { } ~QOrganizerItemRemoveByIdRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QOrganizerItemRemoveByIdRequest("; dbg.nospace() << "itemIds="; dbg.nospace() << m_organizeritemIds; dbg.nospace() << ","; dbg.nospace() << "errorMap="; dbg.nospace() << m_errors; dbg.nospace() << "\n)"; return dbg.maybeSpace(); } #endif QList m_organizeritemIds; QMap m_errors; }; class QOrganizerItemIdFetchRequestPrivate : public QOrganizerAbstractRequestPrivate { public: QOrganizerItemIdFetchRequestPrivate() : QOrganizerAbstractRequestPrivate(QOrganizerAbstractRequest::ItemIdFetchRequest) { } ~QOrganizerItemIdFetchRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerItemIdFetchRequest(\n"; dbg.nospace() << "* itemIds="; dbg.nospace() << m_ids; dbg.nospace() << ",\n"; dbg.nospace() << "* filter="; dbg.nospace() << m_filter; dbg.nospace() << ",\n"; dbg.nospace() << "* sorting="; dbg.nospace() << m_sorting; dbg.nospace() << ",\n"; dbg.nospace() << "* startDate="; dbg.nospace() << m_startDate; dbg.nospace() << ",\n"; dbg.nospace() << "* endDate="; dbg.nospace() << m_endDate; dbg.nospace() << "\n)"; return dbg.maybeSpace(); } #endif QOrganizerItemFilter m_filter; QList m_sorting; QList m_ids; QDateTime m_startDate; QDateTime m_endDate; }; class QOrganizerCollectionFetchRequestPrivate : public QOrganizerAbstractRequestPrivate { public: QOrganizerCollectionFetchRequestPrivate() : QOrganizerAbstractRequestPrivate(QOrganizerAbstractRequest::CollectionFetchRequest) { } ~QOrganizerCollectionFetchRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerCollectionFetchRequest("; dbg.nospace() << "collections="; dbg.nospace() << m_collections; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif QList m_collections; }; class QOrganizerCollectionRemoveRequestPrivate : public QOrganizerAbstractRequestPrivate { public: QOrganizerCollectionRemoveRequestPrivate() : QOrganizerAbstractRequestPrivate(QOrganizerAbstractRequest::CollectionRemoveRequest) { } ~QOrganizerCollectionRemoveRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerCollectionRemoveRequest("; dbg.nospace() << "collectionIds="; dbg.nospace() << m_collectionIds; dbg.nospace() << ","; dbg.nospace() << "errorMap="; dbg.nospace() << m_errors; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif QList m_collectionIds; QMap m_errors; }; class QOrganizerCollectionSaveRequestPrivate : public QOrganizerAbstractRequestPrivate { public: QOrganizerCollectionSaveRequestPrivate() : QOrganizerAbstractRequestPrivate(QOrganizerAbstractRequest::CollectionSaveRequest) { } ~QOrganizerCollectionSaveRequestPrivate() { } #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerCollectionSaveRequest("; dbg.nospace() << "collections="; dbg.nospace() << m_collections; dbg.nospace() << ","; dbg.nospace() << "errorMap="; dbg.nospace() << m_errors; dbg.nospace() << ")"; return dbg.maybeSpace(); } #endif QList m_collections; QMap m_errors; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMREQUESTS_P_H src/organizer/requests/qorganizeritemsaverequest.cpp000066400000000000000000000133471233466112000235620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemsaverequest.h" #include "qorganizeritemrequests_p.h" QT_BEGIN_NAMESPACE_ORGANIZER /*! \class QOrganizerItemSaveRequest \brief The QOrganizerItemSaveRequest class allows a client to asynchronously request that certain organizer items be saved to a backend. \inmodule QtOrganizer \ingroup organizer-requests */ /*! Constructs a new organizer item save request whose parent is the specified \a parent. */ QOrganizerItemSaveRequest::QOrganizerItemSaveRequest(QObject *parent) : QOrganizerAbstractRequest(new QOrganizerItemSaveRequestPrivate, parent) { } /*! Frees memory in use by this request. */ QOrganizerItemSaveRequest::~QOrganizerItemSaveRequest() { } /*! Sets the organizer item to be saved to \a item. Equivalent to calling: \code setItems(QList() << item); \endcode */ void QOrganizerItemSaveRequest::setItem(const QOrganizerItem &item) { Q_D(QOrganizerItemSaveRequest); QMutexLocker ml(&d->m_mutex); d->m_organizeritems.clear(); d->m_organizeritems.append(item); } /*! Sets the list of organizer items to be saved to \a items. */ void QOrganizerItemSaveRequest::setItems(const QList &items) { Q_D(QOrganizerItemSaveRequest); QMutexLocker ml(&d->m_mutex); d->m_organizeritems = items; } /*! Returns the list of organizer items which will be saved if called prior to calling start(), otherwise returns the list of organizer items as they were saved in the organizer item store. */ QList QOrganizerItemSaveRequest::items() const { Q_D(const QOrganizerItemSaveRequest); QMutexLocker ml(&d->m_mutex); return d->m_organizeritems; } /*! Returns the map of input definition list indices to errors which occurred. */ QMap QOrganizerItemSaveRequest::errorMap() const { Q_D(const QOrganizerItemSaveRequest); QMutexLocker ml(&d->m_mutex); return d->m_errors; } /*! Set the list of detail types to restrict saving to \a detailMask. This allows you to perform partial save (and remove) operations on existing items. If \a detailMask is empty (the default), no restrictions will apply, and the passed in items will be saved as is. Otherwise, only details whose types are in the list will be saved. If a detail type is present in the list, but there are no corresponding details in the item passed into this request, any existing details in the manager for that item will be removed. This is useful if you've used a fetch hint to fetch a partial item from a manager so that you can save changes to the details you actually fetched without removing the details you didn't. Additionally, when performing synchronization operations with other managers that don't support the full range of details, you can restrict the update operation to only those details so that you don't lose the extra details that are supported in this manager. \note Some managers do not support partial updates natively, in which case the QtOrganizer framework will emulate the functionality (fetching the whole item, applying the new restricted details, and saving the item back). */ void QOrganizerItemSaveRequest::setDetailMask(const QList &detailMask) { Q_D(QOrganizerItemSaveRequest); QMutexLocker ml(&d->m_mutex); d->m_detailMask = detailMask; } /*! Returns the list of definitions that this request will operate on. If the list is empty, the request will operate on all details. */ QList QOrganizerItemSaveRequest::detailMask() const { Q_D(const QOrganizerItemSaveRequest); QMutexLocker ml(&d->m_mutex); return d->m_detailMask; } #include "moc_qorganizeritemsaverequest.cpp" QT_END_NAMESPACE_ORGANIZER src/organizer/requests/qorganizeritemsaverequest.h000066400000000000000000000060321233466112000232200ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMSAVEREQUEST_H #define QORGANIZERITEMSAVEREQUEST_H #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemSaveRequestPrivate; /* Leaf class */ class Q_ORGANIZER_EXPORT QOrganizerItemSaveRequest : public QOrganizerAbstractRequest { Q_OBJECT public: QOrganizerItemSaveRequest(QObject *parent = 0); ~QOrganizerItemSaveRequest(); void setItem(const QOrganizerItem &item); void setItems(const QList &items); QList items() const; void setDetailMask(const QList &detailMask); QList detailMask() const; QMap errorMap() const; private: Q_DISABLE_COPY(QOrganizerItemSaveRequest) friend class QOrganizerManagerEngine; Q_DECLARE_PRIVATE_D(d_ptr, QOrganizerItemSaveRequest) }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERITEMSAVEREQUEST_H src/organizer/requests/requests.pri000066400000000000000000000023301233466112000201030ustar00rootroot00000000000000INCLUDEPATH += requests \ ./ PUBLIC_HEADERS += requests/qorganizeritemrequests.h \ requests/qorganizercollectionfetchrequest.h \ requests/qorganizercollectionremoverequest.h \ requests/qorganizercollectionsaverequest.h \ requests/qorganizeritemfetchrequest.h \ requests/qorganizeritemfetchforexportrequest.h \ requests/qorganizeritemfetchbyidrequest.h \ requests/qorganizeritemoccurrencefetchrequest.h \ requests/qorganizeritemidfetchrequest.h \ requests/qorganizeritemremoverequest.h \ requests/qorganizeritemsaverequest.h \ requests/qorganizeritemremovebyidrequest.h PRIVATE_HEADERS += requests/qorganizeritemrequests_p.h SOURCES += \ requests/qorganizercollectionfetchrequest.cpp \ requests/qorganizercollectionremoverequest.cpp \ requests/qorganizercollectionsaverequest.cpp \ requests/qorganizeritemfetchrequest.cpp \ requests/qorganizeritemfetchforexportrequest.cpp \ requests/qorganizeritemfetchbyidrequest.cpp \ requests/qorganizeritemoccurrencefetchrequest.cpp \ requests/qorganizeritemidfetchrequest.cpp \ requests/qorganizeritemremoverequest.cpp \ requests/qorganizeritemsaverequest.cpp \ requests/qorganizeritemremovebyidrequest.cpp src/plugins/000077500000000000000000000000001233466112000133245ustar00rootroot00000000000000src/plugins/contacts/000077500000000000000000000000001233466112000151425ustar00rootroot00000000000000src/plugins/contacts/contacts.pro000066400000000000000000000002621233466112000175020ustar00rootroot00000000000000TEMPLATE = subdirs CONFIG += ordered qtHaveModule(jsondb): SUBDIRS += jsondb SUBDIRS += memory #contains(mobility_modules,serviceframework): SUBDIRS += serviceactionmanager src/plugins/contacts/jsondb/000077500000000000000000000000001233466112000164215ustar00rootroot00000000000000src/plugins/contacts/jsondb/jsondb.json000066400000000000000000000000351233466112000205710ustar00rootroot00000000000000{ "Keys": [ "jsondb" ] } src/plugins/contacts/jsondb/jsondb.pro000066400000000000000000000011661233466112000204260ustar00rootroot00000000000000TARGET = qtcontacts_jsondb QT += contacts-private jsondb PLUGIN_TYPE = contacts load(qt_plugin) HEADERS += qcontactjsondbenginefactory.h \ qcontactjsondbengine.h \ qcontactjsondbrequesthandler.h \ qcontactjsondbrequestmanager.h \ qcontactjsondbconverter.h \ qcontactjsondbglobal.h \ qcontactjsondbid.h \ qcontactjsondbstring.h SOURCES += qcontactjsondbenginefactory.cpp \ qcontactjsondbengine.cpp \ qcontactjsondbrequesthandler.cpp \ qcontactjsondbrequestmanager.cpp \ qcontactjsondbconverter.cpp \ qcontactjsondbid.cpp \ qcontactjsondbstring.cpp OTHER_FILES += jsondb.json src/plugins/contacts/jsondb/qcontactjsondbconverter.cpp000066400000000000000000002053301233466112000240740ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactjsondbconverter.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 "qcontactjsondbglobal.h" #include "qcontactjsondbid.h" #include "qcontactjsondbstring.h" QT_BEGIN_NAMESPACE_CONTACTS const int QContactJsonDbConverter::jsonDbVersionLength(10); QContactJsonDbConverter::QContactJsonDbConverter() { initializeMappings(); } QContactJsonDbConverter::~QContactJsonDbConverter() { } bool QContactJsonDbConverter::toQContact(const QJsonObject& object, QContact* contact, const QString &partitionName) { QJsonObject temporaryJsonObject; QString stringValue; stringValue = object.value(QContactJsonDbStr::uuid()).toString(); if (stringValue.isEmpty()) { return false; } QContactAbstractRequest::StorageLocation storageLocation; storageLocation = storageLocationMapping.key(partitionName); contact->setId(QContactId(new QContactJsonDbId(stringValue, storageLocation))); // TODO: other types contact->setType(QContactType::TypeContact); // Go through all fields in loop. QJsonObject::ConstIterator i = object.constBegin(); while (i != object.constEnd()) { if (i.key() == QContactJsonDbStr::version()) { //version QContactVersion contactVersion; jsonDbVersionToContactVersion(i.value().toString(), &contactVersion); contact->appendDetail(contactVersion); } else if (i.key() == detailsToJsonMapping.value(QContactName::Type)) { //name QContactName name; temporaryJsonObject = i.value().toObject(); QHash::ConstIterator nameFieldsIterator = contactNameFieldsMapping.constBegin(); while (nameFieldsIterator != contactNameFieldsMapping.constEnd()) { stringValue = temporaryJsonObject.value(nameFieldsIterator.value()).toString(); if (!stringValue.isEmpty()) { switch (sanitizeContactDetailString(&stringValue)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": Name field of json object does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: name.setValue(nameFieldsIterator.key(), stringValue); break; case QContactJsonDbConverter::EmptyArgumentError: break; } } nameFieldsIterator++; } if (!name.isEmpty()) contact->appendDetail(name); } else if (i.key() == detailsToJsonMapping.value(QContactGender::Type)) { //gender QContactGender gender; temporaryJsonObject = i.value().toObject(); stringValue = temporaryJsonObject.value(detailsToJsonMapping.value(QContactGender::Type)).toString(); if (!stringValue.isEmpty()) gender.setGender(static_cast(genderValuesMapping.key(stringValue))); if (!gender.isEmpty()) contact->appendDetail(gender); } else if (i.key() == detailsToJsonMapping.value(QContactOrganization::Type)) { //organization QJsonArray array = i.value().toArray(); for (int i = 0; i < array.size(); ++i) { QContactOrganization organization; QJsonObject temporaryJsonObject = array.at(i).toObject(); QHash::ConstIterator organizationIter = organizationFieldsMapping.constBegin(); while (organizationIter != organizationFieldsMapping.constEnd()) { stringValue = temporaryJsonObject.value(organizationIter.value()).toString(); if (!stringValue.isEmpty()) { switch (sanitizeContactDetailString(&stringValue)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": Organization field of json object does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: organization.setValue(organizationIter.key(), stringValue); break; case QContactJsonDbConverter::EmptyArgumentError: break; } } organizationIter++; } // logoUrl stringValue = temporaryJsonObject.value(organizationFieldsMapping.value(QContactOrganization::FieldLogoUrl)).toString(); if (!stringValue.isEmpty()) organization.setLogoUrl(QUrl(stringValue)); // Add organization to details if (!organization.isEmpty()) contact->appendDetail(organization); } } else if (i.key() == QContactJsonDbStr::contactDetails()) { temporaryJsonObject = i.value().toObject(); //birthday QString dateString; dateString = temporaryJsonObject[detailsToJsonMapping.value(QContactBirthday::Type)].toString(); if (!dateString.isEmpty()) { QDateTime date = toQDateTime(dateString); QContactBirthday birthday; birthday.setDateTime(date); contact->appendDetail(birthday); } //avatar QString avatarUrlString; avatarUrlString = temporaryJsonObject[detailsToJsonMapping.value(QContactAvatar::Type)].toString(); if (!avatarUrlString.isEmpty()) { QUrl avatarUrl(avatarUrlString); QContactAvatar avatar; avatar.setImageUrl(avatarUrl); contact->appendDetail(avatar); } //ringtone QString ringtoneUrlString; ringtoneUrlString = temporaryJsonObject[detailsToJsonMapping.value(QContactRingtone::Type)].toString(); if (!ringtoneUrlString.isEmpty()) { QUrl ringtoneUrl(ringtoneUrlString); QContactRingtone ringtone; ringtone.setAudioRingtoneUrl(ringtoneUrl); contact->appendDetail(ringtone); } //nickname QContactNickname nick; if (!temporaryJsonObject[detailsToJsonMapping.value(QContactNickname::Type)].toString().isEmpty()) { QString nickString = temporaryJsonObject[detailsToJsonMapping.value(QContactNickname::Type)].toString(); switch (sanitizeContactDetailString(&nickString)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": Nickname field of json object does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: nick.setNickname(nickString); break; case QContactJsonDbConverter::EmptyArgumentError: break; } } if (!nick.isEmpty()) contact->appendDetail(nick); //displayLabel QString displayLabelString; displayLabelString = temporaryJsonObject[detailsToJsonMapping.value(QContactDisplayLabel::Type)].toString(); if (!displayLabelString.isEmpty()) { QContactDisplayLabel label; label.setLabel(displayLabelString); contact->appendDetail(label); } //note QContactNote note; if (!temporaryJsonObject[detailsToJsonMapping.value(QContactNote::Type)].toString().isEmpty()) { QString noteString = temporaryJsonObject[detailsToJsonMapping.value(QContactNote::Type)].toString(); switch (sanitizeContactDetailString(¬eString, 1000)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": Note field of json object does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: note.setNote(noteString); break; case QContactJsonDbConverter::EmptyArgumentError: break; } } if (!note.isEmpty()) { contact->appendDetail(note); } } else if (i.key() == detailsToJsonMapping.value(QContactEmailAddress::Type)) { //email QJsonArray array = i.value().toArray(); for (int i = 0; i < array.size(); ++i) { QContactEmailAddress email; QJsonObject temporaryJsonObject = array[i].toObject(); if (!temporaryJsonObject["value"].toString().isEmpty()) { QString emailString = temporaryJsonObject["value"].toString(); switch (sanitizeContactDetailString(&emailString, 126)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": email field of json object does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: email.setEmailAddress(emailString); break; case QContactJsonDbConverter::EmptyArgumentError: break; } } if (!email.isEmpty()) { updateContexts(temporaryJsonObject,&email); contact->appendDetail(email); } } } else if (i.key() == detailsToJsonMapping.value(QContactPhoneNumber::Type)) { //phone number QJsonArray array = i.value().toArray(); for (int i = 0; i < array.size(); ++i) { QJsonObject temporaryJsonObject = array[i].toObject(); stringValue = temporaryJsonObject.value("value").toString(); if (stringValue.isEmpty()) { qWarning() << Q_FUNC_INFO <<": ignoring phone number field of json object " << object << "because it is empty."; continue; } if (sanitizePhoneNumberString(&stringValue)) { QContactPhoneNumber number; number.setNumber(stringValue); stringValue = temporaryJsonObject["context"].toString(); if (stringValue == QContactJsonDbStr::contextHome() || stringValue == QContactJsonDbStr::contextWork() || stringValue == QContactJsonDbStr::contextOther()) { updateContexts(temporaryJsonObject, &number); } stringValue = temporaryJsonObject["subType"].toString(); if (stringValue == QContactJsonDbStr::subTypeFax()) { QList myType; myType << QContactPhoneNumber::SubTypeFax; number.setSubTypes(myType); } else if (stringValue == QContactJsonDbStr::subTypeCell()) { QList myType; myType << QContactPhoneNumber::SubTypeMobile; number.setSubTypes(myType); } else if (stringValue == QContactJsonDbStr::subTypeVideo()) { QList myType; myType << QContactPhoneNumber::SubTypeVideo; number.setSubTypes(myType); } else if (stringValue == QContactJsonDbStr::subTypeLandline()) { QList myType; myType << QContactPhoneNumber::SubTypeLandline; number.setSubTypes(myType); }; contact->appendDetail(number); } else { qWarning() << Q_FUNC_INFO <<":Number field of json object does not contain a valid " << " jsondb phone number."; return false; } } } else if (i.key() == detailsToJsonMapping.value(QContactAddress::Type)) { //address QJsonArray array = i.value().toArray(); for (int j = 0; j < array.size(); ++j) { QContactAddress address; QJsonObject temporaryJsonObject = array.at(j).toObject(); QHash::ConstIterator addressIter = addressFieldsMapping.constBegin(); while (addressIter != addressFieldsMapping.constEnd()) { stringValue = temporaryJsonObject.value(addressIter.value()).toString(); if (!stringValue.isEmpty()) { switch (sanitizeContactDetailString(&stringValue)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": address field of json object does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: address.setValue(addressIter.key(), stringValue); break; case QContactJsonDbConverter::EmptyArgumentError: break; } } addressIter++; } if (!address.isEmpty()) { updateContexts(temporaryJsonObject, &address); contact->appendDetail(address); } } } else if (i.key() == detailsToJsonMapping.value(QContactUrl::Type)) { //url QJsonArray array = i.value().toArray(); for (int i = 0; i < array.size(); ++i) { QContactUrl url; QJsonObject temporaryJsonObject = array[i].toObject(); url.setUrl(temporaryJsonObject["value"].toString()); if (updateContexts(temporaryJsonObject, &url)) url.setSubType(static_cast(url.contexts().first()));//TODO decide if we use "Context" or "SubTypes" to store the jsondb SubTypes contact->appendDetail(url); } } else if (i.key() == detailsToJsonMapping.value(QContactSyncTarget::Type)) { stringValue = object.value(detailsToJsonMapping.value(QContactSyncTarget::Type)).toString(); if (!stringValue.isEmpty()) { QContactSyncTarget syncTarget; syncTarget.setSyncTarget(stringValue); contact->appendDetail(syncTarget); } } else if (i.key() == detailsToJsonMapping.value(QContactGuid::Type)) { stringValue = object.value(detailsToJsonMapping.value(QContactGuid::Type)).toString(); if (!stringValue.isEmpty()) { QContactGuid guid; guid.setGuid(stringValue); contact->appendDetail(guid); } } else if (i.key().at(0) == QChar('_')) { // skip as it's used internally } else { // we map anything else to extended details QContactExtendedDetail extendedDetail; extendedDetail.setName(i.key()); extendedDetail.setData(i.value().toVariant()); contact->appendDetail(extendedDetail); } ++i; } if (contact->isEmpty()) { return false; } else { return true; }; } bool QContactJsonDbConverter::toJsonContact(QJsonObject* object, const QContact& contact, const QList &detailMask) { QList details = contact.details(); QContactDetail detail; QContactName* name; QContactNickname* nick; QContactDisplayLabel *label; QContactEmailAddress* email; QContactPhoneNumber* number; QContactAddress* address; QContactUrl* url; QContactVersion* version; QContactOrganization* organization; QContactBirthday* birthday; QContactAvatar* avatar; QContactRingtone* ringtone; QContactNote* note; QContactGender* gender; QContactExtendedDetail* extendedDetail; QContactSyncTarget* syncTarget; QContactGuid* guid; QJsonArray phoneNumbers; QJsonArray emails; QJsonArray urls; QJsonArray organizations; QJsonArray addresses; QJsonObject embeddedDetailsObject; if (!contact.id().isNull()) object->insert (QContactJsonDbStr::uuid(), contactIdToUuid(contact.id())); // get all available contact details. object->insert(QContactJsonDbStr::type(), QContactJsonDbStr::contactsJsonDbType()); if (!object->empty()) { if (detailMask.empty()) { // Quickfix for preserving possible extra fields in jsondb contact. // Wipe QContact fields that may be empty/deleted, preserve all other data. object->remove(detailsToJsonMapping.value(QContactName::Type)); object->remove(detailsToJsonMapping.value(QContactGender::Type)); object->remove(detailsToJsonMapping.value(QContactOrganization::Type)); object->remove(detailsToJsonMapping.value(QContactEmailAddress::Type)); object->remove(detailsToJsonMapping.value(QContactPhoneNumber::Type)); object->remove(detailsToJsonMapping.value(QContactAddress::Type)); object->remove(detailsToJsonMapping.value(QContactUrl::Type)); embeddedDetailsObject = object->value(QContactJsonDbStr::contactDetails()).toObject(); object->remove(QContactJsonDbStr::contactDetails()); embeddedDetailsObject.remove(detailsToJsonMapping.value(QContactBirthday::Type)); embeddedDetailsObject.remove(detailsToJsonMapping.value(QContactAvatar::Type)); embeddedDetailsObject.remove(detailsToJsonMapping.value(QContactRingtone::Type)); embeddedDetailsObject.remove(detailsToJsonMapping.value(QContactNickname::Type)); embeddedDetailsObject.remove(detailsToJsonMapping.value(QContactNote::Type)); embeddedDetailsObject.remove(detailsToJsonMapping.value(QContactDisplayLabel::Type)); // Preseserve possible extra contact details jsondb contact object may have. if (!embeddedDetailsObject.isEmpty()) { object->insert(QContactJsonDbStr::contactDetails(), embeddedDetailsObject); } // End of Quickfix } else { // Remove masked details from the object as they will be updated later // with a new value if one exists in the in given QContact. // If a new value does not exists in the QContact the detail stays removed. foreach (const QContactDetail::DetailType &type, detailMask) { // For now, try to remove from both though each type is in one object only. object->remove(detailsToJsonMapping.value(type)); embeddedDetailsObject.remove(detailsToJsonMapping.value(type)); } } } for(int i = 0; i < details.size(); ++i) { detail = details.at(i); // If it is partial save and the detail is not in the mask we keep the original detail. if (!detailMask.isEmpty() && !detailMask.contains(detail.type())) continue; switch (detail.type()) { case QContactDetail::TypeName: { QJsonObject nameObject; name = static_cast(&detail); QMap::const_iterator nameFieldsIterator = name->values().constBegin(); QString name_field; while (nameFieldsIterator != name->values().constEnd()) { if (nameFieldsIterator.value().type() == QVariant::String) { name_field = nameFieldsIterator.value().toString(); switch (sanitizeContactDetailString(&name_field)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": Name detail does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: nameObject[contactNameFieldsMapping.value(nameFieldsIterator.key())] = name_field; break; case QContactJsonDbConverter::EmptyArgumentError: break; } } nameFieldsIterator++; } if (!nameObject.isEmpty()) object->insert(detailsToJsonMapping.value(QContactName::Type), nameObject); break; } case QContactDetail::TypeGender: { QJsonObject genderObject; gender = static_cast(&detail); genderObject[detailsToJsonMapping.value(QContactGender::Type)] = genderValuesMapping.value(gender->gender()); object->insert(detailsToJsonMapping.value(QContactGender::Type), genderObject); break; } case QContactDetail::TypeOrganization: { QJsonObject jsonObject; organization = static_cast(&detail); QMap::const_iterator organizationIter = organization->values().constBegin(); QString organization_field; organization_field = organizationIter.value().toString(); switch (sanitizeContactDetailString(&organization_field)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": Name field of organization detail does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: jsonObject[organizationFieldsMapping.value(QContactOrganization::FieldName)] = organization_field; break; case QContactJsonDbConverter::EmptyArgumentError: break; } // for (int i=0; idepartment().count();i++) { organization_field = organization->department().at(i); // .join("") switch (sanitizeContactDetailString(&organization_field)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": Department field of organization detail does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: jsonObject[organizationFieldsMapping.value(QContactOrganization::FieldDepartment)] = organization_field; break; case QContactJsonDbConverter::EmptyArgumentError: break; } } // organization_field = organization->title(); switch (sanitizeContactDetailString(&organization_field)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": Title field of organization detail does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: jsonObject[organizationFieldsMapping.value(QContactOrganization::FieldTitle)] = organization_field; break; case QContactJsonDbConverter::EmptyArgumentError: break; } // organization_field = organization->role(); switch (sanitizeContactDetailString(&organization_field)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": Role field of organization detail does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: jsonObject[organizationFieldsMapping.value(QContactOrganization::FieldRole)] = organization_field; break; case QContactJsonDbConverter::EmptyArgumentError: break; } // organization_field = organization->assistantName(); switch (sanitizeContactDetailString(&organization_field)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": AssistantName field of organization detail does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: jsonObject[organizationFieldsMapping.value(QContactOrganization::FieldAssistantName)] = organization_field; break; case QContactJsonDbConverter::EmptyArgumentError: break; } // organization_field = organization->location(); switch (sanitizeContactDetailString(&organization_field)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": Location field of organization detail does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: jsonObject[organizationFieldsMapping.value(QContactOrganization::FieldLocation)] = organization_field; break; case QContactJsonDbConverter::EmptyArgumentError: break; } jsonObject[organizationFieldsMapping.value(QContactOrganization::FieldLogoUrl)] = organization->logoUrl().toString(); if (!jsonObject.isEmpty()) { updateContexts(*organization, &jsonObject); organizations.append(jsonObject); break; } } case QContactDetail::TypeBirthday: { birthday = static_cast(&detail); QDateTime date = birthday->dateTime(); QString dateString = toJsonDbDate(date); embeddedDetailsObject[detailsToJsonMapping.value(QContactBirthday::Type)] = dateString; object->insert(QContactJsonDbStr::contactDetails(), embeddedDetailsObject); break; } case QContactDetail::TypeAvatar: { avatar = static_cast(&detail); embeddedDetailsObject[detailsToJsonMapping.value(QContactAvatar::Type)] = avatar->imageUrl().toString(); object->insert(QContactJsonDbStr::contactDetails(), embeddedDetailsObject); break; } case QContactDetail::TypeRingtone: { ringtone = static_cast(&detail); embeddedDetailsObject[detailsToJsonMapping.value(QContactRingtone::Type)] = ringtone->audioRingtoneUrl().toString(); object->insert(QContactJsonDbStr::contactDetails(), embeddedDetailsObject); break; } case QContactDetail::TypeNickname: { nick = static_cast(&detail); QString nickname_field = nick->nickname(); switch (sanitizeContactDetailString(&nickname_field)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": nick detail does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: embeddedDetailsObject[detailsToJsonMapping.value(QContactNickname::Type)] = nickname_field; break; case QContactJsonDbConverter::EmptyArgumentError: break; } if (!embeddedDetailsObject.isEmpty()) object->insert(QContactJsonDbStr::contactDetails(), embeddedDetailsObject); break; } case QContactDetail::TypeDisplayLabel: { label = static_cast(&detail); embeddedDetailsObject[detailsToJsonMapping.value(QContactDisplayLabel::Type)] = label->label(); object->insert(QContactJsonDbStr::contactDetails(), embeddedDetailsObject); break; } case QContactDetail::TypeNote: { note = static_cast(&detail); QString note_field = note->note(); switch (sanitizeContactDetailString(¬e_field, 1000)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": note detail does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: embeddedDetailsObject[detailsToJsonMapping.value(QContactNote::Type)] = note_field; break; case QContactJsonDbConverter::EmptyArgumentError: break; } if (!embeddedDetailsObject.isEmpty()) object->insert(QContactJsonDbStr::contactDetails(), embeddedDetailsObject); break; } case QContactDetail::TypeEmailAddress: { QJsonObject emailObject; email = static_cast(&detail); QString email_field = email->emailAddress(); switch (sanitizeContactDetailString(&email_field, 126)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": email detail does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: emailObject["value"] = email_field; break; case QContactJsonDbConverter::EmptyArgumentError: break; } if (!emailObject.isEmpty()) { updateContexts(*email, &emailObject); emails.append(emailObject); } break; } case QContactDetail::TypePhoneNumber: { QJsonObject phoneObject; number = static_cast(&detail); QString phoneString (number->number()); if (phoneString.isEmpty()) { qWarning() << Q_FUNC_INFO <<": Ignoring number field of detail " << detail << "because it is empty. "; break; } if (sanitizePhoneNumberString(&phoneString)) { phoneObject["value"] = phoneString; updateContexts(*number, &phoneObject); QList subTypes = number->subTypes(); if (!subTypes.empty()) phoneObject["subType"] = phoneNumbersSubtypesMapping.value(number->subTypes().first()); phoneNumbers.append(phoneObject); } else { qWarning() << Q_FUNC_INFO <<": Number field of detail " << detail << "does not contain a valid " << " jsondb phone number."; return false; } break; } case QContactDetail::TypeAddress: { QJsonObject addressObject; address = static_cast(&detail); QMap::const_iterator addressIter = address->values().constBegin(); QString address_field; address_field = addressIter.value().toString(); switch (sanitizeContactDetailString(&address_field)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": Street field of address detail does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: addressObject[addressFieldsMapping.value(QContactAddress::FieldStreet)] = address_field; break; case QContactJsonDbConverter::EmptyArgumentError: break; } // address_field = address->locality(); switch (sanitizeContactDetailString(&address_field)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": Locality field of address detail does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: addressObject[addressFieldsMapping.value(QContactAddress::FieldLocality)] = address_field; break; case QContactJsonDbConverter::EmptyArgumentError: break; } // address_field = address->postcode(); switch (sanitizeContactDetailString(&address_field)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": PostCode field of address detail does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: addressObject[addressFieldsMapping.value(QContactAddress::FieldPostcode)] = address_field; break; case QContactJsonDbConverter::EmptyArgumentError: break; } // address_field = address->postOfficeBox(); switch (sanitizeContactDetailString(&address_field)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": PostOfficeBox field of address detail does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: addressObject[addressFieldsMapping.value(QContactAddress::FieldPostOfficeBox)] = address_field; break; case QContactJsonDbConverter::EmptyArgumentError: break; } // address_field = address->region(); switch (sanitizeContactDetailString(&address_field)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": Region field of address detail does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: addressObject[addressFieldsMapping.value(QContactAddress::FieldRegion)] = address_field; break; case QContactJsonDbConverter::EmptyArgumentError: break; } // address_field = address->country(); switch (sanitizeContactDetailString(&address_field)) { case QContactJsonDbConverter::InvalidArgumentError: qWarning() << Q_FUNC_INFO <<": Country field of address detail does not contain a valid jsondb detail"; return false; break; case QContactJsonDbConverter::NoError: addressObject[addressFieldsMapping.value(QContactAddress::FieldCountry)] = address_field; break; case QContactJsonDbConverter::EmptyArgumentError: break; } // if (!addressObject.isEmpty()) { updateContexts(*address, &addressObject); addresses.append(addressObject); } break; } case QContactDetail::TypeUrl: { QJsonObject urlObject; url = static_cast(&detail); urlObject["value"] = url->url(); updateContexts(*url, &urlObject); urls.append(urlObject); break; } case QContactDetail::TypeVersion: { version = static_cast(&detail); QString jsonDbVersion; contactVersionToJsonDbVersion(*version, &jsonDbVersion); if (!jsonDbVersion.isEmpty()) object->insert(QContactJsonDbStr::version(), jsonDbVersion); break; } case QContactDetail::TypeExtendedDetail: { extendedDetail = static_cast(&detail); QString extDetailName = extendedDetail->name(); if ( (!extDetailName.isEmpty()) && ((extDetailName.at(0) != QChar('_'))) ) { QJsonValue property = QJsonValue::fromVariant(details.at(i).value(QContactExtendedDetail::FieldData)); if (!property.isNull()) object->insert(extDetailName, property); } break; } case QContactDetail::TypeSyncTarget: { syncTarget = static_cast(&detail); if (!syncTarget->syncTarget().isEmpty()) object->insert(QContactJsonDbStr::syncTargetDefinitionName(), syncTarget->syncTarget()); break; } case QContactDetail::TypeGuid: { guid = static_cast(&detail); if (!guid->guid().isEmpty()) object->insert(QContactJsonDbStr::guidDefinitionName(), guid->guid()); break; } case QContactDetail::TypeType: default: break; } } if (!phoneNumbers.isEmpty()) object->insert(detailsToJsonMapping.value(QContactPhoneNumber::Type), phoneNumbers); if (!emails.isEmpty()) object->insert(detailsToJsonMapping.value(QContactEmailAddress::Type), emails); if (!urls.isEmpty()) object->insert(detailsToJsonMapping.value(QContactUrl::Type), urls); if (!organizations.isEmpty()) object->insert(detailsToJsonMapping.value(QContactOrganization::Type), organizations); if (!addresses.isEmpty()) object->insert(detailsToJsonMapping.value(QContactAddress::Type), addresses); return true; } bool QContactJsonDbConverter::updateContexts(const QJsonObject &object, QContactDetail *detail) { if (!detail) return false; QString stringValue = object["context"].toString(); if (stringValue == QContactJsonDbStr::contextHome()) { detail->setContexts(QContactDetail::ContextHome); return true; } else if (stringValue == QContactJsonDbStr::contextWork()) { detail->setContexts(QContactDetail::ContextWork); return true; } else if (stringValue == QContactJsonDbStr::contextOther()) { detail->setContexts(QContactDetail::ContextOther); return true; } return false; } bool QContactJsonDbConverter::updateContexts(const QContactDetail& detail, QJsonObject* object) { QList contexts = detail.contexts(); if(contexts.size() == 0) return false; if(contexts.contains(QContactDetail::ContextHome)) object->insert("context", QContactJsonDbStr::contextHome()); else if(contexts.contains(QContactDetail::ContextWork)) object->insert("context", QContactJsonDbStr::contextWork()); else if(contexts.contains(QContactDetail::ContextOther)) object->insert("context", QContactJsonDbStr::contextOther()); return true; } bool QContactJsonDbConverter::queryFromRequest(QContactAbstractRequest *request,QString &newJsonDbQuery) { bool isValidQuery = false; if (!request) { newJsonDbQuery = ""; return isValidQuery; } newJsonDbQuery = "[?" + QContactJsonDbStr::type() + "=\""+ QContactJsonDbStr::contactsJsonDbType() + "\"]"; switch (request->type()) { case QContactAbstractRequest::ContactSaveRequest: { //TODO: break; } case QContactAbstractRequest::ContactFetchByIdRequest: { QContactFetchByIdRequest* fetchReq = static_cast(request); QString idString; idsToJsondbQuery(fetchReq->contactIds(), idString); newJsonDbQuery.append(idString); isValidQuery = true; break; } case QContactAbstractRequest::ContactFetchRequest: { QContactFetchRequest* fetchReq = static_cast(request); QContactFilter filter = fetchReq->filter(); QString filterString; isValidQuery = compoundFilterToJsondbQuery(filter,filterString); newJsonDbQuery.append(filterString); if (!isValidQuery) return isValidQuery; QList sorting = fetchReq->sorting(); newJsonDbQuery.append(convertSortOrder(sorting)); break; } case QContactAbstractRequest::ContactIdFetchRequest: { newJsonDbQuery.append(QContactJsonDbStr::uuidSelectQuery()); QContactIdFetchRequest* idReq = static_cast(request); QContactFilter filter = idReq->filter(); QString filterString; isValidQuery = compoundFilterToJsondbQuery(filter,filterString); newJsonDbQuery.append(filterString); if (!isValidQuery) return isValidQuery; QList sorting = idReq->sorting(); newJsonDbQuery.append(convertSortOrder(sorting)); break; } default: break; } if (qt_debug_jsondb_contacts()) qDebug() << " JSONDB QUERY: " << newJsonDbQuery; return isValidQuery; } bool QContactJsonDbConverter::compoundFilterToJsondbQuery(const QContactFilter &filter, QString &jsonDbQueryStr) const { bool isValidFilter = true; switch (filter.type()) { case QContactFilter::IntersectionFilter: { const QContactIntersectionFilter isf(filter); const QList filterList = isf.filters(); foreach (const QContactFilter &filter, filterList){ QString filterStr; if (compoundFilterToJsondbQuery(filter, filterStr)) jsonDbQueryStr += filterStr; else //For intersection filter, single filter invalid means empty result from jsondb query isValidFilter = false; } break; } case QContactFilter::UnionFilter: { //not supported yet isValidFilter = false; break; } default: isValidFilter = singleFilterToJsondbQuery(filter, jsonDbQueryStr); break; } if (!isValidFilter) jsonDbQueryStr.clear(); if (qt_debug_jsondb_contacts()) { if (filter.type() == QContactFilter::IntersectionFilter) qDebug()<<"INTERSECTION FILTER PART OF THE QUERY:"<isDigit() || character->toLatin1() == 'p' || character->toLatin1() == 'w' || character->toLatin1() == 'a' || character->toLatin1() == 'b' || character->toLatin1() == 'c' || character->toLatin1() == 'd' || character->toLatin1() == '#' || character->toLatin1() == '*' || character->toLatin1() == '(' || character->toLatin1() == ')' || character->toLatin1() == '-'); } bool QContactJsonDbConverter::idFilterToJsondbQuery(const QContactFilter &filter, QString &newJsonDbQuery) const { QContactIdFilter idFilter(filter); QList ids = idFilter.ids(); if (!ids.isEmpty()) idsToJsondbQuery(ids, newJsonDbQuery); else newJsonDbQuery.append("[?" + QContactJsonDbStr::uuid() + " in []]"); return true; } void QContactJsonDbConverter::idsToJsondbQuery(const QList &ids, QString &newJsonDbQuery) const { if (!ids.isEmpty()) { newJsonDbQuery.append("[?" + QContactJsonDbStr::uuid() + " in ["); foreach (const QContactId &id, ids) { newJsonDbQuery.append("\"" + contactIdToUuid(id) + "\""); newJsonDbQuery.append(","); } newJsonDbQuery.chop(1); newJsonDbQuery.append("]]"); } } QString QContactJsonDbConverter::convertSortOrder(const QList &sortOrders) const { QString newJsonDbQuery; foreach (QContactSortOrder sortOrder, sortOrders) { if (sortOrder.detailType() == QContactName::Type) { sortOrder.direction() == Qt::AscendingOrder ? newJsonDbQuery.append("[/") : newJsonDbQuery.append("[\\"); newJsonDbQuery.append(detailsToJsonMapping.value(QContactName::Type)); if (sortOrder.detailField() == QContactName::FieldFirstName) { newJsonDbQuery.append("." + contactNameFieldsMapping.value(QContactName::FieldFirstName) + "]"); } else if (sortOrder.detailField() == QContactName::FieldLastName) { newJsonDbQuery.append("." + contactNameFieldsMapping.value(QContactName::FieldLastName) + "]"); } } else if (sortOrder.detailType() == QContactEmailAddress::Type) { sortOrder.direction() == Qt::AscendingOrder ? newJsonDbQuery.append("[/") : newJsonDbQuery.append("[\\"); newJsonDbQuery.append(detailsToJsonMapping.value(QContactEmailAddress::Type)); newJsonDbQuery.append("."); // + "0" + "." + "value" + "]"); newJsonDbQuery.append("0"); newJsonDbQuery.append("."); newJsonDbQuery.append("value"); newJsonDbQuery.append("]"); } } if (qt_debug_jsondb_contacts()) qDebug() << "SORTING PART OF THE QUERY: " << newJsonDbQuery; return newJsonDbQuery; } QString QContactJsonDbConverter::contactIdToUuid(const QContactId &id) const { if (id.isNull()) return QString(); const QContactJsonDbId *jsonDbId = static_cast(QContactManagerEngine::engineId(id)); return jsonDbId->uuid().toString(); } QContactId QContactJsonDbConverter::uuidtoContactId(QString &uuid, const QString &partitionName) const { QContactAbstractRequest::StorageLocation storageLocation; storageLocation = storageLocationMapping.key(partitionName); QContactJsonDbId *jsonId = new QContactJsonDbId(uuid, storageLocation); return QContactId(jsonId); } void QContactJsonDbConverter::initializeMappings() { detailsToJsonMapping.insert(QContactName::Type, QContactJsonDbStr::nameDefinitionName()); detailsToJsonMapping.insert(QContactGender::Type, QContactJsonDbStr::genderDefinitionName()); detailsToJsonMapping.insert(QContactPhoneNumber::Type, QContactJsonDbStr::phoneNumberDefinitionName()); detailsToJsonMapping.insert(QContactEmailAddress::Type, QContactJsonDbStr::emailAddressDefinitionName()); detailsToJsonMapping.insert(QContactUrl::Type, QContactJsonDbStr::urlDefinitionName()); detailsToJsonMapping.insert(QContactNickname::Type, QContactJsonDbStr::nicknameDefinitionName()); detailsToJsonMapping.insert(QContactDisplayLabel::Type, QContactJsonDbStr::displayLabelDefinitionName()); detailsToJsonMapping.insert(QContactAddress::Type, QContactJsonDbStr::addressDefinitionName()); detailsToJsonMapping.insert(QContactBirthday::Type, QContactJsonDbStr::birthdayDefinitionName()); detailsToJsonMapping.insert(QContactAvatar::Type, QContactJsonDbStr::avatarDefinitionName()); detailsToJsonMapping.insert(QContactRingtone::Type, QContactJsonDbStr::ringtoneDefinitionName()); detailsToJsonMapping.insert(QContactOrganization::Type,QContactJsonDbStr::organizationDefinitionName()); detailsToJsonMapping.insert(QContactNote::Type, QContactJsonDbStr::noteDefinitionName()); detailsToJsonMapping.insert(QContactSyncTarget::Type, QContactJsonDbStr::syncTargetDefinitionName()); detailsToJsonMapping.insert(QContactGuid::Type, QContactJsonDbStr::guidDefinitionName()); contactNameFieldsMapping.insert(QContactName::FieldFirstName, QContactJsonDbStr::nameFieldFirstName()); contactNameFieldsMapping.insert(QContactName::FieldLastName, QContactJsonDbStr::nameFieldLastName()); contactNameFieldsMapping.insert(QContactName::FieldMiddleName, QContactJsonDbStr::nameFieldMiddleName()); contactNameFieldsMapping.insert(QContactName::FieldPrefix, QContactJsonDbStr::nameFieldPrefix()); contactNameFieldsMapping.insert(QContactName::FieldSuffix, QContactJsonDbStr::nameFieldSuffix()); addressFieldsMapping.insert(QContactAddress::FieldCountry, QContactJsonDbStr::addressFieldCountry()); addressFieldsMapping.insert(QContactAddress::FieldRegion, QContactJsonDbStr::addressFieldRegion()); addressFieldsMapping.insert(QContactAddress::FieldLocality, QContactJsonDbStr::addressFieldLocality()); addressFieldsMapping.insert(QContactAddress::FieldPostcode, QContactJsonDbStr::addressFieldPostcode()); addressFieldsMapping.insert(QContactAddress::FieldPostOfficeBox, QContactJsonDbStr::addressFieldPostOfficeBox()); addressFieldsMapping.insert(QContactAddress::FieldStreet, QContactJsonDbStr::addressFieldStreet()); organizationFieldsMapping.insert(QContactOrganization::FieldName, QContactJsonDbStr::organizationFieldName()); organizationFieldsMapping.insert(QContactOrganization::FieldDepartment, QContactJsonDbStr::organizationFieldDepartment()); organizationFieldsMapping.insert(QContactOrganization::FieldTitle, QContactJsonDbStr::organizationFieldTitle()); organizationFieldsMapping.insert(QContactOrganization::FieldRole, QContactJsonDbStr::organizationFieldRole()); organizationFieldsMapping.insert(QContactOrganization::FieldAssistantName, QContactJsonDbStr::organizationFieldAssistantName()); organizationFieldsMapping.insert(QContactOrganization::FieldLogoUrl, QContactJsonDbStr::organizationFieldLogoUrl()); phoneNumbersSubtypesMapping.insert(QContactPhoneNumber::SubTypeMobile, QContactJsonDbStr::subTypeCell()); phoneNumbersSubtypesMapping.insert(QContactPhoneNumber::SubTypeFax, QContactJsonDbStr::subTypeFax()); phoneNumbersSubtypesMapping.insert(QContactPhoneNumber::SubTypeVideo, QContactJsonDbStr::subTypeVideo()); phoneNumbersSubtypesMapping.insert(QContactPhoneNumber::SubTypeLandline, QContactJsonDbStr::subTypeLandline()); genderValuesMapping.insert(QContactGender::GenderMale, QContactJsonDbStr::genderMale()); genderValuesMapping.insert(QContactGender::GenderFemale, QContactJsonDbStr::genderFemale()); genderValuesMapping.insert(QContactGender::GenderUnspecified, QContactJsonDbStr::genderOther()); contextsToJsonMapping.insert(QContactDetail::ContextHome, QContactJsonDbStr::contextHome()); contextsToJsonMapping.insert(QContactDetail::ContextWork, QContactJsonDbStr::contextWork()); contextsToJsonMapping.insert(QContactDetail::ContextOther, QContactJsonDbStr::contextOther()); storageLocationMapping.insert(QContactAbstractRequest::UserDataStorage, QContactJsonDbStr::userDataPartition()); storageLocationMapping.insert(QContactAbstractRequest::SystemStorage, QContactJsonDbStr::systemPartition()); //TODO: FINISH THE MAPPING(S) //MISSING DETAILS / FIELDS (TO BE ADDED ALSO TO PARSING LOGIC): // - QContactTimestamp // - QContactOnlineAccount } bool QContactJsonDbConverter::toQContacts(const QList& jsonObjects, QList& convertedContacts, QContactManager::Error& error, const QString &partitionName) {//TODO: ERROR HANDLING if (jsonObjects.isEmpty()) { error = QContactManager::DoesNotExistError; return false; } for (int i = 0; i < jsonObjects.size(); i++) { QContact contact; if (this->toQContact(jsonObjects.at(i), &contact, partitionName)) { convertedContacts.append(contact); } } error = QContactManager::NoError; return true; } void QContactJsonDbConverter::createMatchFlagQuery(QString& queryString, QContactFilter::MatchFlags flags, const QString& value) const { // 1)Any flag combined with MatchExactly is a invalid combination this is handled in documentation //Assuming other combinations as valid QString queryWithWildCards; if (flags.testFlag(QContactFilter::MatchExactly)) queryWithWildCards = QStringLiteral("=\""); else queryWithWildCards = QStringLiteral("=~\"/"); if (flags.testFlag(QContactFilter::MatchContains) || flags.testFlag(QContactFilter::MatchEndsWith)) queryWithWildCards += QStringLiteral("*"); queryWithWildCards += value; if (flags.testFlag(QContactFilter::MatchContains) || flags.testFlag(QContactFilter::MatchStartsWith)) queryWithWildCards += QStringLiteral("*"); if (!(flags.testFlag(QContactFilter::MatchExactly))) { queryWithWildCards += QStringLiteral("/w"); if (!(flags.testFlag(QContactFilter::MatchCaseSensitive))) queryWithWildCards += QStringLiteral("i"); } if (flags.testFlag(QContactFilter::MatchFixedString)) queryWithWildCards += QStringLiteral("/"); queryWithWildCards.append("\"]"); queryString.append(queryWithWildCards); } QString QContactJsonDbConverter::toJsonDbDate(const QDateTime& dateAsQDateTime) const { return dateAsQDateTime.toLocalTime().toString("yyyy-MM-dd"); } QDateTime QContactJsonDbConverter::toQDateTime(const QString &jsonDbDate) const { return QDateTime(QDate::fromString(jsonDbDate, Qt::ISODate)); } QContactId QContactJsonDbConverter::jsonDbNotificationObjectToContactId(const QJsonObject &object, QContactAbstractRequest::StorageLocation storageLocation) const { QString jsonUuid = object.value(QContactJsonDbStr::uuid()).toString(); if (jsonUuid.isEmpty()) return QContactId(); else return QContactId(new QContactJsonDbId(jsonUuid, storageLocation)); } void QContactJsonDbConverter::jsonDbVersionToContactVersion(const QString &jsonDbVersion, QContactVersion *contactVersion) const { QStringList jsonDbVersions = jsonDbVersion.split(QLatin1Char('-')); if (jsonDbVersions.size() != 2) return; int sequenceNumber = jsonDbVersions.at(0).toInt(); if (sequenceNumber > 0 && jsonDbVersions.at(1).length() == jsonDbVersionLength) { contactVersion->setSequenceNumber(sequenceNumber); contactVersion->setExtendedVersion(jsonDbVersions.at(1).toLatin1()); } } void QContactJsonDbConverter::contactVersionToJsonDbVersion(const QContactVersion &contactVersion, QString *jsonDbVersion) const { int sequenceNumber = contactVersion.sequenceNumber(); QByteArray extendedVersion = contactVersion.extendedVersion(); if (sequenceNumber > 0 && extendedVersion.length() == jsonDbVersionLength) { *jsonDbVersion = QString::number(sequenceNumber) + QStringLiteral("-") + QString::fromLatin1(extendedVersion.constData()); } } QContactManager::Error QContactJsonDbConverter::jsonDbRequestErrorToContactError(QJsonDbRequest::ErrorCode error) const { switch (error) { case QJsonDbRequest::NoError: return QContactManager::NoError; case QJsonDbRequest::MissingObject: return QContactManager::DoesNotExistError; case QJsonDbRequest::MissingType: case QJsonDbRequest::MissingQuery: case QJsonDbRequest::InvalidLimit: return QContactManager::BadArgumentError; case QJsonDbRequest::InvalidPartition: return QContactManager::InvalidStorageLocationError; case QJsonDbRequest::DatabaseConnectionError: return QContactManager::TimeoutError; default: return QContactManager::UnspecifiedError; } } bool QContactJsonDbConverter::sanitizePhoneNumberString(QString *phoneNumberString) const { if (!phoneNumberString->isEmpty()) { QString trimmedAndLowerCase = phoneNumberString->trimmed().toLower(); QString cleaned; const int len = trimmedAndLowerCase.length(); if (!len) return false; cleaned.reserve(int(len)); QString::ConstIterator numberCharsIterator = trimmedAndLowerCase.constBegin(); //First character can also be equal to '+' if (isValidPhoneNumberCharacter(numberCharsIterator) || *numberCharsIterator == '+') cleaned += *numberCharsIterator; //Now we sanitize the remaining part of the number string //by removing all characters that are neither arabic numbers nor //few more special characters (p, w, a, b, c, d, #, *, (, )) numberCharsIterator++; while (numberCharsIterator != trimmedAndLowerCase.constEnd()) { if (isValidPhoneNumberCharacter(numberCharsIterator)) cleaned += numberCharsIterator->toLatin1(); numberCharsIterator++; } cleaned.squeeze(); *phoneNumberString = cleaned; if ((phoneNumberString->length() >64) || (phoneNumberString->isEmpty())) { return false; } else { return true; } } else { return true; } } QContactAbstractRequest::StorageLocation QContactJsonDbConverter::partitionNameToStorageLocation( const QString partitionName) { return storageLocationMapping.key(partitionName); } const QStringList QContactJsonDbConverter::storageLocationsToPartitionNames( QContactAbstractRequest::StorageLocations storageLocations) { QStringList partitionNames; if (QContactAbstractRequest::UserDataStorage & storageLocations) partitionNames.append(storageLocationMapping[QContactAbstractRequest::UserDataStorage]); if (QContactAbstractRequest::SystemStorage & storageLocations) partitionNames.append(storageLocationMapping[QContactAbstractRequest::SystemStorage]); return partitionNames; } /*! Parses the input string to eliminate extra white spaces and enforce maximum length. */ QContactJsonDbConverter::SanitizeError QContactJsonDbConverter::sanitizeContactDetailString(QString *stringToBeSanitized, int maxStringLength) const { if (!stringToBeSanitized->isEmpty()) { // cut leading and trailing white spaces QString simplified = stringToBeSanitized->simplified(); QString cleaned; const int len = simplified.length(); if (!len) return EmptyArgumentError; // The string is empty after being simplified, return error cleaned.reserve(int(len)); QString::ConstIterator numberCharsIterator = simplified.constBegin(); while (numberCharsIterator != simplified.constEnd()) { cleaned += numberCharsIterator->toLatin1(); numberCharsIterator++; } cleaned.squeeze(); *stringToBeSanitized = cleaned; if ((stringToBeSanitized->length() > maxStringLength) || (stringToBeSanitized->isEmpty())) { qWarning() << Q_FUNC_INFO <<"The string is either empty or too long. The maximum length allowed is " << maxStringLength; return InvalidArgumentError; // String too long, return error } else { return NoError; // No error } } else { return EmptyArgumentError; // The string passed as argument was empty in the first place } } QT_END_NAMESPACE_CONTACTS src/plugins/contacts/jsondb/qcontactjsondbconverter.h000066400000000000000000000131771233466112000235470ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTJSONDBCONVERTER_H #define QCONTACTJSONDBCONVERTER_H #include #include #include #include #include #include "qcontactjsondbengine.h" QT_USE_NAMESPACE_JSONDB QT_BEGIN_NAMESPACE_CONTACTS class QContactJsonDbConverter { public: enum SanitizeError { NoError = 0x0000, EmptyArgumentError = 0x0001, InvalidArgumentError = 0x0002 }; QContactJsonDbConverter(); ~QContactJsonDbConverter(); QContactManager::Error jsonDbRequestErrorToContactError(QJsonDbRequest::ErrorCode error) const; bool toQContact(const QJsonObject& object, QContact* contact, const QString &partitionName); bool toQContacts(const QList &jsonObjects, QList& convertedContacts, QContactManager::Error& error, const QString &partitionName); bool toJsonContact(QJsonObject* object, const QContact& contact, const QList &detailMask = QList()); bool updateContexts(const QJsonObject& object, QContactDetail* detail); bool updateContexts(const QContactDetail& detail, QJsonObject* object); bool queryFromRequest(QContactAbstractRequest* request,QString &jsonDbQueryStr); bool singleFilterToJsondbQuery(const QContactFilter& filter,QString &jsonDbQueryStr) const; bool compoundFilterToJsondbQuery(const QContactFilter &filter, QString &jsonDbQueryStr) const; QString convertSortOrder(const QList& sortOrders) const; QContactId jsonDbNotificationObjectToContactId(const QJsonObject &object, QContactAbstractRequest::StorageLocation storageLocation) const; QString contactIdToUuid(const QContactId &id) const; QContactId uuidtoContactId(QString &uuid, const QString &partitionName) const; void jsonDbVersionToContactVersion(const QString &jsonDbVersion, QContactVersion *contactVersion) const; void contactVersionToJsonDbVersion(const QContactVersion &contactVersion, QString *jsonDbVersion) const; bool sanitizePhoneNumberString(QString *phoneNumberString) const; const QStringList storageLocationsToPartitionNames(QContactAbstractRequest::StorageLocations storageLocations); QContactAbstractRequest::StorageLocation partitionNameToStorageLocation(const QString partitionName); SanitizeError sanitizeContactDetailString(QString *stringToBeSanitized, int maxStringLength = 50) const; private: void initializeMappings(); void createMatchFlagQuery(QString& queryString, QContactFilter::MatchFlags flags, const QString& value) const; QString toJsonDbDate(const QDateTime& dateAsQDateTime) const; QDateTime toQDateTime(const QString& jsonDbDate) const; bool idFilterToJsondbQuery(const QContactFilter &filter, QString &jsonDbQueryStr) const; bool detailFilterToJsondbQuery(const QContactFilter &filter, QString &jsonDbQueryStr) const; void idsToJsondbQuery(const QList &ids, QString &newJsonDbQuery) const; static bool isValidPhoneNumberCharacter (const QChar *character); static const int jsonDbVersionLength; QHash detailsToJsonMapping; QHash contactNameFieldsMapping; QHash organizationFieldsMapping; QHash addressFieldsMapping; QHash phoneNumbersSubtypesMapping; QHash genderValuesMapping; QHash contextsToJsonMapping; QHash storageLocationMapping; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTJSONDBCONVERTER_H src/plugins/contacts/jsondb/qcontactjsondbengine.cpp000066400000000000000000000361571233466112000233430ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactjsondbengine.h" #include #include #include #include "qcontactjsondbconverter.h" #include "qcontactjsondbglobal.h" #include "qcontactjsondbstring.h" #include "qcontactjsondbrequesthandler.h" QT_BEGIN_NAMESPACE_CONTACTS /* class QContactJsonDbEngine \brief The QContactJsonDbEngine class provides an implementation of QContactManagerEngine whose functions always return an error. The JsonDb engine. */ QContactJsonDbEngine::QContactJsonDbEngine(const QMap& parameters) { Q_UNUSED(parameters); m_requestHandler = new QContactJsonDbRequestHandler(); qRegisterMetaType("QContactAbstractRequest::State"); qRegisterMetaType >("QList"); qRegisterMetaType("QContactId"); m_thread = new QThread(); m_thread->start(); connect(this, SIGNAL(requestReceived(QContactAbstractRequest*)), m_requestHandler, SLOT(handleRequest(QContactAbstractRequest*))); m_requestHandler->moveToThread(m_thread); QMetaObject::invokeMethod(m_requestHandler,"init",Qt::BlockingQueuedConnection); m_requestHandler->setEngine(this); } QContactJsonDbEngine::~QContactJsonDbEngine() { if (m_requestHandler) m_requestHandler->deleteLater(); if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; } } bool QContactJsonDbEngine::startRequest(QContactAbstractRequest* req){ QContactManagerEngine::updateRequestState(req, QContactAbstractRequest::ActiveState); connect(req, SIGNAL(destroyed(QObject*)), m_requestHandler, SLOT(removeDestroyed(QObject*)),Qt::QueuedConnection); emit requestReceived(req); return true; } /* \reimp */ QString QContactJsonDbEngine::managerName() const { return QContactJsonDbStr::contactJsonDbEngineName(); } QList QContactJsonDbEngine::supportedContactDetailTypes() const { QList supportedDetails; supportedDetails << QContactAddress::Type << QContactAvatar::Type << QContactBirthday::Type << QContactDisplayLabel::Type << QContactEmailAddress::Type << QContactExtendedDetail::Type << QContactGender::Type << QContactGuid::Type << QContactName::Type << QContactNickname::Type << QContactNote::Type << QContactOrganization::Type << QContactPhoneNumber::Type << QContactRingtone::Type << QContactSyncTarget::Type << QContactType::Type << QContactUrl::Type << QContactVersion::Type; return supportedDetails; } bool QContactJsonDbEngine::validateContact(const QContact& contact, QContactManager::Error* error) const { QContactManagerEngine::validateContact(contact, error); if ((*error == QContactManager::InvalidContactTypeError) || (*error == QContactManager::DoesNotExistError)) return false; QList contactDetailList = contact.details(); for (int i=0; i QContactJsonDbEngine::contactIds(const QContactFilter& filter, const QList& sortOrders, QContactManager::Error* error) const { QContactJsonDbConverter converter; QList contactIds; QVariantMap map; QContactFetchRequest request; request.setFilter(filter); request.setSorting(sortOrders); *error = QContactManager::NoError; //QString query = converter.queryFromRequest(&request); doSyncRequest(&request, 5000); *error = request.error(); if (*error != QContactManager::NoError) { if (qt_debug_jsondb_contacts()) qDebug() << "[QContactJsonDb] Error at " << Q_FUNC_INFO << ":" << *error; return QList(); } QList queryResults = (QList)request.contacts(); // found any results? if(queryResults.size() == 0) { *error = QContactManager::DoesNotExistError; qDebug() << "Error by function contactIds: no contacts found (DoesNotExistError)"; return QList(); } // Convert results for needed format QList results; foreach (const QContact &contact, queryResults) results.append(contact.id()); return results; } QList QContactJsonDbEngine::contacts(const QContactFilter & filter, const QList & sortOrders, const QContactFetchHint & fetchHint, QContactManager::Error* error ) const { // TODO: ERROR HANDLING (?) QList contacts; QContactJsonDbConverter converter; QContactFetchRequest fetchReq; fetchReq.setFilter(filter); fetchReq.setSorting(sortOrders); fetchReq.setFetchHint(fetchHint); *error = QContactManager::NoError; //QString query = converter.queryFromRequest(&fetchReq); doSyncRequest(&fetchReq, 5000); *error = fetchReq.error(); if (*error != QContactManager::NoError) { if (qt_debug_jsondb_contacts()) qDebug() << "[QContactJsonDb] Error at " << Q_FUNC_INFO << ":" << *error; return QList(); } QList queryResults = (QList)fetchReq.contacts(); // found any results? if(queryResults.size() == 0) { *error = QContactManager::DoesNotExistError; qDebug() << "Error by function contacts: no contacts found (DoesNotExistError)"; return QList(); } /* else { converter.toQContacts(jsonDbObjectList, contacts, *this, *error); } */ return queryResults; } QContact QContactJsonDbEngine::contact(const QContactId& contactId, const QContactFetchHint& fetchHint, QContactManager::Error* error) const { QContact contact; QContactJsonDbConverter converter; QContactFetchRequest request; QList filterIds; QContactIdFilter idFilter; QString query; QVariantList results; filterIds.append(contactId); idFilter.setIds(filterIds); request.setFilter(idFilter); request.setFetchHint(fetchHint); *error = QContactManager::NoError; //query = converter.queryFromRequest(&request); doSyncRequest(&request, 5000); *error = request.error(); if (*error != QContactManager::NoError) { if (qt_debug_jsondb_contacts()) qDebug() << "[QContactJsonDb] Error at " << Q_FUNC_INFO << ":" << *error; return QContact(); } QList queryResults = (QList)request.contacts(); // Check if query returned a value and it can be converted if(queryResults.size() == 0) { *error = QContactManager::DoesNotExistError; qDebug() << "Error by function contact: no contact found (DoesNotExistError)"; return QContact(); } // Extract the desired results foreach (QContact curr, queryResults) { if (curr.id() == contactId) contact = curr; ; } return contact; } bool QContactJsonDbEngine::saveContacts(QList* contacts, QMap* errorMap, QContactManager::Error* error) { QContactSaveRequest saveReq; saveReq.setContacts(*contacts); doSyncRequest(&saveReq, 5000); *error = saveReq.error(); if (*error != QContactManager::NoError) { if (qt_debug_jsondb_contacts()) qDebug() << "[QContactJsonDb] Error at " << Q_FUNC_INFO << ":" << *error; } for (int i = 0; i < saveReq.contacts().size(); i++) contacts->replace(i, saveReq.contacts()[i]); *errorMap = saveReq.errorMap(); return *error == QContactManager::NoError; // No problem detected, return NoError } bool QContactJsonDbEngine::removeContacts(const QList& ids, QMap* errorMap, QContactManager::Error* error) { QContactRemoveRequest removeReq; removeReq.setContactIds(ids); doSyncRequest(&removeReq, 5000); *error = removeReq.error(); *errorMap = removeReq.errorMap(); if (*error != QContactManager::NoError) { qWarning() << "Error at function removeContacts:" << *error; return false; } else { return true; } } bool QContactJsonDbEngine::saveContact(QContact* contact, QContactManager::Error* error) { QContactSaveRequest saveReq; *error = QContactManager::NoError; saveReq.setContact(*contact); doSyncRequest(&saveReq, 5000); *error = saveReq.error(); if (*error != QContactManager::NoError) { if (qt_debug_jsondb_contacts()) qDebug() << "[QContactJsonDb] Error at " << Q_FUNC_INFO << ":" << *error; return false; } *contact = saveReq.contacts().first(); // Check if this is the desired behavior !!! return *error == QContactManager::NoError; // No problem detected, return NoError } bool QContactJsonDbEngine::removeContact(const QContactId& contactId, QContactManager::Error* error) { Q_UNUSED(contactId) Q_UNUSED(error) QContactRemoveRequest removeReq; *error = QContactManager::NoError; removeReq.setContactId(contactId); doSyncRequest(&removeReq, 5000); *error = removeReq.error(); if (*error != QContactManager::NoError) { if (qt_debug_jsondb_contacts()) qDebug() << "[QContactJsonDb] Error at " << Q_FUNC_INFO << ":" << *error; return false; } else return true; } bool QContactJsonDbEngine::isFilterSupported(const QContactFilter& filter) const { switch (filter.type()) { case QContactFilter::ContactDetailFilter: { QContactDetailFilter detailFilter = static_cast(filter); int field = detailFilter.detailField(); if (field < 0) return false; switch (detailFilter.detailType()) { case QContactDetail::TypeEmailAddress: if (field != QContactEmailAddress::FieldEmailAddress) return false; case QContactDetail::TypePhoneNumber: if (field != QContactPhoneNumber::FieldNumber) return false; case QContactDetail::TypeUrl: if (field != QContactUrl::FieldUrl) return false; case QContactDetail::TypeName: return true; default: return false; }; return false; } case QContactFilter::InvalidFilter: case QContactFilter::DefaultFilter: case QContactFilter::IdFilter: case QContactFilter::IntersectionFilter: return true; default: return false; } } QList QContactJsonDbEngine::supportedDataTypes() const { QList st; st.append(QVariant::String); st.append(QVariant::Int); st.append(QVariant::UInt); st.append(QVariant::Double); st.append(QVariant::Date); st.append(QVariant::DateTime); st.append(QVariant::Bool); st.append(QVariant::Url); return st; } void QContactJsonDbEngine::requestDestroyed(QContactAbstractRequest* req){ //We inform the handler that this request is about to be destroyed so as to //avoid that the worker handler thread will start handling request objects during //their destruction. QMetaObject::invokeMethod(m_requestHandler,"removeDestroyed",Qt::BlockingQueuedConnection,Q_ARG(QObject*, req)); return QContactManagerEngine::requestDestroyed(req); } bool QContactJsonDbEngine::cancelRequest(QContactAbstractRequest* req){ /* TODO Cancel an in progress async request. If not possible, return false from here. */ return QContactManagerEngine::cancelRequest(req); } bool QContactJsonDbEngine::waitForRequestProgress(QContactAbstractRequest* req, int msecs){ Q_UNUSED(msecs); Q_UNUSED(req); //TODO: can we get progress info from jsondb?? return true; } bool QContactJsonDbEngine::waitForRequestFinished(QContactAbstractRequest* req, int msecs){ bool result = false; result = m_requestHandler->waitForRequestFinished(req, msecs); return result; } bool QContactJsonDbEngine::doSyncRequest(QContactAbstractRequest* req, int msecs) const { Q_UNUSED(msecs); // TODO //if (req->ContactFetchRequest) const_cast(this)->startRequest(req); const_cast(this)->waitForRequestFinished(req, 0); //if (req->FinishedState) if (req->isFinished() == true) return true; else return false; } /* Internal, for debugging */ bool qt_debug_jsondb_contacts() { static int debug_env = -1; if (debug_env == -1) debug_env = QT_PREPEND_NAMESPACE(qgetenv)("QT_DEBUG_JSONDB_CONTACTS").toInt(); return debug_env != 0; } #include "moc_qcontactjsondbengine.cpp" QT_END_NAMESPACE_CONTACTS src/plugins/contacts/jsondb/qcontactjsondbengine.h000066400000000000000000000125321233466112000227770ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTJSONDBENGINE_H #define QCONTACTJSONDBENGINE_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include #include #include "qcontactjsondbstring.h" QT_FORWARD_DECLARE_CLASS(QThread) QT_BEGIN_NAMESPACE_CONTACTS class QContactJsonDbRequestHandler; class QContactJsonDbEngine : public QContactManagerEngine { Q_OBJECT public: ~QContactJsonDbEngine(); QContactJsonDbEngine(const QMap ¶meters = (QMap())); QString managerName() const; bool validateContact(const QContact&, QContactManager::Error* error) const; /* "Self" contact id (MyCard) */ QContactId selfContactId(QContactManager::Error* errors) const; /* Filtering */ QList contactIds(const QContactFilter& filter, const QList& sortOrders, QContactManager::Error* error) const; /* Contacts - Accessors and Mutators */ QContact contact(const QContactId& contactId, const QContactFetchHint& fetchHint, QContactManager::Error* error) const; QList contacts(const QContactFilter& filter, const QList& sortOrders, const QContactFetchHint& fetchHint, QContactManager::Error* error ) const; bool saveContact(QContact* contact, QContactManager::Error* error); bool removeContact(const QContactId& contactId, QContactManager::Error* error); bool saveContacts(QList*, QMap*, QContactManager::Error* error); // implemented in terms of the singular saveContact bool removeContacts(const QList&, QMap*, QContactManager::Error* error); // implemented in terms of the singular removeContact /* Version Reporting */ int implementationVersion() const { return QContactJsonDbStr::ContactJsonDbEngineVersion; } int managerVersion() const { return QContactJsonDbStr::ContactJsonDbEngineVersion; } bool isFilterSupported(const QContactFilter& filter) const; QList supportedDataTypes() const; QList supportedContactTypes() const {return (QList () << QContactType::TypeContact);} QList supportedContactDetailTypes() const; /* Asynchronous Request Support - synchronous versions until thread worker is stable */ void requestDestroyed(QContactAbstractRequest *req); bool startRequest(QContactAbstractRequest *req); bool cancelRequest(QContactAbstractRequest *req); bool waitForRequestProgress(QContactAbstractRequest* req, int msecs); bool waitForRequestFinished(QContactAbstractRequest* req, int msecs); signals: void requestReceived(QContactAbstractRequest* req); private: bool doSyncRequest(QContactAbstractRequest* req, int msecs) const; Q_DISABLE_COPY(QContactJsonDbEngine); QQueue m_asynchronousOperations; // async requests to be performed. QContactJsonDbRequestHandler *m_requestHandler; QThread* m_thread; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTJSONDBENGINE_H src/plugins/contacts/jsondb/qcontactjsondbenginefactory.cpp000066400000000000000000000054501233466112000247230ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactjsondbengine.h" #include "qcontactjsondbenginefactory.h" #include "qcontactjsondbid.h" QT_BEGIN_NAMESPACE_CONTACTS QContactJsonDbEngineFactory::QContactJsonDbEngineFactory() { } QContactManagerEngine* QContactJsonDbEngineFactory::engine(const QMap& parameters, QContactManager::Error* error) { Q_UNUSED(error); return new QContactJsonDbEngine(parameters); //Manager engine will take ownership of this object. } QContactEngineId *QContactJsonDbEngineFactory::createContactEngineId(const QMap ¶meters, const QString &engineIdString) const { Q_UNUSED(parameters) return new QContactJsonDbId(engineIdString); } QString QContactJsonDbEngineFactory::managerName() const { return QContactJsonDbStr::contactJsonDbEngineName(); } #include "moc_qcontactjsondbenginefactory.cpp" QT_END_NAMESPACE_CONTACTS src/plugins/contacts/jsondb/qcontactjsondbenginefactory.h000066400000000000000000000060301233466112000243630ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTJSONDBENGINEFACTORY_H #define QCONTACTJSONDBENGINEFACTORY_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class Q_DECL_EXPORT QContactJsonDbEngineFactory : public QContactManagerEngineFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QContactManagerEngineFactoryInterface" FILE "jsondb.json") public: QContactJsonDbEngineFactory(); QContactManagerEngine* engine(const QMap& parameters, QContactManager::Error*); QString managerName() const; QContactEngineId *createContactEngineId(const QMap ¶meters, const QString &engineIdString) const; private: Q_DISABLE_COPY(QContactJsonDbEngineFactory) }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTJSONDBENGINEFACTORY_H src/plugins/contacts/jsondb/qcontactjsondbglobal.h000066400000000000000000000041571233466112000227760ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTJSONDBGLOBAL_H #define QCONTACTJSONDBGLOBAL_H QT_BEGIN_NAMESPACE_CONTACTS bool qt_debug_jsondb_contacts(); QT_END_NAMESPACE_CONTACTS #endif // QCONTACTJSONDBGLOBAL_H src/plugins/contacts/jsondb/qcontactjsondbid.cpp000066400000000000000000000122661233466112000224650ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Pim module. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactjsondbid.h" #ifndef QT_NO_DEBUG_STREAM #include #endif #include "qcontactjsondbstring.h" QT_BEGIN_NAMESPACE_CONTACTS QContactJsonDbId::QContactJsonDbId(const QString &engineId) { QStringList splitEngineId = engineId.split("/"); if (splitEngineId.size() == 2) { m_uuid = splitEngineId.last(); m_storageLocation = QContactAbstractRequest::StorageLocation( splitEngineId.first().toInt()); } } QContactJsonDbId::QContactJsonDbId(const QContactJsonDbId &other) : m_uuid(other.m_uuid), m_storageLocation(other.m_storageLocation) { } QContactJsonDbId::QContactJsonDbId(const QUuid &uuid, const QContactAbstractRequest::StorageLocation &storageLocation) : m_uuid(uuid), m_storageLocation(storageLocation) { } QContactJsonDbId::~QContactJsonDbId() { } bool QContactJsonDbId::isEqualTo(const QContactEngineId *other) const { const QContactJsonDbId *otherJsonDbId = static_cast(other); QUuid otherUuid = otherJsonDbId->m_uuid; QContactAbstractRequest::StorageLocation otherStorageLocation = otherJsonDbId->m_storageLocation; return ((m_uuid == otherUuid) && (m_storageLocation == otherStorageLocation)); } bool QContactJsonDbId::isLessThan(const QContactEngineId *other) const { const QContactJsonDbId *otherJsonDbId = static_cast(other); QUuid otherUuid = otherJsonDbId->m_uuid; QContactAbstractRequest::StorageLocation otherStorageLocation = otherJsonDbId->m_storageLocation; return ((m_storageLocation < otherStorageLocation) || ((m_storageLocation == otherStorageLocation) && (m_uuid < otherUuid))); } QString QContactJsonDbId::managerUri() const { return QContactJsonDbStr::managerUri(); } QContactEngineId *QContactJsonDbId::clone() const { return new QContactJsonDbId(m_uuid, m_storageLocation); } #ifndef QT_NO_DEBUG_STREAM QDebug &QContactJsonDbId::debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QContactJsonDbId(" << this->toString() << ")"; return dbg.maybeSpace(); } #endif QString QContactJsonDbId::toString() const { QString stringifiedIdFormat("%1/%2"); return stringifiedIdFormat.arg(QString::number(m_storageLocation)).arg(m_uuid.toString()); } uint QContactJsonDbId::hash() const { /* TODO Provide a hash function for your engine-specific id. Note that the hash doesn't strictly need to be unique, since isEqualTo() ensures that individual id's in a single hash-bucket can be uniquely determined; however a better hash function will result in better performance because the ids will be distributed more randomly in a hash table. In the example implementation below, we could simply return the id, since the id is a quint32. In more complex id classes, however, you may need to qHash() individual data members and combine the results somehow. */ return QT_PREPEND_NAMESPACE(qHash)(this->toString()); } /*! * Returns the jsondb uuid of the contact */ QUuid QContactJsonDbId::uuid() const { return m_uuid; } /*! * Returns the storage location where the contact is stored in */ QContactAbstractRequest::StorageLocation QContactJsonDbId::storageLocation() const { return m_storageLocation; } QT_END_NAMESPACE_CONTACTS src/plugins/contacts/jsondb/qcontactjsondbid.h000066400000000000000000000063461233466112000221340ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Pim Module ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTJSONDBID_H #define QCONTACTJSONDBID_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactJsonDbId : public QContactEngineId { public: explicit QContactJsonDbId(const QString &engineId); QContactJsonDbId(const QUuid &uuid, const QContactAbstractRequest::StorageLocation &storageLocation); QContactJsonDbId(const QContactJsonDbId &other); ~QContactJsonDbId(); bool isEqualTo(const QContactEngineId *other) const; bool isLessThan(const QContactEngineId *other) const; QString managerUri() const; QContactEngineId *clone() const; QString toString() const; #ifndef QT_NO_DEBUG_STREAM QDebug &debugStreamOut(QDebug &dbg) const; #endif uint hash() const; QUuid uuid() const; QContactAbstractRequest::StorageLocation storageLocation() const; private: QUuid m_uuid; QContactAbstractRequest::StorageLocation m_storageLocation; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTJSONDBID_H src/plugins/contacts/jsondb/qcontactjsondbrequesthandler.cpp000066400000000000000000001425441233466112000251220ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactjsondbrequesthandler.h" #include #include #include #include #include #include #include "qcontactjsondbconverter.h" #include "qcontactjsondbengine.h" #include "qcontactjsondbglobal.h" #include "qcontactjsondbid.h" #include "qcontactjsondbrequestmanager.h" #include "qcontactjsondbstring.h" QT_BEGIN_NAMESPACE_CONTACTS const int QContactJsonDbRequestHandler::TIMEOUT_INTERVAL(100); QContactJsonDbRequestHandler::QContactJsonDbRequestHandler() : m_engine(0), m_jsonDbConnection(0), m_timer(0) { } QContactJsonDbRequestHandler::~QContactJsonDbRequestHandler() { delete m_timer; delete m_reqStateMutex; delete m_converter; delete m_requestMgr; m_jsonDbConnection->deleteLater(); } void QContactJsonDbRequestHandler::init() { m_reqStateMutex = new QMutex(); m_requestMgr = new QContactJsonDbRequestManager(); m_converter = new QContactJsonDbConverter(); m_jsonDbConnection = new QJsonDbConnection(this); connect(m_jsonDbConnection, SIGNAL(error(QtJsonDb::QJsonDbConnection::ErrorCode,QString)), this, SLOT(onJsonDbConnectionError(QtJsonDb::QJsonDbConnection::ErrorCode,QString))); m_jsonDbConnection->connectToServer(); Q_ASSERT(storageLocationToPartition(QContactAbstractRequest::UserDataStorage) != storageLocationToPartition(QContactAbstractRequest::SystemStorage)); createWatcherForStorageLocation(QContactAbstractRequest::UserDataStorage); createWatcherForStorageLocation(QContactAbstractRequest::SystemStorage); // Be optimistic, reduce them later on error. m_availableStorageLocations = supportedStorageLocations(); } void QContactJsonDbRequestHandler::createWatcherForStorageLocation(QContactAbstractRequest::StorageLocation storageLocation) { QJsonDbWatcher* jsonDbWatcher = new QJsonDbWatcher(this); jsonDbWatcher->setWatchedActions(QJsonDbWatcher::All); jsonDbWatcher->setQuery(QContactJsonDbStr::contactsJsonDbNotificationQuery()); jsonDbWatcher->setPartition(storageLocationToPartition(storageLocation)); new QContactJsonDbPartitionWatcher(this, jsonDbWatcher, storageLocation); m_jsonDbConnection->addWatcher(jsonDbWatcher); } void QContactJsonDbRequestHandler::setEngine(QContactJsonDbEngine *engine) { m_engine = engine; } bool QContactJsonDbRequestHandler::waitForRequestFinished(QContactAbstractRequest *req, int msecs) { // This function is called by the QContactJsonDbEngine thread (main thread) // TODO: timeout handling Q_UNUSED(msecs); QMutexLocker locker(m_reqStateMutex); QWaitCondition waitCondition; if (req->state() == QContactAbstractRequest::FinishedState) return true; else if (req->state() == QContactAbstractRequest::CanceledState) return false; // Request might still be inactive if this function is called immediatelly after sending a signal to // handleRequest slot. The signal goes to event loop and might be handled later than this function is executed. bool requestInactive = !m_requestMgr->setWaitCondition(req,&waitCondition); bool requestFinished; if (msecs <= 0) requestFinished = waitCondition.wait(m_reqStateMutex); else requestFinished = waitCondition.wait(m_reqStateMutex, msecs); if (requestInactive && !requestFinished) { // The request was never started or was started but not yet finished m_requestMgr->removeWaitCondition(req); } return requestFinished; } void QContactJsonDbRequestHandler::handleRequest(QContactAbstractRequest *req) { // Detects what kind of request is at hand, updates the state to "active", calls "addRequest" and // "addRequest" from requestManager, sends resultsAvailable signal QMutexLocker locker(m_reqStateMutex); QContactManager::Error error = QContactManager::NoError; if (m_reqList.contains(req)) { error = QContactManager::UnspecifiedError; qWarning() << Q_FUNC_INFO << "Trying to handle destroyed request: throwing ERROR: " << error; return; } switch (req->type()) { case QContactAbstractRequest::ContactSaveRequest: { QContactSaveRequest* saveReq = static_cast(req); handleContactSaveRequest(saveReq); break; } case QContactAbstractRequest::ContactFetchRequest: { QContactFetchRequest* fetchReq = static_cast(req); handleContactFetchRequest(fetchReq); break; } case QContactAbstractRequest::ContactFetchByIdRequest: { QContactFetchByIdRequest* fetchReq = static_cast(req); handleContactFetchByIdRequest(fetchReq); break; } case QContactAbstractRequest::ContactRemoveRequest: { QContactRemoveRequest* removeReq = static_cast(req); handleContactRemoveRequest(removeReq); break; } case QContactAbstractRequest::ContactIdFetchRequest: { QContactIdFetchRequest* idReq = static_cast(req); handleContactIdFetchRequest(idReq); break; } default: break; } } void QContactJsonDbRequestHandler::handleContactSaveRequest(QContactSaveRequest* saveReq) { //TODO: handle duplicates(?) QList contacts = saveReq->contacts(); QContactManager::Error lastError = QContactManager::NoError; QMap errorMap; m_requestMgr->addRequest(saveReq, contacts); for (int i = 0; i < contacts.size(); i++) { QContactManager::Error error = QContactManager::NoError; QContact contact = contacts.at(i); const QContactAbstractRequest::StorageLocation storageLocationForTheContact = contact.id().isNull() ? saveReq->storageLocation() : extractStorageLocation(contact.id()); const QString partition = storageLocationToPartition(storageLocationForTheContact); if (partition.isEmpty()) { error = QContactManager::InvalidStorageLocationError; errorMap.insert(i,error); lastError = errorPrecedence(lastError, error); continue; } if (!m_engine->validateContact(contact, &error)) { errorMap.insert(i,error); lastError = errorPrecedence(lastError, error); // For an invalid contact in addition to reporting error we also clear the contact id. QContactId contactId; contact.setId(contactId); continue; } if (!contact.id().isNull()) { // Update to existing contact with given id. Fetch complete contact data from jsondb before saving. // This preserves possible extra fields in jsondb contact object. Actual update request is made in // the response handler for this prefetch request. QContactIdFilter idFilter; idFilter.add(contact.id()); QContactFetchRequest *fetchRequest = new QContactFetchRequest(this); fetchRequest->setFilter(idFilter); QString fetchQuery; bool isValid = m_converter->queryFromRequest(fetchRequest, fetchQuery); if (isValid) { m_requestMgr->addRequest(fetchRequest); m_requestMgr->addPrefetchRequest(fetchRequest, saveReq); if (!makeJsonDbRequest(fetchRequest, QContactJsonDbRequestManager::PrefetchForSaveRequest, i, partition, fetchQuery)) { error = QContactManager::TimeoutError; errorMap.insert(i,error); lastError = errorPrecedence( lastError, error); } } else { error = QContactManager::BadArgumentError; errorMap.insert(i,error); lastError = errorPrecedence(lastError, error); } } else { // No prefetch needed, just create a new contact. QJsonObject newJsonDbItem; if (m_converter->toJsonContact(&newJsonDbItem, contact, saveReq->typeMask())) { if (!makeJsonDbRequest(saveReq, QContactJsonDbRequestManager::SaveRequest, i, partition, QString(), QList() << newJsonDbItem)) { error = QContactManager::TimeoutError; errorMap.insert(i,error); lastError = errorPrecedence(lastError, error); } } else { error = QContactManager::BadArgumentError; errorMap.insert(i,error); lastError = errorPrecedence(lastError, error); } } } if (errorMap.size() == contacts.size()) { //None of the contacts could be saved QWaitCondition* waitCondition = m_requestMgr->waitCondition(saveReq); m_requestMgr->removeRequest(saveReq); QContactManagerEngine::updateContactSaveRequest(saveReq, contacts, lastError, errorMap, QContactAbstractRequest::FinishedState); if (waitCondition) waitCondition->wakeAll(); } else { QContactManagerEngine::updateContactSaveRequest(saveReq, contacts, lastError, errorMap, QContactAbstractRequest::ActiveState); } } QString QContactJsonDbRequestHandler::storageLocationToPartition(QContactAbstractRequest::StorageLocation storageLocation) { const QStringList partitions = m_converter->storageLocationsToPartitionNames(QContactAbstractRequest::StorageLocations(storageLocation)); if (partitions.isEmpty()) return QString(); else return partitions.first(); } QContactAbstractRequest::StorageLocations QContactJsonDbRequestHandler::storageLocationsOrDefault( QContactAbstractRequest::StorageLocations storageLocation) { return storageLocation ? storageLocation : QContactAbstractRequest::UserDataStorage; } QContactAbstractRequest::StorageLocation QContactJsonDbRequestHandler::extractStorageLocation(const QContactId &id) { const QContactEngineId *engineId = QContactManagerEngine::engineId(id); if (engineId) return engineId->storageLocation(); else return QContactAbstractRequest::StorageLocation(0); } QContactAbstractRequest::StorageLocations QContactJsonDbRequestHandler::extractStorageLocations(const QList &contactIds) { QContactAbstractRequest::StorageLocations storageLocations(0); foreach (const QContactId &id, contactIds) { storageLocations |= extractStorageLocation(id); } return storageLocations; } void QContactJsonDbRequestHandler::handleContactFetchRequest(QContactFetchRequest* req) { QContactManager::Error error = QContactManager::NoError; QString newJsonDbQuery; m_requestMgr->addRequest(req); if (m_converter->queryFromRequest(req, newJsonDbQuery)) { QContactAbstractRequest::StorageLocations storageLocations = storageLocationsOrDefault(req->storageLocations()); if (storageLocations & ~m_availableStorageLocations) error = QContactManager::InvalidStorageLocationError; QStringList partitions = m_converter->storageLocationsToPartitionNames(storageLocations); if (!partitions.isEmpty()) { foreach (const QString &partition, partitions) { if (!makeJsonDbRequest(req, QContactJsonDbRequestManager::FetchRequest, 0, partition, newJsonDbQuery)) { error = errorPrecedence(error, QContactManager::TimeoutError); break; } } } else { error = errorPrecedence(error, QContactManager::InvalidStorageLocationError); } } else { error = QContactManager::BadArgumentError; } const QList emptyContactList; if (error != QContactManager::NoError) { QWaitCondition* waitCondition = m_requestMgr->waitCondition(req); m_requestMgr->removeRequest(req); QContactManagerEngine::updateContactFetchRequest(req, emptyContactList,error,QContactAbstractRequest::FinishedState); if (waitCondition) waitCondition->wakeAll(); } else { QContactManagerEngine::updateContactFetchRequest(req, emptyContactList, error, QContactAbstractRequest::ActiveState); } } void QContactJsonDbRequestHandler::handleContactFetchByIdRequest(QContactFetchByIdRequest* req) { QContactManager::Error error = QContactManager::NoError; QMap errorMap; QList emptyContactList; QString newJsonDbQuery; m_requestMgr->addRequest(req); m_converter->queryFromRequest(req, newJsonDbQuery); // Always ok for fetch by id requests as an empty query is also ok. QContactAbstractRequest::StorageLocations storageLocations = extractStorageLocations(req->contactIds()); if (storageLocations & ~m_availableStorageLocations) error = QContactManager::InvalidStorageLocationError; QStringList partitions = m_converter-> storageLocationsToPartitionNames(storageLocations); if (!partitions.isEmpty()) { foreach (const QString &partition, partitions) { if (!makeJsonDbRequest(req, QContactJsonDbRequestManager::FetchByIdRequest, 0, partition, newJsonDbQuery)) { error = errorPrecedence(error, QContactManager::TimeoutError); break; } } } else { // None of the ids had valid partition specifie, prepare request with errors and empty contacts. // Here we keep DoesNotExistError for empty contact ids to keep consistency with memory backend // and existing test asset for null operations. for (int index = 0;index < req->contactIds().size();index++) { emptyContactList << QContact(); if (req->contactIds().at(index).isNull()) { errorMap.insert(index, QContactManager::DoesNotExistError); error = errorPrecedence(error, QContactManager::DoesNotExistError); } else { errorMap.insert(index, QContactManager::InvalidStorageLocationError); error = errorPrecedence(error, QContactManager::InvalidStorageLocationError); } } } if (error != QContactManager::NoError) { QWaitCondition* waitCondition = m_requestMgr->waitCondition(req); m_requestMgr->removeRequest(req); QContactManagerEngine::updateContactFetchByIdRequest(req, emptyContactList, error, errorMap, QContactAbstractRequest::FinishedState); if (waitCondition) waitCondition->wakeAll(); } } void QContactJsonDbRequestHandler::handleContactRemoveRequest(QContactRemoveRequest* req) { QContactManager::Error lastError = QContactManager::NoError; QMap errorMap; QList contactIds = req->contactIds(); QContactManagerEngine::updateContactRemoveRequest(req, lastError, errorMap, QContactAbstractRequest::ActiveState); m_requestMgr->addRequest(req); for (int i = 0; i < contactIds.size(); i++) { QContactId contactId = contactIds.at(i); QContactManager::Error error = QContactManager::NoError; if ( (!(contactId.isNull())) && (contactId.managerUri() == QContactJsonDbStr::managerUri()) ) { QContactAbstractRequest::StorageLocation storageLocation = QContactAbstractRequest::StorageLocation(extractStorageLocation(contactId)); QString partition = storageLocationToPartition(storageLocation); if ((storageLocation & ~m_availableStorageLocations) || (partition.isEmpty())) { error = QContactManager::InvalidStorageLocationError; errorMap.insert(i, error); lastError = errorPrecedence(lastError, error); } else { QJsonObject newJsonDbContact; newJsonDbContact.insert(QContactJsonDbStr::type(), QContactJsonDbStr::contactsJsonDbType()); newJsonDbContact.insert(QContactJsonDbStr::uuid(), m_converter->contactIdToUuid(contactId)); if (!makeJsonDbRequest(req, QContactJsonDbRequestManager::RemoveRequest, i, partition, QString(), QList() << newJsonDbContact)) { error = QContactManager::TimeoutError; errorMap.insert(i,error); lastError = errorPrecedence(lastError, error); } } } else { // Set DoesNotExistsError for an id if trying to remove contact with a null // id or id is belonging to another contact manager engine. error = QContactManager::DoesNotExistError; errorMap.insert(i,error); lastError = errorPrecedence(lastError, error); } } if (errorMap.size() == contactIds.size()) { //jsondbrequest could not be created at all //so we remove it from manager and set its state to finished QWaitCondition* waitCondition = m_requestMgr->waitCondition(req); m_requestMgr->removeRequest(req); QContactManagerEngine::updateContactRemoveRequest(req, lastError,errorMap,QContactAbstractRequest::FinishedState); if (waitCondition) waitCondition->wakeAll(); } else { QContactManagerEngine::updateContactRemoveRequest(req, lastError, errorMap, QContactAbstractRequest::ActiveState); } } void QContactJsonDbRequestHandler::handleContactIdFetchRequest(QContactIdFetchRequest *req) { QContactManager::Error error = QContactManager::NoError; m_requestMgr->addRequest(req); QString newJsonDbQuery; if (m_converter->queryFromRequest(req, newJsonDbQuery)) { QContactAbstractRequest::StorageLocations storageLocations = storageLocationsOrDefault(req->storageLocations()); if (storageLocations & ~m_availableStorageLocations) error = QContactManager::InvalidStorageLocationError; QStringList partitions = m_converter->storageLocationsToPartitionNames(storageLocations); if (!partitions.isEmpty()) { foreach (const QString &partition, partitions) { if (!makeJsonDbRequest(req, QContactJsonDbRequestManager::ContactIdFetchRequest, 0, partition, newJsonDbQuery)) { error = errorPrecedence(error, QContactManager::TimeoutError); break; } } } else { error = QContactManager::InvalidStorageLocationError; } } else { error = QContactManager::BadArgumentError; } const QList emptyContactIdList; if (error!=QContactManager::NoError) { QWaitCondition* waitCondition = m_requestMgr->waitCondition(req); m_requestMgr->removeRequest(req); QContactManagerEngine::updateContactIdFetchRequest(req,emptyContactIdList,error, QContactAbstractRequest::FinishedState); if (waitCondition) waitCondition->wakeAll(); } else { QContactManagerEngine::updateContactIdFetchRequest(req,emptyContactIdList,error, QContactAbstractRequest::ActiveState); } } void QContactJsonDbRequestHandler::onJsonDbWatcherNotificationsAvailable(QJsonDbWatcher *jsonDbWatcher, QContactAbstractRequest::StorageLocation storageLocation) { // There is no need for mutex locker since we do not access any request from here. QList notifications = jsonDbWatcher->takeNotifications(); foreach (const QJsonDbNotification ¬ification, notifications) { QJsonObject jsonDbObject = notification.object(); QContactId contactId = m_converter->jsonDbNotificationObjectToContactId(jsonDbObject, storageLocation); switch (notification.action()) { case QJsonDbWatcher::Created: { m_ccs.insertAddedContact(contactId); startTimer(); break; } case QJsonDbWatcher::Updated: { m_ccs.insertChangedContact(contactId); startTimer(); break; } case QJsonDbWatcher::Removed: { m_ccs.insertRemovedContact(contactId); startTimer(); break; } default: break; } } } void QContactJsonDbRequestHandler::startTimer() { // There is no need for mutex locker since we do not access any request data from here. if (!m_timer) { m_timer = new QTimer(this); m_timer->setSingleShot(true); m_timer->setInterval(TIMEOUT_INTERVAL); connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout())); } if (!m_timer->isActive()) m_timer->start(); } void QContactJsonDbRequestHandler::onTimeout() { // There is no need for mutex locker since we do not access any request data from here. if (m_engine) { m_ccs.emitSignals(m_engine); m_ccs.clearAll(); } } void QContactJsonDbRequestHandler::handleResponse(QJsonDbRequest *jsonDbRequest) { QMutexLocker locker(m_reqStateMutex); int contactIndex; QContactJsonDbRequestManager::RequestType requestType; QString partitionName; QContactAbstractRequest *req = m_requestMgr->removeRequest(jsonDbRequest, requestType, contactIndex, &partitionName); // For recovering from no partitions available state. m_availableStorageLocations |= m_converter->partitionNameToStorageLocation(partitionName); // Handle responses for requests having no QContactAbstractRequest associated. switch (requestType) { case QContactJsonDbRequestManager::OrphanRequest: { qWarning() << Q_FUNC_INFO << "Orphaned request:"<< jsonDbRequest; return; } case QContactJsonDbRequestManager::InvalidRequest: { qWarning() << Q_FUNC_INFO << "InvalidrequestType, request:" << jsonDbRequest; return; } default: break; } // Rest of the jsonDbRequest responses should have assosisated QContactAbstractRequest. // However, if client already has deleted the request we just give a warning. if (!req) { qWarning() << Q_FUNC_INFO << "request id" << jsonDbRequest << "missing associated QContactAbstractRequest"; return; } switch (requestType) { case QContactJsonDbRequestManager::PrefetchForSaveRequest: { QContactFetchRequest* preFetchReq = static_cast(req); handleContactSavePrefetchResponse(preFetchReq, jsonDbRequest, contactIndex); break; } case QContactJsonDbRequestManager::UpdateRequest: case QContactJsonDbRequestManager::SaveRequest: { QContactSaveRequest* saveReq = static_cast(req); handleContactSaveResponse(saveReq, jsonDbRequest, contactIndex); break; } case QContactJsonDbRequestManager::FetchRequest: { QContactFetchRequest* fetchReq = static_cast(req); handleContactFetchResponse(fetchReq, jsonDbRequest, partitionName); break; } case QContactJsonDbRequestManager::FetchByIdRequest: { QContactFetchByIdRequest* fetchByIdReq = static_cast(req); handleContactFetchByIdResponse(fetchByIdReq, jsonDbRequest, partitionName); break; } case QContactJsonDbRequestManager::RemoveRequest: { QContactRemoveRequest* removeReq = static_cast(req); handleContactRemoveResponse(removeReq); break; } case QContactJsonDbRequestManager::ContactIdFetchRequest: { QContactIdFetchRequest* idReq = static_cast(req); handleContactIdFetchResponse(idReq, jsonDbRequest); break; } default: break; } } void QContactJsonDbRequestHandler::onJsonDbConnectionError(QtJsonDb::QJsonDbConnection::ErrorCode connectionError, const QString &message) { //currently there are no error codes in QJsonDbConnection //TODO handle them once its there qWarning()<(sender()); int contactIndex; QContactJsonDbRequestManager::RequestType jsonDbRequestType; QString partitionName; QContactAbstractRequest *req = m_requestMgr->removeRequest(request, jsonDbRequestType, contactIndex, &partitionName); switch (jsonDbRequestType) { case QContactJsonDbRequestManager::OrphanRequest: { qWarning() << Q_FUNC_INFO << "Orphaned request with requestId:" << req; return; } case QContactJsonDbRequestManager::InvalidRequest: { qWarning() << Q_FUNC_INFO << "Invalid request with requestId:" << req; return; } default: break; } // Rest of the jsonDbRequest responses should have associated QContactAbstractRequest. // However, if client already has deleted the request we just give a warning. if (!req) { qWarning() << Q_FUNC_INFO << "request id" << req << "missing associated QContactAbstractRequest."; return; } QContactManager::Error contactError = m_converter->jsonDbRequestErrorToContactError(error); if (request && (contactError == QContactManager::InvalidStorageLocationError)) { const QContactAbstractRequest::StorageLocation storageLocation = m_converter->partitionNameToStorageLocation(request->partition()); if (storageLocation & ~supportedStorageLocations()) qWarning() << Q_FUNC_INFO << "Unsupported storage location value:" << storageLocation; m_availableStorageLocations &= ~storageLocation; if (!m_availableStorageLocations) // No partitions available in jsondb, indicate in the error. contactError = errorPrecedence(contactError, QContactManager::MissingPlatformRequirementsError); qCritical("QContacts - JsonDb backend - all storage locations unavailable."); } QContactManager::Error errorStatus = errorPrecedence(req->error(), contactError); switch (jsonDbRequestType) { case QContactJsonDbRequestManager::FetchRequest: { QList emptyContactList; if (m_requestMgr->isRequestCompleted(req)) { QWaitCondition* waitCondition = m_requestMgr->waitCondition(req); m_requestMgr->removeRequest(req); QContactManagerEngine::updateContactFetchRequest (static_cast(req), emptyContactList, errorStatus, QContactAbstractRequest::FinishedState); if (waitCondition) waitCondition->wakeAll(); } else { QContactManagerEngine::updateContactFetchRequest (static_cast(req), emptyContactList, errorStatus, QContactAbstractRequest::ActiveState); } break; } case QContactJsonDbRequestManager::FetchByIdRequest: { QContactFetchByIdRequest *fetchByIdrequest = static_cast(req); if (m_requestMgr->isRequestCompleted(req)) { // There may be already contacts fetched for this request before jsondb error. QMap errorMap = fetchByIdrequest->errorMap(); QContactManager::Error errorToDiscard = QContactManager::NoError; QList contacts = orderedContacts(fetchByIdrequest->contactIds(), fetchByIdrequest->contacts(), &errorMap, &errorToDiscard); QWaitCondition* waitCondition = m_requestMgr->waitCondition(req); m_requestMgr->removeRequest(req); QContactManagerEngine::updateContactFetchByIdRequest(fetchByIdrequest, contacts, errorStatus, errorMap, QContactAbstractRequest::FinishedState); if (waitCondition) waitCondition->wakeAll(); } else { QContactManagerEngine::updateContactFetchByIdRequest(fetchByIdrequest, fetchByIdrequest->contacts(), errorStatus, fetchByIdrequest->errorMap(), QContactAbstractRequest::ActiveState); } break; } // The following two request types are both related to contact save request. // They do share exactly the same handling down below. case QContactJsonDbRequestManager::PrefetchForSaveRequest: case QContactJsonDbRequestManager::SaveRequest: { QContactSaveRequest* saveReq = static_cast(req); QList contacts = saveReq->contacts(); QMap errorMap = saveReq->errorMap(); if (contactError != QContactManager::NoError) { errorMap.insert(contactIndex, contactError); } if (contacts.size() > contactIndex) { qWarning() << Q_FUNC_INFO << "Save request failed for contact at index" << contactIndex << "and contact id" << contacts.at(contactIndex).id(); } else { qWarning() << Q_FUNC_INFO << "Save request failed for contact at index" << contactIndex; } if (m_requestMgr->isRequestCompleted(req)) { QWaitCondition* waitCondition = m_requestMgr->waitCondition(req); m_requestMgr->removeRequest(req); QContactManagerEngine::updateContactSaveRequest(static_cast(req),contacts, contactError,errorMap,QContactAbstractRequest::FinishedState); if (waitCondition) waitCondition->wakeAll(); } else { QContactManagerEngine::updateContactSaveRequest(static_cast(req),contacts, contactError,errorMap,QContactAbstractRequest::ActiveState); } break; } case QContactJsonDbRequestManager::RemoveRequest: { QContactRemoveRequest* removeReq = static_cast(req); QMap errorMap = removeReq->errorMap(); if (contactError != QContactManager::NoError) errorMap.insert(contactIndex, contactError); if (m_requestMgr->isRequestCompleted(req)) { QWaitCondition* waitCondition = m_requestMgr->waitCondition(req); m_requestMgr->removeRequest(req); QContactManagerEngine::updateContactRemoveRequest(removeReq, errorStatus, errorMap, QContactAbstractRequest::FinishedState); if (waitCondition) waitCondition->wakeAll(); } else { // If request not yet completed, just add error to the requests' error map. QContactManagerEngine::updateContactRemoveRequest(removeReq, errorStatus, errorMap, QContactAbstractRequest::ActiveState); } break; } case QContactJsonDbRequestManager::ContactIdFetchRequest: { QContactIdFetchRequest* idFetchReq = static_cast(req); QList ids; // There may be already contact ids fetched for this request before jsondb error. if (idFetchReq) ids = idFetchReq->ids(); if (m_requestMgr->isRequestCompleted(req)) { QWaitCondition* waitCondition = m_requestMgr->waitCondition(req); m_requestMgr->removeRequest(req); QContactManagerEngine::updateContactIdFetchRequest(idFetchReq, ids, contactError, QContactAbstractRequest::FinishedState); if (waitCondition) waitCondition->wakeAll(); } else { QContactManagerEngine::updateContactIdFetchRequest(idFetchReq, ids, contactError, QContactAbstractRequest::ActiveState); } break; } default: break; } } void QContactJsonDbRequestHandler::removeDestroyed(QObject * req) { QMutexLocker locker(m_reqStateMutex); QContactAbstractRequest* aReq = static_cast(req); if ((aReq)&&(!m_reqList.contains(aReq))&&(m_requestMgr->contains(aReq))) { m_reqList.append((aReq)); m_requestMgr->removeRequest(aReq); } } void QContactJsonDbRequestHandler::handleContactSaveResponse(QContactSaveRequest* req, QJsonDbRequest *jsonDbRequest, int index) { QContactManager::Error lastError = req->error(); QMap errorMap = req->errorMap(); QList results = jsonDbRequest->takeResults(); foreach (const QJsonObject &result, results) { QString jsonUuid = result.value(QContactJsonDbStr::uuid()).toString(); if (!jsonUuid.isEmpty()) { QContact contact = req->contacts().at(index); bool isNewContact = (contact.id().isNull() || contact.id().managerUri().isEmpty()); if (isNewContact) { contact.setId(m_converter->uuidtoContactId(jsonUuid, jsonDbRequest->partition())); } m_requestMgr->addContact(req, contact, index); } } if (!m_requestMgr->pendingPrefetchRequests(req) && m_requestMgr->isRequestCompleted(req)) { QList contacts = m_requestMgr->contacts(req); QWaitCondition* waitCondition = m_requestMgr->waitCondition(req); m_requestMgr->removeRequest(req); QContactManagerEngine::updateContactSaveRequest(req, contacts, lastError, errorMap, QContactAbstractRequest::FinishedState); if (waitCondition) waitCondition->wakeAll(); } } void QContactJsonDbRequestHandler::handleContactSavePrefetchResponse(QContactFetchRequest *prefetchReq, QJsonDbRequest *jsonDbRequest, int index) { QContactManager::Error error = QContactManager::NoError; QContactSaveRequest *saveReq = m_requestMgr->removePrefetchRequest(prefetchReq); if (!saveReq) { qWarning() << Q_FUNC_INFO << "prefetch request not found"; return; } QList results = jsonDbRequest->takeResults(); QJsonObject result; // use a local variable instead of const reference in foreach since it is modified in the loop foreach (result, results) { if (result.isEmpty()) { // An empty response for prefetch request means attempt to update a non-existing contact. error = errorPrecedence(error, QContactManager::DoesNotExistError); qWarning() << Q_FUNC_INFO << "Empty prefetch response from jsondb."; } else { // Convert QContact to jsondb contact over the prefetched jsondb contact and save it. QString partition = storageLocationToPartition( extractStorageLocation(saveReq->contacts().at(index).id())); if (!m_converter->toJsonContact(&result, saveReq->contacts().at(index),saveReq->typeMask())) { qWarning() << Q_FUNC_INFO << "Conversion from QContact to QJsonObject failed."; // Converter failed to map this QContact to Jsondb contact. error = errorPrecedence(error, QContactManager::BadArgumentError); } else { // Make save request for this contact. if (!makeJsonDbRequest(saveReq, QContactJsonDbRequestManager::UpdateRequest, index, partition, QString(), QList() << result)) { error = errorPrecedence(error, QContactManager::TimeoutError); } else { return; } } } } // In a rare case of an error we need to update the error map and last error. QMap errorMap = saveReq->errorMap(); if (results.isEmpty()) { error = errorPrecedence(error, QContactManager::DoesNotExistError); } errorMap.insert(index, error); error = errorPrecedence(error, saveReq->error()); QList contacts = m_requestMgr->contacts(saveReq); if ((!m_requestMgr->pendingPrefetchRequests(saveReq)) && m_requestMgr->isRequestCompleted(saveReq)) { // Error happens to the last contact in the save request and the whole request gets finished. QWaitCondition* waitCondition = m_requestMgr->waitCondition(saveReq); m_requestMgr->removeRequest(saveReq); QContactManagerEngine::updateContactSaveRequest(saveReq, contacts, error, errorMap, QContactAbstractRequest::FinishedState); if (waitCondition) waitCondition->wakeAll(); } else { QContactManagerEngine::updateContactSaveRequest(saveReq, contacts, error, errorMap, QContactAbstractRequest::ActiveState); } qWarning() << Q_FUNC_INFO << "Failed for" << saveReq->contacts().at(index).id(); } void QContactJsonDbRequestHandler::handleContactFetchResponse(QContactFetchRequest *req, QJsonDbRequest *jsonDbRequest, const QString &partitionName) { QList contacts; QContactManager::Error error = QContactManager::NoError; if (req) { error = req->error(); contacts = req->contacts(); } QList results = jsonDbRequest->takeResults(); foreach (const QJsonObject &result, results) { if (!result.isEmpty()) { QContact contact; m_converter->toQContact(result, &contact, partitionName); contacts.append(contact); } } if ((contacts.isEmpty()) || results.isEmpty()) error = errorPrecedence(error, QContactManager::DoesNotExistError); if (m_requestMgr->isRequestCompleted(req)) { QWaitCondition* waitCondition = m_requestMgr->waitCondition(req); m_requestMgr->removeRequest(req); QContactManagerEngine::updateContactFetchRequest (req, contacts, error, QContactAbstractRequest::FinishedState); if (waitCondition) waitCondition->wakeAll(); } else { QContactManagerEngine::updateContactFetchRequest(req, contacts, error, QContactAbstractRequest::ActiveState); } } void QContactJsonDbRequestHandler::handleContactFetchByIdResponse(QContactFetchByIdRequest *req, QJsonDbRequest *jsonDbRequest, const QString &partitionName) { QList fetchedContacts; QContactManager::Error error = QContactManager::NoError; if (req) { error = req->error(); fetchedContacts = req->contacts(); } QList resultsFromResponse = jsonDbRequest->takeResults(); foreach (const QJsonObject &result, resultsFromResponse) { if (!result.isEmpty()) { QContact contact; if (m_converter->toQContact(result, &contact, partitionName)) fetchedContacts.append(contact); } } if (m_requestMgr->isRequestCompleted(req)) { QList contacts; QMap errorMap; QList idsFromRequest; if (req) idsFromRequest = req->contactIds(); QContactManager::Error errorFromOrdering = QContactManager::NoError; contacts = orderedContacts(idsFromRequest, fetchedContacts, &errorMap, &errorFromOrdering); error = errorPrecedence(error, errorFromOrdering); QWaitCondition* waitCondition = m_requestMgr->waitCondition(req); m_requestMgr->removeRequest(req); QContactManagerEngine::updateContactFetchByIdRequest(req, contacts, error, errorMap, QContactAbstractRequest::FinishedState); if (waitCondition) waitCondition->wakeAll(); } else { QMap emptyErrorMap; // Error map can be properly updated only when request is completely finished. QContactManagerEngine::updateContactFetchByIdRequest(req, fetchedContacts, error, emptyErrorMap, QContactAbstractRequest::ActiveState); } } QList QContactJsonDbRequestHandler::orderedContacts(const QList &ids, const QList &contacts, QMap *errorMap, QContactManager::Error *lastError) { Q_ASSERT(errorMap); Q_ASSERT(lastError); // Order contacts in the same order with given ids. // First build an index into contacts, value is index into unordered list. QHash idToIndexMap; for (int i = 0; i < contacts.size(); i++) { idToIndexMap.insert(contacts.at(i).id(), i); } // Then ind the order in which the contacts results should be presented // and build ordered contact list and related error map. int index=0; QList ordered; foreach (const QContactId &id, ids) { if (!idToIndexMap.contains(id)) { errorMap->insert(index, QContactManager::DoesNotExistError); *lastError = QContactManager::DoesNotExistError; ordered.append(QContact()); } else { ordered.append(contacts.at(idToIndexMap[id])); } index++; } return ordered; } void QContactJsonDbRequestHandler::handleContactRemoveResponse(QContactRemoveRequest *req) { if (m_requestMgr->isRequestCompleted(req)) { QWaitCondition* waitCondition = m_requestMgr->waitCondition(req); m_requestMgr->removeRequest(req); QContactManager::Error lastError = req->error(); QMap errorMap = req->errorMap(); QContactManagerEngine::updateContactRemoveRequest(req, lastError, errorMap, QContactAbstractRequest::FinishedState); if (waitCondition) waitCondition->wakeAll(); } } void QContactJsonDbRequestHandler::handleContactIdFetchResponse(QContactIdFetchRequest* req, QJsonDbRequest *jsonDbRequest) { QList ids; QContactManager::Error error = QContactManager::NoError; if (req) { ids = req->ids(); error = req->error(); } QList results = jsonDbRequest->takeResults(); foreach (const QJsonObject &result, results) { QString uuid = result.value(QContactJsonDbStr::uuid()).toString(); ids.append(m_converter->uuidtoContactId(uuid, jsonDbRequest->partition())); } if (ids.isEmpty() || results.isEmpty()) error = errorPrecedence(error, QContactManager::DoesNotExistError); if (m_requestMgr->isRequestCompleted(req)) { QWaitCondition* waitCondition = m_requestMgr->waitCondition(req); m_requestMgr->removeRequest(req); QContactManagerEngine::updateContactIdFetchRequest(req, ids, error, QContactAbstractRequest::FinishedState); if (waitCondition) waitCondition->wakeAll(); } else { QContactManagerEngine::updateContactIdFetchRequest(req, ids, error, QContactAbstractRequest::ActiveState); } } bool QContactJsonDbRequestHandler::makeJsonDbRequest(QContactAbstractRequest *contactRequest, QContactJsonDbRequestManager::RequestType jsonDbRequestType, int index, const QString &partition, const QString &query, const QList &objects) { QJsonDbRequest *request; switch (jsonDbRequestType) { case QContactJsonDbRequestManager::PrefetchForSaveRequest: request = new QJsonDbReadRequest(query, this); break; case QContactJsonDbRequestManager::SaveRequest: request = new QJsonDbCreateRequest(objects,this); break; case QContactJsonDbRequestManager::UpdateRequest: request = new QJsonDbUpdateRequest(objects, this); break; case QContactJsonDbRequestManager::ContactIdFetchRequest: request = new QJsonDbReadRequest(query, this); break; case QContactJsonDbRequestManager::RemoveRequest: request = new QJsonDbRemoveRequest(objects,this); break; case QContactJsonDbRequestManager::FetchRequest: request = new QJsonDbReadRequest(query, this); break; case QContactJsonDbRequestManager::FetchByIdRequest: request = new QJsonDbReadRequest(query, this); break; default: request = NULL; break; } QJsonDbWriteRequest *writeRequest = qobject_cast(request); if (writeRequest) writeRequest->setConflictResolutionMode(QJsonDbWriteRequest::Replace); request->setPartition(partition); connect(request, SIGNAL(error(QtJsonDb::QJsonDbRequest::ErrorCode,QString)), this, SLOT(onJsonDbRequestError(QtJsonDb::QJsonDbRequest::ErrorCode, QString))); connect(request, SIGNAL(finished()), this, SLOT(onJsonDbRequestFinished())); if (m_jsonDbConnection->send(request)) { m_requestMgr->addRequest(request, jsonDbRequestType, contactRequest, index); return true; } else { delete request; return false; } } void QContactJsonDbRequestHandler::onJsonDbRequestFinished() { QJsonDbRequest *request = qobject_cast(sender()); if (request) handleResponse(request); } QContactJsonDbPartitionWatcher::QContactJsonDbPartitionWatcher(QContactJsonDbRequestHandler *requestHandler, QJsonDbWatcher *jsonDbWatcher, QContactAbstractRequest::StorageLocation storageLocation) : QObject(requestHandler), m_requestHandler(requestHandler), m_jsonDbWatcher(jsonDbWatcher), m_storageLocation(storageLocation) { connect(m_jsonDbWatcher, SIGNAL(error(QtJsonDb::QJsonDbWatcher::ErrorCode,QString)), this, SLOT(onJsonDbWatcherError(QtJsonDb::QJsonDbWatcher::ErrorCode,QString))); connect(m_jsonDbWatcher, SIGNAL(notificationsAvailable(int)), this, SLOT(onJsonDbWatcherNotificationsAvailable())); } void QContactJsonDbPartitionWatcher::onJsonDbWatcherNotificationsAvailable() { m_requestHandler->onJsonDbWatcherNotificationsAvailable(m_jsonDbWatcher, m_storageLocation); } void QContactJsonDbPartitionWatcher::onJsonDbWatcherError(QtJsonDb::QJsonDbWatcher::ErrorCode error, QString message) { // TODO handle the error when partition is supported if (error!=QJsonDbWatcher::NoError) qCritical()< #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "qcontactjsondbrequestmanager.h" QT_FORWARD_DECLARE_CLASS(QTimer) QT_USE_NAMESPACE_JSONDB QT_BEGIN_NAMESPACE_CONTACTS class QContactJsonDbEngine; class QContactJsonDbConverter; class QContactJsonDbRequestHandler : public QObject { Q_OBJECT public: QContactJsonDbRequestHandler(); virtual ~QContactJsonDbRequestHandler(); void setEngine(QContactJsonDbEngine* engine); void onJsonDbWatcherNotificationsAvailable(QJsonDbWatcher *jsonDbWatcher, QContactAbstractRequest::StorageLocation storageLocation); public slots: bool waitForRequestFinished(QContactAbstractRequest* req, int msecs); void handleRequest(QContactAbstractRequest* req); void handleResponse(QJsonDbRequest *request); void init(); void removeDestroyed(QObject *); void onJsonDbConnectionError(QtJsonDb::QJsonDbConnection::ErrorCode error, const QString &message); void onJsonDbRequestError(QtJsonDb::QJsonDbRequest::ErrorCode error, QString message); void onJsonDbRequestFinished(); private slots: void onTimeout(); private: void createWatcherForStorageLocation(QContactAbstractRequest::StorageLocation storageLocation); QString storageLocationToPartition(QContactAbstractRequest::StorageLocation storageLocation); QContactAbstractRequest::StorageLocations storageLocationsOrDefault( QContactAbstractRequest::StorageLocations storageLocation); QContactAbstractRequest::StorageLocation extractStorageLocation(const QContactId &id); QContactAbstractRequest::StorageLocations extractStorageLocations(const QList &contactIds); inline static QContactAbstractRequest::StorageLocations supportedStorageLocations() { return QContactAbstractRequest::UserDataStorage | QContactAbstractRequest::SystemStorage; } void handleContactSaveRequest(QContactSaveRequest* req); void handleContactFetchRequest(QContactFetchRequest* req); void handleContactRemoveRequest(QContactRemoveRequest* req); void handleContactIdFetchRequest(QContactIdFetchRequest* req); void handleContactFetchByIdRequest(QContactFetchByIdRequest* req); void handleContactSaveResponse(QContactSaveRequest* saveReq, QJsonDbRequest *request, int contactIndex); void handleContactSavePrefetchResponse(QContactFetchRequest *prefetchReq, QJsonDbRequest *request, int contactIndex); void handleContactFetchResponse(QContactFetchRequest* fetchReq, QJsonDbRequest *request, const QString &partitionName); void handleContactRemoveResponse(QContactRemoveRequest* removeReq); void handleContactIdFetchResponse(QContactIdFetchRequest* idReq, QJsonDbRequest *request); void handleContactFetchByIdResponse(QContactFetchByIdRequest *req, QJsonDbRequest *jsonDbRequest, const QString &partitionName); QList orderedContacts(const QList &ids, const QList &contacts, QMap *errorMap, QContactManager::Error *lastError); bool makeJsonDbRequest(QContactAbstractRequest *contactRequest, QContactJsonDbRequestManager::RequestType jsonDbRequestType, int index, const QString &partition, const QString &query = QString(), const QList &objects = QList()); void startTimer(); inline static QContactManager::Error errorPrecedence(QContactManager::Error firstError, QContactManager::Error secondError) { // Currently this makes a simple precedence for error codes. // In case of nonidentical mapping is needed you need to modify this. return (firstError < secondError) ? secondError : firstError; } private: Q_DISABLE_COPY(QContactJsonDbRequestHandler) private: QContactJsonDbEngine* m_engine; QJsonDbConnection *m_jsonDbConnection; QContactJsonDbRequestManager* m_requestMgr; QContactJsonDbConverter* m_converter; // Mutex to make request state changes atomic. // Main thread access the same requests we store in m_reqList. QMutex* m_reqStateMutex; QList m_reqList; // For contact change notifications. QContactChangeSet m_ccs; static const int TIMEOUT_INTERVAL; QTimer *m_timer; // For maintaining storage locations availability status. QContactAbstractRequest::StorageLocations m_availableStorageLocations; }; class QContactJsonDbPartitionWatcher : public QObject { Q_OBJECT public: QContactJsonDbPartitionWatcher(QContactJsonDbRequestHandler *requestHandler, QJsonDbWatcher *jsonDbWatcher, QContactAbstractRequest::StorageLocation storageLocation); private slots: void onJsonDbWatcherNotificationsAvailable(); void onJsonDbWatcherError(QtJsonDb::QJsonDbWatcher::ErrorCode error, QString message); private: Q_DISABLE_COPY(QContactJsonDbPartitionWatcher) private: QContactJsonDbRequestHandler *m_requestHandler; QJsonDbWatcher *m_jsonDbWatcher; QContactAbstractRequest::StorageLocation m_storageLocation; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTJSONDBREQUESTHANDLER_H src/plugins/contacts/jsondb/qcontactjsondbrequestmanager.cpp000066400000000000000000000177741233466112000251250ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactjsondbrequestmanager.h" #include #include QT_BEGIN_NAMESPACE_CONTACTS QContactJsonDbRequestManager::QContactJsonDbRequestManager() { m_operationMutex = new QMutex(); } QContactJsonDbRequestManager::~QContactJsonDbRequestManager() { delete m_operationMutex; } void QContactJsonDbRequestManager::addRequest(QContactAbstractRequest* req, QList items) { QMutexLocker locker(m_operationMutex); if (!m_activeRequests.contains(req)) { QContactRequestData* newData = new QContactRequestData(); newData->m_contactList = items; if (m_inactiveRequests.contains(req)) { newData->m_waitCondition = m_inactiveRequests.value(req); m_inactiveRequests.remove(req); } else { newData->m_waitCondition = 0; } m_activeRequests.insert(req, newData); } } void QContactJsonDbRequestManager::removeRequest(QContactAbstractRequest* req) { QMutexLocker locker(m_operationMutex); if (m_activeRequests.contains(req)) { delete m_activeRequests.value(req); m_activeRequests.remove(req); } } void QContactJsonDbRequestManager::addPrefetchRequest(QContactFetchRequest *prefetchReq, QContactSaveRequest *saveReq) { QMutexLocker locker(m_operationMutex); if (!m_prefetchRequestsMap.contains(prefetchReq)) { m_prefetchRequestsMap.insert(prefetchReq, saveReq); } } QContactSaveRequest* QContactJsonDbRequestManager::removePrefetchRequest(QContactFetchRequest *prefetchReq) { QMutexLocker locker(m_operationMutex); if (m_prefetchRequestsMap.contains(prefetchReq)) { return m_prefetchRequestsMap.take(prefetchReq); } return 0; } void QContactJsonDbRequestManager::addRequest(QJsonDbRequest *jsonDbRequest, RequestType requestType, QContactAbstractRequest *req, int contactIndex) { QMutexLocker locker(m_operationMutex); m_jsonDbRequestTypeMap.insert(jsonDbRequest, requestType); if (m_activeRequests.contains(req)) { m_activeRequests.value(req)->m_jsonDbRequestMap.insert(jsonDbRequest, contactIndex); } } QContactAbstractRequest* QContactJsonDbRequestManager::removeRequest(QJsonDbRequest *jsonDbRequest, RequestType &requestType, int &contactIndex, QString *partitionName) { QMutexLocker locker(m_operationMutex); if (m_jsonDbRequestTypeMap.contains(jsonDbRequest)) { requestType = m_jsonDbRequestTypeMap.value(jsonDbRequest); m_jsonDbRequestTypeMap.remove(jsonDbRequest); if (requestType == OrphanRequest || requestType == InvalidRequest) return 0; } else { qWarning() << Q_FUNC_INFO << "Could not find jsondbRequest:" << jsonDbRequest; requestType = InvalidRequest; return 0; } QList reqList = m_activeRequests.keys(); for (int i = 0; i < reqList.size(); i++) { QContactAbstractRequest* req = reqList.at(i); QMap* requestMap = &(m_activeRequests.value(req)->m_jsonDbRequestMap); if (requestMap->contains(jsonDbRequest)) { *partitionName = jsonDbRequest->partition(); contactIndex = requestMap->value(jsonDbRequest); requestMap->remove(jsonDbRequest); return reqList.at(i); } } // request already deleted so mark this as orphan. requestType = OrphanRequest; return 0; } bool QContactJsonDbRequestManager::setWaitCondition(QContactAbstractRequest *req, QWaitCondition *waitCondition) { // This function is called by JsonDbEngine, which is in another thread QMutexLocker locker(m_operationMutex); if (m_activeRequests.contains(req)) { m_activeRequests.value(req)->m_waitCondition = waitCondition; return true; } else if (!m_inactiveRequests.contains(req)) { // Request and wait condition stored here if waitForRequestFinished function is called without starting // the request or after starting, but before slot JsonDbEngineRequestHandler::handleRequest is called. m_inactiveRequests.insert(req, waitCondition); return false; } //This should never happen... return false; } QWaitCondition* QContactJsonDbRequestManager::waitCondition(QContactAbstractRequest *req) { QMutexLocker locker(m_operationMutex); if (m_activeRequests.contains(req)) { return m_activeRequests.value(req)->m_waitCondition; } return 0; } void QContactJsonDbRequestManager::removeWaitCondition(QContactAbstractRequest *req) { QMutexLocker locker(m_operationMutex); // The request is found only if waitForRequestFinished function was called for an Inactive request // which was never started. if (!m_inactiveRequests.contains(req)) { m_inactiveRequests.remove(req); } } void QContactJsonDbRequestManager::addContact(QContactAbstractRequest* req, QContact item, int contactIndex) { QMutexLocker locker(m_operationMutex); if (m_activeRequests.contains(req)) { m_activeRequests.value(req)->m_contactList.replace(contactIndex, item); } } QList QContactJsonDbRequestManager::contacts(QContactAbstractRequest* req) { QMutexLocker locker(m_operationMutex); if (m_activeRequests.contains(req)) return m_activeRequests.value(req)->m_contactList; else return QList(); } bool QContactJsonDbRequestManager::isRequestCompleted(QContactAbstractRequest* req) { QMutexLocker locker(m_operationMutex); if (m_activeRequests.contains(req)) { return m_activeRequests.value(req)->m_jsonDbRequestMap.isEmpty(); } return true; } bool QContactJsonDbRequestManager::pendingPrefetchRequests(QContactSaveRequest *saveReq) { QMutexLocker locker(m_operationMutex); return ((m_activeRequests.contains(saveReq)) && (!m_prefetchRequestsMap.keys(saveReq).isEmpty())); } bool QContactJsonDbRequestManager::contains(QContactAbstractRequest *req) const { QMutexLocker locker(m_operationMutex); return m_activeRequests.contains(req);// || m_inactiveRequests.contains(req); } #include "moc_qcontactjsondbrequestmanager.cpp" QT_END_NAMESPACE_CONTACTS src/plugins/contacts/jsondb/qcontactjsondbrequestmanager.h000066400000000000000000000114741233466112000245610ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTJSONDBREQUESTMANAGER_H #define QCONTACTJSONDBREQUESTMANAGER_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include #include #include #include QT_USE_NAMESPACE_JSONDB QT_BEGIN_NAMESPACE_CONTACTS class QContactRequestData { public: QContactRequestData() {} QList m_contactList; QMap m_jsonDbRequestMap; QWaitCondition* m_waitCondition; }; class QContactJsonDbRequestManager : public QObject { Q_OBJECT public: enum RequestType { InvalidRequest = 0, OrphanRequest, PrefetchForSaveRequest, SaveRequest, UpdateRequest, FetchRequest, ContactIdFetchRequest, RemoveRequest, FetchByIdRequest }; QContactJsonDbRequestManager(); ~QContactJsonDbRequestManager(); void addRequest(QContactAbstractRequest* req, QList contacts = QList()); void removeRequest(QContactAbstractRequest* req); void addPrefetchRequest(QContactFetchRequest *prefetchReq, QContactSaveRequest *saveReq); QContactSaveRequest* removePrefetchRequest(QContactFetchRequest *prefetchReq); void addRequest(QJsonDbRequest *jsonDbRequest, RequestType requestType, QContactAbstractRequest *req = 0, int contactIndex = -1); QContactAbstractRequest* removeRequest(QJsonDbRequest *jsonDbRequest, RequestType &requestType, int &contactIndex, QString *partitionName); bool setWaitCondition(QContactAbstractRequest* req, QWaitCondition* waitCondition); QWaitCondition* waitCondition(QContactAbstractRequest* req); void removeWaitCondition(QContactAbstractRequest* req); void addContact(QContactAbstractRequest* req, QContact item, int contactIndex); QList contacts(QContactAbstractRequest* req); bool isRequestCompleted(QContactAbstractRequest* req); bool pendingPrefetchRequests(QContactSaveRequest *saveReq); bool contains(QContactAbstractRequest* req) const; signals: public slots: private: Q_DISABLE_COPY(QContactJsonDbRequestManager) private: QMap m_activeRequests; QMap m_inactiveRequests; QMap m_prefetchRequestsMap; QMap m_jsonDbRequestTypeMap; QMutex* m_operationMutex; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTJSONDBREQUESTMANAGER_H src/plugins/contacts/jsondb/qcontactjsondbstring.cpp000066400000000000000000000037271233466112000234010ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactjsondbstring.h" src/plugins/contacts/jsondb/qcontactjsondbstring.h000066400000000000000000000175341233466112000230470ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Pim Module ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTJSONDBSTRING_H #define QCONTACTJSONDBSTRING_H #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactJsonDbStr { public: // Version const static int ContactJsonDbEngineVersion = 1.0; // Engine meta-data inline const static QString contactJsonDbEngineName() { return QStringLiteral("jsondb"); } inline const static QString managerName() { return QStringLiteral("qtcontacts:jsondb::"); } inline const static QString managerUri() { return QStringLiteral("qtcontacts:jsondb:"); } // Jsondb column names inline const static QString type() { return QStringLiteral("_type"); } inline const static QString uuid() { return QStringLiteral("_uuid"); } inline const static QString version() { return QStringLiteral("_version"); } // Jsondb field names inline const static QString nameDefinitionName() { return QStringLiteral("name"); } inline const static QString genderDefinitionName() { return QStringLiteral("gender"); } inline const static QString phoneNumberDefinitionName() { return QStringLiteral("phones"); } inline const static QString emailAddressDefinitionName() { return QStringLiteral("emails"); } inline const static QString urlDefinitionName() { return QStringLiteral("urls"); } inline const static QString nicknameDefinitionName() { return QStringLiteral("nickname"); } inline const static QString displayLabelDefinitionName() { return QStringLiteral("displayLabel"); } inline const static QString addressDefinitionName() { return QStringLiteral("addresses"); } inline const static QString birthdayDefinitionName() { return QStringLiteral("birthday"); } inline const static QString avatarDefinitionName() { return QStringLiteral("photoUrl"); } inline const static QString ringtoneDefinitionName() { return QStringLiteral("ringtoneUrl"); } inline const static QString organizationDefinitionName() { return QStringLiteral("organizations"); } inline const static QString noteDefinitionName() { return QStringLiteral("note"); } inline const static QString syncTargetDefinitionName() { return QStringLiteral("synctarget"); } inline const static QString guidDefinitionName() { return QStringLiteral("guid"); } inline const static QString nameFieldFirstName() { return QStringLiteral("firstName"); } inline const static QString nameFieldLastName() { return QStringLiteral("lastName"); } inline const static QString nameFieldMiddleName() { return QStringLiteral("middleName"); } inline const static QString nameFieldPrefix() { return QStringLiteral("prefix"); } inline const static QString nameFieldSuffix() { return QStringLiteral("suffix"); } inline const static QString addressFieldCountry() { return QStringLiteral("country"); } inline const static QString addressFieldRegion() { return QStringLiteral("region"); } inline const static QString addressFieldLocality() { return QStringLiteral("locality"); } inline const static QString addressFieldPostcode() { return QStringLiteral("postcode"); } inline const static QString addressFieldPostOfficeBox() { return QStringLiteral("postOfficeBox"); } inline const static QString addressFieldStreet() { return QStringLiteral("street"); } inline const static QString organizationFieldName() { return QStringLiteral("name"); } inline const static QString organizationFieldDepartment() { return QStringLiteral("department"); } inline const static QString organizationFieldTitle() { return QStringLiteral("title"); } inline const static QString organizationFieldRole() { return QStringLiteral("role"); } inline const static QString organizationFieldAssistantName() { return QStringLiteral("assistantName"); } inline const static QString organizationFieldLogoUrl() { return QStringLiteral("logoUrl"); } inline const static QString organizationFieldStartDate() { return QStringLiteral("startDate"); } inline const static QString organizationFieldEndDate() { return QStringLiteral("endDate"); } //JsonDb enums inline const static QString genderMale() { return QStringLiteral("male"); } inline const static QString genderFemale() { return QStringLiteral("female"); } inline const static QString genderOther() { return QStringLiteral("other"); } // Jsondb Contexts and Subtypes inline const static QString contextHome() { return QStringLiteral("home"); } inline const static QString contextWork() { return QStringLiteral("work"); } inline const static QString contextOther() { return QStringLiteral("other"); } inline const static QString subTypePref() { return QStringLiteral("pref"); } inline const static QString subTypeFax() { return QStringLiteral("fax"); } inline const static QString subTypeCell() { return QStringLiteral("cell"); } inline const static QString subTypeVideo() { return QStringLiteral("video"); } inline const static QString subTypeLandline() { return QStringLiteral("landline"); } inline const static QString contactDetails() { return QStringLiteral("details"); } // Jsondb actions inline const static QString actionCreate() { return QStringLiteral("create"); } inline const static QString actionUpdate() { return QStringLiteral("update"); } inline const static QString actionRemove() { return QStringLiteral("remove"); } // partition names inline const static QString userDataPartition() { return QStringLiteral("com.nokia.mt.User"); } inline const static QString systemPartition() { return QStringLiteral("com.nokia.mt.System"); } // Queries related inline const static QString contactsJsonDbType() { return QStringLiteral("com.nokia.mt.contacts.Contact"); } inline const static QString contactsJsonDbNotificationQuery() { return QStringLiteral("[?_type in [\"com.nokia.mt.contacts.Contact\"]]"); } inline const static QString uuidSelectQuery() { return QStringLiteral("[={_uuid:_uuid}]"); } }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTJSONDBSTRING_H src/plugins/contacts/memory/000077500000000000000000000000001233466112000164525ustar00rootroot00000000000000src/plugins/contacts/memory/memory.json000066400000000000000000000000351233466112000206530ustar00rootroot00000000000000{ "Keys": [ "memory" ] } src/plugins/contacts/memory/memory.pro000066400000000000000000000003121233466112000205000ustar00rootroot00000000000000TARGET = qtcontacts_memory QT = core contacts PLUGIN_TYPE = contacts load(qt_plugin) HEADERS += \ qcontactmemorybackend_p.h SOURCES += \ qcontactmemorybackend.cpp OTHER_FILES += memory.json src/plugins/contacts/memory/qcontactmemorybackend.cpp000066400000000000000000001160371233466112000235430ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactmemorybackend_p.h" #ifndef QT_NO_DEBUG_STREAM #include #endif #include #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS QContactManagerEngine* QContactMemoryEngineFactory::engine(const QMap ¶meters, QContactManager::Error *error) { Q_UNUSED(error); QContactMemoryEngine *ret = QContactMemoryEngine::createMemoryEngine(parameters); return ret; } QContactEngineId* QContactMemoryEngineFactory::createContactEngineId(const QMap ¶meters, const QString &engineIdString) const { Q_UNUSED(parameters); QContactMemoryEngineId *retn = new QContactMemoryEngineId(parameters, engineIdString); return retn; } QString QContactMemoryEngineFactory::managerName() const { return QString::fromLatin1("memory"); } /*! \class QContactMemoryEngine \inmodule QtContacts \brief The QContactMemoryEngine class provides an in-memory implementation of a contacts backend. \internal It may be used as a reference implementation, or when persistent storage is not required. During construction, it will load the in-memory data associated with the memory store identified by the "id" parameter from the given parameters if it exists, or a new, anonymous store if it does not. Data stored in this engine is only available in the current process. This engine supports sharing, so an internal reference count is increased whenever a manager uses this backend, and is decreased when the manager no longer requires this engine. */ /* static data for manager class */ QMap QContactMemoryEngine::engineDatas; /*! * Factory function for creating a new in-memory backend, based * on the given \a parameters. * * The same engine will be returned for multiple calls with the * same value for the "id" parameter, while one of them is in scope. */ QContactMemoryEngine* QContactMemoryEngine::createMemoryEngine(const QMap ¶meters) { bool anonymous = false; QString idValue = parameters.value(QStringLiteral("id")); if (idValue.isNull() || idValue.isEmpty()) { // no store given? new, anonymous store. idValue = QUuid::createUuid().toString(); anonymous = true; } QContactMemoryEngineData *data = engineDatas.value(idValue); if (data) { data->m_refCount.ref(); } else { data = new QContactMemoryEngineData(); data->m_id = idValue; data->m_anonymous = anonymous; engineDatas.insert(idValue, data); } return new QContactMemoryEngine(data); } /*! * Constructs a new in-memory backend which shares the given \a data with * other shared memory engines. */ QContactMemoryEngine::QContactMemoryEngine(QContactMemoryEngineData *data) : d(data) { qRegisterMetaType("QContactAbstractRequest::State"); qRegisterMetaType >("QList"); qRegisterMetaType("QContactId"); d->m_managerUri = managerUri(); d->m_sharedEngines.append(this); } /*! Frees any memory used by this engine */ QContactMemoryEngine::~QContactMemoryEngine() { d->m_sharedEngines.removeAll(this); if (!d->m_refCount.deref()) { engineDatas.remove(d->m_id); delete d; } } /*! \reimp */ QString QContactMemoryEngine::managerName() const { return QStringLiteral("memory"); } /*! \reimp */ QMap QContactMemoryEngine::managerParameters() const { QMap params; params.insert(QStringLiteral("id"), d->m_id); return params; } /*! \reimp */ bool QContactMemoryEngine::setSelfContactId(const QContactId &contactId, QContactManager::Error *error) { if (contactId.isNull() || d->m_contactIds.contains(contactId)) { *error = QContactManager::NoError; QContactId oldId = d->m_selfContactId; d->m_selfContactId = contactId; QContactChangeSet changeSet; changeSet.setOldAndNewSelfContactId(QPair(oldId, contactId)); d->emitSharedSignals(&changeSet); return true; } *error = QContactManager::DoesNotExistError; return false; } /*! \reimp */ QContactId QContactMemoryEngine::selfContactId(QContactManager::Error *error) const { *error = QContactManager::DoesNotExistError; if (!d->m_selfContactId.isNull()) *error = QContactManager::NoError; return d->m_selfContactId; } /*! \reimp */ QContact QContactMemoryEngine::contact(const QContactId &contactId, const QContactFetchHint &fetchHint, QContactManager::Error *error) const { Q_UNUSED(fetchHint); // no optimizations are possible in the memory backend; ignore the fetch hint. int index = d->m_contactIds.indexOf(contactId); if (index != -1) { // found the contact successfully. *error = QContactManager::NoError; return d->m_contacts.at(index); } *error = QContactManager::DoesNotExistError; return QContact(); } /*! \reimp */ QList QContactMemoryEngine::contactIds(const QContactFilter &filter, const QList &sortOrders, QContactManager::Error *error) const { /* Special case the fast case */ if (filter.type() == QContactFilter::DefaultFilter && sortOrders.count() == 0) { return d->m_contactIds; } else { QList clist = contacts(filter, sortOrders, QContactFetchHint(), error); /* Extract the ids */ QList ids; foreach (const QContact &c, clist) ids.append(c.id()); return ids; } } /*! \reimp */ QList QContactMemoryEngine::contacts(const QContactFilter &filter, const QList &sortOrders, const QContactFetchHint &fetchHint, QContactManager::Error *error) const { Q_UNUSED(fetchHint); // no optimizations are possible in the memory backend; ignore the fetch hint. Q_UNUSED(error); QList sorted; /* First filter out contacts - check for default filter first */ if (filter.type() == QContactFilter::DefaultFilter) { foreach(const QContact&c, d->m_contacts) { QContactManagerEngine::addSorted(&sorted,c, sortOrders); } } else { foreach(const QContact&c, d->m_contacts) { if (QContactManagerEngine::testFilter(filter, c)) QContactManagerEngine::addSorted(&sorted,c, sortOrders); } } return sorted; } /*! Saves the given contact \a theContact, storing any error to \a error and filling the \a changeSet with ids of changed contacts as required Returns true if the operation was successful otherwise false. */ bool QContactMemoryEngine::saveContact(QContact *theContact, QContactChangeSet &changeSet, QContactManager::Error *error) { return saveContact(theContact, changeSet, error, QList()); } /*! \reimp */ bool QContactMemoryEngine::saveContacts(QList *contacts, QMap *errorMap, QContactManager::Error *error) { return saveContacts(contacts, errorMap, error, QList()); } /*! Removes the contact identified by the given \a contactId, storing any error to \a error and filling the \a changeSet with ids of changed contacts and relationships as required. Returns true if the operation was successful otherwise false. */ bool QContactMemoryEngine::removeContact(const QContactId &contactId, QContactChangeSet &changeSet, QContactManager::Error *error) { int index = d->m_contactIds.indexOf(contactId); if (index == -1) { *error = QContactManager::DoesNotExistError; return false; } // remove the contact from any relationships it was in. QContact thisContact = d->m_contacts.at(index); QList allRelationships = relationships(QString(), thisContact, QContactRelationship::Either, error); if (*error != QContactManager::NoError && *error != QContactManager::DoesNotExistError) { *error = QContactManager::UnspecifiedError; // failed to clean up relationships return false; } // this is meant to be a transaction, so if any of these fail, we're in BIG TROUBLE. // a real backend will use DBMS transactions to ensure database integrity. removeRelationships(allRelationships, 0, error); // having cleaned up the relationships, remove the contact from the lists. d->m_contacts.removeAt(index); d->m_contactIds.removeAt(index); *error = QContactManager::NoError; // and if it was the self contact, reset the self contact id if (contactId == d->m_selfContactId) { d->m_selfContactId = QContactId(); changeSet.setOldAndNewSelfContactId(QPair(contactId, QContactId())); } changeSet.insertRemovedContact(contactId); return true; } /*! \reimp */ bool QContactMemoryEngine::removeContacts(const QList &contactIds, QMap *errorMap, QContactManager::Error *error) { if (contactIds.count() == 0) { *error = QContactManager::BadArgumentError; return false; } QContactChangeSet changeSet; QContactId current; QContactManager::Error operationError = QContactManager::NoError; for (int i = 0; i < contactIds.count(); i++) { current = contactIds.at(i); if (!removeContact(current, changeSet, error)) { operationError = *error; if (errorMap) errorMap->insert(i, operationError); } } *error = operationError; d->emitSharedSignals(&changeSet); // return false if some errors occurred return (*error == QContactManager::NoError); } /*! \reimp */ QList QContactMemoryEngine::relationships(const QString &relationshipType, const QContact &participant, QContactRelationship::Role role, QContactManager::Error *error) const { QContact defaultContact; QList retn; for (int i = 0; i < d->m_relationships.size(); i++) { QContactRelationship curr = d->m_relationships.at(i); // check that the relationship type matches if (curr.relationshipType() != relationshipType && !relationshipType.isEmpty()) continue; // if the participantId argument is default constructed, then the relationship matches. if (participant == defaultContact) { retn.append(curr); continue; } // otherwise, check that the participant exists and plays the required role in the relationship. if (role == QContactRelationship::First && curr.first() == participant) { retn.append(curr); } else if (role == QContactRelationship::Second && curr.second() == participant) { retn.append(curr); } else if (role == QContactRelationship::Either && (curr.first() == participant || curr.second() == participant)) { retn.append(curr); } } *error = QContactManager::NoError; if (retn.isEmpty()) *error = QContactManager::DoesNotExistError; return retn; } /*! Saves the given relationship \a relationship, storing any error to \a error and filling the \a changeSet with ids of changed contacts and relationships as required Returns true if the operation was successful otherwise false. */ bool QContactMemoryEngine::saveRelationship(QContactRelationship *relationship, QContactChangeSet &changeSet, QContactManager::Error *error) { // Attempt to validate the relationship. // first, check that the source contact exists and is in this manager. QString myUri = managerUri(); int firstContactIndex = d->m_contactIds.indexOf(relationship->first().id()); if ((!relationship->first().id().managerUri().isEmpty() && relationship->first().id().managerUri() != myUri) ||firstContactIndex == -1) { *error = QContactManager::InvalidRelationshipError; return false; } // second, check that the second contact exists (if it's local); we cannot check other managers' contacts. QContact dest = relationship->second(); int secondContactIndex = d->m_contactIds.indexOf(dest.id()); if (dest.id().managerUri().isEmpty() || dest.id().managerUri() == myUri) { // this entry in the destination list is supposedly stored in this manager. // check that it exists, and that it isn't the source contact (circular) if (secondContactIndex == -1 || dest.id() == relationship->first().id()) { *error = QContactManager::InvalidRelationshipError; return false; } } // the relationship is valid. We need to update the manager URIs in the second contact if it is empty to our URI. if (dest.id().managerUri().isEmpty()) { // need to update the URI relationship->setSecond(dest); } // check to see if the relationship already exists in the database. If so, replace. // We do this because we don't want duplicates in our lists / maps of relationships. *error = QContactManager::NoError; QList allRelationships = d->m_relationships; for (int i = 0; i < allRelationships.size(); i++) { QContactRelationship curr = allRelationships.at(i); if (curr == *relationship) { return true; // TODO: set error to AlreadyExistsError and return false? } } // no matching relationship; must be new. append it to lists in our map of relationships where required. QList firstRelationships = d->m_orderedRelationships.value(relationship->first().id()); QList secondRelationships = d->m_orderedRelationships.value(relationship->second().id()); firstRelationships.append(*relationship); secondRelationships.append(*relationship); d->m_orderedRelationships.insert(relationship->first().id(), firstRelationships); d->m_orderedRelationships.insert(relationship->second().id(), secondRelationships); changeSet.insertAddedRelationshipsContact(relationship->first().id()); changeSet.insertAddedRelationshipsContact(relationship->second().id()); // update the contacts involved QContactManagerEngine::setContactRelationships(&d->m_contacts[firstContactIndex], firstRelationships); QContactManagerEngine::setContactRelationships(&d->m_contacts[secondContactIndex], secondRelationships); // finally, insert into our list of all relationships, and return. d->m_relationships.append(*relationship); return true; } /*! \reimp */ bool QContactMemoryEngine::saveRelationships(QList *relationships, QMap *errorMap, QContactManager::Error *error) { *error = QContactManager::NoError; QContactManager::Error functionError; QContactChangeSet changeSet; for (int i = 0; i < relationships->size(); i++) { QContactRelationship curr = relationships->at(i); saveRelationship(&curr, changeSet, &functionError); if (functionError != QContactManager::NoError && errorMap) errorMap->insert(i, functionError); // and replace the current relationship with the updated version. relationships->replace(i, curr); // also, update the total error if it did not succeed. if (functionError != QContactManager::NoError) *error = functionError; } d->emitSharedSignals(&changeSet); return (*error == QContactManager::NoError); } /*! Removes the given relationship \a relationship, storing any error to \a error and filling the \a changeSet with ids of changed contacts and relationships as required Returns true if the operation was successful otherwise false. */ bool QContactMemoryEngine::removeRelationship(const QContactRelationship &relationship, QContactChangeSet &changeSet, QContactManager::Error *error) { // attempt to remove it from our list of relationships. if (!d->m_relationships.removeOne(relationship)) { *error = QContactManager::DoesNotExistError; return false; } // if that worked, then we need to remove it from the two locations in our map, also. QList firstRelationships = d->m_orderedRelationships.value(relationship.first().id()); QList secondRelationships = d->m_orderedRelationships.value(relationship.second().id()); firstRelationships.removeOne(relationship); secondRelationships.removeOne(relationship); d->m_orderedRelationships.insert(relationship.first().id(), firstRelationships); d->m_orderedRelationships.insert(relationship.second().id(), secondRelationships); // Update the contacts as well int firstContactIndex = d->m_contactIds.indexOf(relationship.first().id()); int secondContactIndex = relationship.second().id().managerUri() == managerUri() ? d->m_contactIds.indexOf(relationship.second().id()) : -1; if (firstContactIndex != -1) QContactMemoryEngine::setContactRelationships(&d->m_contacts[firstContactIndex], firstRelationships); if (secondContactIndex != -1) QContactMemoryEngine::setContactRelationships(&d->m_contacts[secondContactIndex], secondRelationships); // set our changes, and return. changeSet.insertRemovedRelationshipsContact(relationship.first().id()); changeSet.insertRemovedRelationshipsContact(relationship.second().id()); *error = QContactManager::NoError; return true; } /*! \reimp */ bool QContactMemoryEngine::removeRelationships(const QList &relationships, QMap *errorMap, QContactManager::Error *error) { QContactManager::Error functionError; QContactChangeSet cs; for (int i = 0; i < relationships.size(); i++) { removeRelationship(relationships.at(i), cs, &functionError); // update the total error if it did not succeed. if (functionError != QContactManager::NoError) { if (errorMap) errorMap->insert(i, functionError); *error = functionError; } } d->emitSharedSignals(&cs); return (*error == QContactManager::NoError); } /*! \reimp */ void QContactMemoryEngine::requestDestroyed(QContactAbstractRequest *req) { Q_UNUSED(req); } /*! \reimp */ bool QContactMemoryEngine::startRequest(QContactAbstractRequest *req) { updateRequestState(req, QContactAbstractRequest::ActiveState); performAsynchronousOperation(req); return true; } bool QContactMemoryEngine::cancelRequest(QContactAbstractRequest *req) { Q_UNUSED(req); // we can't cancel since we complete immediately return false; } /*! \reimp */ bool QContactMemoryEngine::waitForRequestFinished(QContactAbstractRequest *req, int msecs) { // in our implementation, we always complete any operation we start. Q_UNUSED(msecs); Q_UNUSED(req); return true; } /*! * This slot is called some time after an asynchronous request is started. * It performs the required operation, sets the result and returns. */ void QContactMemoryEngine::performAsynchronousOperation(QContactAbstractRequest *currentRequest) { // store up changes, and emit signals once at the end of the (possibly batch) operation. QContactChangeSet changeSet; // Now perform the active request and emit required signals. Q_ASSERT(currentRequest->state() == QContactAbstractRequest::ActiveState); switch (currentRequest->type()) { case QContactAbstractRequest::ContactFetchRequest: { QContactFetchRequest *r = static_cast(currentRequest); QContactFilter filter = r->filter(); QList sorting = r->sorting(); QContactFetchHint fetchHint = r->fetchHint(); QContactManager::Error operationError = QContactManager::NoError; QList requestedContacts = contacts(filter, sorting, fetchHint, &operationError); // update the request with the results. if (!requestedContacts.isEmpty() || operationError != QContactManager::NoError) updateContactFetchRequest(r, requestedContacts, operationError, QContactAbstractRequest::FinishedState); else updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); } break; case QContactAbstractRequest::ContactFetchByIdRequest: { QContactFetchByIdRequest *r = static_cast(currentRequest); QContactIdFilter idFilter; idFilter.setIds(r->contactIds()); QList sorting; QContactFetchHint fetchHint = r->fetchHint(); QContactManager::Error error = QContactManager::NoError; QList requestedContacts = contacts(idFilter, sorting, fetchHint, &error); // Build an index into the results QHash idMap; // value is index into unsorted if (error == QContactManager::NoError) { for (int i = 0; i < requestedContacts.size(); i++) { idMap.insert(requestedContacts[i].id(), i); } } // Find the order in which the results should be presented // Build up the results and errors QList results; QMap errorMap; int index = 0; foreach (const QContactId &id, r->contactIds()) { if (!idMap.contains(id)) { errorMap.insert(index, QContactManager::DoesNotExistError); error = QContactManager::DoesNotExistError; results.append(QContact()); } else { results.append(requestedContacts[idMap[id]]); } index++; } // update the request with the results. if (!requestedContacts.isEmpty() || error != QContactManager::NoError) QContactManagerEngine::updateContactFetchByIdRequest(r, results, error, errorMap, QContactAbstractRequest::FinishedState); else updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); } break; case QContactAbstractRequest::ContactIdFetchRequest: { QContactIdFetchRequest *r = static_cast(currentRequest); QContactFilter filter = r->filter(); QList sorting = r->sorting(); QContactManager::Error operationError = QContactManager::NoError; QList requestedContactIds = contactIds(filter, sorting, &operationError); if (!requestedContactIds.isEmpty() || operationError != QContactManager::NoError) updateContactIdFetchRequest(r, requestedContactIds, operationError, QContactAbstractRequest::FinishedState); else updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); } break; case QContactAbstractRequest::ContactSaveRequest: { QContactSaveRequest *r = static_cast(currentRequest); QList contacts = r->contacts(); QContactManager::Error operationError = QContactManager::NoError; QMap errorMap; saveContacts(&contacts, &errorMap, &operationError, r->typeMask()); updateContactSaveRequest(r, contacts, operationError, errorMap, QContactAbstractRequest::FinishedState); } break; case QContactAbstractRequest::ContactRemoveRequest: { // this implementation provides scant information to the user // the operation either succeeds (all contacts matching the filter were removed) // or it fails (one or more contacts matching the filter could not be removed) // if a failure occurred, the request error will be set to the most recent // error that occurred during the remove operation. QContactRemoveRequest *r = static_cast(currentRequest); QContactManager::Error operationError = QContactManager::NoError; QList contactsToRemove = r->contactIds(); QMap errorMap; for (int i = 0; i < contactsToRemove.size(); i++) { QContactManager::Error tempError; removeContact(contactsToRemove.at(i), changeSet, &tempError); if (tempError != QContactManager::NoError) { errorMap.insert(i, tempError); operationError = tempError; } } if (!errorMap.isEmpty() || operationError != QContactManager::NoError) updateContactRemoveRequest(r, operationError, errorMap, QContactAbstractRequest::FinishedState); else updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); } break; case QContactAbstractRequest::RelationshipFetchRequest: { QContactRelationshipFetchRequest *r = static_cast(currentRequest); QContactManager::Error operationError = QContactManager::NoError; QList operationErrors; QList allRelationships = relationships(QString(), QContact(), QContactRelationship::Either, &operationError); QList requestedRelationships; // select the requested relationships. for (int i = 0; i < allRelationships.size(); i++) { QContactRelationship currRel = allRelationships.at(i); if (r->first() != QContact() && r->first() != currRel.first()) continue; if (r->second() != QContact() && r->second() != currRel.second()) continue; if (!r->relationshipType().isEmpty() && r->relationshipType() != currRel.relationshipType()) continue; requestedRelationships.append(currRel); } // update the request with the results. if (!requestedRelationships.isEmpty() || operationError != QContactManager::NoError) updateRelationshipFetchRequest(r, requestedRelationships, operationError, QContactAbstractRequest::FinishedState); else updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); } break; case QContactAbstractRequest::RelationshipRemoveRequest: { QContactRelationshipRemoveRequest *r = static_cast(currentRequest); QContactManager::Error operationError = QContactManager::NoError; QList relationshipsToRemove = r->relationships(); QMap errorMap; removeRelationships(r->relationships(), &errorMap, &operationError); if (!errorMap.isEmpty() || operationError != QContactManager::NoError) updateRelationshipRemoveRequest(r, operationError, errorMap, QContactAbstractRequest::FinishedState); else updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); } break; case QContactAbstractRequest::RelationshipSaveRequest: { QContactRelationshipSaveRequest *r = static_cast(currentRequest); QContactManager::Error operationError = QContactManager::NoError; QMap errorMap; QList requestRelationships = r->relationships(); saveRelationships(&requestRelationships, &errorMap, &operationError); // update the request with the results. updateRelationshipSaveRequest(r, requestRelationships, operationError, errorMap, QContactAbstractRequest::FinishedState); } break; default: // unknown request type. break; } // now emit any signals we have to emit d->emitSharedSignals(&changeSet); } void QContactMemoryEngine::partiallySyncDetails(QContact *to, const QContact &from, const QList &mask) { // these details in old contact QList fromDetails; // these details in new contact QList toDetails; // Collect details that match mask foreach (QContactDetail::DetailType type, mask) { fromDetails.append(from.details(type)); toDetails.append(to->details(type)); } // check details to remove foreach (QContactDetail detail, toDetails) { if (!fromDetails.contains(detail)) to->removeDetail(&detail); } // check details to save foreach (QContactDetail detail, fromDetails) { if (!toDetails.contains(detail)) to->saveDetail(&detail); } } /*! * \reimp */ bool QContactMemoryEngine::isRelationshipTypeSupported(const QString& relationshipType, QContactType::TypeValues contactType) const { // the memory backend supports arbitrary relationship types // but some relationship types don't make sense for groups. if (contactType == QContactType::TypeGroup) { if (relationshipType == QContactRelationship::HasSpouse() || relationshipType == QContactRelationship::HasAssistant()) { return false; } } // all other relationship types for all contact types are supported. return true; } /*! * \reimp */ QList QContactMemoryEngine::supportedDataTypes() const { QList st; st.append(QVariant::String); st.append(QVariant::Date); st.append(QVariant::DateTime); st.append(QVariant::Time); st.append(QVariant::Bool); st.append(QVariant::Char); st.append(QVariant::Int); st.append(QVariant::UInt); st.append(QVariant::LongLong); st.append(QVariant::ULongLong); st.append(QVariant::Double); return st; } /*! * The function returns true if the backend natively supports the given filter \a filter, otherwise false. */ bool QContactMemoryEngine::isFilterSupported(const QContactFilter &filter) const { Q_UNUSED(filter); // Until we add hashes for common stuff, fall back to slow code return false; } bool QContactMemoryEngine::saveContacts(QList *contacts, QMap *errorMap, QContactManager::Error *error, const QList &mask) { if (!contacts) { *error = QContactManager::BadArgumentError; return false; } QContactChangeSet changeSet; QContact current; QContactManager::Error operationError = QContactManager::NoError; for (int i = 0; i < contacts->count(); i++) { current = contacts->at(i); if (!saveContact(¤t, changeSet, error, mask)) { operationError = *error; if (errorMap) errorMap->insert(i, operationError); } else { (*contacts)[i] = current; } } *error = operationError; d->emitSharedSignals(&changeSet); // return false if some error occurred return (*error == QContactManager::NoError); } bool QContactMemoryEngine::saveContact(QContact *theContact, QContactChangeSet &changeSet, QContactManager::Error *error, const QList &mask) { // ensure that the contact's details conform to their definitions if (!validateContact(*theContact, error)) { return false; } QContactId id(theContact->id()); if (!id.managerUri().isEmpty() && id.managerUri() != managerUri()) { // the contact doesn't belong to this manager *error = QContactManager::DoesNotExistError; return false; } // check to see if this contact already exists int index = d->m_contactIds.indexOf(id); if (index != -1) { /* We also need to check that there are no modified create only details */ QContact oldContact = d->m_contacts.at(index); if (oldContact.type() != theContact->type()) { *error = QContactManager::AlreadyExistsError; return false; } // check if this is partial save if (!mask.isEmpty()) { QContact tempContact = oldContact; partiallySyncDetails(&tempContact, *theContact, mask); *theContact = tempContact; } QContactTimestamp ts = theContact->detail(QContactTimestamp::Type); ts.setLastModified(QDateTime::currentDateTime()); QContactManagerEngine::setDetailAccessConstraints(&ts, QContactDetail::ReadOnly | QContactDetail::Irremovable); theContact->saveDetail(&ts); // Looks ok, so continue d->m_contacts.replace(index, *theContact); changeSet.insertChangedContact(theContact->id()); } else { // id does not exist; if not zero, fail. QContactId newId; if (theContact->id() != QContactId() && theContact->id() != newId) { // the ID is not empty, and it doesn't identify an existing contact in our database either. *error = QContactManager::DoesNotExistError; return false; } // check if this is partial save if (!mask.isEmpty()) { QContact tempContact; partiallySyncDetails(&tempContact, *theContact, mask); *theContact = tempContact; } /* New contact */ QContactTimestamp ts = theContact->detail(QContactTimestamp::Type); ts.setLastModified(QDateTime::currentDateTime()); ts.setCreated(ts.lastModified()); setDetailAccessConstraints(&ts, QContactDetail::ReadOnly | QContactDetail::Irremovable); theContact->saveDetail(&ts); // update the contact item - set its ID quint32 nextContactId = d->m_nextContactId; // don't increment the persistent version until we're successful or we know it collides. nextContactId += 1; // but do increment the temporary version to check for collision QContactMemoryEngineId *newMemoryEngineId = new QContactMemoryEngineId; newMemoryEngineId->m_contactId = nextContactId; newMemoryEngineId->m_managerUri = d->m_managerUri; QContactId newContactId = QContactId(newMemoryEngineId); theContact->setId(newContactId); // note: do NOT delete the QContactMemoryEngineId -- the QContactId ctor takes ownership of it. // finally, add the contact to our internal lists and return d->m_contacts.append(*theContact); // add contact to list d->m_contactIds.append(theContact->id()); // track the contact id. changeSet.insertAddedContact(theContact->id()); // successful, now increment the persistent version of the next item id. d->m_nextContactId += 1; } *error = QContactManager::NoError; // successful. return true; } /*! \class QContactMemoryEngineId \brief The QContactMemoryEngineId class provides an id which uniquely identifies a QContact stored within a QContactMemoryEngine. \internal It may be used as a reference implementation, although since different platforms have different semantics for ids the precise implementation required may differ. */ QContactMemoryEngineId::QContactMemoryEngineId() : QContactEngineId(), m_contactId(0) { } QContactMemoryEngineId::QContactMemoryEngineId(quint32 contactId, const QString &managerUri) : QContactEngineId(), m_contactId(contactId), m_managerUri(managerUri) { } QContactMemoryEngineId::~QContactMemoryEngineId() { } QContactMemoryEngineId::QContactMemoryEngineId(const QContactMemoryEngineId &other) : QContactEngineId(), m_contactId(other.m_contactId), m_managerUri(other.m_managerUri) { } QContactMemoryEngineId::QContactMemoryEngineId(const QMap ¶meters, const QString &engineIdString) : QContactEngineId() { m_contactId = engineIdString.toInt(); m_managerUri = QContactManager::buildUri("memory", parameters); } bool QContactMemoryEngineId::isEqualTo(const QContactEngineId *other) const { if (m_contactId != static_cast(other)->m_contactId) return false; return true; } bool QContactMemoryEngineId::isLessThan(const QContactEngineId *other) const { const QContactMemoryEngineId *otherPtr = static_cast(other); if (m_managerUri < otherPtr->m_managerUri) return true; if (m_contactId < otherPtr->m_contactId) return true; return false; } QString QContactMemoryEngineId::managerUri() const { return m_managerUri; } QString QContactMemoryEngineId::toString() const { return QString::number(m_contactId); } QContactEngineId* QContactMemoryEngineId::clone() const { return new QContactMemoryEngineId(m_contactId, m_managerUri); } #ifndef QT_NO_DEBUG_STREAM QDebug& QContactMemoryEngineId::debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QContactMemoryEngineId(" << m_managerUri << "," << m_contactId << ")"; return dbg.maybeSpace(); } #endif uint QContactMemoryEngineId::hash() const { return m_contactId; } #include "moc_qcontactmemorybackend_p.cpp" QT_END_NAMESPACE_CONTACTS src/plugins/contacts/memory/qcontactmemorybackend_p.h000066400000000000000000000234411233466112000235230ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtContacts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTMEMORYBACKEND_P_H #define QCONTACTMEMORYBACKEND_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactMemoryEngine; class QContactMemoryEngineFactory : public QContactManagerEngineFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QContactManagerEngineFactoryInterface" FILE "memory.json") public: QContactManagerEngine* engine(const QMap ¶meters, QContactManager::Error*); QString managerName() const; QContactEngineId* createContactEngineId(const QMap ¶meters, const QString &engineIdString) const; }; class QContactMemoryEngineData : public QSharedData { public: QContactMemoryEngineData() : QSharedData() , m_refCount(QAtomicInt(1)) , m_selfContactId() , m_nextContactId(1) , m_anonymous(false) { } QContactMemoryEngineData(const QContactMemoryEngineData &other) : QSharedData(other), m_refCount(QAtomicInt(1)), m_selfContactId(other.m_selfContactId), m_nextContactId(other.m_nextContactId), m_anonymous(other.m_anonymous) { } ~QContactMemoryEngineData() { } static QContactMemoryEngineData *data(QContactMemoryEngine *engine); QAtomicInt m_refCount; QString m_id; // the id parameter value QContactId m_selfContactId; // the "MyCard" contact id QList m_contacts; // list of contacts QList m_contactIds; // list of contact Id's QList m_relationships; // list of contact relationships QMap > m_orderedRelationships; // map of ordered lists of contact relationships QList m_definitionIds; // list of definition types (id's) quint32 m_nextContactId; bool m_anonymous; // Is this backend ever shared? QString m_managerUri; // for faster lookup. void emitSharedSignals(QContactChangeSet *cs) { foreach(QContactManagerEngine* engine, m_sharedEngines) cs->emitSignals(engine); } QList m_sharedEngines; // The list of engines that share this data }; class QContactMemoryEngineId : public QContactEngineId { public: QContactMemoryEngineId(); ~QContactMemoryEngineId(); QContactMemoryEngineId(quint32 contactId, const QString &managerUri); QContactMemoryEngineId(const QContactMemoryEngineId &other); QContactMemoryEngineId(const QMap ¶meters, const QString &engineIdString); bool isEqualTo(const QContactEngineId *other) const; bool isLessThan(const QContactEngineId *other) const; QString managerUri() const; QContactEngineId* clone() const; QString toString() const; #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug &dbg) const; #endif uint hash() const; private: quint32 m_contactId; QString m_managerUri; friend class QContactMemoryEngine; }; class QContactMemoryEngine : public QContactManagerEngine { Q_OBJECT public: static QContactMemoryEngine *createMemoryEngine(const QMap ¶meters); ~QContactMemoryEngine(); /* URI reporting */ QString managerName() const; QMap managerParameters() const; /*! \reimp */ int managerVersion() const {return 1;} virtual QList contactIds(const QContactFilter &filter, const QList &sortOrders, QContactManager::Error *error) const; virtual QList contacts(const QContactFilter &filter, const QList &sortOrders, const QContactFetchHint &fetchHint, QContactManager::Error *error) const; virtual QContact contact(const QContactId &contactId, const QContactFetchHint &fetchHint, QContactManager::Error *error) const; virtual bool saveContacts(QList *contacts, QMap *errorMap, QContactManager::Error *error); virtual bool removeContacts(const QList &contactIds, QMap *errorMap, QContactManager::Error *error); /* "Self" contact id (MyCard) */ virtual bool setSelfContactId(const QContactId &contactId, QContactManager::Error *error); virtual QContactId selfContactId(QContactManager::Error *error) const; /* Relationships between contacts */ virtual QList relationships(const QString &relationshipType, const QContact &participant, QContactRelationship::Role role, QContactManager::Error *error) const; virtual bool saveRelationships(QList *relationships, QMap *errorMap, QContactManager::Error *error); virtual bool removeRelationships(const QList &relationships, QMap *errorMap, QContactManager::Error *error); /*! \reimp */ virtual bool validateContact(const QContact &contact, QContactManager::Error *error) const { return QContactManagerEngine::validateContact(contact, error); } /* Asynchronous Request Support */ virtual void requestDestroyed(QContactAbstractRequest *req); virtual bool startRequest(QContactAbstractRequest *req); virtual bool cancelRequest(QContactAbstractRequest *req); virtual bool waitForRequestFinished(QContactAbstractRequest *req, int msecs); /* Capabilities reporting */ virtual bool isRelationshipTypeSupported(const QString &relationshipType, QContactType::TypeValues contactType) const; virtual bool isFilterSupported(const QContactFilter &filter) const; virtual QList supportedDataTypes() const; /*! \reimp */ virtual QList supportedContactTypes() const { return QContactManagerEngine::supportedContactTypes(); } virtual QList supportedContactDetailTypes() const { return QContactManagerEngine::supportedContactDetailTypes(); } protected: QContactMemoryEngine(QContactMemoryEngineData *data); protected: /* Implement "signal coalescing" for batch functions via change set */ virtual bool saveContact(QContact *theContact, QContactChangeSet &changeSet, QContactManager::Error *error); virtual bool removeContact(const QContactId &contactId, QContactChangeSet &changeSet, QContactManager::Error *error); virtual bool saveRelationship(QContactRelationship *relationship, QContactChangeSet &changeSet, QContactManager::Error *error); virtual bool removeRelationship(const QContactRelationship &relationship, QContactChangeSet &changeSet, QContactManager::Error *error); private: /* For partial save */ bool saveContacts(QList *contacts, QMap *errorMap, QContactManager::Error *error, const QList &mask); bool saveContact(QContact *theContact, QContactChangeSet &changeSet, QContactManager::Error *error, const QList &mask); void partiallySyncDetails(QContact *to, const QContact &from, const QList &mask); void performAsynchronousOperation(QContactAbstractRequest *request); QContactMemoryEngineData *d; static QMap engineDatas; friend class QContactMemoryEngineData; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTMEMORYBACKEND_P_H src/plugins/contacts/serviceactionmanager/000077500000000000000000000000001233466112000213335ustar00rootroot00000000000000src/plugins/contacts/serviceactionmanager/qcontactactionservicemanager_p.cpp000066400000000000000000000170321233466112000303070ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcontactactionservicemanager_p.h" #include #include #include #include "qservice.h" #include "qservicemanager.h" QTM_BEGIN_NAMESPACE Q_GLOBAL_STATIC(QContactActionServiceManager, contactActionServiceManagerInstance) Q_EXPORT_PLUGIN2(qtcontacts_serviceactionmanager, QContactActionServiceManager); /*! \internal \class QContactActionServiceManager This class uses the service framework to discover contact actions which are provided by third parties. It is an implementation detail of QContactAction. */ QContactActionServiceManager* QContactActionServiceManager::instance() { return contactActionServiceManagerInstance(); } QContactActionServiceManager::QContactActionServiceManager() : QObject(), initLock(false) { } QContactActionServiceManager::~QContactActionServiceManager() { // we don't use qDeleteAll() because some factories produce more than one action descriptor. QList keys = m_actionFactoryHash.keys(); QSet deletedFactories; foreach (const QContactActionDescriptor& key, keys) { QContactActionFactory *curr = m_actionFactoryHash.value(key); if (!deletedFactories.contains(curr)) { deletedFactories.insert(curr); delete curr; } } } void QContactActionServiceManager::init() { // XXX NOTE: should already be locked PRIOR to entering this function. if (!initLock) { initLock = true; // fill up our hashes QList sids = m_serviceManager.findInterfaces(); // all services, all interfaces. foreach (const QServiceInterfaceDescriptor& sid, sids) { if (sid.interfaceName() == QContactActionFactory::InterfaceName) { if (static_cast(sid.attribute(QServiceInterfaceDescriptor::ServiceType).toInt()) != QService::Plugin) { continue; // we don't allow IPC contact action factories. } QContactActionFactory* actionFactory = qobject_cast(m_serviceManager.loadInterface(sid)); if (actionFactory) { // if we cannot get the action factory from the service manager, then we don't add it to our hash. QList descriptors = actionFactory->actionDescriptors(); foreach (const QContactActionDescriptor& ad, descriptors) { m_descriptorHash.insert(ad.actionName(), ad); // multihash insert. m_actionFactoryHash.insert(ad, actionFactory); } } } } // and listen for signals. connect(&m_serviceManager, SIGNAL(serviceAdded(QString,QService::Scope)), this, SLOT(serviceAdded(QString))); connect(&m_serviceManager, SIGNAL(serviceRemoved(QString,QService::Scope)), this, SLOT(serviceRemoved(QString))); } } QHash QContactActionServiceManager::actionFactoryHash() { QMutexLocker locker(&m_instanceMutex); init(); return m_actionFactoryHash; } QMultiHash QContactActionServiceManager::descriptorHash() { QMutexLocker locker(&m_instanceMutex); init(); return m_descriptorHash; } void QContactActionServiceManager::serviceAdded(const QString& serviceName) { QMutexLocker locker(&m_instanceMutex); QList sids = m_serviceManager.findInterfaces(serviceName); foreach (const QServiceInterfaceDescriptor& sid, sids) { if (sid.interfaceName() == QContactActionFactory::InterfaceName) { if (static_cast(sid.attribute(QServiceInterfaceDescriptor::ServiceType).toInt()) != QService::Plugin) { continue; // we don't allow IPC contact action factories. } QContactActionFactory* actionFactory = qobject_cast(m_serviceManager.loadInterface(sid)); if (actionFactory) { // if we cannot get the action factory from the service manager, then we don't add it to our hash. QList descriptors = actionFactory->actionDescriptors(); foreach (const QContactActionDescriptor& ad, descriptors) { m_descriptorHash.insert(ad.actionName(), ad); // multihash insert. m_actionFactoryHash.insert(ad, actionFactory); } } } } } void QContactActionServiceManager::serviceRemoved(const QString& serviceName) { QMutexLocker locker(&m_instanceMutex); QList sids = m_serviceManager.findInterfaces(serviceName); foreach (const QServiceInterfaceDescriptor& sid, sids) { if (sid.interfaceName() == QContactActionFactory::InterfaceName) { if (static_cast(sid.attribute(QServiceInterfaceDescriptor::ServiceType).toInt()) != QService::Plugin) { continue; // we don't allow IPC contact action factories. } QList cads = m_actionFactoryHash.keys(); foreach (const QContactActionDescriptor& cad, cads) { if (cad.serviceName() != serviceName) continue; delete m_actionFactoryHash.value(cad); m_actionFactoryHash.remove(cad); m_descriptorHash.remove(cad.actionName(), cad); } } } } #include "moc_qcontactactionservicemanager_p.cpp" QTM_END_NAMESPACE src/plugins/contacts/serviceactionmanager/qcontactactionservicemanager_p.h000066400000000000000000000067371233466112000277660ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTACTIONSERVICEMANAGER_P_H #define QCONTACTACTIONSERVICEMANAGER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include "qservicemanager.h" QTM_BEGIN_NAMESPACE class QContactAction; class QContactActionFactory; class QContactActionServiceManager : public QObject, public QContactActionManagerPlugin { Q_OBJECT Q_INTERFACES(QtMobility::QContactActionManagerPlugin) public: static QContactActionServiceManager* instance(); // this is a private class, so despite being a singleton we make this ctor public. QContactActionServiceManager(); ~QContactActionServiceManager(); QHash actionFactoryHash(); QMultiHash descriptorHash(); public slots: void serviceAdded(const QString& serviceName); void serviceRemoved(const QString& serviceName); private: void init(); bool initLock; QMutex m_instanceMutex; QServiceManager m_serviceManager; QHash m_actionFactoryHash; // descriptor to action factory ptr. QMultiHash m_descriptorHash; // action name to descriptor }; QTM_END_NAMESPACE #endif // QCONTACTACTIONSERVICEMANAGER_P_H src/plugins/contacts/serviceactionmanager/serviceactionmanager.pro000066400000000000000000000006771233466112000262600ustar00rootroot00000000000000TARGET = qtcontacts_serviceactionmanager QT = core contacts PLUGIN_TYPE = contacts load(qt_plugin) HEADERS += \ qcontactactionservicemanager_p.h SOURCES += \ qcontactactionservicemanager_p.cpp INCLUDEPATH += $$SOURCE_DIR/src/contacts $$SOURCE_DIR/src/contacts/details $$SOURCE_DIR/src/contacts/filters $$SOURCE_DIR/src/contacts/requests $$SOURCE_DIR/src/serviceframework CONFIG += mobility MOBILITY = contacts serviceframework src/plugins/organizer/000077500000000000000000000000001233466112000153245ustar00rootroot00000000000000src/plugins/organizer/jsondb/000077500000000000000000000000001233466112000166035ustar00rootroot00000000000000src/plugins/organizer/jsondb/jsondb.json000066400000000000000000000000351233466112000207530ustar00rootroot00000000000000{ "Keys": [ "jsondb" ] } src/plugins/organizer/jsondb/jsondb.pro000066400000000000000000000013141233466112000206030ustar00rootroot00000000000000TARGET = qtorganizer_jsondb QT += organizer network jsondb PLUGIN_TYPE = organizer load(qt_plugin) HEADERS += \ qorganizerjsondbengine.h \ qorganizerjsondbrequestthread.h \ qorganizerjsondbenginefactory.h \ qorganizerjsondbid.h \ qorganizerjsondbrequestmanager.h \ qorganizerjsondbstring.h \ qorganizerjsondbconverter.h \ qorganizerjsondbdatastorage.h SOURCES += \ qorganizerjsondbengine.cpp \ qorganizerjsondbrequestthread.cpp \ qorganizerjsondbenginefactory.cpp \ qorganizerjsondbid.cpp \ qorganizerjsondbrequestmanager.cpp \ qorganizerjsondbstring.cpp \ qorganizerjsondbconverter.cpp \ qorganizerjsondbdatastorage.cpp OTHER_FILES += jsondb.json src/plugins/organizer/jsondb/qorganizerjsondbconverter.cpp000066400000000000000000003325571233466112000246370ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPIM module of the Qt toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizerjsondbconverter.h" #include #include #include #include "qorganizerjsondbengine.h" #include "qorganizerjsondbid.h" #include "qorganizerjsondbstring.h" QT_BEGIN_NAMESPACE_ORGANIZER const int QOrganizerJsonDbConverter::enumMapEnd(-1212); const QMap QOrganizerJsonDbConverter::filterablePropertyNames() { static QMap filterableProperties; if (filterableProperties.isEmpty()) { filterableProperties.insert(QOrganizerItemComment::FieldComment, QOrganizerJsonDbStr::itemComments()); filterableProperties.insert(QOrganizerItemDescription::FieldDescription, QOrganizerJsonDbStr::itemDescription()); filterableProperties.insert(QOrganizerItemDisplayLabel::FieldLabel, QOrganizerJsonDbStr::itemDisplayName()); filterableProperties.insert(QOrganizerEventRsvp::FieldOrganizerEmail, QOrganizerJsonDbStr::eventRsvpOrganizerEmail()); filterableProperties.insert(QOrganizerEventRsvp::FieldOrganizerName, QOrganizerJsonDbStr::eventRsvpOrganizerName()); filterableProperties.insert(QOrganizerEventRsvp::FieldParticipationRole, QOrganizerJsonDbStr::eventRsvpParticipationRole()); filterableProperties.insert(QOrganizerEventRsvp::FieldParticipationStatus, QOrganizerJsonDbStr::eventRsvpParticipationStatus()); filterableProperties.insert(QOrganizerEventRsvp::FieldResponseDate, QOrganizerJsonDbStr::eventRsvpResponseDate()); filterableProperties.insert(QOrganizerEventRsvp::FieldResponseDeadline, QOrganizerJsonDbStr::eventRsvpResponseDeadline()); filterableProperties.insert(QOrganizerEventRsvp::FieldResponseRequirement, QOrganizerJsonDbStr::eventRsvpResponseRequirement()); filterableProperties.insert(QOrganizerEventTime::FieldAllDay, QOrganizerJsonDbStr::eventIsAllDay()); filterableProperties.insert(QOrganizerEventTime::FieldEndDateTime, QOrganizerJsonDbStr::eventEndDateTime()); filterableProperties.insert(QOrganizerEventTime::FieldStartDateTime, QOrganizerJsonDbStr::eventStartDateTime()); filterableProperties.insert(QOrganizerItemGuid::FieldGuid, QOrganizerJsonDbStr::itemGuid()); filterableProperties.insert(QOrganizerItemLocation::FieldLabel, QString(QStringLiteral("%1.%2") .arg(QOrganizerJsonDbStr::eventLocation()) .arg(QOrganizerJsonDbStr::eventLocationDisplayName())) ); filterableProperties.insert(QOrganizerItemLocation::FieldLatitude, QString(QStringLiteral("%1.%2.%3") .arg(QOrganizerJsonDbStr::eventLocation()) .arg(QOrganizerJsonDbStr::eventLocationGeo()) .arg(QOrganizerJsonDbStr::eventLocationGeoLatitude())) ); filterableProperties.insert(QOrganizerItemLocation::FieldLongitude, QString(QStringLiteral("%1.%2.%3") .arg(QOrganizerJsonDbStr::eventLocation()) .arg(QOrganizerJsonDbStr::eventLocationGeo()) .arg(QOrganizerJsonDbStr::eventLocationGeoLongitude())) ); filterableProperties.insert(QOrganizerItemPriority::FieldPriority, QOrganizerJsonDbStr::itemPriority()); filterableProperties.insert(QOrganizerItemTag::FieldTag, QOrganizerJsonDbStr::itemTags()); filterableProperties.insert(QOrganizerTodoProgress::FieldFinishedDateTime, QOrganizerJsonDbStr::todoFinishedDateTime()); filterableProperties.insert(QOrganizerTodoProgress::FieldPercentageComplete, QOrganizerJsonDbStr::todoProgressPercentage()); filterableProperties.insert(QOrganizerTodoProgress::FieldStatus, QOrganizerJsonDbStr::todoStatus()); filterableProperties.insert(QOrganizerTodoTime::FieldAllDay, QOrganizerJsonDbStr::todoIsAllDay()); filterableProperties.insert(QOrganizerTodoTime::FieldDueDateTime, QOrganizerJsonDbStr::todoDueDateTime()); filterableProperties.insert(QOrganizerTodoTime::FieldStartDateTime, QOrganizerJsonDbStr::todoStartDateTime()); }; return filterableProperties; } const QOrganizerJsonDbEnumConversionData *QOrganizerJsonDbConverter::organizerPriorityEnumMap() { static const QOrganizerJsonDbEnumConversionData map[] = { {QOrganizerItemPriority::UnknownPriority, QString(QStringLiteral("UnknownPriority"))}, {QOrganizerItemPriority::ExtremelyHighPriority, QString(QStringLiteral("HighestPriority"))}, {QOrganizerItemPriority::ExtremelyHighPriority, QString(QStringLiteral("ExtremelyHighPriority"))}, {QOrganizerItemPriority::VeryHighPriority, QString(QStringLiteral("VeryHighPriority"))}, {QOrganizerItemPriority::HighPriority, QString(QStringLiteral("HighPriority"))}, {QOrganizerItemPriority::MediumPriority, QString(QStringLiteral("MediumPriority"))}, {QOrganizerItemPriority::LowPriority, QString(QStringLiteral("LowPriority"))}, {QOrganizerItemPriority::VeryLowPriority, QString(QStringLiteral("VeryLowPriority"))}, {QOrganizerItemPriority::ExtremelyLowPriority, QString(QStringLiteral("ExtremelyLowPriority"))}, {QOrganizerItemPriority::LowestPriority, QString(QStringLiteral("LowestPriority"))}, {enumMapEnd, QString::null} }; return map; } const QOrganizerJsonDbEnumConversionData *QOrganizerJsonDbConverter::organizerFrequencyEnumMap() { static const QOrganizerJsonDbEnumConversionData map[] = { {QOrganizerRecurrenceRule::Invalid, QString(QStringLiteral("Invalid"))}, {QOrganizerRecurrenceRule::Daily, QString(QStringLiteral("Daily"))}, {QOrganizerRecurrenceRule::Weekly, QString(QStringLiteral("Weekly"))}, {QOrganizerRecurrenceRule::Monthly, QString(QStringLiteral("Monthly"))}, {QOrganizerRecurrenceRule::Yearly, QString(QStringLiteral("Yearly"))}, {enumMapEnd, QString::null} }; return map; } const QOrganizerJsonDbEnumConversionData *QOrganizerJsonDbConverter::organizerDayEnumMap() { static const QOrganizerJsonDbEnumConversionData map[] = { {Qt::Monday, QString(QStringLiteral("Monday"))}, {Qt::Tuesday, QString(QStringLiteral("Tuesday"))}, {Qt::Wednesday, QString(QStringLiteral("Wednesday"))}, {Qt::Thursday, QString(QStringLiteral("Thursday"))}, {Qt::Friday, QString(QStringLiteral("Friday"))}, {Qt::Saturday, QString(QStringLiteral("Saturday"))}, {Qt::Sunday, QString(QStringLiteral("Sunday"))}, {enumMapEnd, QString::null} }; return map; } const QOrganizerJsonDbEnumConversionData *QOrganizerJsonDbConverter::organizerMonthEnumMap() { static const QOrganizerJsonDbEnumConversionData map[] = { {QOrganizerRecurrenceRule::January, QString(QStringLiteral("January"))}, {QOrganizerRecurrenceRule::February, QString(QStringLiteral("February"))}, {QOrganizerRecurrenceRule::March, QString(QStringLiteral("March"))}, {QOrganizerRecurrenceRule::April, QString(QStringLiteral("April"))}, {QOrganizerRecurrenceRule::May, QString(QStringLiteral("May"))}, {QOrganizerRecurrenceRule::June, QString(QStringLiteral("June"))}, {QOrganizerRecurrenceRule::July, QString(QStringLiteral("July"))}, {QOrganizerRecurrenceRule::August, QString(QStringLiteral("August"))}, {QOrganizerRecurrenceRule::September, QString(QStringLiteral("September"))}, {QOrganizerRecurrenceRule::October, QString(QStringLiteral("October"))}, {QOrganizerRecurrenceRule::November, QString(QStringLiteral("November"))}, {QOrganizerRecurrenceRule::December, QString(QStringLiteral("December"))}, {enumMapEnd, QString::null} }; return map; } const QOrganizerJsonDbEnumConversionData *QOrganizerJsonDbConverter::organizerParticipationStatusMap() { static const QOrganizerJsonDbEnumConversionData map[] = { {QOrganizerEventAttendee::StatusUnknown, QString(QStringLiteral("Unknown"))}, {QOrganizerEventAttendee::StatusAccepted, QString(QStringLiteral("Accepted"))}, {QOrganizerEventAttendee::StatusDeclined, QString(QStringLiteral("Declined"))}, {QOrganizerEventAttendee::StatusTentative, QString(QStringLiteral("Tentative"))}, {QOrganizerEventAttendee::StatusDelegated, QString(QStringLiteral("Delegated"))}, {QOrganizerEventAttendee::StatusInProcess, QString(QStringLiteral("InProcess"))}, {QOrganizerEventAttendee::StatusCompleted, QString(QStringLiteral("Completed"))}, {enumMapEnd, QString::null} }; return map; } const QOrganizerJsonDbEnumConversionData *QOrganizerJsonDbConverter::organizerParticipationRoleMap() { static const QOrganizerJsonDbEnumConversionData map[] = { {QOrganizerEventAttendee::RoleUnknown, QString(QStringLiteral("Unknown"))}, {QOrganizerEventAttendee::RoleOrganizer, QString(QStringLiteral("Organizer"))}, {QOrganizerEventAttendee::RoleChairperson, QString(QStringLiteral("Chairperson"))}, {QOrganizerEventAttendee::RoleHost, QString(QStringLiteral("Host"))}, {QOrganizerEventAttendee::RoleRequiredParticipant, QString(QStringLiteral("RequiredParticipant"))}, {QOrganizerEventAttendee::RoleOptionalParticipant, QString(QStringLiteral("OptionalParticipant"))}, {QOrganizerEventAttendee::RoleNonParticipant, QString(QStringLiteral("NonParticipant"))}, {enumMapEnd, QString::null} }; return map; } const QOrganizerJsonDbEnumConversionData *QOrganizerJsonDbConverter::organizerResponseRequirementMap() { static const QOrganizerJsonDbEnumConversionData map[] = { {QOrganizerEventRsvp::ResponseNotRequired, QString(QStringLiteral("NotRequired"))}, {QOrganizerEventRsvp::ResponseRequired, QString(QStringLiteral("Required"))}, {enumMapEnd, QString::null} }; return map; } const QOrganizerJsonDbEnumConversionData *QOrganizerJsonDbConverter::organizerItemTypeMap() { static const QOrganizerJsonDbEnumConversionData map[] = { {QOrganizerItemType::TypeUndefined, QString(QStringLiteral("Undefined"))}, {QOrganizerItemType::TypeEvent, QString(QStringLiteral("Event"))}, {QOrganizerItemType::TypeEventOccurrence, QString(QStringLiteral("EventOccurrence"))}, {QOrganizerItemType::TypeTodo, QString(QStringLiteral("Todo"))}, {QOrganizerItemType::TypeTodoOccurrence, QString(QStringLiteral("TodoOccurrence"))}, {QOrganizerItemType::TypeJournal, QString(QStringLiteral("Journal"))}, {QOrganizerItemType::TypeNote, QString(QStringLiteral("Note"))}, {enumMapEnd, QString::null} }; return map; } const QOrganizerJsonDbEnumConversionData *QOrganizerJsonDbConverter::organizerTodoStatusMap() { static const QOrganizerJsonDbEnumConversionData map[] = { {QOrganizerTodoProgress::StatusNotStarted, QString(QStringLiteral("NotStarted"))}, {QOrganizerTodoProgress::StatusInProgress, QString(QStringLiteral("InProgress"))}, {QOrganizerTodoProgress::StatusComplete, QString(QStringLiteral("Complete"))}, {enumMapEnd, QString::null} }; return map; } const QOrganizerJsonDbEnumConversionData *QOrganizerJsonDbConverter::organizerStorageLocationMap() { static const QOrganizerJsonDbEnumConversionData map[] = { {QOrganizerJsonDbEngine::UserDataStorage, QString(QStringLiteral("com.nokia.mt.User"))}, {QOrganizerJsonDbEngine::SystemStorage, QString(QStringLiteral("com.nokia.mt.System"))}, {enumMapEnd, QString::null} }; return map; } QOrganizerJsonDbConverter::QOrganizerJsonDbConverter() { } QOrganizerManager::Error QOrganizerJsonDbConverter::jsonDbConnectionErrorToOrganizerError(QJsonDbConnection::ErrorCode error) const { switch (error) { case QJsonDbConnection::NoError: return QOrganizerManager::NoError; } return QOrganizerManager::NoError; } QOrganizerManager::Error QOrganizerJsonDbConverter::jsonDbRequestErrorToOrganizerError(QJsonDbRequest::ErrorCode error) const { // TODO: Add more error codes when they are available in jsondb // currently there is no error code for missing UUID switch (error) { case QJsonDbRequest::NoError: return QOrganizerManager::NoError; case QJsonDbRequest::MissingObject: return QOrganizerManager::DoesNotExistError; case QJsonDbRequest::MissingType: case QJsonDbRequest::MissingQuery: case QJsonDbRequest::InvalidLimit: return QOrganizerManager::BadArgumentError; case QJsonDbRequest::InvalidPartition: // FIXME; We propably need to add more finegrained error inspection // related to partition accesses, now there is only InvalidPartition. return QOrganizerManager::UnspecifiedError; case QJsonDbRequest::DatabaseConnectionError: return QOrganizerManager::UnspecifiedError; default: return QOrganizerManager::UnspecifiedError; } } bool QOrganizerJsonDbConverter::jsonDbObjectToItem(const QJsonObject &object, QOrganizerItem *item, QOrganizerJsonDbEngine::StorageLocation storageLocation) const { QJsonObject objectToParse; // must handle type before reaching the loop QString jsonDbType = object.value(QOrganizerJsonDbStr::jsonDbType()).toString(); if (jsonDbType == QOrganizerJsonDbStr::jsonDbEventType()) { item->setType(QOrganizerItemType::TypeEvent); } else if (jsonDbType == QOrganizerJsonDbStr::jsonDbEventOccurrenceType()) { item->setType(QOrganizerItemType::TypeEventOccurrence); } else if (jsonDbType == QOrganizerJsonDbStr::jsonDbEventViewType()) { // the data is stored in the "value" field, so dirty code here ;) objectToParse = object.value(QOrganizerJsonDbStr::jsonDbValue()).toObject(); if (!objectToParse.value("isVisible").toBool()) return false; objectToParse.insert(QOrganizerJsonDbStr::jsonDbUuid(), object.value(QOrganizerJsonDbStr::jsonDbUuid())); item->setType(QOrganizerItemType::TypeEvent); item->setData(QOrganizerJsonDbStr::eventIsSynthetic(), true); } else if (jsonDbType == QOrganizerJsonDbStr::jsonDbTodoType()) { item->setType(QOrganizerItemType::TypeTodo); } else if (jsonDbType == QOrganizerJsonDbStr::jsonDbTodoOccurrenceType()) { item->setType(QOrganizerItemType::TypeTodoOccurrence); } else { return false; } if (objectToParse.isEmpty()) objectToParse = object; // other mandatory fields bool hasCollectionId(false); bool hasGuid(false); bool hasItemId(false); bool hasItemVersion(false); // go through all fields QJsonObject::const_iterator i = objectToParse.constBegin(); while (i != objectToParse.constEnd()) { if (i.key() == QOrganizerJsonDbStr::jsonDbUuid()) { QString jsonDbUuid = i.value().toString(); if (jsonDbUuid.isEmpty()) return false; QOrganizerJsonDbItemId *jsondbItemId = new QOrganizerJsonDbItemId(); jsondbItemId->setJsonDbUuid(jsonDbUuid); jsondbItemId->setStorageLocation(storageLocation); item->setId(QOrganizerItemId(jsondbItemId)); hasItemId = true; } else if (i.key() == QOrganizerJsonDbStr::itemCollectionUuid()) { QString jsonDbCollectionIdStr = i.value().toString(); if (jsonDbCollectionIdStr.isEmpty()) return false; QOrganizerJsonDbCollectionId *jsondbCollId = new QOrganizerJsonDbCollectionId(); jsondbCollId->setJsonDbUuid(jsonDbCollectionIdStr); jsondbCollId->setStorageLocation(storageLocation); item->setCollectionId(QOrganizerCollectionId(jsondbCollId)); hasCollectionId = true; } else if (i.key() == QOrganizerJsonDbStr::itemGuid()) { QString guid = i.value().toString(); if (guid.isEmpty()) return false; item->setGuid(guid); hasGuid = true; } else if (i.key() == QOrganizerJsonDbStr::itemDisplayName()) { QString displayLabel = i.value().toString(); if (!displayLabel.isEmpty()) item->setDisplayLabel(displayLabel); } else if (i.key() == QOrganizerJsonDbStr::itemDescription()) { QString description = i.value().toString(); if (!description.isEmpty()) item->setDescription(description); } else if (i.key() == QOrganizerJsonDbStr::itemComments()) { QJsonArray array = i.value().toArray(); if (!array.isEmpty()) { QStringList comments; for (int j = 0; j < array.size(); ++j) { QString comment = array.at(j).toString(); if (!comment.isEmpty()) comments.append(comment); } if (!comments.isEmpty()) item->setComments(comments); } } else if (i.key() == QOrganizerJsonDbStr::itemTags()) { QJsonArray array = i.value().toArray(); if (!array.isEmpty()) { QStringList tags; for (int j = 0; j < array.size(); ++j) { QString tag = array.at(j).toString(); if (!tag.isEmpty()) tags.append(tag); } if (!tags.isEmpty()) item->setTags(tags); } } else if (i.key() == QOrganizerJsonDbStr::itemPriority()) { QString jsonDbPriority = i.value().toString(); if (!jsonDbPriority.isEmpty()) { QOrganizerItemPriority priority; priority.setPriority(static_cast(stringToEnum(organizerPriorityEnumMap(), jsonDbPriority))); item->saveDetail(&priority); } } else if (i.key() == QOrganizerJsonDbStr::itemRecurrenceRules()) { QSet recurrenceRules; QJsonArray jsonDbRecurrenceRules = i.value().toArray(); foreach (const QJsonValue &jsonDbRecurrenceRule, jsonDbRecurrenceRules) { QOrganizerRecurrenceRule rule; jsonDbObjectToRecurrenceRule(jsonDbRecurrenceRule.toObject(), &rule); recurrenceRules.insert(rule); } if (!recurrenceRules.isEmpty()) { QOrganizerItemRecurrence recurrence = item->detail(QOrganizerItemDetail::TypeRecurrence); recurrence.setRecurrenceRules(recurrenceRules); item->saveDetail(&recurrence); } } else if (i.key() == QOrganizerJsonDbStr::itemExceptionRules()) { QSet exceptionRules; QJsonArray jsonDbExceptionRules = i.value().toArray(); foreach (const QJsonValue &jsonDbExceptionRule, jsonDbExceptionRules) { QOrganizerRecurrenceRule rule; jsonDbObjectToRecurrenceRule(jsonDbExceptionRule.toObject(), &rule); exceptionRules.insert(rule); } if (!exceptionRules.isEmpty()) { QOrganizerItemRecurrence recurrence = item->detail(QOrganizerItemDetail::TypeRecurrence); recurrence.setExceptionRules(exceptionRules); item->saveDetail(&recurrence); } } else if (i.key() == QOrganizerJsonDbStr::itemRecurrenceDates()) { QSet recurrenceDates; QJsonArray jsonDbRecurrenceDates = i.value().toArray(); foreach (const QJsonValue &jsonDbRecurrenceDate, jsonDbRecurrenceDates) { QDate date = QDate::fromString(jsonDbRecurrenceDate.toString(), Qt::ISODate); if (date.isValid()) recurrenceDates.insert(date); } if (!recurrenceDates.isEmpty()) { QOrganizerItemRecurrence recurrence = item->detail(QOrganizerItemDetail::TypeRecurrence); recurrence.setRecurrenceDates(recurrenceDates); item->saveDetail(&recurrence); } } else if (i.key() == QOrganizerJsonDbStr::itemExceptionDates()) { QSet exceptionDates; QJsonArray jsonDbExceptionDates = i.value().toArray(); foreach (const QJsonValue &jsonDbExceptionDate, jsonDbExceptionDates) { QDate date = QDate::fromString(jsonDbExceptionDate.toString(), Qt::ISODate); if (date.isValid()) exceptionDates.insert(date); } if (!exceptionDates.isEmpty()) { QOrganizerItemRecurrence recurrence = item->detail(QOrganizerItemDetail::TypeRecurrence); recurrence.setExceptionDates(exceptionDates); item->saveDetail(&recurrence); } } else if (i.key() == QOrganizerJsonDbStr::eventStartDateTime()) { // EventStartDateTime is the same as TodoStartDateTime, thus a "hack" here ;) QDateTime startTime = QDateTime::fromString(i.value().toString(), Qt::ISODate); if (startTime.isValid()) { if (item->type() == QOrganizerItemType::TypeEvent || item->type() == QOrganizerItemType::TypeEventOccurrence) { QOrganizerEventTime eventTime = item->detail(QOrganizerItemDetail::TypeEventTime); eventTime.setStartDateTime(startTime); item->saveDetail(&eventTime); } else if (item->type() == QOrganizerItemType::TypeTodo || item->type() == QOrganizerItemType::TypeTodoOccurrence) { QOrganizerTodoTime todoTime = item->detail(QOrganizerItemDetail::TypeTodoTime); todoTime.setStartDateTime(startTime); item->saveDetail(&todoTime); } } } else if (i.key() == QOrganizerJsonDbStr::eventEndDateTime()) { QDateTime endTime = QDateTime::fromString(i.value().toString(), Qt::ISODate); if (endTime.isValid()) { QOrganizerEventTime eventTime = item->detail(QOrganizerItemDetail::TypeEventTime); eventTime.setEndDateTime(endTime); item->saveDetail(&eventTime); } } else if (i.key() == QOrganizerJsonDbStr::todoDueDateTime()) { QDateTime dueTime = QDateTime::fromString(i.value().toString(), Qt::ISODate); if (dueTime.isValid()) { QOrganizerTodoTime todoTime = item->detail(QOrganizerItemDetail::TypeTodoTime); todoTime.setDueDateTime(dueTime); item->saveDetail(&todoTime); } } else if (i.key() == QOrganizerJsonDbStr::eventIsAllDay()) { // EventIsAllDay is the same as TodoIsAllDay, thus a "hack" here ;) bool isAllDay = i.value().toBool(); if (item->type() == QOrganizerItemType::TypeEvent || item->type() == QOrganizerItemType::TypeEventOccurrence) { QOrganizerEventTime eventTime = item->detail(QOrganizerItemDetail::TypeEventTime); eventTime.setAllDay(isAllDay); item->saveDetail(&eventTime); } else if (item->type() == QOrganizerItemType::TypeTodo || item->type() == QOrganizerItemType::TypeTodoOccurrence) { QOrganizerTodoTime todoTime = item->detail(QOrganizerItemDetail::TypeTodoTime); todoTime.setAllDay(isAllDay); item->saveDetail(&todoTime); } } else if (i.key() == QOrganizerJsonDbStr::eventAttendees()) { QJsonArray jsonDbAttendees = i.value().toArray(); foreach (const QJsonValue &jsonDbAttendee, jsonDbAttendees) { QOrganizerEventAttendee attendee; jsonDbObjectToAttendeeDetail(jsonDbAttendee.toObject(), &attendee); if (!attendee.isEmpty()) item->saveDetail(&attendee); } } else if (i.key() == QOrganizerJsonDbStr::eventRsvp()) { QJsonObject jsonDbRsvp = i.value().toObject(); if (!jsonDbRsvp.isEmpty()) { // custom fields are supported for RSVP QOrganizerEventRsvp rsvp; QOrganizerItemExtendedDetail extendedDetail; jsonDbObjectToRsvpDetail(jsonDbRsvp, &rsvp, &extendedDetail); if (!rsvp.isEmpty()) item->saveDetail(&rsvp); if (!extendedDetail.isEmpty()) item->saveDetail((&extendedDetail)); } } else if (i.key() == QOrganizerJsonDbStr::itemOccurrenceParent()) { QOrganizerItemParent parentDetail = item->detail(QOrganizerItemDetail::TypeParent); parentDetail.setParentId(QOrganizerItemId(new QOrganizerJsonDbItemId(i.value().toString()))); item->saveDetail(&parentDetail); } else if (i.key() == QOrganizerJsonDbStr::itemOccurrenceOriginalDate()) { QDate originalDate = QDate::fromString(i.value().toString(), Qt::ISODate); if (originalDate.isValid()) { QOrganizerItemParent parentDetail = item->detail(QOrganizerItemDetail::TypeParent); parentDetail.setOriginalDate(originalDate); item->saveDetail(&parentDetail); } } else if (i.key() == QOrganizerJsonDbStr::itemReminder()) { QJsonObject jsonDbReminder = i.value().toObject(); if (!jsonDbReminder.isEmpty()) { // custom fields are supported for reminders QOrganizerItemAudibleReminder audibleReminder; QOrganizerItemExtendedDetail extendedDetail; jsonDbObjectToAudibleReminderDetail(jsonDbReminder, &audibleReminder, &extendedDetail); if (!audibleReminder.isEmpty()) item->saveDetail(&audibleReminder); if (!extendedDetail.isEmpty()) item->saveDetail((&extendedDetail)); } } else if (i.key() == QOrganizerJsonDbStr::eventLocation()) { QJsonObject jsonDbLocation = i.value().toObject(); if (!jsonDbLocation.isEmpty()) { // custom fields are supported for reminders QOrganizerItemLocation location; QOrganizerItemExtendedDetail extendedDetail; jsonDbObjectToLocationDetail(jsonDbLocation, &location, &extendedDetail); if (!location.isEmpty()) item->saveDetail(&location); if (!extendedDetail.isEmpty()) item->saveDetail((&extendedDetail)); } } else if (i.key() == QOrganizerJsonDbStr::jsonDbVersion()) { QOrganizerItemVersion itemVersion; jsonDbVersionToItemVersion(i.value().toString(), &itemVersion); if (!itemVersion.isEmpty()) { item->saveDetail(&itemVersion); hasItemVersion = true; } } else if (i.key() == QOrganizerJsonDbStr::todoFinishedDateTime()) { QDateTime finishedDateTime = QDateTime::fromString(i.value().toString(), Qt::ISODate); if (finishedDateTime.isValid()) { QOrganizerTodoProgress todoProgress = item->detail(QOrganizerItemDetail::TypeTodoProgress); todoProgress.setFinishedDateTime(finishedDateTime); item->saveDetail(&todoProgress); } } else if (i.key() == QOrganizerJsonDbStr::todoProgressPercentage()) { int progressPercentage = i.value().toDouble(); if (progressPercentage >= 0 && progressPercentage <= 100) { QOrganizerTodoProgress todoProgress = item->detail(QOrganizerItemDetail::TypeTodoProgress); todoProgress.setPercentageComplete(progressPercentage); item->saveDetail(&todoProgress); } } else if (i.key() == QOrganizerJsonDbStr::todoStatus()) { QOrganizerTodoProgress::Status todoStatus = static_cast(stringToEnum(organizerTodoStatusMap(), i.value().toString())); QOrganizerTodoProgress todoProgress = item->detail(QOrganizerItemDetail::TypeTodoProgress); todoProgress.setStatus(todoStatus); item->saveDetail(&todoProgress); } else if (i.key() == QOrganizerJsonDbStr::jsonDbType()) { // skip already handled before the loop } else if (i.key().at(0) == QChar('_')) { // skip as it's used internally } else { // TODO any other fields to filter? QOrganizerItemExtendedDetail extendedDetail; extendedDetail.setName(i.key()); extendedDetail.setData(i.value().toVariant()); item->saveDetail(&extendedDetail); } ++i; } // view object is guaranteed to be correct when generated, and missing several mandatory fields as nomral objects if (jsonDbType == QOrganizerJsonDbStr::jsonDbEventViewType()) return true; return hasCollectionId && hasGuid && hasItemId && hasItemVersion; } bool QOrganizerJsonDbConverter::itemToJsonDbObject(const QOrganizerItem &item, QJsonObject *object) const { const QList details = item.details(); // the first detail should always be item type QOrganizerItemType::ItemType itemType = static_cast(details.at(0).value(QOrganizerItemType::FieldType).toInt()); switch (itemType) { case QOrganizerItemType::TypeEvent: object->insert(QOrganizerJsonDbStr::jsonDbType(), QOrganizerJsonDbStr::jsonDbEventType()); break; case QOrganizerItemType::TypeEventOccurrence: object->insert(QOrganizerJsonDbStr::jsonDbType(), QOrganizerJsonDbStr::jsonDbEventOccurrenceType()); break; case QOrganizerItemType::TypeTodo: object->insert(QOrganizerJsonDbStr::jsonDbType(), QOrganizerJsonDbStr::jsonDbTodoType()); break; case QOrganizerItemType::TypeTodoOccurrence: object->insert(QOrganizerJsonDbStr::jsonDbType(), QOrganizerJsonDbStr::jsonDbTodoOccurrenceType()); break; // case QOrganizerItemType::TypeUndefined: // case QOrganizerItemType::TypeJournal: // case QOrganizerItemType::TypeNote: default: return false; } // item ID if (!item.id().isNull()) object->insert(QOrganizerJsonDbStr::jsonDbUuid(), QOrganizerJsonDbItemId(item.id().toString()).jsondbUuid()); // collection ID has already been generated in QOrganizerJsonDbRequestThread::handleItemSaveRequest() if needed if (!item.collectionId().isNull()) object->insert(QOrganizerJsonDbStr::itemCollectionUuid(), QOrganizerJsonDbCollectionId(item.collectionId().toString()).jsondbUuid()); // certain details that allow multiple instances QJsonArray comments; QJsonArray tags; QJsonArray attendees; // go through all the supported details for (int i = 1; i < details.size(); ++i) { switch (details.at(i).type()) { case QOrganizerItemDetail::TypeComment: { QString comment = details.at(i).value(QOrganizerItemComment::FieldComment).toString(); if (!comment.isEmpty()) comments.append(comment); break; } case QOrganizerItemDetail::TypeDescription: { QString description = details.at(i).value(QOrganizerItemDescription::FieldDescription).toString(); if (!description.isEmpty()) object->insert(QOrganizerJsonDbStr::itemDescription(), description); break; } case QOrganizerItemDetail::TypeDisplayLabel: { QString displayLabel = details.at(i).value(QOrganizerItemDisplayLabel::FieldLabel).toString(); if (!displayLabel.isEmpty()) object->insert(QOrganizerJsonDbStr::itemDisplayName(), displayLabel); break; } case QOrganizerItemDetail::TypeExtendedDetail: { QString name = details.at(i).value(QOrganizerItemExtendedDetail::FieldName).toString(); if (name.isEmpty()) break; QJsonValue data = QJsonValue::fromVariant(details.at(i).value(QOrganizerItemExtendedDetail::FieldData)); if (data.isNull()) break; // custom fields are allowed for reminder, rsvp, and location // in such cases, those values in extended details will be merged with reminder (or other) object in JsonDb // for all other cases, the extended detail is stored as "name: data" in JsonDb if (name == QOrganizerJsonDbStr::itemReminder() || ((itemType == QOrganizerItemType::TypeEvent || itemType == QOrganizerItemType::TypeEventOccurrence) && (name == QOrganizerJsonDbStr::eventRsvp() || name == QOrganizerJsonDbStr::eventLocation()))) { if (!data.isObject()) break; QJsonObject existing = object->value(name).toObject(); if (existing.isEmpty()) { object->insert(name, data); } else { // combining the existing value and the value from the extended detail QJsonObject::const_iterator i = existing.constBegin(); QJsonObject newExtendedDetailProperty = data.toObject(); while (i != existing.constEnd()) { newExtendedDetailProperty.insert(i.key(), i.value()); ++i; } object->insert(name, newExtendedDetailProperty); } break; } object->insert(name, data); break; } case QOrganizerItemDetail::TypeGuid: // GUID has already been generated in QOrganizerJsonDbRequestThread::handleItemSaveRequest() if needed object->insert(QOrganizerJsonDbStr::itemGuid(), details.at(i).value(QOrganizerItemGuid::FieldGuid).toString()); break; case QOrganizerItemDetail::TypeLocation: { // in JsonDb, only events can have locations if (itemType != QOrganizerItemType::TypeEvent && itemType != QOrganizerItemType::TypeEventOccurrence) break; QJsonObject jsonDbLocation; locationDetailToJsonDbObject(details.at(i), &jsonDbLocation); if (!jsonDbLocation.isEmpty()) { // check for possible custom fields QJsonObject location = object->value(QOrganizerJsonDbStr::eventLocation()).toObject(); if (location.isEmpty()) { object->insert(QOrganizerJsonDbStr::eventLocation(), jsonDbLocation); } else { QJsonObject::const_iterator i = jsonDbLocation.constBegin(); while (i != jsonDbLocation.constEnd()) { location.insert(i.key(), i.value()); ++i; } object->insert(QOrganizerJsonDbStr::eventLocation(), location); } } break; } case QOrganizerItemDetail::TypePriority: { if (!details.at(i).isEmpty()) { object->insert(QOrganizerJsonDbStr::itemPriority(), enumToString(organizerPriorityEnumMap(), static_cast(details.at(i).value(QOrganizerItemPriority::FieldPriority).toInt()))); } break; } case QOrganizerItemDetail::TypeRecurrence: { if (itemType != QOrganizerItemType::TypeEvent && itemType != QOrganizerItemType::TypeTodo) break; const QMap values = details.at(i).values(); QMap::const_iterator j = values.constBegin(); while (j != values.constEnd()) { switch (j.key()) { case QOrganizerItemRecurrence::FieldExceptionDates: { QJsonArray exceptionDates; QSet dates = j.value().value >(); foreach (const QDate &date, dates) { if (date.isValid()) exceptionDates.append(date.toString(Qt::ISODate)); } if (!exceptionDates.isEmpty()) object->insert(QOrganizerJsonDbStr::itemExceptionDates(), exceptionDates); break; } case QOrganizerItemRecurrence::FieldRecurrenceDates: { QJsonArray recurrenceDates; QSet dates = j.value().value >(); foreach (const QDate &date, dates) { if (date.isValid()) recurrenceDates.append(date.toString(Qt::ISODate)); } if (!recurrenceDates.isEmpty()) object->insert(QOrganizerJsonDbStr::itemRecurrenceDates(), recurrenceDates); break; } case QOrganizerItemRecurrence::FieldExceptionRules: { QJsonArray exceptionRules; QSet rules = j.value().value >(); foreach (const QOrganizerRecurrenceRule &rule, rules) { QJsonObject exceptionRuleObject; recurrenceRuleToJsonDbObject(rule, &exceptionRuleObject); if (!exceptionRuleObject.isEmpty()) exceptionRules.append(exceptionRuleObject); } if (!exceptionRules.isEmpty()) object->insert(QOrganizerJsonDbStr::itemExceptionRules(), exceptionRules); break; } case QOrganizerItemRecurrence::FieldRecurrenceRules: { QJsonArray recurrenceRules; QSet rules = j.value().value >(); foreach (const QOrganizerRecurrenceRule &rule, rules) { QJsonObject recurrenceRuleObject; recurrenceRuleToJsonDbObject(rule, &recurrenceRuleObject); if (!recurrenceRuleObject.isEmpty()) recurrenceRules.append(recurrenceRuleObject); } if (!recurrenceRules.isEmpty()) object->insert(QOrganizerJsonDbStr::itemRecurrenceRules(), recurrenceRules); break; } default: break; } ++j; } break; } case QOrganizerItemDetail::TypeTag: { QString tag = details.at(i).value(QOrganizerItemTag::FieldTag).toString(); if (!tag.isEmpty()) tags.append(tag); break; } case QOrganizerItemDetail::TypeAudibleReminder: { QJsonObject jsonDbAudibleReminder; audibleReminderDetailToJsonDbObject(details.at(i), &jsonDbAudibleReminder); if (!jsonDbAudibleReminder.isEmpty()) { // check for possible custom fields QJsonObject reminder = object->value(QOrganizerJsonDbStr::itemReminder()).toObject(); if (reminder.isEmpty()) { object->insert(QOrganizerJsonDbStr::itemReminder(), jsonDbAudibleReminder); } else { QJsonObject::const_iterator i = jsonDbAudibleReminder.constBegin(); while (i != jsonDbAudibleReminder.constEnd()) { reminder.insert(i.key(), i.value()); ++i; } object->insert(QOrganizerJsonDbStr::itemReminder(), reminder); } } break; } case QOrganizerItemDetail::TypeVersion: { QString jsonDbVersion; itemVersionToJsonDbVersion(details.at(i), &jsonDbVersion); if (!jsonDbVersion.isEmpty()) object->insert(QOrganizerJsonDbStr::jsonDbVersion(), jsonDbVersion); break; } case QOrganizerItemDetail::TypeEventAttendee: { if (itemType != QOrganizerItemType::TypeEvent && itemType != QOrganizerItemType::TypeEventOccurrence) break; QJsonObject jsonDbAttendee; attendeeDetailToJsonDbObject(details.at(i), &jsonDbAttendee); if (!jsonDbAttendee.isEmpty()) attendees.append(jsonDbAttendee); break; } case QOrganizerItemDetail::TypeEventRsvp: { if (itemType != QOrganizerItemType::TypeEvent && itemType != QOrganizerItemType::TypeEventOccurrence) break; QJsonObject jsonDbRsvp; rsvpDetailToJsonDbObject(details.at(i), &jsonDbRsvp); if (!jsonDbRsvp.isEmpty()) { // check for possible custom fields QJsonObject rsvp = object->value(QOrganizerJsonDbStr::eventRsvp()).toObject(); if (rsvp.isEmpty()) { object->insert(QOrganizerJsonDbStr::eventRsvp(), jsonDbRsvp); } else { QJsonObject::const_iterator i = jsonDbRsvp.constBegin(); while (i != jsonDbRsvp.constEnd()) { rsvp.insert(i.key(), i.value()); ++i; } object->insert(QOrganizerJsonDbStr::eventRsvp(), rsvp); } } break; } case QOrganizerItemDetail::TypeParent: { if (itemType != QOrganizerItemType::TypeEventOccurrence && itemType != QOrganizerItemType::TypeTodoOccurrence) break; const QMap values = details.at(i).values(); QMap::const_iterator j = values.constBegin(); while (j != values.constEnd()) { switch (j.key()) { case QOrganizerItemParent::FieldParentId: { QOrganizerItemId parentId = j.value().value(); if (!parentId.isNull()) { object->insert(QOrganizerJsonDbStr::itemOccurrenceParent(), QOrganizerManagerEngine::engineItemId(parentId)->toString()); break; } } case QOrganizerItemParent::FieldOriginalDate: { QDate originalDate = j.value().toDate(); if (originalDate.isValid()) object->insert(QOrganizerJsonDbStr::itemOccurrenceOriginalDate(), originalDate.toString(Qt::ISODate)); break; } default: break; } ++j; } break; } case QOrganizerItemDetail::TypeEventTime: { if (itemType != QOrganizerItemType::TypeEvent && itemType != QOrganizerItemType::TypeEventOccurrence) break; const QMap values = details.at(i).values(); QMap::const_iterator j = values.constBegin(); while (j != values.constEnd()) { switch (j.key()) { case QOrganizerEventTime::FieldStartDateTime: { QDateTime startDateTime = j.value().toDateTime(); if (startDateTime.isValid()) object->insert(QOrganizerJsonDbStr::eventStartDateTime(), startDateTime.toUTC().toString(Qt::ISODate)); break; } case QOrganizerEventTime::FieldEndDateTime: { QDateTime endDateTime = j.value().toDateTime(); if (endDateTime.isValid()) object->insert(QOrganizerJsonDbStr::eventEndDateTime(), endDateTime.toUTC().toString(Qt::ISODate)); break; } case QOrganizerEventTime::FieldAllDay: { QVariant isAllDay = j.value(); if (isAllDay.canConvert(QVariant::Bool)) object->insert(QOrganizerJsonDbStr::eventIsAllDay(), isAllDay.toBool()); break; } default: break; } ++j; } break; } case QOrganizerItemDetail::TypeTodoTime: { if (itemType != QOrganizerItemType::TypeTodo && itemType != QOrganizerItemType::TypeTodoOccurrence) break; const QMap values = details.at(i).values(); QMap::const_iterator j = values.constBegin(); while (j != values.constEnd()) { switch (j.key()) { case QOrganizerTodoTime::FieldStartDateTime: { QDateTime startDateTime = j.value().toDateTime(); if (startDateTime.isValid()) object->insert(QOrganizerJsonDbStr::todoStartDateTime(), startDateTime.toUTC().toString(Qt::ISODate)); break; } case QOrganizerTodoTime::FieldDueDateTime: { QDateTime dueDateTime = j.value().toDateTime(); if (dueDateTime.isValid()) object->insert(QOrganizerJsonDbStr::todoDueDateTime(), dueDateTime.toUTC().toString(Qt::ISODate)); break; } case QOrganizerTodoTime::FieldAllDay: { QVariant isAllDay = j.value(); if (isAllDay.canConvert(QVariant::Bool)) object->insert(QOrganizerJsonDbStr::todoIsAllDay(), isAllDay.toBool()); break; } default: break; } ++j; } break; } case QOrganizerItemDetail::TypeTodoProgress: { if (itemType != QOrganizerItemType::TypeTodo && itemType != QOrganizerItemType::TypeTodoOccurrence) break;const QMap values = details.at(i).values(); QMap::const_iterator j = values.constBegin(); while (j != values.constEnd()) { switch (j.key()) { case QOrganizerTodoProgress::FieldFinishedDateTime: { QDateTime finishedDateTime = j.value().toDateTime(); if (finishedDateTime.isValid()) object->insert(QOrganizerJsonDbStr::todoFinishedDateTime(), finishedDateTime.toUTC().toString(Qt::ISODate)); break; } case QOrganizerTodoProgress::FieldPercentageComplete: { int percentageComplete = j.value().toInt(); if (percentageComplete >= 0 && percentageComplete <= 100) object->insert(QOrganizerJsonDbStr::todoProgressPercentage(), percentageComplete); break; } case QOrganizerTodoProgress::FieldStatus: { object->insert(QOrganizerJsonDbStr::todoStatus(), enumToString(organizerTodoStatusMap(), j.value().toInt())); break; } default: break; } ++j; } break; } // case QOrganizerItemDetail::TypeUndefined: // case QOrganizerItemDetail::TypeClassification: // case QOrganizerItemDetail::TypeItemType: // case QOrganizerItemDetail::TypeTimestamp: // case QOrganizerItemDetail::TypeReminder: // case QOrganizerItemDetail::TypeEmailReminder: // case QOrganizerItemDetail::TypeVisualReminder: // case QOrganizerItemDetail::TypeJournalTime: default: break; } } if (!comments.isEmpty()) object->insert(QOrganizerJsonDbStr::itemComments(), comments); if (!tags.isEmpty()) object->insert(QOrganizerJsonDbStr::itemTags(), tags); if (!attendees.isEmpty()) object->insert(QOrganizerJsonDbStr::eventAttendees(), attendees); return true; } void QOrganizerJsonDbConverter::attendeeDetailToJsonDbObject(const QOrganizerEventAttendee &attendeeDetail, QJsonObject *object) const { const QMap detailValues = attendeeDetail.values(); QMap::const_iterator i = detailValues.constBegin(); while (i != detailValues.constEnd()) { if (i.key() == QOrganizerEventAttendee::FieldName) { QString name = i.value().toString(); if (!name.isEmpty()) object->insert(QOrganizerJsonDbStr::eventAttendeeName(), name); } else if (i.key() == QOrganizerEventAttendee::FieldEmailAddress) { QString email = i.value().toString(); if (!email.isEmpty()) object->insert(QOrganizerJsonDbStr::eventAttendeeEmailAddress(), email); } else if (i.key() == QOrganizerEventAttendee::FieldAttendeeId) { QString id = i.value().toString(); if (!id.isEmpty()) object->insert(QOrganizerJsonDbStr::eventAttendeeUuid(), id); } else if (i.key() == QOrganizerEventAttendee::FieldParticipationRole) { object->insert(QOrganizerJsonDbStr::eventAttendeeParticipationRole(), enumToString(organizerParticipationRoleMap(), i.value().toInt())); } else if (i.key() == QOrganizerEventAttendee::FieldParticipationStatus) { object->insert(QOrganizerJsonDbStr::eventAttendeeParticipationStatus(), enumToString(organizerParticipationStatusMap(), i.value().toInt())); } ++i; } } void QOrganizerJsonDbConverter::jsonDbObjectToAttendeeDetail(const QJsonObject &object, QOrganizerEventAttendee *attendeeDetail) const { QJsonObject::const_iterator i = object.constBegin(); QString value; while (i != object.constEnd()) { if (i.key() == QOrganizerJsonDbStr::eventAttendeeName()) { value = i.value().toString(); if (!value.isEmpty()) attendeeDetail->setName(value); } else if (i.key() == QOrganizerJsonDbStr::eventAttendeeEmailAddress()) { value = i.value().toString(); if (!value.isEmpty()) attendeeDetail->setEmailAddress(value); } else if (i.key() == QOrganizerJsonDbStr::eventAttendeeUuid()) { value = i.value().toString(); if (!value.isEmpty()) attendeeDetail->setAttendeeId(value); } else if (i.key() == QOrganizerJsonDbStr::eventAttendeeParticipationRole()) { int intValue = stringToEnum(organizerParticipationRoleMap(), i.value().toString()); attendeeDetail->setParticipationRole(static_cast(intValue)); } else if (i.key() == QOrganizerJsonDbStr::eventAttendeeParticipationStatus()) { int intValue = stringToEnum(organizerParticipationStatusMap(), i.value().toString()); attendeeDetail->setParticipationStatus(static_cast(intValue)); } ++i; } } void QOrganizerJsonDbConverter::rsvpDetailToJsonDbObject(const QOrganizerEventRsvp &rsvpDetail, QJsonObject *object) const { const QMap rsvpValues = rsvpDetail.values(); QMap::const_iterator i = rsvpValues.constBegin(); while (i != rsvpValues.constEnd()) { if (i.key() == QOrganizerEventRsvp::FieldOrganizerName) { QString organizerName = i.value().toString(); if (!organizerName.isEmpty()) object->insert(QOrganizerJsonDbStr::eventRsvpOrganizerName(), organizerName); } else if (i.key() == QOrganizerEventRsvp::FieldOrganizerEmail) { QString organizerEmail = i.value().toString(); if (!organizerEmail.isEmpty()) object->insert(QOrganizerJsonDbStr::eventRsvpOrganizerEmail(), organizerEmail); } else if (i.key() == QOrganizerEventRsvp::FieldResponseDeadline) { QDate responseDeadline = i.value().toDate(); if (responseDeadline.isValid()) object->insert(QOrganizerJsonDbStr::eventRsvpResponseDeadline(), responseDeadline.toString(Qt::ISODate)); } else if (i.key() == QOrganizerEventRsvp::FieldResponseDate) { QDate responseDate = i.value().toDate(); if (responseDate.isValid()) object->insert(QOrganizerJsonDbStr::eventRsvpResponseDate(), responseDate.toString(Qt::ISODate)); } else if (i.key() == QOrganizerEventRsvp::FieldParticipationRole) { object->insert(QOrganizerJsonDbStr::eventRsvpParticipationRole(), enumToString(organizerParticipationRoleMap(), i.value().toInt())); } else if (i.key() == QOrganizerEventRsvp::FieldParticipationStatus) { object->insert(QOrganizerJsonDbStr::eventRsvpParticipationStatus(), enumToString(organizerParticipationStatusMap(), i.value().toInt())); } else if (i.key() == QOrganizerEventRsvp::FieldResponseRequirement) { object->insert(QOrganizerJsonDbStr::eventRsvpResponseRequirement(), enumToString(organizerResponseRequirementMap(), i.value().toInt())); } ++i; } } void QOrganizerJsonDbConverter::jsonDbObjectToRsvpDetail(const QJsonObject &object, QOrganizerEventRsvp *rsvpDetail, QOrganizerItemExtendedDetail *extendedDetail) const { QJsonObject::const_iterator i = object.constBegin(); QVariantMap customFields; while (i != object.constEnd()) { if (i.key() == QOrganizerJsonDbStr::eventRsvpOrganizerName()) { QString value = i.value().toString(); if (!value.isEmpty()) rsvpDetail->setOrganizerName(value); } else if (i.key() == QOrganizerJsonDbStr::eventRsvpOrganizerEmail()) { QString value = i.value().toString(); if (!value.isEmpty()) rsvpDetail->setOrganizerEmail(value); } else if (i.key() == QOrganizerJsonDbStr::eventRsvpResponseDeadline()) { QDate date = QDate::fromString(i.value().toString(), Qt::ISODate); if (date.isValid()) rsvpDetail->setResponseDeadline(date); } else if (i.key() == QOrganizerJsonDbStr::eventRsvpResponseDate()) { QDate date = QDate::fromString(i.value().toString(), Qt::ISODate); if (date.isValid()) rsvpDetail->setResponseDate(date); } else if (i.key() == QOrganizerJsonDbStr::eventRsvpParticipationRole()) { int intValue = stringToEnum(organizerParticipationRoleMap(), i.value().toString()); rsvpDetail->setParticipationRole(static_cast(intValue)); } else if (i.key() == QOrganizerJsonDbStr::eventRsvpParticipationStatus()) { int intValue = stringToEnum(organizerParticipationStatusMap(), i.value().toString()); rsvpDetail->setParticipationStatus(static_cast(intValue)); } else if (i.key() == QOrganizerJsonDbStr::eventRsvpResponseRequirement()) { int intValue = stringToEnum(organizerResponseRequirementMap(), i.value().toString()); rsvpDetail->setResponseRequirement(static_cast(intValue)); } else { customFields.insert(i.key(), i.value().toVariant()); } ++i; } if (!customFields.isEmpty()) { extendedDetail->setName(QOrganizerJsonDbStr::eventRsvp()); extendedDetail->setData(customFields); } } void QOrganizerJsonDbConverter::locationDetailToJsonDbObject(const QOrganizerItemLocation &locationDetail, QJsonObject *object) const { const QMap values = locationDetail.values(); QMap::const_iterator i = values.constBegin(); QJsonObject jsonDbGeo; while (i != values.constEnd()) { switch (i.key()) { case QOrganizerItemLocation::FieldLabel: { QString label = i.value().toString(); if (!label.isEmpty()) object->insert(QOrganizerJsonDbStr::eventLocationDisplayName(), label); break; } case QOrganizerItemLocation::FieldLatitude: { QVariant latitude = i.value(); if (latitude.canConvert(QVariant::Double)) jsonDbGeo.insert(QOrganizerJsonDbStr::eventLocationGeoLatitude(), latitude.toDouble()); break; } case QOrganizerItemLocation::FieldLongitude: { QVariant longitude = i.value(); if (longitude.canConvert(QVariant::Double)) jsonDbGeo.insert(QOrganizerJsonDbStr::eventLocationGeoLongitude(), longitude.toDouble()); break; } default: break; } ++i; } if (!jsonDbGeo.isEmpty()) object->insert(QOrganizerJsonDbStr::eventLocationGeo(), jsonDbGeo); } void QOrganizerJsonDbConverter::jsonDbObjectToLocationDetail(const QJsonObject &object, QOrganizerItemLocation *locationDetail, QOrganizerItemExtendedDetail *extendedDetail) const { QJsonObject::const_iterator i = object.constBegin(); QVariantMap customFields; while (i != object.constEnd()) { if (i.key() == QOrganizerJsonDbStr::eventLocationDisplayName()) { QString label = i.value().toString(); if (!label.isEmpty()) locationDetail->setLabel(label); } else if (i.key() == QOrganizerJsonDbStr::eventLocationGeo()) { QJsonObject jsonDbGeo = i.value().toObject(); QJsonObject::const_iterator j = jsonDbGeo.constBegin(); while (j != jsonDbGeo.constEnd()) { if (j.key() == QOrganizerJsonDbStr::eventLocationGeoLatitude()) { if (j.value().isDouble()) locationDetail->setLatitude(j.value().toDouble()); } else if (j.key() == QOrganizerJsonDbStr::eventLocationGeoLongitude()) { if (j.value().isDouble()) locationDetail->setLongitude(j.value().toDouble()); } ++j; } } else { customFields.insert(i.key(), i.value().toVariant()); } ++i; } if (!customFields.isEmpty()) { extendedDetail->setName(QOrganizerJsonDbStr::eventLocation()); extendedDetail->setData(customFields); } } bool QOrganizerJsonDbConverter::jsonDbObjectToCollection(const QJsonObject &object, QOrganizerCollection *collection, bool *isDefaultCollection, QOrganizerJsonDbEngine::StorageLocation storageLocation) const { bool hasCollectionId(false); QVariantMap extendedMetaData; QJsonObject::const_iterator i = object.constBegin(); while (i != object.constEnd()) { if (i.key() == QOrganizerJsonDbStr::jsonDbUuid()) { QString jsonUuid = i.value().toString(); if (jsonUuid.isEmpty()) return false; QOrganizerJsonDbCollectionId *jsondbCollectionId = new QOrganizerJsonDbCollectionId(); jsondbCollectionId->setJsonDbUuid(jsonUuid); jsondbCollectionId->setStorageLocation(storageLocation); collection->setId(QOrganizerCollectionId(jsondbCollectionId)); hasCollectionId = true; } else if (i.key() == QOrganizerJsonDbStr::collectionDisplayName()) { QString name = i.value().toString(); if (!name.isEmpty()) collection->setMetaData(QOrganizerCollection::KeyName, name); } else if (i.key() == QOrganizerJsonDbStr::collectionDescription()) { QString description = i.value().toString(); if (!description.isEmpty()) collection->setMetaData(QOrganizerCollection::KeyDescription, description); } else if (i.key() == QOrganizerJsonDbStr::collectionColor()) { QString color = i.value().toString(); if (!color.isEmpty()) collection->setMetaData(QOrganizerCollection::KeyColor, color); } else if (i.key() == QOrganizerJsonDbStr::collectionImageUrl()) { QString image = i.value().toString(); if (!image.isEmpty()) collection->setMetaData(QOrganizerCollection::KeyImage, image); } else if (i.key() == QOrganizerJsonDbStr::collectionDefaultFlag()) { if (i.value().isBool()) *isDefaultCollection = i.value().toBool(); } else { // custom meta data if (i.key().at(0) != QChar('_') && !i.key().isEmpty() && !i.value().isNull()) extendedMetaData.insert(i.key(), i.value().toVariant()); } ++i; } if (!extendedMetaData.isEmpty()) collection->setMetaData(QOrganizerCollection::KeyExtended, extendedMetaData); return hasCollectionId; } bool QOrganizerJsonDbConverter::collectionToJsonDbObject(const QOrganizerCollection &collection, bool isDefaultCollection, QJsonObject *object) const { QOrganizerCollectionId collectionId = collection.id(); if (!collectionId.isNull()) { object->insert(QOrganizerJsonDbStr::jsonDbUuid(), QOrganizerJsonDbCollectionId(collectionId.toString()).jsondbUuid()); } object->insert(QOrganizerJsonDbStr::jsonDbType(), QOrganizerJsonDbStr::jsonDbCollectionType()); if (isDefaultCollection) object->insert(QOrganizerJsonDbStr::collectionDefaultFlag(), isDefaultCollection); QMap metaData = collection.metaData(); QMap::const_iterator i = metaData.constBegin(); while (i != metaData.constEnd()) { if (i.key() == QOrganizerCollection::KeyColor) { QString colorString = i.value().toString(); if (!colorString.isEmpty()) object->insert(QOrganizerJsonDbStr::collectionColor(), colorString); } else if (i.key() == QOrganizerCollection::KeyDescription) { QString descriptionString = i.value().toString(); if (!descriptionString.isEmpty()) object->insert(QOrganizerJsonDbStr::collectionDescription(), descriptionString); } else if (i.key() == QOrganizerCollection::KeyImage) { QString imageString = i.value().toString(); if (!imageString.isEmpty()) object->insert(QOrganizerJsonDbStr::collectionImageUrl(), imageString); } else if (i.key() == QOrganizerCollection::KeyName) { QString displayNameString = i.value().toString(); if (!displayNameString.isEmpty()) object->insert(QOrganizerJsonDbStr::collectionDisplayName(), displayNameString); } else if (i.key() == QOrganizerCollection::KeyExtended) { QVariantMap variantMap = i.value().toMap(); if (!variantMap.isEmpty()) { QVariantMap::const_iterator j = variantMap.constBegin(); while (j != variantMap.constEnd()) { QString valueString = j.value().toString(); if (!j.key().isEmpty() && !valueString.isEmpty() && (j.key().at(0) != QChar('_') || j.key() == QOrganizerJsonDbStr::jsonDbVersion()) && j.key() != QOrganizerJsonDbStr::collectionDefaultFlag()) { // XXX Should we allow complex data structure, i.e. list or map, for custom meta data? object->insert(j.key(), valueString); } ++j; } } } ++i; } return true; } void QOrganizerJsonDbConverter::jsonDbVersionToItemVersion(const QString &jsonDbVersion, QOrganizerItemVersion *itemVersion) const { QStringList jsonDbVersions = jsonDbVersion.split(QLatin1Char('-')); if (jsonDbVersions.size() != 2) return; int version = jsonDbVersions.at(0).toInt(); if (version > 0) { itemVersion->setVersion(version); itemVersion->setExtendedVersion(jsonDbVersions.at(1).toLatin1()); } } const QStringList QOrganizerJsonDbConverter::storageLocationsFlagToStrings(const QOrganizerJsonDbEngine::StorageLocations storageLocationsFlag) { QStringList storageLocations; if (QOrganizerJsonDbEngine::UserDataStorage & storageLocationsFlag) storageLocations.append(enumToString(organizerStorageLocationMap(), QOrganizerJsonDbEngine::UserDataStorage)); if (QOrganizerJsonDbEngine::SystemStorage & storageLocationsFlag) storageLocations.append(enumToString(organizerStorageLocationMap(), QOrganizerJsonDbEngine::SystemStorage)); return storageLocations; } QOrganizerJsonDbEngine::StorageLocation QOrganizerJsonDbConverter::storageLocationStringToEnum(const QString &storageLocation) { return QOrganizerJsonDbEngine::StorageLocation(stringToEnum(organizerStorageLocationMap(), storageLocation)); } QOrganizerJsonDbEngine::StorageLocations QOrganizerJsonDbConverter::storageLocationListToFlag(const QList storageLocationsList) { QOrganizerJsonDbEngine::StorageLocations locationsFlag(0); foreach (QOrganizerJsonDbEngine::StorageLocation location, storageLocationsList) { locationsFlag |= location; } return locationsFlag; } void QOrganizerJsonDbConverter::itemVersionToJsonDbVersion(const QOrganizerItemVersion &itemVersion, QString *jsonDbVersion) const { int version = itemVersion.version(); QByteArray extendedVersion = itemVersion.extendedVersion(); if (version > 0) { *jsonDbVersion = QString::number(version) + QStringLiteral("-") + QString::fromLatin1(extendedVersion.constData()); } } void QOrganizerJsonDbConverter::jsonDbObjectToRecurrenceRule(const QJsonObject &object, QOrganizerRecurrenceRule *rule) const { QJsonObject::const_iterator i = object.constBegin(); while (i != object.constEnd()) { if (i.key() == QOrganizerJsonDbStr::itemRecurrenceRuleFrequency()) { QString frequency = i.value().toString(); if (!frequency.isEmpty()) rule->setFrequency(static_cast(stringToEnum(organizerFrequencyEnumMap(), frequency))); } else if (i.key() == QOrganizerJsonDbStr::itemRecurrenceRuleInterval()) { int interval = i.value().toDouble(); if (interval >= 0) rule->setInterval(interval); } else if (i.key() == QOrganizerJsonDbStr::itemRecurrenceRuleLimitCount()) { int limitCount = i.value().toDouble(); if (limitCount >= 0) rule->setLimit(limitCount); } else if (i.key() == QOrganizerJsonDbStr::itemRecurrenceRuleLimitDate()) { QDate limitDate = QDate::fromString(i.value().toString(), Qt::ISODate); if (limitDate.isValid()) rule->setLimit(limitDate); } else if (i.key() == QOrganizerJsonDbStr::itemRecurrenceRuleFirstDayOfWeek()) { QString firstDayOfWeek = i.value().toString(); if (!firstDayOfWeek.isEmpty()) rule->setFirstDayOfWeek(static_cast(stringToEnum(organizerDayEnumMap(), firstDayOfWeek))); } else if (i.key() == QOrganizerJsonDbStr::itemRecurrenceRulePositions()) { QJsonArray jsonDbPositions = i.value().toArray(); if (!jsonDbPositions.isEmpty()) { QSet positionsSet; foreach (const QJsonValue &jsonDbPosition, jsonDbPositions) positionsSet.insert(jsonDbPosition.toDouble()); rule->setPositions(positionsSet); } } else if (i.key() == QOrganizerJsonDbStr::itemRecurrenceRuleDaysOfWeek()) { QJsonArray jsonDbDaysOfWeek = i.value().toArray(); if (!jsonDbDaysOfWeek.isEmpty()) { QSet daysOfWeek; foreach (const QJsonValue &jsonDbDayOfWeek, jsonDbDaysOfWeek) daysOfWeek.insert(static_cast(stringToEnum(organizerDayEnumMap(), jsonDbDayOfWeek.toString()))); rule->setDaysOfWeek(daysOfWeek); } } else if (i.key() == QOrganizerJsonDbStr::itemRecurrenceRuleDaysOfMonth()) { QJsonArray jsonDbDaysOfMonth = i.value().toArray(); if (!jsonDbDaysOfMonth.isEmpty()) { QSet daysOfMonth; foreach (const QJsonValue &jsonDbDayOfMonth, jsonDbDaysOfMonth) daysOfMonth.insert(jsonDbDayOfMonth.toDouble()); rule->setDaysOfMonth(daysOfMonth); } } else if (i.key() == QOrganizerJsonDbStr::itemRecurrenceRuleDaysOfYear()) { QJsonArray jsonDbDaysOfYear = i.value().toArray(); if (!jsonDbDaysOfYear.isEmpty()) { QSet daysOfYear; foreach (const QJsonValue &jsonDbDayOfYear, jsonDbDaysOfYear) daysOfYear.insert(jsonDbDayOfYear.toDouble()); rule->setDaysOfYear(daysOfYear); } } else if (i.key() == QOrganizerJsonDbStr::itemRecurrenceRuleWeeksOfYear()) { QJsonArray jsonDbWeeksOfYear = i.value().toArray(); if (!jsonDbWeeksOfYear.isEmpty()) { QSet weeksOfYear; foreach (const QJsonValue &jsonDbWeekOfYear, jsonDbWeeksOfYear) weeksOfYear.insert(jsonDbWeekOfYear.toDouble()); rule->setWeeksOfYear(weeksOfYear); } } else if (i.key() == QOrganizerJsonDbStr::itemRecurrenceRuleMonthsOfYear()) { QJsonArray jsonDbMonthsOfYear = i.value().toArray(); if (!jsonDbMonthsOfYear.isEmpty()) { QSet monthsOfYear; foreach (const QJsonValue &jsonDbMonthOfYear, jsonDbMonthsOfYear) monthsOfYear.insert(static_cast(stringToEnum(organizerMonthEnumMap(), jsonDbMonthOfYear.toString()))); rule->setMonthsOfYear(monthsOfYear); } } ++i; } } void QOrganizerJsonDbConverter::recurrenceRuleToJsonDbObject(const QOrganizerRecurrenceRule &rule, QJsonObject *object) const { // Invalid is the default value, so no need to save QOrganizerRecurrenceRule::Frequency frequency = rule.frequency(); if (frequency != QOrganizerRecurrenceRule::Invalid) object->insert(QOrganizerJsonDbStr::itemRecurrenceRuleFrequency(), enumToString(organizerFrequencyEnumMap(), frequency)); // 1 is the default value, so no need to save int interval = rule.interval(); if (interval > 1) object->insert(QOrganizerJsonDbStr::itemRecurrenceRuleInterval(), rule.interval()); // only saves the limit value if it's used QOrganizerRecurrenceRule::LimitType limitType = rule.limitType(); if (limitType == QOrganizerRecurrenceRule::CountLimit) object->insert(QOrganizerJsonDbStr::itemRecurrenceRuleLimitCount(), rule.limitCount()); else if (limitType == QOrganizerRecurrenceRule::DateLimit) object->insert(QOrganizerJsonDbStr::itemRecurrenceRuleLimitDate(), rule.limitDate().toString(Qt::ISODate)); // Monday is the default value, so no need to save Qt::DayOfWeek firstDayOfWeek = rule.firstDayOfWeek(); if (firstDayOfWeek != Qt::Monday) object->insert(QOrganizerJsonDbStr::itemRecurrenceRuleFirstDayOfWeek(), enumToString(organizerDayEnumMap(), firstDayOfWeek)); QSet positions = rule.positions(); if (!positions.isEmpty()) { QJsonArray positionsList; foreach (int position, positions) positionsList.append(position); object->insert(QOrganizerJsonDbStr::itemRecurrenceRulePositions(), positionsList); } QSet daysOfWeek = rule.daysOfWeek(); if (!daysOfWeek.isEmpty()) { QJsonArray daysOfWeekList; foreach (Qt::DayOfWeek day, daysOfWeek) daysOfWeekList.append(enumToString(organizerDayEnumMap(), day)); object->insert(QOrganizerJsonDbStr::itemRecurrenceRuleDaysOfWeek(), daysOfWeekList); } QSet daysOfMonth = rule.daysOfMonth(); if (!daysOfMonth.isEmpty()) { QJsonArray daysOfMonthList; foreach (int day, daysOfMonth) daysOfMonthList.append(day); object->insert(QOrganizerJsonDbStr::itemRecurrenceRuleDaysOfMonth(), daysOfMonthList); } QSet daysOfYear = rule.daysOfYear(); if (!daysOfYear.isEmpty()) { QJsonArray daysOfYearList; foreach (int day, daysOfYear) daysOfYearList.append(day); object->insert(QOrganizerJsonDbStr::itemRecurrenceRuleDaysOfYear(), daysOfYearList); } QSet weeksOfYear = rule.weeksOfYear(); if (!weeksOfYear.isEmpty()) { QJsonArray weeksOfYearList; foreach (int week, weeksOfYear) weeksOfYearList.append(week); object->insert(QOrganizerJsonDbStr::itemRecurrenceRuleWeeksOfYear(), weeksOfYearList); } QSet monthsOfYear = rule.monthsOfYear(); if (!monthsOfYear.isEmpty()) { QJsonArray monthsOfYearList; foreach (QOrganizerRecurrenceRule::Month month, monthsOfYear) monthsOfYearList.append(enumToString(organizerMonthEnumMap(), month)); object->insert(QOrganizerJsonDbStr::itemRecurrenceRuleMonthsOfYear(), monthsOfYearList); } } void QOrganizerJsonDbConverter::audibleReminderDetailToJsonDbObject(const QOrganizerItemAudibleReminder &itemReminder, QJsonObject *object) const { const QMap reminderValues = itemReminder.values(); QMap::const_iterator i = reminderValues.constBegin(); while (i != reminderValues.constEnd()) { if (i.key() == QOrganizerItemAudibleReminder::FieldSecondsBeforeStart) { int secondsBeforeStart = i.value().toInt(); if (secondsBeforeStart >= 0) object->insert(QOrganizerJsonDbStr::itemReminderSecBeforeStart(), secondsBeforeStart); } else if (i.key() == QOrganizerItemAudibleReminder::FieldRepetitionCount) { int repetitionCount = i.value().toInt(); if (repetitionCount > 0) object->insert(QOrganizerJsonDbStr::itemReminderRepetitionCount(), repetitionCount); } else if (i.key() == QOrganizerItemAudibleReminder::FieldRepetitionDelay) { int repetitionDelay = i.value().toInt(); if (repetitionDelay > 0) object->insert(QOrganizerJsonDbStr::itemReminderRepetitionDelay(), repetitionDelay); } else if (i.key() == QOrganizerItemAudibleReminder::FieldDataUrl) { if (i.value().toUrl().isValid()) object->insert(QOrganizerJsonDbStr::itemReminderDataUrl(), i.value().toString()); } ++i; } } void QOrganizerJsonDbConverter::jsonDbObjectToAudibleReminderDetail(const QJsonObject &object, QOrganizerItemAudibleReminder *itemReminder, QOrganizerItemExtendedDetail *extendedDetail) const { QJsonObject::const_iterator i = object.constBegin(); QVariantMap customFields; while (i != object.constEnd()) { if (i.key() == QOrganizerJsonDbStr::itemReminderSecBeforeStart()) { int seconds = i.value().toDouble(); if (seconds >= 0) itemReminder->setValue(QOrganizerItemAudibleReminder::FieldSecondsBeforeStart, seconds); } else if (i.key() == QOrganizerJsonDbStr::itemReminderRepetitionCount()) { int repetitionCount = i.value().toDouble(); if (repetitionCount > 0) itemReminder->setValue(QOrganizerItemAudibleReminder::FieldRepetitionCount, repetitionCount); } else if (i.key() == QOrganizerJsonDbStr::itemReminderRepetitionDelay()) { int repetitionDelay = i.value().toDouble(); if (repetitionDelay > 0) itemReminder->setValue(QOrganizerItemAudibleReminder::FieldRepetitionDelay, repetitionDelay); } else if (i.key() == QOrganizerJsonDbStr::itemReminderDataUrl()) { QUrl url(i.value().toString()); if (url.isValid()) itemReminder->setValue(QOrganizerItemAudibleReminder::FieldDataUrl, url); } else { customFields.insert(i.key(), i.value().toVariant()); } ++i; } if (!customFields.isEmpty()) { extendedDetail->setName(QOrganizerJsonDbStr::itemReminder()); extendedDetail->setData(customFields); } } int QOrganizerJsonDbConverter::stringToEnum(const QOrganizerJsonDbEnumConversionData* const conversionData, const QString &enumStr) const { int i = 0; while (conversionData[i].enumValue != enumMapEnd) { if (conversionData[i].enumStr == enumStr) return conversionData[i].enumValue; i++; } // first index contains default values return conversionData[0].enumValue; } QString QOrganizerJsonDbConverter::enumToString(const QOrganizerJsonDbEnumConversionData* const conversionData, int enumValue) const { int i = 0; while (conversionData[i].enumValue != enumMapEnd) { if (conversionData[i].enumValue == enumValue) return conversionData[i].enumStr; i++; } // first index contains default values return conversionData[0].enumStr; } bool QOrganizerJsonDbConverter::compoundFilterToJsondbQuery(const QOrganizerItemFilter &filter, QString *jsonDbQueryStr, int *typeFilterFlag) const { bool isValidFilter = true; switch (filter.type()) { case QOrganizerItemFilter::IntersectionFilter: { const QOrganizerItemIntersectionFilter isf(filter); const QList filterList = isf.filters(); foreach (const QOrganizerItemFilter &filter, filterList){ //query filter1 filter2 filter3 ... //query [?definition="value"][?definition="value"][?definition="value"] QString filterStr; int subtypeFilterFlag = supportedItemTypeFlag(); if (compoundFilterToJsondbQuery(filter, &filterStr, &subtypeFilterFlag)) { *jsonDbQueryStr += filterStr; *typeFilterFlag = *typeFilterFlag & subtypeFilterFlag; } else {//For intersection filter, single filter invalid means empty result from jsondb query isValidFilter = false; } } return isValidFilter; } case QOrganizerItemFilter::UnionFilter: { const QOrganizerItemUnionFilter uf(filter); const QList filterList = uf.filters(); int validFilterCount = 0; *typeFilterFlag = 0; foreach (const QOrganizerItemFilter &filter, filterList){ //query filter1 filter2 filter3 ... //query [?definition="value" | definition="value" | definition="value"] QString filterStr; int subtypeFilterFlag = supportedItemTypeFlag(); if (compoundFilterToJsondbQuery(filter, &filterStr, &subtypeFilterFlag)) { *jsonDbQueryStr += filterStr; validFilterCount ++; *typeFilterFlag = *typeFilterFlag | subtypeFilterFlag; } else {//For union filter, single filter invalid means we could skip this filter continue; } } if (validFilterCount > 0) jsonDbQueryStr->replace(QStringLiteral("][?"), QStringLiteral(" | ")); //replace the "][?" to " | " else //no valid filter means empty item list from jsondb isValidFilter = false; return isValidFilter; } default: isValidFilter = singleFilterToJsondbQuery(filter, jsonDbQueryStr, typeFilterFlag); break; } if (!isValidFilter) jsonDbQueryStr->clear(); return isValidFilter; } bool QOrganizerJsonDbConverter::singleFilterToJsondbQuery(const QOrganizerItemFilter& filter, QString *jsonDbQueryStr, int *typeFilterFlag) const { bool isValidFilter = true; switch (filter.type()) { case QOrganizerItemFilter::CollectionFilter: isValidFilter = collectionFilterToJsondbQuery(filter, jsonDbQueryStr); break; case QOrganizerItemFilter::IdFilter: isValidFilter = idFilterToJsondbQuery(filter, jsonDbQueryStr); break; case QOrganizerItemFilter::DetailFieldFilter: isValidFilter = detailFieldFilterToJsondbQuery(filter, jsonDbQueryStr, typeFilterFlag); break; case QOrganizerItemFilter::DetailFilter: isValidFilter = detailFilterToJsondbQuery(filter, jsonDbQueryStr, typeFilterFlag); break; default: break; } return isValidFilter; } QString QOrganizerJsonDbConverter::jsonDbNotificationObjectToOrganizerType(const QJsonObject &object) const { return object.value(QOrganizerJsonDbStr::jsonDbType()).toString(); } QOrganizerItemId QOrganizerJsonDbConverter::jsonDbNotificationObjectToItemId(const QJsonObject &object, QOrganizerJsonDbEngine::StorageLocation storageLocation) const { QString jsonDbUuid = object.value(QOrganizerJsonDbStr::jsonDbUuid()).toString(); if (jsonDbUuid.isEmpty()) { return QOrganizerItemId(); } else { QOrganizerJsonDbItemId *jsondbItemId = new QOrganizerJsonDbItemId(); jsondbItemId->setJsonDbUuid(jsonDbUuid); jsondbItemId->setStorageLocation(storageLocation); return QOrganizerItemId(jsondbItemId); } } QOrganizerCollectionId QOrganizerJsonDbConverter::jsonDbNotificationObjectToCollectionId(const QJsonObject &object, QOrganizerJsonDbEngine::StorageLocation storageLocation) const { QString jsonUuid = object.value(QOrganizerJsonDbStr::jsonDbUuid()).toString(); if (jsonUuid.isEmpty()) { return QOrganizerCollectionId(); } else { QOrganizerJsonDbCollectionId *jsondbCollectionId = new QOrganizerJsonDbCollectionId(); jsondbCollectionId->setJsonDbUuid(jsonUuid); jsondbCollectionId->setStorageLocation(storageLocation); return QOrganizerCollectionId(jsondbCollectionId); } } bool QOrganizerJsonDbConverter::collectionFilterToJsondbQuery(const QOrganizerItemFilter &filter, QString *jsonDbQueryStr) const { bool isValidFilter = true; const QOrganizerItemCollectionFilter cf(filter); const QSet& ids = cf.collectionIds(); if (!ids.empty()) { const QString idTemplate(QStringLiteral("\"%1\",")); QString query; foreach (const QOrganizerCollectionId &id, ids) { if (!id.isNull()) query += idTemplate.arg(QOrganizerJsonDbCollectionId(id.toString()).jsondbUuid()); } if (!query.isEmpty()) { query.truncate(query.length() - 1); *jsonDbQueryStr = QOrganizerJsonDbStr::jsonDbQueryCollectionUuidsTemplate().arg(query); } else { isValidFilter = false; } } else { isValidFilter = false; } return isValidFilter; } bool QOrganizerJsonDbConverter::idFilterToJsondbQuery(const QOrganizerItemFilter &filter, QString *jsonDbQueryStr) const { bool isValidFilter = true; const QOrganizerItemIdFilter idf(filter); const QList& ids = idf.ids(); if (!ids.empty()) { const QString uuidTemplate(QStringLiteral("\"%1\",")); QString query; foreach (const QOrganizerItemId &id, ids) { if (!id.isNull()) query += uuidTemplate.arg(QOrganizerJsonDbItemId(id.toString()).jsondbUuid()); } if (!query.isEmpty()) { query.truncate(query.length() - 1); *jsonDbQueryStr = QOrganizerJsonDbStr::jsonDbQueryUuidsTemplate().arg(query); } else { isValidFilter = false; } } else { isValidFilter = false; } return isValidFilter; } bool QOrganizerJsonDbConverter::isSupportedDetailFieldFilter( const QVariant &fieldValue, QOrganizerItemDetail::DetailType detailType, int detailFieldName, QOrganizerItemFilter::MatchFlags matchFlags) const { bool isValidFilter = true; if (detailType == QOrganizerItemDetail::TypeUndefined && detailFieldName == -1 && fieldValue.toString().isEmpty()) { // no support when any of the fields is empty isValidFilter = false; } else if (QOrganizerItemDetail::TypeJournalTime == detailType || QOrganizerItemDetail::TypeReminder == detailType || QOrganizerItemDetail::TypeAudibleReminder == detailType || QOrganizerItemDetail::TypeVisualReminder == detailType || QOrganizerItemDetail::TypeEmailReminder == detailType || QOrganizerItemDetail::TypeRecurrence == detailType || QOrganizerItemDetail::TypeTimestamp == detailType || QOrganizerItemDetail::TypeEventAttendee == detailType) { // filtering certain details/definitions are currently not supported isValidFilter = false; } else if (QOrganizerItemFilter::MatchExactly != matchFlags && (QOrganizerItemDetail::TypeEventTime == detailType || QOrganizerItemDetail::TypeTodoTime == detailType || QOrganizerItemDetail::TypeTodoProgress == detailType || QOrganizerItemDetail::TypeComment == detailType || (QOrganizerItemDetail::TypeLocation == detailType && (QOrganizerItemLocation::FieldLatitude == detailFieldName || QOrganizerItemLocation::FieldLongitude == detailFieldName)) || QOrganizerItemDetail::TypePriority == detailType || QOrganizerItemDetail::TypeItemType == detailType || QOrganizerItemDetail::TypeTag == detailType || QOrganizerItemDetail::TypeExtendedDetail == detailType || (QOrganizerItemDetail::TypeEventRsvp == detailType && (QOrganizerEventRsvp::FieldParticipationStatus == detailFieldName || QOrganizerEventRsvp::FieldParticipationRole == detailFieldName || QOrganizerEventRsvp::FieldResponseRequirement == detailFieldName || QOrganizerEventRsvp::FieldResponseDeadline == detailFieldName || QOrganizerEventRsvp::FieldResponseDate == detailFieldName)) || QOrganizerItemDetail::TypeParent == detailType)) { // filtering matchflags are not supported for all the types isValidFilter = false; } else if (QVariant::String == fieldValue.type() && !(QOrganizerItemDetail::TypeComment == detailType || QOrganizerItemDetail::TypeDescription == detailType || QOrganizerItemDetail::TypeDisplayLabel == detailType || QOrganizerItemDetail::TypeGuid == detailType || (QOrganizerItemDetail::TypeLocation == detailType && QOrganizerItemLocation::FieldLabel == detailFieldName) || QOrganizerItemDetail::TypeTag == detailType || QOrganizerItemDetail::TypeExtendedDetail == detailType || QOrganizerItemDetail::TypeItemType == detailType || (QOrganizerItemDetail::TypeEventRsvp == detailType && (QOrganizerEventRsvp::FieldOrganizerEmail == detailFieldName || QOrganizerEventRsvp::FieldOrganizerName == detailFieldName)))) { // filtering with QString needs extra attention, not allowed for all the types isValidFilter = false; } return isValidFilter; } bool QOrganizerJsonDbConverter::detailFilterToJsondbQuery(const QOrganizerItemFilter &filter, QString *jsonDbQueryStr, int *typeFilterFlag) const { const QOrganizerItemDetailFilter df(filter); const QOrganizerItemDetail detail(df.detail()); const QOrganizerItemDetail::DetailType detailType (detail.type()); //We do not currently support detailFilter on Comment or Tag details if ( (detailType == QOrganizerItemDetail::TypeComment) || (detailType == QOrganizerItemDetail::TypeTag) || (detail.values().isEmpty()) ) return false; if (detailType == QOrganizerItemDetail::TypeExtendedDetail) { if (detail.values().size() != 2) { //Both Name and Data fields should be present, otherwise filter is invalid return false; } else { QVariant extDetailDataValue = detail.value(QOrganizerItemExtendedDetail::FieldData); QString extDetailValueString = extDetailDataValue.toString(); QJsonValue jsonVal = QJsonValue::fromVariant(extDetailDataValue); if ( jsonVal.isString()) extDetailValueString = QString(QStringLiteral("\"%1\"").arg(extDetailValueString)); jsonDbQueryStr->append(QStringLiteral("[?%1=%2]") .arg(detail.value(QOrganizerItemExtendedDetail::FieldName).toString()) .arg(extDetailValueString)); return true; } } QVariant fieldValue; foreach (int field, QOrganizerJsonDbEngine::supportedDetailFields(detailType)) { if (detail.hasValue(field)) { fieldValue = detail.value(field); if (isSupportedDetailFieldFilter(fieldValue, detailType, field, QOrganizerItemFilter::MatchExactly)) { addFieldToFilterQuery(detailType, field, fieldValue, jsonDbQueryStr, QOrganizerItemFilter::MatchExactly, typeFilterFlag); } else { return false; } } else { jsonDbQueryStr->append(QStringLiteral("[?%1 notExists]").arg(filterablePropertyNames().value(field))); } } return true; } bool QOrganizerJsonDbConverter::detailFieldFilterToJsondbQuery(const QOrganizerItemFilter &filter, QString *jsonDbQueryStr, int *typeFilterFlag) const { /* Jsondb backend specific notes related to OrganizerItemDetailFieldFilter Detail specific: - Currently supported details; EventTime, TodoTime, Comment, Description, DisplayLabel, Gui, Location, Priority, Type, Tag, Parent and Customized(ExtendedDetail). - Type-detail is mapped from enum to string, since the C++ side is using strings. - Comment- and Tag-details can only be filtered with MatchExactly. No wildcards supported. - Customized(ExtendedDetail)-detail can only be used to filter the custom field name, not the data value. Used custom field names should be simple and clear, no spaces or special characters. Matchflags are not supported either, only exact matching works. - No support to filter based only on detail-type without any value. Like filter everything which has Priority-field regardless of the value of the field. Matchflags: - MatchExactly: -Works with all the supported detail types/fields (above) -Only same object type supported for filtering, ie strings as Dates are not supported - MatchFixedString - Ignores any wildcard flags as filters as exact string - MatchContains, MatchEndsWith, MatchStartsWith, MatchCaseSensitive - These are supported only for details having string as a value type - Works only if MatchExactly or MatchFixedString are not set */ const QOrganizerItemDetailFieldFilter dff(filter); const QOrganizerItemDetail::DetailType detailType(dff.detailType()); const int detailField(dff.detailField()); if (isSupportedDetailFieldFilter(dff.value(), detailType, detailField, dff.matchFlags())) { addFieldToFilterQuery(detailType, detailField, dff.value(), jsonDbQueryStr, dff.matchFlags(), typeFilterFlag); return true; } else { return false; } } void QOrganizerJsonDbConverter::addFieldToFilterQuery(QOrganizerItemDetail::DetailType detailType, int detailField, const QVariant &fieldValue, QString *jsonDbQueryStr, QOrganizerItemFilter::MatchFlags matchFlags, int *typeFilterFlag) const { const QString equalsQueryTemplate(QStringLiteral("[?%1=\"%2\"]")); const QString equalsQueryTemplate2(QStringLiteral("[?%1=%2]")); const QString equalsQueryTemplate3(QStringLiteral("[?%1.%2.%3=%4]")); const QString containsQueryTemplate(QStringLiteral("[?%1 contains \"%2\"]")); const QString matchFlagQueryTemplate(QStringLiteral("[?%1%2\"]")); const QString matchFlagQueryTemplate2(QStringLiteral("[?%1.%2%3\"]")); const QString existsQueryTemplate(QStringLiteral("[?%1 exists]")); const QString valueString(fieldValue.toString()); if (QOrganizerItemDetail::TypeEventTime == detailType) { if (QOrganizerEventTime::FieldStartDateTime == detailField) { jsonDbQueryStr->append(equalsQueryTemplate .arg(QOrganizerJsonDbStr::eventStartDateTime()).arg(fieldValue.toDateTime().toUTC().toString(Qt::ISODate))); } else if (QOrganizerEventTime::FieldEndDateTime == detailField) { jsonDbQueryStr->append(equalsQueryTemplate .arg(QOrganizerJsonDbStr::eventEndDateTime()).arg(fieldValue.toDateTime().toUTC().toString(Qt::ISODate))); } else if (QOrganizerEventTime::FieldAllDay == detailField) { jsonDbQueryStr->append(equalsQueryTemplate2 .arg(QOrganizerJsonDbStr::eventIsAllDay()).arg(valueString)); } } else if (QOrganizerItemDetail::TypeTodoTime == detailType) { if (QOrganizerTodoTime::FieldStartDateTime == detailField) { jsonDbQueryStr->append(equalsQueryTemplate .arg(QOrganizerJsonDbStr::todoStartDateTime()).arg(fieldValue.toDateTime().toUTC().toString(Qt::ISODate))); } else if (QOrganizerTodoTime::FieldDueDateTime == detailField) { jsonDbQueryStr->append(equalsQueryTemplate .arg(QOrganizerJsonDbStr::todoDueDateTime()).arg(fieldValue.toDateTime().toUTC().toString(Qt::ISODate))); } else if (QOrganizerTodoTime::FieldAllDay == detailField) { jsonDbQueryStr->append(equalsQueryTemplate2 .arg(QOrganizerJsonDbStr::todoIsAllDay()).arg(valueString)); } } else if (QOrganizerItemDetail::TypeTodoProgress == detailType) { if (QOrganizerTodoProgress::FieldFinishedDateTime == detailField) { jsonDbQueryStr->append(equalsQueryTemplate .arg(QOrganizerJsonDbStr::todoFinishedDateTime()) .arg(fieldValue.toDateTime().toUTC().toString(Qt::ISODate))); } else if (QOrganizerTodoProgress::FieldPercentageComplete == detailField) { jsonDbQueryStr->append(equalsQueryTemplate2 .arg(QOrganizerJsonDbStr::todoProgressPercentage()) .arg(fieldValue.toInt())); } else if (QOrganizerTodoProgress::FieldStatus == detailField) { jsonDbQueryStr->append(equalsQueryTemplate .arg(QOrganizerJsonDbStr::todoStatus()) .arg(enumToString(organizerTodoStatusMap(), fieldValue.toInt()))); } } else if (QOrganizerItemDetail::TypeComment == detailType && QOrganizerItemComment::FieldComment == detailField) { jsonDbQueryStr->append(containsQueryTemplate.arg(QOrganizerJsonDbStr::itemComments()).arg(valueString)); } else if (QOrganizerItemDetail::TypeDescription == detailType && QOrganizerItemDescription::FieldDescription == detailField) { jsonDbQueryStr->append(matchFlagQueryTemplate .arg(QOrganizerJsonDbStr::itemDescription()).arg(createMatchFlagQuery(valueString, matchFlags))); } else if (QOrganizerItemDetail::TypeDisplayLabel == detailType && QOrganizerItemDisplayLabel::FieldLabel == detailField) { jsonDbQueryStr->append(matchFlagQueryTemplate .arg(QOrganizerJsonDbStr::itemDisplayName()) .arg(createMatchFlagQuery(valueString, matchFlags))); } else if (QOrganizerItemDetail::TypeGuid == detailType && QOrganizerItemGuid::FieldGuid == detailField) { jsonDbQueryStr->append(matchFlagQueryTemplate .arg(QOrganizerJsonDbStr::itemGuid()) .arg(createMatchFlagQuery(valueString, matchFlags))); } else if (QOrganizerItemDetail::TypeLocation == detailType) { if (QOrganizerItemLocation::FieldLabel == detailField) { jsonDbQueryStr->append(matchFlagQueryTemplate2 .arg(QOrganizerJsonDbStr::eventLocation()) .arg(QOrganizerJsonDbStr::eventLocationDisplayName()) .arg(createMatchFlagQuery(valueString, matchFlags))); } else if (QOrganizerItemLocation::FieldLongitude == detailField) { jsonDbQueryStr->append(equalsQueryTemplate3 .arg(QOrganizerJsonDbStr::eventLocation()) .arg(QOrganizerJsonDbStr::eventLocationGeo()) .arg(QOrganizerJsonDbStr::eventLocationGeoLongitude()) .arg(valueString)); } else if (QOrganizerItemLocation::FieldLatitude == detailField) { jsonDbQueryStr->append(equalsQueryTemplate3 .arg(QOrganizerJsonDbStr::eventLocation()) .arg(QOrganizerJsonDbStr::eventLocationGeo()) .arg(QOrganizerJsonDbStr::eventLocationGeoLatitude()) .arg(valueString)); } } else if (QOrganizerItemDetail::TypePriority == detailType && QOrganizerItemPriority::FieldPriority == detailField) { jsonDbQueryStr->append(equalsQueryTemplate .arg(QOrganizerJsonDbStr::itemPriority()) .arg(enumToString(organizerPriorityEnumMap(), fieldValue.toInt()))); } else if (QOrganizerItemDetail::TypeItemType == detailType && QOrganizerItemType::FieldType == detailField) { jsonDbQueryStr->append(equalsQueryTemplate .arg(QOrganizerJsonDbStr::jsonDbType()) .arg(QOrganizerJsonDbStr::jsonDbSchemaPrefix() + enumToString(organizerItemTypeMap(), fieldValue.toInt()))); *typeFilterFlag = 0x01 << (fieldValue.toInt() - QOrganizerItemType::TypeUndefined); } else if (QOrganizerItemDetail::TypeTag == detailType && QOrganizerItemTag::FieldTag == detailField) { jsonDbQueryStr->append(containsQueryTemplate.arg(QOrganizerJsonDbStr::itemTags()).arg(valueString)); } else if (QOrganizerItemDetail::TypeParent == detailType) { if (QOrganizerItemParent::FieldParentId == detailField) { const QOrganizerItemEngineId *itemIdPtr = QOrganizerManagerEngine::engineItemId(fieldValue.value()); if (itemIdPtr) { jsonDbQueryStr->append(equalsQueryTemplate .arg(QOrganizerJsonDbStr::itemOccurrenceParent()) .arg(itemIdPtr->toString())); } } else if (QOrganizerItemParent::FieldOriginalDate == detailField) { jsonDbQueryStr->append(equalsQueryTemplate .arg(QOrganizerJsonDbStr::itemOccurrenceOriginalDate()).arg(fieldValue.toDate().toString(Qt::ISODate))); } } else if (QOrganizerItemDetail::TypeExtendedDetail == detailType && QOrganizerItemExtendedDetail::FieldName == detailField) { jsonDbQueryStr->append(existsQueryTemplate.arg(valueString)); } else if (QOrganizerItemDetail::TypeEventRsvp == detailType) { if (QOrganizerEventRsvp::FieldParticipationStatus == detailField) { jsonDbQueryStr->append(matchFlagQueryTemplate2 .arg(QOrganizerJsonDbStr::eventRsvp()) .arg(QOrganizerJsonDbStr::eventRsvpParticipationStatus()) .arg(createMatchFlagQuery(enumToString(organizerParticipationStatusMap(), fieldValue.toInt()), matchFlags))); } else if (QOrganizerEventRsvp::FieldParticipationRole == detailField) { jsonDbQueryStr->append(matchFlagQueryTemplate2 .arg(QOrganizerJsonDbStr::eventRsvp()) .arg(QOrganizerJsonDbStr::eventRsvpParticipationRole()) .arg(createMatchFlagQuery(enumToString(organizerParticipationRoleMap(), fieldValue.toInt()), matchFlags))); } else if (QOrganizerEventRsvp::FieldResponseRequirement == detailField) { jsonDbQueryStr->append(matchFlagQueryTemplate2 .arg(QOrganizerJsonDbStr::eventRsvp()) .arg(QOrganizerJsonDbStr::eventRsvpResponseRequirement()) .arg(createMatchFlagQuery(enumToString(organizerResponseRequirementMap(), fieldValue.toInt()), matchFlags))); } else if (QOrganizerEventRsvp::FieldResponseDeadline == detailField) { jsonDbQueryStr->append(matchFlagQueryTemplate2 .arg(QOrganizerJsonDbStr::eventRsvp()) .arg(QOrganizerJsonDbStr::eventRsvpResponseDeadline()) .arg(createMatchFlagQuery(fieldValue.toDate().toString(Qt::ISODate), matchFlags))); } else if (QOrganizerEventRsvp::FieldResponseDate == detailField) { jsonDbQueryStr->append(matchFlagQueryTemplate2 .arg(QOrganizerJsonDbStr::eventRsvp()) .arg(QOrganizerJsonDbStr::eventRsvpResponseDate()) .arg(createMatchFlagQuery(fieldValue.toDate().toString(Qt::ISODate), matchFlags))); } else if (QOrganizerEventRsvp::FieldOrganizerName == detailField) { jsonDbQueryStr->append(matchFlagQueryTemplate2 .arg(QOrganizerJsonDbStr::eventRsvp()) .arg(QOrganizerJsonDbStr::eventRsvpOrganizerName()) .arg(createMatchFlagQuery(valueString, matchFlags))); } else if (QOrganizerEventRsvp::FieldOrganizerEmail == detailField) { jsonDbQueryStr->append(matchFlagQueryTemplate2 .arg(QOrganizerJsonDbStr::eventRsvp()) .arg(QOrganizerJsonDbStr::eventRsvpOrganizerEmail()) .arg(createMatchFlagQuery(valueString, matchFlags))); } } } QString QOrganizerJsonDbConverter::createMatchFlagQuery(const QString &value, QOrganizerItemFilter::MatchFlags flags) const { // with current enumeration, 3 means both MatchContains and MatchEndsWith (QTBUG-20035) const uint realMatchType = flags & 0x0F; QString queryWithWildCards; if (QOrganizerItemFilter::MatchExactly == flags) queryWithWildCards = QStringLiteral("=\""); else queryWithWildCards = QStringLiteral("=~\"/"); if (QOrganizerItemFilter::MatchContains == realMatchType || QOrganizerItemFilter::MatchEndsWith == realMatchType) queryWithWildCards += QStringLiteral("*"); queryWithWildCards += value; if (QOrganizerItemFilter::MatchContains == realMatchType || QOrganizerItemFilter::MatchStartsWith == realMatchType) queryWithWildCards += QStringLiteral("*"); if (QOrganizerItemFilter::MatchExactly != flags && !(QOrganizerItemFilter::MatchFixedString & flags)) { queryWithWildCards += QStringLiteral("/w"); if (!(QOrganizerItemFilter::MatchCaseSensitive & flags)) queryWithWildCards += QStringLiteral("i"); } return queryWithWildCards; } int QOrganizerJsonDbConverter::supportedItemTypeFlag() const { // int typeFilterFlag = 0x01 << QOrganizerItemType::TypeUndefined - QOrganizerItemType::TypeUndefined // + 0x01 << (QOrganizerItemType::TypeEvent - QOrganizerItemType::TypeUndefined) // + 0x01 << (QOrganizerItemType::TypeEventOccurrence - QOrganizerItemType::TypeUndefined); // + 0x01 << (QOrganizerItemType::TypeTodo - QOrganizerItemType::TypeUndefined); // + 0x01 << (QOrganizerItemType::TypeTodoOccurrence - QOrganizerItemType::TypeUndefined); return 0xf1;//typeFilterFlag; } bool QOrganizerJsonDbConverter::itemTypeFlagToJsonDbEventQuery(const int &flag, QString *jsonDbQueryStr) { if (!flag) return false; if (supportedItemTypeFlag() == flag) *jsonDbQueryStr = QOrganizerJsonDbStr::jsonDbQueryAllEventItems() + *jsonDbQueryStr; else if (!((flag >> (QOrganizerItemType::TypeEvent - QOrganizerItemType::TypeUndefined)) & 0x01) && !((flag >> (QOrganizerItemType::TypeEventOccurrence - QOrganizerItemType::TypeUndefined)) & 0x01)) { return false; } return true; } bool QOrganizerJsonDbConverter::itemTypeFlagToJsonDbTodoQuery(const int &flag, QString *jsonDbQueryStr) { if (!flag) return false; if (supportedItemTypeFlag() == flag) *jsonDbQueryStr = QOrganizerJsonDbStr::jsonDbQueryAllTodoItems() + *jsonDbQueryStr; else if (!((flag >> (QOrganizerItemType::TypeTodo - QOrganizerItemType::TypeUndefined)) & 0x01) && !((flag >> (QOrganizerItemType::TypeTodoOccurrence - QOrganizerItemType::TypeUndefined)) & 0x01)) { return false; } return true; } bool QOrganizerJsonDbConverter::createJsonDbQuery(const QOrganizerItemFilter &filter, const QDateTime &startTime, const QDateTime &endTime, QList *jsonDbQueryList) { QString filterString; int typeFilterFlag = supportedItemTypeFlag(); if (compoundFilterToJsondbQuery(filter, &filterString, &typeFilterFlag)) { if (startTime.isValid() || endTime.isValid()) { QString eventJsonDbQuery; if (itemTypeFlagToJsonDbEventQuery(typeFilterFlag, &eventJsonDbQuery)) { eventJsonDbQuery += filterString; if (startTime.isValid()) eventJsonDbQuery += QOrganizerJsonDbStr::jsonDbQueryEventEndDateTimeTemplate().arg(startTime.toUTC().toString(Qt::ISODate)); if (endTime.isValid()) eventJsonDbQuery += QOrganizerJsonDbStr::jsonDbQueryEventStartDateTimeTemplate().arg(endTime.toUTC().toString(Qt::ISODate)); jsonDbQueryList->append(eventJsonDbQuery); } if (itemTypeFlagToJsonDbTodoQuery(typeFilterFlag, &filterString)) jsonDbQueryList->append(filterString); } else { //No time period terms and create one request for both todo and event if (supportedItemTypeFlag() == typeFilterFlag) filterString = QOrganizerJsonDbStr::jsonDbQueryAllItems() + filterString; jsonDbQueryList->append(filterString); } return true; } return false; } QT_END_NAMESPACE_ORGANIZER src/plugins/organizer/jsondb/qorganizerjsondbconverter.h000066400000000000000000000220221233466112000242630ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERJSONDBCONVERTER_H #define QORGANIZERJSONDBCONVERTER_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include #include #include #include "qorganizerjsondbengine.h" QT_USE_NAMESPACE_JSONDB QT_BEGIN_NAMESPACE_ORGANIZER struct QOrganizerJsonDbEnumConversionData { int enumValue; const QString enumStr; }; class QOrganizerJsonDbConverter { public: QOrganizerJsonDbConverter(); QOrganizerManager::Error jsonDbConnectionErrorToOrganizerError(QJsonDbConnection::ErrorCode error) const; QOrganizerManager::Error jsonDbRequestErrorToOrganizerError(QJsonDbRequest::ErrorCode error) const; bool jsonDbObjectToItem(const QJsonObject &object, QOrganizerItem *item, QOrganizerJsonDbEngine::StorageLocation storageLocation) const; bool itemToJsonDbObject(const QOrganizerItem &item, QJsonObject *object) const; bool jsonDbObjectToCollection(const QJsonObject &object, QOrganizerCollection *collection, bool *isDefaultCollection, QOrganizerJsonDbEngine::StorageLocation storageLocation) const; bool collectionToJsonDbObject(const QOrganizerCollection &collection, bool isDefaultCollection, QJsonObject *object) const; // filter handling bool singleFilterToJsondbQuery(const QOrganizerItemFilter &filter, QString *jsonDbQueryStr, int *typeFilterFlag) const; bool compoundFilterToJsondbQuery(const QOrganizerItemFilter &filter, QString *jsonDbQueryStr, int *typeFilterFlag) const; // notification handling QString jsonDbNotificationObjectToOrganizerType(const QJsonObject &object) const; QOrganizerItemId jsonDbNotificationObjectToItemId(const QJsonObject &object, QOrganizerJsonDbEngine::StorageLocation storageLocation) const; QOrganizerCollectionId jsonDbNotificationObjectToCollectionId(const QJsonObject &object, QOrganizerJsonDbEngine::StorageLocation storageLocation) const; void jsonDbVersionToItemVersion(const QString &jsonDbVersion, QOrganizerItemVersion *itemVersion) const; // storage location handling const QStringList storageLocationsFlagToStrings(const QOrganizerJsonDbEngine::StorageLocations storageLocationsFlag); QOrganizerJsonDbEngine::StorageLocation storageLocationStringToEnum(const QString &storageLocation); QOrganizerJsonDbEngine::StorageLocations storageLocationListToFlag(const QList storageLocationsList); int supportedItemTypeFlag() const; bool itemTypeFlagToJsonDbEventQuery(const int &flag, QString *jsonDbQueryStr); bool itemTypeFlagToJsonDbTodoQuery(const int &flag, QString *jsonDbQueryStr); bool createJsonDbQuery(const QOrganizerItemFilter &filter, const QDateTime &startTime, const QDateTime &endTime, QList *jsonDbQueryList); private: void itemVersionToJsonDbVersion(const QOrganizerItemVersion &itemVersion, QString *jsonDbVersion) const; void jsonDbObjectToRecurrenceRule(const QJsonObject &object, QOrganizerRecurrenceRule *rule) const; void recurrenceRuleToJsonDbObject(const QOrganizerRecurrenceRule &rule, QJsonObject *object) const; void audibleReminderDetailToJsonDbObject(const QOrganizerItemAudibleReminder &itemReminder, QJsonObject *object) const; void jsonDbObjectToAudibleReminderDetail(const QJsonObject &object, QOrganizerItemAudibleReminder *itemReminder, QOrganizerItemExtendedDetail *extendedDetail) const; int stringToEnum(const QOrganizerJsonDbEnumConversionData *const conversionData, const QString &enumStr) const; QString enumToString(const QOrganizerJsonDbEnumConversionData *const conversionData, int enumValue) const; void attendeeDetailToJsonDbObject(const QOrganizerEventAttendee &attendeeDetail, QJsonObject *object) const; void jsonDbObjectToAttendeeDetail(const QJsonObject &object, QOrganizerEventAttendee *attendeeDetail) const; void rsvpDetailToJsonDbObject(const QOrganizerEventRsvp &rsvpDetail, QJsonObject *object) const; void jsonDbObjectToRsvpDetail(const QJsonObject &object, QOrganizerEventRsvp *rsvpDetail, QOrganizerItemExtendedDetail *extendedDetail) const; void locationDetailToJsonDbObject(const QOrganizerItemLocation &locationDetail, QJsonObject *object) const; void jsonDbObjectToLocationDetail(const QJsonObject &object, QOrganizerItemLocation *locationDetail, QOrganizerItemExtendedDetail *extendedDetail) const; // separate filter type specific handling bool collectionFilterToJsondbQuery(const QOrganizerItemFilter &filter, QString *jsonDbQueryStr) const; bool idFilterToJsondbQuery(const QOrganizerItemFilter &filter, QString *jsonDbQueryStr) const; bool detailFilterToJsondbQuery(const QOrganizerItemFilter &filter, QString *jsonDbQueryStr, int *typeFilterFlag) const; bool detailFieldFilterToJsondbQuery(const QOrganizerItemFilter &filter, QString *jsonDbQueryStr, int *typeFilterFlag) const; bool isSupportedDetailFieldFilter(const QVariant &fieldValue, QOrganizerItemDetail::DetailType detailType, int detailFieldName, QOrganizerItemFilter::MatchFlags matchFlags) const; QString createMatchFlagQuery(const QString &value, QOrganizerItemFilter::MatchFlags flags) const; void addFieldToFilterQuery(QOrganizerItemDetail::DetailType detailType, int detailField, const QVariant &fieldValue, QString *jsonDbQueryStr, QOrganizerItemFilter::MatchFlags matchFlags, int *typeFilterFlag) const; static const int enumMapEnd; static const QMap filterablePropertyNames(); static const QOrganizerJsonDbEnumConversionData *organizerPriorityEnumMap(); static const QOrganizerJsonDbEnumConversionData *organizerFrequencyEnumMap(); static const QOrganizerJsonDbEnumConversionData *organizerDayEnumMap(); static const QOrganizerJsonDbEnumConversionData *organizerMonthEnumMap(); static const QOrganizerJsonDbEnumConversionData *organizerParticipationStatusMap(); static const QOrganizerJsonDbEnumConversionData *organizerParticipationRoleMap(); static const QOrganizerJsonDbEnumConversionData *organizerResponseRequirementMap(); static const QOrganizerJsonDbEnumConversionData *organizerItemTypeMap(); static const QOrganizerJsonDbEnumConversionData *organizerTodoStatusMap(); static const QOrganizerJsonDbEnumConversionData *organizerStorageLocationMap(); }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERJSONDBCONVERTER_H src/plugins/organizer/jsondb/qorganizerjsondbdatastorage.cpp000066400000000000000000001154521233466112000251170ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizerjsondbdatastorage.h" #include #include #include #include #include "qorganizerjsondbid.h" #include "qorganizerjsondbstring.h" QT_BEGIN_NAMESPACE_ORGANIZER QOrganizerJsonDbDataStorage::QOrganizerJsonDbDataStorage() : m_waitMutex(0) , m_jsonDbConnection(0) , m_mandatoryStorageLocationMissing(false) { m_availableStorageLocations<* items, QMap* errorMap, QOrganizerManager::Error* error, QOrganizerJsonDbEngine::StorageLocation storageLocation) { if (items->isEmpty()) return; initRequestData(SaveItems, errorMap, error); m_saveToStorageLocation = storageLocation; m_resultItems = items; processRequest(); clearRequestData(); } QList QOrganizerJsonDbDataStorage::items(const QDateTime &startDate, const QDateTime &endDate, const QOrganizerItemFilter &filter, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error, QOrganizerJsonDbEngine::StorageLocations storageLocations, FetchType type, const QOrganizerItemId &parentId) { Q_UNUSED(sortOrders); Q_UNUSED(fetchHint); initRequestData(Items, 0, error); m_start = startDate; m_end = endDate; m_filter = filter; if (type == FetchItemOccurrences) m_parentItemId = parentId; m_fetchType = type; m_fetchFromStorageLocations = storageLocations; processRequest(); QList fetchedItems = m_items; clearRequestData(); return fetchedItems; } QList QOrganizerJsonDbDataStorage::itemsById(const QList& itemIds, QMap* errorMap, QOrganizerManager::Error *error, QOrganizerJsonDbEngine::StorageLocations storageLocations) { if (itemIds.isEmpty()) return QList(); initRequestData(ItemsById, errorMap, error); m_itemIds = itemIds; m_fetchFromStorageLocations = storageLocations; processRequest(); QList fetchedItems = m_items; clearRequestData(); return fetchedItems; } void QOrganizerJsonDbDataStorage::removeItems(const QList& itemIds, QMap* errorMap, QOrganizerManager::Error* error) { if (itemIds.isEmpty()) return; initRequestData(RemoveItems, errorMap, error); m_itemIds = itemIds; processRequest(); clearRequestData(); } void QOrganizerJsonDbDataStorage::saveCollections(QMap* collections, QMap* errorMap, QOrganizerManager::Error* error, QOrganizerJsonDbEngine::StorageLocation storageLocation) { if (collections->isEmpty()) return; initRequestData(SaveCollections, errorMap, error); m_resultCollections = collections; m_isDefaultCollection = false; m_saveToStorageLocation = storageLocation; processRequest(); clearRequestData(); } QList QOrganizerJsonDbDataStorage::collections(QOrganizerManager::Error *error, QOrganizerJsonDbEngine::StorageLocations storageLocations) { initRequestData(Collections, 0, error); m_fetchFromStorageLocations = storageLocations; processRequest(); QList fetchedCollections; if (*error == QOrganizerManager::NoError) fetchedCollections = m_collections; clearRequestData(); return fetchedCollections; } void QOrganizerJsonDbDataStorage::removeCollections(const QMap& collectionIds, QMap* errorMap, QOrganizerManager::Error* error) { if (collectionIds.isEmpty()) return; initRequestData(RemoveCollections, errorMap, error); m_removeCollectionIds = collectionIds; processRequest(); clearRequestData(); } void QOrganizerJsonDbDataStorage::createDefaultCollection(QOrganizerCollection* defaultCollection, QOrganizerManager::Error* error) { QMap errorMap; initRequestData(SaveCollections, &errorMap, error); QMap collections; collections.insert(0, *defaultCollection); m_resultCollections = &collections; m_isDefaultCollection = true; m_saveToStorageLocation = QOrganizerJsonDbEngine::UserDataStorage; processRequest(); *defaultCollection = m_resultCollections->value(0); m_defaultCollection = *defaultCollection; clearRequestData(); } QOrganizerCollection QOrganizerJsonDbDataStorage::defaultCollection() { // Fetching collections is the first operation done with the backend // and the availability of storage locations is also checked. if (m_defaultCollection.id().isNull()) { // m_defaultCollection is updated every time collections are fetched QOrganizerManager::Error error; collections(&error, m_converter.storageLocationListToFlag(m_availableStorageLocations)); } return m_defaultCollection; } QSet QOrganizerJsonDbDataStorage::collectionIds() { if (m_collectionIds.isEmpty()) { // m_collectionIds is updated every time collections are fetched QOrganizerManager::Error error; collections(&error, m_converter.storageLocationListToFlag(m_availableStorageLocations)); } return m_collectionIds; } QOrganizerJsonDbEngine::StorageLocations QOrganizerJsonDbDataStorage::availableStorageLocationsFlag() const { return m_availableStorageLocationsFlag; } void QOrganizerJsonDbDataStorage::run() { m_waitMutex = new QMutex(); m_jsonDbConnection = new QJsonDbConnection(this); connect(m_jsonDbConnection, SIGNAL(error(QtJsonDb::QJsonDbConnection::ErrorCode,QString)), this, SLOT(onJsonDbConnectionError(QtJsonDb::QJsonDbConnection::ErrorCode,QString))); m_jsonDbConnection->connectToServer(); registerForNotifications(); connect(this, SIGNAL(requestInitialized()), this, SLOT(handleRequest())); emit threadInitialized(); QThread::run(); } //////////////////////////////////////////////////////////////////////////////// // These functions are run in the context of QOrganizerJsonDbDataStorage thread //////////////////////////////////////////////////////////////////////////////// void QOrganizerJsonDbDataStorage::registerForNotifications() { const QStringList storageLocations = m_converter.storageLocationsFlagToStrings( QOrganizerJsonDbEngine::UserDataStorage | QOrganizerJsonDbEngine::SystemStorage); const int count = storageLocations.count(); QJsonDbWatcher *watcher; for (int i=0;isetWatchedActions(QJsonDbWatcher::All); watcher->setQuery(QOrganizerJsonDbStr::jsonDbNotificationQuery()); watcher->setPartition(storageLocations.at(i)); connect(watcher, SIGNAL(error(QtJsonDb::QJsonDbWatcher::ErrorCode,QString)), this, SLOT(onJsonDbWatcherError(QtJsonDb::QJsonDbWatcher::ErrorCode,QString))); connect(watcher, SIGNAL(notificationsAvailable(int)), this, SLOT(onJsonDbWatcherNotificationsAvailable())); m_jsonDbConnection->addWatcher(watcher); m_watchers.append(watcher); } } void QOrganizerJsonDbDataStorage::onJsonDbConnectionError(QtJsonDb::QJsonDbConnection::ErrorCode error, const QString &message) { Q_UNUSED(error) Q_UNUSED(message) // currently there's no real errors from JsonDb Q_ASSERT(m_converter.jsonDbConnectionErrorToOrganizerError(error) == QOrganizerManager::NoError); } void QOrganizerJsonDbDataStorage::onJsonDbRequestError(QtJsonDb::QJsonDbRequest::ErrorCode error, const QString &message) { Q_UNUSED(message) // if the error() signal is emitted, the finished() signal won't be emitted, so need to call handleResponse() QJsonDbRequest *request = qobject_cast(sender()); if (request) { QOrganizerManager::Error organizerError = m_converter.jsonDbRequestErrorToOrganizerError(error); // check first the storage location if (QOrganizerManager::UnspecifiedError == organizerError && !m_mandatoryStorageLocationMissing) { const QOrganizerJsonDbEngine::StorageLocation requestStorageLocation = m_converter.storageLocationStringToEnum(request->partition()); m_availableStorageLocations.removeOne(requestStorageLocation); m_availableStorageLocationsFlag = m_converter.storageLocationListToFlag(m_availableStorageLocations); if (QOrganizerJsonDbEngine::UserDataStorage == requestStorageLocation) { qCritical("Organizer - JsonDb backend does not work without UserDataStorage!"); m_mandatoryStorageLocationMissing = true; organizerError = QOrganizerManager::UnspecifiedError; } } handleResponse(organizerError, request); } } void QOrganizerJsonDbDataStorage::onJsonDbRequestFinished() { QJsonDbRequest *request = qobject_cast(sender()); if (request) handleResponse(QOrganizerManager::NoError, request); } void QOrganizerJsonDbDataStorage::onJsonDbWatcherError(QtJsonDb::QJsonDbWatcher::ErrorCode error, const QString &message) { if (QJsonDbWatcher::NoError != error) { QJsonDbWatcher *watcher = qobject_cast(sender()); if (watcher) { qCritical("Organizer - Not able to register for jsondb-notifications on '%s'!", qPrintable(watcher->partition())); qCritical("Organizer - Error: '%s'.", qPrintable(message)); m_watchers.removeOne(watcher); watcher->deleteLater(); } } } void QOrganizerJsonDbDataStorage::onJsonDbWatcherNotificationsAvailable() { // no mutex is needed since this slot doesn't touch any member variables QJsonDbWatcher *watcher = qobject_cast(sender()); if (watcher) { const QList ¬ifications = watcher->takeNotifications(); for (int i = 0; i < notifications.size(); ++i) { QJsonObject jsonDbObject = notifications.at(i).object(); QString jsonDbType = m_converter.jsonDbNotificationObjectToOrganizerType(jsonDbObject); const QOrganizerJsonDbEngine::StorageLocation storageLocation = m_converter.storageLocationStringToEnum(watcher->partition()); if (jsonDbType == QOrganizerJsonDbStr::jsonDbEventType() || jsonDbType == QOrganizerJsonDbStr::jsonDbEventViewType() || jsonDbType == QOrganizerJsonDbStr::jsonDbEventOccurrenceType() || jsonDbType == QOrganizerJsonDbStr::jsonDbTodoType() || jsonDbType == QOrganizerJsonDbStr::jsonDbTodoOccurrenceType()) { switch (notifications.at(i).action()) { case QJsonDbWatcher::Created: emit itemAdded(m_converter.jsonDbNotificationObjectToItemId(jsonDbObject, storageLocation)); break; case QJsonDbWatcher::Updated: emit itemChanged(m_converter.jsonDbNotificationObjectToItemId(jsonDbObject, storageLocation)); break; case QJsonDbWatcher::Removed: emit itemRemoved(m_converter.jsonDbNotificationObjectToItemId(jsonDbObject, storageLocation)); break; default: break; } } else if (jsonDbType == QOrganizerJsonDbStr::jsonDbCollectionType()) { switch (notifications.at(i).action()) { case QJsonDbWatcher::Created: { QOrganizerCollectionId newlyAddedId = m_converter.jsonDbNotificationObjectToCollectionId(jsonDbObject, storageLocation); m_collectionIds.insert(newlyAddedId); emit collectionAdded(newlyAddedId); break; } case QJsonDbWatcher::Updated: { emit collectionChanged(m_converter.jsonDbNotificationObjectToCollectionId(jsonDbObject, storageLocation)); break; } case QJsonDbWatcher::Removed: { QOrganizerCollectionId removedId = m_converter.jsonDbNotificationObjectToCollectionId(jsonDbObject, storageLocation); m_collectionIds.remove(removedId); emit collectionRemoved(removedId); break; } default: break; } } } } } void QOrganizerJsonDbDataStorage::handleRequest() { switch (m_requestType) { case SaveItems: handleSaveItemsRequest(); break; case Items: handleItemsRequest(); break; case ItemsById: handleItemsByIdRequest(); break; case RemoveItems: handleRemoveItemsRequest(); break; case SaveCollections: handleSaveCollectionsRequest(); break; case Collections: handleCollectionsRequest(); break; case RemoveCollections: handleRemoveCollectionsRequest(); break; default: break; } } void QOrganizerJsonDbDataStorage::handleResponse(QOrganizerManager::Error error, QJsonDbRequest *request) { int index = -1; QMap::iterator i = m_requestIndexMap.find(request); if (i != m_requestIndexMap.end()) { index = i.value(); m_requestIndexMap.erase(i); } else { // It might fall here if handling of some request was stopped due to timeout, // then remaining responses may come when no request is processed and m_requestIndexMap // has been cleared. In such cases, do nothing. return; } switch (m_requestType) { case SaveItems: handleSaveItemsResponse(index, error, request); break; case Items: handleItemsResponse(error, request); break; case ItemsById: handleItemsByIdResponse(error, request); break; case RemoveItems: handleRemoveItemsResponse(index, error); break; case SaveCollections: handleSaveCollectionsResponse(index, error, request); break; case Collections: handleCollectionsResponse(error, request); break; case RemoveCollections: handleRemoveCollectionsResponse(error); break; case Invalid: // no active request at the moment, internal variables have been cleared and some pointers have // NULL value, so response is just ignored default: break; } delete request; } void QOrganizerJsonDbDataStorage::handleSaveItemsRequest() { bool requestSent = false; QMap::const_iterator i = m_resultItems->begin(); while (i != m_resultItems->end()) { QOrganizerItemId itemId = i.value().id(); bool itemIsNew = itemId.isNull(); QJsonObject jsonDbItem; if (m_converter.itemToJsonDbObject(i.value(), &jsonDbItem)) { JsonDbRequestType requestType; QOrganizerJsonDbEngine::StorageLocation storageLocation(QOrganizerJsonDbEngine::UserDataStorage); if (itemIsNew) { requestType = JsonDbCreateRequest; // first save, storage location given in request. if not, by default store to UserData. storageLocation = m_saveToStorageLocation ? m_saveToStorageLocation : QOrganizerJsonDbEngine::UserDataStorage; } else { requestType = JsonDbUpdateRequest; const QOrganizerJsonDbItemId *itemIdPtr = static_cast(QOrganizerManagerEngine::engineItemId(itemId)); if (!itemIdPtr) { *m_error = QOrganizerManager::UnspecifiedError; m_errorMap->insert(i.key(), *m_error); } else { // item has already been saved before, use location from id storageLocation = itemIdPtr->storageLocation(); } } if (makeJsonDbRequest(requestType, i.key(), storageLocation, QString(), QList() << jsonDbItem)) requestSent = true; } else { *m_error = QOrganizerManager::InvalidItemTypeError; m_errorMap->insert(i.key(), *m_error); } ++i; } if (!requestSent) m_syncWaitCondition.wakeAll(); } void QOrganizerJsonDbDataStorage::handleSaveItemsResponse(int index, QOrganizerManager::Error error, QJsonDbRequest *request) { if (error == QOrganizerManager::NoError) { QList results = request->takeResults(); if (results.size() > 0) { QOrganizerItem item = m_resultItems->value(index); QJsonObject jsonDbItem = results.at(0); bool gotUuid = !item.id().isNull(); bool gotVersion = false; QJsonObject::const_iterator i = jsonDbItem.constBegin(); while (i != jsonDbItem.constEnd()) { if (!gotUuid && i.key() == QOrganizerJsonDbStr::jsonDbUuid()) { // it's a new item, and we need to set the item ID gotUuid = true; QString jsonDbUuid = i.value().toString(); if (!jsonDbUuid.isEmpty()) { QOrganizerJsonDbItemId* itemId = new QOrganizerJsonDbItemId; itemId->setJsonDbUuid(jsonDbUuid); itemId->setStorageLocation(m_converter.storageLocationStringToEnum(request->partition())); item.setId(QOrganizerItemId(itemId)); } } else if (!gotVersion && i.key() == QOrganizerJsonDbStr::jsonDbVersion()) { gotVersion = true; QString jsonDbVersion = i.value().toString(); if (!jsonDbVersion.isEmpty()) { QOrganizerItemVersion itemVersion = item.detail(QOrganizerItemDetail::TypeVersion); m_converter.jsonDbVersionToItemVersion(jsonDbVersion, &itemVersion); item.saveDetail(&itemVersion); } } if (gotUuid && gotVersion) break; ++i; } m_resultItems->insert(index, item); } } else { *m_error = error; m_errorMap->insert(index, *m_error); } if (m_requestIndexMap.isEmpty()) { // All transactions have been handled, request has been completed m_syncWaitCondition.wakeAll(); } } void QOrganizerJsonDbDataStorage::handleItemsRequest() { QString jsonDbQuery; switch (m_fetchType) { case FetchItems: // break; case FetchItemIds: { // TODO: it would be enough to get just a list of uuids from db // For now we fetch the whole item, even though more optimal would be to fetch // only uuid, startdate and enddate fields // for now, this is common for FetchItems and FetchItemIds // Apply Filter and get jsondb query expression QList jsonDbQueryList; if (m_converter.createJsonDbQuery(m_filter, m_start, m_end, &jsonDbQueryList)) { foreach (const QString &jsonDbQuery, jsonDbQueryList) { foreach (QOrganizerJsonDbEngine::StorageLocation location, m_availableStorageLocations) { if (m_fetchFromStorageLocations & location) makeJsonDbRequest(JsonDbReadRequest, 0, location, jsonDbQuery); } } } else { *m_error = QOrganizerManager::BadArgumentError; } break; } case FetchParents: { // This is used for fetching all parent items foreach (QOrganizerJsonDbEngine::StorageLocation location, m_availableStorageLocations) { if (m_fetchFromStorageLocations & location) { if (makeJsonDbRequest(JsonDbReadRequest, 0, location, QOrganizerJsonDbStr::jsonDbQueryParentItems())) { if (location == QOrganizerJsonDbEngine::SystemStorage) { // can't query normal object and view object at the same time // TODO only query view objects for when needed makeJsonDbRequest(JsonDbReadRequest, 0, location, QOrganizerJsonDbStr::jsonDbQueryEventViewParentItems()); } } } } break; } case FetchItemOccurrences: { const QOrganizerItemEngineId *itemIdPtr = QOrganizerManagerEngine::engineItemId(m_parentItemId); if (itemIdPtr) { jsonDbQuery = QOrganizerJsonDbStr::jsonDbQueryOccurrenceItemsByParent() .arg(itemIdPtr->toString()); foreach (QOrganizerJsonDbEngine::StorageLocation location, m_availableStorageLocations) { if (m_fetchFromStorageLocations & location) makeJsonDbRequest(JsonDbReadRequest, 0, location, jsonDbQuery); } } break; } default: break; } if (m_requestIndexMap.isEmpty()) m_syncWaitCondition.wakeAll(); } void QOrganizerJsonDbDataStorage::handleItemsResponse(QOrganizerManager::Error error, QJsonDbRequest *request) { if (error == QOrganizerManager::NoError) { QList results = request->takeResults(); for (int i = 0; i < results.size(); ++i) { QOrganizerItem item; if (m_converter.jsonDbObjectToItem(results.at(i), &item, m_converter.storageLocationStringToEnum(request->partition()))) { if (m_start.isValid() || m_end.isValid()) { QOrganizerItemType::ItemType itemType = item.type(); if ((QOrganizerItemType::TypeTodo == itemType || QOrganizerItemType::TypeTodoOccurrence == itemType) && !QOrganizerManagerEngine::isItemBetweenDates(item, m_start, m_end)) { continue; } } // if m_fetchType is FetchParents, items are always added to m_items, because m_start and m_end // are default constructed QDateTimes m_items.append(item); } } } else { *m_error = error; } // when querying view objects, 2 queries are fired, so need to check if (m_requestIndexMap.isEmpty()) m_syncWaitCondition.wakeAll(); } void QOrganizerJsonDbDataStorage::handleItemsByIdRequest() { const QString uuidTemplate(QStringLiteral("\"%1\",")); QString itemQuery; for (int i = 0; i < m_itemIds.size(); ++i) itemQuery += uuidTemplate.arg(m_itemIds.at(i).isNull() ? QString() : QOrganizerJsonDbItemId(m_itemIds.at(i).toString()).jsondbUuid()); // remove the last "," itemQuery.truncate(itemQuery.length() - 1); QString newJsonDbQuery(QOrganizerJsonDbStr::jsonDbQueryAllItems()); newJsonDbQuery.append(QOrganizerJsonDbStr::jsonDbQueryUuidsTemplate().arg(itemQuery)); QString viewObjectJsonDbQuery(QOrganizerJsonDbStr::jsonDbQueryEventViews()); viewObjectJsonDbQuery.append(QOrganizerJsonDbStr::jsonDbQueryUuidsTemplate().arg(itemQuery)); foreach (QOrganizerJsonDbEngine::StorageLocation location, m_availableStorageLocations) { if (m_fetchFromStorageLocations & location) { makeJsonDbRequest(JsonDbReadRequest, 0, location, newJsonDbQuery); if (location == QOrganizerJsonDbEngine::SystemStorage) makeJsonDbRequest(JsonDbReadRequest, 0, location, viewObjectJsonDbQuery); } } if (m_requestIndexMap.isEmpty()) m_syncWaitCondition.wakeAll(); } void QOrganizerJsonDbDataStorage::handleItemsByIdResponse(QOrganizerManager::Error error, QJsonDbRequest *request) { if (error == QOrganizerManager::NoError) { QList results = request->takeResults(); for (int i = 0; i < results.size(); ++i) { QOrganizerItem *item = new QOrganizerItem; if (m_converter.jsonDbObjectToItem(results.at(i), item, m_converter.storageLocationStringToEnum(request->partition()))) { m_idItemMap.insert(item->id(), *item); } } } else { *m_error = error; } if (m_requestIndexMap.isEmpty()) { // All transactions have been handled, request has been completed for (int i = 0; i < m_itemIds.size(); ++i) { QOrganizerItem item = m_idItemMap.value(m_itemIds[i], QOrganizerItem()); m_items.append(item); if (item.id().isNull()) { *m_error = QOrganizerManager::DoesNotExistError; m_errorMap->insert(i, *m_error); } } m_syncWaitCondition.wakeAll(); } } void QOrganizerJsonDbDataStorage::handleRemoveItemsRequest() { bool requestSent = false; for (int i = 0; i < m_itemIds.size(); ++i) { QJsonObject jsonDbItem; if (m_itemIds.at(i).isNull()) { *m_error = QOrganizerManager::DoesNotExistError; m_errorMap->insert(i, QOrganizerManager::DoesNotExistError); continue; } const QOrganizerJsonDbItemId jsondbItemId(m_itemIds.at(i).toString()); jsonDbItem.insert(QOrganizerJsonDbStr::jsonDbUuid(), jsondbItemId.jsondbUuid()); if (makeJsonDbRequest(JsonDbRemoveRequest, i, jsondbItemId.storageLocation(), QString(), QList() << jsonDbItem)) requestSent = true; } if (!requestSent) m_syncWaitCondition.wakeAll(); } void QOrganizerJsonDbDataStorage::handleRemoveItemsResponse(int index, QOrganizerManager::Error error) { if (error != QOrganizerManager::NoError) { *m_error = error; m_errorMap->insert(index, *m_error); } if (m_requestIndexMap.isEmpty()) { // All transactions have been handled, request has been completed m_syncWaitCondition.wakeAll(); } } void QOrganizerJsonDbDataStorage::handleSaveCollectionsRequest() { bool requestSent = false; QMap::const_iterator i = m_resultCollections->constBegin(); while (i != m_resultCollections->constEnd()) { QOrganizerCollection collection = i.value(); bool collectionIsNew = collection.id().isNull(); // check whether this is a default collection // if we are creating a new default collection, then m_isDefaultCollection is true // if we are modifying an existing default collection, then collection id should be the same // as m_defaultCollection id. // ignore storageLocation on request, if collection is updated (it has been saved already atleast once) bool convertToDefaultCollection; QOrganizerJsonDbEngine::StorageLocation storageLocation(QOrganizerJsonDbEngine::UserDataStorage); if (collectionIsNew) { convertToDefaultCollection = m_isDefaultCollection; // first save, storage location given in request. if not, by default store to UserData. storageLocation = m_saveToStorageLocation ? m_saveToStorageLocation : QOrganizerJsonDbEngine::UserDataStorage; } else { const QOrganizerJsonDbCollectionId *collectionIdPtr = static_cast(QOrganizerManagerEngine::engineCollectionId(collection.id())); if (collectionIdPtr) { convertToDefaultCollection = (collection.id() == m_defaultCollection.id()); // collection has already been saved before, so we get storage location from id storageLocation = collectionIdPtr->storageLocation(); } } QJsonObject jsonDbCollection; if (m_converter.collectionToJsonDbObject(collection, convertToDefaultCollection, &jsonDbCollection)) { JsonDbRequestType requestType; if (collectionIsNew) requestType = JsonDbCreateRequest; else requestType = JsonDbUpdateRequest; if (makeJsonDbRequest(requestType, i.key(), storageLocation, QString(), QList() << jsonDbCollection)) requestSent = true; } else { *m_error = QOrganizerManager::InvalidCollectionError; m_errorMap->insert(i.key(), *m_error); } ++i; } if (!requestSent) m_syncWaitCondition.wakeAll(); } void QOrganizerJsonDbDataStorage::handleSaveCollectionsResponse(int index, QOrganizerManager::Error error, QJsonDbRequest *request) { if (error == QOrganizerManager::NoError) { QOrganizerCollection collection = m_resultCollections->value(index); if (collection.id().isNull()) { QList results = request->takeResults(); if (results.size() > 0) { QString jsonUuid = results.at(0).value(QOrganizerJsonDbStr::jsonDbUuid()).toString(); if (!jsonUuid.isEmpty()) { QOrganizerJsonDbCollectionId *collectionId = new QOrganizerJsonDbCollectionId; collectionId->setJsonDbUuid(jsonUuid); collectionId->setStorageLocation(m_converter.storageLocationStringToEnum(request->partition())); collection.setId(QOrganizerCollectionId(collectionId)); m_collectionIds.insert(collection.id()); // have we modified default collection if (collection.id() == m_defaultCollection.id()) m_defaultCollection = collection; } } m_resultCollections->insert(index, collection); } } else { *m_error = error; m_errorMap->insert(index, *m_error); } if (m_requestIndexMap.isEmpty()) { // All transactions have been handled, request has been completed m_syncWaitCondition.wakeAll(); } } void QOrganizerJsonDbDataStorage::handleCollectionsRequest() { foreach (QOrganizerJsonDbEngine::StorageLocation location, m_availableStorageLocations) { if (m_fetchFromStorageLocations & location) makeJsonDbRequest(JsonDbReadRequest, 0, location, QOrganizerJsonDbStr::jsonDbQueryAllCollections()); } if (m_requestIndexMap.isEmpty()) m_syncWaitCondition.wakeAll(); } void QOrganizerJsonDbDataStorage::handleCollectionsResponse(QOrganizerManager::Error error, QJsonDbRequest *request) { if (error == QOrganizerManager::NoError) { QList results = request->takeResults(); for (int i = 0; i < results.size(); ++i) { QOrganizerCollection collection; bool isDefaultCollection(false); if (m_converter.jsonDbObjectToCollection(results.at(i), &collection, &isDefaultCollection, m_converter.storageLocationStringToEnum(request->partition()))) { if (isDefaultCollection) m_defaultCollection = collection; m_collections.append(collection); m_collectionIds.insert(collection.id()); } } } else { *m_error = error; } if (m_requestIndexMap.isEmpty()) { // All transactions have been handled, request has been completed m_syncWaitCondition.wakeAll(); } } void QOrganizerJsonDbDataStorage::handleRemoveCollectionsRequest() { QMap::const_iterator i = m_removeCollectionIds.constBegin(); while (i != m_removeCollectionIds.constEnd()) { QJsonObject jsonDbObject; const QOrganizerCollectionEngineId *collectionIdPtr = QOrganizerManagerEngine::engineCollectionId(i.value()); if (collectionIdPtr) { QOrganizerJsonDbCollectionId jsondbCollectionId(collectionIdPtr->toString()); jsonDbObject.insert(QOrganizerJsonDbStr::jsonDbUuid(), jsondbCollectionId.jsondbUuid()); makeJsonDbRequest(JsonDbRemoveRequest, 0, jsondbCollectionId.storageLocation(), QString(), QList() << jsonDbObject); } ++i; } if (m_requestIndexMap.isEmpty()) m_syncWaitCondition.wakeAll(); } void QOrganizerJsonDbDataStorage::handleRemoveCollectionsResponse(QOrganizerManager::Error error) { if (error == QOrganizerManager::NoError) { QMap::const_iterator i = m_removeCollectionIds.constBegin(); while (i != m_removeCollectionIds.constEnd()) { m_collectionIds.remove(i.value()); ++i; } } else { *m_error = error; QMap::const_iterator i = m_removeCollectionIds.constBegin(); while (i != m_removeCollectionIds.constEnd()) { m_errorMap->insert(i.key(), error); ++i; } } m_syncWaitCondition.wakeAll(); } bool QOrganizerJsonDbDataStorage::makeJsonDbRequest( JsonDbRequestType jsonDbRequestType, int index, QOrganizerJsonDbEngine::StorageLocation storageLocation, const QString &query, const QList &objects) { QJsonDbRequest *request = 0; switch (jsonDbRequestType) { case JsonDbReadRequest: request = new QJsonDbReadRequest(query, this); break; case JsonDbCreateRequest: request = new QJsonDbCreateRequest(objects, this); break; case JsonDbUpdateRequest: request = new QJsonDbUpdateRequest(objects, this); break; case JsonDbRemoveRequest: request = new QJsonDbRemoveRequest(objects, this); break; default: return false; } QJsonDbWriteRequest *writeRequest = qobject_cast(request); if (writeRequest) writeRequest->setConflictResolutionMode(QJsonDbWriteRequest::Replace); const QStringList storageLocationStrings = m_converter.storageLocationsFlagToStrings(storageLocation); request->setPartition(storageLocationStrings.isEmpty() ? m_converter.storageLocationsFlagToStrings(QOrganizerJsonDbEngine::UserDataStorage).first() : storageLocationStrings.first()); connect(request, SIGNAL(error(QtJsonDb::QJsonDbRequest::ErrorCode,QString)), this, SLOT(onJsonDbRequestError(QtJsonDb::QJsonDbRequest::ErrorCode,QString))); connect(request, SIGNAL(finished()), this, SLOT(onJsonDbRequestFinished())); if (m_jsonDbConnection->send(request)) { m_requestIndexMap.insert(request, index); return true; } else { delete request; *m_error = QOrganizerManager::UnspecifiedError; if (m_errorMap) m_errorMap->insert(index, QOrganizerManager::UnspecifiedError); } return false; } void QOrganizerJsonDbDataStorage::processRequest() { // storage location related checks if (m_mandatoryStorageLocationMissing) { *m_error = QOrganizerManager::UnspecifiedError; return; } emit requestInitialized(); QMutexLocker locker(m_waitMutex); int msecs = 10000; // TODO: handle timeout nicely bool requestFinished = m_syncWaitCondition.wait(m_waitMutex, msecs); if (!requestFinished) { qWarning() << "Timeout, no response received!!!"; // timeout TODO: check which items have completed and update error map accordingly... } } void QOrganizerJsonDbDataStorage::initRequestData(RequestType requestType, QMap* errorMap, QOrganizerManager::Error* error) { m_requestType = requestType; m_errorMap = errorMap; m_error = error; m_requestIndexMap.clear(); m_resultItems = 0; m_resultCollections = 0; m_idItemMap.clear(); m_items.clear(); QOrganizerItemId tmpId; m_parentItemId = tmpId; m_fetchType = NoFetch; m_itemIds.clear(); m_collections.clear(); m_removeItemCollectionIds.clear(); m_removeItemParentIds.clear(); m_removeCollectionIds.clear(); m_isDefaultCollection = false; m_saveToStorageLocation = QOrganizerJsonDbEngine::UserDataStorage; m_fetchFromStorageLocations = 0; } void QOrganizerJsonDbDataStorage::clearRequestData() { m_requestType = Invalid; m_errorMap = 0; m_error = 0; m_requestIndexMap.clear(); m_resultItems = 0; m_resultCollections = 0; m_idItemMap.clear(); m_items.clear(); QOrganizerItemId tmpId; m_parentItemId = tmpId; m_fetchType = NoFetch; m_itemIds.clear(); m_collections.clear(); m_removeItemCollectionIds.clear(); m_removeItemParentIds.clear(); m_removeCollectionIds.clear(); m_isDefaultCollection = false; m_saveToStorageLocation = QOrganizerJsonDbEngine::UserDataStorage; m_fetchFromStorageLocations = 0; } #include "moc_qorganizerjsondbdatastorage.cpp" QT_END_NAMESPACE_ORGANIZER src/plugins/organizer/jsondb/qorganizerjsondbdatastorage.h000066400000000000000000000240421233466112000245560ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERJSONDBDATASTORAGE_H #define QORGANIZERJSONDBDATASTORAGE_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include "qorganizerjsondbconverter.h" QT_USE_NAMESPACE_JSONDB QT_BEGIN_NAMESPACE_ORGANIZER // The purpose of this class is to provide synchronous access to jsondb and also hide jsondb // specific details such as filter creation and object conversions. // All public methods of this class are called from the thread of the caller. The functions store // their parameters to member variables, emit request signal and start to wait until the request has // been completed. The request signal is handled in the separate thread. Handler functions typically // start jsondb requests. When responses are received from jsondb, results are stored to member variables // and waiting caller thread is woken up. class QOrganizerJsonDbDataStorage: public QThread { Q_OBJECT public: enum FetchType { NoFetch, FetchItems, FetchItemIds, FetchItemOccurrences, FetchParents }; QOrganizerJsonDbDataStorage(); ~QOrganizerJsonDbDataStorage(); void saveItems(QMap* items, QMap* errorMap, QOrganizerManager::Error* error, QOrganizerJsonDbEngine::StorageLocation storageLocation); QList items(const QDateTime& startDate, const QDateTime& endDate, const QOrganizerItemFilter& filter, const QList& sortOrders, const QOrganizerItemFetchHint& fetchHint, QOrganizerManager::Error* error, QOrganizerJsonDbEngine::StorageLocations storageLocations, FetchType type = FetchItems, const QOrganizerItemId &parentId = QOrganizerItemId()); QList itemsById(const QList& itemIds, QMap* errorMap, QOrganizerManager::Error* error, QOrganizerJsonDbEngine::StorageLocations storageLocations); void removeItems(const QList& itemIds, QMap* errorMap, QOrganizerManager::Error* error); void saveCollections(QMap* collections, QMap* errorMap, QOrganizerManager::Error* error, QOrganizerJsonDbEngine::StorageLocation storageLocation); QList collections(QOrganizerManager::Error* error, QOrganizerJsonDbEngine::StorageLocations storageLocations); void removeCollections(const QMap& collectionIds, QMap* errorMap, QOrganizerManager::Error* error); void createDefaultCollection(QOrganizerCollection* defaultCollection, QOrganizerManager::Error* error); QOrganizerCollection defaultCollection(); QSet collectionIds(); QOrganizerJsonDbEngine::StorageLocations availableStorageLocationsFlag() const; signals: void threadInitialized(); void requestInitialized(); void itemAdded(const QOrganizerItemId &itemId); void itemChanged(const QOrganizerItemId &itemId); void itemRemoved(const QOrganizerItemId &itemId); void collectionAdded(const QOrganizerCollectionId &collectionId); void collectionChanged(const QOrganizerCollectionId &collectionId); void collectionRemoved(const QOrganizerCollectionId &collectionId); protected: virtual void run(); private slots: void handleRequest(); void onJsonDbConnectionError(QtJsonDb::QJsonDbConnection::ErrorCode error, const QString &message); void onJsonDbRequestError(QtJsonDb::QJsonDbRequest::ErrorCode error, const QString &message); void onJsonDbRequestFinished(); void onJsonDbWatcherError(QtJsonDb::QJsonDbWatcher::ErrorCode error, const QString &message); void onJsonDbWatcherNotificationsAvailable(); private: enum RequestType { Invalid, SaveItems, Items, ItemsById, RemoveItems, SaveCollections, Collections, RemoveCollections }; void handleResponse(QOrganizerManager::Error error, QJsonDbRequest *request); void handleSaveItemsRequest(); void handleSaveItemsResponse(int index, QOrganizerManager::Error error, QJsonDbRequest *request); void handleItemsRequest(); void handleItemsResponse(QOrganizerManager::Error error, QJsonDbRequest *request); void handleItemsByIdRequest(); void handleItemsByIdResponse(QOrganizerManager::Error error, QJsonDbRequest *request); void handleRemoveItemsRequest(); void handleRemoveItemsResponse(int index, QOrganizerManager::Error error); void handleSaveCollectionsRequest(); void handleSaveCollectionsResponse(int index, QOrganizerManager::Error error, QJsonDbRequest *request); void handleCollectionsRequest(); void handleCollectionsResponse(QOrganizerManager::Error error, QJsonDbRequest *request); void handleRemoveCollectionsRequest(); void handleRemoveCollectionsResponse(QOrganizerManager::Error error); void handleSaveAlarmRequest(); void handleSaveAlarmResponse(QOrganizerManager::Error error); void handleAlarmIdRequest(); void handleAlarmIdResponse(QOrganizerManager::Error error, QJsonDbRequest *request); void handleRemoveAlarmRequest(); void handleRemoveAlarmResponse(QOrganizerManager::Error error); enum JsonDbRequestType { JsonDbReadRequest = 0, JsonDbCreateRequest, JsonDbUpdateRequest, JsonDbRemoveRequest }; bool makeJsonDbRequest(JsonDbRequestType jsonDbRequestType, int index, QOrganizerJsonDbEngine::StorageLocation storageLocation, const QString &query = QString(), const QList &objects = QList()); void processRequest(); void initRequestData(RequestType requestType, QMap* errorMap, QOrganizerManager::Error* error); void clearRequestData(); void registerForNotifications(); QMutex* m_waitMutex; QWaitCondition m_syncWaitCondition; QOrganizerJsonDbConverter m_converter; QJsonDbConnection *m_jsonDbConnection; // "collection cache" QSet m_collectionIds; QOrganizerCollection m_defaultCollection; QString m_notificationObjectUuid; QList m_watchers; // request data // common RequestType m_requestType; QMap m_requestIndexMap; // map from request to item / collection index QMap* m_errorMap; QOrganizerManager::Error* m_error; // storage location QList m_availableStorageLocations; QOrganizerJsonDbEngine::StorageLocations m_availableStorageLocationsFlag; QOrganizerJsonDbEngine::StorageLocation m_saveToStorageLocation; QOrganizerJsonDbEngine::StorageLocations m_fetchFromStorageLocations; bool m_mandatoryStorageLocationMissing; // SaveItems QMap* m_resultItems; // map from item index to item // Items QList m_items; QDateTime m_start; QDateTime m_end; QOrganizerItemFilter m_filter; // QList m_sortOrders; // QOrganizerItemFetchHint m_fetchHint; FetchType m_fetchType; QOrganizerItemId m_parentItemId; QHash m_idItemMap; // RemoveItems (itemsById) QList m_itemIds; // RemoveItemsByCollectionId QList m_removeItemCollectionIds; // RemoveItemsByParentId QList m_removeItemParentIds; // SaveCollection QMap* m_resultCollections; // map from collection index to collection bool m_isDefaultCollection; // Collections QList m_collections; // RemoveCollections QMap m_removeCollectionIds; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERJSONDBDATASTORAGE_H src/plugins/organizer/jsondb/qorganizerjsondbengine.cpp000066400000000000000000000733241233466112000240670ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizerjsondbengine.h" #include #include #include #include "qorganizerjsondbrequestthread.h" #include "qorganizerjsondbstring.h" QT_BEGIN_NAMESPACE_ORGANIZER QOrganizerJsonDbEngine::QOrganizerJsonDbEngine(QOrganizerManager::Error *error) : m_requestHandlerThread(new QOrganizerJsonDbRequestThread) { // Register types to be able to send them as parameters in cross-thread signals qRegisterMetaType("QOrganizerAbstractRequest::State"); qRegisterMetaType >("QList"); qRegisterMetaType >("QList"); qRegisterMetaType("QAbstractSocket::SocketState"); qRegisterMetaType > >("QList >"); qRegisterMetaType > >("QList >"); qRegisterMetaType("QOrganizerItemId"); qRegisterMetaType("QOrganizerCollectionId"); m_requestHandlerThread->moveToThread(m_requestHandlerThread); // making sure that thread is started before exiting this function QEventLoop loop; connect(m_requestHandlerThread, SIGNAL(initialized()), &loop, SLOT(quit())); m_requestHandlerThread->start(); loop.exec(); connect(this, SIGNAL(requestReceived(QOrganizerAbstractRequest*)), m_requestHandlerThread, SLOT(handleRequest(QOrganizerAbstractRequest*))); m_requestHandlerThread->setEngine(this); *error = QOrganizerManager::NoError; } QOrganizerJsonDbEngine::~QOrganizerJsonDbEngine() { if (m_requestHandlerThread) { m_requestHandlerThread->exit(); m_requestHandlerThread->wait(); delete m_requestHandlerThread; } } QString QOrganizerJsonDbEngine::managerName() const { return QOrganizerJsonDbStr::jsonDbManagerName(); } QMap QOrganizerJsonDbEngine::managerParameters() const { /* TODO - in case you have any actual parameters that are relevant that you saved in the factory method, return them here */ return QMap(); } QList QOrganizerJsonDbEngine::itemOccurrences(const QOrganizerItem &parentItem, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error) { QOrganizerItemOccurrenceFetchRequest occurrenceFetchReq; occurrenceFetchReq.setParentItem(parentItem); occurrenceFetchReq.setStartDate(startDateTime); occurrenceFetchReq.setEndDate(endDateTime); occurrenceFetchReq.setMaxOccurrences(maxCount); occurrenceFetchReq.setFetchHint(fetchHint); *error = QOrganizerManager::NoError; startRequest(&occurrenceFetchReq); if (waitForRequestFinished(&occurrenceFetchReq, 0)) { *error = occurrenceFetchReq.error(); return occurrenceFetchReq.itemOccurrences(); } return QList(); } QList QOrganizerJsonDbEngine::itemIds(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, const QList &sortOrders, QOrganizerManager::Error *error) { QOrganizerItemIdFetchRequest idFetchReq; idFetchReq.setStartDate(startDateTime); idFetchReq.setEndDate(endDateTime); idFetchReq.setFilter(filter); idFetchReq.setSorting(sortOrders); if (startRequest(&idFetchReq)) { if (waitForRequestFinished(&idFetchReq, 0)) *error = idFetchReq.error(); else *error = QOrganizerManager::TimeoutError; return idFetchReq.itemIds(); } else { *error = QOrganizerManager::NotSupportedError; return QList(); } } QList QOrganizerJsonDbEngine::items(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error) { QOrganizerItemFetchRequest request; request.setFilter(filter); request.setStartDate(startDateTime); request.setEndDate(endDateTime); request.setMaxCount(maxCount); request.setSorting(sortOrders); request.setFetchHint(fetchHint); if (startRequest(&request)) { if (waitForRequestFinished(&request, 0)) *error = request.error(); else *error = QOrganizerManager::TimeoutError; return request.items(); } else { *error = QOrganizerManager::NotSupportedError; return QList(); } } QList QOrganizerJsonDbEngine::itemsForExport(const QDateTime &startDateTime, const QDateTime &endDateTime, const QOrganizerItemFilter &filter, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error) { QOrganizerItemFetchForExportRequest request; request.setStartDate(startDateTime); request.setEndDate(endDateTime); request.setFilter(filter); request.setSorting(sortOrders); request.setFetchHint(fetchHint); if (startRequest(&request)) { if (waitForRequestFinished(&request, 0)) *error = request.error(); else *error = QOrganizerManager::TimeoutError; return request.items(); } else { *error = QOrganizerManager::NotSupportedError; return QList(); } } QList QOrganizerJsonDbEngine::items(const QList &itemIds, const QOrganizerItemFetchHint &fetchHint, QMap *errorMap, QOrganizerManager::Error *error) { QOrganizerItemFetchByIdRequest fetchByIdReq; fetchByIdReq.setIds(itemIds); fetchByIdReq.setFetchHint(fetchHint); if (startRequest(&fetchByIdReq)) { if (waitForRequestFinished(&fetchByIdReq, 0)) { *error = fetchByIdReq.error(); *errorMap = fetchByIdReq.errorMap(); } else { *error = QOrganizerManager::TimeoutError; } return fetchByIdReq.items(); } else { *error = QOrganizerManager::NotSupportedError; return QList(); } } bool QOrganizerJsonDbEngine::saveItems(QList *items, const QList &detailMask, QMap *errorMap, QOrganizerManager::Error *error) { QOrganizerItemSaveRequest request; request.setItems(*items); request.setDetailMask(detailMask); if (startRequest(&request)) { if (waitForRequestFinished(&request, 0)) { *error = request.error(); *errorMap = request.errorMap(); } else { *error = QOrganizerManager::TimeoutError; } *items = request.items(); return (*error == QOrganizerManager::NoError); } else { *error = QOrganizerManager::NotSupportedError; return false; } } bool QOrganizerJsonDbEngine::removeItems(const QList &itemIds, QMap *errorMap, QOrganizerManager::Error *error) { QOrganizerItemRemoveByIdRequest removeByIdReq; removeByIdReq.setItemIds(itemIds); startRequest(&removeByIdReq); if (waitForRequestFinished(&removeByIdReq, 0)) { *errorMap = removeByIdReq.errorMap(); *error = removeByIdReq.error(); } return *error == QOrganizerManager::NoError; } bool QOrganizerJsonDbEngine::removeItems(const QList *items, QMap* errorMap, QOrganizerManager::Error* error) { QOrganizerItemRemoveRequest removeReq; removeReq.setItems(*items); if (startRequest(&removeReq)) { if (waitForRequestFinished(&removeReq, 0)) { *errorMap = removeReq.errorMap(); *error = removeReq.error(); } else { *error = QOrganizerManager::TimeoutError; } } else { *error = QOrganizerManager::NotSupportedError; } return (*error == QOrganizerManager::NoError); } QOrganizerCollection QOrganizerJsonDbEngine::defaultCollection(QOrganizerManager::Error* error) { *error = QOrganizerManager::NoError; return m_requestHandlerThread->defaultCollection(); } QOrganizerCollection QOrganizerJsonDbEngine::collection(const QOrganizerCollectionId& collectionId, QOrganizerManager::Error* error) { *error = QOrganizerManager::NoError; QList collectionList = collections(error); for (int i = 0; i < collectionList.size(); i++) { if (collectionList[i].id() == collectionId) return collectionList[i]; } *error = QOrganizerManager::DoesNotExistError; return QOrganizerCollection(); } QList QOrganizerJsonDbEngine::collections(QOrganizerManager::Error* error) { QOrganizerCollectionFetchRequest collectionFetchReq; if (startRequest(&collectionFetchReq)) { if (waitForRequestFinished(&collectionFetchReq, 0)) *error = collectionFetchReq.error(); else *error = QOrganizerManager::TimeoutError; return collectionFetchReq.collections(); } else { *error = QOrganizerManager::NotSupportedError; return QList(); } } bool QOrganizerJsonDbEngine::saveCollection(QOrganizerCollection* collection, QOrganizerManager::Error* error) { QOrganizerCollectionSaveRequest collectionSaveReq; collectionSaveReq.setCollection(*collection); if (startRequest(&collectionSaveReq)) { if (waitForRequestFinished(&collectionSaveReq, 0)) *error = collectionSaveReq.error(); else *error = QOrganizerManager::TimeoutError; *collection = collectionSaveReq.collections().at(0); return *error == QOrganizerManager::NoError; } else { *error = QOrganizerManager::NotSupportedError; return false; } } bool QOrganizerJsonDbEngine::removeCollection(const QOrganizerCollectionId& collectionId, QOrganizerManager::Error* error) { QOrganizerCollectionRemoveRequest collectionRemoveReq; collectionRemoveReq.setCollectionId(collectionId); if (startRequest(&collectionRemoveReq)) { if (waitForRequestFinished(&collectionRemoveReq, 0)) *error = collectionRemoveReq.error(); else *error = QOrganizerManager::TimeoutError; return *error == QOrganizerManager::NoError; } else { *error = QOrganizerManager::NotSupportedError; return false; } } bool QOrganizerJsonDbEngine::startRequest(QOrganizerAbstractRequest* req) { QList itemList; QList idList; QList collectionList; QOrganizerManager::Error error = QOrganizerManager::NoError; QMap errorMap; m_requestHandlerThread->addRequest(req); switch (req->type()) { case QOrganizerAbstractRequest::ItemFetchRequest: { QOrganizerManagerEngine::updateItemFetchRequest(static_cast(req), itemList, error, QOrganizerAbstractRequest::ActiveState); break; } case QOrganizerAbstractRequest::ItemIdFetchRequest: { QOrganizerManagerEngine::updateItemIdFetchRequest(static_cast(req), idList, error, QOrganizerAbstractRequest::ActiveState); break; } case QOrganizerAbstractRequest::ItemFetchByIdRequest: { QOrganizerManagerEngine:: updateItemFetchByIdRequest(static_cast(req), itemList, error, errorMap, QOrganizerAbstractRequest::ActiveState); break; } case QOrganizerAbstractRequest::ItemFetchForExportRequest: { QOrganizerManagerEngine::updateItemFetchForExportRequest(static_cast(req), itemList, error, QOrganizerAbstractRequest::ActiveState); break; } case QOrganizerAbstractRequest::ItemOccurrenceFetchRequest: { QOrganizerManagerEngine::updateItemOccurrenceFetchRequest(static_cast(req), itemList, error, QOrganizerAbstractRequest::ActiveState); break; } case QOrganizerAbstractRequest::ItemSaveRequest: { QOrganizerItemSaveRequest* saveReq = static_cast(req); itemList = saveReq->items(); QOrganizerManagerEngine::updateItemSaveRequest(saveReq, itemList, error, errorMap, QOrganizerAbstractRequest::ActiveState); break; } case QOrganizerAbstractRequest::ItemRemoveRequest: { QOrganizerManagerEngine::updateItemRemoveRequest(static_cast(req), error, errorMap, QOrganizerAbstractRequest::ActiveState); break; } case QOrganizerAbstractRequest::ItemRemoveByIdRequest: { QOrganizerManagerEngine::updateItemRemoveByIdRequest(static_cast(req), error, errorMap, QOrganizerAbstractRequest::ActiveState); break; } case QOrganizerAbstractRequest::CollectionFetchRequest: { QOrganizerManagerEngine::updateCollectionFetchRequest(static_cast(req), collectionList, error, QOrganizerAbstractRequest::ActiveState); break; } case QOrganizerAbstractRequest::CollectionSaveRequest: { QOrganizerCollectionSaveRequest* collectionSaveReq = static_cast(req); collectionList = collectionSaveReq->collections(); QOrganizerManagerEngine::updateCollectionSaveRequest(collectionSaveReq, collectionList, error, errorMap, QOrganizerAbstractRequest::ActiveState); break; } case QOrganizerAbstractRequest::CollectionRemoveRequest: { QOrganizerCollectionRemoveRequest* collectionRemoveReq = static_cast(req); QOrganizerManagerEngine::updateCollectionRemoveRequest(collectionRemoveReq, error, errorMap, QOrganizerAbstractRequest::ActiveState); break; } default: return false; } emit requestReceived(req); return true; } bool QOrganizerJsonDbEngine::cancelRequest(QOrganizerAbstractRequest* req) { /* TODO Cancel an in progress async request. If not possible, return false from here. */ return QOrganizerManagerEngine::cancelRequest(req); } bool QOrganizerJsonDbEngine::waitForRequestFinished(QOrganizerAbstractRequest* req, int msecs) { return m_requestHandlerThread->waitForRequestFinished(req, msecs); } void QOrganizerJsonDbEngine::requestDestroyed(QOrganizerAbstractRequest* req) { m_requestHandlerThread->requestDestroyed(req); } QList QOrganizerJsonDbEngine::supportedFilters() const { QList supported; supported << QOrganizerItemFilter::InvalidFilter << QOrganizerItemFilter::DetailFilter << QOrganizerItemFilter::DetailFieldFilter << QOrganizerItemFilter::IntersectionFilter << QOrganizerItemFilter::UnionFilter << QOrganizerItemFilter::IdFilter << QOrganizerItemFilter::CollectionFilter << QOrganizerItemFilter::DefaultFilter; return supported; } QList QOrganizerJsonDbEngine::supportedItemDetails(QOrganizerItemType::ItemType itemType) const { QList supportedDetails; supportedDetails << QOrganizerItemDetail::TypeItemType << QOrganizerItemDetail::TypeGuid // << QOrganizerItemDetail::TypeTimestamp << QOrganizerItemDetail::TypeDisplayLabel << QOrganizerItemDetail::TypeDescription << QOrganizerItemDetail::TypeComment << QOrganizerItemDetail::TypeTag << QOrganizerItemDetail::TypeExtendedDetail << QOrganizerItemDetail::TypeVersion; if (itemType == QOrganizerItemType::TypeEvent) { supportedDetails << QOrganizerItemDetail::TypeRecurrence << QOrganizerItemDetail::TypeEventTime << QOrganizerItemDetail::TypePriority << QOrganizerItemDetail::TypeLocation << QOrganizerItemDetail::TypeReminder << QOrganizerItemDetail::TypeAudibleReminder << QOrganizerItemDetail::TypeEventAttendee << QOrganizerItemDetail::TypeEventRsvp; } else if (itemType == QOrganizerItemType::TypeTodo) { supportedDetails << QOrganizerItemDetail::TypeRecurrence << QOrganizerItemDetail::TypeTodoTime << QOrganizerItemDetail::TypePriority << QOrganizerItemDetail::TypeTodoProgress << QOrganizerItemDetail::TypeReminder << QOrganizerItemDetail::TypeAudibleReminder; } else if (itemType == QOrganizerItemType::TypeEventOccurrence) { supportedDetails << QOrganizerItemDetail::TypeParent << QOrganizerItemDetail::TypeEventTime << QOrganizerItemDetail::TypePriority << QOrganizerItemDetail::TypeLocation << QOrganizerItemDetail::TypeReminder << QOrganizerItemDetail::TypeAudibleReminder << QOrganizerItemDetail::TypeEventAttendee << QOrganizerItemDetail::TypeEventRsvp; } else if (itemType == QOrganizerItemType::TypeTodoOccurrence) { supportedDetails << QOrganizerItemDetail::TypeParent << QOrganizerItemDetail::TypeTodoTime << QOrganizerItemDetail::TypePriority << QOrganizerItemDetail::TypeTodoProgress << QOrganizerItemDetail::TypeReminder << QOrganizerItemDetail::TypeAudibleReminder; } else { // We don't support Journal and Note, yet ;) supportedDetails.clear(); } return supportedDetails; } QList QOrganizerJsonDbEngine::supportedItemTypes() const { QList ret; ret << QOrganizerItemType::TypeEvent; ret << QOrganizerItemType::TypeEventOccurrence; // ret << QOrganizerItemType::TypeJournal; // ret << QOrganizerItemType::TypeNote; ret << QOrganizerItemType::TypeTodo; ret << QOrganizerItemType::TypeTodoOccurrence; return ret; } const QList QOrganizerJsonDbEngine::supportedDetailFields(QOrganizerItemDetail::DetailType detailType) { static QMap > supportedFieldsByType; if (supportedFieldsByType.isEmpty()) { supportedFieldsByType.insert(QOrganizerItemDetail::TypeAudibleReminder, QList() << QOrganizerItemAudibleReminder::FieldDataUrl << QOrganizerItemAudibleReminder::FieldRepetitionCount << QOrganizerItemAudibleReminder::FieldRepetitionDelay << QOrganizerItemAudibleReminder::FieldSecondsBeforeStart); supportedFieldsByType.insert(QOrganizerItemDetail::TypeClassification, QList() << QOrganizerItemClassification::FieldClassification); supportedFieldsByType.insert(QOrganizerItemDetail::TypeComment, QList() << QOrganizerItemComment::FieldComment); supportedFieldsByType.insert(QOrganizerItemDetail::TypeDescription, QList() << QOrganizerItemDescription::FieldDescription); supportedFieldsByType.insert(QOrganizerItemDetail::TypeDisplayLabel, QList() << QOrganizerItemDisplayLabel::FieldLabel); supportedFieldsByType.insert(QOrganizerItemDetail::TypeEmailReminder, QList() << QOrganizerItemEmailReminder::FieldAttachments << QOrganizerItemEmailReminder::FieldBody << QOrganizerItemEmailReminder::FieldRecipients << QOrganizerItemEmailReminder::FieldRepetitionCount << QOrganizerItemEmailReminder::FieldRepetitionDelay << QOrganizerItemEmailReminder::FieldSecondsBeforeStart << QOrganizerItemEmailReminder::FieldSubject); supportedFieldsByType.insert(QOrganizerItemDetail::TypeEventAttendee, QList() << QOrganizerEventAttendee::FieldAttendeeId << QOrganizerEventAttendee::FieldEmailAddress << QOrganizerEventAttendee::FieldName << QOrganizerEventAttendee::FieldParticipationRole << QOrganizerEventAttendee::FieldParticipationStatus); supportedFieldsByType.insert(QOrganizerItemDetail::TypeEventRsvp, QList() << QOrganizerEventRsvp::FieldOrganizerEmail << QOrganizerEventRsvp::FieldOrganizerName << QOrganizerEventRsvp::FieldParticipationRole << QOrganizerEventRsvp::FieldParticipationStatus << QOrganizerEventRsvp::FieldResponseDate << QOrganizerEventRsvp::FieldResponseDeadline << QOrganizerEventRsvp::FieldResponseRequirement); supportedFieldsByType.insert(QOrganizerItemDetail::TypeEventTime, QList() << QOrganizerEventTime::FieldAllDay << QOrganizerEventTime::FieldEndDateTime << QOrganizerEventTime::FieldStartDateTime); supportedFieldsByType.insert(QOrganizerItemDetail::TypeExtendedDetail, QList() << QOrganizerItemExtendedDetail::FieldData << QOrganizerItemExtendedDetail::FieldName); supportedFieldsByType.insert(QOrganizerItemDetail::TypeGuid, QList() << QOrganizerItemGuid::FieldGuid); supportedFieldsByType.insert(QOrganizerItemDetail::TypeItemType, QList() << QOrganizerItemType::FieldType); supportedFieldsByType.insert(QOrganizerItemDetail::TypeJournalTime, QList() << QOrganizerJournalTime::FieldEntryDateTime); supportedFieldsByType.insert(QOrganizerItemDetail::TypeLocation, QList() << QOrganizerItemLocation::FieldLabel << QOrganizerItemLocation::FieldLatitude << QOrganizerItemLocation::FieldLongitude); supportedFieldsByType.insert(QOrganizerItemDetail::TypeParent, QList() << QOrganizerItemParent::FieldOriginalDate << QOrganizerItemParent::FieldParentId); supportedFieldsByType.insert(QOrganizerItemDetail::TypePriority, QList() << QOrganizerItemPriority::FieldPriority); supportedFieldsByType.insert(QOrganizerItemDetail::TypeRecurrence, QList() << QOrganizerItemRecurrence::FieldExceptionDates << QOrganizerItemRecurrence::FieldExceptionRules << QOrganizerItemRecurrence::FieldRecurrenceDates << QOrganizerItemRecurrence::FieldRecurrenceRules); supportedFieldsByType.insert(QOrganizerItemDetail::TypeReminder, QList() << QOrganizerItemReminder::FieldRepetitionCount << QOrganizerItemReminder::FieldRepetitionDelay << QOrganizerItemReminder::FieldSecondsBeforeStart); supportedFieldsByType.insert(QOrganizerItemDetail::TypeTag, QList() << QOrganizerItemTag::FieldTag); supportedFieldsByType.insert(QOrganizerItemDetail::TypeTimestamp, QList() << QOrganizerItemTimestamp::FieldCreated << QOrganizerItemTimestamp::FieldLastModified); supportedFieldsByType.insert(QOrganizerItemDetail::TypeTodoProgress, QList() << QOrganizerTodoProgress::FieldFinishedDateTime << QOrganizerTodoProgress::FieldPercentageComplete << QOrganizerTodoProgress::FieldStatus); supportedFieldsByType.insert(QOrganizerItemDetail::TypeTodoTime, QList() << QOrganizerTodoTime::FieldAllDay << QOrganizerTodoTime::FieldDueDateTime << QOrganizerTodoTime::FieldStartDateTime); supportedFieldsByType.insert(QOrganizerItemDetail::TypeUndefined, QList()); supportedFieldsByType.insert(QOrganizerItemDetail::TypeVersion, QList() << QOrganizerItemVersion::FieldExtendedVersion << QOrganizerItemVersion::FieldVersion); supportedFieldsByType.insert(QOrganizerItemDetail::TypeVisualReminder, QList() << QOrganizerItemVisualReminder::FieldDataUrl << QOrganizerItemVisualReminder::FieldMessage << QOrganizerItemVisualReminder::FieldRepetitionCount << QOrganizerItemVisualReminder::FieldRepetitionDelay << QOrganizerItemVisualReminder::FieldSecondsBeforeStart); } return supportedFieldsByType.value(detailType, QList()); } #include "moc_qorganizerjsondbengine.cpp" QT_END_NAMESPACE_ORGANIZER src/plugins/organizer/jsondb/qorganizerjsondbengine.h000066400000000000000000000210051233466112000235210ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERJSONDBENGINE_H #define QORGANIZERJSONDBENGINE_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include QT_BEGIN_NAMESPACE_ORGANIZER /* Organizer jsondb backend storage location related documentation (will be moved later to proper location) Storage locations are locations where the organizer data can be stored. Currently there are two locations; UserDataStorage and SystemStorage. These can be seen on the QOrganizerAbstractRequest API's QOrganizerAbstractRequest::StorageLocation. Organizer jsondb backend engine requires storage locations (partitions in qtjsondb-module terminology) to be present. If MissingPlatformRequirementsError is received from requests, it means that the mandatory UserDataStorage storage location is missing or cannot be accessed due to missing security access rights and Organizer jsondb backend cannot work properly. InvalidStorageLocationError is returned in cases when the operation is targeted to non-mandatory, but not accessible storage location. To create missing partitions, create a file named partitions.json having the following lines in it: [ { "name" : "com.nokia.mt.User" }, { "name" : "com.nokia.mt.System" } ] Then when starting the jsondb process on background, start it with path to partitions.json -file defined like this -> jsondb -config-path /home/me/myjsondbfiles/ If you use QOrganizerManagerEngine APIs, please note that the QOrganizerJsonDbEngineData's implementation directs all the operations to UserDataStorage storage location. If you want to target operations to other storage locations, you need to use the asynchronous requests instead. There are some restrictions with organizer data and storing it to storage locations: - Once an item or collection is saved to one storage location, you cannot change it to another storage location. The storage location parameter is ignored on the following updates. - A collection and all the items it contains must also exist in the same storage location. If not, InvalidCollectionError is returned when attempting to save the item. By default items and collections are stored in UserDataStorage storage location and fetched from there. Storage location information is included in the engine item id syntax in following way: [QOrganizerAbstractRequest::StorageLocation]/[jsondb uuid] */ class QOrganizerJsonDbRequestThread; class QOrganizerJsonDbEngine : public QOrganizerManagerEngine { Q_OBJECT public: enum StorageLocation { UserDataStorage = 0x1, SystemStorage = 0x2 }; Q_DECLARE_FLAGS(StorageLocations, StorageLocation) QOrganizerJsonDbEngine(QOrganizerManager::Error *error); ~QOrganizerJsonDbEngine(); /* URI reporting */ QString managerName() const; QMap managerParameters() const; // items QList items(const QList &itemIds, const QOrganizerItemFetchHint &fetchHint, QMap *errorMap, QOrganizerManager::Error *error); QList items(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error); QList itemIds(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, const QList &sortOrders, QOrganizerManager::Error *error); QList itemOccurrences(const QOrganizerItem &parentItem, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error); QList itemsForExport(const QDateTime &startDateTime, const QDateTime &endDateTime, const QOrganizerItemFilter &filter, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error); bool saveItems(QList *items, const QList &detailMask, QMap *errorMap, QOrganizerManager::Error *error); bool removeItems(const QList &itemIds, QMap *errorMap, QOrganizerManager::Error *error); bool removeItems(const QList *items, QMap* errorMap, QOrganizerManager::Error* error); // collections QOrganizerCollection defaultCollection(QOrganizerManager::Error* error); QOrganizerCollection collection(const QOrganizerCollectionId& collectionId, QOrganizerManager::Error* error); QList collections(QOrganizerManager::Error* error); bool saveCollection(QOrganizerCollection* collection, QOrganizerManager::Error* error); bool removeCollection(const QOrganizerCollectionId& collectionId, QOrganizerManager::Error* error); /* Capabilities reporting */ QList supportedFilters() const; QList supportedItemDetails(QOrganizerItemType::ItemType itemType) const; QList supportedItemTypes() const; /* Asynchronous Request Support */ void requestDestroyed(QOrganizerAbstractRequest* req); bool startRequest(QOrganizerAbstractRequest* req); bool cancelRequest(QOrganizerAbstractRequest* req); bool waitForRequestFinished(QOrganizerAbstractRequest* req, int msecs); /* JsonDb Engine internal capability reporting */ static const QList supportedDetailFields(QOrganizerItemDetail::DetailType detailType); signals: void requestReceived(QOrganizerAbstractRequest* req); private: friend class QOrganizerJsonDbEngineFactory; QOrganizerJsonDbRequestThread *m_requestHandlerThread; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QOrganizerJsonDbEngine::StorageLocations) QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERJSONDBENGINE_H src/plugins/organizer/jsondb/qorganizerjsondbenginefactory.cpp000066400000000000000000000063111233466112000254470ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizerjsondbenginefactory.h" #include "qorganizerjsondbengine.h" #include "qorganizerjsondbid.h" #include "qorganizerjsondbstring.h" QT_BEGIN_NAMESPACE_ORGANIZER QOrganizerManagerEngine *QOrganizerJsonDbEngineFactory::engine(const QMap ¶meters, QOrganizerManager::Error *error) { Q_UNUSED(parameters) // manager takes ownership return new QOrganizerJsonDbEngine(error); } QOrganizerItemEngineId *QOrganizerJsonDbEngineFactory::createItemEngineId(const QMap ¶meters, const QString &idString) const { Q_UNUSED(parameters) QOrganizerJsonDbItemId *retn = new QOrganizerJsonDbItemId; if (!idString.isEmpty()) retn->setFullEngineId(idString); return retn; } QOrganizerCollectionEngineId *QOrganizerJsonDbEngineFactory::createCollectionEngineId(const QMap ¶meters, const QString &idString) const { Q_UNUSED(parameters) QOrganizerJsonDbCollectionId *retn = new QOrganizerJsonDbCollectionId; if (!idString.isEmpty()) retn->setFullEngineId(idString); return retn; } QString QOrganizerJsonDbEngineFactory::managerName() const { return QOrganizerJsonDbStr::jsonDbManagerName(); } #include "moc_qorganizerjsondbenginefactory.cpp" QT_END_NAMESPACE_ORGANIZER src/plugins/organizer/jsondb/qorganizerjsondbenginefactory.h000066400000000000000000000057731233466112000251270ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERJSONDBENGINEFACTORY_H #define QORGANIZERJSONDBENGINEFACTORY_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerJsonDbEngineFactory : public QOrganizerManagerEngineFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QOrganizerManagerEngineFactoryInterface" FILE "jsondb.json") public: QOrganizerManagerEngine *engine(const QMap ¶meters, QOrganizerManager::Error *error); QOrganizerItemEngineId *createItemEngineId(const QMap ¶meters, const QString &idString) const; QOrganizerCollectionEngineId *createCollectionEngineId(const QMap ¶meters, const QString &idString) const; QString managerName() const; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERJSONDBENGINEFACTORY_H src/plugins/organizer/jsondb/qorganizerjsondbid.cpp000066400000000000000000000211131233466112000232030ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizerjsondbid.h" #ifndef QT_NO_DEBUG_STREAM #include #endif #include "qorganizerjsondbstring.h" QT_BEGIN_NAMESPACE_ORGANIZER QOrganizerJsonDbItemId::QOrganizerJsonDbItemId() : QOrganizerItemEngineId() , m_jsonDbUuid(QString()) , m_storageLocation(QOrganizerJsonDbEngine::UserDataStorage) { } QOrganizerJsonDbItemId::QOrganizerJsonDbItemId(const QString &fullEngineId) : QOrganizerItemEngineId() { splitId(fullEngineId, m_jsonDbUuid, m_storageLocation); } QOrganizerJsonDbItemId::QOrganizerJsonDbItemId(const QOrganizerJsonDbItemId &other) : QOrganizerItemEngineId(), m_jsonDbUuid(other.m_jsonDbUuid), m_storageLocation(other.m_storageLocation) { } QOrganizerJsonDbItemId::~QOrganizerJsonDbItemId() { } bool QOrganizerJsonDbItemId::isEqualTo(const QOrganizerItemEngineId *other) const { const QOrganizerJsonDbItemId* id = static_cast(other); return ((m_jsonDbUuid == id->m_jsonDbUuid) && (m_storageLocation == id->m_storageLocation)); } bool QOrganizerJsonDbItemId::isLessThan(const QOrganizerItemEngineId *other) const { const QOrganizerJsonDbItemId* id = static_cast(other); if (m_storageLocation == id->m_storageLocation) return (m_jsonDbUuid < id->m_jsonDbUuid); else return (m_storageLocation < id->m_storageLocation); } QString QOrganizerJsonDbItemId::managerUri() const { return QOrganizerJsonDbStr::jsonDbManagerUri(); } QOrganizerItemEngineId *QOrganizerJsonDbItemId::clone() const { QOrganizerJsonDbItemId *newId = new QOrganizerJsonDbItemId(); newId->setJsonDbUuid(m_jsonDbUuid); newId->setStorageLocation(m_storageLocation); return newId; } #ifndef QT_NO_DEBUG_STREAM QDebug &QOrganizerJsonDbItemId::debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerJsonDbItemId(" << toString() << ")"; return dbg.maybeSpace(); } #endif QString QOrganizerJsonDbItemId::toString() const { return QString("%1/%2").arg(m_storageLocation).arg(m_jsonDbUuid); } uint QOrganizerJsonDbItemId::hash() const { return QT_PREPEND_NAMESPACE(qHash)(this->toString()); } void QOrganizerJsonDbItemId::setFullEngineId(const QString &fullEngineId) { splitId(fullEngineId, m_jsonDbUuid, m_storageLocation); } QString QOrganizerJsonDbItemId::jsondbUuid() const { return m_jsonDbUuid; } void QOrganizerJsonDbItemId::setJsonDbUuid(const QString &jsonDbUuid) { m_jsonDbUuid = jsonDbUuid; } QOrganizerJsonDbEngine::StorageLocation QOrganizerJsonDbItemId::storageLocation() const { return m_storageLocation; } void QOrganizerJsonDbItemId::setStorageLocation(QOrganizerJsonDbEngine::StorageLocation storageLocation) { m_storageLocation = storageLocation; } void QOrganizerJsonDbItemId::splitId(const QString &fullId, QString &jsondbUuid, QOrganizerJsonDbEngine::StorageLocation &storageLocation) { // separate engine id part, if full id given QString engineId = fullId.contains(":") ? fullId.mid(fullId.lastIndexOf(":")+1) : fullId; // separate storagelocation and collection id from each other const QStringList splittedEngineId = engineId.split(QStringLiteral("/")); storageLocation = (QOrganizerJsonDbEngine::StorageLocation)splittedEngineId.first().toInt(); jsondbUuid = splittedEngineId.last(); } QOrganizerJsonDbCollectionId::QOrganizerJsonDbCollectionId() : QOrganizerCollectionEngineId() , m_jsonDbUuid(QString()) , m_storageLocation(QOrganizerJsonDbEngine::UserDataStorage) { } QOrganizerJsonDbCollectionId::QOrganizerJsonDbCollectionId(const QString &fullEngineId) : QOrganizerCollectionEngineId() { splitId(fullEngineId, m_jsonDbUuid, m_storageLocation); } QOrganizerJsonDbCollectionId::QOrganizerJsonDbCollectionId(const QOrganizerJsonDbCollectionId &other) : QOrganizerCollectionEngineId() , m_jsonDbUuid(other.m_jsonDbUuid) , m_storageLocation(other.m_storageLocation) { } QOrganizerJsonDbCollectionId::~QOrganizerJsonDbCollectionId() { } bool QOrganizerJsonDbCollectionId::isEqualTo(const QOrganizerCollectionEngineId *other) const { const QOrganizerJsonDbCollectionId* collId = static_cast(other); return ((m_jsonDbUuid == collId->m_jsonDbUuid) && (m_storageLocation == collId->m_storageLocation)); } bool QOrganizerJsonDbCollectionId::isLessThan(const QOrganizerCollectionEngineId *other) const { const QOrganizerJsonDbCollectionId* collId = static_cast(other); if (m_storageLocation == collId->m_storageLocation) return (m_jsonDbUuid < collId->m_jsonDbUuid); else return (m_storageLocation < collId->m_storageLocation); } QString QOrganizerJsonDbCollectionId::managerUri() const { return QOrganizerJsonDbStr::jsonDbManagerUri(); } QOrganizerCollectionEngineId *QOrganizerJsonDbCollectionId::clone() const { QOrganizerJsonDbCollectionId *newId = new QOrganizerJsonDbCollectionId(); newId->setJsonDbUuid(m_jsonDbUuid); newId->setStorageLocation(m_storageLocation); return newId; } #ifndef QT_NO_DEBUG_STREAM QDebug &QOrganizerJsonDbCollectionId::debugStreamOut(QDebug &dbg) const { dbg.nospace() << "QOrganizerJsonDbCollectionId(" << toString() << ")"; return dbg.maybeSpace(); } #endif QString QOrganizerJsonDbCollectionId::toString() const { return QString("%1/%2").arg(m_storageLocation).arg(m_jsonDbUuid); } uint QOrganizerJsonDbCollectionId::hash() const { return QT_PREPEND_NAMESPACE(qHash)(toString()); } void QOrganizerJsonDbCollectionId::setFullEngineId(const QString &fullEngineId) { splitId(fullEngineId, m_jsonDbUuid, m_storageLocation); } QString QOrganizerJsonDbCollectionId::jsondbUuid() const { return m_jsonDbUuid; } void QOrganizerJsonDbCollectionId::setJsonDbUuid(const QString &jsonDbUuid) { m_jsonDbUuid = jsonDbUuid; } QOrganizerJsonDbEngine::StorageLocation QOrganizerJsonDbCollectionId::storageLocation() const { return m_storageLocation; } void QOrganizerJsonDbCollectionId::setStorageLocation(QOrganizerJsonDbEngine::StorageLocation storageLocation) { m_storageLocation = storageLocation; } void QOrganizerJsonDbCollectionId::splitId(const QString &fullId, QString &jsondbUuid, QOrganizerJsonDbEngine::StorageLocation &storageLocation) { // separate engine id part, if full id given QString engineId = fullId.contains(":") ? fullId.mid(fullId.lastIndexOf(":")+1) : fullId; // separate storagelocation and collection id from each other const QStringList splittedEngineId = engineId.split(QStringLiteral("/")); storageLocation = QOrganizerJsonDbEngine::StorageLocation(splittedEngineId.first().toInt()); jsondbUuid = splittedEngineId.last(); } QT_END_NAMESPACE_ORGANIZER src/plugins/organizer/jsondb/qorganizerjsondbid.h000066400000000000000000000114041233466112000226520ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERJSONDBID_H #define QORGANIZERJSONDBID_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include "qorganizerjsondbengine.h" QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerJsonDbItemId : public QOrganizerItemEngineId { public: QOrganizerJsonDbItemId(); QOrganizerJsonDbItemId(const QString &fullEngineId); ~QOrganizerJsonDbItemId(); QOrganizerJsonDbItemId(const QOrganizerJsonDbItemId &other); bool isEqualTo(const QOrganizerItemEngineId *other) const; bool isLessThan(const QOrganizerItemEngineId *other) const; QString managerUri() const; QOrganizerItemEngineId *clone() const; QString toString() const; #ifndef QT_NO_DEBUG_STREAM QDebug &debugStreamOut(QDebug &dbg) const; #endif uint hash() const; void setFullEngineId(const QString &fullEngineId); QString jsondbUuid() const; void setJsonDbUuid(const QString &jsonDbUuid); QOrganizerJsonDbEngine::StorageLocation storageLocation() const; void setStorageLocation(QOrganizerJsonDbEngine::StorageLocation storageLocation); private: void splitId(const QString &fullId, QString &jsondbUuid, QOrganizerJsonDbEngine::StorageLocation &storageLocation); private: QString m_jsonDbUuid; QOrganizerJsonDbEngine::StorageLocation m_storageLocation; }; class QOrganizerJsonDbCollectionId : public QOrganizerCollectionEngineId { public: QOrganizerJsonDbCollectionId(); QOrganizerJsonDbCollectionId(const QString &fullEngineId); ~QOrganizerJsonDbCollectionId(); QOrganizerJsonDbCollectionId(const QOrganizerJsonDbCollectionId &other); bool isEqualTo(const QOrganizerCollectionEngineId *other) const; bool isLessThan(const QOrganizerCollectionEngineId *other) const; QString managerUri() const; QOrganizerCollectionEngineId *clone() const; QString toString() const; #ifndef QT_NO_DEBUG_STREAM QDebug &debugStreamOut(QDebug &dbg) const; #endif uint hash() const; void setFullEngineId(const QString &fullEngineId); QString jsondbUuid() const; void setJsonDbUuid(const QString &jsonDbUuid); QOrganizerJsonDbEngine::StorageLocation storageLocation() const; void setStorageLocation(QOrganizerJsonDbEngine::StorageLocation storageLocation); private: void splitId(const QString &fullId, QString &jsondbUuid, QOrganizerJsonDbEngine::StorageLocation &storageLocation); private: QString m_jsonDbUuid; QOrganizerJsonDbEngine::StorageLocation m_storageLocation; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERJSONDBID_H src/plugins/organizer/jsondb/qorganizerjsondbrequestmanager.cpp000066400000000000000000000115501233466112000256360ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizerjsondbrequestmanager.h" #include QT_BEGIN_NAMESPACE_ORGANIZER QOrganizerJsonDbRequestManager::QOrganizerJsonDbRequestManager() { m_operationMutex = new QMutex(); } QOrganizerJsonDbRequestManager::~QOrganizerJsonDbRequestManager() { for (QMap::const_iterator i = m_requests.begin(); i != m_requests.end(); ++i) delete i.value(); delete m_operationMutex; } void QOrganizerJsonDbRequestManager::addRequest(QOrganizerAbstractRequest *req) { QMutexLocker locker(m_operationMutex); if (!m_requests.contains(req)) { QOrganizerJsonDbRequestData *newData = new QOrganizerJsonDbRequestData(); newData->m_status = QOrganizerJsonDbRequestManager::Inactive; newData->m_waitCondition = 0; m_requests.insert(req, newData); } } void QOrganizerJsonDbRequestManager::removeRequest(QOrganizerAbstractRequest *req) { QMutexLocker locker(m_operationMutex); if (m_requests.contains(req)) { delete m_requests.value(req); m_requests.remove(req); } } bool QOrganizerJsonDbRequestManager::setActive(QOrganizerAbstractRequest *req) { // This function is called from JsonDbEngine thread QMutexLocker locker(m_operationMutex); if (m_requests.contains(req)) { m_requests.value(req)->m_status = QOrganizerJsonDbRequestManager::Active; return true; } return false; } bool QOrganizerJsonDbRequestManager::setDeleted(QOrganizerAbstractRequest *req) { // This function is called from JsonDbEngine thread QMutexLocker locker(m_operationMutex); if (m_requests.contains(req)) { m_requests.value(req)->m_status = QOrganizerJsonDbRequestManager::Deleted; return true; } return false; } bool QOrganizerJsonDbRequestManager::setWaitCondition(QOrganizerAbstractRequest *req, QWaitCondition *waitCondition) { // This function is called from JsonDbEngine thread QMutexLocker locker(m_operationMutex); if (m_requests.contains(req)) { m_requests.value(req)->m_waitCondition = waitCondition; return true; } return false; } QWaitCondition *QOrganizerJsonDbRequestManager::waitCondition(QOrganizerAbstractRequest *req) { QMutexLocker locker(m_operationMutex); if (m_requests.contains(req)) return m_requests.value(req)->m_waitCondition; return 0; } void QOrganizerJsonDbRequestManager::removeWaitCondition(QOrganizerAbstractRequest *req) { QMutexLocker locker(m_operationMutex); if (m_requests.contains(req)) m_requests.value(req)->m_waitCondition = 0; } QOrganizerJsonDbRequestManager::HandlingStatus QOrganizerJsonDbRequestManager::requestStatus(QOrganizerAbstractRequest *req) { QMutexLocker locker(m_operationMutex); if (m_requests.contains(req)) return m_requests.value(req)->m_status; return QOrganizerJsonDbRequestManager::Invalid; } QT_END_NAMESPACE_ORGANIZER src/plugins/organizer/jsondb/qorganizerjsondbrequestmanager.h000066400000000000000000000077041233466112000253110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERJSONDBREQUESTMANAGER_H #define QORGANIZERJSONDBREQUESTMANAGER_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerJsonDbRequestData; class QOrganizerJsonDbRequestManager { public: // HandlingStatus gives info about in which phase the handling of the request is. // The status is not necessarily in sync with the request status (QOrganizerAbstractRequest::state()) enum HandlingStatus { Inactive = 0, // request handling not yet started Active, // request handling started, not yet finished Canceled, // request handling should be finished due to cancellation Deleted, // request deleted Invalid // status of a non-existing request }; QOrganizerJsonDbRequestManager(); ~QOrganizerJsonDbRequestManager(); void addRequest(QOrganizerAbstractRequest *req); void removeRequest(QOrganizerAbstractRequest *req); bool setActive(QOrganizerAbstractRequest *req); bool setDeleted(QOrganizerAbstractRequest *req); QOrganizerJsonDbRequestManager::HandlingStatus requestStatus(QOrganizerAbstractRequest *req); bool setWaitCondition(QOrganizerAbstractRequest *req, QWaitCondition *waitCondition); QWaitCondition *waitCondition(QOrganizerAbstractRequest *req); void removeWaitCondition(QOrganizerAbstractRequest *req); private: QMap m_requests; QMutex *m_operationMutex; }; class QOrganizerJsonDbRequestData { public: QOrganizerJsonDbRequestData() {} QWaitCondition *m_waitCondition; QOrganizerJsonDbRequestManager::HandlingStatus m_status; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERJSONDBREQUESTMANAGER_H src/plugins/organizer/jsondb/qorganizerjsondbrequestthread.cpp000066400000000000000000001742701233466112000255040ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizerjsondbrequestthread.h" #include #include #include #include #include #include #include #include #include "qorganizerjsondbdatastorage.h" #include "qorganizerjsondbid.h" #include "qorganizerjsondbrequestmanager.h" #include "qorganizerjsondbstring.h" QT_BEGIN_NAMESPACE_ORGANIZER const int QOrganizerJsonDbRequestThread::TIMEOUT_INTERVAL(100); const int QOrganizerJsonDbRequestThread::DefaultTimePeriod(1461); const int QOrganizerJsonDbRequestThread::MaxOccurrenceCount(50); QOrganizerJsonDbRequestThread::QOrganizerJsonDbRequestThread() : m_engine(0) , m_storage(0) , m_requestMgr(0) , m_reqStateMutex(0) , m_timer(0) { } QOrganizerJsonDbRequestThread::~QOrganizerJsonDbRequestThread() { delete m_reqStateMutex; m_reqStateMutex = 0; delete m_requestMgr; m_requestMgr = 0; m_storage->exit(); m_storage->wait(); delete m_storage; m_storage = 0; } void QOrganizerJsonDbRequestThread::run() { m_reqStateMutex = new QMutex(); m_requestMgr = new QOrganizerJsonDbRequestManager(); m_storage = new QOrganizerJsonDbDataStorage(); m_storage->moveToThread(m_storage); QEventLoop loop; connect(m_storage, SIGNAL(threadInitialized()), &loop, SLOT(quit())); m_storage->start(); loop.exec(); connect(m_storage, SIGNAL(itemAdded(QOrganizerItemId)), this, SLOT(onItemAdded(QOrganizerItemId))); connect(m_storage, SIGNAL(itemChanged(QOrganizerItemId)), this, SLOT(onItemChanged(QOrganizerItemId))); connect(m_storage, SIGNAL(itemRemoved(QOrganizerItemId)), this, SLOT(onItemRemoved(QOrganizerItemId))); connect(m_storage, SIGNAL(collectionAdded(QOrganizerCollectionId)), this, SLOT(onCollectionAdded(QOrganizerCollectionId))); connect(m_storage, SIGNAL(collectionChanged(QOrganizerCollectionId)), this, SLOT(onCollectionChanged(QOrganizerCollectionId))); connect(m_storage, SIGNAL(collectionRemoved(QOrganizerCollectionId)), this, SLOT(onCollectionRemoved(QOrganizerCollectionId))); initDefaultCollection(); emit initialized(); QThread::run(); } void QOrganizerJsonDbRequestThread::setEngine(QOrganizerJsonDbEngine *engine) { // This function is called by the QOrganizerJsonDbEngine thread (main thread) m_engine = engine; } void QOrganizerJsonDbRequestThread::addRequest(QOrganizerAbstractRequest* req) { // This function is called by the QOrganizerJsonDbEngine thread (main thread) QMutexLocker locker(m_reqStateMutex); m_requestMgr->addRequest(req); } bool QOrganizerJsonDbRequestThread::waitForRequestFinished(QOrganizerAbstractRequest *req, int msecs) { // This function is called by the QOrganizerJsonDbEngine thread (main thread) // TODO: timeout handling Q_UNUSED(msecs); QMutexLocker locker(m_reqStateMutex); QWaitCondition waitCondition; if (req->state() == QOrganizerAbstractRequest::FinishedState) return true; else if (req->state() == QOrganizerAbstractRequest::CanceledState) return false; // Request might still be inactive if this function is called immediatelly after sending a signal to // handleRequest slot. The signal goes to event loop and might be handled later than this function is executed. m_requestMgr->setWaitCondition(req, &waitCondition); bool requestFinished; if (msecs <= 0) requestFinished = waitCondition.wait(m_reqStateMutex); else requestFinished = waitCondition.wait(m_reqStateMutex, msecs); m_requestMgr->removeWaitCondition(req); return requestFinished; } void QOrganizerJsonDbRequestThread::requestDestroyed(QOrganizerAbstractRequest* req) { // This function is called by the QOrganizerJsonDbEngine thread (main thread) QMutexLocker locker(m_reqStateMutex); m_requestMgr->setDeleted(req); } QOrganizerCollection QOrganizerJsonDbRequestThread::defaultCollection() const { // This function is called by the QOrganizerJsonDbEngine thread (main thread) QMutexLocker locker(m_reqStateMutex); return m_storage->defaultCollection(); } bool QOrganizerJsonDbRequestThread::validRequest(QOrganizerAbstractRequest *req) { QOrganizerJsonDbRequestManager::HandlingStatus status = m_requestMgr->requestStatus(req); if (status == QOrganizerJsonDbRequestManager::Deleted) { m_requestMgr->removeRequest(req); return false; } else if (status == QOrganizerJsonDbRequestManager::Invalid) { return false; } return true; } bool QOrganizerJsonDbRequestThread::validPlatform(QOrganizerAbstractRequest *req) { if (!(QOrganizerJsonDbEngine::UserDataStorage & m_storage->availableStorageLocationsFlag())) { // UserDataStorage not available is a fatal issue qCritical("Organizer - JsonDb backend does not work without UserDataStorage!"); finishRequest(*req, QOrganizerManager::UnspecifiedError, QMap()); return false; } else { return true; } } void QOrganizerJsonDbRequestThread::handleRequest(QOrganizerAbstractRequest *req) { QMutexLocker locker(m_reqStateMutex); if (!validRequest(req)) return; if (!validPlatform(req)) return; switch (req->type()) { case QOrganizerAbstractRequest::ItemSaveRequest: handleItemSaveRequest(static_cast(req)); break; case QOrganizerAbstractRequest::ItemFetchRequest: handleItemFetchRequest(static_cast(req)); break; case QOrganizerAbstractRequest::ItemIdFetchRequest: handleItemIdFetchRequest(static_cast(req)); break; case QOrganizerAbstractRequest::ItemFetchByIdRequest: handleItemFetchByIdRequest(static_cast(req)); break; case QOrganizerAbstractRequest::ItemFetchForExportRequest: handleItemFetchForExportRequest(static_cast(req)); break; case QOrganizerAbstractRequest::ItemOccurrenceFetchRequest: handleItemOccurrenceFetchRequest(static_cast(req)); break; case QOrganizerAbstractRequest::ItemRemoveRequest: handleItemRemoveRequest(static_cast (req)); break; case QOrganizerAbstractRequest::ItemRemoveByIdRequest: handleItemRemoveByIdRequest(static_cast (req)); break; case QOrganizerAbstractRequest::CollectionSaveRequest: handleCollectionSaveRequest(static_cast (req)); break; case QOrganizerAbstractRequest::CollectionFetchRequest: handleCollectionFetchRequest(static_cast(req)); break; case QOrganizerAbstractRequest::CollectionRemoveRequest: handleCollectionRemoveRequest(static_cast(req)); break; default: break; } } void QOrganizerJsonDbRequestThread::finishRequest(QOrganizerAbstractRequest &request, QOrganizerManager::Error latestError, const QMap &errorMap, const QList &itemList, const QList &collectionList, const QList &itemIdList) { QWaitCondition* waitCondition = m_requestMgr->waitCondition(&request); m_requestMgr->removeRequest(&request); switch (request.type()) { case QOrganizerAbstractRequest::ItemSaveRequest: QOrganizerManagerEngine::updateItemSaveRequest(qobject_cast(&request), itemList, latestError, errorMap, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::ItemFetchRequest: QOrganizerManagerEngine::updateItemFetchRequest(qobject_cast(&request), itemList, latestError, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::ItemIdFetchRequest: QOrganizerManagerEngine::updateItemIdFetchRequest(qobject_cast(&request), itemIdList, latestError, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::ItemFetchByIdRequest: QOrganizerManagerEngine::updateItemFetchByIdRequest(qobject_cast(&request), itemList, latestError, errorMap, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::ItemFetchForExportRequest: QOrganizerManagerEngine::updateItemFetchForExportRequest(qobject_cast(&request), itemList, latestError, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::ItemOccurrenceFetchRequest: QOrganizerManagerEngine::updateItemOccurrenceFetchRequest(qobject_cast(&request), itemList, latestError, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::ItemRemoveRequest: QOrganizerManagerEngine::updateItemRemoveRequest(qobject_cast(&request), latestError, errorMap, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::ItemRemoveByIdRequest: QOrganizerManagerEngine::updateItemRemoveByIdRequest(qobject_cast(&request), latestError, errorMap, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::CollectionSaveRequest: QOrganizerManagerEngine::updateCollectionSaveRequest(qobject_cast(&request), collectionList, latestError, errorMap, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::CollectionFetchRequest: QOrganizerManagerEngine::updateCollectionFetchRequest(qobject_cast(&request), collectionList, latestError, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::CollectionRemoveRequest: QOrganizerManagerEngine::updateCollectionRemoveRequest(qobject_cast(&request), latestError, errorMap, QOrganizerAbstractRequest::FinishedState); break; default: break; } if (waitCondition) waitCondition->wakeAll(); } void QOrganizerJsonDbRequestThread::onItemAdded(const QOrganizerItemId &itemId) { m_ics.insertAddedItem(itemId); startTimer(); } void QOrganizerJsonDbRequestThread::onItemChanged(const QOrganizerItemId &itemId) { m_ics.insertChangedItem(itemId); startTimer(); } void QOrganizerJsonDbRequestThread::onItemRemoved(const QOrganizerItemId &itemId) { m_ics.insertRemovedItem(itemId); startTimer(); } void QOrganizerJsonDbRequestThread::onCollectionAdded(const QOrganizerCollectionId &collectionId) { m_ccs.insertAddedCollection(collectionId); startTimer(); } void QOrganizerJsonDbRequestThread::onCollectionChanged(const QOrganizerCollectionId &collectionId) { m_ccs.insertChangedCollection(collectionId); startTimer(); } void QOrganizerJsonDbRequestThread::onCollectionRemoved(const QOrganizerCollectionId &collectionId) { m_ccs.insertRemovedCollection(collectionId); startTimer(); } void QOrganizerJsonDbRequestThread::startTimer() { if (!m_timer) { m_timer = new QTimer(this); m_timer->setSingleShot(true); m_timer->setInterval(TIMEOUT_INTERVAL); connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout())); } if (!m_timer->isActive()) m_timer->start(); } void QOrganizerJsonDbRequestThread::onTimeout() { if (m_engine) { m_ics.emitSignals(m_engine); m_ics.clearAll(); m_ccs.emitSignals(m_engine); m_ccs.clearAll(); } } void QOrganizerJsonDbRequestThread::handleItemSaveRequest(QOrganizerItemSaveRequest* saveReq) { QMap itemMap; QMap itemIsNewStatusMap; QMap errorMap; QOrganizerManager::Error latestError = QOrganizerManager::NoError; QList items = saveReq->items(); QMap parentItemMap; QMap parentErrorMap; QOrganizerManager::Error parentError = QOrganizerManager::NoError; // if not defined, backend decides the default storage location -> UserDataStorage QOrganizerJsonDbEngine::StorageLocation storageLocation = QOrganizerJsonDbEngine::UserDataStorage; m_requestMgr->setActive(saveReq); for (int i = 0; i < items.size(); i++) { QOrganizerItem item = items.at(i); QOrganizerItem parentItem; bool itemIsNew = item.id().isNull(); bool itemIsOccurrence = (item.type() == QOrganizerItemType::TypeEventOccurrence || item.type() == QOrganizerItemType::TypeTodoOccurrence); bool errorFound = false; QString managerUri = QOrganizerManager::buildUri(m_engine->managerName(), m_engine->managerParameters()); // check manager uri if is the same with the engine uri if (!itemIsNew && (managerUri != item.id().managerUri())) { latestError = QOrganizerManager::BadArgumentError; errorFound = true; } // TODO: to be replaced by new validity check, collection id and guid should not be mandatory fields // this checks e.g. that occurrences have parent ids and original dates? // // ensure that the organizeritem's details conform to their definitions // if (!errorFound && !m_engine->validateItem(item, &latestError)) // errorFound = true; // check for view object if (item.data(QOrganizerJsonDbStr::eventIsSynthetic()).toBool()) { item.setData(QOrganizerJsonDbStr::eventIsSynthetic(), false); item.setId(QOrganizerItemId()); storageLocation = QOrganizerJsonDbEngine::SystemStorage; itemIsNew = true; } // Check the request is targeted to available storage location. // Storage location errors are prioritised over less important other possible errors. if (!errorFound) { QOrganizerManager::Error error = QOrganizerManager::NoError; if (itemIsNew) error = checkRequestSpecificStorageLocation(storageLocation); else error = checkRequestSpecificStorageLocation(static_cast(QOrganizerManagerEngine::engineItemId(item.id()))->storageLocation()); if (QOrganizerManager::NoError != error) { latestError = error; errorFound = true; } } if (!errorFound) { if (itemIsOccurrence) { parentItem = fetchParentItem(item); if (parentItem.isEmpty()) { latestError = QOrganizerManager::InvalidOccurrenceError; errorFound = true; } } } if (!errorFound) { if (itemIsOccurrence && !fixParentReferences(&item, parentItem)) { latestError = QOrganizerManager::InvalidOccurrenceError; errorFound = true; } } if (!errorFound) { if (itemIsOccurrence && !typesAreRelated(item.type(), parentItem.type())) { latestError = QOrganizerManager::InvalidOccurrenceError; errorFound = true; } } if (!errorFound) { if (!fixGuidReferences(&item, parentItem)) { latestError = QOrganizerManager::InvalidOccurrenceError; errorFound = true; } } if (!errorFound) { if (!fixCollectionReferences(&item, parentItem, itemIsNew, storageLocation)) { latestError = QOrganizerManager::InvalidCollectionError; errorFound = true; } } if (!errorFound && itemIsOccurrence) { // add exception date to parent item QOrganizerItemParent parentDetail = item.detail(QOrganizerItemDetail::TypeParent); QDate originalDate = parentDetail.originalDate(); QOrganizerItemRecurrence recurrenceDetail = parentItem.detail(QOrganizerItemDetail::TypeRecurrence); QSet exceptionDates = recurrenceDetail.exceptionDates(); if (!exceptionDates.contains(originalDate)) exceptionDates.insert(originalDate); if (parentItem.type() == QOrganizerItemType::TypeEvent) { QOrganizerEvent *parentEvent = static_cast(&parentItem); parentEvent->setExceptionDates(exceptionDates); } else if (parentItem.type() == QOrganizerItemType::TypeTodo) { QOrganizerTodo *parentTodo = static_cast(&parentItem); parentTodo->setExceptionDates(exceptionDates); } parentItemMap.insert(i, parentItem); QOrganizerItemRecurrence rec = parentItem.detail(QOrganizerItemDetail::TypeRecurrence); } // else if (!errorFound && !itemIsOccurrence) { // What to do when updating a parent item, i.e. one with recurrence? should we e.g. check if any exception dates have been removed and // remove those exceptions from db? // NOTE: currently there is work-around for this: some cross-checking is done when fetching items // } // remove version in case the item ID is reset if (itemIsNew) { QOrganizerItemVersion version = item.detail(QOrganizerItemDetail::TypeVersion); if (!version.isEmpty()) item.removeDetail(&version); } if (errorFound) { errorMap.insert(i, latestError); } else { itemMap.insert(i, item); itemIsNewStatusMap.insert(i, itemIsNew); } } // save items if (!itemMap.isEmpty()) { m_storage->saveItems(&itemMap, &errorMap, &latestError, storageLocation); QMap::const_iterator i = itemMap.constBegin(); while (i != itemMap.constEnd()) { if (!errorMap.contains(i.key())) items.replace(i.key(), i.value()); // always replacing because of version updating else parentItemMap.remove(i.key()); // the item was not saved, let's not save the parent item either ++i; } } // save parent items with modified exception dates if (!parentItemMap.isEmpty()) m_storage->saveItems(&parentItemMap, &parentErrorMap, &parentError, storageLocation); finishRequest(*saveReq, latestError, errorMap, items); } void QOrganizerJsonDbRequestThread::handleItemFetchRequest(QOrganizerItemFetchRequest *fetchReq) { QOrganizerManager::Error latestError = QOrganizerManager::NoError; m_requestMgr->setActive(fetchReq); // UserDataStorage is default storage location, if not otherwise set QOrganizerJsonDbEngine::StorageLocations storageLocations = QOrganizerJsonDbEngine::UserDataStorage; latestError = checkRequestSpecificStorageLocation(storageLocations); QList items; if (QOrganizerManager::NoError == latestError) items = internalItems(fetchReq->startDate(), fetchReq->endDate(), fetchReq->filter(), fetchReq->sorting(), fetchReq->fetchHint(), &latestError, false, storageLocations); finishRequest(*fetchReq, latestError, QMap(), items.mid(0, fetchReq->maxCount())); } void QOrganizerJsonDbRequestThread::handleItemIdFetchRequest(QOrganizerItemIdFetchRequest *idFetchReq) { QOrganizerManager::Error latestError = QOrganizerManager::NoError; m_requestMgr->setActive(idFetchReq); // UserDataStorage is default storage location, if not otherwise set QOrganizerJsonDbEngine::StorageLocations storageLocations = QOrganizerJsonDbEngine::UserDataStorage; latestError = checkRequestSpecificStorageLocation(storageLocations); QList items; if (QOrganizerManager::NoError == latestError) items = internalItems(idFetchReq->startDate(), idFetchReq->endDate(), idFetchReq->filter(), idFetchReq->sorting(), QOrganizerItemFetchHint(), &latestError, true, storageLocations); QList ids; for (int i = 0; i < items.length(); i++) { ids.append(items[i].id()); } finishRequest(*idFetchReq, latestError, QMap(), QList(), QList(), ids); } void QOrganizerJsonDbRequestThread::handleItemFetchByIdRequest(QOrganizerItemFetchByIdRequest *fetchByIdReq) { QMap errorMap; QOrganizerManager::Error latestError = QOrganizerManager::NoError; m_requestMgr->setActive(fetchByIdReq); QList items; if (!fetchByIdReq->ids().isEmpty()) { QOrganizerJsonDbEngine::StorageLocations storageLocationsNeeded = resolveNeededStorageLocationsForItems(fetchByIdReq->ids()); latestError = checkRequestSpecificStorageLocation(storageLocationsNeeded); if (QOrganizerManager::NoError == latestError) items = m_storage->itemsById(fetchByIdReq->ids(), &errorMap, &latestError, storageLocationsNeeded); } finishRequest(*fetchByIdReq, latestError, errorMap, items); } void QOrganizerJsonDbRequestThread::handleItemFetchForExportRequest(QOrganizerItemFetchForExportRequest *fetchForExportReq) { QOrganizerManager::Error latestError = QOrganizerManager::NoError; m_requestMgr->setActive(fetchForExportReq); // UserDataStorage is default storage location, if not otherwise set QOrganizerJsonDbEngine::StorageLocations storageLocations = QOrganizerJsonDbEngine::UserDataStorage; latestError = checkRequestSpecificStorageLocation(storageLocations); QList items; if (QOrganizerManager::NoError == latestError) items = internalItems(fetchForExportReq->startDate(), fetchForExportReq->endDate(), fetchForExportReq->filter(), fetchForExportReq->sorting(), fetchForExportReq->fetchHint(), &latestError, true, storageLocations); finishRequest(*fetchForExportReq, latestError, QMap(), items); } void QOrganizerJsonDbRequestThread::handleItemOccurrenceFetchRequest(QOrganizerItemOccurrenceFetchRequest *occurrenceFetchReq) { QOrganizerManager::Error latestError = QOrganizerManager::NoError; m_requestMgr->setActive(occurrenceFetchReq); QList items = internalItemOccurrences(occurrenceFetchReq->parentItem(), occurrenceFetchReq->startDate(), occurrenceFetchReq->endDate(), occurrenceFetchReq->fetchHint(), occurrenceFetchReq->maxOccurrences(), true, true, 0, &latestError); finishRequest(*occurrenceFetchReq, latestError, QMap(), items); } void QOrganizerJsonDbRequestThread::handleItemRemoveRequest(QOrganizerItemRemoveRequest *removeReq) { QMap errorMap; QOrganizerManager::Error latestError = QOrganizerManager::NoError; QMultiMap exceptionDates; // map from original remove req items list index to parent id QMap parentIds; // map from original remove req items list index to item id QMap itemIds; // map from parent item list index to parent item QMap modifiedParents; QList items = removeReq->items(); m_requestMgr->setActive(removeReq); QOrganizerItem item; int i; for (i = 0; i < items.size(); i++) { item = items[i]; bool nullItemId = item.id().isNull(); if ((item.type() == QOrganizerItemType::TypeEventOccurrence || item.type() == QOrganizerItemType::TypeTodoOccurrence) && nullItemId) { // Dealing with occurence parent // - check first if the occurence is valid // - check then the storage location defined in id to be available QOrganizerItemParent parentDetail = item.detail(QOrganizerItemDetail::TypeParent); if (!parentDetail.parentId().isNull() && parentDetail.originalDate().isValid()) { if (QOrganizerManager::NoError == checkRequestSpecificStorageLocation( static_cast(QOrganizerManagerEngine::engineItemId(parentDetail.parentId()))->storageLocation())) { // ok, insert to parentIds exceptionDates.insert(parentDetail.parentId(), parentDetail.originalDate()); parentIds.insert(i, parentDetail.parentId()); } else { // invalid storage location latestError = QOrganizerManager::UnspecifiedError; errorMap.insert(i, latestError); } } else { // invalid occurence latestError = QOrganizerManager::InvalidOccurrenceError; errorMap.insert(i, latestError); } } else { // Dealing with normal item // - check first if the id is valid // - check then the storage location defined in id to be available if (!nullItemId) { if (QOrganizerManager::NoError == checkRequestSpecificStorageLocation( static_cast(QOrganizerManagerEngine::engineItemId(item.id()))->storageLocation())) { // ok, insert to itemIds itemIds.insert(i, item.id()); } else { // invalid storage location latestError = QOrganizerManager::UnspecifiedError; errorMap.insert(i, latestError); } } else { // invalid id, since on removal there should not be "new items" latestError = QOrganizerManager::BadArgumentError; errorMap.insert(i, latestError); } } } if (!itemIds.isEmpty() || !parentIds.isEmpty()) { // handle normal events QMap removeErrorMap; QOrganizerManager::Error removeError = QOrganizerManager::NoError; QList itemIdsList = itemIds.values(); removeItems(itemIdsList, &removeError, &removeErrorMap); if (!removeErrorMap.isEmpty()) { int itemIdsValuesIndex = 0; QMap::const_iterator iterator = itemIds.constBegin(); while (iterator != itemIds.constEnd()) { if (removeErrorMap.contains(itemIdsValuesIndex)) { latestError = removeErrorMap.value(itemIdsValuesIndex); errorMap.insert(iterator.key(), latestError); } itemIdsValuesIndex++; } } // check if any of the removed "normal" items was the parent of an occurrence to be removed if (!exceptionDates.isEmpty()) { foreach (const QOrganizerItemId &id, itemIdsList) { if (exceptionDates.contains(id)) exceptionDates.remove(id); } } // handle generated item occurrences: add new exception date to item's parent item recurrence detail QMap fetchErrorMap; QOrganizerManager::Error fetchError = QOrganizerManager::NoError; QOrganizerJsonDbEngine::StorageLocations storageLocationsNeeded = resolveNeededStorageLocationsForItems(exceptionDates.keys()); QList parentItems = m_storage->itemsById(exceptionDates.keys(), &fetchErrorMap, &fetchError, storageLocationsNeeded); for (i = 0; i < parentItems.size(); i++) { if (!fetchErrorMap.contains(i)) { QOrganizerItem parentItem = parentItems[i]; QList addDateList = exceptionDates.values(parentItem.id()); if (addDateList.isEmpty()) continue; QOrganizerItemRecurrence recurrenceDetail = parentItem.detail(QOrganizerItemDetail::TypeRecurrence); QSet exceptionDateSet = recurrenceDetail.exceptionDates(); for (int j = 0; j < addDateList.size(); j++) exceptionDateSet.insert(addDateList[j]); recurrenceDetail.setExceptionDates(exceptionDateSet); parentItem.saveDetail(&recurrenceDetail); modifiedParents.insert(i, parentItem); } } QMap saveErrorMap; QOrganizerManager::Error saveError = QOrganizerManager::NoError; // modifiedParents all are already old items, so the storageLocation-param in saveItems() will be ignored if (!modifiedParents.isEmpty()) m_storage->saveItems(&modifiedParents, &saveErrorMap, &saveError, QOrganizerJsonDbEngine::UserDataStorage); if (!fetchErrorMap.isEmpty() || !saveErrorMap.isEmpty()) { for (i = 0; i < parentItems.size(); i++) { if (fetchErrorMap.contains(i) || saveErrorMap.contains(i)) { QOrganizerItemId parentId = parentItems[i].id(); // find the indexes in the original items list where this parent item was the parent of occurrence QList originalIndexes = parentIds.keys(parentId); for (int j = 0; j < originalIndexes.size(); j++) { latestError = QOrganizerManager::InvalidOccurrenceError; errorMap.insert(j, latestError); } } } } } finishRequest(*removeReq, latestError, errorMap); } void QOrganizerJsonDbRequestThread::handleItemRemoveByIdRequest(QOrganizerItemRemoveByIdRequest *removeByIdReq) { QMap errorMap; QOrganizerManager::Error latestError = QOrganizerManager::NoError; QList itemIds = removeByIdReq->itemIds(); m_requestMgr->setActive(removeByIdReq); QList items; if (!itemIds.isEmpty()) { // FIXME: something to combine with validation work bool validItemIds = false; for (int i = 0; i < itemIds.size(); i++) { const QOrganizerItemId &itemId = itemIds.at(i); if (!itemId.isNull()) { validItemIds = true; break; } } if (validItemIds) { QOrganizerJsonDbEngine::StorageLocations storageLocationsNeeded = resolveNeededStorageLocationsForItems(itemIds); latestError = checkRequestSpecificStorageLocation(storageLocationsNeeded); if (QOrganizerManager::NoError == latestError) removeItems(itemIds, &latestError, &errorMap); } else { latestError = QOrganizerManager::DoesNotExistError; } } finishRequest(*removeByIdReq, latestError, errorMap); } void QOrganizerJsonDbRequestThread::handleCollectionSaveRequest(QOrganizerCollectionSaveRequest *collectionSaveReq) { QMap collectionMap; QMap collectionIsNewStatusMap; QMap errorMap; QOrganizerManager::Error latestError = QOrganizerManager::NoError; QList collections = collectionSaveReq->collections(); m_requestMgr->setActive(collectionSaveReq); // if not defined, backend decides the default storage location -> UserDataStorage const QOrganizerJsonDbEngine::StorageLocation storageLocation = QOrganizerJsonDbEngine::UserDataStorage; for (int i = 0; i < collections.size(); i++) { QOrganizerCollection collection = collections.at(i); bool collectionIsNew = collection.id().isNull(); bool errorFound = false; if (!collectionIsNew) { const QString managerUri = QOrganizerManager::buildUri(m_engine->managerName(), m_engine->managerParameters()); if (managerUri != collection.id().managerUri()) {// check manager uri if is the same with the engine uri latestError = QOrganizerManager::BadArgumentError; errorFound = true; } } // Check the request is targeted to available storage location. // Storage location errors are prioritised over less important other possible errors. if (!errorFound) { QOrganizerManager::Error error = QOrganizerManager::NoError; if (collectionIsNew) error = checkRequestSpecificStorageLocation(storageLocation); else error = checkRequestSpecificStorageLocation(static_cast(QOrganizerManagerEngine::engineCollectionId(collection.id()))->storageLocation()); if (QOrganizerManager::NoError != error) { latestError = error; errorFound = true; } } if (errorFound) { errorMap.insert(i, latestError); } else { collectionMap.insert(i, collection); collectionIsNewStatusMap.insert(i, collectionIsNew); } } if (!collectionMap.isEmpty()) { m_storage->saveCollections(&collectionMap, &errorMap, &latestError, QOrganizerJsonDbEngine::UserDataStorage); QMap::const_iterator i = collectionMap.constBegin(); while (i != collectionMap.constEnd()) { if (!errorMap.contains(i.key())) { if (collectionIsNewStatusMap.value(i.key())) collections.replace(i.key(), i.value()); } ++i; } } finishRequest(*collectionSaveReq, latestError, errorMap, QList(), collections); } void QOrganizerJsonDbRequestThread::handleCollectionFetchRequest(QOrganizerCollectionFetchRequest *collectionFetchReq) { m_requestMgr->setActive(collectionFetchReq); QOrganizerManager::Error latestError = QOrganizerManager::NoError; // UserDataStorage is default storage location, if not otherwise set QOrganizerJsonDbEngine::StorageLocations storageLocations = QOrganizerJsonDbEngine::UserDataStorage; latestError = checkRequestSpecificStorageLocation(storageLocations); QList collections; if (QOrganizerManager::NoError == latestError) collections = m_storage->collections(&latestError, storageLocations); finishRequest(*collectionFetchReq, latestError, QMap(), QList(), collections); } void QOrganizerJsonDbRequestThread::handleCollectionRemoveRequest(QOrganizerCollectionRemoveRequest* collectionRemoveReq) { QMap errorMap; QOrganizerManager::Error latestError = QOrganizerManager::NoError; QList collectionIds = collectionRemoveReq->collectionIds(); m_requestMgr->setActive(collectionRemoveReq); if (!collectionIds.isEmpty()) { QOrganizerJsonDbEngine::StorageLocations storageLocationsNeeded = resolveNeededStorageLocationsForCollections(collectionIds); latestError = checkRequestSpecificStorageLocation(storageLocationsNeeded); if (QOrganizerManager::NoError == latestError) { // only contain valid ones, i.e. default collection, empty, non-existing ones are removed QMap validCollectionIds; for (int i = 0; i < collectionIds.size(); ++i) { if (collectionIds.at(i) == m_storage->defaultCollection().id()) { latestError = QOrganizerManager::PermissionsError; errorMap.insert(i, latestError); qWarning() << QOrganizerJsonDbStr::warningDefaultCollectionRemove(); } else if (m_storage->collectionIds().contains(collectionIds.at(i))) { validCollectionIds.insert(i, collectionIds.at(i)); } else { latestError = QOrganizerManager::BadArgumentError; errorMap.insert(i, latestError); } } int errorCount = errorMap.size(); if (!validCollectionIds.isEmpty()) { m_storage->removeCollections(validCollectionIds, &errorMap, &latestError); // either all removed, or none removed if (errorCount == errorMap.size()) { // remove all items in those collections QOrganizerItemCollectionFilter collectonFilter; collectonFilter.setCollectionIds(QSet::fromList(validCollectionIds.values())); QOrganizerJsonDbEngine::StorageLocations storageLocationsNeeded = resolveNeededStorageLocationsForCollections(validCollectionIds.values()); QList items = m_storage->items(QDateTime(), QDateTime(), collectonFilter, QList(), QOrganizerItemFetchHint(), &latestError, storageLocationsNeeded, QOrganizerJsonDbDataStorage::FetchItemIds); QList itemIds; for (int i = 0; i < items.length(); ++i) itemIds.append(items.at(i).id()); removeItems(itemIds, &latestError, &errorMap); } } } } finishRequest(*collectionRemoveReq, latestError, errorMap); } void QOrganizerJsonDbRequestThread::initDefaultCollection() { QOrganizerCollection defaultCollection = m_storage->defaultCollection(); if (defaultCollection.id().isNull()) { QOrganizerManager::Error error; defaultCollection.setMetaData(QOrganizerCollection::KeyName, QOrganizerJsonDbStr::defaultCollectionDisplayName()); m_storage->createDefaultCollection(&defaultCollection, &error); } } // Save helpers QOrganizerItem QOrganizerJsonDbRequestThread::fetchParentItem(const QOrganizerItem &occurrence) { QList tmpParentItems; QList parentItemIdList; QMap parentErrorMap; QOrganizerManager::Error parentError = QOrganizerManager::NoError; QOrganizerItemParent parentDetail = occurrence.detail(QOrganizerItemDetail::TypeParent); if (!parentDetail.isEmpty() && parentDetail.hasValue(QOrganizerItemParent::FieldParentId)) { parentItemIdList.append(parentDetail.parentId()); QOrganizerJsonDbEngine::StorageLocations storageLocationsNeeded = resolveNeededStorageLocationsForItems(parentItemIdList); tmpParentItems = m_storage->itemsById(parentItemIdList, &parentErrorMap, &parentError, storageLocationsNeeded); if (parentError == QOrganizerManager::NoError && tmpParentItems.length() > 0) return tmpParentItems[0]; } // if parent was not found based on id, try with guid if (!occurrence.guid().isEmpty()) { QOrganizerItemDetailFieldFilter guidFilter; guidFilter.setDetail(QOrganizerItemDetail::TypeGuid, QOrganizerItemGuid::FieldGuid); guidFilter.setValue(occurrence.guid()); parentError = QOrganizerManager::NoError; QOrganizerJsonDbEngine::StorageLocations allLocations(QOrganizerJsonDbEngine::UserDataStorage | QOrganizerJsonDbEngine::SystemStorage); tmpParentItems = m_storage->items(QDateTime(), QDateTime(), guidFilter, QList(), QOrganizerItemFetchHint(), &parentError, allLocations); if (parentError == QOrganizerManager::NoError && tmpParentItems.length() > 0) return tmpParentItems[0]; } return QOrganizerItem(); } /*! Returns true if and only if \a occurrenceType is the "Occurrence" version of \a parentType. */ bool QOrganizerJsonDbRequestThread::typesAreRelated(QOrganizerItemType::ItemType occurrenceType, QOrganizerItemType::ItemType parentType) { return ((parentType == QOrganizerItemType::TypeEvent && occurrenceType == QOrganizerItemType::TypeEventOccurrence) || (parentType == QOrganizerItemType::TypeTodo && occurrenceType == QOrganizerItemType::TypeTodoOccurrence)); } bool QOrganizerJsonDbRequestThread::fixParentReferences(QOrganizerItem *item, const QOrganizerItem &parentItem) { // bool itemIsOccurrence = !parentItem.isEmpty(); QOrganizerItemParent parentDetail = item->detail(QOrganizerItemDetail::TypeParent); if (!parentDetail.hasValue(QOrganizerItemParent::FieldOriginalDate)) return false; if (!parentDetail.hasValue(QOrganizerItemParent::FieldParentId)) { parentDetail.setParentId(parentItem.id()); item->saveDetail(&parentDetail); } return true; } bool QOrganizerJsonDbRequestThread::fixGuidReferences(QOrganizerItem *item, const QOrganizerItem &parentItem) { bool itemIsOccurrence = !parentItem.isEmpty(); if (!itemIsOccurrence && item->guid().isEmpty()) { item->setGuid(QUuid::createUuid().toString()); return true; } if (itemIsOccurrence) { // item is an occurrence if (parentItem.guid().isEmpty()) { return false; } else if (item->guid().isEmpty()) { item->setGuid(parentItem.guid()); return true; } else if (item->guid() != parentItem.guid()) { return false; } } return true; } bool QOrganizerJsonDbRequestThread::fixCollectionReferences(QOrganizerItem *item, const QOrganizerItem &parentItem, bool itemIsNew, QOrganizerJsonDbEngine::StorageLocation storageLocation) { bool itemIsOccurrence = !parentItem.isEmpty(); QOrganizerCollectionId collectionId = item->collectionId(); QOrganizerCollectionId parentCollectionId = parentItem.collectionId(); if (itemIsOccurrence && (parentCollectionId.isNull() || !m_storage->collectionIds().contains(parentCollectionId))) return false; if (!collectionId.isNull()) { //If we could find the collection id in collection id list if (!m_storage->collectionIds().contains(collectionId)) return false; if (itemIsOccurrence) { // Does this occurrence have different collection id than it's parent if (collectionId != parentCollectionId) return false; } } else { if (itemIsOccurrence) item->setCollectionId(parentCollectionId); else item->setCollectionId(m_storage->defaultCollection().id()); } const QOrganizerJsonDbCollectionId *collectionIdPtr = static_cast(QOrganizerManagerEngine::engineCollectionId(item->collectionId())); const QOrganizerJsonDbItemId *itemIdPtr = static_cast(QOrganizerManagerEngine::engineItemId(item->id())); if (!itemIdPtr && !itemIsNew) return false; QOrganizerJsonDbEngine::StorageLocation collectionStorageLocation; if (collectionIdPtr) collectionStorageLocation = collectionIdPtr->storageLocation(); else return false; if (itemIsNew && storageLocation != collectionStorageLocation) return false; else if (!itemIsNew && itemIdPtr->storageLocation() != collectionStorageLocation) return false; return true; } // Fetch helpers QList QOrganizerJsonDbRequestThread::internalItems(const QDateTime& startDate, const QDateTime& endDate, const QOrganizerItemFilter& filter, const QList& sortOrders, const QOrganizerItemFetchHint& fetchHint, QOrganizerManager::Error* error, bool forExport, QOrganizerJsonDbEngine::StorageLocations storageLocations) const { QList timeUndefined; QSet parentsAdded; QSet parentsToBeAdded; QMultiMap defaultSorted; QList sorted; QMap > exceptionDateMap; bool isDefaultFilter = (filter.type() == QOrganizerItemFilter::DefaultFilter); // fetch all parents stored to db QList parentItems = m_storage->items(QDateTime(), QDateTime(), QOrganizerItemFilter(), QList(), fetchHint, error, storageLocations, QOrganizerJsonDbDataStorage::FetchParents); if (QOrganizerManager::NoError != *error) return sorted; // fetch all items (normal items and exception occurrences) stored in the given time period // apply filters QList items = m_storage->items(startDate, endDate, filter, sortOrders, fetchHint, error, storageLocations, QOrganizerJsonDbDataStorage::FetchItems); // generate occurrences for all parent items foreach (QOrganizerItem parent, parentItems) { if (!parent.detail(QOrganizerItemDetail::TypeRecurrence).isEmpty()) { QOrganizerManager::Error recError = QOrganizerManager::NoError; QList exceptionDates; QList recItems = internalItemOccurrences(parent, startDate, endDate, fetchHint, forExport ? 1 : QOrganizerJsonDbRequestThread::MaxOccurrenceCount, false, false, &exceptionDates, &recError); if (!exceptionDates.isEmpty()) exceptionDateMap.insert(parent.id(), exceptionDates); QOrganizerItem toAdd; foreach (const QOrganizerItem &occurrence, recItems) { if (!isDefaultFilter) { if (!QOrganizerManagerEngine::testFilter(filter, occurrence)) continue; } // if forExport is true, this loop is executed max. once if (forExport) { parentsAdded.insert(parent.id()); toAdd = parent; } else { toAdd = occurrence; } if (sortOrders.isEmpty()) { if (!QOrganizerManagerEngine::addDefaultSorted(&defaultSorted, toAdd)) timeUndefined.append(toAdd); } else { QOrganizerManagerEngine::addSorted(&sorted, toAdd, sortOrders); } } } } // add all normal items and exception occurrences to return list foreach (const QOrganizerItem &item, items) { // this is either Event or Todo // or exception EventOccurrence or TodoOccurrence which has been stored to database if (!item.detail(QOrganizerItemDetail::TypeRecurrence).isEmpty()) { // parent items have already been handled continue; } QOrganizerItemParent parentDetail = item.detail(QOrganizerItemDetail::TypeParent); if (!parentDetail.isEmpty() && !exceptionDateMap.value(parentDetail.parentId()).contains(parentDetail.originalDate())) continue; if (forExport && !parentDetail.isEmpty()) { QOrganizerItemId parentId = parentDetail.parentId(); if (!parentId.isNull()) parentsToBeAdded.insert(parentId); } if (sortOrders.isEmpty()) { if (!QOrganizerManagerEngine::addDefaultSorted(&defaultSorted, item)) timeUndefined.append(item); } else { QOrganizerManagerEngine::addSorted(&sorted, item, sortOrders); } } if (forExport && !parentsToBeAdded.isEmpty()) { foreach (QOrganizerItem item, parentItems) { if (parentsToBeAdded.contains(item.id()) && !parentsAdded.contains(item.id())) { if (sortOrders.isEmpty()) QOrganizerManagerEngine::addDefaultSorted(&defaultSorted, item); else QOrganizerManagerEngine::addSorted(&sorted, item, sortOrders); } } } if (sortOrders.isEmpty()) { sorted = defaultSorted.values(); sorted.append(timeUndefined); } return sorted; } QList QOrganizerJsonDbRequestThread::internalItemOccurrences(const QOrganizerItem &parentItem, const QDateTime &periodStart, const QDateTime &periodEnd, const QOrganizerItemFetchHint &fetchHint, int maxCount, bool includeExceptions, bool sortItems, QList *exceptionDates, QOrganizerManager::Error *error) const { // given the generating item, grab it's QOrganizerItemRecurrence detail (if it exists), and calculate all of the dates within the given period. QDateTime realPeriodStart(periodStart); QDateTime realPeriodEnd(periodEnd); QDateTime initialDateTime; if (parentItem.type() == QOrganizerItemType::TypeEvent) { QOrganizerEvent evt = parentItem; initialDateTime = evt.startDateTime(); } else if (parentItem.type() == QOrganizerItemType::TypeTodo) { QOrganizerTodo todo = parentItem; initialDateTime = todo.startDateTime().isValid() ? todo.startDateTime() : todo.dueDateTime(); } else { // erm... not a recurring item in our schema... return QList(); } if (realPeriodStart.isValid() && initialDateTime.isValid()) { if (initialDateTime > realPeriodStart) realPeriodStart = initialDateTime; } else if (initialDateTime.isValid()) { realPeriodStart = initialDateTime; } if (!periodEnd.isValid()) { // If no endDateTime is given, we'll only generate items that occur within the next 4 years of realPeriodStart. if (realPeriodStart.isValid()) realPeriodEnd = realPeriodStart.addDays(QOrganizerJsonDbRequestThread::DefaultTimePeriod); else realPeriodEnd = QDateTime::currentDateTimeUtc().addDays(QOrganizerJsonDbRequestThread::DefaultTimePeriod); } if (realPeriodStart > realPeriodEnd) { *error = QOrganizerManager::BadArgumentError; return QList(); } QList retn; QList xoccurrences; QOrganizerItemRecurrence recur = parentItem.detail(QOrganizerItemDetail::TypeRecurrence); if (includeExceptions) { // first, retrieve all persisted instances (exceptions) which occur between the specified datetimes. QOrganizerItemFilter filter; QOrganizerItemSortOrder sortOrder; QOrganizerJsonDbEngine::StorageLocations storageLocationsNeeded = resolveNeededStorageLocationsForItems(QList()<items(realPeriodStart, realPeriodEnd, filter, sortOrder, fetchHint, error, storageLocationsNeeded, QOrganizerJsonDbDataStorage::FetchItemOccurrences, parentItem.id()); } // then, generate the required (unchanged) instances from the parentItem. // before doing that, we have to find out all of the exception dates. QList xdates; foreach (const QDate &xdate, recur.exceptionDates()) xdates += xdate; if (realPeriodStart.isValid()) { QSet xrules = recur.exceptionRules(); foreach (const QOrganizerRecurrenceRule& xrule, xrules) { if (xrule.frequency() != QOrganizerRecurrenceRule::Invalid && ((xrule.limitType() != QOrganizerRecurrenceRule::DateLimit) || (xrule.limitDate() >= realPeriodStart.date()))) { // we cannot skip it, since it applies in the given time period. QList xdatetimes = QOrganizerManagerEngine::generateDateTimes(initialDateTime, xrule, realPeriodStart, realPeriodEnd, QOrganizerJsonDbRequestThread::MaxOccurrenceCount); foreach (const QDateTime& xdatetime, xdatetimes) xdates += xdatetime.date(); } } } // now generate a list of rdates (from the recurrenceDates and recurrenceRules) // QMap is used for storing dates, because we don't want to have duplicate dates and // we want to have dates sorted // Only key of the map is relevant (QDateTime), the value (int) is not used QMap rdateMap; foreach (const QDate& rdate, recur.recurrenceDates()) rdateMap.insert(QDateTime(rdate, initialDateTime.time()), 0); bool hasValidRule = false; if (realPeriodStart.isValid()) { QSet rrules = recur.recurrenceRules(); foreach (const QOrganizerRecurrenceRule& rrule, rrules) { if (rrule.frequency() != QOrganizerRecurrenceRule::Invalid) { hasValidRule = true; if ((rrule.limitType() != QOrganizerRecurrenceRule::DateLimit) || (rrule.limitDate() >= realPeriodStart.date())) { // we cannot skip it, since it applies in the given time period. QList rdatetimes = QOrganizerManagerEngine::generateDateTimes(initialDateTime, rrule, realPeriodStart, realPeriodEnd, QOrganizerJsonDbRequestThread::MaxOccurrenceCount); foreach (const QDateTime& rdatetime, rdatetimes) rdateMap.insert(rdatetime, 0); } } } } // now order the contents of retn by date QList rdates = rdateMap.keys(); if (!hasValidRule && initialDateTime.isValid() && qBinaryFind(rdates, initialDateTime) == rdates.constEnd()) { rdates.prepend(initialDateTime); } // now for each rdate which isn't also an xdate foreach (const QDateTime& rdate, rdates) { if ((rdate >= realPeriodStart && rdate <= realPeriodEnd) || (!realPeriodStart.isValid() && !realPeriodEnd.isValid() && rdate.isValid())) { if (!xdates.contains(rdate.date())) { // generate the required instance and add it to the return list. retn.append(QOrganizerManagerEngine::generateOccurrence(parentItem, rdate)); } else if (includeExceptions) { for (int i = 0; i < xoccurrences.size(); i++) { QOrganizerItemParent parentDetail = xoccurrences[i].detail(QOrganizerItemDetail::TypeParent); if (parentDetail.originalDate() == rdate.date()) retn.append(xoccurrences[i]); } } else if (exceptionDates) { exceptionDates->append(rdate.date()); } } } if (sortItems) { // should we always sort if a maxCount is given? QMultiMap defaultSorted; foreach (QOrganizerItem item, retn) QOrganizerManagerEngine::addDefaultSorted(&defaultSorted, item); retn = defaultSorted.values(); } // and return the first maxCount entries. return retn.mid(0, maxCount); } void QOrganizerJsonDbRequestThread::removeItems(const QList &itemIds, QOrganizerManager::Error *error, QMap *errorMap) { QMap tmpErrorMap; QOrganizerManager::Error tmpError = QOrganizerManager::NoError; QList removedParentIds; QList occurrenceIds; if (!itemIds.isEmpty()) { // fetch items to find out if there are any persisted occurrences or parent items among them QOrganizerJsonDbEngine::StorageLocations storageLocationsNeeded = resolveNeededStorageLocationsForItems(itemIds); QList items = m_storage->itemsById(itemIds, &tmpErrorMap, &tmpError, storageLocationsNeeded); foreach (QOrganizerItem item, items) { if ((item.type() == QOrganizerItemType::TypeEvent || item.type() == QOrganizerItemType::TypeTodo) && !item.detail(QOrganizerItemDetail::TypeRecurrence).isEmpty()) removedParentIds.append(item.id()); } m_storage->removeItems(itemIds, errorMap, error); // remove all persisted occurrences of removed parent items if (!removedParentIds.isEmpty()) { // get all exception occurrence ids QOrganizerItemUnionFilter unionFilter; for (int i = 0; i < removedParentIds.size(); i++) { QOrganizerItemDetailFieldFilter detailFieldFilter; detailFieldFilter.setDetail(QOrganizerItemDetail::TypeParent, QOrganizerItemParent::FieldParentId); detailFieldFilter.setValue(QVariant::fromValue(removedParentIds[i])); unionFilter.append(detailFieldFilter); } tmpError = QOrganizerManager::NoError; QOrganizerJsonDbEngine::StorageLocations storageLocationsNeeded = resolveNeededStorageLocationsForItems(removedParentIds); QList occurrences = m_storage->items(QDateTime(), QDateTime(), unionFilter, QList(), QOrganizerItemFetchHint(), &tmpError, storageLocationsNeeded); for (int j = 0; j < occurrences.size(); j++) { occurrenceIds.append(occurrences[j].id()); } } tmpErrorMap.clear(); tmpError = QOrganizerManager::NoError; m_storage->removeItems(occurrenceIds, &tmpErrorMap, &tmpError); } } QOrganizerJsonDbEngine::StorageLocations QOrganizerJsonDbRequestThread::resolveNeededStorageLocationsForItems(const QList &itemIds) const { // figure out wich storage locations are needed based on items const QOrganizerJsonDbEngine::StorageLocations availableStorageLocations = m_storage->availableStorageLocationsFlag(); QOrganizerJsonDbEngine::StorageLocations storageLocationsNeeded(0); foreach (QOrganizerItemId id, itemIds) { const QOrganizerJsonDbItemId *engineId = static_cast(QOrganizerManagerEngine::engineItemId(id)); if (!engineId) continue; const QOrganizerJsonDbEngine::StorageLocations locations = engineId->storageLocation(); if (locations > 0) { if (locations & QOrganizerJsonDbEngine::UserDataStorage) storageLocationsNeeded |= QOrganizerJsonDbEngine::UserDataStorage; else if (locations & QOrganizerJsonDbEngine::SystemStorage) storageLocationsNeeded |= QOrganizerJsonDbEngine::SystemStorage; if (storageLocationsNeeded == availableStorageLocations) break; } } return storageLocationsNeeded; } QOrganizerJsonDbEngine::StorageLocations QOrganizerJsonDbRequestThread::resolveNeededStorageLocationsForCollections(const QList &collectionIds) const { // figure out wich storage locations are needed based on collections const QOrganizerJsonDbEngine::StorageLocations availableStorageLocations = m_storage->availableStorageLocationsFlag(); QOrganizerJsonDbEngine::StorageLocations storageLocationsNeeded(0); foreach (QOrganizerCollectionId id, collectionIds) { const QOrganizerJsonDbCollectionId *engineId = static_cast(QOrganizerManagerEngine::engineCollectionId(id)); if (!engineId) continue; const QOrganizerJsonDbEngine::StorageLocations locations = engineId->storageLocation(); if (locations > 0) { if (locations & QOrganizerJsonDbEngine::UserDataStorage) storageLocationsNeeded |= QOrganizerJsonDbEngine::UserDataStorage; else if (locations & QOrganizerJsonDbEngine::SystemStorage) storageLocationsNeeded |= QOrganizerJsonDbEngine::SystemStorage; if (storageLocationsNeeded == availableStorageLocations) break; } } return storageLocationsNeeded; } QOrganizerManager::Error QOrganizerJsonDbRequestThread::checkRequestSpecificStorageLocation(const QOrganizerJsonDbEngine::StorageLocations &requestSpecificStorageLocations) { // Check the request is targeted to available storage location. const QOrganizerJsonDbEngine::StorageLocations availableStoragelocations = m_storage->availableStorageLocationsFlag(); if (requestSpecificStorageLocations && ((requestSpecificStorageLocations | availableStoragelocations) == availableStoragelocations)) return QOrganizerManager::NoError; else return QOrganizerManager::UnspecifiedError; } #include "moc_qorganizerjsondbrequestthread.cpp" QT_END_NAMESPACE_ORGANIZER src/plugins/organizer/jsondb/qorganizerjsondbrequestthread.h000066400000000000000000000202211233466112000251330ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERJSONDBREQUESTTHREAD_H #define QORGANIZERJSONDBREQUESTTHREAD_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include "qorganizerjsondbengine.h" QT_FORWARD_DECLARE_CLASS(QMutex) QT_FORWARD_DECLARE_CLASS(QTimer) QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerJsonDbRequestManager; class QOrganizerJsonDbEngine; class QOrganizerJsonDbDataStorage; class QOrganizerJsonDbRequestThread : public QThread { Q_OBJECT public: QOrganizerJsonDbRequestThread(); virtual ~QOrganizerJsonDbRequestThread(); void setEngine(QOrganizerJsonDbEngine* engine); void addRequest(QOrganizerAbstractRequest* req); bool waitForRequestFinished(QOrganizerAbstractRequest* req, int msecs); void requestDestroyed(QOrganizerAbstractRequest* req); QOrganizerCollection defaultCollection() const; public slots: void handleRequest(QOrganizerAbstractRequest* req); signals: void initialized(); protected: virtual void run(); private slots: // Since these three slots are triggered either by signals from data storage for notifications // from JsonDb, or by the timer, and they are executed only in this thread, no mutex is needed. void onItemAdded(const QOrganizerItemId &itemId); void onItemChanged(const QOrganizerItemId &itemId); void onItemRemoved(const QOrganizerItemId &itemId); void onCollectionAdded(const QOrganizerCollectionId &collectionId); void onCollectionChanged(const QOrganizerCollectionId &collectionId); void onCollectionRemoved(const QOrganizerCollectionId &collectionId); void onTimeout(); private: void handleItemSaveRequest(QOrganizerItemSaveRequest* saveReq); void handleItemFetchRequest(QOrganizerItemFetchRequest* fetchReq); void handleItemIdFetchRequest(QOrganizerItemIdFetchRequest* idFetchReq); void handleItemFetchByIdRequest(QOrganizerItemFetchByIdRequest* fetchByIdReq); void handleItemFetchForExportRequest(QOrganizerItemFetchForExportRequest *fetchForExportReq); void handleItemOccurrenceFetchRequest(QOrganizerItemOccurrenceFetchRequest* occurrenceFetchReq); void handleItemRemoveRequest(QOrganizerItemRemoveRequest* removeReq); void handleItemRemoveByIdRequest(QOrganizerItemRemoveByIdRequest *removeByIdReq); void handleCollectionSaveRequest(QOrganizerCollectionSaveRequest* collectionSaveReq); void handleCollectionFetchRequest(QOrganizerCollectionFetchRequest* collectionFetchReq); void handleCollectionRemoveRequest(QOrganizerCollectionRemoveRequest* collectionRemoveReq); void initDefaultCollection(); void finishRequest(QOrganizerAbstractRequest &request, QOrganizerManager::Error latestError, const QMap &errorMap, const QList &itemList = QList(), const QList &collectionList = QList(), const QList &itemIdList = QList()); QOrganizerItem fetchParentItem(const QOrganizerItem &occurrence); bool typesAreRelated(QOrganizerItemType::ItemType occurrenceType, QOrganizerItemType::ItemType parentType); bool fixParentReferences(QOrganizerItem *item, const QOrganizerItem &parentItem); bool fixGuidReferences(QOrganizerItem *item, const QOrganizerItem &parentItem); bool fixCollectionReferences(QOrganizerItem *item, const QOrganizerItem &parentItem, bool itemIsNew, QOrganizerJsonDbEngine::StorageLocation storageLocation); QList internalItems(const QDateTime &startDate, const QDateTime &endDate, const QOrganizerItemFilter &filter, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error, bool forExport, QOrganizerJsonDbEngine::StorageLocations storageLocations) const; QList internalItemOccurrences(const QOrganizerItem &parentItem, const QDateTime &periodStart, const QDateTime &periodEnd, const QOrganizerItemFetchHint &fetchHint, int maxCount, bool includeExceptions, bool sortItems, QList *exceptionDates, QOrganizerManager::Error *error) const; void removeItems(const QList &itemIds, QOrganizerManager::Error *error, QMap *errorMap); void removeAlarmObjects(const QList &itemIds, const QMap &errorMap); bool validRequest(QOrganizerAbstractRequest *req); bool validPlatform(QOrganizerAbstractRequest *req); QOrganizerManager::Error checkRequestSpecificStorageLocation(const QOrganizerJsonDbEngine::StorageLocations &requestSpecificStorageLocations); QOrganizerJsonDbEngine::StorageLocations resolveNeededStorageLocationsForItems(const QList &itemIds) const; QOrganizerJsonDbEngine::StorageLocations resolveNeededStorageLocationsForCollections(const QList &collectionIds) const; // Member variables QOrganizerJsonDbEngine* m_engine; QOrganizerJsonDbDataStorage* m_storage; QOrganizerJsonDbRequestManager* m_requestMgr; //Mutex to make the request state changes atomic QMutex* m_reqStateMutex; // Handle item / collection changes // They are only used by the notification system, so no mutex is needed. static const int TIMEOUT_INTERVAL; QTimer *m_timer; QOrganizerItemChangeSet m_ics; QOrganizerCollectionChangeSet m_ccs; void startTimer(); // Only used by onItemChanged() and onCollectionChanged() // constants for generating occurrences // number of days to use as time period for generating occurrences if no period is defined static const int DefaultTimePeriod; static const int MaxOccurrenceCount; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERJSONDBREQUESTTHREAD_H src/plugins/organizer/jsondb/qorganizerjsondbstring.cpp000066400000000000000000000037321233466112000241240ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizerjsondbstring.h" src/plugins/organizer/jsondb/qorganizerjsondbstring.h000066400000000000000000000335311233466112000235710ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERJSONDBSTRING_H #define QORGANIZERJSONDBSTRING_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerJsonDbStr { public: // JsonDb manager name inline const static QString jsonDbManagerUri() { return QStringLiteral("qtorganizer:jsondb:"); } inline const static QString jsonDbManagerName() { return QStringLiteral("jsondb"); } inline const static QString jsonDbSchemaPrefix() { return QStringLiteral("com.nokia.mt.organizer."); } // JsonDb object property names inline const static QString jsonDbUuid() { return QStringLiteral("_uuid"); } inline const static QString jsonDbType() { return QStringLiteral("_type"); } inline const static QString jsonDbData() { return QStringLiteral("data"); } inline const static QString jsonDbCount() { return QStringLiteral("count"); } inline const static QString jsonDbError() { return QStringLiteral("error"); } inline const static QString jsonDbCode() { return QStringLiteral("code"); } inline const static QString jsonDbVersion() {return QStringLiteral("_version"); } inline const static QString jsonDbValue() { return QStringLiteral("value"); } // JsonDb query strings inline const static QString jsonDbQueryAllItems() { return QStringLiteral("[?_type in [\"com.nokia.mt.organizer.Event\", \"com.nokia.mt.organizer.Todo\", \"com.nokia.mt.organizer.EventOccurrence\", \"com.nokia.mt.organizer.TodoOccurrence\"]]"); } inline const static QString jsonDbQueryAllEventItems() { return QStringLiteral("[?_type in [\"com.nokia.mt.organizer.Event\", \"com.nokia.mt.organizer.EventOccurrence\"]]"); } inline const static QString jsonDbQueryEventTypeItems() { return QStringLiteral("[?_type=\"com.nokia.mt.organizer.Event\"]"); } inline const static QString jsonDbQueryEventOccurenceTypeItems() { return QStringLiteral("[?_type=\"com.nokia.mt.organizer.EventOccurrence\"]"); } inline const static QString jsonDbQueryAllTodoItems() { return QStringLiteral("[?_type in [\"com.nokia.mt.organizer.Todo\", \"com.nokia.mt.organizer.TodoOccurrence\"]]"); } inline const static QString jsonDbQueryTodoTypeItems() { return QStringLiteral("[?_type=\"com.nokia.mt.organizer.Todo\"]"); } inline const static QString jsonDbQueryTodoOccurenceTypeItems() { return QStringLiteral("[?_type=\"com.nokia.mt.organizer.TodoOccurrence\"]"); } inline const static QString jsonDbQueryAllCollections() { return QStringLiteral("[?_type=\"com.nokia.mt.organizer.Collection\"]"); } inline const static QString jsonDbQueryParentItems() { return QStringLiteral("[?_type in [\"com.nokia.mt.organizer.Event\", \"com.nokia.mt.organizer.Todo\"]][?recurrenceDates exists | recurrenceRules exists | exceptionDates exists | exceptionRules exists]"); } inline const static QString jsonDbQueryOccurrenceItems() { return QStringLiteral("[?_type in [\"com.nokia.mt.organizer.EventOccurrence\", \"com.nokia.mt.organizer.TodoOccurrence\"]]"); } inline const static QString jsonDbQueryOccurrenceItemsByParent() { return QStringLiteral("[?_type in [\"com.nokia.mt.organizer.EventOccurrence\", \"com.nokia.mt.organizer.TodoOccurrence\"]][?parentUuid = \"%1\"]"); } inline const static QString jsonDbQueryEventViews() { return QStringLiteral("[?_type=\"com.nokia.mt.organizer.EventView\"]"); } inline const static QString jsonDbQueryEventViewParentItems() { return QStringLiteral("[?_type=\"com.nokia.mt.organizer.EventView\"][?value.recurrenceRules exists]"); } inline const static QString jsonDbQueryCollectionUuidsTemplate() { return QStringLiteral("[?collectionUuid in [%1]]"); } inline const static QString jsonDbQueryUuidsTemplate() { return QStringLiteral("[?_uuid in [%1]]"); } inline const static QString jsonDbNotificationQuery() { return QStringLiteral("[?_type in [\"com.nokia.mt.organizer.Event\", \"com.nokia.mt.organizer.EventView\", \"com.nokia.mt.organizer.EventOccurrence\", \"com.nokia.mt.organizer.Todo\", \"com.nokia.mt.organizer.TodoOccurrence\", \"com.nokia.mt.organizer.Collection\"]]"); } inline const static QString jsonDbQueryEventStartDateTimeTemplate() { return QStringLiteral("[?startDateTime<=\"%1\"]"); } inline const static QString jsonDbQueryEventEndDateTimeTemplate() { return QStringLiteral("[?endDateTime>=\"%1\"]"); } // collections inline const static QString jsonDbCollectionType() { return QStringLiteral("com.nokia.mt.organizer.Collection"); } inline const static QString collectionDefaultFlag() { return QStringLiteral("isDefault"); } inline const static QString collectionDisplayName() { return QStringLiteral("displayName"); } inline const static QString collectionDescription() { return QStringLiteral("description"); } inline const static QString collectionColor() { return QStringLiteral("color"); } inline const static QString collectionImageUrl() { return QStringLiteral("imageUrl"); } inline const static QString defaultCollectionDisplayName() { return QStringLiteral("defaultCollection"); } // items inline const static QString itemCollectionUuid() { return QStringLiteral("collectionUuid"); } inline const static QString itemComments() { return QStringLiteral("comments"); } inline const static QString itemDescription() { return QStringLiteral("description"); } inline const static QString itemDisplayName() { return QStringLiteral("displayName"); } inline const static QString itemGuid() { return QStringLiteral("guid"); } inline const static QString itemTags() { return QStringLiteral("tags"); } inline const static QString itemPriority() { return QStringLiteral("priority"); } inline const static QString itemRecurrenceDates() { return QStringLiteral("recurrenceDates"); } inline const static QString itemRecurrenceRules() { return QStringLiteral("recurrenceRules"); } inline const static QString itemExceptionDates() { return QStringLiteral("exceptionDates"); } inline const static QString itemExceptionRules() { return QStringLiteral("exceptionRules"); } // item reminder inline const static QString itemReminder() { return QStringLiteral("reminder"); } inline const static QString itemReminderSecBeforeStart() { return QStringLiteral("secondsBeforeStart"); } inline const static QString itemReminderRepetitionCount() { return QStringLiteral("repetitionCount"); } inline const static QString itemReminderRepetitionDelay() { return QStringLiteral("repetitionDelay"); } inline const static QString itemReminderDataUrl() { return QStringLiteral("dataUrl"); } // item recurrence rules inline const static QString itemRecurrenceRuleFirstDayOfWeek() { return QStringLiteral("firstDayOfWeek"); } inline const static QString itemRecurrenceRuleMonthsOfYear() { return QStringLiteral("monthsOfYear"); } inline const static QString itemRecurrenceRuleFrequency() { return QStringLiteral("frequency"); } inline const static QString itemRecurrenceRulePositions() { return QStringLiteral("positions"); } inline const static QString itemRecurrenceRuleDaysOfMonth() { return QStringLiteral("daysOfMonth"); } inline const static QString itemRecurrenceRuleDaysOfYear() { return QStringLiteral("daysOfYear"); } inline const static QString itemRecurrenceRuleDaysOfWeek() { return QStringLiteral("daysOfWeek"); } inline const static QString itemRecurrenceRuleWeeksOfYear() { return QStringLiteral("weeksOfYear"); } inline const static QString itemRecurrenceRuleInterval() { return QStringLiteral("interval"); } inline const static QString itemRecurrenceRuleLimitCount() { return QStringLiteral("limitCount"); } inline const static QString itemRecurrenceRuleLimitDate() { return QStringLiteral("limitDate"); } // occurrence item parent inline const static QString itemOccurrenceParent() { return QStringLiteral("parentUuid"); } inline const static QString itemOccurrenceOriginalDate() { return QStringLiteral("originalDate"); } // events inline const static QString jsonDbEventType() { return QStringLiteral("com.nokia.mt.organizer.Event"); } inline const static QString jsonDbEventOccurrenceType() { return QStringLiteral("com.nokia.mt.organizer.EventOccurrence"); } inline const static QString eventStartDateTime() { return QStringLiteral("startDateTime"); } inline const static QString eventEndDateTime() { return QStringLiteral("endDateTime"); } inline const static QString eventIsAllDay() { return QStringLiteral("isAllDay"); } // event views inline const static QString jsonDbEventViewType() { return QStringLiteral("com.nokia.mt.organizer.EventView"); } inline const static QString eventIsSynthetic() { return QStringLiteral("isSynthetic"); } // event location inline const static QString eventLocation() { return QStringLiteral("location"); } inline const static QString eventLocationDisplayName() { return QStringLiteral("displayName"); } inline const static QString eventLocationGeo() { return QStringLiteral("geo"); } inline const static QString eventLocationGeoLatitude() { return QStringLiteral("latitude"); } inline const static QString eventLocationGeoLongitude() { return QStringLiteral("longitude"); } // event attendees inline const static QString eventAttendees() { return QStringLiteral("attendees"); } inline const static QString eventAttendeeName() { return QStringLiteral("name"); } inline const static QString eventAttendeeParticipationRole() { return QStringLiteral("participationRole"); } inline const static QString eventAttendeeParticipationStatus() { return QStringLiteral("participationStatus"); } inline const static QString eventAttendeeEmailAddress() { return QStringLiteral("emailAddress"); } inline const static QString eventAttendeeUuid() { return QStringLiteral("attendeeUuid"); } // event rsvp inline const static QString eventRsvp() { return QStringLiteral("rsvp"); } inline const static QString eventRsvpParticipationStatus() { return QStringLiteral("participationStatus"); } inline const static QString eventRsvpParticipationRole() { return QStringLiteral("participationRole"); } inline const static QString eventRsvpResponseRequirement() { return QStringLiteral("responseRequirement"); } inline const static QString eventRsvpResponseDeadline() { return QStringLiteral("responseDeadline"); } inline const static QString eventRsvpResponseDate() { return QStringLiteral("responseDate"); } inline const static QString eventRsvpOrganizerName() { return QStringLiteral("organizerName"); } inline const static QString eventRsvpOrganizerEmail() { return QStringLiteral("organizerEmail"); } // TODOs inline const static QString jsonDbTodoType() { return QStringLiteral("com.nokia.mt.organizer.Todo"); } inline const static QString jsonDbTodoOccurrenceType() { return QStringLiteral("com.nokia.mt.organizer.TodoOccurrence"); } inline const static QString todoStartDateTime() { return QStringLiteral("startDateTime"); } inline const static QString todoDueDateTime() { return QStringLiteral("dueDateTime"); } inline const static QString todoIsAllDay() { return QStringLiteral("isAllDay"); } inline const static QString todoFinishedDateTime() { return QStringLiteral("finishedDateTime"); } inline const static QString todoProgressPercentage() { return QStringLiteral("progressPercentage"); } inline const static QString todoStatus() { return QStringLiteral("status"); } //warning string inline const static QString warningCollectionRemove() { return QStringLiteral("Number of items deleted from jsondb is not equal to request!!"); } inline const static QString warningDefaultCollectionRemove() { return QStringLiteral("Default collection can not be removed!"); } }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERJSONDBSTRING_H src/plugins/organizer/memory/000077500000000000000000000000001233466112000166345ustar00rootroot00000000000000src/plugins/organizer/memory/memory.json000066400000000000000000000000351233466112000210350ustar00rootroot00000000000000{ "Keys": [ "memory" ] } src/plugins/organizer/memory/memory.pro000066400000000000000000000003411233466112000206640ustar00rootroot00000000000000TARGET = qtorganizer_memory QT = core organizer-private PLUGIN_TYPE = organizer load(qt_plugin) HEADERS += \ qorganizeritemmemorybackend_p.h SOURCES += \ qorganizeritemmemorybackend.cpp OTHER_FILES += memory.json src/plugins/organizer/memory/qorganizeritemmemorybackend.cpp000066400000000000000000002252571233466112000251560ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizeritemmemorybackend_p.h" #include #include #include #include #include #ifndef QT_NO_DEBUG_STREAM #include #endif #include #include QT_BEGIN_NAMESPACE_ORGANIZER QOrganizerManagerEngine* QOrganizerItemMemoryFactory::engine(const QMap& parameters, QOrganizerManager::Error* error) { Q_UNUSED(error); QOrganizerItemMemoryEngine *ret = QOrganizerItemMemoryEngine::createMemoryEngine(parameters); return ret; } QOrganizerItemEngineId* QOrganizerItemMemoryFactory::createItemEngineId(const QMap& parameters, const QString& idString) const { Q_UNUSED(parameters); QOrganizerItemMemoryEngineId *retn = new QOrganizerItemMemoryEngineId(idString); return retn; } QOrganizerCollectionEngineId* QOrganizerItemMemoryFactory::createCollectionEngineId(const QMap& parameters, const QString& idString) const { Q_UNUSED(parameters); QOrganizerCollectionMemoryEngineId *retn = new QOrganizerCollectionMemoryEngineId(idString); return retn; } QString QOrganizerItemMemoryFactory::managerName() const { return QString::fromLatin1("memory"); } /*! \class QOrganizerItemMemoryEngine \brief The QOrganizerItemMemoryEngine class provides an in-memory implementation of an organizer item backend. \inmodule QtOrganizer \internal It may be used as a reference implementation, or when persistent storage is not required. During construction, it will load the in-memory data associated with the memory store identified by the "id" parameter from the given parameters if it exists, or a new, anonymous store if it does not. Data stored in this engine is only available in the current process. This engine supports sharing, so an internal reference count is increased whenever a manager uses this backend, and is decreased when the manager no longer requires this engine. */ /*! \class QOrganizerItemMemoryEngineId \brief The QOrganizerItemMemoryEngineId class provides an id which uniquely identifies a QOrganizerItem stored within a collection stored within a a QOrganizerItemMemoryEngine. \internal It may be used as a reference implementation, although since different platforms have different semantics for ids (datastore-unique versus calendar-unique, etc), the precise implementation required may differ. */ QOrganizerItemMemoryEngineId::QOrganizerItemMemoryEngineId() : QOrganizerItemEngineId(), m_collectionId(0), m_itemId(0) { } QOrganizerItemMemoryEngineId::QOrganizerItemMemoryEngineId(quint32 collectionId, quint32 itemId, const QString& managerUri) : QOrganizerItemEngineId(), m_collectionId(collectionId), m_itemId(itemId), m_managerUri(managerUri) { } QOrganizerItemMemoryEngineId::~QOrganizerItemMemoryEngineId() { } QOrganizerItemMemoryEngineId::QOrganizerItemMemoryEngineId(const QOrganizerItemMemoryEngineId& other) : QOrganizerItemEngineId(), m_collectionId(other.m_collectionId), m_itemId(other.m_itemId), m_managerUri(other.m_managerUri) { } QOrganizerItemMemoryEngineId::QOrganizerItemMemoryEngineId(const QString& idString) : QOrganizerItemEngineId() { int temp = 0; int colonIndex = idString.indexOf(QStringLiteral(":"), 0); m_collectionId = idString.mid(temp, colonIndex).toUInt(); temp = colonIndex + 1; colonIndex = idString.indexOf(QStringLiteral(":"), temp); m_itemId = idString.mid(temp, (colonIndex-temp)).toUInt(); temp = colonIndex + 1; m_managerUri = idString.mid(temp); } bool QOrganizerItemMemoryEngineId::isEqualTo(const QOrganizerItemEngineId* other) const { // note: we don't need to check the collectionId because itemIds in the memory // engine are unique regardless of which collection the item is in; also, we // don't need to check the managerUri, because this function is not called if // the managerUris don't match. if (m_itemId != static_cast(other)->m_itemId) return false; return true; } bool QOrganizerItemMemoryEngineId::isLessThan(const QOrganizerItemEngineId* other) const { // order by collection, then by item in collection. const QOrganizerItemMemoryEngineId* otherPtr = static_cast(other); if (m_managerUri < otherPtr->m_managerUri) return true; if (m_collectionId < otherPtr->m_collectionId) return true; if (m_collectionId == otherPtr->m_collectionId) return (m_itemId < otherPtr->m_itemId); return false; } QString QOrganizerItemMemoryEngineId::managerUri() const { return m_managerUri; } QString QOrganizerItemMemoryEngineId::toString() const { return (QString::number(m_collectionId) % QLatin1Char(':') % QString::number(m_itemId) % QLatin1Char(':') % m_managerUri); } QOrganizerItemEngineId* QOrganizerItemMemoryEngineId::clone() const { return new QOrganizerItemMemoryEngineId(m_collectionId, m_itemId, m_managerUri); } #ifndef QT_NO_DEBUG_STREAM QDebug& QOrganizerItemMemoryEngineId::debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QOrganizerItemMemoryEngineId(" << m_collectionId << ", " << m_itemId << "," << m_managerUri << ")"; return dbg.maybeSpace(); } #endif uint QOrganizerItemMemoryEngineId::hash() const { // Note: doesn't need to be unique, since == ensures difference. // Hash function merely determines distribution in a hash table. // We certainly don't want to qHash(managerUri) since that's slow. // We don't need anything other than itemId in the memory engine, // since it's unique across collections. return m_itemId; } /*! \class QOrganizerCollectionMemoryEngineId \brief The QOrganizerCollectionMemoryEngineId class provides an id which uniquely identifies a QOrganizerCollection stored within a collection stored within a a QOrganizerCollectionMemoryEngine. \internal It may be used as a reference implementation, although since different platforms have different semantics for ids (datastore-unique versus calendar-unique, etc), the precise implementation required may differ. */ QOrganizerCollectionMemoryEngineId::QOrganizerCollectionMemoryEngineId() : QOrganizerCollectionEngineId(), m_collectionId(0) { } QOrganizerCollectionMemoryEngineId::QOrganizerCollectionMemoryEngineId(quint32 collectionId, const QString& managerUri) : QOrganizerCollectionEngineId(), m_collectionId(collectionId), m_managerUri(managerUri) { } QOrganizerCollectionMemoryEngineId::QOrganizerCollectionMemoryEngineId(const QOrganizerCollectionMemoryEngineId& other) : QOrganizerCollectionEngineId(), m_collectionId(other.m_collectionId) { } QOrganizerCollectionMemoryEngineId::QOrganizerCollectionMemoryEngineId(const QString& idString) : QOrganizerCollectionEngineId() { int colonIndex = idString.indexOf(QStringLiteral(":")); m_collectionId = idString.mid(0, colonIndex).toUInt(); m_managerUri = idString.mid(colonIndex+1); } QOrganizerCollectionMemoryEngineId::~QOrganizerCollectionMemoryEngineId() { } bool QOrganizerCollectionMemoryEngineId::isEqualTo(const QOrganizerCollectionEngineId* other) const { // note: we don't need to check the managerUri because this function is not called // if the managerUris are different. if (m_collectionId != static_cast(other)->m_collectionId) return false; return true; } bool QOrganizerCollectionMemoryEngineId::isLessThan(const QOrganizerCollectionEngineId* other) const { // order by collection, then by item in collection. const QOrganizerCollectionMemoryEngineId* otherPtr = static_cast(other); if (m_managerUri < otherPtr->m_managerUri) return true; if (m_collectionId < otherPtr->m_collectionId) return true; return false; } QString QOrganizerCollectionMemoryEngineId::managerUri() const { return m_managerUri; } QString QOrganizerCollectionMemoryEngineId::toString() const { return (QString::number(m_collectionId) % QLatin1Char(':') % m_managerUri); } QOrganizerCollectionEngineId* QOrganizerCollectionMemoryEngineId::clone() const { return new QOrganizerCollectionMemoryEngineId(m_collectionId, m_managerUri); } #ifndef QT_NO_DEBUG_STREAM QDebug& QOrganizerCollectionMemoryEngineId::debugStreamOut(QDebug& dbg) const { dbg.nospace() << "QOrganizerCollectionMemoryEngineId(" << m_collectionId << "," << m_managerUri << ")"; return dbg.maybeSpace(); } #endif uint QOrganizerCollectionMemoryEngineId::hash() const { return m_collectionId; } typedef QHash EngineDatas; Q_GLOBAL_STATIC(EngineDatas, theEngineDatas); /*! Constructor of a QOrganizerItemMemoryEngineData object */ QOrganizerItemMemoryEngineData::QOrganizerItemMemoryEngineData() : QSharedData(), m_nextOrganizerItemId(1), m_nextOrganizerCollectionId(2) { } /*! * Factory function for creating a new in-memory backend, based * on the given \a parameters. * * The same engine will be returned for multiple calls with the * same value for the "id" parameter, while one of them is in scope. */ QOrganizerItemMemoryEngine* QOrganizerItemMemoryEngine::createMemoryEngine(const QMap& parameters) { QString idValue = parameters.value(QStringLiteral("id")); EngineDatas &engineDatas = *theEngineDatas(); QOrganizerItemMemoryEngineData* data = engineDatas.value(idValue); if (!data) { data = new QOrganizerItemMemoryEngineData(); // no store given? new, anonymous store. if (!idValue.isEmpty()) { data->m_id = idValue; engineDatas.insert(idValue, data); } } data->ref.ref(); return new QOrganizerItemMemoryEngine(data); } /*! * Constructs a new in-memory backend which shares the given \a data with * other shared memory engines. */ QOrganizerItemMemoryEngine::QOrganizerItemMemoryEngine(QOrganizerItemMemoryEngineData* data) : d(data) { d->m_sharedEngines.append(this); // the default collection always exists. if (d->m_idToCollectionHash.isEmpty()) { d->m_managerUri = managerUri(); QOrganizerCollectionId defaultId = d->defaultCollectionId(); QOrganizerCollection defaultCollection; defaultCollection.setId(defaultId); defaultCollection.setMetaData(QOrganizerCollection::KeyName, QString(QStringLiteral("Default Collection"))); d->m_idToCollectionHash.insert(defaultId, defaultCollection); } } /*! Frees any memory used by this engine */ QOrganizerItemMemoryEngine::~QOrganizerItemMemoryEngine() { d->m_sharedEngines.removeAll(this); if (!d->ref.deref()) { if (!d->m_id.isEmpty()) { EngineDatas &engineDatas = *theEngineDatas(); engineDatas.remove(d->m_id); } delete d; } } /*! \reimp */ QString QOrganizerItemMemoryEngine::managerName() const { return QStringLiteral("memory"); } /*! \reimp */ QMap QOrganizerItemMemoryEngine::managerParameters() const { QMap params; params.insert(QStringLiteral("id"), d->m_id); return params; } QList QOrganizerItemMemoryEngine::items(const QList &itemIds, const QOrganizerItemFetchHint &fetchHint, QMap *errorMap, QOrganizerManager::Error *error) { Q_UNUSED(fetchHint) QList items; items.reserve(itemIds.size()); QOrganizerItem tmp; for (int i = 0; i < itemIds.size(); ++i) { tmp = item(itemIds.at(i)); items.append(tmp); if (tmp.isEmpty()) errorMap->insert(i, QOrganizerManager::DoesNotExistError); } *error = errorMap->isEmpty() ? QOrganizerManager::NoError : QOrganizerManager::DoesNotExistError; return items; } QList QOrganizerItemMemoryEngine::itemIds(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, const QList &sortOrders, QOrganizerManager::Error *error) { if (startDateTime.isNull() && endDateTime.isNull() && filter.type() == QOrganizerItemFilter::DefaultFilter && sortOrders.count() == 0) return d->m_idToItemHash.keys(); else return QOrganizerManager::extractIds(itemsForExport(startDateTime, endDateTime, filter, sortOrders, QOrganizerItemFetchHint(), error)); } QList QOrganizerItemMemoryEngine::internalItemOccurrences(const QOrganizerItem& parentItem, const QDateTime& periodStart, const QDateTime& periodEnd, int maxCount, bool includeExceptions, bool sortItems, QList *exceptionDates, QOrganizerManager::Error* error) const { // given the generating item, grab it's QOrganizerItemRecurrence detail (if it exists), and calculate all of the dates within the given period. // how would a real backend do this? // Also, should this also return the exception instances (ie, return any persistent instances with parent information == parent item?) // XXX TODO: in detail validation, ensure that the referenced parent Id exists... QDateTime realPeriodStart(periodStart); QDateTime realPeriodEnd(periodEnd); QDateTime initialDateTime; if (parentItem.type() == QOrganizerItemType::TypeEvent) { QOrganizerEvent evt = parentItem; initialDateTime = evt.startDateTime().isValid() ? evt.startDateTime() : evt.endDateTime(); } else if (parentItem.type() == QOrganizerItemType::TypeTodo) { QOrganizerTodo todo = parentItem; initialDateTime = todo.startDateTime().isValid() ? todo.startDateTime() : todo.dueDateTime(); } else { // erm... not a recurring item in our schema... return QList(); } if (realPeriodStart.isValid() && initialDateTime.isValid()) { if (initialDateTime > realPeriodStart) realPeriodStart = initialDateTime; } else if (initialDateTime.isValid()) { realPeriodStart = initialDateTime; } if (!periodEnd.isValid()) { // If no endDateTime is given, we'll only generate items that occur within the next 4 years of realPeriodStart. realPeriodEnd.setDate(realPeriodStart.date().addDays(1461)); realPeriodEnd.setTime(realPeriodStart.time()); } if (realPeriodStart > realPeriodEnd) { *error = QOrganizerManager::BadArgumentError; return QList(); } QList retn; QList xoccurrences; QOrganizerItemRecurrence recur = parentItem.detail(QOrganizerItemDetail::TypeRecurrence); if (includeExceptions) { // first, retrieve all persisted instances (exceptions) which occur between the specified datetimes. foreach (const QOrganizerItem& item, d->m_idToItemHash) { if (item.detail(QOrganizerItemDetail::TypeParent).value(QOrganizerItemParent::FieldParentId) == parentItem.id()) { QDateTime lowerBound; QDateTime upperBound; if (item.type() == QOrganizerItemType::TypeEventOccurrence) { QOrganizerEventOccurrence instance = item; lowerBound = instance.startDateTime(); upperBound = instance.endDateTime(); } else { QOrganizerTodoOccurrence instance = item; lowerBound = instance.startDateTime(); upperBound = instance.dueDateTime(); } if ((lowerBound.isNull() || lowerBound >= realPeriodStart) && (upperBound.isNull() || upperBound <= realPeriodEnd)) { // this occurrence fulfils the criteria. xoccurrences.append(item); } } } } // then, generate the required (unchanged) instances from the parentItem. // before doing that, we have to find out all of the exception dates. QList xdates; foreach (const QDate& xdate, recur.exceptionDates()) { xdates += xdate; } if (realPeriodStart.isValid()) { QSet xrules = recur.exceptionRules(); foreach (const QOrganizerRecurrenceRule& xrule, xrules) { if (xrule.frequency() != QOrganizerRecurrenceRule::Invalid && ((xrule.limitType() != QOrganizerRecurrenceRule::DateLimit) || (xrule.limitDate() >= realPeriodStart.date()))) { // we cannot skip it, since it applies in the given time period. QList xdatetimes = generateDateTimes(initialDateTime, xrule, realPeriodStart, realPeriodEnd, 50); // max count of 50 is arbitrary... foreach (const QDateTime& xdatetime, xdatetimes) xdates += xdatetime.date(); } } } // now generate a list of rdates (from the recurrenceDates and recurrenceRules) // QMap is used for storing dates, because we don't want to have duplicate dates and // we want to have dates sorted // Only key of the map is relevant (QDateTime), the value (int) is not used QMap rdateMap; foreach (const QDate& rdate, recur.recurrenceDates()) rdateMap.insert(QDateTime(rdate, initialDateTime.time()), 0); if (realPeriodStart.isValid()) { QSet rrules = recur.recurrenceRules(); foreach (const QOrganizerRecurrenceRule& rrule, rrules) { if (rrule.frequency() != QOrganizerRecurrenceRule::Invalid && ((rrule.limitType() != QOrganizerRecurrenceRule::DateLimit) || (rrule.limitDate() >= realPeriodStart.date()))) { // we cannot skip it, since it applies in the given time period. QList rdatetimes = generateDateTimes(initialDateTime, rrule, realPeriodStart, realPeriodEnd, 50); // max count of 50 is arbitrary... foreach (const QDateTime& rdatetime, rdatetimes) rdateMap.insert(rdatetime, 0); } } } // now order the contents of retn by date QList rdates = rdateMap.keys(); if (initialDateTime.isValid() && !recur.recurrenceDates().isEmpty() && qBinaryFind(rdates, initialDateTime) == rdates.constEnd()) { rdates.prepend(initialDateTime); } // now for each rdate which isn't also an xdate foreach (const QDateTime& rdate, rdates) { if (rdate >= realPeriodStart && rdate <= realPeriodEnd) { if (!xdates.contains(rdate.date())) { // generate the required instance and add it to the return list. retn.append(QOrganizerManagerEngine::generateOccurrence(parentItem, rdate)); } else if (includeExceptions) { for (int i = 0; i < xoccurrences.size(); i++) { QOrganizerItemParent parentDetail = xoccurrences[i].detail(QOrganizerItemDetail::TypeParent); if (parentDetail.originalDate() == rdate.date()) retn.append(xoccurrences[i]); } } else if (exceptionDates) { exceptionDates->append(rdate.date()); } } } if (sortItems) { // should we always sort if a maxCount is given? QMultiMap defaultSorted; foreach (QOrganizerItem item, retn) QOrganizerManagerEngine::addDefaultSorted(&defaultSorted, item); retn = defaultSorted.values(); } // and return the first maxCount entries. return retn.mid(0, maxCount); } QList QOrganizerItemMemoryEngine::itemOccurrences(const QOrganizerItem &parentItem, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error) { Q_UNUSED(fetchHint); return internalItemOccurrences(parentItem, startDateTime, endDateTime, maxCount, true, true, 0, error); } QList QOrganizerItemMemoryEngine::items(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error) { QList list; if (sortOrders.size() > 0) { list = internalItems(startDateTime, endDateTime, filter, sortOrders, fetchHint, error, false); } else { QOrganizerItemSortOrder sortOrder; sortOrder.setDetail(QOrganizerItemDetail::TypeEventTime, QOrganizerEventTime::FieldStartDateTime); sortOrder.setDirection(Qt::AscendingOrder); QList sortOrders; sortOrders.append(sortOrder); sortOrder.setDetail(QOrganizerItemDetail::TypeTodoTime, QOrganizerTodoTime::FieldStartDateTime); sortOrders.append(sortOrder); sortOrder.setDetail(QOrganizerItemDetail::TypeTodoTime, QOrganizerTodoTime::FieldStartDateTime); sortOrders.append(sortOrder); list = internalItems(startDateTime, endDateTime, filter, sortOrders, fetchHint, error, false); } if (maxCount < 0) return list; else return list.mid(0, maxCount); } QList QOrganizerItemMemoryEngine::itemsForExport(const QDateTime &startDateTime, const QDateTime &endDateTime, const QOrganizerItemFilter &filter, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error) { return internalItems(startDateTime, endDateTime, filter, sortOrders, fetchHint, error, true); } QList QOrganizerItemMemoryEngine::itemsForExport(const QList &ids, const QOrganizerItemFetchHint &fetchHint, QMap *errorMap, QOrganizerManager::Error *error) { QOrganizerItemIdFilter filter; filter.setIds(ids); QList unsorted = itemsForExport(QDateTime(), QDateTime(), filter, QOrganizerItemSortOrder(), fetchHint, error); // Build an index into the results QHash idMap; // value is index into unsorted if (*error == QOrganizerManager::NoError) { for (int i = 0; i < unsorted.size(); i++) { idMap.insert(unsorted[i].id(), i); } } // Build up the results and errors QList results; for (int i = 0; i < ids.count(); i++) { QOrganizerItemId id(ids[i]); if (!idMap.contains(id)) { if (errorMap) errorMap->insert(i, QOrganizerManager::DoesNotExistError); if (*error == QOrganizerManager::NoError) *error = QOrganizerManager::DoesNotExistError; results.append(QOrganizerItem()); } else { results.append(unsorted[idMap[id]]); } } return results; } QOrganizerItem QOrganizerItemMemoryEngine::item(const QOrganizerItemId& organizeritemId) const { return d->m_idToItemHash.value(organizeritemId); } QList QOrganizerItemMemoryEngine::internalItems(const QDateTime& startDate, const QDateTime& endDate, const QOrganizerItemFilter& filter, const QList& sortOrders, const QOrganizerItemFetchHint& fetchHint, QOrganizerManager::Error* error, bool forExport) const { Q_UNUSED(fetchHint); // no optimisations are possible in the memory backend; ignore the fetch hint. Q_UNUSED(error); QList sorted; QSet parentsAdded; bool isDefFilter = (filter.type() == QOrganizerItemFilter::DefaultFilter); foreach(const QOrganizerItem& c, d->m_idToItemHash) { if (itemHasReccurence(c)) { addItemRecurrences(sorted, c, startDate, endDate, filter, sortOrders, forExport, &parentsAdded); } else { if ((isDefFilter || QOrganizerManagerEngine::testFilter(filter, c)) && QOrganizerManagerEngine::isItemBetweenDates(c, startDate, endDate)) { QOrganizerManagerEngine::addSorted(&sorted,c, sortOrders); if (forExport && (c.type() == QOrganizerItemType::TypeEventOccurrence || c.type() == QOrganizerItemType::TypeTodoOccurrence)) { QOrganizerItemId parentId(c.detail(QOrganizerItemDetail::TypeParent).value(QOrganizerItemParent::FieldParentId)); if (!parentsAdded.contains(parentId)) { parentsAdded.insert(parentId); QOrganizerManagerEngine::addSorted(&sorted, item(parentId), sortOrders); } } } } } return sorted; } void QOrganizerItemMemoryEngine::addItemRecurrences(QList& sorted, const QOrganizerItem& c, const QDateTime& startDate, const QDateTime& endDate, const QOrganizerItemFilter& filter, const QList& sortOrders, bool forExport, QSet* parentsAdded) const { QOrganizerManager::Error error = QOrganizerManager::NoError; if (forExport && parentsAdded->contains(c.id())) return; QList recItems = internalItemOccurrences(c, startDate, endDate, forExport ? 1 : 50, false, false, 0, &error); // XXX TODO: why maxcount of 50? if (filter.type() == QOrganizerItemFilter::DefaultFilter) { foreach(const QOrganizerItem& oi, recItems) { QOrganizerManagerEngine::addSorted(&sorted, forExport ? c : oi, sortOrders); if (forExport) parentsAdded->insert(c.id()); } } else { foreach(const QOrganizerItem& oi, recItems) { if (QOrganizerManagerEngine::testFilter(filter, oi)) { QOrganizerManagerEngine::addSorted(&sorted, forExport ? c : oi, sortOrders); if (forExport) parentsAdded->insert(c.id()); } } } } /*! Saves the given organizeritem \a theOrganizerItem, storing any error to \a error and filling the \a changeSet with ids of changed organizeritems as required */ bool QOrganizerItemMemoryEngine::saveItem(QOrganizerItem* theOrganizerItem, QOrganizerItemChangeSet& changeSet, QOrganizerManager::Error* error) { QOrganizerCollectionId targetCollectionId = theOrganizerItem->collectionId(); // check that the collection exists (or is null :. default collection): if (!targetCollectionId.isNull() && !d->m_idToCollectionHash.contains(targetCollectionId)) { *error = QOrganizerManager::InvalidCollectionError; return false; } if (theOrganizerItem->type() == QOrganizerItemType::TypeUndefined) { *error = QOrganizerManager::InvalidItemTypeError; return false; } // check to see if this organizer item already exists QOrganizerItemId theOrganizerItemId = theOrganizerItem->id(); QHash::const_iterator hashIterator = d->m_idToItemHash.find(theOrganizerItemId); if (hashIterator != d->m_idToItemHash.constEnd()) { /* We also need to check that there are no modified create only details */ QOrganizerItem oldOrganizerItem = hashIterator.value(); if (oldOrganizerItem.type() != theOrganizerItem->type()) { *error = QOrganizerManager::AlreadyExistsError; return false; } // check that the old and new collection is the same (ie, not attempting to save to a different collection) if (targetCollectionId.isNull()) { // it already exists, so save it where it already exists. targetCollectionId = d->m_itemsInCollectionsHash.key(theOrganizerItemId); } else if (!d->m_itemsInCollectionsHash.values(targetCollectionId).contains(theOrganizerItemId)) { // the given collection id was non-null but doesn't already contain this item. error. *error = QOrganizerManager::InvalidCollectionError; return false; } QOrganizerItemTimestamp ts = theOrganizerItem->detail(QOrganizerItemDetail::TypeTimestamp); ts.setLastModified(QDateTime::currentDateTime()); theOrganizerItem->saveDetail(&ts); if (!fixOccurrenceReferences(theOrganizerItem, error)) { return false; } // Looks ok, so continue d->m_idToItemHash.insert(theOrganizerItemId, *theOrganizerItem); // replacement insert. changeSet.insertChangedItem(theOrganizerItemId); // cross-check if stored exception occurrences are still valid if (itemHasReccurence(oldOrganizerItem)) { // if we are updating an existing item and the item had recurrence defined, it might // have some exception occurrences which don't match the current recurrence. // should we also check and remove exception dates if e.g. limit date or limit count has been changed // to be earlier (or smaller) than before thus invelidating an exception date..? QList occurrenceIds = d->m_parentIdToChildIdHash.values(theOrganizerItemId); QOrganizerManager::Error occurrenceError = QOrganizerManager::NoError; if (!occurrenceIds.isEmpty()) { if (itemHasReccurence(*theOrganizerItem)) { // generate occurrences to get the dates when there can be an exception occurrence // if the new item does not have recurrence, all exception occurrences of this item // are removed QList exceptionDates; QList occurrences = internalItemOccurrences(*theOrganizerItem, QDateTime(), QDateTime(), -1, false, false, &exceptionDates, &occurrenceError); foreach (const QOrganizerItemId &occurrenceId, occurrenceIds) { // remove all occurrence ids from the list which have valid exception date QOrganizerItemParent parentDetail = d->m_idToItemHash.value(occurrenceId).detail(QOrganizerItemDetail::TypeParent); if (!parentDetail.isEmpty() && exceptionDates.contains(parentDetail.originalDate())) occurrenceIds.removeOne(occurrenceId); } } foreach (const QOrganizerItemId &occurrenceId, occurrenceIds) removeItem(occurrenceId, changeSet, &occurrenceError); } } } else { // id does not exist; if not zero, fail. if (!theOrganizerItemId.isNull()) { // the ID is not empty, and it doesn't identify an existing organizer item in our database either. *error = QOrganizerManager::DoesNotExistError; return false; } /* New organizer item */ QOrganizerItemTimestamp ts = theOrganizerItem->detail(QOrganizerItemDetail::TypeTimestamp); ts.setLastModified(QDateTime::currentDateTime()); ts.setCreated(ts.lastModified()); theOrganizerItem->saveDetail(&ts); if (!fixOccurrenceReferences(theOrganizerItem, error)) { return false; } // set the guid if not set if (theOrganizerItem->guid().isEmpty()) theOrganizerItem->setGuid(QUuid::createUuid().toString()); // if we're saving an exception occurrence, we need to add it's original date as an exdate to the parent. QOrganizerItemId parentId; if (theOrganizerItem->type() == QOrganizerItemType::TypeEventOccurrence || theOrganizerItem->type() == QOrganizerItemType::TypeTodoOccurrence) { // update the event or the todo by adding an EX-DATE which corresponds to the original date of the occurrence being saved. QOrganizerItemParent origin = theOrganizerItem->detail(QOrganizerItemDetail::TypeParent); parentId = origin.parentId(); // for occurrences, if given a null collection id, save it in the same collection as the parent. // otherwise, ensure that the parent is in the same collection. You cannot save an exception to a different collection than the parent. if (targetCollectionId.isNull()) { targetCollectionId = d->m_itemsInCollectionsHash.key(parentId); if (targetCollectionId.isNull()) { *error = QOrganizerManager::UnspecifiedError; // this should never occur; parent should _always_ be in a collection. return false; } } else if (!d->m_itemsInCollectionsHash.values(targetCollectionId).contains(parentId)) { // nope, the specified collection doesn't contain the parent. error. *error = QOrganizerManager::InvalidCollectionError; return false; } QMap tempErrorMap; QOrganizerManager::Error tempError = QOrganizerManager::NoError; const QList candidates = items(QList() << parentId, QOrganizerItemFetchHint(), &tempErrorMap, &tempError); if (tempError != QOrganizerManager::NoError || candidates.isEmpty()) { *error = tempError != QOrganizerManager::NoError ? tempError : QOrganizerManager::UnspecifiedError; return false; } QOrganizerItem parentItem = candidates.first(); QDate originalDate = origin.originalDate(); QOrganizerItemRecurrence recurrence = parentItem.detail(QOrganizerItemDetail::TypeRecurrence); QSet currentExceptionDates = recurrence.exceptionDates(); if (!currentExceptionDates.contains(originalDate)) { currentExceptionDates << originalDate; recurrence.setExceptionDates(currentExceptionDates); parentItem.saveDetail(&recurrence); d->m_idToItemHash.insert(parentId, parentItem); // replacement insert changeSet.insertChangedItem(parentId); // is this correct? it's an exception, so change parent? } } // if target collection id is null, set to default id. if (targetCollectionId.isNull()) targetCollectionId = d->defaultCollectionId(); // update the organizer item - set its ID const QOrganizerCollectionMemoryEngineId *colEngineId = static_cast(QOrganizerManagerEngine::engineCollectionId(targetCollectionId)); QOrganizerItemMemoryEngineId* newId = new QOrganizerItemMemoryEngineId(colEngineId->m_collectionId, d->m_nextOrganizerItemId++, d->m_managerUri); // note: do NOT delete the QOrganizerItemMemoryEngineId -- the QOrganizerItemId ctor takes ownership of it. theOrganizerItemId = QOrganizerItemId(newId); theOrganizerItem->setId(theOrganizerItemId); // finally, add the organizer item to our internal lists and return theOrganizerItem->setCollectionId(targetCollectionId); d->m_idToItemHash.insert(theOrganizerItemId, *theOrganizerItem); // add organizer item to hash if (!parentId.isNull()) { // if it was an occurrence, we need to add it to the children hash. d->m_parentIdToChildIdHash.insert(parentId, theOrganizerItemId); } d->m_itemsInCollectionsHash.insert(targetCollectionId, theOrganizerItemId); changeSet.insertAddedItem(theOrganizerItemId); } *error = QOrganizerManager::NoError; // successful. return true; } /*! * For Occurrence type items, ensure the ParentId and the Guid are set consistently. Returns * false and sets \a error on error, returns true otherwise. */ bool QOrganizerItemMemoryEngine::fixOccurrenceReferences(QOrganizerItem* theItem, QOrganizerManager::Error* error) { if (theItem->type() == QOrganizerItemType::TypeEventOccurrence || theItem->type() == QOrganizerItemType::TypeTodoOccurrence) { const QString guid = theItem->guid(); QOrganizerItemParent instanceOrigin = theItem->detail(QOrganizerItemDetail::TypeParent); if (!instanceOrigin.originalDate().isValid()) { *error = QOrganizerManager::InvalidOccurrenceError; return false; } QOrganizerItemId parentId = instanceOrigin.parentId(); if (!guid.isEmpty()) { if (!parentId.isNull()) { QOrganizerManager::Error tempError; QMap tempErrorMap; QOrganizerItem parentItem = items(QList() << parentId, QOrganizerItemFetchHint(), &tempErrorMap, &tempError).at(0); if (guid != parentItem.guid() || !typesAreRelated(theItem->type(), parentItem.type())) { // parentId and guid are both set and inconsistent, or the parent is the wrong // type *error = QOrganizerManager::InvalidOccurrenceError; return false; } } else { // guid set but not parentId // find an item with the given guid foreach (const QOrganizerItem& item, d->m_idToItemHash) { if (item.guid() == guid) { parentId = item.id(); break; } } if (parentId.isNull()) { // couldn't find an item with the given guid *error = QOrganizerManager::InvalidOccurrenceError; return false; } QOrganizerManager::Error tempError; QMap tempErrorMap; QOrganizerItem parentItem = items(QList() << parentId, QOrganizerItemFetchHint(), &tempErrorMap, &tempError).at(0); if (!typesAreRelated(theItem->type(), parentItem.type())) { // the parent is the wrong type *error = QOrganizerManager::InvalidOccurrenceError; return false; } // found a matching item - set the parentId of the occurrence QOrganizerItemParent origin = theItem->detail(QOrganizerItemDetail::TypeParent); origin.setParentId(parentId); theItem->saveDetail(&origin); } } else if (!parentId.isNull()) { QOrganizerManager::Error tempError; QMap tempErrorMap; QOrganizerItem parentItem = items(QList() << parentId, QOrganizerItemFetchHint(), &tempErrorMap, &tempError).at(0); if (parentItem.guid().isEmpty() || !typesAreRelated(theItem->type(), parentItem.type())) { // found the matching item but it has no guid, or it isn't the right type *error = QOrganizerManager::InvalidOccurrenceError; return false; } theItem->setGuid(parentItem.guid()); } else { // neither parentId or guid is supplied *error = QOrganizerManager::InvalidOccurrenceError; return false; } } return true; } /*! * Returns true if and only if \a occurrenceType is the "Occurrence" version of \a parentType. */ bool QOrganizerItemMemoryEngine::typesAreRelated(QOrganizerItemType::ItemType occurrenceType, QOrganizerItemType::ItemType parentType) { return ((parentType == QOrganizerItemType::TypeEvent && occurrenceType == QOrganizerItemType::TypeEventOccurrence) || (parentType == QOrganizerItemType::TypeTodo && occurrenceType == QOrganizerItemType::TypeTodoOccurrence)); } bool QOrganizerItemMemoryEngine::saveItems(QList* organizeritems, QMap* errorMap, QOrganizerManager::Error* error) { Q_ASSERT(errorMap); errorMap->clear(); if (!organizeritems) { *error = QOrganizerManager::BadArgumentError; return false; } QOrganizerItemChangeSet changeSet; QOrganizerItem current; QOrganizerManager::Error operationError = QOrganizerManager::NoError; for (int i = 0; i < organizeritems->count(); i++) { current = organizeritems->at(i); if (!saveItem(¤t, changeSet, error)) { operationError = *error; errorMap->insert(i, operationError); } else { (*organizeritems)[i] = current; } } *error = operationError; d->emitSharedSignals(&changeSet); // return false if some error occurred return (*error == QOrganizerManager::NoError); } /*! \reimp */ bool QOrganizerItemMemoryEngine::saveItems(QList *items, const QList &detailMask, QMap *errorMap, QOrganizerManager::Error *error) { // TODO should the default implementation do the right thing, or return false? if (detailMask.isEmpty()) { // Non partial, just pass it on return saveItems(items, errorMap, error); } else { // Partial item save. // Basically // Need to: // 1) fetch existing items // 2) strip out details in definitionMask for existing items // 3) copy the details from the passed in list for existing items // 4) for any new items, copy the masked details to a blank item // 5) save the modified ones // 6) update the id of any new items // 7) transfer any errors from saving to errorMap QList existingItemIds; // Error conditions: // 1) bad id passed in (can't fetch) // 2) bad fetch (can't save partial update) // 3) bad save error // all of which needs to be returned in the error map QHash existingIdMap; // items index to existingItems index // Try to figure out which of our arguments are new items for (int i = 0; i < items->count(); i++) { // See if there's a itemId that's not from this manager const QOrganizerItem item = items->at(i); if (item.id().managerUri() == managerUri()) { if (!item.id().isNull()) { existingIdMap.insert(i, existingItemIds.count()); existingItemIds.append(item.id()); } else { // Strange. it's just a new item } } else if (!item.id().managerUri().isEmpty() || !item.id().isNull()) { // Hmm, error (wrong manager) errorMap->insert(i, QOrganizerManager::DoesNotExistError); } // else new item } // Now fetch the existing items QMap fetchErrors; QOrganizerManager::Error fetchError = QOrganizerManager::NoError; QList existingItems = this->itemsForExport(existingItemIds, QOrganizerItemFetchHint(), &fetchErrors, &fetchError); // Prepare the list to save QList itemsToSave; QList savedToOriginalMap; // itemsToSave index to items index for (int i = 0; i < items->count(); i++) { // See if this is an existing item or a new one const int fetchedIdx = existingIdMap.value(i, -1); QOrganizerItem itemToSave; if (fetchedIdx >= 0) { // See if we had an error if (fetchErrors[fetchedIdx] != QOrganizerManager::NoError) { errorMap->insert(i, fetchErrors[fetchedIdx]); continue; } // Existing item we should have fetched itemToSave = existingItems.at(fetchedIdx); // QOrganizerItemData::removeOnly() is not exported, so we can only do this... foreach (QOrganizerItemDetail::DetailType mask, detailMask) { QList details(itemToSave.details(mask)); foreach (QOrganizerItemDetail detail, details) itemToSave.removeDetail(&detail); } } else if (errorMap->contains(i)) { // A bad argument. Leave it out of the itemsToSave list continue; } else { // new item itemToSave.setType(items->at(i).type()); } // Now copy in the details from the arguments const QOrganizerItem& item = items->at(i); // Perhaps this could do this directly rather than through saveDetail // but that would duplicate the checks for display label etc foreach (QOrganizerItemDetail::DetailType name, detailMask) { QList details = item.details(name); foreach (QOrganizerItemDetail detail, details) itemToSave.saveDetail(&detail); } savedToOriginalMap.append(i); itemsToSave.append(itemToSave); } // Now save them QMap saveErrors; QOrganizerManager::Error saveError = QOrganizerManager::NoError; saveItems(&itemsToSave, &saveErrors, &saveError); // Now update the passed in arguments, where necessary // Update IDs of the items list for (int i = 0; i < itemsToSave.count(); i++) { (*items)[savedToOriginalMap[i]].setId(itemsToSave[i].id()); } // Populate the errorMap with the errorMap of the attempted save QMap::iterator it(saveErrors.begin()); while (it != saveErrors.end()) { if (it.value() != QOrganizerManager::NoError) { errorMap->insert(savedToOriginalMap[it.key()], it.value()); } it++; } return errorMap->isEmpty(); } } /*! Removes the organizer item identified by the given \a organizeritemId, storing any error to \a error and filling the \a changeSet with ids of changed organizer items as required */ bool QOrganizerItemMemoryEngine::removeItem(const QOrganizerItemId& organizeritemId, QOrganizerItemChangeSet& changeSet, QOrganizerManager::Error* error) { QHash::const_iterator hashIterator = d->m_idToItemHash.find(organizeritemId); if (hashIterator == d->m_idToItemHash.constEnd()) { *error = QOrganizerManager::DoesNotExistError; return false; } // if it is a child item, remove itself from the children hash QOrganizerItem thisItem = hashIterator.value(); QOrganizerItemParent parentDetail = thisItem.detail(QOrganizerItemDetail::TypeParent); if (!parentDetail.parentId().isNull()) { d->m_parentIdToChildIdHash.remove(parentDetail.parentId(), organizeritemId); } // if it is a parent item, remove any children. QList childrenIds = d->m_parentIdToChildIdHash.values(organizeritemId); foreach (const QOrganizerItemId& childId, childrenIds) { // remove the child occurrence from our lists. d->m_idToItemHash.remove(childId); d->m_itemsInCollectionsHash.remove(d->m_itemsInCollectionsHash.key(childId), childId); changeSet.insertRemovedItem(childId); } // remove the organizer item from the lists. d->m_idToItemHash.remove(organizeritemId); d->m_parentIdToChildIdHash.remove(organizeritemId); d->m_itemsInCollectionsHash.remove(d->m_itemsInCollectionsHash.key(organizeritemId), organizeritemId); *error = QOrganizerManager::NoError; changeSet.insertRemovedItem(organizeritemId); return true; } /*! Removes the organizer item occurrence identified by the given \a organizeritem. Removing a generated occurrence means adding a new exception date to parent items exception date list. Stores any error to \a error and fills the \a changeSet with ids of changed organizer items as required */ bool QOrganizerItemMemoryEngine::removeOccurrence(const QOrganizerItem &organizeritem, QOrganizerItemChangeSet &changeSet, QOrganizerManager::Error *error) { QOrganizerItemParent parentDetail = organizeritem.detail(QOrganizerItemDetail::TypeParent); if (parentDetail.parentId().isNull()) { *error = QOrganizerManager::InvalidOccurrenceError; return false; } QHash::const_iterator hashIterator = d->m_idToItemHash.find(parentDetail.parentId()); if (hashIterator == d->m_idToItemHash.constEnd()) { *error = QOrganizerManager::InvalidOccurrenceError; return false; } else { QOrganizerItem parentItem = hashIterator.value(); QOrganizerItemRecurrence recurrenceDetail = parentItem.detail(QOrganizerItemDetail::TypeRecurrence); QSet exceptionDates = recurrenceDetail.exceptionDates(); exceptionDates.insert(parentDetail.originalDate()); recurrenceDetail.setExceptionDates(exceptionDates); parentItem.saveDetail(&recurrenceDetail); d->m_idToItemHash.insert(parentDetail.parentId(), parentItem); changeSet.insertChangedItem(parentDetail.parentId()); } *error = QOrganizerManager::NoError; return true; } /*! \reimp */ bool QOrganizerItemMemoryEngine::removeItems(const QList &itemIds, QMap *errorMap, QOrganizerManager::Error *error) { Q_ASSERT(errorMap); if (itemIds.count() == 0) { *error = QOrganizerManager::BadArgumentError; return false; } QOrganizerItemChangeSet changeSet; QOrganizerItemId current; QOrganizerManager::Error operationError = QOrganizerManager::NoError; for (int i = 0; i < itemIds.count(); i++) { current = itemIds.at(i); if (!removeItem(current, changeSet, error)) { operationError = *error; errorMap->insert(i, operationError); } } *error = operationError; d->emitSharedSignals(&changeSet); // return false if some errors occurred return (*error == QOrganizerManager::NoError); } /*! \reimp */ bool QOrganizerItemMemoryEngine::removeItems(const QList *items, QMap *errorMap, QOrganizerManager::Error *error) { Q_ASSERT(errorMap); if (items->count() == 0) { *error = QOrganizerManager::BadArgumentError; return false; } QOrganizerItemChangeSet changeSet; QOrganizerItem current; QSet removedParentIds; QOrganizerManager::Error operationError = QOrganizerManager::NoError; for (int i = 0; i < items->count(); i++) { current = items->at(i); QOrganizerManager::Error tempError = QOrganizerManager::NoError; if ((current.type() == QOrganizerItemType::TypeEventOccurrence || current.type() == QOrganizerItemType::TypeTodoOccurrence) && current.id().isNull()) { // this is a generated occurrence, modify parent items exception dates QOrganizerItemParent parentDetail = current.detail(QOrganizerItemDetail::TypeParent); if (removedParentIds.isEmpty() || !removedParentIds.contains(parentDetail.parentId())) removeOccurrence(current, changeSet, &tempError); } else { removeItem(current.id(), changeSet, &tempError); if (tempError == QOrganizerManager::NoError && itemHasReccurence(current)) removedParentIds.insert(current.id()); } if (tempError != QOrganizerManager::NoError) { errorMap->insert(i, tempError); operationError = tempError; } } *error = operationError; d->emitSharedSignals(&changeSet); // return false if some errors occurred return (*error == QOrganizerManager::NoError); } QOrganizerCollection QOrganizerItemMemoryEngine::defaultCollection(QOrganizerManager::Error* error) { const QOrganizerCollectionId defaultCollectionId = d->defaultCollectionId(); Q_ASSERT(d->m_idToCollectionHash.contains(defaultCollectionId)); *error = QOrganizerManager::NoError; return d->m_idToCollectionHash.value(defaultCollectionId); } QOrganizerCollection QOrganizerItemMemoryEngine::collection(const QOrganizerCollectionId& collectionId, QOrganizerManager::Error* error) { if (d->m_idToCollectionHash.contains(collectionId)) { *error = QOrganizerManager::NoError; return d->m_idToCollectionHash.value(collectionId); } *error = QOrganizerManager::DoesNotExistError; return QOrganizerCollection(); } QList QOrganizerItemMemoryEngine::collections(QOrganizerManager::Error* error) { Q_ASSERT(!d->m_idToCollectionHash.isEmpty()); *error = QOrganizerManager::NoError; return d->m_idToCollectionHash.values(); } bool QOrganizerItemMemoryEngine::saveCollection(QOrganizerCollection* collection, QOrganizerManager::Error* error) { QOrganizerCollectionId collectionId = collection->id(); QOrganizerCollectionChangeSet cs; if (d->m_idToCollectionHash.contains(collectionId)) { // this collection already exists. update our internal list // if the collection has been modified. if (d->m_idToCollectionHash.value(collectionId) == *collection) { *error = QOrganizerManager::NoError; return true; } cs.insertChangedCollection(collectionId); } else { // this must be a new collection. check that the id is null. if (!collectionId.isNull() && collectionId.managerUri() != d->m_managerUri) { // nope, this collection belongs in another manager, or has been deleted. *error = QOrganizerManager::DoesNotExistError; return false; } // this is a new collection with a null id; create a new id, add it to our list. collectionId = QOrganizerCollectionId(new QOrganizerCollectionMemoryEngineId(d->m_nextOrganizerCollectionId++, d->m_managerUri)); collection->setId(collectionId); cs.insertAddedCollection(collectionId); } d->m_idToCollectionHash.insert(collectionId, *collection); d->emitSharedSignals(&cs); *error = QOrganizerManager::NoError; return true; } bool QOrganizerItemMemoryEngine::removeCollection(const QOrganizerCollectionId& collectionId, QOrganizerManager::Error* error) { if (collectionId == d->defaultCollectionId()) { // attempting to remove the default collection. this is not allowed in the memory engine. *error = QOrganizerManager::PermissionsError; return false; } // try to find the collection to remove it (and the items it contains) if (d->m_idToCollectionHash.contains(collectionId)) { // found the collection to remove. remove the items in the collection. const QList itemsToRemove = d->m_itemsInCollectionsHash.values(collectionId); if (!itemsToRemove.isEmpty()) { QMap errorMap; if (!removeItems(itemsToRemove, &errorMap, error)) { // without transaction support, we can't back out. but the operation should fail. return false; } } // now remove the collection from our lists. d->m_idToCollectionHash.remove(collectionId); d->m_itemsInCollectionsHash.remove(collectionId); QOrganizerCollectionChangeSet cs; cs.insertRemovedCollection(collectionId); d->emitSharedSignals(&cs); *error = QOrganizerManager::NoError; return true; } // the collection doesn't exist... *error = QOrganizerManager::DoesNotExistError; return false; } /*! \reimp */ void QOrganizerItemMemoryEngine::requestDestroyed(QOrganizerAbstractRequest* req) { Q_UNUSED(req); } /*! \reimp */ bool QOrganizerItemMemoryEngine::startRequest(QOrganizerAbstractRequest* req) { updateRequestState(req, QOrganizerAbstractRequest::ActiveState); performAsynchronousOperation(req); return true; } /*! \reimp */ bool QOrganizerItemMemoryEngine::cancelRequest(QOrganizerAbstractRequest* req) { Q_UNUSED(req); // we can't cancel since we complete immediately return false; } /*! \reimp */ bool QOrganizerItemMemoryEngine::waitForRequestFinished(QOrganizerAbstractRequest* req, int msecs) { // in our implementation, we always complete any operation we start. Q_UNUSED(msecs); Q_UNUSED(req); return true; } QList QOrganizerItemMemoryEngine::supportedItemDetails(QOrganizerItemType::ItemType itemType) const { QList supportedDetails; supportedDetails << QOrganizerItemDetail::TypeItemType << QOrganizerItemDetail::TypeGuid << QOrganizerItemDetail::TypeTimestamp << QOrganizerItemDetail::TypeDisplayLabel << QOrganizerItemDetail::TypeDescription << QOrganizerItemDetail::TypeComment << QOrganizerItemDetail::TypeTag << QOrganizerItemDetail::TypeClassification << QOrganizerItemDetail::TypeExtendedDetail; if (itemType == QOrganizerItemType::TypeEvent) { supportedDetails << QOrganizerItemDetail::TypeRecurrence << QOrganizerItemDetail::TypeEventTime << QOrganizerItemDetail::TypePriority << QOrganizerItemDetail::TypeLocation << QOrganizerItemDetail::TypeReminder << QOrganizerItemDetail::TypeAudibleReminder << QOrganizerItemDetail::TypeEmailReminder << QOrganizerItemDetail::TypeVisualReminder; } else if (itemType == QOrganizerItemType::TypeTodo) { supportedDetails << QOrganizerItemDetail::TypeRecurrence << QOrganizerItemDetail::TypeTodoTime << QOrganizerItemDetail::TypePriority << QOrganizerItemDetail::TypeTodoProgress << QOrganizerItemDetail::TypeReminder << QOrganizerItemDetail::TypeAudibleReminder << QOrganizerItemDetail::TypeEmailReminder << QOrganizerItemDetail::TypeVisualReminder; } else if (itemType == QOrganizerItemType::TypeEventOccurrence) { supportedDetails << QOrganizerItemDetail::TypeParent << QOrganizerItemDetail::TypeEventTime << QOrganizerItemDetail::TypePriority << QOrganizerItemDetail::TypeLocation << QOrganizerItemDetail::TypeReminder << QOrganizerItemDetail::TypeAudibleReminder << QOrganizerItemDetail::TypeEmailReminder << QOrganizerItemDetail::TypeVisualReminder; } else if (itemType == QOrganizerItemType::TypeTodoOccurrence) { supportedDetails << QOrganizerItemDetail::TypeParent << QOrganizerItemDetail::TypeTodoTime << QOrganizerItemDetail::TypePriority << QOrganizerItemDetail::TypeTodoProgress << QOrganizerItemDetail::TypeReminder << QOrganizerItemDetail::TypeAudibleReminder << QOrganizerItemDetail::TypeEmailReminder << QOrganizerItemDetail::TypeVisualReminder; } else if (itemType == QOrganizerItemType::TypeJournal) { supportedDetails << QOrganizerItemDetail::TypeJournalTime; } else if (itemType == QOrganizerItemType::TypeNote) { // nothing ;) } else { supportedDetails.clear(); } return supportedDetails; } /*! * This slot is called some time after an asynchronous request is started. * It performs the required operation, sets the result and returns. */ void QOrganizerItemMemoryEngine::performAsynchronousOperation(QOrganizerAbstractRequest *currentRequest) { // store up changes, and emit signals once at the end of the (possibly batch) operation. QOrganizerItemChangeSet changeSet; // Now perform the active request and emit required signals. Q_ASSERT(currentRequest->state() == QOrganizerAbstractRequest::ActiveState); switch (currentRequest->type()) { case QOrganizerAbstractRequest::ItemFetchRequest: { QOrganizerItemFetchRequest* r = static_cast(currentRequest); QOrganizerItemFilter filter = r->filter(); QList sorting = r->sorting(); QOrganizerItemFetchHint fetchHint = r->fetchHint(); QDateTime startDate = r->startDate(); QDateTime endDate = r->endDate(); QOrganizerManager::Error operationError = QOrganizerManager::NoError; QList requestedOrganizerItems = items(filter, startDate, endDate, -1, sorting, fetchHint, &operationError); // update the request with the results. if (!requestedOrganizerItems.isEmpty() || operationError != QOrganizerManager::NoError) updateItemFetchRequest(r, requestedOrganizerItems, operationError, QOrganizerAbstractRequest::FinishedState); else updateRequestState(currentRequest, QOrganizerAbstractRequest::FinishedState); } break; case QOrganizerAbstractRequest::ItemFetchByIdRequest: { QOrganizerItemFetchByIdRequest* r = static_cast(currentRequest); // fetch hint cannot be used in memory backend QOrganizerManager::Error operationError = QOrganizerManager::NoError; QMap errorMap; QList requestedOrganizerItems; for (int i = 0; i < r->ids().size(); i++) { QOrganizerItem item = d->m_idToItemHash.value(r->ids().at(i), QOrganizerItem()); requestedOrganizerItems.append(item); if (item.isEmpty()) errorMap.insert(i, QOrganizerManager::DoesNotExistError); } // update the request with the results. if (!requestedOrganizerItems.isEmpty() || operationError != QOrganizerManager::NoError || !errorMap.isEmpty()) QOrganizerManagerEngine::updateItemFetchByIdRequest(r, requestedOrganizerItems, operationError, errorMap, QOrganizerAbstractRequest::FinishedState); else updateRequestState(currentRequest, QOrganizerAbstractRequest::FinishedState); } break; case QOrganizerAbstractRequest::ItemFetchForExportRequest: { QOrganizerItemFetchForExportRequest* r = static_cast(currentRequest); QOrganizerItemFilter filter = r->filter(); QList sorting = r->sorting(); QOrganizerItemFetchHint fetchHint = r->fetchHint(); QDateTime startDate = r->startDate(); QDateTime endDate = r->endDate(); QOrganizerManager::Error operationError = QOrganizerManager::NoError; QList requestedOrganizerItems = itemsForExport(startDate, endDate, filter, sorting, fetchHint, &operationError); // update the request with the results. if (!requestedOrganizerItems.isEmpty() || operationError != QOrganizerManager::NoError) updateItemFetchForExportRequest(r, requestedOrganizerItems, operationError, QOrganizerAbstractRequest::FinishedState); else updateRequestState(currentRequest, QOrganizerAbstractRequest::FinishedState); } break; case QOrganizerAbstractRequest::ItemOccurrenceFetchRequest: { QOrganizerItemOccurrenceFetchRequest* r = static_cast(currentRequest); QOrganizerItem parentItem(r->parentItem()); QDateTime startDate(r->startDate()); QDateTime endDate(r->endDate()); int countLimit = r->maxOccurrences(); QOrganizerItemFetchHint fetchHint = r->fetchHint(); QOrganizerManager::Error operationError = QOrganizerManager::NoError; QList requestedOrganizerItems = itemOccurrences(parentItem, startDate, endDate, countLimit, fetchHint, &operationError); // update the request with the results. if (!requestedOrganizerItems.isEmpty() || operationError != QOrganizerManager::NoError) updateItemOccurrenceFetchRequest(r, requestedOrganizerItems, operationError, QOrganizerAbstractRequest::FinishedState); else updateRequestState(currentRequest, QOrganizerAbstractRequest::FinishedState); } break; case QOrganizerAbstractRequest::ItemIdFetchRequest: { QOrganizerItemIdFetchRequest* r = static_cast(currentRequest); QOrganizerItemFilter filter = r->filter(); QList sorting = r->sorting(); QDateTime startDate = r->startDate(); QDateTime endDate = r->endDate(); QOrganizerManager::Error operationError = QOrganizerManager::NoError; QList requestedOrganizerItemIds = itemIds(filter, startDate, endDate, sorting, &operationError); if (!requestedOrganizerItemIds.isEmpty() || operationError != QOrganizerManager::NoError) updateItemIdFetchRequest(r, requestedOrganizerItemIds, operationError, QOrganizerAbstractRequest::FinishedState); else updateRequestState(currentRequest, QOrganizerAbstractRequest::FinishedState); } break; case QOrganizerAbstractRequest::ItemSaveRequest: { QOrganizerItemSaveRequest* r = static_cast(currentRequest); QList organizeritems = r->items(); QOrganizerManager::Error operationError = QOrganizerManager::NoError; QMap errorMap; saveItems(&organizeritems, &errorMap, &operationError); updateItemSaveRequest(r, organizeritems, operationError, errorMap, QOrganizerAbstractRequest::FinishedState); } break; case QOrganizerAbstractRequest::ItemRemoveRequest: { QOrganizerItemRemoveRequest* r = static_cast(currentRequest); QOrganizerManager::Error operationError = QOrganizerManager::NoError; QList organizeritemsToRemove = r->items(); QSet removedParentIds; QMap errorMap; for (int i = 0; i < organizeritemsToRemove.size(); i++) { QOrganizerItem item = organizeritemsToRemove[i]; QOrganizerManager::Error tempError = QOrganizerManager::NoError; if ((item.type() == QOrganizerItemType::TypeEventOccurrence || item.type() == QOrganizerItemType::TypeTodoOccurrence) && item.id().isNull()) { // this is a generated occurrence, modify parent items exception dates QOrganizerItemParent parentDetail = item.detail(QOrganizerItemDetail::TypeParent); if (removedParentIds.isEmpty() || !removedParentIds.contains(parentDetail.parentId())) removeOccurrence(item, changeSet, &tempError); } else { removeItem(item.id(), changeSet, &tempError); if (tempError == QOrganizerManager::NoError && itemHasReccurence(item)) removedParentIds.insert(item.id()); } if (tempError != QOrganizerManager::NoError) { errorMap.insert(i, tempError); operationError = tempError; } } if (!errorMap.isEmpty() || operationError != QOrganizerManager::NoError) updateItemRemoveRequest(r, operationError, errorMap, QOrganizerAbstractRequest::FinishedState); else updateRequestState(currentRequest, QOrganizerAbstractRequest::FinishedState); } break; case QOrganizerAbstractRequest::ItemRemoveByIdRequest: { QOrganizerItemRemoveByIdRequest* r = static_cast(currentRequest); QOrganizerManager::Error operationError = QOrganizerManager::NoError; QList organizeritemsToRemove = r->itemIds(); QMap errorMap; for (int i = 0; i < organizeritemsToRemove.size(); i++) { QOrganizerManager::Error tempError = QOrganizerManager::NoError; removeItem(organizeritemsToRemove.at(i), changeSet, &tempError); if (tempError != QOrganizerManager::NoError) { errorMap.insert(i, tempError); operationError = tempError; } } if (!errorMap.isEmpty() || operationError != QOrganizerManager::NoError) updateItemRemoveByIdRequest(r, operationError, errorMap, QOrganizerAbstractRequest::FinishedState); else updateRequestState(currentRequest, QOrganizerAbstractRequest::FinishedState); } break; case QOrganizerAbstractRequest::CollectionFetchRequest: { QOrganizerCollectionFetchRequest* r = static_cast(currentRequest); QOrganizerManager::Error operationError = QOrganizerManager::NoError; QList requestedOrganizerCollections = collections(&operationError); // update the request with the results. updateCollectionFetchRequest(r, requestedOrganizerCollections, operationError, QOrganizerAbstractRequest::FinishedState); } break; case QOrganizerAbstractRequest::CollectionSaveRequest: { QOrganizerCollectionSaveRequest* r = static_cast(currentRequest); QList collections = r->collections(); QList retn; QOrganizerManager::Error operationError = QOrganizerManager::NoError; QMap errorMap; for (int i = 0; i < collections.size(); ++i) { QOrganizerManager::Error tempError = QOrganizerManager::NoError; QOrganizerCollection curr = collections.at(i); if (!saveCollection(&curr, &tempError)) { errorMap.insert(i, tempError); operationError = tempError; } retn.append(curr); } updateCollectionSaveRequest(r, retn, operationError, errorMap, QOrganizerAbstractRequest::FinishedState); } break; case QOrganizerAbstractRequest::CollectionRemoveRequest: { // removes the collections identified in the list of ids. QOrganizerCollectionRemoveRequest* r = static_cast(currentRequest); QOrganizerManager::Error operationError = QOrganizerManager::NoError; QList collectionsToRemove = r->collectionIds(); QMap errorMap; for (int i = 0; i < collectionsToRemove.size(); i++) { QOrganizerManager::Error tempError = QOrganizerManager::NoError; removeCollection(collectionsToRemove.at(i), &tempError); if (tempError != QOrganizerManager::NoError) { errorMap.insert(i, tempError); operationError = tempError; } } if (!errorMap.isEmpty() || operationError != QOrganizerManager::NoError) updateCollectionRemoveRequest(r, operationError, errorMap, QOrganizerAbstractRequest::FinishedState); else updateRequestState(currentRequest, QOrganizerAbstractRequest::FinishedState); } break; default: // unknown request type. break; } // now emit any signals we have to emit d->emitSharedSignals(&changeSet); } #include "moc_qorganizeritemmemorybackend_p.cpp" QT_END_NAMESPACE_ORGANIZER src/plugins/organizer/memory/qorganizeritemmemorybackend_p.h000066400000000000000000000326661233466112000251420ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTMEMORYBACKEND_P_H #define QCONTACTMEMORYBACKEND_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemMemoryFactory : public QOrganizerManagerEngineFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QOrganizerManagerEngineFactoryInterface" FILE "memory.json") public: QOrganizerManagerEngine* engine(const QMap& parameters, QOrganizerManager::Error*); QOrganizerItemEngineId* createItemEngineId(const QMap& parameters, const QString& idString) const; QOrganizerCollectionEngineId* createCollectionEngineId(const QMap& parameters, const QString& idString) const; QString managerName() const; }; class QOrganizerItemMemoryEngineId : public QOrganizerItemEngineId { public: QOrganizerItemMemoryEngineId(); QOrganizerItemMemoryEngineId(quint32 collectionId, quint32 itemId, const QString& managerUri); ~QOrganizerItemMemoryEngineId(); QOrganizerItemMemoryEngineId(const QOrganizerItemMemoryEngineId& other); QOrganizerItemMemoryEngineId(const QString& idString); bool isEqualTo(const QOrganizerItemEngineId* other) const; bool isLessThan(const QOrganizerItemEngineId* other) const; QString managerUri() const; QOrganizerItemEngineId* clone() const; QString toString() const; #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const; #endif uint hash() const; private: quint32 m_collectionId; quint32 m_itemId; QString m_managerUri; friend class QOrganizerItemMemoryEngine; }; class QOrganizerCollectionMemoryEngineId : public QOrganizerCollectionEngineId { public: QOrganizerCollectionMemoryEngineId(); QOrganizerCollectionMemoryEngineId(quint32 collectionId, const QString& managerUri); ~QOrganizerCollectionMemoryEngineId(); QOrganizerCollectionMemoryEngineId(const QOrganizerCollectionMemoryEngineId& other); QOrganizerCollectionMemoryEngineId(const QString& idString); bool isEqualTo(const QOrganizerCollectionEngineId* other) const; bool isLessThan(const QOrganizerCollectionEngineId* other) const; QString managerUri() const; QOrganizerCollectionEngineId* clone() const; QString toString() const; #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const; #endif uint hash() const; private: quint32 m_collectionId; QString m_managerUri; friend class QOrganizerItemMemoryEngine; }; class QOrganizerAbstractRequest; class QOrganizerManagerEngine; class QOrganizerItemMemoryEngineData : public QSharedData { public: QOrganizerItemMemoryEngineData(); ~QOrganizerItemMemoryEngineData() { } QString m_id; // the id parameter value inline QOrganizerCollectionId defaultCollectionId() const { enum { DefaultCollectionLocalId = 1 }; // default collection has id of 1. return QOrganizerCollectionId(new QOrganizerCollectionMemoryEngineId(DefaultCollectionLocalId, m_managerUri)); } QHash m_idToItemHash; // hash of id to the item identified by that id QMultiHash m_parentIdToChildIdHash; // hash of id to that item's children's ids QHash m_idToCollectionHash; // hash of id to the collection identified by that id QMultiHash m_itemsInCollectionsHash; // hash of collection ids to the ids of items the collection contains. quint32 m_nextOrganizerItemId; // the m_itemId portion of a QOrganizerItemMemoryEngineId. quint32 m_nextOrganizerCollectionId; // the m_collectionId portion of a QOrganizerCollectionMemoryEngineId. QString m_managerUri; // for faster lookup. void emitSharedSignals(QOrganizerCollectionChangeSet *cs) { foreach (QOrganizerManagerEngine *engine, m_sharedEngines) cs->emitSignals(engine); } void emitSharedSignals(QOrganizerItemChangeSet* cs) { foreach(QOrganizerManagerEngine* engine, m_sharedEngines) cs->emitSignals(engine); } QList m_sharedEngines; // The list of engines that share this data }; class QOrganizerItemMemoryEngine : public QOrganizerManagerEngine { Q_OBJECT public: static QOrganizerItemMemoryEngine *createMemoryEngine(const QMap& parameters); ~QOrganizerItemMemoryEngine(); /* URI reporting */ QString managerName() const; QMap managerParameters() const; // items QList items(const QList &itemIds, const QOrganizerItemFetchHint &fetchHint, QMap *errorMap, QOrganizerManager::Error *error); QList items(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error); QList itemIds(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, const QList &sortOrders, QOrganizerManager::Error *error); QList itemOccurrences(const QOrganizerItem &parentItem, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error); QList itemsForExport(const QDateTime &startDateTime, const QDateTime &endDateTime, const QOrganizerItemFilter &filter, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error); bool saveItems(QList *items, const QList &detailMask, QMap *errorMap, QOrganizerManager::Error *error); bool removeItems(const QList &itemIds, QMap *errorMap, QOrganizerManager::Error *error); bool removeItems(const QList *items, QMap* errorMap, QOrganizerManager::Error* error); // collections QOrganizerCollection defaultCollection(QOrganizerManager::Error* error); QOrganizerCollection collection(const QOrganizerCollectionId &collectionId, QOrganizerManager::Error *error); QList collections(QOrganizerManager::Error* error); bool saveCollection(QOrganizerCollection* collection, QOrganizerManager::Error* error); bool removeCollection(const QOrganizerCollectionId& collectionId, QOrganizerManager::Error* error); /* Asynchronous Request Support */ virtual void requestDestroyed(QOrganizerAbstractRequest* req); virtual bool startRequest(QOrganizerAbstractRequest* req); virtual bool cancelRequest(QOrganizerAbstractRequest* req); virtual bool waitForRequestFinished(QOrganizerAbstractRequest* req, int msecs); /* Capabilities reporting */ /*! \reimp */ virtual QList supportedFilters() const { QList supported; supported << QOrganizerItemFilter::InvalidFilter << QOrganizerItemFilter::DetailFilter << QOrganizerItemFilter::DetailFieldFilter << QOrganizerItemFilter::DetailRangeFilter << QOrganizerItemFilter::IntersectionFilter << QOrganizerItemFilter::UnionFilter << QOrganizerItemFilter::IdFilter << QOrganizerItemFilter::CollectionFilter << QOrganizerItemFilter::DefaultFilter; return supported; } /*! \reimp */ virtual QList supportedItemDetails(QOrganizerItemType::ItemType itemType) const; /*! \reimp */ virtual QList supportedItemTypes() const { return QList() << QOrganizerItemType::TypeEvent << QOrganizerItemType::TypeEventOccurrence << QOrganizerItemType::TypeJournal << QOrganizerItemType::TypeNote << QOrganizerItemType::TypeTodo << QOrganizerItemType::TypeTodoOccurrence; } protected: QOrganizerItemMemoryEngine(QOrganizerItemMemoryEngineData* data); protected: /* Implement "signal coalescing" for batch functions via change set */ virtual bool saveItem(QOrganizerItem* theOrganizerItem, QOrganizerItemChangeSet& changeSet, QOrganizerManager::Error* error); virtual bool removeItem(const QOrganizerItemId& organizeritemId, QOrganizerItemChangeSet& changeSet, QOrganizerManager::Error* error); virtual bool removeOccurrence(const QOrganizerItem& organizeritem, QOrganizerItemChangeSet& changeSet, QOrganizerManager::Error* error); private: QOrganizerItem item(const QOrganizerItemId& organizeritemId) const; bool saveItems(QList* organizeritems, QMap* errorMap, QOrganizerManager::Error* error); QList itemsForExport(const QList &ids, const QOrganizerItemFetchHint &fetchHint, QMap *errorMap, QOrganizerManager::Error *error); QList internalItems(const QDateTime& startDate, const QDateTime& endDate, const QOrganizerItemFilter& filter, const QList& sortOrders, const QOrganizerItemFetchHint& fetchHint, QOrganizerManager::Error* error, bool forExport) const; QList internalItemOccurrences(const QOrganizerItem& parentItem, const QDateTime& periodStart, const QDateTime& periodEnd, int maxCount, bool includeExceptions, bool sortItems, QList *exceptionDates, QOrganizerManager::Error* error) const; void addItemRecurrences(QList& sorted, const QOrganizerItem& c, const QDateTime& startDate, const QDateTime& endDate, const QOrganizerItemFilter& filter, const QList& sortOrders, bool forExport, QSet* parentsAdded) const; bool fixOccurrenceReferences(QOrganizerItem* item, QOrganizerManager::Error* error); bool typesAreRelated(QOrganizerItemType::ItemType occurrenceType, QOrganizerItemType::ItemType parentType); void performAsynchronousOperation(QOrganizerAbstractRequest* request); QOrganizerItemMemoryEngineData* d; }; QT_END_NAMESPACE_ORGANIZER #endif // QCONTACTMEMORYBACKEND_P_H src/plugins/organizer/organizer.pro000066400000000000000000000003441233466112000200470ustar00rootroot00000000000000TEMPLATE = subdirs CONFIG += ordered qtHaveModule(jsondb): SUBDIRS += jsondb # Only compile this for tests (to make sure it compiles).. don't deploy this contains(QT_BUILD_PARTS,tests): SUBDIRS += skeleton SUBDIRS += memory src/plugins/organizer/skeleton/000077500000000000000000000000001233466112000171505ustar00rootroot00000000000000src/plugins/organizer/skeleton/qorganizerskeleton.cpp000066400000000000000000001004331233466112000236030ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qorganizerskeleton_p.h" #ifndef QT_NO_DEBUG_STREAM #include #endif QT_BEGIN_NAMESPACE_ORGANIZER QOrganizerManagerEngine* QOrganizerItemSkeletonFactory::engine(const QMap& parameters, QOrganizerManager::Error* error) { Q_UNUSED(parameters); Q_UNUSED(error); /* TODO - if you understand any specific parameters. save them in the engine so that engine::managerParameters can return them */ QOrganizerItemSkeletonEngine* ret = new QOrganizerItemSkeletonEngine(); // manager takes ownership and will clean up. return ret; } QOrganizerItemEngineId* QOrganizerItemSkeletonFactory::createItemEngineId(const QMap& parameters, const QString& idString) const { /* TODO Instantiate your engine-specific item id in this function. If idString is not empty, then you should deserialize the idString (the opposite of your QOrganizerItemEngineId derived-class' toString() function), otherwise you should instantiate an empty engine-specific collection id. This function allows clients to deserialize serialized ids from your engine. */ Q_UNUSED(parameters); QOrganizerItemSkeletonEngineId* retn = new QOrganizerItemSkeletonEngineId; if (!idString.isEmpty()) retn->m_itemId = idString.toUInt(); return retn; } QOrganizerCollectionEngineId* QOrganizerItemSkeletonFactory::createCollectionEngineId(const QMap& parameters, const QString& idString) const { /* TODO Instantiate your engine-specific collection id in this function. If idString is not empty, then you should deserialize the idString (the opposite of your QOrganizerCollectionEngineId derived-class' toString() function), otherwise you should instantiate an empty engine-specific collection id. This function allows clients to deserialize serialized ids from your engine. */ Q_UNUSED(parameters); QOrganizerCollectionSkeletonEngineId* retn = new QOrganizerCollectionSkeletonEngineId; if (!idString.isEmpty()) retn->m_collectionId = idString.toUInt(); return retn; } QString QOrganizerItemSkeletonFactory::managerName() const { /* TODO - put your engine name here */ return QStringLiteral("skeleton"); } QOrganizerItemSkeletonEngineId::QOrganizerItemSkeletonEngineId() : QOrganizerItemEngineId(), m_itemId(0) { /* TODO Initialize any data members of your engine-specific item id in the constructor. This default constructor should not be used when returning a null id, but is provided in order to allow use of the ids in a list, and as an enabler for the implementation of QOrganizerItemId. When returning a null id, the backend should simply return a default constructed QOrganizerItemId. In this example, we use just a single quint32 to identify the item, however your engine may require more information in order to uniquely identify an item within it (e.g., a collection identifier plus an item identifier, and perhaps a datastore identifier which identifies the datastore in which the collection can be found). */ } QOrganizerItemSkeletonEngineId::QOrganizerItemSkeletonEngineId(quint32 itemId) : QOrganizerItemEngineId(), m_itemId(itemId) { /* TODO Whatever data members your particular class has, should be passed as arguments to a ctor of this type. This is the constructor which will be used by your engine code. In particular, you will most likely be returning to clients an id by calling: QOrganizerItemId id(new QOrganizerItemSkeletonEngineId(3)); or something similar. Note that the QOrganizerItemId constructor which takes a QOrganizerItemEngineId pointer as a parameter takes ownership of that pointer (and so controls its lifetime). */ } QOrganizerItemSkeletonEngineId::QOrganizerItemSkeletonEngineId(const QOrganizerItemSkeletonEngineId& other) : QOrganizerItemEngineId(), m_itemId(other.m_itemId) { /* TODO - implement a copy constructor for your engine-specific id class */ } QOrganizerItemSkeletonEngineId::~QOrganizerItemSkeletonEngineId() { /* TODO - Clean up any memory in use by your engine-specific id. */ } bool QOrganizerItemSkeletonEngineId::isEqualTo(const QOrganizerItemEngineId* other) const { /* TODO The isEqualTo(other) function is called by the QOrganizerItemId::operator==(other) function. You must implement this in terms of the data members which your class contains. An example implementation is provided below, for the case where only a single quint32 is required to uniquely identify an item in a manager. */ quint32 otherItemId = static_cast(other)->m_itemId; if (m_itemId != otherItemId) return false; return true; } bool QOrganizerItemSkeletonEngineId::isLessThan(const QOrganizerItemEngineId* other) const { /* TODO The isLessThan(other) function is called by the QOrganizerItemId::operator<(other) function. You must implement this in terms of the data members which your class contains. An example implementation is provided below, for the case where only a single quint32 is required to uniquely identify an item in a manager. */ quint32 otherItemId = static_cast(other)->m_itemId; return (m_itemId < otherItemId); } QString QOrganizerItemSkeletonEngineId::managerUri() const { // TODO: make this return the actual managerUri (including params) of the // engine it is associated with static const QString uri(QStringLiteral("qtorganizer:skeleton:")); return uri; } QOrganizerItemEngineId* QOrganizerItemSkeletonEngineId::clone() const { /* TODO When a QOrganizerItemId is copied or assigned, it performs a clone of the engine-specific id. This function is called in that case. Implement this function so that the data members of your engine-specific id are deep-copied. An example implementation for the case where an item can be uniquely identified with just a single quint32 is given below. */ QOrganizerItemSkeletonEngineId *myClone = new QOrganizerItemSkeletonEngineId; myClone->m_itemId = m_itemId; return myClone; } #ifndef QT_NO_DEBUG_STREAM QDebug& QOrganizerItemSkeletonEngineId::debugStreamOut(QDebug& dbg) const { /* TODO In order to allow clients to debug applications, you must implement this function. We recommend streaming the name of your class followed by the values of the data members in your engine-specific id class in parentheses. An example implementation for the case where an item can be uniquely identified with just a single quint32 is given below. Note that you must include the #ifndef QT_NO_DEBUG_STREAM preprocessor directive block in order to ensure compilation in environments where that directive is defined. */ dbg.nospace() << "QOrganizerItemSkeletonEngineId(" << m_itemId << ")"; return dbg.maybeSpace(); } #endif QString QOrganizerItemSkeletonEngineId::toString() const { /* TODO In order to allow clients to serialize QOrganizerItemId's, you must implement this function. An example implementation for the case where an item can be uniquely identified with just a single quint32 is given below. */ return QString::number(m_itemId); } uint QOrganizerItemSkeletonEngineId::hash() const { /* TODO Provide a hash function for your engine-specific id. Note that the hash doesn't strictly need to be unique, since isEqualTo() ensures that individual id's in a single hash-bucket can be uniquely determined; however a better hash function will result in better performance because the ids will be distributed more randomly in a hash table. In the example implementation below, we could simply return the id, since the id is a quint32. In more complex id classes, however, you may need to qHash() individual data members and combine the results somehow. */ return QT_PREPEND_NAMESPACE(qHash)(m_itemId); } QOrganizerCollectionSkeletonEngineId::QOrganizerCollectionSkeletonEngineId() : QOrganizerCollectionEngineId(), m_collectionId(0) { /* TODO Initialize any data members of your engine-specific collection id in the constructor. This default constructor should not be used when returning a null id, but is provided in order to allow use of the ids in a list, and as an enabler for the implementation of QOrganizerCollectionId. When returning a null id, the backend should simply return a default constructed QOrganizerCollectionId. In this example, we use just a single quint32 to identify the collection, however your engine may require more information in order to uniquely identify a collection within it (e.g., a collection identifier plus a datastore identifier which identifies the datastore in which the collection can be found). */ } QOrganizerCollectionSkeletonEngineId::QOrganizerCollectionSkeletonEngineId(quint32 collectionId) : QOrganizerCollectionEngineId(), m_collectionId(collectionId) { /* TODO Whatever data members your particular class has, should be passed as arguments to a ctor of this type. This is the constructor which will be used by your engine code. In particular, you will most likely be returning to clients an id by calling: QOrganizerCollectionId id(new QOrganizerCollectionSkeletonEngineId(3)); or something similar. Note that the QOrganizerCollectionId constructor which takes a QOrganizerCollectionEngineId pointer as a parameter takes ownership of that pointer (and so controls its lifetime). */ } QOrganizerCollectionSkeletonEngineId::QOrganizerCollectionSkeletonEngineId(const QOrganizerCollectionSkeletonEngineId& other) : QOrganizerCollectionEngineId(), m_collectionId(other.m_collectionId) { /* TODO - implement a copy constructor for your engine-specific id class */ } QOrganizerCollectionSkeletonEngineId::~QOrganizerCollectionSkeletonEngineId() { /* TODO - Clean up any memory in use by your engine-specific id. */ } bool QOrganizerCollectionSkeletonEngineId::isEqualTo(const QOrganizerCollectionEngineId* other) const { /* TODO The isEqualTo(other) function is called by the QOrganizerCollectionId::operator==(other) function. You must implement this in terms of the data members which your class contains. An example implementation is provided below, for the case where only a single quint32 is required to uniquely identify a collection in a manager. */ quint32 otherCollectionId = static_cast(other)->m_collectionId; if (m_collectionId != otherCollectionId) return false; return true; } bool QOrganizerCollectionSkeletonEngineId::isLessThan(const QOrganizerCollectionEngineId* other) const { /* TODO The isLessThan(other) function is called by the QOrganizerCollectionId::operator<(other) function. You must implement this in terms of the data members which your class contains. An example implementation is provided below, for the case where only a single quint32 is required to uniquely identify a collection in a manager. */ quint32 otherCollectionId = static_cast(other)->m_collectionId; if (m_collectionId < otherCollectionId) return true; return false; } QString QOrganizerCollectionSkeletonEngineId::managerUri() const { // TODO: make this return the actual managerUri (including params) of the // engine it is associated with static const QString uri(QStringLiteral("qtorganizer:skeleton:")); return uri; } QOrganizerCollectionEngineId* QOrganizerCollectionSkeletonEngineId::clone() const { /* TODO When a QOrganizerCollectionId is copied or assigned, it performs a clone of the engine-specific id. This function is called in that case. Implement this function so that the data members of your engine-specific id are deep-copied. An example implementation for the case where a collection can be uniquely identified with just a single quint32 is given below. */ QOrganizerCollectionSkeletonEngineId *myClone = new QOrganizerCollectionSkeletonEngineId; myClone->m_collectionId = m_collectionId; return myClone; } #ifndef QT_NO_DEBUG_STREAM QDebug& QOrganizerCollectionSkeletonEngineId::debugStreamOut(QDebug& dbg) const { /* TODO In order to allow clients to debug applications, you must implement this function. We recommend streaming the name of your class followed by the values of the data members in your engine-specific id class in parentheses. An example implementation for the case where a collection can be uniquely identified with just a single quint32 is given below. Note that you must include the #ifndef QT_NO_DEBUG_STREAM preprocessor directive block in order to ensure compilation in environments where that directive is defined. */ dbg.nospace() << "QOrganizerCollectionSkeletonEngineId(" << m_collectionId << ")"; return dbg.maybeSpace(); } #endif QString QOrganizerCollectionSkeletonEngineId::toString() const { /* TODO In order to allow clients to serialize QOrganizerCollectionId's, you must implement this function. An example implementation for the case where a collection can be uniquely identified with just a single quint32 is given below. */ return QString::number(m_collectionId); } uint QOrganizerCollectionSkeletonEngineId::hash() const { /* TODO Provide a hash function for your engine-specific id. Note that the hash doesn't strictly need to be unique, since isEqualTo() ensures that individual id's in a single hash-bucket can be uniquely determined; however a better hash function will result in better performance because the ids will be distributed more randomly in a hash table. In the example implementation below, we could simply return the id, since the id is a quint32. In more complex id classes, however, you may need to qHash() individual data members and combine the results somehow. */ return QT_PREPEND_NAMESPACE(qHash)(m_collectionId); } QOrganizerItemSkeletonEngine::~QOrganizerItemSkeletonEngine() { /* TODO clean up your stuff. Perhaps a QScopedPointer or QSharedDataPointer would be in order */ } QString QOrganizerItemSkeletonEngine::managerName() const { /* TODO - put your engine name here */ return QStringLiteral("skeleton"); } QMap QOrganizerItemSkeletonEngine::managerParameters() const { /* TODO - in case you have any actual parameters that are relevant that you saved in the factory method, return them here */ return QMap(); } QList QOrganizerItemSkeletonEngine::itemOccurrences(const QOrganizerItem &parentItem, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error) { /* TODO This function should create a list of occurrences that occur in the time period from the supplied item, generated from the parent item. The periodStart should always be valid, and either the periodEnd or the maxCount will be valid (if periodEnd is valid, use that. Otherwise use the count). It's permissible to limit the number of items returned... Basically, if the parent item is an Event, a list of EventOccurrences should be returned. Similarly for Todo/TodoOccurrence. If there are no instances, return an empty list. The returned items should have a QOrganizerItemParent detail that points to the parentItem and the original instance that the event would have occurred on (e.g. with an exception). They should not have recurrence information details in them. We might change the signature to split up the periodStart + periodEnd / periodStart + maxCount cases. */ return QOrganizerManagerEngine::itemOccurrences(parentItem, startDateTime, endDateTime, maxCount, fetchHint, error); } QList QOrganizerItemSkeletonEngine::itemsForExport(const QDateTime &startDateTime, const QDateTime &endDateTime, const QOrganizerItemFilter &filter, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error) { return QOrganizerManagerEngine::itemsForExport(startDateTime, endDateTime, filter, sortOrders, fetchHint, error); } QList QOrganizerItemSkeletonEngine::itemIds(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, const QList &sortOrders, QOrganizerManager::Error *error) { /* TODO Given the supplied filter and sort order, fetch the list of items [not instances] that correspond, and return their ids. If you don't support the filter or sort orders, you can fetch a partially (or un-) filtered list and ask the helper functions to filter and sort it for you. If you do have to fetch, consider setting a fetch hint that restricts the information to that needed for filtering/sorting. */ *error = QOrganizerManager::NotSupportedError; // TODO <- remove this QList partiallyFilteredItems; // = ..., your code here.. [TODO] QList ret; foreach(const QOrganizerItem& item, partiallyFilteredItems) { if (QOrganizerManagerEngine::isItemBetweenDates(item, startDateTime, endDateTime) && QOrganizerManagerEngine::testFilter(filter, item)) { QOrganizerManagerEngine::addSorted(&ret, item, sortOrders); } } return QOrganizerManager::extractIds(ret); } QList QOrganizerItemSkeletonEngine::items(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error) { /* TODO Given the supplied filter and sort order, fetch the list of items [not instances] that correspond, and return them. If you don't support the filter or sort orders, you can fetch a partially (or un-) filtered list and ask the helper functions to filter and sort it for you. The fetch hint suggests how much of the item to fetch. You can ignore the fetch hint and fetch everything (but you must fetch at least what is mentioned in the fetch hint). */ Q_UNUSED(startDateTime) Q_UNUSED(endDateTime) Q_UNUSED(maxCount) Q_UNUSED(fetchHint) *error = QOrganizerManager::NotSupportedError; // TODO <- remove this QList partiallyFilteredItems; // = ..., your code here.. [TODO] QList ret; foreach(const QOrganizerItem& item, partiallyFilteredItems) { if (QOrganizerManagerEngine::isItemBetweenDates(item, startDateTime, endDateTime) && QOrganizerManagerEngine::testFilter(filter, item)) { QOrganizerManagerEngine::addSorted(&ret, item, sortOrders); } } /* An alternative formulation, depending on how your engine is implemented is just: foreach(const QOrganizerItemId& id, itemIds(filter, sortOrders, error)) { ret.append(item(id, fetchHint, error); } */ return ret; } QList QOrganizerItemSkeletonEngine::items(const QList &itemIds, const QOrganizerItemFetchHint &fetchHint, QMap *errorMap, QOrganizerManager::Error* error) { /* TODO Fetch a single item by id. The fetch hint suggests how much of the item to fetch. You can ignore the fetch hint and fetch everything (but you must fetch at least what is mentioned in the fetch hint). */ return QOrganizerManagerEngine::items(itemIds, fetchHint, errorMap, error); } bool QOrganizerItemSkeletonEngine::saveItems(QList *items, const QList &detailMask, QMap* errorMap, QOrganizerManager::Error* error) { /* TODO Save a list of items into the collection specified in each (or their current collection if no collection is specified and they already exist, or the default collection if no collection is specified and they do not exist). For each item, convert it to your type, assign an item id, and update the QOrganizerItem's ID (in the list above - e.g. *items[idx] = updated item). Then, examine the collection id specified in each item and save the item in that collection. If you encounter an error (e.g. converting to type, or saving), insert an entry into the map above at the corresponding index (e.g. errorMap->insert(idx, QOIM::InvalidDetailError). You should set the "error" variable as well (first, last, most serious error etc). The item passed in should be validated according to the schema. */ return QOrganizerManagerEngine::saveItems(items, detailMask, errorMap, error); } bool QOrganizerItemSkeletonEngine::removeItems(const QList &itemIds, QMap *errorMap, QOrganizerManager::Error *error) { /* TODO Remove a list of items, given by their id. If you encounter an error, insert an error into the appropriate place in the error map, and update the error variable as well. DoesNotExistError should be used if the id refers to a non existent item. */ return QOrganizerManagerEngine::removeItems(itemIds, errorMap, error); } QOrganizerCollection QOrganizerItemSkeletonEngine::defaultCollection(QOrganizerManager::Error* error) { /* TODO This allows clients to determine which collection an item will be saved, if the item is saved via saveItems() without specifying a collection id of a collection in which to save the item, via item->setCollectionId(). There is always at least one collection in a manager, and all items are saved in exactly one collection. */ return QOrganizerManagerEngine::defaultCollection(error); } QOrganizerCollection QOrganizerItemSkeletonEngine::collection(const QOrganizerCollectionId& collectionId, QOrganizerManager::Error* error) { /* TODO This allows clients to retrieve a collection by (manager) id. Prior to saving items, clients will set which collection the item will/should be saved by calling item->setCollectionId(). */ return QOrganizerManagerEngine::collection(collectionId, error); } QList QOrganizerItemSkeletonEngine::collections(QOrganizerManager::Error* error) { /* TODO This allows clients to retrieve a list of all of the collections currently in this manager. Some backends will have a prepopulated list of valid collections, others will not. A collection can have properties like colour, description, perhaps a priority, etc etc. */ return QOrganizerManagerEngine::collections(error); } bool QOrganizerItemSkeletonEngine::saveCollection(QOrganizerCollection* collection, QOrganizerManager::Error* error) { /* TODO This allows clients to create or update collections if supported by the backend. */ return QOrganizerManagerEngine::saveCollection(collection, error); } bool QOrganizerItemSkeletonEngine::removeCollection(const QOrganizerCollectionId& collectionId, QOrganizerManager::Error* error) { /* TODO This allows clients to remove collections if supported by the backend. When a collection is removed, all items in the collection are removed. That is, they are _not_ transferred to another collection. If the user attempts to remove the collection which is the default collection, the backend may decide whether to fail (with a permissions error) or to succeed and arbitrarily choose another collection to be the default collection. */ return QOrganizerManagerEngine::removeCollection(collectionId, error); } bool QOrganizerItemSkeletonEngine::startRequest(QOrganizerAbstractRequest* req) { /* TODO This is the entry point to the async API. The request object describes the type of request (switch on req->type()). Req will not be null when called by the framework. Generally, you can queue the request and process them at some later time (probably in another thread). Once you start a request, call the updateRequestState and/or the specific updateXXXXXRequest functions to mark it in the active state. If your engine is particularly fast, or the operation involves only in memory data, you can process and complete the request here. That is probably not the case, though. Note that when the client is threaded, and the request might live on a different thread, you might need to be careful with locking. In particular, the request might be deleted while you are still working on it. In this case, your requestDestroyed function will be called while the request is still valid, and you should block in that function until your worker thread (etc) has been notified not to touch that request any more. We plan to provide some boiler plate code that will allow you to: 1) implement the sync functions, and have the async versions call the sync in another thread 2) or implement the async versions of the function, and have the sync versions call the async versions. It's not ready yet, though. Return true if the request can be started, false otherwise. You can set an error in the request if you like. */ return QOrganizerManagerEngine::startRequest(req); } bool QOrganizerItemSkeletonEngine::cancelRequest(QOrganizerAbstractRequest* req) { /* TODO Cancel an in progress async request. If not possible, return false from here. */ return QOrganizerManagerEngine::cancelRequest(req); } bool QOrganizerItemSkeletonEngine::waitForRequestFinished(QOrganizerAbstractRequest* req, int msecs) { /* TODO Wait for a request to complete (up to a max of msecs milliseconds). Return true if the request is finished (including if it was already). False otherwise. You should really implement this function, if nothing else than as a delay, since clients may call this in a loop. It's best to avoid processing events, if you can, or at least only process non-UI events. */ return QOrganizerManagerEngine::waitForRequestFinished(req, msecs); } void QOrganizerItemSkeletonEngine::requestDestroyed(QOrganizerAbstractRequest* req) { /* TODO This is called when a request is being deleted. It lets you know: 1) the client doesn't care about the request any more. You can still complete it if you feel like it. 2) you can't reliably access any properties of the request pointer any more. The pointer will be invalid once this function returns. This means that if you have a worker thread, you need to let that thread know that the request object is not valid and block until that thread acknowledges it. One way to do this is to have a QSet (or QMap) that tracks active requests, and insert into that set in startRequest, and remove in requestDestroyed (or when it finishes or is cancelled). Protect that set/map with a mutex, and make sure you take the mutex in the worker thread before calling any of the QOIAR::updateXXXXXXRequest functions. And be careful of lock ordering problems :D */ return QOrganizerManagerEngine::requestDestroyed(req); } QList QOrganizerItemSkeletonEngine::supportedFilters() const { // TODO if you engine can natively support the filter, return true. Otherwise you should emulate support in the item{Ids} functions. return QList(); } QList QOrganizerItemSkeletonEngine::supportedItemDetails(QOrganizerItemType::ItemType itemType) const { // TODO - return which [predefined] details this engine supports for this item type Q_UNUSED(itemType) return QList(); } QList QOrganizerItemSkeletonEngine::supportedItemTypes() const { // TODO - return which [predefined] types this engine supports QList ret; ret << QOrganizerItemType::TypeEvent; ret << QOrganizerItemType::TypeEventOccurrence; ret << QOrganizerItemType::TypeJournal; ret << QOrganizerItemType::TypeNote; ret << QOrganizerItemType::TypeTodo; ret << QOrganizerItemType::TypeTodoOccurrence; return ret; } #include "moc_qorganizerskeleton_p.cpp" QT_END_NAMESPACE_ORGANIZER src/plugins/organizer/skeleton/qorganizerskeleton_p.h000066400000000000000000000220351233466112000235700ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERSKELETON_P_H #define QORGANIZERSKELETON_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemSkeletonFactory : public QOrganizerManagerEngineFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QOrganizerManagerEngineFactoryInterface" FILE "skeleton.json") public: QOrganizerManagerEngine* engine(const QMap& parameters, QOrganizerManager::Error*); QOrganizerItemEngineId* createItemEngineId(const QMap& parameters, const QString& idString) const; QOrganizerCollectionEngineId* createCollectionEngineId(const QMap& parameters, const QString& idString) const; QString managerName() const; }; class QOrganizerCollectionSkeletonEngineId : public QOrganizerCollectionEngineId { public: QOrganizerCollectionSkeletonEngineId(); QOrganizerCollectionSkeletonEngineId(quint32 collectionId); ~QOrganizerCollectionSkeletonEngineId(); QOrganizerCollectionSkeletonEngineId(const QOrganizerCollectionSkeletonEngineId& other); bool isEqualTo(const QOrganizerCollectionEngineId* other) const; bool isLessThan(const QOrganizerCollectionEngineId* other) const; QString managerUri() const; QOrganizerCollectionEngineId* clone() const; QString toString() const; #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const; #endif uint hash() const; // data members: // Your backend can use whatever it likes as an id internally. // In this example, we use just a single quint32, but you can // use any datatype you need to (filename string, etc). quint32 m_collectionId; }; class QOrganizerItemSkeletonEngineId : public QOrganizerItemEngineId { public: QOrganizerItemSkeletonEngineId(); QOrganizerItemSkeletonEngineId(quint32 itemId); ~QOrganizerItemSkeletonEngineId(); QOrganizerItemSkeletonEngineId(const QOrganizerItemSkeletonEngineId& other); bool isEqualTo(const QOrganizerItemEngineId* other) const; bool isLessThan(const QOrganizerItemEngineId* other) const; QString managerUri() const; QOrganizerItemEngineId* clone() const; QString toString() const; #ifndef QT_NO_DEBUG_STREAM QDebug& debugStreamOut(QDebug& dbg) const; #endif uint hash() const; // data members: // Your backend can use whatever it likes as an id internally. // In this example, we use just a single quint32, but you can // use a pair of ints (one for collectionId, one for itemId) // or any other information (uuid string, etc). quint32 m_itemId; }; class QOrganizerItemSkeletonEngineData : public QSharedData { public: QOrganizerItemSkeletonEngineData() : QSharedData() { } QOrganizerItemSkeletonEngineData(const QOrganizerItemSkeletonEngineData& other) : QSharedData(other) { } ~QOrganizerItemSkeletonEngineData() { } }; class QOrganizerItemSkeletonEngine : public QOrganizerManagerEngine { Q_OBJECT public: static QOrganizerItemSkeletonEngine *createSkeletonEngine(const QMap& parameters); ~QOrganizerItemSkeletonEngine(); /* URI reporting */ QString managerName() const; QMap managerParameters() const; // items QList items(const QList &itemIds, const QOrganizerItemFetchHint &fetchHint, QMap *errorMap, QOrganizerManager::Error* error); QList items(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error); QList itemIds(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, const QList &sortOrders, QOrganizerManager::Error *error); QList itemOccurrences(const QOrganizerItem &parentItem, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error); QList itemsForExport(const QDateTime &startDateTime, const QDateTime &endDateTime, const QOrganizerItemFilter &filter, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error); bool saveItems(QList *items, const QList &detailMask, QMap *errorMap, QOrganizerManager::Error *error); bool removeItems(const QList &itemIds, QMap *errorMap, QOrganizerManager::Error *error); // collections QOrganizerCollection defaultCollection(QOrganizerManager::Error* error); QOrganizerCollection collection(const QOrganizerCollectionId& collectionId, QOrganizerManager::Error* error); QList collections(QOrganizerManager::Error* error); bool saveCollection(QOrganizerCollection* collection, QOrganizerManager::Error* error); bool removeCollection(const QOrganizerCollectionId& collectionId, QOrganizerManager::Error* error); /* Capabilities reporting */ QList supportedFilters() const; QList supportedItemDetails(QOrganizerItemType::ItemType itemType) const; QList supportedItemTypes() const; /* Asynchronous Request Support */ void requestDestroyed(QOrganizerAbstractRequest* req); bool startRequest(QOrganizerAbstractRequest* req); bool cancelRequest(QOrganizerAbstractRequest* req); bool waitForRequestFinished(QOrganizerAbstractRequest* req, int msecs); private: QOrganizerItemSkeletonEngineData* d; friend class QOrganizerItemSkeletonFactory; }; QT_END_NAMESPACE_ORGANIZER #endif // QORGANIZERSKELETON_P_H src/plugins/organizer/skeleton/skeleton.json000066400000000000000000000000371233466112000216670ustar00rootroot00000000000000{ "Keys": [ "skeleton" ] } src/plugins/organizer/skeleton/skeleton.pro000066400000000000000000000002711233466112000215160ustar00rootroot00000000000000TARGET = qtorganizer_skeleton QT += organizer PLUGIN_TYPE = organizer load(qt_plugin) HEADERS += qorganizerskeleton_p.h SOURCES += qorganizerskeleton.cpp OTHER_FILES += skeleton.json src/plugins/plugins.pro000066400000000000000000000000701233466112000155240ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS += contacts organizer versit src/plugins/versit/000077500000000000000000000000001233466112000146405ustar00rootroot00000000000000src/plugins/versit/backuphandler/000077500000000000000000000000001233466112000174435ustar00rootroot00000000000000src/plugins/versit/backuphandler/backuphandler.json000066400000000000000000000001021233466112000231320ustar00rootroot00000000000000{ "Keys": [ "org.qt-project.Qt.BackupVCardHandlerFactory" ] } src/plugins/versit/backuphandler/backuphandler.pro000066400000000000000000000003121233466112000227640ustar00rootroot00000000000000TARGET = qtversit_backuphandler QT += contacts versit-private PLUGIN_TYPE = versit load(qt_plugin) HEADERS += backupvcardhandler.h SOURCES += backupvcardhandler.cpp OTHER_FILES += backuphandler.json src/plugins/versit/backuphandler/backupvcardhandler.cpp000066400000000000000000000247351233466112000240050ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "backupvcardhandler.h" #include #include #include #include #include #include #include #include #include #include /* When these conditions are satisfied, QStringLiteral is implemented by gcc's statement-expression extension. However, in this file it will not work, because "statement-expressions are not allowed outside functions nor in template-argument lists". Fall back to the less-performant QLatin1String in this case. */ #if defined(QStringLiteral) && defined(QT_UNICODE_LITERAL_II) && defined(Q_CC_GNU) && !defined(Q_COMPILER_LAMBDA) # undef QStringLiteral # define QStringLiteral QLatin1String #endif QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSIT /* See QVersitContactImporter::createBackupHandler() */ class BackupVCardHandler : public QVersitContactHandler { public: BackupVCardHandler(); void propertyProcessed(const QVersitDocument& document, const QVersitProperty& property, const QContact& contact, bool* alreadyProcessed, QList* updatedDetails); void documentProcessed(const QVersitDocument& document, QContact* contact); void detailProcessed(const QContact& contact, const QContactDetail& detail, const QVersitDocument& document, QSet* processedFields, QList* toBeRemoved, QList* toBeAdded); void contactProcessed(const QContact& contact, QVersitDocument* document); private: static void serializeValue(QVersitProperty* property, const QVariant& value); DetailGroupMap mDetailGroupMap; // remembers which details came from which groups int mDetailNumber; }; const QString PropertyName(QStringLiteral("X-NOKIA-QCONTACTFIELD")); const QString DetailTypeParameter(QStringLiteral("DETAIL")); const QString FieldParameter(QStringLiteral("FIELD")); const QString DatatypeParameter(QStringLiteral("DATATYPE")); const QString DatatypeParameterVariant(QStringLiteral("VARIANT")); const QString DatatypeParameterDate(QStringLiteral("DATE")); const QString DatatypeParameterDateTime(QStringLiteral("DATETIME")); const QString DatatypeParameterTime(QStringLiteral("TIME")); const QString DatatypeParameterBool(QStringLiteral("BOOL")); const QString DatatypeParameterInt(QStringLiteral("INT")); const QString DatatypeParameterUInt(QStringLiteral("UINT")); const QString DatatypeParameterUrl(QStringLiteral("URL")); const QString GroupPrefix(QStringLiteral("G")); QSet BackupVCardHandlerFactory::profiles() const { QSet retval; retval.insert(QVersitContactHandlerFactory::ProfileBackup()); return retval; } QString BackupVCardHandlerFactory::name() const { return QStringLiteral("org.qt-project.Qt.BackupVCardHandlerFactory"); } int BackupVCardHandlerFactory::index() const { // Prefer to run this plugin last. return -1; } QVersitContactHandler* BackupVCardHandlerFactory::createHandler() const { return new BackupVCardHandler(); } QStringList BackupVCardHandlerFactory::keys() const { return QStringList() << name(); } BackupVCardHandler::BackupVCardHandler() : mDetailNumber(0) { } void BackupVCardHandler::propertyProcessed( const QVersitDocument& document, const QVersitProperty& property, const QContact& contact, bool* alreadyProcessed, QList* updatedDetails) { Q_UNUSED(document) Q_UNUSED(property) Q_UNUSED(contact) Q_UNUSED(alreadyProcessed) Q_UNUSED(updatedDetails) } void BackupVCardHandler::documentProcessed( const QVersitDocument& document, QContact* contact) { Q_UNUSED(document) Q_UNUSED(contact) } void BackupVCardHandler::detailProcessed( const QContact& contact, const QContactDetail& detail, const QVersitDocument& document, QSet* processedFields, QList* toBeRemoved, QList* toBeAdded) { Q_UNUSED(contact) Q_UNUSED(document) Q_UNUSED(toBeRemoved) if (detail.accessConstraints().testFlag(QContactDetail::ReadOnly)) return; QMap fields = detail.values(); // fields from the same detail have the same group so the importer can collate them QString detailGroup = GroupPrefix + QString::number(mDetailNumber++); int toBeAddedCount = toBeAdded->count(); bool propertiesSynthesized = false; for (QMap::const_iterator it = fields.constBegin(); it != fields.constEnd(); it++) { if (!processedFields->contains(it.key()) && !it.value().toString().isEmpty()) { // Generate a property for the unknown field QVersitProperty property; property.setGroups(QStringList(detailGroup)); property.setName(PropertyName); property.insertParameter(DetailTypeParameter, QString::number(detail.type())); property.insertParameter(FieldParameter, QString::number(it.key())); serializeValue(&property, it.value()); toBeAdded->append(property); propertiesSynthesized = true; processedFields->insert(it.key()); } } if (propertiesSynthesized) { // We need to group the already-generated properties with the newly synthesized ones for (int i = 0; i < toBeAddedCount; i++) { QVersitProperty& property = (*toBeAdded)[i]; property.setGroups(property.groups() << detailGroup); } } } void BackupVCardHandler::serializeValue(QVersitProperty* property, const QVariant& value) { // serialize the value if (value.type() == QVariant::String || value.type() == QVariant::ByteArray) { // store QStrings and QByteArrays as-is property->setValue(value); } else if (value.type() == QVariant::Date) { // Store a QDate as a string QString valueString(value.toDate().toString(Qt::ISODate)); property->insertParameter(DatatypeParameter, DatatypeParameterDate); property->setValue(valueString); } else if (value.type() == QVariant::Time) { // Store a QTime as a string QString valueString(value.toTime().toString(Qt::ISODate)); property->insertParameter(DatatypeParameter, DatatypeParameterTime); property->setValue(valueString); } else if (value.type() == QVariant::DateTime) { // Store a QDateTime as a string QString valueString(value.toDateTime().toString(Qt::ISODate)); property->insertParameter(DatatypeParameter, DatatypeParameterDateTime); property->setValue(valueString); } else if (value.type() == QVariant::Bool) { // Store an int as a string QString valueString(QString::number(value.toBool() ? 1 : 0)); property->insertParameter(DatatypeParameter, DatatypeParameterBool); property->setValue(valueString); } else if (value.type() == QVariant::Int) { // Store an int as a string QString valueString(QString::number(value.toInt())); property->insertParameter(DatatypeParameter, DatatypeParameterInt); property->setValue(valueString); } else if (value.type() == QVariant::UInt) { // Store a uint as a string QString valueString(QString::number(value.toUInt())); property->insertParameter(DatatypeParameter, DatatypeParameterUInt); property->setValue(valueString); } else if (value.type() == QVariant::Url) { // Store a QUrl as a string QString valueString(value.toUrl().toString()); property->insertParameter(DatatypeParameter, DatatypeParameterUrl); property->setValue(valueString); } else { // Store other types by serializing the QVariant in a QByteArray QByteArray valueBytes; QDataStream stream(&valueBytes, QIODevice::WriteOnly); stream << value; property->insertParameter(DatatypeParameter, DatatypeParameterVariant); property->setValue(valueBytes); } } void BackupVCardHandler::contactProcessed( const QContact& contact, QVersitDocument* document) { Q_UNUSED(contact) Q_UNUSED(document) mDetailNumber = 0; } #include "moc_backupvcardhandler.cpp" QT_END_NAMESPACE_VERSIT src/plugins/versit/backuphandler/backupvcardhandler.h000066400000000000000000000050031233466112000234350ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef TESTVCARDHANDLER_H #define TESTVCARDHANDLER_H #include #include QT_BEGIN_NAMESPACE_VERSIT class BackupVCardHandlerFactory : public QVersitContactHandlerFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QVersitContactHandlerFactoryInterface" FILE "backuphandler.json") public: QSet profiles() const; QString name() const; int index() const; QVersitContactHandler* createHandler() const; virtual QStringList keys() const; }; QT_END_NAMESPACE_VERSIT #endif // TESTVCARDHANDLER_H src/plugins/versit/vcardpreserver/000077500000000000000000000000001233466112000176755ustar00rootroot00000000000000src/plugins/versit/vcardpreserver/vcardpreserver.cpp000066400000000000000000000154001233466112000234360ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "vcardpreserver.h" #include #include #include #include #include #include #include #include /* When these conditions are satisfied, QStringLiteral is implemented by gcc's statement-expression extension. However, in this file it will not work, because "statement-expressions are not allowed outside functions nor in template-argument lists". Fall back to the less-performant QLatin1String in this case. */ #if defined(QStringLiteral) && defined(QT_UNICODE_LITERAL_II) && defined(Q_CC_GNU) && !defined(Q_COMPILER_LAMBDA) # undef QStringLiteral # define QStringLiteral QLatin1String #endif QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSIT class VCardPreserver : public QVersitContactHandler { public: VCardPreserver(); void propertyProcessed(const QVersitDocument& document, const QVersitProperty& property, const QContact& contact, bool* alreadyProcessed, QList* updatedDetails); void documentProcessed(const QVersitDocument& document, QContact* contact); void detailProcessed(const QContact& contact, const QContactDetail& detail, const QVersitDocument& document, QSet* processedFields, QList* toBeRemoved, QList* toBeAdded); void contactProcessed(const QContact& contact, QVersitDocument* document); }; const QContactDetail::DetailType DetailType(QContactDetail::TypeExtendedDetail); const QContactExtendedDetail::ExtendedDetailField KeyField(QContactExtendedDetail::FieldName); const QContactExtendedDetail::ExtendedDetailField ValueField(QContactExtendedDetail::FieldData); QSet VCardPreserverFactory::profiles() const { QSet retval; // TODO: use this line in 1.2 when the constant is enabled //retval.insert(QVersitContactHandlerFactory::ProfilePreserve); retval.insert(QStringLiteral("Preserve")); return retval; } QString VCardPreserverFactory::name() const { return QStringLiteral("org.qt-project.Qt.VCardPreserverFactory"); } int VCardPreserverFactory::index() const { // Prefer to run this plugin last, but before the backup handler. return -2; } QVersitContactHandler* VCardPreserverFactory::createHandler() const { return new VCardPreserver(); } QStringList VCardPreserverFactory::keys() const { return QStringList() << name(); } VCardPreserver::VCardPreserver() { } void VCardPreserver::propertyProcessed(const QVersitDocument& document, const QVersitProperty& property, const QContact& contact, bool* alreadyProcessed, QList* updatedDetails) { Q_UNUSED(contact) Q_UNUSED(document) if (!updatedDetails->isEmpty() || *alreadyProcessed) { return; } QContactDetail detail(DetailType); detail.setValue(KeyField, property.name()); detail.setValue(ValueField, property.value()); updatedDetails->append(detail); *alreadyProcessed = true; } void VCardPreserver::documentProcessed(const QVersitDocument& document, QContact* contact) { Q_UNUSED(document) Q_UNUSED(contact) } void VCardPreserver::detailProcessed(const QContact& contact, const QContactDetail& detail, const QVersitDocument& document, QSet* processedFields, QList* toBeRemoved, QList* toBeAdded) { Q_UNUSED(contact) Q_UNUSED(document) Q_UNUSED(toBeRemoved) if (detail.type() == DetailType && processedFields->isEmpty()) { QString key(detail.value(KeyField).toString()); QString value(detail.value(ValueField).toString()); if (!key.isEmpty() && !value.isEmpty()) { QVersitProperty property; property.setName(key); property.setValue(value); toBeAdded->append(property); } processedFields->insert(KeyField); processedFields->insert(ValueField); } } void VCardPreserver::contactProcessed(const QContact& contact, QVersitDocument* document) { Q_UNUSED(contact) Q_UNUSED(document) } #include "moc_vcardpreserver.cpp" QT_END_NAMESPACE_VERSIT src/plugins/versit/vcardpreserver/vcardpreserver.h000066400000000000000000000047721233466112000231150ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef VCARDPRESERVER_H #define VCARDPRESERVER_H #include #include QT_BEGIN_NAMESPACE_VERSIT class VCardPreserverFactory : public QVersitContactHandlerFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QVersitContactHandlerFactoryInterface" FILE "vcardpreserver.json") public: QSet profiles() const; QString name() const; int index() const; QVersitContactHandler* createHandler() const; virtual QStringList keys() const; }; QT_END_NAMESPACE_VERSIT #endif // VCARDPRESERVER_H src/plugins/versit/vcardpreserver/vcardpreserver.json000066400000000000000000000000761233466112000236300ustar00rootroot00000000000000{ "Keys": [ "org.qt-project.Qt.VCardPreserverFactory" ] } src/plugins/versit/vcardpreserver/vcardpreserver.pro000066400000000000000000000002741233466112000234570ustar00rootroot00000000000000TARGET = qtversit_vcardpreserver QT += contacts versit PLUGIN_TYPE = versit load(qt_plugin) HEADERS += vcardpreserver.h SOURCES += vcardpreserver.cpp OTHER_FILES += vcardpreserver.json src/plugins/versit/versit.pro000066400000000000000000000001161233466112000166740ustar00rootroot00000000000000TEMPLATE = subdirs CONFIG += ordered SUBDIRS += backuphandler vcardpreserver src/src.pro000066400000000000000000000002101233466112000131450ustar00rootroot00000000000000TEMPLATE = subdirs CONFIG += ordered SUBDIRS = contacts organizer versit versitorganizer plugins qtHaveModule(qml): SUBDIRS += imports src/versit/000077500000000000000000000000001233466112000131575ustar00rootroot00000000000000src/versit/doc/000077500000000000000000000000001233466112000137245ustar00rootroot00000000000000src/versit/doc/qtversit.qdocconf000066400000000000000000000035211233466112000173240ustar00rootroot00000000000000include($QT_INSTALL_DOCS/global/qt-html-templates-offline.qdocconf) include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) project = QtVersit description = Qt Versit Reference Documentation url = http://qt-project.org/doc/qt-5.0/qtversit-index.html version = 5.0.0 qhp.projects = QtVersit qhp.QtVersit.file = qtversit.qhp qhp.QtVersit.namespace = org.qt-project.qtversit.500 qhp.QtVersit.virtualFolder = qdoc qhp.QtVersit.indexTitle = Qt Versit Reference Documentation qhp.QtVersit.indexRoot = qhp.QtVersit.filterAttributes = qtversit 5.0.0 qtrefdoc qhp.QtVersit.customFilters.Qt.name = QtVersit 5.0.0 qhp.QtVersit.customFilters.Qt.filterAttributes = qtversit 5.0.0 qhp.QtVersit.subprojects = classes overviews examples qhp.QtVersit.subprojects.classes.title = Classes qhp.QtVersit.subprojects.classes.indexTitle = Qt Versit's Classes qhp.QtVersit.subprojects.classes.selectors = class fake:headerfile qhp.QtVersit.subprojects.classes.sortPages = true qhp.QtVersit.subprojects.overviews.title = Overviews qhp.QtVersit.subprojects.overviews.indexTitle = All Overviews and HOWTOs qhp.QtVersit.subprojects.overviews.selectors = fake:page,group,module qhp.QtVersit.subprojects.examples.title = Qt Versit Examples qhp.QtVersit.subprojects.examples.indexTitle = Qt Versit Examples qhp.QtVersit.subprojects.examples.selectors = fake:example tagfile = ../../../doc/qtversit/qtversit.tags headerdirs += .. \ ../../versitorganizer \ ../../plugins/versit sourcedirs += .. \ ../../versitorganizer \ ../../plugins/versit exampledirs += ../../../examples/versit \ snippets/ imagedirs += images depends += qtorganizer qtbase qtcontacts src/versit/doc/snippets/000077500000000000000000000000001233466112000155715ustar00rootroot00000000000000src/versit/doc/snippets/doc_src_qtversit.cpp000066400000000000000000000046101233466112000216530ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //! [versit include] #include //! [versit include] //! [versit organizer include] #include //! [versit organizer include] //! [namespace] QtVersit QT_BEGIN_NAMESPACE_VERSIT QT_END_NAMESPACE_VERSIT QTVERSIT_USE_NAMESPACE //! [namespace] //! [organizer namespace] QtVersitOrganizer QT_BEGIN_NAMESPACE_VERSITORGANIZER QT_END_NAMESPACE_VERSITORGANIZER QTVERSITORGANIZER_USE_NAMESPACE //! [organizer namespace] src/versit/doc/snippets/doc_src_qtversit.pro000066400000000000000000000040611233466112000216710ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #! [versit project modifications] QT += versit versitorganizer #! [versit project modifications] src/versit/doc/snippets/qtversitdocsample/000077500000000000000000000000001233466112000213425ustar00rootroot00000000000000src/versit/doc/snippets/qtversitdocsample/qtversitdocsample.cpp000066400000000000000000000276451233466112000256350ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include QCONTACTS_USE_NAMESPACE QTVERSIT_USE_NAMESPACE void completeExample(); void exportExample(); void importExample(); //! [Resource handler] class MyResourceHandler : public QVersitDefaultResourceHandler { public: bool saveResource(const QByteArray& contents, const QVersitProperty& property, QString* location) { Q_UNUSED(property) *location = QString::number(qrand()); QFile file(*location); file.open(QIODevice::WriteOnly); file.write(contents); // In a real implementation, consider when this file will be deleted. return true; } bool loadResource(const QString& location, QByteArray* contents, QString* mimeType) { return QVersitDefaultResourceHandler::loadResource(location, contents, mimeType); } }; //! [Resource handler] #if 0 int main(int argc, char *argv[]) { Q_UNUSED(argc) Q_UNUSED(argv) completeExample(); exportExample(); importExample(); } #endif void completeExample() { // Create the input vCard //! [Complete example - create] QBuffer input; input.open(QBuffer::ReadWrite); QByteArray inputVCard = "BEGIN:VCARD\r\nVERSION:2.1\r\nFN:John Citizen\r\nN:Citizen;John;Q;;\r\nEND:VCARD\r\n"; input.write(inputVCard); input.seek(0); //! [Complete example - create] // Parse the input into QVersitDocuments //! [Complete example - read] // Note: we could also use the more convenient QVersitReader(QByteArray) constructor. QVersitReader reader; reader.setDevice(&input); reader.startReading(); // Remember to check the return value reader.waitForFinished(); QList inputDocuments = reader.results(); //! [Complete example - read] // Convert the QVersitDocuments to QContacts //! [Complete example - import] QVersitContactImporter importer; if (!importer.importDocuments(inputDocuments)) return; QList contacts = importer.contacts(); // Note that the QContacts are not saved yet. // Use QContactManager::saveContacts() for saving if necessary //! [Complete example - import] // Export the QContacts back to QVersitDocuments //! [Complete example - export] QVersitContactExporter exporter; if (!exporter.exportContacts(contacts, QVersitDocument::VCard30Type)) return; QList outputDocuments = exporter.documents(); //! [Complete example - export] // Encode the QVersitDocument back to a vCard //! [Complete example - write] // Note: we could also use the more convenient QVersitWriter(QByteArray*) constructor. QBuffer output; output.open(QBuffer::ReadWrite); QVersitWriter writer; writer.setDevice(&output); writer.startWriting(outputDocuments); // Remember to check the return value writer.waitForFinished(); // output.buffer() now contains a vCard //! [Complete example - write] } void exportExample() { //! [Export example] QVersitContactExporter contactExporter; QContact contact; // Create a name QContactName name; name.setFirstName(QString::fromLatin1("John")); contact.saveDetail(&name); if (!contactExporter.exportContacts(QList() << contact, QVersitDocument::VCard30Type)) return; QList versitDocuments = contactExporter.documents(); // detailHandler.mUnknownDetails now contains the list of unknown details //! [Export example] } void importExample() { //! [Import example] QVersitContactImporter importer; QVersitDocument document; QVersitProperty property; property.setName(QString::fromLatin1("N")); property.setValue("Citizen;John;Q;;"); document.addProperty(property); property.setName(QString::fromLatin1("X-UNKNOWN-PROPERTY")); property.setValue("some value"); document.addProperty(property); if (importer.importDocuments(QList() << document)) { QList contactList = importer.contacts(); // contactList.first() now contains the "N" property as a QContactName // propertyHandler.mUnknownProperties contains the list of unknown properties } //! [Import example] } //! [Import relationship example] /*! Adds contacts in \a newContacts into \a manager, converting categories specified with tags into group membership relationships. Note that this implementation uses the synchronous API of QContactManager for clarity. It is recommended that the asynchronous API is used in practice. Relationships are added so that if a contact, A, has a tag "b", then a HasMember relationship is created between a group contact in the manager, B with display label "b", and contact A. If there does not exist a group contact with display label "b", one is created. */ void insertWithGroups(const QList& newContacts, QContactManager* manager) { // Cache map from group names to QContactIds QMap groupMap; foreach (QContact contact, newContacts) { if (!manager->saveContact(&contact)) continue; // In practice, better error handling may be required foreach (const QContactTag& tag, contact.details()) { QString groupName = tag.tag(); QContactId groupId; if (groupMap.contains(groupName)) { // We've already seen a group with the right name groupId = groupMap.value(groupName); } else { QContactDetailFilter groupFilter; groupFilter.setDetailDefinitionName(QContactType::DefinitionName); groupFilter.setValue(QLatin1String(QContactType::TypeGroup)); groupFilter.setMatchFlags(QContactFilter::MatchExactly); // In practice, some detail other than the display label could be used QContactDetailFilter nameFilter = QContactDisplayLabel::match(groupName); QList matchingGroups = manager->contactIds(groupFilter & nameFilter); if (!matchingGroups.isEmpty()) { // Found an existing group in the manager QContactId groupId; groupMap.insert(groupName, groupId); } else { // Make a new group QContact groupContact; QContactName name; name.setCustomLabel(groupName); // Beware that not all managers support custom label groupContact.saveDetail(&name); if (!manager->saveContact(&groupContact)) continue; // In practice, better error handling may be required groupId = groupContact.id(); groupMap.insert(groupName, groupId); } } // Add the relationship QContactRelationship rel; rel.setFirst(groupId); rel.setRelationshipType(QContactRelationship::HasMember); rel.setSecond(contact.id()); manager->saveRelationship(&rel); } } } //! [Import relationship example] //! [Export relationship example] /*! Adds QContactTag details to the \a contacts, based on the \a relationships. Tags are created such that if a group contact, B with display label "b", has a HasMember relationship with a contact, A, then a QContactTag, "b", is added to A. Group contacts can be passed in with the \a contacts list. If a contact is part of a group which is not in \a contacts, the \a manager is queried to find them. */ void createTagsFromGroups(QList* contacts, const QList& relationships, const QContactManager* manager) { // Map from QContactIds to group names QMap groupMap; // Map from QContactIds to indices into the contacts list QMap indexMap; // Build up groupMap and indexMap for (int i = 0; i < contacts->size(); ++i) { QContact contact = contacts->at(i); if (contact.type() == QContactType::TypeGroup) { // In practice, you may want to check that there aren't two distinct groups with the // same name, and you may want to use a field other than display label. groupMap.insert(contact.id(), contact.displayLabel()); } indexMap.insert(contact.id(), i); } // Now add all the tags specified by the group relationships foreach (const QContactRelationship& rel, relationships) { if (rel.relationshipType() == QContactRelationship::HasMember && indexMap.contains(rel.second())) { QString groupName = groupMap.value(rel.first()); // Have we seen the group before? if (groupName.isEmpty()) { // Try and find the group in the manager QContactId groupId = rel.second(); QContactFetchHint fetchHint; fetchHint.setDetailDefinitionsHint(QStringList(QContactDisplayLabel::DefinitionName)); QContact contact = manager->contact(groupId, fetchHint); if (!contact.isEmpty()) { groupName = contact.displayLabel(); groupMap.insert(groupId, groupName); // Cache the group id/name } } if (!groupName.isEmpty()) { // Add the tag QContactTag tag; tag.setTag(groupName); (*contacts)[indexMap.value(rel.second())].saveDetail(&tag); } } } } //! [Export relationship example] src/versit/doc/snippets/qtversitdocsample/qtversitdocsample.pro000066400000000000000000000011731233466112000256370ustar00rootroot00000000000000###################################################################### # # Simple example of how to use the versit API # ###################################################################### TEMPLATE = lib TARGET = qtversitdocsample include(../../../../features/basic_examples_setup.pri) INCLUDEPATH += ../../../../src/global \ ../../../../src/contacts \ ../../../../src/contacts/requests \ ../../../../src/contacts/filters \ ../../../../src/contacts/details \ ../../../../src/versit CONFIG += console QT = contacts versit SOURCES += qtversitdocsample.cpp src/versit/doc/src/000077500000000000000000000000001233466112000145135ustar00rootroot00000000000000src/versit/doc/src/vcardsupport.qdoc000066400000000000000000000132161233466112000201220ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page vcardsupport.html \target vcardsupport \title Supported vCard Features \section1 Properties The following table lists the vCard properties that the \l{Qt Versit} module supports. \table \header \li Versit Property \li QContactDetail \row \li ADR \li QContactAddress \row \li BDAY \li QContactBirthday \row \li CATEGORIES, X-CATEGORIES* \li QContactTag \row \li FN \li QContactName (CustomLabel field) \row \li GEO \li QContactGeoLocation \row \li EMAIL \li QContactEmailAddress \row \li IMPP, X-IMPP* \li QContactOnlineAccount (SubTypeImpp) \row \li LOGO \li QContactOrganization (LogoUrl field) \row \li N \li QContactName \row \li NICKNAME, X-NICKNAME*, X-EPOCSECONDNAME* \li QContactNickname \row \li NOTE \li QContactNote \row \li ORG \li QContactOrganization (Name field) \row \li PHOTO \li QContactAvatar for URLs. No support by default for base-64 encoded images. \row \li REV \li QContactTimestamp \row \li ROLE \li QContactOrganization (Role field) \row \li SOUND \li QContactRingtone \row \li TEL \li QContactPhoneNumber \row \li TITLE \li QContactOrganization (Title field) \row \li UID \li QContactGuid \row \li URL \li QContactUrl \row \li X-ANNIVERSARY \li QContactAnniversary \row \li X-ASSISTANT \li QContactOrganization (Assistant field) \row \li X-ASSISTANT-TEL \li QContactPhoneNumber (SubTypeAssistant) \row \li X-CHILDREN \li QContactFamily (Children field) \row \li X-QTPROJECT-FAVORITE \li QContactFavorite \row \li X-GENDER \li QContactGender \row \li X-JABBER \li QContactOnlineAccount (SubTypeImpp) \row \li X-QTPROJECT-EXTENDED-DETAIL** \li QContactExtendedDetail \row \li X-SIP \li QContactOnlineAccount \row \li X-SPOUSE \li QContactFamily (Spouse field) \endtable * These are understood by the importer, but are not generated by the exporter. ** Extended detail data is serialized in JSON format. Since JSON natively supports booleans, (floating-point) numbers/doubles, strings, arrays/lists and objects/maps, other data types are converted to these when exporting. Importing the detail will generate an extended detail of one of the supported types listed above. See QJsonValue::fromVariant() in QtCore for the list of type conversions. \section1 Parameters The Versit module supports the following vCard parameter: \list \li ENCODING (for base64 or quoted-printable values) \li CHARSET (text character set for a specific property) \li VALUE (for LOGO, SOUND and PHOTO to specify whether the resource is external or embedded) \li TYPE (see below) \endlist The following table lists the values that are supported for the TYPE parameter: \table \header \li Value of TYPE parameter \li Value of QContactDetail Context/SubType \row \li HOME \li QContactDetail::ContextHome \row \li WORK \li QContactDetail::ContextWork \row \li DOM \li QContactAddress::SubTypeDomestic \row \li INTL \li QContactAddress::SubTypeInternational \row \li POSTAL \li QContactAddress::SubTypePostal \row \li PARCEL \li QContactAddress::SubTypeParcel \row \li VOICE \li QContactPhoneNumber::SubTypeVoice \row \li CELL \li QContactPhoneNumber::SubTypeMobile \row \li MODEM \li QContactPhoneNumber::SubTypeModem \row \li CAR \li QContactPhoneNumber::SubTypeCar \row \li VIDEO \li QContactPhoneNumber::SubTypeVideo \row \li FAX \li QContactPhoneNumber::SubTypeFax \row \li BBS \li QContactPhoneNumber::SubTypeBulletinBoardSystem \row \li PAGER \li QContactPhoneNumber::SubTypePager \row \li ISDN \li QContactPhoneNumber::SubTypeLandline \row \li SWIS \li QContactOnlineAccount::SubTypeVideoShare \row \li VOIP \li QContactOnlineAccount::SubTypeSipVoip \endtable PREF is partially supported. If a property has the parameter TYPE set to PREF, the QContactDetail that it generates is placed at the front of the list saved to the resultant QContact. This has the effect that calling QContact::detail() will return it in preference over other details. However, the existence of the PREF parameter is not preserved, so it won't appear when exporting the contact back to vCard. */ src/versit/doc/src/versit-index.qdoc000066400000000000000000000057521233466112000200150ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page qtversit-index.html \title Qt Versit \brief Qt Versit enables clients to import and export contact and calendar information in vCard and iCalendar formats. Most of the time your contacts do not remain in isolation in a single device, but coexist on various mobile phones, computers, and servers. A standard way to exchange the contacts is through the Versit \reg documents. The Qt Versit API enables you to import calendar or contact information in VCard and iCalendar format to the Qt Organizer and Qt Contact API, and to store the information on the current platform. Alternatively, you can export organizer and contact information to VCal or iCalendar files which you can then import into other calendars and contact storages, or easily share through Bluetooth or email, for example. The \l{Qt Organizer} module has information about supported \l{Supported iCalendar Features}{iCalendar features}. \section1 Getting Started To include the definitions of the module's classes, use the following directives. The generic Qt Versit is used in the contacts use case, and it is extended with Qt Organizer: \snippet doc_src_qtversit.cpp versit include \snippet doc_src_qtversit.cpp versit organizer include To use the C++ library in your application, add the following configuration option to your \c .pro file: \snippet doc_src_qtversit.pro versit project modifications \section1 Related Information \section2 Guides \list \li \l{Supported vCard Features} \li \l{Qt Versit Overview} \li \l{Qt Versit Plugins} \endlist \section2 References \list \li \l{Qt Versit C++ Classes} \endlist */ src/versit/doc/src/versit.qdoc000066400000000000000000000122231233466112000166770ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page versit.html \title Qt Versit Overview \brief An API to import and export to the vCard and iCalendar formats. \ingroup qtpim-c++ \tableofcontents \section1 Overview The Versit API is part of a collection of Personal Information Management API's providing a library to convert \l{QContact}{QContacts} to and from \l{http://en.wikipedia.org/wiki/Vcard}{vCard} files, and to convert \l{QOrganizerItem}{QOrganizerItems} to and from \l{http://en.wikipedia.org/wiki/ICalendar}{iCalendar} files. Because vCard and iCalendar share the same structure, they are represented in abstract form with a common class, namely QVersitDocument. vCard and iCalendar files can be parsed into QVersitDocument form using QVersitReader. QVersitDocument objects can be written to an I/O device using QVersitWriter. A QVersitDocument object can represent either a vCard or an iCalendar. \l{QVersitDocument}{QVersitDocuments} representing vCards can be converted to \l{QContact}{QContacts} using QVersitContactImporter. \l{QVersitDocument}{QVersitDocuments} representing iCalendars can be converted to \l{QOrganizerItem}{QOrganizerItems} using QVersitOrganizerImporter. \l{QContact}{QContacts} and \l{QOrganizerItem}{QOrganizerItems} can be converted to \l{QVersitDocument}{QVersitDocuments} using QVersitContactExporter and QVersitOrganizerExporter, respectively. Currently \l{QVersitReader} and \l{QVersitWriter} support reading and writing vCard 2.1, vCard 3.0 and iCalendar 2.0 format documents. Please note that the Organizer API is still under development, so the Qt Versit API classes related to importing and exporting organizer items is subject to change. Versit \reg is a trademark of the Internet Mail Consortium. For full list of classes, see \l{Qt Versit C++ API}. \section1 Importing and Exporting Data The following example goes through the process of reading a vCard and importing it to QContact format, then exporting and writing it back out. First, let's create some data to read. In this case, we create a QBuffer as a demonstration, but any QIODevice will work. \snippet qtversitdocsample/qtversitdocsample.cpp Complete example - create QVersitReader can be used to parse a vCard or iCalendar from an I/O device to produce a list of QVersitDocuments. \snippet qtversitdocsample/qtversitdocsample.cpp Complete example - read QVersitDocuments aren't very useful to the QtContacts API. They need to be imported using the QVersitContactImporter. If the QVersitDocuments were iCalendar objects, they could be imported using QVersitOrganizerImporter. \snippet qtversitdocsample/qtversitdocsample.cpp Complete example - import Conversely, QVersitContactExporter can be used to convert from QContacts to QVersitDocuments. (QVersitOrganizerExporter can be used for QOrganizerItem): \snippet qtversitdocsample/qtversitdocsample.cpp Complete example - export To complete the exporting process, QVersitWriter can be used to write to an I/O device. \snippet qtversitdocsample/qtversitdocsample.cpp Complete example - write \section1 Classes The main classes for a client interested in importing or exporting vCard and iCalendar documents are: \list \li \l{QVersitProperty} \li \l{QVersitDocument} \li \l{QVersitReader} \li \l{QVersitWriter} \li \l{QVersitContactImporter} \li \l{QVersitContactExporter} \li \l{QVersitOrganizerImporter} \li \l{QVersitOrganizerExporter} \endlist It is also possible to extend the behaviour of the importer and exporter classes by writing handlers and plugins. For more details, see \l{Qt Versit Plugins}. \annotatedlist versit-extension \section1 Supported Features Please see the following documents for supported Versit Module features: \list \li \l{vcardsupport}{Supported vCard Features} document for a list of vCard features that the Versit module supports. \li \l{icalsupport}{Supported iCalendar Features} document for a list of iCal features that the Organizer Versit module supports. \endlist */ src/versit/doc/src/versitclasses.qdoc000066400000000000000000000042671233466112000202660ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \module QtVersit \title Qt Versit C++ Classes \ingroup modules \brief The QtVersit module provides classes for personal information management. The \l{Qt Versit} API allows you to take standard VCard and VCal formatted test files and import your calendar or contact information into the \l{Qt Organizer} and \l{Qt Contact} APIs, resepectively. Alternatively the organizer and contacts information and export to VCal or VCard files which you can then import into other calendars and contacts, or easily share this with others through bluetooth or email, for example. To include the definitions of the module's classes, use the following directive: \snippet doc_src_qtversit.cpp versit include \snippet doc_src_qtversit.cpp versit organizer include To link against the module, add this line to your \l qmake \c .pro file: \snippet doc_src_qtversit.pro versit project modifications */ src/versit/doc/src/versitplugins.qdoc000066400000000000000000000137111233466112000203040ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the documentation of the Qt PIM Module. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: http://www.gnu.org/copyleft/fdl.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page versitplugins.html \title Qt Versit Plugins While the \l {Qt Versit Overview}{QtVersit API} provides a convenient way to import and export vCards, it is common to encounter domain-specific vCard properties that the Versit importer and exporter classes don't support. While it would be convenient if the base Versit module could support everything, that is not possible because there may be properties with the same name that have different semantics in different domains. \section1 Local Extension with Handlers To remedy this, some hooks are provided to allow clients to alter the behaviour of QVersitContactImporter and QVersitContactExporter. The basic mechanisms that allow this are the QVersitContactImporterPropertyHandlerV2 and the QVersitContactExporterDetailHandlerV2 interfaces. A client can supplement the importer and exporter classes by implementing these interfaces and associating them using QVersitContactImporter::setPropertyHandler() and QVersitContactExporter::setDetailHandler(). \section1 Global Extension with Plugins While these interfaces allow a single client to supplement the behaviour of import and export, there are many cases where the entire deployment of the Versit library will be operating under a known context. For example, the library might be deployed on a device on a particular network where all of its peers are known to support certain properties. In this situation, it's desirable for all clients of the Versit library on that device to support those properties through the Qt Versit API. It is possible to extend the library globally by installing plugins that provide handlers automatically to all users of the library on the system. Writing a plugin involves these steps: \list \li Implement a handler class that inherits from QVersitContactHandler. \li Implement a plugin class that inherits from QObject and QVersitContactHandlerFactory and implements the createHandler() function to create the handler class. \li Include the following two lines at the top of the factory declaration: \code Q_OBJECT Q_INTERFACES(QtVersit::QVersitContactHandlerFactory) \endcode \li Export the plugin using the Q_EXPORT_PLUGIN2 macro. \li Build the plugin using a suitable \tt{.pro} file. \li Deploy the plugin in the \tt{plugins/versit} directory. \endlist Please see the relevant documentation in Qt for more details on writing a plugin. \section2 Example Plugin: Backup and Restore A plugin is provided with the Qt Versit module that provides backup and restore functionality to the exporter and importer. These can be used by creating the exporter and importer under the "backup" profile: \code QVersitContactExporter exporter(QVersitContactHandlerFactory::ProfileBackup); \endcode \code QVersitContactImporter importer(QVersitContactHandlerFactory::ProfileBackup); \endcode When applied to the exporter, this handler encodes all writable details that the exporter doesn't recognise. The format it uses to encode the detail is as follows: \list \li All generated properties will have the name X-NOKIA-QCONTACTFIELD \li All generated properties will have a single Versit group, and all properties generated from a single detail will have the same group. \li All generated properties will have at least the parameters DETAIL, which holds the definition name of the QContactDetail from which it was generated, and FIELD, which holds the name of the field within the detail from which it was generated. \li If the field is of type QString or QByteArray, the property's value is set directly to the value of the field. (For a QByteArray value, the QVersitWriter will base-64 encode it.) \li If the field is of type bool, int, uint, QDate, QTime, QDateTime or QUrl a the property's value is set to a string representation of the field. A parameter DATATYPE is added to the property with value BOOL, INT, UINT, DATE, TIME or DATETIME depending on the type. \li If the field is of some other type, the field value is encoded to a QByteArray via QDataStream (and the resulting byte array is base-64 encoded by the QVersitWriter). In this case, the parameter DATATYPE=VARIANT is added to the Versit property. \endlist For example, a detail with definition name "Pet" and fields "Name"="Rex" and "Age"=(int)14 will be exported to the vCard properties: \code G0.X-NOKIA-QCONTACTFIELD;DETAIL=Pet;FIELD=Name:Rex G0.X-NOKIA-QCONTACTFIELD;DETAIL=Pet;FIELD=Age;DATATYPE=INT:14 \endcode And the next detail (say, "Pet" with a field "Name"="Molly" will generate: \code G1.X-NOKIA-QCONTACTFIELD;DETAIL=Pet;FIELD=Name:Molly \endcode When applied to the importer, this handler decodes the properties that were generated by the exporter under the backup profile. The code for this plugin can be perused in the \tt{plugins/versit/backuphandler} directory. */ src/versit/qvcard21writer.cpp000066400000000000000000000244221233466112000165470ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qvcard21writer_p.h" #include #include #include "qversitproperty.h" QT_BEGIN_NAMESPACE_VERSIT /*! Constructs a writer. */ QVCard21Writer::QVCard21Writer(QVersitDocument::VersitType type) : QVersitDocumentWriter(type) { } QTextEncoder* QVCard21Writer::utf8Encoder() { static QTextEncoder* encoder = 0; if (encoder == 0) { encoder = QTextCodec::codecForName("UTF-8")->makeEncoder(); // Hack so the encoder doesn't output a byte order mark encoder->fromUnicode(QString()); } return encoder; } /*! Destroys a writer. */ QVCard21Writer::~QVCard21Writer() { } /*! * Encodes the \a property and writes it to the device. */ void QVCard21Writer::encodeVersitProperty(const QVersitProperty& property) { encodeGroupsAndName(property); QMultiHash parameters = property.parameters(); QVariant variant(property.variantValue()); QString renderedValue; QByteArray renderedBytes; /* Structured values need to have their components backslash-escaped (in vCard 2.1, semicolons must be escaped for compound values and commas must be escaped for list values). */ if (variant.type() == QVariant::StringList) { QStringList values = property.variantValue().toStringList(); QString separator; if (property.valueType() == QVersitProperty::CompoundType) { separator = QStringLiteral(";"); } else { if (property.valueType() != QVersitProperty::ListType) { qWarning("Variant value is a QStringList but the property's value type is neither " "CompoundType or ListType"); } // Assume it's a ListType separator = QStringLiteral(","); } QString replacement = QLatin1Char('\\') + separator; QRegExp separatorRegex = QRegExp(separator); // Check first if any of the values need to be UTF-8 encoded (if so, all of them must be // UTF-8 encoded) bool forceUtf8 = requiresUtf8(values); bool first = true; foreach (QString value, values) { if (!(value.isEmpty() && property.valueType() == QVersitProperty::ListType)) { encodeVersitValue(parameters, value, forceUtf8); if (!first) { renderedValue += separator; } renderedValue += value.replace(separatorRegex, replacement); first = false; } } } else if (variant.type() == QVariant::String) { renderedValue = variant.toString(); encodeVersitValue(parameters, renderedValue, false); } else if (variant.type() == QVariant::ByteArray) { parameters.replace(QStringLiteral("ENCODING"), QStringLiteral("BASE64")); if (mCodecIsAsciiCompatible) // optimize by not converting to unicode renderedBytes = variant.toByteArray().toBase64(); else renderedValue = QLatin1String(variant.toByteArray().toBase64().data()); } // Encode parameters encodeParameters(parameters); // Encode value writeString(QStringLiteral(":")); if (variant.canConvert()) { writeCrlf(); QVersitDocument embeddedDocument = variant.value(); encodeVersitDocument(embeddedDocument); } else if (variant.type() == QVariant::String || variant.type() == QVariant::StringList) { // Some devices don't support vCard-style line folding if the property is // quoted-printable-encoded. Therefore, we use QP soft linebreaks if the property is being // QP-encoded, and normal vCard folding otherwise. if (parameters.contains(QStringLiteral("ENCODING"), QStringLiteral("QUOTED-PRINTABLE"))) writeStringQp(renderedValue); else writeString(renderedValue); } else if (variant.type() == QVariant::ByteArray) { // One extra folding before the value and // one extra line break after the value are needed in vCard 2.1 writeCrlf(); writeString(QStringLiteral(" ")); if (renderedBytes.isEmpty()) writeString(renderedValue); else writeBytes(renderedBytes); writeCrlf(); } writeCrlf(); } /*! Returns true if and only if the current codec is incapable of encoding any of the \a values */ bool QVCard21Writer::requiresUtf8(const QStringList& values) { foreach (const QString& value, values) { if (!mCodec->canEncode(value) // if codec is ASCII and there is a character > U+007F in value, encode it as UTF-8 || (mCodecIsAscii && containsNonAscii(value))) { return true; } } return false; } /*! Performs Quoted-Printable encoding and charset encoding on \a value as per vCard 2.1 spec. Returns true if the value will need to be encoded with UTF-8, false if mCodec is sufficient. */ void QVCard21Writer::encodeVersitValue(QMultiHash& parameters, QString& value, bool forceUtf8) { // Add the CHARSET parameter, if necessary and encode in UTF-8 later if (forceUtf8 || !mCodec->canEncode(value) // if codec is ASCII and there is a character > U+007F in value, encode it as UTF-8 || (mCodecIsAscii && containsNonAscii(value))) { parameters.replace(QStringLiteral("CHARSET"), QStringLiteral("UTF-8")); value = QString::fromLatin1(utf8Encoder()->fromUnicode(value)); } // Quoted-Printable encode the value and add Quoted-Printable parameter, if necessary if (quotedPrintableEncode(value)) parameters.replace(QStringLiteral("ENCODING"), QStringLiteral("QUOTED-PRINTABLE")); } int sortIndexOfTypeValue(const QString& type) { if ( type == QStringLiteral("CELL") || type == QStringLiteral("FAX")) { return 0; } else if (type == QStringLiteral("HOME") || type == QStringLiteral("WORK")) { return 1; } else { return 2; } } bool typeValueLessThan(const QString& a, const QString& b) { return sortIndexOfTypeValue(a) < sortIndexOfTypeValue(b); } /*! Ensure CELL and FAX are at the front because they are "more important" and some vCard parsers may ignore everything after the first TYPE */ void sortTypeValues(QStringList* values) { std::sort(values->begin(), values->end(), typeValueLessThan); } /*! * Encodes the \a parameters and writes it to the device. */ void QVCard21Writer::encodeParameters(const QMultiHash& parameters) { QList names = parameters.uniqueKeys(); foreach (const QString& name, names) { QStringList values = parameters.values(name); if (name == QStringLiteral("TYPE")) { // TYPE parameters should be sorted sortTypeValues(&values); } foreach (const QString& value, values) { writeString(QStringLiteral(";")); if (name.length() > 0 && name != QStringLiteral("TYPE")) { writeString(name); writeString(QStringLiteral("=")); } writeString(value); } } } bool QVCard21Writer::containsNonAscii(const QString& str) { for (int i = 0; i < str.length(); i++) { if (str[i].unicode() > 127) return true; } return false; } /*! * Encodes special characters in \a text * using Quoted-Printable encoding (RFC 1521). * Returns true if at least one character was encoded. */ bool QVCard21Writer::quotedPrintableEncode(QString& text) { bool encoded = false; for (int i=0; i 122 && c < 256)); } QT_END_NAMESPACE_VERSIT src/versit/qvcard21writer_p.h000066400000000000000000000060021233466112000165250ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVCARD21WRITER_P_H #define QVCARD21WRITER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include QT_BEGIN_NAMESPACE_VERSIT class Q_VERSIT_EXPORT QVCard21Writer : public QVersitDocumentWriter { public: QVCard21Writer(QVersitDocument::VersitType type); ~QVCard21Writer(); void encodeVersitProperty(const QVersitProperty& property); bool requiresUtf8(const QStringList& values); void encodeVersitValue(QMultiHash& parameters, QString& value, bool forceUtf8); void encodeParameters(const QMultiHash& parameters); static bool containsNonAscii(const QString& str); static bool quotedPrintableEncode(QString& text); static bool shouldBeQuotedPrintableEncoded(QChar chr); private: static QTextEncoder* utf8Encoder(); }; QT_END_NAMESPACE_VERSIT #endif // QVCARD21WRITER_P_H src/versit/qvcard30writer.cpp000066400000000000000000000155701233466112000165530ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qvcard30writer_p.h" #include #include #include #include "qversitproperty.h" #include "qversitutils_p.h" QT_BEGIN_NAMESPACE_VERSIT /*! Constructs a writer. */ QVCard30Writer::QVCard30Writer(QVersitDocument::VersitType type) : QVersitDocumentWriter(type) { mPropertyNameMappings.insert( QStringLiteral("X-NICKNAME"),QStringLiteral("NICKNAME")); mPropertyNameMappings.insert( QStringLiteral("X-IMPP"),QStringLiteral("IMPP")); } /*! Destroys a writer. */ QVCard30Writer::~QVCard30Writer() { } /*! * Encodes the \a property and writes it to the device. */ void QVCard30Writer::encodeVersitProperty(const QVersitProperty& property) { QVersitProperty modifiedProperty(property); QString name = mPropertyNameMappings.value(property.name(),property.name()); modifiedProperty.setName(name); encodeGroupsAndName(modifiedProperty); QVariant variant(modifiedProperty.variantValue()); if (variant.type() == QVariant::ByteArray) { modifiedProperty.insertParameter(QStringLiteral("ENCODING"), QStringLiteral("b")); } encodeParameters(modifiedProperty.parameters()); writeString(QStringLiteral(":")); QString renderedValue; QByteArray renderedBytes; if (variant.canConvert()) { QVersitDocument embeddedDocument = variant.value(); QByteArray data; QBuffer buffer(&data); buffer.open(QIODevice::WriteOnly); QVCard30Writer subWriter(mType); subWriter.setCodec(mCodec); subWriter.setDevice(&buffer); subWriter.encodeVersitDocument(embeddedDocument); QString documentString(mCodec->toUnicode(data)); backSlashEscape(&documentString); renderedValue = documentString; } else if (variant.type() == QVariant::String) { renderedValue = variant.toString(); if (property.valueType() != QVersitProperty::PreformattedType) { backSlashEscape(&renderedValue); } } else if (variant.type() == QVariant::StringList) { // We need to backslash escape and concatenate the values in the list QStringList values = property.variantValue().toStringList(); QString separator; if (property.valueType() == QVersitProperty::CompoundType) { separator = QStringLiteral(";"); } else { if (property.valueType() != QVersitProperty::ListType) { qWarning("Variant value is a QStringList but the property's value type is neither " "CompoundType or ListType"); } // Assume it's a ListType separator = QStringLiteral(","); } bool first = true; foreach (QString value, values) { if (!(value.isEmpty() && property.valueType() == QVersitProperty::ListType)) { if (!first) { renderedValue += separator; } backSlashEscape(&value); renderedValue += value; first = false; } } } else if (variant.type() == QVariant::ByteArray) { if (mCodecIsAsciiCompatible) // optimize by not converting to unicode renderedBytes = variant.toByteArray().toBase64(); else renderedValue = QLatin1String(variant.toByteArray().toBase64().data()); } if (renderedBytes.isEmpty()) writeString(renderedValue); else writeBytes(renderedBytes); writeCrlf(); } /*! * Encodes the \a parameters and writes it to the device. */ void QVCard30Writer::encodeParameters(const QMultiHash& parameters) { QList names = parameters.uniqueKeys(); foreach (QString nameString, names) { writeString(QStringLiteral(";")); QStringList values = parameters.values(nameString); backSlashEscape(&nameString); writeString(nameString); writeString(QStringLiteral("=")); for (int i=0; i 0) writeString(QStringLiteral(",")); QString value = values.at(i); backSlashEscape(&value); writeString(value); } } } /*! * Performs backslash escaping for line breaks (CRLFs), semicolons, backslashes and commas according * to RFC 2426. This is called on parameter names and values and property values. * Colons ARE NOT escaped because the examples in RFC2426 suggest that they shouldn't be. */ void QVCard30Writer::backSlashEscape(QString* text) { static const QString m1(QStringLiteral("([;,\\\\])")); static const QString r1(QStringLiteral("\\\\1")); static const QString m2(QStringLiteral("\r\n|\r|\n")); static const QString r2(QStringLiteral("\\n")); /* replaces ; with \; , with \, \ with \\ */ text->replace(QRegExp(m1), r1); // replaces any CRLFs with \n text->replace(QRegExp(m2), r2); } QT_END_NAMESPACE_VERSIT src/versit/qvcard30writer_p.h000066400000000000000000000053641233466112000165370ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVCARD30WRITER_P_H #define QVCARD30WRITER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include QT_BEGIN_NAMESPACE_VERSIT class Q_VERSIT_EXPORT QVCard30Writer : public QVersitDocumentWriter { public: QVCard30Writer(QVersitDocument::VersitType type); ~QVCard30Writer(); void encodeVersitProperty(const QVersitProperty& property); void encodeParameters(const QMultiHash& parameters); static void backSlashEscape(QString* text); QHash mPropertyNameMappings; }; QT_END_NAMESPACE_VERSIT #endif // QVCARD30WRITER_P_H src/versit/qvcardrestorehandler_p.cpp000066400000000000000000000211271233466112000204270ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qvcardrestorehandler_p.h" #include #include #include #include "qversitproperty.h" /* When these conditions are satisfied, QStringLiteral is implemented by gcc's statement-expression extension. However, in this file it will not work, because "statement-expressions are not allowed outside functions nor in template-argument lists". Fall back to the less-performant QLatin1String in this case. */ #if defined(QStringLiteral) && defined(QT_UNICODE_LITERAL_II) && defined(Q_CC_GNU) && !defined(Q_COMPILER_LAMBDA) # undef QStringLiteral # define QStringLiteral QLatin1String #endif QT_BEGIN_NAMESPACE_VERSIT const QString QVCardRestoreHandler::PropertyName(QStringLiteral("X-NOKIA-QCONTACTFIELD")); const QString QVCardRestoreHandler::DetailTypeParameter(QStringLiteral("DETAIL")); const QString QVCardRestoreHandler::FieldParameter(QStringLiteral("FIELD")); const QString QVCardRestoreHandler::DatatypeParameter(QStringLiteral("DATATYPE")); const QString QVCardRestoreHandler::DatatypeParameterVariant(QStringLiteral("VARIANT")); const QString QVCardRestoreHandler::DatatypeParameterDate(QStringLiteral("DATE")); const QString QVCardRestoreHandler::DatatypeParameterDateTime(QStringLiteral("DATETIME")); const QString QVCardRestoreHandler::DatatypeParameterTime(QStringLiteral("TIME")); const QString QVCardRestoreHandler::DatatypeParameterBool(QStringLiteral("BOOL")); const QString QVCardRestoreHandler::DatatypeParameterInt(QStringLiteral("INT")); const QString QVCardRestoreHandler::DatatypeParameterUInt(QStringLiteral("UINT")); const QString QVCardRestoreHandler::DatatypeParameterUrl(QStringLiteral("URL")); const QString QVCardRestoreHandler::GroupPrefix(QStringLiteral("G")); /* * Returns a list of details generated from a Versit group. */ QList DetailGroupMap::detailsInGroup(const QString& groupName) const { QList detailIds = mDetailGroupName.keys(groupName); QList details; foreach (int detailId, detailIds) { details << mDetailById[detailId]; } return details; } /* * Inserts the association between \a detail and \a groupName to the map. * The detail must have a key (ie. have already been saved in a contact) and the group name must not * be the empty string. */ void DetailGroupMap::insert(const QString& groupName, const QContactDetail& detail) { Q_ASSERT(!groupName.isEmpty()); mDetailGroupName[detail.key()] = groupName; mDetailById[detail.key()] = detail; } /* * Replaces the detail currently in the map with \a detail. * The detail must have a key (ie. have already been saved in a contact). */ void DetailGroupMap::update(const QContactDetail& detail) { Q_ASSERT(detail.key()); mDetailById[detail.key()] = detail; } /*! * Removes details and groups from the map. */ void DetailGroupMap::clear() { mDetailGroupName.clear(); mDetailById.clear(); } bool QVCardRestoreHandler::propertyProcessed( const QVersitProperty& property, QList* updatedDetails) { bool success = false; QString group; if (!property.groups().isEmpty()) group = property.groups().first(); if (property.name() == PropertyName) { if (property.groups().size() != 1) return false; QMultiHash parameters = property.parameters(); QContactDetail::DetailType detailType = QContactDetail::DetailType(parameters.value(DetailTypeParameter).toUInt()); QString fieldName = parameters.value(FieldParameter); // Find a detail previously seen with the same definitionName, which was generated from // a property from the same group QContactDetail detail(detailType); foreach (const QContactDetail& previousDetail, mDetailGroupMap.detailsInGroup(group)) { if (previousDetail.type() == detailType) { detail = previousDetail; } } // If not found, it's a new empty detail with the definitionName set. detail.setValue(fieldName.toInt(), deserializeValue(property)); // Replace the equivalent detail in updatedDetails with the new one QMutableListIterator it(*updatedDetails); while (it.hasNext()) { if (it.next().key() == detail.key()) { it.remove(); break; } } updatedDetails->append(detail); success = true; } if (!group.isEmpty()) { // Keep track of which details were generated from which Versit groups foreach (const QContactDetail& detail, *updatedDetails) { mDetailGroupMap.insert(group, detail); } } return success; } QVariant QVCardRestoreHandler::deserializeValue(const QVersitProperty& property) { // Import the field if (property.parameters().contains(DatatypeParameter, DatatypeParameterVariant)) { // The value was stored as a QVariant serialized in a QByteArray QDataStream stream(property.variantValue().toByteArray()); QVariant value; stream >> value; return value; } else if (property.parameters().contains(DatatypeParameter, DatatypeParameterDate)) { // The value was a QDate serialized as a string return QDate::fromString(property.value(), Qt::ISODate); } else if (property.parameters().contains(DatatypeParameter, DatatypeParameterTime)) { // The value was a QTime serialized as a string return QTime::fromString(property.value(), Qt::ISODate); } else if (property.parameters().contains(DatatypeParameter, DatatypeParameterDateTime)) { // The value was a QDateTime serialized as a string return QDateTime::fromString(property.value(), Qt::ISODate); } else if (property.parameters().contains(DatatypeParameter, DatatypeParameterBool)) { // The value was a bool serialized as a string return property.value().toInt() != 0; } else if (property.parameters().contains(DatatypeParameter, DatatypeParameterInt)) { // The value was an int serialized as a string return property.value().toInt(); } else if (property.parameters().contains(DatatypeParameter, DatatypeParameterUInt)) { // The value was a uint serialized as a string return property.value().toUInt(); } else if (property.parameters().contains(DatatypeParameter, DatatypeParameterUrl)) { // The value was a QUrl serialized as a string return QUrl(property.value()); } else { // The value was stored as a QString or QByteArray return property.variantValue(); } } void QVCardRestoreHandler::documentProcessed() { mDetailGroupMap.clear(); } QT_END_NAMESPACE_VERSIT src/versit/qvcardrestorehandler_p.h000066400000000000000000000077571233466112000201110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVCARDRESTOREHANDLER_P_H #define QVCARDRESTOREHANDLER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSIT class QVersitProperty; // This is a map from Versit group names to the details that were generated from properties with the // said groups. Multiple details can be associated with a single group. class DetailGroupMap { public: QList detailsInGroup(const QString& groupName) const; void insert(const QString& groupName, const QContactDetail& detail); void update(const QContactDetail& detail); void clear(); private: QHash mDetailGroupName; // detailid -> group name QHash mDetailById; // detailid -> detail }; class QVCardRestoreHandler { public: bool propertyProcessed(const QVersitProperty& property, QList* updatedDetails); void documentProcessed(); const static QString PropertyName; const static QString DetailTypeParameter; const static QString FieldParameter; const static QString DatatypeParameter; const static QString DatatypeParameterVariant; const static QString DatatypeParameterDate; const static QString DatatypeParameterDateTime; const static QString DatatypeParameterTime; const static QString DatatypeParameterBool; const static QString DatatypeParameterInt; const static QString DatatypeParameterUInt; const static QString DatatypeParameterUrl; const static QString GroupPrefix; private: static QVariant deserializeValue(const QVersitProperty& property); DetailGroupMap mDetailGroupMap; // remembers which details came from which groups }; QT_END_NAMESPACE_VERSIT #endif // QVCARDRESTOREHANDLER_P_H src/versit/qversitcontactexporter.cpp000066400000000000000000000370131233466112000205310ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitcontactexporter.h" #include "qversitcontactexporter_p.h" QT_BEGIN_NAMESPACE_VERSIT /*! \deprecated \class QVersitContactExporterDetailHandler \brief The QVersitContactExporterDetailHandler class is the legacy interface for clients wishing to implement custom export behaviour for certain contact details. This interface is replaced by QVersitContactExporterDetailHandlerV2. For general information on extending Qt Versit, see the document on \l{Qt Versit Plugins}. \sa QVersitContactExporter */ /*! \fn QVersitContactExporterDetailHandler::~QVersitContactExporterDetailHandler() Frees any memory in use by this handler. */ /*! \fn bool QVersitContactExporterDetailHandler::preProcessDetail(const QContact& contact, const QContactDetail& detail, QVersitDocument* document) Process \a detail and update \a document with the corresponding QVersitProperty(s). \a contact provides the context within which the detail was found. Returns true if the detail has been handled and requires no further processing, false otherwise. This function is called on every QContactDetail encountered during an export. Supply this function and return true to implement custom export behaviour. */ /*! \fn bool QVersitContactExporterDetailHandler::postProcessDetail(const QContact& contact, const QContactDetail& detail, bool alreadyProcessed, QVersitDocument* document) Process \a detail and update \a document with the corresponding QVersitProperty(s). \a contact provides the context within which the detail was found. \a alreadyProcessed is true if the detail has already been processed either by \l preProcessDetail() or by QVersitContactExporter itself. Returns true if the detail has been handled, false otherwise. This function is called on every \l QContactDetail encountered during an export. This can be used to implement support for QContactDetails not supported by QVersitContactExporter. */ /*! \class QVersitContactExporterDetailHandlerV2 \brief The QVersitContactExporterDetailHandlerV2 class is an interface for specifying custom export behaviour for certain contact details. \ingroup versit-extension \inmodule QtVersit This interface supercedes QVersitContactExporterDetailHandler. For general information on extending Qt Versit, see the document on \l{Qt Versit Plugins}. \sa QVersitContactExporter */ /*! \fn QVersitContactExporterDetailHandlerV2::~QVersitContactExporterDetailHandlerV2() Frees any memory in use by this handler. */ /*! \fn void QVersitContactExporterDetailHandlerV2::detailProcessed(const QContact& contact, const QContactDetail& detail, const QVersitDocument& document, QSet* processedFields, QList* toBeRemoved, QList* toBeAdded) Process \a detail and provide a list of updated \l{QVersitProperty}{QVersitProperties} by modifying the \a toBeRemoved and \a toBeAdded lists. This function is called on every QContactDetail encountered during an export, after the detail has been processed by the QVersitContactExporter. An implementation of this function can be made to provide support for QContactDetails not supported by QVersitContactExporter. The supplied \a contact is the container for the \a detail. \a processedFields contains a list of fields in the \a detail that were considered by the QVersitContactExporter or another handler in processing the detail. \a document holds the state of the document before the detail was processed by the exporter. \a toBeRemoved and \a toBeAdded are initially filled with a list of properties that the exporter will remove from and add to the document. These lists can be modified (by removing, modifying or adding properties) by the handler to control the changes that will actually be made to the document. If a property is to be modified in the document, the old version will appear in the \a toBeRemoved list and the new version will appear in the \a toBeAdded list. When the handler uses a field from the detail, it should update the processedFields set to reflect this to inform later handlers that the field has already been processed. After the handler returns control back to the exporter, the properties in the \a toBeRemoved list will be removed and the properties in the \a toBeAdded list will be appended to the document. */ /*! \fn void QVersitContactExporterDetailHandlerV2::contactProcessed(const QContact& contact, QVersitDocument* document) Perform any final processing on the \a document generated by the \a contact. This can be implemented by the handler to clear any internal state before moving onto the next contact. This function is called after all QContactDetails have been handled by the QVersitContactExporter. */ /*! \class QVersitContactExporter \brief The QVersitContactExporter class converts \l {QContact}{QContacts} into \l {QVersitDocument}{QVersitDocuments}. \ingroup versit \inmodule QtVersit This class is used to convert lists of \l {QContact}{QContacts} (which may be stored in a QContactManager) into lists of \l {QVersitDocument}{QVersitDocuments} (which may be written to an I/O device using QVersitReader. Unless there is an error, there is a one-to-one mapping between contacts and Versit documents. The exporter can be extended by clients by associating resource and detail handlers. Here is a simple example of how to use QVersitContactExporter: \snippet qtversitdocsample/qtversitdocsample.cpp Export example \section1 Extension via handlers A \l QVersitResourceHandler is associated with the exporter to supply the behaviour for loading files from persistent storage. By default, this is set to a \l QVersitDefaultResourceHandler, which supports basic resource loading from the file system. An alternative resource handler can be specified with setResourceHandler(). By associating a \l QVersitContactExporterDetailHandlerV2 with the exporter using setDetailHandler(), the client can pass in a handler to override the processing of details and/or handle details that QVersitContactExporter doesn't support. Also, handlers can be implicitly associated to an exporter through the \l{Qt Versit Plugins}{handler plugin mechanism}. The exporter can be constructed with a profile, which gives hints about what kind of handlers should be added to it. For example, the backup profile can be used to instruct the exporter to encode any unknown details in the vCard such that it can be reconstructed later (a QVersitContactImporter constructed under the backup profile can be used to decode it). To illustrate, a backup exporter can be constructed with: \code QVersitContactExporter exporter(QVersitContactHandlerFactory::ProfileBackup); \endcode For more details on how the backup plugin works, see \l{Qt Versit Plugins} \section1 Exporting group relationships The exporter does not handle QContactRelationships at all. Some managers use the \l{QContactRelationship::HasMember()}{HasMember} QContactRelationship along with contacts of type \l{QContactType::TypeGroup}{TypeGroup} to indicate categorization of contacts. In vCard, categorization is represented by the CATEGORIES property, which has semantics most similar to the QContactTag detail. For contact manager backends that supports groups but not QContactTag, if the categorization information needs to be retained through CATEGORIES vCard properties, extra work can be done to convert from group relationships to QContactTag before passing the contact list to the exporter. Below is some example code that does this translation. \snippet qtversitdocsample/qtversitdocsample.cpp Export relationship example \section1 Other implementation notes Some Symbian and Android devices do not properly handle the \c FN vCard property. If a DisplayLabel is set on a contact and this class is used to generate a vCard, the receiving device might display this label in an unexpected field (eg. Last Name). The vCard specification requires the \c FN property to be present. However, because of this interoperability issue, Qt Versit only generates an FN property if a Displaylabel is set. \sa QVersitDocument, QVersitProperty, QVersitResourceHandler, QVersitContactExporterDetailHandlerV2 */ /*! \enum QVersitContactExporter::Error This enum specifies an error that occurred during the most recent call to exportContacts() \value NoError The most recent operation was successful \value EmptyContactError One of the contacts was empty \value NoNameError One of the contacts has no QContactName field */ /*! * Constructs a new contact exporter */ QVersitContactExporter::QVersitContactExporter() : d(new QVersitContactExporterPrivate()) { } /*! * Constructs a new exporter for the given \a profile. The profile strings should be one of those * defined by QVersitContactHandlerFactory, or a value otherwise agreed to by a \l{Qt Versit * Plugins}{Versit plugin}. * * The profile determines which plugins will be loaded to supplement the exporter. */ QVersitContactExporter::QVersitContactExporter(const QString& profile) { if (profile.isEmpty()) d = new QVersitContactExporterPrivate(QStringList()); else d = new QVersitContactExporterPrivate(QStringList() << profile); } /*! * Constructs a new exporter for the given \a profiles. The profile strings should be one of those * defined by QVersitContactHandlerFactory, or a value otherwise agreed to by a \l{Qt Versit * Plugins}{Versit plugin}. * * The profiles determine which plugins will be loaded to supplement the exporter. */ QVersitContactExporter::QVersitContactExporter(const QStringList& profiles) : d(new QVersitContactExporterPrivate(profiles)) { } /*! * Frees any memory in use by this contact exporter. */ QVersitContactExporter::~QVersitContactExporter() { delete d; } /*! * Converts \a contacts into a list of corresponding QVersitDocuments, using the format given by * \a versitType. * * Returns true on success. If any of the contacts could not be exported, false is returned and * errorMap() will return a list describing the errors that occurred. The successfully exported * documents will still be available via documents(). * * \sa documents(), errorMap() */ bool QVersitContactExporter::exportContacts( const QList& contacts, QVersitDocument::VersitType versitType) { int contactIndex = 0; d->mDocuments.clear(); d->mErrors.clear(); bool ok = true; foreach (const QTCONTACTS_PREPEND_NAMESPACE(QContact)& contact, contacts) { if (contact.isEmpty()) { d->mErrors[contactIndex] = EmptyContactError; ok = false; continue; } QVersitDocument versitDocument; versitDocument.setType(versitType); versitDocument.setComponentType(QStringLiteral("VCARD")); d->exportContact(contact, versitDocument); d->mDocuments.append(versitDocument); contactIndex++; } return ok; } /*! * Returns the documents exported in the most recent call to exportContacts(). * * \sa exportContacts() */ QList QVersitContactExporter::documents() const { return d->mDocuments; } /*! * \obsolete * * Use \l errorMap() instead. */ QMap QVersitContactExporter::errors() const { return d->mErrors; } /*! * Returns the map of errors encountered in the most recent call to exportContacts(). The key is * the index into the input list of contacts and the value is the error that occurred on that * contact. * * \sa exportContacts() */ QMap QVersitContactExporter::errorMap() const { return d->mErrors; } /*! * \deprecated * Sets \a handler to be the handler for processing QContactDetails, or 0 to have no handler. * * Does not take ownership of the handler. The client should ensure the handler remains valid for * the lifetime of the exporter. This function is used for version 1 handlers. * * Only one detail handler can be set. If another detail handler (of any version) was * previously set, it will no longer be associated with the exporter. */ void QVersitContactExporter::setDetailHandler(QVersitContactExporterDetailHandler* handler) { d->mDetailHandlerVersion = 1; d->mDetailHandler = handler; d->mDetailHandler2 = 0; } /*! * Sets \a handler to be the handler for processing QContactDetails, or 0 to have no handler. * * Does not take ownership of the handler. The client should ensure the handler remains valid for * the lifetime of the exporter. This function is used for version 2 and higher handlers. * * Only one detail handler can be set. If another detail handler (of any version) was * previously set, it will no longer be associated with the exporter. */ void QVersitContactExporter::setDetailHandler(QVersitContactExporterDetailHandlerV2* handler) { d->mDetailHandlerVersion = 2; d->mDetailHandler = 0; d->mDetailHandler2 = handler; } /*! * \deprecated * Gets the handler for processing QContactDetails. */ QVersitContactExporterDetailHandler* QVersitContactExporter::detailHandler() const { return d->mDetailHandler; } /*! * Sets \a handler to be the handler to load files with, or 0 to have no handler. * * Does not take ownership of the handler. The client should ensure the handler remains valid for * the lifetime of the exporter. */ void QVersitContactExporter::setResourceHandler(QVersitResourceHandler* handler) { d->mResourceHandler = handler; } /*! * Returns the associated resource handler. */ QVersitResourceHandler* QVersitContactExporter::resourceHandler() const { return d->mResourceHandler; } QT_END_NAMESPACE_VERSIT src/versit/qversitcontactexporter.h000066400000000000000000000111111233466112000201650ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITCONTACTEXPORTER_H #define QVERSITCONTACTEXPORTER_H #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactDetail; QT_END_NAMESPACE_CONTACTS QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSIT class QVersitContactExporterPrivate; class Q_VERSIT_EXPORT QVersitContactExporterDetailHandler { public: virtual ~QVersitContactExporterDetailHandler() {} virtual bool preProcessDetail(const QContact& contact, const QContactDetail& detail, QVersitDocument* document) = 0; virtual bool postProcessDetail(const QContact& contact, const QContactDetail& detail, bool alreadyProcessed, QVersitDocument* document) = 0; }; class Q_VERSIT_EXPORT QVersitContactExporterDetailHandlerV2 { public: virtual ~QVersitContactExporterDetailHandlerV2() {} virtual void detailProcessed(const QContact& contact, const QContactDetail& detail, const QVersitDocument& document, QSet* processedFields, QList* toBeRemoved, QList* toBeAdded) = 0; virtual void contactProcessed(const QContact& contact, QVersitDocument* document) = 0; }; class Q_VERSIT_EXPORT QVersitContactExporter { public: enum Error { NoError = 0, EmptyContactError, NoNameError }; QVersitContactExporter(); explicit QVersitContactExporter(const QString& profile); explicit QVersitContactExporter(const QStringList& profile); ~QVersitContactExporter(); bool exportContacts(const QList& contacts, QVersitDocument::VersitType versitType = QVersitDocument::VCard30Type); QList documents() const; QMap errorMap() const; void setDetailHandler(QVersitContactExporterDetailHandlerV2* handler); void setResourceHandler(QVersitResourceHandler* handler); QVersitResourceHandler* resourceHandler() const; /* deprecated */ QMap errors() const; void setDetailHandler(QVersitContactExporterDetailHandler* handler); QVersitContactExporterDetailHandler* detailHandler() const; private: QVersitContactExporterPrivate* d; }; QT_END_NAMESPACE_VERSIT #endif // QVERSITCONTACTEXPORTER_H src/versit/qversitcontactexporter_p.cpp000066400000000000000000001126251233466112000210530ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitcontactexporter.h" #include "qversitcontactexporter_p.h" #include #include #include #include "qversitcontactimporter_p.h" #include "qversitcontactsdefs_p.h" #include "qversitutils_p.h" #include "qversitcontacthandler.h" #include "qversitcontactpluginloader_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSIT /*! * Constructor. */ QVersitContactExporterPrivate::QVersitContactExporterPrivate(const QStringList& profiles) : mDetailHandler(NULL), mDetailHandler2(NULL), mDetailHandlerVersion(0), mDefaultResourceHandler(new QVersitDefaultResourceHandler), mResourceHandler(mDefaultResourceHandler) { // Detail mappings int versitPropertyCount = sizeof(versitContactDetailMappings)/sizeof(VersitContactDetailMapping); // Put them in in reverse order so the entries at the top of the list take precedence for (int i = versitPropertyCount-1; i >= 0; i--) { mPropertyMappings.insert( versitContactDetailMappings[i].detailType, QPair( versitContactDetailMappings[i].detailField, QLatin1String(versitContactDetailMappings[i].versitPropertyName))); } // Contexts mappings int contextCount = sizeof(versitContextMappings)/sizeof(VersitContextMapping); for (int i=0; i < contextCount; i++) { mContextMappings.insert( versitContextMappings[i].contactContext, QLatin1String(versitContextMappings[i].versitString)); } // Subtypes mappings int subTypeCount = sizeof(versitSubTypeMappings)/sizeof(VersitSubTypeMapping); for (int i=0; i < subTypeCount; i++) { mSubTypeMappings.insert( QPair( versitSubTypeMappings[i].detailType, versitSubTypeMappings[i].contactSubType), QLatin1String(versitSubTypeMappings[i].versitString)); } mPluginDetailHandlers = QVersitContactPluginLoader::instance()->createContactHandlers(profiles); } /*! * Destructor. */ QVersitContactExporterPrivate::~QVersitContactExporterPrivate() { delete mDefaultResourceHandler; foreach (QVersitContactHandler* pluginHandler, mPluginDetailHandlers) { delete pluginHandler; } } /*! * Export QT Contact into Versit Document. */ void QVersitContactExporterPrivate::exportContact( const QContact& contact, QVersitDocument& document) { QList allDetails = contact.details(); foreach (const QContactDetail& detail, allDetails) { if (mDetailHandler && mDetailHandler->preProcessDetail(contact, detail, &document)) continue; QList removedProperties; QList generatedProperties; QSet processedFields; switch (detail.type()) { case QContactDetail::TypeAddress: encodeAddress(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypeAnniversary: encodeAnniversary(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypeAvatar: encodeAvatar(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypeBirthday: encodeBirthDay(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypeDisplayLabel: encodeDisplayLabel(detail, document, &removedProperties, &generatedProperties, &processedFields); break; case QContactDetail::TypeEmailAddress: encodeEmail(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypeExtendedDetail: encodeExtendedDetail(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypeFamily: encodeFamily(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypeFavorite: encodeFavorite(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypeGender: encodeGender(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypeGeoLocation: encodeGeoLocation(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypeGuid: encodeUid(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypeName: encodeName(detail, document, &removedProperties, &generatedProperties, &processedFields); break; case QContactDetail::TypeNickname: encodeNickname(detail, document, &removedProperties, &generatedProperties, &processedFields); break; case QContactDetail::TypeNote: encodeNote(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypeOnlineAccount: encodeOnlineAccount(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypeOrganization: encodeOrganization(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypePhoneNumber: encodePhoneNumber(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypeRingtone: encodeRingtone(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypeTag: encodeTag(detail, document, &removedProperties, &generatedProperties, &processedFields); break; case QContactDetail::TypeTimestamp: encodeRev(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypeUrl: encodeUrl(detail, &generatedProperties, &processedFields); break; case QContactDetail::TypeVersion: encodeVersion(detail, &generatedProperties, &processedFields); break; default: break; } // run plugin handlers foreach (QVersitContactExporterDetailHandlerV2* handler, mPluginDetailHandlers) { handler->detailProcessed(contact, detail, document, &processedFields, &removedProperties, &generatedProperties); } // run the v2 handler, if set if (mDetailHandler2 && mDetailHandlerVersion > 1) { mDetailHandler2->detailProcessed(contact, detail, document, &processedFields, &removedProperties, &generatedProperties); } foreach(const QVersitProperty& property, removedProperties) { document.removeProperty(property); } foreach(const QVersitProperty& property, generatedProperties) { document.addProperty(property); } if (mDetailHandler && mDetailHandlerVersion == 1) { mDetailHandler->postProcessDetail(contact, detail, !processedFields.isEmpty(), &document); } } // run plugin handlers foreach (QVersitContactExporterDetailHandlerV2* handler, mPluginDetailHandlers) { handler->contactProcessed(contact, &document); } // run the v2 handler, if set if (mDetailHandler2 && mDetailHandlerVersion > 1) { mDetailHandler2->contactProcessed(contact, &document); } ensureDocumentContainsName(&document); return; } /*! * Adds to \a document an empty "N" property if it doesn't already have one. */ void QVersitContactExporterPrivate::ensureDocumentContainsName(QVersitDocument* document) { bool containsN = false; foreach (const QVersitProperty& property, document->properties()) { const QString& name = property.name(); if (name == QStringLiteral("N")) { containsN = true; } } if (!containsN) { QVersitProperty nProperty; nProperty.setValueType(QVersitProperty::CompoundType); nProperty.setName(QStringLiteral("N")); nProperty.setValue(QStringList() << QString() << QString() << QString() << QString() << QString()); document->addProperty(nProperty); } } /*! * Encode Contact Name Field Information into the Versit Document */ void QVersitContactExporterPrivate::encodeName( const QContactDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields) { Q_UNUSED(document); Q_UNUSED(removedProperties); const QContactName &contactName = static_cast(detail); if (!contactName.lastName().isEmpty() || !contactName.firstName().isEmpty() || !contactName.middleName().isEmpty() || !contactName.prefix().isEmpty() || !contactName.suffix().isEmpty()) { QVersitProperty property; property.setName(mPropertyMappings.value(detail.type()).second); property.setValue(QStringList() << contactName.lastName() << contactName.firstName() << contactName.middleName() << contactName.prefix() << contactName.suffix()); property.setValueType(QVersitProperty::CompoundType); *generatedProperties << property; } *processedFields << QContactName::FieldLastName << QContactName::FieldFirstName << QContactName::FieldMiddleName << QContactName::FieldPrefix << QContactName::FieldSuffix; } /*! * Encode Phone Number Field Information into the Versit Document */ void QVersitContactExporterPrivate::encodePhoneNumber( const QContactDetail& detail, QList* generatedProperties, QSet* processedFields) { const QContactPhoneNumber &phoneNumber = static_cast(detail); QList subTypes = phoneNumber.subTypes(); QList phoneNumberContextInt; for (int i=0; i* generatedProperties, QSet* processedFields) { const QContactEmailAddress &emailAddress = static_cast(detail); QList emailAddressContextInt; for (int i=0; i* generatedProperties, QSet* processedFields) { const QContactAddress &address = static_cast(detail); QList addressContextInt; for (int i=0; i* generatedProperties, QSet* processedFields) { const QContactUrl &contactUrl = static_cast(detail); QList contactUrlContextInt; for (int i=0; i* generatedProperties, QSet* processedFields) { const QContactGuid &uid = static_cast(detail); QVersitProperty property; property.setName(mPropertyMappings.value(detail.type()).second); property.setValue(uid.guid()); *generatedProperties << property; *processedFields << QContactGuid::FieldGuid; } /*! * Encode REV Field Information into the Versit Document */ void QVersitContactExporterPrivate::encodeRev( const QContactDetail& detail, QList* generatedProperties, QSet* processedFields) { const QContactTimestamp &rev = static_cast(detail); QString value; QVersitProperty property; property.setName(mPropertyMappings.value(detail.type()).second); if ( rev.lastModified().toString(Qt::ISODate).size() ) { if ( rev.lastModified().timeSpec() == Qt::UTC ) { value = rev.lastModified().toString(Qt::ISODate); if( !value.endsWith(QLatin1Char('Z'), Qt::CaseInsensitive) ) { value += QLatin1Char('Z'); } } else { value = rev.lastModified().toString(Qt::ISODate); } property.setValue(value); *generatedProperties << property; *processedFields << QContactTimestamp::FieldModificationTimestamp; } else if ( rev.created().toString(Qt::ISODate).size()) { if ( rev.created().timeSpec() == Qt::UTC ) { value = rev.created().toString(Qt::ISODate); if( !value.endsWith(QLatin1Char('Z'), Qt::CaseInsensitive) ) { value += QLatin1Char('Z'); } } else { value = rev.created().toString(Qt::ISODate); } property.setValue(value); *generatedProperties << property; *processedFields << QContactTimestamp::FieldCreationTimestamp; } } /*! * Encode Contact Version Field Information into the Versit Document */ void QVersitContactExporterPrivate::encodeVersion( const QContactDetail& detail, QList* generatedProperties, QSet* processedFields) { const QContactVersion &version = static_cast(detail); QVersitProperty property; property.setName(mPropertyMappings.value(detail.type()).second); QStringList values(QString::number(version.sequenceNumber())); values.append(QString::fromLocal8Bit(version.extendedVersion())); property.setValue(values); property.setValueType(QVersitProperty::CompoundType); *generatedProperties << property; *processedFields << QContactVersion::FieldSequenceNumber << QContactVersion::FieldExtendedVersion; } /*! * Encode BirthDay Field Information into the Versit Document */ void QVersitContactExporterPrivate::encodeBirthDay( const QContactDetail& detail, QList* generatedProperties, QSet* processedFields) { const QContactBirthday &bday = static_cast(detail); QVersitProperty property; property.setName(mPropertyMappings.value(detail.type()).second); QVariant variant = bday.value(QContactBirthday::FieldBirthday); QString value; if (variant.type() == QVariant::Date) { value = variant.toDate().toString(Qt::ISODate); } else if (variant.type() == QVariant::DateTime) { value = variant.toDateTime().toString(Qt::ISODate); } else { return; } property.setValue(value); *generatedProperties << property; *processedFields << QContactBirthday::FieldBirthday; } /*! * Encodes displaylabel property information into the Versit Document */ void QVersitContactExporterPrivate::encodeDisplayLabel( const QContactDetail &detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields) { const QContactDisplayLabel &displaylabelDetail = static_cast(detail); QVersitProperty property = VersitUtils::takeProperty(document, QStringLiteral("FN"), removedProperties); property.setName(QStringLiteral("FN")); QStringList value(property.variantValue().toStringList()); value.append(displaylabelDetail.label()); property.setValue(value); *generatedProperties << property; *processedFields << QContactDisplayLabel::FieldLabel; } /*! * Encode Comment i.e. Note Field Information into the Versit Document */ void QVersitContactExporterPrivate::encodeNote( const QContactDetail& detail, QList* generatedProperties, QSet* processedFields) { const QContactNote &contactNote = static_cast(detail); QVersitProperty property; property.setName(mPropertyMappings.value(detail.type()).second); property.setValue(contactNote.note()); *generatedProperties << property; *processedFields << QContactNote::FieldNote; } /*! * Encode Geo Prpoperties Field Information into the Versit Document */ void QVersitContactExporterPrivate::encodeGeoLocation( const QContactDetail& detail, QList* generatedProperties, QSet* processedFields) { const QContactGeoLocation &geoLocation = static_cast(detail); QVersitProperty property; property.setName(mPropertyMappings.value(detail.type()).second); property.setValue(QStringList() << QString::number(geoLocation.latitude()) << QString::number(geoLocation.longitude())); property.setValueType(QVersitProperty::CompoundType); *generatedProperties << property; *processedFields << QContactGeoLocation::FieldLongitude << QContactGeoLocation::FieldLatitude; } /*! * Encode organization properties to the versit document */ void QVersitContactExporterPrivate::encodeOrganization( const QContactDetail& detail, QList* generatedProperties, QSet* processedFields) { const QContactOrganization &organization = static_cast(detail); if (organization.title().length() > 0) { QVersitProperty property; property.setName(QStringLiteral("TITLE")); property.setValue(organization.title()); *generatedProperties << property; *processedFields << QContactOrganization::FieldTitle; } if (organization.name().length() > 0 || organization.department().size() > 0) { QVersitProperty property; property.setName(QStringLiteral("ORG")); QStringList values(organization.name()); values.append(organization.department()); property.setValue(values); property.setValueType(QVersitProperty::CompoundType); *generatedProperties << property; *processedFields << QContactOrganization::FieldName << QContactOrganization::FieldDepartment; } if (organization.logoUrl().isValid()) { QVersitProperty property; if (encodeContentFromFile(organization.logoUrl().toString(), property)) { property.setName(QStringLiteral("LOGO")); *generatedProperties << property; *processedFields << QContactOrganization::FieldLogoUrl; } } if (organization.assistantName().length() > 0) { QVersitProperty property; property.setName(QStringLiteral("X-ASSISTANT")); property.setValue(organization.assistantName()); *generatedProperties << property; *processedFields << QContactOrganization::FieldAssistantName; } if (organization.role().length() > 0) { QVersitProperty property; property.setName(QStringLiteral("ROLE")); property.setValue(organization.role()); *generatedProperties << property; *processedFields << QContactOrganization::FieldRole; } } void QVersitContactExporterPrivate::encodeRingtone( const QContactDetail &detail, QList* generatedProperties, QSet* processedFields) { const QContactRingtone &ringtone = static_cast(detail); QVersitProperty property; property.setName(mPropertyMappings.value(detail.type()).second); QUrl audioUrl(ringtone.audioRingtoneUrl()); // Url value if (!audioUrl.scheme().isEmpty() && !audioUrl.host().isEmpty() && audioUrl.scheme() != QStringLiteral("file")) { property.insertParameter(QStringLiteral("VALUE"), QStringLiteral("URL")); property.setValue(audioUrl.toString()); *generatedProperties << property; *processedFields << QContactRingtone::FieldAudioRingtoneUrl; // Local value } else if (encodeContentFromFile(ringtone.audioRingtoneUrl().toLocalFile(), property)) { *generatedProperties << property; *processedFields << QContactRingtone::FieldAudioRingtoneUrl; } } /*! * Encode avatar URIs into the Versit Document */ void QVersitContactExporterPrivate::encodeAvatar( const QContactDetail &detail, QList* generatedProperties, QSet* processedFields) { QVersitProperty property; property.setName(QStringLiteral("PHOTO")); const QContactAvatar &contactAvatar = static_cast(detail); QUrl imageUrl(contactAvatar.imageUrl()); // XXX: fix up this mess: checking the scheme here and in encodeContentFromFile, // organisation logo and ringtone are QStrings but avatar is a QUrl if (!imageUrl.scheme().isEmpty() && !imageUrl.host().isEmpty() && imageUrl.scheme() != QStringLiteral("file")) { property.insertParameter(QStringLiteral("VALUE"), QStringLiteral("URL")); property.setValue(imageUrl.toString()); *generatedProperties << property; *processedFields << QContactAvatar::FieldImageUrl; } else { if (encodeContentFromFile(contactAvatar.imageUrl().toLocalFile(), property)) { *generatedProperties << property; *processedFields << QContactAvatar::FieldImageUrl; } } } /*! * Encode gender property information into Versit Document */ void QVersitContactExporterPrivate::encodeGender( const QContactDetail &detail, QList* generatedProperties, QSet* processedFields) { const QContactGender &gender = static_cast(detail); if (!gender.gender()) return; QVersitProperty property; property.setName(mPropertyMappings.value(detail.type()).second); switch (gender.gender()) { case QContactGender::GenderMale: property.setValue(QStringLiteral("Male")); break; case QContactGender::GenderFemale: property.setValue(QStringLiteral("Female")); break; case QContactGender::GenderUnspecified: property.setValue(QStringLiteral("Unspecified")); break; default: // May only happen if new gender values are added to QContactGender // without adding them support above. qWarning() << "Trying to encode unknown gender value."; return; } *generatedProperties << property; *processedFields << QContactGender::FieldGender; } /*! * Encodes nickname property information into the Versit Document */ void QVersitContactExporterPrivate::encodeNickname( const QContactDetail &detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields) { const QContactNickname &nicknameDetail = static_cast(detail); QVersitProperty property = VersitUtils::takeProperty(document, QStringLiteral("X-NICKNAME"), removedProperties); property.setName(QStringLiteral("X-NICKNAME")); QStringList value(property.variantValue().toStringList()); value.append(nicknameDetail.nickname()); property.setValue(value); property.setValueType(QVersitProperty::ListType); *generatedProperties << property; *processedFields << QContactNickname::FieldNickname; } /*! * Encodes a contact tag into the Versit Document */ void QVersitContactExporterPrivate::encodeTag( const QContactDetail &detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields) { const QContactTag &tagDetail = static_cast(detail); QVersitProperty property = VersitUtils::takeProperty(document, QStringLiteral("CATEGORIES"), removedProperties); property.setName(QStringLiteral("CATEGORIES")); QStringList value(property.variantValue().toStringList()); value.append(tagDetail.tag()); property.setValue(value); property.setValueType(QVersitProperty::ListType); *generatedProperties << property; *processedFields << QContactTag::FieldTag; } /*! * Encode anniversary information into Versit Document */ void QVersitContactExporterPrivate::encodeAnniversary( const QContactDetail &detail, QList* generatedProperties, QSet* processedFields) { const QContactAnniversary &anniversary = static_cast(detail); QVersitProperty property; property.setName(mPropertyMappings.value(detail.type()).second); property.setValue(anniversary.originalDate().toString(Qt::ISODate)); *generatedProperties << property; *processedFields << QContactAnniversary::FieldOriginalDate; } /*! * Encode online account information into the Versit Document */ void QVersitContactExporterPrivate::encodeOnlineAccount( const QContactDetail &detail, QList* generatedProperties, QSet* processedFields) { const QContactOnlineAccount &onlineAccount = static_cast(detail); QList subTypes = onlineAccount.subTypes(); QContactOnlineAccount::Protocol protocol = onlineAccount.protocol(); QString propertyName; if (protocol == QContactOnlineAccount::ProtocolJabber) { propertyName = QStringLiteral("X-JABBER"); } else if (protocol == QContactOnlineAccount::ProtocolAim) { propertyName = QStringLiteral("X-AIM"); } else if (protocol == QContactOnlineAccount::ProtocolIcq) { propertyName = QStringLiteral("X-ICQ"); } else if (protocol == QContactOnlineAccount::ProtocolMsn) { propertyName = QStringLiteral("X-MSN"); } else if (protocol == QContactOnlineAccount::ProtocolQq) { propertyName = QStringLiteral("X-QQ"); } else if (protocol == QContactOnlineAccount::ProtocolYahoo) { propertyName = QStringLiteral("X-YAHOO"); } else if (protocol == QContactOnlineAccount::ProtocolSkype) { propertyName = QStringLiteral("X-SKYPE"); } else if (subTypes.contains(QContactOnlineAccount::SubTypeSip) || subTypes.contains(QContactOnlineAccount::SubTypeSipVoip) || subTypes.contains(QContactOnlineAccount::SubTypeVideoShare)) { propertyName = QStringLiteral("X-SIP"); } else if (subTypes.contains(QContactOnlineAccount::SubTypeImpp)) { propertyName = QStringLiteral("X-IMPP"); } if (!propertyName.isEmpty()) { QList onlineAccountContextInt; for (int i=0; i* generatedProperties, QSet* processedFields) { const QContactFamily &family = static_cast(detail); if (family.spouse().size()) { QVersitProperty property; property.setName(QStringLiteral("X-SPOUSE")); property.setValue(family.spouse()); *generatedProperties << property; *processedFields << QContactFamily::FieldSpouse; } if (family.children().size()) { QVersitProperty property; property.setName(QStringLiteral("X-CHILDREN")); property.setValue(family.children()); property.setValueType(QVersitProperty::ListType); *generatedProperties << property; *processedFields << QContactFamily::FieldChildren; } } /*! * Encode favorite versit property if its supported in Versit Document */ void QVersitContactExporterPrivate::encodeFavorite( const QContactDetail &detail, QList* generatedProperties, QSet* processedFields) { const QContactFavorite &favorite = static_cast(detail); QVersitProperty property; property.setName(mPropertyMappings.value(detail.type()).second); QString isFavorite = favorite.isFavorite() ? QStringLiteral("true") : QStringLiteral("false"); property.setValue(QStringList() << isFavorite << QString::number(favorite.index())); property.setValueType(QVersitProperty::CompoundType); *generatedProperties << property; *processedFields << QContactFavorite::FieldFavorite << QContactFavorite::FieldIndex; } /*! * Encode extended detail into the Versit Document */ void QVersitContactExporterPrivate::encodeExtendedDetail( const QContactDetail &detail, QList* generatedProperties, QSet* processedFields) { const QContactExtendedDetail &extendedDetail = static_cast(detail); QVersitProperty property; property.setName(mPropertyMappings.value(extendedDetail.type()).second); QString dataAsJson; if (VersitUtils::convertToJson(extendedDetail.data(), &dataAsJson)) { property.setValue(QStringList() << extendedDetail.name() << dataAsJson); } else { qWarning() << Q_FUNC_INFO << "Failed to export an extended detail"; return; } property.setValueType(QVersitProperty::CompoundType); *generatedProperties << property; *processedFields << QContactExtendedDetail::FieldName << QContactExtendedDetail::FieldData; } /*! * Check if \a resourceIdentifier represents a valid remote resource */ bool QVersitContactExporterPrivate::isValidRemoteUrl( const QString& resourceIdentifier) { QUrl remoteResource(resourceIdentifier); if ((!remoteResource.scheme().isEmpty() && !remoteResource.host().isEmpty()) || resourceIdentifier.contains(QStringLiteral("www."), Qt::CaseInsensitive)) return true; return false; } /*! * Encode parameters to \a property */ void QVersitContactExporterPrivate::encodeParameters( QVersitProperty& property, const QContactDetail::DetailType detailType, const QList& contexts, const QList& subTypes) { QList contextsList(contexts); // Contexts should be encoded first QList subtypesList(subTypes); // Contexts should be encoded first while (!contextsList.isEmpty()) { int value = contextsList.takeLast(); QString mappedValue = mContextMappings.value(value); if (mappedValue.length() > 0) { // QVersitProperty::addParameter inserts into beginning. // This is why the last value is taken from the list property.insertParameter(QStringLiteral("TYPE"),mappedValue); } } while (!subtypesList.isEmpty()) { int value = subtypesList.takeLast(); QString mappedValue = mSubTypeMappings.value(QPair( detailType, value)); if (mappedValue.length() > 0) { // QVersitProperty::addParameter inserts into beginning. // This is why the last value is taken from the list property.insertParameter(QStringLiteral("TYPE"),mappedValue); } } } /*! * Encode embedded content from the given \a resourcePath into \a property. */ bool QVersitContactExporterPrivate::encodeContentFromFile(const QString& resourcePath, QVersitProperty& property) { bool encodeContent = false; QVariant value; QByteArray imageData; QString mimeType; if (isValidRemoteUrl( resourcePath )) { encodeContent = true; value.setValue(resourcePath); property.insertParameter(QStringLiteral("VALUE"), QStringLiteral("uri")); } else if (mResourceHandler && mResourceHandler->loadResource(resourcePath, &imageData, &mimeType)) { value.setValue(imageData); if (!mimeType.isEmpty()) { // If mimeType is (eg.) "image/jpeg", set type parameter to "JPEG" int slashIndex = mimeType.indexOf(QLatin1Char('/')); if (slashIndex >= 0) property.insertParameter(QStringLiteral("TYPE"), mimeType.remove(0, slashIndex+1).toUpper()); } encodeContent = true; } else { // The file doesn't exist. Don't encode the path to a local file. } property.setValue(value); return encodeContent; } QT_END_NAMESPACE_VERSIT src/versit/qversitcontactexporter_p.h000066400000000000000000000174131233466112000205170ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITCONTACTEXPORTER_P_H #define QVERSITCONTACTEXPORTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContact; class QContactDetail; QT_END_NAMESPACE_CONTACTS QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSIT class QVersitContactHandler; class QVersitContactExporterPrivate { public: QVersitContactExporterPrivate(const QStringList& profiles = QStringList()); ~QVersitContactExporterPrivate(); void exportContact(const QContact& contact, QVersitDocument& versitDocument); protected: static void ensureDocumentContainsName(QVersitDocument* document); void encodeName(const QContactDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet *processedFields); void encodePhoneNumber(const QContactDetail& detail, QList* generatedProperties, QSet *processedFields); void encodeEmail(const QContactDetail& detail, QList* generatedProperties, QSet *processedFields); void encodeAddress(const QContactDetail& detail, QList* generatedProperties, QSet *processedFields); void encodeUrl(const QContactDetail& detail, QList* generatedProperties, QSet *processedFields); void encodeUid(const QContactDetail& detail, QList* generatedProperties, QSet *processedFields); void encodeRev(const QContactDetail& detail, QList* generatedProperties, QSet *processedFields); void encodeVersion(const QContactDetail &detail, QList* generatedProperties, QSet *processedFields); void encodeBirthDay(const QContactDetail& detail, QList* generatedProperties, QSet *processedFields); void encodeNote(const QContactDetail& detail, QList* generatedProperties, QSet *processedFields); void encodeGeoLocation( const QContactDetail& detail, QList* generatedProperties, QSet* processedFields); void encodeOrganization(const QContactDetail& detail, QList* generatedProperties, QSet *processedFields); void encodeRingtone(const QContactDetail &detail, QList* generatedProperties, QSet *processedFields); void encodeAvatar(const QContactDetail &detail, QList* generatedProperties, QSet *processedFields); void encodeGender(const QContactDetail &detail, QList* generatedProperties, QSet *processedFields); void encodeNickname(const QContactDetail &detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet *processedFields); void encodeTag(const QContactDetail &detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet *processedFields); void encodeAnniversary(const QContactDetail &detail, QList* generatedProperties, QSet *processedFields); void encodeOnlineAccount(const QContactDetail &detail, QList* generatedProperties, QSet *processedFields); void encodeFamily(const QContactDetail &detail, QList* generatedProperties, QSet *processedFields); void encodeFavorite(const QContactDetail &detail, QList* generatedProperties, QSet *processedFields); void encodeDisplayLabel( const QContactDetail &detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields); void encodeExtendedDetail(const QContactDetail &detail, QList* generatedProperties, QSet *processedFields); bool isValidRemoteUrl(const QString& resourceIdentifier); void encodeParameters(QVersitProperty& property, const QContactDetail::DetailType detailType, const QList &contexts, const QList &subTypes=QList()); bool encodeContentFromFile(const QString& resourcePath, QVersitProperty& property); public: // Data QList mDocuments; QMap mErrors; QVersitContactExporterDetailHandler* mDetailHandler; QVersitContactExporterDetailHandlerV2* mDetailHandler2; QList mPluginDetailHandlers; int mDetailHandlerVersion; QVersitDefaultResourceHandler* mDefaultResourceHandler; QVersitResourceHandler* mResourceHandler; // detail type -> : QMap > mPropertyMappings; QMap< QPair, QString> mSubTypeMappings; QHash< int ,QString> mContextMappings; }; QT_END_NAMESPACE_VERSIT #endif // QVERSITCONTACTEXPORTER_P_H src/versit/qversitcontacthandler.cpp000066400000000000000000000131731233466112000202770ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitcontacthandler.h" QT_BEGIN_NAMESPACE_VERSIT /*! \internal */ QStringList QVersitContactHandlerFactory::keys() const { return QStringList() << name(); } /*! \class QVersitContactHandler \brief The QVersitContactHandler class is a union of the QVersitContactImporterPropertyHandlerV2 and QVersitContactExporterDetailHandlerV2 interfaces. \ingroup versit-extension \inmodule QtVersit */ /*! \fn QVersitContactHandler::~QVersitContactHandler() This frees any memory used by the QVersitContactHandler. */ /*! \class QVersitContactHandlerFactory \brief The QVersitContactHandlerFactory class provides the interface for Versit plugins. \ingroup versit-extension \inmodule QtVersit This class provides a simple interface for the creation of QVersitContactHandler instances. Implement this interface to write a Versit plugin. For more details, see \l{Qt Versit Plugins}. */ /*! \fn static const QString QVersitContactHandlerFactory::ProfileSync() The constant string signifying a plugin that is relevant to import and export in a synchronization context. \sa profiles(), QVersitContactImporter::QVersitContactImporter(), QVersitContactExporter::QVersitContactExporter() */ /*! \fn static const QString QVersitContactHandlerFactory::ProfileBackup() The constant string signifying a plugin that will backup a QContact to vCard, so that exporting, then importing a contact will not lose any data. \sa profiles(), QVersitContactImporter::QVersitContactImporter(), QVersitContactExporter::QVersitContactExporter() */ /* TODO: make this a qdoc comment in 1.2 \variable QVersitContactHandlerFactory::ProfilePreserve The constant string signifying a plugin that will preserve a vCard within a QContact, so that importing, then exporting a vCard will not lose any data. \sa profiles(), QVersitContactImporter::QVersitContactImporter(), QVersitContactExporter::QVersitContactExporter() */ /*! \fn QVersitContactHandlerFactory::~QVersitContactHandlerFactory() This frees any memory used by the QVersitContactHandlerFactory. */ /*! \fn QSet QVersitContactHandlerFactory::profiles() const This function can be overridden to allow a plugin to report which profiles it is to be active under. If this (as in the default case) returns the empty set, it indicates that the plugin should be loaded under all profiles. If it returns a non-empty set, it will only be loaded for those profiles that are specified by the importer/exporter class. */ /*! \fn QString QVersitContactHandlerFactory::name() const This function should return a unique string that identifies the handlers provided by this factory. Typically, this will be of the form "org.qt-project.Qt.SampleVersitContactHandler" with the appropriate domain and handler name substituted. */ /*! \fn int QVersitContactHandlerFactory::index() const This function should return an index that helps with determining the order in which to run the plugins. Plugins are run in the following order: \list \li Positively-indexed, ascending \li Zero-indexed \li Negatively-indexed, ascending \endlist For example, plugins with an index of 1 are run first and plugins of index -1 are run last. If more than one plugin share an index, the order of execution between them is undefined. By default, this returns 0, which is recommended for plugins with no special ordering requirements. */ /*! \fn QVersitContactHandler* QVersitContactHandlerFactory::createHandler() const This function is called by the Versit importer or exporter class to create an instance of the handler provided by this factory. */ #include "moc_qversitcontacthandler.cpp" QT_END_NAMESPACE_VERSIT src/versit/qversitcontacthandler.h000066400000000000000000000074551233466112000177520ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITCONTACTHANDLER_H #define QVERSITCONTACTHANDLER_H #include #include #include #include QT_BEGIN_NAMESPACE_VERSIT // qdoc seems to not find QVersitContactHandler if it is declared first.. ugh class QVersitContactHandler; struct Q_VERSIT_EXPORT QVersitContactHandlerFactoryInterface : public QFactoryInterface { virtual QSet profiles() const =0; virtual QString name() const = 0; virtual int index() const = 0; virtual QVersitContactHandler* createHandler() const = 0; }; QT_END_NAMESPACE_VERSIT QT_BEGIN_NAMESPACE #define QT_VERSIT_CONTACT_HANDLER_INTERFACE "org.qt-project.Qt.QVersitContactHandlerFactoryInterface" Q_DECLARE_INTERFACE(QtVersit::QVersitContactHandlerFactoryInterface, QT_VERSIT_CONTACT_HANDLER_INTERFACE) QT_END_NAMESPACE QT_BEGIN_NAMESPACE_VERSIT class Q_VERSIT_EXPORT QVersitContactHandlerFactory : public QObject, public QVersitContactHandlerFactoryInterface { Q_OBJECT Q_INTERFACES(QtVersit::QVersitContactHandlerFactoryInterface:QFactoryInterface) public: virtual ~QVersitContactHandlerFactory() {} virtual QSet profiles() const { return QSet(); } virtual QString name() const = 0; virtual int index() const { return 0; } virtual QVersitContactHandler* createHandler() const = 0; virtual QStringList keys() const; inline static const QString ProfileSync() {return QStringLiteral("Sync");}; inline static const QString ProfileBackup() {return QStringLiteral("Backup");}; }; class Q_VERSIT_EXPORT QVersitContactHandler : public QVersitContactImporterPropertyHandlerV2, public QVersitContactExporterDetailHandlerV2 { public: virtual ~QVersitContactHandler() {} }; QT_END_NAMESPACE_VERSIT #endif // QVERSITCONTACTHANDLER_H src/versit/qversitcontactimporter.cpp000066400000000000000000000350261233466112000205240ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitcontactimporter.h" #include "qversitcontactimporter_p.h" QT_BEGIN_NAMESPACE_VERSIT /*! \deprecated \class QVersitContactImporterPropertyHandler \brief The QVersitContactImporterPropertyHandler class is the legacy interface for specifying custom import behaviour for vCard properties. This interface is replaced by QVersitContactImporterPropertyHandlerV2. For general information on extending Qt Versit, see the document on \l{Qt Versit Plugins}. \sa QVersitContactImporter */ /*! \fn QVersitContactImporterPropertyHandler::~QVersitContactImporterPropertyHandler() Frees any memory in use by this handler. */ /*! \fn bool QVersitContactImporterPropertyHandler::preProcessProperty(const QVersitDocument& document, const QVersitProperty& property, int contactIndex, QContact* contact) Process \a property and update \a contact with the corresponding QContactDetail(s). \a document provides the context within which the property was found. \a contactIndex specifies the position that \a contact will take in the list returned by \l QVersitContactImporter::importDocuments(). Returns true if the property has been handled and requires no further processing, false otherwise. This function is called on every QVersitProperty encountered during an import. Supply this function and return true to implement custom import behaviour. */ /*! \fn bool QVersitContactImporterPropertyHandler::postProcessProperty(const QVersitDocument& document, const QVersitProperty& property, bool alreadyProcessed, int contactIndex, QContact* contact) Process \a property and update \a contact with the corresponding QContactDetail(s). \a document provides the context within which the property was found. \a contactIndex specifies the position that \a contact will take in the list returned by \l QVersitContactImporter::importDocuments(). \a alreadyProcessed is true if the detail has already been processed either by \l preProcessProperty() or by QVersitContactImporter itself. Returns true if the property has been handled, false otherwise. This function is called on every QVersitProperty encountered during an import. This can be used to implement support for vCard properties not supported by QVersitContactImporter. */ /*! \class QVersitContactImporterPropertyHandlerV2 \brief The QVersitContactImporterPropertyHandlerV2 class is an interface for specifying custom import behaviour for vCard properties. \ingroup versit-extension \inmodule QtVersit This interface supercedes QVersitContactImporterPropertyHandler. For general information on extending Qt Versit, see the document on \l{Qt Versit Plugins}. \sa QVersitContactImporter */ /*! \fn QVersitContactImporterPropertyHandlerV2::~QVersitContactImporterPropertyHandlerV2() Frees any memory in use by this handler. */ /*! \fn void QVersitContactImporterPropertyHandlerV2::propertyProcessed(const QVersitDocument& document, const QVersitProperty& property, const QContact& contact, bool* alreadyProcessed, QList* updatedDetails) Process \a property and provide a list of updated details by adding them to \a updatedDetails. This function is called on every QVersitProperty encountered during an import, after the property has been processed by the QVersitContactImporter. An implementation of this function can be made to provide support for vCard properties not supported by QVersitContactImporter. The supplied \a document is the container for the \a property. \a alreadyProcessed is true if the QVersitContactImporter or another handler was successful in processing the property. If it is false and the handler processes the property, it should be set to true to inform later handlers that the property requires no further processing. \a contact holds the state of the contact before the property was processed by the importer. \a updatedDetails is initially filled with a list of details that the importer will update, and can be modified (by removing, modifying or adding details to the list) */ /*! \fn void QVersitContactImporterPropertyHandlerV2::documentProcessed(const QVersitDocument& document, QContact* contact) Perform any final processing on the \a contact generated by the \a document. This can be implemented by the handler to clear any internal state before moving onto the next document. This function is called after all QVersitProperties have been handled by the QVersitContactImporter. */ /*! \class QVersitContactImporter \brief The QVersitContactImporter class converts \l{QVersitDocument}{QVersitDocuments} to \l{QContact}{QContacts}. \ingroup versit \inmodule QtVersit This class is used to convert lists of \l{QVersitDocument}{QVersitDocuments} (which may be produced by a QVersitReader) to lists of \l{QContact}{QContacts} (which may be saved into a QContactManager. Unless there is an error, there is a one-to-one mapping between Versit documents and QContacts. The importer can be extended by clients by associating resource and property handlers. Here is a simple example of how to use QVersitContactImporter: \snippet qtversitdocsample/qtversitdocsample.cpp Import example \section1 Extension via handlers A \l QVersitResourceHandler is associated with the importer to supply the behaviour for saving files to persistent storage. By default, this is set to a \l QVersitDefaultResourceHandler, which does not save files to persistent storage. Note that photos found in vCards are not saved to disk by default. If a full-sized image needs to be loaded from a URL and persisted on disk, a custom QVersitResourceHandler should be supplied which implements this. By associating a QVersitContactImporterPropertyHandlerV2 with the importer using setPropertyHandler(), the client can pass in a handler to override the processing of properties and/or handle properties that QVersitContactImporter doesn't support. Also, handlers can be implicitly associated to an importer through the \l{Qt Versit Plugins}{handler plugin mechanism}. The importer can be constructed with a profile, which gives hints about what kind of handlers should be added to it. For example, the backup profile can be used to instruct the importer to interpret properties that have been generated by a backup-profiled QVersitContactExporter. To illustrate, a backup importer can be constructed with: \code QVersitContactImporter importer(QVersitContactHandlerFactory::ProfileBackup); \endcode For more details on how the backup plugin works, see \l{Qt Versit Plugins} \section1 Importing categories The importer imports the vCard CATEGORIES property by converting each category to a QContactTag. Some managers may not have support for QContactTag, but instead support categorization using the \l{QContactRelationship::HasMember}{HasMember} QContactRelationship along with contacts of type \l{QContactType::TypeGroup}{TypeGroup}. For these backends, if the categorization information needs to be retained through group relationships, extra work needs to be done to do the conversion. Below is some example code that does this translation. \snippet qtversitdocsample/qtversitdocsample.cpp Import relationship example \sa QVersitDocument, QVersitProperty, QVersitResourceHandler, QVersitContactImporterPropertyHandlerV2 */ /*! \enum QVersitContactImporter::Error This enum specifies an error that occurred during the most recent call to importDocuments() \value NoError The most recent operation was successful \value InvalidDocumentError One of the documents is not a vCard \value EmptyDocumentError One of the documents is empty */ /*! Constructs a new importer */ QVersitContactImporter::QVersitContactImporter() : d(new QVersitContactImporterPrivate) { } /*! * Constructs a new importer for the given \a profile. The profile strings should be one of those * defined by QVersitContactHandlerFactory, or a value otherwise agreed to by a \l{Qt Versit * Plugins}{Versit plugin}. * * The profile determines which plugins will be loaded to supplement the importer. */ QVersitContactImporter::QVersitContactImporter(const QString& profile) { if (profile.isEmpty()) d = new QVersitContactImporterPrivate(QStringList()); else d = new QVersitContactImporterPrivate(QStringList(profile)); } /*! * Constructs a new importer for the given \a profiles. The profile strings should be one of those * defined by QVersitContactHandlerFactory, or a value otherwise agreed to by a \l{Qt Versit * Plugins}{Versit plugin}. * * The profiles determine which plugins will be loaded to supplement the importer. */ QVersitContactImporter::QVersitContactImporter(const QStringList& profiles) : d(new QVersitContactImporterPrivate(profiles)) { } /*! Frees the memory used by the importer */ QVersitContactImporter::~QVersitContactImporter() { delete d; } /*! * Converts \a documents into a corresponding list of QContacts. After calling this, the converted * contacts can be retrieved by calling contacts(). * Returns true on success. If any of the documents cannot be imported as contacts (eg. they aren't * vCards), false is returned and errorMap() will return a list describing the errors that occurred. * The successfully imported documents will still be available via contacts(). * * \sa contacts(), errorMap() */ bool QVersitContactImporter::importDocuments(const QList& documents) { int documentIndex = 0; int contactIndex = 0; d->mContacts.clear(); d->mErrors.clear(); bool ok = true; foreach (const QVersitDocument& document, documents) { QContact contact; QVersitContactImporter::Error error; if (d->importContact(document, contactIndex, &contact, &error)) { d->mContacts.append(contact); contactIndex++; } else { d->mErrors.insert(documentIndex, error); ok = false; } documentIndex++; } return ok; } /*! * Returns the contacts imported in the most recent call to importDocuments(). * * \sa importDocuments() */ QList QVersitContactImporter::contacts() const { return d->mContacts; } /*! * \obsolete * * Use \l errorMap() instead. */ QMap QVersitContactImporter::errors() const { return d->mErrors; } /*! * Returns the map of errors encountered in the most recent call to importDocuments(). The key is * the index into the input list of documents and the value is the error that occurred on that * document. * * \sa importDocuments() */ QMap QVersitContactImporter::errorMap() const { return d->mErrors; } /*! * \deprecated * Sets \a handler to be the handler for processing QVersitProperties, or 0 to have no handler. * * Does not take ownership of the handler. The client should ensure the handler remains valid for * the lifetime of the exporter. This function is used for version 1 handlers. * * Only one property handler can be set. If another property handler (of any version) was * previously set, it will no longer be associated with the importer. */ void QVersitContactImporter::setPropertyHandler(QVersitContactImporterPropertyHandler* handler) { d->mPropertyHandlerVersion = 1; d->mPropertyHandler = handler; d->mPropertyHandler2 = NULL; } /*! * Sets \a handler to be the handler for processing QVersitProperties, or 0 to have no handler. * * Does not take ownership of the handler. The client should ensure the handler remains valid for * the lifetime of the exporter. This function is used for version 2 and higher handlers. * * Only one property handler can be set. If another property handler (of any version) was * previously set, it will no longer be associated with the importer. */ void QVersitContactImporter::setPropertyHandler(QVersitContactImporterPropertyHandlerV2* handler) { d->mPropertyHandlerVersion = 2; d->mPropertyHandler = 0; d->mPropertyHandler2 = handler; } /*! * \deprecated * Gets the handler for processing QVersitProperties. */ QVersitContactImporterPropertyHandler* QVersitContactImporter::propertyHandler() const { return d->mPropertyHandler; } /*! * Sets \a handler to be the handler to save files with, or 0 to have no handler. * * Does not take ownership of the handler. The client should ensure the handler remains valid for * the lifetime of the exporter. */ void QVersitContactImporter::setResourceHandler(QVersitResourceHandler* handler) { d->mResourceHandler = handler; } /*! * Returns the associated resource handler. */ QVersitResourceHandler* QVersitContactImporter::resourceHandler() const { return d->mResourceHandler; } QT_END_NAMESPACE_VERSIT src/versit/qversitcontactimporter.h000066400000000000000000000110631233466112000201640ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITCONTACTIMPORTER_H #define QVERSITCONTACTIMPORTER_H #include #include #include #include #include #include QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSIT class QVersitProperty; class QVersitContactImporterPrivate; class Q_VERSIT_EXPORT QVersitContactImporterPropertyHandler { public: virtual ~QVersitContactImporterPropertyHandler() {} virtual bool preProcessProperty(const QVersitDocument& document, const QVersitProperty& property, int contactIndex, QContact* contact) = 0; virtual bool postProcessProperty(const QVersitDocument& document, const QVersitProperty& property, bool alreadyProcessed, int contactIndex, QContact* contact) = 0; }; class Q_VERSIT_EXPORT QVersitContactImporterPropertyHandlerV2 { public: virtual ~QVersitContactImporterPropertyHandlerV2() {} virtual void propertyProcessed(const QVersitDocument& document, const QVersitProperty& property, const QContact& contact, bool *alreadyProcessed, QList* updatedDetails) = 0; virtual void documentProcessed(const QVersitDocument& document, QContact* contact) = 0; }; class Q_VERSIT_EXPORT QVersitContactImporter { public: enum Error { NoError = 0, InvalidDocumentError, EmptyDocumentError }; QVersitContactImporter(); explicit QVersitContactImporter(const QString& profile); explicit QVersitContactImporter(const QStringList& profiles); ~QVersitContactImporter(); bool importDocuments(const QList& documents); QList contacts() const; QMap errorMap() const; void setPropertyHandler(QVersitContactImporterPropertyHandlerV2* handler); void setResourceHandler(QVersitResourceHandler* handler); QVersitResourceHandler* resourceHandler() const; /* deprecated */ QMap errors() const; void setPropertyHandler(QVersitContactImporterPropertyHandler* handler); QVersitContactImporterPropertyHandler* propertyHandler() const; private: QVersitContactImporterPrivate* d; }; QT_END_NAMESPACE_VERSIT #endif // QVERSITCONTACTIMPORTER_H src/versit/qversitcontactimporter_p.cpp000066400000000000000000001100761233466112000210420ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitcontactimporter_p.h" #include #include "qversitcontacthandler.h" #include "qversitcontactpluginloader_p.h" #include "qversitcontactsdefs_p.h" #include "qversitdocument.h" #include "qversitpluginsearch_p.h" #include "qversitproperty.h" #include "qversitutils_p.h" QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSIT /*! * Constructor. */ QVersitContactImporterPrivate::QVersitContactImporterPrivate(const QStringList& profiles) : mPropertyHandler(NULL), mPropertyHandler2(NULL), mPropertyHandlerVersion(0), mDefaultResourceHandler(new QVersitDefaultResourceHandler), mResourceHandler(mDefaultResourceHandler) { // Contact detail mappings int versitPropertyCount = sizeof(versitContactDetailMappings)/sizeof(VersitContactDetailMapping); for (int i=0; i < versitPropertyCount; i++) { QString versitPropertyName = QLatin1Literal(versitContactDetailMappings[i].versitPropertyName); QPair contactDetail; contactDetail.first = versitContactDetailMappings[i].detailType; contactDetail.second = versitContactDetailMappings[i].detailField; mDetailMappings.insert(versitPropertyName,contactDetail); } // Context mappings int contextCount = sizeof(versitContextMappings)/sizeof(VersitContextMapping); for (int i=0; i < contextCount; i++) { mContextMappings.insert( versitContextMappings[i].contactContext, QLatin1String(versitContextMappings[i].versitString)); } // Subtype mappings int subTypeCount = sizeof(versitSubTypeMappings)/sizeof(VersitSubTypeMapping); for (int i=0; i < subTypeCount; i++) { mSubTypeMappings.insert( QPair( versitSubTypeMappings[i].detailType, versitSubTypeMappings[i].contactSubType), QLatin1String(versitSubTypeMappings[i].versitString)); } mPluginPropertyHandlers = QVersitContactPluginLoader::instance()->createContactHandlers(profiles); } /*! * Destructor. */ QVersitContactImporterPrivate::~QVersitContactImporterPrivate() { delete mDefaultResourceHandler; foreach (QVersitContactHandler* pluginHandler, mPluginPropertyHandlers) { delete pluginHandler; } } /*! * Generates a QContact from \a versitDocument. */ bool QVersitContactImporterPrivate::importContact( const QVersitDocument& document, int contactIndex, QContact* contact, QVersitContactImporter::Error* error) { if (document.componentType() != QStringLiteral("VCARD") && document.type() != QVersitDocument::VCard21Type && document.type() != QVersitDocument::VCard30Type) { *error = QVersitContactImporter::InvalidDocumentError; return false; } const QList properties = document.properties(); if (properties.size() == 0) { *error = QVersitContactImporter::EmptyDocumentError; return false; } // First, do the properties with PREF set so they appear first in the contact details foreach (const QVersitProperty& property, properties) { QStringList typeParameters = property.parameters().values(QStringLiteral("TYPE")); if (typeParameters.contains(QStringLiteral("PREF"), Qt::CaseInsensitive)) importProperty(document, property, contactIndex, contact); } // ... then, do the rest of the properties. foreach (const QVersitProperty& property, properties) { QStringList typeParameters = property.parameters().values(QStringLiteral("TYPE")); if (!typeParameters.contains(QStringLiteral("PREF"), Qt::CaseInsensitive)) importProperty(document, property, contactIndex, contact); } contact->setType(QContactType::TypeContact); mRestoreHandler.documentProcessed(); // run plugin handlers foreach (QVersitContactImporterPropertyHandlerV2* handler, mPluginPropertyHandlers) { handler->documentProcessed(document, contact); } // run the v2 handler, if set if (mPropertyHandler2 && mPropertyHandlerVersion > 1) { mPropertyHandler2->documentProcessed(document, contact); } return true; } void QVersitContactImporterPrivate::importProperty( const QVersitDocument& document, const QVersitProperty& property, int contactIndex, QContact* contact) { if (mPropertyHandler && mPropertyHandlerVersion == 1 && mPropertyHandler->preProcessProperty(document, property, contactIndex, contact)) return; QPair detailDefinition = mDetailMappings.value(property.name()); QContactDetail::DetailType detailType = detailDefinition.first; QList updatedDetails; bool success = false; // The following functions create and save the details to the contact switch (detailType) { case QContactDetail::TypeAddress: success = createAddress(property, contact, &updatedDetails); // pass in group break; case QContactDetail::TypeAnniversary: success = createAnniversary(property, contact, &updatedDetails); break; case QContactDetail::TypeAvatar: success = createAvatar(property, contact, &updatedDetails); break; case QContactDetail::TypeBirthday: success = createBirthday(property, contact, &updatedDetails); break; case QContactDetail::TypeExtendedDetail: success = createExtendedDetail(property, contact, &updatedDetails); break; case QContactDetail::TypeFamily: success = createFamily(property, contact, &updatedDetails); break; case QContactDetail::TypeFavorite: success = createFavorite(property, contact, &updatedDetails); break; case QContactDetail::TypeGender: success = createGender(property, contact, &updatedDetails); break; case QContactDetail::TypeGeoLocation: success = createGeoLocation(property, contact, &updatedDetails); break; case QContactDetail::TypeName: success = createName(property, contact, &updatedDetails); break; case QContactDetail::TypeNickname: success = createNicknames(property, contact, &updatedDetails); break; case QContactDetail::TypeDisplayLabel: success = createDisplaylabel(property, contact, &updatedDetails); break; case QContactDetail::TypeOnlineAccount: success = createOnlineAccount(property, contact, &updatedDetails); break; case QContactDetail::TypeOrganization: success = createOrganization(property, contact, &updatedDetails); break; case QContactDetail::TypePhoneNumber: success = createPhone(property, contact, &updatedDetails); break; case QContactDetail::TypeRingtone: success = createRingtone(property, contact, &updatedDetails); break; case QContactDetail::TypeTag: success = createTags(property, contact, &updatedDetails); break; case QContactDetail::TypeTimestamp: success = createTimeStamp(property, contact, &updatedDetails); break; case QContactDetail::TypeVersion: success = createVersion(property, contact, &updatedDetails); break; default: // Look up mDetailMappings for a simple mapping from property to detail. success = createNameValueDetail(property, contact, &updatedDetails); break; } if (mRestoreHandler.propertyProcessed(property, &updatedDetails)) success = true; // run plugin handlers foreach (QVersitContactImporterPropertyHandlerV2* handler, mPluginPropertyHandlers) { handler->propertyProcessed(document, property, *contact, &success, &updatedDetails); } // run the v2 handler, if set if (mPropertyHandler2 && mPropertyHandlerVersion > 1) { mPropertyHandler2->propertyProcessed(document, property, *contact, &success, &updatedDetails); } foreach (QContactDetail detail, updatedDetails) { contact->saveDetail(&detail); } // run the v1 handler, if set if (mPropertyHandler && mPropertyHandlerVersion == 1) mPropertyHandler->postProcessProperty(document, property, success, contactIndex, contact); } /*! * Creates a QContactName from \a property */ bool QVersitContactImporterPrivate::createName( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { QContactName name; QContactDetail detail = contact->detail(QContactName::Type); if (!detail.isEmpty()) { // If multiple name properties exist, // discard all except the first occurrence if (!detail.value(QContactName::FieldFirstName).toString().isEmpty()) return false; else name = QContactName(static_cast(detail)); } QVariant variant = property.variantValue(); if (property.valueType() != QVersitProperty::CompoundType || variant.type() != QVariant::StringList) return false; QStringList values = variant.toStringList(); QString value(takeFirst(values)); if (!value.isEmpty()) name.setLastName(value); value = takeFirst(values); if (!value.isEmpty()) name.setFirstName(value); value = takeFirst(values); if (!value.isEmpty()) name.setMiddleName(value); value = takeFirst(values); if (!value.isEmpty()) name.setPrefix(value); value = takeFirst(values); if (!value.isEmpty()) name.setSuffix(value); saveDetailWithContext(updatedDetails, name, extractContexts(property)); return true; } /*! * Creates a QContactPhoneNumber from \a property */ bool QVersitContactImporterPrivate::createPhone( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { Q_UNUSED(contact) QContactPhoneNumber phone; QString value(property.value()); if (value.isEmpty()) return false; phone.setNumber(property.value()); QStringList subTypes(extractSubTypes(property)); QList subTypesInt; foreach (const QString &stringValue, subTypes) { if (!mContextMappings.values().contains(stringValue)) { int mappedValue = mSubTypeMappings.key(stringValue).second; subTypesInt << mappedValue; } } if (property.name() == QStringLiteral("X-ASSISTANT-TEL")) subTypesInt << QContactPhoneNumber::SubTypeAssistant; if (!subTypesInt.isEmpty()) phone.setSubTypes(subTypesInt); saveDetailWithContext(updatedDetails, phone, extractContexts(property)); return true; } /*! * Creates a QContactAddress from \a property */ bool QVersitContactImporterPrivate::createAddress( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { Q_UNUSED(contact) QContactAddress address; QVariant variant = property.variantValue(); if (property.valueType() != QVersitProperty::CompoundType || variant.type() != QVariant::StringList) return false; QStringList addressParts = variant.toStringList(); QString value(takeFirst(addressParts)); if (!value.isEmpty()) address.setPostOfficeBox(value); // There is no setter for the Extended Address in QContactAddress: if (!addressParts.isEmpty()) addressParts.removeFirst(); value = takeFirst(addressParts); if (!value.isEmpty()) address.setStreet(value); value = takeFirst(addressParts); if (!value.isEmpty()) address.setLocality(value); value = takeFirst(addressParts); if (!value.isEmpty()) address.setRegion(value); value = takeFirst(addressParts); if (!value.isEmpty()) address.setPostcode(value); value = takeFirst(addressParts); if (!value.isEmpty()) address.setCountry(value); QStringList subTypes(extractSubTypes(property)); QList subTypesInt; foreach (const QString &stringValue, subTypes) { if (mSubTypeMappings.values().contains(stringValue)) { int mappedValue = mSubTypeMappings.key(stringValue).second; subTypesInt << mappedValue; } } if (!subTypesInt.isEmpty()) address.setSubTypes(subTypesInt); saveDetailWithContext(updatedDetails, address, extractContexts(property)); return true; } /*! * Creates a QContactOrganization from \a property */ bool QVersitContactImporterPrivate::createOrganization( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { QContactOrganization organization; QPair detailTypeAndFieldName = mDetailMappings.value(property.name()); int fieldName = detailTypeAndFieldName.second; QList organizations = contact->details(); foreach(const QContactOrganization& current, organizations) { if (current.value(fieldName).toString().length() == 0) { organization = current; break; } } if (fieldName == QContactOrganization::FieldName) { setOrganizationNames(organization, property); } else if (fieldName == QContactOrganization::FieldTitle) { organization.setTitle(property.value()); } else if (fieldName == QContactOrganization::FieldRole) { organization.setRole(property.value()); } else if (fieldName == QContactOrganization::FieldLogoUrl) { setOrganizationLogo(organization, property); } else if (fieldName == QContactOrganization::FieldAssistantName) { organization.setAssistantName(property.value()); } else { return false; } saveDetailWithContext(updatedDetails, organization, extractContexts(property)); return true; } /*! * Set the organization name and department(s) from \a property. */ void QVersitContactImporterPrivate::setOrganizationNames( QContactOrganization& organization, const QVersitProperty& property) const { QVariant variant = property.variantValue(); if (property.valueType() == QVersitProperty::CompoundType && variant.type() == QVariant::StringList) { QStringList values = variant.toStringList(); QString name(takeFirst(values)); if (!name.isEmpty()) organization.setName(name); if (!values.isEmpty()) organization.setDepartment(values); } } /*! * Set the organization logo from \a property. */ void QVersitContactImporterPrivate::setOrganizationLogo( QContactOrganization& org, const QVersitProperty& property) const { QString location; QByteArray data; saveDataFromProperty(property, &location, &data); if (!location.isEmpty()) org.setLogoUrl(QUrl(location)); } /*! * Creates a QContactTimeStamp from \a property */ bool QVersitContactImporterPrivate::createTimeStamp( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { Q_UNUSED(contact) QContactTimestamp timeStamp; QString value(property.value()); QDateTime dateTime = parseDateTime(value); if (!dateTime.isValid()) return false; timeStamp.setLastModified(dateTime); saveDetailWithContext(updatedDetails, timeStamp, extractContexts(property)); return true; } /*! * Creates a QContactVersion from \a property */ bool QVersitContactImporterPrivate::createVersion( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { // Allow only on version detail, discard others. QContactDetail detail = contact->detail(QContactVersion::Type); if (!detail.isEmpty()) return false; // Only one version detail is created QVariant variant = property.variantValue(); if (property.valueType() != QVersitProperty::CompoundType || variant.type() != QVariant::StringList) return false; QStringList values = variant.toStringList(); bool ok; QContactVersion version; version.setSequenceNumber(takeFirst(values).toInt(&ok)); version.setExtendedVersion(takeFirst(values).toLocal8Bit()); if (ok) { saveDetailWithContext(updatedDetails, version, extractContexts(property)); return true; } else { return false; } } /*! * Creates a QContactAnniversary from \a property */ bool QVersitContactImporterPrivate::createAnniversary( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { Q_UNUSED(contact) QContactAnniversary anniversary; bool justDate = false; QDateTime dateTime = parseDateTime(property.value(), &justDate); if (!dateTime.isValid()) return false; if (justDate) anniversary.setOriginalDate(dateTime.date()); else anniversary.setOriginalDateTime(dateTime); saveDetailWithContext(updatedDetails, anniversary, extractContexts(property)); return true; } /*! * Creates a QContactBirthday from \a property */ bool QVersitContactImporterPrivate::createBirthday( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { Q_UNUSED(contact) QContactBirthday bday; bool justDate = false; QDateTime dateTime = parseDateTime(property.value(), &justDate); if (!dateTime.isValid()) return false; if (justDate) bday.setDate(dateTime.date()); else bday.setDateTime(dateTime); saveDetailWithContext(updatedDetails, bday, extractContexts(property)); return true; } /*! * Creates a QContactDisplayLabel from \a property */ bool QVersitContactImporterPrivate::createDisplaylabel( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { QString label(property.value()); if (!label.isEmpty()) { QContactDisplayLabel displayLabel; QContactDisplayLabel existingDisplayLabel = contact->detail(); if (!existingDisplayLabel.isEmpty()) { displayLabel = existingDisplayLabel; } displayLabel.setLabel(property.value()); saveDetailWithContext(updatedDetails, displayLabel, extractContexts(property)); return true; } else { return false; } } /*! * Creates QContactNicknames from \a property */ bool QVersitContactImporterPrivate::createNicknames( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { Q_UNUSED(contact) QVariant variant = property.variantValue(); if (property.valueType() != QVersitProperty::ListType || variant.type() != QVariant::StringList) return false; QStringList values = variant.toStringList(); QList contexts = extractContexts(property); // We don't want to make duplicates of existing nicknames QSet existingNicknames; foreach (const QContactNickname& nickname, contact->details()) { existingNicknames.insert(nickname.nickname()); } foreach(const QString& value, values) { if (!value.isEmpty() && !existingNicknames.contains(value)) { QContactNickname nickname; nickname.setNickname(value); saveDetailWithContext(updatedDetails, nickname, contexts); existingNicknames.insert(value); } } return true; } /*! * Creates QContactTags from \a property */ bool QVersitContactImporterPrivate::createTags( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { Q_UNUSED(contact) QVariant variant = property.variantValue(); if (property.valueType() != QVersitProperty::ListType || variant.type() != QVariant::StringList) return false; QStringList values = variant.toStringList(); QList contexts = extractContexts(property); // We don't want to make duplicates of existing tags QSet existingTags; foreach (const QContactTag& tag, contact->details()) { existingTags.insert(tag.tag()); } foreach(const QString& value, values) { if (!value.isEmpty() && !existingTags.contains(value)) { QContactTag tag; tag.setTag(value); saveDetailWithContext(updatedDetails, tag, contexts); existingTags.insert(value); } } return true; } /*! * Creates a QContactOnlineAccount from \a property */ bool QVersitContactImporterPrivate::createOnlineAccount( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { Q_UNUSED(contact) QContactOnlineAccount onlineAccount; QString value(property.value()); if (value.isEmpty()) return false; onlineAccount.setAccountUri(property.value()); if (property.name() == QStringLiteral("X-SIP")) { QStringList subTypes = extractSubTypes(property); QList subTypesInt; foreach (const QString &stringValue, subTypes) { int mappedValue = mSubTypeMappings.key(stringValue).second; subTypesInt << mappedValue; } if (subTypes.isEmpty()) subTypesInt.append(QContactOnlineAccount::SubTypeSip); onlineAccount.setSubTypes(subTypesInt); } else if (property.name() == QStringLiteral("X-IMPP") || property.name() == QStringLiteral("IMPP")) { QList subTypeImppList; subTypeImppList << QContactOnlineAccount::SubTypeImpp; onlineAccount.setSubTypes(subTypeImppList); } else if (property.name() == QStringLiteral("X-JABBER")) { QList subTypeImppList; subTypeImppList << QContactOnlineAccount::SubTypeImpp; onlineAccount.setSubTypes(subTypeImppList); onlineAccount.setValue(QContactOnlineAccount::FieldProtocol, QContactOnlineAccount::ProtocolJabber); } else if (property.name() == QStringLiteral("X-AIM")) { onlineAccount.setValue(QContactOnlineAccount::FieldProtocol, QContactOnlineAccount::ProtocolAim); } else if (property.name() == QStringLiteral("X-ICQ")) { onlineAccount.setValue(QContactOnlineAccount::FieldProtocol, QContactOnlineAccount::ProtocolIcq); } else if (property.name() == QStringLiteral("X-MSN")) { onlineAccount.setValue(QContactOnlineAccount::FieldProtocol, QContactOnlineAccount::ProtocolMsn); } else if (property.name() == QStringLiteral("X-QQ")) { onlineAccount.setValue(QContactOnlineAccount::FieldProtocol, QContactOnlineAccount::ProtocolQq); } else if (property.name() == QStringLiteral("X-YAHOO")) { onlineAccount.setValue(QContactOnlineAccount::FieldProtocol, QContactOnlineAccount::ProtocolYahoo); } else if (property.name() == QStringLiteral("X-SKYPE") || property.name() == QStringLiteral("X-SKYPE-USERNAME")) { onlineAccount.setValue(QContactOnlineAccount::FieldProtocol, QContactOnlineAccount::ProtocolSkype); } else { onlineAccount.setValue(QContactOnlineAccount::FieldProtocol, QContactOnlineAccount::ProtocolUnknown); } saveDetailWithContext(updatedDetails, onlineAccount, extractContexts(property)); return true; } /*! * Creates a QContactRingtone from \a property */ bool QVersitContactImporterPrivate::createRingtone( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { Q_UNUSED(contact) QString location; QByteArray data; if (saveDataFromProperty(property, &location, &data) && !location.isEmpty()) { QContactRingtone ringtone; ringtone.setAudioRingtoneUrl(location); saveDetailWithContext(updatedDetails, ringtone, extractContexts(property)); return true; } return false; } /*! * Creates a QContactAvatar from \a property */ bool QVersitContactImporterPrivate::createAvatar( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { Q_UNUSED(contact) QString location; QByteArray data; bool success = false; if (saveDataFromProperty(property, &location, &data) && !location.isEmpty()) { QContactAvatar avatar; avatar.setImageUrl(location); saveDetailWithContext(updatedDetails, avatar, extractContexts(property)); success = true; } return success; } /*! * Creates a QContactGeoLocation from \a property */ bool QVersitContactImporterPrivate::createGeoLocation( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { Q_UNUSED(contact) QContactGeoLocation geo; QVariant variant = property.variantValue(); if (property.valueType() != QVersitProperty::CompoundType || variant.type() != QVariant::StringList) return false; QStringList values = variant.toStringList(); bool ok1; geo.setLatitude(takeFirst(values).toDouble(&ok1)); bool ok2; geo.setLongitude(takeFirst(values).toDouble(&ok2)); if (ok1 && ok2) { saveDetailWithContext(updatedDetails, geo, extractContexts(property)); return true; } else { return false; } } /*! * Creates a QContactFamily from \a property */ bool QVersitContactImporterPrivate::createFamily( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { QString val = property.value(); QContactFamily family = contact->detail(); if (property.name() == QStringLiteral("X-SPOUSE")) { if (val.isEmpty()) return false; family.setSpouse(val); } else if (property.name() == QStringLiteral("X-CHILDREN")) { QVariant variant = property.variantValue(); if (property.valueType() != QVersitProperty::ListType || variant.type() != QVariant::StringList) return false; QStringList values = variant.toStringList(); if (values.isEmpty()) return false; family.setChildren(values); } else { return false; } saveDetailWithContext(updatedDetails, family, extractContexts(property)); return true; } /*! * Creates a QContactFavorite from \a property */ bool QVersitContactImporterPrivate::createFavorite( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { QContactDetail detail = contact->detail(QContactFavorite::Type); if (!detail.isEmpty()) { // If multiple favorite properties exist, // discard all except the first occurrence return false; } QContactFavorite favorite; QVariant variant = property.variantValue(); if (property.valueType() != QVersitProperty::CompoundType || variant.type() != QVariant::StringList) return false; QStringList values = variant.toStringList(); QString value(takeFirst(values)); if (value.isEmpty()) return false; if (value == QStringLiteral("true")) favorite.setFavorite(true); else if (value == QStringLiteral("false")) favorite.setFavorite(false); else return false; value = takeFirst(values); if (value.isEmpty()) return false; bool ok = true; int index = value.toInt(&ok); if (ok) favorite.setIndex(index); else return false; saveDetailWithContext(updatedDetails, favorite, extractContexts(property)); return true; } /*! * Creates a QContactGender from \a property */ bool QVersitContactImporterPrivate::createGender( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { QContactGender gender; QContactDetail detail = contact->detail(QContactGender::Type); if (!detail.isEmpty()) { // If multiple gender properties exist, // discard all except the first occurrence if (!detail.value(QContactGender::FieldGender).toBool()) return false; else gender = QContactGender(static_cast(detail)); } QString val = property.value().toUpper(); if (property.name() != QStringLiteral("X-GENDER") || val.isEmpty()) return false; if (val == QStringLiteral("MALE")) { gender.setGender(QContactGender::GenderMale); } else if (val == QStringLiteral("FEMALE")) { gender.setGender(QContactGender::GenderFemale); } else if (val == QStringLiteral("UNSPECIFIED")) { gender.setGender(QContactGender::GenderUnspecified); } else { return false; } saveDetailWithContext(updatedDetails, gender, extractContexts(property)); return true; } /*! * Creates a QContactExtendedDetail from \a property */ bool QVersitContactImporterPrivate::createExtendedDetail( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { Q_UNUSED(contact) QContactExtendedDetail extendedDetail; const QVariant variant = property.variantValue(); if (property.valueType() != QVersitProperty::CompoundType || variant.type() != QVariant::StringList) return false; QStringList values = variant.toStringList(); extendedDetail.setName(takeFirst(values)); QVariant data; if (VersitUtils::convertFromJson(takeFirst(values), &data)) extendedDetail.setData(data); else return false; saveDetailWithContext(updatedDetails, extendedDetail, extractContexts(property)); return true; } /*! * Creates a simple name-value contact detail. */ bool QVersitContactImporterPrivate::createNameValueDetail( const QVersitProperty& property, QContact* contact, QList* updatedDetails) { Q_UNUSED(contact) QString value(property.value()); if (value.isEmpty()) return false; QPair nameAndValueType = mDetailMappings.value(property.name()); if (nameAndValueType.first == QContactDetail::TypeUndefined) return false; QContactDetail detail(nameAndValueType.first); detail.setValue(nameAndValueType.second, value); saveDetailWithContext(updatedDetails, detail, extractContexts(property)); return true; } /*! * Extracts the list of contexts from \a types */ QList QVersitContactImporterPrivate::extractContexts( const QVersitProperty& property) const { QStringList types = property.parameters().values(QStringLiteral("TYPE")); QList contexts; foreach (const QString& type, types) { QString value = type.toUpper(); if (mContextMappings.values().contains(value)) contexts << mContextMappings.key(value); } return contexts; } /*! * Extracts the list of subtypes from \a property */ QStringList QVersitContactImporterPrivate::extractSubTypes( const QVersitProperty& property) const { QStringList types = property.parameters().values(QStringLiteral("TYPE")); QStringList subTypes; foreach (const QString& type, types) { QString subType = type.toUpper(); if (subType.length() > 0) subTypes += subType; } return subTypes; } /*! * Takes the first value in \a list, or an empty QString is if the list is empty. */ QString QVersitContactImporterPrivate::takeFirst(QList& list) const { return list.empty() ? QString() : list.takeFirst(); } /*! * Parses a date and time from text */ QDateTime QVersitContactImporterPrivate::parseDateTime(QString value, bool *justDate) const { bool hasTime = false; bool utc = value.endsWith(QLatin1Char('Z'), Qt::CaseInsensitive); if (utc) value.chop(1); // take away z from end; QDateTime dateTime; if (value.contains(QLatin1Char('-'))) { dateTime = QDateTime::fromString(value,Qt::ISODate); hasTime = dateTime.isValid() && value.contains(QLatin1Char('T')); } else { switch (value.length()) { case 8: dateTime = QDateTime::fromString(value, QStringLiteral("yyyyMMdd")); break; case 15: dateTime = QDateTime::fromString(value, QStringLiteral("yyyyMMddThhmmss")); hasTime = true; break; // default: return invalid } } if (utc) dateTime.setTimeSpec(Qt::UTC); if (justDate) *justDate = !hasTime && !utc; // UTC implies a time of midnight return dateTime; } /*! * Extracts either a location (URI/filepath) from a \a property, or data (eg. if it was base64 * encoded). If the property contains data, an attempt is made to save it and the location of the * saved resource is recovered to *\a location. The data is stored into *\a data. */ bool QVersitContactImporterPrivate::saveDataFromProperty(const QVersitProperty &property, QString *location, QByteArray *data) const { bool found = false; const QString valueParam = property.parameters().value(QStringLiteral("VALUE")).toUpper(); QVariant variant(property.variantValue()); if (variant.type() == QVariant::String || valueParam == QStringLiteral("URL") || valueParam == QStringLiteral("URI")) { *location = property.value(); found |= !location->isEmpty(); } else if (variant.type() == QVariant::ByteArray) { *data = variant.toByteArray(); if (!data->isEmpty()) { found = true; *location = saveContentToFile(property, *data); } } return found; } /*! * Writes \a data to a file and returns the filename. \a property specifies the context in which * the data was found. */ QString QVersitContactImporterPrivate::saveContentToFile( const QVersitProperty& property, const QByteArray& data) const { QString filename; bool ok = false; if (mResourceHandler) ok = mResourceHandler->saveResource(data, property, &filename); return ok ? filename : QString(); } /*! * Adds \a detail to the \a updatedDetails list. Also sets the contexts to \a contexts if it is not * empty. */ void QVersitContactImporterPrivate::saveDetailWithContext( QList* updatedDetails, QContactDetail detail, const QList& contexts) { if (!contexts.isEmpty()) detail.setContexts(contexts); updatedDetails->append(detail); } QT_END_NAMESPACE_VERSIT src/versit/qversitcontactimporter_p.h000066400000000000000000000157131233466112000205110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITCONTACTIMPORTER_P_H #define QVERSITCONTACTIMPORTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_CONTACTS class QContactOrganization; QT_END_NAMESPACE_CONTACTS QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSIT class QVersitProperty; class QVersitDocument; class QVersitContactHandler; class QVersitContactImporterPrivate { public: QVersitContactImporterPrivate(const QStringList& profiles = QStringList()); ~QVersitContactImporterPrivate(); bool importContact(const QVersitDocument& versitDocument, int contactIndex, QContact* contact, QVersitContactImporter::Error* error); private: void importProperty(const QVersitDocument& document, const QVersitProperty& property, int contactIndex, QContact* contact); bool createName(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createPhone(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createAddress(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createOrganization(const QVersitProperty& property, QContact* contact, QList* updatedDetails); void setOrganizationNames(QContactOrganization& org, const QVersitProperty& property) const; void setOrganizationLogo(QContactOrganization& org, const QVersitProperty& property) const; bool createTimeStamp(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createVersion(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createAnniversary(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createBirthday(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createDisplaylabel(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createNicknames(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createTags(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createOnlineAccount(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createRingtone(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createAvatar(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createGeoLocation(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createFamily(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createFavorite(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createNameValueDetail(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createGender(const QVersitProperty& property, QContact* contact, QList* updatedDetails); bool createExtendedDetail(const QVersitProperty& property, QContact* contact, QList* updatedDetails); QList extractContexts(const QVersitProperty& property) const; QStringList extractSubTypes(const QVersitProperty& property) const; QString takeFirst(QList& list) const; QDateTime parseDateTime(QString text, bool *justDate = 0) const; QString saveContentToFile(const QVersitProperty& property, const QByteArray& data) const; bool saveDataFromProperty(const QVersitProperty& property, QString* location, QByteArray* data) const; void saveDetailWithContext(QList* updatedDetails, QContactDetail detail, const QList& contexts); public: // Data QList mContacts; QMap mErrors; QVersitContactImporterPropertyHandler* mPropertyHandler; QVersitContactImporterPropertyHandlerV2* mPropertyHandler2; QList mPluginPropertyHandlers; int mPropertyHandlerVersion; QVersitDefaultResourceHandler* mDefaultResourceHandler; QVersitResourceHandler* mResourceHandler; QVCardRestoreHandler mRestoreHandler; QHash > mDetailMappings; QMap< QPair, QString> mSubTypeMappings; QHash< int ,QString> mContextMappings; }; QT_END_NAMESPACE_VERSIT #endif // QVERSITCONTACTIMPORTER_P_H src/versit/qversitcontactpluginloader_p.cpp000066400000000000000000000123751233466112000216710ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitcontactpluginloader_p.h" #include #include "qversitpluginsearch_p.h" QT_BEGIN_NAMESPACE_VERSIT /*! A less-than function for factory indices (see QVersitContactHandlerFactory::index()). Positive values come first (ascendingly), then zero, then negative values (ascendingly). */ bool factoryLessThan(QVersitContactHandlerFactory* a, QVersitContactHandlerFactory* b) { if ((a->index() > 0 && b->index() > 0) || (a->index() < 0 && b->index() < 0)) // same sign return a->index() < b->index(); else // a is zero // or b is zero // or opposite sign return b->index() < a->index(); } QVersitContactPluginLoader* QVersitContactPluginLoader::mInstance = NULL; /*! * \class QVersitContactPluginLoader * This is a singleton class that loads Versit plugins for contacts processing \inmodule QtVersit */ QVersitContactPluginLoader::QVersitContactPluginLoader() { } /*! * Returns the singleton instance of the QVersitContactPluginLoader. */ QVersitContactPluginLoader* QVersitContactPluginLoader::instance() { if (!mInstance) mInstance = new QVersitContactPluginLoader; return mInstance; } void QVersitContactPluginLoader::loadPlugins() { QStringList plugins = mobilityPlugins(QStringLiteral("versit")); if (plugins != mPluginPaths) { mPluginPaths = plugins; foreach (const QString& pluginPath, mPluginPaths) { QPluginLoader qpl(pluginPath); QObject* plugin = qpl.instance(); QVersitContactHandlerFactory* contactPlugin = qobject_cast(plugin); if (contactPlugin && !mLoadedFactories.contains(contactPlugin->name())) { mLoadedFactories.insert(contactPlugin->name()); mContactHandlerFactories.append(contactPlugin); } } std::sort(mContactHandlerFactories.begin(), mContactHandlerFactories.end(), factoryLessThan); } } /*! * Creates and returns handlers from the plugin. If \a profiles is the empty string, only handlers * with an empty profile list are returned. If \a profiles is nonempty, only handlers with either * an empty profile list or a profile list that contains the given \a profiles are returned. * * The caller is responsible for deleting all returned handlers. */ QList QVersitContactPluginLoader::createContactHandlers(const QStringList& profiles) { loadPlugins(); QList handlers; foreach (const QVersitContactHandlerFactory* factory, mContactHandlerFactories) { // if the plugin specifies no profiles, include it QSet factoryProfiles(factory->profiles()); bool includePlugin = factory->profiles().isEmpty(); if (!includePlugin) { // if the plugin's profile list intersects with the requested profile list, include it. foreach (const QString& profile, profiles) { if (factoryProfiles.contains(profile)) { includePlugin = true; break; } } } if (includePlugin) { QVersitContactHandler* handler = factory->createHandler(); handlers.append(handler); } } return handlers; } QT_END_NAMESPACE_VERSIT src/versit/qversitcontactpluginloader_p.h000066400000000000000000000056461233466112000213410ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITCONTACTPLUGINLOADER_P_H #define QVERSITCONTACTPLUGINLOADER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include QT_BEGIN_NAMESPACE_VERSIT class QVersitContactPluginLoader { private: QVersitContactPluginLoader(); public: static QVersitContactPluginLoader* instance(); QList createContactHandlers(const QStringList& profiles); private: void loadPlugins(); static QVersitContactPluginLoader* mInstance; QSet mLoadedFactories; QList mContactHandlerFactories; QStringList mPluginPaths; }; QT_END_NAMESPACE_VERSIT #endif // QVERSITCONTACTPLUGINLOADER_P_H src/versit/qversitcontactsdefs_p.h000066400000000000000000000215471233466112000177560ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITCONTACTSDEFS_P_H #define QVERSITCONTACTSDEFS_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSIT //! [Property name mappings] // Mappings from versit property names to Qt contact details // For the case that there are multiple property names corresponding to the same detail, put the // primary one first and it will be the one used when exporting. const VersitContactDetailMapping versitContactDetailMappings[] = { {"ADR", QContactAddress::Type, -1}, {"BDAY", QContactBirthday::Type, QContactBirthday::FieldBirthday}, {"CATEGORIES", QContactTag::Type, QContactTag::FieldTag}, {"FN", QContactDisplayLabel::Type, QContactDisplayLabel::FieldLabel}, {"GEO", QContactGeoLocation::Type, -1}, {"EMAIL", QContactEmailAddress::Type, QContactEmailAddress::FieldEmailAddress}, {"IMPP", QContactOnlineAccount::Type, -1}, {"LOGO", QContactOrganization::Type, QContactOrganization::FieldLogoUrl}, {"N", QContactName::Type, -1}, {"NICKNAME", QContactNickname::Type, QContactNickname::FieldNickname}, {"NOTE", QContactNote::Type, QContactNote::FieldNote}, {"PHOTO", QContactAvatar::Type, -1}, {"ORG", QContactOrganization::Type, QContactOrganization::FieldName}, {"REV", QContactTimestamp::Type, -1}, {"ROLE", QContactOrganization::Type, QContactOrganization::FieldRole}, {"SOUND", QContactRingtone::Type, QContactRingtone::FieldAudioRingtoneUrl}, {"TEL", QContactPhoneNumber::Type, QContactPhoneNumber::FieldNumber}, {"TITLE", QContactOrganization::Type, QContactOrganization::FieldTitle}, {"UID", QContactGuid::Type, QContactGuid::FieldGuid}, {"URL", QContactUrl::Type, QContactUrl::FieldUrl}, {"X-ABUID", QContactGuid::Type, QContactGuid::FieldGuid}, {"X-AIM", QContactOnlineAccount::Type, -1}, {"X-ANNIVERSARY", QContactAnniversary::Type, -1}, {"X-ASSISTANT", QContactOrganization::Type, QContactOrganization::FieldAssistantName}, {"X-ASSISTANT-TEL", QContactPhoneNumber::Type, QContactPhoneNumber::SubTypeAssistant}, {"X-CHILDREN", QContactFamily::Type, QContactFamily::FieldChildren}, {"X-EPOCSECONDNAME",QContactNickname::Type, QContactNickname::FieldNickname}, {"X-EVOLUTION-SPOUSE", QContactFamily::Type, QContactFamily::FieldSpouse}, {"X-EVOLUTION-ANNIVERSARY", QContactAnniversary::Type, -1}, {"X-GADUGADU", QContactOnlineAccount::Type, -1}, {"X-GENDER", QContactGender::Type, QContactGender::FieldGender}, {"X-ICQ", QContactOnlineAccount::Type, -1}, {"X-IMPP", QContactOnlineAccount::Type, -1}, {"X-JABBER", QContactOnlineAccount::Type, -1}, {"X-KADDRESSBOOK-X-SPOUSENAME", QContactFamily::Type, QContactFamily::FieldSpouse}, {"X-KADDRESSBOOK-X-ANNIVERSARY", QContactAnniversary::Type, -1}, {"X-KADDRESSBOOK-X-IMADDRESS", QContactOnlineAccount::Type, -1}, {"X-MS-CARDPICTURE", QContactAvatar::Type, -1}, {"X-MS-IMADDRESS", QContactOnlineAccount::Type, -1}, {"X-MSN", QContactOnlineAccount::Type, -1}, {"X-NICKNAME", QContactNickname::Type, QContactNickname::FieldNickname}, {"X-QQ", QContactOnlineAccount::Type, -1}, {"X-QTPROJECT-EXTENDED-DETAIL", QContactExtendedDetail::Type, -1}, {"X-QTPROJECT-FAVORITE", QContactFavorite::Type, -1}, {"X-QTPROJECT-VERSION", QContactVersion::Type, -1}, {"X-SIP", QContactOnlineAccount::Type, -1}, {"X-SKYPE", QContactOnlineAccount::Type, -1}, {"X-SKYPE-USERNAME", QContactOnlineAccount::Type, -1}, {"X-SPOUSE", QContactFamily::Type, QContactFamily::FieldSpouse}, {"X-YAHOO", QContactOnlineAccount::Type, -1} }; //! [Property name mappings] // Mappings from versit TYPE parameters to Qt contact detail contexts const VersitContextMapping versitContextMappings[] = { {"HOME", QContactDetail::ContextHome}, {"WORK", QContactDetail::ContextWork}, {"OTHER", QContactDetail::ContextOther} }; //! [Property type parameter mappings] // Mappings from versit TYPE parameters to Qt contact detail subtypes const VersitSubTypeMapping versitSubTypeMappings[] = { {"DOM", QContactDetail::TypeAddress, QContactAddress::SubTypeDomestic}, {"INTL", QContactDetail::TypeAddress, QContactAddress::SubTypeInternational}, {"POSTAL", QContactDetail::TypeAddress, QContactAddress::SubTypePostal}, {"PARCEL", QContactDetail::TypeAddress, QContactAddress::SubTypeParcel}, {"VOICE", QContactDetail::TypePhoneNumber, QContactPhoneNumber::SubTypeVoice}, {"CELL", QContactDetail::TypePhoneNumber, QContactPhoneNumber::SubTypeMobile}, {"MODEM", QContactDetail::TypePhoneNumber, QContactPhoneNumber::SubTypeModem}, {"CAR", QContactDetail::TypePhoneNumber, QContactPhoneNumber::SubTypeCar}, {"VIDEO", QContactDetail::TypePhoneNumber, QContactPhoneNumber::SubTypeVideo}, {"FAX", QContactDetail::TypePhoneNumber, QContactPhoneNumber::SubTypeFax}, {"BBS", QContactDetail::TypePhoneNumber, QContactPhoneNumber::SubTypeBulletinBoardSystem}, {"PAGER", QContactDetail::TypePhoneNumber, QContactPhoneNumber::SubTypePager}, {"ISDN", QContactDetail::TypePhoneNumber, QContactPhoneNumber::SubTypeLandline}, {"SWIS", QContactDetail::TypeOnlineAccount, QContactOnlineAccount::SubTypeVideoShare}, {"VOIP", QContactDetail::TypeOnlineAccount, QContactOnlineAccount::SubTypeSipVoip} }; //! [Property type parameter mappings] QT_END_NAMESPACE_VERSIT #endif // QVERSITCONTACTSDEFS_P_H src/versit/qversitdefs_p.h000066400000000000000000000163241233466112000162140ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITDEFS_P_H #define QVERSITDEFS_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QTCONTACTS_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSIT // Mapping between a string in versit specifications and Qt contacts struct VersitContextMapping { const char* versitString; const int contactContext; }; // Mapping between a string in versit specifications and Qt contacts struct VersitSubTypeMapping { const char* versitString; const QContactDetail::DetailType detailType; const int contactSubType; }; struct VersitFileExtensionMapping { const char *mimeType; const char *extension; }; // Mapping between a string in versit specifications and Qt contact details struct VersitContactDetailMapping { const char* versitPropertyName; const QContactDetail::DetailType detailType; const int detailField; }; //! [File extension mappings] // Mappings from mime types to file extensions const VersitFileExtensionMapping versitFileExtensionMappings[] = { {"application/octet-stream", "obj"}, {"audio/x-pn-realaudio", "ra"}, {"application/xml", "wsdl"}, {"application/octet-stream", "dll"}, {"image/x-cmu-raster", "ras"}, {"application/x-pn-realaudio", "ram"}, {"application/x-bcpio", "bcpio"}, {"application/x-sh", "sh"}, {"video/mpeg", "m1v"}, {"image/x-xwindowdump", "xwd"}, {"video/x-msvideo", "avi"}, {"image/x-ms-bmp", "bmp"}, {"application/x-shar", "shar"}, {"application/x-javascript", "js"}, {"application/x-wais-source", "src"}, {"application/x-dvi", "dvi"}, {"audio/x-aiff", "aif"}, {"text/plain", "ksh"}, {"application/msword", "dot"}, {"message/rfc822", "mht"}, {"application/x-pkcs12", "p12"}, {"text/css", "css"}, {"application/x-csh", "csh"}, {"application/vnd.ms-powerpoint", "pwz"}, {"application/pdf", "pdf"}, {"application/x-netcdf", "cdf"}, {"text/plain", "pl"}, {"text/plain", "c"}, {"image/jpeg", "jpe"}, {"image/jpeg", "jpg"}, {"text/x-python", "py"}, {"text/xml", "xml"}, {"image/jpeg", "jpeg"}, {"application/postscript", "ps"}, {"application/x-gtar", "gtar"}, {"image/x-xpixmap", "xpm"}, {"application/x-hdf", "hdf"}, {"message/rfc822", "nws"}, {"text/tab-separated-values", "tsv"}, {"application/xml", "xpdl"}, {"application/pkcs7-mime", "p7c"}, {"application/postscript", "eps"}, {"image/ief", "ief"}, {"application/octet-stream", "so"}, {"application/vnd.ms-excel", "xlb"}, {"image/x-portable-bitmap", "pbm"}, {"application/x-texinfo", "texinfo"}, {"application/vnd.ms-excel", "xls"}, {"application/x-tex", "tex"}, {"text/richtext", "rtx"}, {"text/html", "html"}, {"audio/x-aiff", "aiff"}, {"audio/x-aiff", "aifc"}, {"application/octet-stream", "exe"}, {"text/x-sgml", "sgm"}, {"image/tiff", "tif"}, {"video/mpeg", "mpeg"}, {"application/x-ustar", "ustar"}, {"image/gif", "gif"}, {"application/vnd.ms-powerpoint", "ppt"}, {"application/vnd.ms-powerpoint", "pps"}, {"text/x-sgml", "sgml"}, {"image/x-portable-pixmap", "ppm"}, {"application/x-latex", "latex"}, {"text/plain", "bat"}, {"video/quicktime", "mov"}, {"application/vnd.ms-powerpoint", "ppa"}, {"application/x-troff", "tr"}, {"application/xml", "rdf"}, {"application/xml", "xsl"}, {"message/rfc822", "eml"}, {"application/x-netcdf", "nc"}, {"application/x-sv4cpio", "sv4cpio"}, {"application/octet-stream", "bin"}, {"text/plain", "h"}, {"application/x-tcl", "tcl"}, {"application/msword", "wiz"}, {"application/octet-stream", "o"}, {"application/octet-stream", "a"}, {"application/postscript", "ai"}, {"audio/x-wav", "wav"}, {"text/x-vcard", "vcf"}, {"image/x-xbitmap", "xbm"}, {"text/plain", "txt"}, {"audio/basic", "au"}, {"application/x-troff", "t"}, {"image/tiff", "tiff"}, {"application/x-texinfo", "texi"}, {"application/oda", "oda"}, {"application/x-troff-ms", "ms"}, {"image/x-rgb", "rgb"}, {"application/x-troff-me", "me"}, {"application/x-sv4crc", "sv4crc"}, {"video/quicktime", "qt"}, {"video/mpeg", "mpa"}, {"video/mpeg", "mpg"}, {"video/mpeg", "mpe"}, {"application/msword", "doc"}, {"image/x-portable-graymap", "pgm"}, {"application/vnd.ms-powerpoint", "pot"}, {"application/x-mif", "mif"}, {"application/x-troff", "roff"}, {"text/html", "htm"}, {"application/x-troff-man", "man"}, {"text/x-setext", "etx"}, {"application/zip", "zip"}, {"video/x-sgi-movie", "movie"}, {"application/x-python-code", "pyc"}, {"image/png", "png"}, {"application/x-pkcs12", "pfx"}, {"message/rfc822", "mhtml"}, {"application/x-tar", "tar"}, {"image/x-portable-anymap", "pnm"}, {"application/x-python-code", "pyo"}, {"audio/basic", "snd"}, {"application/x-cpio", "cpio"}, {"application/x-shockwave-flash", "swf"}, {"audio/mpeg", "mp3"}, {"audio/mpeg", "mp2"} }; //! [File extension mappings] QT_END_NAMESPACE_VERSIT #endif // QVERSITDEFS_P_H src/versit/qversitdocument.cpp000066400000000000000000000227151233466112000171260ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitdocument.h" #include "qversitdocument_p.h" #ifndef QT_NO_DEBUG_STREAM #include #endif QT_BEGIN_NAMESPACE_VERSIT /*! \class QVersitDocument \brief The QVersitDocument class is a container for a list of versit properties. \ingroup versit \inmodule QtVersit A vCard is represented in abstract form as a QVersitDocument that consists of a number of properties such as a name (N), a telephone number (TEL) and an email address (EMAIL), for instance. Each of these properties is stored as an instance of a QVersitProperty in a QVersitDocument. In addition to the list of properties, QVersitDocument also records the type of the Versit document in two ways. The VersitType enum describes the format in which the document is to be serialized by QVersitWriter (or the format from which it was read by QVersitReader), and should not be used to infer any semantics about the document data. The componentType field is a string corresponding directly to the value of the BEGIN line in a document. For example, for a vCard, this will always be the string "VCARD"; for an iCalendar, it could be "VCALENDAR", "VEVENT", "VTODO", "VJOURNAL", "VALARM" or "VTIMEZONE". As well as properties, a QVersitDocument can hold other documents. For iCalendar, this is how a single VCALENDAR document can compose documents of type VEVENT, VTODO, etc. For example, for the following iCalendar: \code BEGIN:VCALENDAR VERSION:2.0 BEGIN:VEVENT SUMMARY:Christmas DTSTART:20001225 END:VEVENT END:VCALENDAR \endcode This can be represented as a QVersitDocument of with componentType VCALENDAR and versitType ICalendar20Type. It contains no properties (note: the VERSION property is not stored explicitly as a property) and one sub-document. The sub-document has componentType VEVENT and versitType ICalendar20Type, and contains two properties. QVersitDocument supports implicit sharing. \sa QVersitProperty */ /*! \enum QVersitDocument::VersitType This enum describes a Versit document serialization format and version. \value InvalidType No type specified or a document with an invalid type was parsed \value VCard21Type vCard version 2.1 \value VCard30Type vCard version 3.0 \value VCard40Type vCard version 4.0 \value ICalendar20Type iCalendar version 2.0 */ /*! Constructs a new empty document */ QVersitDocument::QVersitDocument() : d(new QVersitDocumentPrivate()) { } /*! Constructs a new empty document with the type set to \a type */ QVersitDocument::QVersitDocument(VersitType type) : d(new QVersitDocumentPrivate()) { d->mVersitType = type; } /*! Constructs a document that is a copy of \a other */ QVersitDocument::QVersitDocument(const QVersitDocument& other) : d(other.d) { } /*! Frees the memory used by the document */ QVersitDocument::~QVersitDocument() { } /*! Assigns this document to \a other */ QVersitDocument& QVersitDocument::operator=(const QVersitDocument& other) { if (this != &other) d = other.d; return *this; } /*! Returns true if this is equal to \a other; false otherwise. */ bool QVersitDocument::operator==(const QVersitDocument& other) const { return d->mVersitType == other.d->mVersitType && d->mProperties == other.d->mProperties && d->mSubDocuments == other.d->mSubDocuments && d->mComponentType == other.d->mComponentType; } /*! Returns true if this is not equal to \a other; false otherwise. */ bool QVersitDocument::operator!=(const QVersitDocument& other) const { return !(*this == other); } /*! Returns the hash value for \a key. */ uint qHash(const QVersitDocument &key) { int hash = QT_PREPEND_NAMESPACE(qHash)(key.type()); hash += QT_PREPEND_NAMESPACE(qHash)(key.componentType()); hash += key.properties().length() + key.subDocuments().length(); foreach (const QVersitProperty& property, key.properties()) { hash += qHash(property); } foreach (const QVersitDocument& nested, key.subDocuments()) { hash += qHash(nested); } return hash; } #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QVersitDocument& document) { dbg.nospace() << "QVersitDocument(" << document.type() << ", " << document.componentType() << ')'; foreach (const QVersitProperty& property, document.properties()) { dbg.space() << '\n' << property; } foreach (const QVersitDocument& nested, document.subDocuments()) { dbg.space() << '\n' << nested; } return dbg.maybeSpace(); } #endif /*! * Sets the versit document type to \a type. This determines the format in which the document is * to be serialized. */ void QVersitDocument::setType(VersitType type) { d->mVersitType = type; } /*! * Gets the versit document type. */ QVersitDocument::VersitType QVersitDocument::type() const { return d->mVersitType; } /*! * Sets the versit component type to \a componentType (eg. VCARD, VCALENDAR, VEVENT, etc.) */ void QVersitDocument::setComponentType(QString componentType) { d->mComponentType = componentType; } /*! * Gets the versit component type */ QString QVersitDocument::componentType() const { return d->mComponentType; } /*! * Add \a property to the list of contained versit properties. * The property is appended as the last property of the list. */ void QVersitDocument::addProperty(const QVersitProperty& property) { d->mProperties.append(property); } /*! * Removes the property \a property from the versit document. */ void QVersitDocument::removeProperty(const QVersitProperty& property) { d->mProperties.removeAll(property); } /*! * Removes all the properties with the given \a name from the versit document. */ void QVersitDocument::removeProperties(const QString& name) { for (int i=d->mProperties.count()-1; i >=0; i--) { if (d->mProperties[i].name() == name) { d->mProperties.removeAt(i); } } } /*! * Sets the list of properties to \a properties. Logically, all of the existing properties are * removed and all of the supplied \a properties are added. */ void QVersitDocument::setProperties(const QList& properties) { d->mProperties = properties; } /*! * Gets the list of the contained versit properties. * Note that the actual properties cannot be modified using the copy. */ QList QVersitDocument::properties() const { return d->mProperties; } /*! * Adds \a subdocument to the Versit document. */ void QVersitDocument::addSubDocument(const QVersitDocument& subdocument) { d->mSubDocuments.append(subdocument); } /*! * Removes the \a subdocument from the versit document. */ void QVersitDocument::removeSubDocument(const QVersitDocument& subdocument) { d->mSubDocuments.removeAll(subdocument); } /*! * Sets the list of subdocuments to \a documents. */ void QVersitDocument::setSubDocuments(const QList& documents) { d->mSubDocuments = documents; } /*! * Returns the list of subdocuments contained within this Versit document. */ QList QVersitDocument::subDocuments() const { return d->mSubDocuments; } /*! * Returns true if the document is empty. */ bool QVersitDocument::isEmpty() const { return d->mProperties.isEmpty() && d->mSubDocuments.isEmpty() && d->mVersitType == QVersitDocument::InvalidType; } /*! * Clears the document, removing all properties, sub-documents and metadata. */ void QVersitDocument::clear() { d->mProperties.clear(); d->mSubDocuments.clear(); d->mVersitType = QVersitDocument::InvalidType; d->mComponentType.clear(); } QT_END_NAMESPACE_VERSIT src/versit/qversitdocument.h000066400000000000000000000100141233466112000165600ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITDOCUMENT_H #define QVERSITDOCUMENT_H #include #include #include #include QT_BEGIN_NAMESPACE_VERSIT class QVersitProperty; class QVersitDocumentPrivate; class Q_VERSIT_EXPORT QVersitDocument { public: enum VersitType { InvalidType, VCard21Type, // vCard version 2.1 VCard30Type, // vCard version 3.0 (RFC 2426) VCard40Type, // vCard version 4.0 ICalendar20Type,// iCalendar version 2.0 (RFC 2445/5545) }; QVersitDocument(); QVersitDocument(const QVersitDocument& other); QVersitDocument(VersitType type); ~QVersitDocument(); QVersitDocument& operator=(const QVersitDocument& other); bool operator==(const QVersitDocument& other) const; bool operator!=(const QVersitDocument& other) const; // Metadata about the versit document // The type determines the format for serialization void setType(VersitType type); VersitType type() const; // The componentType is the value of the BEGIN property void setComponentType(QString componentType); QString componentType() const; // The content void addProperty(const QVersitProperty& property); void removeProperty(const QVersitProperty& property); void removeProperties(const QString& name); void setProperties(const QList& properties); QList properties() const; void addSubDocument(const QVersitDocument& subdocument); void removeSubDocument(const QVersitDocument& subdocument); void setSubDocuments(const QList& documents); QList subDocuments() const; bool isEmpty() const; void clear(); private: QSharedDataPointer d; }; Q_VERSIT_EXPORT uint qHash(const QVersitDocument& key); #ifndef QT_NO_DEBUG_STREAM Q_VERSIT_EXPORT QDebug operator<<(QDebug dbg, const QVersitDocument& property); #endif QT_END_NAMESPACE_VERSIT Q_DECLARE_METATYPE(QTVERSIT_PREPEND_NAMESPACE(QVersitDocument)) #endif // QVERSITDOCUMENT_H src/versit/qversitdocument_p.cpp000066400000000000000000000046131233466112000174420ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitdocument_p.h" QT_BEGIN_NAMESPACE_VERSIT QVersitDocumentPrivate::QVersitDocumentPrivate() : QSharedData(), mVersitType(QVersitDocument::InvalidType) { } QVersitDocumentPrivate::QVersitDocumentPrivate(const QVersitDocumentPrivate& other) : QSharedData(other), mVersitType(other.mVersitType), mComponentType(other.mComponentType), mProperties(other.mProperties), mSubDocuments(other.mSubDocuments) { } QT_END_NAMESPACE_VERSIT src/versit/qversitdocument_p.h000066400000000000000000000054431233466112000171110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITDOCUMENT_P_H #define QVERSITDOCUMENT_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include QT_BEGIN_NAMESPACE_VERSIT class QVersitDocumentPrivate : public QSharedData { public: QVersitDocumentPrivate(); QVersitDocumentPrivate(const QVersitDocumentPrivate& other); ~QVersitDocumentPrivate() { } QVersitDocument::VersitType mVersitType; QString mComponentType; QList mProperties; QList mSubDocuments; }; QT_END_NAMESPACE_VERSIT #endif // QVERSITDOCUMENT_P_H src/versit/qversitdocumentwriter_p.cpp000066400000000000000000000231071233466112000206760ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitdocumentwriter_p.h" #include #include "qversitutils_p.h" QT_BEGIN_NAMESPACE_VERSIT #define MAX_LINE_LENGTH 76 /*! \class QVersitDocumentWriter \internal \brief The QVersitDocumentWriter class provides an interface for writing a single versit document into a vCard text string. */ /*! Constructs a writer. * \a documentType is the type of Versit document, as printed on the BEGIN line of output * eg. "VCARD" * \a version is the version of the Versit format, as printed on the VERSION line of output. * eg. "2.1" */ QVersitDocumentWriter::QVersitDocumentWriter(QVersitDocument::VersitType type) : mType(type), mDevice(0), mCodec(0), mCodecIsAscii(false), mEncoder(0), mSuccessful(true), mCurrentLineLength(0) { } QVersitDocumentWriter::~QVersitDocumentWriter() { if (mEncoder) delete mEncoder; } /*! Sets the codec to write with. */ void QVersitDocumentWriter::setCodec(QTextCodec *codec) { if (mEncoder) delete mEncoder; mCodec = codec; mEncoder = codec->makeEncoder(); // Hack so the encoder doesn't output a byte order mark for UTF-8. if (mCodec->name() == "UTF-8") mEncoder->fromUnicode(QString()); // UTF-(16|32)(LE|BE) are the only codecs where characters in the base64 range aren't encoded // the same as in ASCII. For ASCII compatible codecs, we can do some optimizations. mCodecIsAsciiCompatible = !(mCodec->name().startsWith("UTF-16") || mCodec->name().startsWith("UTF-32")); } /*! Specifies that the codec is actually ASCII (setCodec must also be called with an ASCII-compatible codec. */ void QVersitDocumentWriter::setAsciiCodec() { mCodecIsAscii = true; } /*! Sets the device to write to. */ void QVersitDocumentWriter::setDevice(QIODevice *device) { mDevice = device; } /*! * Encodes the \a document and writes it to the device. A "VERSION:" line is added iff \a * encodeVersion is true. */ bool QVersitDocumentWriter::encodeVersitDocument(const QVersitDocument& document, bool encodeVersion) { mSuccessful = true; if (document.componentType().isEmpty()) { // for compatibility with code for Qt Mobility 1.0, which didn't have componentType writeString(QStringLiteral("BEGIN:VCARD")); } else { writeString(QStringLiteral("BEGIN:") + document.componentType()); } writeCrlf(); if (encodeVersion) { switch (mType) { case QVersitDocument::VCard21Type: writeString(QStringLiteral("VERSION:2.1")); writeCrlf(); break; case QVersitDocument::VCard30Type: writeString(QStringLiteral("VERSION:3.0")); writeCrlf(); break; case QVersitDocument::VCard40Type: writeString(QStringLiteral("VERSION:4.0")); writeCrlf(); break; case QVersitDocument::ICalendar20Type: writeString(QStringLiteral("VERSION:2.0")); writeCrlf(); break; default: ; // don't print version } } foreach (const QVersitProperty& property, document.properties()) { encodeVersitProperty(property); } foreach (const QVersitDocument& document, document.subDocuments()) { encodeVersitDocument(document, false); } if (document.componentType().isEmpty()) { writeString(QStringLiteral("END:VCARD")); } else { writeString(QStringLiteral("END:") + document.componentType()); } writeCrlf(); // This has been set by the methods called from this function return mSuccessful; } /*! * Encodes the groups and name in the \a property and writes it to the device */ void QVersitDocumentWriter::encodeGroupsAndName(const QVersitProperty& property) { QStringList groups = property.groups(); if (!groups.isEmpty()) { writeString(groups.join(QStringLiteral("."))); writeString(QStringLiteral(".")); } writeString(property.name()); } /*! Writes \a value to the device. This function tracks how many characters have been written to the line and folds (wraps) the line according to RFC2425. */ void QVersitDocumentWriter::writeBytes(const QByteArray &value) { int spaceRemaining = MAX_LINE_LENGTH - mCurrentLineLength; int charsWritten = 0; while (spaceRemaining < value.length() - charsWritten) { // Write the first "spaceRemaining" characters if (mDevice->write(value.constData() + charsWritten, spaceRemaining) < 0 || mDevice->write("\r\n ") < 0) mSuccessful = false; charsWritten += spaceRemaining; spaceRemaining = MAX_LINE_LENGTH - 1; // minus 1 for the space at the front. mCurrentLineLength = 1; } if (mDevice->write(value.constData() + charsWritten) < 0) mSuccessful = false; mCurrentLineLength += value.length() - charsWritten; } /*! Writes \a value to the device. This function tracks how many characters have been written to the line and folds (wraps) the line according to RFC2425. */ void QVersitDocumentWriter::writeString(const QString &value) { int spaceRemaining = MAX_LINE_LENGTH - mCurrentLineLength; int charsWritten = 0; QString crlfSpace(QStringLiteral("\r\n ")); while (spaceRemaining < value.length() - charsWritten) { // Write the first "spaceRemaining" characters QStringRef line(&value, charsWritten, spaceRemaining); charsWritten += spaceRemaining; if (mDevice->write(mEncoder->fromUnicode(line.constData(), line.length())) < 0 || mDevice->write(mEncoder->fromUnicode(crlfSpace)) < 0) mSuccessful = false; spaceRemaining = MAX_LINE_LENGTH - 1; // minus 1 for the space at the front. mCurrentLineLength = 1; } if (mDevice->write(mEncoder->fromUnicode(value.mid(charsWritten))) < 0) mSuccessful = false; mCurrentLineLength += value.length() - charsWritten; } /*! Writes \a value to the device. This function tracks how many characters have been written to the line and wraps the line according to RFC2045 (a Quoted-Printable soft line break is an EQUALS-CR-LF sequence) */ void QVersitDocumentWriter::writeStringQp(const QString &value) { int spaceRemaining = MAX_LINE_LENGTH - mCurrentLineLength - 1; // minus 1 for the equals required at the end int charsWritten = 0; QString softBreak(QStringLiteral("=\r\n")); while (spaceRemaining < value.length() - charsWritten) { // Write the first "spaceRemaining" characters if (value[charsWritten + spaceRemaining - 2] == QLatin1Char('=')) { spaceRemaining -= 2; } else if (value[charsWritten + spaceRemaining - 1] == QLatin1Char('=')) { spaceRemaining -= 1; } QStringRef line(&value, charsWritten, spaceRemaining); charsWritten += spaceRemaining; if (mDevice->write(mEncoder->fromUnicode(line.constData(), line.length())) < 0 || mDevice->write(mEncoder->fromUnicode(softBreak)) < 0) mSuccessful = false; spaceRemaining = MAX_LINE_LENGTH - 1; // minus 1 for the equals required at the end mCurrentLineLength = 0; } if (mDevice->write(mEncoder->fromUnicode(value.mid(charsWritten))) < 0) mSuccessful = false; mCurrentLineLength += value.length() - charsWritten; } /*! Writes a CRLF to the device. By using this function, rather than writeString("\\r\\n"), you will allow the writer to know where a line starts, for folding purposes. */ void QVersitDocumentWriter::writeCrlf() { writeString(QStringLiteral("\r\n")); mCurrentLineLength = 0; } QT_END_NAMESPACE_VERSIT src/versit/qversitdocumentwriter_p.h000066400000000000000000000070341233466112000203440ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITDOCUMENTWRITER_H #define QVERSITDOCUMENTWRITER_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include QT_FORWARD_DECLARE_CLASS(QByteArray) QT_FORWARD_DECLARE_CLASS(QIODevice) QT_FORWARD_DECLARE_CLASS(QTextCodec) QT_FORWARD_DECLARE_CLASS(QTextEncoder) QT_BEGIN_NAMESPACE_VERSIT class QVersitProperty; class Q_VERSIT_EXPORT QVersitDocumentWriter { public: QVersitDocumentWriter(QVersitDocument::VersitType type); virtual ~QVersitDocumentWriter(); void setCodec(QTextCodec* codec); void setAsciiCodec(); void setDevice(QIODevice* device); virtual void encodeVersitProperty(const QVersitProperty& property) = 0; virtual void encodeParameters(const QMultiHash& parameters) = 0; bool encodeVersitDocument(const QVersitDocument& document, bool encodeVersion = true); void encodeGroupsAndName(const QVersitProperty& property); void writeBytes(const QByteArray& value); void writeString(const QString& value); void writeStringQp(const QString& value); void writeCrlf(); protected: QVersitDocument::VersitType mType; QIODevice* mDevice; QTextCodec* mCodec; bool mCodecIsAscii; bool mCodecIsAsciiCompatible; QTextEncoder* mEncoder; bool mSuccessful; int mCurrentLineLength; }; QT_END_NAMESPACE_VERSIT #endif // QVERSITDOCUMENTWRITER_H src/versit/qversitglobal.h000066400000000000000000000054441233466112000162150ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPIM module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITGLOBAL_H #define QVERSITGLOBAL_H #include #if defined(QT_NAMESPACE) # define QTVERSIT_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::QtVersit::name # define QT_BEGIN_NAMESPACE_VERSIT namespace QT_NAMESPACE { namespace QtVersit { # define QT_END_NAMESPACE_VERSIT } } # define QTVERSIT_USE_NAMESPACE using namespace QT_NAMESPACE; using namespace QtVersit; #else # define QTVERSIT_PREPEND_NAMESPACE(name) ::QtVersit::name # define QT_BEGIN_NAMESPACE_VERSIT namespace QtVersit { # define QT_END_NAMESPACE_VERSIT } # define QTVERSIT_USE_NAMESPACE using namespace QtVersit; #endif #ifndef QT_STATIC # if defined(QT_BUILD_VERSIT_LIB) # define Q_VERSIT_EXPORT Q_DECL_EXPORT # else # define Q_VERSIT_EXPORT Q_DECL_IMPORT # endif #else # define Q_VERSIT_EXPORT #endif QT_BEGIN_NAMESPACE_VERSIT QT_END_NAMESPACE_VERSIT #endif // QVERSITGLOBAL_H src/versit/qversitpluginsearch_p.h000066400000000000000000000122151233466112000177520ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITPLUGINSEARCH_H #define QVERSITPLUGINSEARCH_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #if !defined QT_NO_DEBUG #include #endif #include #include #include QT_BEGIN_NAMESPACE_VERSIT #define CHECKDIR(dir) (dir).exists() inline QStringList mobilityPlugins(const QString& plugintype) { #if !defined QT_NO_DEBUG const bool showDebug = qgetenv("QT_DEBUG_PLUGINS").toInt() > 0; #endif QStringList paths = QCoreApplication::libraryPaths(); #ifdef QTM_PLUGIN_PATH paths << QLatin1String(QTM_PLUGIN_PATH); #endif #if !defined QT_NO_DEBUG if (showDebug) qDebug() << "Plugin paths:" << paths; #endif // Temp variable to avoid multiple identical paths // (we don't convert the list to set first, because that loses the order) QSet processed; /* The list of discovered plugins */ QStringList plugins; /* Enumerate our plugin paths */ for (int i=0; i < paths.count(); i++) { if (processed.contains(paths.at(i))) continue; processed.insert(paths.at(i)); QDir pluginsDir(paths.at(i)); if (!CHECKDIR(pluginsDir)) continue; #if defined(Q_OS_WIN) if (pluginsDir.dirName().toLower() == QLatin1String("debug") || pluginsDir.dirName().toLower() == QLatin1String("release")) pluginsDir.cdUp(); #elif defined(Q_OS_MAC) if (pluginsDir.dirName() == QLatin1String("MacOS")) { pluginsDir.cdUp(); pluginsDir.cdUp(); pluginsDir.cdUp(); } #endif QString subdir(QStringLiteral("plugins/")); subdir += plugintype; if (pluginsDir.path().endsWith(QStringLiteral("/plugins")) || pluginsDir.path().endsWith(QStringLiteral("/plugins/"))) subdir = plugintype; if (CHECKDIR(QDir(pluginsDir.filePath(subdir)))) { pluginsDir.cd(subdir); QStringList files = pluginsDir.entryList(QDir::Files); #if !defined QT_NO_DEBUG if (showDebug) qDebug() << "Looking for " << plugintype << " plugins in" << pluginsDir.path() << files; #endif for (int j=0; j < files.count(); j++) { plugins << pluginsDir.absoluteFilePath(files.at(j)); } } } /* Add application path + plugintype */ QDir appldir(QCoreApplication::applicationDirPath()); if(appldir.cd(plugintype)){ if (!processed.contains(appldir.absolutePath())){ processed.insert(appldir.absolutePath()); QStringList files = appldir.entryList(QDir::Files); #if !defined QT_NO_DEBUG if (showDebug) qDebug() << "Looking for " << plugintype << " plugins in" << appldir.path() << files; #endif for (int j=0; j < files.count(); j++) { plugins << appldir.absoluteFilePath(files.at(j)); } } } return plugins; } QT_END_NAMESPACE_VERSIT #endif // QVERSITPLUGINSEARCH_H src/versit/qversitproperty.cpp000066400000000000000000000302651233466112000171730ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitproperty.h" #include "qversitproperty_p.h" #ifndef QT_NO_DEBUG_STREAM #include #endif #include #include "qversitdocument.h" QT_BEGIN_NAMESPACE_VERSIT /*! \class QVersitProperty \brief The QVersitProperty class stores the name, value, groups and parameters of a Versit property. \ingroup versit \inmodule QtVersit A vCard is represented in abstract form as a QVersitDocument that consists of a number of properties such as a name (N), a telephone number (TEL) and an email address (EMAIL), for instance. Each of these properties is stored as an instance of a QVersitProperty in a QVersitDocument. A QVersitProperty consists of a list of groups, a name, a list of parameters (key/value pairs), and a value. The value of a QVersitProperty is stored as a QVariant and should always be one of four types: QString for textual values, QByteArray for binary data (eg. images), QStringList for structured textual data, or QVersitDocument for nested documents. The \l QVersitReader will parse Versit properties and assign the correct type of object to the property value. The \l QVersitWriter will serialize objects of these types correctly into the (text-based) Versit format. For example, a property might appear in a vCard as: \code ADR;TYPE=home,postal:;;123 Main Street;Any Town;CA;91921-1234 \endcode This would be stored as a QVersitProperty with the name \tt{ADR} and two parameters (both named \tt{TYPE} and with values \tt{home} and \tt{postal} respectively. The value of the QVersitProperty is a QStringList with six strings, and the valueType is CompoundType. QVersitProperty supports implicit sharing. The property name and parameter names of a QVersitProperty are converted to upper-case when they are stored to a QVersitProperty. \sa QVersitDocument */ /*! \enum QVersitProperty::ValueType Describes the type of data held in the property's value. The vCard and iCalendar specifications allows a property value to hold a string, binary data, or a nested document. String values can either be unstructured or structured. Structured strings can be either of compound type or list type. A compound value is one that is delimited by semicolons, allows empty components, and has a property-specific cardinality and ordering. A list value is one that is delimited by commas, does not have empty components, and has no restrictions on cardinality or ordering. \value PlainType The property value holds an unstructured string and can be retrieved with QVersitProperty::value() \value CompoundType The property value holds a compound string and can be retrieved with QVersitProperty::value() \value ListType The property value holds a list of strings and can be retrieved with QVersitProperty::value() \value BinaryType The property value holds a binary value and can be retrieved with QVersitProperty::value() \value VersitDocumentType The property value holds a nested Versit document and can be retrieved \value PreformattedType The property value holds a string that represents exactly the text for the property in the vCard file, bar line-wrapping. That is, if the property were to be written to file it should be written as-is, with no backslash escaping. */ /*! Constructs a new empty property */ QVersitProperty::QVersitProperty() : d(new QVersitPropertyPrivate()) { } /*! Constructs a property that is a copy of \a other */ QVersitProperty::QVersitProperty(const QVersitProperty& other) : d(other.d) { } /*! Frees the memory used by the property */ QVersitProperty::~QVersitProperty() { } /*! Assigns this property to \a other */ QVersitProperty& QVersitProperty::operator=(const QVersitProperty& other) { if (this != &other) d = other.d; return *this; } /*! Returns true if this is equal to \a other; false otherwise. */ bool QVersitProperty::operator==(const QVersitProperty& other) const { bool equal = d->mGroups == other.d->mGroups && d->mName == other.d->mName && d->mParameters == other.d->mParameters && d->mValueType == other.d->mValueType; if (!equal) return false; // QVariant doesn't support == on QVersitDocuments - do it manually if (d->mValue.userType() == qMetaTypeId()) return other.d->mValue.userType() == qMetaTypeId() && d->mValue.value() == other.d->mValue.value(); else return d->mValue == other.d->mValue; } /*! Returns true if this is not equal to \a other; false otherwise. */ bool QVersitProperty::operator!=(const QVersitProperty& other) const { return !(*this == other); } /*! Returns the hash value for \a key. */ uint qHash(const QVersitProperty &key) { uint hash = QT_PREPEND_NAMESPACE(qHash)(key.name()) + QT_PREPEND_NAMESPACE(qHash)(key.value()); foreach (const QString& group, key.groups()) { hash += QT_PREPEND_NAMESPACE(qHash)(group); } QHash::const_iterator it = key.parameters().constBegin(); QHash::const_iterator end = key.parameters().constEnd(); while (it != end) { hash += QT_PREPEND_NAMESPACE(qHash)(it.key()) + QT_PREPEND_NAMESPACE(qHash)(it.value()); ++it; } return hash; } #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QVersitProperty& property) { QStringList groups = property.groups(); QString name = property.name(); QMultiHash parameters = property.parameters(); dbg.nospace() << "QVersitProperty("; foreach (const QString& group, groups) { dbg.nospace() << group << '.'; } dbg.nospace() << name; QHash::const_iterator it; for (it = parameters.constBegin(); it != parameters.constEnd(); ++it) { dbg.nospace() << ';' << it.key() << '=' << it.value(); } if (property.valueType() == QVersitProperty::VersitDocumentType) dbg.nospace() << ':' << property.value(); else dbg.nospace() << ':' << property.variantValue(); dbg.nospace() << ')'; return dbg.maybeSpace(); } #endif /*! * Sets the groups in the property to the given list of \a groups. */ void QVersitProperty::setGroups(const QStringList& groups) { d->mGroups.clear(); foreach (const QString& group, groups) { d->mGroups.append(group); } } /*! * Gets the groups of the property. */ QStringList QVersitProperty::groups() const { return d->mGroups; } /*! * Sets the \a name of the property. * The \a name is converted to upper-case. */ void QVersitProperty::setName(const QString& name) { d->mName = name.toUpper(); } /*! * Gets the name of the property in upper-case. */ QString QVersitProperty::name() const { return d->mName; } /*! * Replaces all the parameters with \a parameters. * The names of the parameters are converted to upper-case. */ void QVersitProperty::setParameters(const QMultiHash& parameters) { d->mParameters.clear(); // Traverse parameters in the reverse order, because they are added to // d->mParameters using insert in QVersitProperty::addParameter QList keys = parameters.uniqueKeys(); for (int i=keys.count()-1; i >= 0; i--) { QString key = keys.at(i); QList values = parameters.values(key); for (int j=values.count()-1; j >= 0; j--) { // Convert all the parameter names and values to upper case insertParameter(key,values.at(j)); } } } /*! * Adds a new parameter with \a name and \a value. * The parameter name is converted to upper-case. */ void QVersitProperty::insertParameter(const QString& name, const QString& value) { d->mParameters.insert(name.toUpper(), value); } /*! * Removes a parameter with \a name and \a value. * * \sa removeParameters() */ void QVersitProperty::removeParameter(const QString& name, const QString& value) { d->mParameters.remove(name.toUpper(), value); } /*! * Removes all parameters with the given \a name. * * \sa removeParameter() */ void QVersitProperty::removeParameters(const QString& name) { d->mParameters.remove(name.toUpper()); } /*! * Return a copy of the contained list of parameters. * Note that actual the parameters cannot be modified using the copy. */ QMultiHash QVersitProperty::parameters() const { return d->mParameters; } /*! * Sets the property value to \a value. */ void QVersitProperty::setValue(const QVariant& value) { d->mValue = value; } /*! * Returns the value of the property. */ QVariant QVersitProperty::variantValue() const { return d->mValue; } /*! * \fn T QVersitProperty::value() const * \overload * Returns the value of the property as a \tt T. */ /*! * Returns the value of the property as a string if possible, otherwise return an empty string. * If the property is stored as a QByteArray, it is decoded using the charset specified in the * property's parameters. * \sa QVariant::toString() */ QString QVersitProperty::value() const { if (d->mValue.type() == QVariant::ByteArray) { if (d->mParameters.contains(QStringLiteral("CHARSET"))) { QTextCodec* codec = QTextCodec::codecForName( d->mParameters.value(QStringLiteral("CHARSET")).toLatin1()); if (codec != NULL) { return codec->toUnicode(d->mValue.toByteArray()); } } return QString(); } else { return d->mValue.toString(); } } /*! * Sets the type of value held in the property to \a type. */ void QVersitProperty::setValueType(QVersitProperty::ValueType type) { d->mValueType = type; } /*! * Returns the type of value held in the property. */ QVersitProperty::ValueType QVersitProperty::valueType() const { return d->mValueType; } /*! * Returns true if the property is empty. */ bool QVersitProperty::isEmpty() const { return d->mGroups.isEmpty() && d->mName.isEmpty() && d->mParameters.isEmpty() && !d->mValue.isValid(); } /*! * Clears the contents of this property. */ void QVersitProperty::clear() { d->mGroups.clear(); d->mName.clear(); d->mValue.clear(); d->mParameters.clear(); d->mValueType = QVersitProperty::PlainType; } QT_END_NAMESPACE_VERSIT src/versit/qversitproperty.h000066400000000000000000000073131233466112000166360ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITPROPERTY_H #define QVERSITPROPERTY_H #include #include #include #include #include QT_BEGIN_NAMESPACE_VERSIT class QVersitPropertyPrivate; class Q_VERSIT_EXPORT QVersitProperty { public: enum ValueType { PlainType, CompoundType, ListType, BinaryType, VersitDocumentType, PreformattedType }; QVersitProperty(); QVersitProperty(const QVersitProperty& other); ~QVersitProperty(); QVersitProperty& operator=(const QVersitProperty& other); bool operator==(const QVersitProperty& other) const; bool operator!=(const QVersitProperty& other) const; void setGroups(const QStringList& groups); QStringList groups() const; void setName(const QString& name); QString name() const; void insertParameter(const QString& name, const QString& value); void removeParameter(const QString& name, const QString& value); void removeParameters(const QString& name); void setParameters(const QMultiHash& parameters); QMultiHash parameters() const; void setValue(const QVariant& value); QVariant variantValue() const; template T value() const { return variantValue().value(); } QString value() const; void setValueType(ValueType type); ValueType valueType() const; bool isEmpty() const; void clear(); private: QSharedDataPointer d; }; Q_VERSIT_EXPORT uint qHash(const QVersitProperty& key); #ifndef QT_NO_DEBUG_STREAM Q_VERSIT_EXPORT QDebug operator<<(QDebug dbg, const QVersitProperty& property); #endif QT_END_NAMESPACE_VERSIT #endif // QVERSITPROPERTY_H src/versit/qversitproperty_p.h000066400000000000000000000061021233466112000171500ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITPROPERTY_P_H #define QVERSITPROPERTY_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include QT_BEGIN_NAMESPACE_VERSIT class QVersitPropertyPrivate : public QSharedData { public: QVersitPropertyPrivate() : QSharedData(), mValueType(QVersitProperty::PlainType) { } QVersitPropertyPrivate(const QVersitPropertyPrivate& other) : QSharedData(other), mGroups(other.mGroups), mName(other.mName), mParameters(other.mParameters), mValue(other.mValue), mValueType(other.mValueType) { } ~QVersitPropertyPrivate() {} QStringList mGroups; QString mName; QMultiHash mParameters; QVariant mValue; QVersitProperty::ValueType mValueType; }; QT_END_NAMESPACE_VERSIT #endif // QVERSITPROPERTY_P_H src/versit/qversitreader.cpp000066400000000000000000000216611233466112000165510ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitreader.h" #include "qversitreader_p.h" #include #include QT_BEGIN_NAMESPACE_VERSIT /*! \class QVersitReader \brief The QVersitReader class provides an interface for reading versit documents such as vCards from a Stream. \ingroup versit \inmodule QtVersit QVersitReader concatenation of Versit documents such as vCards from a text stream and returns a list of QVersitDocument instances. QVersitReader supports reading from an abstract I/O device which can be, for example, a file or a memory buffer. The reading can be done asynchronously, and the waitForFinished() function can be used to make a blocking read. \sa QVersitDocument */ /*! * \enum QVersitReader::Error * This enum specifies an error that occurred during the most recent operation: * \value NoError The most recent operation was successful * \value UnspecifiedError The most recent operation failed for an undocumented reason * \value IOError The most recent operation failed because of a problem with the device * \value OutOfMemoryError The most recent operation failed due to running out of memory * \value NotReadyError The most recent operation failed because there is an operation in progress * \value ParseError The most recent operation failed because the input was malformed */ /*! * \enum QVersitReader::State * Enumerates the various states that a reader may be in at any given time * \value InactiveState Read operation not yet started * \value ActiveState Read operation started, not yet finished * \value CanceledState Read operation is finished due to cancellation * \value FinishedState Read operation successfully completed */ /*! * \fn QVersitReader::stateChanged(QVersitReader::State state) * The signal is emitted by the reader when its state has changed (eg. when it has finished * reading from the device). * \a state is the new state of the reader. */ /*! * \fn QVersitReader::resultsAvailable() * The signal is emitted by the reader as it reads from the device when it has made more Versit * documents available. */ /*! Constructs a new reader. */ QVersitReader::QVersitReader() : d(new QVersitReaderPrivate) { d->init(this); } /*! Constructs a new reader that reads from \a inputDevice. */ QVersitReader::QVersitReader(QIODevice *inputDevice) : d(new QVersitReaderPrivate) { d->init(this); d->mIoDevice = inputDevice; } /*! Constructs a new reader that reads from \a inputData. */ QVersitReader::QVersitReader(const QByteArray &inputData) : d(new QVersitReaderPrivate) { d->init(this); d->mInputBytes.reset(new QBuffer); d->mInputBytes->setData(inputData); d->mInputBytes->open(QIODevice::ReadOnly); d->mIoDevice = d->mInputBytes.data(); } /*! * Frees the memory used by the reader. * Waits until a pending asynchronous reading has been completed. */ QVersitReader::~QVersitReader() { d->wait(); delete d; } /*! * Sets the device used for reading the input to be the given \a device. * Does not take ownership of the device. This overrides any byte array input source set with * setData(). * * The caller must ensure that \a device remains valid for the lifetime of * this QVersitReader object. */ void QVersitReader::setDevice(QIODevice* device) { d->mInputBytes.reset(0); d->mIoDevice = device; } /*! * Returns the device used for reading input, or 0 if no device has been set (or if the input source * was set with setData(). */ QIODevice* QVersitReader::device() const { if (d->mInputBytes.isNull()) return d->mIoDevice; else return 0; } /*! * Sets the data to read from to the byte array input source, \a inputData. * This overrides any device set with setDevice(). */ void QVersitReader::setData(const QByteArray &inputData) { if (d->mInputBytes.isNull()) { d->mInputBytes.reset(new QBuffer); } else if (d->mInputBytes->isOpen()) { d->mInputBytes->close(); } d->mInputBytes->setData(inputData); d->mInputBytes->open(QIODevice::ReadOnly); d->mIoDevice = d->mInputBytes.data(); } /*! * Sets \a codec as the codec for the reader to use when parsing the input stream to. * This codec is not used for values where the CHARSET Versit parameter occurs. * If the codec is null, this denotes that the reader will try to detect the codec * from the input. The codec autodetection algorithm can detect UTF-8, UTF-16 or * UTF-32. If the input is in some 8-bit codec, it will fall back to using the system * locale's codec. */ void QVersitReader::setDefaultCodec(QTextCodec *codec) { d->mDefaultCodec = codec; } /*! * Returns the codec the reader uses when parsing the input stream. If the codec is * null, this denotes that the reader will try to detect the codec from the input. */ QTextCodec* QVersitReader::defaultCodec() const { return d->mDefaultCodec; } /*! * Returns the state of the reader. */ QVersitReader::State QVersitReader::state() const { return d->state(); } /*! * Returns the error encountered by the last operation. */ QVersitReader::Error QVersitReader::error() const { return d->error(); } /*! * Starts reading the input asynchronously. * Returns false if the input device has not been set or opened or * if there is another asynchronous read operation already pending. * Signal \l stateChanged() is emitted with parameter FinishedState * when the reading has finished. * * The device must be already open. The client is responsible for * closing it when finished. */ bool QVersitReader::startReading() { if (d->state() == ActiveState || d->isRunning()) { d->setError(QVersitReader::NotReadyError); return false; } else if (!d->mIoDevice || !d->mIoDevice->isReadable()) { d->setError(QVersitReader::IOError); return false; } else { d->setState(ActiveState); d->setError(NoError); d->setCanceling(false); d->start(); return true; } } /*! * Attempts to asynchronously cancel the read request. */ void QVersitReader::cancel() { d->setCanceling(true); } /*! * If the state is ActiveState, blocks until the reader has finished reading or \a msec milliseconds * has elapsed, returning true if it successfully finishes or is cancelled by the user. * If \a msec is negative or zero, the function blocks until the writer has finished, regardless of * how long it takes. * If the state is FinishedState, returns true immediately. * Otherwise, returns false immediately. */ bool QVersitReader::waitForFinished(int msec) { State state = d->state(); if (state != InactiveState) { if (msec <= 0) return d->wait(ULONG_MAX); else return d->wait(msec); } else { return false; } } /*! * Returns the reading result. Even if there was an error or reading has not completed, a partial * list of results may be returned. */ QList QVersitReader::results() const { QMutexLocker locker(&d->mMutex); return d->mVersitDocuments; } #include "moc_qversitreader.cpp" QT_END_NAMESPACE_VERSIT src/versit/qversitreader.h000066400000000000000000000067271233466112000162240ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITREADER_H #define QVERSITREADER_H #include #include #include QT_FORWARD_DECLARE_CLASS(QByteArray) QT_FORWARD_DECLARE_CLASS(QIODevice) QT_FORWARD_DECLARE_CLASS(QTextCodec) QT_BEGIN_NAMESPACE_VERSIT class QVersitReaderPrivate; // reads a QVersitDocument from i/o device class Q_VERSIT_EXPORT QVersitReader : public QObject { Q_OBJECT public: enum Error { NoError = 0, UnspecifiedError, IOError, OutOfMemoryError, NotReadyError, ParseError }; enum State { InactiveState = 0, ActiveState, CanceledState, FinishedState }; QVersitReader(); QVersitReader(QIODevice* inputDevice); QVersitReader(const QByteArray& inputData); ~QVersitReader(); // input: void setDevice(QIODevice* inputDevice); QIODevice* device() const; void setData(const QByteArray& inputData); void setDefaultCodec(QTextCodec* codec); QTextCodec* defaultCodec() const; // output: QList results() const; State state() const; Error error() const; // reading: public Q_SLOTS: bool startReading(); void cancel(); public: Q_INVOKABLE bool waitForFinished(int msec = -1); Q_SIGNALS: void stateChanged(QVersitReader::State state); void resultsAvailable(); private: // data QVersitReaderPrivate* d; }; QT_END_NAMESPACE_VERSIT Q_DECLARE_METATYPE(QTVERSIT_PREPEND_NAMESPACE(QVersitReader::State)) #endif // QVERSITREADER_H src/versit/qversitreader_p.cpp000066400000000000000000001452601233466112000170720ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitreader_p.h" #include #include #include #include "qversitutils_p.h" QT_BEGIN_NAMESPACE_VERSIT // Some big enough value for nested versit documents to prevent infinite recursion #define MAX_VERSIT_DOCUMENT_NESTING_DEPTH 20 QHash, QVersitProperty::ValueType>* QVersitReaderPrivate::mValueTypeMap = 0; /*! \class LineReader \brief The LineReader class is a wrapper around a QIODevice that allows line-by-line reading. \internal This class keeps an internal buffer which it uses to temporarily store data which it has read from the device but not returned to the user. The isCodecCertain constructor parameter/getter can be used by the client to indicate whether the codec supplied is known for sure, or if it was a guess. */ /*! Constructs a LineReader that reads from the given \a device using the given \a codec. If the \a codec is null, it is guessed at by sniffing the first few bytes of the input. */ LineReader::LineReader(QIODevice* device, QTextCodec *codec) : mDevice(device), mCodec(codec), mIsCodecUtf8Compatible(false), mChunkSize(10000), // Read 10kB at a time mOdometer(0), mSearchFrom(0) { if (!mCodec) { static QTextCodec* utf16be = QTextCodec::codecForName("UTF-16BE"); static QTextCodec* utf16le = QTextCodec::codecForName("UTF-16LE"); static QTextCodec* utf32be = QTextCodec::codecForName("UTF-32BE"); static QTextCodec* utf32le = QTextCodec::codecForName("UTF-32LE"); static const QByteArray beginUtf16be(VersitUtils::encode("BEGIN:", utf16be)); static const QByteArray beginUtf16le(VersitUtils::encode("BEGIN:", utf16le)); static const QByteArray beginUtf32be(VersitUtils::encode("BEGIN:", utf32be)); static const QByteArray beginUtf32le(VersitUtils::encode("BEGIN:", utf32le)); // Do some basic charset detection using the byte-order-mark (BOM) // We need 4 bytes to do BOM sniffing for UTF-32, UTF-16 and UTF-8 QByteArray firstSixBytes = mDevice->read(6); mCodec = QTextCodec::codecForUtfText(firstSixBytes, NULL); if (mCodec) { mIsCodecCertain = true; } else { if (beginUtf16be.startsWith(firstSixBytes)) { mCodec = utf16be; mIsCodecCertain = true; } else if (beginUtf16le.startsWith(firstSixBytes)) { mCodec = utf16le; mIsCodecCertain = true; } else if (beginUtf32be.startsWith(firstSixBytes)) { mCodec = utf32be; mIsCodecCertain = true; } else if (beginUtf32le.startsWith(firstSixBytes)) { mCodec = utf32le; mIsCodecCertain = true; } else { mCodec = QTextCodec::codecForLocale(); mIsCodecCertain = false; mIsCodecUtf8Compatible = true; } } mBuffer = LByteArray(firstSixBytes, 0, 0); } else { mIsCodecCertain = true; } mCrlfList = *VersitUtils::newlineList(mCodec); } /*! Constructs a LineReader that reads from the given \a device using the given \a codec. \a chunkSize is the number of bytes to read at a time (it is useful for testing but this constructor shouldn't otherwise be used). */ LineReader::LineReader(QIODevice* device, QTextCodec *codec, int chunkSize) : mDevice(device), mCodec(codec), mIsCodecCertain(true), mChunkSize(chunkSize), mCrlfList(*VersitUtils::newlineList(mCodec)), mOdometer(0), mSearchFrom(0) { Q_ASSERT(mCodec != NULL); } /*! Attempts to read a line and returns an LByteArray containing the line. This wraps around readOneLine and provides a hack to do additional unwrapping for a malformed vCard where a space is not added to the start of the line continuation. Some malformed vCards we get look like this: (Case 1) ORG:A B C (CRLF-SPACE wrapping is employed for the first time, then the space is subsequently omitted). But a valid vCard can be weirdly wrapped without the CRLF-SPACE, if it's quoted-printable and ends in an equals, eg. (Case 2) ORG;ENCODING=QUOTED-PRINTABLE:A= B= C Unwrap in Case 1 but not in Case 2 - leave that for the QP-decoder in QVR::unencode */ LByteArray LineReader::readLine() { QByteArray colon(VersitUtils::encode(':', mCodec)); QByteArray equals(VersitUtils::encode('=', mCodec)); if (!mPushedLines.isEmpty()) { LByteArray retval(mPushedLines.pop()); return retval; } readOneLine(&mBuffer); // Hack: read the next line and see if it's a continuation of this line while (true) { int prevStart = mBuffer.mStart; int prevEnd = mBuffer.mEnd; // readOneLine only appends to mBuffer so these saved offsets should remain valid readOneLine(&mBuffer); // Get an LByteArray of the previous line. This should be fast because copying the // LByteArray copies the QByteArray, which is implicitly shared LByteArray prevLine(mBuffer.mData, prevStart, prevEnd); if (mBuffer.isEmpty() || mBuffer.contains(colon) || prevLine.endsWith(equals)) { // Normal, the next line is empty, or a new property, or it's been wrapped using // QUOTED-PRINTABLE. Rewind it back one line so it gets read next time round. mBuffer.setBounds(prevStart, prevEnd); break; } else { // Some silly vCard generator has probably wrapped a line without prepending a space // Join the previous line with this line by deleting the characters between prevEnd and // mStart (eg. any newline characters) int crlfLen = mBuffer.mStart-prevEnd; mBuffer.mData.remove(prevEnd, crlfLen); mBuffer.setBounds(prevStart, mBuffer.mEnd - crlfLen); } } mBuffer.dropOldData(); mOdometer += mBuffer.size(); return mBuffer; } /*! Attempts to read a line and updates \a cursor to contain the line. This performes basic line unwrapping as per the vCard specification (eg. if a line begins with a space, it is a continuation of the next line) */ void LineReader::readOneLine(LByteArray* cursor) { cursor->mStart = cursor->mEnd; mSearchFrom = cursor->mStart; // First, look for a newline in the already-existing buffer. If found, return the line. if (tryReadLine(cursor, false)) { return; } // Otherwise, keep reading more data until either a CRLF is found, or there's no more to read. while (!mDevice->atEnd()) { QByteArray temp = mDevice->read(mChunkSize); if (!temp.isEmpty()) { cursor->mData.append(temp); if (tryReadLine(cursor, false)) return; } else { mDevice->waitForReadyRead(500); } } // We've reached the end of the stream. Find a newline from the buffer (or return what's left). tryReadLine(cursor, true); return; } /*! Push a line onto the front of the line reader so it will be returned on the next call to readLine(). If multiple lines are pushed onto a line reader, they are read back in first-in-last-out order */ void LineReader::pushLine(const QByteArray& line) { mPushedLines.push(line); } /*! How many bytes have been returned in the LByteArray in the lifetime of the LineReader. */ int LineReader::odometer() const { return mOdometer; } /*! Returns true if there are no more lines left for readLine() to return. It is possible for atEnd() to return false and for there to be no more data left (eg. if there are trailing newlines at the end of the input. In this case, readLine() will return an empty line. */ bool LineReader::atEnd() const { return mPushedLines.isEmpty() && mDevice->atEnd() && mBuffer.mEnd == mBuffer.mData.size(); } /*! Returns the codec that the LineReader reads with. */ QTextCodec* LineReader::codec() const { return mCodec; } /*! Returns true if the line reader has been told for sure what the codec is, or if a byte-order-mark has told us for sure what the codec is. */ bool LineReader::isCodecCertain() const { return mIsCodecCertain; } /*! Valid if isCodecCertain(), false iff we've seen an invalid utf8 sequence */ bool LineReader::isCodecUtf8Compatible() const { return mIsCodecUtf8Compatible; } void LineReader::setCodecUtf8Incompatible() { mIsCodecUtf8Compatible = false; } /*! * Get the next line of input from the device to parse. Also performs unfolding by removing * sequences of newline-space from the retrieved line. Skips over any newlines at the start of the * input. * * \a cursor is filled with a the line * \a atEnd is true if we've reached the end of the stream * Returns true if a line was completely read (ie. a newline character was found) */ bool LineReader::tryReadLine(LByteArray *cursor, bool atEnd) { int crlfPos = -1; int doubleCrLfCheck = -1; QByteArray space(VersitUtils::encode(' ', mCodec)); QByteArray tab(VersitUtils::encode('\t', mCodec)); QByteArray equals(VersitUtils::encode('=', mCodec)); int spaceLength = space.length(); int equalsLength = equals.length(); forever { foreach(const QByteArrayMatcher& crlf, mCrlfList) { int crlfLength = crlf.pattern().length(); crlfPos = crlf.indexIn(cursor->mData, mSearchFrom); doubleCrLfCheck = crlf.indexIn(cursor->mData, mSearchFrom + crlfLength); if ((crlfPos == cursor->mStart) && (doubleCrLfCheck != crlfPos + crlfLength)) { // Single Newline at start of line. Ignore and Set mStart to directly after it. cursor->mStart += crlfLength; mSearchFrom = cursor->mStart; break; } else if ((crlfPos == cursor->mStart) && (doubleCrLfCheck == crlfPos + crlfLength)) { // Found '=CrLfCrLf' - We choose to see this as badly formed, // but clear end of the versit property. cursor->mData.remove(crlfPos, crlfLength); cursor->mEnd = crlfPos; if (QVersitReaderPrivate::containsAt(cursor->mData, equals, crlfPos - equalsLength) ) { cursor->mData.remove(crlfPos -1, 1); } return true; } else if (crlfPos > cursor->mStart) { // Found the first occurance of CRLF in the current buffer. if (QVersitReaderPrivate::containsAt(cursor->mData, space, crlfPos + crlfLength) || QVersitReaderPrivate::containsAt(cursor->mData, tab, crlfPos + crlfLength)) { // If it's followed by whitespace, collapse it. cursor->mData.remove(crlfPos, crlfLength + spaceLength); mSearchFrom = crlfPos; break; } else if (!atEnd && crlfPos + crlfLength + spaceLength >= cursor->mData.size()) { // If our CRLF is at the end of the current buffer but there's more to read, // it's possible that a space could be hiding on the next read from the device. // Just pretend we didn't see the CRLF and pick it up the next time round. mSearchFrom = crlfPos; return false; } else { // Found the CRLF. // Hack: if malformed vCard files (having no \r\n or \r\n\r\n ending) are // concatenated, we can get a malformed line in the document which looks like: // END:VCARDBEGIN:VCARD // In that situation, we should actually insert the \r\n sequence manually, // and return mEnd after the END:VCARD\r\n position. QByteArray cr(VersitUtils::encode('\r', mCodec)); QByteArray lf(VersitUtils::encode('\n', mCodec)); QByteArray ev(VersitUtils::encode(QByteArray("END:VCARD"), mCodec)); QByteArray evbv(VersitUtils::encode(QByteArray("END:VCARDBEGIN:VCARD"), mCodec)); QByteArray evcrlf(VersitUtils::encode(QByteArray("END:VCARD\r\n"), mCodec)); int crSz = cr.size(); int lfSz = lf.size(); int evSz = ev.size(); int evcrlfSz = evcrlf.size(); QByteArray possiblyMalformedLine = cursor->mData.mid(cursor->mStart, crlfPos-cursor->mStart); int pmlEnd = possiblyMalformedLine.size() - 1; while (true) { if (QVersitReaderPrivate::containsAt(possiblyMalformedLine, cr, pmlEnd - crSz)) { possiblyMalformedLine.chop(crSz); } else if (QVersitReaderPrivate::containsAt(possiblyMalformedLine, lf, pmlEnd - lfSz)) { possiblyMalformedLine.chop(lfSz); } else { break; } } if (possiblyMalformedLine == evbv) { // fix up the malformed line, return the end cursor after it. cursor->mData.replace(cursor->mStart, evSz, evcrlf); cursor->mEnd = cursor->mStart+evcrlfSz; return true; } else { // A well-formed line. cursor->mEnd = crlfPos; return true; } } } } if (crlfPos == -1) { // No CRLF found. cursor->mEnd = cursor->mData.size(); // Next time, continue searching from here. // The largest CRLF will have a size of 8 bytes, so we should backtrack 8 bytes mSearchFrom = qMax(mSearchFrom, cursor->mEnd-8); return false; } } } /*! Links the signals from this to the signals of \a reader. */ void QVersitReaderPrivate::init(QVersitReader* reader) { qRegisterMetaType("QVersitReader::State"); connect(this, SIGNAL(stateChanged(QVersitReader::State)), reader, SIGNAL(stateChanged(QVersitReader::State)),Qt::DirectConnection); connect(this, SIGNAL(resultsAvailable()), reader, SIGNAL(resultsAvailable()), Qt::DirectConnection); } /*! Construct a reader. */ QVersitReaderPrivate::QVersitReaderPrivate() : mIoDevice(0), mDocumentNestingLevel(0), mDefaultCodec(0), mState(QVersitReader::InactiveState), mError(QVersitReader::NoError), mIsCanceling(false) { } /*! Destroy a reader. */ QVersitReaderPrivate::~QVersitReaderPrivate() { } QHash, QVersitProperty::ValueType>* QVersitReaderPrivate::valueTypeMap() { if (mValueTypeMap == 0) { mValueTypeMap = new QHash, QVersitProperty::ValueType>(); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard21Type, QString::fromLatin1("AGENT")), QVersitProperty::VersitDocumentType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard30Type, QString::fromLatin1("AGENT")), QVersitProperty::VersitDocumentType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard40Type, QString::fromLatin1("AGENT")), QVersitProperty::VersitDocumentType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard21Type, QString::fromLatin1("N")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard30Type, QString::fromLatin1("N")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard40Type, QString::fromLatin1("N")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard21Type, QString::fromLatin1("ADR")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard30Type, QString::fromLatin1("ADR")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard40Type, QString::fromLatin1("ADR")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard21Type, QString::fromLatin1("GEO")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard30Type, QString::fromLatin1("GEO")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard40Type, QString::fromLatin1("GEO")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard21Type, QString::fromLatin1("ORG")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard30Type, QString::fromLatin1("ORG")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard40Type, QString::fromLatin1("ORG")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard21Type, QString::fromLatin1("NICKNAME")), QVersitProperty::ListType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard30Type, QString::fromLatin1("NICKNAME")), QVersitProperty::ListType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard40Type, QString::fromLatin1("NICKNAME")), QVersitProperty::ListType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard21Type, QString::fromLatin1("CATEGORIES")), QVersitProperty::ListType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard30Type, QString::fromLatin1("CATEGORIES")), QVersitProperty::ListType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard40Type, QString::fromLatin1("CATEGORIES")), QVersitProperty::ListType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard21Type, QString::fromLatin1("X-CHILDREN")), QVersitProperty::ListType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard30Type, QString::fromLatin1("X-CHILDREN")), QVersitProperty::ListType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard40Type, QString::fromLatin1("X-CHILDREN")), QVersitProperty::ListType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard21Type, QString::fromLatin1("X-NICKNAME")), QVersitProperty::ListType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard30Type, QString::fromLatin1("X-NICKNAME")), QVersitProperty::ListType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard40Type, QString::fromLatin1("X-NICKNAME")), QVersitProperty::ListType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard21Type, QString::fromLatin1("X-QTPROJECT-EXTENDED-DETAIL")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard30Type, QString::fromLatin1("X-QTPROJECT-EXTENDED-DETAIL")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard40Type, QString::fromLatin1("X-QTPROJECT-EXTENDED-DETAIL")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::ICalendar20Type, QString::fromLatin1("X-QTPROJECT-EXTENDED-DETAIL")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard21Type, QString::fromLatin1("X-QTPROJECT-FAVORITE")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard30Type, QString::fromLatin1("X-QTPROJECT-FAVORITE")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard40Type, QString::fromLatin1("X-QTPROJECT-FAVORITE")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard21Type, QString::fromLatin1("X-QTPROJECT-VERSION")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard30Type, QString::fromLatin1("X-QTPROJECT-VERSION")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard40Type, QString::fromLatin1("X-QTPROJECT-VERSION")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::ICalendar20Type, QString::fromLatin1("X-QTPROJECT-VERSION")), QVersitProperty::CompoundType); // Some MeeGo specific types, for EDS/SyncEvolution roundtripping until the API allows // better control over the type of custom properties. mValueTypeMap->insert(qMakePair(QVersitDocument::VCard21Type, QString::fromLatin1("X-EDS-QTCONTACTS")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard30Type, QString::fromLatin1("X-EDS-QTCONTACTS")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard40Type, QString::fromLatin1("X-EDS-QTCONTACTS")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard21Type, QString::fromLatin1("X-SYNCEVO-QTCONTACTS")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard30Type, QString::fromLatin1("X-SYNCEVO-QTCONTACTS")), QVersitProperty::CompoundType); mValueTypeMap->insert(qMakePair(QVersitDocument::VCard40Type, QString::fromLatin1("X-SYNCEVO-QTCONTACTS")), QVersitProperty::CompoundType); } return mValueTypeMap; } /*! * Inherited from QThread, called by QThread when the thread has been started. */ void QVersitReaderPrivate::run() { read(); } /*! * Does the actual reading and sets the error and state as appropriate. * If \a async, then stateChanged() signals are emitted as the reading happens. */ void QVersitReaderPrivate::read() { mMutex.lock(); mVersitDocuments.clear(); mMutex.unlock(); bool canceled = false; LineReader lineReader(mIoDevice, mDefaultCodec); while(!lineReader.atEnd()) { if (isCanceling()) { canceled = true; break; } QVersitDocument document; int oldPos = lineReader.odometer(); bool ok = parseVersitDocument(&lineReader, &document); if (ok) { if (document.isEmpty()) break; else { QMutexLocker locker(&mMutex); mVersitDocuments.append(document); emit resultsAvailable(); } } else { setError(QVersitReader::ParseError); if (lineReader.odometer() == oldPos) break; } }; if (canceled) setState(QVersitReader::CanceledState); else setState(QVersitReader::FinishedState); } void QVersitReaderPrivate::setState(QVersitReader::State state) { mMutex.lock(); mState = state; mMutex.unlock(); emit stateChanged(state); } QVersitReader::State QVersitReaderPrivate::state() const { QMutexLocker locker(&mMutex); return mState; } void QVersitReaderPrivate::setError(QVersitReader::Error error) { QMutexLocker locker(&mMutex); mError = error; } QVersitReader::Error QVersitReaderPrivate::error() const { QMutexLocker locker(&mMutex); return mError; } void QVersitReaderPrivate::setCanceling(bool canceling) { QMutexLocker locker(&mMutex); mIsCanceling = canceling; } bool QVersitReaderPrivate::isCanceling() { QMutexLocker locker(&mMutex); return mIsCanceling; } /*! * Parses a versit document. Returns true if the parsing was successful. */ bool QVersitReaderPrivate::parseVersitDocument(LineReader* lineReader, QVersitDocument* document) { if (mDocumentNestingLevel >= MAX_VERSIT_DOCUMENT_NESTING_DEPTH) return false; // To prevent infinite recursion // If we don't know what type it is, just assume it's a vCard 3.0 if (document->type() == QVersitDocument::InvalidType) document->setType(QVersitDocument::VCard30Type); QVersitProperty property; property = parseNextVersitProperty(document->type(), lineReader); QString propertyValue = property.value().trimmed().toUpper(); if (property.isEmpty()) { // A blank document (or end of file) was found. document->clear(); return true; } else if (property.name() == QStringLiteral("BEGIN")) { if (propertyValue == QStringLiteral("VCARD")) { document->setComponentType(propertyValue); } else if (propertyValue == QStringLiteral("VCALENDAR")) { document->setType(QVersitDocument::ICalendar20Type); document->setComponentType(propertyValue); } else { // Unknown document type document->clear(); return false; } } else { // Some property other than BEGIN was found. document->clear(); return false; } return parseVersitDocumentBody(lineReader, document); } /*! Parse the rest of a versit document after finding a BEGIN line */ bool QVersitReaderPrivate::parseVersitDocumentBody(LineReader* lineReader, QVersitDocument* document) { mDocumentNestingLevel++; bool parsingOk = true; while (true) { /* Grab it */ QVersitProperty property = parseNextVersitProperty(document->type(), lineReader); if (property.name() == QStringLiteral("BEGIN")) { // Nested Versit document QVersitDocument subDocument; subDocument.setType(document->type()); // the nested document inherits the parent's type subDocument.setComponentType(property.value().trimmed().toUpper()); if (!parseVersitDocumentBody(lineReader, &subDocument)) break; document->addSubDocument(subDocument); } else if (property.name() == QStringLiteral("VERSION")) { // A version property if (!setVersionFromProperty(document, property)) { parsingOk = false; break; } } else if (property.name() == QStringLiteral("END")) { // End of document break; } else if (property.name().isEmpty()) { // End of input or some other error parsingOk = false; break; } else { // A normal property - just add it. document->addProperty(property); } } if (!parsingOk) document->clear(); mDocumentNestingLevel--; return parsingOk; } /*! * Parses a versit document and returns whether parsing succeeded. */ QVersitProperty QVersitReaderPrivate::parseNextVersitProperty( QVersitDocument::VersitType versitType, LineReader* lineReader) { LByteArray line = lineReader->readLine(); if (line.isEmpty()) return QVersitProperty(); // Otherwise, do stuff. QPair groupsAndName = extractPropertyGroupsAndName(&line, lineReader->codec()); QVersitProperty property; property.setGroups(groupsAndName.first); property.setName(groupsAndName.second); // set the propertyValueType QPair key = qMakePair(versitType, property.name()); if (valueTypeMap()->contains(key)) property.setValueType(valueTypeMap()->value(key)); if (versitType == QVersitDocument::VCard21Type) parseVCard21Property(&line, &property, lineReader); else if (versitType == QVersitDocument::VCard30Type || versitType == QVersitDocument::VCard40Type || versitType == QVersitDocument::ICalendar20Type) parseVCard30Property(versitType, &line, &property, lineReader); return property; } /*! * Parses the property according to vCard 2.1 syntax. */ void QVersitReaderPrivate::parseVCard21Property(LByteArray* line, QVersitProperty* property, LineReader* lineReader) { property->setParameters(extractVCard21PropertyParams(line, lineReader->codec())); QByteArray value = line->toByteArray(); if (property->valueType() == QVersitProperty::VersitDocumentType) { // Hack to handle cases where start of document is on the same or next line as "AGENT:" if (value == "BEGIN:VCARD") { lineReader->pushLine(value); } else if (value.isEmpty()) { } else { property->clear(); return; } QVersitDocument subDocument(QVersitDocument::VCard21Type); if (!parseVersitDocument(lineReader, &subDocument)) { property->clear(); } else { property->setValue(QVariant::fromValue(subDocument)); } } else { bool isBinary = unencode(&value, property, lineReader); if (isBinary) { property->setValue(value); property->setValueType(QVersitProperty::BinaryType); } else { QTextCodec* ignored = 0; property->setValue(decodeCharset(value, property, lineReader, &ignored)); splitStructuredValue(property, false); } } } /*! * Parses the property according to vCard 3.0 syntax. This function is called for both vCard 3.0 * and iCalendar properties. */ void QVersitReaderPrivate::parseVCard30Property(QVersitDocument::VersitType versitType, LByteArray* line, QVersitProperty* property, LineReader* lineReader) { property->setParameters(extractVCard30PropertyParams(line, lineReader->codec())); QByteArray value = line->toByteArray(); if (property->valueType() == QVersitProperty::VersitDocumentType) { QTextCodec* codec; QString valueString(decodeCharset(value, property, lineReader, &codec)); removeBackSlashEscaping(&valueString); // Make a line reader from the value of the property. QByteArray subDocumentValue(codec->fromUnicode(valueString)); QBuffer subDocumentData(&subDocumentValue); subDocumentData.open(QIODevice::ReadOnly); subDocumentData.seek(0); LineReader subDocumentLineReader(&subDocumentData, codec); // Recursive call! QVersitDocument subDocument(versitType); if (!parseVersitDocument(&subDocumentLineReader, &subDocument)) { property->clear(); } else { property->setValue(QVariant::fromValue(subDocument)); } } else { bool isBinary = unencode(&value, property, lineReader); if (isBinary) { property->setValue(value); property->setValueType(QVersitProperty::BinaryType); } else { QTextCodec* ignored = 0; property->setValue(decodeCharset(value, property, lineReader, &ignored)); bool isList = splitStructuredValue(property, true); // Do backslash unescaping if (isList) { QStringList list = property->value(); for (int i = 0; i < list.length(); i++) { removeBackSlashEscaping(&list[i]); } property->setValue(list); } else { QString value = property->value(); removeBackSlashEscaping(&value); property->setValue(value); } } } } /*! * Sets version to \a document if \a property contains a supported version. */ bool QVersitReaderPrivate::setVersionFromProperty(QVersitDocument* document, const QVersitProperty& property) const { QString value = property.value().trimmed(); if (document->componentType() == QStringLiteral("VCARD") && value == QStringLiteral("2.1")) { document->setType(QVersitDocument::VCard21Type); } else if (document->componentType() == QStringLiteral("VCARD") && value == QStringLiteral("3.0")) { document->setType(QVersitDocument::VCard30Type); } else if (document->componentType() == QStringLiteral("VCARD") && value == QStringLiteral("4.0")) { document->setType(QVersitDocument::VCard40Type); } else if ((document->componentType() == QStringLiteral("VCALENDAR") || document->type() == QVersitDocument::ICalendar20Type) // covers VEVENT, etc. when nested inside a VCALENDAR && value == QStringLiteral("2.0")) { document->setType(QVersitDocument::ICalendar20Type); } else { return false; } return true; } /*! * On entry, \a value should be the byte array to unencode. It is modified to be the unencoded * version. Returns true if and only if the value was base-64 encoded. (This is used as a * heuristic later to decide whether to decode the byte array as text) * \a lineReader is supplied in case more lines need to be read (for quoted-printable). The * \a property is supplied so we know what kind of encoding was used. */ bool QVersitReaderPrivate::unencode(QByteArray* value, QVersitProperty* property, LineReader* lineReader) const { QStringList encodingParameters = property->parameters().values(QStringLiteral("ENCODING")); QStringList typeParameters = property->parameters().values(QStringLiteral("TYPE")); if (encodingParameters.contains(QStringLiteral("QUOTED-PRINTABLE"), Qt::CaseInsensitive)) { // At this point, we need to accumulate bytes until we hit a real line break (no = before // it) value already contains everything up to the character before the newline while (value->endsWith('=')) { value->chop(1); // Get rid of '=' // We add each line (minus the escaped = and newline chars) value->append(lineReader->readLine().toByteArray()); } decodeQuotedPrintable(value); // Remove the encoding parameter as the value is now decoded property->removeParameters(QStringLiteral("ENCODING")); return false; } else if (encodingParameters.contains(QStringLiteral("BASE64"), Qt::CaseInsensitive) || encodingParameters.contains(QStringLiteral("B"), Qt::CaseInsensitive) || typeParameters.contains(QStringLiteral("BASE64"), Qt::CaseInsensitive) || typeParameters.contains(QStringLiteral("B"), Qt::CaseInsensitive)) { *value = QByteArray::fromBase64(*value); // Remove the encoding parameter as the value is now decoded property->removeParameters(QStringLiteral("ENCODING")); return true; } return false; } /*! * Decodes \a value, after working out what charset it is in using the context of \a property and * returns it. The codec used to decode is returned in \a codec. If the CHARSET parameter was * specified, *charsetSpecified is set to true (else, false). */ QString QVersitReaderPrivate::decodeCharset(const QByteArray& value, QVersitProperty* property, LineReader* lineReader, QTextCodec** codec) const { static const QString charset(QStringLiteral("CHARSET")); *codec = NULL; if (property->parameters().contains(charset)) { QString charsetValue = *property->parameters().find(charset); property->removeParameters(charset); *codec = QTextCodec::codecForName(charsetValue.toLatin1()); } else if (!lineReader->isCodecCertain() && lineReader->isCodecUtf8Compatible()) { // Guess the codec because we don't know for sure what it is and it could possibly be // either UTF-8 or an 8-bit codec. if (VersitUtils::isValidUtf8(value)) { // Valid UTF-8 *codec = QTextCodec::codecForName("UTF-8"); } else { // Invalid UTF-8 - don't try to test future properties for UTF-8-compatibility lineReader->setCodecUtf8Incompatible(); } } if (*codec == NULL) *codec = lineReader->codec(); return (*codec)->toUnicode(value); } /*! * Decodes Quoted-Printable encoded (RFC 1521) characters in /a text. */ void QVersitReaderPrivate::decodeQuotedPrintable(QByteArray* text) const { for (int i=0; i < text->length(); i++) { char current = text->at(i); if (current == '=' && i+2 < text->length()) { char next = text->at(i+1); char nextAfterNext = text->at(i+2); if (((next >= 'a' && next <= 'f') || (next >= 'A' && next <= 'F') || (next >= '0' && next <= '9')) && ((nextAfterNext >= 'a' && nextAfterNext <= 'f') || (nextAfterNext >= 'A' && nextAfterNext <= 'F') || (nextAfterNext >= '0' && nextAfterNext <= '9'))) { bool ok; char decodedChar(text->mid(i+1, 2).toInt(&ok,16)); if (ok) { (*text)[i] = decodedChar; text->remove(i+1, 2); } } else if (next == '\r' && nextAfterNext == '\n') { // Newlines can still be found here if they are encoded in a non-default charset. text->remove(i, 3); } } } } /*! * Extracts the groups and the name of the property using \a codec to determine the delimiters * * On entry, \a line should contain a whole line * On exit, \a line will be updated to remove the groups and name */ QPairQVersitReaderPrivate::extractPropertyGroupsAndName( LByteArray* line, QTextCodec *codec) const { const QByteArray semicolon = VersitUtils::encode(';', codec); const QByteArray colon = VersitUtils::encode(':', codec); const QByteArray backslash = VersitUtils::encode('\\', codec); QPair groupsAndName; int length = 0; int separatorLength = semicolon.length(); for (int i = 0; i < line->size() - separatorLength + 1; i++) { if ((containsAt(*line, semicolon, i) && !containsAt(*line, backslash, i-separatorLength)) || containsAt(*line, colon, i)) { length = i; break; } } if (length > 0) { QString trimmedGroupsAndName = codec->toUnicode(line->left(length)).trimmed(); QStringList parts = trimmedGroupsAndName.split(QLatin1Char('.')); if (parts.count() > 1) { groupsAndName.second = parts.takeLast(); groupsAndName.first = parts; } else { groupsAndName.second = trimmedGroupsAndName; } line->chopLeft(length); } return groupsAndName; } /*! * Extracts the property parameters as a QMultiHash using \a codec to determine the delimiters. * The parameters without names are added as "TYPE" parameters. * * On entry \a line should contain the line sans the group and name * On exit, line will be updated to have the parameters removed. */ QMultiHash QVersitReaderPrivate::extractVCard21PropertyParams( LByteArray* line, QTextCodec *codec) const { QMultiHash result; QList paramList = extractParams(line, codec); while (!paramList.isEmpty()) { QByteArray param = paramList.takeLast(); QString name = paramName(param, codec); QString value = paramValue(param, codec); result.insert(name,value); } return result; } /*! * Extracts the property parameters as a QMultiHash using \a codec to determine the delimiters. * The parameters without names are added as "TYPE" parameters. * * On entry \a line should contain the line sans the group and name * On exit, line will be updated to have the parameters removed. */ QMultiHash QVersitReaderPrivate::extractVCard30PropertyParams( LByteArray* line, QTextCodec *codec) const { QMultiHash result; QList paramList = extractParams(line, codec); while (!paramList.isEmpty()) { QByteArray param = paramList.takeLast(); QString name(paramName(param, codec)); removeBackSlashEscaping(&name); QString values = paramValue(param, codec); QStringList valueList = splitValue(values, QLatin1Char(','), QString::SkipEmptyParts, true); foreach (QString value, valueList) { removeBackSlashEscaping(&value); result.insert(name, value); } } return result; } /*! * Extracts the parameters as delimited by semicolons using \a codec to determine the delimiters. * * On entry \a line should contain the content line sans the group and name * On exit, \a line will be updated to only have the value remain */ QList QVersitReaderPrivate::extractParams(LByteArray* line, QTextCodec *codec) const { const QByteArray colon = VersitUtils::encode(':', codec); const QByteArray semicolon = VersitUtils::encode(';', codec); QList params; /* find the end of the name¶ms */ int colonIndex = line->indexOf(colon); if (colonIndex > 0) { QByteArray nameAndParamsString = line->left(colonIndex); params = extractParts(nameAndParamsString, semicolon, codec); /* Update line */ line->chopLeft(colonIndex + colon.length()); } else if (colonIndex == 0) { // No parameters.. advance past it line->chopLeft(colon.length()); } return params; } /*! * Extracts the parts separated by separator discarding the separators escaped with a backslash * encoded with \a codec */ QList QVersitReaderPrivate::extractParts( const QByteArray& text, const QByteArray& separator, QTextCodec* codec) const { QList parts; int partStartIndex = 0; int textLength = text.length(); int separatorLength = separator.length(); const QByteArray backslash = VersitUtils::encode('\\', codec); int backslashLength = backslash.length(); for (int i=0; i < textLength-separatorLength+1; i++) { if (containsAt(text, separator, i) && (i < backslashLength || !containsAt(text, backslash, i-backslashLength))) { int length = i-partStartIndex; QByteArray part = extractPart(text,partStartIndex,length); if (part.length() > 0) parts.append(part); partStartIndex = i+separatorLength; } } // Add the last or only part QByteArray part = extractPart(text,partStartIndex); if (part.length() > 0) parts.append(part); return parts; } /*! * Extracts a substring limited by /a startPosition and /a length. */ QByteArray QVersitReaderPrivate::extractPart( const QByteArray& text, int startPosition, int length) const { QByteArray part; if (startPosition >= 0) part = text.mid(startPosition,length).trimmed(); return part; } /*! * Extracts the name of the parameter using \a codec to determine the delimiters. * No name is interpreted as an implicit "TYPE". */ QString QVersitReaderPrivate::paramName(const QByteArray& parameter, QTextCodec* codec) const { if (parameter.trimmed().length() == 0) return QString(); const QByteArray equals = VersitUtils::encode('=', codec); int equalsIndex = parameter.indexOf(equals); if (equalsIndex > 0) { return codec->toUnicode(parameter.left(equalsIndex)).trimmed(); } return QStringLiteral("TYPE"); } /*! * Extracts the value of the parameter using \a codec to determine the delimiters */ QString QVersitReaderPrivate::paramValue(const QByteArray& parameter, QTextCodec* codec) const { QByteArray value(parameter); const QByteArray equals = VersitUtils::encode('=', codec); int equalsIndex = parameter.indexOf(equals); if (equalsIndex > 0) { int valueLength = parameter.length() - (equalsIndex + equals.length()); value = parameter.right(valueLength).trimmed(); } return codec->toUnicode(value); } /* * Returns true if and only if \a text contains \a ba at \a index * * On entry, index must be >= 0 * * T is either a QByteArray or LByteArray */ template bool QVersitReaderPrivate::containsAt(const T& text, const QByteArray& match, int index) { int n = match.length(); // This check is necessary because constData doesn't ensure it's null terminated at the right place if (text.size() - index < n) return false; const char* textData = text.constData(); const char* matchData = match.constData(); return memcmp(textData+index, matchData, n) == 0; } /*! * If the \a type and the \a property's name is known to contain a structured value, \a property's * value is split according to the type of structuring (compound vs. list) it is known to have. * Returns true if and only if such a split happened (ie. the property value holds a QStringList on * exit). */ bool QVersitReaderPrivate::splitStructuredValue( QVersitProperty* property, bool hasEscapedBackslashes) const { QVariant variant = property->variantValue(); if (property->valueType() == QVersitProperty::CompoundType) { variant.setValue(splitValue(variant.toString(), QLatin1Char(';'), QString::KeepEmptyParts, hasEscapedBackslashes)); property->setValue(variant); return true; } else if (property->valueType() == QVersitProperty::ListType) { variant.setValue(splitValue(variant.toString(), QLatin1Char(','), QString::SkipEmptyParts, hasEscapedBackslashes)); property->setValue(variant); return true; } return false; } /*! * Splits the \a string into substrings wherever \a sep occurs. * If \a hasEscapedBackslashes is false, then a \a sep preceded by a backslash is not considered * a split point (but the backslash is removed). * If \a hasEscapedBackslashes is true, then a \a sep preceded by an odd number of backslashes is * not considered a split point (but one backslash is removed). */ QStringList QVersitReaderPrivate::splitValue(const QString& string, const QChar& sep, QString::SplitBehavior behaviour, bool hasEscapedBackslashes) { QStringList list; bool isEscaped = false; // is the current character escaped int segmentStartIndex = 0; QString segment; for (int i = 0; i < string.length(); i++) { if (string.at(i) == QLatin1Char('\\')) { if (hasEscapedBackslashes) isEscaped = !isEscaped; // two consecutive backslashes make isEscaped false else isEscaped = true; } else if (string.at(i) == sep) { if (isEscaped) { // we see an escaped separator - remove the backslash segment += string.midRef(segmentStartIndex, i-segmentStartIndex-1); segment += sep; } else { // we see a separator segment += string.midRef(segmentStartIndex, i - segmentStartIndex); if (behaviour == QString::KeepEmptyParts || !segment.isEmpty()) list.append(segment); segment.clear(); } segmentStartIndex = i+1; isEscaped = false; } else { // normal character - keep going isEscaped = false; } } // The rest of the string after the last sep. segment += string.midRef(segmentStartIndex); if (behaviour == QString::KeepEmptyParts || !segment.isEmpty()) list.append(segment); return list; } /*! * Removes backslash escaping for line breaks (CRLFs), colons, semicolons, backslashes and commas * according to RFC 2426. This is called on parameter names and values and property values. * Colons ARE unescaped because the text of RFC2426 suggests that they should be. */ void QVersitReaderPrivate::removeBackSlashEscaping(QString* text) { if (!(text->startsWith(QLatin1Char('"')) && text->endsWith(QLatin1Char('"')))) { /* replaces \; with ; \, with , \: with : \\ with \ */ text->replace(QRegExp(QStringLiteral("\\\\([;,:\\\\])")), QStringLiteral("\\1")); // replaces \n with a CRLF text->replace(QStringLiteral("\\n"), QStringLiteral("\r\n"), Qt::CaseInsensitive); } } #include "moc_qversitreader_p.cpp" QT_END_NAMESPACE_VERSIT src/versit/qversitreader_p.h000066400000000000000000000242721233466112000165360ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITREADER_P_H #define QVERSITREADER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include #include #include #include #include #include #include #include QT_FORWARD_DECLARE_CLASS(QBuffer) QT_FORWARD_DECLARE_CLASS(QIODevice) QT_FORWARD_DECLARE_CLASS(QTextCodec) QT_BEGIN_NAMESPACE_VERSIT // The maximum number of bytes allowed to stay in memory after being read. The smaller this is, // the more time spent moving bytes around. The larger it is, the more memory is wasted. static const int MAX_OLD_BYTES_TO_KEEP = 8192; /* * An LByteArray has a subset of QByteArray's interface, plus an efficient chopLeft function * * It stores a QByteArray internally, plus a marker of where it starts and where it ends. */ class LByteArray { public: LByteArray() : mStart(0), mEnd(0) {} explicit LByteArray(const QByteArray& d) :mData(d), mStart(0), mEnd(d.size()) {} LByteArray(const QByteArray& d, int start, int end) :mData(d), mStart(start), mEnd(end) {} bool isEmpty() const { return mEnd <= mStart; } char at(int i) const { return mData.at(mStart + i); } QByteArray toByteArray() const { return mData.mid(mStart, mEnd-mStart); } /* Removes \a n bytes from the start of the QByteArray. */ void chopLeft(int n) { Q_ASSERT(size() >= n && n >= 0); mStart += n; } QByteArray left(int n) { Q_ASSERT(size() >= n && n >= 0); return mData.mid(mStart, n); } int indexOf(const QByteArray& needle) { int index = mData.indexOf(needle, mStart) - mStart; if (index < size()) return index; return -1; } int size() const { return mEnd - mStart; } const char* constData() const { return mData.constData() + mStart; } bool contains(const QByteArray& ba) const { int i = mData.indexOf(ba, mStart); return i > 0 && i <= mEnd - ba.length(); } bool endsWith(const QByteArray& ba) const { // Loop backwards from ba and from mData (starting from index mEnd) if (ba.size() > size()) return false; return memcmp(mData.constData()+mEnd-ba.size(), ba.constData(), ba.size()) == 0; } LByteArray& operator=(const QByteArray& ba) { mData = ba; mStart = 0; mEnd = mData.size(); return *this; } bool operator==(const QByteArray& ba) { return toByteArray() == ba; } bool operator!=(const QByteArray& ba) { return toByteArray() != ba; } private: /* Clears the memory of bytes before the start marker */ void dropOldData() { if (mStart > MAX_OLD_BYTES_TO_KEEP && mEnd >= mStart) { mData.remove(0, mStart); mEnd -= mStart; mStart = 0; } } void setBounds(int start, int end) { mStart = start; mEnd = end; } QByteArray mData; int mStart; int mEnd; friend class LineReader; }; class Q_VERSIT_EXPORT LineReader { public: LineReader(QIODevice* device, QTextCodec* codec); LineReader(QIODevice* device); LineReader(QIODevice* device, QTextCodec* codec, int chunkSize); void init(); void pushLine(const QByteArray& line); int odometer() const; bool atEnd() const; QTextCodec* codec() const; bool isCodecCertain() const; bool isCodecUtf8Compatible() const; void setCodecUtf8Incompatible(); LByteArray readLine(); private: void readOneLine(LByteArray* cursor); bool tryReadLine(LByteArray* cursor, bool atEnd); QIODevice* const mDevice; QTextCodec* mCodec; bool mIsCodecCertain; bool mIsCodecUtf8Compatible; int mChunkSize; // How many bytes to read in one go. QList mCrlfList; QStack mPushedLines; // Stores a lines that has been "pushed" in front by pushLine LByteArray mBuffer; int mOdometer; int mSearchFrom; }; class Q_VERSIT_EXPORT QVersitReaderPrivate : public QThread { Q_OBJECT public: // Constructors and destructor QVersitReaderPrivate(); ~QVersitReaderPrivate(); static QHash, QVersitProperty::ValueType>* valueTypeMap(); void init(QVersitReader* reader); signals: void stateChanged(QVersitReader::State state); void resultsAvailable(); protected: // From QThread void run(); public: // New functions void read(); // mutexed getters and setters. void setState(QVersitReader::State); QVersitReader::State state() const; void setError(QVersitReader::Error); QVersitReader::Error error() const; void setCanceling(bool cancelling); bool isCanceling(); bool parseVersitDocument(LineReader* lineReader, QVersitDocument* document); bool parseVersitDocumentBody(LineReader* lineReader, QVersitDocument* document); QVersitProperty parseNextVersitProperty( QVersitDocument::VersitType versitType, LineReader* lineReader); void parseVCard21Property( LByteArray* text, QVersitProperty* property, LineReader* lineReader); void parseVCard30Property( QVersitDocument::VersitType versitType, LByteArray* text, QVersitProperty* property, LineReader* lineReader); bool setVersionFromProperty( QVersitDocument* document, const QVersitProperty& property) const; bool unencode( QByteArray* value, QVersitProperty* property, LineReader* lineReader) const; QString decodeCharset( const QByteArray& value, QVersitProperty* property, LineReader* lineReader, QTextCodec** codec) const; void decodeQuotedPrintable(QByteArray* text) const; /* These functions operate on a cursor describing a single line */ QPair extractPropertyGroupsAndName(LByteArray* line, QTextCodec* codec) const; QMultiHash extractVCard21PropertyParams(LByteArray* line, QTextCodec* codec) const; QMultiHash extractVCard30PropertyParams(LByteArray* line, QTextCodec* codec) const; // "Private" functions QList extractParams(LByteArray* line, QTextCodec *codec) const; QList extractParts(const QByteArray& text, const QByteArray& separator, QTextCodec *codec) const; QByteArray extractPart(const QByteArray& text, int startPosition, int length=-1) const; QString paramName(const QByteArray& parameter, QTextCodec* codec) const; QString paramValue(const QByteArray& parameter, QTextCodec* codec) const; template static bool containsAt(const T& text, const QByteArray& ba, int index); bool splitStructuredValue(QVersitProperty* property, bool hasEscapedBackslashes) const; static QStringList splitValue(const QString& string, const QChar& sep, QString::SplitBehavior behaviour, bool hasEscapedBackslashes); static void removeBackSlashEscaping(QString* text); // Data public: QPointer mIoDevice; QScopedPointer mInputBytes; // Holds the data set by setData() QList mVersitDocuments; int mDocumentNestingLevel; // Depth in parsing nested Versit documents QTextCodec* mDefaultCodec; QVersitReader::State mState; QVersitReader::Error mError; bool mIsCanceling; mutable QMutex mMutex; private: /* key is the document type and property name, value is the type of property it is. If there is no entry, assume it is a PlainType */ static QHash, QVersitProperty::ValueType>* mValueTypeMap; }; QT_END_NAMESPACE_VERSIT #endif // QVERSITREADER_P_H src/versit/qversitresourcehandler.cpp000066400000000000000000000136071233466112000204750ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitresourcehandler.h" #include #include #include #include "qversitdefs_p.h" #include "qversitproperty.h" QT_BEGIN_NAMESPACE_VERSIT /*! \class QVersitResourceHandler \brief The QVersitResourceHandler class is an interface for clients wishing to implement custom behaviour for loading and saving files to disk when exporting and importing. \ingroup versit-extension \inmodule QtVersit \sa QVersitContactImporter \sa QVersitContactExporter \sa QVersitDefaultResourceHandler */ /*! \fn virtual QVersitResourceHandler::~QVersitResourceHandler() Frees any memory used by the handler. */ /*! \fn virtual bool QVersitResourceHandler::saveResource(const QByteArray& contents, const QVersitProperty& property, QString* location) = 0; Saves the binary data \a contents to a file on a persistent storage medium. \a property holds the QVersitProperty which is the context in which the binary is coming from. The QVersitResourceHandler can use this, for example, to determine file extension it should choose. *\a location is filled with the contents of the file. Returns true on success, false on failure. */ /*! \fn virtual bool QVersitResourceHandler::loadResource(const QString& location, QByteArray* contents, QString* mimeType) = 0 Loads a file from \a location. *\a contents is filled with the contents of the file and *\a mimeType is set to the MIME type that it is determined to be. Returns true on success, false on failure. */ /*! \class QVersitDefaultResourceHandler \brief The QVersitDefaultResourceHandler class provides a default implementation of a Versit resource handler. \ingroup versit-extension An example resource handler implementation: \snippet qtversitdocsample/qtversitdocsample.cpp Resource handler \sa QVersitContactImporter, QVersitContactExporter, QVersitResourceHandler */ class QVersitDefaultResourceHandlerPrivate { public: QMap mFileExtensionMapping; }; /*! Constructs a QVersitDefaultResourceHandler. */ QVersitDefaultResourceHandler::QVersitDefaultResourceHandler() : d(new QVersitDefaultResourceHandlerPrivate) { // File extension mappings int fileExtensionCount = sizeof(versitFileExtensionMappings)/sizeof(VersitFileExtensionMapping); for (int i = 0; i < fileExtensionCount; i++) { d->mFileExtensionMapping.insert( QLatin1String(versitFileExtensionMappings[i].extension), QLatin1String(versitFileExtensionMappings[i].mimeType)); } } /*! Frees any memory used by the resource handler. */ QVersitDefaultResourceHandler::~QVersitDefaultResourceHandler() { delete d; } /*! Default resource loader. Loads file from given \a location into \a contents and returns true if successful. Sets the \a mimeType based on the file extension. */ bool QVersitDefaultResourceHandler::loadResource(const QString& location, QByteArray* contents, QString* mimeType) { QString extension = location.split(QLatin1Char('.')).last().toLower(); *mimeType = d->mFileExtensionMapping.value(extension); if (location.isEmpty()) return false; QFile file(location); if (!file.open(QIODevice::ReadOnly)) return false; if (!file.isReadable()) return false; *contents = file.readAll(); return contents->size() > 0; } /*! Default resource saver. Does nothing and returns false, ignoring \a contents, \a property and \a location. By default, resources aren't persisted because we don't know when it is safe to remove them. */ bool QVersitDefaultResourceHandler::saveResource(const QByteArray& contents, const QVersitProperty& property, QString* location) { Q_UNUSED(contents) Q_UNUSED(property) Q_UNUSED(location) return false; } QT_END_NAMESPACE_VERSIT src/versit/qversitresourcehandler.h000066400000000000000000000060641233466112000201410ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITRESOURCEHANDLER_H #define QVERSITRESOURCEHANDLER_H #include QT_FORWARD_DECLARE_CLASS(QByteArray) QT_FORWARD_DECLARE_CLASS(QString) QT_BEGIN_NAMESPACE_VERSIT class QVersitDefaultResourceHandlerPrivate; class QVersitProperty; class Q_VERSIT_EXPORT QVersitResourceHandler { public: virtual ~QVersitResourceHandler() {} virtual bool loadResource(const QString& location, QByteArray* contents, QString* mimeType) = 0; virtual bool saveResource(const QByteArray& contents, const QVersitProperty& property, QString* location) = 0; }; class Q_VERSIT_EXPORT QVersitDefaultResourceHandler : public QVersitResourceHandler { public: QVersitDefaultResourceHandler(); virtual ~QVersitDefaultResourceHandler(); virtual bool loadResource(const QString& location, QByteArray* contents, QString* mimeType); virtual bool saveResource(const QByteArray& contents, const QVersitProperty& property, QString* location); protected: QVersitDefaultResourceHandlerPrivate* d; }; QT_END_NAMESPACE_VERSIT #endif // QVERSITRESOURCEHANDLER_H src/versit/qversitutils.cpp000066400000000000000000000217211233466112000164440ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitutils_p.h" #include #include #include #include #include #include #include #include "qversitdocument.h" QT_BEGIN_NAMESPACE_VERSIT QTextCodec* VersitUtils::m_previousCodec = 0; QList* VersitUtils::m_newlineList = 0; QByteArray VersitUtils::m_encodingMap[256]; /*! * Encode \a ch with \a codec, without adding an byte-order mark */ QByteArray VersitUtils::encode(char ch, QTextCodec* codec) { if (codec != m_previousCodec) { changeCodec(codec); } return m_encodingMap[(int)ch]; } /*! * Encode \a ba with \a codec, without adding an byte-order mark. \a ba is interpreted as ASCII */ QByteArray VersitUtils::encode(const QByteArray& ba, QTextCodec* codec) { QTextCodec::ConverterState state(QTextCodec::IgnoreHeader); return codec->fromUnicode(QString::fromLatin1(ba.data()).data(), ba.length(), &state); } /*! * Returns the list of DOS, UNIX and Mac newline characters for \a codec. */ QList* VersitUtils::newlineList(QTextCodec* codec) { if (m_newlineList != 0 && codec == m_previousCodec) { return m_newlineList; } changeCodec(codec); return m_newlineList; } /*! * Update the cached tables of pregenerated encoded text with \a codec. */ void VersitUtils::changeCodec(QTextCodec* codec) { // Build m_encodingMap QChar qch; QTextCodec::ConverterState state(QTextCodec::IgnoreHeader); for (int c = 0; c < 256; c++) { qch = QLatin1Char(c); m_encodingMap[c] = codec->fromUnicode(&qch, 1, &state); } // Build m_newlineList if (m_newlineList != 0) delete m_newlineList; m_newlineList = new QList; m_newlineList->append(QByteArrayMatcher(encode("\r\n", codec))); m_newlineList->append(QByteArrayMatcher(encode("\n", codec))); m_newlineList->append(QByteArrayMatcher(encode("\r", codec))); m_previousCodec = codec; } /*! * Finds a property in the \a document with the given \a propertyName, adds it to \a toBeRemoved, * and returns it. */ QVersitProperty VersitUtils::takeProperty(const QVersitDocument& document, const QString& propertyName, QList* toBeRemoved) { foreach (const QVersitProperty& currentProperty, document.properties()) { if (currentProperty.name() == propertyName) { *toBeRemoved << currentProperty; return currentProperty; } } return QVersitProperty(); } /*! * Returns true iff \a bytes is a valid UTF-8 sequence. */ bool VersitUtils::isValidUtf8(const QByteArray& bytes) { int sequenceLength = 1; // number of bytes in total for a sequence int continuation = 0; // number of bytes left in a continuation quint32 codePoint = 0; for (int i = 0; i < bytes.size(); i++) { quint8 byte = bytes[i]; if (continuation == 0) { if (byte & 0x80) { // 1xxxxxxx if (byte & 0x40) { // 11xxxxxx if (byte == 0xc0 || byte == 0xc1) // 1100000x return false; // overlong 2 byte sequence if (byte & 0x20) { // 111xxxxx if (byte & 0x10) { // 1111xxxx if (byte & 0x08) { // 11111xxx // Outside unicode range return false; } else { // 11110xxx sequenceLength = 4; continuation = 3; // three more bytes codePoint = byte & 0x07; // take the last 3 bits } } else { // 1110xxxx sequenceLength = 3; continuation = 2; // two more bytes codePoint = byte & 0x0f; // take last 4 bits } } else { // 110xxxxx sequenceLength = 2; continuation = 1; // one more byte codePoint = byte & 0x1f; // take last 5 bits } } else { // 10xxxxxx // unexpected continuation return false; } } else { // 0xxxxxxx sequenceLength = 1; } } else { // continuation > 0 if ((byte & 0xc0) != 0x80) // 10xxxxxx return false; // expected continuation not found codePoint = (codePoint << 6) | (byte & 0x3f); // append last 6 bits continuation--; } if (continuation == 0) { // Finished decoding a character - it's not overlong and that it's in range switch (sequenceLength) { // 1-byte sequence can't be overlong // 2-byte sequence has already been checked for overlongness case 3: if (codePoint < 0x800) // overlong return false; // Filter out codepoints outside the Unicode range if ((codePoint >= 0xd800 && codePoint <= 0xdfff) // utf-16 surrogate halves || (codePoint >= 0xfffe && codePoint <= 0xffff)) { // reversed utf-16 BOM return false; } break; case 4: if (codePoint < 0x10000 // overlong || codePoint > 0x10ffff) // above Unicode range return false; break; } codePoint = 0; } } return continuation == 0; } /*! * Convert variant \a data to string \a json in JSON format. * * The data is encoded as an array containing one item * to allow the same encoding to be used for both * primitive and compound data types. * * Returns true if the conversion is successful, false otherwise. * * \sa convertFromJson() */ bool VersitUtils::convertToJson(const QVariant &data, QString *json) { const QJsonValue dataAsJsonValue = QJsonValue::fromVariant(data); if (data.isValid() && dataAsJsonValue.isNull()) return false; QJsonArray jsonArray; jsonArray.append(dataAsJsonValue); const QJsonDocument jsonDocument(jsonArray); *json = QString::fromUtf8(jsonDocument.toJson()); return true; } /*! * Convert string \a json in JSON format to variant \a data. * * The format of the json string is assumed to be a one-item array. * * Returns true if the conversion is successful, false otherwise. * * \sa convertToJson() */ bool VersitUtils::convertFromJson(const QString &json, QVariant *data) { const QJsonDocument jsonDoc = QJsonDocument::fromJson(json.toUtf8()); const QJsonValue jsonValue = jsonDoc.array().at(0); if (jsonValue.isUndefined()) return false; *data = jsonValue.toVariant(); return true; } QT_END_NAMESPACE_VERSIT src/versit/qversitutils_p.h000066400000000000000000000072341233466112000164330ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITUTILS_P_H #define QVERSITUTILS_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include QT_FORWARD_DECLARE_CLASS(QString) QT_FORWARD_DECLARE_CLASS(QTextCodec) QT_FORWARD_DECLARE_CLASS(QVariant) QT_BEGIN_NAMESPACE_VERSIT class QVersitDocument; class Q_VERSIT_EXPORT VersitUtils { public: static QByteArray encode(const QByteArray& ba, QTextCodec* codec); static QByteArray encode(char ch, QTextCodec* codec); static QList* newlineList(QTextCodec* codec); static void changeCodec(QTextCodec* codec); static QVersitProperty takeProperty(const QVersitDocument& document, const QString& propertyName, QList* toBeRemoved); static bool isValidUtf8(const QByteArray& bytes); static bool convertToJson(const QVariant &data, QString *json); static bool convertFromJson(const QString &json, QVariant *data); private: // These are caches for performance: // The previous codec that encode(char, QTextCodec) was called with static QTextCodec* m_previousCodec; // The QByteArray corresponding to each char from 0-255, encoded with m_previousCodec static QByteArray m_encodingMap[256]; // List of different newline delimeters, encoded with m_previousCodec static QList* m_newlineList; }; QT_END_NAMESPACE_VERSIT #endif // QVERSITUTILS_P_H src/versit/qversitwriter.cpp000066400000000000000000000245301233466112000166210ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitwriter.h" #include "qversitwriter_p.h" #include #include #include "qversitutils_p.h" QT_BEGIN_NAMESPACE_VERSIT /*! \class QVersitWriter \brief The QVersitWriter class writes Versit documents such as vCards to a device. \ingroup versit \inmodule QtVersit QVersitWriter converts a QVersitDocument into its textual representation. QVersitWriter supports writing to an abstract I/O device which can be for example a file or a memory buffer. The writing can be done asynchronously and the waitForFinished() function can be used to implement a blocking write. The serialization of the document is done in accordance with the type of the QVersitDocument being written. The value of each QVersitProperty is encoded according to the type of object: \list \li \l{QString}{QStrings} are serialized verbatim, unless the default codec of the writer cannot encode the string: in this case, UTF-8 is used to encode it (and the CHARSET parameter added to the property, as per the vCard 2.1 specification). If the document type is vCard 2.1, quoted-printable encoding may also be performed. \li \l{QByteArray}{QByteArrays} are assumed to be binary data and are serialized as base-64 encoded values. \li \l{QVersitDocument}{QVersitDocuments} are serialized as a nested document (eg. as per the AGENT property in vCard). \endlist \sa QVersitDocument, QVersitProperty */ /*! * \enum QVersitWriter::Error * This enum specifies an error that occurred during the most recent operation: * \value NoError The most recent operation was successful * \value UnspecifiedError The most recent operation failed for an undocumented reason * \value IOError The most recent operation failed because of a problem with the device * \value OutOfMemoryError The most recent operation failed due to running out of memory * \value NotReadyError The most recent operation failed because there is an operation in progress */ /*! * \enum QVersitWriter::State * Enumerates the various states that a reader may be in at any given time * \value InactiveState Write operation not yet started * \value ActiveState Write operation started, not yet finished * \value CanceledState Write operation is finished due to cancelation * \value FinishedState Write operation successfully completed */ /*! * \fn QVersitWriter::stateChanged(QVersitWriter::State state) * The signal is emitted by the writer when its state has changed (eg. when it has finished * writing to the device). * \a state is the new state of the writer. */ /*! Constructs a new writer. */ QVersitWriter::QVersitWriter() : d(new QVersitWriterPrivate) { d->init(this); } /*! Constructs a new writer that writes to \a outputDevice. */ QVersitWriter::QVersitWriter(QIODevice *outputDevice) : d(new QVersitWriterPrivate) { d->init(this); d->mIoDevice = outputDevice; } /*! Constructs a new writer that appends to \a outputBytes. */ QVersitWriter::QVersitWriter(QByteArray *outputBytes) : d(new QVersitWriterPrivate) { d->init(this); d->mOutputBytes.reset(new QBuffer); d->mOutputBytes->setBuffer(outputBytes); d->mOutputBytes->open(QIODevice::WriteOnly); d->mIoDevice = d->mOutputBytes.data(); } /*! * Frees the memory used by the writer. * Waits until a pending asynchronous writing has been completed. */ QVersitWriter::~QVersitWriter() { d->wait(); delete d; } /*! * Sets the device used for writing to \a device. * Does not take ownership of the device. */ void QVersitWriter::setDevice(QIODevice* device) { d->mOutputBytes.reset(0); d->mIoDevice = device; } /*! * Returns the device used for writing, or 0 if no device has been set. */ QIODevice* QVersitWriter::device() const { if (d->mOutputBytes.isNull()) return d->mIoDevice; else return 0; } /*! * Sets the default codec for the writer to use for writing the entire output. * * If \a codec is NULL, the writer uses the codec according to the specification prescribed default. * (for vCard 2.1, ASCII; for vCard 3.0, UTF-8). */ void QVersitWriter::setDefaultCodec(QTextCodec *codec) { d->mDefaultCodec = codec; } /*! * Returns the document's codec. */ QTextCodec* QVersitWriter::defaultCodec() const { return d->mDefaultCodec; } /*! * Returns the state of the writer. */ QVersitWriter::State QVersitWriter::state() const { return d->state(); } /*! * Returns the error encountered by the last operation. */ QVersitWriter::Error QVersitWriter::error() const { return d->error(); } /*! * Starts writing \a input to device() asynchronously. The serialization format is determined based * on the contents of the input documents. * * Returns false if the output device has not been set or opened or if there is another asynchronous * write operation already pending. Signal \l stateChanged() is emitted with parameter * FinishedState when the writing has finished. * * The device must be already open. The client is responsible for closing it when finished. */ bool QVersitWriter::startWriting(const QList& input) { return startWriting(input, QVersitDocument::InvalidType); } /*! * Starts writing \a input to device() asynchronously using the serialization format specified by \a * type. If \a type is QVersitDocument::InvalidType, the format will be determined based on the * contents of the input documents. * * Returns false if the output device has not been set or opened or if there is another asynchronous * write operation already pending. Signal \l stateChanged() is emitted with parameter * FinishedState when the writing has finished. * * The device must be already open. The client is responsible for closing it when finished. */ bool QVersitWriter::startWriting(const QList& input, QVersitDocument::VersitType type) { d->mInput = input; if (d->state() == ActiveState || d->isRunning()) { d->setError(QVersitWriter::NotReadyError); return false; } else if (!d->mIoDevice || !d->mIoDevice->isWritable()) { d->setError(QVersitWriter::IOError); return false; } else { d->setState(ActiveState); d->setError(NoError); d->setDocumentType(type); d->start(); return true; } } /*! * Starts writing \a input to device() asynchronously. The serialization format is determined based * on the contents of the input documents. * * Returns false if the output device has not been set or opened or if there is another asynchronous * write operation already pending. Signal \l stateChanged() is emitted with parameter * FinishedState when the writing has finished. * * The device must be already open. The client is responsible for closing it when finished. */ bool QVersitWriter::startWriting(const QVersitDocument& input) { return startWriting(QList() << input, QVersitDocument::InvalidType); } /*! * Starts writing \a input to device() asynchronously using the serialization format specified by \a * type. If \a type is QVersitDocument::InvalidType, the format will be determined based on the * contents of the input documents. * * Returns false if the output device has not been set or opened or if there is another asynchronous * write operation already pending. Signal \l stateChanged() is emitted with parameter * FinishedState when the writing has finished. * * The device must be already open. The client is responsible for closing it when finished. */ bool QVersitWriter::startWriting(const QVersitDocument& input, QVersitDocument::VersitType type) { return startWriting(QList() << input, type); } /*! * Attempts to asynchronously cancel the write request. */ void QVersitWriter::cancel() { d->setCanceling(true); } /*! * If the state is ActiveState, blocks until the writer has finished writing or \a msec milliseconds * has elapsed, returning true if it successfully finishes or is cancelled by the user. * If \a msec is negative or zero, the function blocks until the writer has finished, regardless of * how long it takes. * If the state is FinishedState, returns true immediately. * Otherwise, returns false immediately. */ bool QVersitWriter::waitForFinished(int msec) { State state = d->state(); if (state != InactiveState) { if (msec <= 0) return d->wait(ULONG_MAX); else return d->wait(msec); } else { return false; } } #include "moc_qversitwriter.cpp" QT_END_NAMESPACE_VERSIT src/versit/qversitwriter.h000066400000000000000000000071151233466112000162660ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITWRITER_H #define QVERSITWRITER_H #include #include #include QT_FORWARD_DECLARE_CLASS(QByteArray) QT_FORWARD_DECLARE_CLASS(QIODevice) QT_FORWARD_DECLARE_CLASS(QTextCodec) QT_BEGIN_NAMESPACE_VERSIT class QVersitWriterPrivate; // writes a QVersitDocument to i/o device class Q_VERSIT_EXPORT QVersitWriter : public QObject { Q_OBJECT public: enum Error { NoError = 0, UnspecifiedError, IOError, OutOfMemoryError, NotReadyError }; enum State { InactiveState = 0, ActiveState, CanceledState, FinishedState }; QVersitWriter(); QVersitWriter(QIODevice* outputDevice); QVersitWriter(QByteArray* outputBytes); ~QVersitWriter(); // output device void setDevice(QIODevice* outputDevice); QIODevice* device() const; void setDefaultCodec(QTextCodec* codec); QTextCodec* defaultCodec() const; State state() const; Error error() const; // writing: public Q_SLOTS: bool startWriting(const QList& input); bool startWriting(const QList& input, QVersitDocument::VersitType type); bool startWriting(const QVersitDocument& input); bool startWriting(const QVersitDocument& input, QVersitDocument::VersitType type); void cancel(); public: Q_INVOKABLE bool waitForFinished(int msec = -1); Q_SIGNALS: void stateChanged(QVersitWriter::State state); private: // data QVersitWriterPrivate* d; }; QT_END_NAMESPACE_VERSIT Q_DECLARE_METATYPE(QTVERSIT_PREPEND_NAMESPACE(QVersitWriter::State)) #endif // QVERSITWRITER_H src/versit/qversitwriter_p.cpp000066400000000000000000000154351233466112000171440ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitwriter_p.h" #include #include #include #include "qvcard21writer_p.h" #include "qvcard30writer_p.h" #include "qversitdocumentwriter_p.h" #include "qversitutils_p.h" QT_BEGIN_NAMESPACE_VERSIT /*! Constructs a writer. */ QVersitWriterPrivate::QVersitWriterPrivate() : mIoDevice(0), mState(QVersitWriter::InactiveState), mError(QVersitWriter::NoError), mIsCanceling(false), mDefaultCodec(0) { } /*! Destroys a writer. */ QVersitWriterPrivate::~QVersitWriterPrivate() { } /*! Links the signals from this to the signals of \a writer. */ void QVersitWriterPrivate::init(QVersitWriter* writer) { qRegisterMetaType("QVersitWriter::State"); connect(this, SIGNAL(stateChanged(QVersitWriter::State)), writer, SIGNAL(stateChanged(QVersitWriter::State)), Qt::DirectConnection); } /*! * Do the actual writing and set the error and state appropriately. */ void QVersitWriterPrivate::write() { bool canceled = false; // Try to get the type from the parameter to startWriting... QVersitDocument::VersitType type = documentType(); foreach (const QVersitDocument& document, mInput) { if (isCanceling()) { canceled = true; break; } // Get type from the document if not specified in startWriting if (type == QVersitDocument::InvalidType) type = document.type(); QScopedPointer writer(writerForType(type, document)); QTextCodec* codec = mDefaultCodec; if (codec == NULL) { if (type == QVersitDocument::VCard21Type) { codec = QTextCodec::codecForName("ISO-8859-1"); writer->setAsciiCodec(); } else { codec = QTextCodec::codecForName("UTF-8"); } } writer->setCodec(codec); writer->setDevice(mIoDevice); if (!writer->encodeVersitDocument(document)) { setError(QVersitWriter::IOError); break; } } if (canceled) setState(QVersitWriter::CanceledState); else setState(QVersitWriter::FinishedState); } /*! * Inherited from QThread, called by QThread when the thread has been started. */ void QVersitWriterPrivate::run() { write(); } void QVersitWriterPrivate::setState(QVersitWriter::State state) { mMutex.lock(); mState = state; mMutex.unlock(); emit stateChanged(state); } QVersitWriter::State QVersitWriterPrivate::state() const { QMutexLocker locker(&mMutex); return mState; } void QVersitWriterPrivate::setError(QVersitWriter::Error error) { QMutexLocker locker(&mMutex); mError = error; } QVersitWriter::Error QVersitWriterPrivate::error() const { QMutexLocker locker(&mMutex); return mError; } /*! * Returns a QVersitDocumentWriter that can encode a QVersitDocument of type \a type. * The caller is responsible for deleting the object. */ QVersitDocumentWriter* QVersitWriterPrivate::writerForType(QVersitDocument::VersitType type, const QVersitDocument& document) { switch (type) { case QVersitDocument::InvalidType: { // Neither startWriting or the document provided the type. // Need to infer the type from the document's componentType QString componentType(document.componentType()); if (componentType == QStringLiteral("VCARD")) { return new QVCard30Writer(QVersitDocument::VCard30Type); } else if (componentType == QStringLiteral("VCALENDAR") || componentType == QStringLiteral("VEVENT") || componentType == QStringLiteral("VTODO") || componentType == QStringLiteral("VJOURNAL") || componentType == QStringLiteral("VTIMEZONE") || componentType == QStringLiteral("VALARM")) { return new QVCard30Writer(QVersitDocument::ICalendar20Type); } else { return new QVCard30Writer(QVersitDocument::VCard30Type); } } case QVersitDocument::VCard21Type: return new QVCard21Writer(type); case QVersitDocument::VCard30Type: return new QVCard30Writer(type); default: return new QVCard30Writer(type); } } void QVersitWriterPrivate::setCanceling(bool canceling) { QMutexLocker locker(&mMutex); mIsCanceling = canceling; } bool QVersitWriterPrivate::isCanceling() const { QMutexLocker locker(&mMutex); return mIsCanceling; } void QVersitWriterPrivate::setDocumentType(QVersitDocument::VersitType type) { QMutexLocker locker(&mMutex); mType = type; } QVersitDocument::VersitType QVersitWriterPrivate::documentType() const { QMutexLocker locker(&mMutex); return mType; } #include "moc_qversitwriter_p.cpp" QT_END_NAMESPACE_VERSIT src/versit/qversitwriter_p.h000066400000000000000000000071351233466112000166070ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersit module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITWRITER_P_H #define QVERSITWRITER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include QT_FORWARD_DECLARE_CLASS(QBuffer) QT_BEGIN_NAMESPACE_VERSIT class QVersitDocumentWriter; class QVersitWriterPrivate : public QThread { Q_OBJECT signals: void stateChanged(QVersitWriter::State state); public: QVersitWriterPrivate(); virtual ~QVersitWriterPrivate(); void init(QVersitWriter* writer); void write(); // mutexed getters and setters. void setState(QVersitWriter::State); QVersitWriter::State state() const; void setError(QVersitWriter::Error); QVersitWriter::Error error() const; void setCanceling(bool cancelling); bool isCanceling() const; void setDocumentType(QVersitDocument::VersitType type); QVersitDocument::VersitType documentType() const; void run(); static QVersitDocumentWriter* writerForType(QVersitDocument::VersitType type, const QVersitDocument& document); QIODevice* mIoDevice; QScopedPointer mOutputBytes; // Holds the data set by setData() QList mInput; QVersitWriter::State mState; QVersitWriter::Error mError; bool mIsCanceling; mutable QMutex mMutex; QTextCodec* mDefaultCodec; QVersitDocument::VersitType mType; }; QT_END_NAMESPACE_VERSIT #endif // QVERSITWRITER_P_H src/versit/versit.pro000066400000000000000000000026501233466112000152200ustar00rootroot00000000000000TARGET = QtVersit QT = core contacts MODULE_PLUGIN_TYPES = \ versit QMAKE_DOCS = $$PWD/doc/qtversit.qdocconf PUBLIC_HEADERS += \ qversitglobal.h \ qversitdocument.h \ qversitproperty.h \ qversitreader.h \ qversitwriter.h \ qversitcontactexporter.h \ qversitcontactimporter.h \ qversitcontacthandler.h \ qversitresourcehandler.h PRIVATE_HEADERS += \ qversitdocument_p.h \ qversitdocumentwriter_p.h \ qversitproperty_p.h \ qversitreader_p.h \ qversitwriter_p.h \ qvcard21writer_p.h \ qvcard30writer_p.h \ qvcardrestorehandler_p.h \ qversitcontactexporter_p.h \ qversitcontactimporter_p.h \ qversitdefs_p.h \ qversitcontactsdefs_p.h \ qversitcontactpluginloader_p.h \ qversitutils_p.h \ qversitpluginsearch_p.h SOURCES += \ qversitdocument.cpp \ qversitdocument_p.cpp \ qversitdocumentwriter_p.cpp \ qversitproperty.cpp \ qversitreader.cpp \ qversitreader_p.cpp \ qversitwriter.cpp \ qversitwriter_p.cpp \ qvcard21writer.cpp \ qvcard30writer.cpp \ qvcardrestorehandler_p.cpp \ qversitcontactexporter.cpp \ qversitcontactexporter_p.cpp \ qversitcontactimporter.cpp \ qversitcontactimporter_p.cpp \ qversitresourcehandler.cpp \ qversitcontacthandler.cpp \ qversitcontactpluginloader_p.cpp \ qversitutils.cpp HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS load(qt_module) src/versitorganizer/000077500000000000000000000000001233466112000151005ustar00rootroot00000000000000src/versitorganizer/qtimezones_p.cpp000066400000000000000000000074461233466112000203340ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersitOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qtimezones_p.h" #include QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSITORGANIZER QOrganizerManager* TimeZone::getManager() { // We use the memory engine to do time zone recurrence calculations static QOrganizerManager* manager(new QOrganizerManager(QString::fromLatin1("memory"))); return manager; } QDateTime TimeZone::convert(const QDateTime& dateTime) const { Q_ASSERT(isValid()); QOrganizerManager* manager = getManager(); int offset = 100000; // impossible value QDateTime latestPhase; foreach(const TimeZonePhase& phase, mPhases) { QOrganizerEvent event; event.setStartDateTime(phase.startDateTime()); event.setRecurrenceRules(QSet() << phase.recurrenceRule()); event.setRecurrenceDates(phase.recurrenceDates()); QList occurrences = manager->itemOccurrences(event, phase.startDateTime(), dateTime, 500); if (!occurrences.isEmpty()) { QDateTime phaseStart(static_cast(occurrences.last()).startDateTime()); if (phaseStart > latestPhase) { latestPhase = phaseStart; offset = phase.utcOffset(); } } else { offset = phase.utcOffset(); } } QDateTime retn(dateTime); retn.setTimeSpec(Qt::UTC); if (offset >= -86400 && offset <= 86400) // offset must be within -24hours to +24hours return retn.addSecs(-offset); else return retn; } QDateTime TimeZones::convert(const QDateTime& dateTime, const QString& tzid) const { if (!mTimeZones.contains(tzid)) return QDateTime(); TimeZone tz = mTimeZones.value(tzid); if (!tz.isValid()) return QDateTime(); return tz.convert(dateTime); } QT_END_NAMESPACE_VERSITORGANIZER src/versitorganizer/qtimezones_p.h000066400000000000000000000116031233466112000177670ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersitOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QTIMEZONES_P_H #define QTIMEZONES_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerManager; QT_END_NAMESPACE_ORGANIZER QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSITORGANIZER class TimeZonePhase { public: TimeZonePhase() : mStandard(true), mUtcOffset(100000) {} // invalid offset void setStandard(bool standard) { mStandard = standard; } bool isStandard() const { return mStandard; } void setUtcOffset(int offset) { mUtcOffset = offset; } int utcOffset() const { return mUtcOffset; } void setStartDateTime(const QDateTime& dateTime) { mStartDateTime = dateTime; } QDateTime startDateTime() const { return mStartDateTime; } void setRecurrenceRule(const QOrganizerRecurrenceRule& rrule) { mRecurrenceRule = rrule; } QOrganizerRecurrenceRule recurrenceRule() const { return mRecurrenceRule; } void setRecurrenceDates(const QSet& rdates) { mRecurrenceDates = rdates; } QSet recurrenceDates() const { return mRecurrenceDates; } bool isValid() const { // offset must be within -24 hours to +24 hours return mStartDateTime.isValid() && mUtcOffset < 86400 && mUtcOffset > -86400; } private: bool mStandard; // true for STANDARD, false for DAYLIGHT int mUtcOffset; // in seconds, the offset to apply after mStartDateTime QDateTime mStartDateTime; // local time, when the phase comes into effect QOrganizerRecurrenceRule mRecurrenceRule; QSet mRecurrenceDates; }; class TimeZone { public: QDateTime convert(const QDateTime& dateTime) const; void setTzid(const QString& tzid) { mTzid = tzid; } QString tzid() const { return mTzid; } void addPhase(const TimeZonePhase& phase) { mPhases.append(phase); } bool isValid() const { foreach (const TimeZonePhase& phase, mPhases) { if (!phase.isValid()) return false; } return !mPhases.isEmpty(); } private: static QOrganizerManager* getManager(); QString mTzid; QList mPhases; }; class TimeZones { public: QDateTime convert(const QDateTime& dateTime, const QString& tzid) const; void addTimeZone(const TimeZone& timezone) { if (!timezone.tzid().isEmpty()) mTimeZones.insert(timezone.tzid(), timezone); } private: QHash mTimeZones; }; QT_END_NAMESPACE_VERSITORGANIZER #endif // QTIMEZONES_P_H src/versitorganizer/qversitorganizerdefs_p.h000066400000000000000000000064601233466112000220560ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersitOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITORGANIZERDEFS_P_H #define QVERSITORGANIZERDEFS_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSITORGANIZER // Mapping between a string in versit specifications and Qt contact details struct VersitOrganizerDetailMapping { const char* versitPropertyName; const QOrganizerItemDetail::DetailType detailType; const int detailField; }; // Only put simple mappings in this table; ie. where a Versit property maps to a specific detail's // specific field (and that detail has no other fields of interest) const VersitOrganizerDetailMapping versitOrganizerDetailMappings[] = { // FIXME {"SUMMARY", QOrganizerItemDetail::TypeDisplayLabel, QOrganizerItemDisplayLabel::FieldLabel}, {"DESCRIPTION", QOrganizerItemDetail::TypeDescription, QOrganizerItemDescription::FieldDescription}, {"UID", QOrganizerItemDetail::TypeGuid, QOrganizerItemGuid::FieldGuid}, {"CATEGORIES", QOrganizerItemDetail::TypeTag, QOrganizerItemTag::FieldTag} }; QT_END_NAMESPACE_VERSITORGANIZER #endif // QVERSITORGANIZERDEFS_P_H src/versitorganizer/qversitorganizerexporter.cpp000066400000000000000000000224031233466112000230140ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersitOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitorganizerexporter.h" #include "qversitorganizerexporter_p.h" #include #include QTORGANIZER_USE_NAMESPACE QTVERSIT_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSITORGANIZER /*! \class QVersitOrganizerExporter \brief The QVersitOrganizerExporter class converts \l {QOrganizerItem}{QOrganizerItems} into \l {QVersitDocument}{QVersitDocuments}. \ingroup versit \inmodule QtVersit This class is used to convert a list of \l {QOrganizerItem}{QOrganizerItems} (which may be stored in a QOrganizerManager) into a QVersitDocument (which may be written to an I/O device using QVersitReader. While multiple items are provided as input, a single QVersitDocument is produced as output. Unless there is an error, there is a one-to-one mapping between organizer items and sub-documents of the result. */ /*! \class QVersitOrganizerExporterDetailHandler \brief The QVersitOrganizerExporterDetailHandler class is an interface for specifying custom export behaviour for certain organizer item details. \ingroup versit-extension \inmodule QtVersit For general information on extending Qt Versit, see the document on \l{Qt Versit Plugins}. \sa QVersitOrganizerExporter */ /*! \fn QVersitOrganizerExporterDetailHandler::~QVersitOrganizerExporterDetailHandler() Frees any memory in use by this handler. */ /*! \fn void QVersitOrganizerExporterDetailHandler::detailProcessed(const QOrganizerItem& item, const QOrganizerItemDetail& detail, const QVersitDocument& document, QSet* processedFields, QList* toBeRemoved, QList* toBeAdded) Process \a detail and provide a list of updated \l{QVersitProperty}{QVersitProperties} by modifying the \a toBeRemoved and \a toBeAdded lists. This function is called on every QOrganizerItemDetail encountered during an export, after the detail has been processed by the QVersitOrganizerExporter. An implementation of this function can be made to provide support for QOrganizerItemDetails not supported by QVersitOrganizerExporter. The supplied \a item is the container for the \a detail. \a processedFields contains a list of fields in the \a detail that were considered by the QVersitOrganizerExporter or another handler in processing the detail. \a document holds the state of the document before the detail was processed by the exporter. \a toBeRemoved and \a toBeAdded are initially filled with a list of properties that the exporter will remove from and add to the document. These lists can be modified (by removing, modifying or adding properties) by the handler to control the changes that will actually be made to the document. If a property is to be modified in the document, the old version will appear in the \a toBeRemoved list and the new version will appear in the \a toBeAdded list. When the handler uses a field from the detail, it should update the processedFields set to reflect this to inform later handlers that the field has already been processed. After the handler returns control back to the exporter, the properties in the \a toBeRemoved list will be removed and the properties in the \a toBeAdded list will be appended to the document. */ /*! \fn void QVersitOrganizerExporterDetailHandler::itemProcessed(const QOrganizerItem& item, QVersitDocument* document) Perform any final processing on the \a document generated by the \a item. This can be implemented by the handler to clear any internal state before moving onto the next item. This function is called after all QOrganizerItemDetails have been handled by the QVersitOrganizerExporter. */ /*! \enum QVersitOrganizerExporter::Error This enum specifies an error that occurred during the most recent call to exportItems() \value NoError The most recent operation was successful \value EmptyOrganizerError One of the organizer items was empty \value UnknownComponentTypeError One of the components in the iCalendar file is not supported \value UnderspecifiedOccurrenceError An event or todo exception was found which did not specify both its parent and a specifier for which instance to override */ /*! Constructs a new exporter */ QVersitOrganizerExporter::QVersitOrganizerExporter() : d(new QVersitOrganizerExporterPrivate) { } /*! * Constructs a new exporter for the given \a profile. The profile strings should be one of those * defined by QVersitOrganizerHandlerFactory, or a value otherwise agreed to by a \l{Qt Versit * Plugins}{Versit plugin}. * * The profile determines which plugins will be loaded to supplement the exporter. */ QVersitOrganizerExporter::QVersitOrganizerExporter(const QString& profile) : d(new QVersitOrganizerExporterPrivate(profile)) { } /*! Frees the memory used by the exporter */ QVersitOrganizerExporter::~QVersitOrganizerExporter() { delete d; } /*! * Converts \a items into a QVersitDocument, using the format given by \a versitType. * Returns true on success. If any of the items could not be exported, false is returned and * errorMap() will return a list describing the errors that occurred. The successfully exported * components will still be available via document(). * * \sa document(), errorMap() */ bool QVersitOrganizerExporter::exportItems( const QList& items, QVersitDocument::VersitType versitType) { int itemIndex = 0; d->mErrors.clear(); d->mResult.clear(); d->mResult.setType(versitType); d->mResult.setComponentType(QStringLiteral("VCALENDAR")); bool ok = true; QList results; foreach (const QOrganizerItem& item, items) { QTVERSIT_PREPEND_NAMESPACE(QVersitDocument) document; document.setType(versitType); QVersitOrganizerExporter::Error error; if (d->exportItem(item, &document, &error)) { results.append(document); } else { d->mErrors.insert(itemIndex, error); ok = false; } itemIndex++; } d->mResult.setSubDocuments(results); return ok; } /*! *Returns the document exported in the most recent call to exportItems(). * * \sa exportItems() */ QVersitDocument QVersitOrganizerExporter::document() const { return d->mResult; } /*! * \fn QVersitOrganizerExporter::errorMap() const * *Returns the map of errors encountered in the most recent call to exportItems(). The key is * the index into the input list of organizer items and the value is the error that occurred on that * item. If errors occur, export does not generate EmptyContactError or NoNameError errors but * just succeeds in creating the empty, albeit invalid, vCard. QVersitContactExporter never fails. * * \sa exportItems() */ QMap QVersitOrganizerExporter::errorMap() const { return d->mErrors; } /*! * Sets \a handler to be the handler for processing QOrganizerItemDetails, or 0 to have no handler. * * Does not take ownership of the handler. The client should ensure the handler remains valid for * the lifetime of the exporter. * * Only one detail handler can be set. If another detail handler was previously set, it will no * longer be associated with the exporter. */ void QVersitOrganizerExporter::setDetailHandler(QVersitOrganizerExporterDetailHandler* handler) { d->mDetailHandler = handler; } QT_END_NAMESPACE_VERSITORGANIZER src/versitorganizer/qversitorganizerexporter.h000066400000000000000000000075451233466112000224730ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersitOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITORGANIZEREXPORTER_H #define QVERSITORGANIZEREXPORTER_H #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItemDetail; QT_END_NAMESPACE_ORGANIZER QTORGANIZER_USE_NAMESPACE QTVERSIT_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSITORGANIZER class QVersitOrganizerExporterPrivate; class Q_VERSIT_ORGANIZER_EXPORT QVersitOrganizerExporterDetailHandler { public: virtual ~QVersitOrganizerExporterDetailHandler() {} virtual void detailProcessed(const QOrganizerItem& item, const QOrganizerItemDetail& detail, const QVersitDocument& document, QSet* processedFields, QList* toBeRemoved, QList* toBeAdded) = 0; virtual void itemProcessed(const QOrganizerItem& item, QVersitDocument* document) = 0; }; class Q_VERSIT_ORGANIZER_EXPORT QVersitOrganizerExporter { public: enum Error { NoError = 0, EmptyOrganizerError, UnknownComponentTypeError, UnderspecifiedOccurrenceError }; QVersitOrganizerExporter(); explicit QVersitOrganizerExporter(const QString& profile); ~QVersitOrganizerExporter(); bool exportItems(const QList& items, QVersitDocument::VersitType versitType = QVersitDocument::ICalendar20Type); QVersitDocument document() const; QMap errorMap() const; void setDetailHandler(QVersitOrganizerExporterDetailHandler* handler); private: QVersitOrganizerExporterPrivate* d; }; QT_END_NAMESPACE_VERSITORGANIZER #endif // QVERSITORGANIZEREXPORTER_H src/versitorganizer/qversitorganizerexporter_p.cpp000066400000000000000000001101641233466112000233350ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersitOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitorganizerexporter.h" #include "qversitorganizerexporter_p.h" #include #include #include #include #include #include "qversitorganizerdefs_p.h" #include "qversitorganizerpluginloader_p.h" QTORGANIZER_USE_NAMESPACE QTVERSIT_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSITORGANIZER QVersitOrganizerExporterPrivate::QVersitOrganizerExporterPrivate(const QString& profile) : mDetailHandler(NULL), mTimeZoneHandler(NULL) { int versitPropertyCount = sizeof(versitOrganizerDetailMappings)/sizeof(VersitOrganizerDetailMapping); for (int i = 0; i < versitPropertyCount; i++) { mPropertyMappings.insert( versitOrganizerDetailMappings[i].detailType, QPair( versitOrganizerDetailMappings[i].detailField, QLatin1String(versitOrganizerDetailMappings[i].versitPropertyName))); } mPluginDetailHandlers = QVersitOrganizerPluginLoader::instance()->createOrganizerHandlers(profile); mTimeZoneHandler = QVersitOrganizerPluginLoader::instance()->timeZoneHandler(); } QVersitOrganizerExporterPrivate::~QVersitOrganizerExporterPrivate() { foreach (QVersitOrganizerHandler* pluginHandler, mPluginDetailHandlers) { delete pluginHandler; } } bool QVersitOrganizerExporterPrivate::exportItem( const QOrganizerItem& item, QVersitDocument* document, QVersitOrganizerExporter::Error* error) { if (item.isEmpty()) { *error = QVersitOrganizerExporter::EmptyOrganizerError; return false; } if (item.type() == QOrganizerItemType::TypeEvent || item.type() == QOrganizerItemType::TypeEventOccurrence) { document->setComponentType(QStringLiteral("VEVENT")); } else if (item.type() == QOrganizerItemType::TypeTodo || item.type() == QOrganizerItemType::TypeTodoOccurrence) { document->setComponentType(QStringLiteral("VTODO")); } else if (item.type() == QOrganizerItemType::TypeJournal) { document->setComponentType(QStringLiteral("VJOURNAL")); } else { *error = QVersitOrganizerExporter::UnknownComponentTypeError; return false; } QList allDetails = item.details(); if (allDetails.isEmpty()) { *error = QVersitOrganizerExporter::EmptyOrganizerError; return false; } foreach (const QOrganizerItemDetail& detail, allDetails) { exportDetail(item, detail, document); } // run plugin handlers foreach (QVersitOrganizerExporterDetailHandler* handler, mPluginDetailHandlers) { handler->itemProcessed(item, document); } // run the handler, if set if (mDetailHandler) { mDetailHandler->itemProcessed(item, document); } if (item.type() == QOrganizerItemType::TypeEventOccurrence && !documentContainsUidAndRecurrenceId(*document)) { *error = QVersitOrganizerExporter::UnderspecifiedOccurrenceError; return false; } return true; } void QVersitOrganizerExporterPrivate::exportDetail( const QOrganizerItem& item, const QOrganizerItemDetail& detail, QVersitDocument* document) { QList removedProperties; QList generatedProperties; QSet processedFields; if (detail.type() == QOrganizerItemDetail::TypeEventTime) { encodeEventTimeRange(detail, *document, &removedProperties, &generatedProperties, &processedFields); } else if (detail.type() == QOrganizerItemDetail::TypeTodoTime) { encodeTodoTimeRange(detail, *document, &removedProperties, &generatedProperties, &processedFields); } else if (detail.type() == QOrganizerItemDetail::TypeJournalTime) { encodeJournalTimeRange(detail, *document, &removedProperties, &generatedProperties, &processedFields); } else if (detail.type() == QOrganizerItemDetail::TypeTimestamp) { encodeTimestamp(detail, *document, &removedProperties, &generatedProperties, &processedFields); } else if (detail.type() == QOrganizerItemDetail::TypeRecurrence) { encodeRecurrence(item, detail, *document, &removedProperties, &generatedProperties, &processedFields); } else if (detail.type() == QOrganizerItemDetail::TypePriority) { encodePriority(detail, *document, &removedProperties, &generatedProperties, &processedFields); } else if (detail.type() == QOrganizerItemDetail::TypeParent) { encodeInstanceOrigin(detail, *document, &removedProperties, &generatedProperties, &processedFields); } else if (detail.type() == QOrganizerItemDetail::TypeTodoProgress) { encodeTodoProgress(detail, *document, &removedProperties, &generatedProperties, &processedFields); } else if (detail.type() == QOrganizerItemDetail::TypeComment) { encodeComment(detail, &generatedProperties, &processedFields); } else if (detail.type() == QOrganizerItemDetail::TypeExtendedDetail) { encodeExtendedDetail(detail, &generatedProperties, &processedFields); } else if (detail.type() == QOrganizerItemDetail::TypeVersion) { encodeVersion(detail, *document, &removedProperties, &generatedProperties, &processedFields); } else if (detail.type() == QOrganizerItemDetail::TypeAudibleReminder) { encodeAudibleReminder(item, detail, &generatedProperties, &processedFields); } else if (detail.type() == QOrganizerItemDetail::TypeVisualReminder) { encodeVisualReminder(item, detail, &generatedProperties, &processedFields); } else if (detail.type() == QOrganizerItemDetail::TypeEmailReminder) { encodeEmailReminder(item, detail, &generatedProperties, &processedFields); } else if (mPropertyMappings.contains(detail.type())) { encodeSimpleProperty(detail, *document, &removedProperties, &generatedProperties, &processedFields); } // run the plugin handler foreach (QVersitOrganizerExporterDetailHandler* handler, mPluginDetailHandlers) { handler->detailProcessed(item, detail, *document, &processedFields, &removedProperties, &generatedProperties); } // run the detail handler, if set if (mDetailHandler) { mDetailHandler->detailProcessed(item, detail, *document, &processedFields, &removedProperties, &generatedProperties); } foreach(const QVersitProperty& property, removedProperties) { document->removeProperty(property); } foreach(const QVersitProperty& property, generatedProperties) { if (property.valueType() == QVersitProperty::VersitDocumentType) { QVersitDocument subDocument(QVersitDocument::ICalendar20Type); subDocument = property.value(); document->addSubDocument(subDocument); } else { document->addProperty(property); } } } void QVersitOrganizerExporterPrivate::encodeEventTimeRange( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields) { const QOrganizerEventTime &etr = static_cast(detail); bool isAllDay = etr.isAllDay(); QVersitProperty property = takeProperty(document, QStringLiteral("DTSTART"), removedProperties); property.setName(QStringLiteral("DTSTART")); if (isAllDay) { property.setValue(etr.startDateTime().date().toString(QStringLiteral("yyyyMMdd"))); property.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DATE")); } else { property.setValue(encodeDateTime(etr.startDateTime())); } *generatedProperties << property; property = takeProperty(document, QStringLiteral("DTEND"), removedProperties); property.setName(QStringLiteral("DTEND")); if (isAllDay) { // In iCalendar, the end date is exclusive while in Qt Organizer, it is inclusive. property.setValue(etr.endDateTime().date().addDays(1).toString(QStringLiteral("yyyyMMdd"))); property.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DATE")); } else { property.setValue(encodeDateTime(etr.endDateTime())); } *generatedProperties << property; *processedFields << QOrganizerEventTime::FieldStartDateTime << QOrganizerEventTime::FieldEndDateTime; } void QVersitOrganizerExporterPrivate::encodeTodoTimeRange( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields) { const QOrganizerTodoTime &ttr = static_cast(detail); bool isAllDay = ttr.isAllDay(); QVersitProperty property = takeProperty(document, QStringLiteral("DTSTART"), removedProperties); property.setName(QStringLiteral("DTSTART")); if (isAllDay) { property.setValue(ttr.startDateTime().date().toString(QStringLiteral("yyyyMMdd"))); property.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DATE")); } else { property.setValue(encodeDateTime(ttr.startDateTime())); } *generatedProperties << property; property = takeProperty(document, QStringLiteral("DUE"), removedProperties); property.setName(QStringLiteral("DUE")); if (isAllDay) { property.setValue(ttr.dueDateTime().date().toString(QStringLiteral("yyyyMMdd"))); property.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DATE")); } else { property.setValue(encodeDateTime(ttr.dueDateTime())); } *generatedProperties << property; *processedFields << QOrganizerTodoTime::FieldStartDateTime << QOrganizerTodoTime::FieldDueDateTime; } void QVersitOrganizerExporterPrivate::encodeJournalTimeRange( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields) { const QOrganizerJournalTime &jtr = static_cast(detail); QVersitProperty property = takeProperty(document, QStringLiteral("DTSTART"), removedProperties); property.setName(QStringLiteral("DTSTART")); property.setValue(encodeDateTime(jtr.entryDateTime())); *generatedProperties << property; *processedFields << QOrganizerJournalTime::FieldEntryDateTime; } void QVersitOrganizerExporterPrivate::encodeTimestamp( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields) { const QOrganizerItemTimestamp ×tamp = static_cast(detail); QVersitProperty property = takeProperty(document, QStringLiteral("CREATED"), removedProperties); property.setName(QStringLiteral("CREATED")); property.setValue(encodeDateTime(timestamp.created().toUTC())); *generatedProperties << property; property = takeProperty(document, QStringLiteral("LAST-MODIFIED"), removedProperties); property.setName(QStringLiteral("LAST-MODIFIED")); property.setValue(encodeDateTime(timestamp.lastModified().toUTC())); *generatedProperties << property; *processedFields << QOrganizerItemTimestamp::FieldCreated << QOrganizerItemTimestamp::FieldLastModified; } void QVersitOrganizerExporterPrivate::encodeVersion( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields) { const QOrganizerItemVersion &version = static_cast(detail); QVersitProperty property = takeProperty(document, QStringLiteral("X-QTPROJECT-VERSION"), removedProperties); property.setName(QStringLiteral("X-QTPROJECT-VERSION")); QStringList values(QString::number(version.version())); values.append(QString::fromLocal8Bit(version.extendedVersion())); property.setValue(values); property.setValueType(QVersitProperty::CompoundType); *generatedProperties << property; *processedFields << QOrganizerItemVersion::FieldVersion << QOrganizerItemVersion::FieldExtendedVersion; } void QVersitOrganizerExporterPrivate::encodeRecurrence( const QOrganizerItem& item, const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields) { const QOrganizerItemRecurrence &recurrence = static_cast(detail); QSet rrules = recurrence.recurrenceRules(); QSet exrules = recurrence.exceptionRules(); QSet rdates = recurrence.recurrenceDates(); QSet exdates = recurrence.exceptionDates(); if (!rrules.isEmpty()) { foreach (const QOrganizerRecurrenceRule& rrule, rrules) { encodeRecurRule(QStringLiteral("RRULE"), rrule, generatedProperties); } *processedFields << QOrganizerItemRecurrence::FieldRecurrenceRules; } if (!exrules.isEmpty()) { foreach (const QOrganizerRecurrenceRule& exrule, exrules) { encodeRecurRule(QStringLiteral("EXRULE"), exrule, generatedProperties); } *processedFields << QOrganizerItemRecurrence::FieldExceptionRules; } if (!rdates.isEmpty()) { encodeRecurDates(QStringLiteral("RDATE"), item, rdates, document, removedProperties, generatedProperties); *processedFields << QOrganizerItemRecurrence::FieldRecurrenceDates; } if (!exdates.isEmpty()) { encodeRecurDates(QStringLiteral("EXDATE"), item, exdates, document, removedProperties, generatedProperties); *processedFields << QOrganizerItemRecurrence::FieldExceptionDates; } } /*! Encodes an iCalendar recurrence specification from \a rule. */ void QVersitOrganizerExporterPrivate::encodeRecurRule( const QString& propertyName, const QOrganizerRecurrenceRule& rule, QList* generatedProperties) { QVersitProperty property; property.setName(propertyName); QString value = QStringLiteral("FREQ="); switch (rule.frequency()) { case QOrganizerRecurrenceRule::Daily: value.append(QStringLiteral("DAILY")); break; case QOrganizerRecurrenceRule::Weekly: value.append(QStringLiteral("WEEKLY")); break; case QOrganizerRecurrenceRule::Monthly: value.append(QStringLiteral("MONTHLY")); break; case QOrganizerRecurrenceRule::Yearly: value.append(QStringLiteral("YEARLY")); break; case QOrganizerRecurrenceRule::Invalid: default: return; } if (rule.limitType() == QOrganizerRecurrenceRule::CountLimit) { value.append(QStringLiteral(";COUNT=")); value.append(QString::number(rule.limitCount())); } if (rule.limitType() == QOrganizerRecurrenceRule::DateLimit) { value.append(QStringLiteral(";UNTIL=")); value.append(rule.limitDate().toString(QStringLiteral("yyyyMMdd"))); } if (rule.interval() > 1) { value.append(QStringLiteral(";INTERVAL=")); value.append(QString::number(rule.interval())); } if (!rule.daysOfWeek().isEmpty()) { value.append(QStringLiteral(";BYDAY=")); bool first = true; QList daysOfWeek(QList::fromSet(rule.daysOfWeek())); std::sort(daysOfWeek.begin(), daysOfWeek.end()); foreach (Qt::DayOfWeek day, daysOfWeek) { if (!first) value.append(QStringLiteral(",")); first = false; value.append(weekString(day)); } } if (!rule.daysOfMonth().isEmpty()) { value.append(QStringLiteral(";BYMONTHDAY=")); appendInts(&value, rule.daysOfMonth()); } if (!rule.daysOfYear().isEmpty()) { value.append(QStringLiteral(";BYYEARDAY=")); appendInts(&value, rule.daysOfYear()); } if (!rule.weeksOfYear().isEmpty()) { value.append(QStringLiteral(";BYWEEKNO=")); appendInts(&value, rule.weeksOfYear()); } if (!rule.monthsOfYear().isEmpty()) { value.append(QStringLiteral(";BYMONTH=")); bool first = true; QList months( QList::fromSet(rule.monthsOfYear())); std::sort(months.begin(), months.end()); foreach (QOrganizerRecurrenceRule::Month month, months) { if (!first) value.append(QStringLiteral(",")); first = false; value.append(QString::number(month)); } } if (!rule.positions().isEmpty()) { value.append(QStringLiteral(";BYSETPOS=")); appendInts(&value, rule.positions()); } if (rule.firstDayOfWeek() != Qt::Monday && rule.firstDayOfWeek() > 0) { value.append(QStringLiteral(";WKST=")); value.append(weekString(rule.firstDayOfWeek())); } property.setValue(value); property.setValueType(QVersitProperty::PreformattedType); *generatedProperties << property; } /*! Joins \a list together with commas and appends the result to \a str. */ void QVersitOrganizerExporterPrivate::appendInts(QString* str, const QSet& ints) { bool first = true; QList intList(QList::fromSet(ints)); std::sort(intList.begin(), intList.end()); foreach (int n, intList) { if (!first) str->append(QStringLiteral(",")); first = false; str->append(QString::number(n)); } } /*! Converts \a day to a two-character iCalendar string */ QString QVersitOrganizerExporterPrivate::weekString(Qt::DayOfWeek day) { switch (day) { case Qt::Monday: return QStringLiteral("MO"); case Qt::Tuesday: return QStringLiteral("TU"); case Qt::Wednesday: return QStringLiteral("WE"); case Qt::Thursday: return QStringLiteral("TH"); case Qt::Friday: return QStringLiteral("FR"); case Qt::Saturday: return QStringLiteral("SA"); case Qt::Sunday: return QStringLiteral("SU"); default: return QString(); } } void QVersitOrganizerExporterPrivate::encodeRecurDates( const QString& propertyName, const QOrganizerItem& item, const QSet& dates, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties) { Q_UNUSED(item) QVersitProperty property; property = takeProperty(document, propertyName, removedProperties); property.setName(propertyName); property.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DATE")); QString value = property.value(); bool valueIsEmpty = value.isEmpty(); QList dateList(QList::fromSet(dates)); std::sort(dateList.begin(), dateList.end()); foreach (const QDate& dt, dateList) { QString str; if (dt.isValid()) { str = dt.toString(QStringLiteral("yyyyMMdd")); if (!valueIsEmpty) value += QLatin1Char(','); value += str; valueIsEmpty = false; } } property.setValue(value); *generatedProperties << property; } void QVersitOrganizerExporterPrivate::encodePriority( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields) { const QOrganizerItemPriority &priority = static_cast(detail); QVersitProperty property = takeProperty(document, QStringLiteral("PRIORITY"), removedProperties); property.setName(QStringLiteral("PRIORITY")); property.setValue(QString::number(priority.priority())); *generatedProperties << property; *processedFields << QOrganizerItemPriority::FieldPriority; } void QVersitOrganizerExporterPrivate::encodeInstanceOrigin( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields) { const QOrganizerItemParent &instanceOrigin = static_cast(detail); QVersitProperty property = takeProperty(document, QStringLiteral("RECURRENCE-ID"), removedProperties); property.setName(QStringLiteral("RECURRENCE-ID")); property.setValue(instanceOrigin.originalDate().toString(QStringLiteral("yyyyMMdd"))); *generatedProperties << property; *processedFields << QOrganizerItemParent::FieldOriginalDate; } void QVersitOrganizerExporterPrivate::encodeTodoProgress( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields) { const QOrganizerTodoProgress &todoProgress = static_cast(detail); if (todoProgress.finishedDateTime().isValid()) { QVersitProperty property = takeProperty(document, QStringLiteral("COMPLETED"), removedProperties); property.setName(QStringLiteral("COMPLETED")); property.setValue(todoProgress.finishedDateTime().toString(QStringLiteral("yyyyMMddTHHmmss"))); *generatedProperties << property; *processedFields << QOrganizerTodoProgress::FieldFinishedDateTime; } if (todoProgress.hasValue(QOrganizerTodoProgress::FieldPercentageComplete)) { QVersitProperty property = takeProperty(document, QStringLiteral("PERCENT-COMPLETE"), removedProperties); property.setName(QStringLiteral("PERCENT-COMPLETE")); property.setValue(QString::number(todoProgress.percentageComplete())); *generatedProperties << property; *processedFields << QOrganizerTodoProgress::FieldPercentageComplete; } if (todoProgress.hasValue(QOrganizerTodoProgress::FieldStatus)) { QVersitProperty property = takeProperty(document, QStringLiteral("STATUS"), removedProperties); property.setName(QStringLiteral("STATUS")); switch (todoProgress.status()) { case QOrganizerTodoProgress::StatusNotStarted: property.setValue(QStringLiteral("NEEDS-ACTION")); break; case QOrganizerTodoProgress::StatusInProgress: property.setValue(QStringLiteral("IN-PROCESS")); break; case QOrganizerTodoProgress::StatusComplete: property.setValue(QStringLiteral("COMPLETED")); break; default: return; } *generatedProperties << property; *processedFields << QOrganizerTodoProgress::FieldStatus; } } void QVersitOrganizerExporterPrivate::encodeComment( const QOrganizerItemDetail& detail, QList* generatedProperties, QSet* processedFields) { const QOrganizerItemComment &comment = static_cast(detail); QVersitProperty property; property.setName(QStringLiteral("COMMENT")); property.setValue(comment.comment()); *generatedProperties << property; *processedFields << QOrganizerItemComment::FieldComment; } void QVersitOrganizerExporterPrivate::encodeAudibleReminder( const QOrganizerItem &item, const QOrganizerItemDetail &detail, QList *generatedProperties, QSet *processedFields) { const QOrganizerItemAudibleReminder &audibleReminder = static_cast(detail); QVersitProperty property; QVersitDocument valarmDocument = encodeItemReminderCommonFields(item, audibleReminder, processedFields); const QUrl attachUrl = audibleReminder.dataUrl(); if (!attachUrl.isEmpty()) { property.setName(QStringLiteral("ATTACH")); property.setValue(attachUrl.toString()); valarmDocument.addProperty(property); *processedFields << QOrganizerItemAudibleReminder::FieldDataUrl; } property.setValueType(QVersitProperty::VersitDocumentType); property.setValue(QVariant::fromValue(valarmDocument)); property.setName(QStringLiteral("VALARM")); *generatedProperties << property; } void QVersitOrganizerExporterPrivate::encodeEmailReminder( const QOrganizerItem &item, const QOrganizerItemDetail &detail, QList *generatedProperties, QSet *processedFields) { const QOrganizerItemEmailReminder &emailReminder = static_cast(detail); QVersitProperty property; QVersitDocument valarmDocument = encodeItemReminderCommonFields(item, emailReminder, processedFields); foreach (const QVariant &attachment, emailReminder.attachments()) { property.setName(QStringLiteral("ATTACH")); property.setValue(attachment); valarmDocument.addProperty(property); *processedFields << QOrganizerItemEmailReminder::FieldAttachments; } if (emailReminder.hasValue(QOrganizerItemEmailReminder::FieldRecipients)) { property.setName(QStringLiteral("ATTENDEE")); foreach (const QString &recipient, emailReminder.recipients()) { property.setValue(recipient); valarmDocument.addProperty(property); } *processedFields << QOrganizerItemEmailReminder::FieldRecipients; } // DESCRIPTION and SUMMARY properties are not optional, // so we add them anyway even when empty property.setName(QStringLiteral("DESCRIPTION")); property.setValue(emailReminder.body()); valarmDocument.addProperty(property); *processedFields << QOrganizerItemEmailReminder::FieldBody; property.setName(QStringLiteral("SUMMARY")); property.setValue(emailReminder.subject()); valarmDocument.addProperty(property); *processedFields << QOrganizerItemEmailReminder::FieldSubject; property.setValueType(QVersitProperty::VersitDocumentType); property.setValue(QVariant::fromValue(valarmDocument)); property.setName(QStringLiteral("VALARM")); *generatedProperties << property; } void QVersitOrganizerExporterPrivate::encodeVisualReminder( const QOrganizerItem &item, const QOrganizerItemDetail &detail, QList *generatedProperties, QSet *processedFields) { const QOrganizerItemVisualReminder &visualReminder = static_cast(detail); QVersitProperty property; QVersitDocument valarmDocument = encodeItemReminderCommonFields(item, visualReminder, processedFields); const QUrl attachUrl = visualReminder.dataUrl(); const QString message = visualReminder.message(); if (!attachUrl.isEmpty()) { //ICAL specs do not include ATTACH property for DISPLAY VALARM components //Hence, we add it here as an (optional) extended QTPROJECT-specific property property.setName(QStringLiteral("X-QTPROJECT-ATTACH")); property.setValue(attachUrl.toString()); valarmDocument.addProperty(property); *processedFields << QOrganizerItemAudibleReminder::FieldDataUrl; } //DESCRIPTION property is not optional, so we add it anyway even when empty property.setName(QStringLiteral("DESCRIPTION")); property.setValue(message); valarmDocument.addProperty(property); *processedFields << QOrganizerItemVisualReminder::FieldMessage; property.setValueType(QVersitProperty::VersitDocumentType); property.setValue(QVariant::fromValue(valarmDocument)); property.setName(QStringLiteral("VALARM")); *generatedProperties << property; } QVersitDocument QVersitOrganizerExporterPrivate::encodeItemReminderCommonFields( const QOrganizerItem &item, const QOrganizerItemReminder &reminder, QSet *processedFields) { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList reminderProperties; valarmDocument.setComponentType(QStringLiteral("VALARM")); property.setName(QStringLiteral("ACTION")); switch (reminder.type()) { case QOrganizerItemDetail::TypeAudibleReminder: property.setValue(QStringLiteral("AUDIO")); break; case QOrganizerItemDetail::TypeVisualReminder: property.setValue(QStringLiteral("DISPLAY")); break; case QOrganizerItemDetail::TypeEmailReminder: property.setValue(QStringLiteral("EMAIL")); break; default: return QVersitDocument(); } reminderProperties << property; QMap::const_iterator valueIter = reminder.values().constBegin(); bool triggerFound = false; while (valueIter != reminder.values().constEnd()) { switch (valueIter.key()) { case QOrganizerItemReminder::FieldSecondsBeforeStart: { property.setName(QStringLiteral("TRIGGER")); property.setValue(QString(QStringLiteral("-PT%1S")).arg( QString::number(valueIter.value().toInt()))); switch (item.type()) { case QOrganizerItemType::TypeEvent: case QOrganizerItemType::TypeEventOccurrence: property.insertParameter(QStringLiteral("RELATED"), QStringLiteral("START")); break; case QOrganizerItemType::TypeTodo: case QOrganizerItemType::TypeTodoOccurrence: property.insertParameter(QStringLiteral("RELATED"), QStringLiteral("END")); break; default: break; } triggerFound = true; break; } case QOrganizerItemReminder::FieldRepetitionCount: { property.setName(QStringLiteral("REPEAT")); property.setValue(valueIter.value().toString()); break; } case QOrganizerItemReminder::FieldRepetitionDelay: { property.setName(QStringLiteral("DURATION")); property.setValue(QString(QStringLiteral("PT%1S")).arg( QString::number(valueIter.value().toInt()))); break; } default: valueIter ++; property.clear(); continue; break; } reminderProperties << property; *processedFields << valueIter.key(); property.clear(); valueIter ++; } if (!triggerFound) { property.setName(QStringLiteral("TRIGGER")); property.setValue(QString(QStringLiteral("-PT%1S")).arg( QString::number(0))); reminderProperties << property; } valarmDocument.setProperties(reminderProperties); return valarmDocument; } void QVersitOrganizerExporterPrivate::encodeExtendedDetail( const QOrganizerItemDetail &detail, QList *generatedProperties, QSet *processedFields) { const QOrganizerItemExtendedDetail &extendedDetail = static_cast(detail); QVersitProperty property; property.setName(QStringLiteral("X-QTPROJECT-EXTENDED-DETAIL")); QString dataAsJson; if (VersitUtils::convertToJson(extendedDetail.data(), &dataAsJson)) { property.setValue(QStringList() << extendedDetail.name() << dataAsJson); } else { qWarning() << Q_FUNC_INFO << "Failed to export an extended detail"; return; } property.setValueType(QVersitProperty::CompoundType); *generatedProperties << property; *processedFields << QOrganizerItemExtendedDetail::FieldName << QOrganizerItemExtendedDetail::FieldData; } void QVersitOrganizerExporterPrivate::encodeSimpleProperty( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields) { QPair fieldPropertyMap = mPropertyMappings[detail.type()]; const int& fieldName = fieldPropertyMap.first; const QString& propertyName = fieldPropertyMap.second; QVersitProperty property = takeProperty(document, propertyName, removedProperties); property.setName(propertyName); property.setValue(detail.value(fieldName)); *generatedProperties << property; *processedFields << fieldName; } /*! Formats \a dateTime in ISO 8601 basic format */ QString QVersitOrganizerExporterPrivate::encodeDateTime(const QDateTime& dateTime) { if (dateTime.timeSpec() == Qt::UTC) return dateTime.toString(QStringLiteral("yyyyMMddTHHmmssZ")); else return dateTime.toString(QStringLiteral("yyyyMMddTHHmmss")); } /*! * Returns true if and only if \a document has both the UID and the RECURRENCE-ID properties */ bool QVersitOrganizerExporterPrivate::documentContainsUidAndRecurrenceId(const QVersitDocument &document) { bool hasUid = false; bool hasRecurId = false; foreach (const QVersitProperty& property, document.properties()) { const QString& name = property.name(); if (name == QStringLiteral("UID")) { if (hasRecurId) return true; hasUid = true; } else if (name == QStringLiteral("RECURRENCE-ID")) { if (hasUid) return true; hasRecurId = true; } } return false; } /*! * Finds a property in the \a document with the given \a propertyName, adds it to \a toBeRemoved, * and returns it. */ QVersitProperty QVersitOrganizerExporterPrivate::takeProperty( const QVersitDocument& document, const QString& propertyName, QList* toBeRemoved) { foreach (const QVersitProperty& currentProperty, document.properties()) { if (currentProperty.name() == propertyName) { *toBeRemoved << currentProperty; return currentProperty; } } return QVersitProperty(); } QT_END_NAMESPACE_VERSITORGANIZER src/versitorganizer/qversitorganizerexporter_p.h000066400000000000000000000207301233466112000230010ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersitOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITORGANIZEREXPORTER_P_H #define QVERSITORGANIZEREXPORTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerItem; class QOrganizerItemReminder; class QOrganizerRecurrenceRule; QT_END_NAMESPACE_ORGANIZER QTORGANIZER_USE_NAMESPACE QTVERSIT_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSITORGANIZER class QVersitOrganizerExporterPrivate { public: QVersitOrganizerExporterPrivate(const QString& profile = QString()); ~QVersitOrganizerExporterPrivate(); bool exportItem(const QOrganizerItem& item, QVersitDocument* document, QVersitOrganizerExporter::Error* error); QVersitDocument mResult; QMap mErrors; QVersitOrganizerExporterDetailHandler* mDetailHandler; QList mPluginDetailHandlers; QVersitTimeZoneHandler* mTimeZoneHandler; private: void exportDetail( const QOrganizerItem& item, const QOrganizerItemDetail& detail, QVersitDocument* document); void encodeEventTimeRange( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields); void encodeTodoTimeRange( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields); void encodeJournalTimeRange( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields); void encodeTimestamp( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields); void encodeVersion( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields); void encodeRecurrence( const QOrganizerItem& item, const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields); void encodeRecurRule( const QString& propertyName, const QOrganizerRecurrenceRule& rule, QList* generatedProperties); void appendInts(QString* str, const QSet& ints); QString weekString(Qt::DayOfWeek day); void encodeRecurDates( const QString& propertyName, const QOrganizerItem& item, const QSet& dates, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties); void encodePriority( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields); void encodeInstanceOrigin( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields); void encodeTodoProgress( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields); void encodeComment( const QOrganizerItemDetail& detail, QList* generatedProperties, QSet* processedFields); void encodeAudibleReminder( const QOrganizerItem &item, const QOrganizerItemDetail &detail, QList *generatedProperties, QSet *processedFields); void encodeEmailReminder( const QOrganizerItem &item, const QOrganizerItemDetail &detail, QList *generatedProperties, QSet *processedFields); void encodeVisualReminder( const QOrganizerItem &item, const QOrganizerItemDetail &detail, QList *generatedProperties, QSet *processedFields); QVersitDocument encodeItemReminderCommonFields( const QOrganizerItem &item, const QOrganizerItemReminder &reminder, QSet *processedFields); void encodeExtendedDetail( const QOrganizerItemDetail &detail, QList *generatedProperties, QSet *processedFields); void encodeSimpleProperty( const QOrganizerItemDetail& detail, const QVersitDocument& document, QList* removedProperties, QList* generatedProperties, QSet* processedFields); QString encodeDateTime(const QDateTime& dateTime); bool documentContainsUidAndRecurrenceId(const QVersitDocument& document); QVersitProperty takeProperty( const QVersitDocument& document, const QString& propertyName, QList* toBeRemoved); // definition name -> : QMap > mPropertyMappings; }; QT_END_NAMESPACE_VERSITORGANIZER #endif // QVERSITORGANIZEREXPORTER_P_H src/versitorganizer/qversitorganizerglobal.h000066400000000000000000000057661233466112000220660ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPIM module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITORGANIZERGLOBAL_H #define QVERSITORGANIZERGLOBAL_H #include #if defined(QT_NAMESPACE) # define QTVERSITORGANIZER_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::QtVersitOrganizer::name # define QT_BEGIN_NAMESPACE_VERSITORGANIZER namespace QT_NAMESPACE { namespace QtVersitOrganizer { # define QT_END_NAMESPACE_VERSITORGANIZER } } # define QTVERSITORGANIZER_USE_NAMESPACE using namespace QT_NAMESPACE; using namespace QtVersitOrganizer; #else # define QTVERSITORGANIZER_PREPEND_NAMESPACE(name) ::QtVersitOrganizer::name # define QT_BEGIN_NAMESPACE_VERSITORGANIZER namespace QtVersitOrganizer { # define QT_END_NAMESPACE_VERSITORGANIZER } # define QTVERSITORGANIZER_USE_NAMESPACE using namespace QtVersitOrganizer; #endif #ifndef QT_STATIC # if defined(QT_BUILD_VERSITORGANIZER_LIB) # define Q_VERSIT_ORGANIZER_EXPORT Q_DECL_EXPORT # else # define Q_VERSIT_ORGANIZER_EXPORT Q_DECL_IMPORT # endif #else # define Q_VERSIT_ORGANIZER_EXPORT #endif QT_BEGIN_NAMESPACE_VERSITORGANIZER QT_END_NAMESPACE_VERSITORGANIZER #endif // QVERSITORGANIZERGLOBAL_H src/versitorganizer/qversitorganizerhandler.cpp000066400000000000000000000120751233466112000225650ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersitOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitorganizerhandler.h" QT_BEGIN_NAMESPACE_VERSITORGANIZER /*! \class QVersitOrganizerHandler \brief The QVersitOrganizerHandler class is a union of the QVersitOrganizerImporterPropertyHandlerV2 and QVersitOrganizerExporterDetailHandlerV2 interfaces. \ingroup versit-extension \inmodule QtVersit */ /*! \class QVersitOrganizerHandlerFactory \brief The QVersitOrganizerHandlerFactory class provides the interface for Versit plugins. \ingroup versit-extension \inmodule QtVersit This class provides a simple interface for the creation of QVersitOrganizerHandler instances. Implement this interface to write a Versit plugin. For more details, see \l{Qt Versit Plugins}. */ /*! \fn static const QString QVersitOrganizerHandlerFactory::ProfileSync() The constant string signifying a plugin that is relevant to import and export in a synchronization context. \sa QVersitOrganizerHandlerFactory::profiles(), QVersitOrganizerImporter, QVersitOrganizerExporter */ /*! \fn static const QString QVersitOrganizerHandlerFactory::ProfileBackup() The constant string signifying a plugin that is relevant to import and export in a backup/restore context. \sa profiles(), QVersitOrganizerImporter::QVersitOrganizerImporter(), QVersitOrganizerExporter::QVersitOrganizerExporter() */ /*! \fn QVersitOrganizerHandlerFactory::~QVersitOrganizerHandlerFactory() This frees any memory used by the QVersitOrganizerHandlerFactory. */ /*! \fn QSet QVersitOrganizerHandlerFactory::profiles() const This function can be overridden to allow a plugin to report which profiles it is to be active under. If this (as in the default case) returns the empty set, it indicates that the plugin should be loaded under all profiles. If it returns a non-empty set, it will only be loaded for those profiles that are specified by the importer/exporter class. */ /*! \fn QString QVersitOrganizerHandlerFactory::name() const This function should return a unique string that identifies the handlers provided by this factory. Typically, this will be of the form "org.qt-project.Qt.SampleVersitOrganizerHandler" with the appropriate domain and handler name substituted. */ /*! \fn int QVersitOrganizerHandlerFactory::index() const This function should return an index that helps with determining the order in which to run the plugins. Plugins are run in the following order: \list \li Positively-indexed, ascending \li Zero-indexed \li Negatively-indexed, ascending \endlist For example, plugins with an index of 1 are run first and plugins of index -1 are run last. If more than one plugin share an index, the order of execution between them is undefined. By default, this returns 0, which is recommended for plugins with no special ordering requirements. */ /*! \fn QVersitOrganizerHandlerFactory::createHandler() const This function is called by the Versit importer or exporter class to create an instance of the handler provided by this factory. */ /*! \fn QVersitOrganizerHandler::~QVersitOrganizerHandler() Destroys this QVersitOrganizerHandler. */ QT_END_NAMESPACE_VERSITORGANIZER src/versitorganizer/qversitorganizerhandler.h000066400000000000000000000065301233466112000222310ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersitOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITORGANIZERHANDLER_H #define QVERSITORGANIZERHANDLER_H #include #include QT_BEGIN_NAMESPACE_VERSITORGANIZER // qdoc seems to not find QVersitOrganizerHandler if it is declared first, hence this forward // declaration class QVersitOrganizerHandler; class Q_VERSIT_ORGANIZER_EXPORT QVersitOrganizerHandlerFactory { public: virtual ~QVersitOrganizerHandlerFactory() {} virtual QSet profiles() const { return QSet(); } virtual QString name() const = 0; virtual int index() const { return 0; } virtual QVersitOrganizerHandler* createHandler() const = 0; inline static const QString ProfileSync() {return QStringLiteral("Sync");}; inline static const QString ProfileBackup() {return QStringLiteral("Backup");}; }; class Q_VERSIT_ORGANIZER_EXPORT QVersitOrganizerHandler : public QVersitOrganizerImporterPropertyHandler, public QVersitOrganizerExporterDetailHandler { public: virtual ~QVersitOrganizerHandler() {} }; QT_END_NAMESPACE_VERSITORGANIZER #define QT_VERSIT_ORGANIZER_HANDLER_INTERFACE "org.qt-project.Qt.QVersitOrganizerHandlerFactory" QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(QtVersitOrganizer::QVersitOrganizerHandlerFactory, QT_VERSIT_ORGANIZER_HANDLER_INTERFACE) QT_END_NAMESPACE #endif // QVERSITORGANIZERHANDLER_H src/versitorganizer/qversitorganizerimporter.cpp000066400000000000000000000216571233466112000230170ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersitOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitorganizerimporter.h" #include "qversitorganizerimporter_p.h" #include #include QTVERSIT_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSITORGANIZER /*! \class QVersitOrganizerImporter \brief The QVersitOrganizerImporter class converts \l{QVersitDocument}{QVersitDocuments} to \l{QOrganizerItem}{QOrganizerItems}. \ingroup versit \inmodule QtVersit This class is used to convert a \l QVersitDocument (which may be produced by a QVersitReader) to lists of \l{QOrganizerItem}{QOrganizerItems} (which may be saved into a QOrganizerManager. Unless there is an error, there is a one-to-one mapping between sub-documents of the input Versit document and QOrganizerItems. */ /*! \class QVersitOrganizerImporterPropertyHandler \brief The QVersitOrganizerImporterPropertyHandler class is an interface for specifying custom import behaviour for vCard properties. \ingroup versit-extension \inmodule QtVersit For general information on extending Qt Versit, see the document on \l{Qt Versit Plugins}. \sa QVersitOrganizerImporter */ /*! \fn QVersitOrganizerImporterPropertyHandler::~QVersitOrganizerImporterPropertyHandler() Frees any memory in use by this handler. */ /*! \fn void QVersitOrganizerImporterPropertyHandler::propertyProcessed(const QVersitDocument& document, const QVersitProperty& property, const QOrganizerItem& item, bool* alreadyProcessed, QList* updatedDetails) Process \a property and provide a list of updated details by adding them to \a updatedDetails. This function is called on every QVersitProperty encountered during an import, after the property has been processed by the QVersitOrganizerImporter. An implementation of this function can be made to provide support for vCard properties not supported by QVersitOrganizerImporter. The supplied \a document is the container for the \a property. \a alreadyProcessed is true if the QVersitOrganizerImporter or another handler was successful in processing the property. If it is false and the handler processes the property, it should be set to true to inform later handlers that the property requires no further processing. \a item holds the state of the item before the property was processed by the importer. \a updatedDetails is initially filled with a list of details that the importer will update, and can be modified (by removing, modifying or adding details to the list) */ /*! \fn void QVersitOrganizerImporterPropertyHandler::subDocumentProcessed(const QVersitDocument& topLevel, const QVersitDocument& subDocument, QOrganizerItem* item) Perform any final processing on the \a item generated by the \a subDocument. \a topLevel is the container within which \a subDocument was found. This can be implemented by the handler to clear any internal state before moving onto the next document. This function is called after all QVersitProperties have been handled by the QVersitOrganizerImporter. */ /*! \enum QVersitOrganizerImporter::Error This enum specifies an error that occurred during the most recent call to importDocument() \value NoError The most recent operation was successful \value InvalidDocumentError One of the documents is not an iCalendar file \value EmptyDocumentError One of the documents is empty */ /*! Constructs a new importer */ QVersitOrganizerImporter::QVersitOrganizerImporter() : d(new QVersitOrganizerImporterPrivate) { } /*! * Constructs a new importer for the given \a profile. The profile strings should be one of those * defined by QVersitOrganizerHandlerFactory, or a value otherwise agreed to by a \l{Qt Versit * Plugins}{Versit plugin}. * * The profile determines which plugins will be loaded to supplement the importer. */ QVersitOrganizerImporter::QVersitOrganizerImporter(const QString& profile) : d(new QVersitOrganizerImporterPrivate(profile)) { } /*! Frees the memory used by the importer */ QVersitOrganizerImporter::~QVersitOrganizerImporter() { delete d; } /*! * Converts \a document into a corresponding list of QOrganizerItems. After calling this, the * converted organizer items can be retrieved by calling items(). * * Returns true on success. The document should contain at least one subdocument. In the * importing process, each subdocument roughly corresponds to a QOrganizerItem. If any of the * subdocuments cannot be imported as organizer items (eg. they don't conform to the iCalendar * format), false is returned and errorMap() will return a list describing the errors that occurred. * The successfully imported items will still be available via items(). * * \sa items(), errorMap() */ bool QVersitOrganizerImporter::importDocument(const QVersitDocument& document) { d->mItems.clear(); d->mErrors.clear(); bool ok = true; if (document.type() != QVersitDocument::ICalendar20Type || document.componentType() != QStringLiteral("VCALENDAR")) { d->mErrors.insert(-1, QVersitOrganizerImporter::InvalidDocumentError); return false; } const QList subDocuments = document.subDocuments(); if (subDocuments.isEmpty()) { d->mErrors.insert(-1, QVersitOrganizerImporter::EmptyDocumentError); return false; } int documentIndex = 0; foreach (const QVersitDocument& subDocument, subDocuments) { QOrganizerItem item; QVersitOrganizerImporter::Error error; if (d->importDocument(document, subDocument, &item, &error)) { d->mItems.append(item); } else { // importDocument can return false with no error if it's a non-document component if (error != QVersitOrganizerImporter::NoError) { d->mErrors.insert(documentIndex, error); ok = false; } } documentIndex++; } return ok; } /*! * Returns the organizer items imported in the most recent call to importDocument(). * * \sa importDocument() */ QList QVersitOrganizerImporter::items() const { return d->mItems; } /*! * Returns the map of errors encountered in the most recent call to importDocument(). * * The key is the zero based index of the sub document within the container document, or -1 for an error * with the container document itself. The value is the error that occurred on that document. * * \sa importDocument() */ QMap QVersitOrganizerImporter::errorMap() const { return d->mErrors; } /*! * Sets \a handler to be the handler for processing QVersitProperties, or 0 to have no handler. * * Does not take ownership of the handler. The client should ensure the handler remains valid for * the lifetime of the importer. * * Only one property handler can be set. If another property handler was previously set, it will no * longer be associated with the importer. */ void QVersitOrganizerImporter::setPropertyHandler(QVersitOrganizerImporterPropertyHandler* handler) { d->mPropertyHandler = handler; } QT_END_NAMESPACE_VERSITORGANIZER src/versitorganizer/qversitorganizerimporter.h000066400000000000000000000074041233466112000224560ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersitOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITORGANIZERIMPORTER_H #define QVERSITORGANIZERIMPORTER_H #include #include #include #include #include #include QT_BEGIN_NAMESPACE_VERSIT class QVersitDocument; class QVersitProperty; QT_END_NAMESPACE_VERSIT QTORGANIZER_USE_NAMESPACE QTVERSIT_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSITORGANIZER class QVersitOrganizerImporterPrivate; class Q_VERSIT_ORGANIZER_EXPORT QVersitOrganizerImporterPropertyHandler { public: virtual ~QVersitOrganizerImporterPropertyHandler() {} virtual void propertyProcessed(const QVersitDocument& document, const QVersitProperty& property, const QOrganizerItem& item, bool *alreadyProcessed, QList* updatedDetails) = 0; virtual void subDocumentProcessed(const QVersitDocument& topLevel, const QVersitDocument& subDocument, QOrganizerItem* item) = 0; }; class Q_VERSIT_ORGANIZER_EXPORT QVersitOrganizerImporter { public: enum Error { NoError = 0, InvalidDocumentError, EmptyDocumentError }; QVersitOrganizerImporter(); explicit QVersitOrganizerImporter(const QString& profile); ~QVersitOrganizerImporter(); bool importDocument(const QVersitDocument& document); QList items() const; QMap errorMap() const; void setPropertyHandler(QVersitOrganizerImporterPropertyHandler* handler); private: QVersitOrganizerImporterPrivate* d; }; QT_END_NAMESPACE_VERSITORGANIZER #endif // QVERSITORGANIZERIMPORTER_H src/versitorganizer/qversitorganizerimporter_p.cpp000066400000000000000000001340071233466112000233300ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersitOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitorganizerimporter_p.h" #include #include #include #include #include "qversitorganizerdefs_p.h" #include "qversitorganizerhandler.h" #include "qversitorganizerpluginloader_p.h" #include "qversittimezonehandler.h" QTORGANIZER_USE_NAMESPACE QTVERSIT_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSITORGANIZER QVersitOrganizerImporterPrivate::QVersitOrganizerImporterPrivate(const QString& profile) : mPropertyHandler(NULL), mTimeZoneHandler(NULL), mDurationSpecified(false) { int versitPropertyCount = sizeof(versitOrganizerDetailMappings)/sizeof(VersitOrganizerDetailMapping); for (int i = 0; i < versitPropertyCount; i++) { mPropertyMappings.insert( QLatin1String(versitOrganizerDetailMappings[i].versitPropertyName), QPair( versitOrganizerDetailMappings[i].detailType, versitOrganizerDetailMappings[i].detailField)); } mPluginPropertyHandlers = QVersitOrganizerPluginLoader::instance()->createOrganizerHandlers(profile); mTimeZoneHandler = QVersitOrganizerPluginLoader::instance()->timeZoneHandler(); } QVersitOrganizerImporterPrivate::~QVersitOrganizerImporterPrivate() { foreach (QVersitOrganizerHandler* pluginHandler, mPluginPropertyHandlers) { delete pluginHandler; } } bool QVersitOrganizerImporterPrivate::importDocument( const QVersitDocument& topLevel, const QVersitDocument& subDocument, QOrganizerItem* item, QVersitOrganizerImporter::Error* error) { if (subDocument.componentType() == QStringLiteral("VEVENT")) { item->setType(QOrganizerItemType::TypeEvent); } else if (subDocument.componentType() == QStringLiteral("VTODO")) { item->setType(QOrganizerItemType::TypeTodo); } else if (subDocument.componentType() == QStringLiteral("VJOURNAL")) { item->setType(QOrganizerItemType::TypeJournal); } else if (subDocument.componentType() == QStringLiteral("VTIMEZONE")) { mTimeZones.addTimeZone(importTimeZone(subDocument)); *error = QVersitOrganizerImporter::NoError; return false; } else { *error = QVersitOrganizerImporter::InvalidDocumentError; return false; } const QList properties = subDocument.properties(); if ( (subDocument.subDocuments().isEmpty()) && (properties.isEmpty())) { *error = QVersitOrganizerImporter::EmptyDocumentError; return false; } foreach (const QVersitProperty& property, properties) { importProperty(subDocument, property, item); } if (!subDocument.subDocuments().isEmpty()) { foreach (const QVersitDocument &nestedSubDoc, subDocument.subDocuments()) foreach (const QVersitProperty &nestedProp, nestedSubDoc.properties()) { importProperty(nestedSubDoc, nestedProp, item); if (nestedSubDoc.componentType() == QStringLiteral("VALARM")) break; } } // run plugin handlers foreach (QVersitOrganizerImporterPropertyHandler* handler, mPluginPropertyHandlers) { handler->subDocumentProcessed(topLevel, subDocument, item); } // run property handlers if (mPropertyHandler) { mPropertyHandler->subDocumentProcessed(topLevel, subDocument, item); } return true; } void QVersitOrganizerImporterPrivate::importProperty( const QVersitDocument& document, const QVersitProperty& property, QOrganizerItem* item) { QList updatedDetails; bool success = false; if (property.name() == QStringLiteral("CREATED")) { success = createTimestampCreated(property, item, &updatedDetails); } else if (property.name() == QStringLiteral("LAST-MODIFIED")) { success = createTimestampModified(property, item, &updatedDetails); } else if (property.name() == QStringLiteral("X-QTPROJECT-VERSION")) { success = createVersion(property, item, &updatedDetails); } else if (property.name() == QStringLiteral("PRIORITY")) { success = createPriority(property, item, &updatedDetails); } else if (property.name() == QStringLiteral("COMMENT")) { success = createComment(property, &updatedDetails); } else if (property.name() == QStringLiteral("X-QTPROJECT-EXTENDED-DETAIL")) { success = createExtendedDetail(property, &updatedDetails); } else if (mPropertyMappings.contains(property.name())) { success = createSimpleDetail(property, item, &updatedDetails); } else if (document.componentType() == QStringLiteral("VEVENT")) { if (property.name() == QStringLiteral("DTSTART")) { success = createStartDateTime(property, item, &updatedDetails); } else if (property.name() == QStringLiteral("DTEND")) { success = createEndDateTime(property, item, &updatedDetails); } else if (property.name() == QStringLiteral("DURATION")) { success = createDuration(property, item, &updatedDetails); } else if (property.name() == QStringLiteral("RRULE") || (property.name() == QStringLiteral("EXRULE"))) { success = createRecurrenceRule(property, item, &updatedDetails); } else if (property.name() == QStringLiteral("RDATE") || (property.name() == QStringLiteral("EXDATE"))) { success = createRecurrenceDates(property, item, &updatedDetails); } else if (property.name() == QStringLiteral("RECURRENCE-ID")) { success = createRecurrenceId(property, item, &updatedDetails); } } else if (document.componentType() == QStringLiteral("VTODO")) { if (property.name() == QStringLiteral("DTSTART")) { success = createTodoStartDateTime(property, item, &updatedDetails); } else if (property.name() == QStringLiteral("DUE")) { success = createDueDateTime(property, item, &updatedDetails); } else if (property.name() == QStringLiteral("RRULE") || (property.name() == QStringLiteral("EXRULE"))) { success = createRecurrenceRule(property, item, &updatedDetails); } else if (property.name() == QStringLiteral("RDATE") || (property.name() == QStringLiteral("EXDATE"))) { success = createRecurrenceDates(property, item, &updatedDetails); } else if (property.name() == QStringLiteral("STATUS")) { success = createStatus(property, item, &updatedDetails); } else if (property.name() == QStringLiteral("PERCENT-COMPLETE")) { success = createPercentageComplete(property, item, &updatedDetails); } else if (property.name() == QStringLiteral("COMPLETED")) { success = createFinishedDateTime(property, item, &updatedDetails); } else if (property.name() == QStringLiteral("RECURRENCE-ID")) { success = createRecurrenceId(property, item, &updatedDetails); } } else if (document.componentType() == QStringLiteral("VALARM")) { success = createItemReminder(document, item, &updatedDetails); } else if (document.componentType() == QStringLiteral("VJOURNAL")) { if (property.name() == QStringLiteral("DTSTART")) { success = createJournalEntryDateTime(property, item, &updatedDetails); } } // run plugin handlers foreach (QVersitOrganizerImporterPropertyHandler* handler, mPluginPropertyHandlers) { handler->propertyProcessed(document, property, *item, &success, &updatedDetails); } // run the handler, if set if (mPropertyHandler) { mPropertyHandler->propertyProcessed(document, property, *item, &success, &updatedDetails); } foreach (QOrganizerItemDetail detail, updatedDetails) { item->saveDetail(&detail); } } bool QVersitOrganizerImporterPrivate::createSimpleDetail( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails) { if (property.value().isEmpty()) return false; QPair mapping = mPropertyMappings[property.name()]; QOrganizerItemDetail::DetailType definitionName = mapping.first; int fieldName = mapping.second; QOrganizerItemDetail detail(item->detail(definitionName)); if (detail.isEmpty()) detail = QOrganizerItemDetail(definitionName); detail.setValue(fieldName, property.value()); updatedDetails->append(detail); return true; } bool QVersitOrganizerImporterPrivate::createTimestampCreated( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails) { if (property.value().isEmpty()) return false; QDateTime datetime = parseDateTime(property); if (!datetime.isValid()) return false; QOrganizerItemTimestamp timestamp(item->detail(QOrganizerItemDetail::TypeTimestamp)); timestamp.setCreated(datetime); updatedDetails->append(timestamp); return true; } bool QVersitOrganizerImporterPrivate::createTimestampModified( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails) { if (property.value().isEmpty()) return false; QDateTime datetime = parseDateTime(property); if (!datetime.isValid()) return false; QOrganizerItemTimestamp timestamp(item->detail(QOrganizerItemDetail::TypeTimestamp)); timestamp.setLastModified(datetime); updatedDetails->append(timestamp); return true; } /*! * Takes the first value in \a list, or an empty QString is if the list is empty. */ QString QVersitOrganizerImporterPrivate::takeFirst(QList& list) const { return list.empty() ? QString() : list.takeFirst(); } bool QVersitOrganizerImporterPrivate::createVersion( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails) { QVariant variant = property.variantValue(); if (property.valueType() != QVersitProperty::CompoundType || variant.type() != QVariant::StringList) { return false; } QStringList values = variant.toStringList(); bool conversionOk; QOrganizerItemVersion version(item->detail(QOrganizerItemDetail::TypeVersion)); version.setVersion(takeFirst(values).toInt(&conversionOk)); QString extendedVersionString = takeFirst(values); if (!extendedVersionString.isEmpty()) version.setExtendedVersion(extendedVersionString.toLocal8Bit()); if (conversionOk) { updatedDetails->append(version); return true; } else { return false; } } bool QVersitOrganizerImporterPrivate::createPriority( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails) { if (property.value().length() != 1) return false; bool ok; int p = property.value().toInt(&ok); if (!ok) return false; QOrganizerItemPriority priority(item->detail(QOrganizerItemDetail::TypePriority)); priority.setPriority(QOrganizerItemPriority::Priority(p)); updatedDetails->append(priority); return true; } bool QVersitOrganizerImporterPrivate::createComment( const QVersitProperty& property, QList* updatedDetails) { if (property.value().isEmpty()) return false; QOrganizerItemComment comment; comment.setComment(property.value()); updatedDetails->append(comment); return true; } bool QVersitOrganizerImporterPrivate::createItemReminder( const QVersitDocument& valarmDocument, QOrganizerItem* item, QList* updatedDetails) { int repetitionCount = 0; int repetitionDelay = 0; int secondsBeforeStart = 0; bool alreadySetSecondsBeforeStart = false; const QList valarmProperties = valarmDocument.properties(); QString actionValue; QVariantList attachValues; QString descriptionValue; QString summaryValue; QStringList attendees; foreach (const QVersitProperty &valarmProperty, valarmProperties) { if (valarmProperty.name() == QStringLiteral("TRIGGER")) { secondsBeforeStart = triggerToSecondsBeforeStart(valarmProperty, *item); alreadySetSecondsBeforeStart = true; } else if (valarmProperty.name() == QStringLiteral("REPEAT")) { repetitionCount = valarmProperty.value().toInt(); } else if (valarmProperty.name() == QStringLiteral("DURATION")) { repetitionDelay = Duration::parseDuration(valarmProperty.value()).toSeconds(); } else if (valarmProperty.name() == QStringLiteral("ACTION")) { actionValue = valarmProperty.value().toUpper(); } else if (valarmProperty.name() == QStringLiteral("ATTACH")) { attachValues.append(valarmProperty.variantValue()); } else if (valarmProperty.name() == QStringLiteral("DESCRIPTION")) { descriptionValue = valarmProperty.value(); } else if (valarmProperty.name() == QStringLiteral("SUMMARY")) { summaryValue = valarmProperty.value(); } else if (valarmProperty.name() == QStringLiteral("ATTENDEE")) { attendees << valarmProperty.value(); } } if ((actionValue.isEmpty()) || (!alreadySetSecondsBeforeStart) ) { //ACTION and TRIGGER are mandatory in a VALARM component. return false; } else if (actionValue == QStringLiteral("AUDIO")) { QOrganizerItemAudibleReminder audibleReminder; audibleReminder.setRepetition(repetitionCount, repetitionDelay); audibleReminder.setSecondsBeforeStart(secondsBeforeStart); if (!attachValues.isEmpty()) audibleReminder.setDataUrl(QUrl(attachValues.first().toString())); updatedDetails->append(audibleReminder); return true; } else if (actionValue == QStringLiteral("DISPLAY")) { if (descriptionValue.isNull()) return false;//Invalid since REQUIRED properties are not found QOrganizerItemVisualReminder visualReminder; visualReminder.setRepetition(repetitionCount, repetitionDelay); visualReminder.setSecondsBeforeStart(secondsBeforeStart); if (!descriptionValue.isEmpty()) { visualReminder.setMessage(descriptionValue); updatedDetails->append(visualReminder); return true; } else { return false; } } else if (actionValue == QStringLiteral("EMAIL")) { if (descriptionValue.isNull() || summaryValue.isNull() || attendees.isEmpty()) return false;//Invalid since REQUIRED properties are not found QOrganizerItemEmailReminder emailReminder; emailReminder.setRepetition(repetitionCount, repetitionDelay); emailReminder.setSecondsBeforeStart(secondsBeforeStart); emailReminder.setValue(QOrganizerItemEmailReminder::FieldBody, descriptionValue); emailReminder.setValue(QOrganizerItemEmailReminder::FieldSubject, summaryValue); if (!attachValues.isEmpty()) emailReminder.setValue(QOrganizerItemEmailReminder::FieldAttachments, attachValues); emailReminder.setContents(summaryValue, descriptionValue, attachValues); emailReminder.setRecipients(attendees); updatedDetails->append(emailReminder); return true; } else { //ACTION property had an invalid value return false; } } int QVersitOrganizerImporterPrivate::triggerToSecondsBeforeStart(const QVersitProperty &triggerProperty, const QOrganizerItem &item) { int result = 0; //The default value type for TRIGGER property is DURATION. bool encodedAsDuration = true; if (!triggerProperty.parameters().isEmpty()) { const QString triggerValue = triggerProperty.parameters().value(QStringLiteral("VALUE")).toUpper(); if (triggerValue == QStringLiteral("DATE-TIME")) encodedAsDuration = false; else if ( (!triggerValue.isEmpty()) && (triggerValue != QStringLiteral("DURATION")) ) { return 0;//Invalid trigger property...just return default value. } } if (encodedAsDuration) { const QString related = triggerProperty.parameters().value(QStringLiteral("RELATED")).toUpper(); result = Duration::parseDuration(triggerProperty.value()).toSeconds(); switch (item.type()) { case QOrganizerItemType::TypeTodo: case QOrganizerItemType::TypeTodoOccurrence: { if (related == QStringLiteral("START")) { QOrganizerTodoTime todoTime = item.detail(QOrganizerItemDetail::TypeTodoTime); QDateTime relativeTrigger = todoTime.startDateTime().addSecs(result); result = relativeTrigger.secsTo(todoTime.dueDateTime()); } else if ( (related.isEmpty()) || (related == QStringLiteral("END")) ) { result = (-1 * result); } break; } case QOrganizerItemType::TypeEvent: case QOrganizerItemType::TypeEventOccurrence: { if (related == QStringLiteral("END")) { QOrganizerEventTime eventTime = item.detail(QOrganizerItemDetail::TypeEventTime); QDateTime relativeTrigger = eventTime.endDateTime().addSecs(result); result = relativeTrigger.secsTo(eventTime.startDateTime()); } else if ( (related.isEmpty()) || (related == QStringLiteral("START")) ) { result = (-1 * result); } break; } default: break; } } else { QDateTime absoluteTrigger = parseDateTime(triggerProperty.value()); switch (item.type()) { case QOrganizerItemType::TypeTodo: case QOrganizerItemType::TypeTodoOccurrence: { QOrganizerTodoTime todoTime = item.detail(QOrganizerItemDetail::TypeTodoTime); result = absoluteTrigger.secsTo(todoTime.dueDateTime()); break; } case QOrganizerItemType::TypeEvent: case QOrganizerItemType::TypeEventOccurrence: { QOrganizerEventTime eventTime = item.detail(QOrganizerItemDetail::TypeEventTime); result = absoluteTrigger.secsTo(eventTime.startDateTime()); break; } default: break; } } return result >= 0 ? result: 0; } bool QVersitOrganizerImporterPrivate::createExtendedDetail( const QVersitProperty &property, QList *updatedDetails) { QOrganizerItemExtendedDetail extendedDetail; const QVariant variant = property.variantValue(); if (property.valueType() != QVersitProperty::CompoundType || variant.type() != QVariant::StringList) return false; QStringList values = variant.toStringList(); extendedDetail.setName(takeFirst(values)); QVariant data; if (VersitUtils::convertFromJson(takeFirst(values), &data)) extendedDetail.setData(data); else return false; updatedDetails->append(extendedDetail); return true; } bool QVersitOrganizerImporterPrivate::createRecurrenceId( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails) { QDate date = parseDate(property.value()); if (!date.isValid()) return false; QOrganizerItemParent origin(item->detail(QOrganizerItemDetail::TypeParent)); origin.setOriginalDate(date); updatedDetails->append(origin); item->setType(QOrganizerItemType::TypeEventOccurrence); return true; } /*! Set the startDateTime field of the EventTimeRange detail. If the end date has been set from a * DURATION, it will be updated. */ bool QVersitOrganizerImporterPrivate::createStartDateTime( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails) { if (property.value().isEmpty()) return false; bool hasTime; QDateTime newStart = parseDateTime(property, &hasTime); if (!newStart.isValid()) return false; QOrganizerEventTime etr(item->detail(QOrganizerItemDetail::TypeEventTime)); if (mDurationSpecified) { // Need to fix up the end date to match the duration of the event QDateTime start = etr.startDateTime(); QDateTime end = etr.endDateTime(); if (!start.isValid()) { // not having a start date set is treated as a start date of epoch start = QDateTime(QDate(1970, 1, 1)); } // newEnd = end + (newStart - start) int durationDays = start.daysTo(newStart); QDateTime newEnd = end.addDays(durationDays); int durationSecs = start.addDays(durationDays).secsTo(newStart); newEnd = newEnd.addSecs(durationSecs); etr.setEndDateTime(newEnd); } etr.setStartDateTime(newStart); if (!etr.isAllDay() && !hasTime) etr.setAllDay(true); updatedDetails->append(etr); return true; } /*! Set the endDateTime field of the EventTimeRange detail. */ bool QVersitOrganizerImporterPrivate::createEndDateTime( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails) { if (property.value().isEmpty()) return false; bool hasTime; QDateTime newEnd = parseDateTime(property, &hasTime); if (!newEnd.isValid()) return false; QOrganizerEventTime etr(item->detail(QOrganizerItemDetail::TypeEventTime)); if (!etr.isAllDay() && !hasTime) etr.setAllDay(true); // In iCalendar, the end date is exclusive while in Qt Organizer, it is inclusive. if (etr.isAllDay()) etr.setEndDateTime(newEnd.addDays(-1)); else etr.setEndDateTime(newEnd); updatedDetails->append(etr); mDurationSpecified = false; return true; } /*! Sets the endDateTime field of the EventTimeRange detail using a DURATION property. */ bool QVersitOrganizerImporterPrivate::createDuration( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails) { if (property.value().isEmpty()) return false; Duration duration = Duration::parseDuration(property.value()); if (!duration.isValid()) return false; QOrganizerEventTime etr(item->detail(QOrganizerItemDetail::TypeEventTime)); QDateTime startTime = etr.startDateTime(); if (!startTime.isValid()) { // not having a start date set is treated as a start date of epoch startTime = QDateTime(QDate(1970, 1, 1)); } etr.setEndDateTime( startTime.addDays(7*duration.weeks() + duration.days()) .addSecs(3600*duration.hours() + 60*duration.minutes() + duration.seconds())); updatedDetails->append(etr); mDurationSpecified = true; return true; } /*! Set the StartDateTime field of the TodoTimeRange detail. */ bool QVersitOrganizerImporterPrivate::createTodoStartDateTime( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails) { if (property.value().isEmpty()) return false; bool hasTime; QDateTime newStart = parseDateTime(property, &hasTime); if (!newStart.isValid()) return false; QOrganizerTodoTime ttr(item->detail(QOrganizerItemDetail::TypeTodoTime)); ttr.setStartDateTime(newStart); if (!ttr.isAllDay() && !hasTime) ttr.setAllDay(true); updatedDetails->append(ttr); return true; } /*! Set the DueDateTime field of the TodoTimeRange detail. */ bool QVersitOrganizerImporterPrivate::createDueDateTime( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails) { if (property.value().isEmpty()) return false; bool hasTime; QDateTime newEnd = parseDateTime(property, &hasTime); if (!newEnd.isValid()) return false; QOrganizerTodoTime ttr(item->detail(QOrganizerItemDetail::TypeTodoTime)); ttr.setDueDateTime(newEnd); if (!ttr.isAllDay() && !hasTime) ttr.setAllDay(true); updatedDetails->append(ttr); mDurationSpecified = false; return true; } /*! Set the EntryDateTime field of the JournalTimeRange detail. */ bool QVersitOrganizerImporterPrivate::createJournalEntryDateTime( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails) { if (property.value().isEmpty()) return false; QDateTime dateTime = parseDateTime(property); if (!dateTime.isValid()) return false; QOrganizerJournalTime jtr(item->detail(QOrganizerItemDetail::TypeJournalTime)); jtr.setEntryDateTime(dateTime); updatedDetails->append(jtr); return true; } /*! Parses a datetime stored in the \a property as an ISO 8601 datetime in basic format, either in * UTC time zone, floating time zone, or (if a TZID parameter exists in \a property), as a foreign * time zone (returned as a UTC datetime). Returns an invalid QDateTime if the string cannot be * parsed. * * \a hasTime is set to true if the parsed date-time has a time, or false if it is a date only. * (the time portion is set to some valid but arbitrary value). */ QDateTime QVersitOrganizerImporterPrivate::parseDateTime(const QVersitProperty& property, bool* hasTime) const { const QMultiHash parameters = property.parameters(); if (parameters.find(QStringLiteral("VALUE"), QStringLiteral("DATE")) == parameters.constEnd()) { // try parsing a datetime if (hasTime) *hasTime = true; QDateTime datetime(parseDateTime(property.value())); if (datetime.isValid() && datetime.timeSpec() == Qt::LocalTime) { QMultiHash params = property.parameters(); QString tzid = params.value(QStringLiteral("TZID")); if (!tzid.isEmpty()) { if (tzid.at(0) == QLatin1Char('/') && mTimeZoneHandler) datetime = mTimeZoneHandler->convertTimeZoneToUtc(datetime, tzid); else datetime = mTimeZones.convert(datetime, tzid); } } return datetime; } else { if (hasTime) *hasTime = false; QDateTime retn; retn.setDate(QDate::fromString(property.value(), QStringLiteral("yyyyMMdd"))); retn.setTime(QTime(0, 0, 0)); return retn; } } /*! Parses \a str as an ISO 8601 datetime in basic format, either in UTC timezone or floating * timezone. Returns an invalid QDateTime if the string cannot be parsed. */ QDateTime QVersitOrganizerImporterPrivate::parseDateTime(QString str) const { bool utc = str.endsWith(QLatin1Char('Z'), Qt::CaseInsensitive); if (utc) str.chop(1); // take away z from end; QDateTime dt(QDateTime::fromString(str, QStringLiteral("yyyyMMddTHHmmss"))); if (utc) dt.setTimeSpec(Qt::UTC); return dt; } /*! * Imports a RRULE, EXRULE, RDATE or EXDATE property */ bool QVersitOrganizerImporterPrivate::createRecurrenceRule( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails) { if (property.value().isEmpty()) return false; QOrganizerRecurrenceRule rule; if (!parseRecurRule(property.value(), &rule)) return false; QOrganizerItemRecurrence detail(item->detail(QOrganizerItemDetail::TypeRecurrence)); if (property.name() == QStringLiteral("RRULE")) { detail.setRecurrenceRules(detail.recurrenceRules() << rule); } else if (property.name() == QStringLiteral("EXRULE")) { detail.setExceptionRules(detail.exceptionRules() << rule); } updatedDetails->append(detail); return true; } /*! * Parses an iCalendar recurrence rule string \a str and puts the result in \a rule. * Return true on success, false on failure. */ bool QVersitOrganizerImporterPrivate::parseRecurRule(const QString& str, QOrganizerRecurrenceRule* rule) const { QStringList parts = str.split(QLatin1Char(';')); if (parts.size() == 0) return false; QString freqPart = parts.takeFirst(); QStringList freqParts = freqPart.split(QLatin1Char('=')); if (freqParts.size() != 2) return false; if (freqParts.at(0) != QStringLiteral("FREQ")) return false; QString freqValue = freqParts.at(1); if (freqValue == QStringLiteral("DAILY")) { rule->setFrequency(QOrganizerRecurrenceRule::Daily); } else if (freqValue == QStringLiteral("WEEKLY")) { rule->setFrequency(QOrganizerRecurrenceRule::Weekly); } else if (freqValue == QStringLiteral("MONTHLY")) { rule->setFrequency(QOrganizerRecurrenceRule::Monthly); } else if (freqValue == QStringLiteral("YEARLY")) { rule->setFrequency(QOrganizerRecurrenceRule::Yearly); } else { return false; } foreach (const QString& part, parts) { QStringList keyValue = part.split(QLatin1Char('=')); if (keyValue.size() != 2) return false; parseRecurFragment(keyValue.at(0), keyValue.at(1), rule); } return true; } /*! * Parses a fragment of an iCalendar string (the part between the semicolons) and updates \a rule. * \a key is the part of the fragment before the equals sign and \a value is the part after. */ void QVersitOrganizerImporterPrivate::parseRecurFragment(const QString& key, const QString& value, QOrganizerRecurrenceRule* rule) const { if (key == QStringLiteral("INTERVAL")) { bool ok; int n = value.toInt(&ok); if (ok && n >= 1) rule->setInterval(n); } else if (key == QStringLiteral("COUNT")) { bool ok; int count = value.toInt(&ok); if (ok && count >= 0) { rule->setLimit(count); } } else if (key == QStringLiteral("UNTIL")) { QDate date; if (value.contains(QLatin1Char('T'))) { QDateTime dt = parseDateTime(value); date = dt.date(); } else { date = QDate::fromString(value, QStringLiteral("yyyyMMdd")); } if (date.isValid()) rule->setLimit(date); } else if (key == QStringLiteral("BYDAY")) { QSet days; QStringList dayParts = value.split(QLatin1Char(',')); foreach (QString dayStr, dayParts) { if (dayStr.length() < 2) { // bad day specifier continue; } else if (dayStr.length() > 2) { // parse something like -2SU, meaning the second-last Sunday QString posStr = dayStr; dayStr = dayStr.right(2); // dayStr = last two chars posStr.chop(2); // posStr = all except last two chars bool ok; int pos = posStr.toInt(&ok); if (!ok) continue; rule->setPositions(QSet() << pos); } int day = parseDayOfWeek(dayStr); if (day != -1) { days << (Qt::DayOfWeek)day; } } if (!days.isEmpty()) { rule->setDaysOfWeek(days); } } else if (key == QStringLiteral("BYMONTHDAY")) { QSet days = parseInts(value, -31, 31); if (!days.isEmpty()) { rule->setDaysOfMonth(days); } } else if (key == QStringLiteral("BYWEEKNO")) { QSet weeks = parseInts(value, -53, 53); if (!weeks.isEmpty()) { rule->setWeeksOfYear(weeks); } } else if (key == QStringLiteral("BYMONTH")) { QSet months; QStringList monthParts = value.split(QLatin1Char(',')); foreach (const QString& monthPart, monthParts) { bool ok; int month = monthPart.toInt(&ok); if (ok && month >= 1 && month <= 12) { months << (QOrganizerRecurrenceRule::Month)month; } } if (!months.isEmpty()) { rule->setMonthsOfYear(months); } } else if (key == QStringLiteral("BYYEARDAY")) { QSet days = parseInts(value, -366, 366); if (!days.isEmpty()) { rule->setDaysOfYear(days); } } else if (key == QStringLiteral("BYSETPOS")) { QSet poss = parseInts(value, -366, 366); if (!poss.isEmpty()) { rule->setPositions(poss); } } else if (key == QStringLiteral("WKST")) { int day = parseDayOfWeek(value); if (day != -1) { rule->setFirstDayOfWeek((Qt::DayOfWeek)day); } } } /*! * Parses and returns a comma-separated list of integers. Only non-zero values between \a min and * \a max (inclusive) are added */ QSet QVersitOrganizerImporterPrivate::parseInts(const QString& str, int min, int max) const { QSet values; QStringList parts = str.split(QLatin1Char(',')); foreach (const QString& part, parts) { bool ok; int value = part.toInt(&ok); if (ok && value >= min && value <= max && value != 0) { values << value; } } return values; } /*! * Parses an iCalendar two-character string representing a day of week and returns an int * corresponding to Qt::DayOfWeek. Returns -1 on parse failure. */ int QVersitOrganizerImporterPrivate::parseDayOfWeek(const QString& str) const { if (str == QStringLiteral("MO")) { return Qt::Monday; } else if (str == QStringLiteral("TU")) { return Qt::Tuesday; } else if (str == QStringLiteral("WE")) { return Qt::Wednesday; } else if (str == QStringLiteral("TH")) { return Qt::Thursday; } else if (str == QStringLiteral("FR")) { return Qt::Friday; } else if (str == QStringLiteral("SA")) { return Qt::Saturday; } else if (str == QStringLiteral("SU")) { return Qt::Sunday; } else { return -1; } } /*! * Parses an iCalendar RDATE or EXDATE property and updates the recurrenceDates or * exceptionDates in the Recurrence detail. */ bool QVersitOrganizerImporterPrivate::createRecurrenceDates( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails) { if (property.value().isEmpty()) return false; QSet dates; if (!parseDates(property.value(), &dates)) return false; QOrganizerItemRecurrence detail(item->detail(QOrganizerItemDetail::TypeRecurrence)); if (property.name() == QStringLiteral("RDATE")) { detail.setRecurrenceDates(detail.recurrenceDates() + dates); } else if (property.name() == QStringLiteral("EXDATE")) { detail.setExceptionDates(detail.exceptionDates() + dates); } updatedDetails->append(detail); return true; } /*! * Parses a string like "19970304,19970504,19970704" into a list of QDates */ bool QVersitOrganizerImporterPrivate::parseDates(const QString& str, QSet* dates) const { QStringList parts = str.split(QLatin1Char(',')); if (parts.size() == 0) return false; foreach (QString part, parts) { QDate date = parseDate(part); if (date.isValid()) *dates << date; else return false; } return true; } /*! * Parses a date in either yyyyMMdd or yyyyMMddTHHmmss format (in the latter case, ignoring the * time) */ QDate QVersitOrganizerImporterPrivate::parseDate(QString str) const { int tIndex = str.indexOf(QLatin1Char('T')); if (tIndex >= 0) { str = str.left(tIndex); } return QDate::fromString(str, QStringLiteral("yyyyMMdd")); } bool QVersitOrganizerImporterPrivate::createStatus( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails) { QOrganizerTodoProgress::Status status; if (property.value() == QStringLiteral("COMPLETED")) status = QOrganizerTodoProgress::StatusComplete; else if (property.value() == QStringLiteral("NEEDS-ACTION")) status = QOrganizerTodoProgress::StatusNotStarted; else if (property.value() == QStringLiteral("IN-PROCESS")) status = QOrganizerTodoProgress::StatusInProgress; else return false; QOrganizerTodoProgress progress(item->detail(QOrganizerItemDetail::TypeTodoProgress)); progress.setStatus(status); updatedDetails->append(progress); return true; } bool QVersitOrganizerImporterPrivate::createPercentageComplete( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails) { bool ok = false; int percent = property.value().toInt(&ok); if (!ok) return false; QOrganizerTodoProgress progress(item->detail(QOrganizerItemDetail::TypeTodoProgress)); progress.setPercentageComplete(percent); updatedDetails->append(progress); return true; } bool QVersitOrganizerImporterPrivate::createFinishedDateTime( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails) { if (property.value().isEmpty()) return false; QDateTime datetime = parseDateTime(property); if (!datetime.isValid()) return false; QOrganizerTodoProgress progress(item->detail(QOrganizerItemDetail::TypeTodoProgress)); progress.setFinishedDateTime(datetime); updatedDetails->append(progress); return true; } /*! Parse the iCalendar duration string \a str in an RDP fashion with a two symbol lookahead, and * returns a Duration that represents it. */ Duration Duration::parseDuration(QString str) { QString token = nextToken(&str); if (token.isEmpty()) return invalidDuration(); Duration dur; // Accept a + or - if present if (token == QStringLiteral("+")) { token = nextToken(&str); } else if (token == QStringLiteral("-")) { dur.setNegative(true); token = nextToken(&str); } else if (token.isEmpty()) { return invalidDuration(); } else { // There was no + or - so keep parsing } // Accept a P if (token != QStringLiteral("P")) { return invalidDuration(); } token = nextToken(&str); if (token.isEmpty()) { return invalidDuration(); } else if (token == QStringLiteral("T")) { // we see a time parseDurationTime(&str, &dur); } else if (token.at(0).isDigit()) { // it's either a date or a week - we're not sure yet int value = token.toInt(); // always succeeds because nextToken next returns a mix of digits/nondigits token = nextToken(&str); if (token == QStringLiteral("D")) { // it's a date dur.setDays(value); token = nextToken(&str); // dates optionally define a time if (token == QStringLiteral("T")) parseDurationTime(&str, &dur); } else if (token == QStringLiteral("W")) { dur.setWeeks(value); } else { return invalidDuration(); } } else { return invalidDuration(); } // check that there aren't extra characters on the end if (!str.isEmpty()) dur.setValid(false); return dur; } /*! Parse a duration string starting from after the "T" character. Removes parsed part from \a str * and updates \a dur with the findings. */ void Duration::parseDurationTime(QString* str, Duration* dur) { QString token = nextToken(str); if (token.isEmpty() || !token.at(0).isDigit()) dur->setValid(false); int value = token.toInt(); // always succeeds token = nextToken(str); if (token == QStringLiteral("H")) { dur->setHours(value); if (!str->isEmpty()) parseDurationMinutes(str, dur); } else if (token == QStringLiteral("M")) { dur->setMinutes(value); if (!str->isEmpty()) parseDurationSeconds(str, dur); } else if (token == QStringLiteral("S")) { dur->setSeconds(value); } } /*! Parse a duration string starting from the part describing the number of minutes. Removes parsed * part from \a str and updates \a dur with the findings. */ void Duration::parseDurationMinutes(QString* str, Duration* dur) { QString token = nextToken(str); if (token.isEmpty() || !token.at(0).isDigit()) dur->setValid(false); int value = token.toInt(); // always succeeds token = nextToken(str); if (token != QStringLiteral("M")) { dur->setValid(false); return; } dur->setMinutes(value); if (!str->isEmpty()) parseDurationSeconds(str, dur); } /*! Parse a duration string starting from the part describing the number of seconds. Removes parsed * part from \a str and updates \a dur with the findings. */ void Duration::parseDurationSeconds(QString* str, Duration* dur) { QString token = nextToken(str); if (token.isEmpty() || !token.at(0).isDigit()) dur->setValid(false); int value = token.toInt(); // always succeeds token = nextToken(str); if (token != QStringLiteral("S")) { dur->setValid(false); return; } dur->setSeconds(value); } /*! Removes and returns a "token" from the start of an iCalendar DURATION string, \a str. A token * is either a single +, - or upper-case letter, or a string of digits. If \a str is empty, an * empty string is returned. If \a str is not empty but starts with an invalid character, a null * string is returned. */ QString Duration::nextToken(QString* str) { int len = str->length(); if (len == 0) return QString::fromLatin1(""); // empty (not null) QString QChar first = str->at(0); if (first == QLatin1Char('+') || first == QLatin1Char('-') || first.isUpper()) { QString ret(str->left(1)); *str = str->mid(1); return ret; } else if (first.isDigit()) { // find the largest n such that the leftmost n characters are digits int n = 1; for (n = 1; n < len && str->at(n).isDigit(); n++) { } QString ret = str->left(n); *str = str->mid(n); return ret; } else { return QString(); // null QString } } TimeZone QVersitOrganizerImporterPrivate::importTimeZone(const QVersitDocument& document) const { TimeZone timeZone; foreach (const QVersitProperty& property, document.properties()) { if (property.name() == QStringLiteral("TZID") && !property.value().isEmpty()) { timeZone.setTzid(property.value()); } } foreach (const QVersitDocument& subDocument, document.subDocuments()) { timeZone.addPhase(importTimeZonePhase(subDocument)); } return timeZone; } TimeZonePhase QVersitOrganizerImporterPrivate::importTimeZonePhase(const QVersitDocument& document) const { TimeZonePhase phase; phase.setStandard(document.componentType() == QStringLiteral("STANDARD")); foreach (const QVersitProperty& property, document.properties()) { if (property.name() == QStringLiteral("TZOFFSETTO")) { QString value(property.value()); if (value.size() == 5 && (value.at(0) == QLatin1Char('+') || value.at(0) == QLatin1Char('-')) && value.at(1).isDigit() && value.at(2).isDigit() && value.at(3).isDigit() && value.at(4).isDigit()) { phase.setUtcOffset((value.at(0) == QLatin1Char('+') ? 1 : -1) * ( // deal with sign value.mid(1, 2).toInt() * 3600 // hours part + value.mid(3, 2).toInt() * 60 // minutes part )); } } else if (property.name() == QStringLiteral("DTSTART")) { QDateTime dt(parseDateTime(property.value())); if (dt.isValid() && dt.timeSpec() == Qt::LocalTime) { phase.setStartDateTime(dt); } } else if (property.name() == QStringLiteral("RRULE")) { QOrganizerRecurrenceRule rrule; if (parseRecurRule(property.value(), &rrule)) { phase.setRecurrenceRule(rrule); } } else if (property.name() == QStringLiteral("RDATE")) { QSet dates; if (parseDates(property.value(), &dates)) { phase.setRecurrenceDates(dates); } } } return phase; } QT_END_NAMESPACE_VERSITORGANIZER src/versitorganizer/qversitorganizerimporter_p.h000066400000000000000000000232611233466112000227740ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersitOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITORGANIZERIMPORTER_P_H #define QVERSITORGANIZERIMPORTER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include QT_BEGIN_NAMESPACE_ORGANIZER class QOrganizerRecurrenceRule; QT_END_NAMESPACE_ORGANIZER QTORGANIZER_USE_NAMESPACE QT_BEGIN_NAMESPACE_VERSITORGANIZER class QVersitOrganizerHandler; class QVersitTimeZoneHandler; class Duration { public: Duration() : mNegative(false), mWeeks(0), mDays(0), mHours(0), mMinutes(0), mSeconds(0), mValid(true) {} static Duration invalidDuration() { Duration d; d.setValid(false); return d; } static Duration parseDuration(QString str); static void parseDurationTime(QString* str, Duration* dur); static void parseDurationMinutes(QString* str, Duration* dur); static void parseDurationSeconds(QString* str, Duration* dur); static QString nextToken(QString* str); void setNegative(bool neg) { mNegative = neg; } void setWeeks(int weeks) { mWeeks = weeks; } void setDays(int days) { mDays = days; } void setHours(int hours) { mHours = hours; } void setMinutes(int minutes) { mMinutes = minutes; } void setSeconds(int seconds) { mSeconds = seconds; } void setValid(bool val) { mValid = val; } int weeks() { return mNegative ? -mWeeks : mWeeks; } int days() { return mNegative ? -mDays : mDays; } int hours() { return mNegative ? -mHours : mHours; } int minutes() { return mNegative ? -mMinutes : mMinutes; } int seconds() { return mNegative ? -mSeconds : mSeconds; } bool isValid() { return mValid; } int toSeconds() { return (seconds() + 60*minutes() + 3600*hours() + 86400*days() + 604800*weeks()); } private: bool mNegative; int mWeeks; int mDays; int mHours; int mMinutes; int mSeconds; bool mValid; }; class QVersitOrganizerImporterPrivate { public: QVersitOrganizerImporterPrivate(const QString& profile = QString()); ~QVersitOrganizerImporterPrivate(); bool importDocument(const QVersitDocument& topLevel, const QVersitDocument& subDocument, QOrganizerItem* item, QVersitOrganizerImporter::Error* error); void importProperty(const QVersitDocument& document, const QVersitProperty& property, QOrganizerItem* item); QList mItems; QMap mErrors; QVersitOrganizerImporterPropertyHandler* mPropertyHandler; QList mPluginPropertyHandlers; QVersitTimeZoneHandler* mTimeZoneHandler; bool mDurationSpecified; // true iff a valid DURATION property has been seen in the current // document with no subsequent DTEND property private: bool createSimpleDetail( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails); bool createTimestampCreated( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails); bool createTimestampModified( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails); bool createVersion( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails); bool createPriority( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails); bool createComment( const QVersitProperty& property, QList* updatedDetails); bool createItemReminder( const QVersitDocument &valarmDocument, QOrganizerItem *item, QList *updatedDetails); int triggerToSecondsBeforeStart(const QVersitProperty& triggerProperty, const QOrganizerItem &item); bool createExtendedDetail( const QVersitProperty &property, QList *updatedDetails); bool createRecurrenceId( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails); bool createStartDateTime( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails); bool createEndDateTime( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails); bool createDuration( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails); bool createTodoStartDateTime( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails); bool createDueDateTime( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails); bool createJournalEntryDateTime( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails); QDateTime parseDateTime(const QVersitProperty& property, bool* hasTime = 0) const; QDateTime parseDateTime(QString str) const; bool createRecurrenceRule( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails); bool parseRecurRule(const QString& str, QOrganizerRecurrenceRule* rule) const; void parseRecurFragment(const QString& key, const QString& value, QOrganizerRecurrenceRule* rule) const; QSet parseInts(const QString& str, int min, int max) const; int parseDayOfWeek(const QString& str) const; bool createRecurrenceDates( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails); bool parseDates(const QString& str, QSet* dates) const; QDate parseDate(QString str) const; bool createStatus( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails); bool createPercentageComplete( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails); bool createFinishedDateTime( const QVersitProperty& property, QOrganizerItem* item, QList* updatedDetails); TimeZone importTimeZone(const QVersitDocument& document) const; TimeZonePhase importTimeZonePhase(const QVersitDocument& document) const; QString takeFirst(QList& list) const; // versit property name -> : QMap > mPropertyMappings; TimeZones mTimeZones; }; QT_END_NAMESPACE_VERSITORGANIZER #endif // QVERSITORGANIZERIMPORTER_P_H src/versitorganizer/qversitorganizerpluginloader_p.cpp000066400000000000000000000126261233466112000241560ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersitOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qversitorganizerpluginloader_p.h" #include "qversitorganizerpluginsearch_p.h" #include #include "qversitorganizerhandler.h" #include "qversittimezonehandler.h" QT_BEGIN_NAMESPACE_VERSITORGANIZER /*! A less-than function for factory indices (see QVersitOrganizerHandlerFactory::index()). Positive values come first (ascendingly), then zero, then negative values (ascendingly). */ bool factoryLessThan(QVersitOrganizerHandlerFactory* a, QVersitOrganizerHandlerFactory* b) { if ((a->index() > 0 && b->index() > 0) || (a->index() < 0 && b->index() < 0)) // same sign return a->index() < b->index(); else // a is zero // or b is zero // or opposite sign return b->index() < a->index(); } QVersitOrganizerPluginLoader* QVersitOrganizerPluginLoader::mInstance = NULL; /*! * \class QVersitOrganizerPluginLoader * \internal * This is a singleton class that loads Versit plugins for organizer item processing */ QVersitOrganizerPluginLoader::QVersitOrganizerPluginLoader() : mTimeZoneHandler(NULL) { } /*! * Returns the singleton instance of the QVersitOrganizerPluginLoader. */ QVersitOrganizerPluginLoader* QVersitOrganizerPluginLoader::instance() { if (!mInstance) mInstance = new QVersitOrganizerPluginLoader; return mInstance; } void QVersitOrganizerPluginLoader::loadPlugins() { QStringList plugins = mobilityPlugins(QStringLiteral("versit")); if (plugins != mPluginPaths) { mPluginPaths = plugins; foreach (const QString& pluginPath, mPluginPaths) { QPluginLoader qpl(pluginPath); QObject* plugin = qpl.instance(); QVersitOrganizerHandlerFactory* organizerPlugin = qobject_cast(plugin); if (organizerPlugin && !mLoadedFactories.contains(organizerPlugin->name())) { mLoadedFactories.insert(organizerPlugin->name()); mOrganizerHandlerFactories.append(organizerPlugin); } else if (!mTimeZoneHandler) { QVersitTimeZoneHandler* timeZonePlugin = qobject_cast(plugin); if (timeZonePlugin) { mTimeZoneHandler = timeZonePlugin; } } } std::sort(mOrganizerHandlerFactories.begin(), mOrganizerHandlerFactories.end(), factoryLessThan); } } /*! * Creates and returns handlers from the plugin. If \a profile is the empty string, only handlers * with an empty profile list are returned. If \a profile is nonempty, only handlers with either * an empty profile list or a profile list that contains the given \a profile are returned. * * The caller is responsible for deleting all returned handlers. */ QList QVersitOrganizerPluginLoader::createOrganizerHandlers(const QString& profile) { loadPlugins(); QList handlers; foreach (const QVersitOrganizerHandlerFactory* factory, mOrganizerHandlerFactories) { if (factory->profiles().isEmpty() || (!profile.isEmpty() && factory->profiles().contains(profile))) { QVersitOrganizerHandler* handler = factory->createHandler(); handlers.append(handler); } } return handlers; } QVersitTimeZoneHandler* QVersitOrganizerPluginLoader::timeZoneHandler() { loadPlugins(); return mTimeZoneHandler; } QT_END_NAMESPACE_VERSITORGANIZER src/versitorganizer/qversitorganizerpluginloader_p.h000066400000000000000000000062451233466112000236230ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersitOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITORGANIZERPLUGINLOADER_P_H #define QVERSITORGANIZERPLUGINLOADER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include QT_BEGIN_NAMESPACE_VERSITORGANIZER class QVersitOrganizerHandler; class QVersitOrganizerHandlerFactory; class QVersitTimeZoneHandler; class QVersitOrganizerPluginLoader { private: QVersitOrganizerPluginLoader(); public: static QVersitOrganizerPluginLoader* instance(); QList createOrganizerHandlers(const QString& profile); QVersitTimeZoneHandler* timeZoneHandler(); private: void loadPlugins(); static QVersitOrganizerPluginLoader* mInstance; QSet mLoadedFactories; QList mOrganizerHandlerFactories; QVersitTimeZoneHandler* mTimeZoneHandler; QStringList mPluginPaths; }; QT_END_NAMESPACE_VERSITORGANIZER #endif // QVERSITORGANIZERPLUGINLOADER_P_H src/versitorganizer/qversitorganizerpluginsearch_p.h000066400000000000000000000123261233466112000236170ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersitOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITORGANIZERPLUGINSEARCH_H #define QVERSITORGANIZERPLUGINSEARCH_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #if !defined QT_NO_DEBUG #include #endif #include #include #include QT_BEGIN_NAMESPACE_VERSITORGANIZER #define CHECKDIR(dir) (dir).exists() inline QStringList mobilityPlugins(const QString& plugintype) { #if !defined QT_NO_DEBUG const bool showDebug = qgetenv("QT_DEBUG_PLUGINS").toInt() > 0; #endif QStringList paths = QCoreApplication::libraryPaths(); #ifdef QTM_PLUGIN_PATH paths << QLatin1String(QTM_PLUGIN_PATH); #endif #if !defined QT_NO_DEBUG if (showDebug) qDebug() << "Plugin paths:" << paths; #endif // Temp variable to avoid multiple identical paths // (we don't convert the list to set first, because that loses the order) QSet processed; /* The list of discovered plugins */ QStringList plugins; /* Enumerate our plugin paths */ for (int i=0; i < paths.count(); i++) { if (processed.contains(paths.at(i))) continue; processed.insert(paths.at(i)); QDir pluginsDir(paths.at(i)); if (!CHECKDIR(pluginsDir)) continue; #if defined(Q_OS_WIN) if (pluginsDir.dirName().toLower() == QLatin1String("debug") || pluginsDir.dirName().toLower() == QLatin1String("release")) pluginsDir.cdUp(); #elif defined(Q_OS_MAC) if (pluginsDir.dirName() == QLatin1String("MacOS")) { pluginsDir.cdUp(); pluginsDir.cdUp(); pluginsDir.cdUp(); } #endif QString subdir(QStringLiteral("plugins/")); subdir += plugintype; if (pluginsDir.path().endsWith(QStringLiteral("/plugins")) || pluginsDir.path().endsWith(QStringLiteral("/plugins/"))) subdir = plugintype; if (CHECKDIR(QDir(pluginsDir.filePath(subdir)))) { pluginsDir.cd(subdir); QStringList files = pluginsDir.entryList(QDir::Files); #if !defined QT_NO_DEBUG if (showDebug) qDebug() << "Looking for " << plugintype << " plugins in" << pluginsDir.path() << files; #endif for (int j=0; j < files.count(); j++) { plugins << pluginsDir.absoluteFilePath(files.at(j)); } } } /* Add application path + plugintype */ QDir appldir(QCoreApplication::applicationDirPath()); if(appldir.cd(plugintype)){ if (!processed.contains(appldir.absolutePath())){ processed.insert(appldir.absolutePath()); QStringList files = appldir.entryList(QDir::Files); #if !defined QT_NO_DEBUG if (showDebug) qDebug() << "Looking for " << plugintype << " plugins in" << appldir.path() << files; #endif for (int j=0; j < files.count(); j++) { plugins << appldir.absoluteFilePath(files.at(j)); } } } return plugins; } QT_END_NAMESPACE_VERSITORGANIZER #endif // QVERSITORGANIZERPLUGINSEARCH_H src/versitorganizer/qversittimezonehandler.h000066400000000000000000000101671233466112000220640ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtVersitOrganizer module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVERSITTIMEZONEHANDLER_H #define QVERSITTIMEZONEHANDLER_H #include #include QT_BEGIN_NAMESPACE_VERSITORGANIZER class Q_VERSIT_ORGANIZER_EXPORT QVersitTimeZoneHandler { public: virtual ~QVersitTimeZoneHandler() {} /*! \fn QDateTime QVersitTimeZoneHandler::convertTimeZoneToUtc(const QDateTime& datetime, const QString& timeZoneName); Converts \a datetime to the local time for the time zone identified by \a timeZoneName. The UTC offset of the time zone should be calculated taking into account the daylight savings rules that might apply at \a datetime. \a datetime must be specified with Qt::UTC. The returned QDateTime will be specified with Qt::LocalTime. Note that unlike the usual semantic of Qt::LocalTime, it does not represent a time in the system's local time zone. An invalid QDateTime is returned on failure (eg. if \a timeZoneName is unknown). */ virtual QDateTime convertTimeZoneToUtc(const QDateTime& datetime, const QString& timeZoneName) = 0; /*! \fn QDateTime QVersitTimeZoneHandler::convertUtcToTimeZone(const QDateTime& datetime, const QString& timeZoneName); Converts \a datetime to UTC, treating \a datetime as if it were a local time for a time zone identified by \a timeZoneName. The UTC offset of the time zone should be calculated taking into account the daylight savings rules that might apply at \a datetime. \a datetime must be specified with Qt::LocalTime. Note that unlike the usual semantic of Qt::LocalTime, \a datetime does not represent a time in the system's local time zone. The returned QDateTime will be specified with Qt::UTC. An invalid QDateTime is returned on failure (eg. if \a timeZoneName is unknown). */ virtual QDateTime convertUtcToTimeZone(const QDateTime& datetime, const QString& timeZoneName) = 0; }; QT_END_NAMESPACE_VERSITORGANIZER #define QT_VERSIT_TIMEZONE_HANDLER_INTERFACE "org.qt-project.Qt.QVersitTimeZoneHandler" QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(QtVersitOrganizer::QVersitTimeZoneHandler, QT_VERSIT_TIMEZONE_HANDLER_INTERFACE) QT_END_NAMESPACE #endif src/versitorganizer/versitorganizer.pro000066400000000000000000000014131233466112000210560ustar00rootroot00000000000000TARGET = QtVersitOrganizer QT = core versit-private organizer load(qt_module) PUBLIC_HEADERS += \ qversitorganizerglobal.h \ qversitorganizerhandler.h \ qversitorganizerexporter.h \ qversitorganizerimporter.h \ qversittimezonehandler.h PRIVATE_HEADERS += \ qversitorganizerexporter_p.h \ qversitorganizerimporter_p.h \ qversitorganizerdefs_p.h \ qversitorganizerpluginloader_p.h \ qtimezones_p.h \ qversitorganizerpluginsearch_p.h SOURCES += \ qversitorganizerexporter.cpp \ qversitorganizerexporter_p.cpp \ qversitorganizerimporter.cpp \ qversitorganizerimporter_p.cpp \ qversitorganizerhandler.cpp \ qversitorganizerpluginloader_p.cpp \ qtimezones_p.cpp HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS sync.profile000066400000000000000000000014271233466112000134160ustar00rootroot00000000000000%modules = ( # path to module name map "QtContacts" => "$basedir/src/contacts", "QtOrganizer" => "$basedir/src/organizer", "QtVersit" => "$basedir/src/versit", "QtVersitOrganizer" => "$basedir/src/versitorganizer", ); %moduleheaders = ( # restrict the module headers to those found in relative path ); # Module dependencies. # Every module that is required to build this module should have one entry. # Each of the module version specifiers can take one of the following values: # - A specific Git revision. # - any git symbolic ref resolvable from the module's repository (e.g. "refs/heads/master" to track master branch) # %dependencies = ( "qtbase" => "refs/heads/stable", "qtxmlpatterns" => "refs/heads/stable", "qtdeclarative" => "refs/heads/stable", ); tests/000077500000000000000000000000001233466112000122165ustar00rootroot00000000000000tests/auto/000077500000000000000000000000001233466112000131665ustar00rootroot00000000000000tests/auto/auto.pri000066400000000000000000000001511233466112000146470ustar00rootroot00000000000000TEMPLATE = app CONFIG += console testcase QT += testlib !qtHaveModule(jsondb): DEFINES += QT_NO_JSONDB tests/auto/auto.pro000066400000000000000000000001701233466112000146560ustar00rootroot00000000000000TEMPLATE = subdirs # common SUBDIRS += \ contacts \ organizer \ versit \ versitorganizer \ cmake \ tests/auto/cmake/000077500000000000000000000000001233466112000142465ustar00rootroot00000000000000tests/auto/cmake/CMakeLists.txt000066400000000000000000000002551233466112000170100ustar00rootroot00000000000000 cmake_minimum_required(VERSION 2.8) project(qmake_cmake_files) enable_testing() find_package(Qt5Core REQUIRED) include("${_Qt5CTestMacros}") expect_pass(test_modules) tests/auto/cmake/cmake.pro000066400000000000000000000001121233466112000160420ustar00rootroot00000000000000 # Cause make to do nothing. TEMPLATE = subdirs CONFIG += ctest_testcase tests/auto/cmake/test_modules/000077500000000000000000000000001233466112000167555ustar00rootroot00000000000000tests/auto/cmake/test_modules/CMakeLists.txt000066400000000000000000000010201233466112000215060ustar00rootroot00000000000000 cmake_minimum_required(VERSION 2.8) project(test_modules) find_package(Qt5Core REQUIRED) find_package(Qt5Contacts REQUIRED) find_package(Qt5Organizer REQUIRED) include_directories( ${Qt5Contacts_INCLUDE_DIRS} ${Qt5Organizer_INCLUDE_DIRS} ) add_definitions( ${Qt5Contacts_DEFINITIONS} ${Qt5Organizer_DEFINITIONS} ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Core_EXECUTABLE_COMPILE_FLAGS}") add_executable(mainapp main.cpp) target_link_libraries(mainapp ${Qt5Contacts_LIBRARIES} ${Qt5Organizer_LIBRARIES} ) tests/auto/cmake/test_modules/main.cpp000066400000000000000000000043611233466112000204110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include QTCONTACTS_USE_NAMESPACE QTORGANIZER_USE_NAMESPACE int main(int argc, char **argv) { QContact contact; QOrganizerCollection organizerCollection; return 0; } tests/auto/contacts/000077500000000000000000000000001233466112000150045ustar00rootroot00000000000000tests/auto/contacts/contacts.pro000066400000000000000000000010721233466112000173440ustar00rootroot00000000000000TEMPLATE = subdirs # contacts SUBDIRS += \ qcontact \ qcontactasync \ qcontactdetail \ qcontactdetails \ qcontactfilter \ qcontactsortorder \ #TODO: re-enable the manager plugins test #when it has been adapted to new Qt plugin mechanism # qcontactmanagerplugins \ qcontactrelationship \ # qdeclarativecontact qcontactmanager \ qcontactmanagerdetails \ qcontactmanagerfiltering qtHaveModule(jsondb) { SUBDIRS += qcontactjsondb \ qcontactjsondbconverter \ qcontactjsondbstoragelocations } tests/auto/contacts/qcontact/000077500000000000000000000000001233466112000166205ustar00rootroot00000000000000tests/auto/contacts/qcontact/qcontact.pro000066400000000000000000000002171233466112000211560ustar00rootroot00000000000000include(../../auto.pri) QT += contacts SOURCES += tst_qcontact.cpp HEADERS += ../qcontactidmock.h DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontact/tst_qcontact.cpp000066400000000000000000000612561233466112000220440ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include "../qcontactidmock.h" //TESTED_COMPONENT=src/contacts QTCONTACTS_USE_NAMESPACE class tst_QContact: public QObject { Q_OBJECT public: tst_QContact(); virtual ~tst_QContact(); private slots: void details(); void preferences(); void relationships(); void type(); void tags(); void emptiness(); void idLessThan(); void idHash(); void hash(); void datastream(); void traits(); void idTraits(); void equality(); void inequality(); void preferredDetails(); }; tst_QContact::tst_QContact() { } tst_QContact::~tst_QContact() { } void tst_QContact::details() { // Check that detail keys are unique, regardless of order of initialisation // First, construct the detail first, then the contact QContactOrganization org; org.setTitle("Example Title"); QContact keyTest; QVERIFY(keyTest.saveDetail(&org)); QList allDetails = keyTest.details(); QList detailKeys; foreach (const QContactDetail& det, allDetails) { int currKey = det.key(); QVERIFY(!detailKeys.contains(currKey)); detailKeys.append(currKey); } // Second, construct the detail after the contact has been constructed QContactPhoneNumber num; num.setNumber("123456"); QVERIFY(keyTest.saveDetail(&num)); allDetails = keyTest.details(); detailKeys.clear(); foreach (const QContactDetail& det, allDetails) { int currKey = det.key(); QVERIFY(!detailKeys.contains(currKey)); detailKeys.append(currKey); } // now test for default construction sanity QContact c; // Test there are no details (apart from type) by default QVERIFY(c.isEmpty() == true); QVERIFY(c.details().count() == 1); QVERIFY(c.details(QContactPhoneNumber::Type).count() == 0); QVERIFY(c.details().count() == 0); QVERIFY(c.detail(QContactPhoneNumber::Type).isEmpty()); QVERIFY(c.detail().isEmpty()); QVERIFY(c.details(QContactPhoneNumber::Type).count() == 0); QVERIFY(c.detail(QContactPhoneNumber::Type).isEmpty()); QVERIFY(c.details(QContactPhoneNumber::Type).count() == 0); QVERIFY(c.detail(QContactPhoneNumber::Type).isEmpty()); // Test retrieving the first detail (the contact type) QList details = c.details(); QVERIFY(details.at(0).type() == QContactType::Type); // Fetch non existent detail QContactDetail detail = c.detail(QContactDetail::TypeAddress); QVERIFY(detail.isEmpty()); QVERIFY(detail.type() == QContactDetail::TypeUndefined); // retrieve the first detail using the undefined type accessor. detail = c.detail(QContactDetail::TypeUndefined); QVERIFY(detail == details.at(0)); QVERIFY(c.details(QContactDetail::TypeAddress).count() == 0); // Add a detail QContactPhoneNumber p; p.setNumber("12345678"); QVERIFY(c.saveDetail(&p)); QVERIFY(c.isEmpty() == false); QVERIFY(c.details().count() == 2); QVERIFY(c.details(QContactPhoneNumber::Type).count() == 1); QVERIFY(c.details().count() == 1); QVERIFY(!c.detail(QContactPhoneNumber::Type).isEmpty()); QVERIFY(!c.detail().isEmpty()); QCOMPARE(c.detail(), p); // Remove detail QVERIFY(c.removeDetail(&p)); QVERIFY(c.details().count() == 1); QVERIFY(c.isEmpty() == true); QVERIFY(c.details(QContactPhoneNumber::Type).count() == 0); QVERIFY(c.details().count() == 0); QVERIFY(c.detail(QContactPhoneNumber::Type).isEmpty()); QVERIFY(c.detail().isEmpty()); // Try removing it again QVERIFY(!c.removeDetail(&p)); // Add again, and remove a different way (retrieved copy) QVERIFY(c.saveDetail(&p)); QVERIFY(c.isEmpty() == false); QVERIFY(c.details().count() == 2); QContactPhoneNumber p2 = c.detail(QContactPhoneNumber::Type); QCOMPARE(p, p2); QVERIFY(c.removeDetail(&p2)); QVERIFY(c.details().count() == 1); QVERIFY(c.isEmpty() == true); QVERIFY(c.details(QContactPhoneNumber::Type).count() == 0); QVERIFY(c.details().count() == 0); QVERIFY(c.detail(QContactPhoneNumber::Type).isEmpty()); QVERIFY(c.detail().isEmpty()); QCOMPARE(p, p2); // Add again again, and remove a different way (base class) QVERIFY(c.saveDetail(&p)); QVERIFY(c.details().count() == 2); QContactDetail p3 = c.detail(QContactPhoneNumber::Type); QVERIFY(p == p3); QVERIFY(c.removeDetail(&p3)); QVERIFY(c.details().count() == 1); QVERIFY(c.details(QContactPhoneNumber::Type).count() == 0); QVERIFY(c.details().count() == 0); QVERIFY(c.detail(QContactPhoneNumber::Type).isEmpty()); QVERIFY(c.detail().isEmpty()); QVERIFY(p == p3); // now we want to add multiple details of the same type, and test that retrieval works correctly. p2 = QContactPhoneNumber(); p2.setNumber("22222"); c.saveDetail(&p); c.saveDetail(&p2); QVERIFY(c.details().count() == 3); QVERIFY(c.details(QContactPhoneNumber::Type).count() == 2); QVERIFY(c.details().count() == 2); QVERIFY(!c.detail(QContactPhoneNumber::Type).isEmpty()); QVERIFY(!c.detail().isEmpty()); QCOMPARE(c.detail(), p); QVERIFY(c.removeDetail(&p2)); // now try removing a detail for which we've set a preference QContactEmailAddress pref; pref.setEmailAddress("test@test"); c.saveDetail(&pref); c.setPreferredDetail("SendEmail", pref); QVERIFY(c.isPreferredDetail(QString(), pref)); QVERIFY(c.removeDetail(&pref)); QVERIFY(!c.isPreferredDetail(QString(), pref)); // Now try adding a detail to multiple contacts QContact c2; QVERIFY(c2.isEmpty() == true); QVERIFY(c.saveDetail(&p)); QVERIFY(c2.saveDetail(&p)); QVERIFY(c2.isEmpty() == false); QVERIFY(c.details().count() == 2); QVERIFY(c.details(QContactPhoneNumber::Type).count() == 1); QVERIFY(c.details().count() == 1); QVERIFY(!c.detail(QContactPhoneNumber::Type).isEmpty()); QVERIFY(!c.detail().isEmpty()); QCOMPARE(c.detail(), p); QVERIFY(c2.details().count() == 2); QVERIFY(c2.details(QContactPhoneNumber::Type).count() == 1); QVERIFY(c2.details().count() == 1); QVERIFY(!c2.detail(QContactPhoneNumber::Type).isEmpty()); QVERIFY(!c2.detail().isEmpty()); QCOMPARE(c2.detail(), p); // Now try removing it from one QVERIFY(c.removeDetail(&p)); // Make sure it's gone from the first contact QVERIFY(c.isEmpty() == true); QVERIFY(c.details().count() == 1); QVERIFY(c.details(QContactPhoneNumber::Type).count() == 0); QVERIFY(c.details().count() == 0); QVERIFY(c.detail(QContactPhoneNumber::Type).isEmpty()); QVERIFY(c.detail().isEmpty()); // but not the second QVERIFY(c2.isEmpty() == false); QVERIFY(c2.details().count() == 2); QVERIFY(c2.details(QContactPhoneNumber::Type).count() == 1); QVERIFY(c2.details().count() == 1); QVERIFY(!c2.detail(QContactPhoneNumber::Type).isEmpty()); QVERIFY(!c2.detail().isEmpty()); QCOMPARE(c2.detail(), p); // Now remove it from the second as well QVERIFY(c2.removeDetail(&p)); // Make sure it's gone from both QVERIFY(c.details().count() == 1); QVERIFY(c.details(QContactPhoneNumber::Type).count() == 0); QVERIFY(c.details().count() == 0); QVERIFY(c.detail(QContactPhoneNumber::Type).isEmpty()); QVERIFY(c.detail().isEmpty()); QVERIFY(c2.details().count() == 1); QVERIFY(c2.details(QContactPhoneNumber::Type).count() == 0); QVERIFY(c2.details().count() == 0); QVERIFY(c2.detail(QContactPhoneNumber::Type).isEmpty()); QVERIFY(c2.detail().isEmpty()); // add a, add b, remove a, add a, remove b, remove a QVERIFY(c.saveDetail(&p)); QVERIFY(c2.saveDetail(&p)); QVERIFY(c.removeDetail(&p)); QVERIFY(c.saveDetail(&p)); QVERIFY(c2.removeDetail(&p)); QVERIFY(c.removeDetail(&p)); // Now add a detail with the same values twice QContactPhoneNumber one; QContactPhoneNumber two; one.setNumber("12345"); two.setNumber("12345"); // add it once QVERIFY(c.saveDetail(&one)); QVERIFY(c.details().count() == 2); QVERIFY(c.details(QContactPhoneNumber::Type).count() == 1); QVERIFY(c.details().count() == 1); QVERIFY(!c.detail(QContactPhoneNumber::Type).isEmpty()); QVERIFY(!c.detail().isEmpty()); QCOMPARE(c.detail(), one); // add it twice QVERIFY(c.saveDetail(&two)); QVERIFY(c.details().count() == 3); QVERIFY(c.details(QContactPhoneNumber::Type).count() == 2); QVERIFY(c.details().count() == 2); QVERIFY(!c.detail(QContactPhoneNumber::Type).isEmpty()); QVERIFY(!c.detail().isEmpty()); QCOMPARE(c.detail(), one); QCOMPARE(c.details()[0], one); QCOMPARE(c.details()[1], two); // Remove it once QVERIFY(c.removeDetail(&one)); QVERIFY(c.details().count() == 2); QVERIFY(c.details(QContactPhoneNumber::Type).count() == 1); QVERIFY(c.details().count() == 1); QVERIFY(!c.detail(QContactPhoneNumber::Type).isEmpty()); QVERIFY(!c.detail().isEmpty()); QCOMPARE(c.detail(), two); // Remove it twice QVERIFY(c.removeDetail(&two)); QVERIFY(c.details().count() == 1); QVERIFY(c.details(QContactPhoneNumber::Type).count() == 0); QVERIFY(c.details().count() == 0); QVERIFY(c.detail(QContactPhoneNumber::Type).isEmpty()); QVERIFY(c.detail().isEmpty()); // Null pointer tests QVERIFY(c.saveDetail(0) == false); QVERIFY(c.removeDetail(0) == false); // Reference tests... QContactDetail& ref = one; QVERIFY(c.saveDetail(&one)); QVERIFY(ref == one); one.setNumber("56678"); QVERIFY(c.saveDetail(&one)); QVERIFY(ref == one); // Retrieve the detail again and modify it QContactPhoneNumber three = c.detail(); QVERIFY(ref == three); QVERIFY(one == three); three.setNumber("542343"); QVERIFY(c.saveDetail(&three)); // Now see if we got any updates to ref/one QVERIFY(ref == one); QVERIFY(ref != three); // test saving of a detail with an empty field. QContactPhoneNumber four; four.setNumber(""); c.saveDetail(&four); QVERIFY(c.details(QContactPhoneNumber::Type).count() == 2); QVERIFY(!four.values().isEmpty()); // an empty qstring is not invalid; make sure it exists in the detail. // ensure that clearing a contact's details works correctly QContactName nameDetail; nameDetail.setFirstName("test"); c.saveDetail(&nameDetail); QCOMPARE(c.detail(QContactName::Type).value(QContactName::FieldFirstName).toString(), QString("test")); QVERIFY(c.details().size() > 0); QVERIFY(!c.isEmpty()); QContactId oldId = c.id(); c.clearDetails(); QVERIFY(c.details().size() == 1); // contact type. QCOMPARE(c.detail(QContactName::Type).value(QContactName::FieldFirstName).toString(), QString()); QVERIFY(c.isEmpty()); QCOMPARE(c.id(), oldId); // id shouldn't change. } void tst_QContact::preferences() { QContact c; // test first set QContactDetail det(QContactDetail::TypeExtendedDetail); c.saveDetail(&det); QCOMPARE(c.isPreferredDetail("testAction", det), false); QCOMPARE(c.setPreferredDetail("testAction", det), true); QCOMPARE(c.isPreferredDetail("testAction", det), true); QCOMPARE(c.isPreferredDetail(QString(), det), true); QCOMPARE(c.preferredDetail("testAction"), det); // test replacement QContactDetail det2(QContactDetail::TypeExtendedDetail); c.saveDetail(&det2); QCOMPARE(c.isPreferredDetail("testAction", det2), false); QCOMPARE(c.setPreferredDetail("testAction", det2), true); QCOMPARE(c.isPreferredDetail("testAction", det2), true); QCOMPARE(c.isPreferredDetail("testAction", det), false); QCOMPARE(c.preferredDetail("testAction"), det2); // test for detail that is not part of the contact QContactDetail det3(QContactDetail::TypeEmailAddress); QCOMPARE(c.setPreferredDetail("testAction", det3), false); QCOMPARE(c.preferredDetail("testAction"), det2); // shouldn't have changed. // test invalid set QCOMPARE(c.setPreferredDetail(QString(), det3), false); QCOMPARE(c.setPreferredDetail(QString(), QContactDetail()), false); QCOMPARE(c.setPreferredDetail("testAction", QContactDetail()), false); QCOMPARE(c.preferredDetail("testAction"), det2); // shouldn't have changed. // test invalid query QContactDetail det4; c.saveDetail(&det4); QCOMPARE(c.isPreferredDetail(QString(), QContactDetail()), false); QCOMPARE(c.isPreferredDetail(QString(), det4), false); // valid detail, but no pref set. QCOMPARE(c.isPreferredDetail("testAction", QContactDetail()), false); // test retrieving preferred details QContactDetail pd = c.preferredDetail(QString()); QVERIFY(pd.isEmpty()); pd = c.preferredDetail("testAction"); QVERIFY(pd == det2); // shouldn't have changed. // test for preference for action that hasn't been added QVERIFY(c.preferredDetail("NonexistentAction").isEmpty()); // Remove a non preferred detail QContactDetail det2copy(QContactDetail::TypeExtendedDetail); QVERIFY(c.saveDetail(&det2copy)); QVERIFY(c.isPreferredDetail("testAction", det2) == true); QVERIFY(c.isPreferredDetail("testAction", det2copy) == false); QVERIFY(c.removeDetail(&det2copy)); QVERIFY(c.isPreferredDetail("testAction", det2) == true); QVERIFY(c.isPreferredDetail("testAction", det2copy) == false); // Add it again QVERIFY(c.saveDetail(&det2copy)); QVERIFY(c.isPreferredDetail("testAction", det2) == true); QVERIFY(c.isPreferredDetail("testAction", det2copy) == false); // Remove the preferred detail (the copy should not become preferred) QVERIFY(c.removeDetail(&det2)); QVERIFY(c.isPreferredDetail("testAction", det2) == false); QVERIFY(c.isPreferredDetail("testAction", det2copy) == false); } void tst_QContact::relationships() { QContact c; // boring test, because the default contact has no relationships // we test this more convincingly in the QContactManager tests. QList related = c.relatedContacts(); QVERIFY(related.isEmpty()); related = c.relatedContacts(QContactRelationship::HasMember()); QVERIFY(related.isEmpty()); related = c.relatedContacts(QContactRelationship::HasMember(), QContactRelationship::First); QVERIFY(related.isEmpty()); QList relationshipList = c.relationships(); QVERIFY(relationshipList.isEmpty()); relationshipList = c.relationships(QContactRelationship::HasMember()); QVERIFY(relationshipList.isEmpty()); } void tst_QContact::type() { QContact c; QVERIFY(c.isEmpty() == true); // ensure that the default type is the QContactType::TypeContact type QVERIFY(c.type() == QContactType::TypeContact); // now set it to be a group via the type mutator, and test that it works c.setType(QContactType::TypeGroup); QCOMPARE(c.type(), QContactType::TypeGroup); // set it back to a contact, via the string mutator c.setType(QContactType::TypeContact); QCOMPARE(c.type(), QContactType::TypeContact); QVERIFY(c.isEmpty() == true); // type doesn't affect emptiness } void tst_QContact::tags() { QContact c; QVERIFY(c.tags().isEmpty()); c.addTag("tag 1"); QStringList tags; tags.append("tag 1"); QCOMPARE(c.tags(), tags); QList tagDetails = c.details(); QCOMPARE(tagDetails.size(), 1); QCOMPARE(tagDetails.first().tag(), QStringLiteral("tag 1")); c.clearTags(); QVERIFY(c.tags().isEmpty()); QVERIFY(c.details().isEmpty()); tags.append("tag 2"); // tags is now "tag 1", "tag 2" c.setTags(tags); QCOMPARE(c.tags(), tags); tagDetails = c.details(); QCOMPARE(tagDetails.size(), 2); QCOMPARE(tagDetails.at(0).tag(), QStringLiteral("tag 1")); QCOMPARE(tagDetails.at(1).tag(), QStringLiteral("tag 2")); } void tst_QContact::emptiness() { QContact c; QVERIFY(c.isEmpty() == true); c.setType(QContactType::TypeContact); QVERIFY(c.type() == QContactType::TypeContact); QVERIFY(c.isEmpty() == true); // type doesn't affect emptiness } void tst_QContact::idLessThan() { QContactId id1 = QContactIdMock::createId("a", 1); QContactId id2 = QContactIdMock::createId("a", 1); QVERIFY(!(id1 < id2)); QVERIFY(!(id2 < id1)); QContactId id3 = QContactIdMock::createId("a", 2); QContactId id4 = QContactIdMock::createId("b", 1); QContactId id5 = QContactIdMock::createId("", 2); QVERIFY(id1 < id3); QVERIFY(!(id3 < id1)); QVERIFY(id1 < id4); QVERIFY(!(id4 < id1)); QVERIFY(id3 < id4); QVERIFY(!(id4 < id3)); QVERIFY(id5 < id1); QVERIFY(!(id1 < id5)); } void tst_QContact::idHash() { QContactId id1 = QContactIdMock::createId("a", 1); QContactId id2 = QContactIdMock::createId("a", 1); QContactId id3 = QContactIdMock::createId("b", 1); QVERIFY(qHash(id1) == qHash(id2)); QVERIFY(qHash(id1) != qHash(id3)); QSet set; set.insert(id1); set.insert(id2); set.insert(id3); QCOMPARE(set.size(), 2); } void tst_QContact::hash() { QContactId id = QContactIdMock::createId("a", 1); QContact contact1; contact1.setId(id); QContactDetail detail1(QContactDetail::TypeExtendedDetail); detail1.setValue(QContactDetail::FieldContext, "value"); contact1.saveDetail(&detail1); QContact contact2; contact2.setId(id); contact2.saveDetail(&detail1); QContact contact3; contact3.setId(id); QContactDetail detail3(QContactDetail::TypeExtendedDetail); detail3.setValue(QContactDetail::FieldContext, "another value"); contact3.saveDetail(&detail3); QContact contact4; // no details contact4.setId(id); QContact contact5; // preferred details and relationships shouldn't affect the hash contact5.setId(id); contact5.saveDetail(&detail1); contact5.setPreferredDetail("action", detail1); QContactRelationship rel; QContactManagerEngine::setContactRelationships(&contact5, QList() << rel); QVERIFY(qHash(contact1) == qHash(contact2)); QVERIFY(qHash(contact1) != qHash(contact3)); QVERIFY(qHash(contact1) != qHash(contact4)); QVERIFY(qHash(contact1) == qHash(contact5)); } void tst_QContact::datastream() { QByteArray buffer; QDataStream stream1(&buffer, QIODevice::WriteOnly); QContact contactIn; QContactId id = QContactIdMock::createId("manager", 1234); contactIn.setId(id); QContactPhoneNumber phone; phone.setNumber("5678"); contactIn.saveDetail(&phone); stream1 << contactIn; QVERIFY(buffer.size() > 0); /* TODO: fix me? QDataStream stream2(buffer); QContact contactOut; stream2 >> contactOut; QCOMPARE(contactOut, contactIn);*/ } void tst_QContact::traits() { QVERIFY(sizeof(QContact) == sizeof(void *)); QVERIFY(QTypeInfo::isComplex); QVERIFY(!QTypeInfo::isStatic); QVERIFY(!QTypeInfo::isLarge); QVERIFY(!QTypeInfo::isPointer); QVERIFY(!QTypeInfo::isDummy); } void tst_QContact::idTraits() { QVERIFY(sizeof(QContactId) == sizeof(void *)); QVERIFY(QTypeInfo::isComplex); QVERIFY(!QTypeInfo::isStatic); QVERIFY(!QTypeInfo::isLarge); QVERIFY(!QTypeInfo::isPointer); QVERIFY(!QTypeInfo::isDummy); } void tst_QContact::equality() { QContactName name; name.setFirstName("John"); name.setLastName("Doe"); QContactPhoneNumber number; number.setNumber("7654321"); QContactEmailAddress email; email.setEmailAddress("john.doe@nokia.com"); QContactExtendedDetail xdetail; xdetail.setName("shoesize"); xdetail.setData("45"); // Setup two identical contacts QContact one, two; one.saveDetail(&name); one.saveDetail(&number); one.saveDetail(&email); one.saveDetail(&xdetail); two.saveDetail(&xdetail); two.saveDetail(&email); two.saveDetail(&number); two.saveDetail(&name); QVERIFY(one == two); } void tst_QContact::inequality() { QContactId id = QContactIdMock::createId("a", 123); QContactName name; name.setFirstName("John"); name.setLastName("Doe"); QContactPhoneNumber number; number.setNumber("7654321"); QContactEmailAddress email; email.setEmailAddress("john.doe@nokia.com"); QContactExtendedDetail xdetail; xdetail.setName("shoesize"); xdetail.setData("45"); // Setup two contacts QContact one, two; one.setId(id); QVERIFY(one != two); two.setId(id); QVERIFY(one == two); // insert different amount of details one.saveDetail(&name); one.saveDetail(&number); two.saveDetail(&number); QVERIFY(one != two); two.clearDetails(); // same amount of details with different types two.saveDetail(&number); two.saveDetail(&email); QVERIFY(one != two); two.clearDetails(); // same amount of details with different value name.setFirstName("Jim"); two.saveDetail(&name); two.saveDetail(&number); QVERIFY(one != two); two.clearDetails(); name.setFirstName("John"); // different types of details with same value email.setEmailAddress("7654321"); two.saveDetail(&name); two.saveDetail(&email); QVERIFY(one != two); } void tst_QContact::preferredDetails() { QContactPhoneNumber number; number.setNumber("7654321"); QContactEmailAddress email; email.setEmailAddress("john.doe@nokia.com"); QContactPhoneNumber number2; number2.setNumber("1234567"); QContact one; one.saveDetail(&email); one.saveDetail(&number); one.saveDetail(&number2); one.setPreferredDetail("EMAIL", email); one.setPreferredDetail("PHONE", number); QVERIFY(one.preferredDetail("EMAIL") == email); QVERIFY(one.preferredDetail("PHONE") == number); QMap prefDetails = one.preferredDetails(); QCOMPARE(prefDetails.count(), 2); QVERIFY(prefDetails["EMAIL"] == email); QVERIFY(prefDetails["PHONE"] == number); } QTEST_MAIN(tst_QContact) #include "tst_qcontact.moc" tests/auto/contacts/qcontactactions/000077500000000000000000000000001233466112000202015ustar00rootroot00000000000000tests/auto/contacts/qcontactactions/multiaction/000077500000000000000000000000001233466112000225315ustar00rootroot00000000000000tests/auto/contacts/qcontactactions/multiaction/multiaction.cpp000066400000000000000000000207241233466112000255720ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef ACTIONFACTORYPLUGINTARGET #define ACTIONFACTORYPLUGINTARGET contacts_multiactionfactory #endif #ifndef ACTIONFACTORYPLUGINNAME #define ACTIONFACTORYPLUGINNAME MultiActionFactory #endif #include "multiaction_p.h" #include #include #include #include #include QTCONTACTS_USE_NAMESPACE //! [Example Contact Action Plugin Implementation] QObject* QContactMultiActionPlugin::createInstance(const QServiceInterfaceDescriptor& descriptor, QServiceContext* context, QAbstractSecuritySession* session) { Q_UNUSED(context); Q_UNUSED(session); if (descriptor.interfaceName() == QContactActionFactory::InterfaceName && descriptor.serviceName() == QString(QLatin1String("tst_qcontactactions:multiaction")) && descriptor.majorVersion() == 1 && descriptor.minorVersion() == 1 && descriptor.customAttribute("ActionName") == QString(QLatin1String("call"))) { return new QContactMultiActionFactory; } else { return 0; } } Q_EXPORT_PLUGIN2(contacts_multiaction, QContactMultiActionPlugin); QContactMultiActionFactory::QContactMultiActionFactory() : QContactActionFactory() { m_actionOneDescriptor = createDescriptor("call", "tst_qcontactactions:multiaction", "sip", 1); m_actionTwoDescriptor = createDescriptor("call", "tst_qcontactactions:multiaction", "prop", 1); } QContactMultiActionFactory::~QContactMultiActionFactory() { } QList QContactMultiActionFactory::actionDescriptors() const { QList retn; retn << m_actionOneDescriptor << m_actionTwoDescriptor; return retn; } QContactAction* QContactMultiActionFactory::create(const QContactActionDescriptor& which) const { if (which == m_actionOneDescriptor) return new QContactActionOne; else if (which == m_actionTwoDescriptor) return new QContactActionTwo; else return 0; } QSet QContactMultiActionFactory::supportedTargets(const QContact& contact, const QContactActionDescriptor& which) const { QSet retn; if (which == m_actionOneDescriptor || which == m_actionTwoDescriptor) { // in this example, they support the same targets. QList pndets = contact.details(); for (int i = 0; i < pndets.size(); ++i) { QContactActionTarget curr; curr.setContact(contact); curr.setDetails(QList() << pndets.at(i)); retn << curr; } } return retn; } QContactFilter QContactMultiActionFactory::contactFilter(const QContactActionDescriptor& which) const { if (which == m_actionOneDescriptor || which == m_actionTwoDescriptor) { QContactDetailFilter retn; retn.setDetailDefinitionName(QContactPhoneNumber::DefinitionName, QContactPhoneNumber::FieldNumber); return retn; } return QContactFilter(); } QVariant QContactMultiActionFactory::metaData(const QString& key, const QList& targets, const QVariantMap& parameters, const QContactActionDescriptor& which) const { Q_UNUSED(targets) Q_UNUSED(parameters) if (key == QContactActionDescriptor::MetaDataLabel) return QString("Call with VoIP"); // Label etc if (key == QLatin1String("Provider")) {// our custom metadata - just return which.actionIdentifier return which.actionIdentifier(); } return QVariant(); } bool QContactMultiActionFactory::supportsContact(const QContact& contact, const QContactActionDescriptor& which) const { if (which == m_actionOneDescriptor || which == m_actionTwoDescriptor) return !contact.details().isEmpty(); return false; } QContactActionOne::QContactActionOne() { } QContactActionOne::~QContactActionOne() { } bool QContactActionOne::invokeAction(const QContactActionTarget& target, const QVariantMap& params) { Q_UNUSED(params) // this action only works on (contact + phone number) targets. if (target.details().size() > 1 || target.details().at(0).definitionName() != QContactPhoneNumber::DefinitionName) return false; QTimer::singleShot(1, this, SLOT(performAction())); return true; } bool QContactActionOne::invokeAction(const QList& targets, const QVariantMap& params) { Q_UNUSED(params) foreach (const QContactActionTarget& target, targets) { if (target.details().size() > 1 || target.details().at(0).definitionName() != QContactPhoneNumber::DefinitionName) { return false; } } QTimer::singleShot(1, this, SLOT(performAction())); return true; } QVariantMap QContactActionOne::results() const { return QVariantMap(); } QContactAction::State QContactActionOne::state() const { return QContactAction::FinishedState; } void QContactActionOne::performAction() { QMessageBox::information(0, "ActionOne", "This is action one!"); emit stateChanged(QContactAction::FinishedState); } QContactActionTwo::QContactActionTwo() { } QContactActionTwo::~QContactActionTwo() { } bool QContactActionTwo::invokeAction(const QContactActionTarget& target, const QVariantMap& params) { Q_UNUSED(params) // this action only works on (contact + phone number) targets. Note that it doesn't // have to be the same as QContactActionOne -- it could have an entirely different implementation! if (target.details().size() > 1 || target.details().at(0).definitionName() != QContactPhoneNumber::DefinitionName) return false; QTimer::singleShot(1, this, SLOT(performAction())); return true; } bool QContactActionTwo::invokeAction(const QList& targets, const QVariantMap& params) { Q_UNUSED(params) foreach (const QContactActionTarget& target, targets) { if (target.details().size() > 1 || target.details().at(0).definitionName() != QContactPhoneNumber::DefinitionName) { return false; } } QTimer::singleShot(1, this, SLOT(performAction())); return true; } QVariantMap QContactActionTwo::results() const { return QVariantMap(); } QContactAction::State QContactActionTwo::state() const { return QContactAction::FinishedState; } void QContactActionTwo::performAction() { QMessageBox::information(0, "ActionTwo", "This is action two!"); emit stateChanged(QContactAction::FinishedState); } //! [Example Contact Action Plugin Implementation] tests/auto/contacts/qcontactactions/multiaction/multiaction.pro000066400000000000000000000015601233466112000256050ustar00rootroot00000000000000TEMPLATE = lib CONFIG += plugin testplugin TARGET = $$qtLibraryTarget(contacts_multiaction) PLUGIN_TYPE=contacts include(../../../../common.pri) INCLUDEPATH += ../../../../src/serviceframework \ ../../../../src/contacts \ ../../../../src/contacts/details \ ../../../../src/contacts/requests \ ../../../../src/contacts/filters INCLUDEPATH += ../../ CONFIG += mobility MOBILITY = contacts serviceframework DEFINES += ACTIONPLUGINTARGET=contacts_multiaction DEFINES += ACTIONPLUGINNAME=MultiAction HEADERS += multiaction_p.h SOURCES += multiaction.cpp xml.path = $$DESTDIR/xmldata xml.files = xmldata/multiactionservice.xml xml.CONFIG = no_link no_dependencies explicit_dependencies no_build combine ignore_no_exist no_clean INSTALLS += xml build_pass:ALL_DEPS+=install_xml DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactactions/multiaction/multiaction_p.h000066400000000000000000000136411233466112000255560ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTMULTIACTION_P_H #define QCONTACTMULTIACTION_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include "qserviceinterfacedescriptor.h" #include "qserviceplugininterface.h" #include "qservicecontext.h" #include "qabstractsecuritysession.h" #include #include #include QTCONTACTS_USE_NAMESPACE //! [Example Contact Action Plugin Declaration] /* This action plugin is capable of producing two actions which each have the same action name, service name, interface name and implementation (minor) version, but internally use a different implementation. This difference is reported via the meta data function of the factory (which is exposed to clients via the descriptor which provides a "front end" to the factory). Example use case: Company "Example VoIP Solutions" wants to provide a "Call" action with different implementations. -> it provides a SINGLE plugin which provides two actions, both of which are: - ServiceName = "Example VoIP Solution" - InterfaceName = "org.qt-project.Qt.SampleContactsActionPlugin" (QContactActionFactory::InterfaceName) - Major Version = "1" - Minor Version = "1" - ActionName = "call" (this is a custom property in the service interface xml) -> BUT one of the actions has the custom property: - Provider = "sip" -> where the other action has the custom property: - Provider = "example proprietary protocol" -> the custom properties are available to clients via the QContactActionDescriptor::metaData() function. */ class QContactMultiActionPlugin : public QObject, public QServicePluginInterface { Q_OBJECT Q_INTERFACES(QtMobility::QServicePluginInterface) public: QObject* createInstance(const QServiceInterfaceDescriptor& descriptor, QServiceContext* context, QAbstractSecuritySession* session); }; class QContactMultiActionFactory : public QContactActionFactory { Q_OBJECT public: QContactMultiActionFactory(); ~QContactMultiActionFactory(); QList actionDescriptors() const; QContactAction* create(const QContactActionDescriptor& which) const; QSet supportedTargets(const QContact& contact, const QContactActionDescriptor& which) const; QContactFilter contactFilter(const QContactActionDescriptor& which) const; QVariant metaData(const QString& key, const QList& targets, const QVariantMap& parameters, const QContactActionDescriptor& which) const; bool supportsContact(const QContact& contact, const QContactActionDescriptor& which) const; private: QContactActionDescriptor m_actionOneDescriptor; QContactActionDescriptor m_actionTwoDescriptor; }; class QContactActionOne : public QContactAction { Q_OBJECT public: QContactActionOne(); ~QContactActionOne(); bool invokeAction(const QContactActionTarget& target, const QVariantMap& params = QVariantMap()); bool invokeAction(const QList& targets, const QVariantMap& params = QVariantMap()); QVariantMap results() const; State state() const; private slots: void performAction(); }; class QContactActionTwo : public QContactAction { Q_OBJECT public: QContactActionTwo(); ~QContactActionTwo(); bool invokeAction(const QContactActionTarget& target, const QVariantMap& params = QVariantMap()); bool invokeAction(const QList& targets, const QVariantMap& params = QVariantMap()); QVariantMap results() const; State state() const; private slots: void performAction(); }; //! [Example Contact Action Plugin Declaration] #endif tests/auto/contacts/qcontactactions/multiaction/xmldata/000077500000000000000000000000001233466112000241635ustar00rootroot00000000000000tests/auto/contacts/qcontactactions/multiaction/xmldata/multiactionservice.xml000066400000000000000000000016131233466112000306170ustar00rootroot00000000000000 tst_qcontactactions:multiaction plugins/contacts/libcontacts_multiaction This service provides two test QContactAction implementations for testing purposes. It is also an example of a single plugin providing multiple actions whose descriptors are identical except for their meta data. org.qt-project.Qt.SampleContactsActionPlugin 1.1 call This plugin can instantiate two different QContactAction instances; one which provides the "call" action via the "sip" provider, the other which provides the "call" action via the "example proprietary protocol" provider. tests/auto/contacts/qcontactactions/qcontactactions.pro000066400000000000000000000001771233466112000241250ustar00rootroot00000000000000include(../../../staticconfig.pri) TEMPLATE=subdirs SUBDIRS += sendemailaction \ multiaction \ unittest tests/auto/contacts/qcontactactions/sendemailaction/000077500000000000000000000000001233466112000233405ustar00rootroot00000000000000tests/auto/contacts/qcontactactions/sendemailaction/sendemailaction.cpp000066400000000000000000000156711233466112000272150ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef ACTIONFACTORYPLUGINTARGET #define ACTIONFACTORYPLUGINTARGET contacts_sendemailactionfactory #endif #ifndef ACTIONFACTORYPLUGINNAME #define ACTIONFACTORYPLUGINNAME SendEmailActionFactory #endif #include "sendemailaction_p.h" #include #include #include #include #include #define makestr(x) (#x) #define makename(x) makestr(x) QTCONTACTS_USE_NAMESPACE QObject* QContactSendEmailActionPlugin::createInstance(const QServiceInterfaceDescriptor& descriptor, QServiceContext* context, QAbstractSecuritySession* session) { Q_UNUSED(context); Q_UNUSED(session); if (descriptor.interfaceName() == QContactActionFactory::InterfaceName && descriptor.serviceName() == QString(QLatin1String("tst_qcontactactions:sendemailaction")) && descriptor.majorVersion() == 1 && descriptor.minorVersion() == 1 && descriptor.customAttribute("ActionName") == QString(QLatin1String("SendEmail"))) { return new QContactSendEmailActionFactory(); } else { return 0; } } Q_EXPORT_PLUGIN2(contacts_sendemailaction, QContactSendEmailActionPlugin); QContactSendEmailActionFactory::QContactSendEmailActionFactory() : QContactActionFactory() { m_sendEmailDescriptor = createDescriptor("SendEmail", "tst_qcontactactions:sendemailaction", "sendemailaction", 1); } QContactSendEmailActionFactory::~QContactSendEmailActionFactory() { } QList QContactSendEmailActionFactory::actionDescriptors() const { QList retn; retn << m_sendEmailDescriptor; return retn; } QContactAction* QContactSendEmailActionFactory::create(const QContactActionDescriptor& which) const { // note: if the action factory only ever creates one action, this check can be skipped if (which == m_sendEmailDescriptor) return new QContactSendEmailAction; return 0; } QSet QContactSendEmailActionFactory::supportedTargets(const QContact& contact, const QContactActionDescriptor& which) const { QSet retn; // note: if the action factory only ever creates one action, this check can be skipped if (which != m_sendEmailDescriptor) return retn; QList emdets = contact.details(); for (int i = 0; i < emdets.size(); ++i) { QContactActionTarget curr; curr.setContact(contact); curr.setDetails(QList() << emdets.at(i)); retn << curr; } return retn; } QContactFilter QContactSendEmailActionFactory::contactFilter(const QContactActionDescriptor& which) const { // note: if the action factory only ever creates one action, this check can be skipped if (which != m_sendEmailDescriptor) return QContactFilter(); QContactDetailFilter retn; retn.setDetailDefinitionName(QContactEmailAddress::DefinitionName, QContactEmailAddress::FieldEmailAddress); return retn; } QVariant QContactSendEmailActionFactory::metaData(const QString& key, const QList& targets, const QVariantMap& parameters, const QContactActionDescriptor& which) const { Q_UNUSED(key); Q_UNUSED(targets); Q_UNUSED(parameters); Q_UNUSED(which); return QVariant(); } bool QContactSendEmailActionFactory::supportsContact(const QContact& contact, const QContactActionDescriptor& which) const { // note: if the action factory only ever creates one action, this check can be skipped if (which != m_sendEmailDescriptor) return false; return !contact.details().isEmpty(); } QContactSendEmailAction::QContactSendEmailAction() { } QContactSendEmailAction::~QContactSendEmailAction() { } bool QContactSendEmailAction::isTargetSupported(const QContactActionTarget &target) const { QList dets = target.details(); if (dets.size() != 1 || !target.isValid()) return false; return (dets.at(0).definitionName() == QContactEmailAddress::DefinitionName); } bool QContactSendEmailAction::invokeAction(const QContactActionTarget& target, const QVariantMap& ) { if (!isTargetSupported(target)) return false; QTimer::singleShot(1, this, SLOT(performAction())); return true; } bool QContactSendEmailAction::invokeAction(const QList& targets, const QVariantMap& ) { foreach (const QContactActionTarget& target, targets) { if (!isTargetSupported(target)) { return false; } } QTimer::singleShot(1, this, SLOT(performAction())); return true; } QVariantMap QContactSendEmailAction::results() const { return QVariantMap(); } void QContactSendEmailAction::performAction() { QMessageBox::information(0, "SendEmail Action", "This example action exists as an example of how the action interface may be implemented; it does not offer the advertised functionality."); emit stateChanged(QContactAction::FinishedState); } tests/auto/contacts/qcontactactions/sendemailaction/sendemailaction.pro000066400000000000000000000016101233466112000272170ustar00rootroot00000000000000TEMPLATE = lib CONFIG += plugin testplugin TARGET = $$qtLibraryTarget(contacts_sendemailaction) PLUGIN_TYPE=contacts include(../../../../common.pri) INCLUDEPATH += ../../../../src/serviceframework \ ../../../../src/contacts \ ../../../../src/contacts/details \ ../../../../src/contacts/requests \ ../../../../src/contacts/filters INCLUDEPATH += ../../ CONFIG += mobility MOBILITY = contacts serviceframework DEFINES += ACTIONPLUGINTARGET=contacts_sendemailaction DEFINES += ACTIONPLUGINNAME=SendEmailAction HEADERS += sendemailaction_p.h SOURCES += sendemailaction.cpp xml.path = $$DESTDIR/xmldata xml.files = xmldata/sendemailactionservice.xml xml.CONFIG = no_link no_dependencies explicit_dependencies no_build combine ignore_no_exist no_clean INSTALLS += xml build_pass:ALL_DEPS+=install_xml DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactactions/sendemailaction/sendemailaction_p.h000066400000000000000000000104371233466112000271740ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTSENDEMAILACTION_P_H #define QCONTACTSENDEMAILACTION_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include "qserviceinterfacedescriptor.h" #include "qserviceplugininterface.h" #include "qservicecontext.h" #include "qabstractsecuritysession.h" #include #include #include QTCONTACTS_USE_NAMESPACE class QContactSendEmailActionPlugin : public QObject, public QServicePluginInterface { Q_OBJECT Q_INTERFACES(QtMobility::QServicePluginInterface) public: QObject* createInstance(const QServiceInterfaceDescriptor& descriptor, QServiceContext* context, QAbstractSecuritySession* session); }; class QContactSendEmailActionFactory : public QContactActionFactory { Q_OBJECT public: QContactSendEmailActionFactory(); ~QContactSendEmailActionFactory(); QList actionDescriptors() const; QContactAction* create(const QContactActionDescriptor& which) const; QSet supportedTargets(const QContact& contact, const QContactActionDescriptor& which) const; QContactFilter contactFilter(const QContactActionDescriptor& which) const; QVariant metaData(const QString& key, const QList& targets, const QVariantMap& parameters, const QContactActionDescriptor& which) const; bool supportsContact(const QContact& contact, const QContactActionDescriptor& which) const; private: QContactActionDescriptor m_sendEmailDescriptor; }; class QContactSendEmailAction : public QContactAction { Q_OBJECT public: QContactSendEmailAction(); ~QContactSendEmailAction(); bool invokeAction(const QContactActionTarget& target, const QVariantMap& params = QVariantMap()); bool invokeAction(const QList& targets, const QVariantMap& params = QVariantMap()); QVariantMap results() const; State state() const {return QContactAction::FinishedState;} private slots: void performAction(); private: bool isTargetSupported(const QContactActionTarget &target) const; }; #endif tests/auto/contacts/qcontactactions/sendemailaction/xmldata/000077500000000000000000000000001233466112000247725ustar00rootroot00000000000000tests/auto/contacts/qcontactactions/sendemailaction/xmldata/sendemailactionservice.xml000066400000000000000000000011441233466112000322340ustar00rootroot00000000000000 tst_qcontactactions:sendemailaction plugins/contacts/libcontacts_sendemailaction This service provides the "send email" QContactAction for testing purposes org.qt-project.Qt.SampleContactsActionPlugin 1.1 SendEmail Interface which allows sending emails to a contact tests/auto/contacts/qcontactactions/unittest/000077500000000000000000000000001233466112000220605ustar00rootroot00000000000000tests/auto/contacts/qcontactactions/unittest/tst_qcontactactions.cpp000066400000000000000000000355451233466112000266670ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/contacts #include #include #include #include #include #include "qservicemanager.h" QTCONTACTS_USE_NAMESPACE class tst_QContactActions : public QObject { Q_OBJECT public: tst_QContactActions(); virtual ~tst_QContactActions(); public slots: void init(); void cleanup(); private slots: void contactFunctions(); void testSendEmail(); void testDescriptor(); void testDescriptorHash(); void testTarget(); void traits(); }; tst_QContactActions::tst_QContactActions() { // set the correct path to look for plugins QString path = QApplication::applicationDirPath() + "/dummyplugin/plugins"; QApplication::addLibraryPath(path); // and add the sendemail + call actions to the service framework QServiceManager sm; // clean up any actions/services. QStringList allServices = sm.findServices(); foreach(const QString& serv, allServices) { if (serv.startsWith("tst_qcontact")) { if (!sm.removeService(serv)) { qDebug() << " tst_qca: ctor: cleaning up test service" << serv << "failed:" << sm.error(); } } } if (!sm.addService(QCoreApplication::applicationDirPath() + "/plugins/contacts/xmldata/sendemailactionservice.xml")) qDebug() << " tst_qca: ctor: unable to add SendEmail service:" << sm.error(); if (!sm.addService(QCoreApplication::applicationDirPath() + "/plugins/contacts/xmldata/multiactionservice.xml")) qDebug() << " tst_qca: ctor: unable to add MultiAction service:" << sm.error(); } tst_QContactActions::~tst_QContactActions() { QString path = QApplication::applicationDirPath() + "/dummyplugin/plugins"; QApplication::removeLibraryPath(path); // clean up any actions/services. QServiceManager sm; QStringList allServices = sm.findServices(); foreach(const QString& serv, allServices) { if (serv.startsWith("tst_qcontact")) { if (!sm.removeService(serv)) { qDebug() << " tst_qca: ctor: cleaning up test service" << serv << "failed:" << sm.error(); } } } } void tst_QContactActions::init() { } void tst_QContactActions::cleanup() { } void tst_QContactActions::contactFunctions() { QContact c; // empty contact. QContact c2; // contact with email saved. QContact c3; // two emails QContact c4; // two emails, plus a preference QContact c5; // two emails, plus a preference for an unsupported detail QContactEmailAddress e2; QContactEmailAddress e; e.setEmailAddress("test@nokia.com"); c2.saveDetail(&e); e2.setEmailAddress("secondtest@nokia.com"); c3.saveDetail(&e); c3.saveDetail(&e2); c4.saveDetail(&e); c4.saveDetail(&e2); c5.saveDetail(&e2); // reverse order for c5 c5.saveDetail(&e); c4.setPreferredDetail("SendEmail", e2); c5.setPreferredDetail("SendEmail", c5.detail()); // Get our descriptor QContactActionDescriptor descriptor = QContactAction::actionDescriptors("SendEmail").value(0); QVERIFY(descriptor.actionName() == "SendEmail"); // available actions: // empty contact QList availableActions = c.availableActions(QString()); QVERIFY(availableActions.isEmpty()); // should not contain SendEmail // contact with email availableActions = c2.availableActions(QString()); QVERIFY(!availableActions.isEmpty()); // should contain SendEmail QVERIFY(availableActions.contains(descriptor)); // try various combinations of version and name availableActions = c2.availableActions(); QVERIFY(!availableActions.isEmpty()); // should contain SendEmail QVERIFY(availableActions.contains(descriptor)); availableActions = c2.availableActions("tst_qcontactactions:sendemailaction"); QVERIFY(!availableActions.isEmpty()); // should contain SendEmail availableActions = c2.availableActions(QString()); QVERIFY(!availableActions.isEmpty()); // should contain SendEmail QVERIFY(availableActions.contains(descriptor)); // Again with c3 availableActions = c3.availableActions(QString()); QVERIFY(!availableActions.isEmpty()); // should contain SendEmail QVERIFY(availableActions.contains(descriptor)); // try various combinations of version and name availableActions = c3.availableActions(); QVERIFY(!availableActions.isEmpty()); // should contain SendEmail QVERIFY(availableActions.contains(descriptor)); availableActions = c3.availableActions("tst_qcontactactions:sendemailaction"); QVERIFY(!availableActions.isEmpty()); // should contain SendEmail QVERIFY(availableActions.contains(descriptor)); availableActions = c3.availableActions(QString()); QVERIFY(!availableActions.isEmpty()); // should contain SendEmail QVERIFY(availableActions.contains(descriptor)); } void tst_QContactActions::testSendEmail() { QContact c; QContactEmailAddress e; e.setEmailAddress("test@nokia.com"); c.saveDetail(&e); QVERIFY(QContactAction::availableActions().contains("SendEmail")); QVERIFY(QContactAction::availableActions("tst_qcontactactions:sendemailaction").contains("SendEmail")); QList descrs = QContactAction::actionDescriptors(); bool foundSendEmail = false; for (int i = 0; i < descrs.size(); i++) { if (descrs.at(i).actionName() == QString("SendEmail")) { foundSendEmail = true; break; } } QVERIFY(foundSendEmail); descrs = QContactAction::actionDescriptors(QString()); foundSendEmail = false; for (int i = 0; i < descrs.size(); i++) { QCOMPARE(descrs.at(i).serviceName(), QString("tst_qcontactactions:sendemailaction")); if (descrs.at(i).actionName() == QString("SendEmail")) { foundSendEmail = true; break; } } QVERIFY(foundSendEmail); descrs = QContactAction::actionDescriptors(QString()); foundSendEmail = false; for (int i = 0; i < descrs.size(); i++) { QCOMPARE(descrs.at(i).serviceName(), QString("tst_qcontactactions:sendemailaction")); QCOMPARE(descrs.at(i).implementationVersion(), 1); if (descrs.at(i).actionName() == QString("SendEmail") && descrs.at(i).serviceName() == QString("tst_qcontactactions:sendemailaction") && descrs.at(i).implementationVersion() == 1) { foundSendEmail = true; break; } } QVERIFY(foundSendEmail); descrs = QContactAction::actionDescriptors("SendEmail"); foundSendEmail = false; for (int i = 0; i < descrs.size(); i++) { QCOMPARE(descrs.at(i).actionName(), QString("SendEmail")); if (descrs.at(i).actionName() == QString("SendEmail") && descrs.at(i).serviceName() == QString("tst_qcontactactions:sendemailaction") && descrs.at(i).implementationVersion() == 1) { foundSendEmail = true; break; } } QVERIFY(foundSendEmail); descrs = QContactAction::actionDescriptors(); QContactAction* sendEmail = 0; for (int i = 0; i < descrs.size(); i++) { if (descrs.at(i).actionName() == QString("SendEmail") && descrs.at(i).serviceName() == QString("tst_qcontactactions:sendemailaction") && descrs.at(i).implementationVersion() == 1) { sendEmail = QContactAction::action(descrs.at(i)); break; } } QVERIFY(sendEmail); } void tst_QContactActions::testDescriptor() { // first, test retrieving an invalid action QContactAction* invalidAction = QContactAction::action(QContactActionDescriptor()); QVERIFY(invalidAction == 0); // should be null. QContact c; QContactEmailAddress e; e.setEmailAddress("test@nokia.com"); c.saveDetail(&e); QContactPhoneNumber p; p.setNumber("12345"); c.saveDetail(&p); QVERIFY(QContactAction::availableActions().contains("SendEmail")); QVERIFY(QContactAction::availableActions("tst_qcontactactions:sendemailaction").contains("SendEmail")); QContactActionDescriptor sendEmailDescriptor; QContactActionDescriptor multiActionOneDescriptor; QContactActionDescriptor multiActionTwoDescriptor; QList descrs = QContactAction::actionDescriptors(); QContactAction* sendEmailAction = 0; for (int i = 0; i < descrs.size(); i++) { QContactActionDescriptor temp = descrs.at(i); if (temp.actionName() == QString("SendEmail")) { sendEmailAction = QContactAction::action(temp); QVERIFY(c.availableActions().contains(temp)); // has an email address, so should be available QVERIFY(temp.supportsContact(c)); sendEmailDescriptor = temp; } else if (temp.actionName() == QString("call")) { if (temp.metaData(QString(QLatin1String("Provider"))) == QString(QLatin1String("sip"))) { multiActionOneDescriptor = temp; } else { multiActionTwoDescriptor = temp; } } } QVERIFY(sendEmailDescriptor.isValid()); QVERIFY(multiActionOneDescriptor.isValid()); QVERIFY(multiActionTwoDescriptor.isValid()); QVERIFY(sendEmailAction != 0); delete sendEmailAction; // now test equivalence. The send email action descriptor should // have a different action name to both multi action one and two. QVERIFY(sendEmailDescriptor.actionName() != multiActionOneDescriptor.actionName()); QVERIFY(sendEmailDescriptor.actionName() != multiActionTwoDescriptor.actionName()); QVERIFY(sendEmailDescriptor != multiActionOneDescriptor); QVERIFY(sendEmailDescriptor != multiActionTwoDescriptor); // multi action one and two should have the same action name, service // name and implementation (minor) version. BUT they have different // implementations (Provider is different) so they should NOT be equal. QVERIFY(multiActionOneDescriptor.actionName() == multiActionTwoDescriptor.actionName()); QVERIFY(multiActionOneDescriptor.serviceName() == multiActionTwoDescriptor.serviceName()); QVERIFY(multiActionOneDescriptor.implementationVersion() == multiActionTwoDescriptor.implementationVersion()); QVERIFY(multiActionOneDescriptor != multiActionTwoDescriptor); // verify that the meta data is reported correctly QVERIFY(multiActionOneDescriptor.metaData("Provider") != multiActionTwoDescriptor.metaData("Provider")); } void tst_QContactActions::testDescriptorHash() { QContactActionDescriptor qcad1, qcad2, qcad3; QList alldes = QContactAction::actionDescriptors(); if (alldes.size() > 1) { qcad1 = alldes.at(0); qcad2 = alldes.at(1); QVERIFY(qHash(qcad1) != qHash(qcad2)); qcad3 = alldes.at(0); QVERIFY(qHash(qcad1) == qHash(qcad3)); } if (alldes.size() > 2) { qcad3 = alldes.at(2); QVERIFY(qHash(qcad1) != qHash(qcad2)); QVERIFY(qHash(qcad2) != qHash(qcad3)); QVERIFY(qHash(qcad1) != qHash(qcad3)); } } void tst_QContactActions::testTarget() { // first, create a contact with some details QContact c1; QContactName n1; n1.setFirstName("test"); n1.setLastName("contact"); c1.saveDetail(&n1); QContactPhoneNumber p1; p1.setNumber("12345"); c1.saveDetail(&p1); QContactEmailAddress e1; e1.setEmailAddress("test@example.com"); c1.saveDetail(&e1); QList dl1; dl1 << p1 << e1; QContactActionTarget t1; // default ctor QContactActionTarget t2(c1); // "whole contact" target QContactActionTarget t3(c1, e1); // "specific detail" target QContactActionTarget t4(c1, dl1); // "detail list" target QCOMPARE(t2.contact(), c1); QCOMPARE(t3.contact(), c1); QCOMPARE(t4.contact(), c1); QCOMPARE(t2.details(), QList()); QCOMPARE(t3.details(), QList() << e1); QCOMPARE(t4.details(), dl1); QVERIFY(t1 != t2); QVERIFY(t1 != t3); QVERIFY(t1 != t4); QVERIFY(t2 != t3); QVERIFY(t2 != t4); QVERIFY(t3 != t4); QVERIFY(qHash(t2) != qHash(t3)); QVERIFY(qHash(t2) != qHash(t4)); QVERIFY(qHash(t3) != qHash(t4)); QVERIFY(!t1.isValid()); QVERIFY(t2.isValid()); QVERIFY(t3.isValid()); QVERIFY(t4.isValid()); t1.setContact(c1); t1.setDetails(dl1); QVERIFY(t1.isValid()); QVERIFY(t1 == t4); t2 = t1; QVERIFY(t2.isValid()); // check that assignment operator doesn't destroy validity. QVERIFY(t1 == t2); QVERIFY(t2 == t4); QVERIFY(t2 != t3); } void tst_QContactActions::traits() { QCOMPARE(sizeof(QContactActionDescriptor), sizeof(void *)); QTypeInfo ti; QVERIFY(ti.isComplex); QVERIFY(!ti.isStatic); QVERIFY(!ti.isLarge); QVERIFY(!ti.isPointer); QVERIFY(!ti.isDummy); } QTEST_MAIN(tst_QContactActions) #include "tst_qcontactactions.moc" tests/auto/contacts/qcontactactions/unittest/unittest.pro000066400000000000000000000010341233466112000244570ustar00rootroot00000000000000QT += testlib TEMPLATE=app TARGET=tst_qcontactactions CONFIG+=testcase PLUGIN_SUBDIR=dummyplugin/plugins include(../../../../common.pri) INCLUDEPATH += ../../../../src/contacts \ ../../../../src/contacts/details \ ../../../../src/contacts/requests \ ../../../../src/contacts/filters \ ../../../../src/serviceframework INCLUDEPATH += ../../ CONFIG += mobility MOBILITY = contacts serviceframework SOURCES += tst_qcontactactions.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactasync/000077500000000000000000000000001233466112000176565ustar00rootroot00000000000000tests/auto/contacts/qcontactasync/maliciousplugin/000077500000000000000000000000001233466112000230625ustar00rootroot00000000000000tests/auto/contacts/qcontactasync/maliciousplugin/malicious.json000066400000000000000000000000461233466112000257420ustar00rootroot00000000000000{ "Keys": [ "maliciousplugin" ] } tests/auto/contacts/qcontactasync/maliciousplugin/maliciousplugin.cpp000066400000000000000000000150151233466112000267740ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef MALICIOUSPLUGINTARGET #define MALICIOUSPLUGINTARGET contacts_maliciousplugin #endif #ifndef MALICIOUSPLUGINNAME #define MALICIOUSPLUGINNAME maliciousplugin #endif #define makestr(x) (#x) #define makename(x) makestr(x) #include "maliciousplugin_p.h" #include #include #include #include #include QTCONTACTS_USE_NAMESPACE class MaliciousThreadObject : public QObject { Q_OBJECT public: MaliciousThreadObject() {} public slots: void activateRequest(QContactAbstractRequest* req) { mutex.lock(); if (activeRequests.contains(req)) { QContactManagerEngine::updateRequestState(req, QContactAbstractRequest::ActiveState); } mutex.unlock(); } void finishRequest(QContactAbstractRequest* req) { QContactManager::Error errorResult = QContactManager::NoError; QMap errorsResult; QList idResult; QList contactResult; QList relResult; mutex.lock(); if (activeRequests.contains(req)) { switch(req->type()) { case QContactAbstractRequest::ContactIdFetchRequest: QContactManagerEngine::updateContactIdFetchRequest(static_cast(req), idResult, errorResult, QContactAbstractRequest::FinishedState); break; case QContactAbstractRequest::ContactFetchRequest: QContactManagerEngine::updateContactFetchRequest(static_cast(req), contactResult, errorResult, QContactAbstractRequest::FinishedState); break; default: QContactManagerEngine::updateRequestState(req, QContactAbstractRequest::FinishedState); break; } } activeRequests.remove(req); mutex.unlock(); } public: QMutex mutex; QSet activeRequests; }; class MaliciousThread : public QThread { Q_OBJECT; public: MaliciousThread(); ~MaliciousThread(); QObject* eventLoopQuitHack; }; MaliciousThread::MaliciousThread() { eventLoopQuitHack = new QObject; eventLoopQuitHack->moveToThread(this); connect(eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection); } MaliciousThread::~MaliciousThread() { eventLoopQuitHack->deleteLater(); wait(); } MaliciousAsyncManagerEngine::MaliciousAsyncManagerEngine() : QContactManagerEngine() { thread = new MaliciousThread(); threadObject = new MaliciousThreadObject(); threadObject->moveToThread(thread); connect(this, SIGNAL(doStartRequest(QContactAbstractRequest*)), threadObject, SLOT(activateRequest(QContactAbstractRequest*)), Qt::BlockingQueuedConnection); connect(this, SIGNAL(doFinishRequest(QContactAbstractRequest*)), threadObject, SLOT(finishRequest(QContactAbstractRequest*)), Qt::BlockingQueuedConnection); thread->start(); } MaliciousAsyncManagerEngine::~MaliciousAsyncManagerEngine() { delete thread; delete threadObject; } QString MaliciousAsyncManagerEngine::managerName() const { return QString(makename(MALICIOUSPLUGINNAME)); } bool MaliciousAsyncManagerEngine::startRequest(QContactAbstractRequest* req) { threadObject->mutex.lock(); threadObject->activeRequests.insert(req); threadObject->mutex.unlock(); // Spawn a thread to do stuff on another thread emit doStartRequest(req); emit doFinishRequest(req); return true; } bool MaliciousAsyncManagerEngine::cancelRequest(QContactAbstractRequest *req) { updateRequestState(req, QContactAbstractRequest::CanceledState); QContactManagerEngine::cancelRequest(req); return true; } void MaliciousAsyncManagerEngine::requestDestroyed(QContactAbstractRequest *req) { threadObject->mutex.lock(); threadObject->activeRequests.remove(req); threadObject->mutex.unlock(); QContactManagerEngine::requestDestroyed(req); } QString MaliciousEngineFactory::managerName() const { return QString(makename(MALICIOUSPLUGINNAME)); } QContactManagerEngine* MaliciousEngineFactory::engine(const QMap& parameters, QContactManager::Error* error) { Q_UNUSED(parameters); *error = QContactManager::NoError; return new MaliciousAsyncManagerEngine(); } QContactEngineId* MaliciousEngineFactory::createContactEngineId(const QMap& parameters, const QString& engineIdString) const { Q_UNUSED(parameters); Q_UNUSED(engineIdString); return 0; } #include "maliciousplugin.moc" tests/auto/contacts/qcontactasync/maliciousplugin/maliciousplugin.pro000066400000000000000000000004671233466112000270170ustar00rootroot00000000000000PLUGIN_TYPE = contacts load(qt_plugin) QT += contacts testlib DEFINES += MALICIOUSPLUGINTARGET=contacts_maliciousplugin DEFINES += MALICIOUSPLUGINNAME=maliciousplugin HEADERS += maliciousplugin_p.h SOURCES += maliciousplugin.cpp OTHER_FILES += \ malicious.json DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactasync/maliciousplugin/maliciousplugin_p.h000066400000000000000000000155771233466112000267750ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTMALICIOUSPLUGIN_P_H #define QCONTACTMALICIOUSPLUGIN_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include QT_BEGIN_NAMESPACE class QThread; QT_END_NAMESPACE QTCONTACTS_USE_NAMESPACE class MaliciousThreadObject; class MaliciousAsyncManagerEngine : public QContactManagerEngine { Q_OBJECT public: MaliciousAsyncManagerEngine(); ~MaliciousAsyncManagerEngine(); QString managerName() const; bool startRequest(QContactAbstractRequest* req); bool cancelRequest(QContactAbstractRequest *req); QMap managerParameters() const {return QMap();} int managerVersion() const {return 0;} QList contactIds(const QContactFilter& filter, const QList& sort, QContactManager::Error* error) const { return QContactManagerEngine::contactIds(filter, sort, error); } QList contacts(const QContactFilter& filter, const QList& sort, const QContactFetchHint& fetch, QContactManager::Error* error) const { return QContactManagerEngine::contacts(filter, sort, fetch, error); } QContact contact(const QContactId& id, const QContactFetchHint& fetch, QContactManager::Error* error) const { return QContactManagerEngine::contact(id, fetch, error); } bool saveContacts(QList* contacts, QMap* errorMap, QContactManager::Error* error) { return QContactManagerEngine::saveContacts(contacts, errorMap, error); } bool removeContacts(const QList& contacts, QMap* errorMap, QContactManager::Error* error) { return QContactManagerEngine::removeContacts(contacts, errorMap, error); } /* "Self" contact id (MyCard) */ bool setSelfContactId(const QContactId& id, QContactManager::Error* error) { return QContactManagerEngine::setSelfContactId(id, error); } QContactId selfContactId(QContactManager::Error* error) const { return QContactManagerEngine::selfContactId(error); } /* Relationships between contacts */ QList relationships(const QString& relType, const QContact& contact, QContactRelationship::Role role, QContactManager::Error* error) const { return QContactManagerEngine::relationships(relType, contact, role, error); } bool saveRelationships(QList* relationships, QMap* errorMap, QContactManager::Error* error) { return QContactManagerEngine::saveRelationships(relationships, errorMap, error); } bool removeRelationships(const QList& relationships, QMap* errorMap, QContactManager::Error* error) { return QContactManagerEngine::removeRelationships(relationships, errorMap, error); } /* Contact validation */ bool validateContact(const QContact& contact, QContactManager::Error* error) const { return QContactManagerEngine::validateContact(contact, error); } /* Asynchronous Request Support */ void requestDestroyed(QContactAbstractRequest* req); bool waitForRequestFinished(QContactAbstractRequest* req, int msecs) {return QContactManagerEngine::waitForRequestFinished(req, msecs);} bool isRelationshipTypeSupported(const QString& relType, QContactType::TypeValues type) const { return QContactManagerEngine::isRelationshipTypeSupported(relType, type); } bool isFilterSupported(const QContactFilter& fil) const { return QContactManagerEngine::isFilterSupported(fil); } QList supportedDataTypes() const { return QContactManagerEngine::supportedDataTypes(); } QList supportedContactTypes() const { return QContactManagerEngine::supportedContactTypes(); } signals: void doStartRequest(QContactAbstractRequest *req); void doFinishRequest(QContactAbstractRequest *req); private: QThread* thread; MaliciousThreadObject* threadObject; }; class MaliciousEngineFactory : public QContactManagerEngineFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QContactManagerEngineFactoryInterface" FILE "malicious.json") public: QContactManagerEngine* engine(const QMap& parameters, QContactManager::Error* error); QContactEngineId* createContactEngineId(const QMap& parameters, const QString& engineIdString) const; QString managerName() const; private: MaliciousAsyncManagerEngine mame; }; #endif tests/auto/contacts/qcontactasync/qcontactasync.pro000066400000000000000000000001041233466112000232450ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS += maliciousplugin \ unittest tests/auto/contacts/qcontactasync/unittest/000077500000000000000000000000001233466112000215355ustar00rootroot00000000000000tests/auto/contacts/qcontactasync/unittest/partitions.json000066400000000000000000000001211233466112000246160ustar00rootroot00000000000000[ { "name" : "com.nokia.mt.User" }, { "name" : "com.nokia.mt.System" } ] tests/auto/contacts/qcontactasync/unittest/tst_qcontactasync.cpp000066400000000000000000003135771233466112000260250ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "jsondbprocess.h" #include "qcontactidmock.h" #include "qcontactmanagerdataholder.h" //QContactManagerDataHolder QTCONTACTS_USE_NAMESPACE /* Define an innocuous request (fetch ie doesn't mutate) to "fill up" any queues */ #define FILL_QUEUE_WITH_FETCH_REQUESTS() QContactFetchRequest fqcfr1, fqcfr2, fqcfr3; \ fqcfr1.start(); \ fqcfr2.start(); \ fqcfr3.start(); //TESTED_COMPONENT=src/contacts // Unfortunately the plumbing isn't in place to allow cancelling requests at arbitrary points // in their processing. So we do multiple loops until things work out.. or not #define MAX_OPTIMISTIC_SCHEDULING_LIMIT 100 // Thread capable QThreadSignalSpy (to avoid data races with count/appendArgS) class QThreadSignalSpy: public QObject { public: QThreadSignalSpy(QObject *obj, const char *aSignal) { QMutexLocker m(&lock); #ifdef Q_CC_BOR const int memberOffset = QObject::staticMetaObject.methodCount(); #else static const int memberOffset = QObject::staticMetaObject.methodCount(); #endif Q_ASSERT(obj); Q_ASSERT(aSignal); if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE) { qWarning("QThreadSignalSpy: Not a valid signal, use the SIGNAL macro"); return; } QByteArray ba = QMetaObject::normalizedSignature(aSignal + 1); const QMetaObject *mo = obj->metaObject(); int sigIndex = mo->indexOfMethod(ba.constData()); if (sigIndex < 0) { qWarning("QThreadSignalSpy: No such signal: '%s'", ba.constData()); return; } if (!QMetaObject::connect(obj, sigIndex, this, memberOffset, Qt::DirectConnection, 0)) { qWarning("QThreadSignalSpy: QMetaObject::connect returned false. Unable to connect."); return; } sig = ba; initArgs(mo->method(sigIndex)); } inline bool isValid() const { return !sig.isEmpty(); } inline QByteArray signal() const { return sig; } int qt_metacall(QMetaObject::Call call, int methodId, void **a) { methodId = QObject::qt_metacall(call, methodId, a); if (methodId < 0) return methodId; if (call == QMetaObject::InvokeMetaMethod) { if (methodId == 0) { appendArgs(a); } --methodId; } return methodId; } // The QList API we actually use int count() const { QMutexLocker m(&lock); return savedArgs.count(); } void clear() { QMutexLocker m(&lock); savedArgs.clear(); } private: void initArgs(const QMetaMethod &member) { QList params = member.parameterTypes(); for (int i = 0; i < params.count(); ++i) { int tp = QMetaType::type(params.at(i).constData()); if (tp == QMetaType::Void) qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.", params.at(i).constData()); args << tp; } } void appendArgs(void **a) { QMutexLocker m(&lock); QList list; for (int i = 0; i < args.count(); ++i) { QMetaType::Type type = static_cast(args.at(i)); list << QVariant(type, a[i + 1]); } savedArgs.append(list); } // the full, normalized signal name QByteArray sig; // holds the QMetaType types for the argument list of the signal QList args; mutable QMutex lock; // Different API QList< QVariantList> savedArgs; }; class tst_QContactAsync : public QObject { Q_OBJECT public: tst_QContactAsync(); virtual ~tst_QContactAsync(); public slots: void initTestCase(); void cleanupTestCase(); private: void addManagers(QStringList includes = QStringList()); // add standard managers to the data private slots: void testDestructor(); void testDestructor_data() { addManagers(QStringList(QString("maliciousplugin"))); } void contactFetch(); void contactFetch_data() { addManagers(); } void contactFetchById(); void contactFetchById_data() { addManagers(); } void contactFetchByIdWithEmptyIds(); void contactFetchByIdWithEmptyIds_data() { addManagers(); } void contactFetchByIdMixingEmptyIds(); void contactFetchByIdMixingEmptyIds_data() { addManagers(); } void contactFetchByIdWithNonExistingButValidIds(); void contactFetchByIdWithNonExistingButValidIds_data() { addManagers(); } void contactFetchByIdErrorHandling(); void contactFetchByIdErrorHandling_data() { addManagers(); } void contactIdFetch(); void contactIdFetch_data() { addManagers(); } void contactRemove(); void contactRemove_data() { addManagers(); } void contactRemoveErrorHandling(); void contactRemoveErrorHandling_data() {addManagers();} void contactSave(); void contactSave_data() { addManagers(); } void contactSaveErrorHandling(); void contactSaveErrorHandling_data() { addManagers(); } void contactSaveRemovedContacts(); void contactSaveRemovedContacts_data() { addManagers(); } void contactSaveRemovedContactsWithCleanIds(); void contactSaveRemovedContactsWithCleanIds_data() { addManagers(); } void contactPartialSave(); void contactPartialSave_data() { addManagers(); } void contactPartialSaveAsync(); void contactPartialSaveAsync_data() {addManagers();} void relationshipFetch(); void relationshipFetch_data() { addManagers(); } void relationshipRemove(); void relationshipRemove_data() { addManagers(); } void relationshipSave(); void relationshipSave_data() { addManagers(); } void maliciousManager(); // uses it's own custom data (manager) void testQuickDestruction(); void testQuickDestruction_data() { addManagers(QStringList(QString("maliciousplugin"))); } void threadDelivery(); void threadDelivery_data() { addManagers(QStringList(QString("maliciousplugin"))); } protected slots: void resultsAvailableReceived(); private: bool compareContactLists(QList lista, QList listb); bool compareContacts(QContact ca, QContact cb); bool containsIgnoringTimestamps(const QList& list, const QContact& c); bool compareIgnoringTimestamps(const QContact& ca, const QContact& cb); QContactManager* prepareModel(const QString& uri); Qt::HANDLE m_mainThreadId; Qt::HANDLE m_resultsAvailableSlotThreadId; QScopedPointer managerDataHolder; JsonDbProcess m_jsondbProcess; }; tst_QContactAsync::tst_QContactAsync() { // ensure we can load all of the plugins we need to. QString path = QCoreApplication::applicationDirPath() + QStringLiteral("/dummyplugin/plugins"); QCoreApplication::addLibraryPath(path); qRegisterMetaType("QContactAbstractRequest::State"); // Start JsonDb daemon if needed if (QContactManager::availableManagers().contains("jsondb")) { QString partitions_json = QFINDTESTDATA("partitions.json"); QVERIFY2(!partitions_json.isEmpty(), "partitions.json file is missing"); QVERIFY2(m_jsondbProcess.start(partitions_json), "Failed to start JsonDb process"); } } tst_QContactAsync::~tst_QContactAsync() { if (QContactManager::availableManagers().contains("jsondb")) m_jsondbProcess.terminate(); } void tst_QContactAsync::initTestCase() { managerDataHolder.reset(new QContactManagerDataHolder()); } void tst_QContactAsync::cleanupTestCase() { managerDataHolder.reset(0); } bool tst_QContactAsync::compareContactLists(QList lista, QList listb) { // NOTE: This compare is contact order insensitive. // Remove matching contacts foreach (QContact a, lista) { foreach (QContact b, listb) { if (compareContacts(a, b)) { lista.removeOne(a); listb.removeOne(b); break; } } } return (lista.count() == 0 && listb.count() == 0); } bool tst_QContactAsync::compareContacts(QContact ca, QContact cb) { // NOTE: This compare is contact detail order insensitive. if (ca.id() != cb.id()) return false; QList aDetails = ca.details(); QList bDetails = cb.details(); // Remove matching details foreach (QContactDetail ad, aDetails) { foreach (QContactDetail bd, bDetails) { if (ad == bd) { ca.removeDetail(&ad); cb.removeDetail(&bd); break; } // Special handling for timestamp if (ad.type() == QContactTimestamp::Type && bd.type() == QContactTimestamp::Type) { QContactTimestamp at = static_cast(ad); QContactTimestamp bt = static_cast(bd); if (at.created().toString() == bt.created().toString() && at.lastModified().toString() == bt.lastModified().toString()) { ca.removeDetail(&ad); cb.removeDetail(&bd); break; } } } } return (ca == cb); } bool tst_QContactAsync::containsIgnoringTimestamps(const QList& list, const QContact& c) { QList cl = list; QContact a(c); for (int i = 0; i < cl.size(); i++) { QContact b(cl.at(i)); if (compareIgnoringTimestamps(a, b)) return true; } return false; } bool tst_QContactAsync::compareIgnoringTimestamps(const QContact& ca, const QContact& cb) { // Compares two contacts, ignoring any timestamp details QContact a(ca); QContact b(cb); QList aDetails = a.details(); QList bDetails = b.details(); // They can be in any order, so loop // First remove any matches, and any timestamps foreach (QContactDetail d, aDetails) { foreach (QContactDetail d2, bDetails) { if (d == d2) { a.removeDetail(&d); b.removeDetail(&d2); break; } if (d.type() == QContactTimestamp::Type) { a.removeDetail(&d); } if (d2.type() == QContactTimestamp::Type) { b.removeDetail(&d2); } } } if (a == b) return true; return false; } void tst_QContactAsync::testDestructor() { QFETCH(QString, uri); QContactManager* cm = prepareModel(uri); QContactFetchRequest* req = new QContactFetchRequest; req->setManager(cm); QContactManager* cm2 = prepareModel(uri); QContactFetchRequest* req2 = new QContactFetchRequest; req2->setManager(cm2); // first, delete manager then request delete cm; delete req; // second, delete request then manager delete req2; delete cm2; } void tst_QContactAsync::contactFetch() { QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); QContactFetchRequest cfr; QVERIFY(cfr.type() == QContactAbstractRequest::ContactFetchRequest); // initial state - not started, no manager. QVERIFY(!cfr.isActive()); QVERIFY(!cfr.isFinished()); QVERIFY(!cfr.start()); QVERIFY(!cfr.cancel()); QVERIFY(!cfr.waitForFinished()); // "all contacts" retrieval QContactFilter fil; cfr.setManager(cm.data()); QCOMPARE(cfr.manager(), cm.data()); QVERIFY(!cfr.isActive()); QVERIFY(!cfr.isFinished()); QVERIFY(!cfr.cancel()); QVERIFY(!cfr.waitForFinished()); qRegisterMetaType("QContactFetchRequest*"); QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QContactAbstractRequest::State))); cfr.setFilter(fil); QCOMPARE(cfr.filter(), fil); QVERIFY(!cfr.cancel()); // not started QVERIFY(cfr.start()); //QVERIFY(cfr.isFinished() || !cfr.start()); // already started. // thread scheduling means this is untestable QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); QVERIFY(cfr.waitForFinished()); QVERIFY(cfr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList contactIds = cm->contactIds(); QList contacts = cfr.contacts(); QCOMPARE(contactIds.size(), contacts.size()); for (int i = 0; i < contactIds.size(); i++) { QContact curr = cm->contact(contactIds.at(i)); QVERIFY(contacts.at(i) == curr); } // asynchronous detail filtering QContactDetailFilter dfil; dfil.setDetailType(QContactUrl::Type, QContactUrl::FieldUrl); cfr.setFilter(dfil); QVERIFY(cfr.filter() == dfil); QVERIFY(!cfr.cancel()); // not started QVERIFY(cfr.start()); QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); //QVERIFY(cfr.isFinished() || !cfr.start()); // already started. // thread scheduling means this is untestable QVERIFY(cfr.waitForFinished()); QVERIFY(cfr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); contactIds = cm->contactIds(dfil); contacts = cfr.contacts(); QCOMPARE(contactIds.size(), contacts.size()); for (int i = 0; i < contactIds.size(); i++) { QContact curr = cm->contact(contactIds.at(i)); QVERIFY(contacts.at(i) == curr); } // sort order QContactSortOrder sortOrder; sortOrder.setDetailType(QContactPhoneNumber::Type, QContactPhoneNumber::FieldNumber); QList sorting; sorting.append(sortOrder); cfr.setFilter(fil); cfr.setSorting(sorting); QCOMPARE(cfr.sorting(), sorting); QVERIFY(!cfr.cancel()); // not started QVERIFY(cfr.start()); QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); //QVERIFY(cfr.isFinished() || !cfr.start()); // already started. // thread scheduling means this is untestable QVERIFY(cfr.waitForFinished()); QVERIFY(cfr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); contactIds = cm->contactIds(sorting); contacts = cfr.contacts(); QCOMPARE(contactIds.size(), contacts.size()); for (int i = 0; i < contactIds.size(); i++) { QContact curr = cm->contact(contactIds.at(i)); QVERIFY(contacts.at(i) == curr); } // restrictions sorting.clear(); cfr.setFilter(fil); cfr.setSorting(sorting); QContactFetchHint fetchHint; QList typeHints; typeHints << QContactName::Type; fetchHint.setDetailTypesHint(typeHints); cfr.setFetchHint(fetchHint); QCOMPARE(cfr.fetchHint().detailTypesHint(), typeHints); QVERIFY(!cfr.cancel()); // not started QVERIFY(cfr.start()); QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); //QVERIFY(cfr.isFinished() || !cfr.start()); // already started. // thread scheduling means this is untestable QVERIFY(cfr.waitForFinished()); QVERIFY(cfr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); contactIds = cm->contactIds(sorting); contacts = cfr.contacts(); QCOMPARE(contactIds.size(), contacts.size()); for (int i = 0; i < contactIds.size(); i++) { // create a contact from the restricted data only (id) QContact currFull = cm->contact(contactIds.at(i)); QContact currRestricted; currRestricted.setId(currFull.id()); QList names = currFull.details(); foreach (const QContactName& name, names) { QContactName fullName = name; if (!fullName.isEmpty()) { currRestricted.saveDetail(&fullName); } } // now find the contact in the retrieved list which our restricted contact mimics QContact retrievedRestricted; bool found = false; foreach (const QContact& retrieved, contacts) { if (retrieved.id() == currRestricted.id()) { retrievedRestricted = retrieved; found = true; } } QVERIFY(found); // must exist or fail. // ensure that the contact is the same (except synth fields) QList retrievedDetails = retrievedRestricted.details(); QList expectedDetails = currRestricted.details(); foreach (const QContactDetail& det, expectedDetails) { // ignore backend synthesised details // again, this requires a "default contact details" function to work properly. if (det.type() == QContactTimestamp::Type) { continue; } // everything else in the expected contact should be in the retrieved one. QVERIFY(retrievedDetails.contains(det)); } } // cancelling sorting.clear(); cfr.setFilter(fil); cfr.setSorting(sorting); cfr.setFetchHint(QContactFetchHint()); if (cm->managerName() == "jsondb") QSKIP("JSONDB backend does not support request cancelling, skipping..."); int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. while (true) { QVERIFY(!cfr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(); QVERIFY(cfr.start()); if (!cfr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. spy.clear(); cfr.waitForFinished(); sorting.clear(); cfr.setFilter(fil); cfr.setSorting(sorting); cfr.setFetchHint(QContactFetchHint()); cfr.setFetchHint(QContactFetchHint()); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } continue; } // if we get here, then we are cancelling the request. QVERIFY(cfr.waitForFinished()); QVERIFY(cfr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); break; } // restart, and wait for progress after cancel. while (true) { QVERIFY(!cfr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(); QVERIFY(cfr.start()); if (!cfr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. cfr.waitForFinished(); sorting.clear(); cfr.setFilter(fil); cfr.setSorting(sorting); cfr.setFetchHint(QContactFetchHint()); bailoutCount -= 1; spy.clear(); if (!bailoutCount) { //qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } continue; } cfr.waitForFinished(); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); QVERIFY(!cfr.isActive()); QVERIFY(cfr.state() == QContactAbstractRequest::CanceledState); break; } } void tst_QContactAsync::contactFetchById() { QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); QContactFetchByIdRequest cfr; QVERIFY(cfr.type() == QContactAbstractRequest::ContactFetchByIdRequest); // initial state - not started, no manager. QVERIFY(!cfr.isActive()); QVERIFY(!cfr.isFinished()); QVERIFY(!cfr.start()); QVERIFY(!cfr.cancel()); QVERIFY(!cfr.waitForFinished()); // get all contact ids QList contactIds(cm->contactIds()); // "all contacts" retrieval cfr.setManager(cm.data()); cfr.setIds(contactIds); QCOMPARE(cfr.manager(), cm.data()); QVERIFY(!cfr.isActive()); QVERIFY(!cfr.isFinished()); QVERIFY(!cfr.cancel()); QVERIFY(!cfr.waitForFinished()); qRegisterMetaType("QContactFetchByIdRequest*"); QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QContactAbstractRequest::State))); QVERIFY(!cfr.cancel()); // not started QVERIFY(cfr.start()); //QVERIFY(cfr.isFinished() || !cfr.start()); // already started. // thread scheduling means this is untestable QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); QVERIFY(cfr.waitForFinished()); QVERIFY(cfr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList contacts = cfr.contacts(); QCOMPARE(contactIds.size(), contacts.size()); for (int i = 0; i < contactIds.size(); i++) { QContact curr = cm->contact(contactIds.at(i)); QVERIFY(contacts.at(i) == curr); } } void tst_QContactAsync::contactFetchByIdWithEmptyIds() { QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); QContactFetchByIdRequest cfr; QVERIFY(cfr.type() == QContactAbstractRequest::ContactFetchByIdRequest); // List of qmpty ids. QList contactIds; contactIds.append(QContactId()); contactIds.append(QContactId()); contactIds.append(QContactId()); int expectedCount = 3; // "all contacts" retrieval cfr.setManager(cm.data()); cfr.setIds(contactIds); QCOMPARE(cfr.manager(), cm.data()); QVERIFY(!cfr.isActive()); QVERIFY(!cfr.isFinished()); QVERIFY(!cfr.cancel()); QVERIFY(!cfr.waitForFinished()); qRegisterMetaType("QContactFetchByIdRequest*"); QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QContactAbstractRequest::State))); QVERIFY(!cfr.cancel()); // not started QVERIFY(cfr.start()); QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); QVERIFY(cfr.waitForFinished()); QVERIFY(cfr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList contacts = cfr.contacts(); QCOMPARE(contacts.size(), expectedCount); int contactIndex = 0; foreach (const QContactId id, contactIds) { QCOMPARE(contacts.at(contactIndex), QContact()); QCOMPARE(cfr.errorMap().value(contactIndex),QContactManager::DoesNotExistError); contactIndex++; } } void tst_QContactAsync::contactFetchByIdMixingEmptyIds() { QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); QContactFetchByIdRequest cfr; QVERIFY(cfr.type() == QContactAbstractRequest::ContactFetchByIdRequest); // List of ids contains few empty ones among good ones. int expectedCount = cm->contactIds().size(); QList contactIds; foreach (const QContactId id, cm->contactIds()) { contactIds.append(QContactId()); expectedCount ++; contactIds.append(id); } contactIds.append(QContactId()); expectedCount++; // "Contacts" retrieval cfr.setManager(cm.data()); cfr.setIds(contactIds); QCOMPARE(cfr.manager(), cm.data()); QVERIFY(!cfr.isActive()); QVERIFY(!cfr.isFinished()); QVERIFY(!cfr.cancel()); QVERIFY(!cfr.waitForFinished()); qRegisterMetaType("QContactFetchByIdRequest*"); QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QContactAbstractRequest::State))); QVERIFY(!cfr.cancel()); // not started QVERIFY(cfr.start()); QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); QVERIFY(cfr.waitForFinished()); QVERIFY(cfr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList contacts = cfr.contacts(); QCOMPARE(contacts.size(), expectedCount); int contactIndex = 0; foreach (const QContactId id, contactIds) { if (id == QContactId()) { QCOMPARE(contacts.at(contactIndex), QContact()); QCOMPARE(cfr.errorMap().value(contactIndex),QContactManager::DoesNotExistError); } else { QCOMPARE(contacts.at(contactIndex).id(), id); } contactIndex++; } } void tst_QContactAsync::contactFetchByIdWithNonExistingButValidIds() { QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); QContactFetchByIdRequest cfr; QVERIFY(cfr.type() == QContactAbstractRequest::ContactFetchByIdRequest); // List of ids contains few empty ones among good ones. int expectedCount = 0; QList contactIds; foreach (const QContactId id, cm->contactIds()) { QVERIFY(cm->removeContact(id)); contactIds.append(id); expectedCount ++; } // "Contacts" retrieval cfr.setManager(cm.data()); cfr.setIds(contactIds); QCOMPARE(cfr.manager(), cm.data()); QVERIFY(!cfr.isActive()); QVERIFY(!cfr.isFinished()); QVERIFY(!cfr.cancel()); QVERIFY(!cfr.waitForFinished()); qRegisterMetaType("QContactFetchByIdRequest*"); QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QContactAbstractRequest::State))); QVERIFY(!cfr.cancel()); // not started QVERIFY(cfr.start()); QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); QVERIFY(cfr.waitForFinished()); QVERIFY(cfr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList contacts = cfr.contacts(); QCOMPARE(contacts.size(), expectedCount); int contactIndex = 0; foreach (const QContactId id, contactIds) { QCOMPARE(contacts.at(contactIndex), QContact()); QCOMPARE(cfr.errorMap().value(contactIndex),QContactManager::DoesNotExistError); contactIndex++; } } void tst_QContactAsync::contactFetchByIdErrorHandling() { // Testing error handling by fetching by non existing local ids. QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); QContactFetchByIdRequest cfrCausingErrors; QVERIFY(cfrCausingErrors.type() == QContactAbstractRequest::ContactFetchByIdRequest); // Check initial state - not started, no manager. QVERIFY(!cfrCausingErrors.isActive()); QVERIFY(!cfrCausingErrors.isFinished()); QVERIFY(!cfrCausingErrors.start()); QVERIFY(!cfrCausingErrors.cancel()); QVERIFY(!cfrCausingErrors.waitForFinished()); // Prepare non-existing contact ids for request we like to result. QList nonExistingContactIds; nonExistingContactIds << QContactId() << QContactId() << QContactId(); // "Make contacts" retrieval with some extra checking. cfrCausingErrors.setManager(cm.data()); cfrCausingErrors.setIds(nonExistingContactIds); QCOMPARE(cfrCausingErrors.manager(), cm.data()); QVERIFY(!cfrCausingErrors.isActive()); QVERIFY(!cfrCausingErrors.isFinished()); QVERIFY(!cfrCausingErrors.cancel()); QVERIFY(!cfrCausingErrors.waitForFinished()); qRegisterMetaType("QContactFetchByIdRequest*"); QThreadSignalSpy spy2(&cfrCausingErrors, SIGNAL(stateChanged(QContactAbstractRequest::State))); QVERIFY(!cfrCausingErrors.cancel()); // not started QVERIFY(cfrCausingErrors.start()); QVERIFY((cfrCausingErrors.isActive() && cfrCausingErrors.state() == QContactAbstractRequest::ActiveState) || cfrCausingErrors.isFinished()); QVERIFY(cfrCausingErrors.waitForFinished()); QVERIFY(cfrCausingErrors.isFinished()); QVERIFY(spy2.count() >= 1); // active + finished progress signals spy2.clear(); // Check errors, they should be stored in the errorMap using index of local id in the request as a key for the error. // Note, the returned values are actually set in to the errorMap by common code in qcontactmanagerenginev2wrapper_p.cpp. QVERIFY(cfrCausingErrors.errorMap().value(0) == QContactManager::DoesNotExistError); QVERIFY(cfrCausingErrors.errorMap().value(1) == QContactManager::DoesNotExistError); QVERIFY(cfrCausingErrors.errorMap().value(2) == QContactManager::DoesNotExistError); } void tst_QContactAsync::contactIdFetch() { QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); QContactIdFetchRequest cfr; QVERIFY(cfr.type() == QContactAbstractRequest::ContactIdFetchRequest); // initial state - not started, no manager. QVERIFY(!cfr.isActive()); QVERIFY(!cfr.isFinished()); QVERIFY(!cfr.start()); QVERIFY(!cfr.cancel()); QVERIFY(!cfr.waitForFinished()); // "all contacts" retrieval QContactFilter fil; cfr.setManager(cm.data()); QCOMPARE(cfr.manager(), cm.data()); QVERIFY(!cfr.isActive()); QVERIFY(!cfr.isFinished()); QVERIFY(!cfr.cancel()); QVERIFY(!cfr.waitForFinished()); qRegisterMetaType("QContactIdFetchRequest*"); QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QContactAbstractRequest::State))); cfr.setFilter(fil); QCOMPARE(cfr.filter(), fil); QVERIFY(!cfr.cancel()); // not started QVERIFY(cfr.start()); QVERIFY((cfr.isActive() &&cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); //QVERIFY(cfr.isFinished() || !cfr.start()); // already started. // thread scheduling means this is untestable QVERIFY(cfr.waitForFinished()); QVERIFY(cfr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList contactIds = cm->contactIds(); QList result = cfr.ids(); QCOMPARE(contactIds, result); // asynchronous detail filtering QContactDetailFilter dfil; dfil.setDetailType(QContactUrl::Type, QContactUrl::FieldUrl); cfr.setFilter(dfil); QVERIFY(cfr.filter() == dfil); QVERIFY(!cfr.cancel()); // not started QVERIFY(cfr.start()); QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); //QVERIFY(cfr.isFinished() || !cfr.start()); // already started. // thread scheduling means this is untestable QVERIFY(cfr.waitForFinished()); QVERIFY(cfr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); contactIds = cm->contactIds(dfil); result = cfr.ids(); QCOMPARE(contactIds, result); // sort order QContactSortOrder sortOrder; sortOrder.setDetailType(QContactPhoneNumber::Type, QContactPhoneNumber::FieldNumber); QList sorting; sorting.append(sortOrder); cfr.setFilter(fil); cfr.setSorting(sorting); QCOMPARE(cfr.sorting(), sorting); QVERIFY(!cfr.cancel()); // not started QVERIFY(cfr.start()); QVERIFY((cfr.isActive() && cfr.state() == QContactAbstractRequest::ActiveState) || cfr.isFinished()); //QVERIFY(cfr.isFinished() || !cfr.start()); // already started. // thread scheduling means this is untestable QVERIFY(cfr.waitForFinished()); QVERIFY(cfr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); contactIds = cm->contactIds(sorting); result = cfr.ids(); QCOMPARE(contactIds, result); // cancelling sorting.clear(); cfr.setFilter(fil); cfr.setSorting(sorting); if (cm->managerName() == "jsondb") QSKIP("JSONDB backend does not support request cancelling, skipping..."); int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. while (true) { QVERIFY(!cfr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(); QVERIFY(cfr.start()); if (!cfr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. cfr.waitForFinished(); sorting.clear(); cfr.setFilter(fil); cfr.setSorting(sorting); bailoutCount -= 1; spy.clear(); if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } continue; } // if we get here, then we are cancelling the request. QVERIFY(cfr.waitForFinished()); QVERIFY(cfr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); break; } // restart, and wait for progress after cancel. while (true) { QVERIFY(!cfr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(); QVERIFY(cfr.start()); if (!cfr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. cfr.waitForFinished(); sorting.clear(); cfr.setFilter(fil); cfr.setSorting(sorting); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } continue; } cfr.waitForFinished(); QVERIFY(cfr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); break; } // Error cases not tested here as can not generate common error case // for all backends. For example, for jsondb backend can not generate // error at all. } void tst_QContactAsync::contactRemove() { QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); QContactRemoveRequest crr; QVERIFY(crr.type() == QContactAbstractRequest::ContactRemoveRequest); // initial state - not started, no manager. QVERIFY(!crr.isActive()); QVERIFY(!crr.isFinished()); QVERIFY(!crr.start()); QVERIFY(!crr.cancel()); QVERIFY(!crr.waitForFinished()); // specific contact set crr.setContactId(cm->contactIds()[0]); QVERIFY(crr.contactIds() == QList() << cm->contactIds()[0]); // specific contact removal via detail filter int originalCount = cm->contactIds().size(); QContactDetailFilter dfil; dfil.setDetailType(QContactUrl::Type); crr.setContactIds(cm->contactIds(dfil)); crr.setManager(cm.data()); QCOMPARE(crr.manager(), cm.data()); QVERIFY(!crr.isActive()); QVERIFY(!crr.isFinished()); QVERIFY(!crr.cancel()); QVERIFY(!crr.waitForFinished()); qRegisterMetaType("QContactRemoveRequest*"); QThreadSignalSpy spy(&crr, SIGNAL(stateChanged(QContactAbstractRequest::State))); QVERIFY(!crr.cancel()); // not started QVERIFY(!cm->contactIds(dfil).isEmpty()); QVERIFY(crr.start()); QVERIFY((crr.isActive() &&crr.state() == QContactAbstractRequest::ActiveState) || crr.isFinished()); //QVERIFY(crr.isFinished() || !crr.start()); // already started. // thread scheduling means this is untestable QVERIFY(crr.waitForFinished()); QVERIFY(crr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QCOMPARE(cm->contactIds().size(), originalCount - 1); QVERIFY(cm->contactIds(dfil).isEmpty()); // remove all contacts // delete everything. crr.setContactIds(cm->contactIds()); QVERIFY(!crr.cancel()); // not started QVERIFY(crr.start()); QVERIFY((crr.isActive() && crr.state() == QContactAbstractRequest::ActiveState) || crr.isFinished()); //QVERIFY(crr.isFinished() || !crr.start()); // already started. // thread scheduling means this is untestable QVERIFY(crr.waitForFinished()); QVERIFY(crr.isFinished()); QCOMPARE(cm->contactIds().size(), 0); // no contacts should be left. QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); // cancelling QContact temp; QContactName nameDetail; nameDetail.setFirstName("Should not be removed"); temp.saveDetail(&nameDetail); cm->saveContact(&temp); crr.setContactIds(cm->contactIds(dfil)); if (cm->managerName() == "jsondb") QSKIP("JSONDB backend does not support request cancelling, skipping..."); // attempt to cancel 40 times. If it doesn't work due to threading, bail out. int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; while (true) { QVERIFY(!crr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(); QVERIFY(spy.count() == 0); QVERIFY(crr.start()); if (!crr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. crr.waitForFinished(); crr.setContactIds(cm->contactIds(dfil)); temp.setId(QContactId()); if (!cm->saveContact(&temp)) { QSKIP("Unable to save temporary contact for remove request cancellation test!"); } bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } spy.clear(); continue; } // if we get here, then we are cancelling the request. QVERIFY(crr.waitForFinished()); QVERIFY(crr.isCanceled()); QCOMPARE(cm->contactIds().size(), 1); QCOMPARE(cm->contactIds(), crr.contactIds()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); break; } // restart, and wait for progress after cancel. while (true) { QVERIFY(!crr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(); QVERIFY(crr.start()); if (!crr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. crr.waitForFinished(); crr.setContactIds(cm->contactIds(dfil)); temp.setId(QContactId()); cm->saveContact(&temp); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } spy.clear(); continue; } crr.waitForFinished(); QVERIFY(crr.isCanceled()); QCOMPARE(cm->contactIds().size(), 1); QCOMPARE(cm->contactIds(), crr.contactIds()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); break; } } void tst_QContactAsync::contactRemoveErrorHandling() { QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); // Make save request and make initial setup for it with some verifications. QContactSaveRequest csr; QVERIFY(csr.type() == QContactAbstractRequest::ContactSaveRequest); csr.setManager(cm.data()); QCOMPARE(csr.manager(), cm.data()); QVERIFY(!csr.isActive()); QVERIFY(!csr.isFinished()); QVERIFY(!csr.cancel()); QVERIFY(!csr.waitForFinished()); qRegisterMetaType("QContactSaveRequest*"); QThreadSignalSpy spy(&csr, SIGNAL(stateChanged(QContactAbstractRequest::State))); // Setup a two identical lists of contacts for testing save and remove. QContact testContact1, testContact2, testContact3, testContact4, testContact5, testContact6; QContactName nameDetail; nameDetail.setFirstName("Test Contact1"); testContact1.saveDetail(&nameDetail); nameDetail.setFirstName("Test Contact2"); testContact2.saveDetail(&nameDetail); nameDetail.setFirstName("Test Contact3"); testContact3.saveDetail(&nameDetail); nameDetail.setFirstName("Test Contact4"); testContact4.saveDetail(&nameDetail); nameDetail.setFirstName("Test Contact5"); testContact5.saveDetail(&nameDetail); nameDetail.setFirstName("Test Contact6"); testContact6.saveDetail(&nameDetail); QList saveList, testContacts; saveList << testContact1 << testContact2 << testContact3 << testContact4 << testContact5 << testContact6; testContacts << testContact1 << testContact2 << testContact3 << testContact4 << testContact5 << testContact6; // Check save requests takes test contacts ok, start save and wait for finished. csr.setContacts(testContacts); QCOMPARE(csr.contacts(), saveList); QVERIFY(!csr.cancel()); // not started QVERIFY(csr.start()); QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished()); QVERIFY(csr.waitForFinished()); QVERIFY(csr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); // foreach (QContact testc, csr.contacts()) { // qDebug() << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> CONTACT: " << testc.id().managerUri(); // qDebug() << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> CONTACT: " << testc.id().id(); // } // qDebug() << "Returned errors:" << csr.errorMap(); QVERIFY(csr.errorMap().isEmpty()); // Setup remove initial remove request and verify it takes data ok. QContactRemoveRequest contactRemoveRequest; QVERIFY(contactRemoveRequest.type() == QContactAbstractRequest::ContactRemoveRequest); contactRemoveRequest.setManager(cm.data()); QCOMPARE(contactRemoveRequest.manager(), cm.data()); QVERIFY(!contactRemoveRequest.isActive()); QVERIFY(!contactRemoveRequest.isFinished()); QVERIFY(!contactRemoveRequest.cancel()); QVERIFY(!contactRemoveRequest.waitForFinished()); qRegisterMetaType("QContactSaveRequest*"); QThreadSignalSpy spy2(&contactRemoveRequest, SIGNAL(stateChanged(QContactAbstractRequest::State))); // Setup valid and invalid contact ids for remove request, start it and wait for finished. QContactId emptyId; QContactId failingId = QContactIdMock::createId("Failing", 0); QList toRemove; toRemove << emptyId << cm.data()->contactIds(); toRemove.insert(3, emptyId); toRemove.insert(4, failingId); toRemove << emptyId << failingId << failingId; // qDebug() << "TO REMOVE: " << toRemove; contactRemoveRequest.setContactIds(toRemove); QCOMPARE(contactRemoveRequest.contactIds(), toRemove); QVERIFY(!contactRemoveRequest.cancel()); // not started QVERIFY(contactRemoveRequest.start()); QVERIFY((contactRemoveRequest.isActive() && contactRemoveRequest.state() == QContactAbstractRequest::ActiveState) || contactRemoveRequest.isFinished()); QVERIFY(contactRemoveRequest.waitForFinished()); QVERIFY(contactRemoveRequest.isFinished()); QVERIFY(spy2.count() >= 1); // active + finished progress signals spy2.clear(); // Check ok and errors, empty ids in the beginning, middle and in the end of the contact id list, // qDebug() << "Returned errors:" << contactRemoveRequest.errorMap(); QVERIFY(contactRemoveRequest.errorMap().value(0) == QContactManager::DoesNotExistError); QVERIFY(contactRemoveRequest.errorMap().value(1) == QContactManager::NoError); QVERIFY(contactRemoveRequest.errorMap().value(2) == QContactManager::NoError); QVERIFY(contactRemoveRequest.errorMap().value(3) == QContactManager::DoesNotExistError); QVERIFY(contactRemoveRequest.errorMap().value(4) == QContactManager::DoesNotExistError); QVERIFY(contactRemoveRequest.errorMap().value(5) == QContactManager::NoError); QVERIFY(contactRemoveRequest.errorMap().value(6) == QContactManager::NoError); QVERIFY(contactRemoveRequest.errorMap().value(7) == QContactManager::NoError); QVERIFY(contactRemoveRequest.errorMap().value(9) == QContactManager::NoError); QVERIFY(contactRemoveRequest.errorMap().value(10) == QContactManager::NoError); QVERIFY(contactRemoveRequest.errorMap().value(11) == QContactManager::NoError); QVERIFY(contactRemoveRequest.errorMap().value(12) == QContactManager::DoesNotExistError); QVERIFY(contactRemoveRequest.errorMap().value(13) == QContactManager::DoesNotExistError); QVERIFY(contactRemoveRequest.errorMap().value(14) == QContactManager::DoesNotExistError); // Check that all the contacts have been removed QList contactsLeft = cm.data()->contactIds(); QVERIFY(contactsLeft.isEmpty()); } void tst_QContactAsync::contactSave() { QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); QContactSaveRequest csr; QVERIFY(csr.type() == QContactAbstractRequest::ContactSaveRequest); // initial state - not started, no manager. QVERIFY(!csr.isActive()); QVERIFY(!csr.isFinished()); QVERIFY(!csr.start()); QVERIFY(!csr.cancel()); QVERIFY(!csr.waitForFinished()); // save a new contact int originalCount = cm->contactIds().size(); QContact testContact; QContactName nameDetail; nameDetail.setFirstName("Test Contact"); testContact.saveDetail(&nameDetail); QList saveList; saveList << testContact; csr.setManager(cm.data()); QCOMPARE(csr.manager(), cm.data()); QVERIFY(!csr.isActive()); QVERIFY(!csr.isFinished()); QVERIFY(!csr.cancel()); QVERIFY(!csr.waitForFinished()); qRegisterMetaType("QContactSaveRequest*"); QThreadSignalSpy spy(&csr, SIGNAL(stateChanged(QContactAbstractRequest::State))); csr.setContact(testContact); QCOMPARE(csr.contacts(), saveList); QVERIFY(!csr.cancel()); // not started QVERIFY(csr.start()); QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished()); //QVERIFY(csr.isFinished() || !csr.start()); // already started. // thread scheduling means this is untestable QVERIFY(csr.waitForFinished()); QVERIFY(csr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList expected = csr.contacts(); QCOMPARE(expected.size(), 1); QList result; result << cm->contact(expected.first().id()); //some backends add extra fields, so this doesn't work: //QCOMPARE(result, expected); // XXX: really, we should use isSuperset() from tst_QContactManager, but this will do for now: QVERIFY(result.first().detail() == nameDetail); QCOMPARE(cm->contactIds().size(), originalCount + 1); // update a previously saved contact QContactPhoneNumber phn; phn.setNumber("12345678"); testContact = result.first(); testContact.saveDetail(&phn); saveList.clear(); saveList << testContact; csr.setContacts(saveList); QCOMPARE(csr.contacts(), saveList); QVERIFY(!csr.cancel()); // not started QVERIFY(csr.start()); QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished()); //QVERIFY(csr.isFinished() || !csr.start()); // already started. // thread scheduling means this is untestable QVERIFY(csr.waitForFinished()); QVERIFY(csr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); expected = csr.contacts(); result.clear(); result << cm->contact(expected.first().id()); //QVERIFY(compareContactLists(result, expected)); //here we can't compare the whole contact details, testContact would be updated by async call because we just use QThreadSignalSpy to receive signals. //QVERIFY(containsIgnoringTimestamps(result, testContact)); // XXX: really, we should use isSuperset() from tst_QContactManager, but this will do for now: QVERIFY(result.first().detail().number() == phn.number()); QCOMPARE(cm->contactIds().size(), originalCount + 1); // cancelling QContact temp = testContact; QContactUrl url; url.setUrl("should not get saved"); temp.saveDetail(&url); saveList.clear(); saveList << temp; csr.setContacts(saveList); if (cm->managerName() == "jsondb") QSKIP("JSONDB backend does not support request cancelling, skipping..."); int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. while (true) { QVERIFY(!csr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(); QVERIFY(csr.start()); if (!csr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. csr.waitForFinished(); saveList = csr.contacts(); if (cm->contactIds().size() > (originalCount + 1) && !cm->removeContact(saveList.at(0).id())) { QSKIP("Unable to remove saved contact to test cancellation of contact save request"); } saveList.clear(); saveList << temp; csr.setContacts(saveList); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } spy.clear(); continue; } // if we get here, then we are cancelling the request. QVERIFY(csr.waitForFinished()); QVERIFY(csr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); // verify that the changes were not saved expected.clear(); QList allContacts = cm->contactIds(); for (int i = 0; i < allContacts.size(); i++) { expected.append(cm->contact(allContacts.at(i))); } QVERIFY(!expected.contains(temp)); QCOMPARE(cm->contactIds().size(), originalCount + 1); break; } // restart, and wait for progress after cancel. while (true) { QVERIFY(!csr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(); QVERIFY(csr.start()); if (!csr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. csr.waitForFinished(); saveList = csr.contacts(); if (cm->contactIds().size() > (originalCount + 1) && !cm->removeContact(saveList.at(0).id())) { QSKIP("Unable to remove saved contact to test cancellation of contact save request"); } saveList.clear(); saveList << temp; csr.setContacts(saveList); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } spy.clear(); continue; } csr.waitForFinished(); // now wait until finished (if it hasn't already). QVERIFY(csr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); // verify that the changes were not saved expected.clear(); QList allContacts = cm->contactIds(); for (int i = 0; i < allContacts.size(); i++) { expected.append(cm->contact(allContacts.at(i))); } QVERIFY(!expected.contains(temp)); QCOMPARE(cm->contactIds().size(), originalCount + 1); break; } } void tst_QContactAsync::contactSaveErrorHandling() { QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); QContactSaveRequest csr; QVERIFY(csr.type() == QContactAbstractRequest::ContactSaveRequest); // initial state - not started, no manager. QVERIFY(!csr.isActive()); QVERIFY(!csr.isFinished()); QVERIFY(!csr.start()); QVERIFY(!csr.cancel()); QVERIFY(!csr.waitForFinished()); // Save a group of contacts, including few TypeGroup contacts which are not supported by jsondb backend. QContact testContact1, testContact2, testContact3, testContact4, testContact5, testContact6, testContact7,testContact8; QContactName nameDetail; nameDetail.setFirstName("Test Contact1"); testContact1.saveDetail(&nameDetail); nameDetail.setFirstName("Test Contact2"); testContact2.saveDetail(&nameDetail); nameDetail.setFirstName("Test Contact3"); testContact3.saveDetail(&nameDetail); nameDetail.setFirstName("Test Contact4"); testContact4.saveDetail(&nameDetail); nameDetail.setFirstName("Test Contact5"); testContact5.saveDetail(&nameDetail); nameDetail.setFirstName("Test Contact6"); testContact6.saveDetail(&nameDetail); nameDetail.setFirstName("Test Contact7"); testContact7.saveDetail(&nameDetail); nameDetail.setFirstName("Test Contact8"); testContact8.saveDetail(&nameDetail); // Set group type to first, middle and last contact in the list. QContactType typeDetail; typeDetail.setType(QContactType::TypeGroup); testContact1.saveDetail(&typeDetail); testContact3.saveDetail(&typeDetail); testContact6.saveDetail(&typeDetail); // Set an invalid phone number QContactPhoneNumber invalidPhone; invalidPhone.setNumber(" hjkjkjhkjkhjkhj //////"); testContact7.saveDetail(&invalidPhone); // This phone number can be cleaned up, so no error should be generated QContactPhoneNumber sanitizablePhone; sanitizablePhone.setNumber(" +123458++++++++*#"); testContact5.saveDetail(&sanitizablePhone); // Set an invalid name QContactName invalidName; invalidName.setFirstName(" "); testContact8.saveDetail(&invalidName); QList saveList; saveList << testContact1 << testContact2 << testContact3 << testContact4 << testContact5 << testContact6 << testContact7 << testContact8; csr.setManager(cm.data()); QCOMPARE(csr.manager(), cm.data()); QVERIFY(!csr.isActive()); QVERIFY(!csr.isFinished()); QVERIFY(!csr.cancel()); QVERIFY(!csr.waitForFinished()); qRegisterMetaType("QContactSaveRequest*"); QThreadSignalSpy spy(&csr, SIGNAL(stateChanged(QContactAbstractRequest::State))); QList testContacts; testContacts << testContact1 << testContact2 << testContact3 << testContact4 << testContact5 << testContact6 << testContact7 << testContact8; csr.setContacts(testContacts); QCOMPARE(csr.contacts(), saveList); QVERIFY(!csr.cancel()); // not started QVERIFY(csr.start()); QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished()); //QVERIFY(csr.isFinished() || !csr.start()); // already started. // thread scheduling means this is untestable QVERIFY(csr.waitForFinished()); QVERIFY(csr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); // Check errors, the group type is not supported by jsondb backend so contacts with that detail should report error. // Note, the returned value is actually set/remapped in to the errorMap by common code in qcontactmanagerengine // TODO: move these tests to JsonDb-specific test class if (cm->managerName() == "jsondb") { QCOMPARE(csr.errorMap().value(0), QContactManager::InvalidContactTypeError); QCOMPARE(csr.errorMap().value(2), QContactManager::InvalidContactTypeError); QCOMPARE(csr.errorMap().value(5), QContactManager::InvalidContactTypeError); QCOMPARE(csr.errorMap().value(6), QContactManager::BadArgumentError); QVERIFY(csr.contacts()[5].id().isNull()); QCOMPARE(csr.error(), QContactManager::InvalidContactTypeError); } else { QCOMPARE(csr.errorMap().value(0), QContactManager::NoError); QCOMPARE(csr.errorMap().value(2), QContactManager::NoError); QCOMPARE(csr.errorMap().value(5), QContactManager::NoError); QCOMPARE(csr.errorMap().value(6), QContactManager::NoError); QCOMPARE(csr.error(), QContactManager::NoError); } QCOMPARE(csr.errorMap().value(1), QContactManager::NoError); QCOMPARE(csr.errorMap().value(3), QContactManager::NoError); QCOMPARE(csr.errorMap().value(4), QContactManager::NoError); QCOMPARE(csr.errorMap().value(7), QContactManager::NoError); } void tst_QContactAsync::contactSaveRemovedContacts() { QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); QContactSaveRequest csr; // Make three test contacts. QContactName nameDetail; nameDetail.setFirstName("Test1"); QContact testContact1; testContact1.saveDetail(&nameDetail); nameDetail.setFirstName("Test2"); QContact testContact2; testContact2.saveDetail(&nameDetail); nameDetail.setFirstName("Test3"); QContact testContact3; testContact3.saveDetail(&nameDetail); QList testContacts; testContacts << testContact1 << testContact2 << testContact3; // Save all three test contacts. csr.setManager(cm.data()); qRegisterMetaType("QContactSaveRequest*"); csr.setContacts(testContacts); QVERIFY(csr.start()); QVERIFY(csr.waitForFinished()); QCOMPARE(csr.errorMap().value(0), QContactManager::NoError); QCOMPARE(csr.error(), QContactManager::NoError); // And then remove all of them. QList contactIds = cm->contactIds(); QVERIFY(cm->removeContacts(contactIds)); // Try now to save again the same three test contacts with the ids of // just removed contacts to fail and check proper error code. QVERIFY(csr.start()); QVERIFY(csr.waitForFinished()); QCOMPARE(csr.errorMap().value(0), QContactManager::DoesNotExistError); QCOMPARE(csr.errorMap().value(1), QContactManager::DoesNotExistError); QCOMPARE(csr.errorMap().value(2), QContactManager::DoesNotExistError); QCOMPARE(csr.error(), QContactManager::DoesNotExistError); //test case for existing and non existing contacts after remove in contact //save request nameDetail.setFirstName("Test4"); QContact testContact4; testContact4.saveDetail(&nameDetail); testContacts = csr.contacts(); testContacts << testContact4; csr.setContacts(testContacts); QVERIFY(csr.start()); QVERIFY(csr.waitForFinished()); QCOMPARE(csr.errorMap().value(0), QContactManager::DoesNotExistError); QCOMPARE(csr.errorMap().value(1), QContactManager::DoesNotExistError); QCOMPARE(csr.errorMap().value(2), QContactManager::DoesNotExistError); QCOMPARE(csr.errorMap().value(3), QContactManager::NoError); } void tst_QContactAsync::contactSaveRemovedContactsWithCleanIds() { QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); QContactSaveRequest csr; // Make three test contacts. QContactName nameDetail; nameDetail.setFirstName("Testing1"); QContact testContact1; testContact1.saveDetail(&nameDetail); nameDetail.setFirstName("Testing2"); QContact testContact2; testContact2.saveDetail(&nameDetail); nameDetail.setFirstName("Testing3"); QContact testContact3; testContact3.saveDetail(&nameDetail); QList testContacts; testContacts << testContact1 << testContact2 << testContact3; // Save all three test contacts. csr.setManager(cm.data()); csr.setContacts(testContacts); QVERIFY(csr.start()); QVERIFY(csr.waitForFinished()); QCOMPARE(csr.errorMap().value(0), QContactManager::NoError); QCOMPARE(csr.error(), QContactManager::NoError); // And then remove all of them. QList contactIds = cm->contactIds(); QVERIFY(cm->removeContacts(contactIds)); // Use the same contacts but clean their ids before saving them. QList contactsWithCleanIds = csr.contacts(); contactsWithCleanIds[0].setId(QContactId()); contactsWithCleanIds[1].setId(QContactId()); contactsWithCleanIds[2].setId(QContactId()); csr.setContacts(contactsWithCleanIds); // Save and check no errors occured. QVERIFY(csr.start()); QVERIFY(csr.waitForFinished()); QCOMPARE(csr.error(), QContactManager::NoError); } void tst_QContactAsync::contactPartialSave() { QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); QList contacts(cm->contacts()); QList originalContacts(contacts); QCOMPARE(contacts.count(), 3); qDebug() << Q_FUNC_INFO << "contacts:" << contacts; QContactId aId = contacts[0].id(); QContactId bId = contacts[1].id(); // Test 1: saving a contact with a changed detail masked out does nothing QContactPhoneNumber phn(contacts[0].detail()); phn.setNumber("new number"); contacts[0].saveDetail(&phn); QContactSaveRequest csr; csr.setManager(cm.data()); csr.setContacts(contacts); QList typeMasks; typeMasks << QContactDetail::TypeEmailAddress; csr.setTypeMask(typeMasks); qRegisterMetaType("QContactSaveRequest*"); QThreadSignalSpy spy(&csr, SIGNAL(stateChanged(QContactAbstractRequest::State))); QVERIFY(csr.start()); QVERIFY((csr.isActive() && csr.state() == QContactAbstractRequest::ActiveState) || csr.isFinished()); QVERIFY(csr.waitForFinished()); QVERIFY(csr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QCOMPARE(csr.error(), QContactManager::NoError); QVERIFY(csr.errorMap().isEmpty()); contacts[0] = cm->contact(aId); QCOMPARE(contacts[0].detail().number(), originalContacts[0].detail().number()); // Test 2: saving a contact with a changed detail in the mask changes it QContactEmailAddress email; email.setEmailAddress("me@example.com"); contacts[1].saveDetail(&email); csr.setContacts(contacts); csr.setTypeMask(typeMasks); QVERIFY(csr.start()); QVERIFY(csr.waitForFinished()); QCOMPARE(csr.error(), QContactManager::NoError); QVERIFY(csr.errorMap().isEmpty()); contacts[1] = cm->contact(bId); QCOMPARE(contacts[1].detail().emailAddress(), QString("me@example.com")); // 3) Remove an email address and a phone number QCOMPARE(contacts[1].details().count(), 1); QCOMPARE(contacts[1].details().count(), 1); // Next line added because otherwise the removeDetail fails because the email details key // changes in: contacts[1] = cm->contact(bId); and it does not match email variables key. email = contacts[1].detail(); QVERIFY(contacts[1].removeDetail(&email)); phn = contacts[1].detail(); QVERIFY(contacts[1].removeDetail(&phn)); QVERIFY(contacts[1].details().count() == 0); QVERIFY(contacts[1].details().count() == 0); csr.setContacts(contacts); csr.setTypeMask(typeMasks); QVERIFY(csr.start()); QVERIFY(csr.waitForFinished()); QCOMPARE(csr.error(), QContactManager::NoError); QVERIFY(csr.errorMap().isEmpty()); contacts[1] = cm->contact(bId); QCOMPARE(contacts[1].details().count(), 0); QCOMPARE(contacts[1].details().count(), 1); // 4 - New contact, no details in the mask QContact newContact; newContact.saveDetail(&email); newContact.saveDetail(&phn); contacts.append(newContact); csr.setContacts(contacts); typeMasks.clear(); typeMasks << QContactDetail::TypeOnlineAccount; csr.setTypeMask(typeMasks); QVERIFY(csr.start()); QVERIFY(csr.waitForFinished()); QCOMPARE(csr.error(), QContactManager::NoError); QVERIFY(csr.errorMap().isEmpty()); contacts = csr.contacts(); QCOMPARE(contacts.size()-1, 3); // Just check that we are dealing with the contact at index 3 QContactId dId = contacts[3].id(); contacts[3] = cm->contact(dId); QVERIFY(contacts[3].details().count() == 0); // not saved QVERIFY(contacts[3].details().count() == 0); // not saved // 5 - New contact, some details in the mask QVERIFY(newContact.id().isNull()); QVERIFY(newContact.details().count() == 1); QVERIFY(newContact.details().count() == 1); contacts.append(newContact); csr.setContacts(contacts); typeMasks.clear(); typeMasks << QContactDetail::TypePhoneNumber; csr.setTypeMask(typeMasks); QVERIFY(csr.start()); QVERIFY(csr.waitForFinished()); QCOMPARE(csr.error(), QContactManager::NoError); QVERIFY(csr.errorMap().isEmpty()); contacts = csr.contacts(); QCOMPARE(contacts.size()-1, 4); // Just check that we are dealing with the contact at index 4 QContactId eId = contacts[4].id(); contacts[4] = cm->contact(eId); QCOMPARE(contacts[4].details().count(), 0); // not saved QCOMPARE(contacts[4].details().count(), 1); // saved // 6) Have a bad manager uri in the middle QContactId badId = QContactIdMock::createId("BadManager", 0); contacts[3].setId(badId); csr.setContacts(contacts); QVERIFY(csr.start()); QVERIFY(csr.waitForFinished()); QVERIFY(csr.error() == QContactManager::DoesNotExistError); // error in the middle in this partial save QMap errorMap(csr.errorMap()); QCOMPARE(errorMap.count(), 1);//one error in error map, related to the fetch phase of this partial save QCOMPARE(errorMap[3], QContactManager::DoesNotExistError); // 7) Have a non existing contact in the middle badId = QContactIdMock::createId(cm->managerName(), 0); contacts[3].setId(badId); csr.setContacts(contacts); QVERIFY(csr.start()); QVERIFY(csr.waitForFinished()); QVERIFY(csr.error() == QContactManager::DoesNotExistError); // error in the middle in this partial save errorMap = csr.errorMap(); QCOMPARE(errorMap.count(), 1);//one error in error map, related to the fetch phase of this partial save QCOMPARE(errorMap[3], QContactManager::DoesNotExistError); // 8) A list entirely of new contacts, with no details in the mask QList contacts2; QVERIFY(newContact.id().isNull()); QVERIFY(newContact.details().count() == 1); QVERIFY(newContact.details().count() == 1); contacts2.append(newContact); csr.setContacts(contacts2); typeMasks.clear(); typeMasks << QContactDetail::TypeOrganization; csr.setTypeMask(typeMasks); QVERIFY(csr.start()); QVERIFY(csr.waitForFinished()); QCOMPARE(csr.error(), QContactManager::NoError); QVERIFY(csr.errorMap().isEmpty()); contacts2 = csr.contacts(); QCOMPARE(contacts2.size(), 1); contacts2[0] = cm->contact(contacts2[0].id()); QCOMPARE(contacts2[0].details().count(), 0); // not saved QCOMPARE(contacts2[0].details().count(), 0); // saved // 9) A list entirely of new contacts, with some details in the mask contacts2.clear(); QVERIFY(newContact.id().isNull()); QVERIFY(newContact.details().count() == 1); QVERIFY(newContact.details().count() == 1); contacts2.append(newContact); csr.setContacts(contacts2); typeMasks.clear(); typeMasks << QContactDetail::TypePhoneNumber; csr.setTypeMask(typeMasks); QVERIFY(csr.start()); QVERIFY(csr.waitForFinished()); QCOMPARE(csr.error(), QContactManager::NoError); QVERIFY(csr.errorMap().isEmpty()); contacts2 = csr.contacts(); QCOMPARE(contacts2.size(), 1); contacts2[0] = cm->contact(contacts2[0].id()); QCOMPARE(contacts2[0].details().count(), 0); // not saved QCOMPARE(contacts2[0].details().count(), 1); // saved // 10) A list entirely of new contacts contacts2.clear(); QVERIFY(newContact.id().isNull()); QVERIFY(newContact.details().count() == 1); QVERIFY(newContact.details().count() == 1); contacts2.append(newContact); contacts2.append(newContact); contacts2[0].setId(badId); csr.setContacts(contacts2); QVERIFY(csr.start()); QVERIFY(csr.waitForFinished()); QVERIFY(csr.error() == QContactManager::DoesNotExistError); // error in the middle in this partial save errorMap = csr.errorMap(); QCOMPARE(errorMap.count(), 1);//one error in error map, related to the fetch phase of this partial save QCOMPARE(errorMap[0], QContactManager::DoesNotExistError); } void tst_QContactAsync::contactPartialSaveAsync() { QFETCH(QString, uri); QContactManager* cm = QContactManager::fromUri(uri); QList contacts; int numContacts = 10; // add contacts for (int i = 0; i < numContacts; i++) { QContact c; QContactName name; name.setFirstName("John"); name.setMiddleName(QString::number(i)); name.setLastName("Doe"); QContactPhoneNumber phone; QString number = "555-100"+QString::number(i); phone.setNumber(number); c.saveDetail(&name); c.saveDetail(&phone); contacts.append(c); } QContactSaveRequest *saveRequest = new QContactSaveRequest(); saveRequest->setManager(cm); saveRequest->setContacts(contacts); saveRequest->start(); saveRequest->waitForFinished(20000); QVERIFY(saveRequest->isFinished()); QCOMPARE(saveRequest->contacts().count(), numContacts); delete saveRequest; qRegisterMetaType("QContactAbstractRequest::State"); saveRequest = new QContactSaveRequest(); saveRequest->setManager(cm); saveRequest->setContacts(contacts); QList typeMasks; typeMasks << QContactDetail::TypeTag; saveRequest->setTypeMask(typeMasks); saveRequest->start(); QTest::qWait(1000); QVERIFY(saveRequest->waitForFinished()); QVERIFY(saveRequest->isFinished()); } void tst_QContactAsync::relationshipFetch() { QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); if (cm->managerName() == "jsondb") { QSKIP("This contact manager does not support relationships!"); } if (cm->managerName() == "symbian") { QSKIP("This contact manager does not support the required relationship types for this test to pass!"); } QContactRelationshipFetchRequest rfr; QVERIFY(rfr.type() == QContactAbstractRequest::RelationshipFetchRequest); // initial state - not started, no manager. QVERIFY(!rfr.isActive()); QVERIFY(!rfr.isFinished()); QVERIFY(!rfr.start()); QVERIFY(!rfr.cancel()); QVERIFY(!rfr.waitForFinished()); // "all relationships" retrieval rfr.setManager(cm.data()); QCOMPARE(rfr.manager(), cm.data()); QVERIFY(!rfr.isActive()); QVERIFY(!rfr.isFinished()); QVERIFY(!rfr.cancel()); QVERIFY(!rfr.waitForFinished()); qRegisterMetaType("QContactRelationshipFetchRequest*"); QThreadSignalSpy spy(&rfr, SIGNAL(stateChanged(QContactAbstractRequest::State))); QVERIFY(!rfr.cancel()); // not started QVERIFY(rfr.start()); QVERIFY((rfr.isActive() && rfr.state() == QContactAbstractRequest::ActiveState) || rfr.isFinished()); //QVERIFY(rfr.isFinished() || !rfr.start()); // already started. // thread scheduling means this is untestable QVERIFY(rfr.waitForFinished()); QVERIFY(rfr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList rels = cm->relationships(); QList result = rfr.relationships(); QCOMPARE(rels, result); // specific relationship type retrieval rfr.setRelationshipType(QContactRelationship::HasManager()); QVERIFY(!rfr.cancel()); // not started QVERIFY(rfr.start()); QVERIFY((rfr.isActive() && rfr.state() == QContactAbstractRequest::ActiveState) || rfr.isFinished()); //QVERIFY(rfr.isFinished() || !rfr.start()); // already started. // thread scheduling means this is untestable QVERIFY(rfr.waitForFinished()); QVERIFY(rfr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); rels = cm->relationships(QContactRelationship::HasManager()); result = rfr.relationships(); QCOMPARE(rels, result); // specific source contact retrieval rfr.setRelationshipType(QString()); QList contacts = cm->contactIds(); QContact aContact; foreach (const QContactId& currId, contacts) { QContact curr = cm->contact(currId); if (curr.detail(QContactName::Type).value(QContactName::FieldFirstName) == QString("Aaron")) { aContact = curr; break; } } rfr.setFirst(aContact); QVERIFY(!rfr.cancel()); // not started QVERIFY(rfr.start()); QVERIFY((rfr.isActive() && rfr.state() == QContactAbstractRequest::ActiveState) || rfr.isFinished()); //QVERIFY(rfr.isFinished() || !rfr.start()); // already started. // thread scheduling means this is untestable QVERIFY(rfr.waitForFinished()); QVERIFY(rfr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); rels = cm->relationships(aContact, QContactRelationship::First); result = rfr.relationships(); QCOMPARE(rels, result); // specific participant retrieval #1 - destination participant rfr.setFirst(QContact()); contacts = cm->contactIds(); QContact bContact; foreach (const QContactId& currId, contacts) { QContact curr = cm->contact(currId); if (curr.detail(QContactName::Type).value(QContactName::FieldFirstName) == QString("Bob")) { bContact = curr; break; } } rfr.setSecond(bContact); QVERIFY(!rfr.cancel()); // not started QVERIFY(rfr.start()); QVERIFY((rfr.isActive() && rfr.state() == QContactAbstractRequest::ActiveState) || rfr.isFinished()); //QVERIFY(rfr.isFinished() || !rfr.start()); // already started. // thread scheduling means this is untestable QVERIFY(rfr.waitForFinished()); QVERIFY(rfr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); // retrieve rels where second = id of B, and ensure that we get the same results rels = cm->relationships(bContact, QContactRelationship::Second); result = rfr.relationships(); QCOMPARE(rels, result); // specific participant retrieval #2 - source participant rfr.setFirst(QContact()); contacts = cm->contactIds(); QContact cContact; foreach (const QContactId& currId, contacts) { QContact curr = cm->contact(currId); if (curr.detail(QContactName::Type).value(QContactName::FieldFirstName) == QString("Borris")) { cContact = curr; break; } } rfr.setSecond(cContact); QVERIFY(!rfr.cancel()); // not started QVERIFY(rfr.start()); QVERIFY((rfr.isActive() && rfr.state() == QContactAbstractRequest::ActiveState) || rfr.isFinished()); //QVERIFY(rfr.isFinished() || !rfr.start()); // already started. // thread scheduling means this is untestable QVERIFY(rfr.waitForFinished()); QVERIFY(rfr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); // retrieve rels where first = id of C and compare the results rfr.setFirst(cContact); rfr.setSecond(QContact()); QVERIFY(rfr.start()); QVERIFY(rfr.waitForFinished()); result = rfr.relationships(); rels = cm->relationships(cContact, QContactRelationship::First); QCOMPARE(rels, result); // cancelling rfr.setRelationshipType(QString()); if (cm->managerName() == "jsondb") QSKIP("JSONDB backend does not support request cancelling, skipping..."); int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. while (true) { QVERIFY(!rfr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(); QVERIFY(rfr.start()); if (!rfr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. rfr.waitForFinished(); rfr.setRelationshipType(QString()); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } spy.clear(); continue; } // if we get here, then we are cancelling the request. QVERIFY(rfr.waitForFinished()); QVERIFY(rfr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); break; } // restart, and wait for progress after cancel. while (true) { QVERIFY(!rfr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(); QVERIFY(rfr.start()); if (!rfr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. rfr.waitForFinished(); rfr.setRelationshipType(QString()); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } spy.clear(); continue; } rfr.waitForFinished(); QVERIFY(rfr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); break; } } void tst_QContactAsync::relationshipRemove() { QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); if (cm->managerName() == "jsondb") { QSKIP("This contact manager does not support relationships!"); } if (cm->managerName() == "symbian") { QSKIP("This contact manager does not support the required relationship types for this test to pass!"); } QContactRelationshipRemoveRequest rrr; QVERIFY(rrr.type() == QContactAbstractRequest::RelationshipRemoveRequest); // initial state - not started, no manager. QVERIFY(!rrr.isActive()); QVERIFY(!rrr.isFinished()); QVERIFY(!rrr.start()); QVERIFY(!rrr.cancel()); QVERIFY(!rrr.waitForFinished()); QList contacts = cm->contactIds(); QContact aContact, bContact, cContact; foreach (const QContactId& currId, contacts) { QContact curr = cm->contact(currId); if (curr.detail(QContactName::Type).value(QContactName::FieldFirstName) == QString("Aaron")) { aContact = curr; continue; } if (curr.detail(QContactName::Type).value(QContactName::FieldFirstName) == QString("Bob")) { bContact = curr; continue; } if (curr.detail(QContactName::Type).value(QContactName::FieldFirstName) == QString("Borris")) { cContact = curr; continue; } } // specific source, destination and type removal QList relationships; QContactRelationship r; r.setFirst(aContact); r.setSecond(cContact); r.setRelationshipType(QContactRelationship::HasAssistant()); relationships.push_back(r); rrr.setRelationships(relationships); rrr.setManager(cm.data()); qRegisterMetaType("QContactRelationshipRemoveRequest*"); QThreadSignalSpy spy(&rrr, SIGNAL(stateChanged(QContactAbstractRequest::State))); QCOMPARE(rrr.manager(), cm.data()); QVERIFY(!rrr.isActive()); QVERIFY(!rrr.isFinished()); QVERIFY(!rrr.cancel()); QVERIFY(!rrr.waitForFinished()); QVERIFY(!rrr.cancel()); // not started QVERIFY(rrr.start()); QVERIFY((rrr.isActive() && rrr.state() == QContactAbstractRequest::ActiveState) || rrr.isFinished()); //QVERIFY(rrr.isFinished() || !rrr.start()); // already started. // thread scheduling means this is untestable QVERIFY(rrr.waitForFinished()); QVERIFY(rrr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QCOMPARE(cm->relationships(QContactRelationship::HasAssistant(), cContact, QContactRelationship::Second).size(), 1); // remove (asynchronously) a nonexistent relationship - should fail. r.setFirst(cContact); r.setSecond(aContact); r.setRelationshipType(QContactRelationship::HasManager()); relationships.clear(); relationships.push_back(r); rrr.setRelationship(r); QVERIFY(rrr.relationships() == relationships); rrr.setManager(cm.data()); QVERIFY(!rrr.cancel()); // not started QVERIFY(rrr.start()); QVERIFY((rrr.isActive() && rrr.state() == QContactAbstractRequest::ActiveState) || rrr.isFinished()); //QVERIFY(rrr.isFinished() || !rrr.start()); // already started. // thread scheduling means this is untestable QVERIFY(rrr.waitForFinished()); QVERIFY(rrr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QCOMPARE(cm->relationships(QContactRelationship::HasManager(), cContact, QContactRelationship::First).size(), 0); // QCOMPARE(rrr.error(), QContactManager::DoesNotExistError); // cancelling r.setFirst(cContact); r.setSecond(QContact()); relationships.clear(); relationships.push_back(r); if (cm->managerName() == "jsondb") QSKIP("JSONDB backend does not support request cancelling, skipping..."); int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. while (true) { QVERIFY(!rrr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(); QVERIFY(rrr.start()); if (!rrr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. rrr.waitForFinished(); rrr.setRelationships(relationships); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } spy.clear(); continue; } // if we get here, then we are cancelling the request. QVERIFY(rrr.waitForFinished()); QVERIFY(rrr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); QVERIFY(cm->relationships(cContact).size() != 0); // didn't remove them. break; } // restart, and wait for progress after cancel. while (true) { QVERIFY(!rrr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(); QVERIFY(rrr.start()); if (!rrr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. rrr.waitForFinished(); rrr.setRelationships(relationships); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } spy.clear(); continue; } rrr.waitForFinished(); QVERIFY(rrr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); QVERIFY(cm->relationships(cContact).size() != 0); // didn't remove them. break; } } void tst_QContactAsync::relationshipSave() { QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); if (cm->managerName() == "jsondb") { QSKIP("This contact manager does not support relationships!"); } if (cm->managerName() == "symbian") { QSKIP("This contact manager does not support the required relationship types for this test to pass!"); } QContactRelationshipSaveRequest rsr; QVERIFY(rsr.type() == QContactAbstractRequest::RelationshipSaveRequest); // initial state - not started, no manager. QVERIFY(!rsr.isActive()); QVERIFY(!rsr.isFinished()); QVERIFY(!rsr.start()); QVERIFY(!rsr.cancel()); QVERIFY(!rsr.waitForFinished()); QList contacts = cm->contactIds(); QContact cContact, aContact, bContact; foreach (const QContactId& currId, contacts) { QContact curr = cm->contact(currId); if (curr.detail(QContactName::Type).value(QContactName::FieldFirstName) == QString("Borris")) { cContact = curr; } else if (curr.detail(QContactName::Type).value(QContactName::FieldFirstName) == QString("Bob")) { bContact = curr; } else if (curr.detail(QContactName::Type).value(QContactName::FieldFirstName) == QString("Aaron")) { aContact = curr; } } // save a new relationship int originalCount = cm->relationships(aContact).size(); QContactRelationship testRel; testRel.setFirst(aContact); testRel.setRelationshipType(QContactRelationship::HasSpouse()); testRel.setSecond(bContact); QList saveList; saveList << testRel; rsr.setManager(cm.data()); QCOMPARE(rsr.manager(), cm.data()); QVERIFY(!rsr.isActive()); QVERIFY(!rsr.isFinished()); QVERIFY(!rsr.cancel()); QVERIFY(!rsr.waitForFinished()); qRegisterMetaType("QContactRelationshipSaveRequest*"); QThreadSignalSpy spy(&rsr, SIGNAL(stateChanged(QContactAbstractRequest::State))); rsr.setRelationship(testRel); QCOMPARE(rsr.relationships(), saveList); QVERIFY(!rsr.cancel()); // not started QVERIFY(rsr.start()); QVERIFY((rsr.isActive() && rsr.state() == QContactAbstractRequest::ActiveState) || rsr.isFinished()); //QVERIFY(rsr.isFinished() || !rsr.start()); // already started. // thread scheduling means this is untestable QVERIFY(rsr.waitForFinished()); QVERIFY(rsr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList expected = cm->relationships(QContactRelationship::HasSpouse(), aContact, QContactRelationship::First); QList result = rsr.relationships(); QCOMPARE(expected, result); QVERIFY(result.contains(testRel)); QCOMPARE(cm->relationships(aContact).size(), originalCount + 1); // should be one extra // save a new relationship testRel.setSecond(cContact); saveList.clear(); saveList << testRel; rsr.setRelationships(saveList); QCOMPARE(rsr.relationships(), saveList); QVERIFY(!rsr.cancel()); // not started QVERIFY(rsr.start()); QVERIFY((rsr.isActive() && rsr.state() == QContactAbstractRequest::ActiveState) || rsr.isFinished()); //QVERIFY(rsr.isFinished() || !rsr.start()); // already started. // thread scheduling means this is untestable QVERIFY(rsr.waitForFinished()); QVERIFY(rsr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); expected.clear(); expected = cm->relationships(QContactRelationship::HasSpouse(), aContact, QContactRelationship::First); result = rsr.relationships(); QCOMPARE(result, QList() << testRel); QVERIFY(expected.contains(testRel)); QCOMPARE(cm->relationships(aContact).size(), originalCount + 2); // should now be two extra // cancelling testRel.setSecond(aContact); // shouldn't get saved (circular anyway) saveList.clear(); saveList << testRel; rsr.setRelationships(saveList); if (cm->managerName() == "jsondb") QSKIP("JSONDB backend does not support request cancelling, skipping..."); int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. while (true) { QVERIFY(!rsr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(); QVERIFY(rsr.start()); if (!rsr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. rsr.waitForFinished(); saveList.clear(); saveList << testRel; rsr.setRelationships(saveList); cm->removeRelationship(testRel); // probably shouldn't have been saved anyway (circular) bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } spy.clear(); continue; } // if we get here, then we are cancelling the request. QVERIFY(rsr.waitForFinished()); QVERIFY(rsr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); // verify that the changes were not saved QList aRels = cm->relationships(aContact, QContactRelationship::First); QVERIFY(!aRels.contains(testRel)); QCOMPARE(cm->relationships(aContact).size(), originalCount + 2); // should still only be two extra break; } // restart, and wait for progress after cancel. while (true) { QVERIFY(!rsr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(); QVERIFY(rsr.start()); if (!rsr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. rsr.waitForFinished(); saveList.clear(); saveList << testRel; rsr.setRelationships(saveList); cm->removeRelationship(testRel); // probably shouldn't have been saved anyway (circular) bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } spy.clear(); continue; } rsr.waitForFinished(); QVERIFY(rsr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); // verify that the changes were not saved QList aRels = cm->relationships(aContact, QContactRelationship::First); QVERIFY(!aRels.contains(testRel)); QCOMPARE(cm->relationships(aContact).size(), originalCount + 2); // should still only be two extra break; } } void tst_QContactAsync::maliciousManager() { // use the invalid manager: passes all requests through to base class QContactManager cm("invalid"); QContactFilter fil; // matches everything QContactFetchRequest cfr; cfr.setFilter(fil); cfr.setManager(&cm); QVERIFY(!cfr.start()); QVERIFY(!cfr.cancel()); QVERIFY(!cfr.waitForFinished()); QVERIFY(!cfr.start()); // ensure that the base class implementation of requestDestroyed() is called QContactFetchRequest *destroyedRequest = new QContactFetchRequest; destroyedRequest->setManager(&cm); destroyedRequest->setFilter(fil); QVERIFY(!destroyedRequest->start()); delete destroyedRequest; // now use a malicious manager that deliberately calls // things in a different thread if (!QContactManager::availableManagers().contains("maliciousplugin")) QSKIP("Malicious plugin is not installed, skipping test."); QContactManager mcm("maliciousplugin"); QCOMPARE(mcm.managerName(), QString("maliciousplugin")); QList emptyCList; QList emptyIList; QStringList emptyDNList; cfr.setFilter(fil); cfr.setManager(&mcm); QVERIFY(cfr.start()); QContactIdFetchRequest cifr; cifr.setFilter(fil); cifr.setManager(&mcm); QVERIFY(cifr.start()); QContactRemoveRequest crr; crr.setContactIds(mcm.contactIds(fil)); crr.setManager(&mcm); QVERIFY(crr.start()); QContactSaveRequest csr; csr.setContacts(emptyCList); csr.setManager(&mcm); QVERIFY(csr.start()); } void tst_QContactAsync::testQuickDestruction() { QFETCH(QString, uri); // in this test, we create a manager, fire off a request, and delete the manager, all in quick succession // this is to test for segfaults etc. for (int i = 0; i < 10; i++) { QContactFetchRequest cfr; QContactManager *cm = prepareModel(uri); cfr.setManager(cm); cfr.start(); delete cm; } // in this test, we create a manager, fire off a request, delete the request, then delete the manager, all in quick succession // this is to test for segfaults, etc. for (int i = 0; i < 10; i++) { QContactFetchRequest *cfr = new QContactFetchRequest; QContactManager *cm = prepareModel(uri); cfr->setManager(cm); cfr->start(); delete cfr; delete cm; } // in this test, we create a manager, fire off a request, delete the manager, then delete the request, all in quick succession // this is to test for segfaults, etc. for (int i = 0; i < 10; i++) { QContactFetchRequest *cfr = new QContactFetchRequest; QContactManager *cm = prepareModel(uri); cfr->setManager(cm); cfr->start(); delete cm; delete cfr; } // in this test, we create a manager, fire off a request, and delete the request, all in quick succession // this is to test for segfaults, etc. QContactManager *cm = prepareModel(uri); for (int i = 0; i < 10; i++) { QContactFetchRequest *cfr = new QContactFetchRequest; cfr->setManager(cm); cfr->start(); delete cfr; } delete cm; } void tst_QContactAsync::threadDelivery() { QFETCH(QString, uri); QScopedPointer cm(prepareModel(uri)); m_mainThreadId = cm->thread()->currentThreadId(); m_resultsAvailableSlotThreadId = m_mainThreadId; // now perform a fetch request and check that the progress is delivered to the correct thread. QContactFetchRequest *req = new QContactFetchRequest; req->setManager(cm.data()); connect(req, SIGNAL(resultsAvailable()), this, SLOT(resultsAvailableReceived())); req->start(); int totalWaitTime = 0; QTest::qWait(1); // force it to process events at least once. while (req->state() != QContactAbstractRequest::FinishedState) { // ensure that the progress signal was delivered to the main thread. QCOMPARE(m_mainThreadId, m_resultsAvailableSlotThreadId); QTest::qWait(5); // spin until done totalWaitTime += 5; // break after 30 seconds. if (totalWaitTime > 30000) { delete req; QSKIP("Asynchronous request not complete after 30 seconds!"); } } // ensure that the progress signal was delivered to the main thread. QCOMPARE(m_mainThreadId, m_resultsAvailableSlotThreadId); delete req; } void tst_QContactAsync::resultsAvailableReceived() { QContactFetchRequest *req = qobject_cast(QObject::sender()); Q_ASSERT(req); m_resultsAvailableSlotThreadId = req->thread()->currentThreadId(); } void tst_QContactAsync::addManagers(QStringList stringlist) { QTest::addColumn("uri"); // retrieve the list of available managers QStringList managers = QContactManager::availableManagers(); // remove ones that we know will not pass if (!stringlist.contains("invalid")) managers.removeAll("invalid"); if (!stringlist.contains("maliciousplugin")) managers.removeAll("maliciousplugin"); if (!stringlist.contains("testdummy")) managers.removeAll("testdummy"); if (!stringlist.contains("symbiansim")) managers.removeAll("symbiansim"); // SIM backend does not support all the required details for tests to pass. if (!stringlist.contains("social")) managers.removeAll("social"); if (!stringlist.contains("simcard")) managers.removeAll("simcard"); if (!stringlist.contains("com.nokia.messaging.contacts.engines.mail.contactslookup")) managers.removeAll("com.nokia.messaging.contacts.engines.mail.contactslookup"); foreach(QString mgr, managers) { QMap params; QTest::newRow(QString("mgr='%1'").arg(mgr).toLatin1().constData()) << QContactManager::buildUri(mgr, params); if (mgr == "memory") { params.insert("id", "tst_QContactManager"); QTest::newRow(QString("mgr='%1', params").arg(mgr).toLatin1().constData()) << QContactManager::buildUri(mgr, params); } } } QContactManager* tst_QContactAsync::prepareModel(const QString& managerUri) { QContactManager* cm = QContactManager::fromUri(managerUri); // XXX TODO: ensure that this is the case: // there should be no contacts in the database. QList toRemove = cm->contactIds(); foreach (const QContactId& removeId, toRemove) cm->removeContact(removeId); QContact a, b, c; QContactName aNameDetail; aNameDetail.setFirstName("Aaron"); aNameDetail.setLastName("Aaronson"); a.saveDetail(&aNameDetail); QContactName bNameDetail; bNameDetail.setFirstName("Bob"); bNameDetail.setLastName("Aaronsen"); b.saveDetail(&bNameDetail); QContactName cNameDetail; cNameDetail.setFirstName("Borris"); cNameDetail.setLastName("Aaronsun"); c.saveDetail(&cNameDetail); QContactPhoneNumber phn; phn.setNumber("0123"); c.saveDetail(&phn); phn.setNumber("3456"); b.saveDetail(&phn); phn.setNumber("6789"); a.saveDetail(&phn); QContactUrl url; url.setUrl("http://test.nokia.com"); a.saveDetail(&url); cm->saveContact(&a); cm->saveContact(&b); cm->saveContact(&c); if (cm->contacts().size() != 3) qWarning() << Q_FUNC_INFO << "Failed to prepare model!"; if (cm->managerName() == "jsondb") { return cm; } QContactRelationship arb; arb.setFirst(a); arb.setSecond(b); arb.setRelationshipType(QContactRelationship::HasManager()); cm->saveRelationship(&arb); QContactRelationship brc; brc.setFirst(b); brc.setSecond(c); brc.setRelationshipType(QContactRelationship::HasAssistant()); cm->saveRelationship(&brc); QContactRelationship cra; cra.setFirst(c); cra.setSecond(a); cra.setRelationshipType(QContactRelationship::HasSpouse()); cm->saveRelationship(&cra); QContactRelationship arc; arc.setFirst(a); arc.setSecond(c); arc.setRelationshipType(QContactRelationship::HasAssistant()); cm->saveRelationship(&arc); QContactRelationship crb; crb.setFirst(c); crb.setSecond(b); crb.setRelationshipType(QContactRelationship::IsSameAs()); cm->saveRelationship(&crb); return cm; // TODO: cleanup once test is complete } QTEST_MAIN(tst_QContactAsync) #include "tst_qcontactasync.moc" tests/auto/contacts/qcontactasync/unittest/unittest.pro000066400000000000000000000004641233466112000241420ustar00rootroot00000000000000include(../../../auto.pri) TARGET = tst_qcontactasync QT += contacts qtHaveModule(jsondb): QT += jsondb SOURCES += tst_qcontactasync.cpp HEADERS += ../../qcontactmanagerdataholder.h HEADERS += ../../../jsondbprocess.h INCLUDEPATH += ../.. INCLUDEPATH += ../../.. DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactdetail/000077500000000000000000000000001233466112000200035ustar00rootroot00000000000000tests/auto/contacts/qcontactdetail/qcontactdetail.pro000066400000000000000000000001661233466112000235270ustar00rootroot00000000000000include(../../auto.pri) QT += contacts SOURCES += tst_qcontactdetail.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactdetail/tst_qcontactdetail.cpp000066400000000000000000000513321233466112000244040ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include //TESTED_COMPONENT=src/contacts QTCONTACTS_USE_NAMESPACE class tst_QContactDetail : public QObject { Q_OBJECT public: tst_QContactDetail(); virtual ~tst_QContactDetail(); public slots: void init(); void cleanup(); private slots: void classHierarchy(); void assignment(); void templates(); void contexts(); void values(); void hash(); void datastream(); void traits(); void keys(); void detailUris(); }; tst_QContactDetail::tst_QContactDetail() { } tst_QContactDetail::~tst_QContactDetail() { } void tst_QContactDetail::init() { } void tst_QContactDetail::cleanup() { } void tst_QContactDetail::classHierarchy() { QContactDetail f1; QContactDetail f2; QVERIFY(f1.isEmpty()); QVERIFY(f2.isEmpty()); QContactPhoneNumber p1; p1.setNumber("123456"); QVERIFY(!p1.isEmpty()); QVERIFY(p1.type() == QContactPhoneNumber::Type); QContactName m1; m1.setFirstName("Bob"); QVERIFY(!m1.isEmpty()); QVERIFY(m1.type() == QContactName::Type); QVERIFY(p1 != m1); QVERIFY(f1 == f2); f1 = p1; // f1 is a phonenumber QVERIFY(f1 == p1); f1 = f1; // assign to itself QVERIFY(f1 == f1); QVERIFY(f1 == p1); QVERIFY(f1 != f2); QVERIFY(p1 != f2); p1 = p1; // assign leaf class to itself QVERIFY(p1 == p1); QVERIFY(f1 == p1); QVERIFY(p1 == f1); f2 = f1; // f2 = f1 = phonenumber QVERIFY(f1 == f2); QVERIFY(f2 == f1); QVERIFY(f2 == p1); QVERIFY(f1 == p1); f1 = m1; // f1 = name, f2 = phonenumber QVERIFY(f1 == m1); QVERIFY(f1 != f2); QVERIFY(f2 == p1); QContactPhoneNumber p2(f2); // p2 = f2 = phonenumber QVERIFY(p1 == p2); QVERIFY(p1 == f2); QCOMPARE(p2.number(), p1.number()); QCOMPARE(p2.number(), QString("123456")); p2 = p1; // phone number to phone number QVERIFY(p1 == p2); QVERIFY(p1 == f2); QCOMPARE(p2.number(), p1.number()); QCOMPARE(p2.number(), QString("123456")); p2.setNumber("5678"); // NOTE: implicitly shared, this has caused a detach so p1 != 2 QVERIFY(p1 != p2); QVERIFY(p1 == f2); QVERIFY(p2 != f2); QCOMPARE(p2.number(), QString("5678")); QCOMPARE(p1.number(), QString("123456")); /* Bad assignment */ p2 = m1; // assign a name to a phone number QVERIFY(p2 != m1); QVERIFY(p2.type() == QContactPhoneNumber::Type); // should still be a phone number though QVERIFY(p2.isEmpty()); /* copy ctor */ QContactName m2(m1); QVERIFY(m2 == m1); /* another bad assignment */ m2 = p2; // phone number to a name QVERIFY(m2 != m1); QVERIFY(m2.type() == QContactName::Type); QVERIFY(m2.isEmpty()); /* Check contexts are considered for equality */ p2 = QContactPhoneNumber(); // new id / detach p2.setNumber(p1.number()); p2.setContexts(QContactDetail::ContextHome); QVERIFY(p1 != p2); p2.removeValue(QContactDetail::FieldContext); // note, context is a value. QVERIFY(p1 == p2); // different ids but same values should be equal /* Copy ctor from valid type */ QContactDetail f3(p2); QVERIFY(f3 == p2); QVERIFY(f3.type() == QContactPhoneNumber::Type); /* Copy ctor from invalid type */ QContactPhoneNumber p3(m1); QVERIFY(p3 != m1); QVERIFY(p3.type() == QContactPhoneNumber::Type); QVERIFY(p3.isEmpty()); /* Copy ctore from invalid type, through base type */ f3 = m1; QContactPhoneNumber p4(f3); QVERIFY(p4 != f3); QVERIFY(p4.type() == QContactPhoneNumber::Type); QVERIFY(p4.isEmpty()); /* Try a reference */ p1.setNumber("123456"); QContactDetail& ref = p1; QVERIFY(p1.number() == "123456"); QVERIFY(p1.value(QContactPhoneNumber::FieldNumber) == "123456"); QVERIFY(ref.value(QContactPhoneNumber::FieldNumber) == "123456"); QVERIFY(p1 == ref); QVERIFY(ref == p1); /* Try changing the original */ p1.setNumber("56789"); QVERIFY(p1.number() == "56789"); QVERIFY(p1.value(QContactPhoneNumber::FieldNumber) == "56789"); QVERIFY(ref.value(QContactPhoneNumber::FieldNumber) == "56789"); QVERIFY(p1 == ref); QVERIFY(ref == p1); /* Try changing the reference */ ref.setValue(QContactPhoneNumber::FieldNumber, "654321"); QVERIFY(p1.number() == "654321"); QVERIFY(p1.value(QContactPhoneNumber::FieldNumber) == "654321"); QVERIFY(ref.value(QContactPhoneNumber::FieldNumber) == "654321"); QVERIFY(p1 == ref); QVERIFY(ref == p1); } void tst_QContactDetail::assignment() { QContactPhoneNumber p1, p2; p1.setNumber("12345"); p2.setNumber("54321"); QVERIFY(p1 != p2); p1 = p2; QVERIFY(p1 == p2); QContactEmailAddress e1; e1.setEmailAddress("test@nokia.com"); QVERIFY(e1 != p1); e1 = p1; QVERIFY(e1 != p1); // assignment across types shouldn't work QVERIFY(e1.emailAddress() == QString()); // should reset the detail QCOMPARE(e1, QContactEmailAddress()); QContactManagerEngine::setDetailAccessConstraints(&p2, QContactDetail::Irremovable); QVERIFY(p1 != p2); } void tst_QContactDetail::templates() { QContact c; QContactPhoneNumber p1, p2; p1.setNumber("1234"); p2.setNumber("5678"); QVERIFY(c.saveDetail(&p1)); QVERIFY(c.saveDetail(&p2)); QList l = c.details(QContactPhoneNumber::Type); QCOMPARE(l.count(), 2); QCOMPARE(QContactPhoneNumber(l.at(0)), p1); QCOMPARE(QContactPhoneNumber(l.at(1)), p2); QList l2 = c.details(); QCOMPARE(l2.count(), 2); QCOMPARE(l2.at(0), p1); QCOMPARE(l2.at(1), p2); } void tst_QContactDetail::contexts() { QContactDetail d; QVERIFY(d.contexts().count() == 0); // test set contexts d.setContexts(QContactDetail::ContextWork); QVERIFY(d.contexts().count() == 1); QVERIFY(d.contexts().contains(QContactDetail::ContextWork)); QVERIFY(!d.contexts().contains(QContactDetail::ContextOther)); QVERIFY(!d.contexts().contains(QContactDetail::ContextHome)); QList contexts; contexts.append(QContactDetail::ContextHome); contexts.append(QContactDetail::ContextOther); d.setContexts(contexts); QVERIFY(d.contexts().count() == 2); QVERIFY(!d.contexts().contains(QContactDetail::ContextWork)); QVERIFY(d.contexts().contains(QContactDetail::ContextOther)); QVERIFY(d.contexts().contains(QContactDetail::ContextHome)); QCOMPARE(d.contexts(), contexts); // test that contexts are values. QCOMPARE(d.value >(QContactDetail::FieldContext), d.contexts()); } void tst_QContactDetail::values() { QContactDetail p; QMap emptyValues; QCOMPARE(p.values(), emptyValues); QDateTime dt = QDateTime::currentDateTime(); QTime t = dt.time(); t.setHMS(t.hour(), t.minute(), t.second(), 0); // milliseconds don't round trip through ISODate dt.setTime(t); QDate d = dt.date(); QDateTime ddt(d); // DateTime version of a Date (QTime()) QVERIFY(p.setValue(QContactAddress::FieldStreet, "This is a string")); QVERIFY(p.setValue(QContactAddress::FieldLocality, d)); QVERIFY(p.setValue(QContactAddress::FieldRegion, dt)); QVERIFY(p.setValue(QContactAddress::FieldPostcode, (int)6)); QVERIFY(p.setValue(QContactAddress::FieldSubTypes, d.toString(Qt::ISODate))); QVERIFY(p.setValue(QContactAddress::FieldPostOfficeBox, dt.toString(Qt::ISODate))); // Presence test (const char * version) QVERIFY(p.hasValue(QContactAddress::FieldStreet)); QVERIFY(p.hasValue(QContactAddress::FieldLocality)); QVERIFY(p.hasValue(QContactAddress::FieldRegion)); QVERIFY(p.hasValue(QContactAddress::FieldPostcode)); QVERIFY(p.hasValue(QContactAddress::FieldSubTypes)); QVERIFY(p.hasValue(QContactAddress::FieldPostOfficeBox)); QVERIFY(!p.hasValue(QContactAddress::FieldCountry)); // string accessors with const char* key QCOMPARE(p.value(QContactAddress::FieldStreet).toString(), QString("This is a string")); QCOMPARE(p.value(QContactAddress::FieldLocality).toString(), d.toString(Qt::ISODate)); QCOMPARE(p.value(QContactAddress::FieldRegion).toString(), dt.toString(Qt::ISODate)); QCOMPARE(p.value(QContactAddress::FieldPostcode).toString(), QString("6")); QCOMPARE(p.value(QContactAddress::FieldSubTypes).toString(), d.toString(Qt::ISODate)); QCOMPARE(p.value(QContactAddress::FieldPostOfficeBox).toString(), dt.toString(Qt::ISODate)); // Variant accessor with const char * key QCOMPARE(p.value(QContactAddress::FieldStreet), QVariant(QString("This is a string"))); QCOMPARE(p.value(QContactAddress::FieldLocality), QVariant(d)); QCOMPARE(p.value(QContactAddress::FieldRegion), QVariant(dt)); QCOMPARE(p.value(QContactAddress::FieldPostcode), QVariant((int)6)); QCOMPARE(p.value(QContactAddress::FieldSubTypes), QVariant(d.toString(Qt::ISODate))); QCOMPARE(p.value(QContactAddress::FieldPostOfficeBox), QVariant(dt.toString(Qt::ISODate))); /* Typed accessors, string first, const char* key */ QCOMPARE(p.value(QContactAddress::FieldStreet), QString("This is a string")); QCOMPARE(p.value(QContactAddress::FieldLocality), d.toString(Qt::ISODate)); QCOMPARE(p.value(QContactAddress::FieldRegion), dt.toString(Qt::ISODate)); QCOMPARE(p.value(QContactAddress::FieldPostcode), QString("6")); QCOMPARE(p.value(QContactAddress::FieldSubTypes), d.toString(Qt::ISODate)); QCOMPARE(p.value(QContactAddress::FieldPostOfficeBox), dt.toString(Qt::ISODate)); /* Now individual original types */ QCOMPARE(p.value(QContactAddress::FieldLocality), d); QCOMPARE(p.value(QContactAddress::FieldRegion), dt); QCOMPARE(p.value(QContactAddress::FieldPostcode), 6); /* Now cross types that should fail */ QDate id; QDateTime idt; QCOMPARE(p.value(QContactAddress::FieldStreet), id); QCOMPARE(p.value(QContactAddress::FieldPostcode), id); QCOMPARE(p.value(QContactAddress::FieldStreet), idt); QCOMPARE(p.value(QContactAddress::FieldPostcode), idt); QCOMPARE(p.value(QContactAddress::FieldLocality), 0); QCOMPARE(p.value(QContactAddress::FieldRegion), 0); QCOMPARE(p.value(QContactAddress::FieldStreet), 0); QCOMPARE(p.value(QContactAddress::FieldSubTypes), 0); QCOMPARE(p.value(QContactAddress::FieldPostOfficeBox), 0); /* Cross types that should work.. */ QCOMPARE(p.value(QContactAddress::FieldPostcode), 6); QCOMPARE(p.value(QContactAddress::FieldSubTypes), d); QCOMPARE(p.value(QContactAddress::FieldPostOfficeBox), dt); QCOMPARE(p.value(QContactAddress::FieldRegion), d); QCOMPARE(p.value(QContactAddress::FieldPostOfficeBox), d); QCOMPARE(p.value(QContactAddress::FieldLocality), ddt); QCOMPARE(p.value(QContactAddress::FieldSubTypes), ddt); /* Now set everything again */ QMap values = p.values(); QList keys = values.keys(); foreach (int key, keys) QVERIFY(p.setValue(key, QVariant())); QCOMPARE(p.values(), emptyValues); QVERIFY(p.values().count() == 0); QVERIFY(!p.hasValue(QContactAddress::FieldStreet)); QVERIFY(!p.hasValue(QContactAddress::FieldLocality)); QVERIFY(!p.hasValue(QContactAddress::FieldRegion)); QVERIFY(!p.hasValue(QContactAddress::FieldPostcode)); QVERIFY(!p.hasValue(QContactAddress::FieldSubTypes)); QVERIFY(!p.hasValue(QContactAddress::FieldPostOfficeBox)); QVERIFY(p.value(QContactAddress::FieldStreet).toString() == QString()); QVERIFY(p.value(QContactAddress::FieldStreet) == QVariant()); values.insert(QContactAddress::FieldStreet, "This is a string"); values.insert(QContactAddress::FieldLocality, d); values.insert(QContactAddress::FieldRegion, dt); values.insert(QContactAddress::FieldPostcode, (int)6); values.insert(QContactAddress::FieldSubTypes, d.toString(Qt::ISODate)); values.insert(QContactAddress::FieldPostOfficeBox, dt.toString(Qt::ISODate)); values.insert(QContactAddress::FieldStreet, QString("This is a string")); /* Set values */ keys = values.keys(); foreach (int key, keys) QVERIFY(p.setValue(key, values.value(key))); /* Now repeat the tests with our bulk set map */ QVERIFY(p.hasValue(QContactAddress::FieldStreet)); QVERIFY(p.hasValue(QContactAddress::FieldLocality)); QVERIFY(p.hasValue(QContactAddress::FieldRegion)); QVERIFY(p.hasValue(QContactAddress::FieldPostcode)); QVERIFY(p.hasValue(QContactAddress::FieldSubTypes)); QVERIFY(p.hasValue(QContactAddress::FieldPostOfficeBox)); /* String accessors */ QCOMPARE(p.value(QContactAddress::FieldStreet).toString(), QString("This is a string")); QCOMPARE(p.value(QContactAddress::FieldLocality).toString(), d.toString(Qt::ISODate)); QCOMPARE(p.value(QContactAddress::FieldRegion).toString(), dt.toString(Qt::ISODate)); QCOMPARE(p.value(QContactAddress::FieldPostcode).toString(), QString("6")); QCOMPARE(p.value(QContactAddress::FieldSubTypes).toString(), d.toString(Qt::ISODate)); QCOMPARE(p.value(QContactAddress::FieldPostOfficeBox).toString(), dt.toString(Qt::ISODate)); /* Typed accessors, string first */ QCOMPARE(p.value(QContactAddress::FieldStreet), QString("This is a string")); QCOMPARE(p.value(QContactAddress::FieldLocality), d.toString(Qt::ISODate)); QCOMPARE(p.value(QContactAddress::FieldRegion), dt.toString(Qt::ISODate)); QCOMPARE(p.value(QContactAddress::FieldPostcode), QString("6")); QCOMPARE(p.value(QContactAddress::FieldSubTypes), d.toString(Qt::ISODate)); QCOMPARE(p.value(QContactAddress::FieldPostOfficeBox), dt.toString(Qt::ISODate)); /* Now individual original types */ QCOMPARE(p.value(QContactAddress::FieldLocality), d); QCOMPARE(p.value(QContactAddress::FieldRegion), dt); QCOMPARE(p.value(QContactAddress::FieldPostcode), 6); /* Now cross types that should fail */ QCOMPARE(p.value(QContactAddress::FieldStreet), id); QCOMPARE(p.value(QContactAddress::FieldPostcode), id); QCOMPARE(p.value(QContactAddress::FieldStreet), idt); QCOMPARE(p.value(QContactAddress::FieldPostcode), idt); QCOMPARE(p.value(QContactAddress::FieldLocality), 0); QCOMPARE(p.value(QContactAddress::FieldRegion), 0); QCOMPARE(p.value(QContactAddress::FieldStreet), 0); QCOMPARE(p.value(QContactAddress::FieldSubTypes), 0); QCOMPARE(p.value(QContactAddress::FieldPostOfficeBox), 0); /* Cross types that should work.. */ QCOMPARE(p.value(QContactAddress::FieldSubTypes), d); QCOMPARE(p.value(QContactAddress::FieldPostOfficeBox), dt); QCOMPARE(p.value(QContactAddress::FieldRegion), d); QCOMPARE(p.value(QContactAddress::FieldPostOfficeBox), d); QCOMPARE(p.value(QContactAddress::FieldLocality), ddt); QCOMPARE(p.value(QContactAddress::FieldSubTypes), ddt); /* Reset again */ values = p.values(); keys = values.keys(); foreach (int key, keys) QVERIFY(p.setValue(key, QVariant())); QCOMPARE(p.values(), emptyValues); /* Check adding a null value removes the field */ p.setValue(QContactAddress::FieldStreet, "stringvalue"); QVERIFY(p.values().contains(QContactAddress::FieldStreet)); QVERIFY(p.value(QContactAddress::FieldStreet) == QString("stringvalue")); p.setValue(QContactAddress::FieldStreet, QVariant()); QVERIFY(!p.values().contains(QContactAddress::FieldStreet)); /* Check adding a field whose value is an empty string */ p.setValue(QContactAddress::FieldStreet, ""); QVERIFY(p.values().contains(QContactAddress::FieldStreet)); QVERIFY(p.value(QContactAddress::FieldStreet) == QString("")); /* Check accessing a missing value */ QCOMPARE(p.value(QContactAddress::FieldStreet).toString(), QString()); QVERIFY(p.setValue(QContactAddress::FieldStreet, "changed my mind")); QCOMPARE(p.value(QContactAddress::FieldStreet).toString(), QString("changed my mind")); /* Check removing a missing value */ QVERIFY(!p.removeValue(QContactAddress::FieldCountry)); p.setValue(QContactAddress::FieldCountry, "555"); p.setValue(QContactAddress::FieldPostOfficeBox, "1234"); /* Check removing a real value */ QVERIFY(p.removeValue(QContactAddress::FieldStreet)); QVERIFY(p.removeValue(QContactAddress::FieldCountry)); QVERIFY(p.removeValue(QContactAddress::FieldPostOfficeBox)); } void tst_QContactDetail::hash() { QContactExtendedDetail detail1; detail1.setName("key"); detail1.setData(QVariant("value")); QContactExtendedDetail detail2; detail2.setName("key"); detail2.setData(QVariant("value")); QContactExtendedDetail detail3; detail3.setName("key"); detail3.setData(QVariant("different value")); QVERIFY(qHash(detail1) == qHash(detail2)); QVERIFY(qHash(detail1) != qHash(detail3)); QSet set; set.insert(detail1); set.insert(detail2); set.insert(detail3); QCOMPARE(set.size(), 2); } void tst_QContactDetail::datastream() { QByteArray buffer; QDataStream stream1(&buffer, QIODevice::WriteOnly); QContactExtendedDetail detailIn; detailIn.setName("key1"); detailIn.setData(QVariant("value1")); detailIn.setName("key2"); detailIn.setData(QVariant("value2")); stream1 << detailIn; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); QContactExtendedDetail detailOut; stream2 >> detailOut; QCOMPARE(detailOut, detailIn); } void tst_QContactDetail::traits() { QCOMPARE(sizeof(QContactDetail), sizeof(void *)); QVERIFY(QTypeInfo::isComplex); QVERIFY(!QTypeInfo::isStatic); QVERIFY(!QTypeInfo::isLarge); QVERIFY(!QTypeInfo::isPointer); QVERIFY(!QTypeInfo::isDummy); } void tst_QContactDetail::keys() { QContactDetail d; QContactDetail d2; QVERIFY(d.key() != d2.key()); d = d2; QVERIFY(d.key() == d2.key()); d.resetKey(); QVERIFY(d.key() != d2.key()); } void tst_QContactDetail::detailUris() { QContactDetail d; QVERIFY(d.detailUri().isEmpty()); d.setDetailUri("I'm a detail uri"); QVERIFY(d.detailUri() == "I'm a detail uri"); d.setDetailUri(QString()); QVERIFY(d.detailUri().isEmpty()); QVERIFY(d.linkedDetailUris().isEmpty()); d.setLinkedDetailUris("5555"); QVERIFY(d.linkedDetailUris().count() == 1); QVERIFY(d.linkedDetailUris().count("5555") == 1); QStringList sl; sl << "6666" << "7777"; d.setLinkedDetailUris(sl); QVERIFY(d.linkedDetailUris().count() == 2); QVERIFY(d.linkedDetailUris() == sl); } QTEST_MAIN(tst_QContactDetail) #include "tst_qcontactdetail.moc" tests/auto/contacts/qcontactdetails/000077500000000000000000000000001233466112000201665ustar00rootroot00000000000000tests/auto/contacts/qcontactdetails/qcontactdetails.pro000066400000000000000000000001671233466112000240760ustar00rootroot00000000000000include(../../auto.pri) QT += contacts SOURCES += tst_qcontactdetails.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactdetails/tst_qcontactdetails.cpp000066400000000000000000001534601233466112000247570ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include //TESTED_COMPONENT=src/contacts QTCONTACTS_USE_NAMESPACE class tst_QContactDetails : public QObject { Q_OBJECT public: tst_QContactDetails(); virtual ~tst_QContactDetails(); public slots: void init(); void cleanup(); private slots: // leaf class testing void address(); void anniversary(); void avatar(); void birthday(); void displayLabel(); void emailAddress(); void extendedDetail(); void family(); void favorite(); void gender(); void geolocation(); void globalPresence(); void guid(); void hobby(); void name(); void nickname(); void note(); void onlineAccount(); void organization(); void phoneNumber(); void presence(); void ringtone(); void syncTarget(); void tag(); void timestamp(); void type(); void url(); void version(); }; tst_QContactDetails::tst_QContactDetails() { } tst_QContactDetails::~tst_QContactDetails() { } void tst_QContactDetails::init() { } void tst_QContactDetails::cleanup() { } void tst_QContactDetails::address() { QContact c; QContactAddress a1, a2; QVERIFY(a1.isEmpty()); QVERIFY(a2.isEmpty()); // test property set QCOMPARE(a1.street(), QString()); a1.setStreet("68 Imaginary Avenue"); a1.setCountry("Australia"); QCOMPARE(a1.street(), QString("68 Imaginary Avenue")); QCOMPARE(a1.country(), QString("Australia")); a1.setStreet("1234"); QCOMPARE(a1.street(), QString("1234")); a1.setPostOfficeBox("PO Box 5678"); QCOMPARE(a1.postOfficeBox(), QString("PO Box 5678")); // Sub types a1.setSubTypes(QList() << QContactAddress::SubTypeDomestic); QCOMPARE(a1.subTypes(), QList() << QContactAddress::SubTypeDomestic); QList sl; sl << QContactAddress::SubTypeParcel << QContactAddress::SubTypePostal; a1.setSubTypes(sl); QCOMPARE(a1.subTypes(), sl); // test property add QVERIFY(c.saveDetail(&a1)); QCOMPARE(c.details(QContactAddress::Type).count(), 1); QCOMPARE(QContactAddress(c.details(QContactAddress::Type).value(0)).street(), a1.street()); a2.setStreet("Test"); QVERIFY(c.saveDetail(&a2)); QCOMPARE(c.details(QContactAddress::Type).count(), 2); // test property update a1.setValue(QContactAddress::FieldContext, "label1"); a1.setStreet("12345"); QVERIFY(c.saveDetail(&a1)); // test property remove QVERIFY(c.removeDetail(&a1)); QCOMPARE(c.details(QContactAddress::Type).count(), 1); QVERIFY(c.removeDetail(&a2)); QVERIFY(!c.removeDetail(&a2)); // cannot remove twice QCOMPARE(c.details(QContactAddress::Type).count(), 0); QContactAddress a3; // set street a3.setStreet("Test"); QCOMPARE(a3.street(), QString("Test")); a3.setStreet(""); // set locality a3.setLocality("Test"); QCOMPARE(a3.locality(), QString("Test")); a3.setLocality(""); // set region a3.setRegion("Test"); QCOMPARE(a3.region(), QString("Test")); a3.setRegion(""); // set postcode a3.setPostcode("Test"); QCOMPARE(a3.postcode(), QString("Test")); a3.setPostcode(""); // set country a3.setCountry("Test"); QCOMPARE(a3.country(), QString("Test")); a3.setCountry(""); // subtypes a3.setSubTypes(QList() << QContactAddress::SubTypeDomestic); QCOMPARE(a3.subTypes(), QList() << QContactAddress::SubTypeDomestic); a3.setSubTypes(QList() << QContactAddress::SubTypeInternational << QContactAddress::SubTypePostal); QCOMPARE(a3.subTypes(), QList() << QContactAddress::SubTypeInternational << QContactAddress::SubTypePostal); } void tst_QContactDetails::anniversary() { QContact c; QContactAnniversary a1, a2; QDateTime currDateTime = QDateTime::currentDateTime(); QDate currDate = currDateTime.date(); QDateTime snippedDateTime = QDateTime(currDate); // test property set a1.setCalendarId("1234"); QCOMPARE(a1.calendarId(), QString("1234")); QCOMPARE(a1.value(QContactAnniversary::FieldCalendarId).toString(), QString("1234")); a1.setEvent("4321"); QCOMPARE(a1.event(), QString("4321")); QCOMPARE(a1.value(QContactAnniversary::FieldEvent).toString(), QString("4321")); a1.setSubType(QContactAnniversary::SubTypeWedding); QCOMPARE(a1.subType(), QContactAnniversary::SubTypeWedding); QCOMPARE(static_cast(a1.value(QContactAnniversary::FieldSubType)), QContactAnniversary::SubTypeWedding); a1.setOriginalDate(currDate); QCOMPARE(a1.originalDate(), currDate); QCOMPARE(a1.originalDateTime(), snippedDateTime); QCOMPARE(a1.value(QContactAnniversary::FieldOriginalDate), currDate); QCOMPARE(a1.value(QContactAnniversary::FieldOriginalDate), snippedDateTime); a1.setOriginalDateTime(currDateTime); QCOMPARE(a1.originalDate(), currDate); QCOMPARE(a1.originalDateTime(), currDateTime); QCOMPARE(a1.value(QContactAnniversary::FieldOriginalDate), currDate); QCOMPARE(a1.value(QContactAnniversary::FieldOriginalDate), currDateTime); // test property add QVERIFY(c.saveDetail(&a1)); QCOMPARE(c.details(QContactAnniversary::Type).count(), 1); QCOMPARE(QContactAnniversary(c.details(QContactAnniversary::Type).value(0)).event(), a1.event()); // test property update a1.setValue(QContactAnniversary::FieldContext, "label1"); a1.setCalendarId("12345"); QVERIFY(c.saveDetail(&a1)); QCOMPARE(c.details(QContactAnniversary::Type).value(0).value(QContactAnniversary::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactAnniversary::Type).value(0).value(QContactAnniversary::FieldCalendarId).toString(), QString("12345")); // test property remove QVERIFY(c.removeDetail(&a1)); QCOMPARE(c.details(QContactAnniversary::Type).count(), 0); QVERIFY(c.saveDetail(&a2)); QCOMPARE(c.details(QContactAnniversary::Type).count(), 1); QVERIFY(c.removeDetail(&a2)); QCOMPARE(c.details(QContactAnniversary::Type).count(), 0); QVERIFY(c.removeDetail(&a2) == false); QCOMPARE(c.details(QContactAnniversary::Type).count(), 0); } void tst_QContactDetails::avatar() { QContact c; QContactAvatar a1, a2; // test property set a1.setImageUrl(QUrl("1234")); QCOMPARE(a1.imageUrl(), QUrl("1234")); QCOMPARE(a1.value(QContactAvatar::FieldImageUrl), QUrl("1234")); a2.setVideoUrl(QUrl("videoUrl")); a2.setImageUrl(QUrl("imageUrl")); QCOMPARE(a2.videoUrl(), QUrl("videoUrl")); QCOMPARE(a2.value(QContactAvatar::FieldVideoUrl), QUrl("videoUrl")); QCOMPARE(a2.imageUrl(), QUrl("imageUrl")); QCOMPARE(a2.value(QContactAvatar::FieldImageUrl), QUrl("imageUrl")); // test property add QVERIFY(c.saveDetail(&a1)); QCOMPARE(c.details(QContactAvatar::Type).count(), 1); QCOMPARE(QContactAvatar(c.details(QContactAvatar::Type).value(0)).imageUrl(), a1.imageUrl()); // test property update a1.setValue(QContactAvatar::FieldContext, "label1"); a1.setImageUrl(QUrl("12345")); QVERIFY(c.saveDetail(&a1)); QCOMPARE(c.details(QContactAvatar::Type).value(0).value(QContactAvatar::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactAvatar::Type).value(0).value(QContactAvatar::FieldImageUrl), QUrl("12345")); // test property remove QVERIFY(c.removeDetail(&a1)); QCOMPARE(c.details(QContactAvatar::Type).count(), 0); QVERIFY(c.saveDetail(&a2)); QCOMPARE(c.details(QContactAvatar::Type).count(), 1); QVERIFY(c.removeDetail(&a2)); QCOMPARE(c.details(QContactAvatar::Type).count(), 0); QVERIFY(c.removeDetail(&a2) == false); QCOMPARE(c.details(QContactAvatar::Type).count(), 0); } void tst_QContactDetails::birthday() { QContact c; QContactBirthday b1, b2; QDateTime currDateTime = QDateTime::currentDateTime(); QDate currDate = currDateTime.date(); QDateTime snippedDateTime = QDateTime(currDate); // test property set b1.setDate(currDate); QCOMPARE(b1.date(), currDate); QCOMPARE(b1.dateTime(), snippedDateTime); QCOMPARE(b1.value(QContactBirthday::FieldBirthday), currDate); QCOMPARE(b1.value(QContactBirthday::FieldBirthday), snippedDateTime); b1.setDateTime(currDateTime); QCOMPARE(b1.date(), currDate); QCOMPARE(b1.dateTime(), currDateTime); QCOMPARE(b1.value(QContactBirthday::FieldBirthday), currDate); QCOMPARE(b1.value(QContactBirthday::FieldBirthday), currDateTime); b1.setCalendarId("1234"); QCOMPARE(b1.calendarId(), QString("1234")); QCOMPARE(b1.value(QContactBirthday::FieldCalendarId).toString(), QString("1234")); // test property add QVERIFY(c.saveDetail(&b1)); QCOMPARE(c.details(QContactBirthday::Type).count(), 1); QCOMPARE(QContactBirthday(c.details(QContactBirthday::Type).value(0)).date(), b1.date()); // test property update b1.setValue(QContactBirthday::FieldContext, "label1"); b1.setDate(currDate.addDays(3)); b1.setCalendarId("12345"); QVERIFY(c.saveDetail(&b1)); QCOMPARE(c.details(QContactBirthday::Type).value(0).value(QContactBirthday::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactBirthday::Type).value(0).value(QContactBirthday::FieldBirthday), currDate.addDays(3)); QCOMPARE(c.details(QContactBirthday::Type).value(0).value(QContactBirthday::FieldCalendarId).toString(), QString("12345")); // test property remove QVERIFY(c.removeDetail(&b1)); QCOMPARE(c.details(QContactBirthday::Type).count(), 0); QVERIFY(c.saveDetail(&b2)); QCOMPARE(c.details(QContactBirthday::Type).count(), 1); QVERIFY(c.removeDetail(&b2)); QCOMPARE(c.details(QContactBirthday::Type).count(), 0); QVERIFY(c.removeDetail(&b2) == false); QCOMPARE(c.details(QContactBirthday::Type).count(), 0); } void tst_QContactDetails::displayLabel() { QContact c; QContactDisplayLabel d1, d2; // test property set d1.setLabel("1234"); QCOMPARE(d1.label(), QString("1234")); QCOMPARE(d1.value(QContactDisplayLabel::FieldLabel).toString(), QString("1234")); // test property add QVERIFY(c.saveDetail(&d1)); QCOMPARE(c.details(QContactDisplayLabel::Type).count(), 1); QCOMPARE(QContactDisplayLabel(c.details(QContactDisplayLabel::Type).value(0)).label(), d1.label()); // test property update d1.setValue(QContactDisplayLabel::FieldContext, "label1"); d1.setLabel("12345"); QVERIFY(c.saveDetail(&d1)); QCOMPARE(c.details(QContactDisplayLabel::Type).value(0).value(QContactDisplayLabel::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactDisplayLabel::Type).value(0).value(QContactDisplayLabel::FieldLabel).toString(), QString("12345")); // test property remove QVERIFY(c.removeDetail(&d1)); QCOMPARE(c.details(QContactDisplayLabel::Type).count(), 0); QVERIFY(c.saveDetail(&d2)); QCOMPARE(c.details(QContactDisplayLabel::Type).count(), 1); QVERIFY(c.removeDetail(&d2)); QCOMPARE(c.details(QContactDisplayLabel::Type).count(), 0); QVERIFY(c.removeDetail(&d2) == false); QCOMPARE(c.details(QContactDisplayLabel::Type).count(), 0); } void tst_QContactDetails::emailAddress() { QContact c; QContactEmailAddress e1, e2; // test property set e1.setEmailAddress("1234"); QCOMPARE(e1.emailAddress(), QString("1234")); QCOMPARE(e1.value(QContactEmailAddress::FieldEmailAddress).toString(), QString("1234")); // test property add QVERIFY(c.saveDetail(&e1)); QCOMPARE(c.details(QContactEmailAddress::Type).count(), 1); QCOMPARE(QContactEmailAddress(c.details(QContactEmailAddress::Type).value(0)).emailAddress(), e1.emailAddress()); // test property update e1.setValue(QContactEmailAddress::FieldContext, "label1"); e1.setEmailAddress("12345"); QVERIFY(c.saveDetail(&e1)); QCOMPARE(c.details(QContactEmailAddress::Type).value(0).value(QContactEmailAddress::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactEmailAddress::Type).value(0).value(QContactEmailAddress::FieldEmailAddress).toString(), QString("12345")); // test property remove QVERIFY(c.removeDetail(&e1)); QCOMPARE(c.details(QContactEmailAddress::Type).count(), 0); QVERIFY(c.saveDetail(&e2)); QCOMPARE(c.details(QContactEmailAddress::Type).count(), 1); QVERIFY(c.removeDetail(&e2)); QCOMPARE(c.details(QContactEmailAddress::Type).count(), 0); QVERIFY(c.removeDetail(&e2) == false); QCOMPARE(c.details(QContactEmailAddress::Type).count(), 0); } void tst_QContactDetails::extendedDetail() { QContact c; QContactExtendedDetail extD1, extD2; QVariant v1; // test property set v1.setValue(1); extD1.setName("detail1"); extD1.setValue(QContactExtendedDetail::FieldData, v1); QCOMPARE(extD1.name(), QString("detail1")); QCOMPARE(extD1.data(), v1); // test property add QVERIFY(c.saveDetail(&extD1)); QCOMPARE(c.details(QContactExtendedDetail::Type).count(), 1); QCOMPARE(QContactExtendedDetail(c.details(QContactExtendedDetail::Type).value(0)).name(), extD1.name()); // test property update extD1.setValue(QContactExtendedDetail::FieldContext, "label1"); extD1.setName("newDetail1"); extD1.setData(v1); QVERIFY(c.saveDetail(&extD1)); QCOMPARE(c.details(QContactExtendedDetail::Type).value(0).value(QContactExtendedDetail::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactExtendedDetail::Type).value(0).value(QContactExtendedDetail::FieldData).toInt(), v1.toInt()); QCOMPARE(c.details(QContactExtendedDetail::Type).value(0).value(QContactExtendedDetail::FieldName).toString(), QString("newDetail1")); // test property remove QVERIFY(c.removeDetail(&extD1)); QCOMPARE(c.details(QContactExtendedDetail::Type).count(), 0); extD2 = extD1; QVERIFY(c.saveDetail(&extD2)); QCOMPARE(c.details(QContactExtendedDetail::Type).count(), 1); QVERIFY(c.removeDetail(&extD2)); QCOMPARE(c.details(QContactExtendedDetail::Type).count(), 0); QVERIFY(c.removeDetail(&extD2) == false); QCOMPARE(c.details(QContactExtendedDetail::Type).count(), 0); } void tst_QContactDetails::family() { QContact c; QContactFamily f1, f2; // test property set f1.setSpouse("1234"); QCOMPARE(f1.spouse(), QString("1234")); QCOMPARE(f1.value(QContactFamily::FieldSpouse).toString(), QString("1234")); // test property add QVERIFY(c.saveDetail(&f1)); QCOMPARE(c.details(QContactFamily::Type).count(), 1); QCOMPARE(QContactFamily(c.details(QContactFamily::Type).value(0)).spouse(), f1.spouse()); // test property update f1.setValue(QContactFamily::FieldContext, "label1"); f1.setSpouse("12345"); f1.setChildren(QStringList("54321")); QVERIFY(c.saveDetail(&f1)); QCOMPARE(c.details(QContactFamily::Type).value(0).value(QContactFamily::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactFamily::Type).value(0).value(QContactFamily::FieldSpouse).toString(), QString("12345")); QCOMPARE(c.details(QContactFamily::Type).value(0).value(QContactFamily::FieldChildren), QStringList("54321")); // test property remove f2.setSpouse("1111"); QVERIFY(c.removeDetail(&f1)); QCOMPARE(c.details(QContactFamily::Type).count(), 0); QVERIFY(c.saveDetail(&f2)); QCOMPARE(c.details(QContactFamily::Type).count(), 1); QVERIFY(c.removeDetail(&f2)); QCOMPARE(c.details(QContactFamily::Type).count(), 0); QVERIFY(c.removeDetail(&f2) == false); QCOMPARE(c.details(QContactFamily::Type).count(), 0); } void tst_QContactDetails::favorite() { QContact c; QContactFavorite f1, f2; // first, ensure that the default value is "false" QCOMPARE(c.detail().isFavorite(), false); QCOMPARE(c.detail().index(), 0); // 0 being "no index" :-/ -1 would be better, but not default... QVERIFY(!f1.isFavorite()); // test property set f1.setFavorite(true); QCOMPARE(f1.isFavorite(), true); QCOMPARE(f1.value(QContactFavorite::FieldFavorite), true); // test property add QVERIFY(c.saveDetail(&f1)); QCOMPARE(c.details(QContactFavorite::Type).count(), 1); QCOMPARE(QContactFavorite(c.details(QContactFavorite::Type).value(0)).isFavorite(), f1.isFavorite()); // test property update f1.setValue(QContactFavorite::FieldContext, "label1"); f1.setFavorite(false); f1.setIndex(5); QVERIFY(c.saveDetail(&f1)); QCOMPARE(c.details(QContactFavorite::Type).value(0).value(QContactFavorite::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactFavorite::Type).value(0).value(QContactFavorite::FieldFavorite), false); QCOMPARE(c.details(QContactFavorite::Type).value(0).value(QContactFavorite::FieldIndex), 5); QCOMPARE(c.details().value(0).index(), 5); // test property remove f2.setFavorite(true); QVERIFY(c.removeDetail(&f1)); QCOMPARE(c.details(QContactFavorite::Type).count(), 0); QVERIFY(c.saveDetail(&f2)); QCOMPARE(c.details(QContactFavorite::Type).count(), 1); QVERIFY(c.removeDetail(&f2)); QCOMPARE(c.details(QContactFavorite::Type).count(), 0); QVERIFY(c.removeDetail(&f2) == false); QCOMPARE(c.details(QContactFavorite::Type).count(), 0); } void tst_QContactDetails::gender() { QContact c; QContactGender g1, g2; // test property set g1.setGender(QContactGender::GenderFemale); QCOMPARE(g1.gender(), QContactGender::GenderFemale); // test property add QVERIFY(c.saveDetail(&g1)); QCOMPARE(c.details(QContactGender::Type).count(), 1); QCOMPARE(QContactGender(c.details(QContactGender::Type).value(0)).gender(), g1.gender()); // test property update g1.setValue(QContactGender::FieldContext, "label1"); g1.setGender(QContactGender::GenderMale); QVERIFY(c.saveDetail(&g1)); QCOMPARE(c.details(QContactGender::Type).value(0).value(QContactGender::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactGender::Type).value(0).value(QContactGender::FieldGender).toInt(), (int)QContactGender::GenderMale); // test property remove QVERIFY(c.removeDetail(&g1)); QCOMPARE(c.details(QContactGender::Type).count(), 0); QVERIFY(c.saveDetail(&g2)); QCOMPARE(c.details(QContactGender::Type).count(), 1); QVERIFY(c.removeDetail(&g2)); QCOMPARE(c.details(QContactGender::Type).count(), 0); QVERIFY(c.removeDetail(&g2) == false); QCOMPARE(c.details(QContactGender::Type).count(), 0); } void tst_QContactDetails::geolocation() { QContact c; QContactGeoLocation g1, g2; // test property set g1.setLabel("1234"); QCOMPARE(g1.label(), QString("1234")); QCOMPARE(g1.value(QContactGeoLocation::FieldLabel).toString(), QString("1234")); g1.setAccuracy(3.2); QCOMPARE(g1.accuracy(), 3.2); QCOMPARE(g1.value(QContactGeoLocation::FieldAccuracy), QVariant(3.2)); g1.setAltitude(3.3); QCOMPARE(g1.altitude(), 3.3); QCOMPARE(g1.value(QContactGeoLocation::FieldAltitude), QVariant(3.3)); g1.setAltitudeAccuracy(3.4); QCOMPARE(g1.altitudeAccuracy(), 3.4); QCOMPARE(g1.value(QContactGeoLocation::FieldAltitudeAccuracy), QVariant(3.4)); g1.setHeading(3.5); QCOMPARE(g1.heading(), 3.5); QCOMPARE(g1.value(QContactGeoLocation::FieldHeading), QVariant(3.5)); g1.setLatitude(3.6); QCOMPARE(g1.latitude(), 3.6); QCOMPARE(g1.value(QContactGeoLocation::FieldLatitude), QVariant(3.6)); g1.setLongitude(3.7); QCOMPARE(g1.longitude(), 3.7); QCOMPARE(g1.value(QContactGeoLocation::FieldLongitude), QVariant(3.7)); QDateTime current = QDateTime::currentDateTime(); g1.setTimestamp(current); QCOMPARE(g1.timestamp(), current); QCOMPARE(g1.value(QContactGeoLocation::FieldTimestamp), QVariant(current)); g1.setSpeed(3.8); QCOMPARE(g1.speed(), 3.8); QCOMPARE(g1.value(QContactGeoLocation::FieldSpeed), QVariant(3.8)); // test property add QVERIFY(c.saveDetail(&g1)); QCOMPARE(c.details(QContactGeoLocation::Type).count(), 1); QCOMPARE(QContactGeoLocation(c.details(QContactGeoLocation::Type).value(0)).label(), g1.label()); // test property update g1.setLabel("12345"); QVERIFY(c.saveDetail(&g1)); QCOMPARE(c.details(QContactGeoLocation::Type).value(0).value(QContactGeoLocation::FieldLabel).toString(), QString("12345")); // test property remove QVERIFY(c.removeDetail(&g1)); QCOMPARE(c.details(QContactGeoLocation::Type).count(), 0); QVERIFY(c.saveDetail(&g2)); QCOMPARE(c.details(QContactGeoLocation::Type).count(), 1); QVERIFY(c.removeDetail(&g2)); QCOMPARE(c.details(QContactGeoLocation::Type).count(), 0); QVERIFY(c.removeDetail(&g2) == false); QCOMPARE(c.details(QContactGeoLocation::Type).count(), 0); } void tst_QContactDetails::globalPresence() { QContact c; QContactGlobalPresence p1, p2; // test property set QDateTime ts = QDateTime::currentDateTime(); p1.setTimestamp(ts); p1.setNickname(QString("nick")); p1.setPresenceState(QContactPresence::PresenceExtendedAway); p1.setPresenceStateText("1234"); p1.setPresenceStateImageUrl(QUrl("http://example.com/someimage.png")); p1.setCustomMessage("gone fishing!"); QCOMPARE(p1.timestamp(), ts); QCOMPARE(p1.value(QContactGlobalPresence::FieldTimestamp), ts); QCOMPARE(p1.nickname(), QString("nick")); QCOMPARE(p1.value(QContactGlobalPresence::FieldNickname).toString(), QString("nick")); QCOMPARE(p1.presenceState(), QContactPresence::PresenceExtendedAway); QCOMPARE(p1.value(QContactGlobalPresence::FieldPresenceState), static_cast(QContactPresence::PresenceExtendedAway)); QCOMPARE(p1.presenceStateText(), QString("1234")); QCOMPARE(p1.value(QContactGlobalPresence::FieldPresenceStateText).toString(), QString("1234")); QCOMPARE(p1.presenceStateImageUrl(), QUrl("http://example.com/someimage.png")); QCOMPARE(p1.value(QContactGlobalPresence::FieldPresenceStateImageUrl), QUrl("http://example.com/someimage.png")); QCOMPARE(p1.customMessage(), QString("gone fishing!")); QCOMPARE(p1.value(QContactGlobalPresence::FieldCustomMessage).toString(), QString("gone fishing!")); // test property add QVERIFY(c.saveDetail(&p1)); QCOMPARE(c.details(QContactGlobalPresence::Type).count(), 1); QCOMPARE(QContactGlobalPresence(c.details(QContactGlobalPresence::Type).value(0)).presenceStateText(), p1.presenceStateText()); // test property update p1.setValue(QContactGlobalPresence::FieldContext, "label1"); p1.setPresenceStateText("12345"); QVERIFY(c.saveDetail(&p1)); QCOMPARE(c.details(QContactGlobalPresence::Type).value(0).value(QContactGlobalPresence::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactGlobalPresence::Type).value(0).value(QContactGlobalPresence::FieldPresenceStateText).toString(), QString("12345")); p2.setTimestamp(ts); p2.setNickname(QString("test")); p2.setPresenceState(QContactPresence::PresenceAvailable); p2.setPresenceStateText("online"); p2.setPresenceStateImageUrl(QUrl("http://example.com/someimage.png")); p2.setCustomMessage("C is for generic biscuit-type pastry product!"); // now test that the setPresenceStateImageUrl function doesn't escape spaces and so forth. QString imgUrlStr(QStringLiteral("http://example.com/some image.png")); QUrl imgUrl(imgUrlStr); QContactPresence p3; p3.setPresenceStateImageUrl(imgUrl); QCOMPARE(p3.presenceStateImageUrl(), imgUrl); QCOMPARE(p3.presenceStateImageUrl().toString(), imgUrlStr); // test property remove QVERIFY(c.removeDetail(&p1)); QCOMPARE(c.details(QContactGlobalPresence::Type).count(), 0); QVERIFY(c.saveDetail(&p2)); QCOMPARE(c.details(QContactGlobalPresence::Type).count(), 1); QVERIFY(c.removeDetail(&p2)); QCOMPARE(c.details(QContactGlobalPresence::Type).count(), 0); QVERIFY(c.removeDetail(&p2) == false); QCOMPARE(c.details(QContactGlobalPresence::Type).count(), 0); } void tst_QContactDetails::guid() { QContact c; QContactGuid g1, g2; // test property set g1.setGuid("1234"); QCOMPARE(g1.guid(), QString("1234")); QCOMPARE(g1.value(QContactGuid::FieldGuid).toString(), QString("1234")); // test property add QVERIFY(c.saveDetail(&g1)); QCOMPARE(c.details(QContactGuid::Type).count(), 1); QCOMPARE(QContactGuid(c.details(QContactGuid::Type).value(0)).guid(), g1.guid()); // test property update g1.setValue(QContactGuid::FieldContext, "label1"); g1.setGuid("12345"); QVERIFY(c.saveDetail(&g1)); QCOMPARE(c.details(QContactGuid::Type).value(0).value(QContactGuid::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactGuid::Type).value(0).value(QContactGuid::FieldGuid).toString(), QString("12345")); // test property remove QVERIFY(c.removeDetail(&g1)); QCOMPARE(c.details(QContactGuid::Type).count(), 0); QVERIFY(c.saveDetail(&g2)); QCOMPARE(c.details(QContactGuid::Type).count(), 1); QVERIFY(c.removeDetail(&g2)); QCOMPARE(c.details(QContactGuid::Type).count(), 0); QVERIFY(c.removeDetail(&g2) == false); QCOMPARE(c.details(QContactGuid::Type).count(), 0); } void tst_QContactDetails::hobby() { QContact c; QContactHobby h1, h2; // test property set h1.setHobby("1234"); QCOMPARE(h1.hobby(), QString("1234")); QCOMPARE(h1.value(QContactHobby::FieldHobby).toString(), QString("1234")); // test property add QVERIFY(c.saveDetail(&h1)); QCOMPARE(c.details(QContactHobby::Type).count(), 1); QCOMPARE(QContactHobby(c.details(QContactHobby::Type).value(0)).hobby(), h1.hobby()); // test property update h1.setValue(QContactHobby::FieldContext, "label1"); h1.setHobby("12345"); QVERIFY(c.saveDetail(&h1)); QCOMPARE(c.details(QContactHobby::Type).value(0).value(QContactHobby::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactHobby::Type).value(0).value(QContactHobby::FieldHobby).toString(), QString("12345")); // test property remove h2.setHobby("1111"); QVERIFY(c.removeDetail(&h1)); QCOMPARE(c.details(QContactHobby::Type).count(), 0); QVERIFY(c.saveDetail(&h2)); QCOMPARE(c.details(QContactHobby::Type).count(), 1); QVERIFY(c.removeDetail(&h2)); QCOMPARE(c.details(QContactHobby::Type).count(), 0); QVERIFY(c.removeDetail(&h2) == false); QCOMPARE(c.details(QContactHobby::Type).count(), 0); } void tst_QContactDetails::name() { QContact c; QContactName n1, n2; QVERIFY(n1.isEmpty()); QVERIFY(n2.isEmpty()); // test property set n1.setPrefix("Dr"); n1.setFirstName("Freddy"); n1.setMiddleName("William Preston"); n1.setLastName("Gumboots"); n1.setSuffix("Esquire"); QCOMPARE(n1.prefix(), QString("Dr")); QCOMPARE(n1.firstName(), QString("Freddy")); QCOMPARE(n1.middleName(), QString("William Preston")); QCOMPARE(n1.lastName(), QString("Gumboots")); QCOMPARE(n1.suffix(), QString("Esquire")); // Values based (QString) QCOMPARE(n1.value(QContactName::FieldPrefix).toString(), QString("Dr")); QCOMPARE(n1.value(QContactName::FieldFirstName).toString(), QString("Freddy")); QCOMPARE(n1.value(QContactName::FieldMiddleName).toString(), QString("William Preston")); QCOMPARE(n1.value(QContactName::FieldLastName).toString(), QString("Gumboots")); QCOMPARE(n1.value(QContactName::FieldSuffix).toString(), QString("Esquire")); // test property add QVERIFY(c.saveDetail(&n1)); QCOMPARE(c.details(QContactName::Type).count(), 1); n2.setFirstName("Billy"); n2.setLastName("Galoshes"); QVERIFY(c.saveDetail(&n2)); QCOMPARE(c.details(QContactName::Type).count(), 2); // test property update n1.setValue(QContactName::FieldContext, "label1"); QVERIFY(c.saveDetail(&n1)); // test property remove QVERIFY(c.removeDetail(&n1)); // remove QCOMPARE(c.details(QContactName::Type).count(), 1); QVERIFY(c.saveDetail(&n2)); // save but already added; count remains the same. QCOMPARE(c.details(QContactName::Type).count(), 1); QVERIFY(c.removeDetail(&n2)); // remove it QCOMPARE(c.details(QContactName::Type).count(), 0); QVERIFY(!c.removeDetail(&n2));// remove now fails QVERIFY(c.saveDetail(&n1)); // save the original name QCOMPARE(c.details(QContactName::Type).count(), 1); } void tst_QContactDetails::nickname() { QContact c; QContactNickname n1, n2; // test property set n1.setNickname("1234"); QCOMPARE(n1.nickname(), QString("1234")); QCOMPARE(n1.value(QContactNickname::FieldNickname).toString(), QString("1234")); // test property add QVERIFY(c.saveDetail(&n1)); QCOMPARE(c.details(QContactNickname::Type).count(), 1); QCOMPARE(QContactNickname(c.details(QContactNickname::Type).value(0)).nickname(), n1.nickname()); // test property update n1.setValue(QContactNickname::FieldContext, "label1"); n1.setNickname("12345"); QVERIFY(c.saveDetail(&n1)); QCOMPARE(c.details(QContactNickname::Type).value(0).value(QContactNickname::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactNickname::Type).value(0).value(QContactNickname::FieldNickname).toString(), QString("12345")); // test property remove QVERIFY(c.removeDetail(&n1)); QCOMPARE(c.details(QContactNickname::Type).count(), 0); QVERIFY(c.saveDetail(&n2)); QCOMPARE(c.details(QContactNickname::Type).count(), 1); QVERIFY(c.removeDetail(&n2)); QCOMPARE(c.details(QContactNickname::Type).count(), 0); QVERIFY(c.removeDetail(&n2) == false); QCOMPARE(c.details(QContactNickname::Type).count(), 0); } void tst_QContactDetails::note() { QContact c; QContactNote n1, n2; // test property set n1.setNote("lorem ipsum"); QCOMPARE(n1.note(), QString("lorem ipsum")); QCOMPARE(n1.value(QContactNote::FieldNote).toString(), QString("lorem ipsum")); // test property add QVERIFY(c.saveDetail(&n1)); QCOMPARE(c.details(QContactNote::Type).count(), 1); QCOMPARE(QContactNote(c.details(QContactNote::Type).value(0)).note(), n1.note()); // test property update n1.setValue(QContactNote::FieldContext, "label1"); n1.setNote("orange gypsum"); QVERIFY(c.saveDetail(&n1)); QCOMPARE(c.details(QContactNote::Type).value(0).value(QContactNote::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactNote::Type).value(0).value(QContactNote::FieldNote).toString(), QString("orange gypsum")); // test property remove QVERIFY(c.removeDetail(&n1)); QCOMPARE(c.details(QContactNote::Type).count(), 0); QVERIFY(c.saveDetail(&n2)); QCOMPARE(c.details(QContactNote::Type).count(), 1); QVERIFY(c.removeDetail(&n2)); QCOMPARE(c.details(QContactNote::Type).count(), 0); QVERIFY(c.removeDetail(&n2) == false); QCOMPARE(c.details(QContactNote::Type).count(), 0); } void tst_QContactDetails::onlineAccount() { QContact c; QContactOnlineAccount o1, o2; // test property set o1.setAccountUri("test@nokia.com"); QCOMPARE(o1.accountUri(), QString("test@nokia.com")); QCOMPARE(o1.value(QContactOnlineAccount::FieldAccountUri).toString(), QString("test@nokia.com")); o1.setProtocol(QContactOnlineAccount::ProtocolJabber); QCOMPARE(o1.protocol(), QContactOnlineAccount::ProtocolJabber); // Sub types o1.setSubTypes(QList() << QContactOnlineAccount::SubTypeSip); QCOMPARE(o1.subTypes(), QList() << QContactOnlineAccount::SubTypeSip); QList sl; sl << QContactOnlineAccount::SubTypeImpp << QContactOnlineAccount::SubTypeVideoShare; o1.setSubTypes(sl); QCOMPARE(o1.subTypes(), sl); o1 = QContactOnlineAccount(); o1.setAccountUri("test@nokia.com"); // test property add QVERIFY(c.saveDetail(&o1)); QCOMPARE(c.details(QContactOnlineAccount::Type).count(), 1); QCOMPARE(QContactOnlineAccount(c.details(QContactOnlineAccount::Type).value(0)).accountUri(), o1.accountUri()); QCOMPARE(QContactOnlineAccount(c.details(QContactOnlineAccount::Type).value(0)).accountUri(), o1.accountUri()); // test property update QStringList caps; caps << "cap1" << "cap3" << "cap4"; o1.setValue(QContactOnlineAccount::FieldContext, "label1"); o1.setAccountUri("test2@nokia.com"); o1.setServiceProvider("some provider"); o1.setCapabilities(caps); QVERIFY(c.saveDetail(&o1)); QCOMPARE(c.details(QContactOnlineAccount::Type).value(0).value(QContactOnlineAccount::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactOnlineAccount::Type).value(0).value(QContactOnlineAccount::FieldAccountUri).toString(), QString("test2@nokia.com")); QCOMPARE(c.details(QContactOnlineAccount::Type).value(0).value(QContactOnlineAccount::FieldCapabilities), caps); QCOMPARE(c.details().value(0).capabilities(), caps); QCOMPARE(c.details(QContactOnlineAccount::Type).value(0).value(QContactOnlineAccount::FieldServiceProvider).toString(), QString("some provider")); QCOMPARE(c.details().value(0).serviceProvider(), QString("some provider")); // test property remove QVERIFY(c.removeDetail(&o1)); QCOMPARE(c.details(QContactOnlineAccount::Type).count(), 0); QVERIFY(c.saveDetail(&o2)); QCOMPARE(c.details(QContactOnlineAccount::Type).count(), 1); QVERIFY(c.removeDetail(&o2)); QCOMPARE(c.details(QContactOnlineAccount::Type).count(), 0); QVERIFY(c.removeDetail(&o2) == false); QCOMPARE(c.details(QContactOnlineAccount::Type).count(), 0); } void tst_QContactDetails::organization() { QContact c; QContactOrganization o1, o2; // test property set o1.setName("organization one"); QCOMPARE(o1.name(), QString("organization one")); QCOMPARE(o1.value(QContactOrganization::FieldName).toString(), QString("organization one")); o1.setDepartment(QStringList(QString("department one"))); QCOMPARE(o1.department(), QStringList(QString("department one"))); QCOMPARE(o1.value(QContactOrganization::FieldDepartment), QStringList(QString("department one"))); o1.setLocation("location one"); QCOMPARE(o1.location(), QString("location one")); QCOMPARE(o1.value(QContactOrganization::FieldLocation).toString(), QString("location one")); o1.setLogoUrl(QUrl("logo one")); QCOMPARE(o1.logoUrl(), QUrl("logo one")); QCOMPARE(o1.value(QContactOrganization::FieldLogoUrl), QUrl("logo one")); o1.setTitle("title one"); QCOMPARE(o1.title(), QString("title one")); QCOMPARE(o1.value(QContactOrganization::FieldTitle).toString(), QString("title one")); o1.setAssistantName("assistant one"); QCOMPARE(o1.assistantName(), QString("assistant one")); QCOMPARE(o1.value(QContactOrganization::FieldAssistantName).toString(), QString("assistant one")); // test property add QVERIFY(c.saveDetail(&o1)); QCOMPARE(c.details(QContactOrganization::Type).count(), 1); QCOMPARE(QContactOrganization(c.details(QContactOrganization::Type).value(0)).name(), o1.name()); // test property update o1.setName("organization two"); QVERIFY(c.saveDetail(&o1)); QCOMPARE(c.details(QContactOrganization::Type).value(0).value(QContactOrganization::FieldName).toString(), QString("organization two")); // test property remove QVERIFY(c.removeDetail(&o1)); QCOMPARE(c.details(QContactOrganization::Type).count(), 0); QVERIFY(c.saveDetail(&o2)); QCOMPARE(c.details(QContactOrganization::Type).count(), 1); QVERIFY(c.removeDetail(&o2)); QCOMPARE(c.details(QContactOrganization::Type).count(), 0); QVERIFY(c.removeDetail(&o2) == false); QCOMPARE(c.details(QContactOrganization::Type).count(), 0); // organization-specific API testing o1.setDepartment(QStringList(QString("Imaginary Dept"))); o1.setLocation("Utopia"); o1.setLogoUrl(QUrl("logo.png")); o1.setName("Utopian Megacorporation"); o1.setTitle("Generic Employee"); c.saveDetail(&o1); QVERIFY(c.detail(QContactOrganization::Type).value(QContactOrganization::FieldDepartment) == QStringList(QString("Imaginary Dept"))); QVERIFY(c.detail(QContactOrganization::Type).value(QContactOrganization::FieldLocation).toString() == QString("Utopia")); QVERIFY(c.detail(QContactOrganization::Type).value(QContactOrganization::FieldLogoUrl) == QUrl("logo.png")); QVERIFY(c.detail(QContactOrganization::Type).value(QContactOrganization::FieldName).toString() == QString("Utopian Megacorporation")); QVERIFY(c.detail(QContactOrganization::Type).value(QContactOrganization::FieldTitle).toString() == QString("Generic Employee")); } void tst_QContactDetails::phoneNumber() { QContact c; QContactPhoneNumber p1, p2; // test property set p1.setNumber("1234"); QCOMPARE(p1.number(), QString("1234")); QCOMPARE(p1.value(QContactPhoneNumber::FieldNumber).toString(), QString("1234")); // Sub types p1.setSubTypes(QList() << QContactPhoneNumber::SubTypeCar); QCOMPARE(p1.subTypes(), QList() << QContactPhoneNumber::SubTypeCar); QList sl; sl << QContactPhoneNumber::SubTypeMobile << QContactPhoneNumber::SubTypeFax; p1.setSubTypes(sl); QCOMPARE(p1.subTypes(), sl); // test property add QVERIFY(c.saveDetail(&p1)); QCOMPARE(c.details(QContactPhoneNumber::Type).count(), 1); QCOMPARE(QContactPhoneNumber(c.details(QContactPhoneNumber::Type).value(0)).number(), p1.number()); // test property update p1.setValue(QContactPhoneNumber::FieldContext, "label1"); p1.setNumber("12345"); QVERIFY(c.saveDetail(&p1)); QCOMPARE(c.details(QContactPhoneNumber::Type).value(0).value(QContactPhoneNumber::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactPhoneNumber::Type).value(0).value(QContactPhoneNumber::FieldNumber).toString(), QString("12345")); p1.setSubTypes(QList() << QContactPhoneNumber::SubTypeDtmfMenu); c.saveDetail(&p1); QCOMPARE(c.detail(QContactPhoneNumber::Type).value >(QContactPhoneNumber::FieldSubTypes), QList() << QContactPhoneNumber::SubTypeDtmfMenu); p1.setSubTypes(QList() << QContactPhoneNumber::SubTypeModem << QContactPhoneNumber::SubTypeFax); c.saveDetail(&p1); QVERIFY(c.detail(QContactPhoneNumber::Type).value >(QContactPhoneNumber::FieldSubTypes) == p1.subTypes()); // test property remove QVERIFY(c.removeDetail(&p1)); QCOMPARE(c.details(QContactPhoneNumber::Type).count(), 0); QVERIFY(c.saveDetail(&p2)); QCOMPARE(c.details(QContactPhoneNumber::Type).count(), 1); QVERIFY(c.removeDetail(&p2)); QCOMPARE(c.details(QContactPhoneNumber::Type).count(), 0); QVERIFY(c.removeDetail(&p2) == false); QCOMPARE(c.details(QContactPhoneNumber::Type).count(), 0); } void tst_QContactDetails::presence() { QContact c; QContactPresence p1, p2; // test property set QDateTime ts = QDateTime::currentDateTime(); p1.setTimestamp(ts); p1.setNickname(QString("nick")); p1.setPresenceState(QContactPresence::PresenceExtendedAway); p1.setPresenceStateText("1234"); p1.setPresenceStateImageUrl(QUrl("http://example.com/someimage.png")); p1.setCustomMessage("gone fishing!"); QCOMPARE(p1.timestamp(), ts); QCOMPARE(p1.value(QContactPresence::FieldTimestamp), ts); QCOMPARE(p1.nickname(), QString("nick")); QCOMPARE(p1.value(QContactPresence::FieldNickname).toString(), QString("nick")); QCOMPARE(p1.presenceState(), QContactPresence::PresenceExtendedAway); QCOMPARE(p1.value(QContactPresence::FieldPresenceState), static_cast(QContactPresence::PresenceExtendedAway)); QCOMPARE(p1.presenceStateText(), QString("1234")); QCOMPARE(p1.value(QContactPresence::FieldPresenceStateText).toString(), QString("1234")); QCOMPARE(p1.presenceStateImageUrl(), QUrl("http://example.com/someimage.png")); QCOMPARE(p1.value(QContactPresence::FieldPresenceStateImageUrl), QUrl("http://example.com/someimage.png")); QCOMPARE(p1.customMessage(), QString("gone fishing!")); QCOMPARE(p1.value(QContactPresence::FieldCustomMessage).toString(), QString("gone fishing!")); // test property add QVERIFY(c.saveDetail(&p1)); QCOMPARE(c.details(QContactPresence::Type).count(), 1); QCOMPARE(QContactPresence(c.details(QContactPresence::Type).value(0)).presenceStateText(), p1.presenceStateText()); // test property update p1.setValue(QContactPresence::FieldContext, "label1"); p1.setPresenceStateText("12345"); QVERIFY(c.saveDetail(&p1)); QCOMPARE(c.details(QContactPresence::Type).value(0).value(QContactPresence::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactPresence::Type).value(0).value(QContactPresence::FieldPresenceStateText).toString(), QString("12345")); p2.setTimestamp(ts); p2.setNickname(QString("test")); p2.setPresenceState(QContactPresence::PresenceAvailable); p2.setPresenceStateText("online"); p2.setPresenceStateImageUrl(QUrl("http://example.com/someimage.png")); p2.setCustomMessage("C is for generic biscuit-type pastry product!"); // test property remove QVERIFY(c.removeDetail(&p1)); QCOMPARE(c.details(QContactPresence::Type).count(), 0); QVERIFY(c.saveDetail(&p2)); QCOMPARE(c.details(QContactPresence::Type).count(), 1); QVERIFY(c.removeDetail(&p2)); QCOMPARE(c.details(QContactPresence::Type).count(), 0); QVERIFY(c.removeDetail(&p2) == false); QCOMPARE(c.details(QContactPresence::Type).count(), 0); } void tst_QContactDetails::ringtone() { QContact c; QContactRingtone r1, r2; // test property set r1.setAudioRingtoneUrl(QUrl("audioUrl")); QCOMPARE(r1.audioRingtoneUrl(), QUrl("audioUrl")); QCOMPARE(r1.value(QContactRingtone::FieldAudioRingtoneUrl), QUrl("audioUrl")); // and the other fields r2.setVideoRingtoneUrl(QUrl("videoUrl")); QCOMPARE(r2.videoRingtoneUrl(), QUrl("videoUrl")); QCOMPARE(r2.value(QContactRingtone::FieldVideoRingtoneUrl), QUrl("videoUrl")); r2.setVibrationRingtoneUrl(QUrl("vibrationUrl")); QCOMPARE(r2.vibrationRingtoneUrl(), QUrl("vibrationUrl")); QCOMPARE(r2.value(QContactRingtone::FieldVibrationRingtoneUrl), QUrl("vibrationUrl")); // test property add QVERIFY(c.saveDetail(&r1)); QCOMPARE(c.details(QContactRingtone::Type).count(), 1); QCOMPARE(QContactRingtone(c.details(QContactRingtone::Type).value(0)).audioRingtoneUrl(), r1.audioRingtoneUrl()); // test property update r1.setValue(QContactRingtone::FieldContext, "label1"); r1.setAudioRingtoneUrl(QUrl("audioUrl2")); QVERIFY(c.saveDetail(&r1)); QCOMPARE(c.details(QContactRingtone::Type).value(0).value(QContactRingtone::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactRingtone::Type).value(0).value(QContactRingtone::FieldAudioRingtoneUrl), QUrl("audioUrl2")); // test property remove QVERIFY(c.removeDetail(&r1)); QCOMPARE(c.details(QContactRingtone::Type).count(), 0); QVERIFY(c.saveDetail(&r2)); QCOMPARE(c.details(QContactRingtone::Type).count(), 1); QVERIFY(c.removeDetail(&r2)); QCOMPARE(c.details(QContactRingtone::Type).count(), 0); QVERIFY(c.removeDetail(&r2) == false); QCOMPARE(c.details(QContactRingtone::Type).count(), 0); } void tst_QContactDetails::syncTarget() { QContact c; QContactSyncTarget s1, s2; // test property set s1.setSyncTarget("1234"); QCOMPARE(s1.syncTarget(), QString("1234")); QCOMPARE(s1.value(QContactSyncTarget::FieldSyncTarget).toString(), QString("1234")); // test property add QVERIFY(c.saveDetail(&s1)); QCOMPARE(c.details(QContactSyncTarget::Type).count(), 1); QCOMPARE(QContactSyncTarget(c.details(QContactSyncTarget::Type).value(0)).syncTarget(), s1.syncTarget()); // test property update s1.setValue(QContactSyncTarget::FieldContext, "label1"); s1.setSyncTarget("12345"); QVERIFY(c.saveDetail(&s1)); QCOMPARE(c.details(QContactSyncTarget::Type).value(0).value(QContactSyncTarget::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactSyncTarget::Type).value(0).value(QContactSyncTarget::FieldSyncTarget).toString(), QString("12345")); // test property remove QVERIFY(c.removeDetail(&s1)); QCOMPARE(c.details(QContactSyncTarget::Type).count(), 0); QVERIFY(c.saveDetail(&s2)); QCOMPARE(c.details(QContactSyncTarget::Type).count(), 1); QVERIFY(c.removeDetail(&s2)); QCOMPARE(c.details(QContactSyncTarget::Type).count(), 0); QVERIFY(c.removeDetail(&s2) == false); QCOMPARE(c.details(QContactSyncTarget::Type).count(), 0); } void tst_QContactDetails::tag() { QContact c; QContactTag t1, t2; // test property set t1.setTag("red"); QCOMPARE(t1.tag(), QString("red")); QCOMPARE(t1.value(QContactTag::FieldTag).toString(), QString("red")); // test property add QVERIFY(c.saveDetail(&t1)); QCOMPARE(c.details(QContactTag::Type).count(), 1); QCOMPARE(QContactTag(c.details(QContactTag::Type).value(0)).tag(), t1.tag()); QVERIFY(c.saveDetail(&t2)); QCOMPARE(c.details(QContactTag::Type).count(), 2); // test property update t1.setValue(QContactTag::FieldContext, "label1"); t1.setTag("green"); QVERIFY(c.saveDetail(&t1)); QCOMPARE(c.details(QContactTag::Type).value(0).value(QContactTag::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactTag::Type).value(0).value(QContactTag::FieldTag).toString(), QString("green")); // test property remove QVERIFY(c.removeDetail(&t1)); QCOMPARE(c.details(QContactTag::Type).count(), 1); QVERIFY(c.removeDetail(&t2)); QCOMPARE(c.details(QContactTag::Type).count(), 0); QVERIFY(c.removeDetail(&t2) == false); QCOMPARE(c.details(QContactTag::Type).count(), 0); } void tst_QContactDetails::timestamp() { QContact c; QContactTimestamp t1, t2; QDateTime modified = QDateTime::currentDateTime(); QDateTime created = modified.addSecs(-43); // test property set t1.setCreated(created); QCOMPARE(t1.created(), created); QCOMPARE(t1.value(QContactTimestamp::FieldCreationTimestamp).toDateTime(), created); // test property add QVERIFY(c.saveDetail(&t1)); QCOMPARE(c.details(QContactTimestamp::Type).count(), 1); QCOMPARE(QContactTimestamp(c.details(QContactTimestamp::Type).value(0)).created(), t1.created()); // test property update t1.setValue(QContactTimestamp::FieldContext, "label1"); t1.setLastModified(modified); QVERIFY(c.saveDetail(&t1)); QCOMPARE(c.details(QContactTimestamp::Type).value(0).value(QContactTimestamp::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactTimestamp::Type).value(0).value(QContactTimestamp::FieldCreationTimestamp).toDateTime(), created); QCOMPARE(c.details(QContactTimestamp::Type).value(0).value(QContactTimestamp::FieldModificationTimestamp).toDateTime(), modified); // test property remove QVERIFY(c.removeDetail(&t1)); QCOMPARE(c.details(QContactTimestamp::Type).count(), 0); t2.setCreated(created.addSecs(15)); QVERIFY(c.saveDetail(&t2)); QCOMPARE(c.details(QContactTimestamp::Type).count(), 1); QVERIFY(c.removeDetail(&t2)); QCOMPARE(c.details(QContactTimestamp::Type).count(), 0); QVERIFY(c.removeDetail(&t2) == false); QCOMPARE(c.details(QContactTimestamp::Type).count(), 0); } void tst_QContactDetails::type() { QContact c; QContactType t1, t2; // test property set t1.setType(QContactType::TypeGroup); QCOMPARE(t1.type(), QContactType::TypeGroup); QCOMPARE(static_cast(t1.value(QContactType::FieldType)), QContactType::TypeGroup); // test property add QVERIFY(c.saveDetail(&t1)); QCOMPARE(c.details(QContactType::Type).count(), 1); QCOMPARE(QContactType(c.details(QContactType::Type).value(0)).type(), t1.type()); // test property update t1.setType(QContactType::TypeContact); QVERIFY(c.saveDetail(&t1)); QCOMPARE(static_cast(c.details(QContactType::Type).value(0).value(QContactType::FieldType)), QContactType::TypeContact); // test property remove QVERIFY(!c.removeDetail(&t1)); // cannot remove type QCOMPARE(c.details(QContactType::Type).count(), 1); t2.setType(QContactType::TypeGroup); QVERIFY(c.saveDetail(&t2)); // overwrites t1 QCOMPARE(c.details(QContactType::Type).count(), 1); QVERIFY(!c.removeDetail(&t2)); // cannot remove type - "succeeds" but count remains unchanged QCOMPARE(c.details(QContactType::Type).count(), 1); QVERIFY(!c.removeDetail(&t2)); QCOMPARE(c.details(QContactType::Type).count(), 1); } void tst_QContactDetails::url() { QContact c; QContactUrl u1, u2; // test property set u1.setUrl("1234"); QCOMPARE(u1.url(), QString("1234")); QCOMPARE(u1.value(QContactUrl::FieldUrl).toString(), QString("1234")); u1.setSubType(QContactUrl::SubTypeHomePage); QCOMPARE(u1.subType(), QContactUrl::SubTypeHomePage); QCOMPARE(static_cast(u1.value(QContactUrl::FieldSubType)), QContactUrl::SubTypeHomePage); // test property add QVERIFY(c.saveDetail(&u1)); QCOMPARE(c.details(QContactUrl::Type).count(), 1); QCOMPARE(QContactUrl(c.details(QContactUrl::Type).value(0)).url(), u1.url()); // test property update u1.setValue(QContactUrl::FieldContext, "label1"); u1.setUrl("12345"); QVERIFY(c.saveDetail(&u1)); QCOMPARE(c.details(QContactUrl::Type).value(0).value(QContactUrl::FieldContext).toString(), QString("label1")); QCOMPARE(c.details(QContactUrl::Type).value(0).value(QContactUrl::FieldUrl).toString(), QString("12345")); // now as above, but with the QUrl setter. QUrl urlValue("http://www.example.com"); QContactUrl u3; u3.setUrl(urlValue); QCOMPARE(u3.url(), urlValue.toString()); QVERIFY(c.saveDetail(&u3)); QVERIFY(c.details(QContactUrl::Type).contains(u3)); u3.setUrl(QString(QStringLiteral("http://www.anotherexample.com"))); QCOMPARE(u3.url(), QString(QStringLiteral("http://www.anotherexample.com"))); QVERIFY(c.saveDetail(&u3)); // test property remove QVERIFY(c.removeDetail(&u3)); QVERIFY(c.removeDetail(&u1)); QCOMPARE(c.details(QContactUrl::Type).count(), 0); QVERIFY(c.saveDetail(&u2)); QCOMPARE(c.details(QContactUrl::Type).count(), 1); QVERIFY(c.removeDetail(&u2)); QCOMPARE(c.details(QContactUrl::Type).count(), 0); QVERIFY(c.removeDetail(&u2) == false); QCOMPARE(c.details(QContactUrl::Type).count(), 0); } void tst_QContactDetails::version() { QContact c; QContactVersion v1, v2; QByteArray extendedVersion("Qt rules!"); QByteArray anotherExtendedVersion("Qt rules again!"); // test property set v1.setSequenceNumber(64); QCOMPARE(v1.sequenceNumber(), 64); QCOMPARE(v1.value(QContactVersion::FieldSequenceNumber).toInt(), 64); v1.setExtendedVersion(extendedVersion); QCOMPARE(v1.extendedVersion(), extendedVersion); QCOMPARE(v1.value(QContactVersion::FieldExtendedVersion).toByteArray(), extendedVersion); // test property add QVERIFY(c.saveDetail(&v1)); QCOMPARE(c.details(QContactVersion::Type).count(), 1); QCOMPARE(QContactVersion(c.details(QContactVersion::Type).value(0)).sequenceNumber(), v1.sequenceNumber()); QCOMPARE(QContactVersion(c.details(QContactVersion::Type).value(0)).extendedVersion(), v1.extendedVersion()); QVERIFY(c.saveDetail(&v2)); QCOMPARE(c.details(QContactVersion::Type).count(), 2); // test property update v1.setSequenceNumber(65); v1.setExtendedVersion(anotherExtendedVersion); QVERIFY(c.saveDetail(&v1)); QCOMPARE(c.details(QContactVersion::Type).value(0).value(QContactVersion::FieldSequenceNumber).toInt(), 65); QCOMPARE(c.details(QContactVersion::Type).value(0).value(QContactVersion::FieldExtendedVersion).toByteArray(), anotherExtendedVersion); // test property remove QVERIFY(c.removeDetail(&v1)); QCOMPARE(c.details(QContactVersion::Type).count(), 1); QVERIFY(c.removeDetail(&v2)); QCOMPARE(c.details(QContactVersion::Type).count(), 0); QVERIFY(c.removeDetail(&v2) == false); QCOMPARE(c.details(QContactVersion::Type).count(), 0); } QTEST_MAIN(tst_QContactDetails) #include "tst_qcontactdetails.moc" tests/auto/contacts/qcontactfilter/000077500000000000000000000000001233466112000200265ustar00rootroot00000000000000tests/auto/contacts/qcontactfilter/qcontactfilter.pro000066400000000000000000000001661233466112000235750ustar00rootroot00000000000000include(../../auto.pri) QT += contacts SOURCES += tst_qcontactfilter.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactfilter/tst_qcontactfilter.cpp000066400000000000000000001226651233466112000244620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include //TESTED_COMPONENT=src/contacts QTCONTACTS_USE_NAMESPACE Q_DECLARE_METATYPE(QContact) Q_DECLARE_METATYPE(QContactFilter) class tst_QContactFilter : public QObject { Q_OBJECT public: tst_QContactFilter(); virtual ~tst_QContactFilter(); public slots: void init(); void cleanup(); private slots: void classHierarchy(); void intersectionFilter(); void unionFilter(); void detailFilter(); void detailRangeFilter(); void changeLogFilter(); void actionFilter(); void relationshipFilter(); void boringFilters(); void idListFilter(); void canonicalizedFilter(); void canonicalizedFilter_data(); void testFilter(); void testFilter_data(); void datastream(); void datastream_data(); void traits(); }; tst_QContactFilter::tst_QContactFilter() { } tst_QContactFilter::~tst_QContactFilter() { } void tst_QContactFilter::init() { } void tst_QContactFilter::cleanup() { } void tst_QContactFilter::classHierarchy() { /* Test "casting" up and down the hierarchy */ QContactDetailRangeFilter drf; QVERIFY(drf.type() == QContactFilter::ContactDetailRangeFilter); drf.setDetailType(QContactDetail::TypeExtendedDetail, QContactExtendedDetail::FieldName); drf.setRange(1, 20); QContactFilter f = drf; QVERIFY(f.type() == QContactFilter::ContactDetailRangeFilter); QContactDetailRangeFilter drf2 = f; QVERIFY(drf2.type() == QContactFilter::ContactDetailRangeFilter); QVERIFY(drf2.detailType() == QContactDetail::TypeExtendedDetail); QVERIFY(drf2.detailField() == QContactExtendedDetail::FieldName); QVERIFY(drf2.maxValue() == 20); QVERIFY(drf2.minValue() == 1); /* Now try to check if we dangle pointers at all */ { QContactFilter f2 = drf2; } QVERIFY(drf2.type() == QContactFilter::ContactDetailRangeFilter); QVERIFY(drf2.detailType() == QContactDetail::TypeExtendedDetail); QVERIFY(drf2.detailField() == QContactExtendedDetail::FieldName); QVERIFY(drf2.maxValue() == 20); QVERIFY(drf2.minValue() == 1); { QContactDetailRangeFilter rf2 = drf2; rf2.setDetailType(QContactDetail::TypeAddress, QContactAddress::FieldStreet); QVERIFY(rf2.detailType() == QContactDetail::TypeAddress); QVERIFY(drf2.detailType() == QContactDetail::TypeExtendedDetail); } QVERIFY(drf2.type() == QContactFilter::ContactDetailRangeFilter); QVERIFY(drf2.detailType() == QContactDetail::TypeExtendedDetail); QVERIFY(drf2.detailField() == QContactExtendedDetail::FieldName); QVERIFY(drf2.maxValue() == 20); QVERIFY(drf2.minValue() == 1); /* Try creating a default filter and making sure we don't break */ QContactFilter deff, deff2; QVERIFY(deff.type() == QContactFilter::DefaultFilter); QVERIFY(deff == deff); QVERIFY(deff == deff2); QVERIFY(deff != drf2); QVERIFY(drf2 != deff); QContactFilter fdeff = deff; QVERIFY(fdeff.type() == QContactFilter::DefaultFilter); QVERIFY(fdeff == deff); QVERIFY(fdeff == deff2); /* Now some "invalid" filters */ QContactInvalidFilter iff, iff2; QVERIFY(iff.type() == QContactFilter::InvalidFilter); QVERIFY(iff == iff); QVERIFY(iff == iff2); QVERIFY(iff != drf2); QVERIFY(drf2 != iff); QContactFilter fiff = iff; QVERIFY(fiff.type() == QContactFilter::InvalidFilter); QVERIFY(fiff == iff); QVERIFY(fiff == iff2); /* Now test some "cross casting" */ } void tst_QContactFilter::intersectionFilter() { /* Test boolean ops */ QContactDetailFilter df; df.setDetailType(QContactDetail::TypeExtendedDetail, QContactExtendedDetail::FieldName); QContactDetailFilter df2; df2.setDetailType(QContactDetail::TypeAddress, QContactAddress::FieldStreet); QContactDetailFilter df3; df3.setDetailType(QContactDetail::TypePhoneNumber, QContactPhoneNumber::FieldNumber); QContactIntersectionFilter bf; bf << df << df2; QContactFilter f = df & df2; QVERIFY(bf == f); QContactFilter f2 = bf & df3; QVERIFY(f2.type() == QContactFilter::IntersectionFilter); QContactIntersectionFilter bf2 = f2; QVERIFY(bf2 == f2); QCOMPARE(bf2.filters().count(), 2); QVERIFY(bf2.filters().at(0) == bf); QVERIFY(bf2.filters().at(1) == df3); f2 = df3 & bf; QVERIFY(f2.type() == QContactFilter::IntersectionFilter); bf2 = f2; QVERIFY(bf2 == f2); QCOMPARE(bf2.filters().count(), 2); QVERIFY(bf2.filters().at(0) == df3); QVERIFY(bf2.filters().at(1) == bf); /* Save this list */ QList filterList = bf2.filters(); f2 = df & df2 & df3; QVERIFY(f2.type() == QContactFilter::IntersectionFilter); bf2 = f2; QVERIFY(bf2 == f2); QCOMPARE(bf2.filters().count(), 2); QVERIFY(bf2.filters().at(0) == (df & df2)); QVERIFY(bf2.filters().at(1) == df3); /* Self assignment should do nothing */ bf2 = bf2; QVERIFY(bf2 == f2); /* Test set filter */ bf2.setFilters(filterList); QCOMPARE(bf2.filters().count(), 2); QVERIFY(bf2.filters().at(0) == df3); QVERIFY(bf2.filters().at(1) == bf); /* Test remove */ bf2.remove(bf); QCOMPARE(bf2.filters().count(), 1); QVERIFY(bf2.filters().at(0) == df3); /* Double remove, should do nothing */ bf2.remove(bf); QCOMPARE(bf2.filters().count(), 1); QVERIFY(bf2.filters().at(0) == df3); /* Append/prepend */ QContactIntersectionFilter bf3; bf3.append(df); QVERIFY(bf3.filters().count() == 1); bf3.prepend(df2); QVERIFY(bf3.filters().count() == 2); QVERIFY(bf3.filters().at(0) == df2); QVERIFY(bf3.filters().at(1) == df); bf3.append(df3); QVERIFY(bf3.filters().count() == 3); QVERIFY(bf3.filters().at(0) == df2); QVERIFY(bf3.filters().at(1) == df); QVERIFY(bf3.filters().at(2) == df3); bf3.prepend(df3); QVERIFY(bf3.filters().count() == 4); QVERIFY(bf3.filters().at(0) == df3); QVERIFY(bf3.filters().at(1) == df2); QVERIFY(bf3.filters().at(2) == df); QVERIFY(bf3.filters().at(3) == df3); } void tst_QContactFilter::unionFilter() { /* Test boolean ops */ QContactDetailFilter df; df.setDetailType(QContactDetail::TypeExtendedDetail, QContactExtendedDetail::FieldName); QContactDetailFilter df2; df2.setDetailType(QContactDetail::TypeAddress, QContactAddress::FieldStreet); QContactDetailFilter df3; df3.setDetailType(QContactDetail::TypePhoneNumber, QContactPhoneNumber::FieldNumber); QContactUnionFilter bf; bf << df << df2; QContactFilter f = df | df2; QVERIFY(bf == f); QContactFilter f2 = bf | df3; QVERIFY(f2.type() == QContactFilter::UnionFilter); QContactUnionFilter bf2 = f2; QVERIFY(bf2 == f2); QCOMPARE(bf2.filters().count(), 3); QVERIFY(bf2.filters().at(0) == df); QVERIFY(bf2.filters().at(1) == df2); QVERIFY(bf2.filters().at(2) == df3); f2 = df3 | bf; QVERIFY(f2.type() == QContactFilter::UnionFilter); bf2 = f2; QVERIFY(bf2 == f2); QCOMPARE(bf2.filters().count(), 3); QVERIFY(bf2.filters().at(0) == df3); QVERIFY(bf2.filters().at(1) == df); QVERIFY(bf2.filters().at(2) == df2); /* Save this list */ QList filterList = bf2.filters(); f2 = df | df2 | df3; QVERIFY(f2.type() == QContactFilter::UnionFilter); bf2 = f2; QVERIFY(bf2 == f2); QCOMPARE(bf2.filters().count(), 3); QVERIFY(bf2.filters().at(0) == df); QVERIFY(bf2.filters().at(1) == df2); QVERIFY(bf2.filters().at(2) == df3); /* Self assignment should do nothing */ bf2 = bf2; QVERIFY(bf2 == f2); /* Test set filter */ bf2.setFilters(filterList); QCOMPARE(bf2.filters().count(), 3); QVERIFY(bf2.filters().at(0) == df3); QVERIFY(bf2.filters().at(1) == df); QVERIFY(bf2.filters().at(2) == df2); /* Test remove */ bf2.remove(df); QCOMPARE(bf2.filters().count(), 2); QVERIFY(bf2.filters().at(0) == df3); QVERIFY(bf2.filters().at(1) == df2); /* Double remove, should do nothing */ bf2.remove(df); QCOMPARE(bf2.filters().count(), 2); QVERIFY(bf2.filters().at(0) == df3); QVERIFY(bf2.filters().at(1) == df2); /* Append/prepend */ QContactUnionFilter bf3; bf3.append(df); QVERIFY(bf3.filters().count() == 1); bf3.prepend(df2); QVERIFY(bf3.filters().count() == 2); QVERIFY(bf3.filters().at(0) == df2); QVERIFY(bf3.filters().at(1) == df); bf3.append(df3); QVERIFY(bf3.filters().count() == 3); QVERIFY(bf3.filters().at(0) == df2); QVERIFY(bf3.filters().at(1) == df); QVERIFY(bf3.filters().at(2) == df3); bf3.prepend(df3); QVERIFY(bf3.filters().count() == 4); QVERIFY(bf3.filters().at(0) == df3); QVERIFY(bf3.filters().at(1) == df2); QVERIFY(bf3.filters().at(2) == df); QVERIFY(bf3.filters().at(3) == df3); } void tst_QContactFilter::actionFilter() { QContactActionFilter af; /* Test initial conditions */ QVERIFY(af.type() == QContactFilter::ActionFilter); QVERIFY(af.actionName().isEmpty()); af.setActionName("Action Name"); QVERIFY(af.actionName() == "Action Name"); af.setActionName(QString()); QVERIFY(af.actionName().isEmpty()); /* Test op= */ QContactFilter f = af; QVERIFY(f == af); QContactActionFilter af2 = f; QVERIFY(af2 == af); /* Self assignment should do nothing */ af2 = af2; QVERIFY(af2 == af); QContactDetailFilter dfil; QContactActionFilter af3(dfil); QVERIFY(af3.type() == QContactFilter::ActionFilter); QContactActionFilter af4(af); QVERIFY(af4 == af); af = dfil; QVERIFY(af == af3); af = af3; af.setActionName("test"); // should force a detach } void tst_QContactFilter::changeLogFilter() { QContactChangeLogFilter cf; QContactChangeLogFilter cfadded(QContactChangeLogFilter::EventAdded); QContactChangeLogFilter cfchanged(QContactChangeLogFilter::EventChanged); QContactChangeLogFilter cfremoved(QContactChangeLogFilter::EventRemoved); QVERIFY(cf.type() == QContactFilter::ChangeLogFilter); QVERIFY(cf.eventType() == QContactChangeLogFilter::EventAdded); QVERIFY(cfadded.type() == QContactFilter::ChangeLogFilter); QVERIFY(cfadded.eventType() == QContactChangeLogFilter::EventAdded); QVERIFY(cfchanged.type() == QContactFilter::ChangeLogFilter); QVERIFY(cfchanged.eventType() == QContactChangeLogFilter::EventChanged); QVERIFY(cfremoved.type() == QContactFilter::ChangeLogFilter); QVERIFY(cfremoved.eventType() == QContactChangeLogFilter::EventRemoved); /* Just to break the naming scheme */ cfchanged.setEventType(QContactChangeLogFilter::EventAdded); QVERIFY(cfchanged.eventType() == QContactChangeLogFilter::EventAdded); QVERIFY(cf.since() == QDateTime()); QDateTime now = QDateTime::currentDateTime(); cf.setSince(now); QVERIFY(cf.since() == now); cf.setSince(QDateTime()); QVERIFY(cf.since() == QDateTime()); /* Test op= */ QContactFilter f = cf; QVERIFY(f == cf); QContactChangeLogFilter cf2 = f; QVERIFY(cf2 == cf); /* Self assignment should do nothing */ cf2 = cf2; QVERIFY(cf2 == cf); QContactDetailFilter dfil; QContactChangeLogFilter cf3(dfil); QVERIFY(cf3.type() == QContactFilter::ChangeLogFilter); QContactChangeLogFilter cf4(cf); QVERIFY(cf4 == cf); cf = dfil; QVERIFY(cf == cf3); cf = cf3; cf.setEventType(QContactChangeLogFilter::EventRemoved); // force a detach } void tst_QContactFilter::detailFilter() { QContactDetailFilter df; QVERIFY(df.type() == QContactFilter::ContactDetailFilter); QVERIFY(df.detailType() == QContactDetail::TypeUndefined); QVERIFY(df.detailField() == -1); QVERIFY(df.matchFlags() == 0); QVERIFY(df.value().isNull()); df.setDetailType(QContactDetail::TypeAddress); QVERIFY(df.detailType() == QContactDetail::TypeAddress); QVERIFY(df.detailField() == -1); QVERIFY(df.matchFlags() == 0); QVERIFY(df.value().isNull()); df.setDetailType(QContactDetail::TypeAddress, QContactAddress::FieldStreet); QVERIFY(df.detailType() == QContactDetail::TypeAddress); QVERIFY(df.detailField() == QContactAddress::FieldStreet); QVERIFY(df.matchFlags() == 0); QVERIFY(df.value().isNull()); df.setMatchFlags(QContactFilter::MatchExactly); QVERIFY(df.matchFlags() == QContactFilter::MatchExactly); df.setValue(5); QVERIFY(df.value() == 5); df.setValue("String value"); QVERIFY(df.value() == "String value"); /* Test op= */ QContactFilter f = df; QVERIFY(f == df); QContactDetailFilter df2 = f; QVERIFY(df2 == df); QVERIFY(df2.detailType() == QContactDetail::TypeAddress); QVERIFY(df2.detailField() == QContactAddress::FieldStreet); /* Self assignment should do nothing */ df2 = df2; QVERIFY(df2 == df); /* Some cross casting */ QContactDetailRangeFilter rf; /* Directly */ df2 = rf; QVERIFY(df2.type() == QContactFilter::ContactDetailFilter); QVERIFY(df2.detailType() == QContactDetail::TypeUndefined); QVERIFY(df2.detailField() == -1); QVERIFY(df2.value().isNull()); /* reset it */ df2 = df; QVERIFY(df2.detailType() == QContactDetail::TypeAddress); QVERIFY(df2.detailField() == QContactAddress::FieldStreet); /* Through base class */ f = rf; df2 = f; QVERIFY(df2.detailType() == QContactDetail::TypeUndefined); QVERIFY(df2.detailField() == -1); QVERIFY(df2.value().isNull()); /* Now test copy ctor */ QContactDetailFilter df3(rf); QVERIFY(df3.type() == QContactFilter::ContactDetailFilter); QVERIFY(df3.detailType() == QContactDetail::TypeUndefined); QVERIFY(df3.detailField() == -1); QVERIFY(df3.value().isNull()); /* reset it */ df3 = df; QVERIFY(df3.detailType() == QContactDetail::TypeAddress); QVERIFY(df3.detailField() == QContactAddress::FieldStreet); /* Now test copy ctor through base class */ QContactDetailFilter df4(f); QVERIFY(df4.type() == QContactFilter::ContactDetailFilter); QVERIFY(df4.detailType() == QContactDetail::TypeUndefined); QVERIFY(df4.detailField() == -1); QVERIFY(df4.value().isNull()); /* reset it */ df4 = df; QVERIFY(df4.detailType() == QContactDetail::TypeAddress); QVERIFY(df4.detailField() == QContactAddress::FieldStreet); } void tst_QContactFilter::detailRangeFilter() { QContactDetailRangeFilter rf; QVERIFY(rf.type() == QContactFilter::ContactDetailRangeFilter); QVERIFY(rf.detailType() == QContactDetail::TypeUndefined); QVERIFY(rf.detailField() == -1); QVERIFY(rf.matchFlags() == 0); QVERIFY(rf.minValue().isNull()); QVERIFY(rf.maxValue().isNull()); QVERIFY(rf.rangeFlags() == (QContactDetailRangeFilter::ExcludeUpper | QContactDetailRangeFilter::IncludeLower)); rf.setDetailType(QContactDetail::TypeExtendedDetail); QVERIFY(rf.detailType() == QContactDetail::TypeExtendedDetail); QVERIFY(rf.detailField() == -1); QVERIFY(rf.matchFlags() == 0); QVERIFY(rf.minValue().isNull()); QVERIFY(rf.maxValue().isNull()); QVERIFY(rf.rangeFlags() == (QContactDetailRangeFilter::ExcludeUpper | QContactDetailRangeFilter::IncludeLower)); rf.setDetailType(QContactDetail::TypeExtendedDetail, QContactExtendedDetail::FieldName); QVERIFY(rf.detailType() == QContactDetail::TypeExtendedDetail); QVERIFY(rf.detailField() == QContactExtendedDetail::FieldName); QVERIFY(rf.matchFlags() == 0); QVERIFY(rf.minValue().isNull()); QVERIFY(rf.maxValue().isNull()); QVERIFY(rf.rangeFlags() == (QContactDetailRangeFilter::ExcludeUpper | QContactDetailRangeFilter::IncludeLower)); rf.setMatchFlags(QContactFilter::MatchExactly); QVERIFY(rf.matchFlags() == QContactFilter::MatchExactly); rf.setMatchFlags(QContactFilter::MatchCaseSensitive); QVERIFY(rf.matchFlags() == QContactFilter::MatchCaseSensitive); // Contains is not allowed rf.setMatchFlags(QContactFilter::MatchCaseSensitive | QContactFilter::MatchContains); QVERIFY(rf.matchFlags() == QContactFilter::MatchCaseSensitive); rf.setMatchFlags(QContactFilter::MatchEndsWith); QVERIFY(rf.matchFlags() == QContactFilter::MatchExactly); // 0 rf.setRange(5, 10); QVERIFY(rf.minValue() == 5); QVERIFY(rf.maxValue() == 10); QVERIFY(rf.rangeFlags() == (QContactDetailRangeFilter::ExcludeUpper | QContactDetailRangeFilter::IncludeLower)); rf.setRange(QVariant(), 11); QVERIFY(rf.minValue().isNull()); QVERIFY(rf.maxValue() == 11); QVERIFY(rf.rangeFlags() == (QContactDetailRangeFilter::ExcludeUpper | QContactDetailRangeFilter::IncludeLower)); rf.setRange(6, QVariant()); QVERIFY(rf.minValue() == 6); QVERIFY(rf.maxValue().isNull()); QVERIFY(rf.rangeFlags() == (QContactDetailRangeFilter::ExcludeUpper | QContactDetailRangeFilter::IncludeLower)); rf.setRange(QVariant(), QVariant()); QVERIFY(rf.minValue().isNull()); QVERIFY(rf.maxValue().isNull()); QVERIFY(rf.rangeFlags() == (QContactDetailRangeFilter::ExcludeUpper | QContactDetailRangeFilter::IncludeLower)); rf.setRange(5, 10, QContactDetailRangeFilter::ExcludeLower); QVERIFY(rf.minValue() == 5); QVERIFY(rf.maxValue() == 10); QVERIFY(rf.rangeFlags() == (QContactDetailRangeFilter::ExcludeUpper | QContactDetailRangeFilter::ExcludeLower)); rf.setRange(QVariant(), 11, QContactDetailRangeFilter::IncludeUpper); QVERIFY(rf.minValue().isNull()); QVERIFY(rf.maxValue() == 11); QVERIFY(rf.rangeFlags() == (QContactDetailRangeFilter::IncludeUpper | QContactDetailRangeFilter::IncludeLower)); rf.setRange(6, QVariant(), QContactDetailRangeFilter::ExcludeLower | QContactDetailRangeFilter::IncludeUpper); QVERIFY(rf.minValue() == 6); QVERIFY(rf.maxValue().isNull()); QVERIFY(rf.rangeFlags() == (QContactDetailRangeFilter::IncludeUpper | QContactDetailRangeFilter::ExcludeLower)); rf.setRange(QVariant(), QVariant(), QContactDetailRangeFilter::ExcludeUpper | QContactDetailRangeFilter::IncludeLower); QVERIFY(rf.minValue().isNull()); QVERIFY(rf.maxValue().isNull()); QVERIFY(rf.rangeFlags() == (QContactDetailRangeFilter::ExcludeUpper | QContactDetailRangeFilter::IncludeLower)); /* Test op= */ QContactFilter f = rf; QVERIFY(f == rf); QContactDetailRangeFilter rf2 = f; QVERIFY(rf2 == rf); rf2 = rf; QVERIFY(rf2 == f); /* Self assignment should do nothing */ rf2 = rf2; QVERIFY(rf2 == rf); } void tst_QContactFilter::relationshipFilter() { QContactRelationshipFilter crf; QVERIFY(crf.type() == QContactFilter::RelationshipFilter); QVERIFY(crf.relationshipType() == QString()); QVERIFY(crf.relatedContact() == QContact()); QContact newContact; /*newContact.id().setManagerUri("test"); newContact.id().setLocalId(QContactId(5));*/ crf.setRelatedContact(newContact); QVERIFY(crf.relationshipType() == QString()); QVERIFY(crf.relatedContact() == newContact); crf.setRelatedContactRole(QContactRelationship::First); QVERIFY(crf.relationshipType() == QString()); QVERIFY(crf.relatedContact() == newContact); crf.setRelationshipType(QContactRelationship::HasManager()); QVERIFY(crf.relationshipType() == QContactRelationship::HasManager()); QVERIFY(crf.relatedContact() == newContact); /* Test op= */ QContactFilter f = crf; QVERIFY(f == crf); QContactRelationshipFilter crf2 = f; QVERIFY(crf2 == crf); /* Self assignment should do nothing */ crf2 = crf2; QVERIFY(crf2 == crf); QContactDetailFilter dfil; QContactRelationshipFilter crf3(dfil); QVERIFY(crf3.type() == QContactFilter::RelationshipFilter); // should be a blank rel fil QContactRelationshipFilter crf4(crf); QVERIFY(crf4 == crf); crf = dfil; QVERIFY(crf == crf3); crf = crf3; crf.setRelationshipType("test"); // force a detach } void tst_QContactFilter::boringFilters() { QContactFilter all; QVERIFY(all.type() == QContactFilter::DefaultFilter); QContactInvalidFilter invalid; QVERIFY(invalid.type() == QContactFilter::InvalidFilter); QVERIFY(all != invalid); QVERIFY(!(all == invalid)); /* Test op= */ QContactFilter f = all; QVERIFY(f == all); QContactFilter f2; f2 = f; QVERIFY(f2 == all); /* Self assignment should do nothing */ f2 = f2; QVERIFY(f2 == all); /* InvalidFilter, op= */ QContactInvalidFilter inv2 = invalid; QVERIFY(inv2 == invalid); QContactInvalidFilter inv3; inv3 = inv2; QVERIFY(inv3 == invalid); inv3 = inv3; QVERIFY(inv3 == invalid); inv3 = all; QVERIFY(inv3 == invalid); // won't be all } void tst_QContactFilter::idListFilter() { QContactIdFilter idf; QVERIFY(idf.type() == QContactFilter::IdFilter); QVERIFY(idf.ids().count() == 0); QList ids; ids << QContactId() << QContactId() << QContactId(); idf.setIds(ids); QVERIFY(idf.ids() == ids); idf.setIds(QList()); QVERIFY(idf.ids().count() == 0); /* Test op= */ idf.setIds(ids); QContactFilter f = idf; QVERIFY(f == idf); QContactIdFilter idf2 = f; QVERIFY(idf2 == idf); QVERIFY(idf2.ids() == ids); idf2 = idf; QVERIFY(idf2 == f); /* Self assignment should do nothing */ idf2 = idf2; QVERIFY(idf2 == idf); QContactDetailFilter dfil; QContactIdFilter idf3(dfil); QVERIFY(idf3.type() == QContactFilter::IdFilter); // should be a blank id list filter QContactIdFilter idf4(idf); QVERIFY(idf4 == idf); // should be a copy of idf. idf = dfil; // now assign. QVERIFY(idf == idf3); // again, should be a blank id list filter. idf = idf3; idf.setIds(ids); // force a detach } void tst_QContactFilter::canonicalizedFilter() { QFETCH(QContactFilter, in); QFETCH(QContactFilter, expected); QContactFilter out = QContactManagerEngine::canonicalizedFilter(in); QCOMPARE(out, expected); } void tst_QContactFilter::canonicalizedFilter_data() { QTest::addColumn("in"); QTest::addColumn("expected"); QContactFilter detailFilter1 = QContactName::match("1"); QContactFilter detailFilter2 = QContactName::match("2"); QContactInvalidFilter invalidFilter; QContactFilter defaultFilter; { QTest::newRow("Normal detail filter") << static_cast(detailFilter1) << static_cast(detailFilter1); } { QContactIntersectionFilter qcif; qcif << detailFilter1; qcif << detailFilter2; QTest::newRow("Normal intersection filter") << static_cast(qcif) << static_cast(qcif); } { QContactUnionFilter qcuf; qcuf << detailFilter1; qcuf << detailFilter2; QTest::newRow("Normal intersection filter") << static_cast(qcuf) << static_cast(qcuf); } { QContactIntersectionFilter qcif; QTest::newRow("Empty intersection") << static_cast(qcif) << static_cast(defaultFilter); } { QContactUnionFilter qcuf; QTest::newRow("Empty union") << static_cast(qcuf) << static_cast(invalidFilter); } { QContactIntersectionFilter qcif; qcif << detailFilter1; QTest::newRow("Single entry intersection filter") << static_cast(qcif) << static_cast(detailFilter1); } { QContactUnionFilter qcuf; qcuf << detailFilter1; QTest::newRow("Single entry union filter") << static_cast(qcuf) << static_cast(detailFilter1); } { QContactIntersectionFilter qcif; qcif << invalidFilter; qcif << detailFilter1; qcif << detailFilter2; QTest::newRow("Intersection with invalid") << static_cast(qcif) << static_cast(invalidFilter); } { QContactIntersectionFilter qcif; qcif << defaultFilter; qcif << detailFilter1; qcif << detailFilter2; QContactIntersectionFilter expected; expected << detailFilter1; expected << detailFilter2; QTest::newRow("Intersection with default") << static_cast(qcif) << static_cast(expected); } { QContactUnionFilter qcuf; qcuf << invalidFilter; qcuf << detailFilter1; qcuf << detailFilter2; QContactUnionFilter expected; expected << detailFilter1; expected << detailFilter2; QTest::newRow("Union with invalid") << static_cast(qcuf) << static_cast(expected); } { QContactUnionFilter qcuf; qcuf << defaultFilter; qcuf << detailFilter1; qcuf << detailFilter2; QTest::newRow("Union with default") << static_cast(qcuf) << static_cast(defaultFilter); } { QContactIdFilter qclif; QTest::newRow("Empty local id filter") << static_cast(qclif) << static_cast(invalidFilter); } { QContactIdFilter qclif; qclif.setIds(QList() << QContactId() << QContactId()); QTest::newRow("Normal local id filter") << static_cast(qclif) << static_cast(qclif); } { QContactDetailRangeFilter qcdrf; qcdrf.setDetailType(QContactName::Type, QContactName::FieldFirstName); QContactDetailFilter expected; expected.setDetailType(QContactName::Type, QContactName::FieldFirstName); QTest::newRow("Null valued range filter") << static_cast(qcdrf) << static_cast(expected); } { QContactDetailRangeFilter qcdrf; qcdrf.setDetailType(QContactName::Type, QContactName::FieldFirstName); qcdrf.setRange(QStringLiteral("a"), QStringLiteral("a")); qcdrf.setMatchFlags(QContactFilter::MatchFixedString); QContactDetailFilter expected; expected.setDetailType(QContactName::Type, QContactName::FieldFirstName); expected.setValue(QStringLiteral("a")); expected.setMatchFlags(QContactFilter::MatchFixedString); QTest::newRow("Equal valued range filter") << static_cast(qcdrf) << static_cast(expected); } { QContactDetailRangeFilter qcdrf; qcdrf.setDetailType(QContactName::Type, QContactName::FieldFirstName); qcdrf.setRange(QStringLiteral("a"), QStringLiteral("a"), QContactDetailRangeFilter::ExcludeLower | QContactDetailRangeFilter::ExcludeUpper); qcdrf.setMatchFlags(QContactFilter::MatchFixedString); QTest::newRow("Equal valued range filter with excluded bounds") << static_cast(qcdrf) << static_cast(invalidFilter); } { QContactDetailRangeFilter qcdrf; qcdrf.setDetailType(QContactName::Type, QContactName::FieldFirstName); qcdrf.setRange(QStringLiteral("a"), QStringLiteral("b")); qcdrf.setMatchFlags(QContactFilter::MatchFixedString); QTest::newRow("Normal range filter") << static_cast(qcdrf) << static_cast(qcdrf); } { QContactDetailRangeFilter qcdrf; qcdrf.setDetailType(QContactName::Type, QContactName::FieldFirstName); qcdrf.setRange(QVariant(QVariant::String), QVariant(QVariant::String)); // null bounds qcdrf.setMatchFlags(QContactFilter::MatchFixedString); QContactDetailFilter qcdf; qcdf.setDetailType(QContactName::Type, QContactName::FieldFirstName); qcdf.setMatchFlags(QContactFilter::MatchFixedString); qcdf.setValue(QVariant(QVariant::String)); QTest::newRow("Null valued range filter") << static_cast(qcdrf) << static_cast(qcdf); } { QContactDetailRangeFilter qcdrf; qcdrf.setDetailType(QContactName::Type, QContactName::FieldFirstName); qcdrf.setRange(QVariant(QVariant::String), QStringLiteral("a")); // min is null qcdrf.setMatchFlags(QContactFilter::MatchFixedString); QTest::newRow("One sided range filter") << static_cast(qcdrf) << static_cast(qcdrf); } { QContactDetailRangeFilter qcdrf; QTest::newRow("Empty range filter") << static_cast(qcdrf) << static_cast(invalidFilter); } { QContactDetailFilter qcdf; QTest::newRow("Empty detail filter") << static_cast(qcdf) << static_cast(invalidFilter); } } void tst_QContactFilter::testFilter() { QFETCH(QContact, contact); QFETCH(QContactFilter, filter); QFETCH(bool, expected); QCOMPARE(QContactManagerEngine::testFilter(filter, contact), expected); } void tst_QContactFilter::testFilter_data() { QTest::addColumn("contact"); QTest::addColumn("filter"); QTest::addColumn("expected"); { QContact contact; QContactName name; name.setFirstName(QStringLiteral("first")); name.setMiddleName(QStringLiteral("middle")); name.setLastName(QStringLiteral("last")); name.setPrefix(QStringLiteral("prefix")); name.setSuffix(QStringLiteral("suffix")); contact.saveDetail(&name); QTest::newRow("QContactName::match firstname") << contact << QContactName::match("first") << true; QTest::newRow("QContactName::match lastname") << contact << QContactName::match("last") << true; QTest::newRow("QContactName::match middlename") << contact << QContactName::match("middle") << true; QTest::newRow("QContactName::match prefix") << contact << QContactName::match("prefix") << true; QTest::newRow("QContactName::match suffix") << contact << QContactName::match("suffix") << true; QTest::newRow("QContactName::match first last") << contact << QContactName::match(QStringLiteral("first"), QStringLiteral("last")) << true; QTest::newRow("QContactName::match substring") << contact << QContactName::match(QStringLiteral("irs")) << true; QTest::newRow("QContactName::match first last substring") << contact << QContactName::match(QStringLiteral("irs"), QStringLiteral("as")) << true; QTest::newRow("QContactName::match negative") << contact << QContactName::match("foo") << false; QContactDetailFilter dfWithEmptyValue; dfWithEmptyValue.setDetailType(QContactName::Type, QContactName::FieldFirstName); dfWithEmptyValue.setMatchFlags(QContactFilter::MatchExactly); dfWithEmptyValue.setValue(""); QTest::newRow("QContactDetailFilter exact match for empty value") << contact << (QContactFilter)dfWithEmptyValue << false; QContactDetailFilter dfWithEmptyFieldName; dfWithEmptyFieldName.setDetailType(QContactName::Type); dfWithEmptyFieldName.setMatchFlags(QContactFilter::MatchExactly); dfWithEmptyFieldName.setValue(""); QTest::newRow("QContactDetailFilter exact match for detail name but empty field name") << contact << (QContactFilter)dfWithEmptyFieldName << true; } { QContact contact; QContactDisplayLabel displaylabel; displaylabel.setLabel("foo"); contact.saveDetail(&displaylabel); QTest::newRow("QContactDisplayLabel::match positive") << contact << QContactDisplayLabel::match("foo") << true; QTest::newRow("QContactDisplayLabel::match positive substring") << contact << QContactDisplayLabel::match("o") << true; QTest::newRow("QContactDisplayLabel::match negative") << contact << QContactDisplayLabel::match("bar") << false; } { QContact contact; QContactPhoneNumber phone; phone.setNumber("1234"); contact.saveDetail(&phone); QTest::newRow("QContactPhoneNumber::match positive") << contact << QContactPhoneNumber::match("1234") << true; QTest::newRow("QContactPhoneNumber::match negative") << contact << QContactPhoneNumber::match("5678") << false; } { QContact contact; QContactEmailAddress email; email.setEmailAddress("foo"); contact.saveDetail(&email); QTest::newRow("QContactEmailAddress::match positive") << contact << QContactEmailAddress::match("foo") << true; QTest::newRow("QContactEmailAddress::match positive substring") << contact << QContactEmailAddress::match("o") << true; QTest::newRow("QContactEmailAddress::match negative") << contact << QContactEmailAddress::match("bar") << false; } { QContact contact; QContactOrganization org; org.setDepartment(QStringList("one")); // Single department as a stringlist contact.saveDetail(&org); QContactDetailFilter df; df.setDetailType(QContactOrganization::Type, QContactOrganization::FieldDepartment); // First case sensitive df.setMatchFlags(QContactDetailFilter::MatchCaseSensitive); df.setValue("one"); // this is a string QTest::newRow("QContactOrganization::match single positive against string") << contact << QContactFilter(df) << true; df.setValue(QStringList("one")); // this is a stringlist of the same length QTest::newRow("QContactOrganization::match single positive against stringlist") << contact << QContactFilter(df) << true; df.setValue(QString("two")); QTest::newRow("QContactOrganization::match negative string") << contact << QContactFilter(df) << false; df.setValue(QStringList("two")); QTest::newRow("QContactOrganization::match negative stringlist") << contact << QContactFilter(df) << false; df.setValue(QString("ONE")); QTest::newRow("QContactOrganization::match negative case sensitive string") << contact << QContactFilter(df) << false; df.setValue(QStringList("ONE")); QTest::newRow("QContactOrganization::match negative case sensitive stringlist") << contact << QContactFilter(df) << false; // Now case insensitive df.setMatchFlags(0); df.setValue("one"); // this is a string QTest::newRow("QContactOrganization::match positive insensitive against string") << contact << QContactFilter(df) << true; df.setValue(QStringList("one")); // this is a stringlist of the same length QTest::newRow("QContactOrganization::match positive insensitive against stringlist") << contact << QContactFilter(df) << true; df.setValue(QString("two")); QTest::newRow("QContactOrganization::match negative insensitive string") << contact << QContactFilter(df) << false; df.setValue(QStringList("two")); QTest::newRow("QContactOrganization::match negative insensitive stringlist") << contact << QContactFilter(df) << false; df.setValue(QString("ONE")); QTest::newRow("QContactOrganization::match positive case insensitive string 2") << contact << QContactFilter(df) << true; df.setValue(QStringList("ONE")); QTest::newRow("QContactOrganization::match positive case insensitive stringlist 2") << contact << QContactFilter(df) << true; } } void tst_QContactFilter::datastream() { QFETCH(QContactFilter, filterIn); QByteArray buffer; QDataStream stream1(&buffer, QIODevice::WriteOnly); stream1 << filterIn; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); QContactFilter filterOut; stream2 >> filterOut; QCOMPARE(filterOut, filterIn); } void tst_QContactFilter::datastream_data() { QTest::addColumn("filterIn"); { QContactFilter filter; QTest::newRow("default") << filter; } { QContactActionFilter filter; filter.setActionName("action name"); QTest::newRow("action") << (QContactFilter)filter; } { QContactChangeLogFilter filter; filter.setEventType(QContactChangeLogFilter::EventAdded); filter.setSince(QDateTime(QDate(2010, 6, 1), QTime(1, 2, 3))); QTest::newRow("changelog") << (QContactFilter)filter; } { QContactDetailFilter filter; filter.setDetailType(QContactDetail::TypeAddress, QContactPhoneNumber::FieldNumber); filter.setMatchFlags(QContactFilter::MatchEndsWith); filter.setValue("ski"); QTest::newRow("detail") << (QContactFilter)filter; } { QContactIntersectionFilter filter; QTest::newRow("intersection") << (QContactFilter)filter; } { QContactInvalidFilter filter; QTest::newRow("invalid") << (QContactFilter)filter; } { QContactIdFilter filter; filter.setIds(QList() << QContactId() << QContactId() << QContactId()); QTest::newRow("localid") << (QContactFilter)filter; } { QContactRelationshipFilter filter; filter.setRelationshipType("member"); QContact contact; filter.setRelatedContact(contact); filter.setRelatedContactRole(QContactRelationship::First); QTest::newRow("relationship") << (QContactFilter)filter; } { QContactUnionFilter filter; QTest::newRow("union") << (QContactFilter)filter; } } void tst_QContactFilter::traits() { QCOMPARE(sizeof(QContactFilter), sizeof(void *)); QVERIFY(QTypeInfo::isComplex); QVERIFY(!QTypeInfo::isStatic); QVERIFY(!QTypeInfo::isLarge); QVERIFY(!QTypeInfo::isPointer); QVERIFY(!QTypeInfo::isDummy); QCOMPARE(sizeof(QContactDetailFilter), sizeof(void*)); QCOMPARE(sizeof(QContactChangeLogFilter), sizeof(void*)); } QTEST_MAIN(tst_QContactFilter) #include "tst_qcontactfilter.moc" tests/auto/contacts/qcontactidmock.h000066400000000000000000000074161233466112000201700ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTIDMOCK_H #define QCONTACTIDMOCK_H #include #include // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // QTCONTACTS_USE_NAMESPACE class QContactIdMock : public QContactEngineId { public: QContactIdMock(const QString& managerUri, uint id) : m_managerUri(managerUri), m_id(id) {} bool isEqualTo(const QContactEngineId* other) const { if (m_managerUri == static_cast(other)->m_managerUri) return m_id == static_cast(other)->m_id; return false; } bool isLessThan(const QContactEngineId* other) const { if (m_managerUri == static_cast(other)->m_managerUri) return m_id < static_cast(other)->m_id; return m_managerUri < static_cast(other)->m_managerUri; } QString localId() const { return QString::number(m_id); } QString managerUri() const { return "qtcontacts:" + m_managerUri; } QContactEngineId* clone() const { QContactIdMock* cloned = new QContactIdMock(m_managerUri, m_id); return cloned; } QDebug& debugStreamOut(QDebug& dbg) const { return dbg << m_managerUri << m_id; } QString toString() const { return m_managerUri + QString("::") + QString::number(m_id); } uint hash() const { return qHash(toString()); } static QContactId createId(const QString& managerUri, uint id) { return QContactId(new QContactIdMock(managerUri, id)); } private: QString m_managerUri; uint m_id; }; #endif tests/auto/contacts/qcontactjsondb/000077500000000000000000000000001233466112000200205ustar00rootroot00000000000000tests/auto/contacts/qcontactjsondb/partitions.json000066400000000000000000000001211233466112000231010ustar00rootroot00000000000000[ { "name" : "com.nokia.mt.User" }, { "name" : "com.nokia.mt.System" } ] tests/auto/contacts/qcontactjsondb/qcontactjsondb.pro000066400000000000000000000031501233466112000235550ustar00rootroot00000000000000include(../../auto.pri) QT += contacts contacts-private jsondb SOURCES += tst_qcontactjsondbengine.cpp \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbenginefactory.cpp \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbengine.cpp \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbrequesthandler.cpp \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbrequestmanager.cpp \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbconverter.cpp \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbstring.cpp \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbid.cpp \ qcontactjsondbbackup.cpp \ synchronizedjsondbclient.cpp \ synchronizedworker.cpp DEFINES += SRCDIR=\\\"$$PWD/\\\" INCLUDEPATH += ../../../../src/plugins/contacts/jsondb INCLUDEPATH += ../.. HEADERS += qcontactjsondbbackup.h \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbenginefactory.h \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbengine.h \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbrequesthandler.h \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbrequestmanager.h \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbconverter.h \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbstring.h \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbid.h \ synchronizedjsondbclient.h \ synchronizedworker.h \ ../../jsondbprocess.h DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactjsondb/qcontactjsondbbackup.cpp000066400000000000000000000161531233466112000247340ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include "qcontactjsondbbackup.h" #include "qcontactjsondbconverter.h" #include "qcontactjsondbstring.h" QT_BEGIN_NAMESPACE_CONTACTS QContactJsonDbBackup::QContactJsonDbBackup() { m_dbClient = new SynchronizedJsonDbClient(); backupJsonDb(); } QContactJsonDbBackup::~QContactJsonDbBackup() { revertJsonDb(); delete m_dbClient; } bool QContactJsonDbBackup::loadTestData() { QContactManager cm; QContactSaveRequest csr; // save new contacts QContact testContact; QContactName nameDetail = testContact.detail(); nameDetail.setFirstName("Harry"); nameDetail.setLastName("Baker"); testContact.saveDetail(&nameDetail); QContactEmailAddress emailDetail; emailDetail.setEmailAddress("Harry.Baker@ovi.com"); emailDetail.setContexts(QContactDetail::ContextHome); testContact.saveDetail(&emailDetail); QContactPhoneNumber phoneNumberDetail; QList mySubTypes; mySubTypes << QContactPhoneNumber::SubTypeMobile; phoneNumberDetail.setSubTypes(mySubTypes); phoneNumberDetail.setNumber("+358507654321"); testContact.saveDetail(&phoneNumberDetail); QList saveList; saveList << testContact; nameDetail.setFirstName("Paul"); nameDetail.setLastName("Thomson"); testContact.saveDetail(&nameDetail); emailDetail.setEmailAddress("Paul.Thomson@ovi.com"); emailDetail.setContexts(QContactDetail::ContextHome); testContact.saveDetail(&emailDetail); phoneNumberDetail.setSubTypes(mySubTypes); phoneNumberDetail.setNumber("+358507654321"); testContact.saveDetail(&phoneNumberDetail); saveList << testContact; nameDetail.setFirstName("Julie"); nameDetail.setLastName("King"); testContact.saveDetail(&nameDetail); emailDetail.setEmailAddress("Julie.King@ovi.com"); emailDetail.setContexts(QContactDetail::ContextHome); testContact.saveDetail(&emailDetail); phoneNumberDetail.setSubTypes(mySubTypes); phoneNumberDetail.setNumber("+3585012345"); testContact.saveDetail(&phoneNumberDetail); saveList << testContact; nameDetail.setFirstName("Angelina"); nameDetail.setLastName("Row"); testContact.saveDetail(&nameDetail); emailDetail.setEmailAddress("Angelina.Row@ovi.com"); emailDetail.setContexts(QContactDetail::ContextHome); testContact.saveDetail(&emailDetail); phoneNumberDetail.setSubTypes(mySubTypes); phoneNumberDetail.setNumber("+3585012345"); testContact.saveDetail(&phoneNumberDetail); saveList << testContact; nameDetail.setFirstName("Natalie"); nameDetail.setLastName("Watson"); testContact.saveDetail(&nameDetail); emailDetail.setEmailAddress("Natalie.Watson@ovi.com"); emailDetail.setContexts(QContactDetail::ContextHome); testContact.saveDetail(&emailDetail); phoneNumberDetail.setSubTypes(mySubTypes); phoneNumberDetail.setNumber("+358507654321"); testContact.saveDetail(&phoneNumberDetail); saveList << testContact; csr.setManager(&cm); csr.setContacts(saveList); csr.start(); if (!csr.waitForFinished()) return false; if (csr.error() != QContactManager::NoError) return false; return true; } void QContactJsonDbBackup::backupJsonDb() { // Backup partitions for testing. QString query = "[?_type=\"com.nokia.mt.contacts.Contact\"]"; m_backupDataSystem = m_dbClient->query(query, QContactJsonDbStr::systemPartition()); m_backupDataUser = m_dbClient->query(query, QContactJsonDbStr::userDataPartition()); } bool QContactJsonDbBackup::revertJsonDb() { bool requestStatus = cleanJsonDb(); requestStatus = requestStatus && addContacts(m_backupDataSystem, QContactJsonDbStr::systemPartition()); requestStatus = requestStatus && addContacts(m_backupDataUser, QContactJsonDbStr::userDataPartition()); m_backupDataSystem.clear(); m_backupDataUser.clear(); return requestStatus; } bool QContactJsonDbBackup::cleanJsonDb() { QString query = "[?_type=\"com.nokia.mt.contacts.Contact\"]"; QList map = m_dbClient->query(query, QContactJsonDbStr::systemPartition()); bool requestStatus = deleteContacts(map, QContactJsonDbStr::systemPartition()); map = m_dbClient->query(query, QContactJsonDbStr::userDataPartition()); requestStatus = requestStatus && deleteContacts(map, QContactJsonDbStr::userDataPartition()); return requestStatus; } bool QContactJsonDbBackup::addContacts(const QList &objects, QString partition) { QList map; for (int i = 0; i < objects.size(); ++i) { QJsonObject item; item = objects[i]; map = m_dbClient->create(item, partition); } return true; } bool QContactJsonDbBackup::deleteContacts(const QList &objects, QString partition) { QList map; for(int i = 0; i < objects.size(); ++i) { QJsonObject item; item = objects[i]; map = m_dbClient->remove(item, partition); } return true; } int QContactJsonDbBackup::wasteSomeTime() { int x=0; int y=0; for (int i=0; i<30; i++) { for (int j=0; j<100000000; j++) { y=(j*j); } x=i+y; } return x; } QT_END_NAMESPACE_CONTACTS tests/auto/contacts/qcontactjsondb/qcontactjsondbbackup.h000066400000000000000000000052641233466112000244020ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTJSONDBBACKUP_H #define QCONTACTJSONDBBACKUP_H #include #include #include "synchronizedjsondbclient.h" QT_BEGIN_NAMESPACE_CONTACTS class QContactJsonDbBackup { public: QContactJsonDbBackup(); ~QContactJsonDbBackup(); bool loadTestData(); bool cleanJsonDb(); private: void backupJsonDb(); bool revertJsonDb(); bool addContacts(const QList &objects, QString partition = QString()); bool deleteContacts(const QList &objects, QString partition = QString()); int wasteSomeTime(); QList m_backupData, m_backupDataSystem, m_backupDataUser; SynchronizedJsonDbClient* m_dbClient; }; QT_END_NAMESPACE_CONTACTS #endif // QCONTACTJSONDBBACKUP_H tests/auto/contacts/qcontactjsondb/synchronizedjsondbclient.cpp000066400000000000000000000104401233466112000256410ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include "qcontactjsondbstring.h" #include "synchronizedjsondbclient.h" #include "qcontactjsondbstring.h" QTCONTACTS_USE_NAMESPACE SynchronizedJsonDbClient::SynchronizedJsonDbClient() { m_jsonDbConnection = new QJsonDbConnection(); m_worker = new SynchronizedWorker(); QObject::connect(m_jsonDbConnection, SIGNAL(error(QtJsonDb::QJsonDbConnection::ErrorCode,QString)), m_worker, SLOT(onJsonDbConnectionError(QtJsonDb::QJsonDbConnection::ErrorCode,QString))); m_jsonDbConnection->connectToServer(); } SynchronizedJsonDbClient::~SynchronizedJsonDbClient() { delete m_worker; delete m_jsonDbConnection; } QList SynchronizedJsonDbClient::query(const QString &query, QString partition) { QJsonDbRequest *request; request = new QJsonDbReadRequest(query, m_worker); request->setPartition(partition); return getResults(request); } QList SynchronizedJsonDbClient::create(const QJsonObject &objects, QString partition) { QJsonDbRequest *request; request = new QJsonDbCreateRequest(objects, m_worker); request->setPartition(partition); return getResults(request); } QList SynchronizedJsonDbClient::update(const QJsonObject &objects, QString partition) { QJsonDbRequest *request = new QJsonDbUpdateRequest(objects, m_worker); request->setPartition(partition); return getResults(request); } QList SynchronizedJsonDbClient::remove(const QJsonObject &objects, QString partition) { QJsonDbRequest *request = new QJsonDbRemoveRequest(objects,m_worker); request->setPartition(partition); return getResults(request); } QList SynchronizedJsonDbClient::getResults(QJsonDbRequest *request) { QObject::connect(request, SIGNAL(finished()), m_worker, SLOT(onJsonDbRequestFinished())); QObject::connect(request, SIGNAL(error(QtJsonDb::QJsonDbRequest::ErrorCode,QString)), m_worker, SLOT(onJsonDbRequestError(QtJsonDb::QJsonDbRequest::ErrorCode, QString))); if (m_jsonDbConnection->send(request)) { m_worker->exec(); // Wait for db client to finish QList results = request->takeResults(); return results; } else { return QList(); } } tests/auto/contacts/qcontactjsondb/synchronizedjsondbclient.h000066400000000000000000000060371233466112000253150ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef SYNCHRONIZEDJSONDBCLIENT_H #define SYNCHRONIZEDJSONDBCLIENT_H #include #include #include #include #include #include "synchronizedworker.h" #include "qcontactjsondbstring.h" QT_USE_NAMESPACE_JSONDB QT_BEGIN_NAMESPACE_CONTACTS class SynchronizedJsonDbClient { public: SynchronizedJsonDbClient(); ~SynchronizedJsonDbClient(); QList query(const QString &query, QString partition = QContactJsonDbStr::userDataPartition()); QList create(const QJsonObject &query, QString partition = QContactJsonDbStr::userDataPartition()); QList update(const QJsonObject &query, QString partition = QContactJsonDbStr::userDataPartition()); QList remove(const QJsonObject &query, QString partition = QContactJsonDbStr::userDataPartition()); private: QList getResults(QJsonDbRequest *request); QJsonDbConnection *m_jsonDbConnection; SynchronizedWorker* m_worker; }; QT_END_NAMESPACE_CONTACTS #endif // SYNCHRONIZEDJSONDBCLIENT_H tests/auto/contacts/qcontactjsondb/synchronizedworker.cpp000066400000000000000000000051471233466112000245040ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include "synchronizedworker.h" SynchronizedWorker::SynchronizedWorker(QObject *parent) : QEventLoop(parent) { } void SynchronizedWorker::onJsonDbRequestFinished() { QEventLoop::quit(); } void SynchronizedWorker::onJsonDbRequestError(QtJsonDb::QJsonDbRequest::ErrorCode requestError, QString message) { qWarning() << Q_FUNC_INFO << "Jsondb request error:" << requestError << "message:" << message; QEventLoop::quit(); } void SynchronizedWorker::onJsonDbConnectionError(QtJsonDb::QJsonDbConnection::ErrorCode connectionError, const QString &message) { qWarning() << Q_FUNC_INFO << "Jsondb connection error:" << connectionError << "message:" << message; } tests/auto/contacts/qcontactjsondb/synchronizedworker.h000066400000000000000000000050711233466112000241450ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef SYNCHRONIZEDWORKER_H #define SYNCHRONIZEDWORKER_H #include #include #include #include #include QT_USE_NAMESPACE_JSONDB class SynchronizedWorker : public QEventLoop { Q_OBJECT public: explicit SynchronizedWorker(QObject *parent = 0); public slots: void onJsonDbRequestFinished(); void onJsonDbRequestError(QtJsonDb::QJsonDbRequest::ErrorCode requestError, QString message); void onJsonDbConnectionError(QtJsonDb::QJsonDbConnection::ErrorCode connectionError, const QString &message); signals: private: }; #endif // SYNCHRONIZEDWORKER_H tests/auto/contacts/qcontactjsondb/tst_qcontactjsondbengine.cpp000066400000000000000000002726021233466112000256310ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include "qcontactjsondbengine.h" #include "qcontactjsondbenginefactory.h" #include "qcontactjsondbbackup.h" #include "qcontactjsondbglobal.h" #include "qcontactjsondbconverter.h" #include "qcontactjsondbid.h" #include "synchronizedjsondbclient.h" #include "jsondbprocess.h" QTCONTACTS_USE_NAMESPACE class tst_QContactJsondbEngine : public QObject { Q_OBJECT public: tst_QContactJsondbEngine(); ~tst_QContactJsondbEngine(); public slots: void initTestCase(); void init(); void cleanup(); void cleanupTestCase(); private Q_SLOTS: void testSelfContactId(); void testContactIds(); void testContacts(); void testContacts_data(); void testContact(); void testContact_data(); void testSaveContact(); void testSaveContacts(); void testRemoveContacts(); void testRemoveContact(); void testContactDetailFilter(); void testContactDetailFilter_data(); void testContactSortOrder(); void testContactSortOrder_data(); void testContactUpdate(); void testExtendedDetailsFromJsonDb(); void testExtendedDetailsToJsonDb(); void testSaveAndFetchToDefaultStorage(); void testSaveAndFetchToUserDataStorage(); void testSaveAndFetchToSystemStorage(); void testPartialSaveToDefaultStorage(); void testPartialSaveToUserdataStorage(); void testPartialSaveToSystemStorage(); void testSaveAndFetchToDefaultAndUserDataStorages(); void testSaveAndFetchToDefaulAndSystemStorages(); void testMultiFetchFromUserAndSystemStorages(); void testFetchByIdFromUserAndSystemStorages(); void testErrorsOnFetchFromWrongStorage(); void testFetchWithIdsFromTwoStoragesInIdFilter(); void testFetchErrorWithIdsFromWrongStorageInIdFilter(); void testRemoveFromDefaultStorage(); void testRemoveFromUserStorage(); void testRemoveFromSystemStorage(); void testRemoveFromUserAndSystemStorages(); void testUpdateToUserDataStorage(); void testUpdateToSystemDataStorage(); void testWrongUpdateToDefaultStorage(); void testWrongUpdateToSystemStorage(); void testIdFetchRequestFromUserAndSystemStorages(); void testSynchronousSaveAndFetchToDefaultStorage(); private: void createPartition(const QString partition); QContactId saveTestContact(QContactManager &cm, QString &firstName, QContactAbstractRequest::StorageLocation storageLocation = QContactAbstractRequest::StorageLocation(0)); QContactId partialSaveTestContact(QContactManager &cm, QString &firstName, QContactAbstractRequest::StorageLocation storageLocation = QContactAbstractRequest::StorageLocation(0)); QContactManager::Error updateTestContact(QContactManager &cm, QContact &contact, QContactId &contactId, QContactAbstractRequest::StorageLocation storageLocation = QContactAbstractRequest::StorageLocation(0)); QList fetchTestContacts(QContactManager &cm, QContactAbstractRequest::StorageLocations storageLocations = 0x0); QList fetchTestContactsById(QContactManager &cm, const QList &ids, QContactManager::Error &error); QContactManager::Error fetchTestContactByIdFilter(QContactManager &cm, QContactId &id, QContact &contact, QContactAbstractRequest::StorageLocations storageLocations = 0x0); QContactManager::Error fetchTestContactsByIdFilter(QContactManager &cm, QList &ids, QList &contacts, QContactAbstractRequest::StorageLocations storageLocations = 0x0); QContactManager::Error removeTestContact(QContactManager &cm, QContactId &contactId); QContactJsonDbEngine* m_engine; QContactJsonDbBackup* m_myBackup; SynchronizedJsonDbClient* m_dbClient; JsonDbProcess m_jsondbProcess; }; void tst_QContactJsondbEngine::initTestCase() { // Backup and clear jsondb. if (!m_myBackup) { m_myBackup = new QContactJsonDbBackup; m_myBackup->cleanJsonDb(); } } void tst_QContactJsondbEngine::init() { m_myBackup->cleanJsonDb(); QVERIFY2(m_myBackup->loadTestData(), "Failed to load test data"); // TODO: Proper fetching for test data. } void tst_QContactJsondbEngine::cleanup() { } void tst_QContactJsondbEngine::cleanupTestCase() { if (m_myBackup) { delete m_myBackup; m_myBackup = 0; } } tst_QContactJsondbEngine::tst_QContactJsondbEngine():m_myBackup(0) { m_engine = new QContactJsonDbEngine(); m_dbClient = new SynchronizedJsonDbClient(); QString partitions_json = QFINDTESTDATA("partitions.json"); QVERIFY2(!partitions_json.isEmpty(), "partitions.json file is missing"); QVERIFY2(m_jsondbProcess.start(partitions_json), "Failed to start JsonDb process"); } tst_QContactJsondbEngine::~tst_QContactJsondbEngine() { delete m_engine; delete m_dbClient; m_jsondbProcess.terminate(); } void tst_QContactJsondbEngine::createPartition(const QString partition) { // Check if partition already exist. QString partitionQuery = "[?_type=\"Partition\"][?name=\"" + partition + "\"]"; QList queryResponse = m_dbClient->query(partitionQuery); qDebug() << Q_FUNC_INFO << "partition check response:" << queryResponse; // If no such partition, create one. if (queryResponse.isEmpty()) { QJsonObject object; object.insert(QStringLiteral("_type"), QStringLiteral("Partition")); object.insert(QStringLiteral("name"), partition); QVariantMap partitionCreateResponse = m_dbClient->create(object).first().toVariantMap(); qWarning() << Q_FUNC_INFO << "Created partition:" << partition << "with reponse:" << partitionCreateResponse; return; } // Debug, remove when this works. qDebug() << Q_FUNC_INFO << "Partition query response for partition:" << partition << "is" << queryResponse; } QContactId tst_QContactJsondbEngine::saveTestContact(QContactManager &cm, QString &firstName, QContactAbstractRequest::StorageLocation storageLocation) { QContactName nameDetail; nameDetail.setFirstName(firstName); nameDetail.setLastName("StorageLocationTestContact"); QContact testContact; testContact.saveDetail(&nameDetail); QContactSaveRequest saveRequest; saveRequest.setManager(&cm); saveRequest.setContact(testContact); if (storageLocation) saveRequest.setStorageLocation(storageLocation); saveRequest.start(); saveRequest.waitForFinished(); return saveRequest.contacts().first().id(); } QContactManager::Error tst_QContactJsondbEngine::updateTestContact(QContactManager &cm, QContact &contact, QContactId &contactId, QContactAbstractRequest::StorageLocation storageLocation) { QContactSaveRequest saveRequest; saveRequest.setManager(&cm); saveRequest.setContact(contact); if (storageLocation) saveRequest.setStorageLocation(storageLocation); saveRequest.start(); saveRequest.waitForFinished(); contactId = saveRequest.contacts().first().id(); return saveRequest.error(); } QList tst_QContactJsondbEngine::fetchTestContacts(QContactManager &cm, QContactAbstractRequest::StorageLocations storageLocations) { QContactFetchRequest fetchRequest; fetchRequest.setManager(&cm); if (storageLocations) fetchRequest.setStorageLocations(storageLocations); fetchRequest.start(); fetchRequest.waitForFinished(); QList contacts = fetchRequest.contacts(); return contacts; } QContactManager::Error tst_QContactJsondbEngine::fetchTestContactsByIdFilter(QContactManager &cm, QList &ids, QList &contacts, QContactAbstractRequest::StorageLocations storageLocations) { QContactIdFilter idFilter; foreach (QContactId id, ids) idFilter.add(id); QContactFetchRequest fetchRequest; fetchRequest.setManager(&cm); fetchRequest.setFilter(idFilter); if (storageLocations) fetchRequest.setStorageLocations(storageLocations); fetchRequest.start(); fetchRequest.waitForFinished(); if (fetchRequest.contacts().isEmpty()) contacts = QList(); else contacts = fetchRequest.contacts(); return fetchRequest.error(); } QContactManager::Error tst_QContactJsondbEngine::fetchTestContactByIdFilter(QContactManager &cm, QContactId &id, QContact &contact, QContactAbstractRequest::StorageLocations storageLocations) { QContactIdFilter idFilter; idFilter.add(id); QContactFetchRequest fetchRequest; fetchRequest.setManager(&cm); fetchRequest.setFilter(idFilter); if (storageLocations) fetchRequest.setStorageLocations(storageLocations); fetchRequest.start(); fetchRequest.waitForFinished(); if (!fetchRequest.contacts().isEmpty()) contact = fetchRequest.contacts().first(); return fetchRequest.error(); } QList tst_QContactJsondbEngine::fetchTestContactsById(QContactManager &cm, const QList &ids, QContactManager::Error &error) { QContactFetchByIdRequest fetchByIdRequest; fetchByIdRequest.setManager(&cm); fetchByIdRequest.setIds(ids); fetchByIdRequest.start(); fetchByIdRequest.waitForFinished(); error = fetchByIdRequest.error(); QList contacts = fetchByIdRequest.contacts(); return contacts; } QContactManager::Error tst_QContactJsondbEngine::removeTestContact(QContactManager &cm, QContactId &contactId) { QContactRemoveRequest removeRequest; removeRequest.setManager(&cm); removeRequest.setContactId(contactId); removeRequest.start(); removeRequest.waitForFinished(); return removeRequest.error(); } void tst_QContactJsondbEngine::testSaveAndFetchToDefaultStorage() { // Get original count of contacts in the defaul storage. QContactManager cm; int originalCount = cm.contactIds().size(); // Test save and fetch to default storage. QString name("SaveToDefault"); saveTestContact(cm, name); QList contacts = fetchTestContacts(cm); QCOMPARE(contacts.size(), originalCount+1); bool contactFound = false; foreach (QContact curr, contacts) { if (curr.detail().firstName() == name) contactFound=true; } QVERIFY(contactFound == true); // Check explicitely that fetch from userData storage gives the same. contacts = fetchTestContacts(cm, QContactAbstractRequest::UserDataStorage); QCOMPARE(contacts.size(), originalCount+1); contactFound = false; foreach (QContact curr, contacts) { if (curr.detail().firstName() == name) contactFound=true; } QVERIFY(contactFound == true); // Verify the system storage is not affected (it should be cleaned by test init). contacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); QCOMPARE(contacts.size(), 0); } void tst_QContactJsondbEngine::testSaveAndFetchToUserDataStorage() { // Get original count of contacts in the defaul storage. QContactManager cm; int originalCount = cm.contactIds().size(); // Test save and fetch to userdata storage. QString name("SaveToUser"); saveTestContact(cm, name, QContactAbstractRequest::UserDataStorage); QList contacts = fetchTestContacts(cm, QContactAbstractRequest::UserDataStorage); QCOMPARE(contacts.size(), originalCount+1); bool contactFound = false; foreach (QContact curr, contacts) { if (curr.detail().firstName() == name) contactFound=true; } QVERIFY(contactFound == true); // Verify the fetch from default gives the same. contacts = fetchTestContacts(cm); QCOMPARE(contacts.size(), originalCount+1); contactFound = false; foreach (QContact curr, contacts) { if (curr.detail().firstName() == name) contactFound=true; } QVERIFY(contactFound == true); // Verify the system storage is not affected (it should be cleaned by test init). contacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); QCOMPARE(contacts.size(), 0); } void tst_QContactJsondbEngine::testSaveAndFetchToSystemStorage() { // Get original count of contacts in the defaul storage. QContactManager cm; int originalCount = cm.contactIds().size(); // Test save and fetch to system storage. QString saveName("SaveToSystem"); QContactId originalId = saveTestContact(cm, saveName, QContactAbstractRequest::SystemStorage); QList contacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); QCOMPARE(contacts.size(), 1); QCOMPARE(contacts.first().id(), originalId); QCOMPARE(contacts.first().detail().firstName(), saveName); // Verify userData storage is not affected. contacts = fetchTestContacts(cm, QContactAbstractRequest::UserDataStorage); QCOMPARE(contacts.size(), originalCount); bool contactFound = false; foreach (QContact curr, contacts) { if (curr.detail().firstName() == saveName) contactFound = true; } QVERIFY(!contactFound); // Verify default storage is not affected. contacts = fetchTestContacts(cm); QCOMPARE(contacts.size(), originalCount); contactFound = false; foreach (QContact curr, contacts) { if (curr.detail().firstName() == saveName) contactFound = true; } QVERIFY(!contactFound); } QContactId tst_QContactJsondbEngine::partialSaveTestContact(QContactManager &cm, QString &firstName, QContactAbstractRequest::StorageLocation storageLocation) { QContact testContact; QContactName nameDetail; nameDetail.setFirstName(firstName); nameDetail.setLastName("StorageLocationTestContact"); testContact.saveDetail(&nameDetail); QContactEmailAddress emailDetail; emailDetail.setEmailAddress("partial.save@email.address"); testContact.saveDetail(&emailDetail); QContactPhoneNumber phoneDetail; phoneDetail.setNumber("4711-4711"); testContact.saveDetail(&phoneDetail); QContactSaveRequest saveRequest; saveRequest.setManager(&cm); saveRequest.setContact(testContact); QList mask; mask.append(QContactDetail::TypeName); saveRequest.setTypeMask(mask); if (storageLocation) saveRequest.setStorageLocation(storageLocation); saveRequest.start(); saveRequest.waitForFinished(); return saveRequest.contacts().first().id(); } void tst_QContactJsondbEngine::testPartialSaveToDefaultStorage() { // Get original count of contacts in the defaul storage. QContactManager cm; int originalCount = cm.contactIds().size(); // Test partial save and fetch to userdata storage. QString saveName("PartialSaveToUser"); QContactId originalId = partialSaveTestContact(cm, saveName); QList contacts = fetchTestContacts(cm); // Verify default storage got the contact. contacts = fetchTestContacts(cm); QCOMPARE(contacts.size(), originalCount +1); bool contactFound = false; foreach (QContact curr, contacts) { if (curr.detail().firstName() == saveName) { QCOMPARE(curr.id(), originalId); contactFound = true; } } QVERIFY(contactFound); // Verify system storage is not affected. contacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); QCOMPARE(contacts.size(), 0); } void tst_QContactJsondbEngine::testPartialSaveToUserdataStorage() { // Get original count of contacts in the defaul storage. QContactManager cm; int originalCount = cm.contactIds().size(); // Test partial save and fetch to userdata storage. QString saveName("PartialSaveToUser"); QContactId originalId = partialSaveTestContact(cm, saveName, QContactAbstractRequest::UserDataStorage); QList contacts = fetchTestContacts(cm, QContactAbstractRequest::UserDataStorage); // Verify userdata storage got the contact. contacts = fetchTestContacts(cm); QCOMPARE(contacts.size(), originalCount +1); bool contactFound = false; foreach (QContact curr, contacts) { if (curr.detail().firstName() == saveName) { QCOMPARE(curr.id(), originalId); contactFound = true; } } QVERIFY(contactFound); // Verify system storage is not affected. contacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); QCOMPARE(contacts.size(), 0); } void tst_QContactJsondbEngine::testPartialSaveToSystemStorage() { // Get original count of contacts in the defaul storage. QContactManager cm; int originalCount = cm.contactIds().size(); // Test partial save and fetch to system storage. QString saveName("PartialSaveToSystem"); QContactId originalId = partialSaveTestContact(cm, saveName, QContactAbstractRequest::SystemStorage); QList contacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); QCOMPARE(contacts.size(), 1); QCOMPARE(contacts.first().id(), originalId); QCOMPARE(contacts.first().detail().firstName(), saveName); // Verify userData storage is not affected. contacts = fetchTestContacts(cm, QContactAbstractRequest::UserDataStorage); QCOMPARE(contacts.size(), originalCount); bool contactFound = false; foreach (QContact curr, contacts) { if (curr.detail().firstName() == saveName) contactFound = true; } QVERIFY(!contactFound); // Verify default storage is not affected. contacts = fetchTestContacts(cm); QCOMPARE(contacts.size(), originalCount); contactFound = false; foreach (QContact curr, contacts) { if (curr.detail().firstName() == saveName) contactFound = true; } QVERIFY(!contactFound); } void tst_QContactJsondbEngine::testSaveAndFetchToDefaultAndUserDataStorages() { // Get original count of contacts in the defaul storage. QContactManager cm; int originalCount = cm.contactIds().size(); // Save test contact to default and userData storages. QString nameForDefaultStorageContact("SaveToDefault"); QContactId contactIdDefaultStorage = saveTestContact(cm, nameForDefaultStorageContact); QString nameForUserStorageContact("SaveToUser"); QContactId contactIdUserdataStorage = saveTestContact(cm, nameForUserStorageContact, QContactAbstractRequest::UserDataStorage); // Check that fetch from default storage gives them both back to us. QList contacts = fetchTestContacts(cm); QCOMPARE(contacts.size(), originalCount+2); int contactsWithNameInDefaultStorage = 0; int contactsWithNameInUserStorage = 0; foreach (QContact curr, contacts) { if (curr.detail().firstName() == nameForDefaultStorageContact) { QCOMPARE(curr.id(), contactIdDefaultStorage); contactsWithNameInDefaultStorage++; } if (curr.detail().firstName() == nameForUserStorageContact) { QCOMPARE(curr.id(), contactIdUserdataStorage); contactsWithNameInUserStorage++; } } QCOMPARE(contactsWithNameInDefaultStorage, 1); QCOMPARE(contactsWithNameInUserStorage, 1); // Verify the fetch from the userData gives the same. contacts = fetchTestContacts(cm, QContactAbstractRequest::UserDataStorage); QCOMPARE(contacts.size(), originalCount+2); contactsWithNameInDefaultStorage = 0; contactsWithNameInUserStorage = 0; foreach (QContact curr, contacts) { if (curr.detail().firstName() == nameForDefaultStorageContact) { QCOMPARE(curr.id(), contactIdDefaultStorage); contactsWithNameInDefaultStorage++; } if (curr.detail().firstName() == nameForUserStorageContact) { QCOMPARE(curr.id(), contactIdUserdataStorage); contactsWithNameInUserStorage++; } } QCOMPARE(contactsWithNameInDefaultStorage, 1); QCOMPARE(contactsWithNameInUserStorage, 1); // Verify the system storage is not affected (it should be cleaned by test init). contacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); QCOMPARE(contacts.size(), 0); } void tst_QContactJsondbEngine::testSaveAndFetchToDefaulAndSystemStorages() { // Get original count of contacts in the defaul storage. QContactManager cm; int originalCount = cm.contactIds().size(); // Save test contact to default and system storages. QString nameForDefaultStorageContact("SaveToDefault"); QContactId contactIdForDefaultStorageContact = saveTestContact(cm, nameForDefaultStorageContact); QString nameForSystemStorageContact("SaveToSystem"); QContactId contactIdForSystemStorageContact = saveTestContact(cm, nameForSystemStorageContact, QContactAbstractRequest::SystemStorage); // Check that fetch from default storage gives the right contact back to us. QList contacts = fetchTestContacts(cm); QCOMPARE(contacts.size(), originalCount+1); int contactsFound = 0; foreach (QContact current, contacts) { if (current.detail().firstName() == nameForDefaultStorageContact) { QCOMPARE(current.id(), contactIdForDefaultStorageContact); contactsFound++; } } QCOMPARE(contactsFound, 1); // Check that fetch from system storage gives the right contact back to us. contacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); QCOMPARE(contacts.size(), 1); QCOMPARE(contacts.first().id(), contactIdForSystemStorageContact); QCOMPARE(contacts.first().detail().firstName(), nameForSystemStorageContact); } void tst_QContactJsondbEngine::testMultiFetchFromUserAndSystemStorages() { // Get original count of contacts in the defaul storage. QContactManager cm; int originalCount = cm.contactIds().size(); // Save test contact to default and system storages. QString nameForDefaultStorageContact("FetchFromDefault"); QContactId idForDefaultStorageContact = saveTestContact(cm, nameForDefaultStorageContact); QString nameForSystemStorageContact("FetchFromSystem"); QContactId idForSystemStorageContact = saveTestContact(cm, nameForSystemStorageContact, QContactAbstractRequest::SystemStorage); // Check that fetch from both storages at the same time gives the right contacts back to us. QList contacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage | QContactAbstractRequest::UserDataStorage); QCOMPARE(contacts.size(), originalCount+2); int contactsDefaultFound = 0; int contactsSystemFound = 0; foreach (QContact curr, contacts) { if (curr.detail().firstName() == nameForDefaultStorageContact) { contactsDefaultFound++; QCOMPARE(curr.id(), idForDefaultStorageContact); } if (curr.detail().firstName() == nameForSystemStorageContact) { contactsSystemFound++; QCOMPARE(curr.id(), idForSystemStorageContact); } } QCOMPARE(contactsDefaultFound, 1); QCOMPARE(contactsSystemFound, 1); } void tst_QContactJsondbEngine::testFetchByIdFromUserAndSystemStorages() { // Save test contact to user and system storages. QContactManager cm; QString nameForUserdataStorageContact("FetchByIdFromDefault"); QContactId idForUserdataStorageContact = saveTestContact(cm, nameForUserdataStorageContact); QString nameForSystemStorageContact("FetchbyIdFromSystem"); QContactId idForSystemStorageContact = saveTestContact(cm, nameForSystemStorageContact, QContactAbstractRequest::SystemStorage); QList ids; ids << idForUserdataStorageContact << idForSystemStorageContact; // Check that fetch from both storages at the same time gives the right contacts back to us. QContactManager::Error error = QContactManager::NoError; QList contacts = fetchTestContactsById(cm, ids, error); QCOMPARE(error, QContactManager::NoError); QCOMPARE(contacts.size(), 2); int contactsUserdataFound = 0; int contactsSystemFound = 0; foreach (QContact curr, contacts) { if (curr.detail().firstName() == nameForUserdataStorageContact) { contactsUserdataFound++; QCOMPARE(curr.id(), idForUserdataStorageContact); } if (curr.detail().firstName() == nameForSystemStorageContact) { contactsSystemFound++; QCOMPARE(curr.id(), idForSystemStorageContact); } } QCOMPARE(contactsUserdataFound, 1); QCOMPARE(contactsSystemFound, 1); } void tst_QContactJsondbEngine::testErrorsOnFetchFromWrongStorage() { // Save a test contact to system storage and fetch by Id from default and user storages. QContactManager cm; QString name("FetchError"); QContactId originalId = saveTestContact(cm, name, QContactAbstractRequest::SystemStorage); QContact contact; QCOMPARE(fetchTestContactByIdFilter(cm, originalId, contact), QContactManager::DoesNotExistError); QCOMPARE(fetchTestContactByIdFilter(cm, originalId, contact, QContactAbstractRequest::UserDataStorage), QContactManager::DoesNotExistError); // Save a test contact to default storage and fetch from system storage. originalId = saveTestContact(cm, name); QCOMPARE(fetchTestContactByIdFilter(cm, originalId, contact, QContactAbstractRequest::SystemStorage), QContactManager::DoesNotExistError); // Save a test contact to user storage and fetch from system storage. originalId = saveTestContact(cm, name, QContactAbstractRequest::UserDataStorage); QCOMPARE(fetchTestContactByIdFilter(cm, originalId, contact, QContactAbstractRequest::SystemStorage), QContactManager::DoesNotExistError); } void tst_QContactJsondbEngine::testFetchWithIdsFromTwoStoragesInIdFilter() { // Save two test contacts to both user and system storages and fetch by Id one from both of them. QContactManager cm; QString name("FetchErrorMultipleIds"); QContactId contactIdDefault = saveTestContact(cm, name); contactIdDefault = saveTestContact(cm, name); QContactId contactIdSystem = saveTestContact(cm, name, QContactAbstractRequest::SystemStorage); contactIdSystem = saveTestContact(cm, name, QContactAbstractRequest::SystemStorage); QList ids; ids << contactIdDefault << contactIdSystem; QList contacts; // Check we get only the right contact from default storage (user storage) when no storage defined. QCOMPARE(fetchTestContactsByIdFilter(cm, ids, contacts), QContactManager::NoError); QCOMPARE(contacts.size(), 1); QCOMPARE(contacts.first().id(), contactIdDefault); QCOMPARE(contacts.first().detail().firstName(), name); // Check we get only the right contact from system storage when explicitely defining the storage. QCOMPARE(fetchTestContactsByIdFilter(cm, ids, contacts, QContactAbstractRequest::SystemStorage), QContactManager::NoError); QCOMPARE(contacts.size(), 1); QCOMPARE(contacts.first().id(), contactIdSystem); QCOMPARE(contacts.first().detail().firstName(), name); } void tst_QContactJsondbEngine::testFetchErrorWithIdsFromWrongStorageInIdFilter() { // Save two test contacts to both user and system storages. QContactManager cm; QString name("FetchErrorMultipleIds"); QContactId contactIdDefault = saveTestContact(cm, name); contactIdDefault = saveTestContact(cm, name); QContactId contactIdSystem = saveTestContact(cm, name, QContactAbstractRequest::SystemStorage); contactIdSystem = saveTestContact(cm, name, QContactAbstractRequest::SystemStorage); // Check fetch error when none of the ids in filter match with the default storage location. QList idsNotInDefaultStorage; idsNotInDefaultStorage << contactIdSystem << contactIdSystem; QList contacts; QCOMPARE(fetchTestContactsByIdFilter(cm, idsNotInDefaultStorage, contacts), QContactManager::DoesNotExistError); QCOMPARE(contacts.size(), 0); // Check fetch error when none of the ids in filter match with the system storage location. QList idsNotInSystemStorage; idsNotInSystemStorage << contactIdDefault << contactIdDefault; QCOMPARE(fetchTestContactsByIdFilter(cm, idsNotInSystemStorage, contacts, QContactAbstractRequest::SystemStorage), QContactManager::DoesNotExistError); QCOMPARE(contacts.size(), 0); } void tst_QContactJsondbEngine::testRemoveFromDefaultStorage() { // Get original count of contacts in the defaul and system storages. QContactManager cm; int originalCount = cm.contactIds().size(); QList contacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); int originalCountInSystemStorage = contacts.size(); // Save and remove test contact to default storage and check it is not there. QString name("RemoveFromDefault"); QContactId id = saveTestContact(cm, name); QCOMPARE(removeTestContact(cm, id), QContactManager::NoError); // Check the contact is removed from the default storage. contacts = fetchTestContacts(cm); QCOMPARE(contacts.size(), originalCount); foreach (QContact curr, contacts) { QVERIFY(curr.detail().firstName() != name); QVERIFY(curr.id() != id); } // Check remove did not affect to amount of contacts in the system storage. contacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); QCOMPARE(contacts.size(), originalCountInSystemStorage); // Check re-remove fails with proper error code. QCOMPARE(removeTestContact(cm, id), QContactManager::DoesNotExistError); } void tst_QContactJsondbEngine::testRemoveFromUserStorage() { // Get original count of contacts in the defaul and system storages. QContactManager cm; int originalCount = cm.contactIds().size(); QList contacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); int originalCountInSystemStorage = contacts.size(); // Save a test contact to user data storage and then remove it. QString name("RemoveFromUser"); QContactId id = saveTestContact(cm, name, QContactAbstractRequest::UserDataStorage); QCOMPARE(removeTestContact(cm, id), QContactManager::NoError); // Check the contact is removed from the user data storage. contacts = fetchTestContacts(cm, QContactAbstractRequest::UserDataStorage); QCOMPARE(contacts.size(), originalCount); foreach (QContact curr, contacts) { QVERIFY(curr.detail().firstName() != name); QVERIFY(curr.id() != id); } // Check remove did not affect to amount of contacts in the system storage. contacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); QCOMPARE(contacts.size(), originalCountInSystemStorage); // Check re-remove fails with proper error code. QCOMPARE(removeTestContact(cm, id), QContactManager::DoesNotExistError); } void tst_QContactJsondbEngine::testRemoveFromSystemStorage() { // Get original count of contacts in the defaul and system storages. QContactManager cm; int originalCount = cm.contactIds().size(); // Save test contact to system storage. QString name("RemoveFromSystem"); QContactId id = saveTestContact(cm, name, QContactAbstractRequest::SystemStorage); // Check that remove from system storage succeeds. QCOMPARE(removeTestContact(cm, id), QContactManager::NoError); QList contacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); QCOMPARE(contacts.size(), 0); // Check remove did not affect to amount of contacts in the user data storage. contacts = fetchTestContacts(cm, QContactAbstractRequest::UserDataStorage); QCOMPARE(contacts.size(), originalCount); // Check re-remove fails with proper error code. QCOMPARE(removeTestContact(cm, id), QContactManager::DoesNotExistError); } void tst_QContactJsondbEngine::testRemoveFromUserAndSystemStorages() { // Get original count of contacts in the defaul and system storages. QContactManager cm; int originalCount = cm.contactIds().size(); // Save test contact to default and system storages. QString nameForDefaultStorageContact("RemoveFromDefault"); QContactId idForDefaultStorageContact = saveTestContact(cm, nameForDefaultStorageContact); QString nameForSystemStorageContact("RemoveFromSystem"); QContactId idForSystemStorageContact = saveTestContact(cm, nameForSystemStorageContact, QContactAbstractRequest::SystemStorage); // Check that remove from system storage succeeds. QCOMPARE(removeTestContact(cm, idForSystemStorageContact), QContactManager::NoError); QList contacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); QCOMPARE(contacts.size(), 0); // Check that fetch from default storage still has the right number of contacts and a test contact. contacts = fetchTestContacts(cm); QCOMPARE(contacts.size(), originalCount+1); int contactsFound = 0; foreach (QContact curr, contacts) { if (curr.detail().firstName() == nameForDefaultStorageContact) contactsFound++; } QCOMPARE(contactsFound, 1); // Check remove from user data storage succeeds. QCOMPARE(removeTestContact(cm, idForDefaultStorageContact), QContactManager::NoError); contacts = fetchTestContacts(cm); QCOMPARE(contacts.size(), originalCount); contactsFound = 0; foreach (QContact curr, contacts) { if (curr.detail().firstName() == nameForDefaultStorageContact) contactsFound++; } QCOMPARE(contactsFound, 0); // Check re-remove for both contacts fails with proper error codes. QCOMPARE(removeTestContact(cm, idForSystemStorageContact), QContactManager::DoesNotExistError); QCOMPARE(removeTestContact(cm, idForDefaultStorageContact), QContactManager::DoesNotExistError); } void tst_QContactJsondbEngine::testUpdateToUserDataStorage() { // Get original count of contacts in the defaul and system storages. QContactManager cm; int originalCount = cm.contactIds().size(); // Save a test contact to user data storage and fetch it back. QString name("UpdateToUser"); QContactId originalId = saveTestContact(cm, name, QContactAbstractRequest::UserDataStorage); QContact contact; QCOMPARE(fetchTestContactByIdFilter(cm, originalId, contact), QContactManager::NoError); QCOMPARE(contact.id(), originalId); QCOMPARE(contact.detail().firstName(), name); // Update contact with new lastname specific for this test case. QString updatedLastName("UpdatedToUserData"); QContactName nameDetail = contact.detail(QContactDetail::TypeName); nameDetail.setLastName(updatedLastName); contact.saveDetail(&nameDetail); QContactId idFromUpdate; QVERIFY(updateTestContact(cm, contact, idFromUpdate) == QContactManager::NoError); QCOMPARE (idFromUpdate, originalId); // Fetch with id filter and verify contact data. QContact fetchedContact; QCOMPARE(fetchTestContactByIdFilter(cm, originalId, fetchedContact), QContactManager::NoError); QCOMPARE(fetchedContact.id(), originalId); QCOMPARE(fetchedContact.detail().firstName(), name); QCOMPARE(fetchedContact.detail().lastName(), updatedLastName); // Verify the default storage contacts amount. QList defaultStorageContacts = fetchTestContacts(cm); QCOMPARE(defaultStorageContacts.size(), originalCount + 1); // Verify the user storage contacts amount. QList userdataContacts = fetchTestContacts(cm, QContactAbstractRequest::UserDataStorage); QCOMPARE(userdataContacts.size(), originalCount +1); // Verify the system storage is not affected (it should be clean by test init). QList systemStorageContacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); QCOMPARE(systemStorageContacts.size(), 0); } void tst_QContactJsondbEngine::testUpdateToSystemDataStorage() { // Get original count of contacts in the defaul and system storages. QContactManager cm; int originalCount = cm.contactIds().size(); // Save and fetch test contact to user data storage. QString name("UpdateToSystem"); QContactId originalId = saveTestContact(cm, name, QContactAbstractRequest::SystemStorage); QContact contact; QCOMPARE(fetchTestContactByIdFilter(cm, originalId, contact, QContactAbstractRequest::SystemStorage), QContactManager::NoError); QCOMPARE(contact.id(), originalId); QCOMPARE(contact.detail().firstName(), name); // Update contact with new lastname specific for this test case. QString updatedLastName("UpdatedToSystemRestricted"); QContactName nameDetail = contact.detail(QContactDetail::TypeName); nameDetail.setLastName(updatedLastName); contact.saveDetail(&nameDetail); QContactId idFromUpdate; QVERIFY(updateTestContact(cm, contact, idFromUpdate) == QContactManager::NoError); QCOMPARE (idFromUpdate, originalId); // Fetch with id filter and verify contact data. QContact fetchedContact; QCOMPARE(fetchTestContactByIdFilter(cm, originalId, fetchedContact, QContactAbstractRequest::SystemStorage), QContactManager::NoError); QCOMPARE(fetchedContact.id(), originalId); QCOMPARE(fetchedContact.detail().firstName(), name); QCOMPARE(fetchedContact.detail().lastName(), updatedLastName); // Verify the cointact is in the system storage. QList systemStorageContacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); QCOMPARE(systemStorageContacts.size(), 1); // Verify the default storage contacts amount. QList defaultStorageContacts = fetchTestContacts(cm); QCOMPARE(defaultStorageContacts.size(), originalCount); // Verify the user storage contacts amount. QList userdataContacts = fetchTestContacts(cm, QContactAbstractRequest::UserDataStorage); QCOMPARE(userdataContacts.size(), originalCount); } void tst_QContactJsondbEngine::testWrongUpdateToDefaultStorage() { // Get original count of contacts in the defaul and system storages. QContactManager cm; int originalCount = cm.contactIds().size(); // Save and fetch test contact to system storage. QString name("WrongUpdateToDefault"); QContactId originalId = saveTestContact(cm, name, QContactAbstractRequest::SystemStorage); QContact contact; QCOMPARE(fetchTestContactByIdFilter(cm, originalId, contact, QContactAbstractRequest::SystemStorage), QContactManager::NoError); QCOMPARE(contact.id(), originalId); QCOMPARE(contact.detail().firstName(), name); // Try to update contact to "wrong" storage location. // By documentation it should go to it's home storage instead of defined one. QString updatedLastName("UpdateToWrongStorageGoestoHomeStorage"); QContactName nameDetail = contact.detail(QContactDetail::TypeName); nameDetail.setLastName(updatedLastName); contact.saveDetail(&nameDetail); QContactId idFromUpdate; QCOMPARE(updateTestContact(cm, contact, idFromUpdate), QContactManager::NoError); QCOMPARE (idFromUpdate, originalId); // Fetch with id filter from default to verify proper error code. QContact fetchedContact; QCOMPARE(fetchTestContactByIdFilter(cm, originalId, fetchedContact), QContactManager::DoesNotExistError); // Verify the default storage contacts amount did not change. QList defaultStorageContacts = fetchTestContacts(cm); QCOMPARE(defaultStorageContacts.size(), originalCount); // Verify the user storage contacts amount did not change. QList userdataContacts = fetchTestContacts(cm, QContactAbstractRequest::UserDataStorage); QCOMPARE(userdataContacts.size(), originalCount); // Verify the system storage, there should be only the updated test contact. QList systemStorageContacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); QCOMPARE(systemStorageContacts.size(), 1); QCOMPARE(systemStorageContacts.first().detail().firstName(), name); QCOMPARE(systemStorageContacts.first().detail().lastName(), updatedLastName); } void tst_QContactJsondbEngine::testWrongUpdateToSystemStorage() { // Get original count of contacts in the defaul and system storages. QContactManager cm; int originalCount = cm.contactIds().size(); // Save and fetch test contact to default storage. QString name("WrongUpdateToSystem"); QContactId originalId = saveTestContact(cm, name); QContact contact; QCOMPARE(fetchTestContactByIdFilter(cm, originalId, contact), QContactManager::NoError); QCOMPARE(contact.id(), originalId); QCOMPARE(contact.detail().firstName(), name); // Try to update contact to "wrong" storage location. // By documentation it should go to 'home' storage instead. QString updatedLastName("UpdateToWrongStorageGoesToHomeStorage"); QContactName nameDetail = contact.detail(QContactDetail::TypeName); nameDetail.setLastName(updatedLastName); contact.saveDetail(&nameDetail); QContactId idFromUpdate; QCOMPARE(updateTestContact(cm, contact, idFromUpdate, QContactAbstractRequest::SystemStorage), QContactManager::NoError); QCOMPARE (idFromUpdate, originalId); // Fetch with id filter frp, system storage to verify the correct error code. QContact fetchedContact; QCOMPARE(fetchTestContactByIdFilter(cm, originalId, fetchedContact, QContactAbstractRequest::SystemStorage), QContactManager::DoesNotExistError); // Verify the default storage contacts amount and updated test contact there. QList defaultStorageContacts = fetchTestContacts(cm); QCOMPARE(defaultStorageContacts.size(), originalCount + 1); bool contactFound = false; foreach (QContact curr, defaultStorageContacts) { if (curr.detail().firstName() == name) { contactFound = true; QCOMPARE(curr.detail().lastName(), updatedLastName); } } QVERIFY(contactFound == true); // Verify the user storage contacts amount. QList userdataContacts = fetchTestContacts(cm, QContactAbstractRequest::UserDataStorage); QCOMPARE(userdataContacts.size(), originalCount +1); // Verify the system storage is not affected (it should be clean by test init). QList systemStorageContacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); QCOMPARE(systemStorageContacts.size(), 0); } void tst_QContactJsondbEngine::testIdFetchRequestFromUserAndSystemStorages() { // Get original count of contacts in the defaul and system storages. QContactManager cm; int originalCount = cm.contactIds().size(); QList contacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); int originalCountInSystemStorage = contacts.size(); // Save a test contact to both user and system storage. QString name("FetchByIdTest"); QContactId contactIdDefault = saveTestContact(cm, name); QContactId contactIdSystem = saveTestContact(cm, name, QContactAbstractRequest::SystemStorage); // Fetch ids using id fetch request. QContactIdFetchRequest idFetchRequest; idFetchRequest.setManager(&cm); idFetchRequest.setStorageLocations(QContactAbstractRequest::UserDataStorage | QContactAbstractRequest::SystemStorage); idFetchRequest.start(); idFetchRequest.waitForFinished(); QCOMPARE(idFetchRequest.error(), QContactManager::NoError); // Check we get the right ids. QCOMPARE(idFetchRequest.ids().size(), originalCount + originalCountInSystemStorage + 2); bool contactIdDefaultFound = false; bool contactIdSystemFound = false; foreach (QContactId id, idFetchRequest.ids()) { if (id == contactIdDefault) contactIdDefaultFound = true; if (id == contactIdSystem) contactIdSystemFound = true; } QVERIFY(contactIdDefaultFound); QVERIFY(contactIdSystemFound); } void tst_QContactJsondbEngine::testSynchronousSaveAndFetchToDefaultStorage() { // Get original count of contacts in the defaul and system storages. QContactManager cm; int originalCount = cm.contactIds().size(); // Save two test contacts to default partition. QContact testContact; QContactName nameDetail; nameDetail.setFirstName("Test Contact 1"); testContact.saveDetail(&nameDetail); QList saveList; saveList << testContact; QContact testContact2; QContactName nameDetail2; nameDetail2.setFirstName("Test Contact 2"); testContact2.saveDetail(&nameDetail2); saveList << testContact2; bool returnValue = cm.saveContacts(&saveList); QVERIFY(returnValue == true); QCOMPARE (cm.contactIds().size(), originalCount+2); // Fetch and check contacts from the default partition. QList ids = cm.contactIds(); QList contacts = cm.contacts(ids); QCOMPARE(contacts.size(), originalCount+2); int contactsWithTest1 = 0; int contactsWithTest2 = 0; foreach (QContact curr, contacts) { if (curr.detail() == nameDetail) { contactsWithTest1++; QCOMPARE(curr.id(), saveList.at(0).id()); } if (curr.detail() == nameDetail2) { contactsWithTest2++; QCOMPARE(curr.id(), saveList.at(1).id()); } } QCOMPARE(contactsWithTest1, 1); QCOMPARE(contactsWithTest2, 1); // Fetch contacts from userData storage explicitely and check they are // there as the userData storage should be the default storage. contacts = fetchTestContacts(cm, QContactAbstractRequest::UserDataStorage); QCOMPARE(contacts.size(), originalCount+2); contactsWithTest1 = 0; contactsWithTest2 = 0; foreach (QContact curr, contacts) { if (curr.detail() == nameDetail) { contactsWithTest1++; QCOMPARE(curr.id(), saveList.at(0).id()); } if (curr.detail() == nameDetail2) { contactsWithTest2++; QCOMPARE(curr.id(), saveList.at(1).id()); } } QCOMPARE(contactsWithTest1, 1); QCOMPARE(contactsWithTest2, 1); // Fetch contacts from the system storage to verify new contact is not available // there. The storage should be clean by test init. contacts = fetchTestContacts(cm, QContactAbstractRequest::SystemStorage); QCOMPARE(contacts.size(), 0); } void tst_QContactJsondbEngine::testSelfContactId() { qDebug() << "NOTE: THE IDENTIFICATION FIELD DOES NOT EXIST YET IN JSON SCHEMA, just returns \"not found\""; QContactManager myContactManager; QContactId myId; myId = myContactManager.selfContactId(); qDebug() << "MyId is:" << myId; } void tst_QContactJsondbEngine::testContactIds() { QContactManager myContactManager; QContactFilter filter; QList sortOrders; QList resultContactIds = myContactManager.contactIds(filter, sortOrders); QList contacts = myContactManager.contacts(filter, sortOrders); // used as reference. Not possible to use a table here because the IDs change every time the test is triggered... QList referenceContactIds; foreach (QContact contact, contacts) { referenceContactIds.append(contact.id()); } QCOMPARE(resultContactIds.size(), referenceContactIds.size()); for (int i = 0; i < resultContactIds.size(); i++) { QVERIFY(resultContactIds.at(i) == referenceContactIds.at(i)); } } void tst_QContactJsondbEngine::testContacts() { QContactManager cm; // "all contacts" retrieval QContactFilter filter; QList sortOrders; QList contacts = cm.contacts(filter, sortOrders); QCOMPARE(contacts.size(), 5); QFETCH(QString, expectedContact1FirstName); QFETCH(QString, expectedContact1LastName); QFETCH(QString, expectedContact2FirstName); QFETCH(QString, expectedContact2LastName); QFETCH(QString, expectedContact3FirstName); QFETCH(QString, expectedContact3LastName); QFETCH(QString, expectedContact4FirstName); QFETCH(QString, expectedContact4LastName); QFETCH(QString, expectedContact5FirstName); QFETCH(QString, expectedContact5LastName); QList expectedContacts; expectedContacts << expectedContact1FirstName << expectedContact1LastName << expectedContact2FirstName << expectedContact2LastName << expectedContact3FirstName << expectedContact3LastName << expectedContact4FirstName << expectedContact4LastName << expectedContact5FirstName << expectedContact5LastName; QVERIFY(expectedContacts.size() == 10); for (int i = 0; i < contacts.size(); i++) { bool contactFound = false; QString firstNameString = contacts.at(i).detail().firstName(); QString lastNameString = contacts.at(i).detail().lastName(); for (int j = 0; j < expectedContacts.size(); j++) { if ( (firstNameString == expectedContacts.at(j)) && (lastNameString == expectedContacts.at(j+1)) ) contactFound=true; } QCOMPARE(contactFound, true); } } void tst_QContactJsondbEngine::testContacts_data() { QTest::addColumn("expectedContact1FirstName"); QTest::addColumn("expectedContact1LastName"); QTest::addColumn("expectedContact2FirstName"); QTest::addColumn("expectedContact2LastName"); QTest::addColumn("expectedContact3FirstName"); QTest::addColumn("expectedContact3LastName"); QTest::addColumn("expectedContact4FirstName"); QTest::addColumn("expectedContact4LastName"); QTest::addColumn("expectedContact5FirstName"); QTest::addColumn("expectedContact5LastName"); { QTest::newRow("expectedContactsNames") << "Harry" << "Baker" << "Paul" << "Thomson" << "Julie" << "King" << "Angelina" << "Row" << "Natalie" << "Watson"; } } void tst_QContactJsondbEngine::testContact() { QContactManager cm; // contact retrieval QContactFilter filter; QList sortOrders; QList resultContactIds = cm.contactIds(filter, sortOrders); QVERIFY(resultContactIds.size() > 0); QContactId id = resultContactIds.at(1); // Just a random ID picked QContact contact = cm.contact(id); QVERIFY(id == contact.id()); QFETCH(QString, expectedContact1FirstName); QFETCH(QString, expectedContact1LastName); QFETCH(QString, expectedContact2FirstName); QFETCH(QString, expectedContact2LastName); QFETCH(QString, expectedContact3FirstName); QFETCH(QString, expectedContact3LastName); QFETCH(QString, expectedContact4FirstName); QFETCH(QString, expectedContact4LastName); QFETCH(QString, expectedContact5FirstName); QFETCH(QString, expectedContact5LastName); QList expectedContacts; expectedContacts << expectedContact1FirstName << expectedContact1LastName << expectedContact2FirstName << expectedContact2LastName << expectedContact3FirstName << expectedContact3LastName << expectedContact4FirstName << expectedContact4LastName << expectedContact5FirstName << expectedContact5LastName; QVERIFY(expectedContacts.size() == 10); bool contactFound = false; QString firstNameString = contact.detail().firstName(); QString lastNameString = contact.detail().lastName(); for (int j = 0; j < expectedContacts.size(); j++) { if ( (firstNameString == expectedContacts.at(j)) && (lastNameString == expectedContacts.at(j+1)) ) contactFound=true; } QCOMPARE(contactFound, true); } void tst_QContactJsondbEngine::testContact_data() { QTest::addColumn("expectedContact1FirstName"); QTest::addColumn("expectedContact1LastName"); QTest::addColumn("expectedContact2FirstName"); QTest::addColumn("expectedContact2LastName"); QTest::addColumn("expectedContact3FirstName"); QTest::addColumn("expectedContact3LastName"); QTest::addColumn("expectedContact4FirstName"); QTest::addColumn("expectedContact4LastName"); QTest::addColumn("expectedContact5FirstName"); QTest::addColumn("expectedContact5LastName"); { QTest::newRow("expectedContactsNames") << "Harry" << "Baker" << "Paul" << "Thomson" << "Julie" << "King" << "Angelina" << "Row" << "Natalie" << "Watson"; } } void tst_QContactJsondbEngine::testContactUpdate() { // Fetch existing contacts. QContactManager cm; QContactIdFilter idFilter; QList sortOrders; QList contacts = cm.contacts(sortOrders); // fetch all contacts QVERIFY(contacts.size() > 0); // Take first of them as test contact for update test. QContact testContact = contacts[0]; idFilter.add(testContact.id()); QContactFetchRequest fetchRequest; fetchRequest.setFilter(idFilter); // Convert QContact to JsonDb contact and insert extra fields to json contact. QJsonObject jsonContact; QContactJsonDbConverter jsonDbConverter; int extraFieldInContact = 3; int extraFieldInContactDetails = 5; int extraFieldInContactEmails = 8; if (jsonDbConverter.toJsonContact(&jsonContact, testContact)) { jsonContact.insert("extra3", extraFieldInContact); QJsonObject embeddedDetails; embeddedDetails["extra5"] = extraFieldInContactDetails; jsonContact.insert("details", embeddedDetails); QJsonObject embeddedEmail; embeddedEmail["extra8"] = extraFieldInContactEmails; jsonContact.insert("emails", embeddedEmail); } // Save test contact with extra fields directly to the jsondb. QVariantMap jsonResponse = m_dbClient->update(jsonContact, QContactJsonDbStr::userDataPartition()).first().toVariantMap(); // Update the test contact through the QContacts API with some details. QContactName nameDetail = testContact.detail(); nameDetail.setFirstName("Kary"); nameDetail.setLastName("Krant"); testContact.saveDetail(&nameDetail); QContactEmailAddress emailDetail; testContact.removeDetail(&emailDetail); emailDetail.setEmailAddress("Kary.Krant@ovi.com"); emailDetail.setContexts(QContactDetail::ContextHome); testContact.saveDetail(&emailDetail); QContactPhoneNumber phoneNumberDetail; testContact.removeDetail(&phoneNumberDetail); QList currSubtype; currSubtype << QContactPhoneNumber::SubTypeMobile; phoneNumberDetail.setSubTypes(currSubtype); phoneNumberDetail.setNumber("+358507654322"); testContact.saveDetail(&phoneNumberDetail); cm.saveContact(&testContact); // TODO: Check the updated fields are properly there. QList contactsNow = cm.contacts(idFilter, sortOrders); foreach (QContact cntc, contactsNow ) { if (cntc.id() == testContact.id()) { } } // Fetch directly from jsondb the test contact data. QString finalQuery; QVERIFY(jsonDbConverter.queryFromRequest(&fetchRequest, finalQuery)); QVariantMap finalJsonContact = m_dbClient->query(finalQuery, QContactJsonDbStr::userDataPartition()).first().toVariantMap(); QVERIFY(!finalJsonContact.isEmpty()); QVariantMap embeddedDetailsMap = finalJsonContact["details"].value(); QVariantMap embeddedEmailMap = finalJsonContact["emails"].value(); // Check that update did not change extra fields in contact object. QCOMPARE(finalJsonContact.value("extra3").toInt(),extraFieldInContact); // Check that update did not change extra fields in contact embededed details object. QCOMPARE(embeddedDetailsMap.value("extra5").toInt(),extraFieldInContactDetails); // We do not preserve extra fields for other embedded objects like emails. QCOMPARE(embeddedEmailMap.value("extra8"), QVariant()); } void tst_QContactJsondbEngine::testExtendedDetailsFromJsonDb() { // Fetch existing contacts. QContactManager cm; QContactIdFilter idFilter; QList sortOrders; QList contacts = cm.contacts(sortOrders); // fetch all contacts QVERIFY(contacts.size() > 0); // Take first of them as test contact. QContact testContact = contacts[0]; idFilter.add(testContact.id()); // Convert QContact to JsonDb contact and insert unknown properties to json contact. QJsonObject jsonContact; QContactJsonDbConverter jsonDbConverter; int extraFieldInContactDetails = 5; QVERIFY(jsonDbConverter.toJsonContact(&jsonContact, testContact)); jsonContact.insert("simpleStringDetail", QJsonValue::fromVariant("Simple string as detail data.")); QVariantMap variantMap; variantMap.insert("MapItemInt", 1); variantMap.insert("MapItemQString", "QStringData"); variantMap.insert("MapItemQVariant", QVariant( QString("QStringInQVariant"))); variantMap.insert("MapItemQVariantList", QVariantList() << QString("QStringInVariantList") << 5 ); QJsonObject jsonData; jsonData.insert("QStringItem", QJsonValue::fromVariant("Content for QStringItem.")); jsonData.insert("MapInMap", QJsonObject::fromVariantMap(variantMap)); jsonContact.insert("complexVariantMapDetail", jsonData); jsonContact.insert("anotherSimilarComplexVariantMapDetail", jsonData); QJsonObject embeddedDetails; embeddedDetails["extra5"] = extraFieldInContactDetails; jsonContact.insert("details", embeddedDetails); // Save test contact with unknown properties directly to the jsondb. QVariantMap jsonResponse = m_dbClient->update(jsonContact,QContactJsonDbStr::userDataPartition()).first().toVariantMap(); // Check that the unknown properties come up as extended details. QList contactsNow = cm.contacts(idFilter, sortOrders); foreach (QContact contact, contactsNow ) { if (contact.id() == testContact.id()) { QList extendedDetails = contact.details(); QCOMPARE(extendedDetails.size(), 3); for (int i = 0; i < extendedDetails.size(); ++i) { if (extendedDetails.at(i).name() == "simpleStringDetail") { QCOMPARE(extendedDetails[i].data().toString(), QString("Simple string as detail data.")); } if (extendedDetails.at(i).name() == "complexVariantMapDetail") { QVariantMap extendedDetailItems = extendedDetails[i].data().toMap(); QCOMPARE(extendedDetailItems.value("QStringItem").toString(), QString("Content for QStringItem.")); QVariantMap returnMap = extendedDetailItems.value("MapInMap").toMap(); QCOMPARE(returnMap.value("MapItemInt").toInt(), 1); QCOMPARE(returnMap.value("MapItemQString").toString(), QString("QStringData")); QCOMPARE(returnMap.value("MapItemQVariant").toString(), QString("QStringInQVariant")); QCOMPARE(returnMap.value("MapItemQVariantList").toList().at(0).toString(), QString("QStringInVariantList")); QCOMPARE(returnMap.value("MapItemQVariantList").toList().at(1).toInt(), 5); } if (extendedDetails.at(i).name() == "anotherSimilarComplexVariantMapDetail") { QVariantMap extendedDetailItems = extendedDetails[i].data().toMap(); QCOMPARE(extendedDetailItems.value("QStringItem").toString(), QString("Content for QStringItem.")); QVariantMap returnMap = extendedDetailItems.value("MapInMap").toMap(); QCOMPARE(returnMap.value("MapItemInt").toInt(), 1); QCOMPARE(returnMap.value("MapItemQString").toString(), QString("QStringData")); QCOMPARE(returnMap.value("MapItemQVariant").toString(), QString("QStringInQVariant")); QCOMPARE(returnMap.value("MapItemQVariantList").toList().at(0).toString(), QString("QStringInVariantList")); QCOMPARE(returnMap.value("MapItemQVariantList").toList().at(1).toInt(), 5); } } } } } void tst_QContactJsondbEngine::testExtendedDetailsToJsonDb() { // Fetch existing contacts. QContactManager cm; QContactIdFilter idFilter; QList sortOrders; QList contacts = cm.contacts(sortOrders); // fetch all contacts QVERIFY(contacts.size() > 0); // Take first of them as test contact for update test. QContact testContact = contacts[0]; idFilter.add(testContact.id()); QContactFetchRequest fetchRequest; fetchRequest.setFilter(idFilter); // Add simple string as extended detail. QContactExtendedDetail extendedDetail; extendedDetail.setName("extendedDetailQString"); extendedDetail.setData(QString("Simple QString as extended detail.")); testContact.saveDetail(&extendedDetail); // Add QVariantList as extended detail. QContactExtendedDetail variantListDetail; variantListDetail.setName("extendedDetailQVariantList"); variantListDetail.setData(QVariantList() << QString("QString in the QVariantlist detail.") << QVariant(1) << QString("Another QString in the QVariantlist detail.") << 2); testContact.saveDetail(&variantListDetail); // Add exteded details with a complex variantmap data in to test contact and save it. QContactExtendedDetail complexExtendedDetail; complexExtendedDetail.setName("complexExtendedDetail"); QVariantMap variantMap; variantMap.insert("mapItemInt", 3); variantMap.insert("mapItemQString", QString("QString item in QVariantMap")); variantMap.insert("mapItemVariant", QVariant(QString("QString item as QVariant in QVariantMap"))); variantMap.insert("mapItemVariantList", QVariantList() << QString("QString item in QVariantList in QVarianMap") << QVariant(4) << QString("Another QString item in QVariantList in QVariantMap")); complexExtendedDetail.setData(variantMap); testContact.saveDetail(&complexExtendedDetail); cm.saveContact(&testContact); //Fetch saved test contact data directly from jsondb. QContactJsonDbConverter jsonDbConverter; QString finalQuery; QVERIFY(jsonDbConverter.queryFromRequest(&fetchRequest, finalQuery)); QVariantMap jsonContact = m_dbClient->query(finalQuery, QContactJsonDbStr::userDataPartition()).first().toVariantMap(); // Check simple extended detail got correctly to jsondb. QCOMPARE(jsonContact["extendedDetailQString"].value().toString(),QString("Simple QString as extended detail.")); // Check variantlist exteded details got correctly to jsondb. QVariantList variantList = jsonContact["extendedDetailQVariantList"].value().toList(); QCOMPARE(variantList.size(), 4); QCOMPARE( variantList[0].toString(), QString("QString in the QVariantlist detail.")); QCOMPARE( variantList[1].toInt(), 1); QCOMPARE( variantList[2].toString(), QString("Another QString in the QVariantlist detail.")); QCOMPARE( variantList[3].toInt(),2); // Check complex extended detail field got correctly to jsondb. QVariantMap convertedVariantMap = jsonContact["complexExtendedDetail"].value().toMap(); QCOMPARE( convertedVariantMap["mapItemInt"].toInt(), 3); QCOMPARE( convertedVariantMap["mapItemQString"].toString(),QString("QString item in QVariantMap")); QCOMPARE( convertedVariantMap["mapItemVariant"].toString(),QString("QString item as QVariant in QVariantMap")); variantList = convertedVariantMap["mapItemVariantList"].toList(); QCOMPARE(variantList.size(), 3); QCOMPARE( variantList[0].toString(), QString("QString item in QVariantList in QVarianMap")); QCOMPARE( variantList[1].toInt(), 4); QCOMPARE( variantList[2].toString(), QString("Another QString item in QVariantList in QVariantMap")); } void tst_QContactJsondbEngine::testSaveContact() { QContactManager cm; // save a new contact int originalCount = cm.contactIds().size(); QContact testContact; QContactName nameDetail; nameDetail.setFirstName("Test Contact"); testContact.saveDetail(&nameDetail); // testing QContactFilter filter; QList sortOrders; QList contacts = cm.contacts(filter, sortOrders); bool returnValue = cm.saveContact(&testContact); QVERIFY(returnValue == true); QContactId testContactId = testContact.id(); QContact expected = cm.contact(testContactId); QCOMPARE(cm.contactIds().size(), originalCount + 1); QCOMPARE(testContact.id(), expected.id()); QCOMPARE(testContact.detail(QContactName::Type), expected.detail(QContactName::Type)); } void tst_QContactJsondbEngine::testSaveContacts() { QContactManager cm; // save new contacts int originalCount = cm.contactIds().size(); QContact testContact; QContactName nameDetail; nameDetail.setFirstName("Test Contact 1"); testContact.saveDetail(&nameDetail); QList saveList; saveList << testContact; QContact testContact2; QContactName nameDetail2; nameDetail2.setFirstName("Test Contact 2"); testContact2.saveDetail(&nameDetail2); saveList << testContact2; // dummy contact that will not be added QContact testContact3; QContactName nameDetail3; nameDetail3.setFirstName("Test Contact 3"); testContact3.saveDetail(&nameDetail3); // testing QContactFilter filter; QList sortOrders; QList initialContacts = cm.contacts(filter, sortOrders); bool returnValue = cm.saveContacts(&saveList); QVERIFY(returnValue == true); QList contacts = cm.contacts(filter, sortOrders); bool contact1Found = false; bool contact2Found = false; foreach (QContact curr, contacts) { if (curr.detail() == nameDetail) contact1Found=true; if (curr.detail() == nameDetail2) contact2Found=true; } QVERIFY(contact1Found == true); QVERIFY(contact2Found == true); QCOMPARE(cm.contactIds().size(), originalCount + 2); } void tst_QContactJsondbEngine::testContactDetailFilter() { QContactManager myContactManager; QList sortOrders; // ------------ MATCH FLAG = CONTAINS ------------ // retrieve contacts filtering by first name QString firstName = " li "; // This is to test the string sanitation: the filter will use the // sanitized version of this string, thus: "li" QContactDetailFilter dfil; dfil.setDetailType(QContactName::Type, QContactName::FieldFirstName); dfil.setValue(firstName); dfil.setMatchFlags(QContactFilter::MatchContains); QVERIFY(dfil.matchFlags() == QContactFilter::MatchContains); QList contacts = myContactManager.contacts(dfil, sortOrders); QCOMPARE(contacts.size(), 3); QFETCH(QString, expectedContact3FirstName); QFETCH(QString, expectedContact4FirstName); QFETCH(QString, expectedContact5FirstName); QList expectedContacts; expectedContacts << expectedContact3FirstName << expectedContact4FirstName << expectedContact5FirstName; QVERIFY(expectedContacts.size() == 3); for (int i = 0; i < contacts.size(); i++) { bool contactFound = false; QString firstNameString = contacts.at(i).detail().firstName(); for (int j = 0; j < expectedContacts.size(); j++) { if (firstNameString == expectedContacts.at(j)) contactFound=true; } QCOMPARE(contactFound, true); } // retrieve contacts filtering by Last name QString lastName = "o"; dfil.setDetailType(QContactName::Type, QContactName::FieldLastName); dfil.setValue(lastName); QVERIFY(dfil.matchFlags() == QContactFilter::MatchContains); contacts = myContactManager.contacts(dfil, sortOrders); QVERIFY(contacts.size() == 3); QFETCH(QString, expectedContact2LastName); QFETCH(QString, expectedContact4LastName); QFETCH(QString, expectedContact5LastName); expectedContacts.clear(); expectedContacts << expectedContact2LastName << expectedContact4LastName << expectedContact5LastName; QVERIFY(expectedContacts.size() == 3); for (int i = 0; i < contacts.size(); i++) { bool contactFound = false; QString lastNameString = contacts.at(i).detail().lastName(); for (int j = 0; j < expectedContacts.size(); j++) { if (lastNameString == expectedContacts.at(j)) contactFound=true; } QCOMPARE(contactFound, true); } // retrieve contacts filtering by phone number QString phoneNr = " 6543HHHH HHHHHHHH"; //The number above will be sanitized by jsondb contacts plugin //Hence, the filter will look for contacts matching the //sanitized version of phoneNr, i.e., "6543" dfil.setDetailType(QContactPhoneNumber::Type, QContactPhoneNumber::FieldNumber); dfil.setValue(phoneNr); QVERIFY(dfil.matchFlags() == QContactFilter::MatchContains); contacts = myContactManager.contacts(dfil, sortOrders); QCOMPARE(contacts.size(), 3); QFETCH(QString, expectedContact1LastName); // Contacts 2 and 5 were fetched already expectedContacts.clear(); expectedContacts << expectedContact1LastName << expectedContact2LastName << expectedContact5LastName; QVERIFY(expectedContacts.size() == 3); for (int i = 0; i < contacts.size(); i++) { bool contactFound = false; QString lastNameString = contacts.at(i).detail().lastName(); for (int j = 0; j < expectedContacts.size(); j++) { if (lastNameString == expectedContacts.at(j)) contactFound=true; } QCOMPARE(contactFound, true); } // retrieve contacts filtering by email QString email = " on "; // This is to test the string sanitation: the filter will use the // sanitized version of this string, thus: "on" dfil.setDetailType(QContactEmailAddress::Type, QContactEmailAddress::FieldEmailAddress); dfil.setValue(email); QVERIFY(dfil.matchFlags() == QContactFilter::MatchContains); contacts = myContactManager.contacts(dfil, sortOrders); QVERIFY(contacts.size() == 2); expectedContacts.clear(); QFETCH(QString, expectedContact2EmailAddress); QFETCH(QString, expectedContact5EmailAddress); expectedContacts << expectedContact2EmailAddress << expectedContact5EmailAddress; QVERIFY(expectedContacts.size() == 2); for (int i = 0; i < contacts.size(); i++) { bool contactFound = false; QString firstNameString = contacts.at(i).detail().firstName(); QString lastNameString = contacts.at(i).detail().lastName(); QString emailAddress = "mailto:"; emailAddress.append(firstNameString + "." + lastNameString + "@ovi.com"); for (int j = 0; j < expectedContacts.size(); j++) { if (emailAddress == expectedContacts.at(j)) contactFound=true; } QCOMPARE(contactFound, true); } // ------------ MATCH FLAG = MATCH EXACTLY ------------ // retrieve contacts filtering by first name firstName = "Julie"; dfil.setDetailType(QContactName::Type, QContactName::FieldFirstName); dfil.setValue(firstName); dfil.setMatchFlags(QContactFilter::MatchExactly); QVERIFY(dfil.matchFlags() == QContactFilter::MatchExactly); contacts = myContactManager.contacts(dfil, sortOrders); QVERIFY(contacts.size() == 1); expectedContacts.clear(); expectedContacts << expectedContact3FirstName; QVERIFY(expectedContacts.size() == 1); for (int i = 0; i < contacts.size(); i++) { bool contactFound = false; QString firstNameString = contacts.at(i).detail().firstName(); for (int j = 0; j < expectedContacts.size(); j++) { if (firstNameString == expectedContacts.at(j)) contactFound=true; } QCOMPARE(contactFound, true); } // retrieve contacts filtering by Last name lastName = "Thomson"; dfil.setDetailType(QContactName::Type, QContactName::FieldLastName); dfil.setValue(lastName); QVERIFY(dfil.matchFlags() == QContactFilter::MatchExactly); contacts = myContactManager.contacts(dfil, sortOrders); QVERIFY(contacts.size() == 1); expectedContacts.clear(); expectedContacts << expectedContact2LastName; QVERIFY(expectedContacts.size() == 1); for (int i = 0; i < contacts.size(); i++) { bool contactFound = false; QString lastNameString = contacts.at(i).detail().lastName(); for (int j = 0; j < expectedContacts.size(); j++) { if (lastNameString == expectedContacts.at(j)) contactFound=true; } QCOMPARE(contactFound, true); } // retrieve contacts filtering by phone number phoneNr = "+358507654321"; dfil.setDetailType(QContactPhoneNumber::Type, QContactPhoneNumber::FieldNumber); dfil.setValue(phoneNr); QVERIFY(dfil.matchFlags() == QContactFilter::MatchExactly); contacts = myContactManager.contacts(dfil, sortOrders); QVERIFY(contacts.size() == 3); expectedContacts.clear(); expectedContacts << expectedContact1LastName << expectedContact2LastName << expectedContact5LastName; QVERIFY(expectedContacts.size() == 3); for (int i = 0; i < contacts.size(); i++) { bool contactFound = false; QString lastNameString = contacts.at(i).detail().lastName(); for (int j = 0; j < expectedContacts.size(); j++) { if (lastNameString == expectedContacts.at(j)) contactFound=true; } QCOMPARE(contactFound, true); } // retrieve contacts filtering by email email = "Angelina.Row@ovi.com"; dfil.setDetailType(QContactEmailAddress::Type, QContactEmailAddress::FieldEmailAddress); dfil.setValue(email); QVERIFY(dfil.matchFlags() == QContactFilter::MatchExactly); contacts = myContactManager.contacts(dfil, sortOrders); QVERIFY(contacts.size() == 1); expectedContacts.clear(); QFETCH(QString, expectedContact4EmailAddress); expectedContacts.clear(); expectedContacts << expectedContact4EmailAddress; QVERIFY(expectedContacts.size() == 1); for (int i = 0; i < contacts.size(); i++) { bool contactFound = false; QString firstNameString = contacts.at(i).detail().firstName(); QString lastNameString = contacts.at(i).detail().lastName(); QString emailAddress = "mailto:"; emailAddress.append(firstNameString + "." + lastNameString + "@ovi.com"); for (int j = 0; j < expectedContacts.size(); j++) { if (emailAddress == expectedContacts.at(j)) contactFound=true; } QCOMPARE(contactFound, true); } // retrieve contacts filtering by local ID QList ids = myContactManager.contactIds(sortOrders); // retrieve all ids // Retrieve an empty list of ids QContactIdFilter idf; QVERIFY(idf.type() == QContactFilter::IdFilter); QVERIFY(idf.ids().count() == 0); contacts = myContactManager.contacts(idf, sortOrders); QVERIFY(contacts.size() == 0); // Retrieve only the first ID QContactId firstId = ids.first(); QList shortList; shortList << firstId; idf.setIds(shortList); QVERIFY(idf.ids() == shortList); QContact contact = myContactManager.contact(firstId); QVERIFY(firstId == contact.id()); contacts = myContactManager.contacts(idf, sortOrders); QVERIFY(contacts.size() == 1); QFETCH(QString, expectedContact1FirstName); QFETCH(QString, expectedContact2FirstName); QFETCH(QString, expectedContact3LastName); expectedContacts.clear(); expectedContacts << expectedContact1FirstName << expectedContact1LastName << expectedContact2FirstName << expectedContact2LastName << expectedContact3FirstName << expectedContact3LastName << expectedContact4FirstName << expectedContact4LastName << expectedContact5FirstName << expectedContact5LastName; QVERIFY(expectedContacts.size() == 10); bool contactFound = false; QString firstNameString = contact.detail().firstName(); QString lastNameString = contact.detail().lastName(); for (int j = 0; j < expectedContacts.size(); j++) { if ( (firstNameString == expectedContacts.at(j)) && (lastNameString == expectedContacts.at(j+1)) ) contactFound=true; } QCOMPARE(contactFound, true); // Retrieve multiple IDs idf.setIds(ids); QVERIFY(idf.ids() == ids); contacts = myContactManager.contacts(idf, sortOrders); QVERIFY(contacts.size() == 5); expectedContacts.clear(); expectedContacts << expectedContact1FirstName << expectedContact1LastName << expectedContact2FirstName << expectedContact2LastName << expectedContact3FirstName << expectedContact3LastName << expectedContact4FirstName << expectedContact4LastName << expectedContact5FirstName << expectedContact5LastName; QVERIFY(expectedContacts.size() == 10); for (int i = 0; i < contacts.size(); i++) { bool contactFound = false; QString firstNameString = contacts.at(i).detail().firstName(); QString lastNameString = contacts.at(i).detail().lastName(); for (int j = 0; j < expectedContacts.size(); j++) { if ( (firstNameString == expectedContacts.at(j)) && (lastNameString == expectedContacts.at(j+1)) ) contactFound=true; } QCOMPARE(contactFound, true); } // ------------ MATCH FLAG = MatchStartsWith ------------ // retrieve contacts filtering by first name firstName = "Jul"; dfil.setDetailType(QContactName::Type, QContactName::FieldFirstName); dfil.setValue(firstName); dfil.setMatchFlags(QContactFilter::MatchStartsWith); QVERIFY(dfil.matchFlags() == QContactFilter::MatchStartsWith); contacts = myContactManager.contacts(dfil, sortOrders); QVERIFY(contacts.size() == 1); expectedContacts.clear(); expectedContacts << expectedContact3FirstName; QVERIFY(expectedContacts.size() == 1); for (int i = 0; i < contacts.size(); i++) { bool contactFound = false; QString firstNameString = contacts.at(i).detail().firstName(); for (int j = 0; j < expectedContacts.size(); j++) { if (firstNameString == expectedContacts.at(j)) contactFound=true; } QCOMPARE(contactFound, true); } // ------------ MATCH FLAG = MatchEndsWith ------------ // retrieve contacts filtering by first name firstName = "ie"; dfil.setDetailType(QContactName::Type, QContactName::FieldFirstName); dfil.setValue(firstName); dfil.setMatchFlags(QContactFilter::MatchEndsWith); QVERIFY(dfil.matchFlags() == QContactFilter::MatchEndsWith); contacts = myContactManager.contacts(dfil, sortOrders); QVERIFY(contacts.size() == 2); expectedContacts.clear(); expectedContacts << expectedContact3FirstName << expectedContact5FirstName; QVERIFY(expectedContacts.size() == 2); for (int i = 0; i < contacts.size(); i++) { bool contactFound = false; QString firstNameString = contacts.at(i).detail().firstName(); for (int j = 0; j < expectedContacts.size(); j++) { if (firstNameString == expectedContacts.at(j)) contactFound=true; } QCOMPARE(contactFound, true); } } void tst_QContactJsondbEngine::testContactDetailFilter_data() { QTest::addColumn("expectedContact1FirstName"); QTest::addColumn("expectedContact1LastName"); QTest::addColumn("expectedContact2FirstName"); QTest::addColumn("expectedContact2LastName"); QTest::addColumn("expectedContact3FirstName"); QTest::addColumn("expectedContact3LastName"); QTest::addColumn("expectedContact4FirstName"); QTest::addColumn("expectedContact4LastName"); QTest::addColumn("expectedContact5FirstName"); QTest::addColumn("expectedContact5LastName"); QTest::addColumn("expectedContact2EmailAddress"); QTest::addColumn("expectedContact4EmailAddress"); QTest::addColumn("expectedContact5EmailAddress"); { QTest::newRow("expectedContactsNames") << "Harry" << "Baker" << "Paul" << "Thomson" << "Julie" << "King" << "Angelina" << "Row" << "Natalie" << "Watson" << "mailto:Paul.Thomson@ovi.com" << "mailto:Angelina.Row@ovi.com" << "mailto:Natalie.Watson@ovi.com"; } } void tst_QContactJsondbEngine::testContactSortOrder() { QContactManager myContactManager; // sort order QContactSortOrder sortOrder; sortOrder.setDetailType(QContactName::Type, QContactName::FieldFirstName); QList sortOrders; sortOrders.append(sortOrder); // retrieve all the contacts and sort first names alphabetically QContactFilter filter; QList contacts = myContactManager.contacts(filter, sortOrders); QCOMPARE(contacts.size(), 5); QFETCH(QString, expectedContact1FirstName); QFETCH(QString, expectedContact2FirstName); QFETCH(QString, expectedContact3FirstName); QFETCH(QString, expectedContact4FirstName); QFETCH(QString, expectedContact5FirstName); QList expectedContacts; expectedContacts << expectedContact4FirstName << expectedContact1FirstName << expectedContact3FirstName << expectedContact5FirstName << expectedContact2FirstName; QCOMPARE(expectedContacts.size(), 5); if (qt_debug_jsondb_contacts()) qDebug() << " (TEST) expected ordered contacts: " << expectedContacts; for (int i = 0; i < contacts.size(); i++) { QVERIFY (contacts.at(i).detail().firstName() == expectedContacts.at(i)); } // now sort last names alphabetically sortOrder.setDetailType(QContactName::Type, QContactName::FieldLastName); sortOrders.clear(); sortOrders.append(sortOrder); contacts = myContactManager.contacts(filter, sortOrders); if (qt_debug_jsondb_contacts()) qDebug() << " (TEST) contacts sorted by surname: " << contacts; QVERIFY(contacts.size() == 5); expectedContacts.clear(); QFETCH(QString, expectedContact1LastName); QFETCH(QString, expectedContact2LastName); QFETCH(QString, expectedContact3LastName); QFETCH(QString, expectedContact4LastName); QFETCH(QString, expectedContact5LastName); expectedContacts << expectedContact1LastName << expectedContact3LastName << expectedContact4LastName << expectedContact2LastName << expectedContact5LastName; if (qt_debug_jsondb_contacts()) qDebug() << " (TEST) EXPECTED contacts sorted by surname: " << expectedContacts; for (int i = 0; i < contacts.size(); i++) { QVERIFY (contacts.at(i).detail().lastName() == expectedContacts.at(i)); } // now sort emails alphabetically sortOrder.setDetailType(QContactEmailAddress::Type, QContactEmailAddress::FieldEmailAddress); sortOrders.clear(); sortOrders.append(sortOrder); contacts = myContactManager.contacts(filter, sortOrders); if (qt_debug_jsondb_contacts()) qDebug() << " (TEST) contacts sorted by email address: " << contacts; QVERIFY(contacts.size() == 5); expectedContacts.clear(); QFETCH(QString, expectedContact1EmailAddress); QFETCH(QString, expectedContact2EmailAddress); QFETCH(QString, expectedContact3EmailAddress); QFETCH(QString, expectedContact4EmailAddress); QFETCH(QString, expectedContact5EmailAddress); expectedContacts << expectedContact4EmailAddress << expectedContact1EmailAddress << expectedContact3EmailAddress << expectedContact5EmailAddress << expectedContact2EmailAddress; QVERIFY(expectedContacts.size() == 5); if (qt_debug_jsondb_contacts()) qDebug() << " (TEST) EXPECTED contacts sorted by email: " << expectedContacts; for (int i = 0; i < contacts.size(); i++) { QVERIFY (contacts.at(i).detail().emailAddress() == expectedContacts.at(i)); } // retrieve contacts filtering them by first name, then sort the results alphabetically sortOrder.setDetailType(QContactName::Type, QContactName::FieldFirstName); sortOrders.clear(); sortOrders.append(sortOrder); QString firstName = "li"; QContactDetailFilter dfil; dfil.setDetailType(QContactName::Type, QContactName::FieldFirstName); dfil.setValue(firstName); dfil.setMatchFlags(QContactFilter::MatchContains); QVERIFY(dfil.matchFlags() == QContactFilter::MatchContains); contacts = myContactManager.contacts(dfil, sortOrders); if (qt_debug_jsondb_contacts()) qDebug() << " (TEST) contacts filtered and sorted by first name: " << contacts; expectedContacts.clear(); QVERIFY(contacts.size() == 3); expectedContacts << expectedContact4FirstName << expectedContact3FirstName << expectedContact5FirstName; QVERIFY(expectedContacts.size() == 3); if (qt_debug_jsondb_contacts()) qDebug() << " (TEST) expected filtered and ordered contacts: " << expectedContacts; for (int i = 0; i < expectedContacts.size(); i++) { QVERIFY (contacts.at(i).detail().firstName() == expectedContacts.at(i)); } // now sort the filtered results by email address sortOrder.setDetailType(QContactEmailAddress::Type, QContactEmailAddress::FieldEmailAddress); sortOrders.clear(); sortOrders.append(sortOrder); firstName = ""; dfil.setDetailType(QContactName::Type, QContactName::FieldFirstName); dfil.setValue(firstName); dfil.setMatchFlags(QContactFilter::MatchContains); QVERIFY(dfil.matchFlags() == QContactFilter::MatchContains); contacts = myContactManager.contacts(dfil, sortOrders); if (qt_debug_jsondb_contacts()) qDebug() << " (TEST) filtered contacts sorted by email address: " << contacts; QVERIFY(contacts.size() == 5); } void tst_QContactJsondbEngine::testContactSortOrder_data() { QTest::addColumn("expectedContact1FirstName"); QTest::addColumn("expectedContact1LastName"); QTest::addColumn("expectedContact2FirstName"); QTest::addColumn("expectedContact2LastName"); QTest::addColumn("expectedContact3FirstName"); QTest::addColumn("expectedContact3LastName"); QTest::addColumn("expectedContact4FirstName"); QTest::addColumn("expectedContact4LastName"); QTest::addColumn("expectedContact5FirstName"); QTest::addColumn("expectedContact5LastName"); QTest::addColumn("expectedContact1EmailAddress"); QTest::addColumn("expectedContact2EmailAddress"); QTest::addColumn("expectedContact3EmailAddress"); QTest::addColumn("expectedContact4EmailAddress"); QTest::addColumn("expectedContact5EmailAddress"); { QTest::newRow("expectedContactsNames") << "Harry" << "Baker" << "Paul" << "Thomson" << "Julie" << "King" << "Angelina" << "Row" << "Natalie" << "Watson" << "Harry.Baker@ovi.com" << "Paul.Thomson@ovi.com" << "Julie.King@ovi.com" << "Angelina.Row@ovi.com" << "Natalie.Watson@ovi.com"; } } void tst_QContactJsondbEngine::testRemoveContacts() { QContactManager cm; // Test error generation when passing empty Id list QList emptyList; bool returnValue = cm.removeContacts(emptyList); QVERIFY(returnValue == false); QVERIFY(cm.error() == 10); // Check if the generated error is in fact "BadArgumentError" // Test error generation when passing invalid Ids: empty string QList voidList; voidList << QContactId(); returnValue = cm.removeContacts(voidList); QVERIFY(returnValue == false); QVERIFY(cm.error() == 1); // Check if the generated error is in fact "DoesNotExistError" // TODO: This is the same as above, clean or use real invalid id. // Test error generation when passing invalid Ids: non existing string QList invalidList; invalidList << QContactId(); qDebug() << "(non existing contact Id) TO REMOVE: " << invalidList; returnValue = cm.removeContacts(invalidList); QVERIFY(returnValue == false); QVERIFY(cm.error() == 1); // Check whether the generated error code is in fact "DoesNotExistError" // Remove only two contacts QList saveList; QContactName nameDetail; QContact contactToRemove1; nameDetail.setFirstName("Contact To Remove 1"); contactToRemove1.saveDetail(&nameDetail); saveList << contactToRemove1; cm.saveContact(&contactToRemove1); QContactId contactToRemove1Id = contactToRemove1.id(); QContact contactToRemove2; nameDetail.setFirstName("Contact To Remove 2"); contactToRemove2.saveDetail(&nameDetail); saveList << contactToRemove2; cm.saveContact(&contactToRemove2); QContactId contactToRemove2Id = contactToRemove2.id(); QList contactsToRemove; QContactFilter filter; QList sortOrders; QList contacts = cm.contacts(filter, sortOrders); QContactName nameDetail1; nameDetail1.setFirstName("Contact To Remove 1"); QContactName nameDetail2; nameDetail2.setFirstName("Contact To Remove 2"); foreach (QContact curr, contacts) { if (curr.detail() == nameDetail1) contactsToRemove << curr.id(); if (curr.detail() == nameDetail2) contactsToRemove << curr.id(); } if (qt_debug_jsondb_contacts()) qDebug() << "(two contacts) TO REMOVE: " << contactsToRemove; returnValue = cm.removeContacts(contactsToRemove); QVERIFY(returnValue == true); QVERIFY(cm.error() == 0); // Check if there are errors //QCOMPARE(cm.contactIds().size(), originalCount - 2); // Does not work when there are multiple contacts with the same name bool contact1Found = false; bool contact2Found = false; contacts = cm.contacts(filter, sortOrders); // Update the contact list after removal foreach (QContact curr, contacts) { if (curr.detail() == nameDetail1) contact1Found=true; if (curr.detail() == nameDetail2) contact2Found=true; } QVERIFY(contact1Found == false); QVERIFY(contact2Found == false); QList toRemove = cm.contactIds(); if (qt_debug_jsondb_contacts()) qDebug() << "TO REMOVE: " << toRemove; returnValue = cm.removeContacts(toRemove); QVERIFY(returnValue == true || cm.error() == QContactManager::BadArgumentError); // It currently gives BadArgumentError if empty list given (no contacts in cm). QList contactsLeft = cm.contactIds(); qDebug() << "NOTE: the error message by doSyncRequest in function contactIds is due to the fact that there are no contacts left in the database"; QVERIFY(contactsLeft.isEmpty()); // Check that all the contacts have been removed // save two new contacts (just not to leave the database empty) QContact newContact1; nameDetail.setFirstName("new contact 1"); newContact1.saveDetail(&nameDetail); saveList << newContact1; cm.saveContact(&newContact1); QContact newContact2; nameDetail.setFirstName("new contact 2"); newContact2.saveDetail(&nameDetail); saveList << newContact2; cm.saveContact(&newContact2); } void tst_QContactJsondbEngine::testRemoveContact() { QContactManager cm; // save a new contact QContact contactToRemove; QContactName nameDetail; nameDetail.setFirstName("Contact To Remove"); contactToRemove.saveDetail(&nameDetail); cm.saveContact(&contactToRemove); QContactId contactToRemoveId = contactToRemove.id(); // Remove the contact bool returnValue = cm.removeContact(contactToRemoveId); QVERIFY(returnValue == true); QVERIFY(cm.error() == 0); // Check if there are errors bool contactToRemoveFound = false; QContactFilter filter; QList sortOrders; QList contacts = cm.contacts(filter, sortOrders); // Update the contact list after removal foreach (QContact curr, contacts) { if (curr.detail() == nameDetail) contactToRemoveFound=true; } QVERIFY(contactToRemoveFound == false); } QTEST_MAIN(tst_QContactJsondbEngine); #include "tst_qcontactjsondbengine.moc" tests/auto/contacts/qcontactjsondbconverter/000077500000000000000000000000001233466112000217505ustar00rootroot00000000000000tests/auto/contacts/qcontactjsondbconverter/qcontactjsondbconverter.pro000066400000000000000000000024601233466112000274400ustar00rootroot00000000000000include(../../auto.pri) QT += contacts contacts-private jsondb SOURCES += tst_qcontactjsondbconverter.cpp \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbconverter.cpp \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbenginefactory.cpp \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbengine.cpp \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbrequesthandler.cpp \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbrequestmanager.cpp \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbid.cpp \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbstring.cpp HEADERS += ../../../../src/plugins/contacts/jsondb/qcontactjsondbenginefactory.h \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbengine.h \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbconverter.h \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbrequesthandler.h \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbrequestmanager.h \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbid.h \ ../../../../src/plugins/contacts/jsondb/qcontactjsondbstring.h INCLUDEPATH += ../../../../src/plugins/contacts/jsondb DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactjsondbconverter/tst_qcontactjsondbconverter.cpp000066400000000000000000001710001233466112000303110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "qcontactjsondbconverter.h" #include "qcontactjsondbengine.h" #include "qcontactjsondbstring.h" #include "qcontactjsondbid.h" QTCONTACTS_USE_NAMESPACE class tst_QcontactJsondbConverter : public QObject { Q_OBJECT public: tst_QcontactJsondbConverter(); private Q_SLOTS: void toQContactTest(); void toJsonContactTest(); void toQContactsTest(); void updateContextsTest(); void queryFromRequestTest(); void convertCompoundFilterTest(); void convertSortOrderTest(); void contactIdToUuidTest(); private: void testJsonDetailItems(const QJsonObject& values, const QString& extractField, const QMap& fields, bool testSize = true); void initializeJsonContact(QJsonObject& jsonContact, unsigned int numbering = 0); private: QString m_partitionName; }; tst_QcontactJsondbConverter::tst_QcontactJsondbConverter() : m_partitionName(QContactJsonDbStr::userDataPartition()) { // nothing needed } void tst_QcontactJsondbConverter::toQContactTest() { QContactJsonDbConverter converter; QContactJsonDbEngine engine; QJsonObject jsonContact; QContact contact; QJsonObject jsonData; QContactDetail detail; // test name (set in initialization) initializeJsonContact(jsonContact); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactName::Type); QVERIFY(!detail.isEmpty()); QContactName* name = static_cast(&detail); QVERIFY(name != NULL); QCOMPARE(name->firstName(), QString("John")); QCOMPARE(name->lastName(), QString("Doe")); QCOMPARE(name->middleName(), QString("Tom")); // cleanup contact.clearDetails(); // test name with white spaces to be trimmed jsonData.insert("firstName", QString("John")); jsonData.insert("lastName", QString("Doe ")); jsonData.insert("middleName", QString(" Tom")); jsonContact.insert(QContactJsonDbStr::nameDefinitionName(), jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactName::Type); QVERIFY(!detail.isEmpty()); name = static_cast(&detail); QVERIFY(name != NULL); QCOMPARE(name->firstName(), QString("John")); QCOMPARE(name->lastName(), QString("Doe")); QCOMPARE(name->middleName(), QString("Tom")); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // test invalid name (name is too long) jsonData.insert("firstName", QString(" John ")); jsonData.insert("lastName", QString("Vvvvvvvvvveeeeeeeeeerrrrrrrrrryyyyyyyyyylongnameeeeeeeeee")); jsonData.insert("middleName", QString(" Tom")); jsonContact.insert(QContactJsonDbStr::nameDefinitionName(), jsonData); QVERIFY(!converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactName::Type); QVERIFY(detail.isEmpty()); name = static_cast(&detail); QVERIFY(name->isEmpty()); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // test passing an empty string in the first place: invalid QString emptyName; jsonData.insert("firstName", emptyName); jsonData.insert("lastName", emptyName); jsonContact.insert(QContactJsonDbStr::nameDefinitionName(), jsonData); QVERIFY(!converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactName::Type); QVERIFY(detail.isEmpty()); name = static_cast(&detail); QVERIFY(name->isEmpty()); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // test with a string which becomes empty after sanitizing it: invalid, contact not saved QString invalidName = " "; jsonData.insert("firstName", QString(" John ")); jsonData.insert("lastName", invalidName); jsonContact.insert(QContactJsonDbStr::nameDefinitionName(), jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactName::Type); QVERIFY(!detail.isEmpty()); name = static_cast(&detail); QVERIFY(!name->isEmpty()); QVERIFY(name->lastName().isEmpty()); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // gender initializeJsonContact(jsonContact); jsonData.insert("gender", QString("male")); jsonContact.insert("gender", jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactGender::Type); QVERIFY(!detail.isEmpty()); QContactGender* gender = static_cast(&detail); QVERIFY(gender != NULL); QCOMPARE(gender->gender(), QContactGender::GenderMale); // cleanup contact.clearDetails(); jsonData = QJsonObject(); // organization initializeJsonContact(jsonContact); jsonData.insert("name", QString("ACME")); jsonData.insert("department", QString("Spy")); jsonData.insert("title", QString("Vice President")); jsonData.insert("role", QString("Superhero")); jsonData.insert("assistantName", QString("Daisy Duck")); jsonData.insert("logoUrl", QString("http://www.acme.com/logo.jpg")); QJsonArray organizationData; organizationData.append(jsonData); jsonContact.insert("organizations", organizationData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactOrganization::Type); QVERIFY(!detail.isEmpty()); QContactOrganization* org = static_cast(&detail); QVERIFY(org != NULL); QCOMPARE(org->name(), QString("ACME")); QCOMPARE(org->department().at(0), QString("Spy")); QCOMPARE(org->title(), QString("Vice President")); QCOMPARE(org->role(), QString("Superhero")); QCOMPARE(org->assistantName(), QString("Daisy Duck")); QCOMPARE(org->logoUrl(), QUrl("http://www.acme.com/logo.jpg")); // cleanup contact.clearDetails(); jsonData = QJsonObject(); // test organization with white spaces to be trimmed jsonData.insert("name", QString(" ACME2")); jsonData.insert("department", QString("Spy2 ")); jsonData.insert("title", QString(" Vice President 2")); jsonData.insert("role", QString(" Superhero2 ")); jsonData.insert("assistantName", QString(" Daisy Duck 2")); QJsonArray organizationData1; organizationData1.append(jsonData); jsonContact.insert("organizations", organizationData1); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactOrganization::Type); QVERIFY(!detail.isEmpty()); org = static_cast(&detail); QVERIFY(org != NULL); QCOMPARE(org->name(), QString("ACME2")); QCOMPARE(org->department().at(0), QString("Spy2")); QCOMPARE(org->title(), QString("Vice President 2")); QCOMPARE(org->role(), QString("Superhero2")); QCOMPARE(org->assistantName(), QString("Daisy Duck 2")); // cleanup contact.clearDetails(); jsonData = QJsonObject(); // test invalid organization name (name is too long) jsonData.insert("name", QString("Vvvvvvvvvveeeeeeeeeerrrrrrrrrryyyyyyyyyylongnameeeeeeeeee")); QJsonArray organizationData2; organizationData2.append(jsonData); jsonContact.insert(QContactJsonDbStr::organizationDefinitionName(), organizationData2); QVERIFY(!converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactOrganization::Type); QVERIFY(detail.isEmpty()); org = static_cast(&detail); QVERIFY(org->isEmpty()); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // test passing an empty string in the first place: allowed jsonData.insert("name", emptyName); jsonContact.insert(QContactJsonDbStr::organizationDefinitionName(), jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactOrganization::Type); QVERIFY(detail.isEmpty()); org = static_cast(&detail); QVERIFY(org->isEmpty()); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // test with a string which becomes empty after sanitizing it: detail is ignored jsonData.insert("name", invalidName); QJsonArray organizationData3; organizationData3.append(jsonData); jsonContact.insert(QContactJsonDbStr::organizationDefinitionName(), organizationData3); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactOrganization::Type); QVERIFY(detail.isEmpty()); org = static_cast(&detail); QVERIFY(org->isEmpty()); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // birthday initializeJsonContact(jsonContact); QDateTime birthDayDate = QDateTime::fromString("1979-11-22", Qt::ISODate); jsonData.insert("birthday", QStringLiteral("1979-11-22")); jsonContact.insert("details", jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactBirthday::Type); QVERIFY(!detail.isEmpty()); QContactBirthday* bd = static_cast(&detail); QVERIFY(bd != NULL); QCOMPARE(bd->dateTime(), birthDayDate); // cleanup contact.clearDetails(); // avatar initializeJsonContact(jsonContact); jsonData.insert("photoUrl", QString("http://www.acme.com/logo.jpg")); jsonContact.insert("details", jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactAvatar::Type); QVERIFY(!detail.isEmpty()); QContactAvatar* avatar = static_cast(&detail); QVERIFY(avatar != NULL); QCOMPARE(avatar->imageUrl(), QUrl("http://www.acme.com/logo.jpg")); // cleanup contact.clearDetails(); // ringtone initializeJsonContact(jsonContact); jsonData.insert("ringtoneUrl", QString("http://www.acme.com/ring.mp3")); jsonContact.insert("details", jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactRingtone::Type); QVERIFY(!detail.isEmpty()); QContactRingtone* ring = static_cast(&detail); QVERIFY(ring != NULL); QCOMPARE(ring->audioRingtoneUrl(), QUrl("http://www.acme.com/ring.mp3")); // cleanup contact.clearDetails(); jsonData = QJsonObject(); // nickname initializeJsonContact(jsonContact); jsonData.insert("nickname", QString("Chupacabra")); jsonContact.insert("details", jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactNickname::Type); QVERIFY(!detail.isEmpty()); QContactNickname* nick = static_cast(&detail); QVERIFY(nick != NULL); QCOMPARE(nick->nickname(), QString("Chupacabra")); // cleanup contact.clearDetails(); jsonData = QJsonObject(); // test nickname with white spaces to be trimmed jsonData.insert("nickname", QString(" Chupacabra")); jsonContact.insert("details", jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactNickname::Type); QVERIFY(!detail.isEmpty()); nick = static_cast(&detail); QVERIFY(nick != NULL); QCOMPARE(nick->nickname(), QString("Chupacabra")); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // test invalid nickname (nickname is too long) jsonData.insert("nickname", QString("Vvvvvvvvvveeeeeeeeeerrrrrrrrrryyyyyyyyyylongnameeeeeeeeee")); jsonContact.insert("details", jsonData); QVERIFY(!converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactNickname::Type); QVERIFY(detail.isEmpty()); nick = static_cast(&detail); QVERIFY(nick->isEmpty()); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // test passing an empty string in the first place: allowed jsonData.insert("nickname", emptyName); jsonContact.insert("details", jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactNickname::Type); QVERIFY(detail.isEmpty()); nick = static_cast(&detail); QVERIFY(nick->isEmpty()); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // test with a string which becomes empty after sanitizing it: invalid, detail ignored jsonData.insert("nickname", invalidName); jsonContact.insert("details", jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactNickname::Type); QVERIFY(detail.isEmpty()); nick = static_cast(&detail); QVERIFY(nick->isEmpty()); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // note initializeJsonContact(jsonContact); jsonData.insert("note", QString("test note")); jsonContact.insert("details", jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactNote::Type); QVERIFY(!detail.isEmpty()); QContactNote* note = static_cast(&detail); QVERIFY(note != NULL); QCOMPARE(note->note(), QString("test note")); // cleanup contact.clearDetails(); jsonData = QJsonObject(); // test note with white spaces to be trimmed jsonData.insert("note", QString(" Chupacabra")); jsonContact.insert("details", jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactNote::Type); QVERIFY(!detail.isEmpty()); note = static_cast(&detail); QVERIFY(note != NULL); QCOMPARE(note->note(), QString("Chupacabra")); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // test invalid note (note is too long) QString tooLongNote; for (int i=0; i<10001; i++) { tooLongNote += 'a'; } jsonData.insert("note", tooLongNote); jsonContact.insert("details", jsonData); QVERIFY(!converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactNote::Type); QVERIFY(detail.isEmpty()); note = static_cast(&detail); QVERIFY(note->isEmpty()); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // test passing an empty string in the first place: allowed jsonData.insert("note", emptyName); jsonContact.insert("details", jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactNote::Type); QVERIFY(detail.isEmpty()); note = static_cast(&detail); QVERIFY(note->isEmpty()); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // test with a string which becomes empty after sanitizing it: invalid, detail ignored jsonData.insert("note", invalidName); jsonContact.insert("details", jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactNote::Type); QVERIFY(detail.isEmpty()); note = static_cast(&detail); QVERIFY(note->isEmpty()); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // email initializeJsonContact(jsonContact); jsonData.insert("value", QString("john@doe.com")); jsonData.insert("context", QString("home")); QJsonArray emails; emails.append(jsonData); jsonContact.insert("emails", emails); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactEmailAddress::Type); QVERIFY(!detail.isEmpty()); QContactEmailAddress* email = static_cast(&detail); QVERIFY(email != NULL); QCOMPARE(email->emailAddress(), QString("john@doe.com")); QVERIFY(email->contexts().size() == 1); QVERIFY(email->contexts()[0] == QContactEmailAddress::ContextHome); // cleanup contact.clearDetails(); jsonData = QJsonObject(); emails.removeFirst(); // test email with white spaces to be trimmed jsonData.insert("value", QString(" john@doe.com")); jsonData.insert("context", QString("home")); emails.append(jsonData); jsonContact.insert("emails", emails); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactEmailAddress::Type); QVERIFY(!detail.isEmpty()); email = static_cast(&detail); QVERIFY(email != NULL); QCOMPARE(email->emailAddress(), QString("john@doe.com")); QVERIFY(email->contexts().size() == 1); QVERIFY(email->contexts()[0] == QContactEmailAddress::ContextHome); // cleanup contact.clearDetails(); jsonData = QJsonObject(); emails.removeFirst(); // test invalid email (email is too long) QString tooLongEmail; for (int i=0; i<127; i++) { tooLongEmail += 'a'; } jsonData.insert("value", tooLongEmail); jsonData.insert("context", QString("home")); emails.append(jsonData); jsonContact.insert("emails", emails); QVERIFY(!converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactEmailAddress::Type); QVERIFY(detail.isEmpty()); email = static_cast(&detail); QVERIFY(email != NULL); QVERIFY(email->isEmpty()); // cleanup contact.clearDetails(); jsonData = QJsonObject(); emails.removeFirst(); // test passing an empty string in the first place: allowed jsonData.insert("value", emptyName); jsonData.insert("context", QString("home")); emails.append(jsonData); jsonContact.insert("emails", emails); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactEmailAddress::Type); QVERIFY(detail.isEmpty()); email = static_cast(&detail); QVERIFY(email->isEmpty()); // cleanup contact.clearDetails(); jsonData = QJsonObject(); emails.removeFirst(); // test with a string which becomes empty after sanitizing it: invalid, contact not saved jsonData.insert("value", invalidName); jsonData.insert("context", QString("home")); emails.append(jsonData); jsonContact.insert("emails", emails); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactEmailAddress::Type); QVERIFY(detail.isEmpty()); email = static_cast(&detail); QVERIFY(email->isEmpty()); // cleanup contact.clearDetails(); jsonData = QJsonObject(); emails.removeFirst(); // phone number initializeJsonContact(jsonContact); jsonData.insert("value", QString("0507654321")); jsonData.insert("subType", QString("cell")); QJsonArray phones; phones.append(jsonData); jsonContact.insert("phones", phones); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactPhoneNumber::Type); QVERIFY(!detail.isEmpty()); QContactPhoneNumber* phone = static_cast(&detail); QVERIFY(phone != NULL); QCOMPARE(phone->number(), QString("0507654321")); QVERIFY(phone->subTypes().size() == 1); QVERIFY(phone->subTypes()[0] == QContactPhoneNumber::SubTypeMobile); // cleanup contact.clearDetails(); phones = QJsonArray(); jsonData = QJsonObject(); //let's try with a phone number containing a few invalid characters jsonData.insert("value", QString(" +123(45678)90-+++abcdefghijklmnopqrstuvwxyz#*+++++ ")); jsonData.insert("subType", QString("cell")); phones.append(jsonData); jsonContact.insert("phones", phones); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactPhoneNumber::Type); QVERIFY(!detail.isEmpty()); phone = static_cast(&detail); QVERIFY(phone != NULL); QCOMPARE(phone->number(), QString("+123(45678)90-abcdpw#*")); QVERIFY(phone->subTypes().size() == 1); QVERIFY(phone->subTypes()[0] == QContactPhoneNumber::SubTypeMobile); // cleanup jsonData = QJsonObject(); phones = QJsonArray(); contact.clearDetails(); //let's try with an invalid phone number jsonData.insert("value", QString(" efghijk\"\"][[[]]] ")); jsonData.insert("subType", QString("cell")); phones.append(jsonData); jsonContact.insert("phones", phones); QVERIFY(!converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactPhoneNumber::Type); QVERIFY(detail.isEmpty()); phone = static_cast(&detail); QVERIFY(phone->isEmpty()); contact.clearDetails(); //yet another invalid phone number (too long) QString tooLongNumber; for (int i=0; i<100; i++) { tooLongNumber += QString::number(i/10); } jsonData = QJsonObject(); phones = QJsonArray(); jsonData.insert("value", tooLongNumber); phones.append(jsonData); jsonContact.insert("phones", phones); QVERIFY(!converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactPhoneNumber::Type); QVERIFY(detail.isEmpty()); phone = static_cast(&detail); QVERIFY(phone->isEmpty()); // cleanup jsonData = QJsonObject(); phones = QJsonArray(); contact.clearDetails(); //last but not least, empty phone number should be ignored QString emptyNumber; jsonData.insert("value", emptyNumber); phones.append(jsonData); jsonContact.insert("phones", phones); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); QVERIFY(contact.details(QContactPhoneNumber::Type).isEmpty()); phone = static_cast(&detail); QVERIFY(phone->isEmpty()); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // address initializeJsonContact(jsonContact); jsonData.insert("country", QString("Finland")); jsonData.insert("postOfficeBox", QString("347")); jsonData.insert("postcode", QString("33101")); jsonData.insert("locality", QString("Tampere")); jsonData.insert("region", QString("Pirkanmaa")); jsonData.insert("street", QString("PL 347")); QJsonArray addresses; addresses.append(jsonData); jsonContact.insert("addresses", addresses); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactAddress::Type); QVERIFY(!detail.isEmpty()); QContactAddress* addr = static_cast(&detail); QVERIFY(addr != NULL); QCOMPARE(addr->country(), QString("Finland")); QCOMPARE(addr->postOfficeBox(), QString("347")); QCOMPARE(addr->postcode(), QString("33101")); QCOMPARE(addr->locality(), QString("Tampere")); QCOMPARE(addr->region(), QString("Pirkanmaa")); QCOMPARE(addr->street(), QString("PL 347")); // cleanup contact.clearDetails(); jsonData = QJsonObject(); // test address with white spaces to be trimmed jsonData.insert("country", QString(" Finland2")); jsonData.insert("postOfficeBox", QString("347 ")); jsonData.insert("postcode", QString(" 33101 ")); jsonData.insert("locality", QString(" Tampere2 ")); jsonData.insert("region", QString(" Pirkanmaa2 ")); jsonData.insert("street", QString(" PL 347 ")); QJsonArray addressData1; addressData1.append(jsonData); jsonContact.insert("addresses", addressData1); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactAddress::Type); QVERIFY(!detail.isEmpty()); addr = static_cast(&detail); QVERIFY(addr != NULL); QCOMPARE(addr->country(), QString("Finland2")); QCOMPARE(addr->postOfficeBox(), QString("347")); QCOMPARE(addr->postcode(), QString("33101")); QCOMPARE(addr->locality(), QString("Tampere2")); QCOMPARE(addr->region(), QString("Pirkanmaa2")); QCOMPARE(addr->street(), QString("PL 347")); // cleanup contact.clearDetails(); jsonData = QJsonObject(); // test invalid address name (name is too long) jsonData.insert("country", QString("Vvvvvvvvvveeeeeeeeeerrrrrrrrrryyyyyyyyyylongnameeeeeeeeee")); QJsonArray addressData2; addressData2.append(jsonData); jsonContact.insert(QContactJsonDbStr::addressDefinitionName(), addressData2); QVERIFY(!converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactAddress::Type); QVERIFY(detail.isEmpty()); addr = static_cast(&detail); QVERIFY(org->isEmpty()); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // test passing an empty string in the first place: allowed jsonData.insert("country", emptyName); jsonContact.insert(QContactJsonDbStr::addressDefinitionName(), jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactAddress::Type); QVERIFY(detail.isEmpty()); addr = static_cast(&detail); QVERIFY(org->isEmpty()); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // test with a string which becomes empty after sanitizing it: invalid, contact not saved jsonData.insert("country", invalidName); QJsonArray addressData3; addressData3.append(jsonData); jsonContact.insert(QContactJsonDbStr::addressDefinitionName(), addressData3); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactAddress::Type); QVERIFY(detail.isEmpty()); addr = static_cast(&detail); QVERIFY(org->isEmpty()); // cleanup jsonData = QJsonObject(); contact.clearDetails(); // url initializeJsonContact(jsonContact); jsonData.insert("value", QString("http://www.acme.com")); QJsonArray urls; urls.append(jsonData); jsonContact.insert("urls", urls); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactUrl::Type); QVERIFY(!detail.isEmpty()); QContactUrl* url = static_cast(&detail); QVERIFY(url != NULL); QCOMPARE(url->url(), QString("http://www.acme.com")); // cleanup contact.clearDetails(); jsonData = QJsonObject(); // version initializeJsonContact(jsonContact); QString sequenceNumberString (QStringLiteral("1")); QString versionString (QStringLiteral("1234567890")); QString jsonVersion(sequenceNumberString + "-" + versionString); QByteArray expectedExtendedVersion(versionString.toLatin1()); jsonContact.insert("_version", jsonVersion); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactVersion::Type); QVERIFY(!detail.isEmpty()); QContactVersion* version = static_cast(&detail); QVERIFY(version != NULL); QCOMPARE(version->extendedVersion(), expectedExtendedVersion); QCOMPARE(version->sequenceNumber(), sequenceNumberString.toInt()); // cleanup contact.clearDetails(); jsonData = QJsonObject(); // extended detail with simple string data initializeJsonContact(jsonContact); jsonContact.insert("simpleStringDetail", QString("Simple string as detail data.")); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); QList extendedDetails = contact.details(); QCOMPARE(extendedDetails[0].name(), QString("simpleStringDetail")); QCOMPARE(extendedDetails[0].data().toString(), QString("Simple string as detail data.")); // cleanup contact.clearDetails(); jsonData = QJsonObject(); // extended detail with QJsonObject as data initializeJsonContact(jsonContact); jsonData.insert("Item1", QString("Content for list item 1.")); jsonData.insert("Item2", int(2)); jsonData.insert("Item3", QString("Content for list item 3.")); jsonData.insert("Item4", QString("Content for list item 4.")); jsonContact.insert("jsonObjectDetail", jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactExtendedDetail::Type); QContactExtendedDetail* extendedDetail = static_cast(&detail); QCOMPARE(extendedDetail->name(), QString("jsonObjectDetail")); QJsonObject extendedDetailItems = QJsonObject::fromVariantMap(extendedDetail->data().toMap()); QCOMPARE(extendedDetailItems.value("Item1").toString(), QString("Content for list item 1.")); QCOMPARE(extendedDetailItems.value("Item2").toVariant().toInt(), 2); QCOMPARE(extendedDetailItems.value("Item3").toString(), QString("Content for list item 3.")); QCOMPARE(extendedDetailItems.value("Item4").toString(), QString("Content for list item 4.")); // cleanup contact.clearDetails(); jsonData = QJsonObject(); // extended detail with QJsonArray as data initializeJsonContact(jsonContact); QStringList extendedDetailsStringList; extendedDetailsStringList<<"QStringInListFirst"<<"QStringInListSecond"; QJsonArray array = QJsonArray::fromStringList(extendedDetailsStringList); jsonContact.insert("jsonArrayDetail", array); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactExtendedDetail::Type); extendedDetail = static_cast(&detail); QCOMPARE(extendedDetail->name(), QString("jsonArrayDetail")); QJsonArray extendedDetailListItems = QJsonArray::fromVariantList(extendedDetail->data().toList()); QCOMPARE(extendedDetailListItems.count(),2); QCOMPARE(extendedDetailListItems.at(0).toString(), QString("QStringInListFirst")); QCOMPARE(extendedDetailListItems.at(1).toString(), QString("QStringInListSecond")); // cleanup contact.clearDetails(); jsonData = QJsonObject(); // extended detail with complex structure as data initializeJsonContact(jsonContact); QVariantMap variantMap; QJsonArray array2; QString expectedStringFromVariantList(QStringLiteral("QStringInVariantList")); int expectedIntFromVariantList = 5; array2.append(expectedStringFromVariantList); array2.append(expectedIntFromVariantList); variantMap.insert("ItemInt", 1); variantMap.insert("ItemQString", QString("QStringData")); variantMap.insert("ItemQVariant", QJsonValue( QString("QStringInQVariant")).toVariant()); variantMap.insert("ItemQVariantList", array2.toVariantList() ); jsonData.insert("QStringItem", QString("Content for QStringItem.")); jsonData.insert("arrayInMap", QJsonObject::fromVariantMap(variantMap)); jsonContact.insert("complexObjectDetail", jsonData); QVERIFY(converter.toQContact(jsonContact, &contact, m_partitionName)); detail = contact.detail(QContactExtendedDetail::Type); extendedDetail = static_cast(&detail); QCOMPARE(extendedDetail->name(), QString("complexObjectDetail")); extendedDetailItems = QJsonObject::fromVariantMap(extendedDetail->data().toMap()); QCOMPARE(extendedDetailItems.value("QStringItem").toString(), QString("Content for QStringItem.")); QJsonObject returnedObject = extendedDetailItems.value("arrayInMap").toObject(); QCOMPARE(returnedObject.value("ItemInt").toVariant().toInt(), 1); QCOMPARE(returnedObject.value("ItemQString").toString(), QString("QStringData")); QCOMPARE(returnedObject.value("ItemQVariant").toString(), QString("QStringInQVariant")); QCOMPARE(returnedObject.value("ItemQVariantList").toVariant().toList().size(), array2.size()); QCOMPARE(returnedObject.value("ItemQVariantList").toVariant().toList().at(0).toString(), expectedStringFromVariantList); QCOMPARE(returnedObject.value("ItemQVariantList").toVariant().toList().at(1).toInt(), expectedIntFromVariantList); // cleanup contact.clearDetails(); jsonData = QJsonObject(); } void tst_QcontactJsondbConverter::toQContactsTest() { QList contacts; // create bunch of contacts and convert those for(int i = 1; i < 10; ++i) { QJsonObject contact; QJsonObject phoneObject; QJsonObject emailObject; // add name field initializeJsonContact(contact, i); // add phone number phoneObject.insert("value", QString("050765432") + QString().setNum(i)); phoneObject.insert("subType", QString("home")); QJsonArray phones; phones.append(phoneObject); contact.insert("phones", phones); // add email address emailObject.insert("value", QString().setNum(i) + "john@doe.com"); emailObject.insert("subType", QString("home")); QJsonArray emails; emails.append(emailObject); contact.insert("emails", emails); contacts.append(contact); } QContactJsonDbConverter converter; QContactJsonDbEngine engine; QContactManager::Error error; QList qcontacts; QContact qcontact; QContactDetail detail; QString number; // convert QVERIFY(converter.toQContacts(contacts, qcontacts, error, m_partitionName)); QCOMPARE(error, QContactManager::NoError); QCOMPARE(qcontacts.size(), contacts.size()); int i = 1; foreach(qcontact, qcontacts) { number = QString().setNum(i++); detail = qcontact.detail(QContactName::Type); QVERIFY(!detail.isEmpty()); QContactName* name = static_cast(&detail); QVERIFY(name != NULL); QCOMPARE(name->firstName(), "John" + number); QCOMPARE(name->lastName(), "Doe" + number); QCOMPARE(name->middleName(), "Tom" + number); detail = qcontact.detail(QContactPhoneNumber::Type); QVERIFY(!detail.isEmpty()); QContactPhoneNumber* phone = static_cast(&detail); QVERIFY(phone != NULL); QCOMPARE(phone->number(), "050765432" + number); detail = qcontact.detail(QContactEmailAddress::Type); QVERIFY(!detail.isEmpty()); QContactEmailAddress* email = static_cast(&detail); QVERIFY(email != NULL); QCOMPARE(email->emailAddress(), number + "john@doe.com"); } } void tst_QcontactJsondbConverter::toJsonContactTest() { QContactJsonDbConverter converter; QJsonObject jsonContact; QContact contact; QMap testFields; // Test name conversion QContactName name; name.setFirstName("John"); name.setLastName("Doe"); name.setMiddleName("Tom"); contact.saveDetail(&name); QVERIFY(converter.toJsonContact(&jsonContact, contact)); testFields.insert("firstName", "John"); testFields.insert("lastName", "Doe"); testFields.insert("middleName", "Tom"); // test fields testJsonDetailItems(jsonContact, QContactJsonDbStr::nameDefinitionName(), testFields); // cleanup contact.clearDetails(); jsonContact = QJsonObject(); testFields.clear(); // Test Gender QContactGender gender; gender.setGender(QContactGender::GenderMale); contact.saveDetail(&gender); QVERIFY(converter.toJsonContact(&jsonContact, contact)); testFields.insert("gender", "male"); // test fields testJsonDetailItems(jsonContact, "gender", testFields); // cleanup contact.clearDetails(); jsonContact = QJsonObject(); testFields.clear(); // Test organization QContactOrganization organization; organization.setName("ACME"); organization.setDepartment(QStringList() << "Spy"); organization.setTitle("Vice President"); organization.setRole("Superhero"); organization.setAssistantName("Daisy Duck"); organization.setLogoUrl(QUrl("http://www.acme.com/logo.jpg")); QList OrganizationContextList; OrganizationContextList << QContactDetail::ContextWork; organization.setContexts(OrganizationContextList); contact.saveDetail(&organization); QVERIFY(converter.toJsonContact(&jsonContact, contact)); testFields.insert("name", "ACME"); testFields.insert("department", "Spy"); testFields.insert("title", "Vice President"); testFields.insert("role", "Superhero"); testFields.insert("assistantName", "Daisy Duck"); testFields.insert("logoUrl", "http://www.acme.com/logo.jpg"); testFields.insert("context", "work"); // test fields testJsonDetailItems(jsonContact, "organizations", testFields); // cleanup contact.clearDetails(); jsonContact = QJsonObject(); testFields.clear(); // Test contact details // birthday QContactBirthday birthday; birthday.setDate(QDate(1979, 11, 22)); contact.saveDetail(&birthday); QVERIFY(converter.toJsonContact(&jsonContact, contact)); testFields.insert("birthday", "1979-11-22"); // avatar QContactAvatar avatar; avatar.setImageUrl(QUrl("http://www.acme.com/logo.jpg")); contact.saveDetail(&avatar); testFields.insert("photoUrl", "http://www.acme.com/logo.jpg"); // ringtone QContactRingtone ringtone; ringtone.setAudioRingtoneUrl(QUrl("http://www.acme.com/ring.mp3")); contact.saveDetail(&ringtone); testFields.insert("ringtoneUrl", "http://www.acme.com/ring.mp3"); // nickname QContactNickname nick; nick.setNickname("Chupacabra"); contact.saveDetail(&nick); testFields.insert("nickname", "Chupacabra"); // note QContactNote note; note.setNote("test note"); contact.saveDetail(¬e); testFields.insert("note", "test note"); // convert QVERIFY(converter.toJsonContact(&jsonContact, contact)); // test fields testJsonDetailItems(jsonContact, "details", testFields); //cleanup contact.clearDetails(); jsonContact = QJsonObject(); testFields.clear(); // email QContactEmailAddress email; email.setEmailAddress("john@doe.com"); email.setContexts(QContactEmailAddress::ContextHome); contact.saveDetail(&email); QVERIFY(converter.toJsonContact(&jsonContact, contact)); testFields.insert("value", "john@doe.com"); testFields.insert("context", "home"); testJsonDetailItems(jsonContact, "emails", testFields); //cleanup contact.clearDetails(); jsonContact = QJsonObject(); testFields.clear(); // phone number QContactPhoneNumber number; number.setNumber("0507654321"); QList phoneNumberContextList; phoneNumberContextList << QContactPhoneNumber::ContextWork; number.setContexts(phoneNumberContextList); contact.saveDetail(&number); QVERIFY(converter.toJsonContact(&jsonContact, contact)); testFields.insert("value", "0507654321"); testFields.insert("context", "work"); testJsonDetailItems(jsonContact, "phones", testFields); //cleanup contact.clearDetails(); jsonContact = QJsonObject(); testFields.clear(); //let's try with a phone number containing a few invalid characters number.setNumber(" +123(45678)90-+++abcdefghijklmnopqrstuvwxyz#*+++++ "); number.setContexts(phoneNumberContextList); contact.saveDetail(&number); QVERIFY(converter.toJsonContact(&jsonContact, contact)); testFields.insert("value", "+123(45678)90-abcdpw#*"); testFields.insert("context", "work"); testJsonDetailItems(jsonContact, "phones", testFields); //cleanup contact.clearDetails(); jsonContact = QJsonObject(); testFields.clear(); //let's try with an invalid phone number number.setNumber(" efghijk\"\"][[[]]] "); number.setContexts(phoneNumberContextList); contact.saveDetail(&number); QVERIFY(!converter.toJsonContact(&jsonContact, contact)); contact.clearDetails(); //yet another invalid phone number (too long) QString tooLongNumber; for (int i=0; i<100; i++) { tooLongNumber += QString::number(i/10); } number.setNumber(tooLongNumber); contact.saveDetail(&number); QVERIFY(!converter.toJsonContact(&jsonContact, contact)); //cleanup contact.clearDetails(); jsonContact = QJsonObject(); testFields.clear(); //last but not least empty phone number should be ignored QString emptyNumber; number.setNumber(emptyNumber); contact.saveDetail(&number); QVERIFY(converter.toJsonContact(&jsonContact, contact)); QCOMPARE(jsonContact.value("phones").toArray().size(), 0); //cleanup contact.clearDetails(); jsonContact = QJsonObject(); testFields.clear(); // Test address conversion QContactAddress address; address.setCountry("Finland"); address.setPostcode("33101"); address.setStreet("PL 347"); address.setPostOfficeBox("347"); address.setRegion("Pirkanmaa"); address.setLocality("Tampere"); contact.saveDetail(&address); QVERIFY(converter.toJsonContact(&jsonContact, contact)); // test fields testFields.insert("country", "Finland"); testFields.insert("postOfficeBox", "347"); testFields.insert("postcode", "33101"); testFields.insert("locality", "Tampere"); testFields.insert("region", "Pirkanmaa"); testFields.insert("street", "PL 347"); // test fields testJsonDetailItems(jsonContact, "addresses", testFields); // cleanup contact.clearDetails(); jsonContact = QJsonObject(); testFields.clear(); // Test url conversion QContactUrl url; url.setUrl("http://www.acme.com"); contact.saveDetail(&url); QVERIFY(converter.toJsonContact(&jsonContact, contact)); // test fields testFields.insert("value", "http://www.acme.com"); testJsonDetailItems(jsonContact, "urls", testFields); // cleanup contact.clearDetails(); jsonContact = QJsonObject(); testFields.clear(); // Test version conversion QContactVersion version; QString sequenceNumberString (QStringLiteral("1")); QString versionString (QStringLiteral("1234567890")); QString expectedJsonVersion(sequenceNumberString + "-" + versionString); QByteArray expectedExtendedVersion(versionString.toLatin1()); version.setSequenceNumber(1); version.setExtendedVersion(expectedExtendedVersion); contact.saveDetail(&version); QVERIFY(converter.toJsonContact(&jsonContact, contact)); // test fields QCOMPARE((jsonContact.value(QContactJsonDbStr::version())).toString(), expectedJsonVersion); // cleanup contact.clearDetails(); // extended detail with a simple string as data. QContactExtendedDetail extendedDetail; extendedDetail.setName("extendedDetail1"); extendedDetail.setData("Simple data for an extened detail."); contact.saveDetail(&extendedDetail); QVERIFY(converter.toJsonContact(&jsonContact, contact)); // test size, type and actual field QCOMPARE((jsonContact.value(QContactJsonDbStr::type())).toString(), QContactJsonDbStr::contactsJsonDbType()); QCOMPARE((jsonContact.value("extendedDetail1")).toString(), QString("Simple data for an extened detail.")); // cleanup contact.clearDetails(); // extended detail with QJsonArray as data QContactExtendedDetail listExtendedDetail; listExtendedDetail.setName("listExtendedDetail"); QStringList stringList; stringList << "QStringInListFirst" <<"QStringInListSecond"; QJsonArray extendedDetailListItems = QJsonArray::fromStringList(stringList); listExtendedDetail.setData(extendedDetailListItems.toVariantList()); contact.saveDetail(&listExtendedDetail); QVERIFY(converter.toJsonContact(&jsonContact, contact)); QCOMPARE(jsonContact.value(QContactJsonDbStr::type()).toString(), QContactJsonDbStr::contactsJsonDbType()); QJsonArray convertedVariantList = jsonContact.value("listExtendedDetail").toArray(); QCOMPARE(convertedVariantList.count(),2); QCOMPARE(convertedVariantList.at(0).toString(), QString("QStringInListFirst")); QCOMPARE(convertedVariantList.at(1).toString(), QString("QStringInListSecond")); // cleanup contact.clearDetails(); // extended detail with a complex variantmap data QContactExtendedDetail complexExtendedDetail; complexExtendedDetail.setName("complexExtendedDetail"); QVariantMap variantMap; variantMap.insert("mapItemInt", 1); variantMap.insert("mapItemQString", QString("QStringItem")); variantMap.insert("mapItemVariant", QVariant(QString("QStringItemInVariant"))); variantMap.insert("mapItemVariantList", QVariantList() << QString("QStringItemInVariantList") << QVariant(5) << QString("AnotherQStringItemInVariantList")); complexExtendedDetail.setData(variantMap); contact.saveDetail(&complexExtendedDetail); initializeJsonContact(jsonContact); QVERIFY(converter.toJsonContact(&jsonContact, contact)); // test size, type and actual extended detail fields QCOMPARE(jsonContact.size(), 3); QCOMPARE(jsonContact.value(QContactJsonDbStr::type()).toString(), QContactJsonDbStr::contactsJsonDbType()); QJsonObject convertedObject = jsonContact.value("complexExtendedDetail").toObject(); QCOMPARE( convertedObject.value("mapItemInt").toVariant().toInt(), 1); QCOMPARE( convertedObject["mapItemQString"].toString(),QString("QStringItem")); QCOMPARE( convertedObject["mapItemVariant"].toString(),QString("QStringItemInVariant")); QJsonArray jsonArray = convertedObject.value("mapItemVariantList").toArray(); QCOMPARE( jsonArray.at(0).toString(), QString("QStringItemInVariantList")); QCOMPARE( jsonArray.at(1).toVariant().toInt(), 5); QCOMPARE( jsonArray.at(2).toString(), QString("AnotherQStringItemInVariantList")); // cleanup contact.clearDetails(); jsonContact = QJsonObject(); } void tst_QcontactJsondbConverter::updateContextsTest() { QContactJsonDbConverter converter; QContactJsonDbEngine engine; QStringList contexts; contexts << "home" << "work" << "other"; QList qcontexts; qcontexts << QContactDetail::ContextHome << QContactDetail::ContextWork << QContactDetail::ContextOther; QString context; QContactDetail qdetail; QJsonObject detail; int i = 0; // test every context foreach(context, contexts) { detail.insert("value", QString("0507654321")); detail.insert("context", context); QVERIFY(converter.updateContexts(detail, &qdetail)); QVERIFY(qdetail.contexts().size() == 1); QVERIFY(qdetail.contexts()[0] == qcontexts.at(i++)); } } void tst_QcontactJsondbConverter::queryFromRequestTest() { QContactJsonDbConverter converter; // ContactSaveRequest // Functionality still missing // ContactFetchRequest with different filters QContactFetchRequest request; // Contactdetail with field, without value QContactDetailFilter detailFilter; detailFilter.setDetailType(QContactName::Type); request.setFilter(detailFilter); QString jsonDbQuery; QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?name exists]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // Contactdetail with field and value detailFilter.setDetailType(QContactName::Type, QContactName::FieldFirstName); detailFilter.setValue("John"); request.setFilter(detailFilter); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?name.firstName=\"John\"]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // Contactdetail with matchflags // Contains flag detailFilter.setMatchFlags(QContactFilter::MatchContains); request.setFilter(detailFilter); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?name.firstName=~\"/*John*/wi\"]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // Exactly flag detailFilter.setMatchFlags(QContactFilter::MatchExactly); request.setFilter(detailFilter); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?name.firstName=\"John\"]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // Starts with flag detailFilter.setMatchFlags(QContactFilter::MatchStartsWith); request.setFilter(detailFilter); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?name.firstName=~\"/John*/wi\"]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // Ends with flag detailFilter.setMatchFlags(QContactFilter::MatchEndsWith); request.setFilter(detailFilter); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?name.firstName=~\"/*John/wi\"]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // CaseSensitive flag detailFilter.setMatchFlags(QContactFilter::MatchCaseSensitive); request.setFilter(detailFilter); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?name.firstName=~\"/John/w\"]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // FixedString flag detailFilter.setMatchFlags(QContactFilter::MatchFixedString); request.setFilter(detailFilter); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?name.firstName=~\"/John/wi/\"]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // Multiple flags "MatchFixedString and CaseSensitive" QContactFilter::MatchFlags flags; flags = QContactFilter::MatchFixedString; flags = flags | QContactFilter::MatchCaseSensitive; detailFilter.setMatchFlags(flags); request.setFilter(detailFilter); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?name.firstName=~\"/John/w/\"]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // Multiple flags "MatchFixedString and MatchStartsWith" flags = QContactFilter::MatchFixedString; flags = flags | QContactFilter::MatchStartsWith; detailFilter.setMatchFlags(flags); request.setFilter(detailFilter); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?name.firstName=~\"/John*/wi/\"]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // Multiple flags "MatchFixedString and MatchEndsWith" flags = QContactFilter::MatchFixedString; flags = flags | QContactFilter::MatchEndsWith; detailFilter.setMatchFlags(flags); request.setFilter(detailFilter); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?name.firstName=~\"/*John/wi/\"]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // Multiple flags "MatchFixedString and MatchContains" flags = QContactFilter::MatchFixedString; flags = flags | QContactFilter::MatchContains; detailFilter.setMatchFlags(flags); request.setFilter(detailFilter); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?name.firstName=~\"/*John*/wi/\"]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // Multiple flags "MatchFixedString, MatchStartsWith and MatchCaseSensitive" flags = QContactFilter::MatchFixedString; flags = flags | QContactFilter::MatchStartsWith; flags = flags | QContactFilter::MatchCaseSensitive; detailFilter.setMatchFlags(flags); request.setFilter(detailFilter); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?name.firstName=~\"/John*/w/\"]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // Multiple flags "Starts with and CaseSensitive" flags = QContactFilter::MatchStartsWith; flags = flags | QContactFilter::MatchCaseSensitive; detailFilter.setMatchFlags(flags); request.setFilter(detailFilter); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?name.firstName=~\"/John*/w\"]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // Multiple flags "Ends with and CaseSensitive" flags = QContactFilter::MatchEndsWith; flags = flags | QContactFilter::MatchCaseSensitive; detailFilter.setMatchFlags(flags); request.setFilter(detailFilter); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?name.firstName=~\"/*John/w\"]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // Multiple flags "Contains and CaseSensitive" flags = QContactFilter::MatchContains; flags = flags | QContactFilter::MatchCaseSensitive; detailFilter.setMatchFlags(flags); request.setFilter(detailFilter); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?name.firstName=~\"/*John*/w\"]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // Detail range filtering // Functionality still missing // contactId filtering QContactIdFilter idFilter; QUuid filterUuid("123"); QString expectedUuid = filterUuid.toString(); QContactJsonDbId *engineId = new QContactJsonDbId(filterUuid, QContactAbstractRequest::UserDataStorage); QContactId testId (engineId); QList ids; ids.append(testId); idFilter.setIds(ids); request.setFilter(idFilter); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?_uuid in [\"" + expectedUuid + "\"]]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // Sortings QContactSortOrder sort; // Sort by firstname, descending sort.setDetailType(QContactName::Type, QContactName::FieldFirstName); sort.setDirection(Qt::DescendingOrder); request.setSorting(QList() << sort); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?_uuid in [\"" + expectedUuid + "\"]][\\name.firstName]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); // Sort by lastname, ascending sort.setDetailType(QContactName::Type, QContactName::FieldLastName); sort.setDirection(Qt::AscendingOrder); request.setSorting(QList() << sort); QVERIFY(converter.queryFromRequest(&request,jsonDbQuery)); QCOMPARE(jsonDbQuery, QString("[?_type=\"%1\"][?_uuid in [\"" + expectedUuid + "\"]][/name.lastName]").arg(QContactJsonDbStr::contactsJsonDbType())); jsonDbQuery.clear(); } void tst_QcontactJsondbConverter::convertCompoundFilterTest() { QContactJsonDbConverter converter; // prepare ContactId filter QContactIdFilter idFilter; QUuid filterUuid("123"); QString expectedUuid = filterUuid.toString(); QContactJsonDbId *engineId = new QContactJsonDbId(filterUuid, QContactAbstractRequest::UserDataStorage); QContactId testId (engineId); QList ids; ids.append(testId); idFilter.setIds(ids); //prepare detail filter QContactDetailFilter detailFilter; detailFilter.setDetailType(QContactName::Type, QContactName::FieldFirstName); detailFilter.setValue("John"); //prepare Intersection of contactIdFilter and detailFilter QContactIntersectionFilter isf; isf.append(detailFilter); isf.append(idFilter); // now convert the Intersection Filter to Jsondb query QContactFilter filter(isf); QString filterStr; QVERIFY(converter.compoundFilterToJsondbQuery(filter,filterStr)); QCOMPARE(filterStr, QString("[?name.firstName=\"John\"][?_uuid in [\"" + expectedUuid + "\"]]")); filterStr.clear(); } void tst_QcontactJsondbConverter::convertSortOrderTest() { // Functionality still missing } void tst_QcontactJsondbConverter::contactIdToUuidTest() { QContactJsonDbConverter converter; QUuid uuid("123"); QContactJsonDbId *engineId = new QContactJsonDbId(uuid, QContactAbstractRequest::UserDataStorage); QContactId qid(engineId); QString jsonUuid; jsonUuid = converter.contactIdToUuid(qid); QCOMPARE(jsonUuid, uuid.toString()); } void tst_QcontactJsondbConverter::testJsonDetailItems(const QJsonObject& values, const QString& extractField, const QMap& fields, bool testSize) { // result should be type field and value field QCOMPARE(values.size(), 2); QCOMPARE(values.value(QContactJsonDbStr::type()).toString(), QContactJsonDbStr::contactsJsonDbType()); // extract data values QJsonObject jsonTmp; if (values.value(extractField).isArray()) { QJsonArray array = values.value(extractField).toArray(); jsonTmp = array[0].toObject(); } else { jsonTmp = values.value(extractField).toObject(); } // extract data should have as many fields as compared data if(testSize) QCOMPARE(jsonTmp.size(), fields.size()); QMapIterator i(fields); while(i.hasNext()) { i.next(); QCOMPARE(jsonTmp.value(i.key()).toString(), i.value()); } } void tst_QcontactJsondbConverter::initializeJsonContact(QJsonObject& jsonContact, unsigned int numbering) { jsonContact = QJsonObject(); jsonContact.insert(QContactJsonDbStr::type(), QContactJsonDbStr::contactsJsonDbType()); jsonContact.insert(QContactJsonDbStr::uuid(), QString("123")); QJsonObject jsonData; QString number(""); if(numbering > 0) { number = QString().setNum(numbering); } jsonData.insert("firstName", QString("John" + number)); jsonData.insert("lastName", QString("Doe" + number)); jsonData.insert("middleName", QString("Tom" + number)); jsonContact.insert(QContactJsonDbStr::nameDefinitionName(), jsonData); } QTEST_APPLESS_MAIN(tst_QcontactJsondbConverter); #include "tst_qcontactjsondbconverter.moc" tests/auto/contacts/qcontactjsondbstoragelocations/000077500000000000000000000000001233466112000233215ustar00rootroot00000000000000tests/auto/contacts/qcontactjsondbstoragelocations/missingstoragelocations/000077500000000000000000000000001233466112000302735ustar00rootroot00000000000000missingstoragelocations.pro000066400000000000000000000001431233466112000357060ustar00rootroot00000000000000tests/auto/contacts/qcontactjsondbstoragelocations/missingstoragelocationsTEMPLATE = subdirs qtHaveModule(jsondb): SUBDIRS += nosystemstorage nouserdatastorage nostorages tests/auto/contacts/qcontactjsondbstoragelocations/missingstoragelocations/nostorages/000077500000000000000000000000001233466112000324575ustar00rootroot00000000000000tests/auto/contacts/qcontactjsondbstoragelocations/missingstoragelocations/nostorages/nostorages.pro000066400000000000000000000011221233466112000353610ustar00rootroot00000000000000include(../../../../auto.pri) TARGET = tst_nostorages QT += contacts qtHaveModule(jsondb): QT += jsondb SOURCES += tst_nostorages.cpp \ ../tst_missingstorage.cpp \ ../../../../../../src/plugins/contacts/jsondb/qcontactjsondbid.cpp HEADERS += ../tst_missingstorage.h \ ../../../../jsondbprocess.h \ ../../../../../../src/plugins/contacts/jsondb/qcontactjsondbid.h INCLUDEPATH += .. INCLUDEPATH += ../../../.. INCLUDEPATH += ../../../../../../src/plugins/contacts/jsondb OTHER_FILES += partitions.json DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 partitions.json000066400000000000000000000000041233466112000354610ustar00rootroot00000000000000tests/auto/contacts/qcontactjsondbstoragelocations/missingstoragelocations/nostorages[ ] tst_nostorages.cpp000066400000000000000000000165131233466112000361700ustar00rootroot00000000000000tests/auto/contacts/qcontactjsondbstoragelocations/missingstoragelocations/nostorages/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ // This module implements no storage locations related test data for a set of // test cases shared with other similar modules for cases of no userdata // storage and no system storage. #include "tst_missingstorage.h" Q_DECLARE_METATYPE(QContactAbstractRequest::StorageLocations) Q_DECLARE_METATYPE(QContactManager::Error) tst_StoragesLocationsMissing::tst_StoragesLocationsMissing() { QString partitions_json = QFINDTESTDATA("partitions.json"); QVERIFY2(!partitions_json.isEmpty(), "partitions.json file is missing"); QVERIFY2(m_jsondbProcess.start(partitions_json), "Failed to start JsonDb process"); } tst_StoragesLocationsMissing::~tst_StoragesLocationsMissing() { m_jsondbProcess.terminate(); } void tst_StoragesLocationsMissing::init() { // Nothing to init for tests in this module. } void tst_StoragesLocationsMissing::fetchData() { QTest::addColumn("storageLocations"); QTest::addColumn("expectedError"); QTest::newRow("from user") << int(QContactAbstractRequest::UserDataStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from system") << int(QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from both") << int(QContactAbstractRequest::SystemStorage | QContactAbstractRequest::UserDataStorage) << QContactManager::MissingPlatformRequirementsError; QTest::newRow("from invalid") << (0x8) << QContactManager::InvalidStorageLocationError; QTest::newRow("from invalid with user") << int(0x8 | QContactAbstractRequest::UserDataStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from invalid with system") << int(0x8 | QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; } void tst_StoragesLocationsMissing::idData() { QTest::addColumn("jsonId"); QTest::addColumn("storageLocation"); QTest::addColumn("expectedError"); QTest::newRow("from user") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << int(QContactAbstractRequest::UserDataStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from system") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << int(QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from invalid") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << (0x8) << QContactManager::InvalidStorageLocationError; QTest::newRow("with empty id") << QString() << (0x0) << QContactManager::InvalidStorageLocationError; } void tst_StoragesLocationsMissing::idsData() { QTest::addColumn("jsonId"); QTest::addColumn("firstStorageLocation"); QTest::addColumn("secondStorageLocation"); QTest::addColumn("expectedError"); QTest::newRow("from user") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << int(QContactAbstractRequest::UserDataStorage) << int(QContactAbstractRequest::UserDataStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from system") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << int(QContactAbstractRequest::SystemStorage) << int(QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from both") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << int(QContactAbstractRequest::UserDataStorage) << int(QContactAbstractRequest::SystemStorage) << QContactManager::MissingPlatformRequirementsError; QTest::newRow("from invalid") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << (0x8) << int(QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("with empty id") << QString() << int(QContactAbstractRequest::UserDataStorage) << int(QContactAbstractRequest::SystemStorage) << QContactManager::MissingPlatformRequirementsError; } void tst_StoragesLocationsMissing::saveData() { QTest::addColumn("storageLocation"); QTest::addColumn("expectedError"); QTest::newRow("to user") << int(QContactAbstractRequest::UserDataStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("to system") << int(QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("to invalid") << (0x8) << QContactManager::InvalidStorageLocationError; } tests/auto/contacts/qcontactjsondbstoragelocations/missingstoragelocations/nosystemstorage/000077500000000000000000000000001233466112000335415ustar00rootroot00000000000000nosystemstorage.pro000066400000000000000000000011321233466112000374470ustar00rootroot00000000000000tests/auto/contacts/qcontactjsondbstoragelocations/missingstoragelocations/nosystemstorageinclude(../../../../auto.pri) TARGET = tst_nosystemstorage QT += contacts qtHaveModule(jsondb): QT += jsondb SOURCES += tst_nosystemstorage.cpp \ ../tst_missingstorage.cpp \ ../../../../../../src/plugins/contacts/jsondb/qcontactjsondbid.cpp HEADERS += ../tst_missingstorage.h \ ../../../../jsondbprocess.h \ ../../../../../../src/plugins/contacts/jsondb/qcontactjsondbid.h INCLUDEPATH += .. INCLUDEPATH += ../../../.. INCLUDEPATH += ../../../../../../src/plugins/contacts/jsondb OTHER_FILES += partitions.json DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 partitions.json000066400000000000000000000000511233466112000365450ustar00rootroot00000000000000tests/auto/contacts/qcontactjsondbstoragelocations/missingstoragelocations/nosystemstorage[ { "name" : "com.nokia.mt.User" } ] tst_nosystemstorage.cpp000066400000000000000000000173431233466112000403360ustar00rootroot00000000000000tests/auto/contacts/qcontactjsondbstoragelocations/missingstoragelocations/nosystemstorage/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ // This module implements no system storage location related test data for a // set of tests shared with other similar modules for cases of no userdata // storage and no storages at all. #include "tst_missingstorage.h" Q_DECLARE_METATYPE(QContactAbstractRequest::StorageLocations) Q_DECLARE_METATYPE(QContactManager::Error) tst_StoragesLocationsMissing::tst_StoragesLocationsMissing() { QString partitions_json = QFINDTESTDATA("partitions.json"); QVERIFY2(!partitions_json.isEmpty(), "partitions.json file is missing"); QVERIFY2(m_jsondbProcess.start(partitions_json), "Failed to start JsonDb process"); } tst_StoragesLocationsMissing::~tst_StoragesLocationsMissing() { m_jsondbProcess.terminate(); } void tst_StoragesLocationsMissing::init() { // Make sure all tests in this test case start with an empty userdata storage. QContactIdFetchRequest idFetchRequest; idFetchRequest.setManager(m_contactManager); idFetchRequest.start(); idFetchRequest.waitForFinished(); if (!idFetchRequest.ids().isEmpty()) { QContactRemoveRequest removeRequest; removeRequest.setManager(m_contactManager); removeRequest.setContactIds(idFetchRequest.ids()); removeRequest.start(); removeRequest.waitForFinished(); } } void tst_StoragesLocationsMissing::fetchData() { QTest::addColumn("storageLocations"); QTest::addColumn("expectedError"); QTest::newRow("from user") << int(QContactAbstractRequest::UserDataStorage) << QContactManager::DoesNotExistError; QTest::newRow("from system") << int(QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from both") << int(QContactAbstractRequest::SystemStorage | QContactAbstractRequest::UserDataStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from invalid") << (0x8) << QContactManager::InvalidStorageLocationError; QTest::newRow("from invalid with user") << int(0x8 | QContactAbstractRequest::UserDataStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from invalid with system") << int(0x8 | QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; } void tst_StoragesLocationsMissing::idData() { QTest::addColumn("jsonId"); QTest::addColumn("storageLocation"); QTest::addColumn("expectedError"); QTest::newRow("from user") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << int(QContactAbstractRequest::UserDataStorage) << QContactManager::DoesNotExistError; QTest::newRow("from system") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << int(QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from invalid") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << (0x8) << QContactManager::InvalidStorageLocationError; QTest::newRow("with empty id") << QString() << (0x0) << QContactManager::InvalidStorageLocationError; } void tst_StoragesLocationsMissing::idsData() { QTest::addColumn("jsonId"); QTest::addColumn("firstStorageLocation"); QTest::addColumn("secondStorageLocation"); QTest::addColumn("expectedError"); QTest::newRow("from user") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << int(QContactAbstractRequest::UserDataStorage) << int(QContactAbstractRequest::UserDataStorage) << QContactManager::DoesNotExistError; QTest::newRow("from system") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << int(QContactAbstractRequest::SystemStorage) << int(QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from both") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << int(QContactAbstractRequest::UserDataStorage) << int(QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from invalid") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << (0x8) << int(QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("with empty id") << QString() << int(QContactAbstractRequest::UserDataStorage) << int(QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; } void tst_StoragesLocationsMissing::saveData() { QTest::addColumn("storageLocation"); QTest::addColumn("expectedError"); QTest::newRow("to user") << int(QContactAbstractRequest::UserDataStorage) << QContactManager::NoError; QTest::newRow("to system") << int(QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("to invalid") << (0x8) << QContactManager::InvalidStorageLocationError; } tests/auto/contacts/qcontactjsondbstoragelocations/missingstoragelocations/nouserdatastorage/000077500000000000000000000000001233466112000340255ustar00rootroot00000000000000nouserdatastorage.pro000066400000000000000000000011361233466112000402230ustar00rootroot00000000000000tests/auto/contacts/qcontactjsondbstoragelocations/missingstoragelocations/nouserdatastorageinclude(../../../../auto.pri) TARGET = tst_nouserdatastorage QT += contacts qtHaveModule(jsondb): QT += jsondb SOURCES += tst_nouserdatastorage.cpp \ ../tst_missingstorage.cpp \ ../../../../../../src/plugins/contacts/jsondb/qcontactjsondbid.cpp HEADERS += ../tst_missingstorage.h \ ../../../../jsondbprocess.h \ ../../../../../../src/plugins/contacts/jsondb/qcontactjsondbid.h INCLUDEPATH += .. INCLUDEPATH += ../../../.. INCLUDEPATH += ../../../../../../src/plugins/contacts/jsondb OTHER_FILES += partitions.json DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 partitions.json000066400000000000000000000000531233466112000370330ustar00rootroot00000000000000tests/auto/contacts/qcontactjsondbstoragelocations/missingstoragelocations/nouserdatastorage[ { "name" : "com.nokia.mt.System" } ] tst_nouserdatastorage.cpp000066400000000000000000000202201233466112000410720ustar00rootroot00000000000000tests/auto/contacts/qcontactjsondbstoragelocations/missingstoragelocations/nouserdatastorage/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ // This module implements no userdata storage location related test data for // a set of tests shared with other similar modules for cases of no system // storage and no storages at all. #include "tst_missingstorage.h" Q_DECLARE_METATYPE(QContactAbstractRequest::StorageLocations) Q_DECLARE_METATYPE(QContactManager::Error) tst_StoragesLocationsMissing::tst_StoragesLocationsMissing() { QString partitions_json = QFINDTESTDATA("partitions.json"); QVERIFY2(!partitions_json.isEmpty(), "partitions.json file is missing"); QVERIFY2(m_jsondbProcess.start(partitions_json), "Failed to start JsonDb process"); } tst_StoragesLocationsMissing::~tst_StoragesLocationsMissing() { m_jsondbProcess.terminate(); } void tst_StoragesLocationsMissing::init() { // Make sure all tests in this test case start with an empty system storage. QContactIdFetchRequest idFetchRequest; idFetchRequest.setManager(m_contactManager); idFetchRequest.setStorageLocations(QContactAbstractRequest::SystemStorage); idFetchRequest.start(); idFetchRequest.waitForFinished(); if (!idFetchRequest.ids().isEmpty()) { QContactRemoveRequest removeRequest; removeRequest.setManager(m_contactManager); removeRequest.setContactIds(idFetchRequest.ids()); removeRequest.start(); removeRequest.waitForFinished(); } } void tst_StoragesLocationsMissing::fetchData() { QTest::addColumn("storageLocations"); QTest::addColumn("expectedError"); QTest::newRow("from user") << int(QContactAbstractRequest::UserDataStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from system") << int(QContactAbstractRequest::SystemStorage) << QContactManager::DoesNotExistError; QTest::newRow("from both") << int(QContactAbstractRequest::SystemStorage | QContactAbstractRequest::UserDataStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from invalid") << (0x8) << QContactManager::InvalidStorageLocationError; QTest::newRow("from invalid with user") << int(0x8 | QContactAbstractRequest::UserDataStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from invalid with system") << int(0x8 | QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; } void tst_StoragesLocationsMissing::idData() { QTest::addColumn("jsonId"); QTest::addColumn("storageLocation"); QTest::addColumn("expectedError"); QTest::newRow("from user") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << int(QContactAbstractRequest::UserDataStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from system") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << int(QContactAbstractRequest::SystemStorage) << QContactManager::DoesNotExistError; QTest::newRow("from invalid") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << (0x8) << QContactManager::InvalidStorageLocationError; QTest::newRow("with empty id") << QString() << (0x0) << QContactManager::InvalidStorageLocationError; } void tst_StoragesLocationsMissing::idsData() { QTest::addColumn("jsonId"); QTest::addColumn("firstStorageLocation"); QTest::addColumn("secondStorageLocation"); QTest::addColumn("expectedError"); QTest::newRow("from user") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << int(QContactAbstractRequest::UserDataStorage) << int(QContactAbstractRequest::UserDataStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from system") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << int(QContactAbstractRequest::SystemStorage) << int(QContactAbstractRequest::SystemStorage) << QContactManager::DoesNotExistError; QTest::newRow("from user and system") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << int(QContactAbstractRequest::UserDataStorage) << int(QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from invalid with user") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << (0x8) << int(QContactAbstractRequest::UserDataStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from invalid with system") << QString("a096c227-fc6f-46ad-89b8-1883fb490a38") << (0x8) << int(QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("from user and system with an empty id") << QString() << int(QContactAbstractRequest::UserDataStorage) << int(QContactAbstractRequest::SystemStorage) << QContactManager::InvalidStorageLocationError; } void tst_StoragesLocationsMissing::saveData() { QTest::addColumn("storageLocation"); QTest::addColumn("expectedError"); QTest::newRow("to user") << int(QContactAbstractRequest::UserDataStorage) << QContactManager::InvalidStorageLocationError; QTest::newRow("to system") << int(QContactAbstractRequest::SystemStorage) << QContactManager::NoError; QTest::newRow("to invalid") << (0x8) << QContactManager::InvalidStorageLocationError; } tests/auto/contacts/qcontactjsondbstoragelocations/missingstoragelocations/tst_missingstorage.cpp000066400000000000000000000156541233466112000347420ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "tst_missingstorage.h" Q_DECLARE_METATYPE(QContactAbstractRequest::StorageLocations) Q_DECLARE_METATYPE(QContactManager::Error) void tst_StoragesLocationsMissing::initTestCase() { m_contactManager = new QContactManager("jsondb"); } void tst_StoragesLocationsMissing::cleanupTestCase() { if (m_contactManager) delete m_contactManager; } QContactManager::Error tst_StoragesLocationsMissing::errorFromManager(QContactAbstractRequest &request, QContactManager *cm) { request.setManager(cm); request.start(); request.waitForFinished(); request.isFinished(); return request.error(); } void tst_StoragesLocationsMissing::fetch() { QFETCH(int, storageLocations); QFETCH(QContactManager::Error, expectedError); QContactManager *cm = new QContactManager("jsondb"); QContactFetchRequest request; request.setStorageLocations(QContactAbstractRequest::StorageLocations(storageLocations)); QCOMPARE(errorFromManager(request, cm), expectedError); delete cm; } void tst_StoragesLocationsMissing::idFetch() { QFETCH(int, storageLocations); QFETCH(QContactManager::Error, expectedError); QContactManager *cm = new QContactManager("jsondb"); QContactIdFetchRequest request; request.setStorageLocations(QContactAbstractRequest::StorageLocations(storageLocations)); QCOMPARE(errorFromManager(request, cm), expectedError); delete cm; } void tst_StoragesLocationsMissing::fetchById() { QFETCH(QString, jsonId); QFETCH(int, storageLocation); QFETCH(QContactManager::Error, expectedError); QContactManager *cm = new QContactManager("jsondb"); QList contactIds; QContactEngineId *engineId = new QContactJsonDbId(jsonId , QContactAbstractRequest::StorageLocation(storageLocation)); contactIds.append(QContactId(engineId)); // QContactId takes the ownership of the pointer. QContactFetchByIdRequest request; request.setIds(contactIds); QCOMPARE(errorFromManager(request, cm), expectedError); delete cm; } void tst_StoragesLocationsMissing::fetchByIds() { QFETCH(QString, jsonId); QFETCH(int, firstStorageLocation); QFETCH(int, secondStorageLocation); QFETCH(QContactManager::Error, expectedError); QContactManager *cm = new QContactManager("jsondb"); QContactEngineId *firstEngineId = new QContactJsonDbId(jsonId , QContactAbstractRequest::StorageLocation(firstStorageLocation)); QContactEngineId *secondEngineId = new QContactJsonDbId(jsonId , QContactAbstractRequest::StorageLocation(secondStorageLocation)); QList contactIds; contactIds.append(QContactId(firstEngineId)); // QContactId takes the ownership of the pointer. contactIds.append(QContactId(secondEngineId)); QContactFetchByIdRequest request; request.setIds(contactIds); QCOMPARE(errorFromManager(request, cm), expectedError); delete cm; } void tst_StoragesLocationsMissing::removeById() { QFETCH(QString, jsonId); QFETCH(int, storageLocation); QFETCH(QContactManager::Error, expectedError); QContactManager *cm = new QContactManager("jsondb"); QContactEngineId *engineId = new QContactJsonDbId(jsonId , QContactAbstractRequest::StorageLocation(storageLocation)); QList contactIds; contactIds.append(QContactId(engineId)); // QContactId takes the ownership of the pointer. QContactRemoveRequest request; request.setContactIds(contactIds); QCOMPARE(errorFromManager(request, cm), expectedError); delete cm; } void tst_StoragesLocationsMissing::removeByIds() { QFETCH(QString, jsonId); QFETCH(int, firstStorageLocation); QFETCH(int, secondStorageLocation); QFETCH(QContactManager::Error, expectedError); QContactManager *cm = new QContactManager("jsondb"); QContactEngineId *firstEngineId = new QContactJsonDbId(jsonId , QContactAbstractRequest::StorageLocation(firstStorageLocation)); QContactEngineId *secondEngineId = new QContactJsonDbId(jsonId , QContactAbstractRequest::StorageLocation(secondStorageLocation)); QList contactIds; contactIds.append(QContactId(firstEngineId)); // QContactId takes the ownership of the pointer. contactIds.append(QContactId(secondEngineId)); QContactRemoveRequest request; request.setContactIds(contactIds); QCOMPARE(errorFromManager(request, cm), expectedError); delete cm; } QList tst_StoragesLocationsMissing::makeTestContactsForSave() { QList contacts; contacts << QContact(); return contacts; } void tst_StoragesLocationsMissing::save() { QFETCH(int, storageLocation); QFETCH(QContactManager::Error, expectedError); QContactManager *cm = new QContactManager("jsondb"); QList testContacts = makeTestContactsForSave(); QContactSaveRequest request; request.setContacts(testContacts); request.setStorageLocation(QContactAbstractRequest::StorageLocation(storageLocation)); QCOMPARE(errorFromManager(request, cm), expectedError); delete cm; } QTEST_MAIN(tst_StoragesLocationsMissing) tests/auto/contacts/qcontactjsondbstoragelocations/missingstoragelocations/tst_missingstorage.h000066400000000000000000000066561233466112000344110ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef TST_MISSINGSTORAGE_H #define TST_MISSINGSTORAGE_H #include #include #include #include #include #include #include #include #include #include #include "qcontactjsondbid.h" #include "jsondbprocess.h" QTCONTACTS_USE_NAMESPACE class tst_StoragesLocationsMissing : public QObject { Q_OBJECT public: tst_StoragesLocationsMissing(); virtual ~tst_StoragesLocationsMissing(); public slots: void initTestCase(); void cleanupTestCase(); void init(); private slots: void fetch(); void fetch_data() {fetchData();} void idFetch(); void idFetch_data() {fetchData();} void fetchById(); void fetchById_data() {idData();} void fetchByIds(); void fetchByIds_data() {idsData();} void removeById(); void removeById_data() {idData();} void removeByIds(); void removeByIds_data() {idsData();} void save(); void save_data() {saveData();} private: void fetchData(); void idData(); void idsData(); void saveData(); QContactManager::Error errorFromManager(QContactAbstractRequest &request, QContactManager *cm); QList makeTestContactsForSave(); QContactManager *m_contactManager; JsonDbProcess m_jsondbProcess; }; #endif tests/auto/contacts/qcontactjsondbstoragelocations/qcontactjsondbstoragelocations.pro000066400000000000000000000001151233466112000323550ustar00rootroot00000000000000TEMPLATE = subdirs qtHaveModule(jsondb): SUBDIRS += missingstoragelocations tests/auto/contacts/qcontactmanager/000077500000000000000000000000001233466112000201535ustar00rootroot00000000000000tests/auto/contacts/qcontactmanager/lazy.json000066400000000000000000000000031233466112000220160ustar00rootroot00000000000000{} tests/auto/contacts/qcontactmanager/partitions.json000066400000000000000000000001211233466112000232340ustar00rootroot00000000000000[ { "name" : "com.nokia.mt.User" }, { "name" : "com.nokia.mt.System" } ] tests/auto/contacts/qcontactmanager/qcontactmanager.pro000066400000000000000000000005511233466112000240450ustar00rootroot00000000000000include(../../auto.pri) QT += contacts versit contacts-private qtHaveModule(jsondb) { QT += jsondb } SOURCES += tst_qcontactmanager.cpp HEADERS += ../qcontactmanagerdataholder.h HEADERS += ../../jsondbprocess.h \ ../qcontactidmock.h OTHER_FILES += lazy.json INCLUDEPATH += .. \ ../.. DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactmanager/tst_qcontactmanager.cpp000066400000000000000000004330721233466112000247310ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #define QT_STATICPLUGIN #include #include #include #include #include "qcontactmanagerdataholder.h" #include "jsondbprocess.h" #include "qcontactidmock.h" #if defined(USE_VERSIT_PLZ) // This makes it easier to create specific QContacts #include #include #include QTVERSIT_USE_NAMESPACE #endif QTCONTACTS_USE_NAMESPACE #define QCONTACTMANAGER_REMOVE_VERSIONS_FROM_URI(params) params.remove(QString::fromLatin1(QTCONTACTS_VERSION_NAME)); \ params.remove(QString::fromLatin1(QTCONTACTS_IMPLEMENTATION_VERSION_NAME)) #define QTRY_COMPARE_SIGNALS_LOCALID_COUNT(__signalSpy, __expectedCount) \ do { \ int __spiedSigCount = 0; \ const int __step = 50; \ const int __timeout = 5000; \ for (int __i = 0; __i < __timeout; __i+=__step) { \ /* accumulate added from signals */ \ __spiedSigCount = 0; \ const QList > __spiedSignals = __signalSpy; \ foreach (const QList &__arguments, __spiedSignals) { \ foreach (QContactId __localId, __arguments.first().value >()) { \ QVERIFY(!(__localId.isNull())); \ __spiedSigCount++; \ } \ } \ if(__spiedSigCount == __expectedCount) { \ break; \ } \ QTest::qWait(__step); \ } \ QCOMPARE(__spiedSigCount, __expectedCount); \ } while(0) //TESTED_COMPONENT=src/contacts // to get QFETCH to work with the template expression... typedef QMap tst_QContactManager_QStringMap; Q_DECLARE_METATYPE(tst_QContactManager_QStringMap) Q_DECLARE_METATYPE(QList) /* A class that no backend can support */ class UnsupportedMetatype { int foo; }; Q_DECLARE_METATYPE(UnsupportedMetatype) Q_DECLARE_METATYPE(QContact) Q_DECLARE_METATYPE(QContactManager::Error) Q_DECLARE_METATYPE(Qt::CaseSensitivity) class tst_QContactManager : public QObject { Q_OBJECT public: tst_QContactManager(); virtual ~tst_QContactManager(); private: void dumpContactDifferences(const QContact& a, const QContact& b); void dumpContact(const QContact &c); void dumpContacts(QContactManager *cm); bool isSuperset(const QContact& ca, const QContact& cb); QList removeAllDefaultDetails(const QList& details); void addManagers(); // add standard managers to the data QContact createContact(QString firstName, QString lastName, QString phoneNumber); void saveContactName(QContact *contact,QContactName *contactName, const QString &name) const; JsonDbProcess m_jsondbProcess; QScopedPointer managerDataHolder; public slots: void initTestCase(); void cleanupTestCase(); private slots: void doDump(); void doDump_data() {addManagers();} /* Special test with special data */ void uriParsing(); void nameSynthesis(); /* Tests that are run on all managers */ void metadata(); void nullIdOperations(); void add(); void update(); void remove(); void addAndUpdate(); void batch(); void observerDeletion(); void signalEmission(); void actionPreferences(); void selfContactId(); void detailOrders(); void relationships(); void contactType(); void lateDeletion(); void compareVariant(); #if defined(USE_VERSIT_PLZ) void partialSave(); void partialSave_data() {addManagers();} #endif /* Tests that take no data */ void errorStayingPut(); void ctors(); void invalidManager(); void memoryManager(); void overrideManager(); void changeSet(); void fetchHint(); void lazyConnections(); void testInterSectionOfIdFilters(); void testInterSectionOfIdAndDetailFilters(); /* Special test with special data */ void uriParsing_data(); void nameSynthesis_data(); void compareVariant_data(); /* Tests that are run on all managers */ void metadata_data() {addManagers();} void nullIdOperations_data() {addManagers();} void add_data() {addManagers();} void update_data() {addManagers();} void remove_data() {addManagers();} void addAndUpdate_data() {addManagers();} void batch_data() {addManagers();} void signalEmission_data() {addManagers();} void actionPreferences_data() {addManagers();} void selfContactId_data() {addManagers();} void detailOrders_data() {addManagers();} void relationships_data() {addManagers();} void contactType_data() {addManagers();} void lateDeletion_data() {addManagers();} void testInterSectionOfIdFilters_data() {addManagers();} void testInterSectionOfIdAndDetailFilters_data() {addManagers();} }; // Helper class that connects to a signal on ctor, and disconnects on dtor class QTestSignalSink : public QObject { Q_OBJECT public: // signal and object must remain valid for the lifetime QTestSignalSink(QObject *object, const char *signal) : mObject(object), mSignal(signal) { connect(object, signal, this, SLOT(ignored())); } ~QTestSignalSink() { disconnect(mObject, mSignal, this, SLOT(ignored())); } public slots: void ignored() {} private: QObject *mObject; const char * const mSignal; }; QT_BEGIN_NAMESPACE static uint qHash(const QMetaMethod &m) { return qHash(m.methodIndex()); } QT_END_NAMESPACE /* Two backends for testing lazy signal connections */ class QContactLazyEngine2 : public QContactManagerEngine { public: QContactLazyEngine2() {} QString managerName() const {return "lazy2";} /*! \reimp */ int managerVersion() const {return 0;} void connectNotify(const QMetaMethod &signal) { connectionCounts[signal]++; } void disconnectNotify(const QMetaMethod &signal) { connectionCounts[signal]--; } static QHash connectionCounts; // signal to count }; QHash QContactLazyEngine2::connectionCounts; class QContactLazyEngine : public QContactManagerEngine { public: QContactLazyEngine() {} QString managerName() const { return "lazy";} /*! \reimp */ int managerVersion() const {return 0;} void connectNotify(const QMetaMethod &signal) { connectionCounts[signal]++; } void disconnectNotify(const QMetaMethod &signal) { connectionCounts[signal]--; } static QHash connectionCounts; // signal to count }; QHash QContactLazyEngine::connectionCounts; /* Static lazy engine factory */ class LazyEngineFactory : public QContactManagerEngineFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QContactManagerEngineFactoryInterface" FILE "lazy.json") public: QContactManagerEngine* engine(const QMap& parameters, QContactManager::Error* error); QContactEngineId* createContactEngineId(const QMap& parameters, const QString& idString) const; QString managerName() const {return "testlazy";} }; QContactManagerEngine* LazyEngineFactory::engine(const QMap& parameters, QContactManager::Error* error) { // Return one or the other Q_UNUSED(error) if (parameters.value("version") == QString("1")) return new QContactLazyEngine(); else return new QContactLazyEngine2(); } QContactEngineId* LazyEngineFactory::createContactEngineId(const QMap& parameters, const QString& idString) const { Q_UNUSED(parameters) Q_UNUSED(idString) return new QContactIdMock("",1); } tst_QContactManager::tst_QContactManager() { } tst_QContactManager::~tst_QContactManager() { } void tst_QContactManager::initTestCase() { // Start JsonDb daemon if needed if (QContactManager::availableManagers().contains("jsondb")) { QString partitions_json = QFINDTESTDATA("partitions.json"); QVERIFY2(!partitions_json.isEmpty(), "partitions.json file is missing"); QVERIFY2(m_jsondbProcess.start(partitions_json), "Failed to start JsonDb process"); } managerDataHolder.reset(new QContactManagerDataHolder()); /* Make sure these other test plugins are NOT loaded by default */ // These are now removed from the list of managers in addManagers() //QVERIFY(!QContactManager::availableManagers().contains("testdummy")); //QVERIFY(!QContactManager::availableManagers().contains("teststaticdummy")); //QVERIFY(!QContactManager::availableManagers().contains("maliciousplugin")); } void tst_QContactManager::cleanupTestCase() { managerDataHolder.reset(0); if (QContactManager::availableManagers().contains("jsondb")) m_jsondbProcess.terminate(); } void tst_QContactManager::dumpContactDifferences(const QContact& ca, const QContact& cb) { // Try to narrow down the differences QContact a(ca); QContact b(cb); QContactName n1 = a.detail(QContactName::Type); QContactName n2 = b.detail(QContactName::Type); // Check the name components in more detail QCOMPARE(n1.firstName(), n2.firstName()); QCOMPARE(n1.middleName(), n2.middleName()); QCOMPARE(n1.lastName(), n2.lastName()); QCOMPARE(n1.prefix(), n2.prefix()); QCOMPARE(n1.suffix(), n2.suffix()); // Now look at the rest QList aDetails = a.details(); QList bDetails = b.details(); // They can be in any order, so loop // First remove any matches foreach(QContactDetail d, aDetails) { foreach(QContactDetail d2, bDetails) { if(d == d2) { a.removeDetail(&d); b.removeDetail(&d2); break; } } } // Now dump the extra details that were unmatched in A (note that Type is always present). // We ignore timestamp since it can get autogenerated too aDetails = a.details(); bDetails = b.details(); foreach(QContactDetail d, aDetails) { if (d.type() != QContactType::Type && d.type() != QContactTimestamp::Type) qDebug() << "A contact had extra detail:" << d.type() << d.values(); } // and same for B foreach(QContactDetail d, bDetails) { if (d.type() != QContactType::Type && d.type() != QContactTimestamp::Type) qDebug() << "B contact had extra detail:" << d.type() << d.values(); } if (a.type() != b.type()) { qDebug() << "A contact type =" << a.type(); qDebug() << "B contact type =" << b.type(); } } bool tst_QContactManager::isSuperset(const QContact& ca, const QContact& cb) { // returns true if contact ca is a superset of contact cb // we use this test instead of equality because dynamic information // such as presence/location, and synthesized information such as (possibly) type, // may differ between a contact in memory and the contact in the managed store. QContact a(ca); QContact b(cb); QList aDetails = a.details(); QList bDetails = b.details(); // They can be in any order, so loop // First remove any matches foreach(QContactDetail d, aDetails) { foreach(QContactDetail d2, bDetails) { if(d == d2) { a.removeDetail(&d); b.removeDetail(&d2); break; } } } // Second remove any superset matches (eg, backend adds a field) aDetails = a.details(); bDetails = b.details(); foreach (QContactDetail d, aDetails) { foreach (QContactDetail d2, bDetails) { if (d.type() == d2.type()) { bool canRemove = true; QMap d2map = d2.values(); foreach (int key, d2map.keys()) { if (d.value(key) != d2.value(key)) { // d can have _more_ keys than d2, // but not _less_; and it cannot // change the value. canRemove = false; } } if (canRemove) { // if we get to here, we can remove the details. a.removeDetail(&d); b.removeDetail(&d2); break; } } } } QList validTypes; validTypes << QContactType::TypeContact << QContactType::TypeGroup; // check for contact type updates if (validTypes.contains(a.type())) if (validTypes.contains(b.type())) if (a.type() != b.type()) return false; // nonempty type is different. // Now check to see if b has any details remaining; if so, a is not a superset. // Note Type can never be removed. if (b.details().size() > 1 || (b.details().size() == 1 && (b.details().value(0).type() != QContactType::Type))) return false; return true; } void tst_QContactManager::dumpContact(const QContact& contact) { QContactManager m; qDebug() << "Contact: " << contact.id(); QList details = contact.details(); foreach(QContactDetail d, details) { qDebug() << " " << d.type() << ":"; qDebug() << " Vals:" << d.values(); } } void tst_QContactManager::dumpContacts(QContactManager *cm) { QList ids = cm->contactIds(); qDebug() << "There are" << ids.count() << "contacts in" << cm->managerUri(); foreach (QContactId id, ids) { QContact c = cm->contact(id); dumpContact(c); } } void tst_QContactManager::uriParsing_data() { QTest::addColumn("uri"); QTest::addColumn("good"); // is this a good uri or not QTest::addColumn("manager"); QTest::addColumn >("parameters"); QMap inparameters; inparameters.insert("foo", "bar"); inparameters.insert("bazflag", QString()); inparameters.insert("bar", "glob"); QMap inparameters2; inparameters2.insert("this has spaces", QString()); inparameters2.insert("and& an", " &"); inparameters2.insert("and an ", "=quals"); QTest::newRow("built") << QContactManager::buildUri("manager", inparameters) << true << "manager" << inparameters; QTest::newRow("built with escaped parameters") << QContactManager::buildUri("manager", inparameters2) << true << "manager" << inparameters2; QTest::newRow("no scheme") << "this should not split" << false << QString() << tst_QContactManager_QStringMap(); QTest::newRow("wrong scheme") << "invalidscheme:foo bar" << false << QString() << tst_QContactManager_QStringMap(); QTest::newRow("right scheme, no colon") << "qtcontacts" << false << QString() << tst_QContactManager_QStringMap(); QTest::newRow("no manager, colon, no params") << "qtcontacts::" << false << "manager" << tst_QContactManager_QStringMap(); QTest::newRow("yes manager, no colon, no params") << "qtcontacts:manager" << true << "manager" << tst_QContactManager_QStringMap(); QTest::newRow("yes manager, yes colon, no params") << "qtcontacts:manager:" << true << "manager"<< tst_QContactManager_QStringMap(); QTest::newRow("yes params") << "qtcontacts:manager:foo=bar&bazflag=&bar=glob" << true << "manager" << inparameters; QTest::newRow("yes params but misformed") << "qtcontacts:manager:foo=bar&=gloo&bar=glob" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 2") << "qtcontacts:manager:=&=gloo&bar=glob" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 3") << "qtcontacts:manager:==" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 4") << "qtcontacts:manager:&&" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 5") << "qtcontacts:manager:&goo=bar" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 6") << "qtcontacts:manager:goo&bar" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 7") << "qtcontacts:manager:goo&bar&gob" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 8") << "qtcontacts:manager:==&&==&goo=bar" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 9") << "qtcontacts:manager:foo=bar=baz" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 10") << "qtcontacts:manager:foo=bar=baz=glob" << false << "manager" << inparameters; QTest::newRow("no manager but yes params") << "qtcontacts::foo=bar&bazflag=&bar=glob" << false << QString() << inparameters; QTest::newRow("no manager or params") << "qtcontacts::" << false << QString() << inparameters; QTest::newRow("no manager or params or colon") << "qtcontacts:" << false << QString() << inparameters; } void tst_QContactManager::addManagers() { QTest::addColumn("uri"); QStringList managers = QContactManager::availableManagers(); /* Known one that will not pass */ managers.removeAll("invalid"); managers.removeAll("testdummy"); managers.removeAll("teststaticdummy"); managers.removeAll("maliciousplugin"); managers.removeAll("testlazy"); // "internal" engines managers.removeAll("social"); managers.removeAll("simcard"); managers.removeAll("com.nokia.messaging.contacts.engines.mail.contactslookup"); foreach(QString mgr, managers) { QMap params; QTest::newRow(QString("mgr='%1'").arg(mgr).toLatin1().constData()) << QContactManager::buildUri(mgr, params); if (mgr == "memory") { params.insert("id", "tst_QContactManager"); QTest::newRow(QString("mgr='%1', params").arg(mgr).toLatin1().constData()) << QContactManager::buildUri(mgr, params); } } } /* * Helper method for creating a QContact instance with name and phone number * details. */ QContact tst_QContactManager::createContact( QString firstName, QString lastName, QString phoneNumber) { QContact contact; QContactName n; if (!firstName.isEmpty()) { n.setFirstName(firstName); }; if (!lastName.isEmpty()) { n.setLastName(lastName); } if (!n.isEmpty()) { contact.saveDetail(&n); } if (!phoneNumber.isEmpty()) { QContactPhoneNumber ph; ph.setNumber(phoneNumber); contact.saveDetail(&ph); } return contact; } void tst_QContactManager::saveContactName(QContact *contact, QContactName *contactName, const QString &name) const { contactName->setFirstName(name); contactName->setLastName(name); contact->saveDetail(contactName); } void tst_QContactManager::metadata() { // ensure that the backend is publishing its metadata (name / parameters / uri) correctly QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QVERIFY(QContactManager::buildUri(cm->managerName(), cm->managerParameters()) == cm->managerUri()); } void tst_QContactManager::nullIdOperations() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QVERIFY(!cm->removeContact(QContactId())); QVERIFY(cm->error() == QContactManager::DoesNotExistError); QContact c = cm->contact(QContactId()); QVERIFY(c.id() == QContactId()); QVERIFY(c.isEmpty()); QVERIFY(cm->error() == QContactManager::DoesNotExistError); } void tst_QContactManager::uriParsing() { QFETCH(QString, uri); QFETCH(bool, good); QFETCH(QString, manager); QFETCH(tst_QContactManager_QStringMap, parameters); QString outmanager; QMap outparameters; if (good) { /* Good split */ /* Test splitting */ QVERIFY(QContactManager::parseUri(uri, 0, 0)); // no out parms // 1 out param QVERIFY(QContactManager::parseUri(uri, &outmanager, 0)); QCOMPARE(manager, outmanager); QVERIFY(QContactManager::parseUri(uri, 0, &outparameters)); QCONTACTMANAGER_REMOVE_VERSIONS_FROM_URI(outparameters); QCOMPARE(parameters, outparameters); outmanager.clear(); outparameters.clear(); QVERIFY(QContactManager::parseUri(uri, &outmanager, &outparameters)); QCONTACTMANAGER_REMOVE_VERSIONS_FROM_URI(outparameters); QCOMPARE(manager, outmanager); QCOMPARE(parameters, outparameters); } else { /* bad splitting */ outmanager.clear(); outparameters.clear(); QVERIFY(QContactManager::parseUri(uri, 0, 0) == false); QVERIFY(QContactManager::parseUri(uri, &outmanager, 0) == false); QVERIFY(outmanager.isEmpty()); QVERIFY(QContactManager::parseUri(uri, 0, &outparameters) == false); QCONTACTMANAGER_REMOVE_VERSIONS_FROM_URI(outparameters); QVERIFY(outparameters.isEmpty()); /* make sure the in parameters don't change with a bad split */ outmanager = manager; outparameters = parameters; QVERIFY(QContactManager::parseUri(uri, &outmanager, 0) == false); QCOMPARE(manager, outmanager); QVERIFY(QContactManager::parseUri(uri, 0, &outparameters) == false); QCONTACTMANAGER_REMOVE_VERSIONS_FROM_URI(outparameters); QCOMPARE(parameters, outparameters); } } void tst_QContactManager::ctors() { /* test the different ctors to make sure we end up with the same uri */ QVERIFY(QContactManager::availableManagers().count() >= 1); // invalid + something else QVERIFY(QContactManager::availableManagers().contains("invalid")); QString defaultStore = QContactManager::availableManagers().value(0); qDebug() << "Available managers:" << QContactManager::availableManagers(); QMap randomParameters; randomParameters.insert("something", "old"); randomParameters.insert("something...", "new"); randomParameters.insert("something ", "borrowed"); randomParameters.insert(" something", "blue"); QObject parent; QContactManager cm; // default QContactManager cm2(defaultStore); QContactManager cm3(defaultStore, QMap()); QContactManager cm9b(0); // QObject* ctor, should be same as cm2 etc QContactManager cm9c(&parent); // same as cm2 etc. QScopedPointer cm5(QContactManager::fromUri(QContactManager::buildUri(defaultStore, QMap()))); QScopedPointer cm6(QContactManager::fromUri(cm.managerUri())); // uri is not a name; should fail. QScopedPointer cm9(QContactManager::fromUri(QString(), &parent)); QVERIFY(cm9->parent() == &parent); QVERIFY(cm9b.parent() == 0); QVERIFY(cm9c.parent() == &parent); /* OLD TEST WAS THIS: */ //QCOMPARE(cm.managerUri(), cm2.managerUri()); //QCOMPARE(cm.managerUri(), cm3.managerUri()); //QCOMPARE(cm.managerUri(), cm5->managerUri()); //QCOMPARE(cm.managerUri(), cm6->managerUri()); //QCOMPARE(cm.managerUri(), cm9->managerUri()); /* NEW TEST IS THIS: Test that the names of the managers are the same */ QCOMPARE(cm.managerName(), cm2.managerName()); QCOMPARE(cm.managerName(), cm3.managerName()); QCOMPARE(cm.managerName(), cm5->managerName()); QCOMPARE(cm.managerName(), cm6->managerName()); QCOMPARE(cm.managerName(), cm9->managerName()); QCOMPARE(cm.managerName(), cm9b.managerName()); QCOMPARE(cm.managerName(), cm9c.managerName()); /* Test that we get invalid stores when we do silly things */ QContactManager em("non existent"); QContactManager em2("non existent", QMap()); QContactManager em3("memory", randomParameters); /* Also invalid, since we don't have one of these anyway */ QScopedPointer em4(QContactManager::fromUri("invalid uri")); QScopedPointer em5(QContactManager::fromUri(QContactManager::buildUri("nonexistent", QMap()))); QScopedPointer em6(QContactManager::fromUri(em3.managerUri())); /* * Sets of stores that should be equivalent: * - 1, 2, 4, 5 * - 3, 6 */ /* First some URI testing for equivalent stores */ QVERIFY(em.managerUri() == em2.managerUri()); QVERIFY(em.managerUri() == em5->managerUri()); QVERIFY(em.managerUri() == em4->managerUri()); QVERIFY(em2.managerUri() == em4->managerUri()); QVERIFY(em2.managerUri() == em5->managerUri()); QVERIFY(em4->managerUri() == em5->managerUri()); QVERIFY(em3.managerUri() == em6->managerUri()); /* Test the stores that should not be the same */ QVERIFY(em.managerUri() != em3.managerUri()); QVERIFY(em.managerUri() != em6->managerUri()); /* now the components */ QCOMPARE(em.managerName(), QString("invalid")); QCOMPARE(em2.managerName(), QString("invalid")); QCOMPARE(em3.managerName(), QString("memory")); QCOMPARE(em4->managerName(), QString("invalid")); QCOMPARE(em5->managerName(), QString("invalid")); QCOMPARE(em6->managerName(), QString("memory")); QCOMPARE(em.managerParameters(), tst_QContactManager_QStringMap()); QCOMPARE(em2.managerParameters(), tst_QContactManager_QStringMap()); QCOMPARE(em4->managerParameters(), tst_QContactManager_QStringMap()); QCOMPARE(em5->managerParameters(), tst_QContactManager_QStringMap()); QCOMPARE(em3.managerParameters(), em6->managerParameters()); // memory engine discards the given params, replaces with id. // Finally test the platform specific engines are actually the defaults #if defined(QT_JSONDB_ENABLED) QCOMPARE(defaultStore, QString("jsondb")); #elif !defined(QT_NO_JSONDB) QCOMPARE(defaultStore, QString::fromLatin1("jsondb")); #else QCOMPARE(defaultStore, QString("invalid")); #endif } void tst_QContactManager::doDump() { // Only do this if it has been explicitly selected if (QCoreApplication::arguments().contains("doDump")) { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); dumpContacts(cm.data()); } } Q_DECLARE_METATYPE(QVariant) void tst_QContactManager::add() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QContact alice = createContact("Alice", "inWonderland", "1234567"); int currCount = cm->contactIds().count(); QVERIFY(cm->saveContact(&alice)); QVERIFY(cm->error() == QContactManager::NoError); QVERIFY(!alice.id().managerUri().isEmpty()); QVERIFY(!(alice.id().isNull())); QCOMPARE(cm->contactIds().count(), currCount+1); QContact added = cm->contact(alice.id()); QVERIFY(added.id() == alice.id()); if (!isSuperset(added, alice)) { dumpContacts(cm.data()); dumpContactDifferences(added, alice); QCOMPARE(added, alice); } // now a contact with many details of a particular definition // if the detail is not unique it should then support minimum of two of the same kind const int nrOfdetails = 2; QContact veryContactable = createContact("Very", "Contactable", ""); for (int i = 0; i < nrOfdetails; i++) { QString phnStr = QString::number(i); QContactPhoneNumber vcphn; vcphn.setNumber(phnStr); QVERIFY(veryContactable.saveDetail(&vcphn)); } // check that all the numbers were added successfully QVERIFY(veryContactable.details(QContactPhoneNumber::Type).size() == nrOfdetails); // check if it can be saved QVERIFY(cm->saveContact(&veryContactable)); // verify save QContact retrievedContactable = cm->contact(veryContactable.id()); if (!isSuperset(retrievedContactable, veryContactable)) { dumpContactDifferences(veryContactable, retrievedContactable); QCOMPARE(veryContactable, retrievedContactable); } } void tst_QContactManager::testInterSectionOfIdAndDetailFilters() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); //prepare filter data QContact alice = createContact("Alice", "inWonderland", "1234567"); QVERIFY(cm->saveContact(&alice)); QContact john = createContact("John", "inFinland", "2345678"); QVERIFY(cm->saveContact(&john)); QContactIdFilter idFilter1; QList contactIdList; contactIdList.append(alice.id()); idFilter1.setIds(contactIdList); QContactDetailFilter df; df.setDetailType(QContactName::Type,QContactName::FieldFirstName); df.setValue("Alice"); contactIdList.clear(); QContactIntersectionFilter isf; isf.append(idFilter1); isf.append(df); //Intersection filter of a matching Idfilter and detailfilter QList contactList = cm->contacts(isf); QCOMPARE(contactList.size(), 1); contactIdList.clear(); //Intersection filter of a non matching Idfilter and detailfilter isf.remove(df); df.setValue("John"); isf.append(df); contactList = cm->contacts(isf); QCOMPARE(contactList.size(), 0); } void tst_QContactManager::testInterSectionOfIdFilters() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); //prepare filter data QContact alice = createContact("Alice", "inWonderland", "1234567"); QVERIFY(cm->saveContact(&alice)); QContact john = createContact("John", "inFinland", "2345678"); QVERIFY(cm->saveContact(&john)); QContactIdFilter idFilter1; QList contactIdList; contactIdList.append(alice.id()); idFilter1.setIds(contactIdList); QContactIdFilter idFilter2; contactIdList.clear(); contactIdList.append(john.id()); idFilter2.setIds(contactIdList); contactIdList.clear(); //Test intersection filter with two different idFilters QContactIntersectionFilter isf; isf.append(idFilter1); isf.append(idFilter2); QList contactList = cm->contacts(idFilter1); QCOMPARE(contactList.size(), 1); QCOMPARE(contactList.at(0).id(), alice.id()); contactIdList.clear(); //When we intersect with two idFilters each having different //contact Ids the result should be zero contactList = cm->contacts(isf); QCOMPARE(contactList.size(), 0); //Test intersection filter with two idFilters //each having same contactid as other the result should be 1 isf.remove(idFilter2); contactIdList.append(alice.id()); idFilter2.setIds(contactIdList); contactIdList.clear(); isf.append(idFilter2); contactList = cm->contacts(isf); QCOMPARE(contactList.size(), 1); contactIdList.clear(); // Test intersection filter with two idFilters // idFilter1 has alice's contactId // And idfilter2 has alice's and John's contactId // The result should be one and its alice contactIdList << alice.id() << john.id(); idFilter2.setIds(contactIdList); isf.append(idFilter2); contactIdList.clear(); contactList = cm->contacts(isf); QCOMPARE(contactList.size(), 1); QCOMPARE(contactList.at(0).id(), alice.id()); //clean up filters and the contactIdList isf.remove(idFilter2); isf.remove(idFilter1); contactIdList.clear(); //Test with null contactId filter: idFilter1 has one contact and idFilter2 has null id //idFilters with null id should not return any thing so result should be zero contactIdList.append(alice.id()); idFilter1.setIds(contactIdList); contactIdList.clear(); contactIdList.append(QContactId()); idFilter2.setIds(contactIdList); isf.append(idFilter1); isf.append(idFilter2); QCOMPARE(isf.filters().size(), 2); contactList = cm->contacts(isf); QCOMPARE(contactList.size(), 0); //clean up filters and the contactIdList isf.remove(idFilter2); isf.remove(idFilter1); contactIdList.clear(); //Three filters: intersection of two IdFilters each having the same contact id //as the other with an IdFilter having one different contactId the result should be zero contactIdList.append(alice.id()); idFilter1.setIds(contactIdList); contactIdList.clear(); contactIdList.append(john.id()); idFilter2.setIds(contactIdList); contactIdList.clear(); QContactIdFilter idFilter3; contactIdList.append(john.id()); idFilter3.setIds(contactIdList); contactIdList.clear(); isf.append(idFilter1); isf.append(idFilter2); isf.append(idFilter3); QCOMPARE(isf.filters().size(), 3); contactList = cm->contacts(isf); QCOMPARE(contactList.size(), 0); //clean up filters and the contactIdList isf.remove(idFilter1); isf.remove(idFilter2); isf.remove(idFilter3); contactIdList.clear(); } void tst_QContactManager::update() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); /* Save a new contact first */ int contactCount = cm->contacts().size(); QContact alice = createContact("Alice", "inWonderland", "1234567"); QVERIFY(cm->saveContact(&alice)); QVERIFY(cm->error() == QContactManager::NoError); contactCount += 1; // added a new contact. QCOMPARE(cm->contacts().size(), contactCount); /* Update name */ QContactName name = alice.detail(QContactName::Type); saveContactName(&alice, &name, "updated"); QVERIFY(cm->saveContact(&alice)); QVERIFY(cm->error() == QContactManager::NoError); saveContactName(&alice, &name, "updated2"); QVERIFY(cm->saveContact(&alice)); QVERIFY(cm->error() == QContactManager::NoError); alice = cm->contact(alice.id()); // force reload of (persisted) alice QContact updated = cm->contact(alice.id()); QContactName updatedName = updated.detail(QContactName::Type); updatedName.setFirstName("updated2"); // Necessary to ensure that updatedName and name are actually the same QCOMPARE(updatedName, name); QCOMPARE(cm->contacts().size(), contactCount); // contact count should be the same, no new contacts /* Test that adding a new detail doesn't cause unwanted side effects */ int detailCount = alice.details().size(); QContactEmailAddress email; email.setEmailAddress("test@example.com"); alice.saveDetail(&email); QVERIFY(cm->saveContact(&alice)); QCOMPARE(cm->contacts().size(), contactCount); // contact count shoudl be the same, no new contacts // This test is dangerous, since backends can add timestamps etc... detailCount += 1; QCOMPARE(detailCount, alice.details().size()); // adding a detail should cause the detail count to increase by one. /* Test that removal of fields in a detail works */ QContactPhoneNumber phn = alice.detail(); phn.setNumber("1234567"); phn.setContexts(QContactDetail::ContextHome); alice.saveDetail(&phn); QVERIFY(cm->saveContact(&alice)); alice = cm->contact(alice.id()); // force reload of (persisted) alice QVERIFY(alice.detail().contexts().contains(QContactDetail::ContextHome)); // check context saved. phn = alice.detail(); // reload the detail, since it's key could have changed phn.setContexts(QList ()); // remove context field. alice.saveDetail(&phn); QVERIFY(cm->saveContact(&alice)); alice = cm->contact(alice.id()); // force reload of (persisted) alice QVERIFY(alice.detail().contexts().isEmpty()); // check context removed. QCOMPARE(cm->contacts().size(), contactCount); // removal of a field of a detail shouldn't affect the contact count // This test is dangerous, since backends can add timestamps etc... QCOMPARE(detailCount, alice.details().size()); // removing a field from a detail should affect the detail count /* Test that removal of details works */ phn = alice.detail(); // reload the detail, since it's key could have changed alice.removeDetail(&phn); QVERIFY(cm->saveContact(&alice)); alice = cm->contact(alice.id()); // force reload of (persisted) alice QVERIFY(alice.details().isEmpty()); // no such detail. QCOMPARE(cm->contacts().size(), contactCount); // removal of a detail shouldn't affect the contact count // This test is dangerous, since backends can add timestamps etc... //detailCount -= 1; //QCOMPARE(detailCount, alice.details().size()); // removing a detail should cause the detail count to decrease by one. if (cm->supportedContactTypes().contains(QContactType::TypeGroup)) { // Try changing types - not allowed // from contact -> group alice.setType(QContactType::TypeGroup); QContactName na = alice.detail(QContactName::Type); alice.removeDetail(&na); QVERIFY(!cm->saveContact(&alice)); QVERIFY(cm->error() == QContactManager::AlreadyExistsError); // from group -> contact QContact jabberwock = createContact("", "", "1234567890"); jabberwock.setType(QContactType::TypeGroup); QVERIFY(cm->saveContact(&jabberwock)); jabberwock.setType(QContactType::TypeContact); QVERIFY(!cm->saveContact(&jabberwock)); QVERIFY(cm->error() == QContactManager::AlreadyExistsError); } } void tst_QContactManager::remove() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); /* Save a new contact first */ QContact alice = createContact("Alice", "inWonderland", "1234567"); QVERIFY(cm->saveContact(&alice)); QVERIFY(cm->error() == QContactManager::NoError); QVERIFY(alice.id() != QContactId()); /* Remove the created contact */ const int contactCount = cm->contactIds().count(); QVERIFY(cm->removeContact(alice.id())); QCOMPARE(cm->contactIds().count(), contactCount - 1); QVERIFY(cm->contact(alice.id()).isEmpty()); QCOMPARE(cm->error(), QContactManager::DoesNotExistError); } void tst_QContactManager::addAndUpdate() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); int originalCount = cm->contactIds().size(); // save a few new contacts QList saveList; QContactName nameDetail; for (int i=0; i<20; i++) { QContact testContact; nameDetail.setFirstName(QStringLiteral("Test Contact ") + QString::number(i)); testContact.saveDetail(&nameDetail); saveList << testContact; } QMap errorMap; cm->saveContacts(&saveList, &errorMap); QCOMPARE(cm->contactIds().size(), originalCount + saveList.size()); QCOMPARE(errorMap.size(), 0); // update previously saved contacts for (int i=0; isaveContacts(&saveList, &errorMap); QCOMPARE(cm->contactIds().size(), originalCount + saveList.size()); QCOMPARE(errorMap.size(), 0); foreach (const QContact contact, saveList) { QVERIFY(cm->removeContact(contact.id())); } } void tst_QContactManager::batch() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); /* First test null pointer operations */ QVERIFY(!cm->saveContacts(NULL, NULL)); QVERIFY(cm->error() == QContactManager::BadArgumentError); QVERIFY(!cm->removeContacts(QList(), NULL)); QVERIFY(cm->error() == QContactManager::BadArgumentError); // Get supported name field int nameField = QContactName::FieldFirstName; /* Now add 3 contacts, all valid */ QContact a; QContactName na; na.setValue(nameField, "XXXXXX Albert"); a.saveDetail(&na); QContact b; QContactName nb; nb.setValue(nameField, "XXXXXX Bob"); b.saveDetail(&nb); QContact c; QContactName nc; nc.setValue(nameField, "XXXXXX Carol"); c.saveDetail(&nc); QList contacts; contacts << a << b << c; QMap errorMap; // Add one dummy error to test if the errors are reset errorMap.insert(0, QContactManager::NoError); QVERIFY(cm->saveContacts(&contacts, &errorMap)); QVERIFY(cm->error() == QContactManager::NoError); QVERIFY(errorMap.count() == 0); /* Make sure our contacts got updated too */ QVERIFY(contacts.count() == 3); QVERIFY(contacts.at(0).id() != QContactId()); QVERIFY(contacts.at(1).id() != QContactId()); QVERIFY(contacts.at(2).id() != QContactId()); QVERIFY(contacts.at(0).detail(QContactName::Type) == na); QVERIFY(contacts.at(1).detail(QContactName::Type) == nb); QVERIFY(contacts.at(2).detail(QContactName::Type) == nc); /* Retrieve again */ a = cm->contact(contacts.at(0).id()); b = cm->contact(contacts.at(1).id()); c = cm->contact(contacts.at(2).id()); QVERIFY(contacts.at(0).detail(QContactName::Type) == na); QVERIFY(contacts.at(1).detail(QContactName::Type) == nb); QVERIFY(contacts.at(2).detail(QContactName::Type) == nc); /* Save again, with a null error map */ QVERIFY(cm->saveContacts(&contacts, NULL)); QVERIFY(cm->error() == QContactManager::NoError); /* Now make an update to them all */ QContactPhoneNumber number; number.setNumber("1234567"); QVERIFY(contacts[0].saveDetail(&number)); number.setNumber("234567"); QVERIFY(contacts[1].saveDetail(&number)); number.setNumber("34567"); QVERIFY(contacts[2].saveDetail(&number)); QVERIFY(cm->saveContacts(&contacts, &errorMap)); QVERIFY(cm->error() == QContactManager::NoError); QVERIFY(errorMap.count() == 0); /* Retrieve them and check them again */ a = cm->contact(contacts.at(0).id()); b = cm->contact(contacts.at(1).id()); c = cm->contact(contacts.at(2).id()); QVERIFY(contacts.at(0).detail(QContactName::Type) == na); QVERIFY(contacts.at(1).detail(QContactName::Type) == nb); QVERIFY(contacts.at(2).detail(QContactName::Type) == nc); QVERIFY(a.details().count() == 1); QVERIFY(b.details().count() == 1); QVERIFY(c.details().count() == 1); QVERIFY(a.details().at(0).number() == "1234567"); QVERIFY(b.details().at(0).number() == "234567"); QVERIFY(c.details().at(0).number() == "34567"); /* Retrieve them with the batch ID fetch API */ QList batchIds; batchIds << a.id() << b.id() << c.id(); // Null error map first (doesn't crash) QList batchFetch = cm->contacts(batchIds, QContactFetchHint(), 0); QVERIFY(cm->error() == QContactManager::NoError); QVERIFY(batchFetch.count() == 3); QVERIFY(batchFetch.at(0).detail(QContactName::Type) == na); QVERIFY(batchFetch.at(1).detail(QContactName::Type) == nb); QVERIFY(batchFetch.at(2).detail(QContactName::Type) == nc); // With error map batchFetch = cm->contacts(batchIds, QContactFetchHint(), &errorMap); QVERIFY(cm->error() == QContactManager::NoError); QVERIFY(errorMap.count() == 0); QVERIFY(batchFetch.count() == 3); QVERIFY(batchFetch.at(0).detail(QContactName::Type) == na); QVERIFY(batchFetch.at(1).detail(QContactName::Type) == nb); QVERIFY(batchFetch.at(2).detail(QContactName::Type) == nc); /* Now an empty id */ batchIds.clear(); batchIds << QContactId() << a.id() << b.id() << c.id(); batchFetch = cm->contacts(batchIds, QContactFetchHint(), 0); QVERIFY(cm->error() != QContactManager::NoError); QVERIFY(batchFetch.count() == 4); QVERIFY(batchFetch.at(0).detail(QContactName::Type) == QContactDetail()); QVERIFY(batchFetch.at(1).detail(QContactName::Type) == na); QVERIFY(batchFetch.at(2).detail(QContactName::Type) == nb); QVERIFY(batchFetch.at(3).detail(QContactName::Type) == nc); batchFetch = cm->contacts(batchIds, QContactFetchHint(), &errorMap); QVERIFY(cm->error() != QContactManager::NoError); QVERIFY(batchFetch.count() == 4); QVERIFY(errorMap.count() == 1); QVERIFY(errorMap[0] == QContactManager::DoesNotExistError); QVERIFY(batchFetch.at(0).detail(QContactName::Type) == QContactDetail()); QVERIFY(batchFetch.at(1).detail(QContactName::Type) == na); QVERIFY(batchFetch.at(2).detail(QContactName::Type) == nb); QVERIFY(batchFetch.at(3).detail(QContactName::Type) == nc); /* Now multiple of the same contact */ batchIds.clear(); batchIds << c.id() << b.id() << c.id() << a.id() << a.id(); batchFetch = cm->contacts(batchIds, QContactFetchHint(), &errorMap); QVERIFY(cm->error() == QContactManager::NoError); QVERIFY(batchFetch.count() == 5); QVERIFY(errorMap.count() == 0); QVERIFY(batchFetch.at(0).detail(QContactName::Type) == nc); QVERIFY(batchFetch.at(1).detail(QContactName::Type) == nb); QVERIFY(batchFetch.at(2).detail(QContactName::Type) == nc); QVERIFY(batchFetch.at(3).detail(QContactName::Type) == na); QVERIFY(batchFetch.at(4).detail(QContactName::Type) == na); /* Now delete them all */ QList ids; ids << a.id() << b.id() << c.id(); QVERIFY(cm->removeContacts(ids, &errorMap)); QVERIFY(errorMap.count() == 0); QVERIFY(cm->error() == QContactManager::NoError); /* Make sure the contacts really don't exist any more */ QVERIFY(cm->contact(a.id()).id() == QContactId()); QVERIFY(cm->contact(a.id()).isEmpty()); QVERIFY(cm->error() == QContactManager::DoesNotExistError); QVERIFY(cm->contact(b.id()).id() == QContactId()); QVERIFY(cm->contact(b.id()).isEmpty()); QVERIFY(cm->error() == QContactManager::DoesNotExistError); QVERIFY(cm->contact(c.id()).id() == QContactId()); QVERIFY(cm->contact(c.id()).isEmpty()); QVERIFY(cm->error() == QContactManager::DoesNotExistError); /* Now try removing with all invalid ids (e.g. the ones we just removed) */ ids.clear(); ids << a.id() << b.id() << c.id(); QVERIFY(!cm->removeContacts(ids, &errorMap)); QVERIFY(cm->error() == QContactManager::DoesNotExistError); QVERIFY(errorMap.count() == 3); QVERIFY(errorMap.values().at(0) == QContactManager::DoesNotExistError); QVERIFY(errorMap.values().at(1) == QContactManager::DoesNotExistError); QVERIFY(errorMap.values().at(2) == QContactManager::DoesNotExistError); /* And again with a null error map */ QVERIFY(!cm->removeContacts(ids, NULL)); QVERIFY(cm->error() == QContactManager::DoesNotExistError); /* Try adding some new ones again, this time one with an error */ contacts.clear(); a.setId(QContactId()); b.setId(QContactId()); c.setId(QContactId()); /* Make B the bad guy */ QContactDetail bad(static_cast(123456));//does not exist and will break if you add it bad.setValue(12345, "Very bad"); b.saveDetail(&bad); contacts << a << b << c; /* Fix up B and re save it */ QVERIFY(contacts[1].removeDetail(&bad)); QVERIFY(cm->saveContacts(&contacts, &errorMap)); QVERIFY(errorMap.count() == 0); QVERIFY(cm->error() == QContactManager::NoError); // Save and remove a fourth contact. Store the id. a.setId(QContactId()); QVERIFY(cm->saveContact(&a)); QContactId removedId = a.id(); QVERIFY(cm->removeContact(removedId)); /* Now delete 3 items, but with one bad argument */ ids.clear(); ids << contacts.at(0).id(); ids << removedId; ids << contacts.at(2).id(); QVERIFY(!cm->removeContacts(ids, &errorMap)); QVERIFY(cm->error() != QContactManager::NoError); /* Again, the backend has the choice of either removing the successful ones, or not */ QVERIFY(errorMap.count() > 0); QVERIFY(errorMap.count() <= 3); // A might have gone through if (errorMap.keys().contains(0)) { QVERIFY(errorMap.value(0) != QContactManager::NoError); QVERIFY(contacts.at(0).id() == QContactId()); } else { QVERIFY(contacts.at(0).id() != QContactId()); } /* B should definitely have failed */ QVERIFY(errorMap.value(1) == QContactManager::DoesNotExistError); QVERIFY(ids.at(1) == removedId); // A might have gone through if (errorMap.keys().contains(2)) { QVERIFY(errorMap.value(2) != QContactManager::NoError); QVERIFY(contacts.at(2).id() == QContactId()); } else { QVERIFY(contacts.at(2).id() != QContactId()); } } void tst_QContactManager::invalidManager() { /* Create an invalid manager */ QContactManager manager("this should never work"); QVERIFY(manager.managerName() == "invalid"); QVERIFY(manager.managerVersion() == 0); /* also, test the other ctor behaviour is sane also */ QContactManager anotherManager("this should never work", 15); QVERIFY(anotherManager.managerName() == "invalid"); QVERIFY(anotherManager.managerVersion() == 0); /* Now test that all the operations fail */ QVERIFY(manager.contactIds().count() == 0); QVERIFY(manager.error() == QContactManager::NotSupportedError); QContact foo; QContactName nf; nf.setLastName("Lastname"); foo.saveDetail(&nf); QVERIFY(manager.error() == QContactManager::NotSupportedError); QVERIFY(manager.saveContact(&foo) == false); QVERIFY(manager.error() == QContactManager::NotSupportedError); QVERIFY(foo.id() == QContactId()); QVERIFY(manager.contactIds().count() == 0); QVERIFY(manager.contact(foo.id()).id() == QContactId()); QVERIFY(manager.contact(foo.id()).isEmpty()); QVERIFY(manager.error() == QContactManager::NotSupportedError); QVERIFY(manager.removeContact(foo.id()) == false); QVERIFY(manager.error() == QContactManager::NotSupportedError); QMap errorMap; errorMap.insert(0, QContactManager::NoError); QVERIFY(!manager.saveContacts(0, &errorMap)); QVERIFY(manager.errorMap().count() == 0); QVERIFY(errorMap.count() == 0); QVERIFY(manager.error() == QContactManager::BadArgumentError); /* filters */ QContactFilter f; // matches everything QContactDetailFilter df; df.setDetailType(QContactDisplayLabel::Type, QContactDisplayLabel::FieldLabel); QVERIFY(manager.contactIds(QContactFilter()).count() == 0); QVERIFY(manager.error() == QContactManager::NotSupportedError); QVERIFY(manager.contactIds(df).count() == 0); QVERIFY(manager.error() == QContactManager::NotSupportedError); QVERIFY(manager.contactIds(f | f).count() == 0); QVERIFY(manager.error() == QContactManager::NotSupportedError); QVERIFY(manager.contactIds(df | df).count() == 0); QVERIFY(manager.error() == QContactManager::NotSupportedError); QVERIFY(manager.isFilterSupported(f) == false); QVERIFY(manager.isFilterSupported(df) == false); QList list; list << foo; QVERIFY(!manager.saveContacts(&list, &errorMap)); QVERIFY(errorMap.count() == 0); QVERIFY(manager.error() == QContactManager::NotSupportedError); QVERIFY(!manager.removeContacts(QList(), &errorMap)); QVERIFY(errorMap.count() == 0); QVERIFY(manager.error() == QContactManager::BadArgumentError); QList idlist; idlist << foo.id(); QVERIFY(!manager.removeContacts(idlist, &errorMap)); QVERIFY(errorMap.count() == 0); QVERIFY(manager.error() == QContactManager::NotSupportedError); /* Self contact id */ QVERIFY(!manager.setSelfContactId(QContactId())); QVERIFY(manager.error() == QContactManager::NotSupportedError); QVERIFY(manager.selfContactId() == QContactId()); QVERIFY(manager.error() == QContactManager::NotSupportedError || manager.error() == QContactManager::DoesNotExistError); /* Relationships */ QContact one, two; QContactRelationship invalidRel; invalidRel.setFirst(one); invalidRel.setSecond(two); QList invalidRelList; invalidRelList << invalidRel; QVERIFY(!manager.saveRelationship(&invalidRel)); QVERIFY(manager.error() == QContactManager::NotSupportedError); QVERIFY(manager.relationships().isEmpty()); QVERIFY(manager.error() == QContactManager::NotSupportedError); manager.saveRelationships(&invalidRelList, NULL); QVERIFY(manager.error() == QContactManager::NotSupportedError); manager.removeRelationships(invalidRelList, NULL); QVERIFY(manager.error() == QContactManager::NotSupportedError || manager.error() == QContactManager::DoesNotExistError); /* Capabilities */ QVERIFY(manager.supportedDataTypes().count() == 0); } void tst_QContactManager::memoryManager() { QMap params; QContactManager m1("memory"); params.insert("random", "shouldNotBeUsed"); QContactManager m2("memory", params); params.insert("id", "shouldBeUsed"); QContactManager m3("memory", params); QContactManager m4("memory", params); params.insert("id", QString("")); QContactManager m5("memory", params); // should be another anonymous // add a contact to each of m1, m2, m3 QContact c; QContactName nc; nc.setFirstName("John"); nc.setLastName("Civilian"); c.saveDetail(&nc); m1.saveContact(&c); c.setId(QContactId()); QContact c2; QContactName nc2 = c2.detail(QContactName::Type); c2 = c; nc2.setMiddleName("Public"); c2.saveDetail(&nc2); m2.saveContact(&c2); // save c2 first; c will be given a higher id m2.saveContact(&c); // save c to m2 c.setId(QContactId()); nc.setSuffix("MD"); c.saveDetail(&nc); m3.saveContact(&c); /* test that m1 != m2 != m3 and that m3 == m4 */ // check the counts are correct - especially note m4 and m3. QCOMPARE(m1.contactIds().count(), 1); QCOMPARE(m2.contactIds().count(), 2); QCOMPARE(m3.contactIds().count(), 1); QCOMPARE(m4.contactIds().count(), 1); QCOMPARE(m5.contactIds().count(), 0); // remove c2 from m2 - ensure that this doesn't affect any other manager. m2.removeContact(c2.id()); QCOMPARE(m1.contactIds().count(), 1); QCOMPARE(m2.contactIds().count(), 1); QCOMPARE(m3.contactIds().count(), 1); QCOMPARE(m4.contactIds().count(), 1); QCOMPARE(m5.contactIds().count(), 0); // check that the contacts contained within are different. // note that in the m1->m2 case, only the id will be different! QVERIFY(m1.contact(m1.contactIds().at(0)) != m2.contact(m2.contactIds().at(0))); QVERIFY(m1.contact(m1.contactIds().at(0)) != m3.contact(m3.contactIds().at(0))); QVERIFY(m2.contact(m2.contactIds().at(0)) != m3.contact(m3.contactIds().at(0))); QVERIFY(m3.contact(m3.contactIds().at(0)) == m4.contact(m4.contactIds().at(0))); // now, we should be able to remove from m4, and have m3 empty QVERIFY(m4.removeContact(c.id())); QCOMPARE(m3.contactIds().count(), 0); QCOMPARE(m4.contactIds().count(), 0); QCOMPARE(m5.contactIds().count(), 0); } void tst_QContactManager::overrideManager() { QString defaultStore = QContactManager::availableManagers().value(0); // preserve existing environment override QString overrideManager = qgetenv("QTCONTACTS_MANAGER_OVERRIDE"); // override with specific managers qputenv("QTCONTACTS_MANAGER_OVERRIDE", "memory"); QContactManager m1; QCOMPARE(m1.managerName(), QString::fromLatin1("memory")); qputenv("QTCONTACTS_MANAGER_OVERRIDE", "invalid"); QContactManager m2; QCOMPARE(m2.managerName(), QString::fromLatin1("invalid")); qputenv("QTCONTACTS_MANAGER_OVERRIDE", ""); QContactManager m3; QCOMPARE(m3.managerName(), defaultStore); qputenv("QTCONTACTS_MANAGER_OVERRIDE", overrideManager.toLatin1()); } #if defined(SYMBIAN_BACKEND_S60_VERSION_31) || defined(SYMBIAN_BACKEND_S60_VERSION_32) || defined(SYMBIAN_BACKEND_S60_VERSION_50) /* Some symbian-specific unit tests. */ void tst_QContactManager::symbianManager() { QFETCH(QString, uri); QString managerName; QMap managerParameters; QContactManager::parseUri(uri, &managerName, &managerParameters); if (managerName != QString("symbian")) return; /* Firstly, a test for invalid storage type crash - QTMOBILITY-470 */ // open the contact database, and create a new contact CContactDatabase* cntdb = CContactDatabase::OpenL(); CleanupStack::PushL(cntdb); CContactItem* testItem = CContactCard::NewLC(); // create a new thumbnail field with (invalid) storage type KStorageTypeText instead of KStorageTypeStore CContactItemField* thumbnailField; thumbnailField = CContactItemField::NewLC(KStorageTypeText, KUidContactFieldPicture); thumbnailField->SetMapping(KUidContactFieldVCardMapPHOTO); thumbnailField->AddFieldTypeL(KUidContactFieldVCardMapBMP); thumbnailField->ResetStore(); // set the thumbnail data in the thumbnail field, and add it to the contact _LIT8(KThumbnailDataString, "Dummy Thumbnail Data String"); thumbnailField->StoreStorage()->SetThingL(KThumbnailDataString); testItem->AddFieldL(*thumbnailField); CleanupStack::Pop(thumbnailField); // save the updated contact. cntdb->CommitContactL(*testItem); cntdb->CloseContactL(testItem->Id()); CleanupStack::PopAndDestroy(2); // testItem, cntdb // force database to read thumbnail with invalid storage type. crash if not handled properly. QScopedPointer cm(QContactManager::fromUri(uri)); QList allContacts = cm->contacts(); } #endif void tst_QContactManager::nameSynthesis_data() { QTest::addColumn("expected"); QTest::addColumn("addname"); QTest::addColumn("prefix"); QTest::addColumn("first"); QTest::addColumn("middle"); QTest::addColumn("last"); QTest::addColumn("suffix"); QTest::addColumn("addcompany"); QTest::addColumn("company"); QTest::addColumn("addname2"); QTest::addColumn("secondprefix"); QTest::addColumn("secondfirst"); QTest::addColumn("secondmiddle"); QTest::addColumn("secondlast"); QTest::addColumn("secondsuffix"); QTest::addColumn("addcompany2"); QTest::addColumn("secondcompany"); QString e; // empty string.. gets a work out /* Various empty ones */ QTest::newRow("empty contact") << e << false << e << e << e << e << e << false << e << false << e << e << e << e << e << false << e; QTest::newRow("empty name") << e << true << e << e << e << e << e << false << e << false << e << e << e << e << e << false << e; QTest::newRow("empty names") << e << true << e << e << e << e << e << false << e << true << e << e << e << e << e << false << e; QTest::newRow("empty org") << e << false << e << e << e << e << e << true << e << false << e << e << e << e << e << true << e; QTest::newRow("empty orgs") << e << false << e << e << e << e << e << true << e << false << e << e << e << e << e << true << e; QTest::newRow("empty orgs and names") << e << true << e << e << e << e << e << true << e << true << e << e << e << e << e << true << e; /* Single values */ QTest::newRow("prefix") << "Prefix" << true << "Prefix" << e << e << e << e << false << e << false << e << e << e << e << e << false << e; QTest::newRow("first") << "First" << true << e << "First" << e << e << e << false << e << false << e << e << e << e << e << false << e; QTest::newRow("middle") << "Middle" << true << e << e << "Middle" << e << e << false << e << false << e << e << e << e << e << false << e; QTest::newRow("last") << "Last" << true << e << e << e << "Last" << e << false << e << false << e << e << e << e << e << false << e; QTest::newRow("suffix") << "Suffix" << true << e << e << e << e << "Suffix" << false << e << false << e << e << e << e << e << false << e; /* Single values in the second name */ QTest::newRow("prefix in second") << "Prefix" << false << "Prefix" << e << e << e << e << false << e << true << "Prefix" << e << e << e << e << false << e; QTest::newRow("first in second") << "First" << false << e << "First" << e << e << e << false << e << true << e << "First" << e << e << e << false << e; QTest::newRow("middle in second") << "Middle" << false << e << e << "Middle" << e << e << false << e << true << e << e << "Middle" << e << e << false << e; QTest::newRow("last in second") << "Last" << false << e << e << e << "Last" << e << false << e << true << e << e << e << "Last" << e << false << e; QTest::newRow("suffix in second") << "Suffix" << false << e << e << e << e << "Suffix" << false << e << true << e << e << e << e << "Suffix" << false << e; /* Multiple name values */ QTest::newRow("prefix first") << "Prefix First" << true << "Prefix" << "First" << e << e << e << false << e << false << e << e << e << e << e << false << e; QTest::newRow("prefix middle") << "Prefix Middle" << true << "Prefix" << e << "Middle" << e << e << false << e << false << e << e << e << e << e << false << e; QTest::newRow("prefix last") << "Prefix Last" << true << "Prefix" << e << e << "Last" << e << false << e << false << e << e << e << e << e << false << e; QTest::newRow("prefix suffix") << "Prefix Suffix" << true << "Prefix" << e << e << e << "Suffix" << false << e << false << e << e << e << e << e << false << e; QTest::newRow("first middle") << "First Middle" << true << e << "First" << "Middle" << e << e << false << e << false << e << e << e << e << e << false << e; QTest::newRow("first last") << "First Last" << true << e << "First" << e << "Last" << e << false << e << false << e << e << e << e << e << false << e; QTest::newRow("first suffix") << "First Suffix" << true << e << "First" << e << e << "Suffix" << false << e << false << e << e << e << e << e << false << e; QTest::newRow("middle last") << "Middle Last" << true << e << e << "Middle" << "Last" << e << false << e << false << e << e << e << e << e << false << e; QTest::newRow("middle suffix") << "Middle Suffix" << true << e << e << "Middle" << e << "Suffix" << false << e << false << e << e << e << e << e << false << e; QTest::newRow("last suffix") << "Last Suffix" << true << e << e << e << "Last" << "Suffix" << false << e << false << e << e << e << e << e << false << e; /* Everything.. */ QTest::newRow("all name") << "Prefix First Middle Last Suffix" << true << "Prefix" << "First" << "Middle" << "Last" << "Suffix" << false << e << false << e << e << e << e << e << false << e; QTest::newRow("all name second") << "Prefix First Middle Last Suffix" << false << "Prefix" << "First" << "Middle" << "Last" << "Suffix" << false << e << true << "Prefix" << "First" << "Middle" << "Last" << "Suffix" << false << e; /* Org */ QTest::newRow("org") << "Company" << false << e << e << e << e << e << true << "Company" << false << e << e << e << e << e << false << e; QTest::newRow("second org") << "Company" << false << e << e << e << e << e << false << e << false << e << e << e << e << e << true << "Company"; /* Mix */ QTest::newRow("org and empty name") << "Company" << true << e << e << e << e << e << true << "Company" << false << e << e << e << e << e << false << e; QTest::newRow("name and empty org") << "Prefix First Middle Last Suffix" << true << "Prefix" << "First" << "Middle" << "Last" << "Suffix" << false << e << false << e << e << e << e << e << false << e; /* names are preferred to orgs */ QTest::newRow("name and org") << "Prefix First Middle Last Suffix" << true << "Prefix" << "First" << "Middle" << "Last" << "Suffix" << true << "Company" << false << e << e << e << e << e << false << e; } void tst_QContactManager::nameSynthesis() { QContactManager cm("memory"); QFETCH(QString, expected); QFETCH(QString, prefix); QFETCH(QString, first); QFETCH(QString, middle); QFETCH(QString, last); QFETCH(QString, suffix); QFETCH(QString, company); QFETCH(QString, secondprefix); QFETCH(QString, secondfirst); QFETCH(QString, secondmiddle); QFETCH(QString, secondlast); QFETCH(QString, secondsuffix); QFETCH(QString, secondcompany); QFETCH(bool, addname); QFETCH(bool, addname2); QFETCH(bool, addcompany); QFETCH(bool, addcompany2); /* Test the default name synthesis code */ QContact c; QContactName name, name2; QContactOrganization org, org2; name.setPrefix(prefix); name.setFirstName(first); name.setMiddleName(middle); name.setLastName(last); name.setSuffix(suffix); name2.setPrefix(secondprefix); name2.setFirstName(secondfirst); name2.setMiddleName(secondmiddle); name2.setLastName(secondlast); name2.setSuffix(secondsuffix); org.setName(company); org2.setName(secondcompany); if (addname) c.saveDetail(&name); if (addname2) c.saveDetail(&name2); if (addcompany) c.saveDetail(&org); if (addcompany2) c.saveDetail(&org2); } void tst_QContactManager::observerDeletion() { QContactManager *manager = new QContactManager("memory"); QContact c; QVERIFY(manager->saveContact(&c)); QContactId id = c.id(); QContactObserver *observer = new QContactObserver(manager, id); Q_UNUSED(observer) delete manager; delete observer; // Test for bug MOBILITY-2566 - that QContactObserver doesn't crash when it is // destroyed after the associated QContactManager } void tst_QContactManager::signalEmission() { QTest::qWait(500); // clear the signal queue QFETCH(QString, uri); QScopedPointer m1(QContactManager::fromUri(uri)); qRegisterMetaType("QContactId"); qRegisterMetaType >("QList"); QSignalSpy spyCA(m1.data(), SIGNAL(contactsAdded(QList))); QSignalSpy spyCM(m1.data(), SIGNAL(contactsChanged(QList))); QSignalSpy spyCR(m1.data(), SIGNAL(contactsRemoved(QList))); QTestSignalSink casink(m1.data(), SIGNAL(contactsAdded(QList))); QTestSignalSink cmsink(m1.data(), SIGNAL(contactsChanged(QList))); QTestSignalSink crsink(m1.data(), SIGNAL(contactsRemoved(QList))); QList args; QList arg; QContact c; QList batchAdd; QList batchRemove; int addSigCount = 0; // the expected signal counts. int modSigCount = 0; int remSigCount = 0; // verify add emits signal added QContactName nc; saveContactName(&c, &nc, "John"); QVERIFY(m1->saveContact(&c)); QContactId cid = c.id(); addSigCount += 1; QTRY_COMPARE(spyCA.count(), addSigCount); args = spyCA.takeFirst(); addSigCount -= 1; arg = args.first().value >(); QVERIFY(arg.count() == 1); QCOMPARE(QContactId(arg.at(0)), cid); QScopedPointer c1Observer(new QContactObserver(m1.data(), cid)); QScopedPointer spyCOM1(new QSignalSpy(c1Observer.data(), SIGNAL(contactChanged()))); QScopedPointer spyCOR1(new QSignalSpy(c1Observer.data(), SIGNAL(contactRemoved()))); // verify save modified emits signal changed saveContactName(&c, &nc, "Citizen"); QVERIFY(m1->saveContact(&c)); modSigCount += 1; QTRY_COMPARE(spyCM.count(), modSigCount); QTRY_COMPARE(spyCOM1->count(), 1); args = spyCM.takeFirst(); modSigCount -= 1; arg = args.first().value >(); QVERIFY(arg.count() == 1); QCOMPARE(QContactId(arg.at(0)), cid); // verify remove emits signal removed m1->removeContact(c.id()); remSigCount += 1; QTRY_COMPARE(spyCR.count(), remSigCount); QTRY_COMPARE(spyCOR1->count(), 1); args = spyCR.takeFirst(); remSigCount -= 1; arg = args.first().value >(); QVERIFY(arg.count() == 1); QCOMPARE(QContactId(arg.at(0)), cid); // verify multiple adds works as advertised QContact c2, c3; QContactName nc2, nc3; saveContactName(&c2, &nc2, "Mark"); saveContactName(&c3, &nc3, "Garry"); QVERIFY(m1->saveContact(&c2)); QVERIFY(m1->saveContact(&c3)); QTRY_COMPARE(spyCM.count(), modSigCount); QTRY_VERIFY(spyCA.count() > addSigCount); addSigCount = spyCA.count(); spyCOM1->clear(); spyCOR1->clear(); QScopedPointer c2Observer(new QContactObserver(m1.data(), c2.id())); QScopedPointer c3Observer(new QContactObserver(m1.data(), c3.id())); QScopedPointer spyCOM2(new QSignalSpy(c2Observer.data(), SIGNAL(contactChanged()))); QScopedPointer spyCOM3(new QSignalSpy(c3Observer.data(), SIGNAL(contactChanged()))); QScopedPointer spyCOR2(new QSignalSpy(c2Observer.data(), SIGNAL(contactRemoved()))); QScopedPointer spyCOR3(new QSignalSpy(c3Observer.data(), SIGNAL(contactRemoved()))); // verify multiple modifies works as advertised saveContactName(&c2, &nc2, "M."); QVERIFY(m1->saveContact(&c2)); saveContactName(&c2, &nc2, "Mark"); saveContactName(&c3, &nc3, "G."); QVERIFY(m1->saveContact(&c2)); QVERIFY(m1->saveContact(&c3)); QTRY_VERIFY(spyCM.count() > modSigCount); modSigCount = spyCM.count(); QTRY_VERIFY(spyCOM2->count() > 0); QTRY_VERIFY(spyCOM3->count() > 0); QCOMPARE(spyCOM1->count(), 0); // verify multiple removes works as advertised m1->removeContact(c3.id()); remSigCount += 1; m1->removeContact(c2.id()); remSigCount += 1; QTRY_VERIFY(spyCOR2->count() > 0); QTRY_VERIFY(spyCOR3->count() > 0); QCOMPARE(spyCOR1->count(), 0); /* Now test the batch equivalents */ spyCA.clear(); spyCM.clear(); spyCR.clear(); /* Batch adds - set ids to zero so add succeeds. */ c.setId(QContactId()); c2.setId(QContactId()); c3.setId(QContactId()); batchAdd << c << c2 << c3; QMap errorMap; QVERIFY(m1->saveContacts(&batchAdd, &errorMap)); QVERIFY(batchAdd.count() == 3); c = batchAdd.at(0); c2 = batchAdd.at(1); c3 = batchAdd.at(2); // We want to see one contactsAdded signal listing id's for all three contacts. QTRY_COMPARE(spyCA.count(), 1); { QList sigids = spyCA.takeFirst().at(0).value >(); QVERIFY(sigids.contains(c.id())); QVERIFY(sigids.contains(c2.id())); QVERIFY(sigids.contains(c3.id())); } QTRY_COMPARE(spyCM.count(), 0); c1Observer.reset(new QContactObserver(m1.data(), c.id())); c2Observer.reset(new QContactObserver(m1.data(), c2.id())); c3Observer.reset(new QContactObserver(m1.data(), c3.id())); spyCOM1.reset(new QSignalSpy(c1Observer.data(), SIGNAL(contactChanged()))); spyCOM2.reset(new QSignalSpy(c2Observer.data(), SIGNAL(contactChanged()))); spyCOM3.reset(new QSignalSpy(c3Observer.data(), SIGNAL(contactChanged()))); spyCOR1.reset(new QSignalSpy(c1Observer.data(), SIGNAL(contactRemoved()))); spyCOR2.reset(new QSignalSpy(c2Observer.data(), SIGNAL(contactRemoved()))); spyCOR3.reset(new QSignalSpy(c3Observer.data(), SIGNAL(contactRemoved()))); QTRY_COMPARE(spyCR.count(), 0); /* Batch modifies */ QContactName modifiedName = c.detail(QContactName::Type); saveContactName(&c, &modifiedName, "Modified number 1"); modifiedName = c2.detail(QContactName::Type); saveContactName(&c2, &modifiedName, "Modified number 2"); modifiedName = c3.detail(QContactName::Type); saveContactName(&c3, &modifiedName, "Modified number 3"); batchAdd.clear(); batchAdd << c << c2 << c3; QVERIFY(m1->saveContacts(&batchAdd, &errorMap)); // We want to see one contactsChanged signal listing id's for all three contacts. QTRY_COMPARE(spyCM.count(), 1); { QList sigids = spyCM.takeFirst().at(0).value >(); QVERIFY(sigids.contains(c.id())); QVERIFY(sigids.contains(c2.id())); QVERIFY(sigids.contains(c3.id())); } QTRY_COMPARE(spyCOM1->count(), 1); QTRY_COMPARE(spyCOM2->count(), 1); QTRY_COMPARE(spyCOM3->count(), 1); /* Batch removes */ batchRemove << c.id() << c2.id() << c3.id(); QVERIFY(m1->removeContacts(batchRemove, &errorMap)); // We want to see one contactsRemoved signal listing id's for all three contacts. QTRY_COMPARE(spyCR.count(), 1); { QList sigids = spyCR.takeFirst().at(0).value >(); QVERIFY(sigids.contains(c.id())); QVERIFY(sigids.contains(c2.id())); QVERIFY(sigids.contains(c3.id())); } QTRY_COMPARE(spyCOR1->count(), 1); QTRY_COMPARE(spyCOR2->count(), 1); QTRY_COMPARE(spyCOR3->count(), 1); QTRY_COMPARE(spyCA.count(), 0); QTRY_COMPARE(spyCM.count(), 0); QScopedPointer m2(QContactManager::fromUri(uri)); // During construction SIM backend (m2) will try writing contacts with // nickname, email and additional number to find out if the SIM card // will support these fields. The other backend (m1) will then receive // signals about that. These need to be caught so they don't interfere // with the tests. (This trial and error method is used because existing // API for checking the availability of these fields is not public.) // NOTE: This applies only to pre 10.1 platforms (S60 3.1, 3.2, ect.) if (uri.contains("symbiansim")) { QTest::qWait(0); spyCA.clear(); spyCM.clear(); spyCR.clear(); } } void tst_QContactManager::errorStayingPut() { /* Make sure that when we clone a manager, we don't clone the error */ QMap params; params.insert("id", "error isolation test"); QContactManager m1("memory",params); QVERIFY(m1.error() == QContactManager::NoError); /* Remove an invalid contact to get an error */ QVERIFY(m1.removeContact(QContactId()) == false); QVERIFY(m1.error() == QContactManager::DoesNotExistError); /* Create a new manager with hopefully the same backend */ QContactManager m2("memory", params); QVERIFY(m1.error() == QContactManager::DoesNotExistError); QVERIFY(m2.error() == QContactManager::NoError); /* Cause an error on the other ones and check the first is not affected */ m2.saveContacts(0, 0); QVERIFY(m1.error() == QContactManager::DoesNotExistError); QVERIFY(m2.error() == QContactManager::BadArgumentError); QContact c; int unknown = 12345; QContactDetail::DetailType unknownType = static_cast(unknown); QContactDetail d(unknownType); d.setValue(unknown, 5); c.saveDetail(&d); QVERIFY(m1.saveContact(&c) == false); QVERIFY(m1.error() == QContactManager::InvalidDetailError); QVERIFY(m2.error() == QContactManager::BadArgumentError); } void tst_QContactManager::actionPreferences() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); // early out if the manager doesn't support action preference saving. if (cm->managerName() == "jsondb") { QSKIP("Manager does not support action preferences"); } // create a sample contact QContactAvatar a; a.setImageUrl(QUrl("test.png")); QContactPhoneNumber p1; p1.setNumber("12345"); QContactPhoneNumber p2; p2.setNumber("34567"); QContactPhoneNumber p3; p3.setNumber("56789"); QContactUrl u; u.setUrl("http://test.nokia.com"); QContactName n; QContact c; saveContactName(&c, &n, "TestContact"); c.saveDetail(&a); c.saveDetail(&p1); c.saveDetail(&p2); c.saveDetail(&p3); c.saveDetail(&u); // set a preference for dialing a particular saved phonenumber. c.setPreferredDetail("Dial", p2); QVERIFY(cm->saveContact(&c)); // save the contact QContact loaded = cm->contact(c.id()); // reload the contact // test that the preference was saved correctly. QContactDetail pref = loaded.preferredDetail("Dial"); QVERIFY(pref == p2); cm->removeContact(c.id()); } void tst_QContactManager::changeSet() { QContactId id = QContactIdMock::createId("a", 1); QContactChangeSet changeSet; QVERIFY(changeSet.addedContacts().isEmpty()); QVERIFY(changeSet.changedContacts().isEmpty()); QVERIFY(changeSet.removedContacts().isEmpty()); changeSet.insertAddedContact(id); QVERIFY(!changeSet.addedContacts().isEmpty()); QVERIFY(changeSet.changedContacts().isEmpty()); QVERIFY(changeSet.removedContacts().isEmpty()); QVERIFY(changeSet.addedContacts().contains(id)); changeSet.insertChangedContact(id); changeSet.insertChangedContacts(QList() << id); QVERIFY(changeSet.changedContacts().size() == 1); // set, should only be added once. QVERIFY(!changeSet.addedContacts().isEmpty()); QVERIFY(!changeSet.changedContacts().isEmpty()); QVERIFY(changeSet.removedContacts().isEmpty()); QVERIFY(changeSet.changedContacts().contains(id)); changeSet.clearChangedContacts(); QVERIFY(changeSet.changedContacts().isEmpty()); changeSet.insertRemovedContacts(QList() << id); QVERIFY(changeSet.removedContacts().contains(id)); changeSet.clearRemovedContacts(); QVERIFY(changeSet.removedContacts().isEmpty()); QVERIFY(changeSet.dataChanged() == false); QContactChangeSet changeSet2; changeSet2 = changeSet; QVERIFY(changeSet.addedContacts() == changeSet2.addedContacts()); changeSet.emitSignals(0); changeSet2.clearAddedContacts(); QVERIFY(changeSet2.addedContacts().isEmpty()); changeSet2.insertAddedContacts(changeSet.addedContacts().toList()); QVERIFY(changeSet.addedContacts() == changeSet2.addedContacts()); changeSet2.clearAll(); QVERIFY(changeSet.addedContacts() != changeSet2.addedContacts()); QContactChangeSet changeSet3(changeSet2); QVERIFY(changeSet.addedContacts() != changeSet3.addedContacts()); QVERIFY(changeSet2.addedContacts() == changeSet3.addedContacts()); changeSet.setDataChanged(true); QVERIFY(changeSet.dataChanged() == true); QVERIFY(changeSet.dataChanged() != changeSet2.dataChanged()); QVERIFY(changeSet.dataChanged() != changeSet3.dataChanged()); changeSet.emitSignals(0); changeSet.addedRelationshipsContacts().insert(id); changeSet.insertAddedRelationshipsContacts(QList() << id); QVERIFY(changeSet.addedRelationshipsContacts().contains(id)); changeSet.clearAddedRelationshipsContacts(); QVERIFY(changeSet.addedRelationshipsContacts().isEmpty()); changeSet.insertRemovedRelationshipsContacts(QList() << id); QVERIFY(changeSet.removedRelationshipsContacts().contains(id)); changeSet.clearRemovedRelationshipsContacts(); QVERIFY(changeSet.removedRelationshipsContacts().isEmpty()); changeSet.emitSignals(0); changeSet.removedRelationshipsContacts().insert(id); changeSet.emitSignals(0); changeSet.setOldAndNewSelfContactId(QPair(QContactId(), id)); changeSet2 = changeSet; QVERIFY(changeSet2.addedRelationshipsContacts() == changeSet.addedRelationshipsContacts()); QVERIFY(changeSet2.removedRelationshipsContacts() == changeSet.removedRelationshipsContacts()); QVERIFY(changeSet2.oldAndNewSelfContactId() == changeSet.oldAndNewSelfContactId()); changeSet.emitSignals(0); changeSet.setOldAndNewSelfContactId(QPair(id, QContactId())); QVERIFY(changeSet2.oldAndNewSelfContactId() != changeSet.oldAndNewSelfContactId()); changeSet.setDataChanged(true); changeSet.emitSignals(0); } void tst_QContactManager::fetchHint() { // This just tests the accessors and mutators (API). // See tst_qcontactmanagerfiltering for the "backend support" test. QContactFetchHint hint; hint.setOptimizationHints(QContactFetchHint::NoBinaryBlobs); QCOMPARE(hint.optimizationHints(), QContactFetchHint::NoBinaryBlobs); QStringList rels; rels << QContactRelationship::HasMember(); hint.setRelationshipTypesHint(rels); QCOMPARE(hint.relationshipTypesHint(), rels); QList types; types << QContactName::Type << QContactPhoneNumber::Type; hint.setDetailTypesHint(types); QCOMPARE(hint.detailTypesHint(), types); QSize prefImageSize(33, 33); hint.setPreferredImageSize(prefImageSize); QCOMPARE(hint.preferredImageSize(), prefImageSize); int limit = 15; hint.setMaxCountHint(limit); QCOMPARE(hint.maxCountHint(), limit); } void tst_QContactManager::selfContactId() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); // early out if the manager doesn't support self contact id saving QContactId selfContact = cm->selfContactId(); if (cm->managerName() == "jsondb") QSKIP("JSONDB backend does not support selfContact at the moment, skipping..."); // create a new "self" contact and retrieve its Id QVERIFY(cm->error() == QContactManager::NoError || cm->error() == QContactManager::DoesNotExistError); QContact self; QContactPhoneNumber selfPhn; selfPhn.setNumber("12345"); self.saveDetail(&selfPhn); if (!cm->saveContact(&self)) { QSKIP("Unable to save the generated self contact"); } QContactId newSelfContact = self.id(); // Setup signal spy qRegisterMetaType("QContactId"); QSignalSpy spy(cm.data(), SIGNAL(selfContactIdChanged(QContactId,QContactId))); QTestSignalSink sink(cm.data(), SIGNAL(selfContactIdChanged(QContactId,QContactId))); // Set new self contact QVERIFY(cm->setSelfContactId(newSelfContact)); QVERIFY(cm->error() == QContactManager::NoError); QTRY_VERIFY(spy.count() == 1); QVERIFY(spy.at(0).count() == 2); // note: for some reason qvariant_cast(spy.at(0).at(0)) returns always zero // because the type is not recognized. Hence the ugly casting below. QVERIFY(*((const QContactId*) spy.at(0).at(0).constData()) == selfContact); QVERIFY(*((const QContactId*) spy.at(0).at(1).constData()) == newSelfContact); QVERIFY(cm->selfContactId() == newSelfContact); // Remove self contact if (!cm->removeContact(self.id())) { QSKIP("Unable to remove self contact"); } QTRY_VERIFY(spy.count() == 2); QVERIFY(spy.at(1).count() == 2); QVERIFY(*((const QContactId*) spy.at(1).at(0).constData()) == newSelfContact); QVERIFY(*((const QContactId*) spy.at(1).at(1).constData()) == QContactId()); QVERIFY(cm->selfContactId() == QContactId()); // ensure reset after removed. // reset to original state. cm->setSelfContactId(selfContact); } QList tst_QContactManager::removeAllDefaultDetails(const QList& details) { QList newlist; foreach (const QContactDetail d, details) { if (d.type() != QContactType::Type && d.type() != QContactTimestamp::Type) { newlist << d; } } return newlist; } void tst_QContactManager::detailOrders() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); if (cm->managerName() == "jsondb" || cm->managerName() == "memory") QSKIP("Skipping: This manager does not support detail ordering!"); QContact a; //phone numbers QContactPhoneNumber number1, number2, number3; number1.setNumber("11111111"); number1.setContexts(QContactPhoneNumber::ContextHome); number2.setNumber("22222222"); number2.setContexts(QContactPhoneNumber::ContextWork); number3.setNumber("33333333"); number3.setContexts(QContactPhoneNumber::ContextOther); a.saveDetail(&number1); a.saveDetail(&number2); a.saveDetail(&number3); QVERIFY(cm->saveContact(&a)); a = cm->contact(a.id()); QList details = a.details(QContactPhoneNumber::Type); QVERIFY(details.count() == 3); QVERIFY(details.at(0).value(QContactPhoneNumber::FieldContext) == QContactPhoneNumber::ContextHome); QVERIFY(details.at(1).value(QContactPhoneNumber::FieldContext) == QContactPhoneNumber::ContextWork); QVERIFY(details.at(2).value(QContactPhoneNumber::FieldContext) == QContactPhoneNumber::ContextOther); number2 = details.at(1); QVERIFY(a.removeDetail(&number2)); QVERIFY(cm->saveContact(&a)); a = cm->contact(a.id()); details = a.details(QContactPhoneNumber::Type); QVERIFY(details.count() == 2); QVERIFY(details.at(0).value(QContactPhoneNumber::FieldContext) == QContactPhoneNumber::ContextHome); QVERIFY(details.at(1).value(QContactPhoneNumber::FieldContext) == QContactPhoneNumber::ContextOther); a.saveDetail(&number2); QVERIFY(cm->saveContact(&a)); a = cm->contact(a.id()); details = a.details(QContactPhoneNumber::Type); QVERIFY(details.count() == 3); QVERIFY(details.at(0).value(QContactPhoneNumber::FieldContext) == QContactPhoneNumber::ContextHome); QVERIFY(details.at(1).value(QContactPhoneNumber::FieldContext) == QContactPhoneNumber::ContextOther); QVERIFY(details.at(2).value(QContactPhoneNumber::FieldContext) == QContactPhoneNumber::ContextWork); //addresses QContactAddress address1, address2, address3; address1.setStreet("Brandl St"); address1.setRegion("Brisbane"); address3 = address2 = address1; address1.setContexts(QContactAddress::ContextHome); address2.setContexts(QContactAddress::ContextWork); address3.setContexts(QContactAddress::ContextOther); QVERIFY(a.saveDetail(&address1)); QVERIFY(a.saveDetail(&address2)); QVERIFY(a.saveDetail(&address3)); QVERIFY(cm->saveContact(&a)); a = cm->contact(a.id()); details = a.details(QContactAddress::Type); QCOMPARE(details.count(), 3); // 1 location - they're unique // Detail keys for the moment are not persistent through an contact save / fetch address3 = details.at(0); QVERIFY(a.removeDetail(&address3)); // remove the most recent. address2 = details.at(1); // It is necessary to re-load the detail to ensure that its key is correct QVERIFY(a.removeDetail(&address2)); QVERIFY(cm->saveContact(&a)); a = cm->contact(a.id()); details = a.details(QContactAddress::Type); QVERIFY(details.count() == 0); // unique, remove one means none left. a.saveDetail(&address2); QVERIFY(cm->saveContact(&a)); a = cm->contact(a.id()); details = a.details(QContactAddress::Type); QVERIFY(details.count() == 1); // add one back. //emails QContactEmailAddress email1, email2, email3; email1.setEmailAddress("aaron@example.com"); email3 = email2 = email1; email1.setContexts(QContactEmailAddress::ContextHome); email2.setContexts(QContactEmailAddress::ContextWork); email3.setContexts(QContactEmailAddress::ContextOther); a.saveDetail(&email1); a.saveDetail(&email2); a.saveDetail(&email3); QVERIFY(cm->saveContact(&a)); a = cm->contact(a.id()); details = a.details(QContactEmailAddress::Type); QVERIFY(details.count() == 1); // Detail keys for the moment are not persistent through an contact save / fetch email3 = details.at(0); QVERIFY(a.removeDetail(&email3)); // remove the most recent. email2 = details.at(1); // It is necessary to re-load the detail to ensure that its key is correct QVERIFY(a.removeDetail(&email2)); QVERIFY(cm->saveContact(&a)); a = cm->contact(a.id()); details = a.details(QContactEmailAddress::Type); QVERIFY(details.count() == 0); // unique, remove one means none left. a.saveDetail(&email2); QVERIFY(cm->saveContact(&a)); a = cm->contact(a.id()); details = a.details(QContactEmailAddress::Type); QVERIFY(details.count() == 1); // add one back. QVERIFY(cm->removeContact(a.id())); QVERIFY(cm->error() == QContactManager::NoError); } void tst_QContactManager::relationships() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); if (cm->managerName() == "jsondb") QSKIP("Jsondb doesnt support relationships"); // save some contacts QContact source; QContact dest1, dest2, dest3, dest4; QContactPhoneNumber n1, n2, n3, n4; n1.setNumber("1"); n2.setNumber("2"); n3.setNumber("3"); n4.setNumber("4"); dest1.saveDetail(&n1); dest2.saveDetail(&n2); dest3.saveDetail(&n3); dest4.saveDetail(&n4); cm->saveContact(&source); cm->saveContact(&dest1); cm->saveContact(&dest2); cm->saveContact(&dest3); cm->saveContact(&dest4); // check if manager supports relationships if (cm->managerName() == "jsondb") { // ensure that the operations all fail as required. QContactRelationship r1, r2, r3; r1.setFirst(source); r1.setSecond(dest1); r1.setRelationshipType(QContactRelationship::HasManager()); r2.setFirst(source); r2.setSecond(dest2); r2.setRelationshipType(QContactRelationship::HasManager()); r3.setFirst(source); r3.setSecond(dest3); r3.setRelationshipType(QContactRelationship::HasManager()); QList batchList; batchList << r2 << r3; // test save and remove QVERIFY(!cm->saveRelationship(&r1)); QVERIFY(cm->error() == QContactManager::NotSupportedError); QVERIFY(!cm->removeRelationship(r1)); QVERIFY(cm->error() == QContactManager::NotSupportedError); cm->saveRelationships(&batchList, NULL); QVERIFY(cm->error() == QContactManager::NotSupportedError); // test retrieval QList retrieveList; retrieveList = cm->relationships(source, QContactRelationship::First); QVERIFY(retrieveList.isEmpty()); QVERIFY(cm->error() == QContactManager::NotSupportedError); retrieveList = cm->relationships(source, QContactRelationship::Second); QVERIFY(retrieveList.isEmpty()); QVERIFY(cm->error() == QContactManager::NotSupportedError); retrieveList = cm->relationships(source, QContactRelationship::Either); // Either QVERIFY(retrieveList.isEmpty()); QVERIFY(cm->error() == QContactManager::NotSupportedError); retrieveList = cm->relationships(QContactRelationship::HasManager(), source, QContactRelationship::First); QVERIFY(retrieveList.isEmpty()); QVERIFY(cm->error() == QContactManager::NotSupportedError); retrieveList = cm->relationships(QContactRelationship::HasManager(), source, QContactRelationship::Second); QVERIFY(retrieveList.isEmpty()); QVERIFY(cm->error() == QContactManager::NotSupportedError); retrieveList = cm->relationships(QContactRelationship::HasManager(), source, QContactRelationship::Either); QVERIFY(retrieveList.isEmpty()); QVERIFY(cm->error() == QContactManager::NotSupportedError); retrieveList = cm->relationships(QContactRelationship::HasManager(), source); QVERIFY(retrieveList.isEmpty()); QVERIFY(cm->error() == QContactManager::NotSupportedError); retrieveList = cm->relationships(QContactRelationship::HasManager()); QVERIFY(retrieveList.isEmpty()); QVERIFY(cm->error() == QContactManager::NotSupportedError); return; } // Get supported relationship types QStringList availableRelationshipTypes; if (cm->isRelationshipTypeSupported(QContactRelationship::HasMember())) availableRelationshipTypes << QContactRelationship::HasMember(); if (cm->isRelationshipTypeSupported(QContactRelationship::HasAssistant())) availableRelationshipTypes << QContactRelationship::HasAssistant(); if (cm->isRelationshipTypeSupported(QContactRelationship::HasManager())) availableRelationshipTypes << QContactRelationship::HasManager(); if (cm->isRelationshipTypeSupported(QContactRelationship::HasSpouse())) availableRelationshipTypes << QContactRelationship::HasSpouse(); if (cm->isRelationshipTypeSupported(QContactRelationship::IsSameAs())) availableRelationshipTypes << QContactRelationship::IsSameAs(); // add some arbitrary type for testing if (availableRelationshipTypes.count()) availableRelationshipTypes.insert(0, "test-arbitrary-relationship-type"); else { availableRelationshipTypes.append("test-arbitrary-relationship-type"); availableRelationshipTypes.append(QContactRelationship::HasMember()); availableRelationshipTypes.append(QContactRelationship::HasAssistant()); } // Verify that we have relationship types. If there are none then the manager // is saying it supports relationships but does not actually implement any // relationship type. QVERIFY(!availableRelationshipTypes.isEmpty()); // Some backends (eg. symbian) require that when type is "HasMember" // then "first" contact must be a group. if (availableRelationshipTypes.at(0) == QContactRelationship::HasMember()) { cm->removeContact(source.id()); source.setId(QContactId()); source.setType(QContactType::TypeGroup); cm->saveContact(&source); } // build our relationship - source is the manager all of the dest contacts. QContactRelationship customRelationshipOne; customRelationshipOne.setFirst(source); customRelationshipOne.setSecond(dest1); customRelationshipOne.setRelationshipType(availableRelationshipTypes.at(0)); QCOMPARE(customRelationshipOne.first(), source); QCOMPARE(customRelationshipOne.second(), dest1); QVERIFY(customRelationshipOne.relationshipType() == availableRelationshipTypes.at(0)); // save the relationship int managerRelationshipsCount = cm->relationships(availableRelationshipTypes.at(0)).count(); QVERIFY(cm->saveRelationship(&customRelationshipOne)); // test our accessors. QCOMPARE(cm->relationships(availableRelationshipTypes.at(0)).count(), (managerRelationshipsCount + 1)); QVERIFY(cm->relationships(availableRelationshipTypes.at(0), source).count() == 1); // remove the dest1 contact, relationship should be removed. cm->removeContact(dest1.id()); QCOMPARE(cm->relationships(availableRelationshipTypes.at(0), dest1, QContactRelationship::Second).count(), 0); // modify and save the relationship customRelationshipOne.setSecond(dest2); QVERIFY(cm->saveRelationship(&customRelationshipOne)); // attempt to save the relationship again. XXX TODO: what should the result be? currently succeeds (overwrites) int relationshipsCount = cm->relationships().count(); QVERIFY(cm->saveRelationship(&customRelationshipOne)); // succeeds, but just overwrites QCOMPARE(relationshipsCount, cm->relationships().count()); // shouldn't change; save should have overwritten. // removing the source contact should result in removal of the relationship. QVERIFY(cm->removeContact(source.id())); QCOMPARE(cm->relationships().count(), relationshipsCount - 1); // the relationship should have been removed. // now ensure that qcontact relationship caching works as required - perhaps this should be in tst_QContact? source.setId(QContactId()); // reset id so we can resave QVERIFY(cm->saveContact(&source)); // save source again. customRelationshipOne.setFirst(source); customRelationshipOne.setSecond(dest2); QVERIFY(cm->saveRelationship(&customRelationshipOne)); // Add a second relationship QContactRelationship customRelationshipTwo; customRelationshipTwo.setFirst(source); if (availableRelationshipTypes.count() > 1) customRelationshipTwo.setRelationshipType(availableRelationshipTypes.at(1)); else customRelationshipTwo.setRelationshipType(availableRelationshipTypes.at(0)); customRelationshipTwo.setSecond(dest3); QVERIFY(cm->saveRelationship(&customRelationshipTwo)); // currently, the contacts are "stale" - no cached relationships QVERIFY(dest3.relatedContacts().isEmpty()); QVERIFY(dest3.relationships().isEmpty()); QVERIFY(dest2.relatedContacts().isEmpty()); QVERIFY(dest2.relationships().isEmpty()); // now refresh the contacts dest3 = cm->contact(dest3.id()); dest2 = cm->contact(dest2.id()); source = cm->contact(source.id()); // and test again. QVERIFY(source.relatedContacts(QString(), QContactRelationship::First).isEmpty()); // source is always the first, so this should be empty. QVERIFY(source.relatedContacts(QString(), QContactRelationship::Second).contains(dest2)); QVERIFY(source.relatedContacts(QString(), QContactRelationship::Either).contains(dest2)); QVERIFY(source.relatedContacts(QString(), QContactRelationship::Second).contains(dest3)); QVERIFY(source.relatedContacts(QString(), QContactRelationship::Either).contains(dest3)); QVERIFY(source.relatedContacts(availableRelationshipTypes.at(0), QContactRelationship::Second).contains(dest2)); QVERIFY(source.relatedContacts(availableRelationshipTypes.at(0), QContactRelationship::First).isEmpty()); QVERIFY(dest2.relatedContacts().contains(source)); QVERIFY(dest2.relationships().contains(customRelationshipOne)); QVERIFY(!dest2.relationships().contains(customRelationshipTwo)); QVERIFY(dest2.relationships(availableRelationshipTypes.at(0)).contains(customRelationshipOne)); QVERIFY(!dest2.relationships(availableRelationshipTypes.at(0)).contains(customRelationshipTwo)); QVERIFY(dest2.relatedContacts(availableRelationshipTypes.at(0)).contains(source)); QVERIFY(dest2.relatedContacts(availableRelationshipTypes.at(0), QContactRelationship::First).contains(source)); QVERIFY(dest2.relatedContacts(availableRelationshipTypes.at(0), QContactRelationship::Second).isEmpty()); QVERIFY(!dest2.relatedContacts(availableRelationshipTypes.at(0), QContactRelationship::Second).contains(source)); QVERIFY(dest3.relatedContacts().contains(source)); QVERIFY(!dest3.relationships().contains(customRelationshipOne)); QVERIFY(dest3.relationships().contains(customRelationshipTwo)); QVERIFY(!dest3.relationships(availableRelationshipTypes.at(0)).contains(customRelationshipOne)); // Test iteration QList relats = source.relationships(); QList::iterator it = relats.begin(); while (it != relats.end()) { QContact firstContact = it->first(); QVERIFY(firstContact == source); QVERIFY(it->second() == dest2 || it->second() == dest3); it++; } if (availableRelationshipTypes.count() > 1) { QVERIFY(source.relatedContacts(availableRelationshipTypes.at(1), QContactRelationship::Second).contains(dest3)); QVERIFY(source.relatedContacts(availableRelationshipTypes.at(1), QContactRelationship::First).isEmpty()); QVERIFY(dest2.relationships(availableRelationshipTypes.at(1)).isEmpty()); QVERIFY(!dest3.relationships(availableRelationshipTypes.at(0)).contains(customRelationshipTwo)); QVERIFY(dest3.relationships(availableRelationshipTypes.at(1)).contains(customRelationshipTwo)); QVERIFY(!dest3.relationships(availableRelationshipTypes.at(1)).contains(customRelationshipOne)); QVERIFY(dest3.relatedContacts(availableRelationshipTypes.at(1)).contains(source)); QVERIFY(!dest3.relatedContacts(availableRelationshipTypes.at(0)).contains(source)); QVERIFY(dest3.relatedContacts(availableRelationshipTypes.at(1)).contains(source)); // role = either QVERIFY(!dest3.relatedContacts(availableRelationshipTypes.at(1), QContactRelationship::Second).contains(source)); QVERIFY(dest3.relatedContacts(availableRelationshipTypes.at(1), QContactRelationship::First).contains(source)); QVERIFY(dest2.relatedContacts(availableRelationshipTypes.at(1)).isEmpty()); } else { QVERIFY(source.relatedContacts(availableRelationshipTypes.at(0), QContactRelationship::Second).contains(dest3)); } // Cleanup a bit QMap errorMap; QList moreRels; moreRels << customRelationshipOne << customRelationshipTwo; errorMap.insert(5, QContactManager::BadArgumentError); QVERIFY(cm->removeRelationships(moreRels, &errorMap)); QVERIFY(errorMap.count() == 0); // test batch API and ordering in contacts QList currentRelationships = cm->relationships(source, QContactRelationship::First); QList batchList; QContactRelationship br1, br2, br3; br1.setFirst(source); br1.setSecond(dest2); br1.setRelationshipType(availableRelationshipTypes.at(0)); br2.setFirst(source); br2.setSecond(dest3); br2.setRelationshipType(availableRelationshipTypes.at(0)); if (availableRelationshipTypes.count() > 1) { br3.setFirst(source); br3.setSecond(dest3); br3.setRelationshipType(availableRelationshipTypes.at(1)); } else { br3.setFirst(source); br3.setSecond(dest4); br3.setRelationshipType(availableRelationshipTypes.at(0)); } batchList << br1 << br2 << br3; // ensure that the batch save works properly cm->saveRelationships(&batchList, NULL); QCOMPARE(cm->error(), QContactManager::NoError); QList batchRetrieve = cm->relationships(source, QContactRelationship::First); QVERIFY(batchRetrieve.contains(br1)); QVERIFY(batchRetrieve.contains(br2)); QVERIFY(batchRetrieve.contains(br3)); // remove a single relationship QVERIFY(cm->removeRelationship(br3)); batchRetrieve = cm->relationships(source, QContactRelationship::First); QVERIFY(batchRetrieve.contains(br1)); QVERIFY(batchRetrieve.contains(br2)); QVERIFY(!batchRetrieve.contains(br3)); // has already been removed. // now ensure that the batch remove works and we get returned to the original state. batchList.removeOne(br3); cm->removeRelationships(batchList, NULL); QVERIFY(cm->error() == QContactManager::NoError); QCOMPARE(cm->relationships(source, QContactRelationship::First), currentRelationships); // attempt to save relationships between an existing source but non-existent destination QContact nonexistentDest; quint32 idSeed = 0x5544; QContactId nonexistentLocalId = QContactId::fromString(QString::number(idSeed)); while (true) { nonexistentLocalId = cm->contact(nonexistentLocalId).id(); if (nonexistentLocalId == QContactId()) { // found a "spare" local id (no contact with that id) break; } // keep looking... idSeed += 1; nonexistentLocalId = QContactId::fromString(QString::number(idSeed)); QVERIFY(nonexistentLocalId != QContactId()); // integer overflow check. } // nonexistentDest.id().setLocalId(nonexistentLocalId); QContactRelationship maliciousRel; maliciousRel.setFirst(source); maliciousRel.setSecond(nonexistentDest); maliciousRel.setRelationshipType("nokia-test-invalid-relationship-type"); QVERIFY(!cm->saveRelationship(&maliciousRel)); // attempt to save a circular relationship - should fail! maliciousRel.setFirst(source); maliciousRel.setSecond(source); maliciousRel.setRelationshipType(availableRelationshipTypes.at(0)); QVERIFY(!cm->saveRelationship(&maliciousRel)); // more negative testing, but force manager to recognise the empty URI QContact circularContact = source; maliciousRel.setFirst(circularContact); maliciousRel.setSecond(circularContact); maliciousRel.setRelationshipType(availableRelationshipTypes.at(0)); QVERIFY(!cm->saveRelationship(&maliciousRel)); maliciousRel.setFirst(source); maliciousRel.setSecond(circularContact); maliciousRel.setRelationshipType(availableRelationshipTypes.at(0)); QVERIFY(!cm->saveRelationship(&maliciousRel)); maliciousRel.setFirst(circularContact); maliciousRel.setSecond(source); maliciousRel.setRelationshipType(availableRelationshipTypes.at(0)); QVERIFY(!cm->saveRelationship(&maliciousRel)); // attempt to save a relationship where the source contact comes from another manager QContactId invalidId; circularContact.setId(invalidId); maliciousRel.setFirst(circularContact); // an invalid source contact maliciousRel.setSecond(dest2); // a valid destination contact maliciousRel.setRelationshipType(availableRelationshipTypes.at(0)); QVERIFY(!cm->saveRelationship(&maliciousRel)); // remove the nonexistent relationship relationshipsCount = cm->relationships().count(); QVERIFY(!cm->removeRelationship(maliciousRel)); // does not exist; fail remove. QVERIFY(cm->error() == QContactManager::DoesNotExistError || cm->error() == QContactManager::InvalidRelationshipError); QCOMPARE(cm->relationships().count(), relationshipsCount); // should be unchanged. // now we want to ensure that a relationship is removed if one of the contacts is removed. customRelationshipOne.setFirst(source); customRelationshipOne.setSecond(dest2); customRelationshipOne.setRelationshipType(availableRelationshipTypes.at(0)); // Test batch save with an error map moreRels.clear(); moreRels << customRelationshipOne; errorMap.insert(0, QContactManager::BadArgumentError); QVERIFY(cm->saveRelationships(&moreRels, &errorMap)); QVERIFY(cm->error() == QContactManager::NoError); QVERIFY(errorMap.count() == 0); // should be reset source = cm->contact(source.id()); dest2 = cm->contact(dest2.id()); QVERIFY(cm->removeContact(dest2.id())); // remove dest2, the relationship should be removed QVERIFY(cm->relationships(availableRelationshipTypes.at(0), dest2, QContactRelationship::Second).isEmpty()); source = cm->contact(source.id()); QVERIFY(!source.relatedContacts().contains(dest2)); // and it shouldn't appear in cache. // now clean up and remove our dests. QVERIFY(cm->removeContact(source.id())); QVERIFY(cm->removeContact(dest3.id())); // attempt to save relationships with nonexistent contacts QVERIFY(!cm->saveRelationship(&br1)); QVERIFY(cm->error() == QContactManager::InvalidRelationshipError); cm->saveRelationships(&batchList, NULL); QVERIFY(cm->error() == QContactManager::InvalidRelationshipError); QVERIFY(!cm->removeRelationship(br1)); QVERIFY(cm->error() == QContactManager::DoesNotExistError || cm->error() == QContactManager::InvalidRelationshipError); cm->removeRelationships(batchList, NULL); QVERIFY(cm->error() == QContactManager::DoesNotExistError || cm->error() == QContactManager::InvalidRelationshipError); } void tst_QContactManager::contactType() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); if (!cm->supportedContactTypes().contains(QContactType::TypeGroup)) QSKIP("Skipping: This manager does not support group contacts!"); QContact g1, g2, c; g1.setType(QContactType::TypeGroup); g2.setType(QContactType::TypeGroup); QContactPhoneNumber g1p, g2p, cp; g1p.setNumber("22222"); g2p.setNumber("11111"); cp.setNumber("33333"); g1.saveDetail(&g1p); g2.saveDetail(&g2p); c.saveDetail(&cp); QVERIFY(cm->saveContact(&g1)); QVERIFY(cm->saveContact(&g2)); QVERIFY(cm->saveContact(&c)); // test that the accessing by type works properly QContactDetailFilter groupFilter; groupFilter.setDetailType(QContactType::Type, QContactType::FieldType); groupFilter.setValue(QContactType::TypeGroup); QVERIFY(cm->contactIds(groupFilter).contains(g1.id())); QVERIFY(cm->contactIds(groupFilter).contains(g2.id())); QVERIFY(!cm->contactIds(groupFilter).contains(c.id())); QList sortOrders; QContactSortOrder byPhoneNumber; byPhoneNumber.setDetailType(QContactPhoneNumber::Type, QContactPhoneNumber::FieldNumber); sortOrders.append(byPhoneNumber); // and ensure that sorting works properly with typed contacts also QList sortedIds = cm->contactIds(groupFilter, sortOrders); QVERIFY(sortedIds.indexOf(g2.id()) < sortedIds.indexOf(g1.id())); cm->removeContact(g1.id()); cm->removeContact(g2.id()); cm->removeContact(c.id()); } #if defined(USE_VERSIT_PLZ) void tst_QContactManager::partialSave() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QVersitContactImporter imp; QVersitReader reader(QByteArray( "BEGIN:VCARD\r\nFN:Alice\r\nN:Alice\r\nTEL:12345\r\nEND:VCARD\r\n" "BEGIN:VCARD\r\nFN:Bob\r\nN:Bob\r\nTEL:5678\r\nEND:VCARD\r\n" "BEGIN:VCARD\r\nFN:Carol\r\nN:Carol\r\nEMAIL:carol@example.com\r\nEND:VCARD\r\n" "BEGIN:VCARD\r\nFN:David\r\nN:David\r\nORG:DavidCorp\r\nEND:VCARD\r\n")); reader.startReading(); reader.waitForFinished(); QCOMPARE(reader.error(), QVersitReader::NoError); QCOMPARE(reader.results().count(), 4); QVERIFY(imp.importDocuments(reader.results())); QCOMPARE(imp.contacts().count(), 4); QVERIFY(imp.contacts()[0].displayLabel() == QStringLiteral("Alice")); QVERIFY(imp.contacts()[1].displayLabel() == QStringLiteral("Bob")); QVERIFY(imp.contacts()[2].displayLabel() == QStringLiteral("Carol")); QVERIFY(imp.contacts()[3].displayLabel() == QStringLiteral("David")); QList contacts = imp.contacts(); QMap errorMap; // First save these contacts QVERIFY(cm->saveContacts(&contacts, &errorMap)); QList originalContacts = contacts; // Now try some partial save operations // 0) empty mask == full save // 1) Ignore an added phonenumber // 2) Only save a modified phonenumber, not a modified email // 3) Remove an email address & phone, mask out phone // 4) new contact, no details in the mask // 5) new contact, some details in the mask // 6) Have a bad manager uri in the middle // 7) Have a non existing contact in the middle // 8) A list entirely of new contacts QContactPhoneNumber pn; pn.setNumber("111111"); contacts[0].saveDetail(&pn); // 0) empty mask QVERIFY(cm->saveContacts(&contacts, QStringList(), &errorMap)); // That should have updated everything QContact a = cm->contact(originalContacts[0].id()); QVERIFY(a.details().count() == 2); // 1) Add a phone number to b, mask it out contacts[1].saveDetail(&pn); QVERIFY(cm->saveContacts(&contacts, QStringList(QContactEmailAddress::Type), &errorMap)); QVERIFY(errorMap.isEmpty()); QContact b = cm->contact(originalContacts[1].id()); QVERIFY(b.details().count() == 1); // 2) save a modified detail in the mask QContactEmailAddress e; e.setEmailAddress("example@example.com"); contacts[1].saveDetail(&e); // contacts[1] should have both phone and email QVERIFY(cm->saveContacts(&contacts, QStringList(QContactEmailAddress::Type), &errorMap)); QVERIFY(errorMap.isEmpty()); b = cm->contact(originalContacts[1].id()); QVERIFY(b.details().count() == 1); QVERIFY(b.details().count() == 1); // 3) Remove an email address and a phone number QVERIFY(contacts[1].removeDetail(&e)); QVERIFY(contacts[1].removeDetail(&pn)); QVERIFY(contacts[1].details().count() == 0); QVERIFY(contacts[1].details().count() == 1); QVERIFY(cm->saveContacts(&contacts, QStringList(QContactEmailAddress::Type), &errorMap)); QVERIFY(errorMap.isEmpty()); b = cm->contact(originalContacts[1].id()); QVERIFY(b.details().count() == 1); QVERIFY(b.details().count() == 0); // 4 - New contact, no details in the mask QContact newContact = originalContacts[3]; newContact.setId(QContactId()); contacts.append(newContact); QVERIFY(cm->saveContacts(&contacts, QStringList(QContactEmailAddress::Type), &errorMap)); QVERIFY(errorMap.isEmpty()); QVERIFY(contacts[4].id() != 0); // Saved b = cm->contact(contacts[4].id()); QVERIFY(b.details().count() == 0); // not saved QVERIFY(b.details().count() == 0); // not saved // 5 - New contact, some details in the mask newContact = originalContacts[2]; newContact.setId(QContactId()); contacts.append(newContact); QVERIFY(cm->saveContacts(&contacts, QStringList(QContactEmailAddress::Type), &errorMap)); QVERIFY(errorMap.isEmpty()); QVERIFY(contacts[5].id() != 0); // Saved b = cm->contact(contacts[5].id()); QVERIFY(b.details().count() == 1); QVERIFY(b.details().count() == 0); // not saved // 6) Have a bad manager uri in the middle followed by a save error QContactId id4(contacts[4].id()); QContactId badId(id4); badId.setManagerUri(QString()); contacts[4].setId(badId); QContactDetail badDetail("BadDetail"); badDetail.setValue("BadField", "BadValue"); contacts[5].saveDetail(&badDetail); QVERIFY(!cm->saveContacts(&contacts, QStringList("BadDetail"), &errorMap)); QCOMPARE(errorMap.count(), 2); QCOMPARE(errorMap[4], QContactManager::DoesNotExistError); QCOMPARE(errorMap[5], QContactManager::InvalidDetailError); // 7) Have a non existing contact in the middle followed by a save error badId = id4; badId.setLocalId("987234"); // something nonexistent contacts[4].setId(badId); QVERIFY(!cm->saveContacts(&contacts, QStringList("BadDetail"), &errorMap)); QCOMPARE(errorMap.count(), 2); QCOMPARE(errorMap[4], QContactManager::DoesNotExistError); QCOMPARE(errorMap[5], QContactManager::InvalidDetailError); // 8 - New contact, no details in the mask newContact = originalContacts[3]; QCOMPARE(newContact.details().count(), 1); QCOMPARE(newContact.details().count(), 1); newContact.setId(QContactId()); QList contacts2; contacts2.append(newContact); QVERIFY(cm->saveContacts(&contacts2, QStringList(QContactEmailAddress::Type), &errorMap)); QVERIFY(errorMap.isEmpty()); QVERIFY(contacts2[0].id() != 0); // Saved b = cm->contact(contacts2[0].id()); QVERIFY(b.details().count() == 0); // not saved QVERIFY(b.details().count() == 0); // not saved // 9 - A list with only a new contact, with some details in the mask newContact = originalContacts[2]; newContact.setId(QContactId()); contacts2.clear(); contacts2.append(newContact); QVERIFY(cm->saveContacts(&contacts2, QStringList(QContactEmailAddress::Type), &errorMap)); QVERIFY(errorMap.isEmpty()); QVERIFY(contacts2[0].id() != 0); // Saved b = cm->contact(contacts2[0].id()); QVERIFY(b.details().count() == 1); QVERIFY(b.details().count() == 0); // not saved // 10 - A list with new a contact for the wrong manager, followed by a new contact with an // invalid detail newContact = originalContacts[2]; newContact.setId(QContactId()); contacts2.clear(); contacts2.append(newContact); contacts2.append(newContact); contacts2[0].setId(badId); contacts2[1].saveDetail(&badDetail); QVERIFY(!cm->saveContacts(&contacts2, QStringList("BadDetail"), &errorMap)); QCOMPARE(errorMap.count(), 2); QCOMPARE(errorMap[0], QContactManager::DoesNotExistError); QCOMPARE(errorMap[1], QContactManager::InvalidDetailError); } #endif void tst_QContactManager::lateDeletion() { // Create some engines, but make them get deleted at shutdown QFETCH(QString, uri); QContactManager* cm = QContactManager::fromUri(uri); cm->setParent(qApp); // now do nothing } void tst_QContactManager::lazyConnections() { QSKIP("Skipping: Lazy manager engine does not currently work with new plugin system!"); //TODO: adapt lazy manager engine to new plugin system QMap parameters; parameters["version"] = QString("1"); QContactManager lazy1("testlazy", parameters); QContactManager lazy2("testlazy"); QCOMPARE(lazy1.managerName(), QString("lazy")); QCOMPARE(lazy2.managerName(), QString("lazy2")); // Make sure the initial connection counts are empty QCOMPARE(QContactLazyEngine::connectionCounts.count(), 0); QCOMPARE(QContactLazyEngine2::connectionCounts.count(), 0); // Lazy 1 first { QTestSignalSink casink(&lazy1, SIGNAL(contactsAdded(QList))); // See if we got one connection QCOMPARE(QContactLazyEngine::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine::contactsAdded)), 1); QCOMPARE(QContactLazyEngine::connectionCounts.count(), 1); // Go out of scope, and see if disconnect is called } QCOMPARE(QContactLazyEngine::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine::contactsAdded)), 0); QCOMPARE(QContactLazyEngine::connectionCounts.count(), 1); // Lazy2 second { QTestSignalSink casink(&lazy2, SIGNAL(contactsAdded(QList))); // See if we got one connection QCOMPARE(QContactLazyEngine2::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine2::contactsAdded)), 1); QCOMPARE(QContactLazyEngine2::connectionCounts.count(), 1); // Go out of scope, and see if disconnect is called } QCOMPARE(QContactLazyEngine2::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine2::contactsAdded)), 0); QCOMPARE(QContactLazyEngine2::connectionCounts.count(), 1); // Just make sure all the signals get connected correctly { QTestSignalSink casink(&lazy1, SIGNAL(contactsAdded(QList))); QTestSignalSink crsink(&lazy1, SIGNAL(contactsRemoved(QList))); QTestSignalSink cmsink(&lazy1, SIGNAL(contactsChanged(QList))); QTestSignalSink dcsink(&lazy1, SIGNAL(dataChanged())); QTestSignalSink rasink(&lazy1, SIGNAL(relationshipsAdded(QList))); QTestSignalSink rrsink(&lazy1, SIGNAL(relationshipsRemoved(QList))); QTestSignalSink scsink(&lazy1, SIGNAL(selfContactIdChanged(QContactId,QContactId))); // See if we got all the connections QCOMPARE(QContactLazyEngine::connectionCounts.count(), 7); QCOMPARE(QContactLazyEngine::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine::contactsAdded)), 1); QCOMPARE(QContactLazyEngine::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine::contactsChanged)), 1); QCOMPARE(QContactLazyEngine::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine::contactsRemoved)), 1); QCOMPARE(QContactLazyEngine::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine::dataChanged)), 1); QCOMPARE(QContactLazyEngine::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine::relationshipsAdded)), 1); QCOMPARE(QContactLazyEngine::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine::relationshipsRemoved)), 1); QCOMPARE(QContactLazyEngine::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine::selfContactIdChanged)), 1); } QCOMPARE(QContactLazyEngine::connectionCounts.count(), 7); QCOMPARE(QContactLazyEngine::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine::contactsAdded)), 0); QCOMPARE(QContactLazyEngine::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine::contactsChanged)), 0); QCOMPARE(QContactLazyEngine::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine::contactsRemoved)), 0); QCOMPARE(QContactLazyEngine::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine::dataChanged)), 0); QCOMPARE(QContactLazyEngine::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine::relationshipsAdded)), 0); QCOMPARE(QContactLazyEngine::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine::relationshipsRemoved)), 0); QCOMPARE(QContactLazyEngine::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine::selfContactIdChanged)), 0); // and for lazy2 { QTestSignalSink casink(&lazy2, SIGNAL(contactsAdded(QList))); QTestSignalSink crsink(&lazy2, SIGNAL(contactsRemoved(QList))); QTestSignalSink cmsink(&lazy2, SIGNAL(contactsChanged(QList))); QTestSignalSink dcsink(&lazy2, SIGNAL(dataChanged())); QTestSignalSink rasink(&lazy2, SIGNAL(relationshipsAdded(QList))); QTestSignalSink rrsink(&lazy2, SIGNAL(relationshipsRemoved(QList))); QTestSignalSink scsink(&lazy2, SIGNAL(selfContactIdChanged(QContactId,QContactId))); // See if we got all the connections QCOMPARE(QContactLazyEngine2::connectionCounts.count(), 7); QCOMPARE(QContactLazyEngine2::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine2::contactsAdded)), 1); QCOMPARE(QContactLazyEngine2::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine2::contactsChanged)), 1); QCOMPARE(QContactLazyEngine2::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine2::contactsRemoved)), 1); QCOMPARE(QContactLazyEngine2::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine2::dataChanged)), 1); QCOMPARE(QContactLazyEngine2::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine2::relationshipsAdded)), 1); QCOMPARE(QContactLazyEngine2::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine2::relationshipsRemoved)), 1); QCOMPARE(QContactLazyEngine2::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine2::selfContactIdChanged)), 1); } QCOMPARE(QContactLazyEngine2::connectionCounts.count(), 7); QCOMPARE(QContactLazyEngine2::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine2::contactsAdded)), 0); QCOMPARE(QContactLazyEngine2::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine2::contactsChanged)), 0); QCOMPARE(QContactLazyEngine2::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine2::contactsRemoved)), 0); QCOMPARE(QContactLazyEngine2::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine2::dataChanged)), 0); QCOMPARE(QContactLazyEngine2::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine2::relationshipsAdded)), 0); QCOMPARE(QContactLazyEngine2::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine2::relationshipsRemoved)), 0); QCOMPARE(QContactLazyEngine2::connectionCounts.value(QMetaMethod::fromSignal(&QContactLazyEngine2::selfContactIdChanged)), 0); } void tst_QContactManager::compareVariant() { // Exercise this function a bit QFETCH(QVariant, a); QFETCH(QVariant, b); QFETCH(Qt::CaseSensitivity, cs); QFETCH(int, expected); int comparison = QContactManagerEngine::compareVariant(a, b, cs); // Since compareVariant is a little imprecise (just sign matters) // convert that here. if (comparison < 0) comparison = -1; else if (comparison > 0) comparison = 1; QCOMPARE(comparison, expected); comparison = QContactManagerEngine::compareVariant(b, a, cs); if (comparison < 0) comparison = -1; else if (comparison > 0) comparison = 1; // The sign should be flipped now QVERIFY((comparison + expected) == 0); } void tst_QContactManager::compareVariant_data() { QTest::addColumn("a"); QTest::addColumn("b"); QTest::addColumn("cs"); QTest::addColumn("expected"); // bool QTest::newRow("bool <") << QVariant(false) << QVariant(true) << Qt::CaseInsensitive << -1; QTest::newRow("bool =") << QVariant(false) << QVariant(false) << Qt::CaseInsensitive << -0; QTest::newRow("bool >") << QVariant(true) << QVariant(false) << Qt::CaseInsensitive << 1; // char (who uses these??) QTest::newRow("char <") << QVariant(QChar('a')) << QVariant(QChar('b')) << Qt::CaseInsensitive << -1; QTest::newRow("char < ci") << QVariant(QChar('A')) << QVariant(QChar('b')) << Qt::CaseInsensitive << -1; QTest::newRow("char < ci 2") << QVariant(QChar('a')) << QVariant(QChar('B')) << Qt::CaseInsensitive << -1; QTest::newRow("char < cs") << QVariant(QChar('a')) << QVariant(QChar('b')) << Qt::CaseSensitive << -1; QTest::newRow("char < cs") << QVariant(QChar('A')) << QVariant(QChar('b')) << Qt::CaseSensitive << -1; QTest::newRow("char = ci") << QVariant(QChar('a')) << QVariant(QChar('a')) << Qt::CaseInsensitive << 0; QTest::newRow("char = ci 2") << QVariant(QChar('a')) << QVariant(QChar('A')) << Qt::CaseInsensitive << 0; QTest::newRow("char = ci 3") << QVariant(QChar('A')) << QVariant(QChar('a')) << Qt::CaseInsensitive << 0; QTest::newRow("char = ci 4") << QVariant(QChar('A')) << QVariant(QChar('A')) << Qt::CaseInsensitive << 0; QTest::newRow("char = cs") << QVariant(QChar('a')) << QVariant(QChar('a')) << Qt::CaseSensitive << 0; QTest::newRow("char = cs 2") << QVariant(QChar('A')) << QVariant(QChar('A')) << Qt::CaseSensitive << 0; QTest::newRow("char >") << QVariant(QChar('b')) << QVariant(QChar('a')) << Qt::CaseInsensitive << 1; QTest::newRow("char > ci") << QVariant(QChar('b')) << QVariant(QChar('A')) << Qt::CaseInsensitive << 1; QTest::newRow("char > ci 2") << QVariant(QChar('B')) << QVariant(QChar('a')) << Qt::CaseInsensitive << 1; QTest::newRow("char > cs") << QVariant(QChar('b')) << QVariant(QChar('a')) << Qt::CaseSensitive << 1; QTest::newRow("char > cs") << QVariant(QChar('b')) << QVariant(QChar('A')) << Qt::CaseSensitive << 1; // Some numeric types // uint QTest::newRow("uint < boundary") << QVariant(uint(1)) << QVariant(uint(-1)) << Qt::CaseInsensitive << -1; QTest::newRow("uint <") << QVariant(uint(1)) << QVariant(uint(2)) << Qt::CaseInsensitive << -1; QTest::newRow("uint =") << QVariant(uint(2)) << QVariant(uint(2)) << Qt::CaseInsensitive << 0; QTest::newRow("uint = 0") << QVariant(uint(0)) << QVariant(uint(0)) << Qt::CaseInsensitive << 0; QTest::newRow("uint = boundary") << QVariant(uint(-1)) << QVariant(uint(-1)) << Qt::CaseInsensitive << 0; QTest::newRow("uint >") << QVariant(uint(5)) << QVariant(uint(2)) << Qt::CaseInsensitive << 1; QTest::newRow("uint > boundary") << QVariant(uint(-1)) << QVariant(uint(2)) << Qt::CaseInsensitive << 1; // boundary // int (hmm, signed 32 bit assumed) QTest::newRow("int < boundary") << QVariant(int(0x80000000)) << QVariant(int(0x7fffffff)) << Qt::CaseInsensitive << -1; QTest::newRow("int <") << QVariant(int(1)) << QVariant(int(2)) << Qt::CaseInsensitive << -1; QTest::newRow("int =") << QVariant(int(2)) << QVariant(int(2)) << Qt::CaseInsensitive << 0; QTest::newRow("int = 0") << QVariant(int(0)) << QVariant(int(0)) << Qt::CaseInsensitive << 0; QTest::newRow("int = boundary") << QVariant(int(0x80000000)) << QVariant(int(0x80000000)) << Qt::CaseInsensitive << 0; QTest::newRow("int >") << QVariant(int(5)) << QVariant(int(2)) << Qt::CaseInsensitive << 1; QTest::newRow("int > boundary") << QVariant(int(0x7fffffff)) << QVariant(int(0x80000000)) << Qt::CaseInsensitive << 1; // boundary // ulonglong QTest::newRow("ulonglong < boundary") << QVariant(qulonglong(1)) << QVariant(qulonglong(-1)) << Qt::CaseInsensitive << -1; QTest::newRow("ulonglong <") << QVariant(qulonglong(1)) << QVariant(qulonglong(2)) << Qt::CaseInsensitive << -1; QTest::newRow("ulonglong =") << QVariant(qulonglong(2)) << QVariant(qulonglong(2)) << Qt::CaseInsensitive << 0; QTest::newRow("ulonglong = 0") << QVariant(qulonglong(0)) << QVariant(qulonglong(0)) << Qt::CaseInsensitive << 0; QTest::newRow("ulonglong = boundary") << QVariant(qulonglong(-1)) << QVariant(qulonglong(-1)) << Qt::CaseInsensitive << 0; QTest::newRow("ulonglong >") << QVariant(qulonglong(5)) << QVariant(qulonglong(2)) << Qt::CaseInsensitive << 1; QTest::newRow("ulonglong > boundary") << QVariant(qulonglong(-1)) << QVariant(qulonglong(2)) << Qt::CaseInsensitive << 1; // boundary // longlong QTest::newRow("longlong < boundary") << QVariant(qlonglong(0x8000000000000000LL)) << QVariant(qlonglong(0x7fffffffffffffffLL)) << Qt::CaseInsensitive << -1; QTest::newRow("longlong <") << QVariant(qlonglong(1)) << QVariant(qlonglong(2)) << Qt::CaseInsensitive << -1; QTest::newRow("longlong =") << QVariant(qlonglong(2)) << QVariant(qlonglong(2)) << Qt::CaseInsensitive << 0; QTest::newRow("longlong = 0") << QVariant(qlonglong(0)) << QVariant(qlonglong(0)) << Qt::CaseInsensitive << 0; QTest::newRow("longlong = boundary") << QVariant(qlonglong(0x8000000000000000LL)) << QVariant(qlonglong(0x8000000000000000LL)) << Qt::CaseInsensitive << 0; QTest::newRow("longlong >") << QVariant(qlonglong(5)) << QVariant(qlonglong(2)) << Qt::CaseInsensitive << 1; QTest::newRow("longlong > boundary") << QVariant(qlonglong(0x7fffffffffffffffLL)) << QVariant(qlonglong(0x8000000000000000LL)) << Qt::CaseInsensitive << 1; // boundary // double (hmm, skips NaNs etc) QTest::newRow("double < inf 2") << QVariant(-qInf()) << QVariant(qInf()) << Qt::CaseInsensitive << -1; QTest::newRow("double < inf") << QVariant(1.0) << QVariant(qInf()) << Qt::CaseInsensitive << -1; QTest::newRow("double <") << QVariant(1.0) << QVariant(2.0) << Qt::CaseInsensitive << -1; QTest::newRow("double =") << QVariant(2.0) << QVariant(2.0) << Qt::CaseInsensitive << 0; QTest::newRow("double = 0") << QVariant(0.0) << QVariant(0.0) << Qt::CaseInsensitive << 0; QTest::newRow("double = inf") << QVariant(qInf()) << QVariant(qInf()) << Qt::CaseInsensitive << 0; QTest::newRow("double >") << QVariant(5.0) << QVariant(2.0) << Qt::CaseInsensitive << 1; QTest::newRow("double > inf") << QVariant(qInf()) << QVariant(5.0) << Qt::CaseInsensitive << 1; QTest::newRow("double > inf 2") << QVariant(0.0) << QVariant(-qInf()) << Qt::CaseInsensitive << 1; QTest::newRow("double > inf 3") << QVariant(qInf()) << QVariant(-qInf()) << Qt::CaseInsensitive << 1; // strings QTest::newRow("string <") << QVariant(QString("a")) << QVariant(QString("b")) << Qt::CaseInsensitive << -1; QTest::newRow("string <") << QVariant(QString("a")) << QVariant(QString("B")) << Qt::CaseInsensitive << -1; QTest::newRow("string <") << QVariant(QString("A")) << QVariant(QString("b")) << Qt::CaseInsensitive << -1; QTest::newRow("string <") << QVariant(QString("A")) << QVariant(QString("B")) << Qt::CaseInsensitive << -1; QTest::newRow("string < cs") << QVariant(QString("a")) << QVariant(QString("b")) << Qt::CaseSensitive << -1; QTest::newRow("string < cs 2") << QVariant(QString("A")) << QVariant(QString("b")) << Qt::CaseSensitive << -1; QTest::newRow("string < length") << QVariant(QString("a")) << QVariant(QString("aa")) << Qt::CaseInsensitive << -1; QTest::newRow("string < length cs") << QVariant(QString("a")) << QVariant(QString("aa")) << Qt::CaseSensitive << -1; QTest::newRow("string < length 2") << QVariant(QString("a")) << QVariant(QString("ba")) << Qt::CaseInsensitive << -1; QTest::newRow("string < length cs 2") << QVariant(QString("a")) << QVariant(QString("ba")) << Qt::CaseSensitive << -1; QTest::newRow("string aa < b") << QVariant(QString("aa")) << QVariant(QString("b")) << Qt::CaseInsensitive << -1; QTest::newRow("string aa < b cs") << QVariant(QString("aa")) << QVariant(QString("b")) << Qt::CaseSensitive << -1; QTest::newRow("string '' < a") << QVariant(QString("")) << QVariant(QString("aa")) << Qt::CaseInsensitive << -1; QTest::newRow("string '' < aa cs") << QVariant(QString("")) << QVariant(QString("aa")) << Qt::CaseSensitive << -1; QTest::newRow("string 0 < a") << QVariant(QString()) << QVariant(QString("aa")) << Qt::CaseInsensitive << -1; QTest::newRow("string 0 < aa cs") << QVariant(QString()) << QVariant(QString("aa")) << Qt::CaseSensitive << -1; QTest::newRow("string '' = ''") << QVariant(QString("")) << QVariant(QString("")) << Qt::CaseInsensitive << 0; QTest::newRow("string '' = '' cs") << QVariant(QString("")) << QVariant(QString("")) << Qt::CaseSensitive << 0; QTest::newRow("string 0 = 0") << QVariant(QString()) << QVariant(QString()) << Qt::CaseInsensitive << 0; QTest::newRow("string 0 = 0 cs") << QVariant(QString()) << QVariant(QString()) << Qt::CaseSensitive << 0; QTest::newRow("string a = a") << QVariant(QString("a")) << QVariant(QString("a")) << Qt::CaseInsensitive << 0; QTest::newRow("string a = a cs") << QVariant(QString("a")) << QVariant(QString("a")) << Qt::CaseSensitive << 0; // Stringlists // {} < {"a"} < {"aa"} < {"aa","bb"} < {"aa", "cc"} < {"bb"} QStringList empty; QStringList listA("a"); QStringList listAA("aa"); QStringList listAABB; listAABB << "aa" << "bb"; QStringList listAACC; listAACC << "aa" << "cc"; QStringList listBB; listBB << "bb"; QStringList listCCAA; listCCAA << "cc" << "aa"; QStringList listA2("A"); QStringList listAA2("AA"); QTest::newRow("stringlist {} < {a}") << QVariant(empty) << QVariant(listA) << Qt::CaseInsensitive << -1; QTest::newRow("stringlist {} < {a} cs") << QVariant(empty) << QVariant(listA) << Qt::CaseSensitive << -1; QTest::newRow("stringlist {} < {A}") << QVariant(empty) << QVariant(listA2) << Qt::CaseInsensitive << -1; QTest::newRow("stringlist {} < {A} cs") << QVariant(empty) << QVariant(listA2) << Qt::CaseSensitive << -1; QTest::newRow("stringlist {a} < {aa}") << QVariant(listA) << QVariant(listAA) << Qt::CaseInsensitive << -1; QTest::newRow("stringlist {a} < {aa} cs") << QVariant(listA) << QVariant(listAA) << Qt::CaseSensitive << -1; QTest::newRow("stringlist {a} < {AA}") << QVariant(listA) << QVariant(listAA2) << Qt::CaseInsensitive << -1; QTest::newRow("stringlist {a} < {AA} cs") << QVariant(listA) << QVariant(listAA2) << Qt::CaseSensitive << -1; QTest::newRow("stringlist {A} < {aa,bb}") << QVariant(listA2) << QVariant(listAABB) << Qt::CaseInsensitive << -1; QTest::newRow("stringlist {A} < {aa,bb} cs") << QVariant(listA2) << QVariant(listAABB) << Qt::CaseSensitive << -1; QTest::newRow("stringlist {aa} < {aa,bb}") << QVariant(listAA) << QVariant(listAABB) << Qt::CaseInsensitive << -1; QTest::newRow("stringlist {aa} < {aa,bb} cs") << QVariant(listAA) << QVariant(listAABB) << Qt::CaseSensitive << -1; QTest::newRow("stringlist {aa,bb} < {aa,cc}") << QVariant(listAABB) << QVariant(listAACC) << Qt::CaseInsensitive << -1; QTest::newRow("stringlist {aa,bb} < {aa,cc} cs") << QVariant(listAABB) << QVariant(listAACC) << Qt::CaseSensitive << -1; QTest::newRow("stringlist {aa,cc} < {bb}") << QVariant(listAACC) << QVariant(listBB) << Qt::CaseInsensitive << -1; QTest::newRow("stringlist {aa,cc} < {bb} cs") << QVariant(listAACC) << QVariant(listBB) << Qt::CaseSensitive << -1; // equality QTest::newRow("stringlist {} = {}") << QVariant(empty) << QVariant(empty) << Qt::CaseInsensitive << 0; QTest::newRow("stringlist {} = {} cs") << QVariant(empty) << QVariant(empty) << Qt::CaseSensitive << 0; QTest::newRow("stringlist {aa} = {aa}") << QVariant(listAA) << QVariant(listAA) << Qt::CaseInsensitive << 0; QTest::newRow("stringlist {aa} = {AA}") << QVariant(listAA) << QVariant(listAA2) << Qt::CaseInsensitive << 0; QTest::newRow("stringlist {aa} = {aa} cs") << QVariant(listAA) << QVariant(listAA) << Qt::CaseSensitive << 0; // Times QTime t0; QTime t1(0,0,0,0); QTime t2(0,59,0,0); QTime t3(1,0,0,0); QTime t4(23,59,59,999); QTest::newRow("times t0 < t1") << QVariant(t0) << QVariant(t1) << Qt::CaseInsensitive << -1; QTest::newRow("times t1 < t2") << QVariant(t1) << QVariant(t2) << Qt::CaseInsensitive << -1; QTest::newRow("times t2 < t3") << QVariant(t2) << QVariant(t3) << Qt::CaseInsensitive << -1; QTest::newRow("times t3 < t4") << QVariant(t3) << QVariant(t4) << Qt::CaseInsensitive << -1; QTest::newRow("times t0 = t0") << QVariant(t0) << QVariant(t0) << Qt::CaseInsensitive << 0; QTest::newRow("times t4 = t4") << QVariant(t4) << QVariant(t4) << Qt::CaseInsensitive << 0; // Dates QDate d0; QDate d1 = QDate::fromJulianDay(1); QDate d2(1,1,1); QDate d3(2011,6,9); QDate d4 = QDate::fromJulianDay(0x7fffffff); QDate d5 = QDate::fromJulianDay(0x80000000); QDate d6 = QDate::fromJulianDay(0xffffffff); QTest::newRow("dates d0 < d1") << QVariant(d0) << QVariant(d1) << Qt::CaseInsensitive << -1; QTest::newRow("dates d1 < d2") << QVariant(d1) << QVariant(d2) << Qt::CaseInsensitive << -1; QTest::newRow("dates d2 < d3") << QVariant(d2) << QVariant(d3) << Qt::CaseInsensitive << -1; QTest::newRow("dates d3 < d4") << QVariant(d3) << QVariant(d4) << Qt::CaseInsensitive << -1; QTest::newRow("dates d4 < d5") << QVariant(d4) << QVariant(d5) << Qt::CaseInsensitive << -1; QTest::newRow("dates d5 < d6") << QVariant(d5) << QVariant(d6) << Qt::CaseInsensitive << -1; QTest::newRow("dates d0 < d6") << QVariant(d0) << QVariant(d6) << Qt::CaseInsensitive << -1; QTest::newRow("dates d1 < d6") << QVariant(d1) << QVariant(d6) << Qt::CaseInsensitive << -1; QTest::newRow("dates d0 = d0") << QVariant(d0) << QVariant(d0) << Qt::CaseInsensitive << 0; QTest::newRow("dates d1 = d1") << QVariant(d1) << QVariant(d1) << Qt::CaseInsensitive << 0; QTest::newRow("dates d2 = d2") << QVariant(d2) << QVariant(d2) << Qt::CaseInsensitive << 0; QTest::newRow("dates d3 = d3") << QVariant(d3) << QVariant(d3) << Qt::CaseInsensitive << 0; QTest::newRow("dates d4 = d4") << QVariant(d4) << QVariant(d4) << Qt::CaseInsensitive << 0; QTest::newRow("dates d5 = d5") << QVariant(d5) << QVariant(d5) << Qt::CaseInsensitive << 0; QTest::newRow("dates d6 = d6") << QVariant(d6) << QVariant(d6) << Qt::CaseInsensitive << 0; // DateTimes // Somewhat limited testing here QDateTime dt0; QDateTime dt1(d1, t1); QDateTime dt2(d1, t2); QDateTime dt3(d4, t4); QDateTime dt4(d5, t1); QDateTime dt5(d6, t4); // end of the universe // Note: this test used to verify that null QDateTime compared less than a non-null date prior to 1AD // The current Qt behavior is to compare null as the UNIX epoch, so we will follow that QTest::newRow("datetimes dt0 > dt1") << QVariant(dt0) << QVariant(dt1) << Qt::CaseInsensitive << 1; QTest::newRow("datetimes dt1 < dt2") << QVariant(dt1) << QVariant(dt2) << Qt::CaseInsensitive << -1; QTest::newRow("datetimes dt2 < dt3") << QVariant(dt2) << QVariant(dt3) << Qt::CaseInsensitive << -1; QTest::newRow("datetimes dt3 < dt4") << QVariant(dt3) << QVariant(dt4) << Qt::CaseInsensitive << -1; QTest::newRow("datetimes dt4 < dt5") << QVariant(dt4) << QVariant(dt5) << Qt::CaseInsensitive << -1; QTest::newRow("datetimes dt0 < dt5") << QVariant(dt0) << QVariant(dt5) << Qt::CaseInsensitive << -1; QTest::newRow("datetimes dt1 < dt5") << QVariant(dt1) << QVariant(dt5) << Qt::CaseInsensitive << -1; QTest::newRow("datetimes dt0 = dt0") << QVariant(dt0) << QVariant(dt0) << Qt::CaseInsensitive << 0; QTest::newRow("datetimes dt1 = dt1") << QVariant(dt1) << QVariant(dt1) << Qt::CaseInsensitive << 0; QTest::newRow("datetimes dt2 = dt2") << QVariant(dt2) << QVariant(dt2) << Qt::CaseInsensitive << 0; QTest::newRow("datetimes dt3 = dt3") << QVariant(dt3) << QVariant(dt3) << Qt::CaseInsensitive << 0; QTest::newRow("datetimes dt4 = dt4") << QVariant(dt4) << QVariant(dt4) << Qt::CaseInsensitive << 0; QTest::newRow("datetimes dt5 = dt5") << QVariant(dt5) << QVariant(dt5) << Qt::CaseInsensitive << 0; } QTEST_MAIN(tst_QContactManager) #include "tst_qcontactmanager.moc" tests/auto/contacts/qcontactmanagerdataholder.h000066400000000000000000000100721233466112000223540ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTMANAGERDATAHOLDER_H #define QCONTACTMANAGERDATAHOLDER_H #include #include #include #include #include // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // QT_BEGIN_NAMESPACE_CONTACTS class QContact; QT_END_NAMESPACE_CONTACTS QTCONTACTS_USE_NAMESPACE class QContactManagerDataHolder { public: QContactManagerDataHolder() { QStringList managerNames = QContactManager::availableManagers(); foreach(const QString& mgr, managerNames) { // Don't bother with these if (mgr == "memory" || mgr == "invalid" || mgr == "testdummy" || mgr == "maliciousplugin" || mgr == "testlazy") continue; QMap params; QString mgrUri = QContactManager::buildUri(mgr, params); QContactManager* cm = QContactManager::fromUri(mgrUri); if (cm) { qDebug() << "Saving contacts for" << mgrUri; QList contacts = cm->contacts(); savedContacts.insert(cm->managerName(),contacts); QList ids; foreach(const QContact& contact, contacts) ids.append(contact.id()); cm->removeContacts(ids, 0); delete cm; } } } ~QContactManagerDataHolder() { foreach(const QString& mgrUri, savedContacts.keys()) { QContactManager* cm = QContactManager::fromUri(mgrUri); if (cm) { qDebug() << "Restoring contacts for" << mgrUri; QList contacts = savedContacts.value(mgrUri); cm->saveContacts(&contacts, 0); // XXX this doesn't restore relationships.. delete cm; } } } private: QMap > savedContacts; }; #endif // QCONTACTMANAGERDATAHOLDER_H tests/auto/contacts/qcontactmanagerdetails/000077500000000000000000000000001233466112000215215ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerdetails/partitions.json000066400000000000000000000001211233466112000246020ustar00rootroot00000000000000[ { "name" : "com.nokia.mt.User" }, { "name" : "com.nokia.mt.System" } ] tests/auto/contacts/qcontactmanagerdetails/qcontactmanagerdetails.pro000066400000000000000000000004161233466112000267610ustar00rootroot00000000000000include(../../auto.pri) QT += contacts contacts-private qtHaveModule(jsondb) { QT += jsondb } HEADERS += ../qcontactmanagerdataholder.h SOURCES += tst_qcontactmanagerdetails.cpp INCLUDEPATH += .. \ ../.. DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactmanagerdetails/tst_qcontactmanagerdetails.cpp000066400000000000000000000405201233466112000276350ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include "qcontactmanagerdataholder.h" #include "jsondbprocess.h" QTCONTACTS_USE_NAMESPACE // to get QFETCH to work with the template expression... typedef QMap tst_QContactManagerDetails_QStringMap; Q_DECLARE_METATYPE(tst_QContactManagerDetails_QStringMap) class tst_QContactManagerDetails : public QObject { Q_OBJECT public: tst_QContactManagerDetails(); virtual ~tst_QContactManagerDetails(); private: void addManagers(); template void removeDetail( QContact& contact ) { T detail = contact.detail(); contact.removeDetail( &detail ); } bool saveAndLoadContact( QContactManager *cm, QContact &original, QContact &loaded ); void saveAndVerifyContact( QContactManager *cm, QContact &original ); QScopedPointer managerDataHolder; JsonDbProcess m_jsondbProcess; private slots: void initTestCase(); void cleanupTestCase(); void testAddress(); void testAddress_data() {addManagers();} void testAnniversary(); void testAnniversary_data() {addManagers();} void testAvatar(); void testAvatar_data() {addManagers();} void testBirthday(); void testBirthday_data() {addManagers();} void testDisplayLabel(); void testDisplayLabel_data() {addManagers();} void testEmailAddress(); void testEmailAddress_data() {addManagers();} void testEmptyExtendedDetail(); void testEmptyExtendedDetail_data() {addManagers();} void testExtendedDetail(); void testExtendedDetail_data() {addManagers();} void testGuid(); void testGuid_data() {addManagers();} void testName(); void testName_data() {addManagers();} void testNickName(); void testNickName_data() {addManagers();} void testOrganisation(); void testOrganisation_data() {addManagers();} void testOnlineAccount(); void testOnlineAccount_data() {addManagers();} void testPhoneNumber(); void testPhoneNumber_data() {addManagers();} void testSyncTarget(); void testSyncTarget_data() {addManagers();} void testUrl(); void testUrl_data() {addManagers();} void testRingtone(); void testRingtone_data() {addManagers();} private slots: }; tst_QContactManagerDetails::tst_QContactManagerDetails() { } tst_QContactManagerDetails::~tst_QContactManagerDetails() { } void tst_QContactManagerDetails::addManagers() { QTest::addColumn("uri"); QStringList managers = QContactManager::availableManagers(); /* Known one that will not pass */ managers.removeAll("invalid"); managers.removeAll("testdummy"); managers.removeAll("teststaticdummy"); managers.removeAll("maliciousplugin"); foreach(QString mgr, managers) { QMap params; QTest::newRow(QString("mgr='%1'").arg(mgr).toLatin1().constData()) << QContactManager::buildUri(mgr, params); } } void tst_QContactManagerDetails::initTestCase() { // Start JsonDb daemon if needed if (QContactManager::availableManagers().contains("jsondb")) { QString partitions_json = QFINDTESTDATA("partitions.json"); QVERIFY2(!partitions_json.isEmpty(), "partitions.json file is missing"); QVERIFY2(m_jsondbProcess.start(partitions_json), "Failed to start JsonDb process"); } managerDataHolder.reset(new QContactManagerDataHolder()); } void tst_QContactManagerDetails::cleanupTestCase() { managerDataHolder.reset(0); if (QContactManager::availableManagers().contains("jsondb")) m_jsondbProcess.terminate(); } bool tst_QContactManagerDetails::saveAndLoadContact( QContactManager *cm, QContact &original, QContact &loaded ) { // Save contact if( cm->saveContact(&original) == false ) return false; // Check the id if (original.id().isNull()) return false; // Load same contact from database loaded = cm->contact(original.id()); if( cm->error() ) return false; // Ignore some details which are not relevant and will mess // up direct comparison between two contacts. removeDetail(original); removeDetail(loaded); removeDetail(original); removeDetail(loaded); removeDetail(loaded); return true; } void tst_QContactManagerDetails::saveAndVerifyContact( QContactManager *cm, QContact &original ) { QContact loaded; QVERIFY( saveAndLoadContact( cm, original, loaded ) ); if (original != loaded) { qDebug() << "expected: " << original; qDebug() << "loaded: " << loaded; QCOMPARE( loaded.details().count(), original.details().count() ); QCOMPARE( loaded, original ); } } void tst_QContactManagerDetails::testAddress() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QContact c; QContactAddress a; a.setStreet("street"); a.setLocality("locality"); a.setRegion("region"); a.setPostcode("postcode"); a.setCountry("country"); a.setPostOfficeBox("POBox"); c.saveDetail( &a ); saveAndVerifyContact( cm.data(), c ); QContact c1; QContactAddress a1; a1.setStreet("street1"); a1.setLocality("locality1"); a1.setRegion("region1"); a1.setPostcode("postcode1"); a1.setCountry("country1"); a1.setPostOfficeBox("POBox1"); c1.saveDetail( &a1 ); QContactAddress a2; a2.setStreet("street2"); a2.setLocality("locality2"); a2.setRegion("region2"); a2.setPostcode("postcode2"); a2.setCountry("country2"); a2.setPostOfficeBox("POBox2"); c1.saveDetail( &a2 ); QContact c2; saveAndLoadContact( cm.data(), c1, c2 ); QList addresses = c2.details(); QCOMPARE(addresses.count(), 2); QCOMPARE(addresses[0], a1); QCOMPARE(addresses[1], a2); QContact c3; // General address QContactAddress a3; a3.setStreet("street1"); c3.saveDetail( &a3 ); // home address QContactAddress a4; a4.setStreet("street2"); a4.setContexts( QContactDetail::ContextHome ); c3.saveDetail( &a4 ); // work address QContactAddress a5; a5.setStreet("street3"); a5.setContexts( QContactDetail::ContextWork ); c3.saveDetail( &a5 ); saveAndVerifyContact( cm.data(), c3 ); } void tst_QContactManagerDetails::testAnniversary() { QFETCH(QString, uri); if (uri == "qtcontacts:jsondb:") { QSKIP("JsonDb backend does not support QContactAnniversary detail!"); } else { QScopedPointer cm(QContactManager::fromUri(uri)); QContact c; QContactAnniversary a; a.setOriginalDate( QDate(2009,9,9) ); c.saveDetail( &a ); saveAndVerifyContact( cm.data(), c ); } } void tst_QContactManagerDetails::testAvatar() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QContact c; QContactAvatar a; a.setImageUrl( QUrl::fromLocalFile("c:\\foo\\bar.jpeg") ); c.saveDetail( &a ); saveAndVerifyContact( cm.data(), c ); } void tst_QContactManagerDetails::testBirthday() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QContact c; QContactBirthday b; b.setDateTime( QDateTime(QDate(2001,1,1)) ); c.saveDetail( &b ); saveAndVerifyContact( cm.data(), c ); } void tst_QContactManagerDetails::testEmailAddress() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QContact c; QContactEmailAddress e; e.setEmailAddress( "foo@bar.com" ); c.saveDetail( &e ); saveAndVerifyContact( cm.data(), c ); } void tst_QContactManagerDetails::testName() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QContact c; QContactName n; n.setPrefix( "prefix" ); n.setFirstName( "first" ); n.setMiddleName( "middle" ); n.setLastName( "last" ); n.setSuffix( "suffix" ); c.saveDetail( &n ); saveAndVerifyContact( cm.data(), c ); } void tst_QContactManagerDetails::testDisplayLabel() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QContact originalContact; QContactDisplayLabel dl1; dl1.setLabel("displayLabel1"); originalContact.saveDetail(&dl1); saveAndVerifyContact(cm.data(), originalContact); QContact loadedContact; QVERIFY(saveAndLoadContact(cm.data(), originalContact, loadedContact)); QCOMPARE(loadedContact.details().count(), originalContact.details().count()); QCOMPARE(loadedContact, originalContact); } void tst_QContactManagerDetails::testNickName() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QContact originalContact; QContactNickname n1; n1.setNickname("nickname1"); originalContact.saveDetail(&n1); saveAndVerifyContact(cm.data(), originalContact); QContactNickname n2; n2.setNickname("nickname2"); originalContact.saveDetail(&n2); QContact loadedContact; QVERIFY(saveAndLoadContact(cm.data(), originalContact, loadedContact)); if (uri == "qtcontacts:jsondb:") QEXPECT_FAIL("", "JsonDb backend does not support QContactNickname properly!", Abort); QCOMPARE(loadedContact.details().count(), originalContact.details().count()); QCOMPARE(loadedContact, originalContact); } void tst_QContactManagerDetails::testOrganisation() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QContact c; QContactOrganization o; o.setName( "Foreign legion" ); o.setTitle( "Bicycle mechanic" ); c.saveDetail( &o ); saveAndVerifyContact( cm.data(), c ); } void tst_QContactManagerDetails::testOnlineAccount() { QFETCH(QString, uri); if (uri == "qtcontacts:jsondb:") { QSKIP("JsonDb backend does not support QContactOnlineAccount detail!"); } else { QScopedPointer cm(QContactManager::fromUri(uri)); QContact c; QContactOnlineAccount o; o.setAccountUri( "john@example.com" ); o.setProtocol(QContactOnlineAccount::ProtocolJabber); c.saveDetail( &o ); saveAndVerifyContact( cm.data(), c ); } } void tst_QContactManagerDetails::testPhoneNumber() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QContact c; // General landline number QContactPhoneNumber n2; n2.setNumber( "2" ); n2.setSubTypes( QList() << QContactPhoneNumber::SubTypeLandline ); c.saveDetail( &n2 ); saveAndVerifyContact( cm.data(), c ); QContact c2; // home mobile number QContactPhoneNumber n1; n1.setNumber( "1" ); n1.setSubTypes( QList() << QContactPhoneNumber::SubTypeMobile ); n1.setContexts( QContactDetail::ContextHome ); c2.saveDetail( &n1 ); // work landline number QContactPhoneNumber n3; n3.setNumber( "3" ); n3.setSubTypes( QList() << QContactPhoneNumber::SubTypeLandline ); n3.setContexts( QContactDetail::ContextWork ); c2.saveDetail( &n3 ); saveAndVerifyContact( cm.data(), c2 ); } void tst_QContactManagerDetails::testSyncTarget() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QContact c; QContactSyncTarget syncTarget; syncTarget.setSyncTarget("TestSyncTarget" ); c.saveDetail( &syncTarget ); saveAndVerifyContact( cm.data(), c ); } void tst_QContactManagerDetails::testUrl() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QContact originalContact; QContactUrl u; u.setUrl("http://failblog.org"); u.setSubType(QContactUrl::SubTypeHomePage); originalContact.saveDetail(&u); QContact loadedContact; QVERIFY(saveAndLoadContact(cm.data(), originalContact, loadedContact)); QCOMPARE( loadedContact.details().count(), originalContact.details().count() ); if (uri == "qtcontacts:jsondb:") QEXPECT_FAIL("", "JsonDb backend does not support QContactUrl detail properly!", Abort); QCOMPARE( loadedContact, originalContact ); } void tst_QContactManagerDetails::testRingtone() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QUrl rtUrl(QUrl::fromLocalFile("/home/user/sample.wav")); QContact c; QContactRingtone rt; rt.setAudioRingtoneUrl(rtUrl); c.saveDetail(&rt); saveAndVerifyContact( cm.data(), c ); } void tst_QContactManagerDetails::testEmptyExtendedDetail() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QContact c; // Adding an empty extended detail QContactExtendedDetail emptyDetail; QVERIFY(c.saveDetail(&emptyDetail)); if (uri == "qtcontacts:jsondb:") { QSKIP("This manager does not store empty extended details, so skipping empty extended detail test!"); } else { saveAndVerifyContact(cm.data(), c); } } void tst_QContactManagerDetails::testExtendedDetail() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QContact c; QContactExtendedDetail extendedDetail1, extendedDetail2; extendedDetail1.setName("testExtDetailVariantList"); extendedDetail1.setData(QVariantList() << QString("QString in the QVariantlist detail.") << QVariant(1) << QString("Another QString in the QVariantlist detail.") << 2); QVERIFY(c.saveDetail(&extendedDetail1)); saveAndVerifyContact(cm.data(), c); // Adding two extended details to same contact QContact c2; extendedDetail2.setName("testExtDetailInt"); extendedDetail2.setData(2); QVERIFY (c2.saveDetail(&extendedDetail2)); QVERIFY (c2.saveDetail(&extendedDetail1)); saveAndVerifyContact(cm.data(), c2); // Adding same details but in different order QContact c3; QVERIFY (c3.saveDetail(&extendedDetail1)); QVERIFY (c3.saveDetail(&extendedDetail2)); saveAndVerifyContact(cm.data(), c3); } void tst_QContactManagerDetails::testGuid() { QFETCH(QString, uri); QScopedPointer cm(QContactManager::fromUri(uri)); QContact c; QContactGuid guid; guid.setGuid("TestGUID" ); c.saveDetail( &guid ); saveAndVerifyContact( cm.data(), c ); } QTEST_MAIN(tst_QContactManagerDetails) #include "tst_qcontactmanagerdetails.moc" tests/auto/contacts/qcontactmanagerfiltering/000077500000000000000000000000001233466112000220575ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerfiltering/qcontactmanagerfiltering.pro000066400000000000000000000002401233466112000276500ustar00rootroot00000000000000include(../../auto.pri) TEMPLATE=subdirs SUBDIRS += unittest qtHaveModule(serviceframework) { DEFINES += INCLUDE_TESTACTIONS SUBDIRS += testactions } tests/auto/contacts/qcontactmanagerfiltering/testactions/000077500000000000000000000000001233466112000244175ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerfiltering/testactions/booleanaction/000077500000000000000000000000001233466112000272345ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerfiltering/testactions/booleanaction/booleanaction.pro000066400000000000000000000015641233466112000326010ustar00rootroot00000000000000TEMPLATE = lib CONFIG += plugin testplugin TARGET = $$qtLibraryTarget(contacts_booleanaction) PLUGIN_TYPE=contacts include(../../../../../common.pri) INCLUDEPATH += ../../../../../src/serviceframework \ ../../../../../src/contacts \ ../../../../../src/contacts/details \ ../../../../../src/contacts/requests \ ../../../../../src/contacts/filters INCLUDEPATH += ../../../ CONFIG += mobility MOBILITY = contacts serviceframework DEFINES += ACTIONPLUGINTARGET=contacts_booleanaction DEFINES += ACTIONPLUGINNAME=BooleanAction HEADERS += booleanaction_p.h xml.path = $$DESTDIR/xmldata xml.files = xmldata/booleanactionservice.xml xml.CONFIG = no_link no_dependencies explicit_dependencies no_build combine ignore_no_exist no_clean INSTALLS += xml build_pass:ALL_DEPS+=install_xml DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactmanagerfiltering/testactions/booleanaction/booleanaction_p.h000066400000000000000000000140121233466112000325370ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTTESTBOOLEANACTION_P_H #define QCONTACTTESTBOOLEANACTION_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include "qserviceplugininterface.h" #include "qservicecontext.h" #include "qserviceinterfacedescriptor.h" #include #include #include QTCONTACTS_USE_NAMESPACE class DummyAction : public QContactAction { public: DummyAction(QObject* parent = 0) { Q_UNUSED(parent) } bool invokeAction(const QContactActionTarget&, const QVariantMap&) { // Well, do something emit stateChanged(QContactAction::FinishedState); return true; } bool invokeAction(const QList&, const QVariantMap&) { // Well, do something emit stateChanged(QContactAction::FinishedState); return true; } QVariantMap results() const { return QVariantMap(); } State state() const {return QContactAction::FinishedState;} }; class QBooleanAction : public DummyAction { Q_OBJECT public: QBooleanAction(QObject *parent = 0) : DummyAction(parent) {} ~QBooleanAction() {} }; class QBooleanActionFactory : public QContactActionFactory { Q_OBJECT public: QBooleanActionFactory(QObject* parent = 0) : QContactActionFactory(parent) { m_actionDescriptor = createDescriptor("BooleanAction", "tst_qcontactmanagerfiltering:booleanaction", "booleanaction", 1); } ~QBooleanActionFactory() { } QList actionDescriptors() const { QList retn; retn << m_actionDescriptor; return retn; } QContactAction* create(const QContactActionDescriptor& which) const { Q_UNUSED(which) return new QBooleanAction; } QSet supportedTargets(const QContact& contact, const QContactActionDescriptor& which) const { Q_UNUSED(which) QSet retn; QList boolDets = contact.details("BooleanDefinition"); foreach (const QContactDetail& det, boolDets) { if (det.variantValue("BooleanField").toBool()) { QContactActionTarget curr(contact, det); retn << curr; } } return retn; } QContactFilter contactFilter(const QContactActionDescriptor& which) const { Q_UNUSED(which) /* This only likes bools that are true */ QContactDetailFilter df; // XXX TODO: find some way to pass the defAndFieldNamesForTypeForActions value for Bool to this function... df.setDetailDefinitionName("BooleanDefinition", "BooleanField"); df.setValue(true); return df; } QVariant metaData(const QString& key, const QList& targets, const QVariantMap& parameters, const QContactActionDescriptor& which) const { Q_UNUSED(key); Q_UNUSED(targets); Q_UNUSED(parameters); Q_UNUSED(which); return QVariant(); } bool supportsContact(const QContact& contact, const QContactActionDescriptor& which) const { return supportedTargets(contact, which).isEmpty(); } private: QContactActionDescriptor m_actionDescriptor; }; class QBooleanActionPlugin : public QObject, public QServicePluginInterface { Q_OBJECT Q_INTERFACES(QtMobility::QServicePluginInterface) public: QObject* createInstance(const QServiceInterfaceDescriptor& descriptor, QServiceContext* context, QAbstractSecuritySession* session) { Q_UNUSED(context); Q_UNUSED(session); if (descriptor.interfaceName() == QContactActionFactory::InterfaceName) return new QBooleanActionFactory(this); else return 0; } }; Q_EXPORT_PLUGIN2(contacts_booleanaction, QBooleanActionPlugin); #endif tests/auto/contacts/qcontactmanagerfiltering/testactions/booleanaction/xmldata/000077500000000000000000000000001233466112000306665ustar00rootroot00000000000000booleanactionservice.xml000066400000000000000000000011361233466112000355300ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerfiltering/testactions/booleanaction/xmldata tst_qcontactmanagerfiltering:booleanaction plugins/contacts/libcontacts_booleanaction This service provides a QContactAction plugin for testing purposes - boolean org.qt-project.Qt.SampleContactsActionPlugin 1.1 BooleanAction Interface which returns a boolean tests/auto/contacts/qcontactmanagerfiltering/testactions/dateaction/000077500000000000000000000000001233466112000265325ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerfiltering/testactions/dateaction/dateaction.pro000066400000000000000000000015451233466112000313740ustar00rootroot00000000000000TEMPLATE = lib CONFIG += plugin testplugin TARGET = $$qtLibraryTarget(contacts_dateaction) PLUGIN_TYPE=contacts include(../../../../../common.pri) INCLUDEPATH += ../../../../../src/serviceframework \ ../../../../../src/contacts \ ../../../../../src/contacts/details \ ../../../../../src/contacts/requests \ ../../../../../src/contacts/filters INCLUDEPATH += ../../../ CONFIG += mobility MOBILITY = contacts serviceframework DEFINES += ACTIONPLUGINTARGET=contacts_dateaction DEFINES += ACTIONPLUGINNAME=DateAction HEADERS += dateaction_p.h xml.path = $$DESTDIR/xmldata xml.files = xmldata/dateactionservice.xml xml.CONFIG = no_link no_dependencies explicit_dependencies no_build combine ignore_no_exist no_clean INSTALLS += xml build_pass:ALL_DEPS+=install_xml DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactmanagerfiltering/testactions/dateaction/dateaction_p.h000066400000000000000000000137451233466112000313470ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTTESTDATEACTION_P_H #define QCONTACTTESTDATEACTION_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include "qserviceplugininterface.h" #include "qservicecontext.h" #include "qserviceinterfacedescriptor.h" #include #include #include QTCONTACTS_USE_NAMESPACE class DummyAction : public QContactAction { public: DummyAction(QObject* parent = 0) { Q_UNUSED(parent) } QVariantMap metaData() const {return QVariantMap();} bool invokeAction(const QContactActionTarget&, const QVariantMap&) { // Well, do something emit stateChanged(QContactAction::FinishedState); return true; } bool invokeAction(const QList&, const QVariantMap&) { // Well, do something emit stateChanged(QContactAction::FinishedState); return true; } QVariantMap results() const { return QVariantMap(); } State state() const {return QContactAction::FinishedState;} }; /* Static actions for testing matching */ class QDateAction : public DummyAction { Q_OBJECT public: QDateAction(QObject *parent = 0) : DummyAction(parent) {} ~QDateAction() {} }; class QDateActionFactory : public QContactActionFactory { Q_OBJECT public: QDateActionFactory(QObject* parent = 0) : QContactActionFactory(parent) { m_actionDescriptor = createDescriptor("DateAction", "tst_qcontactmanagerfiltering:dateaction", "dateaction", 1); } ~QDateActionFactory() { } QList actionDescriptors() const { QList retn; retn << m_actionDescriptor; return retn; } QContactAction* create(const QContactActionDescriptor& which) const { Q_UNUSED(which) return new QDateAction; } QSet supportedTargets(const QContact& contact, const QContactActionDescriptor& which) const { Q_UNUSED(which) QSet retn; QList dateDets = contact.details("DateDefinition"); foreach (const QContactDetail& det, dateDets) { if (det.variantValue("DateField").isValid()) { QContactActionTarget curr(contact, det); retn << curr; } } return retn; } QContactFilter contactFilter(const QContactActionDescriptor& which) const { Q_UNUSED(which) QContactDetailFilter df; // XXX TODO: find some way to pass the defAndFieldNamesForTypeForActions value for Date to this function... df.setDetailDefinitionName("DateDefinition", "DateField"); return df; } QVariant metaData(const QString& key, const QList& targets, const QVariantMap& parameters, const QContactActionDescriptor& which) const { Q_UNUSED(key); Q_UNUSED(targets); Q_UNUSED(parameters); Q_UNUSED(which); return QVariant(); } bool supportsContact(const QContact& contact, const QContactActionDescriptor& which) const { return supportedTargets(contact, which).isEmpty(); } private: QContactActionDescriptor m_actionDescriptor; }; class QDateActionPlugin : public QObject, public QServicePluginInterface { Q_OBJECT Q_INTERFACES(QtMobility::QServicePluginInterface) public: QObject* createInstance(const QServiceInterfaceDescriptor& descriptor, QServiceContext* context, QAbstractSecuritySession* session) { Q_UNUSED(context); Q_UNUSED(session); if (descriptor.interfaceName() == QContactActionFactory::InterfaceName) return new QDateActionFactory(this); else return 0; } }; Q_EXPORT_PLUGIN2(contacts_dateaction, QDateActionPlugin); #endif tests/auto/contacts/qcontactmanagerfiltering/testactions/dateaction/xmldata/000077500000000000000000000000001233466112000301645ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerfiltering/testactions/dateaction/xmldata/dateactionservice.xml000066400000000000000000000011171233466112000344020ustar00rootroot00000000000000 tst_qcontactmanagerfiltering:dateaction plugins/contacts/libcontacts_dateaction This service provides a QContactAction plugin for testing purposes - date org.qt-project.Qt.SampleContactsActionPlugin 1.1 DateAction Interface which returns a date tests/auto/contacts/qcontactmanagerfiltering/testactions/integeraction/000077500000000000000000000000001233466112000272525ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerfiltering/testactions/integeraction/integeraction.pro000066400000000000000000000015641233466112000326350ustar00rootroot00000000000000TEMPLATE = lib CONFIG += plugin testplugin TARGET = $$qtLibraryTarget(contacts_integeraction) PLUGIN_TYPE=contacts include(../../../../../common.pri) INCLUDEPATH += ../../../../../src/serviceframework \ ../../../../../src/contacts \ ../../../../../src/contacts/details \ ../../../../../src/contacts/requests \ ../../../../../src/contacts/filters INCLUDEPATH += ../../../ CONFIG += mobility MOBILITY = contacts serviceframework DEFINES += ACTIONPLUGINTARGET=contacts_integeraction DEFINES += ACTIONPLUGINNAME=IntegerAction HEADERS += integeraction_p.h xml.path = $$DESTDIR/xmldata xml.files = xmldata/integeractionservice.xml xml.CONFIG = no_link no_dependencies explicit_dependencies no_build combine ignore_no_exist no_clean INSTALLS += xml build_pass:ALL_DEPS+=install_xml DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactmanagerfiltering/testactions/integeraction/integeraction_p.h000066400000000000000000000137661233466112000326120ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTTESTINTEGERACTION_P_H #define QCONTACTTESTINTEGERACTION_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include "qserviceplugininterface.h" #include "qservicecontext.h" #include "qserviceinterfacedescriptor.h" #include #include #include QTCONTACTS_USE_NAMESPACE class DummyAction : public QContactAction { public: DummyAction(QObject* parent = 0) { Q_UNUSED(parent) } QVariantMap metaData() const {return QVariantMap();} bool invokeAction(const QContactActionTarget&, const QVariantMap&) { // Well, do something emit stateChanged(QContactAction::FinishedState); return true; } bool invokeAction(const QList&, const QVariantMap&) { // Well, do something emit stateChanged(QContactAction::FinishedState); return true; } QVariantMap results() const { return QVariantMap(); } State state() const {return QContactAction::FinishedState;} }; class QIntegerAction : public DummyAction { Q_OBJECT public: QIntegerAction(QObject* parent = 0) : DummyAction(parent) {} ~QIntegerAction() {} }; class QIntegerActionFactory : public QContactActionFactory { Q_OBJECT public: QIntegerActionFactory(QObject* parent = 0) : QContactActionFactory(parent) { m_actionDescriptor = createDescriptor("IntegerAction", "tst_qcontactmanagerfiltering:integeraction", "integeraction", 1); } ~QIntegerActionFactory() { } QList actionDescriptors() const { QList retn; retn << m_actionDescriptor; return retn; } QContactAction* create(const QContactActionDescriptor& which) const { Q_UNUSED(which) return new QIntegerAction; } QSet supportedTargets(const QContact& contact, const QContactActionDescriptor& which) const { Q_UNUSED(which) QSet retn; QList intDets = contact.details("IntegerDefinition"); foreach (const QContactDetail& det, intDets) { if (det.variantValue("IntegerField").isValid()) { QContactActionTarget curr(contact, det); retn << curr; } } return retn; } QContactFilter contactFilter(const QContactActionDescriptor& which) const { Q_UNUSED(which) QContactDetailFilter df; // XXX TODO: find some way to pass the defAndFieldNamesForTypeForActions value for Integer to this function... df.setDetailDefinitionName("IntegerDefinition", "IntegerField"); return df; } QVariant metaData(const QString& key, const QList& targets, const QVariantMap& parameters, const QContactActionDescriptor& which) const { Q_UNUSED(key); Q_UNUSED(targets); Q_UNUSED(parameters); Q_UNUSED(which); return QVariant(); } bool supportsContact(const QContact& contact, const QContactActionDescriptor& which) const { return supportedTargets(contact, which).isEmpty(); } private: QContactActionDescriptor m_actionDescriptor; }; class QIntegerActionPlugin : public QObject, public QServicePluginInterface { Q_OBJECT Q_INTERFACES(QtMobility::QServicePluginInterface) public: QObject* createInstance(const QServiceInterfaceDescriptor& descriptor, QServiceContext* context, QAbstractSecuritySession* session) { Q_UNUSED(context); Q_UNUSED(session); if (descriptor.interfaceName() == QContactActionFactory::InterfaceName) return new QIntegerActionFactory(this); else return 0; } }; Q_EXPORT_PLUGIN2(contacts_integeraction, QIntegerActionPlugin); #endif tests/auto/contacts/qcontactmanagerfiltering/testactions/integeraction/xmldata/000077500000000000000000000000001233466112000307045ustar00rootroot00000000000000integeractionservice.xml000066400000000000000000000011501233466112000355600ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerfiltering/testactions/integeraction/xmldata tst_qcontactmanagerfiltering:integeraction plugins/contacts/libcontacts_integeraction This service provides a QContactAction plugin for testing purposes - integer org.qt-project.Qt.SampleContactsActionPlugin 1.1 IntegerAction Interface which allows returning an integer tests/auto/contacts/qcontactmanagerfiltering/testactions/numberaction/000077500000000000000000000000001233466112000271055ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerfiltering/testactions/numberaction/numberaction.pro000066400000000000000000000015531233466112000323210ustar00rootroot00000000000000TEMPLATE = lib CONFIG += plugin testplugin TARGET = $$qtLibraryTarget(contacts_numberaction) PLUGIN_TYPE=contacts include(../../../../../common.pri) INCLUDEPATH += ../../../../../src/serviceframework \ ../../../../../src/contacts \ ../../../../../src/contacts/details \ ../../../../../src/contacts/requests \ ../../../../../src/contacts/filters INCLUDEPATH += ../../../ CONFIG += mobility MOBILITY = contacts serviceframework DEFINES += ACTIONPLUGINTARGET=contacts_numberaction DEFINES += ACTIONPLUGINNAME=NumberAction HEADERS += numberaction_p.h xml.path = $$DESTDIR/xmldata xml.files = xml/numberactionservice.xml xml.CONFIG = no_link no_dependencies explicit_dependencies no_build combine ignore_no_exist no_clean INSTALLS += xml build_pass:ALL_DEPS+=install_xml DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactmanagerfiltering/testactions/numberaction/numberaction_p.h000066400000000000000000000150701233466112000322660ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTTESTNUMBERACTION_P_H #define QCONTACTTESTNUMBERACTION_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include "qserviceplugininterface.h" #include "qservicecontext.h" #include "qserviceinterfacedescriptor.h" #include #include #include QTCONTACTS_USE_NAMESPACE class DummyAction : public QContactAction { public: DummyAction(QObject* parent = 0) { Q_UNUSED(parent) } QVariantMap metaData() const {return QVariantMap();} bool invokeAction(const QContactActionTarget&, const QVariantMap&) { // Well, do something emit stateChanged(QContactAction::FinishedState); return true; } bool invokeAction(const QList&, const QVariantMap&) { // Well, do something emit stateChanged(QContactAction::FinishedState); return true; } QVariantMap results() const { return QVariantMap(); } State state() const {return QContactAction::FinishedState;} }; class QNumberAction : public DummyAction { Q_OBJECT public: QNumberAction(QObject *parent = 0) : DummyAction(parent) {} ~QNumberAction() {} }; class QNumberActionFactory : public QContactActionFactory { Q_OBJECT public: QNumberActionFactory(QObject* parent = 0) : QContactActionFactory(parent) { m_actionDescriptor = createDescriptor("NumberAction", "tst_qcontactmanagerfiltering:numberaction", "numberaction", 1); } ~QNumberActionFactory() { } QList actionDescriptors() const { QList retn; retn << m_actionDescriptor; return retn; } QContactAction* create(const QContactActionDescriptor& which) const { Q_UNUSED(which) return new QNumberAction; } QSet supportedTargets(const QContact& contact, const QContactActionDescriptor& which) const { Q_UNUSED(which) QSet retn; QList intDets = contact.details("IntegerDefinition"); foreach (const QContactDetail& det, intDets) { if (det.variantValue("IntegerField").isValid()) { QContactActionTarget curr(contact, det); retn << curr; } } QList doubleDets = contact.details("DoubleDefinition"); foreach (const QContactDetail& det, doubleDets) { if (det.variantValue("DoubleField").isValid()) { QContactActionTarget curr(contact, det); retn << curr; } } return retn; } QContactFilter contactFilter(const QContactActionDescriptor& which) const { Q_UNUSED(which) QContactDetailFilter df; // XXX TODO: find some way to pass the defAndFieldNamesForTypeForActions value for Double to this function... df.setDetailDefinitionName("DoubleDefinition", "DoubleField"); QContactDetailFilter df2; // XXX TODO: find some way to pass the defAndFieldNamesForTypeForActions value for Integer to this function... df2.setDetailDefinitionName("IntegerDefinition", "IntegerField"); /* We like either doubles or integers */ return df | df2; } QVariant metaData(const QString& key, const QList& targets, const QVariantMap& parameters, const QContactActionDescriptor& which) const { Q_UNUSED(key); Q_UNUSED(targets); Q_UNUSED(parameters); Q_UNUSED(which); return QVariant(); } bool supportsContact(const QContact& contact, const QContactActionDescriptor& which) const { return supportedTargets(contact, which).isEmpty(); } private: QContactActionDescriptor m_actionDescriptor; }; class QNumberActionPlugin : public QObject, public QServicePluginInterface { Q_OBJECT Q_INTERFACES(QtMobility::QServicePluginInterface) public: QObject* createInstance(const QServiceInterfaceDescriptor& descriptor, QServiceContext* context, QAbstractSecuritySession* session) { Q_UNUSED(context); Q_UNUSED(session); if (descriptor.interfaceName() == QContactActionFactory::InterfaceName) return new QNumberActionFactory(this); else return 0; } }; Q_EXPORT_PLUGIN2(contacts_numberaction, QNumberActionPlugin); #endif tests/auto/contacts/qcontactmanagerfiltering/testactions/numberaction/xml/000077500000000000000000000000001233466112000277055ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerfiltering/testactions/numberaction/xml/numberactionservice.xml000066400000000000000000000011311233466112000344720ustar00rootroot00000000000000 tst_qcontactmanagerfiltering:numberaction plugins/contacts/libcontacts_numberaction This service provides a QContactAction plugin for testing purposes - number org.qt-project.Qt.SampleContactsActionPlugin 1.1 NumberAction Interface which returns a number tests/auto/contacts/qcontactmanagerfiltering/testactions/phonenumberaction/000077500000000000000000000000001233466112000301375ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerfiltering/testactions/phonenumberaction/phonenumberaction.pro000066400000000000000000000016101233466112000343770ustar00rootroot00000000000000TEMPLATE = lib CONFIG += plugin testplugin TARGET = $$qtLibraryTarget(contacts_phonenumberaction) PLUGIN_TYPE=contacts include(../../../../../common.pri) INCLUDEPATH += ../../../../../src/serviceframework \ ../../../../../src/contacts \ ../../../../../src/contacts/details \ ../../../../../src/contacts/requests \ ../../../../../src/contacts/filters INCLUDEPATH += ../../../ CONFIG += mobility MOBILITY = contacts serviceframework DEFINES += ACTIONPLUGINTARGET=contacts_phonenumberaction DEFINES += ACTIONPLUGINNAME=PhoneNumberAction HEADERS += phonenumberaction_p.h xml.path = $$DESTDIR/xmldata xml.files = xmldata/phonenumberactionservice.xml xml.CONFIG = no_link no_dependencies explicit_dependencies no_build combine ignore_no_exist no_clean INSTALLS += xml build_pass:ALL_DEPS+=install_xml DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactmanagerfiltering/testactions/phonenumberaction/phonenumberaction_p.h000066400000000000000000000142341233466112000343530ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QCONTACTTESTPHONENUMBERACTION_P_H #define QCONTACTTESTPHONENUMBERACTION_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include "qserviceplugininterface.h" #include "qservicecontext.h" #include "qserviceinterfacedescriptor.h" #include #include #include QTCONTACTS_USE_NAMESPACE class DummyAction : public QContactAction { public: DummyAction(QObject* parent = 0) { Q_UNUSED(parent) } QVariantMap metaData() const {return QVariantMap();} bool invokeAction(const QContactActionTarget&, const QVariantMap&) { // Well, do something emit stateChanged(QContactAction::FinishedState); return true; } bool invokeAction(const QList&, const QVariantMap&) { // Well, do something emit stateChanged(QContactAction::FinishedState); return true; } QVariantMap results() const { return QVariantMap(); } State state() const {return QContactAction::FinishedState;} }; /* Static actions for testing matching */ class QPhoneNumberAction : public DummyAction { Q_OBJECT public: QPhoneNumberAction(QObject *parent = 0) : DummyAction(parent) {} ~QPhoneNumberAction() {} }; class QPhoneNumberActionFactory : public QContactActionFactory { Q_OBJECT public: QPhoneNumberActionFactory(QObject* parent = 0) : QContactActionFactory(parent) { m_actionDescriptor = createDescriptor("PhoneNumberAction", "tst_qcontactmanagerfiltering:phonenumberaction", "phonenumberaction", 1); } ~QPhoneNumberActionFactory() { } QList actionDescriptors() const { QList retn; retn << m_actionDescriptor; return retn; } QContactAction* create(const QContactActionDescriptor& which) const { Q_UNUSED(which) return new QPhoneNumberAction; } QSet supportedTargets(const QContact& contact, const QContactActionDescriptor& which) const { Q_UNUSED(which) QSet retn; QList pnDets = contact.details(QContactPhoneNumber::DefinitionName); foreach (const QContactDetail& det, pnDets) { if (!det.value(QContactPhoneNumber::FieldNumber).isEmpty()) { QContactActionTarget curr(contact, det); retn << curr; } } return retn; } QContactFilter contactFilter(const QContactActionDescriptor& which) const { Q_UNUSED(which) QContactDetailFilter df; // XXX TODO: find some way to pass the defAndFieldNamesForTypeForActions value for Integer to this function... df.setDetailDefinitionName(QContactPhoneNumber::DefinitionName, QContactPhoneNumber::FieldNumber); return df; } QVariant metaData(const QString& key, const QList& targets, const QVariantMap& parameters, const QContactActionDescriptor& which) const { Q_UNUSED(key); Q_UNUSED(targets); Q_UNUSED(parameters); Q_UNUSED(which); return QVariant(); } bool supportsContact(const QContact& contact, const QContactActionDescriptor& which) const { return supportedTargets(contact, which).isEmpty(); } private: QContactActionDescriptor m_actionDescriptor; }; class QPhoneNumberActionPlugin : public QObject, public QServicePluginInterface { Q_OBJECT Q_INTERFACES(QtMobility::QServicePluginInterface) public: QObject* createInstance(const QServiceInterfaceDescriptor& descriptor, QServiceContext* context, QAbstractSecuritySession* session) { Q_UNUSED(context); Q_UNUSED(session); if (descriptor.interfaceName() == QContactActionFactory::InterfaceName) return new QPhoneNumberActionFactory(this); else return 0; } }; Q_EXPORT_PLUGIN2(contacts_phonenumberaction, QPhoneNumberActionPlugin); #endif tests/auto/contacts/qcontactmanagerfiltering/testactions/phonenumberaction/xmldata/000077500000000000000000000000001233466112000315715ustar00rootroot00000000000000phonenumberactionservice.xml000066400000000000000000000011621233466112000373350ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerfiltering/testactions/phonenumberaction/xmldata tst_qcontactmanagerfiltering:phonenumberaction plugins/contacts/libcontacts_phonenumberaction This service provides a QContactAction plugin for testing purposes - phonenumber org.qt-project.Qt.SampleContactsActionPlugin 1.1 PhoneNumberAction Interface which returns a phonenumber tests/auto/contacts/qcontactmanagerfiltering/testactions/testactions.pro000066400000000000000000000002061233466112000274770ustar00rootroot00000000000000include(../../../../staticconfig.pri) TEMPLATE=subdirs SUBDIRS=booleanaction dateaction integeraction numberaction phonenumberaction tests/auto/contacts/qcontactmanagerfiltering/unittest/000077500000000000000000000000001233466112000237365ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerfiltering/unittest/tst_qcontactmanagerfiltering.cpp000066400000000000000000006361331233466112000324230ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #define QT_STATICPLUGIN #include #ifdef INCLUDE_TESTACTIONS #include #endif #include #include "qcontactmanagerdataholder.h" //QContactManagerDataHolder #include #ifdef Q_OS_UNIX #include #endif //TESTED_COMPONENT=src/contacts // Q_ASSERT replacement, since we often run in release builds #define Q_FATAL_VERIFY(statement) \ do { \ if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) \ qFatal("severe failure encountered, test cannot continue"); \ } while (0) QTCONTACTS_USE_NAMESPACE /* * This test is mostly just for testing sorting and filtering - * having it in tst_QContactManager makes maintenance more * difficult! */ Q_DECLARE_METATYPE(QVariant) Q_DECLARE_METATYPE(QContactManager*) Q_DECLARE_METATYPE(QContactDetail::DetailType) /* * Global variables: * These are the definition and field names used by the actions for their matching. */ QMap > defAndFieldNamesForTypeForActions; namespace { bool validDetailType(const QPair &pair) { return pair.first != QContactDetail::TypeUndefined; } bool validDetailField(const QPair &pair) { return pair.first != QContactDetail::TypeUndefined && pair.second != -1; } } /* * We use this code to compare the output and expected lists of filtering * where no sort order is implied. * TODO: use this instead of QCOMPARE in the various filtering tests! */ #define QCOMPARE_UNSORTED(output, expected) if (output.size() != expected.size()) { \ QCOMPARE(output, expected); \ } else { \ for (int i = 0; i < output.size(); i++) { \ if (!expected.contains(output.at(i))) { \ QCOMPARE(output, expected); \ } \ } \ } class tst_QContactManagerFiltering : public QObject { Q_OBJECT public: tst_QContactManagerFiltering(); virtual ~tst_QContactManagerFiltering(); private: void dumpContactDifferences(const QContact& a, const QContact& b); void dumpContact(const QContact &c); void dumpContacts(); bool isSuperset(const QContact& ca, const QContact& cb); QList prepareModel(QContactManager* cm); // add the standard contacts QString convertIds(QList allIds, QList ids, QChar minimumContact = 'a', QChar maximumContact = 'z'); // convert back to "abcd" QContact createContact(QContactManager* cm, QContactType::TypeValues type, const QString &name); QMap > > defAndFieldNamesForTypePerManager; QMultiMap contactsAddedToManagers; QList managers; QScopedPointer managerDataHolder; QTestData& newMRow(const char *tag, QContactManager *cm); private slots: void initTestCase(); void cleanupTestCase(); void rangeFiltering(); // XXX should take all managers void rangeFiltering_data(); void detailStringFiltering(); // XXX should take all managers void detailStringFiltering_data(); void detailPhoneNumberFiltering(); void detailPhoneNumberFiltering_data(); #ifdef INCLUDE_TESTACTIONS void actionPlugins(); void actionFiltering(); void actionFiltering_data(); #endif void detailVariantFiltering(); void detailVariantFiltering_data(); void intersectionFiltering(); void intersectionFiltering_data(); void unionFiltering(); void unionFiltering_data(); void relationshipFiltering(); void relationshipFiltering_data(); void changelogFiltering(); void changelogFiltering_data(); void idListFiltering(); void idListFiltering_data(); void convenienceFiltering(); void convenienceFiltering_data(); void sorting(); // XXX should take all managers void sorting_data(); void multiSorting(); void multiSorting_data(); void invalidFiltering_data(); void invalidFiltering(); void allFiltering_data(); void allFiltering(); void fetchHint_data(); void fetchHint(); }; tst_QContactManagerFiltering::tst_QContactManagerFiltering() { // In order to make our tests reliable, set the C locale QLocale::setDefault(QLocale::c()); #if defined(Q_OS_UNIX) && !defined(QT_USE_ICU) // Without ICU, we also need to set the locale on unix ::setlocale(LC_ALL, "C"); #endif } tst_QContactManagerFiltering::~tst_QContactManagerFiltering() { } void tst_QContactManagerFiltering::initTestCase() { managerDataHolder.reset(new QContactManagerDataHolder()); // firstly, build a list of the managers we wish to test. QStringList managerNames = QContactManager::availableManagers(); /* Known one that will not pass */ managerNames.removeAll("invalid"); managerNames.removeAll("testdummy"); managerNames.removeAll("teststaticdummy"); managerNames.removeAll("maliciousplugin"); // Symbiansim backend does not support the required details for these // tests to pass. Symbiansim backend specific unit test tst_simcm is // testing filtering instead. managerNames.removeAll("symbiansim"); // Some internal engines on Maemo6 managerNames.removeAll("social"); managerNames.removeAll("simcard"); managerNames.removeAll("com.nokia.messaging.contacts.engines.mail.contactslookup"); foreach(QString mgr, managerNames) { QMap params; QString mgrUri = QContactManager::buildUri(mgr, params); QContactManager* cm = QContactManager::fromUri(mgrUri); cm->setObjectName(mgr); managers.append(cm); if (mgr == "memory") { params.insert("id", "tst_QContactManager"); mgrUri = QContactManager::buildUri(mgr, params); cm = QContactManager::fromUri(mgrUri); cm->setObjectName("memory[params]"); managers.append(cm); } } // for each manager that we wish to test, prepare the model. foreach (QContactManager* cm, managers) { QList addedContacts = prepareModel(cm); if (addedContacts != contactsAddedToManagers.values(cm)) { qDebug() << "prepareModel returned:" << addedContacts; qDebug() << "contactsAdded are: " << contactsAddedToManagers.values(cm); qFatal("returned list different from saved contacts list!"); } } #ifdef INCLUDE_TESTACTIONS qDebug() << "Finished preparing each manager for test! About to load test actions:"; QServiceManager sm; QStringList allServices = sm.findServices(); foreach(const QString& serv, allServices) { if (serv.startsWith("tst_qcontactmanagerfiltering:")) { if (!sm.removeService(serv)) { qDebug() << " tst_qca: ctor: cleaning up test service" << serv << "failed:" << sm.error(); } } } QStringList myServices; myServices << "BooleanAction" << "DateAction" << "IntegerAction" << "NumberAction" << "PhoneNumberAction"; foreach (const QString& serv, myServices) { QString builtPath = QCoreApplication::applicationDirPath() + "/plugins/contacts/xmldata/" + serv.toLower() + "service.xml"; if (!sm.addService(builtPath)) { qDebug() << " tst_qca: ctor: unable to add" << serv << "service:" << sm.error(); } } #endif qDebug() << "Done!"; } void tst_QContactManagerFiltering::cleanupTestCase() { // first, remove any contacts that we've added to any managers. foreach (QContactManager* manager, managers) { QList contactIds = contactsAddedToManagers.values(manager); manager->removeContacts(contactIds, 0); } contactsAddedToManagers.clear(); // finally, we can delete all of our manager instances qDeleteAll(managers); managers.clear(); defAndFieldNamesForTypePerManager.clear(); // And restore old contacts managerDataHolder.reset(0); #ifdef INCLUDE_TESTACTIONS // clean up any actions/services. QServiceManager sm; QStringList allServices = sm.findServices(); foreach(const QString& serv, allServices) { if (serv.startsWith("tst_qcontactmanagerfiltering:")) { if (!sm.removeService(serv)) { qDebug() << " tst_qca: ctor: cleaning up test service" << serv << "failed:" << sm.error(); } } } #endif } QString tst_QContactManagerFiltering::convertIds(QList allIds, QList ids, QChar minimumContact, QChar maximumContact) { QString ret; /* Expected is of the form "abcd".. it's possible that there are some extra contacts */ for (int i = 0; i < ids.size(); i++) { if (allIds.indexOf(ids.at(i)) >= 0) { QChar curr = ('a' + allIds.indexOf(ids.at(i))); if (curr >= minimumContact && curr <= maximumContact) { ret += curr; } } } return ret; } QTestData& tst_QContactManagerFiltering::newMRow(const char *tag, QContactManager *cm) { // allocate a tag QString foo = QString("%1[%2]").arg(tag).arg(cm->objectName()); return QTest::newRow(foo.toLatin1().constData()); } void tst_QContactManagerFiltering::detailStringFiltering_data() { QTest::addColumn("cm"); QTest::addColumn("detailType"); QTest::addColumn("detailField"); QTest::addColumn("value"); QTest::addColumn("matchflags"); QTest::addColumn("expected"); QVariant ev; // empty variant QString es; // empty string QContactDetail::DetailType name = QContactName::Type; int firstname = QContactName::FieldFirstName; int lastname = QContactName::FieldLastName; int middlename = QContactName::FieldMiddleName; int prefixname = QContactName::FieldPrefix; int suffixname = QContactName::FieldSuffix; QContactDetail::DetailType nickname = QContactNickname::Type; int nicknameField = QContactNickname::FieldNickname; QContactDetail::DetailType emailaddr = QContactEmailAddress::Type; int emailfield = QContactEmailAddress::FieldEmailAddress; QContactDetail::DetailType phonenumber = QContactPhoneNumber::Type; int number = QContactPhoneNumber::FieldNumber; for (int i = 0; i < managers.size(); i++) { QContactManager *manager = managers.at(i); newMRow("Name == Aaro", manager) << manager << name << firstname << QVariant("Aaro") << 0 << es; newMRow("Name == Aaron", manager) << manager << name << firstname << QVariant("Aaron") << 0 << "a"; newMRow("Name == aaron", manager) << manager << name << firstname << QVariant("aaron") << 0 << "a"; newMRow("Name == Aaron, case sensitive", manager) << manager << name << firstname << QVariant("Aaron") << (int)(QContactFilter::MatchCaseSensitive) << "a"; newMRow("Name == aaron, case sensitive", manager) << manager << name << firstname << QVariant("aaron") << (int)(QContactFilter::MatchCaseSensitive) << es; newMRow("Name == A, begins", manager) << manager << name << firstname << QVariant("A") << (int)(QContactFilter::MatchStartsWith) << "a"; newMRow("Name == Aaron, begins", manager) << manager << name << firstname << QVariant("Aaron") << (int)(QContactFilter::MatchStartsWith) << "a"; newMRow("Name == aaron, begins", manager) << manager << name << firstname << QVariant("aaron") << (int)(QContactFilter::MatchStartsWith) << "a"; newMRow("Name == Aaron, begins, case sensitive", manager) << manager << name << firstname << QVariant("Aaron") << (int)(QContactFilter::MatchStartsWith | QContactFilter::MatchCaseSensitive) << "a"; newMRow("Name == aaron, begins, case sensitive", manager) << manager << name << firstname << QVariant("aaron") << (int)(QContactFilter::MatchStartsWith | QContactFilter::MatchCaseSensitive) << es; newMRow("Name == Aaron1, begins", manager) << manager << name << firstname << QVariant("Aaron1") << (int)(QContactFilter::MatchStartsWith) << es; newMRow("Last name == A, begins", manager) << manager << name << lastname << QVariant("A") << (int)(QContactFilter::MatchStartsWith) << "abc"; newMRow("Last name == Aaronson, begins", manager) << manager << name << lastname << QVariant("Aaronson") << (int)(QContactFilter::MatchStartsWith) << "a"; newMRow("Last Name == Aaronson1, begins", manager) << manager << name << lastname << QVariant("Aaronson1") << (int)(QContactFilter::MatchStartsWith) << es; newMRow("Name == Aar, begins", manager) << manager << name << firstname << QVariant("Aar") << (int)(QContactFilter::MatchStartsWith) << "a"; newMRow("Name == aar, begins", manager) << manager << name << firstname << QVariant("aar") << (int)(QContactFilter::MatchStartsWith) << "a"; newMRow("Name == Aar, begins, case sensitive", manager) << manager << name << firstname << QVariant("Aar") << (int)(QContactFilter::MatchStartsWith | QContactFilter::MatchCaseSensitive) << "a"; newMRow("Name == aar, begins, case sensitive", manager) << manager << name << firstname << QVariant("aar") << (int)(QContactFilter::MatchStartsWith | QContactFilter::MatchCaseSensitive) << es; newMRow("Name == aro, contains", manager) << manager << name << firstname << QVariant("aro") << (int)(QContactFilter::MatchContains) << "a"; newMRow("Name == ARO, contains", manager) << manager << name << firstname << QVariant("ARO") << (int)(QContactFilter::MatchContains) << "a"; newMRow("Name == aro, contains, case sensitive", manager) << manager << name << firstname << QVariant("aro") << (int)(QContactFilter::MatchContains | QContactFilter::MatchCaseSensitive) << "a"; newMRow("Name == ARO, contains, case sensitive", manager) << manager << name << firstname << QVariant("ARO") << (int)(QContactFilter::MatchContains | QContactFilter::MatchCaseSensitive) << es; newMRow("Name == ron, ends", manager) << manager << name << firstname << QVariant("ron") << (int)(QContactFilter::MatchEndsWith) << "a"; newMRow("Name == ARON, ends", manager) << manager << name << firstname << QVariant("ARON") << (int)(QContactFilter::MatchEndsWith) << "a"; newMRow("Name == aron, ends, case sensitive", manager) << manager << name << firstname << QVariant("aron") << (int)(QContactFilter::MatchEndsWith | QContactFilter::MatchCaseSensitive) << "a"; newMRow("Name == ARON, ends, case sensitive", manager) << manager << name << firstname << QVariant("ARON") << (int)(QContactFilter::MatchEndsWith | QContactFilter::MatchCaseSensitive) << es; newMRow("Last name == n, ends", manager) << manager << name << lastname << QVariant("n") << (int)(QContactFilter::MatchEndsWith) << "abc"; newMRow("Name == Aaron, fixed", manager) << manager << name << firstname << QVariant("Aaron") << (int)(QContactFilter::MatchFixedString) << "a"; newMRow("Name == aaron, fixed", manager) << manager << name << firstname << QVariant("aaron") << (int)(QContactFilter::MatchFixedString) << "a"; newMRow("Name == Aaron, fixed, case sensitive", manager) << manager << name << firstname << QVariant("Aaron") << (int)(QContactFilter::MatchFixedString | QContactFilter::MatchCaseSensitive) << "a"; newMRow("Name == aaron, fixed, case sensitive", manager) << manager << name << firstname << QVariant("aaron") << (int)(QContactFilter::MatchFixedString | QContactFilter::MatchCaseSensitive) << es; // middle name newMRow("MName == Arne", manager) << manager << name << middlename << QVariant("Arne") << (int)(QContactFilter::MatchContains) << "a"; // prefix newMRow("Prefix == Sir", manager) << manager << name << prefixname << QVariant("Sir") << (int)(QContactFilter::MatchContains) << "a"; // suffix newMRow("Suffix == Dr.", manager) << manager << name << suffixname << QVariant("Dr.") << (int)(QContactFilter::MatchContains) << "a"; // nickname newMRow("Nickname detail exists", manager) << manager << nickname << -1 << QVariant() << 0 << "ab"; newMRow("Nickname == Aaron, contains", manager) << manager << nickname << nicknameField << QVariant("Aaron") << (int)(QContactFilter::MatchContains) << "a"; // email newMRow("Email == Aaron@Aaronson.com", manager) << manager << emailaddr << emailfield << QVariant("Aaron@Aaronson.com") << 0 << "a"; newMRow("Email == Aaron@Aaronsen.com", manager) << manager << emailaddr << emailfield << QVariant("Aaron@Aaronsen.com") << 0 << es; // phone number newMRow("Phone number detail exists", manager) << manager << phonenumber << -1 << QVariant("") << 0 << "ab"; newMRow("Phone number = 5551212", manager) << manager << phonenumber << number << QVariant("5551212") << (int) QContactFilter::MatchExactly << "a"; newMRow("Phone number = 34, contains", manager) << manager << phonenumber << number << QVariant("34") << (int) QContactFilter::MatchContains << "b"; newMRow("Phone number = 555, starts with", manager) << manager << phonenumber << number << QVariant("555") << (int) QContactFilter::MatchStartsWith << "ab"; newMRow("Phone number = 1212, ends with", manager) << manager << phonenumber << number << QVariant("1212") << (int) QContactFilter::MatchEndsWith << "a"; newMRow("Phone number = 555-1212, match phone number", manager) << manager << phonenumber << number << QVariant("555-1212") << (int) QContactFilter::MatchPhoneNumber << "a"; // hyphens will be ignored by the match algorithm newMRow("Phone number = 555, keypad collation", manager) << manager << phonenumber << number << QVariant("555") << (int) (QContactFilter::MatchKeypadCollation | QContactFilter::MatchStartsWith) << "ab"; /* Converting other types to strings */ QPair defAndFieldNames = defAndFieldNamesForTypePerManager.value(manager).value("Integer"); if (validDetailField(defAndFieldNames)) { QTest::newRow("integer == 20") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant("20") << 0 << es; QTest::newRow("integer == 20, as string") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant("20") << (int)(QContactFilter::MatchFixedString) << "b"; QTest::newRow("integer == 20, begins with, string") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant("20") << (int)(QContactFilter::MatchFixedString | QContactFilter::MatchStartsWith) << "b"; QTest::newRow("integer == 2, begins with, string") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant("2") << (int)(QContactFilter::MatchFixedString | QContactFilter::MatchStartsWith) << "b"; QTest::newRow("integer == 20, ends with, string") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant("20") << (int)(QContactFilter::MatchFixedString | QContactFilter::MatchEndsWith) << "bc"; QTest::newRow("integer == 0, ends with, string") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant("0") << (int)(QContactFilter::MatchFixedString | QContactFilter::MatchEndsWith) << "abc"; QTest::newRow("integer == 20, contains, string") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant("20") << (int)(QContactFilter::MatchFixedString | QContactFilter::MatchContains) << "bc"; QTest::newRow("integer == 0, contains, string") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant("0") << (int)(QContactFilter::MatchFixedString | QContactFilter::MatchContains) << "abc"; } /* Detail filter semantics: empty definition or field */ newMRow("Invalid type", manager) << manager << static_cast(-1) << -1 << QVariant("A") << (int)(QContactFilter::MatchStartsWith) << es; // empty definition name means filter matches nothing newMRow("Invalid field", manager) << manager << name << -1 << QVariant("A") << (int)(QContactFilter::MatchStartsWith) << "abcdefghijk"; // invalid field matches any with a name detail } } void tst_QContactManagerFiltering::detailStringFiltering() { QFETCH(QContactManager*, cm); QFETCH(QContactDetail::DetailType, detailType); QFETCH(int, detailField); QFETCH(QVariant, value); QFETCH(int, matchflags); QFETCH(QString, expected); QList contacts = contactsAddedToManagers.values(cm); QList ids; QContactDetailFilter df; df.setDetailType(detailType, detailField); df.setValue(value); df.setMatchFlags(QContactFilter::MatchFlags(matchflags)); if (cm->managerName() == "memory") { /* At this point, since we're using memory, assume the filter isn't really supported */ QVERIFY(cm->isFilterSupported(df) == false); } ids = cm->contactIds(df); QString output = convertIds(contacts, ids, 'a', 'k'); // don't include the convenience filtering contacts QEXPECT_FAIL("integer == 20", "Not sure if this should pass or fail", Continue); QCOMPARE_UNSORTED(output, expected); } void tst_QContactManagerFiltering::detailPhoneNumberFiltering_data() { QTest::addColumn("cm"); QTest::addColumn("detailType"); QTest::addColumn("detailField"); QTest::addColumn("value"); QTest::addColumn("matchflags"); QTest::addColumn("expected"); // ITU-T standard keypad collation: // 2 = abc, 3 = def, 4 = ghi, 5 = jkl, 6 = mno, 7 = pqrs, 8 = tuv, 9 = wxyz, 0 = space QContactDetail::DetailType phoneType = QContactPhoneNumber::Type; int phoneField = QContactPhoneNumber::FieldNumber; QContactDetail::DetailType nameType = QContactName::Type; int nameField = QContactName::FieldFirstName; // just test the first name. // purely to test phone number filtering. for (int i = 0; i < managers.size(); i++) { QContactManager *manager = managers.at(i); // first, keypad collation testing (ITU-T / T9 testing) QTest::newRow("t9 aaron") << manager << nameType << nameField << QVariant(QString("22766")) << (int)(QContactFilter::MatchKeypadCollation) << "a"; QTest::newRow("t9 bob") << manager << nameType << nameField << QVariant(QString("262")) << (int)(QContactFilter::MatchKeypadCollation) << "b"; QTest::newRow("t9 john") << manager << nameType << nameField << QVariant(QString("5646")) << (int)(QContactFilter::MatchKeypadCollation) << "efg"; QTest::newRow("t9 bo") << manager << nameType << nameField << QVariant(QString("26")) << (int)(QContactFilter::MatchKeypadCollation | QContactFilter::MatchStartsWith) << "bc"; // bob, boris QTest::newRow("t9 zzzz") << manager << nameType << nameField << QVariant(QString("9999")) << (int)(QContactFilter::MatchKeypadCollation) << ""; // nobody. // now do phone number matching - first, aaron's phone number QTest::newRow("a phone hyphen") << manager << phoneType << phoneField << QVariant(QString("555-1212")) << (int)(QContactFilter::MatchPhoneNumber) << "a"; QTest::newRow("a phone plus") << manager << phoneType << phoneField << QVariant(QString("+5551212")) << (int)(QContactFilter::MatchPhoneNumber) << "a"; QTest::newRow("a phone brackets") << manager << phoneType << phoneField << QVariant(QString("(555)1212")) << (int)(QContactFilter::MatchPhoneNumber) << "a"; QTest::newRow("a phone nospaces") << manager << phoneType << phoneField << QVariant(QString("5551212")) << (int)(QContactFilter::MatchPhoneNumber) << "a"; QTest::newRow("a phone single space") << manager << phoneType << phoneField << QVariant(QString("555 1212")) << (int)(QContactFilter::MatchPhoneNumber) << "a"; QTest::newRow("a phone random spaces") << manager << phoneType << phoneField << QVariant(QString("55 512 12")) << (int)(QContactFilter::MatchPhoneNumber) << "a"; QTest::newRow("a phone every space") << manager << phoneType << phoneField << QVariant(QString("5 5 5 1 2 1 2")) << (int)(QContactFilter::MatchPhoneNumber) << "a"; QTest::newRow("a phone plus hyphen") << manager << phoneType << phoneField << QVariant(QString("+555-1212")) << (int)(QContactFilter::MatchPhoneNumber) << "a"; QTest::newRow("a phone plus brackets") << manager << phoneType << phoneField << QVariant(QString("+5(55)1212")) << (int)(QContactFilter::MatchPhoneNumber) << "a"; QTest::newRow("a phone plus brackets hyphen") << manager << phoneType << phoneField << QVariant(QString("+5(55)1-212")) << (int)(QContactFilter::MatchPhoneNumber) << "a"; QTest::newRow("a phone plus brackets hyphen spaces") << manager << phoneType << phoneField << QVariant(QString("+5 (55) 1-212")) << (int)(QContactFilter::MatchPhoneNumber) << "a"; // XXX TODO: should we test for character to number conversions (eg, dial 1800-PESTCONTROL) etc ? //QTest::newRow("a phone characters") << manager << phoneType << phoneField << QVariant(QString("jjj1a1a")) << (int)(QContactFilter::MatchPhoneNumber) << "a"; // 5551212 //QTest::newRow("a phone characters") << manager << phoneType << phoneField << QVariant(QString("jkl1b1a")) << (int)(QContactFilter::MatchPhoneNumber) << "a"; // 5551212 // then matches bob's phone number QTest::newRow("b phone hyphen") << manager << phoneType << phoneField << QVariant(QString("555-3456")) << (int)(QContactFilter::MatchPhoneNumber) << "b"; QTest::newRow("b phone plus") << manager << phoneType << phoneField << QVariant(QString("+5553456")) << (int)(QContactFilter::MatchPhoneNumber) << "b"; QTest::newRow("b phone brackets") << manager << phoneType << phoneField << QVariant(QString("(555)3456")) << (int)(QContactFilter::MatchPhoneNumber) << "b"; QTest::newRow("b phone nospaces") << manager << phoneType << phoneField << QVariant(QString("5553456")) << (int)(QContactFilter::MatchPhoneNumber) << "b"; QTest::newRow("b phone single space") << manager << phoneType << phoneField << QVariant(QString("555 3456")) << (int)(QContactFilter::MatchPhoneNumber) << "b"; QTest::newRow("b phone random spaces") << manager << phoneType << phoneField << QVariant(QString("55 534 56")) << (int)(QContactFilter::MatchPhoneNumber) << "b"; QTest::newRow("b phone every space") << manager << phoneType << phoneField << QVariant(QString("5 5 5 3 4 5 6")) << (int)(QContactFilter::MatchPhoneNumber) << "b"; QTest::newRow("b phone plus hyphen") << manager << phoneType << phoneField << QVariant(QString("+555-3456")) << (int)(QContactFilter::MatchPhoneNumber) << "b"; QTest::newRow("b phone plus brackets") << manager << phoneType << phoneField << QVariant(QString("+5(55)3456")) << (int)(QContactFilter::MatchPhoneNumber) << "b"; QTest::newRow("b phone plus brackets hyphen") << manager << phoneType << phoneField << QVariant(QString("+5(55)3-456")) << (int)(QContactFilter::MatchPhoneNumber) << "b"; QTest::newRow("b phone plus brackets hyphen spaces") << manager << phoneType << phoneField << QVariant(QString("+5 (55) 3-456")) << (int)(QContactFilter::MatchPhoneNumber) << "b"; // then match no phone numbers (negative testing) -- 555-9999 matches nobody in our test set. QTest::newRow("no phone hyphen") << manager << phoneType << phoneField << QVariant(QString("555-9999")) << (int)(QContactFilter::MatchPhoneNumber) << ""; QTest::newRow("no phone plus") << manager << phoneType << phoneField << QVariant(QString("+5559999")) << (int)(QContactFilter::MatchPhoneNumber) << ""; QTest::newRow("no phone brackets") << manager << phoneType << phoneField << QVariant(QString("(555)9999")) << (int)(QContactFilter::MatchPhoneNumber) << ""; QTest::newRow("no phone nospaces") << manager << phoneType << phoneField << QVariant(QString("5559999")) << (int)(QContactFilter::MatchPhoneNumber) << ""; QTest::newRow("no phone single space") << manager << phoneType << phoneField << QVariant(QString("555 9999")) << (int)(QContactFilter::MatchPhoneNumber) << ""; QTest::newRow("no phone random spaces") << manager << phoneType << phoneField << QVariant(QString("55 599 99")) << (int)(QContactFilter::MatchPhoneNumber) << ""; QTest::newRow("no phone every space") << manager << phoneType << phoneField << QVariant(QString("5 5 5 9 9 9 9")) << (int)(QContactFilter::MatchPhoneNumber) << ""; QTest::newRow("no phone plus hyphen") << manager << phoneType << phoneField << QVariant(QString("+555-9999")) << (int)(QContactFilter::MatchPhoneNumber) << ""; QTest::newRow("no phone plus brackets") << manager << phoneType << phoneField << QVariant(QString("+5(55)9999")) << (int)(QContactFilter::MatchPhoneNumber) << ""; QTest::newRow("no phone plus brackets hyphen") << manager << phoneType << phoneField << QVariant(QString("+5(55)9-999")) << (int)(QContactFilter::MatchPhoneNumber) << ""; QTest::newRow("no phone plus brackets hyphen spaces") << manager << phoneType << phoneField << QVariant(QString("+5 (55) 9-999")) << (int)(QContactFilter::MatchPhoneNumber) << ""; // then match both aaron and bob via starts with QTest::newRow("ab phone starts nospace") << manager << phoneType << phoneField << QVariant(QString("555")) << (int)(QContactFilter::MatchPhoneNumber | QContactFilter::MatchStartsWith) << "ab"; QTest::newRow("ab phone starts hyphen") << manager << phoneType << phoneField << QVariant(QString("555-")) << (int)(QContactFilter::MatchPhoneNumber | QContactFilter::MatchStartsWith) << "ab"; QTest::newRow("ab phone starts space") << manager << phoneType << phoneField << QVariant(QString("55 5")) << (int)(QContactFilter::MatchPhoneNumber | QContactFilter::MatchStartsWith) << "ab"; QTest::newRow("ab phone starts brackets") << manager << phoneType << phoneField << QVariant(QString("(555)")) << (int)(QContactFilter::MatchPhoneNumber | QContactFilter::MatchStartsWith) << "ab"; QTest::newRow("ab phone starts plus") << manager << phoneType << phoneField << QVariant(QString("+555")) << (int)(QContactFilter::MatchPhoneNumber | QContactFilter::MatchStartsWith) << "ab"; QTest::newRow("ab phone starts hyphen space") << manager << phoneType << phoneField << QVariant(QString("5 55-")) << (int)(QContactFilter::MatchPhoneNumber | QContactFilter::MatchStartsWith) << "ab"; QTest::newRow("ab phone starts hyphen space brackets") << manager << phoneType << phoneField << QVariant(QString("5 (55)-")) << (int)(QContactFilter::MatchPhoneNumber | QContactFilter::MatchStartsWith) << "ab"; QTest::newRow("ab phone starts hyphen space brackets plus") << manager << phoneType << phoneField << QVariant(QString("+5 (55)-")) << (int)(QContactFilter::MatchPhoneNumber | QContactFilter::MatchStartsWith) << "ab"; } } void tst_QContactManagerFiltering::detailPhoneNumberFiltering() { QFETCH(QContactManager*, cm); QFETCH(QContactDetail::DetailType, detailType); QFETCH(int, detailField); QFETCH(QVariant, value); QFETCH(int, matchflags); QFETCH(QString, expected); // note: this test is exactly the same as string filtering, but uses different fields and specific matchflags. QList contacts = contactsAddedToManagers.values(cm); QList ids; QContactDetailFilter df; df.setDetailType(detailType, detailField); df.setValue(value); df.setMatchFlags(QContactFilter::MatchFlags(matchflags)); if (cm->managerName() == "memory") { /* At this point, since we're using memory, assume the filter isn't really supported */ QVERIFY(cm->isFilterSupported(df) == false); } ids = cm->contactIds(df); QString output = convertIds(contacts, ids, 'a', 'k'); // don't include the convenience filtering contacts //QSKIP("TODO: fix default implementation of phone number matching!"); QCOMPARE_UNSORTED(output, expected); } void tst_QContactManagerFiltering::detailVariantFiltering_data() { QTest::addColumn("cm"); QTest::addColumn("detailType"); QTest::addColumn("detailField"); QTest::addColumn("setValue"); QTest::addColumn("value"); QTest::addColumn("expected"); QVariant ev; // empty variant QString es; // empty string const int invalidField(666); for (int i = 0; i < managers.size(); i++) { QContactManager *manager = managers.at(i); /* Nothings */ newMRow("no field", manager) << manager << static_cast(-1) << -1 << false << ev << es; newMRow("no type", manager) << manager << static_cast(-1) << static_cast(QContactName::FieldFirstName) << false << ev << es; /* Strings (name) */ newMRow("first name presence", manager) << manager << QContactName::Type << static_cast(QContactName::FieldFirstName) << false << ev << "abcdefghijk"; newMRow("first name == Aaron", manager) << manager << QContactName::Type << static_cast(QContactName::FieldFirstName) << true << QVariant("Aaron") << "a"; /* * Doubles * B has double(4.0) * C has double(4.0) * D has double(-128.0) */ QPair defAndFieldNames = defAndFieldNamesForTypePerManager.value(manager).value("Double"); if (validDetailField(defAndFieldNames)) { newMRow("double presence", manager) << manager << defAndFieldNames.first << -1 << false << ev << "bcd"; QTest::newRow("double presence (inc field)") << manager << defAndFieldNames.first << defAndFieldNames.second << false << ev << "bcd"; QTest::newRow("double presence (wrong field)") << manager << defAndFieldNames.first << invalidField << false << ev << es; QTest::newRow("double value (no match)") << manager << defAndFieldNames.first << defAndFieldNames.second << true << QVariant(3.5) << es; QTest::newRow("double value (wrong type)") << manager << defAndFieldNames.first << defAndFieldNames.second << true << QVariant(QDateTime()) << es; QTest::newRow("double value (wrong field, no match)") << manager << defAndFieldNames.first << invalidField << true << QVariant(3.5) << es; newMRow("double value", manager) << manager << defAndFieldNames.first << defAndFieldNames.second << true << QVariant(4.0) << "bc"; QTest::newRow("double value (wrong field)") << manager << defAndFieldNames.first << invalidField << true << QVariant(4.0) << es; QTest::newRow("double value 2") << manager << defAndFieldNames.first << defAndFieldNames.second << true << QVariant(-128.0) << "d"; QTest::newRow("double value 2 (wrong field)") << manager << defAndFieldNames.first << invalidField << true << QVariant(-128.0) << es; } /* * Integers * A has 10 * B has 20 * C has -20 */ defAndFieldNames = defAndFieldNamesForTypePerManager.value(manager).value("Integer"); if (validDetailField(defAndFieldNames)) { newMRow("integer presence", manager) << manager << defAndFieldNames.first << -1 << false << ev << "abc"; QTest::newRow("integer presence (inc field)") << manager << defAndFieldNames.first << defAndFieldNames.second << false << ev << "abc"; QTest::newRow("integer presence (wrong field)") << manager << defAndFieldNames.first << invalidField << false << ev << es; QTest::newRow("integer value (no match)") << manager << defAndFieldNames.first << defAndFieldNames.second << true << QVariant(50) << es; QTest::newRow("integer value (wrong type)") << manager << defAndFieldNames.first << defAndFieldNames.second << true << QVariant(3.5) << es; QTest::newRow("integer value (wrong field, no match)") << manager << defAndFieldNames.first << invalidField << true << QVariant(50) << es; newMRow("integer value", manager) << manager << defAndFieldNames.first << defAndFieldNames.second << true << QVariant(10) << "a"; QTest::newRow("integer value (wrong field)") << manager << defAndFieldNames.first << invalidField << true << QVariant(10) << es; QTest::newRow("integer value 2") << manager << defAndFieldNames.first << defAndFieldNames.second << true << QVariant(-20) << "c"; QTest::newRow("integer value 2 (wrong field)") << manager << defAndFieldNames.first << invalidField << true << QVariant(-20) << es; } /* * Date times * A has QDateTime(QDate(2009, 06, 29), QTime(16, 52, 23, 0)) * C has QDateTime(QDate(2009, 06, 29), QTime(16, 54, 17, 0)) */ const QDateTime adt(QDate(2009, 06, 29), QTime(16, 52, 23, 0)); const QDateTime cdt(QDate(2009, 06, 29), QTime(16, 54, 17, 0)); defAndFieldNames = defAndFieldNamesForTypePerManager.value(manager).value("DateTime"); if (validDetailField(defAndFieldNames)) { newMRow("datetime presence", manager) << manager << defAndFieldNames.first << -1 << false << ev << "ac"; QTest::newRow("datetime presence (inc field)") << manager << defAndFieldNames.first << defAndFieldNames.second << false << ev << "ac"; QTest::newRow("datetime presence (wrong field)") << manager << defAndFieldNames.first << invalidField << false << ev << es; QTest::newRow("datetime value (no match)") << manager << defAndFieldNames.first << defAndFieldNames.second << true << QVariant(QDateTime(QDate(2100,5,13), QTime(5,5,5))) << es; QTest::newRow("datetime value (wrong type)") << manager << defAndFieldNames.first << defAndFieldNames.second << true << QVariant(3.5) << es; QTest::newRow("datetime value (wrong field, no match)") << manager << defAndFieldNames.first << invalidField << true << QVariant(QDateTime(QDate(2100,5,13), QTime(5,5,5))) << es; newMRow("datetime value", manager) << manager << defAndFieldNames.first << defAndFieldNames.second << true << QVariant(adt) << "a"; QTest::newRow("datetime value (wrong field)") << manager << defAndFieldNames.first << invalidField << true << QVariant(adt) << es; QTest::newRow("datetime value 2") << manager << defAndFieldNames.first << defAndFieldNames.second << true << QVariant(cdt)<< "c"; QTest::newRow("datetime value 2 (wrong field)") << manager << defAndFieldNames.first << invalidField << true << QVariant(cdt) << es; } /* * Dates * A has QDate(1988, 1, 26) * B has QDate(2492, 5, 5) * D has QDate(2770, 10, 1) */ const QDate ad(1988, 1, 26); const QDate bd(2492, 5, 5); const QDate dd(2770, 10, 1); defAndFieldNames = defAndFieldNamesForTypePerManager.value(manager).value("Date"); if (validDetailField(defAndFieldNames)) { newMRow("date presence", manager) << manager << defAndFieldNames.first << -1 << false << ev << "abd"; QTest::newRow("date presence (inc field)") << manager << defAndFieldNames.first << defAndFieldNames.second << false << ev << "abd"; QTest::newRow("date presence (wrong field)") << manager << defAndFieldNames.first << invalidField << false << ev << es; QTest::newRow("date value (no match)") << manager << defAndFieldNames.first < contacts = contactsAddedToManagers.values(cm); QList ids; QContactDetailFilter df; df.setDetailType(detailType, detailField); if (setValue) df.setValue(value); if (cm->managerName() == "memory") { /* At this point, since we're using memory, assume the filter isn't really supported */ QVERIFY(cm->isFilterSupported(df) == false); } ids = cm->contactIds(df); QString output = convertIds(contacts, ids, 'a', 'k'); // don't include the convenience filtering contacts QCOMPARE_UNSORTED(output, expected); } void tst_QContactManagerFiltering::rangeFiltering_data() { QTest::addColumn("cm"); QTest::addColumn("detailType"); QTest::addColumn("detailField"); QTest::addColumn("minrange"); QTest::addColumn("maxrange"); QTest::addColumn("setrfs"); QTest::addColumn("rangeflagsi"); QTest::addColumn("setmfs"); QTest::addColumn("matchflagsi"); QTest::addColumn("expected"); QVariant ev; // empty variant QString es; // empty string const int invalidField(666); QContactDetail::DetailType nameType = QContactName::Type; int firstname = QContactName::FieldFirstName; QContactDetail::DetailType phoneType = QContactPhoneNumber::Type; int phonenum = QContactPhoneNumber::FieldNumber; int csflag = (int)QContactFilter::MatchCaseSensitive; for (int i = 0; i < managers.size(); i++) { QContactManager *manager = managers.at(i); /* First, cover the "empty detailType / detailField / ranges" cases */ newMRow("invalid detailType", manager) << manager << QContactDetail::TypeUndefined << firstname << QVariant("A") << QVariant("Bob") << false << 0 << true << 0 << es; newMRow("defn presence test", manager) << manager << nameType << -1 << QVariant("A") << QVariant("Bob") << false << 0 << true << 0 << "abcdefghijk"; newMRow("field presence test", manager) << manager << phoneType << phonenum << QVariant() << QVariant() << false << 0 << true << 0 << "ab"; newMRow("good type, bad field", manager) << manager << nameType << invalidField << QVariant("A") << QVariant("Bob") << false << 0 << true << 0 << es; newMRow("bad def", manager) << manager << static_cast(-1) << -1 << QVariant("A") << QVariant("Bob") << false << 0 << true << 0 << es; /* Presence for fields that aren't there */ newMRow("defn presence test negative", manager) << manager << QContactFamily::Type << -1 << ev << ev << false << 0 << false << 0 << es; newMRow("field presence test negative", manager) << manager << QContactFamily::Type << invalidField << ev << ev << false << 0 << false << 0 << es; newMRow("defn yes, field no presence test negative", manager) << manager << nameType << invalidField << ev << ev << false << 0 << false << 0 << es; newMRow("no max, all results", manager) << manager << nameType << firstname << QVariant("a") << QVariant() << false << 0 << true << 0 << "abcdefghijk"; newMRow("no max, some results", manager) << manager << nameType << firstname << QVariant("bob") << QVariant() << false << 0 << true << 0 << "bcdefghijk"; newMRow("no max, no results", manager) << manager << nameType << firstname << QVariant("ZamBeZI") << QVariant() << false << 0 << true << 0 << es; newMRow("no min, all results", manager) << manager << nameType << firstname << QVariant() << QVariant("zambezi") << false << 0 << true << 0 << "abcdefghijk"; newMRow("no min, some results", manager) << manager << nameType << firstname << QVariant() << QVariant("bOb") << false << 0 << true << 0 << "a"; newMRow("no min, no results", manager) << manager << nameType << firstname << QVariant() << QVariant("aardvark") << false << 0 << true << 0 << es; /* now case sensitive */ newMRow("no max, cs, all results", manager) << manager << nameType << firstname << QVariant("A") << QVariant() << false << 0 << true << csflag << "abcdefghijk"; newMRow("no max, cs, some results", manager) << manager << nameType << firstname << QVariant("Bob") << QVariant() << false << 0 << true << csflag << "bcdefghijk"; newMRow("no max, cs, no results", manager) << manager << nameType << firstname << QVariant("Xambezi") << QVariant() << false << 0 << true << csflag << "hijk"; newMRow("no min, cs, most results", manager) << manager << nameType << firstname << QVariant() << QVariant("Xambezi") << false << 0 << true << csflag << "abcdefg"; newMRow("no min, cs, some results", manager) << manager << nameType << firstname << QVariant() << QVariant("Bob") << false << 0 << true << csflag << "a"; newMRow("no min, cs, no results", manager) << manager << nameType << firstname << QVariant() << QVariant("Aardvark") << false << 0 << true << csflag << es; newMRow("no max, cs, badcase, all results", manager) << manager << nameType << firstname << QVariant("A") << QVariant() << false << 0 << true << csflag << "abcdefghijk"; #ifdef Q_OS_SYMBIAN qWarning() << "Test case \"no max, cs, badcase, some results\" will fail on symbian platform because of QString::localeAwareCompare is not actually locale aware"; #endif newMRow("no max, cs, badcase, some results", manager) << manager << nameType << firstname << QVariant("bob") << QVariant() << false << 0 << true << csflag #ifdef QT_USE_ICU // Case sensitivity is handled differently with/without ICU (in one case, the char sequence is // 'A-Za-z', in the other it is 'AaBb..Zz') - the results are therefore highly divergent << "bcdefghijk"; #else << "hj"; #endif newMRow("no max, cs, badcase, no results", manager) << manager << nameType << firstname << QVariant("XAMBEZI") << QVariant() << false << 0 << true << csflag << "hijk"; newMRow("no min, cs, badcase, all results", manager) << manager << nameType << firstname << QVariant() << QVariant("XAMBEZI") << false << 0 << true << csflag << "abcdefg"; #ifdef Q_OS_SYMBIAN qWarning() << "Test case \"no min, cs, badcase, some results\" will fail on symbian platform because of QString::localeAwareCompare is not actually locale aware"; #endif newMRow("no min, cs, badcase, some results", manager) << manager << nameType << firstname << QVariant() << QVariant("BOB") << false << 0 << true << csflag #ifdef QT_USE_ICU << "ab"; #else << "a"; #endif newMRow("no min, cs, badcase, no results", manager) << manager << nameType << firstname << QVariant() << QVariant("AARDVARK") << false << 0 << true << csflag << es; /* 'a' has phone number ("5551212") */ QTest::newRow("range1") << manager << phoneType << phonenum << QVariant("5551200") << QVariant("5551220") << false << 0 << false << 0 << "a"; /* A(Aaron Aaronson), B(Bob Aaronsen), C(Boris Aaronsun), D(Dennis FitzMacyntire) */ // string range matching - no matchflags set. QTest::newRow("string range - no matchflags - 1") << manager << nameType << firstname << QVariant("A") << QVariant("Bob") << false << 0 << true << 0 << "a"; QTest::newRow("string range - no matchflags - 2") << manager << nameType << firstname << QVariant("A") << QVariant("Bob") << true << (int)(QContactDetailRangeFilter::IncludeLower | QContactDetailRangeFilter::ExcludeUpper) << true << 0 << "a"; QTest::newRow("string range - no matchflags - 3") << manager << nameType << firstname << QVariant("A") << QVariant("Bob") << true << (int)(QContactDetailRangeFilter::ExcludeLower | QContactDetailRangeFilter::ExcludeUpper) << true << 0 << "a"; QTest::newRow("string range - no matchflags - 4") << manager << nameType << firstname << QVariant("A") << QVariant("Bob") << true << (int)(QContactDetailRangeFilter::IncludeLower | QContactDetailRangeFilter::IncludeUpper) << true << 0 << "ab"; QTest::newRow("string range - no matchflags - 5") << manager << nameType << firstname << QVariant("A") << QVariant("Bob") << true << (int)(QContactDetailRangeFilter::ExcludeLower | QContactDetailRangeFilter::IncludeUpper) << true << 0 << "ab"; QTest::newRow("string range - no matchflags - 6") << manager << nameType << firstname << QVariant("Bob") << QVariant("Boris") << true << (int)(QContactDetailRangeFilter::ExcludeLower | QContactDetailRangeFilter::IncludeUpper) << true << 0 << "c"; QTest::newRow("string range - no matchflags - 7") << manager << nameType << firstname << QVariant("Bob") << QVariant("Boris") << true << (int)(QContactDetailRangeFilter::IncludeLower | QContactDetailRangeFilter::ExcludeUpper) << true << 0 << "b"; QTest::newRow("string range - no matchflags - 8") << manager << nameType << firstname << QVariant("Bob") << QVariant("Boris") << true << (int)(QContactDetailRangeFilter::IncludeLower | QContactDetailRangeFilter::IncludeUpper) << true << 0 << "bc"; QTest::newRow("string range - no matchflags - 9") << manager << nameType << firstname << QVariant("Bob") << QVariant("Boris") << true << (int)(QContactDetailRangeFilter::ExcludeLower | QContactDetailRangeFilter::ExcludeUpper) << true << 0 << ""; QTest::newRow("string range - no matchflags - 10") << manager << nameType << firstname << QVariant("Barry") << QVariant("C") << true << (int)(QContactDetailRangeFilter::ExcludeLower | QContactDetailRangeFilter::ExcludeUpper) << true << 0 << "bc"; // string range matching - QContactFilter::MatchStartsWith should produce the same results as without matchflags set. QTest::newRow("string range - startswith - 1") << manager << nameType << firstname << QVariant("A") << QVariant("Bo") << true << (int)(QContactDetailRangeFilter::IncludeLower | QContactDetailRangeFilter::ExcludeUpper) << true << (int)(QContactFilter::MatchStartsWith) << "a"; QTest::newRow("string range - startswith - 2") << manager << nameType << firstname << QVariant("A") << QVariant("Bo") << true << (int)(QContactDetailRangeFilter::ExcludeLower | QContactDetailRangeFilter::ExcludeUpper) << true << (int)(QContactFilter::MatchStartsWith) << "a"; QTest::newRow("string range - startswith - 3") << manager << nameType << firstname << QVariant("A") << QVariant("Bo") << true << (int)(QContactDetailRangeFilter::IncludeLower | QContactDetailRangeFilter::IncludeUpper) << true << (int)(QContactFilter::MatchStartsWith) << "ab"; QTest::newRow("string range - startswith - 4") << manager << nameType << firstname << QVariant("A") << QVariant("Bo") << true << (int)(QContactDetailRangeFilter::ExcludeLower | QContactDetailRangeFilter::IncludeUpper) << true << (int)(QContactFilter::MatchStartsWith) << "ab"; QTest::newRow("string range - startswith - 5") << manager << nameType << firstname << QVariant("Bo") << QVariant("C") << true << (int)(QContactDetailRangeFilter::ExcludeLower | QContactDetailRangeFilter::IncludeUpper) << true << (int)(QContactFilter::MatchStartsWith) << "c"; QTest::newRow("string range - startswith - 6") << manager << nameType << firstname << QVariant("Bo") << QVariant("C") << true << (int)(QContactDetailRangeFilter::IncludeLower | QContactDetailRangeFilter::ExcludeUpper) << true << (int)(QContactFilter::MatchStartsWith) << "bc"; QTest::newRow("string range - startswith - 7") << manager << nameType << firstname << QVariant("Bo") << QVariant("C") << true << (int)(QContactDetailRangeFilter::IncludeLower | QContactDetailRangeFilter::IncludeUpper) << true << (int)(QContactFilter::MatchStartsWith) << "bc"; QTest::newRow("string range - startswith - 8") << manager << nameType << firstname << QVariant("Bo") << QVariant("C") << true << (int)(QContactDetailRangeFilter::ExcludeLower | QContactDetailRangeFilter::ExcludeUpper) << true << (int)(QContactFilter::MatchStartsWith) << "c"; QTest::newRow("string range - startswith - 9") << manager << nameType << firstname << QVariant("Barry") << QVariant("C") << true << (int)(QContactDetailRangeFilter::ExcludeLower | QContactDetailRangeFilter::ExcludeUpper) << true << (int)(QContactFilter::MatchStartsWith) << "bc"; // Open ended starts with QTest::newRow("string range - startswith open top - 1") << manager << nameType << firstname << QVariant("A") << ev << true << (int)(QContactDetailRangeFilter::IncludeLower) << true << (int)(QContactFilter::MatchStartsWith) << "abcdefghijk"; QTest::newRow("string range - startswith open top - 2") << manager << nameType << firstname << QVariant("A") << ev << true << (int)(QContactDetailRangeFilter::ExcludeLower) << true << (int)(QContactFilter::MatchStartsWith) << "abcdefghijk"; QTest::newRow("string range - startswith open top - 3") << manager << nameType << firstname << QVariant("Aaron") << ev << true << (int)(QContactDetailRangeFilter::IncludeLower) << true << (int)(QContactFilter::MatchStartsWith) << "abcdefghijk"; QTest::newRow("string range - startswith open top - 4") << manager << nameType << firstname << QVariant("Aaron") << ev << true << (int)(QContactDetailRangeFilter::ExcludeLower) << true << (int)(QContactFilter::MatchStartsWith) << "bcdefghijk"; QTest::newRow("string range - startswith open bottom - 1") << manager << nameType << firstname << ev << QVariant("Borit") << true << (int)(QContactDetailRangeFilter::IncludeUpper) << true << (int)(QContactFilter::MatchStartsWith) << "abc"; QTest::newRow("string range - startswith open bottom - 2") << manager << nameType << firstname << ev << QVariant("Borit") << true << (int)(QContactDetailRangeFilter::ExcludeUpper) << true << (int)(QContactFilter::MatchStartsWith) << "abc"; QTest::newRow("string range - startswith open bottom - 3") << manager << nameType << firstname << ev << QVariant("Boris") << true << (int)(QContactDetailRangeFilter::IncludeUpper) << true << (int)(QContactFilter::MatchStartsWith) << "abc"; QTest::newRow("string range - startswith open bottom - 4") << manager << nameType << firstname << ev << QVariant("Boris") << true << (int)(QContactDetailRangeFilter::ExcludeUpper) << true << (int)(QContactFilter::MatchStartsWith) << "ab"; /* A(10), B(20), C(-20) */ // Now integer range testing QPair defAndFieldNames = defAndFieldNamesForTypePerManager.value(manager).value("Integer"); if (validDetailField(defAndFieldNames)) { QTest::newRow("int range - no rangeflags - 1") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant(9) << QVariant(9) << false << 0 << false << 0 << es; QTest::newRow("int range - no rangeflags - 2") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant(9) << QVariant(10) << false << 0 << false << 0 << es; QTest::newRow("int range - no rangeflags - 3") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant(9) << QVariant(11) << false << 0 << false << 0 << "a"; QTest::newRow("int range - no rangeflags - 4") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant(10) << QVariant(10) << false << 0 << false << 0 << es; QTest::newRow("int range - rangeflags - 1") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant(10) << QVariant(10) << true << (int)(QContactDetailRangeFilter::ExcludeLower | QContactDetailRangeFilter::ExcludeUpper) << false << 0 << es; QTest::newRow("int range - rangeflags - 2") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant(10) << QVariant(10) << true << (int)(QContactDetailRangeFilter::IncludeLower | QContactDetailRangeFilter::ExcludeUpper) << false << 0 << es; QTest::newRow("int range - rangeflags - 3") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant(10) << QVariant(10) << true << (int)(QContactDetailRangeFilter::ExcludeLower | QContactDetailRangeFilter::IncludeUpper) << false << 0 << es; QTest::newRow("int range - rangeflags - 4") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant(10) << QVariant(10) << true << (int)(QContactDetailRangeFilter::IncludeLower | QContactDetailRangeFilter::IncludeUpper) << false << 0 << "a"; QTest::newRow("int range - rangeflags - 5") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant(10) << QVariant(11) << true << (int)(QContactDetailRangeFilter::IncludeLower | QContactDetailRangeFilter::IncludeUpper) << false << 0 << "a"; QTest::newRow("int range - rangeflags - 6") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant(11) << QVariant(11) << true << (int)(QContactDetailRangeFilter::IncludeLower | QContactDetailRangeFilter::IncludeUpper) << false << 0 << es; QTest::newRow("int range - rangeflags - 7") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant(-30) << QVariant(-19) << true << (int)(QContactDetailRangeFilter::IncludeLower | QContactDetailRangeFilter::IncludeUpper) << false << 0 << "c"; QTest::newRow("int range - rangeflags - 8") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant(-20) << QVariant(-30) << true << (int)(QContactDetailRangeFilter::IncludeLower | QContactDetailRangeFilter::IncludeUpper) << false << 0 << es; QTest::newRow("int range - rangeflags - variant - 1") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant(9) << QVariant() << true << (int)(QContactDetailRangeFilter::IncludeLower | QContactDetailRangeFilter::IncludeUpper) << false << 0 << "ab"; QTest::newRow("int range - rangeflags - variant - 2") << manager << defAndFieldNames.first << defAndFieldNames.second << QVariant() << QVariant(11) << true << (int)(QContactDetailRangeFilter::IncludeLower | QContactDetailRangeFilter::IncludeUpper) << false << 0 << "ac"; } } } void tst_QContactManagerFiltering::rangeFiltering() { QFETCH(QContactManager*, cm); QFETCH(QContactDetail::DetailType, detailType); QFETCH(int, detailField); QFETCH(QVariant, minrange); QFETCH(QVariant, maxrange); QFETCH(bool, setrfs); QFETCH(int, rangeflagsi); QFETCH(bool, setmfs); QFETCH(int, matchflagsi); QFETCH(QString, expected); QContactDetailRangeFilter::RangeFlags rangeflags = (QContactDetailRangeFilter::RangeFlags)rangeflagsi; QContactFilter::MatchFlags matchflags = (QContactFilter::MatchFlags) matchflagsi; QList contacts = contactsAddedToManagers.values(cm); QList ids; /* Build the range filter */ QContactDetailRangeFilter drf; drf.setDetailType(detailType, detailField); if (setrfs) drf.setRange(minrange, maxrange, rangeflags); else drf.setRange(minrange, maxrange); if (setmfs) drf.setMatchFlags(matchflags); if (cm->managerName() == "memory") { /* At this point, since we're using memory, assume the filter isn't really supported */ QVERIFY(cm->isFilterSupported(drf) == false); } ids = cm->contactIds(drf); QString output = convertIds(contacts, ids, 'a', 'k'); // don't include the convenience filtering contacts QEXPECT_FAIL("string range - startswith - 3", "No handling for startsWith exists in QContactDetailRangeFilter implementation", Continue); QEXPECT_FAIL("string range - startswith - 4", "No handling for startsWith exists in QContactDetailRangeFilter implementation", Continue); QEXPECT_FAIL("string range - startswith - 5", "No handling for startsWith exists in QContactDetailRangeFilter implementation", Continue); QEXPECT_FAIL("string range - startswith - 8", "No handling for startsWith exists in QContactDetailRangeFilter implementation", Continue); QCOMPARE_UNSORTED(output, expected); } void tst_QContactManagerFiltering::intersectionFiltering_data() { QTest::addColumn("cm"); QTest::addColumn("firstfilter"); QTest::addColumn("fftype"); // 1 = detail, 2 = detailrange, 3 = groupmembership, 4 = union, 5 = intersection QTest::addColumn("ffdetailtype"); QTest::addColumn("ffdetailfield"); QTest::addColumn("ffsetvalue"); QTest::addColumn("ffvalue"); QTest::addColumn("ffminrange"); QTest::addColumn("ffmaxrange"); QTest::addColumn("secondfilter"); QTest::addColumn("sftype"); QTest::addColumn("sfdetailtype"); QTest::addColumn("sfdetailfield"); QTest::addColumn("sfsetvalue"); QTest::addColumn("sfvalue"); QTest::addColumn("sfminrange"); QTest::addColumn("sfmaxrange"); QTest::addColumn("order"); QTest::addColumn("expected"); QString es; // empty string. for (int i = 0; i < managers.size(); i++) { QContactManager *manager = managers.at(i); // for the following tests, terminology: // X will be an (empty) intersection filter created in the test // Y will be the first filter defined here // Z will be the second filter defined here // WITH Y AND Z AS DETAIL FILTERS (with no overlap between Y and Z results) // For these tests, Y matches "bc" and Z matches "a" // X && Y - X empty so es QPair integerDefAndFieldNames = defAndFieldNamesForTypePerManager.value(manager).value("Integer"); QPair booleanDefAndFieldNames = defAndFieldNamesForTypePerManager.value(manager).value("Bool"); if (validDetailField(integerDefAndFieldNames) && validDetailField(booleanDefAndFieldNames)) { QTest::newRow("A1") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << false << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(10) << QVariant() << QVariant() << "XY" << es; // Y && X - X empty so es QTest::newRow("A2") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << false << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(10) << QVariant() << QVariant() << "YX" << es; // Y && Z - matches "a" and "bc" - so intersected = es QTest::newRow("A3") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "YZ" << es; // Z && Y - matches "bc" and "a" - so intersected = es QTest::newRow("A4") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "ZY" << es; // X && Z - X empty so es QTest::newRow("A5") << manager << false << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "XZ" << es; // Z && X - X empty so es QTest::newRow("A6") << manager << false << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "ZX" << es; // X && Y && Z - X empty so es QTest::newRow("A7") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "XYZ" << es; // X && Z && Y - X empty so es QTest::newRow("A8") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "XZY" << es; // Y && X && Z - X empty so es QTest::newRow("A9") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "YXZ" << es; // Z && X && Y - X empty so es QTest::newRow("A10") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "ZXY" << es; // Y && Z && X - X empty so es QTest::newRow("A11") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "YZX" << es; // Z && Y && X - X empty so es QTest::newRow("A12") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "ZYX" << es; // WITH Y AND Z AS DETAIL FILTERS (with some overlap between Y and Z results) // For these tests, Y matches "bc", Z matches "b" // X && Y - X empty so es QTest::newRow("B1") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << false << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(20) << QVariant() << QVariant() << "XY" << es; // Y && X - X empty so es QTest::newRow("B2") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << false << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(20) << QVariant() << QVariant() << "YX" << es; // Y && Z - matches "b" and "bc" - so intersected = "b" QTest::newRow("B3") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "YZ" << "b"; // Z && Y - matches "bc" and "b" - so intersected = "b" QTest::newRow("B4") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "ZY" << "b"; // X && Z - X empty so es QTest::newRow("B5") << manager << false << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "XZ" << es; // Z && X - X empty so es QTest::newRow("B6") << manager << false << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "ZX" << es; // X && Y && Z - X empty so es QTest::newRow("B7") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "XYZ" << es; // X && Z && Y - X empty so es QTest::newRow("B8") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "XZY" << es; // Y && X && Z - X empty so es QTest::newRow("B9") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "YXZ" << es; // Z && X && Y - X empty so es QTest::newRow("B10") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "ZXY" << es; // Y && Z && X - X empty so es QTest::newRow("B11") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "YZX" << es; // Z && Y && X - X empty so es QTest::newRow("B12") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "ZYX" << es; } //--------------------------- // WITH Y AND Z AS RANGE FILTERS (with no overlap between Y and Z results) // For these tests, Y matches "a", Z matches "b" // X && Y - X empty so es if (validDetailField(integerDefAndFieldNames)) { QTest::newRow("C1") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << false << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "XY" << es; // Y && X - X empty so es QTest::newRow("C2") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << false << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "YX" << es; // Y && Z - no overlap so es QTest::newRow("C3") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "YZ" << es; // Z && Y - no overlap so es QTest::newRow("C4") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "ZY" << es; // X && Z - X empty so es QTest::newRow("C5") << manager << false << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "XZ" << es; // Z && X - X empty so es QTest::newRow("C6") << manager << false << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "ZX" << es; // X && Y && Z - X empty so es QTest::newRow("C7") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "XYZ" << es; // X && Z && Y - X empty so es QTest::newRow("C8") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "XZY" << es; // Y && X && Z - X empty so es QTest::newRow("C9") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "YXZ" << es; // Z && X && Y - X empty so es QTest::newRow("C10") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "ZXY" << es; // Y && Z && X - X empty so es QTest::newRow("C11") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "YZX" << es; // Z && Y && X - X empty so es QTest::newRow("C12") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "ZYX" << es; // WITH Y AND Z AS RANGE FILTERS (with some overlap between Y and Z results) // For these tests, Y matches "ab", Z matches "b" // X && Y - X empty so es QTest::newRow("D1") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << false << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "XY" << es; // Y && X - X empty so es QTest::newRow("D2") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << false << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "YX" << es; // Y && Z - Y matches "ab", Z matches "b", intersection = "b" QTest::newRow("D3") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "YZ" << "b"; // Z && Y - Y matches "ab", Z matches "b", intersection = "b" QTest::newRow("D4") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "ZY" << "b"; // X && Z - X empty so es QTest::newRow("D5") << manager << false << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "XZ" << es; // Z && X - X empty so es QTest::newRow("D6") << manager << false << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "ZX" << es; // X && Y && Z - X empty so es QTest::newRow("D7") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "XYZ" << es; // X && Z && Y - X empty so es QTest::newRow("D8") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "XZY" << es; // Y && X && Z - X empty so es QTest::newRow("D9") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "YXZ" << es; // Z && X && Y - X empty so es QTest::newRow("D10") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "ZXY" << es; // Y && Z && X - X empty so es QTest::newRow("D11") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "YZX" << es; // Z && Y && X - X empty so es QTest::newRow("D12") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "ZYX" << es; } } } void tst_QContactManagerFiltering::intersectionFiltering() { QFETCH(QContactManager*, cm); QFETCH(bool, firstfilter); QFETCH(int, fftype); // 1 = detail, 2 = detailrange, 3 = groupmembership, 4 = union, 5 = intersection QFETCH(QContactDetail::DetailType, ffdetailtype); QFETCH(int, ffdetailfield); QFETCH(bool, ffsetvalue); QFETCH(QVariant, ffvalue); QFETCH(QVariant, ffminrange); QFETCH(QVariant, ffmaxrange); QFETCH(bool, secondfilter); QFETCH(int, sftype); QFETCH(QContactDetail::DetailType, sfdetailtype); QFETCH(int, sfdetailfield); QFETCH(bool, sfsetvalue); QFETCH(QVariant, sfvalue); QFETCH(QVariant, sfminrange); QFETCH(QVariant, sfmaxrange); QFETCH(QString, order); QFETCH(QString, expected); QContactFilter *x = new QContactIntersectionFilter(); QContactFilter *y = 0, *z = 0; if (firstfilter) { switch (fftype) { case 1: // detail filter y = new QContactDetailFilter(); static_cast(y)->setDetailType(ffdetailtype, ffdetailfield); if (ffsetvalue) static_cast(y)->setValue(ffvalue); break; case 2: // range filter y = new QContactDetailRangeFilter(); static_cast(y)->setDetailType(ffdetailtype, ffdetailfield); static_cast(y)->setRange(ffminrange, ffmaxrange); break; case 3: // group membership filter case 4: // union filter case 5: // intersection filter break; default: QVERIFY(false); // force fail. break; } } if (secondfilter) { switch (sftype) { case 1: // detail filter z = new QContactDetailFilter(); static_cast(z)->setDetailType(sfdetailtype, sfdetailfield); if (sfsetvalue) static_cast(z)->setValue(sfvalue); break; case 2: // range filter z = new QContactDetailRangeFilter(); static_cast(z)->setDetailType(sfdetailtype, sfdetailfield); static_cast(z)->setRange(sfminrange, sfmaxrange); break; case 3: // group membership filter case 4: // union filter case 5: // intersection filter break; default: QVERIFY(false); // force fail. break; } } // control variables - order: starts, ends, mids bool sX = false; bool sY = false; bool sZ = false; bool eX = false; bool eY = false; bool eZ = false; bool mX = false; bool mY = false; bool mZ = false; if (order.startsWith("X")) sX = true; if (order.startsWith("Y")) sY = true; if (order.startsWith("Z")) sZ = true; if (order.endsWith("X")) eX = true; if (order.endsWith("Y")) eY = true; if (order.endsWith("Z")) eZ = true; if (order.size() > 2) { if (order.at(1) == 'X') mX = true; if (order.at(1) == 'Y') mY = true; if (order.at(1) == 'Z') mZ = true; } // now perform the filtering. QContactIntersectionFilter resultFilter; if (sX) { if (mY && eZ) resultFilter = *x & *y & *z; else if (mZ && eY) resultFilter = *x & *z & *y; else if (eY) resultFilter = *x & *y; else if (eZ) resultFilter = *x & *z; } else if (sY) { if (mX && eZ) resultFilter = *y & *x & *z; else if (mZ && eX) resultFilter = *y & *z & *x; else if (eX) resultFilter = *y & *x; else if (eZ) resultFilter = *y & *z; } else if (sZ) { if (mX && eY) resultFilter = *z & *x & *y; else if (mY && eX) resultFilter = *z & *y & *x; else if (eX) resultFilter = *z & *x; else if (eY) resultFilter = *z & *y; } QList contacts = contactsAddedToManagers.values(cm); QList ids; ids = cm->contactIds(resultFilter); QString output = convertIds(contacts, ids, 'a', 'k'); // don't include the convenience filtering contacts QCOMPARE_UNSORTED(output, expected); delete x; if (y) delete y; if (z) delete z; } void tst_QContactManagerFiltering::unionFiltering_data() { QTest::addColumn("cm"); QTest::addColumn("firstfilter"); QTest::addColumn("fftype"); // 1 = detail, 2 = detailrange, 3 = groupmembership, 4 = union, 5 = intersection QTest::addColumn("ffdetailtype"); QTest::addColumn("ffdetailfield"); QTest::addColumn("ffsetvalue"); QTest::addColumn("ffvalue"); QTest::addColumn("ffminrange"); QTest::addColumn("ffmaxrange"); QTest::addColumn("secondfilter"); QTest::addColumn("sftype"); QTest::addColumn("sfdetailtype"); QTest::addColumn("sfdetailfield"); QTest::addColumn("sfsetvalue"); QTest::addColumn("sfvalue"); QTest::addColumn("sfminrange"); QTest::addColumn("sfmaxrange"); QTest::addColumn("order"); QTest::addColumn("expected"); QString es; // empty string. for (int i = 0; i < managers.size(); i++) { QContactManager *manager = managers.at(i); // for the following tests, terminology: // X will be an (empty) union filter created in the test // Y will be the first filter defined here // Z will be the second filter defined here // WITH Y AND Z AS DETAIL FILTERS (with no overlap between Y and Z results) // For these tests, Y matches "bc" and Z matches "a" // X || Y - X empty, Y matches "bc" so union = "bc" QPair integerDefAndFieldNames = defAndFieldNamesForTypePerManager.value(manager).value("Integer"); QPair booleanDefAndFieldNames = defAndFieldNamesForTypePerManager.value(manager).value("Bool"); if (validDetailField(integerDefAndFieldNames) && validDetailField(booleanDefAndFieldNames)) { QTest::newRow("A1") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << false << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(10) << QVariant() << QVariant() << "XY" << "bc"; // Y || X - Y matches "bc", X empty so union = "bc" QTest::newRow("A2") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << false << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(10) << QVariant() << QVariant() << "YX" << "bc"; // Y || Z - Y matches "bc" and Z matches "a" - so union = "abc" QTest::newRow("A3") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "YZ" << "abc"; // Z || Y - Y matches "bc" and Z matches "a" - so union = "abc" QTest::newRow("A4") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "ZY" << "abc"; // X || Z - X empty, Z matches "a" so "a" QTest::newRow("A5") << manager << false << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << false << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "XZ" << "a"; // Z || X - X empty, Z matches "a" so "a" QTest::newRow("A6") << manager << false << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << false << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "ZX" << "a"; // X || Y || Z - X empty, Y matches "bc", Z matches "a" so "abc" QTest::newRow("A7") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "XYZ" << "abc"; // X || Z || Y - X empty, Y matches "bc", Z matches "a" so "abc" QTest::newRow("A8") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "XZY" << "abc"; // Y || X || Z - X empty, Y matches "bc", Z matches "a" so "abc" QTest::newRow("A9") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "YXZ" << "abc"; // Z || X || Y - X empty, Y matches "bc", Z matches "a" so "abc" QTest::newRow("A10") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "ZXY" << "abc"; // Y || Z || X - X empty, Y matches "bc", Z matches "a" so "abc" QTest::newRow("A11") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "YZX" << "abc"; // Z || Y || X - X empty, Y matches "bc", Z matches "a" so "abc" QTest::newRow("A12") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(10) << QVariant() << QVariant() << "ZYX" << "abc"; // WITH Y AND Z AS DETAIL FILTERS (with some overlap between Y and Z results) // For these tests, Y matches "bc", Z matches "b" // X || Y - X empty, Y matches "b", so "bc" QTest::newRow("B1") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << false << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(20) << QVariant() << QVariant() << "XY" << "bc"; // Y || X - X empty, Y matches "bc", so "bc" QTest::newRow("B2") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << false << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(20) << QVariant() << QVariant() << "YX" << "bc"; // Y || Z - X empty, Y matches "bc", Z matches "b" so "bc" QTest::newRow("B3") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "YZ" << "bc"; // Z || Y - X empty, Y matches "bc", Z matches "b" so "bc" QTest::newRow("B4") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "ZY" << "bc"; // X || Z - X empty, Z matches "b" so "b" QTest::newRow("B5") << manager << false << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "XZ" << "b"; // Z || X - X empty, Z matches "b" so "b" QTest::newRow("B6") << manager << false << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "ZX" << "b"; // X || Y || Z - X empty, Y matches "bc", Z matches "b" so "bc" QTest::newRow("B7") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "XYZ" << "bc"; // X || Z || Y - X empty, Y matches "bc", Z matches "b" so "bc" QTest::newRow("B8") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "XZY" << "bc"; // Y || X || Z - X empty, Y matches "bc", Z matches "b" so "bc" QTest::newRow("B9") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "YXZ" << "bc"; // Z || X || Y - X empty, Y matches "bc", Z matches "b" so "bc" QTest::newRow("B10") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "ZXY" << "bc"; // Y || Z || X - X empty, Y matches "bc", Z matches "b" so "bc" QTest::newRow("B11") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "YZX" << "bc"; // Z || Y || X - X empty, Y matches "bc", Z matches "b" so "bc" QTest::newRow("B12") << manager << true << 1 << booleanDefAndFieldNames.first << booleanDefAndFieldNames.second << true << QVariant(false) << QVariant() << QVariant() << true << 1 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << true << QVariant(20) << QVariant() << QVariant() << "ZYX" << "bc"; } //--------------------------- // WITH Y AND Z AS RANGE FILTERS (with no overlap between Y and Z results) // For these tests, Y matches "a", Z matches "b" // X || Y - X empty, Y matches "a" so "a" if (validDetailField(integerDefAndFieldNames)) { QTest::newRow("C1") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << false << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "XY" << "a"; // Y || X - X empty, Y matches "a" so "a" QTest::newRow("C2") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << false << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "YX" << "a"; // Y || Z - Y matches "a", Z matches "b" so "ab" QTest::newRow("C3") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "YZ" << "ab"; // Z || Y - Y matches "a", Z matches "b" so "ab" QTest::newRow("C4") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "ZY" << "ab"; // X || Z - X empty, Z matches "b" so "b" QTest::newRow("C5") << manager << false << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "XZ" << "b"; // Z || X - X empty, Z matches "b" so "b" QTest::newRow("C6") << manager << false << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "ZX" << "b"; // X || Y || Z - X empty, Y matches "a", Z matches "b" so "ab" QTest::newRow("C7") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "XYZ" << "ab"; // X || Z || Y - X empty, Y matches "a", Z matches "b" so "ab" QTest::newRow("C8") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "XZY" << "ab"; // Y || X || Z - X empty, Y matches "a", Z matches "b" so "ab" QTest::newRow("C9") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "YXZ" << "ab"; // Z || X || Y - X empty, Y matches "a", Z matches "b" so "ab" QTest::newRow("C10") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "ZXY" << "ab"; // Y || Z || X - X empty, Y matches "a", Z matches "b" so "ab" QTest::newRow("C11") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "YZX" << "ab"; // Z || Y || X - X empty, Y matches "a", Z matches "b" so "ab" QTest::newRow("C12") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(15) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "ZYX" << "ab"; // WITH Y AND Z AS RANGE FILTERS (with some overlap between Y and Z results) // For these tests, Y matches "ab", Z matches "b" // X || Y - X empty, Y matches "ab" so "ab" QTest::newRow("D1") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << false << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "XY" << "ab"; // Y || X - X empty, Y matches "ab" so "ab" QTest::newRow("D2") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << false << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "YX" << "ab"; // Y || Z - Y matches "ab", Z matches "b", union = "ab" QTest::newRow("D3") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "YZ" << "ab"; // Z || Y - Y matches "ab", Z matches "b", union = "ab" QTest::newRow("D4") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "ZY" << "ab"; // X || Z - X empty, Z matches "b" so "b" QTest::newRow("D5") << manager << false << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "XZ" << "b"; // Z || X - X empty, Z matches "b" so "b" QTest::newRow("D6") << manager << false << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "ZX" << "b"; // X || Y || Z - X empty, Y matches "ab", Z matches "b" so "ab" QTest::newRow("D7") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "XYZ" << "ab"; // X || Z || Y - X empty, Y matches "ab", Z matches "b" so "ab" QTest::newRow("D8") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "XZY" << "ab"; // Y || X || Z - X empty, Y matches "ab", Z matches "b" so "ab" QTest::newRow("D9") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "YXZ" << "ab"; // Z || X || Y - X empty, Y matches "ab", Z matches "b" so "ab" QTest::newRow("D10") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "ZXY" << "ab"; // Y || Z || X - X empty, Y matches "ab", Z matches "b" so "ab" QTest::newRow("D11") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "YZX" << "ab"; // Z || Y || X - X empty, Y matches "ab", Z matches "b" so "ab" QTest::newRow("D12") << manager << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(5) << QVariant(25) << true << 2 << integerDefAndFieldNames.first << integerDefAndFieldNames.second << false << QVariant(0) << QVariant(15) << QVariant(25) << "ZYX" << "ab"; } } } void tst_QContactManagerFiltering::unionFiltering() { QFETCH(QContactManager*, cm); QFETCH(bool, firstfilter); QFETCH(int, fftype); // 1 = detail, 2 = detailrange, 3 = groupmembership, 4 = union, 5 = intersection QFETCH(QContactDetail::DetailType, ffdetailtype); QFETCH(int, ffdetailfield); QFETCH(bool, ffsetvalue); QFETCH(QVariant, ffvalue); QFETCH(QVariant, ffminrange); QFETCH(QVariant, ffmaxrange); QFETCH(bool, secondfilter); QFETCH(int, sftype); QFETCH(QContactDetail::DetailType, sfdetailtype); QFETCH(int, sfdetailfield); QFETCH(bool, sfsetvalue); QFETCH(QVariant, sfvalue); QFETCH(QVariant, sfminrange); QFETCH(QVariant, sfmaxrange); QFETCH(QString, order); QFETCH(QString, expected); QContactFilter *x = new QContactUnionFilter(); QContactFilter *y = 0, *z = 0; if (firstfilter) { switch (fftype) { case 1: // detail filter y = new QContactDetailFilter(); static_cast(y)->setDetailType(ffdetailtype, ffdetailfield); if (ffsetvalue) static_cast(y)->setValue(ffvalue); break; case 2: // range filter y = new QContactDetailRangeFilter(); static_cast(y)->setDetailType(ffdetailtype, ffdetailfield); static_cast(y)->setRange(ffminrange, ffmaxrange); break; case 3: // group membership filter case 4: // union filter case 5: // intersection filter break; default: QVERIFY(false); // force fail. break; } } if (secondfilter) { switch (sftype) { case 1: // detail filter z = new QContactDetailFilter(); static_cast(z)->setDetailType(sfdetailtype, sfdetailfield); if (sfsetvalue) static_cast(z)->setValue(sfvalue); break; case 2: // range filter z = new QContactDetailRangeFilter(); static_cast(z)->setDetailType(sfdetailtype, sfdetailfield); static_cast(z)->setRange(sfminrange, sfmaxrange); break; case 3: // group membership filter case 4: // union filter case 5: // intersection filter break; default: QVERIFY(false); // force fail. break; } } // control variables - order: starts, ends, mids bool sX = false; bool sY = false; bool sZ = false; bool eX = false; bool eY = false; bool eZ = false; bool mX = false; bool mY = false; bool mZ = false; if (order.startsWith("X")) sX = true; if (order.startsWith("Y")) sY = true; if (order.startsWith("Z")) sZ = true; if (order.endsWith("X")) eX = true; if (order.endsWith("Y")) eY = true; if (order.endsWith("Z")) eZ = true; if (order.size() > 2) { if (order.at(1) == 'X') mX = true; if (order.at(1) == 'Y') mY = true; if (order.at(1) == 'Z') mZ = true; } // now perform the filtering. QContactUnionFilter resultFilter; if (sX) { if (mY && eZ) resultFilter = *x | *y | *z; else if (mZ && eY) resultFilter = *x | *z | *y; else if (eY) resultFilter = *x | *y; else if (eZ) resultFilter = *x | *z; } else if (sY) { if (mX && eZ) resultFilter = *y | *x | *z; else if (mZ && eX) resultFilter = *y | *z | *x; else if (eX) resultFilter = *y | *x; else if (eZ) resultFilter = *y | *z; } else if (sZ) { if (mX && eY) resultFilter = *z | *x | *y; else if (mY && eX) resultFilter = *z | *y | *x; else if (eX) resultFilter = *z | *x; else if (eY) resultFilter = *z | *y; } QList contacts = contactsAddedToManagers.values(cm); QList ids; ids = cm->contactIds(resultFilter); QString output = convertIds(contacts, ids, 'a', 'k'); // don't include the convenience filtering contacts QCOMPARE_UNSORTED(output, expected); delete x; if (y) delete y; if (z) delete z; } void tst_QContactManagerFiltering::relationshipFiltering_data() { QTest::addColumn("cm"); QTest::addColumn("relatedContactRole"); QTest::addColumn("relationshipType"); QTest::addColumn("relatedContact"); QTest::addColumn("expected"); const int firstRole(static_cast(QContactRelationship::First)); const int secondRole(static_cast(QContactRelationship::Second)); const int eitherRole(static_cast(QContactRelationship::Either)); for (int i = 0; i < managers.size(); i++) { QContactManager *manager = managers.at(i); // HasMember QTest::newRow("RF-1") << manager << secondRole << QContactRelationship::HasMember() << '\0' << "a"; QTest::newRow("RF-2") << manager << firstRole << QContactRelationship::HasMember() << '\0' << "b"; QTest::newRow("RF-3") << manager << eitherRole << QContactRelationship::HasMember() << '\0' << "ab"; // match any contact that has an assistant QTest::newRow("RF-4") << manager << secondRole << QContactRelationship::HasAssistant() << '\0' << "a"; // match any contact that is an assistant QTest::newRow("RF-5") << manager << firstRole << QContactRelationship::HasAssistant() << '\0' << "b"; // match any contact that has an assistant or is an assistant QTest::newRow("RF-6") << manager << eitherRole << QContactRelationship::HasAssistant() << '\0' << "ab"; // IsSameAs QTest::newRow("RF-7") << manager << secondRole << QContactRelationship::IsSameAs() << '\0' << "a"; QTest::newRow("RF-8") << manager << firstRole << QContactRelationship::IsSameAs() << '\0' << "b"; QTest::newRow("RF-9") << manager << eitherRole << QContactRelationship::IsSameAs() << '\0' << "ab"; // Aggregates QTest::newRow("RF-10") << manager << secondRole << QContactRelationship::Aggregates() << '\0' << "a"; QTest::newRow("RF-11") << manager << firstRole << QContactRelationship::Aggregates() << '\0' << "b"; QTest::newRow("RF-12") << manager << eitherRole << QContactRelationship::Aggregates() << '\0' << "ab"; // HasManager QTest::newRow("RF-13") << manager << secondRole << QContactRelationship::HasManager() << '\0' << "a"; QTest::newRow("RF-14") << manager << firstRole << QContactRelationship::HasManager() << '\0' << "b"; QTest::newRow("RF-15") << manager << eitherRole << QContactRelationship::HasManager() << '\0' << "ab"; // HasSpouse QTest::newRow("RF-16") << manager << secondRole << QContactRelationship::HasSpouse() << '\0' << "a"; QTest::newRow("RF-17") << manager << firstRole << QContactRelationship::HasSpouse() << '\0' << "b"; QTest::newRow("RF-18") << manager << eitherRole << QContactRelationship::HasSpouse() << '\0' << "ab"; // Unknown relationship QTest::newRow("RF-19") << manager << secondRole << QStringLiteral("UnknownRelationship") << '\0' << "a"; QTest::newRow("RF-20") << manager << firstRole << QStringLiteral("UnknownRelationship") << '\0' << "b"; QTest::newRow("RF-21") << manager << eitherRole << QStringLiteral("UnknownRelationship") << '\0' << "ab"; // match any contact that is the related contact in a relationship with contact-A QTest::newRow("RF-22") << manager << secondRole << QString() << 'a' << ""; // match any contact has contact-A as the related contact QTest::newRow("RF-23") << manager << firstRole << QString() << 'a' << "b"; // match any contact that has any relationship with contact-A QTest::newRow("RF-24") << manager << eitherRole << QString() << 'a' << "b"; // match any contact that is the related contact in a relationship with contact-B QTest::newRow("RF-25") << manager << secondRole << QString() << 'b' << "a"; // match any contact has contact-B as the related contact QTest::newRow("RF-26") << manager << firstRole << QString() << 'b' << ""; // match any contact that has any relationship with contact-B QTest::newRow("RF-27") << manager << eitherRole << QString() << 'b' << "a"; } } QContact tst_QContactManagerFiltering::createContact(QContactManager* cm, QContactType::TypeValues type, const QString &name) { QContact contact; contact.setType(type); QContactName contactName; for (int i = QContactName::FieldPrefix; i <= QContactName::FieldSuffix; ++i) { contactName.setValue(i, name); } contact.saveDetail(&contactName); cm->saveContact(&contact); return contact; } void tst_QContactManagerFiltering::relationshipFiltering() { QFETCH(QContactManager*, cm); QFETCH(int, relatedContactRole); QFETCH(QString, relationshipType); QFETCH(char, relatedContact); QFETCH(QString, expected); // TODO: A little re-factoring could be used to make the test case more readable // 1. Create contacts to be used in relationship testing QContact contactA; if(relationshipType == QContactRelationship::HasMember()) { // Change contact type to group as this is required at least by symbian backend // TODO: should it be possible to query this constraint from the backend? contactA = createContact(cm, QContactType::TypeGroup, "ContactA"); } else { contactA = createContact(cm, QContactType::TypeContact, "ContactA"); } QContact contactB = createContact(cm, QContactType::TypeContact, "ContactB"); // 2. Create the relationship between the contacts QContact first; first.setId(contactA.id()); QContact second; second.setId(contactB.id()); QContactRelationship h2i; h2i.setFirst(first); h2i.setSecond(second); h2i.setRelationshipType(relationshipType); // save and check error code bool succeeded = false; if(cm->isRelationshipTypeSupported(relationshipType, contactA.type()) && cm->isRelationshipTypeSupported(relationshipType, contactB.type())) { succeeded = true; QVERIFY(cm->saveRelationship(&h2i)); QCOMPARE(cm->error(), QContactManager::NoError); } else { QVERIFY(!cm->saveRelationship(&h2i)); QCOMPARE(cm->error(), QContactManager::NotSupportedError); } // 3. Construct the filter QContactRelationshipFilter crf; crf.setRelatedContactRole(static_cast(relatedContactRole)); crf.setRelationshipType(relationshipType); if (relatedContact == 'a') { crf.setRelatedContact(first); } else if (relatedContact == 'b') { crf.setRelatedContact(second); } // 4. Grab the filtering results QList contacts; contacts.append(contactA.id()); contacts.append(contactB.id()); QList ids = cm->contactIds(crf); QString output = convertIds(contacts, ids, 'a', 'k'); // don't include the convenience filtering contacts // Check that a different relationship type does not match crf.setRelationshipType(QStringLiteral("Some other type")); QList ids2 = cm->contactIds(crf); // 5. Remove the created relationship and contacts if(succeeded) { // Check that an existing relationship can be removed QVERIFY(cm->removeRelationship(h2i)); QCOMPARE(cm->error(), QContactManager::NoError); } else { // Check that non-existing relationship cannot be removed QVERIFY(!cm->removeRelationship(h2i)); //TODO: what is the expected error code? //QCOMPARE(cm->error(), QContactManager::DoesNotExistError); } foreach (const QContactId& cid, contacts) { cm->removeContact(cid); } // 6. Verify the filtering result if(succeeded) { QCOMPARE_UNSORTED(output, expected); QCOMPARE(ids2, QList()); } else { QString msg = "Manager does not support relationship type " + relationshipType + " between " + contactA.type() + " and " + contactB.type() + " type contacts."; QSKIP(msg.toLatin1()); } } void tst_QContactManagerFiltering::sorting_data() { QTest::addColumn("cm"); QTest::addColumn("detailType"); QTest::addColumn("detailField"); QTest::addColumn("directioni"); QTest::addColumn("setbp"); QTest::addColumn("blankpolicyi"); QTest::addColumn("casesensitivityi"); QTest::addColumn("expected"); QTest::addColumn("unstable"); int firstname = QContactName::FieldFirstName; int lastname = QContactName::FieldLastName; QContactDetail::DetailType nameType = QContactName::Type; QContactDetail::DetailType dlType = QContactDisplayLabel::Type; int dlfld = QContactDisplayLabel::FieldLabel; int asc = Qt::AscendingOrder; int desc = Qt::DescendingOrder; int bll = QContactSortOrder::BlanksLast; int blf = QContactSortOrder::BlanksFirst; int cs = Qt::CaseSensitive; int ci = Qt::CaseInsensitive; for (int i = 0; i < managers.size(); i++) { QContactManager *manager = managers.at(i); QPair integerDefAndFieldNames = defAndFieldNamesForTypePerManager.value(manager).value("Integer"); QPair stringDefAndFieldNames = defAndFieldNamesForTypePerManager.value(manager).value("String"); #ifdef Q_OS_SYMBIAN qWarning() << "Test case \"first ascending\" will fail on symbian platform because of QString::localeAwareCompare is not actually locale aware"; #endif newMRow("first ascending", manager) << manager << nameType << firstname << asc << false << 0 << cs #ifdef QT_USE_ICU // Case sensitivity is handled differently with/without ICU (in one case, the char sequence is // 'A-Za-z', in the other it is 'AaBb..Zz') - the results are therefore divergent << "abcdefghjik" #else << "abcdefgikjh" #endif << "efg"; // efg have the same first name #ifdef Q_OS_SYMBIAN qWarning() << "Test case \"first descending\" will fail on symbian platform because of QString::localeAwareCompare is not actually locale aware"; #endif newMRow("first descending", manager) << manager << nameType << firstname << desc << false << 0 << cs #ifdef QT_USE_ICU << "kijhefgdcba" #else << "hjkiefgdcba" #endif << "efg";// efg have the same first name newMRow("last ascending", manager) << manager << nameType << lastname << asc << false << 0 << cs << "bacdefghijk" << "hijk"; // all have a well defined, sortable last name except hijk #ifdef Q_OS_SYMBIAN qWarning() << "Test case \"last descending\" will fail on symbian platform because of QString::localeAwareCompare is not actually locale aware"; #endif newMRow("last descending", manager) << manager << nameType << lastname << desc << false << 0 << cs << "gfedcabhijk" << "hijk"; // all have a well defined, sortable last name except hijk if (validDetailField(integerDefAndFieldNames)) { newMRow("integer ascending, blanks last", manager) << manager << integerDefAndFieldNames.first << integerDefAndFieldNames.second << asc << true << bll << cs << "cabgfedhijk" << "gfedhijk"; // gfedhijk have no integer newMRow("integer descending, blanks last", manager) << manager << integerDefAndFieldNames.first << integerDefAndFieldNames.second << desc << true << bll << cs << "bacgfedhijk" << "gfedhijk"; // gfedhijk have no integer newMRow("integer ascending, blanks first", manager) << manager << integerDefAndFieldNames.first << integerDefAndFieldNames.second << asc << true << blf << cs << "hijkdefgcab" << "gfedhijk"; // gfedhijk have no integer newMRow("integer descending, blanks first", manager) << manager << integerDefAndFieldNames.first << integerDefAndFieldNames.second << desc << true << blf << cs << "hijkdefgbac" << "gfedhijk"; // gfedhijk have no integer } if (validDetailField(stringDefAndFieldNames)) { QTest::newRow("string ascending (null value), blanks first") << manager << stringDefAndFieldNames.first << stringDefAndFieldNames.second << asc << true << blf << cs << "feabcdg" << "fehijk"; // f and e have blank string QTest::newRow("string ascending (null value), blanks last") << manager << stringDefAndFieldNames.first << stringDefAndFieldNames.second << asc << true << bll << cs << "abcdgef" << "efhijk"; // f and e have blank string } newMRow("display label insensitive", manager) << manager << dlType << dlfld << asc << false << 0 << ci << "abcdefghjik" << "efghji"; #ifdef Q_OS_SYMBIAN qWarning() << "Test case \"display label sensitive\" will fail on symbian platform because of QString::localeAwareCompare is not actually locale aware"; #endif newMRow("display label sensitive", manager) << manager << dlType << dlfld << asc << false << 0 << cs << "abcdefghjik" << "efg"; } } void tst_QContactManagerFiltering::sorting() { QFETCH(QContactManager*, cm); QFETCH(QContactDetail::DetailType, detailType); QFETCH(int, detailField); QFETCH(int, directioni); QFETCH(bool, setbp); QFETCH(int, blankpolicyi); QFETCH(int, casesensitivityi); QFETCH(QString, expected); QFETCH(QString, unstable); Qt::SortOrder direction = (Qt::SortOrder)directioni; QContactSortOrder::BlankPolicy blankpolicy = (QContactSortOrder::BlankPolicy) blankpolicyi; Qt::CaseSensitivity casesensitivity = (Qt::CaseSensitivity) casesensitivityi; QList contacts = contactsAddedToManagers.values(cm); QList ids; /* Build the sort order */ QContactSortOrder s; s.setDetailType(detailType, detailField); s.setDirection(direction); if (setbp) s.setBlankPolicy(blankpolicy); s.setCaseSensitivity(casesensitivity); ids = cm->contactIds(s); QString output = convertIds(contacts, ids, 'a', 'k'); // don't include the convenience filtering contacts // It's possible to get some contacts back in an arbitrary order (since we single sort) if (unstable.length() > 1) { // ensure that the maximum distance between unstable elements in the output is the size of the unstable string. int firstIndex = -1; int lastIndex = -1; for (int i = 0; i < output.size(); i++) { if (unstable.contains(output.at(i))) { firstIndex = i; break; } } for (int i = output.size() - 1; i >= 0; i--) { if (unstable.contains(output.at(i))) { lastIndex = i; break; } } if (firstIndex == -1 || lastIndex == -1) { bool containsAllUnstableElements = false; QVERIFY(containsAllUnstableElements); } bool unstableElementsAreGrouped = ((lastIndex - firstIndex) == (unstable.length() - 1)); QVERIFY(unstableElementsAreGrouped); // now remove all unstable elements from the output for (int i = 1; i < unstable.length(); i++) { output.remove(unstable.at(i)); expected.remove(unstable.at(i)); } } // These tests pass only due to the un-sorted order matching the tested order: //QEXPECT_FAIL("display label insensitive[memory]", "memory backend does not add QContactDisplayLabel details", Continue); //QEXPECT_FAIL("display label insensitive[memory[params]]", "memory backend does not add QContactDisplayLabel details", Continue); QEXPECT_FAIL("display label sensitive[memory]", "memory backend does not add QContactDisplayLabel details", Continue); QEXPECT_FAIL("display label sensitive[memory[params]]", "memory backend does not add QContactDisplayLabel details", Continue); QCOMPARE(output, expected); /* Now do a check with a filter involved; the filter should not affect the sort order */ QContactDetailFilter presenceName; presenceName.setDetailType(QContactName::Type); ids = cm->contactIds(presenceName, s); output = convertIds(contacts, ids, 'a', 'k'); // don't include the convenience filtering contacts // It's possible to get some contacts back in an arbitrary order (since we single sort) if (unstable.length() > 1) { // ensure that the maximum distance between unstable elements in the output is the size of the unstable string. int firstIndex = -1; int lastIndex = -1; for (int i = 0; i < output.size(); i++) { if (unstable.contains(output.at(i))) { firstIndex = i; break; } } for (int i = output.size() - 1; i >= 0; i--) { if (unstable.contains(output.at(i))) { lastIndex = i; break; } } if (firstIndex == -1 || lastIndex == -1) { bool containsAllUnstableElements = false; QVERIFY(containsAllUnstableElements); } bool unstableElementsAreGrouped = ((lastIndex - firstIndex) == (unstable.length() - 1)); QVERIFY(unstableElementsAreGrouped); // now remove all unstable elements from the output for (int i = 1; i < unstable.length(); i++) { output.remove(unstable.at(i)); expected.remove(unstable.at(i)); } } //QEXPECT_FAIL("display label insensitive[memory]", "memory backend does not add QContactDisplayLabel details", Continue); //QEXPECT_FAIL("display label insensitive[memory[params]]", "memory backend does not add QContactDisplayLabel details", Continue); QEXPECT_FAIL("display label sensitive[memory]", "memory backend does not add QContactDisplayLabel details", Continue); QEXPECT_FAIL("display label sensitive[memory[params]]", "memory backend does not add QContactDisplayLabel details", Continue); QCOMPARE(output, expected); } void tst_QContactManagerFiltering::multiSorting_data() { QTest::addColumn("cm"); QTest::addColumn("firstsort"); QTest::addColumn("fsdetailtype"); QTest::addColumn("fsdetailfield"); QTest::addColumn("fsdirectioni"); QTest::addColumn("secondsort"); QTest::addColumn("ssdetailtype"); QTest::addColumn("ssdetailfield"); QTest::addColumn("ssdirectioni"); QTest::addColumn("expected"); QTest::addColumn("efgunstable"); QString es; int firstname = QContactName::FieldFirstName; int lastname = QContactName::FieldLastName; QContactDetail::DetailType nameType = QContactName::Type; QContactDetail::DetailType phoneType = QContactPhoneNumber::Type; int numberfield = QContactPhoneNumber::FieldNumber; int asc = Qt::AscendingOrder; int desc = Qt::DescendingOrder; for (int i = 0; i < managers.size(); i++) { QContactManager *manager = managers.at(i); QPair stringDefAndFieldNames = defAndFieldNamesForTypePerManager.value(manager).value("String"); QTest::newRow("1") << manager << true << nameType << firstname << asc << true << nameType << lastname << asc << "abcdefg" << false; QTest::newRow("2") << manager << true << nameType << firstname << asc << true << nameType << lastname << desc << "abcdgfe" << false; QTest::newRow("3") << manager << true << nameType << firstname << desc << true << nameType << lastname << asc << "efgdcba" << false; QTest::newRow("4") << manager << true << nameType << firstname << desc << true << nameType << lastname << desc << "gfedcba" << false; QTest::newRow("5") << manager << true << nameType << firstname << asc << false << nameType << lastname << asc << "abcdefg" << true; QTest::newRow("5b") << manager << true << nameType << firstname << asc << true << static_cast(-1) << -1 << asc << "abcdefg" << true; QTest::newRow("6") << manager << false << nameType << firstname << asc << true << nameType << lastname << asc << "bacdefg" << false; // This test is completely unstable; no sort criteria means dependent upon internal sort order of manager. //QTest::newRow("7") << manager // << false << nameType << firstname << asc // << false << nameType << lastname << asc // << "abcdefg" << false; // XXX Isn't this totally unstable? if (validDetailField(stringDefAndFieldNames)) { QTest::newRow("8") << manager << true << stringDefAndFieldNames.first << stringDefAndFieldNames.second << asc << false << stringDefAndFieldNames.first << stringDefAndFieldNames.second << desc << "abcdgef" << false; // default policy = blanks last, and ef have no value (e is empty, f is null) QTest::newRow("8b") << manager << true << stringDefAndFieldNames.first << stringDefAndFieldNames.second << asc << false << static_cast(-1) << -1 << desc << "abcdgef" << false; // default policy = blanks last, and ef have no value (e is empty, f is null) } QTest::newRow("9") << manager << true << phoneType << numberfield << asc << true << nameType << lastname << desc << "abgfedc" << false; QTest::newRow("10") << manager << true << nameType << firstname << asc << true << nameType << firstname << desc << "abcdefg" << true; } } void tst_QContactManagerFiltering::multiSorting() { QFETCH(QContactManager*, cm); QFETCH(bool, firstsort); QFETCH(QContactDetail::DetailType, fsdetailtype); QFETCH(int, fsdetailfield); QFETCH(int, fsdirectioni); QFETCH(bool, secondsort); QFETCH(QContactDetail::DetailType, ssdetailtype); QFETCH(int, ssdetailfield); QFETCH(int, ssdirectioni); QFETCH(QString, expected); QFETCH(bool, efgunstable); Qt::SortOrder fsdirection = (Qt::SortOrder)fsdirectioni; Qt::SortOrder ssdirection = (Qt::SortOrder)ssdirectioni; QList contacts = contactsAddedToManagers.values(cm); /* Build the sort orders */ QContactSortOrder fs; fs.setDetailType(fsdetailtype, fsdetailfield); fs.setDirection(fsdirection); QContactSortOrder ss; ss.setDetailType(ssdetailtype, ssdetailfield); ss.setDirection(ssdirection); QList sortOrders; if (firstsort) sortOrders.append(fs); if (secondsort) sortOrders.append(ss); QList ids = cm->contactIds(sortOrders); QString output = convertIds(contacts, ids, 'a', 'k'); // don't include the convenience filtering contacts // Remove the display label tests output.remove('h'); output.remove('i'); output.remove('j'); output.remove('k'); // Just like the single sort test, we might get some contacts back in indeterminate order // (but their relative position with other contacts should not change) if (efgunstable) { QVERIFY(output.count('e') == 1); QVERIFY(output.count('f') == 1); QVERIFY(output.count('g') == 1); output.remove('f'); output.remove('g'); expected.remove('f'); expected.remove('g'); } QCOMPARE(output, expected); } #ifdef INCLUDE_TESTACTIONS void tst_QContactManagerFiltering::actionPlugins() { QStringList actions = QContactAction::availableActions(); QVERIFY(actions.contains("Boolean")); QVERIFY(actions.contains("Number")); /* Ignore the version if the vendor is not set */ actions = QContactAction::availableActions(QString()); QVERIFY(actions.contains("Boolean")); QVERIFY(actions.contains("Number")); actions = QContactAction::availableActions("NumberCo"); QVERIFY(actions.contains("Number")); QVERIFY(!actions.contains("Boolean")); actions = QContactAction::availableActions("IntegerCo"); QVERIFY(actions.contains("Number")); QVERIFY(!actions.contains("Boolean")); actions = QContactAction::availableActions("BooleanCo"); QVERIFY(!actions.contains("Number")); QVERIFY(actions.contains("Boolean")); actions = QContactAction::availableActions("IntegerCo"); QVERIFY(actions.contains("Number")); QVERIFY(!actions.contains("Boolean")); actions = QContactAction::availableActions("BooleanCo"); QVERIFY(!actions.contains("Number")); QVERIFY(actions.contains("Boolean")); } void tst_QContactManagerFiltering::actionFiltering_data() { QTest::addColumn("cm"); QTest::addColumn("actionName"); QTest::addColumn("expected"); QString es; for (int i = 0; i < managers.size(); i++) { QContactManager *manager = managers.at(i); QPair booleanDefAndFieldNames = defAndFieldNamesForTypePerManager.value(manager).value("Bool"); QPair integerDefAndFieldNames = defAndFieldNamesForTypePerManager.value(manager).value("Integer"); QPair dateDefAndFieldNames = defAndFieldNamesForTypePerManager.value(manager).value("Date"); newMRow("bad actionname", manager) << manager << "No such action" << ""; QString expected; if (validDetailField(integerDefAndFieldNames) || validDetailField(booleanDefAndFieldNames)) { expected = "abcd"; } else if (validDetailField(dateDefAndFieldNames)) { expected = "abd"; } else { /* contact a,b have phone number, so at least phone number action can match them */ expected = "ab"; } QTest::newRow("empty (any action matches)") << manager << es << expected; if (validDetailField(integerDefAndFieldNames)) { newMRow("Number", manager) << manager << "NumberAction" << "abcd"; QTest::newRow("Number (NumberCo)") << manager << "NumberAction" << "abcd"; } if (validDetailField(booleanDefAndFieldNames)) { /* Boolean testing */ newMRow("Boolean action", manager) << manager << "BooleanAction" << "a"; newMRow("BooleanCo", manager) << manager << es << "a"; } if (validDetailField(booleanDefAndFieldNames)) { newMRow("Boolean action matching true", manager) << manager << es << "a"; newMRow("Boolean action matching false", manager) << manager << es << es; } /* Recursive filtering */ QTest::newRow("Recursive action 1") << manager << "IntersectionRecursive" << es; QTest::newRow("Recursive action 2") << manager << "UnionRecursive" << es; QTest::newRow("Recursive action 3") << manager << "PairRecursive" << es; QTest::newRow("Recursive action 4") << manager << "AnotherPairRecursive" << es; QTest::newRow("Recursive action 5") << manager << "Recursive" << es; } } void tst_QContactManagerFiltering::actionFiltering() { QFETCH(QContactManager*, cm); QFETCH(QString, actionName); QFETCH(QString, expected); // only test the memory engine - action filtering + service framework plugin loading // codepaths are tested fully this way since the codepath for other engines is that of // the memory engine, and only the memory engine has the required definitions and fields. if (cm->managerName() != QString(QLatin1String("memory"))) return; /* Load the definition and field names for the various variant types for the current manager */ defAndFieldNamesForTypeForActions = defAndFieldNamesForTypePerManager.value(cm); if (!defAndFieldNamesForTypeForActions.isEmpty()) { QContactActionFilter af; af.setActionName(actionName); QList ids = cm->contactIds(af); QList contacts = contactsAddedToManagers.values(cm); qDebug() << " actionName =" << actionName; QString output = convertIds(contacts, ids, 'a', 'k'); // don't include the convenience filtering contacts QCOMPARE_UNSORTED(output, expected); } } #endif void tst_QContactManagerFiltering::idListFiltering_data() { QTest::addColumn("cm"); QTest::addColumn("input"); QTest::addColumn("expected"); QString es; for (int i = 0; i < managers.size(); i++) { QContactManager *manager = managers.at(i); newMRow("empty", manager) << manager << es << es; newMRow("a", manager) << manager << "a" << "a"; newMRow("ab", manager) << manager << "ab" << "ab"; newMRow("aa", manager) << manager << "aa" << "a"; newMRow("ba", manager) << manager << "ba" << "ab"; newMRow("abcd", manager) << manager << "abcd" << "abcd"; newMRow("abcde", manager) << manager << "abcde" << "abcd"; } } void tst_QContactManagerFiltering::idListFiltering() { QFETCH(QContactManager*, cm); QFETCH(QString, input); QFETCH(QString, expected); QList contacts = contactsAddedToManagers.values(cm); QList ids; // extra id that won't exist QContactId e; /* Convert the input to a list of ids */ foreach(QChar c, input) { if (c == 'a') ids << contacts.at(0); else if (c == 'b') ids << contacts.at(1); else if (c == 'c') ids << contacts.at(2); else if (c == 'd') ids << contacts.at(3); else if (c == 'e') ids << e; } /* And do the search */ QContactIdFilter idf; idf.setIds(ids); /* Retrieve contacts matching the filter, and compare (unsorted) output */ ids = cm->contactIds(idf); QString output = convertIds(contacts, ids, 'a', 'k'); // don't include the convenience filtering contacts QCOMPARE_UNSORTED(output, expected); } void tst_QContactManagerFiltering::convenienceFiltering_data() { QTest::addColumn("cm"); QTest::addColumn("addressSubString"); QTest::addColumn("addressEnabled"); QTest::addColumn("emailAddressSubString"); QTest::addColumn("emailEnabled"); QTest::addColumn("phoneSubString"); QTest::addColumn("phoneEnabled"); QTest::addColumn("displayLabelSubString"); QTest::addColumn("displayLabelEnabled"); QTest::addColumn("nameSubString"); QTest::addColumn("nameEnabled"); QTest::addColumn("favoriteEnabled"); QTest::addColumn("tagSubString"); QTest::addColumn("tagEnabled"); QTest::addColumn("expected"); QString es; // empty string foreach (QContactManager *manager, managers) { const QList supportedTypes(manager->supportedContactDetailTypes()); if (supportedTypes.contains(QContactAddress::Type)) { newMRow("address matching only", manager) << manager << "streetstring" << true << es << false << es << false << es << false << es << false << false // Favorite has no substring associated. << es << false << "l"; } if (supportedTypes.contains(QContactEmailAddress::Type)) { newMRow("emailAddress matching only", manager) << manager << es << false << "@test.com" << true << es << false << es << false << es << false << false << es << false << "m"; } if (supportedTypes.contains(QContactPhoneNumber::Type)) { newMRow("phone matching only", manager) << manager << es << false << es << false << "12345" << true << es << false << es << false << false << es << false << "n"; } if (supportedTypes.contains(QContactDisplayLabel::Type)) { newMRow("displayLabel matching only", manager) << manager << es << false << es << false << es << false << "Freddy" << true << es << false << false << es << false << "o"; } if (supportedTypes.contains(QContactName::Type)) { newMRow("name matching only", manager) << manager << es << false << es << false << es << false << es << false << "Frederic" << true << false << es << false << "p"; } if (supportedTypes.contains(QContactFavorite::Type)) { newMRow("favorite matching only", manager) << manager << es << false << es << false << es << false << es << false << es << false << true << es << false << "q"; } if (supportedTypes.contains(QContactTag::Type)) { newMRow("tag matching only", manager) << manager << es << false << es << false << es << false << es << false << es << false << false << "Football" << true << "r"; } if (supportedTypes.contains(QContactAddress::Type) && supportedTypes.contains(QContactPhoneNumber::Type)) { newMRow("address or phone matching", manager) << manager << "streetstring" << true << es << false << "12345" << true << es << false << es << false << false << es << false << "ln"; } if (supportedTypes.contains(QContactFavorite::Type) && supportedTypes.contains(QContactTag::Type)) { newMRow("favorite or tag matching", manager) << manager << es << false << es << false << es << false << es << false << es << false << true << "Football" << true << "qr"; } } } void tst_QContactManagerFiltering::convenienceFiltering() { QFETCH(QContactManager*, cm); QFETCH(QString, addressSubString); QFETCH(bool, addressEnabled); QFETCH(QString, emailAddressSubString); QFETCH(bool, emailEnabled); QFETCH(QString, phoneSubString); QFETCH(bool, phoneEnabled); QFETCH(QString, displayLabelSubString); QFETCH(bool, displayLabelEnabled); QFETCH(QString, nameSubString); QFETCH(bool, nameEnabled); QFETCH(bool, favoriteEnabled); QFETCH(QString, tagSubString); QFETCH(bool, tagEnabled); QFETCH(QString, expected); QContactFilter af = QContactAddress::match(addressSubString); QContactFilter ef = QContactEmailAddress::match(emailAddressSubString); QContactFilter pf = QContactPhoneNumber::match(phoneSubString); QContactFilter df = QContactDisplayLabel::match(displayLabelSubString); QContactFilter nf = QContactName::match(nameSubString); QContactFilter ff = QContactFavorite::match(); QContactFilter tf = QContactTag::match(tagSubString); QList convenienceFilters; if (addressEnabled) convenienceFilters << af; if (emailEnabled) convenienceFilters << ef; if (phoneEnabled) convenienceFilters << pf; if (displayLabelEnabled) convenienceFilters << df; if (nameEnabled) convenienceFilters << nf; if (favoriteEnabled) convenienceFilters << ff; if (tagEnabled) convenienceFilters << tf; QContactFilter finalFilter; finalFilter = convenienceFilters.at(0); if (convenienceFilters.size() > 1) { for (int i = 1; i < convenienceFilters.size(); ++i) { // if more than one filter, we union them. finalFilter = (finalFilter | convenienceFilters.at(i)); } } /* Retrieve contacts matching the filter, and ensure that the results are expected */ QList ids = cm->contactIds(finalFilter); // build a string containing letters corresponding to the ids we retrieved. QList contacts = contactsAddedToManagers.values(cm); QString resultString = convertIds(contacts, ids, 'l', 'r'); // just the convenience filtering contacts (L->R) QEXPECT_FAIL("displayLabel matching only[memory]", "memory backend does not add QContactDisplayLabel details", Continue); QEXPECT_FAIL("displayLabel matching only[memory[params]]", "memory backend does not add QContactDisplayLabel details", Continue); QCOMPARE(resultString, expected); } void tst_QContactManagerFiltering::invalidFiltering_data() { QTest::addColumn("cm"); for (int i = 0; i < managers.size(); i++) { QContactManager *manager = managers.at(i); QTest::newRow(manager->managerName().toLatin1().constData()) << manager; } } void tst_QContactManagerFiltering::invalidFiltering() { QFETCH(QContactManager*, cm); QList contacts = contactsAddedToManagers.values(cm); QContactInvalidFilter f; // invalid QList ids = cm->contactIds(f); QVERIFY(ids.count() == 0); // Try unions/intersections of invalids too ids = cm->contactIds(f | f); QVERIFY(ids.count() == 0); ids = cm->contactIds(f & f); QVERIFY(ids.count() == 0); } void tst_QContactManagerFiltering::allFiltering_data() { QTest::addColumn("cm"); for (int i = 0; i < managers.size(); i++) { QContactManager *manager = managers.at(i); QTest::newRow(manager->managerName().toLatin1().constData()) << manager; } } void tst_QContactManagerFiltering::allFiltering() { QFETCH(QContactManager*, cm); QList contacts = contactsAddedToManagers.values(cm); QContactFilter f; // default = permissive QList ids = cm->contactIds(f); QVERIFY(ids.count() == contacts.size()); QString output = convertIds(contacts, ids, 'a', 'k'); // don't include the convenience filtering contacts QString expected = convertIds(contacts, contacts, 'a', 'k'); // :) QCOMPARE_UNSORTED(output, expected); // Try unions/intersections of defaults ids = cm->contactIds(f | f); output = convertIds(contacts, ids, 'a', 'k'); // don't include the convenience filtering contacts QCOMPARE_UNSORTED(output, expected); ids = cm->contactIds(f & f); output = convertIds(contacts, ids, 'a', 'k'); // don't include the convenience filtering contacts QCOMPARE_UNSORTED(output, expected); } void tst_QContactManagerFiltering::fetchHint_data() { QTest::addColumn("cm"); for (int i = 0; i < managers.size(); i++) { QContactManager *manager = managers.at(i); QTest::newRow(manager->managerName().toLatin1().constData()) << manager; } } void tst_QContactManagerFiltering::fetchHint() { QFETCH(QContactManager*, cm); // if no fetch hint is provided, the manager should return all data // if a fetch hint is provided, it should have a clearly defined effect, // unless it is ignored by the manager, in which case the result should // be equivalent to not providing a fetch hint. // we use a defined sort order for the retrieval of contacts to make comparison simple. // We sort on name, because we include name details in the fetch hint. QList nameSort; QContactSortOrder firstNameSort, middleNameSort, lastNameSort; firstNameSort.setDetailType(QContactName::Type, QContactName::FieldFirstName); middleNameSort.setDetailType(QContactName::Type, QContactName::FieldMiddleName); lastNameSort.setDetailType(QContactName::Type, QContactName::FieldLastName); nameSort << lastNameSort << middleNameSort << firstNameSort; // fetch all contacts from the manager. QList allContacts = cm->contacts(nameSort); // define some maximum count limit, and some list of detail definitions to retrieve. int countLimit = (allContacts.size() / 2) + 1; QList defs; defs << QContactName::Type << QContactPhoneNumber::Type; // test that the manager doesn't incorrectly implement fetch hints. // we test max count limit, and detail definition limits. // XXX TODO: other hints! QContactFetchHint mclh; // max count limited hint QContactFetchHint ddh; // detail definitions hint mclh.setMaxCountHint(countLimit); ddh.setDetailTypesHint(defs); // the next part of the test requires some contacts to be saved in the manager. if (allContacts.size() == 0) { QSKIP("No contacts in manager; skipping fetch hint limit test."); } // test with a hint which sets a maximum count limit for retrieved contacts. QList mclhContacts = cm->contacts(nameSort, mclh); QVERIFY(allContacts.size() >= mclhContacts.size()); if (allContacts.size() > mclh.maxCountHint()) { // shouldn't return an arbitrarily smaller amount of contacts. QVERIFY(mclhContacts.size() == mclh.maxCountHint() || mclhContacts.size() == allContacts.size()); } for (int i = 0; i < mclhContacts.size(); ++i) { // the sort order should still be defined. QVERIFY(mclhContacts.at(i) == allContacts.at(i)); } // now test with a hint which describes which details the client is interested in. QList ddhContacts = cm->contacts(nameSort, ddh); QCOMPARE(ddhContacts.size(), allContacts.size()); for (int i = 0; i < allContacts.size(); ++i) { QContact a = allContacts.at(i); QContact b = ddhContacts.at(i); // since we're sorting on a detail which should exist (since it was included in the hint) // the order of the contacts returned shouldn't have changed. QVERIFY(a.id() == b.id()); // check that the hint didn't remove names or phones. QCOMPARE(a.details(QContactName::Type).size(), b.details(QContactName::Type).size()); QCOMPARE(a.details(QContactPhoneNumber::Type).size(), b.details(QContactPhoneNumber::Type).size()); // other details are not necessarily returned. QVERIFY(a.details().size() >= b.details().size()); } } void tst_QContactManagerFiltering::changelogFiltering_data() { QTest::addColumn("cm"); QTest::addColumn >("contacts"); QTest::addColumn("eventType"); QTest::addColumn("since"); QTest::addColumn("expected"); int added = (int)QContactChangeLogFilter::EventAdded; int changed = (int)QContactChangeLogFilter::EventChanged; int removed = (int)QContactChangeLogFilter::EventRemoved; for (int i = 0; i < managers.size(); i++) { QContactManager *manager = managers.at(i); QList contacts = contactsAddedToManagers.values(manager); QContact a,b,c,d; a = manager->contact(contacts.at(0)); b = manager->contact(contacts.at(1)); c = manager->contact(contacts.at(2)); d = manager->contact(contacts.at(3)); QDateTime ac = a.detail().created(); QDateTime bc = b.detail().created(); QDateTime cc = c.detail().created(); QDateTime dc = d.detail().created(); QDateTime am = a.detail().lastModified(); QDateTime bm = b.detail().lastModified(); QDateTime cm = c.detail().lastModified(); QDateTime dm = d.detail().lastModified(); newMRow("Added since before start", manager) << manager << contacts << added << ac.addSecs(-1) << "abcdefg"; newMRow("Added since first", manager) << manager << contacts << added << ac << "abcdefg"; newMRow("Added since second", manager) << manager << contacts << added << bc << "bcdefg"; newMRow("Added since third", manager) << manager << contacts << added << cc << "cdefg"; newMRow("Added since fourth", manager) << manager << contacts << added << dc << "defg"; newMRow("Added since after fourth", manager) << manager << contacts << added << dc.addSecs(1) << "efg"; newMRow("Added since first changed", manager) << manager << contacts << added << am << ""; newMRow("Added since second changed", manager) << manager << contacts << added << bm << ""; newMRow("Added since third changed", manager) << manager << contacts << added << cm << ""; newMRow("Added since fourth changed", manager) << manager << contacts << added << cm << ""; newMRow("Changed since before start", manager) << manager << contacts << changed << ac.addSecs(-1) << "abcdefg"; newMRow("Changed since first", manager) << manager << contacts << changed << ac << "abcdefg"; newMRow("Changed since second", manager) << manager << contacts << changed << bc << "abcdefg"; newMRow("Changed since third", manager) << manager << contacts << changed << cc << "abcdefg"; newMRow("Changed since fourth", manager) << manager << contacts << changed << dc << "abcdefg"; newMRow("Changed since after fourth", manager) << manager << contacts << changed << dc.addSecs(1) << "abcefg"; newMRow("Changed since first changed", manager) << manager << contacts << changed << am << "a"; newMRow("Changed since second changed", manager) << manager << contacts << changed << bm << "ab"; newMRow("Changed since third changed", manager) << manager << contacts << changed << cm << "abc"; newMRow("Changed since fourth changed", manager) << manager << contacts << changed << dm << "abcdefg"; // These are currently useless.. newMRow("Removed since before start", manager) << manager << contacts << removed << ac.addSecs(-1) << ""; newMRow("Removed since first", manager) << manager << contacts << removed << ac << ""; newMRow("Removed since second", manager) << manager << contacts << removed << bc << ""; newMRow("Removed since third", manager) << manager << contacts << removed << cc << ""; newMRow("Removed since fourth", manager) << manager << contacts << removed << dc << ""; newMRow("Removed since after fourth", manager) << manager << contacts << removed << dc.addSecs(1) << ""; } } void tst_QContactManagerFiltering::changelogFiltering() { QFETCH(int, eventType); QFETCH(QDateTime, since); QFETCH(QString, expected); QFETCH(QContactManager*, cm); QFETCH(QList, contacts); QList ids; QContactChangeLogFilter clf((QContactChangeLogFilter::EventType)eventType); clf.setSince(since); ids = cm->contactIds(clf); QString output = convertIds(contacts, ids, 'a', 'g'); // don't include the convenience filtering contacts QCOMPARE(output, expected); // unsorted? or sorted? } QList tst_QContactManagerFiltering::prepareModel(QContactManager *cm) { // Sleep between contact additions to allow change log filtering to differentiate contacts by timestamp // Assume 1-second resolution or better const int napTime = 1000; QMap > definitionDetails; definitionDetails.insert("String", qMakePair(QContactGuid::Type, static_cast(QContactGuid::FieldGuid))); definitionDetails.insert("Integer", qMakePair(QContactPresence::Type, static_cast(QContactPresence::FieldPresenceState))); definitionDetails.insert("DateTime", qMakePair(QContactAnniversary::Type, static_cast(QContactAnniversary::FieldOriginalDate))); definitionDetails.insert("Date", qMakePair(QContactBirthday::Type, static_cast(QContactBirthday::FieldBirthday))); definitionDetails.insert("Bool", qMakePair(QContactFavorite::Type, static_cast(QContactFavorite::FieldFavorite))); definitionDetails.insert("Double", qMakePair(QContactDetail::TypeGeoLocation, static_cast(QContactGeoLocation::FieldLatitude))); // Currently unused: definitionDetails.insert("LongLong", qMakePair(QContactDetail::TypeUndefined, -1)); definitionDetails.insert("ULongLong", qMakePair(QContactDetail::TypeUndefined, -1)); definitionDetails.insert("Time", qMakePair(QContactDetail::TypeUndefined, -1)); definitionDetails.insert("UInt", qMakePair(QContactDetail::TypeUndefined, -1)); definitionDetails.insert("Char", qMakePair(QContactDetail::TypeUndefined, -1)); defAndFieldNamesForTypePerManager.insert(cm, definitionDetails); /* Add some contacts */ QContact contactA, contactB, contactC, contactD; QContactName name; QContactPhoneNumber number; // Create details for each relevant value type QContactDetail stringDetail(definitionDetails.value("String").first); QContactDetail integerDetail(definitionDetails.value("Integer").first); QContactDetail datetimeDetail(definitionDetails.value("DateTime").first); QContactDetail doubleDetail(definitionDetails.value("Double").first); QContactDetail boolDetail(definitionDetails.value("Bool").first); QContactDetail longlongDetail(definitionDetails.value("LongLong").first); QContactDetail ulonglongDetail(definitionDetails.value("ULongLong").first); QContactDetail dateDetail(definitionDetails.value("Date").first); QContactDetail timeDetail(definitionDetails.value("Time").first); QContactDetail uintDetail(definitionDetails.value("UInt").first); QContactDetail charDetail(definitionDetails.value("Char").first); name.setFirstName("Aaron"); name.setLastName("Aaronson"); name.setMiddleName("Arne"); name.setPrefix("Sir"); name.setSuffix("Dr."); QContactNickname nick; nick.setNickname("Sir Aaron"); QContactEmailAddress emailAddr; emailAddr.setEmailAddress("Aaron@Aaronson.com"); number.setNumber("5551212"); stringDetail.setValue(definitionDetails.value("String").second, "Aaron Aaronson"); integerDetail.setValue(definitionDetails.value("Integer").second, 10); datetimeDetail.setValue(definitionDetails.value("DateTime").second, QDateTime(QDate(2009, 06, 29), QTime(16, 52, 23, 0))); boolDetail.setValue(definitionDetails.value("Bool").second, true); ulonglongDetail.setValue(definitionDetails.value("ULongLong").second, (qulonglong)120000000000LL); // 120B dateDetail.setValue(definitionDetails.value("Date").second, QDate(1988, 1, 26)); timeDetail.setValue(definitionDetails.value("Time").second, QTime(16,52,23,0)); contactA.saveDetail(&name); contactA.saveDetail(&nick); contactA.saveDetail(&emailAddr); contactA.saveDetail(&number); if (validDetailField(definitionDetails.value("String"))) contactA.saveDetail(&stringDetail); if (validDetailField(definitionDetails.value("Integer"))) contactA.saveDetail(&integerDetail); if (validDetailField(definitionDetails.value("DateTime"))) contactA.saveDetail(&datetimeDetail); if (validDetailField(definitionDetails.value("Bool"))) contactA.saveDetail(&boolDetail); if (validDetailField(definitionDetails.value("ULongLong"))) contactA.saveDetail(&ulonglongDetail); if (validDetailField(definitionDetails.value("Date"))) contactA.saveDetail(&dateDetail); if (validDetailField(definitionDetails.value("Time"))) contactA.saveDetail(&timeDetail); name = QContactName(); name.setFirstName("Bob"); name.setLastName("Aaronsen"); nick.setNickname("Sir Bob"); number.setNumber("5553456"); stringDetail.setValue(definitionDetails.value("String").second, "Bob Aaronsen"); integerDetail.setValue(definitionDetails.value("Integer").second, 20); doubleDetail.setValue(definitionDetails.value("Double").second, 4.0); boolDetail.setValue(definitionDetails.value("Bool").second, false); ulonglongDetail.setValue(definitionDetails.value("ULongLong").second, (qulonglong) 80000000000LL); // 80B uintDetail.setValue(definitionDetails.value("UInt").second, 4000000000u); // 4B dateDetail.setValue(definitionDetails.value("Date").second, QDate(2492, 5, 5)); timeDetail.setValue(definitionDetails.value("Time").second, QTime(15,52,23,0)); charDetail.setValue(definitionDetails.value("Char").second, QVariant(QChar('b'))); contactB.saveDetail(&name); contactB.saveDetail(&nick); contactB.saveDetail(&number); if (validDetailField(definitionDetails.value("String"))) contactB.saveDetail(&stringDetail); if (validDetailField(definitionDetails.value("Integer"))) contactB.saveDetail(&integerDetail); if (validDetailField(definitionDetails.value("Double"))) contactB.saveDetail(&doubleDetail); if (validDetailField(definitionDetails.value("Bool"))) contactB.saveDetail(&boolDetail); if (validDetailField(definitionDetails.value("ULongLong"))) contactB.saveDetail(&ulonglongDetail); if (validDetailField(definitionDetails.value("UInt"))) contactB.saveDetail(&uintDetail); if (validDetailField(definitionDetails.value("Date"))) contactB.saveDetail(&dateDetail); if (validDetailField(definitionDetails.value("Time"))) contactB.saveDetail(&timeDetail); if (validDetailField(definitionDetails.value("Char"))) contactB.saveDetail(&charDetail); name.setFirstName("Boris"); name.setLastName("Aaronsun"); stringDetail.setValue(definitionDetails.value("String").second, "Boris Aaronsun"); integerDetail.setValue(definitionDetails.value("Integer").second, -20); datetimeDetail.setValue(definitionDetails.value("DateTime").second, QDateTime(QDate(2009, 06, 29), QTime(16, 54, 17, 0))); longlongDetail.setValue(definitionDetails.value("LongLong").second, (qlonglong)8000000000LL); // 8B charDetail.setValue(definitionDetails.value("Char").second, QVariant(QChar('c'))); contactC.saveDetail(&name); if (validDetailField(definitionDetails.value("String"))) contactC.saveDetail(&stringDetail); if (validDetailField(definitionDetails.value("Integer"))) contactC.saveDetail(&integerDetail); if (validDetailField(definitionDetails.value("DateTime"))) contactC.saveDetail(&datetimeDetail); if (validDetailField(definitionDetails.value("Double"))) contactC.saveDetail(&doubleDetail); if (validDetailField(definitionDetails.value("Bool"))) contactC.saveDetail(&boolDetail); if (validDetailField(definitionDetails.value("LongLong"))) contactC.saveDetail(&longlongDetail); if (validDetailField(definitionDetails.value("ULongLong"))) contactC.saveDetail(&ulonglongDetail); if (validDetailField(definitionDetails.value("Char"))) contactC.saveDetail(&charDetail); name.setFirstName("Dennis"); name.setLastName("FitzMacintyre"); stringDetail.setValue(definitionDetails.value("String").second, "Dennis FitzMacintyre"); doubleDetail.setValue(definitionDetails.value("Double").second, -128.0); longlongDetail.setValue(definitionDetails.value("LongLong").second, (qlonglong)-14000000000LL); uintDetail.setValue(definitionDetails.value("UInt").second, 3000000000u); // 3B dateDetail.setValue(definitionDetails.value("Date").second, QDate(2770, 10, 1)); contactD.saveDetail(&name); if (validDetailField(definitionDetails.value("String"))) contactD.saveDetail(&stringDetail); if (validDetailField(definitionDetails.value("Double"))) contactD.saveDetail(&doubleDetail); if (validDetailField(definitionDetails.value("LongLong"))) contactD.saveDetail(&longlongDetail); if (validDetailField(definitionDetails.value("UInt"))) contactD.saveDetail(&uintDetail); if (validDetailField(definitionDetails.value("Date"))) contactD.saveDetail(&dateDetail); qDebug() << "Generating contacts with different timestamps, please wait.."; int originalContactCount = cm->contactIds().count(); bool successfulSave = cm->saveContact(&contactA); Q_FATAL_VERIFY(successfulSave); QTest::qSleep(napTime); successfulSave = cm->saveContact(&contactB); Q_FATAL_VERIFY(successfulSave); QTest::qSleep(napTime); successfulSave = cm->saveContact(&contactC); Q_FATAL_VERIFY(successfulSave); QTest::qSleep(napTime); successfulSave = cm->saveContact(&contactD); Q_FATAL_VERIFY(successfulSave); QTest::qSleep(napTime); /* Now add some contacts specifically for multisorting */ QContact contactE,contactF,contactG; QContactName n; n.setFirstName("John"); n.setLastName("Smithee"); stringDetail.setValue(definitionDetails.value("String").second, ""); if (validDetailField(definitionDetails.value("String"))) contactE.saveDetail(&stringDetail); contactE.saveDetail(&n); n = QContactName(); n.setFirstName("John"); n.setLastName("Smithey"); contactF.saveDetail(&n); n = QContactName(); n.setFirstName("John"); n.setLastName("Smithy"); stringDetail.setValue(definitionDetails.value("String").second, "zzz"); if (validDetailField(definitionDetails.value("String"))) contactG.saveDetail(&stringDetail); contactG.saveDetail(&n); successfulSave = cm->saveContact(&contactE); Q_FATAL_VERIFY(successfulSave); successfulSave = cm->saveContact(&contactF); Q_FATAL_VERIFY(successfulSave); successfulSave = cm->saveContact(&contactG); Q_FATAL_VERIFY(successfulSave); originalContactCount += 7; Q_FATAL_VERIFY(cm->contactIds().count() == originalContactCount); /* Now some for the locale aware sorting */ /* In the C locale, all lowercase chars sort after uppercase */ QContact contactH, contactI, contactJ, contactK; QContactName n2; n2.setFirstName("xander"); contactH.saveDetail(&n2); n2.setFirstName("Xander"); contactI.saveDetail(&n2); n2.setFirstName("xAnder"); contactJ.saveDetail(&n2); n2.setFirstName("Yarrow"); contactK.saveDetail(&n2); Q_FATAL_VERIFY(cm->saveContact(&contactH)); Q_FATAL_VERIFY(cm->saveContact(&contactI)); Q_FATAL_VERIFY(cm->saveContact(&contactJ)); Q_FATAL_VERIFY(cm->saveContact(&contactK)); /* Ensure the last modified times are different */ QTest::qSleep(napTime); QContactName modifiedName = contactC.detail(QContactName::Type); contactC.saveDetail(&modifiedName); cm->saveContact(&contactC); QTest::qSleep(napTime); modifiedName = contactB.detail(QContactName::Type); contactB.saveDetail(&modifiedName); cm->saveContact(&contactB); QTest::qSleep(napTime); modifiedName = contactA.detail(QContactName::Type); contactA.saveDetail(&modifiedName); cm->saveContact(&contactA); QTest::qSleep(napTime); /* Now some for convenience filtering */ QList allTypes = cm->supportedContactDetailTypes(); // Contact L ---------------------------------------- QContact contactL; if (allTypes.contains(QContactAddress::Type)) { QContactAddress ladr; ladr.setStreet("streetstring road"); // Contact L matches streetstring. ladr.setLocality("testplace"); ladr.setRegion("somewhere"); contactL.saveDetail(&ladr); } if (allTypes.contains(QContactEmailAddress::Type)) { QContactEmailAddress led; led.setEmailAddress("frad@test.domain"); contactL.saveDetail(&led); } if (allTypes.contains(QContactPhoneNumber::Type)) { QContactPhoneNumber lp; lp.setNumber("11111"); contactL.saveDetail(&lp); } if (allTypes.contains(QContactName::Type)) { QContactName ln; ln.setFirstName("Fradarick"); ln.setLastName("Gumboots"); contactL.saveDetail(&ln); } if (allTypes.contains(QContactTag::Type)) { QContactTag lt; lt.setTag("Soccer"); contactL.saveDetail(<); } // Contact M ---------------------------------------- QContact contactM; if (allTypes.contains(QContactAddress::Type)) { QContactAddress madr; madr.setStreet("some road"); madr.setLocality("testplace"); madr.setRegion("somewhere"); contactM.saveDetail(&madr); } if (allTypes.contains(QContactEmailAddress::Type)) { QContactEmailAddress med; med.setEmailAddress("frbd@test.com"); // Contact M matches @test.com contactM.saveDetail(&med); } if (allTypes.contains(QContactPhoneNumber::Type)) { QContactPhoneNumber mp; mp.setNumber("22222"); contactM.saveDetail(&mp); } if (allTypes.contains(QContactName::Type)) { QContactName mn; mn.setFirstName("Frbdbrick"); mn.setLastName("Gumboots"); contactM.saveDetail(&mn); } if (allTypes.contains(QContactTag::Type)) { QContactTag mt; mt.setTag("Soccer"); contactM.saveDetail(&mt); } // Contact N ---------------------------------------- QContact contactN; if (allTypes.contains(QContactAddress::Type)) { QContactAddress nadr; nadr.setStreet("some road"); nadr.setLocality("testplace"); nadr.setRegion("somewhere"); contactN.saveDetail(&nadr); } if (allTypes.contains(QContactEmailAddress::Type)) { QContactEmailAddress ned; ned.setEmailAddress("frcd@test.domain"); contactN.saveDetail(&ned); } if (allTypes.contains(QContactPhoneNumber::Type)) { QContactPhoneNumber np; np.setNumber("12345"); // Contact N matches 12345 contactN.saveDetail(&np); } if (allTypes.contains(QContactName::Type)) { QContactName nn; nn.setFirstName("Frcdcrick"); nn.setLastName("Gumboots"); contactN.saveDetail(&nn); } if (allTypes.contains(QContactTag::Type)) { QContactTag nt; nt.setTag("Soccer"); contactN.saveDetail(&nt); } // Contact O ---------------------------------------- QContact contactO; if (allTypes.contains(QContactAddress::Type)) { QContactAddress oadr; oadr.setStreet("some road"); oadr.setLocality("testplace"); oadr.setRegion("somewhere"); contactO.saveDetail(&oadr); } if (allTypes.contains(QContactEmailAddress::Type)) { QContactEmailAddress oed; oed.setEmailAddress("frdd@test.domain"); contactO.saveDetail(&oed); } if (allTypes.contains(QContactPhoneNumber::Type)) { QContactPhoneNumber op; op.setNumber("44444"); contactO.saveDetail(&op); } if (allTypes.contains(QContactName::Type)) { QContactName on; on.setFirstName("Freddy"); // Contact O matches Freddy on.setLastName("Gumboots"); contactO.saveDetail(&on); } if (allTypes.contains(QContactTag::Type)) { QContactTag ot; ot.setTag("Soccer"); contactO.saveDetail(&ot); } // Contact P ---------------------------------------- QContact contactP; if (allTypes.contains(QContactAddress::Type)) { QContactAddress padr; padr.setStreet("some road"); padr.setLocality("testplace"); padr.setRegion("somewhere"); contactP.saveDetail(&padr); } if (allTypes.contains(QContactEmailAddress::Type)) { QContactEmailAddress ped; ped.setEmailAddress("fred@test.domain"); contactP.saveDetail(&ped); } if (allTypes.contains(QContactPhoneNumber::Type)) { QContactPhoneNumber pp; pp.setNumber("55555"); contactP.saveDetail(&pp); } if (allTypes.contains(QContactName::Type)) { QContactName pn; pn.setFirstName("Frederick"); // Contact P matches Frederic (contains). pn.setLastName("Gumboots"); contactP.saveDetail(&pn); } if (allTypes.contains(QContactTag::Type)) { QContactTag pt; pt.setTag("Soccer"); contactP.saveDetail(&pt); } // Contact Q ---------------------------------------- QContact contactQ; if (allTypes.contains(QContactAddress::Type)) { QContactAddress qadr; qadr.setStreet("some road"); qadr.setLocality("testplace"); qadr.setRegion("somewhere"); contactQ.saveDetail(&qadr); } if (allTypes.contains(QContactEmailAddress::Type)) { QContactEmailAddress qed; qed.setEmailAddress("frfd@test.domain"); contactQ.saveDetail(&qed); } if (allTypes.contains(QContactPhoneNumber::Type)) { QContactPhoneNumber qp; qp.setNumber("66666"); contactQ.saveDetail(&qp); } if (allTypes.contains(QContactName::Type)) { QContactName qn; qn.setFirstName("Frfdfrick"); qn.setLastName("Gumboots"); contactQ.saveDetail(&qn); } if (allTypes.contains(QContactTag::Type)) { QContactTag qt; qt.setTag("Soccer"); contactQ.saveDetail(&qt); } if (allTypes.contains(QContactFavorite::Type)) { QContactFavorite qf; qf.setFavorite(true); // Contact Q matches favorite = true contactQ.saveDetail(&qf); } // Contact R ---------------------------------------- QContact contactR; if (allTypes.contains(QContactAddress::Type)) { QContactAddress radr; radr.setStreet("some road"); radr.setLocality("testplace"); radr.setRegion("somewhere"); contactR.saveDetail(&radr); } if (allTypes.contains(QContactEmailAddress::Type)) { QContactEmailAddress red; red.setEmailAddress("frgd@test.domain"); contactR.saveDetail(&red); } if (allTypes.contains(QContactPhoneNumber::Type)) { QContactPhoneNumber rp; rp.setNumber("77777"); contactR.saveDetail(&rp); } if (allTypes.contains(QContactName::Type)) { QContactName rn; rn.setFirstName("Frgdgrick"); rn.setLastName("Gumboots"); contactR.saveDetail(&rn); } if (allTypes.contains(QContactTag::Type)) { QContactTag rt; rt.setTag("Football"); // Contact R matches Football contactR.saveDetail(&rt); } // --------------------- save. Q_FATAL_VERIFY(cm->saveContact(&contactL)); Q_FATAL_VERIFY(cm->saveContact(&contactM)); Q_FATAL_VERIFY(cm->saveContact(&contactN)); Q_FATAL_VERIFY(cm->saveContact(&contactO)); Q_FATAL_VERIFY(cm->saveContact(&contactP)); Q_FATAL_VERIFY(cm->saveContact(&contactQ)); Q_FATAL_VERIFY(cm->saveContact(&contactR)); // --------------------- end. /* Reload the contacts to pick up any changes */ contactA = cm->contact(contactA.id()); contactB = cm->contact(contactB.id()); contactC = cm->contact(contactC.id()); contactD = cm->contact(contactD.id()); contactE = cm->contact(contactE.id()); contactF = cm->contact(contactF.id()); contactG = cm->contact(contactG.id()); contactH = cm->contact(contactH.id()); contactI = cm->contact(contactI.id()); contactJ = cm->contact(contactJ.id()); contactK = cm->contact(contactK.id()); contactL = cm->contact(contactL.id()); contactM = cm->contact(contactM.id()); contactN = cm->contact(contactN.id()); contactO = cm->contact(contactO.id()); contactP = cm->contact(contactP.id()); contactQ = cm->contact(contactQ.id()); contactR = cm->contact(contactR.id()); QList list; /* Add our newly saved contacts to our internal list of added contacts */ foreach (const QContactId &id, (QList() << contactR.id() << contactQ.id() << contactP.id() << contactO.id() << contactN.id() << contactM.id() << contactL.id() << contactK.id() << contactJ.id() << contactI.id() << contactH.id() << contactG.id() << contactF.id() << contactE.id() << contactD.id() << contactC.id() << contactB.id() << contactA.id())) { if (!id.isNull()) { contactsAddedToManagers.insert(cm, id); list.prepend(id); } } return list; } /* ============ Utility functions ============= */ void tst_QContactManagerFiltering::dumpContactDifferences(const QContact& ca, const QContact& cb) { // Try to narrow down the differences QContact a(ca); QContact b(cb); QContactName n1 = a.detail(QContactName::Type); QContactName n2 = b.detail(QContactName::Type); // Check the name components in more detail QCOMPARE(n1.firstName(), n2.firstName()); QCOMPARE(n1.middleName(), n2.middleName()); QCOMPARE(n1.lastName(), n2.lastName()); QCOMPARE(n1.prefix(), n2.prefix()); QCOMPARE(n1.suffix(), n2.suffix()); // Check the display label QCOMPARE(a.detail().label(), b.detail().label()); // Now look at the rest QList aDetails = a.details(); QList bDetails = b.details(); // They can be in any order, so loop // First remove any matches foreach(QContactDetail d, aDetails) { foreach(QContactDetail d2, bDetails) { if(d == d2) { a.removeDetail(&d); b.removeDetail(&d2); break; } } } // Now dump the extra details that were unmatched in A aDetails = a.details(); bDetails = b.details(); foreach(QContactDetail d, aDetails) { if (d.type() != QContactDisplayLabel::Type) qDebug() << "A contact had extra detail:" << d.type() << d.values(); } // and same for B foreach(QContactDetail d, bDetails) { if (d.type() != QContactDisplayLabel::Type) qDebug() << "B contact had extra detail:" << d.type() << d.values(); } QCOMPARE(b, a); } bool tst_QContactManagerFiltering::isSuperset(const QContact& ca, const QContact& cb) { // returns true if contact ca is a superset of contact cb // we use this test instead of equality because dynamic information // such as presence/location, and synthesised information such as // display label and (possibly) type, may differ between a contact // in memory and the contact in the managed store. QContact a(ca); QContact b(cb); QList aDetails = a.details(); QList bDetails = b.details(); // They can be in any order, so loop // First remove any matches foreach(QContactDetail d, aDetails) { foreach(QContactDetail d2, bDetails) { if(d == d2) { a.removeDetail(&d); b.removeDetail(&d2); break; } } } // check for contact type updates if (a.type() != b.type()) return false; // nonempty type is different. // Now check to see if b has any details remaining; if so, a is not a superset. // Note that the DisplayLabel and Type can never be removed. if (b.details().size() > 2 || (b.details().size() == 2 && (b.details().value(0).type() != QContactDisplayLabel::Type || b.details().value(1).type() != QContactType::Type))) return false; return true; } void tst_QContactManagerFiltering::dumpContact(const QContact& contact) { QContactManager m; qDebug() << "Contact: " << contact.id() << "(" << contact.detail().label() << ")"; QList details = contact.details(); foreach(QContactDetail d, details) { qDebug() << " " << d.type() << ":"; qDebug() << " Vals:" << d.values(); } } void tst_QContactManagerFiltering::dumpContacts() { QContactManager m; QList ids = m.contactIds(); foreach(QContactId id, ids) { QContact c = m.contact(id); dumpContact(c); } } QTEST_MAIN(tst_QContactManagerFiltering) #include "tst_qcontactmanagerfiltering.moc" #include "qcontactmanager.h" tests/auto/contacts/qcontactmanagerfiltering/unittest/unittest.pro000066400000000000000000000005461233466112000263440ustar00rootroot00000000000000include(../../../auto.pri) QT += contacts versit contacts-private qtHaveModule(serviceframework) { QT += serviceframework } contains(QT_CONFIG,icu):DEFINES += QT_USE_ICU SOURCES += tst_qcontactmanagerfiltering.cpp HEADERS += ../../qcontactmanagerdataholder.h INCLUDEPATH += ../.. \ ../../.. DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactmanagerplugins/000077500000000000000000000000001233466112000215555ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerplugins/dummyotherplugin/000077500000000000000000000000001233466112000251715ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerplugins/dummyotherplugin/dummyotherplugin.cpp000066400000000000000000000046431233466112000313200ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ /* Implement a dynamic plugin with a non contacts interface to make sure it doesn't get used */ #include #include class BoringInterface { public: void doNothing() {} }; QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(BoringInterface, "REALLYBORING!"); QT_END_NAMESPACE class BoringFactory : public QObject, public BoringInterface { Q_OBJECT Q_INTERFACES(BoringInterface) }; Q_EXPORT_PLUGIN2(contacts_testotherdummy, BoringFactory); #include "dummyotherplugin.moc" tests/auto/contacts/qcontactmanagerplugins/dummyotherplugin/dummyotherplugin.pro000066400000000000000000000002661233466112000313330ustar00rootroot00000000000000CONFIG += testplugin TARGET = contacts_testotherdummy PLUGIN_TYPE=contacts load(qt_plugin) QT += contacts SOURCES += dummyotherplugin.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactmanagerplugins/dummyplugin/000077500000000000000000000000001233466112000241275ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerplugins/dummyplugin/dummy.json000066400000000000000000000000401233466112000261470ustar00rootroot00000000000000{ "Keys": [ "dummytest" ] } tests/auto/contacts/qcontactmanagerplugins/dummyplugin/dummyplugin.cpp000066400000000000000000000146641233466112000272200ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ /* We build multiple plugins with the same code, just different names */ #ifndef DUMMYPLUGINTARGET #define DUMMYPLUGINTARGET contacts_testdummy #endif #define makestr(x) (#x) #define makename(x) makestr(x) #include #include #include #include "dummyplugin.h" QTCONTACTS_USE_NAMESPACE QContactManagerEngine* DummyEngineFactory::engine(const QMap& parameters, QContactManager::Error* error) { return new DummyEngine(parameters, error); } QString DummyEngineFactory::managerName() const { #ifdef DUMMYPLUGINNAME return QString(makename(DUMMYPLUGINNAME)); #else return QString(); #endif } DummyEngine::DummyEngine(const QMap& parameters, QContactManager::Error* error) { Q_UNUSED(parameters); *error = QContactManager::AlreadyExistsError; // Another random choice } DummyEngine::DummyEngine(const DummyEngine& other) : QContactManagerEngine() { Q_UNUSED(other); } DummyEngine& DummyEngine::operator=(const DummyEngine& other) { Q_UNUSED(other); return *this; } DummyEngine::~DummyEngine() { } QContactManagerEngine* DummyEngine::clone() { // Disallow sharing return new DummyEngine(*this); } void DummyEngine::deref() { delete this; } QString DummyEngine::managerName() const { #ifdef DUMMYPLUGINNAME return QString(makename(DUMMYPLUGINNAME)); #else return QString(); #endif } QList DummyEngine::contacts(QContactManager::Error* error) const { QList allCIds; if (allCIds.count() > 0 && *error == QContactManager::NoError) *error = QContactManager::DoesNotExistError; return allCIds; } QContact DummyEngine::contact(const QContactId& contactId, QContactManager::Error* error) const { Q_UNUSED(contactId); *error = QContactManager::DoesNotExistError; return QContact(); } QContact DummyEngine::contact(const QContactId& contactId, const QContactFetchHint& fetchHint, QContactManager::Error* error) const { Q_UNUSED(contactId); Q_UNUSED(fetchHint); *error = QContactManager::DoesNotExistError; return QContact(); } bool DummyEngine::saveContact(QContact* contact, bool batch, QContactManager::Error* error) { // ensure that the contact's details conform to their definitions if (!validateContact(*contact, error)) { *error = QContactManager::InvalidDetailError; return false; } // success! QContactId newId; contact->setId(newId); *error = QContactManager::NoError; // if we need to emit signals (ie, this isn't part of a batch operation) // then emit the correct one. if (!batch) { QList emitList; emitList.append(contact->id()); emit contactsAdded(emitList); } return true; } bool DummyEngine::removeContact(const QContactId& contactId, bool batch, QContactManager::Error* error) { /* TODO: fix this if (contactId != "5") { *error = QContactManager::DoesNotExistError; return false; }*/ *error = QContactManager::NoError; // if we need to emit signals (ie, this isn't part of a batch operation) // then emit the correct one. if (!batch) { QList emitList; emitList.append(contactId); emit contactsRemoved(emitList); } return true; } /*! Returns the capabilities of the in-memory engine. */ QStringList DummyEngine::capabilities() const { QStringList caplist; caplist << "Locking" << "Batch" << "ReadOnly" << "Filtering" << "Sorting" << "Preferences"; // ie, doesn't support: Changelog, Volatile, Asynchronous. return caplist; } /*! * Returns a list of definition identifiers which are natively (fast) filterable * on the default backend store managed by the manager from which the capabilities object was accessed */ QStringList DummyEngine::fastFilterableDefinitions() const { QStringList fastlist; fastlist << "Name::First" << "Name::Last" << "PhoneNumber::PhoneNumber" << "EmailAddress::EmailAddress"; return fastlist; } /*! * Returns the list of data types supported by the vCard engine */ QList DummyEngine::supportedDataTypes() const { QList st; st.append(QVariant::String); st.append(QVariant::Date); st.append(QVariant::DateTime); return st; } QContactEngineId* DummyEngineFactory::createContactEngineId(const QMap& parameters, const QString& engineIdString) const { return 0; } #include "moc_dummyplugin.cpp" tests/auto/contacts/qcontactmanagerplugins/dummyplugin/dummyplugin.h000066400000000000000000000160201233466112000266510ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef DUMMYPLUGIN_H #define DUMMYPLUGIN_H #include #include #include #include QTCONTACTS_USE_NAMESPACE class DummyEngine : public QContactManagerEngine { Q_OBJECT public: DummyEngine(const QMap& parameters, QContactManager::Error* error); DummyEngine(const DummyEngine& other); ~DummyEngine(); DummyEngine& operator=(const DummyEngine& other); QContactManagerEngine* clone(); void deref(); QString managerName() const; /* Contacts - Accessors and Mutators */ QList contacts(QContactManager::Error* error) const; QContact contact(const QContactId& contactId, QContactManager::Error* error) const; QContact contact(const QContactId& contactId, const QContactFetchHint& fetchHint, QContactManager::Error* error) const; bool saveContact(QContact* contact, bool batch, QContactManager::Error* error); bool removeContact(const QContactId& contactId, bool batch, QContactManager::Error* error); /* Capabilities reporting */ QStringList capabilities() const; QStringList fastFilterableDefinitions() const; QList supportedDataTypes() const; QMap managerParameters() const {return QMap();} int managerVersion() const {return 0;} QList contactIds(const QContactFilter&, const QList&, QContactManager::Error* error) const { *error = QContactManager::NotSupportedError; return QList(); } QList contacts(const QContactFilter&, const QList&, const QContactFetchHint&, QContactManager::Error* error) const { *error = QContactManager::NotSupportedError; return QList(); } bool saveContacts(QList*, QMap*, QContactManager::Error* error) { *error = QContactManager::NotSupportedError; return false; } bool removeContacts(const QList&, QMap*, QContactManager::Error* error) { *error = QContactManager::NotSupportedError; return false; } QContact conformingContact(const QContact&, QContactManager::Error* error) { *error = QContactManager::NotSupportedError; return QContact(); } /* Synthesize the display label of a contact */ virtual QString synthesizedDisplayLabel(const QContact&, QContactManager::Error* error) const { *error = QContactManager::NotSupportedError; return QString(); } /* "Self" contact id (MyCard) */ virtual bool setSelfContactId(const QContactId&, QContactManager::Error* error) { *error = QContactManager::NotSupportedError; return false; } QContactId selfContactId(QContactManager::Error* error) const { return QContactManagerEngine::selfContactId(error); } /* Relationships between contacts */ virtual QList relationships(const QString&, const QContactId&, QContactRelationship::Role, QContactManager::Error* error) const { *error = QContactManager::NotSupportedError; return QList(); } virtual bool saveRelationships(QList*, QMap*, QContactManager::Error* error) { *error = QContactManager::NotSupportedError; return false; } virtual bool removeRelationships(const QList&, QMap*, QContactManager::Error* error) { *error = QContactManager::NotSupportedError; return false; } /* Validation for saving */ virtual bool validateContact(const QContact&, QContactManager::Error* error) const { *error = QContactManager::NotSupportedError; return false; } /* Asynchronous Request Support */ virtual void requestDestroyed(QContactAbstractRequest*) {} virtual bool startRequest(QContactAbstractRequest*) {return false;} virtual bool cancelRequest(QContactAbstractRequest*) {return false;} virtual bool waitForRequestFinished(QContactAbstractRequest*, int) {return false;} virtual bool isRelationshipTypeSupported(const QString&, const QString&) const { return false; } virtual bool isFilterSupported(const QContactFilter&) const { return false; } virtual QList supportedContactTypes() const { return QList(); } }; class DummyEngineFactory : public QContactManagerEngineFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QContactManagerEngineFactoryInterface" FILE "dummy.json") public: QContactManagerEngine* engine(const QMap& parameters, QContactManager::Error* error); QContactEngineId* createContactEngineId(const QMap& parameters, const QString& engineIdString) const; QString managerName() const; }; #endif // DUMMYPLUGIN_H tests/auto/contacts/qcontactmanagerplugins/dummyplugin/dummyplugin.pro000066400000000000000000000003541233466112000272250ustar00rootroot00000000000000CONFIG += testplugin TARGET = contacts_testdummy PLUGIN_TYPE=contacts load(qt_plugin) QT += contacts SOURCES += dummyplugin.cpp HEADERS += dummyplugin.h DEFINES += DUMMYPLUGINNAME=testdummy DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactmanagerplugins/dummyplugincopy/000077500000000000000000000000001233466112000250225ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerplugins/dummyplugincopy/dummyplugincopy.pro000066400000000000000000000005021233466112000310060ustar00rootroot00000000000000CONFIG += testplugin TARGET = contacts_testdummycopy PLUGIN_TYPE=contacts load(qt_plugin) QT += contacts DEFINES += DUMMYPLUGINTARGET=contacts_testdummycopy DEFINES += DUMMYPLUGINNAME=testdummy SOURCES += ../dummyplugin/dummyplugin.cpp HEADERS += ../dummyplugin/dummyplugin.h DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactmanagerplugins/dummypluginempty/000077500000000000000000000000001233466112000252065ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerplugins/dummypluginempty/dummypluginempty.pro000066400000000000000000000004371233466112000313650ustar00rootroot00000000000000CONFIG += testplugin TARGET = contacts_testdummyempty PLUGIN_TYPE=contacts load(qt_plugin) QT += contacts DEFINES += DUMMYPLUGINTARGET=contacts_testdummyempty SOURCES += ../dummyplugin/dummyplugin.cpp HEADERS += ../dummyplugin/dummyplugin.h DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactmanagerplugins/dummyplugininvalid/000077500000000000000000000000001233466112000254765ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerplugins/dummyplugininvalid/dummyplugininvalid.pro000066400000000000000000000005061233466112000321420ustar00rootroot00000000000000CONFIG += testplugin TARGET = contacts_testdummyinvalid PLUGIN_TYPE=contacts load(qt_plugin) QT += contacts DEFINES += DUMMYPLUGINTARGET=contacts_testdummyinvalid DEFINES += DUMMYPLUGINNAME=invalid SOURCES += ../dummyplugin/dummyplugin.cpp HEADERS += ../dummyplugin/dummyplugin.h DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactmanagerplugins/qcontactmanagerplugins.pro000066400000000000000000000002331233466112000270460ustar00rootroot00000000000000TEMPLATE=subdirs SUBDIRS += \ dummyplugincopy \ dummyplugininvalid \ dummyotherplugin \ dummypluginempty \ dummyplugin \ unittest tests/auto/contacts/qcontactmanagerplugins/unittest/000077500000000000000000000000001233466112000234345ustar00rootroot00000000000000tests/auto/contacts/qcontactmanagerplugins/unittest/tst_qcontactmanagerplugins.cpp000066400000000000000000000156631233466112000316160ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/contacts /* Force a static plugin */ #define QT_STATICPLUGIN #include #include #include QTCONTACTS_USE_NAMESPACE class tst_QContactManagerPlugins : public QObject { Q_OBJECT public: tst_QContactManagerPlugins(); virtual ~tst_QContactManagerPlugins(); public slots: void init(); void cleanup(); private slots: void testDummy(); }; /* Test a static factory as well */ class DummyStaticEngineFactory : public QObject, public QContactManagerEngineFactory { Q_OBJECT Q_INTERFACES(QtContacts::QContactManagerEngineFactory) public: QContactManagerEngine* engine(const QMap& parameters, QContactManager::Error* error); QContactEngineId* createContactEngineId(const QMap& parameters, const QString& engineIdString) const; QString managerName() const {return "teststaticdummy";} }; QContactManagerEngine* DummyStaticEngineFactory::engine(const QMap& parameters, QContactManager::Error* error) { Q_UNUSED(parameters); *error = QContactManager::LockedError; // random unlikely error return 0; // always fail, haha } QContactEngineId* DummyStaticEngineFactory::createContactEngineId(const QMap& parameters, const QString& engineIdString) const { Q_UNUSED(parameters); Q_UNUSED(engineIdString); return 0; } Q_EXPORT_PLUGIN2(contacts_teststaticdummy, DummyStaticEngineFactory) Q_IMPORT_PLUGIN(contacts_teststaticdummy) /* And a copy */ Q_EXPORT_PLUGIN2(contacts_teststaticdummycopy, DummyStaticEngineFactory) Q_IMPORT_PLUGIN(contacts_teststaticdummycopy) /* And test an impostor as well */ class ImpostorEngineFactory : public QObject, public QContactManagerEngineFactory { Q_OBJECT Q_INTERFACES(QtContacts::QContactManagerEngineFactory) public: QContactManagerEngine* engine(const QMap& , QContactManager::Error* ) {return 0;} QContactEngineId* createContactEngineId(const QMap& parameters, const QString& engineIdString) const {return 0;} QString managerName() const {return "memory";} }; Q_EXPORT_PLUGIN2(contacts_testimpostordummy, ImpostorEngineFactory) Q_IMPORT_PLUGIN(contacts_testimpostordummy) /* And test another impostor as well */ class ImpostorEngineFactory2 : public QObject, public QContactManagerEngineFactory { Q_OBJECT Q_INTERFACES(QtContacts::QContactManagerEngineFactory) public: QContactManagerEngine* engine(const QMap& , QContactManager::Error* ) {return 0;} QContactEngineId* createContactEngineId(const QMap& parameters, const QString& engineIdString) const {return 0;} QString managerName() const {return "invalid";} }; Q_EXPORT_PLUGIN2(contacts_testimpostordummy2, ImpostorEngineFactory2) Q_IMPORT_PLUGIN(contacts_testimpostordummy2) /* An empty interface name */ class EmptyEngineFactory : public QObject, public QContactManagerEngineFactory { Q_OBJECT Q_INTERFACES(QtContacts::QContactManagerEngineFactory) public: QContactManagerEngine* engine(const QMap& , QContactManager::Error* ) {return 0;} QContactEngineId* createContactEngineId(const QMap& parameters, const QString& engineIdString) const {return 0;} QString managerName() const {return QString();} }; Q_EXPORT_PLUGIN2(contacts_teststaticemptydummy, EmptyEngineFactory) Q_IMPORT_PLUGIN(contacts_teststaticemptydummy) /* And a different interface one too */ class BoringInterface { public: void doNothing() {} }; QT_BEGIN_NAMESPACE Q_DECLARE_INTERFACE(BoringInterface, "REALLYBORING!") QT_END_NAMESPACE class BoringFactory : public QObject, public BoringInterface { Q_OBJECT Q_INTERFACES(BoringInterface) }; Q_EXPORT_PLUGIN2(contacts_testboring, BoringFactory) Q_IMPORT_PLUGIN(contacts_testboring) tst_QContactManagerPlugins::tst_QContactManagerPlugins() { } tst_QContactManagerPlugins::~tst_QContactManagerPlugins() { } void tst_QContactManagerPlugins::init() { /* Add a path to our plugin path */ QString path = QApplication::applicationDirPath() + "/dummyplugin/plugins"; QApplication::addLibraryPath(path); QApplication::addLibraryPath(path); // Test the plugin path deduplication code path = QApplication::applicationDirPath() + "/dummyplugin"; QApplication::addLibraryPath(path); QApplication::addLibraryPath("/"); // strictly to test a cdUp :/ } void tst_QContactManagerPlugins::cleanup() { } void tst_QContactManagerPlugins::testDummy() { QVERIFY(QContactManager::availableManagers().contains("testdummy")); QVERIFY(QContactManager::availableManagers().contains("teststaticdummy")); QContactManager m1("teststaticdummy"); // should fail QVERIFY(m1.managerName() == "invalid"); QVERIFY(m1.error() == QContactManager::LockedError); QContactManager m2("testdummy"); QVERIFY(m2.managerName() == "testdummy"); } QTEST_MAIN(tst_QContactManagerPlugins) #include "tst_qcontactmanagerplugins.moc" tests/auto/contacts/qcontactmanagerplugins/unittest/unittest.pro000066400000000000000000000002541233466112000260360ustar00rootroot00000000000000include(../../../auto.pri) TARGET = tst_qcontactmanagerplugins QT += widgets contacts SOURCES += tst_qcontactmanagerplugins.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactmemusage/000077500000000000000000000000001233466112000203445ustar00rootroot00000000000000tests/auto/contacts/qcontactmemusage/qcontactmemusage.pro000066400000000000000000000001701233466112000244240ustar00rootroot00000000000000include(../../auto.pri) QT += contacts SOURCES += tst_qcontactmemusage.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactmemusage/tst_qcontactmemusage.cpp000066400000000000000000000206321233466112000253050ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include //TESTED_COMPONENT=src/contacts QTCONTACTS_USE_NAMESPACE // Define global op new so we can track some amount of allocations static qulonglong cAllocated = 0; static qulonglong nAllocations = 0; /* void * operator new(size_t sz) { nAllocations++; cAllocated += sz; return malloc(sz); } void * operator new[](size_t sz) { nAllocations++; cAllocated += sz; return malloc(sz); } void operator delete(void * block) { if (block) free(block); } void operator delete[](void *block) { if (block) free(block); } */ // Also hook malloc (with glibc) void *(*old_hook)(size_t, const void*); void *qmallochook(size_t size, const void* ) { void * ret; nAllocations++; cAllocated += size; __malloc_hook = old_hook; ret = malloc(size); __malloc_hook = qmallochook; return ret; } // I can't seem to get the __malloc_init_hook method working (C++ mangling?) static int qmallocinithook() { old_hook = __malloc_hook; __malloc_hook = qmallochook; return 1; } Q_CONSTRUCTOR_FUNCTION(qmallocinithook); class QMemUsed { public: QMemUsed() : mLabel(0), mAllocated(cAllocated), mAllocations(nAllocations), mItems(0) { } QMemUsed(const char* name) : mLabel(name), mAllocated(cAllocated), mAllocations(nAllocations), mItems(0) { } QMemUsed(const char* name, int nItems) : mLabel(name), mAllocated(cAllocated), mAllocations(nAllocations), mItems(nItems) { } ~QMemUsed() { mAllocated = cAllocated - mAllocated; mAllocations = nAllocations - mAllocations; QString allocations = mAllocations > 1 ? QString("allocations") : QString("allocation"); if (mItems > 1) { QString msg("%1 bytes in %2 %8 (for %3 x %6 -> %4 bytes, %5 allocations per %7)"); qDebug() << msg.arg(mAllocated).arg(mAllocations).arg(mItems).arg(((double)mAllocated) / mItems).arg(((double) mAllocations) / mItems).arg(mLabel).arg(mLabel).arg(allocations).toLatin1().constData(); } else if (mLabel.latin1()){ QString msg("%1 bytes in %2 %4 for %3"); qDebug() << msg.arg(mAllocated).arg(mAllocations).arg(mLabel).arg(allocations).toLatin1().constData(); } else { QString msg("%1 bytes in %2 %3"); qDebug() << msg.arg(mAllocated).arg(mAllocations).arg(allocations).toLatin1().constData(); } } QLatin1String mLabel; qulonglong mAllocated; qulonglong mAllocations; int mItems; }; QTCONTACTS_USE_NAMESPACE class tst_QContactMemUsage : public QObject { Q_OBJECT public: tst_QContactMemUsage(); virtual ~tst_QContactMemUsage(); public slots: void init(); void cleanup(); private slots: void single(); void multiple(); private: QMemUsed mem; }; tst_QContactMemUsage::tst_QContactMemUsage() { } tst_QContactMemUsage::~tst_QContactMemUsage() { } void tst_QContactMemUsage::init() { } void tst_QContactMemUsage::cleanup() { } void tst_QContactMemUsage::single() { // Try single allocations { QMemUsed m("PhoneNumber"); QContactPhoneNumber p1; } { QMemUsed m("Address"); QContactAddress a; } { QMemUsed m("Name"); QContactName p1; } } void tst_QContactMemUsage::multiple() { // First 10 { QMemUsed m("PhoneNumber", 10); QContactPhoneNumber p1; QContactPhoneNumber p2; QContactPhoneNumber p3; QContactPhoneNumber p4; QContactPhoneNumber p5; QContactPhoneNumber p6; QContactPhoneNumber p7; QContactPhoneNumber p8; QContactPhoneNumber p9; QContactPhoneNumber p10; } { QMemUsed m("Address", 10); QContactAddress p1; QContactAddress p2; QContactAddress p3; QContactAddress p4; QContactAddress p5; QContactAddress p6; QContactAddress p7; QContactAddress p8; QContactAddress p9; QContactAddress p10; } { QMemUsed m("Address", 10); QContactAddress p1; QContactAddress p2; QContactAddress p3; QContactAddress p4; QContactAddress p5; QContactAddress p6; QContactAddress p7; QContactAddress p8; QContactAddress p9; QContactAddress p10; } { QMemUsed m("Name", 10); QContactName p1; QContactName p2; QContactName p3; QContactName p4; QContactName p5; QContactName p6; QContactName p7; QContactName p8; QContactName p9; QContactName p10; } { QList details; QMemUsed m("PhoneNumber", 100); for (int i = 0; i < 100; i++) { QContactPhoneNumber a; details.append(a); } } { QList details; QMemUsed m("Address", 100); for (int i = 0; i < 100; i++) { QContactAddress a; details.append(a); } } { QList details; QMemUsed m("Name", 100); for (int i = 0; i < 100; i++) { QContactName a; details.append(a); } } // Now set some values { QString v("1234"); QMemUsed m("PhoneNumber with number"); QContactPhoneNumber p; p.setNumber(v); } { QVariant v("1234"); QMemUsed m("PhoneNumber with number as variant"); QContactPhoneNumber p; p.setValue(QContactPhoneNumber::FieldNumber, v); } { QMemUsed m("PhoneNumber with number and subtype"); QContactPhoneNumber p; p.setNumber("1234"); p.setSubTypes(QList() << QContactPhoneNumber::SubTypeMobile); } { QMemUsed m("PhoneNumber with custom fields"); QContactPhoneNumber p; QVariant v = QString("1234"); p.setValue(QContactAddress::FieldCountry, v); p.setValue(QContactUrl::FieldUrl, v); p.setValue(QContactEmailAddress::FieldEmailAddress, v); p.setValue(QContactName::FieldFirstName, v); p.setValue(QContactName::FieldLastName, v); p.setValue(QContactName::FieldMiddleName, v); p.setValue(QContactName::FieldPrefix, v); p.setValue(QContactName::FieldSuffix, v); p.setValue(QContactAddress::FieldLocality, v); p.setValue(QContactAddress::FieldRegion, v); } } QTEST_MAIN(tst_QContactMemUsage) #include "tst_qcontactmemusage.moc" tests/auto/contacts/qcontactrelationship/000077500000000000000000000000001233466112000212425ustar00rootroot00000000000000tests/auto/contacts/qcontactrelationship/qcontactrelationship.pro000066400000000000000000000002341233466112000262210ustar00rootroot00000000000000include(../../auto.pri) QT += contacts SOURCES += tst_qcontactrelationship.cpp HEADERS += ../qcontactidmock.h DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qcontactrelationship/tst_qcontactrelationship.cpp000066400000000000000000000137211233466112000271020ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/contacts #include #include #include "../qcontactidmock.h" QTCONTACTS_USE_NAMESPACE class tst_QContactRelationship: public QObject { Q_OBJECT public: tst_QContactRelationship(); virtual ~tst_QContactRelationship(); private slots: void operations(); void emptiness(); void hash(); void datastream(); }; tst_QContactRelationship::tst_QContactRelationship() { } tst_QContactRelationship::~tst_QContactRelationship() { } void tst_QContactRelationship::operations() { QContactRelationship r1; QContact contact1, contact2; QVERIFY(r1.first() == QContact()); QVERIFY(r1.second() == QContact()); QVERIFY(r1.relationshipType() == QString()); r1.setFirst(contact1); QVERIFY(r1.first() == contact1); r1.setSecond(contact2); QVERIFY(r1.second() == contact2); QVERIFY(r1.first() == contact1); QVERIFY(r1.relationshipType() == QString()); r1.setRelationshipType(QContactRelationship::HasSpouse()); QVERIFY(r1.relationshipType() == QContactRelationship::HasSpouse()); } void tst_QContactRelationship::emptiness() { QContactRelationship r1, r2, r3; QContact contact1, contact2, contact3; contact1.setId(QContactIdMock::createId("test", 1)); contact2.setId(QContactIdMock::createId("test", 2)); contact3.setId(QContactIdMock::createId("test", 3)); QVERIFY(r1.first() == QContact()); QVERIFY(r1.second() == QContact()); QVERIFY(r1.relationshipType() == QString()); r1.setFirst(contact1); QVERIFY(r1.first() == contact1); r1.setSecond(contact2); QVERIFY(r1.second() == contact2); QVERIFY(r1.first() == contact1); QVERIFY(r1.relationshipType() == QString()); r1.setRelationshipType(QContactRelationship::HasSpouse()); QVERIFY(r1.relationshipType() == QContactRelationship::HasSpouse()); r2 = r1; QVERIFY(r1 == r2); QVERIFY(r1 != r3); QVERIFY(r2 != r3); r3.setFirst(contact3); r3.setSecond(contact2); r3.setRelationshipType(QContactRelationship::HasAssistant()); r2.setFirst(contact3); QVERIFY(r1 != r2); QVERIFY(r2 != r3); QVERIFY(r3 != r1); } void tst_QContactRelationship::hash() { QContactRelationship r1; QContact contact1; contact1.setId(QContactIdMock::createId("a", 1)); r1.setFirst(contact1); QContact contact2; contact2.setId(QContactIdMock::createId("b", 2)); r1.setSecond(contact2); r1.setRelationshipType(QContactRelationship::HasMember()); QContactRelationship r2; r2.setFirst(contact1); r2.setSecond(contact2); r2.setRelationshipType(QContactRelationship::HasMember()); QContactRelationship r3; r3.setFirst(contact1); QContact contact3; contact1.setId(QContactIdMock::createId("c", 3)); r3.setSecond(contact3); r3.setRelationshipType(QContactRelationship::HasMember()); QVERIFY(qHash(r1) == qHash(r2)); QVERIFY(qHash(r1) != qHash(r3)); } void tst_QContactRelationship::datastream() { QSKIP("Datastream is not currently supported when using the QContactIdMock class"); //After QContactId introduction, the QCOMPARE at then end this case will fail, since //relationshipOut will contain contacts with "null" QContactIds, whereas //relationshipIn will contain contacts with QContactIds referring to //manager "a", which is an invalid one. QByteArray buffer; QDataStream stream1(&buffer, QIODevice::WriteOnly); QContactRelationship relationshipIn; QContact contact1; contact1.setId(QContactIdMock::createId("a", 1)); relationshipIn.setFirst(contact1); QContact contact2; contact2.setId(QContactIdMock::createId("a", 2)); relationshipIn.setSecond(contact2); relationshipIn.setRelationshipType(QContactRelationship::HasMember()); stream1 << relationshipIn; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); QContactRelationship relationshipOut; stream2 >> relationshipOut; QCOMPARE(relationshipOut, relationshipIn); } QTEST_MAIN(tst_QContactRelationship) #include "tst_qcontactrelationship.moc" tests/auto/contacts/qcontactsortorder/000077500000000000000000000000001233466112000205645ustar00rootroot00000000000000tests/auto/contacts/qcontactsortorder/qcontactsortorder.pro000066400000000000000000000001171233466112000250650ustar00rootroot00000000000000include(../../auto.pri) QT += contacts SOURCES += tst_qcontactsortorder.cpp tests/auto/contacts/qcontactsortorder/tst_qcontactsortorder.cpp000066400000000000000000000463161233466112000257540ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include //TESTED_COMPONENT=src/contacts QTCONTACTS_USE_NAMESPACE Q_DECLARE_METATYPE(QContact) Q_DECLARE_METATYPE(QContactSortOrder) class tst_QContactSortOrder : public QObject { Q_OBJECT public slots: void init(); void cleanup(); private slots: void traits(); void sortObject(); void compareContact_data(); void compareContact(); void datastream_data(); void datastream(); void debugstream_data(); void debugstream(); }; void tst_QContactSortOrder::init() { } void tst_QContactSortOrder::cleanup() { } void tst_QContactSortOrder::traits() { QTypeInfo ti; QVERIFY(ti.isComplex); QVERIFY(!ti.isStatic); QVERIFY(!ti.isLarge); QVERIFY(!ti.isPointer); QVERIFY(!ti.isDummy); QCOMPARE(uint(ti.sizeOf), uint(sizeof(void *))); } void tst_QContactSortOrder::sortObject() { QContactSortOrder sortorder; /* Defaults */ QVERIFY(sortorder.blankPolicy() == QContactSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QContactDetail::TypeUndefined); QVERIFY(sortorder.detailField() == -1); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.caseSensitivity() == Qt::CaseSensitive); QVERIFY(!sortorder.isValid()); QVERIFY(sortorder == QContactSortOrder()); /* Blank Policy */ sortorder.setBlankPolicy(QContactSortOrder::BlanksFirst); QVERIFY(sortorder.blankPolicy() == QContactSortOrder::BlanksFirst); QVERIFY(sortorder.detailType() == QContactDetail::TypeUndefined); QVERIFY(sortorder.detailField() == -1); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.caseSensitivity() == Qt::CaseSensitive); QVERIFY(!sortorder.isValid()); QVERIFY(sortorder != QContactSortOrder()); sortorder.setBlankPolicy(QContactSortOrder::BlanksLast); QVERIFY(sortorder.blankPolicy() == QContactSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QContactDetail::TypeUndefined); QVERIFY(sortorder.detailField() == -1); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.caseSensitivity() == Qt::CaseSensitive); QVERIFY(!sortorder.isValid()); QVERIFY(sortorder == QContactSortOrder()); /* Direction */ sortorder.setDirection(Qt::DescendingOrder); QVERIFY(sortorder.direction() == Qt::DescendingOrder); QVERIFY(sortorder.blankPolicy() == QContactSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QContactDetail::TypeUndefined); QVERIFY(sortorder.detailField() == -1); QVERIFY(sortorder.caseSensitivity() == Qt::CaseSensitive); QVERIFY(!sortorder.isValid()); QVERIFY(sortorder != QContactSortOrder()); sortorder.setDirection(Qt::AscendingOrder); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.blankPolicy() == QContactSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QContactDetail::TypeUndefined); QVERIFY(sortorder.detailField() == -1); QVERIFY(sortorder.caseSensitivity() == Qt::CaseSensitive); QVERIFY(!sortorder.isValid()); QVERIFY(sortorder == QContactSortOrder()); /* Case sensitivity */ sortorder.setCaseSensitivity(Qt::CaseInsensitive); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.blankPolicy() == QContactSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QContactDetail::TypeUndefined); QVERIFY(sortorder.detailField() == -1); QVERIFY(sortorder.caseSensitivity() == Qt::CaseInsensitive); QVERIFY(!sortorder.isValid()); QVERIFY(sortorder != QContactSortOrder()); sortorder.setCaseSensitivity(Qt::CaseSensitive); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.blankPolicy() == QContactSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QContactDetail::TypeUndefined); QVERIFY(sortorder.detailField() == -1); QVERIFY(sortorder.caseSensitivity() == Qt::CaseSensitive); QVERIFY(!sortorder.isValid()); QVERIFY(sortorder == QContactSortOrder()); /* Definitions */ sortorder.setDetailType(QContactDetail::DetailType(), -1); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.blankPolicy() == QContactSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QContactDetail::TypeUndefined); QVERIFY(sortorder.detailField() == -1); QVERIFY(!sortorder.isValid()); sortorder.setDetailType(QContactDetail::TypeUndefined, -1); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.blankPolicy() == QContactSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QContactDetail::TypeUndefined); QVERIFY(sortorder.detailField() == -1); QVERIFY(!sortorder.isValid()); sortorder.setDetailType(QContactDetail::TypeAddress, QContactAddress::FieldStreet); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.blankPolicy() == QContactSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QContactDetail::TypeAddress); QVERIFY(sortorder.detailField() == QContactAddress::FieldStreet); QVERIFY(sortorder.isValid()); sortorder.setDetailType(QContactDetail::DetailType(), QContactAddress::FieldStreet); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.blankPolicy() == QContactSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QContactDetail::TypeUndefined); QVERIFY(sortorder.detailField() == QContactAddress::FieldStreet); QVERIFY(!sortorder.isValid()); /* Copy ctor */ sortorder.setDetailType(QContactDetail::TypeAddress, QContactAddress::FieldStreet); sortorder.setBlankPolicy(QContactSortOrder::BlanksFirst); sortorder.setDirection(Qt::DescendingOrder); QVERIFY(sortorder.direction() == Qt::DescendingOrder); QVERIFY(sortorder.blankPolicy() == QContactSortOrder::BlanksFirst); QVERIFY(sortorder.detailType() == QContactDetail::TypeAddress); QVERIFY(sortorder.detailField() == QContactAddress::FieldStreet); QVERIFY(sortorder.isValid()); QContactSortOrder other(sortorder); QVERIFY(other.direction() == Qt::DescendingOrder); QVERIFY(other.blankPolicy() == QContactSortOrder::BlanksFirst); QVERIFY(other.detailType() == QContactDetail::TypeAddress); QVERIFY(other.detailField() == QContactAddress::FieldStreet); QVERIFY(other.isValid()); QVERIFY(other == sortorder); QVERIFY(!(other != sortorder)); other.setDetailType(QContactDetail::TypePhoneNumber, QContactAddress::FieldStreet); QVERIFY(other != sortorder); other.setDetailType(QContactDetail::TypeAddress, QContactAddress::FieldRegion); QVERIFY(other != sortorder); /* Assignment operator */ QContactSortOrder another; another = other; QVERIFY(another.direction() == Qt::DescendingOrder); QVERIFY(another.blankPolicy() == QContactSortOrder::BlanksFirst); QVERIFY(another.detailType() == QContactDetail::TypeAddress); QVERIFY(another.detailField() == QContactAddress::FieldRegion); QVERIFY(another.isValid()); QVERIFY(another == other); QVERIFY(!(other != another)); /* Self assignment */ another = another; QVERIFY(another.direction() == Qt::DescendingOrder); QVERIFY(another.blankPolicy() == QContactSortOrder::BlanksFirst); QVERIFY(another.detailType() == QContactDetail::TypeAddress); QVERIFY(another.detailField() == QContactAddress::FieldRegion); QVERIFY(another.isValid()); QVERIFY(another == other); QVERIFY(!(other != another)); } void tst_QContactSortOrder::compareContact_data() { QTest::addColumn("contact1"); QTest::addColumn("contact2"); QTest::addColumn("sortOrder"); QTest::addColumn("expected"); QContact emptyContact; { QContact contact1; QContactNote note1; note1.setNote(QLatin1String("note")); contact1.saveDetail(¬e1); QContactDisplayLabel displayLabel1; displayLabel1.setLabel(QLatin1String("My label")); contact1.saveDetail(&displayLabel1); QContactGuid guid1; guid1.setGuid(QLatin1String("98765")); contact1.saveDetail(&guid1); QContact contact2; QContactNote note2; note2.setNote(QLatin1String("NOTE")); contact2.saveDetail(¬e2); QContactDisplayLabel displayLabel2; displayLabel2.setLabel(QLatin1String("label")); contact2.saveDetail(&displayLabel2); QContactGuid guid2; guid2.setGuid(QLatin1String("12345")); contact2.saveDetail(&guid2); QContactName name; name.setFirstName("John"); contact2.saveDetail(&name); QContactSortOrder sortOrder; QTest::newRow("empty sortorder") << contact1 << contact2 << sortOrder << 0; sortOrder.setDetailType(QContactDetail::TypeNote, QContactNote::FieldNote); QTest::newRow("empty contacts") << emptyContact << emptyContact << sortOrder << 0; QTest::newRow("equals") << contact1 << contact1 << sortOrder << 0; QTest::newRow("equals") << contact2 << contact2 << sortOrder << 0; sortOrder.setBlankPolicy(QContactSortOrder::BlanksFirst); { sortOrder.setDetailType(QContactDetail::TypeName, -1); QTest::newRow("blanks first, field presence") << emptyContact << contact2 << sortOrder << -1; QTest::newRow("blanks first, field presence") << contact1 << contact2 << sortOrder << -1; sortOrder.setDetailType(QContactDetail::TypeNote, QContactNote::FieldNote); QTest::newRow("blanks first") << emptyContact << contact2 << sortOrder << -1; QTest::newRow("blanks first") << contact2 << emptyContact << sortOrder << 1; sortOrder.setDetailType(QContactDetail::TypeName, QContactName::FieldFirstName); QTest::newRow("blanks first") << emptyContact << contact2 << sortOrder << -1; sortOrder.setDetailType(QContactDetail::TypeTag, QContactTag::FieldTag); QTest::newRow("blanks first") << contact1 << contact2 << sortOrder << 0; } sortOrder.setBlankPolicy(QContactSortOrder::BlanksLast); { sortOrder.setDetailType(QContactDetail::TypeName, -1); QTest::newRow("blanks last, field presence") << emptyContact << contact2 << sortOrder << 1; QTest::newRow("blanks last, field presence") << contact1 << contact2 << sortOrder << 1; sortOrder.setDetailType(QContactDetail::TypeNote, QContactNote::FieldNote); QTest::newRow("blanks last") << emptyContact << contact2 << sortOrder << 1; QTest::newRow("blanks last") << contact2 << emptyContact << sortOrder << -1; sortOrder.setDetailType(QContactDetail::TypeName, QContactName::FieldFirstName); QTest::newRow("blanks first") << emptyContact << contact2 << sortOrder << 1; sortOrder.setDetailType(QContactDetail::TypeTag, QContactTag::FieldTag); QTest::newRow("blanks first") << contact1 << contact2 << sortOrder << 0; } sortOrder.setDirection(Qt::AscendingOrder); sortOrder.setCaseSensitivity(Qt::CaseSensitive); { sortOrder.setDetailType(QContactDetail::TypeNote, QContactNote::FieldNote); QTest::newRow("asc, cs") << contact1 << contact2 << sortOrder << -1; QTest::newRow("asc, cs") << contact2 << contact1 << sortOrder << 1; sortOrder.setCaseSensitivity(Qt::CaseInsensitive); QTest::newRow("asc, ci") << contact1 << contact2 << sortOrder << 0; sortOrder.setDetailType(QContactDetail::TypeDisplayLabel, QContactDisplayLabel::FieldLabel); QTest::newRow("asc, ci") << contact1 << contact2 << sortOrder << 1; QTest::newRow("asc, ci") << contact2 << contact1 << sortOrder << -1; } sortOrder.setDirection(Qt::DescendingOrder); { sortOrder.setCaseSensitivity(Qt::CaseSensitive); sortOrder.setDetailType(QContactDetail::TypeNote, QContactNote::FieldNote); QTest::newRow("desc, cs") << contact1 << contact2 << sortOrder << 1; QTest::newRow("desc, cs") << contact2 << contact1 << sortOrder << -1; sortOrder.setCaseSensitivity(Qt::CaseInsensitive); QTest::newRow("desc, ci") << contact1 << contact2 << sortOrder << 0; sortOrder.setDetailType(QContactDetail::TypeDisplayLabel, QContactDisplayLabel::FieldLabel); QTest::newRow("desc, ci") << contact1 << contact2 << sortOrder << -1; QTest::newRow("desc, ci") << contact2 << contact1 << sortOrder << 1; } } { // ensure non-existence is treated just like blank-ness QContact contact1; QContactName name1; name1.setFirstName("John"); name1.setLastName("Smith"); contact1.saveDetail(&name1); QContact contact2; QContactName name2; name2.setFirstName("John"); contact2.saveDetail(&name2); QContactSortOrder sortOrder; sortOrder.setDetailType(QContactDetail::TypeName, QContactName::FieldLastName); sortOrder.setDirection(Qt::AscendingOrder); sortOrder.setCaseSensitivity(Qt::CaseInsensitive); sortOrder.setBlankPolicy(QContactSortOrder::BlanksFirst); QTest::newRow("non-existence vs blank-ness, blanks first") << contact1 << contact2 << sortOrder << 1; QTest::newRow("non-existence vs blank-ness, blanks first") << emptyContact << contact1 << sortOrder << -1; QTest::newRow("non-existence vs blank-ness, blanks first") << emptyContact << contact2 << sortOrder << 0; sortOrder.setBlankPolicy(QContactSortOrder::BlanksLast); QTest::newRow("non-existence vs blank-ness, blanks last") << contact1 << contact2 << sortOrder << -1; QTest::newRow("non-existence vs blank-ness, blanks last") << emptyContact << contact1 << sortOrder << 1; QTest::newRow("non-existence vs blank-ness, blanks last") << emptyContact << contact2 << sortOrder << 0; } } void tst_QContactSortOrder::compareContact() { QFETCH(QContact, contact1); QFETCH(QContact, contact2); QFETCH(QContactSortOrder, sortOrder); QFETCH(int, expected); int actual = QContactManagerEngine::compareContact(contact1, contact2, (QList() << sortOrder)); actual = qBound(-1, actual, 1); QCOMPARE(actual, expected); } void tst_QContactSortOrder::datastream_data() { QTest::addColumn("sortOrderIn"); { QContactSortOrder sortOrder; QTest::newRow("default") << sortOrder; } { QContactSortOrder sortOrder; sortOrder.setBlankPolicy(QContactSortOrder::BlanksFirst); sortOrder.setDirection(Qt::DescendingOrder); sortOrder.setCaseSensitivity(Qt::CaseInsensitive); QTest::newRow("invalid") << sortOrder; } { QContactSortOrder sortOrder; sortOrder.setDetailType(QContactDetail::TypeNote, QContactNote::FieldNote); QTest::newRow("detail") << sortOrder; } { QContactSortOrder sortOrder; sortOrder.setDetailType(QContactDetail::TypeNote, QContactNote::FieldNote); sortOrder.setBlankPolicy(QContactSortOrder::BlanksFirst); sortOrder.setDirection(Qt::DescendingOrder); sortOrder.setCaseSensitivity(Qt::CaseInsensitive); QTest::newRow("complete") << sortOrder; } } void tst_QContactSortOrder::datastream() { QFETCH(QContactSortOrder, sortOrderIn); QByteArray buffer; QDataStream stream1(&buffer, QIODevice::WriteOnly); stream1 << sortOrderIn; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); QContactSortOrder sortOrderOut; stream2 >> sortOrderOut; QCOMPARE(sortOrderOut, sortOrderIn); } void tst_QContactSortOrder::debugstream_data() { QTest::addColumn("sortOrder"); QTest::addColumn("messageExpected"); { QContactSortOrder sortOrder; QTest::newRow("default") << sortOrder << "QContactSortOrder(detailType=0,detailField=-1,blankPolicy=1,direction=0,caseSensitivity=1)"; } { QContactSortOrder sortOrder; sortOrder.setBlankPolicy(QContactSortOrder::BlanksFirst); sortOrder.setDirection(Qt::DescendingOrder); sortOrder.setCaseSensitivity(Qt::CaseInsensitive); QTest::newRow("invalid") << sortOrder << "QContactSortOrder(detailType=0,detailField=-1,blankPolicy=0,direction=1,caseSensitivity=0)"; } { QContactSortOrder sortOrder; sortOrder.setDetailType(QContactDetail::TypeNote, QContactNote::FieldNote); QTest::newRow("detail") << sortOrder << "QContactSortOrder(detailType=17,detailField=0,blankPolicy=1,direction=0,caseSensitivity=1)"; } { QContactSortOrder sortOrder; sortOrder.setDetailType(QContactDetail::TypeNote, QContactNote::FieldNote); sortOrder.setBlankPolicy(QContactSortOrder::BlanksFirst); sortOrder.setDirection(Qt::DescendingOrder); sortOrder.setCaseSensitivity(Qt::CaseInsensitive); QTest::newRow("complete") << sortOrder << "QContactSortOrder(detailType=17,detailField=0,blankPolicy=0,direction=1,caseSensitivity=0)"; } } void tst_QContactSortOrder::debugstream() { QFETCH(QContactSortOrder, sortOrder); QFETCH(QString, messageExpected); QTest::ignoreMessage(QtDebugMsg, messageExpected.toUtf8()); qDebug() << sortOrder; } QTEST_MAIN(tst_QContactSortOrder) #include "tst_qcontactsortorder.moc" tests/auto/contacts/qdeclarativecontact/000077500000000000000000000000001233466112000210245ustar00rootroot00000000000000tests/auto/contacts/qdeclarativecontact/qdeclarativecontact.pro000066400000000000000000000106041233466112000255670ustar00rootroot00000000000000include(../../auto.pri) QT += contacts qml versit DEFINES+=IGNORE_METAOBJECTBUILDER_EXPORT HEADERS += ../../../src/import/contacts/qdeclarativecontactdetail_p.h\ ../../../src/import/contacts/qdeclarativecontactrelationship_p.h\ ../../../src/import/contacts/qdeclarativecontactfetchhint_p.h\ ../../../src/import/contacts/qdeclarativecontactmodel_p.h\ ../../../src/import/contacts/qdeclarativecontactsortorder_p.h\ ../../../src/import/contacts/qdeclarativecontactfilter_p.h\ ../../../src/import/contacts/qdeclarativecontact_p.h\ ../../../src/import/contacts/qdeclarativecontactimageprovider_p.h\ ../../../src/import/contacts/qdeclarativecontactrelationshipmodel_p.h\ ../../../src/import/contacts/details/qdeclarativecontactaddress_p.h\ ../../../src/import/contacts/details/qdeclarativecontactname_p.h\ ../../../src/import/contacts/details/qdeclarativecontactanniversary_p.h\ ../../../src/import/contacts/details/qdeclarativecontactnickname_p.h\ ../../../src/import/contacts/details/qdeclarativecontactavatar_p.h\ ../../../src/import/contacts/details/qdeclarativecontactnote_p.h\ ../../../src/import/contacts/details/qdeclarativecontactbirthday_p.h\ ../../../src/import/contacts/details/qdeclarativecontactonlineaccount_p.h\ ../../../src/import/contacts/details/qdeclarativecontactdetails_p.h\ ../../../src/import/contacts/details/qdeclarativecontactorganization_p.h\ ../../../src/import/contacts/details/qdeclarativecontactdisplaylabel_p.h\ ../../../src/import/contacts/details/qdeclarativecontactphonenumber_p.h\ ../../../src/import/contacts/details/qdeclarativecontactemailaddress_p.h\ ../../../src/import/contacts/details/qdeclarativecontactpresence_p.h\ ../../../src/import/contacts/details/qdeclarativecontactfamily_p.h\ ../../../src/import/contacts/details/qdeclarativecontactringtone_p.h\ ../../../src/import/contacts/details/qdeclarativecontactfavorite_p.h\ ../../../src/import/contacts/details/qdeclarativecontactsynctarget_p.h\ ../../../src/import/contacts/details/qdeclarativecontactgender_p.h\ ../../../src/import/contacts/details/qdeclarativecontacttag_p.h\ ../../../src/import/contacts/details/qdeclarativecontactgeolocation_p.h\ ../../../src/import/contacts/details/qdeclarativecontacttimestamp_p.h\ ../../../src/import/contacts/details/qdeclarativecontactglobalpresence_p.h\ ../../../src/import/contacts/details/qdeclarativecontactguid_p.h\ ../../../src/import/contacts/details/qdeclarativecontacturl_p.h \ ../../../src/import/contacts/details/qdeclarativecontacthobby_p.h \ ../../../src/import/contacts/filters/qdeclarativecontactactionfilter_p.h\ ../../../src/import/contacts/filters/qdeclarativecontactintersectionfilter_p.h \ ../../../src/import/contacts/filters/qdeclarativecontactchangelogfilter_p.h \ ../../../src/import/contacts/filters/qdeclarativecontactinvalidfilter_p.h \ ../../../src/import/contacts/filters/qdeclarativecontactdetailfilter_p.h \ ../../../src/import/contacts/filters/qdeclarativecontactidfilter_p.h \ ../../../src/import/contacts/filters/qdeclarativecontactdetailrangefilter_p.h \ ../../../src/import/contacts/filters/qdeclarativecontactrelationshipfilter_p.h \ ../../../src/import/contacts/filters/qdeclarativecontactfilters_p.h \ ../../../src/import/contacts/filters/qdeclarativecontactunionfilter_p.h SOURCES += tst_qdeclarativecontact.cpp \ ../../../src/import/contacts/qdeclarativecontactdetail.cpp\ ../../../src/import/contacts/qdeclarativecontactmetaobject.cpp\ ../../../src/import/contacts/qdeclarativecontactrelationship.cpp\ ../../../src/import/contacts/qdeclarativecontactfetchhint.cpp\ ../../../src/import/contacts/qdeclarativecontactmodel.cpp\ ../../../src/import/contacts/qdeclarativecontactsortorder.cpp\ ../../../src/import/contacts/qdeclarativecontactfilter.cpp\ ../../../src/import/contacts/qdeclarativecontact.cpp\ ../../../src/import/contacts/qdeclarativecontactrelationshipmodel.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qdeclarativecontact/tst_qdeclarativecontact.cpp000066400000000000000000001265421233466112000264540ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include "qdeclarativecontactmodel_p.h" #include "qdeclarativecontactsortorder_p.h" #include "qdeclarativecontact_p.h" #include "qdeclarativecontactfilters_p.h" #include "qdeclarativecontactdetails_p.h" #include "qdeclarativecontactfetchhint_p.h" #include "qdeclarativecontactrelationship_p.h" #include "qdeclarativecontactrelationshipmodel_p.h" #ifdef Q_OS_SYMBIAN // In Symbian OS test data is located in applications private dir #define SRCDIR "." #endif QTCONTACTS_USE_NAMESPACE class tst_QDeclarativeContact : public QObject { Q_OBJECT public: tst_QDeclarativeContact(); virtual ~tst_QDeclarativeContact(); public slots: void initTestCase(); void cleanupTestCase(); void init(); private slots: void contact(); void contactModel(); void filter(); void details(); void relationship(); void relationshipModel(); void sortOrder(); void fetchHint(); void construction(); void construction_data(); private: QObject* createComponent(const QString& componentString); private: // Engine is needed for instantiating declarative components QQmlEngine engine; }; tst_QDeclarativeContact::tst_QDeclarativeContact() {} tst_QDeclarativeContact::~tst_QDeclarativeContact() {} void tst_QDeclarativeContact::initTestCase() { } void tst_QDeclarativeContact::cleanupTestCase() { } void tst_QDeclarativeContact::init() { } void tst_QDeclarativeContact::construction() { QFETCH(QString, componentString); QFETCH(QString, expectedClassName); QFETCH(bool, shouldSucceed); QObject* obj = createComponent(componentString); if (shouldSucceed) { if (obj == 0) { qWarning("--------- ------------- ------------- ---------------------- ------------ "); qWarning("--------- could not instantiate components from contact -------------- "); qWarning("--------- declarative plugin. make sure it is built and found ------------"); qWarning(" ---------under {QTDIR}/imports, or c:/sys/bin on Symbian ----------- "); qWarning("--------- ------------- ------------- ---------------------- ------------ "); } QVERIFY(obj != 0); QCOMPARE(obj->metaObject()->className(), expectedClassName.toLatin1().constData()); } else { QVERIFY(obj == 0); } delete obj; } void tst_QDeclarativeContact::construction_data() { QTest::addColumn("expectedClassName"); QTest::addColumn("componentString"); QTest::addColumn("shouldSucceed"); // ContactModel QTest::newRow("ContactModel: No properties") << "QDeclarativeContactModel" << "import Qt 4.7 \n import QtMobility.contacts 1.1 \n ContactModel {}" << true; QTest::newRow("ContactModel: Only id property") << "QDeclarativeContactModel" << "import Qt 4.7 \n import QtMobility.contacts 1.1 \n ContactModel {id: contactModelId}" << true; QTest::newRow("ContactModel: Valuetype properties") << "QDeclarativeContactModel" << "import Qt 4.7 \n import QtMobility.contacts 1.1 \n ContactModel {id: contactModelId; manager:'memory'}" << true; QTest::newRow("ContactModel: With filter") << "QDeclarativeContactModel" << "import Qt 4.7 \n import QtMobility.contacts 1.1 \n ContactModel {id: contactModelId; filter: DetailFilter{} }" << true; QTest::newRow("ContactModel: With fetchHint") << "QDeclarativeContactModel" << "import Qt 4.7 \n import QtMobility.contacts 1.1 \n ContactModel {id: contactModelId; fetchHint:FetchHint {id:hint; optimizationHints:FetchHint.AllRequired} }" << true; } void tst_QDeclarativeContact::contact() { QObject* obj = createComponent("import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Contact {id:contact; }"); QVERIFY(obj != 0); QVERIFY(obj->property("contactId").toInt() == 0); QVERIFY(obj->property("manager").toString() == ""); QVERIFY(obj->property("modified").toBool() == false); delete obj; obj = createComponent("import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Contact {" "id:contact;" "address.street : '53 Brandl St';" "address.locality : 'Brisbane';" "address.region : 'QLD';" "address.postcode : '4111';" "address.country : 'Australia';" "address.postOfficeBox : '1111';" "address.subTypes : [Address.Parcel, Address.Domestic, Address.Postal, Address.International];" "Anniversary {" "calendarId: 'birthday';" "originalDate : '2010-08-12';" "event : 'party';" "subType : Anniversary.Memorial;" "}\n" "}"); QVERIFY(obj != 0); delete obj; } void tst_QDeclarativeContact::contactModel() { //TODO // QQmlComponent component(&engine); // component.loadUrl(QUrl::fromLocalFile(SRCDIR "./data/contactmodel.qml")); // QDeclarativeContactModel *contactModel = static_cast(component.create()); // QVERIFY(contactModel != 0); // //other qverfies // delete contactModel; } void tst_QDeclarativeContact::filter() { QObject* filter = 0; //action filter { filter = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "ActionFilter{" "actionName : 'email';" "}"); QVERIFY(filter != 0); QVERIFY(filter->property("actionName").toString() == QLatin1String("email")); QDeclarativeContactFilter* df = static_cast(filter); QVERIFY(df != 0); QVERIFY(df->filter().type() == QContactFilter::ActionFilter); QContactActionFilter af = df->filter(); QVERIFY(af.actionName() == QLatin1String("email")); QSignalSpy spy(filter, SIGNAL(filterChanged())); filter->setProperty("actionName", "call"); QVERIFY(filter->property("actionName").toString() == QLatin1String("call")); QTRY_COMPARE(spy.count(), 1); delete filter; filter = 0; } //changelog filter { filter = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "ChangeLogFilter{" "since : '2010-11-16 12:12:12';" "eventType : 'EventAdded';" "}"); QVERIFY(filter != 0); QVERIFY(filter->property("since").toDateTime() == QDateTime(QDate(2010, 11, 16), QTime(12, 12, 12))); QVERIFY(filter->property("eventType").toInt() == QDeclarativeContactChangeLogFilter::EventAdded); QDeclarativeContactChangeLogFilter* df = static_cast(filter); QVERIFY(df != 0); QVERIFY(df->filter().type() == QContactFilter::ChangeLogFilter); QContactChangeLogFilter cf = df->filter(); QVERIFY(cf.since() == QDateTime(QDate(2010, 11, 16), QTime(12, 12, 12))); QVERIFY(cf.eventType() == QContactChangeLogFilter::EventAdded); QSignalSpy spy(filter, SIGNAL(filterChanged())); filter->setProperty("eventType", "EventChanged"); filter->setProperty("since", QDateTime(QDate(2010, 12, 17), QTime(12, 12, 12))); cf = df->filter(); QVERIFY(cf.since() == QDateTime(QDate(2010, 12, 17), QTime(12, 12, 12))); QVERIFY(cf.eventType() == QContactChangeLogFilter::EventChanged); QTRY_COMPARE(spy.count(), 2); delete filter; filter = 0; } //detail filter { filter = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "DetailFilter{" "detail : ContactDetail.Address;" "field : Address.Street;" "value : 'Brandl St';" "matchFlags : Filter.MatchContains | Filter.MatchCaseSensitive;" "}"); QVERIFY(filter != 0); QVERIFY(filter->property("detail").toString() == QLatin1String("Address")); QVERIFY(filter->property("field").toString() == QLatin1String("Street")); QVERIFY(filter->property("value").toString() == QLatin1String("Brandl St")); QDeclarativeContactFilter::MatchFlags flags; flags = ~flags & filter->property("matchFlags").toInt(); QVERIFY(flags.testFlag(QDeclarativeContactFilter::MatchContains) && flags.testFlag(QDeclarativeContactFilter::MatchCaseSensitive)); QDeclarativeContactDetailFilter* df = static_cast(filter); QVERIFY(df != 0); QVERIFY(df->filter().type() == QContactFilter::ContactDetailFilter); QContactDetailFilter cdf = df->filter(); QVERIFY(cdf.detailDefinitionName() == QLatin1String("Address")); QVERIFY(cdf.detailFieldName() == QLatin1String("Street")); QVERIFY(cdf.value().toString() == QLatin1String("Brandl St")); QVERIFY(cdf.matchFlags().testFlag(QContactFilter::MatchContains)); QVERIFY(cdf.matchFlags().testFlag(QContactFilter::MatchCaseSensitive)); QSignalSpy spy(filter, SIGNAL(filterChanged())); filter->setProperty("detail", "PhoneNumber"); filter->setProperty("field", "number"); filter->setProperty("value", "123456"); filter->setProperty("matchFlags", (int)(QDeclarativeContactFilter::MatchFlags(QDeclarativeContactFilter::MatchExactly))); cdf = df->filter(); QVERIFY(cdf.detailDefinitionName() == QLatin1String("PhoneNumber")); QVERIFY(cdf.detailFieldName() == QLatin1String("number")); QVERIFY(cdf.value().toString() == QLatin1String("123456")); QVERIFY(cdf.matchFlags().testFlag(QContactFilter::MatchExactly)); QVERIFY(!cdf.matchFlags().testFlag(QContactFilter::MatchCaseSensitive)); QTRY_COMPARE(spy.count(), 4); delete filter; filter = 0; } //detail range filter { filter = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "DetailRangeFilter{" "detail : ContactDetail.Address;" "field : Address.Postcode;" "min:'4000';" "max:'4222';" "matchFlags : Filter.MatchCaseSensitive | Filter.MatchFixedString;" "rangeFlags : DetailRangeFilter.ExcludeLower | DetailRangeFilter.IncludeUpper;" "}"); QVERIFY(filter != 0); QVERIFY(filter->property("detail").toString() == QLatin1String("Address")); QVERIFY(filter->property("field").toString() == QLatin1String("Postcode")); QVERIFY(filter->property("min").toString() == QLatin1String("4000")); QVERIFY(filter->property("max").toString() == QLatin1String("4222")); QDeclarativeContactFilter::MatchFlags flags; flags = ~flags & filter->property("matchFlags").toInt(); QVERIFY(flags.testFlag(QDeclarativeContactFilter::MatchCaseSensitive) && flags.testFlag(QDeclarativeContactFilter::MatchFixedString)); QDeclarativeContactDetailRangeFilter::RangeFlags rflags; rflags = ~rflags & filter->property("rangeFlags").toInt(); QVERIFY(rflags.testFlag(QDeclarativeContactDetailRangeFilter::ExcludeLower) && rflags.testFlag(QDeclarativeContactDetailRangeFilter::IncludeUpper)); QDeclarativeContactDetailFilter* df = static_cast(filter); QVERIFY(df != 0); QVERIFY(df->filter().type() == QContactFilter::ContactDetailRangeFilter); QContactDetailRangeFilter rf = df->filter(); QVERIFY(rf.detailDefinitionName() == QLatin1String("Address")); QVERIFY(rf.detailFieldName() == QLatin1String("Postcode")); QVERIFY(rf.minValue().toString() == QLatin1String("4000")); QVERIFY(rf.maxValue().toString() == QLatin1String("4222")); QVERIFY(rf.matchFlags().testFlag(QContactFilter::MatchFixedString)); QVERIFY(rf.matchFlags().testFlag(QContactFilter::MatchCaseSensitive)); QVERIFY(rf.rangeFlags().testFlag(QContactDetailRangeFilter::ExcludeLower)); QVERIFY(rf.rangeFlags().testFlag(QContactDetailRangeFilter::IncludeUpper)); QSignalSpy spy(filter, SIGNAL(filterChanged())); filter->setProperty("detail", "PhoneNumber"); filter->setProperty("field", "number"); filter->setProperty("min", "111111"); filter->setProperty("max", "999999"); filter->setProperty("matchFlags", (int)(QDeclarativeContactFilter::MatchFlags(QDeclarativeContactFilter::MatchExactly))); filter->setProperty("rangeFlags", (int)(QDeclarativeContactDetailRangeFilter::RangeFlags(QDeclarativeContactDetailRangeFilter::ExcludeUpper))); rf = df->filter(); QVERIFY(rf.detailDefinitionName() == QLatin1String("PhoneNumber")); QVERIFY(rf.detailFieldName() == QLatin1String("number")); QVERIFY(rf.minValue().toString() == QLatin1String("111111")); QVERIFY(rf.maxValue().toString() == QLatin1String("999999")); QVERIFY(rf.matchFlags().testFlag(QContactFilter::MatchExactly)); QVERIFY(!rf.matchFlags().testFlag(QContactFilter::MatchCaseSensitive)); QVERIFY(rf.rangeFlags().testFlag(QContactDetailRangeFilter::ExcludeUpper)); QVERIFY(!rf.rangeFlags().testFlag(QContactDetailRangeFilter::IncludeUpper)); QTRY_COMPARE(spy.count(), 6); delete filter; filter = 0; } //local id filter { filter = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "IdFilter{" "ids : ['1111','2222','3333'];" "}"); QVERIFY(filter != 0); QVERIFY(filter->property("ids").toStringList().contains(QLatin1String("1111"))); QVERIFY(filter->property("ids").toStringList().contains(QLatin1String("2222"))); QVERIFY(filter->property("ids").toStringList().contains(QLatin1String("3333"))); QDeclarativeContactLocalIdFilter* df = static_cast(filter); QVERIFY(df != 0); QVERIFY(df->filter().type() == QContactFilter::LocalIdFilter); QContactLocalIdFilter lif = df->filter(); QVERIFY(lif.ids().size() == 3); QVERIFY(lif.ids().contains(1111)); QVERIFY(lif.ids().contains(2222)); QVERIFY(lif.ids().contains(3333)); QSignalSpy spy(filter, SIGNAL(filterChanged())); filter->setProperty("ids", QStringList() << QLatin1String("5555")); QVERIFY(filter->property("ids").toStringList().contains(QLatin1String("5555"))); QTRY_COMPARE(spy.count(), 1); delete filter; filter = 0; } //intersection filter { filter = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "IntersectionFilter{" " filters: [IdFilter {ids : ['1111','2222','3333']}," " ActionFilter {actionName : 'email'}]" "}"); QVERIFY(filter != 0); QDeclarativeContactIntersectionFilter* df = static_cast(filter); QVERIFY(df != 0); QVERIFY(df->filter().type() == QContactFilter::IntersectionFilter); QContactIntersectionFilter cif = df->filter(); QVERIFY(cif.filters().size() == 2); QVERIFY(cif.filters().front().type() == QContactFilter::LocalIdFilter); QVERIFY(cif.filters().back().type() == QContactFilter::ActionFilter); QContactLocalIdFilter idf = cif.filters().front(); QVERIFY(idf.ids().contains(1111)); QVERIFY(idf.ids().contains(2222)); QVERIFY(idf.ids().contains(3333)); QContactActionFilter caf = cif.filters().back(); QVERIFY(caf.actionName() == QLatin1String("email")); delete filter; filter = 0; } //union filter { filter = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "UnionFilter{" " IdFilter {ids : ['1111','2222','3333']}" " ActionFilter {actionName : 'email'}" "}"); QVERIFY(filter != 0); QDeclarativeContactUnionFilter* df = static_cast(filter); QVERIFY(df != 0); QVERIFY(df->filter().type() == QContactFilter::UnionFilter); QContactUnionFilter cuf = df->filter(); QVERIFY(cuf.filters().size() == 2); QVERIFY(cuf.filters().front().type() == QContactFilter::LocalIdFilter); QVERIFY(cuf.filters().back().type() == QContactFilter::ActionFilter); QContactLocalIdFilter idf = cuf.filters().front(); QVERIFY(idf.ids().contains(1111)); QVERIFY(idf.ids().contains(2222)); QVERIFY(idf.ids().contains(3333)); QContactActionFilter caf = cuf.filters().back(); QVERIFY(caf.actionName() == QLatin1String("email")); delete filter; filter = 0; } //relationship filter { filter = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "RelationshipFilter{\n" "relationshipType:Relationship.HasMember;\n" "relatedContactId:1111;\n" "relatedContactRole:Relationship.Either;\n" "}"); QVERIFY(filter != 0); QDeclarativeContactRelationshipFilter* df = static_cast(filter); QVERIFY(df != 0); QVERIFY(df->filter().type() == QContactFilter::RelationshipFilter); QContactRelationshipFilter rf = df->filter(); QVERIFY(rf.relationshipType() == QContactRelationship::HasMember); QVERIFY(rf.relatedContactId().localId() == 1111); QVERIFY(rf.relatedContactRole() == QContactRelationship::Either); QSignalSpy spy(filter, SIGNAL(filterChanged())); filter->setProperty("relationshipType", QContactRelationship::HasManager); filter->setProperty("relatedContactId", 2222 ); filter->setProperty("relatedContactRole", QLatin1String("First")); QTRY_COMPARE(spy.count(), 3); rf = df->filter(); QVERIFY(rf.relationshipType() == QContactRelationship::HasManager); QVERIFY(rf.relatedContactId().localId() == 2222); QVERIFY(rf.relatedContactRole() == QContactRelationship::First); delete filter; filter = 0; } } void tst_QDeclarativeContact::details() { QObject* obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Address{street : '53 Brandl St';" "locality : 'Brisbane';" "region : 'QLD';" "postcode : '4111';" "country : 'Australia';" "postOfficeBox : '1111';" "subTypes : [Address.Parcel, Address.Domestic, Address.Postal, Address.International];}" ); QVERIFY(obj != 0); QVERIFY(obj->property("street").toString() == QLatin1String("53 Brandl St")); QVERIFY(obj->property("locality").toString() == QLatin1String("Brisbane")); QVERIFY(obj->property("region").toString() == QLatin1String("QLD")); QVERIFY(obj->property("postcode").toString() == QLatin1String("4111")); QVERIFY(obj->property("country").toString() == QLatin1String("Australia")); QVERIFY(obj->property("postOfficeBox").toString() == QLatin1String("1111")); QVERIFY(obj->property("subTypes").toStringList().contains(QLatin1String("Parcel"))); QVERIFY(obj->property("subTypes").toStringList().contains(QLatin1String("Domestic"))); QVERIFY(obj->property("subTypes").toStringList().contains(QLatin1String("Postal"))); QVERIFY(obj->property("subTypes").toStringList().contains(QLatin1String("International"))); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Anniversary {" "calendarId: 'birthday';" "originalDate : '2010-08-12';" "event : 'party';" "subType : Anniversary.Memorial;" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("calendarId").toString() == QLatin1String("birthday")); QVERIFY(obj->property("originalDate").toDate() == QDate(2010, 8, 12)); QVERIFY(obj->property("event").toString() == QLatin1String("party")); QVERIFY(obj->property("subType").toInt() == QDeclarativeContactAnniversary::Memorial); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Avatar {" "imageUrl: 'http://www.example.com/avatar.jpg';" "videoUrl : 'http://www.example.com/avatar.avi';" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("imageUrl").toUrl() == QUrl("http://www.example.com/avatar.jpg")); QVERIFY(obj->property("videoUrl").toUrl() == QUrl("http://www.example.com/avatar.avi")); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Birthday {" "birthday: '1980-12-31';" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("birthday").toDate() == QDate(1980, 12, 31)); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "EmailAddress {" "emailAddress: 'tom@example.com';" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("emailAddress").toString() == QLatin1String("tom@example.com")); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Family {" "spouse: 'Tom';" "children: ['Ben', 'Sophie'];" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("spouse").toString() == QLatin1String("Tom")); QVERIFY(obj->property("children").toStringList().contains(QLatin1String("Ben"))); QVERIFY(obj->property("children").toStringList().contains(QLatin1String("Sophie"))); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Favorite {" "favorite: true;" "index: 1;" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("favorite").toBool()); QVERIFY(obj->property("index").toInt() == 1); obj->setProperty("favorite", false); obj->setProperty("index", 2); QVERIFY(!obj->property("favorite").toBool()); QVERIFY(obj->property("index").toInt() == 2); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Gender {" "gender: Gender.Male;" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("gender").toInt() == QDeclarativeContactGender::Male); obj->setProperty("gender", "Female"); QVERIFY(obj->property("gender").toInt() == QDeclarativeContactGender::Female); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Location {" "label: 'Office';" "latitude: 121.111;" "longitude: 121.111;" "accuracy:0.2;" "altitude:23.2;" "altitudeAccuracy:2.3;" "heading:3234.1;" "speed:33.5;" "timestamp:'2010-11-13';" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("label").toString() == QLatin1String("Office")); obj->setProperty("label", "Home"); QVERIFY(obj->property("label").toString() == QLatin1String("Home")); QVERIFY(qFuzzyCompare(obj->property("latitude").toDouble(), 121.111)); obj->setProperty("latitude", 222.222); QVERIFY(qFuzzyCompare(obj->property("latitude").toDouble(), 222.222)); QVERIFY(qFuzzyCompare(obj->property("longitude").toDouble(), 121.111)); obj->setProperty("longitude", 333.222); QVERIFY(qFuzzyCompare(obj->property("longitude").toDouble(), 333.222)); QVERIFY(qFuzzyCompare(obj->property("accuracy").toDouble(), 0.2)); obj->setProperty("accuracy", 111.11); QVERIFY(qFuzzyCompare(obj->property("accuracy").toDouble(), 111.11)); QVERIFY(qFuzzyCompare(obj->property("altitude").toDouble(), 23.2)); obj->setProperty("altitude", 12.2); QVERIFY(qFuzzyCompare(obj->property("altitude").toDouble(), 12.2)); QVERIFY(qFuzzyCompare(obj->property("altitudeAccuracy").toDouble(), 2.3)); obj->setProperty("altitudeAccuracy", 3.2); QVERIFY(qFuzzyCompare(obj->property("altitudeAccuracy").toDouble(), 3.2)); QVERIFY(qFuzzyCompare(obj->property("heading").toDouble(), 3234.1)); obj->setProperty("heading", 322.2); QVERIFY(qFuzzyCompare(obj->property("heading").toDouble(), 322.2)); QVERIFY(qFuzzyCompare(obj->property("speed").toDouble(), 33.5)); obj->setProperty("speed", 21.2); QVERIFY(qFuzzyCompare(obj->property("speed").toDouble(), 21.2)); QVERIFY(obj->property("timestamp").toDate() == QDate(2010,11,13)); obj->setProperty("timestamp", QDateTime(QDate(2010, 12, 24), QTime(8, 23, 21))); QVERIFY(obj->property("timestamp").toDateTime().time() == QTime(8, 23, 21)); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Guid {" "guid: '1121233322';" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("guid").toString() == QLatin1String("1121233322") ); obj->setProperty("guid", "xyzcds"); QVERIFY(obj->property("guid").toString() == QLatin1String("xyzcds")); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Hobby {" "hobby: 'swimming';" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("hobby").toString() == QLatin1String("swimming") ); obj->setProperty("hobby", "sleeping"); QVERIFY(obj->property("hobby").toString() == QLatin1String("sleeping")); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Nickname {" "nickname: 'Tommy';" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("nickname").toString() == QLatin1String("Tommy") ); obj->setProperty("nickname", "Charlie"); QVERIFY(obj->property("nickname").toString() == QLatin1String("Charlie")); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Note {" "note: 'some notes';" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("note").toString() == QLatin1String("some notes") ); obj->setProperty("note", "more notes"); QVERIFY(obj->property("note").toString() == QLatin1String("more notes")); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "SyncTarget {" "syncTarget: 'skype';" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("syncTarget").toString() == QLatin1String("skype") ); obj->setProperty("syncTarget", "msn"); QVERIFY(obj->property("syncTarget").toString() == QLatin1String("msn")); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Tag {" "tag: 'tag1';" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("tag").toString() == QLatin1String("tag1") ); obj->setProperty("tag", "tag2"); QVERIFY(obj->property("tag").toString() == QLatin1String("tag2")); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Url {" "url: 'http://www.example.com';" "subType:'HomePage';" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("url").toString() == QLatin1String("http://www.example.com") ); QVERIFY(obj->property("subType").toString() == QLatin1String("HomePage")); obj->setProperty("url", "http://www.nokia.com"); obj->setProperty("subType", "Favourite"); QVERIFY(obj->property("url").toString() == QLatin1String("http://www.nokia.com")); QVERIFY(obj->property("subType").toString() == QLatin1String("Favourite")); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Timestamp {" "lastModified: '2010-11-10 12:12:12';" "created:'2010-10-10 13:54:34';" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("lastModified").toDateTime() == QDateTime(QDate(2010, 11, 10), QTime(12, 12, 12)) ); QVERIFY(obj->property("created").toDateTime() == QDateTime(QDate(2010, 10, 10), QTime(13, 54, 34)) ); obj->setProperty("lastModified", QDateTime(QDate(2011, 12, 12), QTime(13, 13, 13))); obj->setProperty("created", QDateTime(QDate(2009, 11, 11), QTime(10, 10, 10))); QVERIFY(obj->property("lastModified").toDateTime() == QDateTime(QDate(2011, 12, 12), QTime(13, 13, 13)) ); QVERIFY(obj->property("created").toDateTime() == QDateTime(QDate(2009, 11, 11), QTime(10, 10, 10)) ); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Ringtone {" "audioRingtoneUrl: 'http://www.example.com/audio.wav';" "videoRingtoneUrl : 'http://www.example.com/video.avi';" "vibrationRingtoneUrl : 'http://www.example.com/vibration.wav';" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("audioRingtoneUrl").toUrl() == QUrl("http://www.example.com/audio.wav")); QVERIFY(obj->property("videoRingtoneUrl").toUrl() == QUrl("http://www.example.com/video.avi")); QVERIFY(obj->property("vibrationRingtoneUrl").toUrl() == QUrl("http://www.example.com/vibration.wav")); obj->setProperty("audioRingtoneUrl", QUrl()); obj->setProperty("videoRingtoneUrl", QUrl()); obj->setProperty("vibrationRingtoneUrl", QUrl()); QVERIFY(obj->property("audioRingtoneUrl").toUrl().isEmpty()); QVERIFY(obj->property("videoRingtoneUrl").toUrl().isEmpty()); QVERIFY(obj->property("vibrationRingtoneUrl").toUrl().isEmpty()); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Presence {" "timestamp: '2010-11-15 11:59:12';" "nickname : 'Charles';" "state :'Busy';" "stateText :'working' ;" "imageUrl: 'http://www.example.com/me.png';" "customMessage:'qt everywhere';" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("timestamp").toDateTime() == QDateTime(QDate(2010, 11, 15), QTime(11, 59, 12))); QVERIFY(obj->property("nickname").toString() == QLatin1String("Charles")); QVERIFY(obj->property("state").toInt() == QDeclarativeContactPresence::Busy); QVERIFY(obj->property("stateText").toString() == QLatin1String("working")); QVERIFY(obj->property("imageUrl").toString() == QLatin1String("http://www.example.com/me.png")); QVERIFY(obj->property("customMessage").toString() == QLatin1String("qt everywhere")); obj->setProperty("timestamp", QDateTime(QDate(2010, 11, 16), QTime(12, 04, 12))); obj->setProperty("nickname", "Charlie"); obj->setProperty("state", "Away"); obj->setProperty("stateText", "away for lunch"); obj->setProperty("imageUrl", "http://www.example.com/me2.png"); obj->setProperty("customMessage", "nice food today"); QVERIFY(obj->property("timestamp").toDateTime() == QDateTime(QDate(2010, 11, 16), QTime(12, 04, 12))); QVERIFY(obj->property("nickname").toString() == QLatin1String("Charlie")); QVERIFY(obj->property("state").toInt() == QDeclarativeContactPresence::Away); QVERIFY(obj->property("stateText").toString() == QLatin1String("away for lunch")); QVERIFY(obj->property("imageUrl").toString() == QLatin1String("http://www.example.com/me2.png")); QVERIFY(obj->property("customMessage").toString() == QLatin1String("nice food today")); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "PhoneNumber {" "number: '12345678';" "subTypes : ['Mobile', 'Voice'];" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("number").toString() == QLatin1String("12345678")); QVERIFY(obj->property("subTypes").toStringList().contains("Mobile")); obj->setProperty("number", "87654321"); obj->setProperty("subTypes", QStringList() << "Fax" << "Voice"); QVERIFY(obj->property("number").toString() == QLatin1String("87654321")); QVERIFY(obj->property("subTypes").toStringList().contains("Voice")); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Name {" "prefix:'Mr.';" "firstName :'First';" "middleName : 'Middle';" "lastName : 'Last';" "suffix : 'Suffix';" "customLabel: 'Mr. Example'" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("prefix").toString() == QLatin1String("Mr.")); QVERIFY(obj->property("firstName").toString() == QLatin1String("First")); QVERIFY(obj->property("middleName").toString() == QLatin1String("Middle")); QVERIFY(obj->property("lastName").toString() == QLatin1String("Last")); QVERIFY(obj->property("suffix").toString() == QLatin1String("Suffix")); QVERIFY(obj->property("customLabel").toString() == QLatin1String("Mr. Example")); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "OnlineAccount {" "accountUri:'gtalk';" "serviceProvider :'Google';" "capabilities : ['text', 'audio', 'video'];" "subTypes : [OnlineAccount.SipVoip, OnlineAccount.Impp];" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("accountUri").toString() == QLatin1String("gtalk")); QVERIFY(obj->property("serviceProvider").toString() == QLatin1String("Google")); QVERIFY(obj->property("capabilities").toStringList().contains("text")); QVERIFY(obj->property("subTypes").toStringList().contains("SipVoip")); delete obj; obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "Organization {" "name:'Nokia';" "logoUrl :'http://www.nokia.com/logo.gif';" "department : ['mobility', 'web'];" "location : 'Brisbane';" "role :'R&D';" "title :'programmer';" "assistantName : 'assistant';" "}\n" ); QVERIFY(obj != 0); QVERIFY(obj->property("name").toString() == QLatin1String("Nokia")); QVERIFY(obj->property("logoUrl").toUrl() == QUrl("http://www.nokia.com/logo.gif")); QVERIFY(obj->property("department").toStringList().contains("mobility")); QVERIFY(obj->property("role").toString() == QLatin1String("R&D")); QVERIFY(obj->property("title").toString() == QLatin1String("programmer")); QVERIFY(obj->property("assistantName").toString() == QLatin1String("assistant")); delete obj; obj = 0; } void tst_QDeclarativeContact::relationship() { //TODO } void tst_QDeclarativeContact::relationshipModel() { //TODO } void tst_QDeclarativeContact::sortOrder() { QObject* obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "SortOrder{" " detail :ContactDetail.Address;" " field : Address.Street;" " direction : Qt.AscendingOrder;" " blankPolicy : SortOrder.BlanksFirst;" " caseSensitivity:Qt.CaseInsensitive;" "}"); QVERIFY(obj != 0); qDebug() << obj->property("detail"); QVERIFY(obj->property("detail").toString() == QLatin1String("Address")); QVERIFY(obj->property("field").toString() == QLatin1String("Street")); QVERIFY(obj->property("direction").toInt() == Qt::AscendingOrder); QVERIFY(obj->property("blankPolicy").toInt() == QContactSortOrder::BlanksFirst); QVERIFY(obj->property("caseSensitivity").toInt() == Qt::CaseInsensitive); QSignalSpy spy(obj, SIGNAL(sortOrderChanged())); obj->setProperty("detail", QLatin1String("PhoneNumber")); obj->setProperty("field", QLatin1String("number")); obj->setProperty("direction", Qt::DescendingOrder); obj->setProperty("blankPolicy", QContactSortOrder::BlanksLast); obj->setProperty("caseSensitivity", Qt::CaseSensitive); QTRY_COMPARE(spy.count(), 5); QDeclarativeContactSortOrder* cso = static_cast(obj); QVERIFY(cso != 0); delete obj; } void tst_QDeclarativeContact::fetchHint() { QObject* obj = 0; obj = createComponent( "import Qt 4.7\n" "import QtMobility.contacts 1.1\n" "FetchHint{" " detailDefinitionsHint :['PhoneNumber', 'Address'];" " relationshipTypesHint : ['HasMember', 'HasManager'];" " optimizationHints : FetchHint.NoRelationships | FetchHint.NoBinaryBlobs;" " imageWidth : 90;" " imageHeight : 80;" "}"); QVERIFY(obj != 0); QVERIFY(obj->property("detailDefinitionsHint").toStringList().contains("PhoneNumber")); QVERIFY(obj->property("detailDefinitionsHint").toStringList().contains("Address")); QVERIFY(obj->property("detailDefinitionsHint").toStringList().size() == 2); QVERIFY(obj->property("relationshipTypesHint").toStringList().size() == 2); QVERIFY(obj->property("relationshipTypesHint").toStringList().contains("HasMember")); QVERIFY(obj->property("relationshipTypesHint").toStringList().contains("HasManager")); QVERIFY(obj->property("optimizationHints").toInt() == (int)(QContactFetchHint::NoRelationships | QContactFetchHint::NoBinaryBlobs)); QVERIFY(obj->property("imageWidth").toInt() == 90); QVERIFY(obj->property("imageHeight").toInt() == 80); QSignalSpy spy(obj, SIGNAL(fetchHintChanged())); obj->setProperty("detailDefinitionsHint", QStringList() << "EmailAddress" << "Avatar"); obj->setProperty("relationshipTypesHint", QStringList() << "IsSameAs"); obj->setProperty("optimizationHints", (int)(QContactFetchHint::AllRequired)); obj->setProperty("imageWidth", 100); obj->setProperty("imageHeight", 200); QTRY_COMPARE(spy.count(), 5); QVERIFY(obj->property("detailDefinitionsHint").toStringList().contains("EmailAddress")); QVERIFY(obj->property("detailDefinitionsHint").toStringList().contains("Avatar")); QVERIFY(obj->property("detailDefinitionsHint").toStringList().size() == 2); QVERIFY(obj->property("relationshipTypesHint").toStringList().size() == 1); QVERIFY(obj->property("relationshipTypesHint").toStringList().contains("IsSameAs")); QVERIFY(obj->property("optimizationHints").toInt() == (int)(QContactFetchHint::AllRequired)); QVERIFY(obj->property("imageWidth").toInt() == 100); QVERIFY(obj->property("imageHeight").toInt() == 200); delete obj; } /* Helper function to create components from given string. */ QObject* tst_QDeclarativeContact::createComponent(const QString& componentString) { QQmlComponent component(&engine); component.setData(componentString.toLatin1(), QUrl::fromLocalFile("")); QObject* source_obj = component.create(); if (!source_obj) qDebug() << component.errorString(); return source_obj; } QTEST_MAIN(tst_QDeclarativeContact) #include "tst_qdeclarativecontact.moc" tests/auto/contacts/qmlcontacts/000077500000000000000000000000001233466112000173345ustar00rootroot00000000000000tests/auto/contacts/qmlcontacts/qmlcontacts.pro000066400000000000000000000040151233466112000224060ustar00rootroot00000000000000TEMPLATE=app TARGET=tst_qmlcontacts SOURCES += tst_qmlcontacts.cpp OTHER_FILES += \ testcases/ContactsJsonDbPartition.qml \ testcases/ContactsJsonDbPartitions.qml \ testcases/ContactsJsonDbPartitionTestHelper.qml \ testcases/ContactsJsonDbTestHelper.qml \ testcases/ContactsSavingTestCase.qml \ testcases/ContactsSignalingTestCase.qml \ testcases/ContactsTestHelper.qml \ testcases/initialize_jsondb_for_tests.qml \ testcases/SignalingJsonDb.qml \ testcases/tst_contact_add_detail.qml \ testcases/tst_contact_addresses.qml \ testcases/tst_contact_detail_access.qml \ testcases/tst_contactdetail.qml \ testcases/tst_contact_emails.qml \ testcases/tst_contact_extendeddetails.qml \ testcases/tst_contactmodel_signals.qml \ testcases/tst_contact_modification.qml \ testcases/tst_contact_organizations.qml \ testcases/tst_contact_phonenumbers.qml \ testcases/tst_contactrelationship.qml \ testcases/tst_contact_remove_detail.qml \ testcases/tst_contacts_clear_details_e2e.qml \ testcases/tst_contacts_details_saving_e2e.qml \ testcases/tst_contacts_e2e.qml \ testcases/tst_contacts_export_import_e2e.qml \ testcases/tst_contacts_export_import_signaling_e2e.qml \ testcases/tst_contacts_extended_detail_e2e.qml \ testcases/tst_contacts_fetch_contacts_e2e.qml \ testcases/tst_contacts_filtering_by_detail_e2e.qml \ testcases/tst_contacts_filtering_e2e.qml \ testcases/tst_contact_signals.qml \ testcases/tst_contacts_intersection_filter_e2e.qml \ testcases/tst_contacts_jsondb_partitions_e2e.qml \ testcases/tst_contacts_jsondb_to_model_notification_e2e.qml \ testcases/tst_contacts_model_to_model_notification_e2e.qml \ testcases/tst_contacts_remove_contacts_e2e.qml \ testcases/tst_contacts_remove_detail_e2e.qml \ testcases/tst_contacts_save_contact_e2e.qml \ testcases/tst_contacts_sorting_e2e.qml \ testcases/tst_contact_urls.qml QT += qmltest contacts versit DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/contacts/qmlcontacts/testcases/000077500000000000000000000000001233466112000213325ustar00rootroot00000000000000tests/auto/contacts/qmlcontacts/testcases/ContactsJsonDbPartition.qml000066400000000000000000000041161233466112000266170ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtContacts 5.0 Item { property int storageLocation property string name property ContactsJsonDbTestHelper testHelper } tests/auto/contacts/qmlcontacts/testcases/ContactsJsonDbPartitions.qml000066400000000000000000000046111233466112000270020ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 Item { property alias userPartition: userPartition property alias systemPartition: systemPartition ContactsJsonDbPartition { id: userPartition storageLocation: ContactModel.UserDataStorage name: "com.nokia.mt.User" } ContactsJsonDbPartition { id: systemPartition storageLocation: ContactModel.SystemStorage name: "com.nokia.mt.System" } } tests/auto/contacts/qmlcontacts/testcases/ContactsJsonDbTestCase.qml000066400000000000000000000056061233466112000263660ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSavingTestCase { name: "ContactsJsonDbTestCase" id: contactsJsonDbTestCase ContactsJsonDbPartitions { id: jsonDbPartitions } property string jsonDbPartitionForDefaultStorageLocation: jsonDbPartitions.userPartition.name ContactsJsonDbTestHelper { id: jsonDbTestHelper partition: jsonDbPartitionForDefaultStorageLocation } function initJsonDbAccess() { jsonDbTestHelper.initTestHelper(); } function createContactToJsonDb(contact) { jsonDbTestHelper.createContactToJsonDb(contact); } function removeContactFromJsonDb(contact) { jsonDbTestHelper.removeContactFromJsonDb(contact); } function updateContactInJsonDb(contact, update) { jsonDbTestHelper.updateContactInJsonDb(contact, update); } function queryContactsInJsonDb() { return jsonDbTestHelper.queryContactsInJsonDb(); } } tests/auto/contacts/qmlcontacts/testcases/ContactsJsonDbTestHelper.qml000066400000000000000000000136111233466112000267250ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 import QtJsonDb 1.0 as JsonDb // Top-level element should have a default property to allow declaring elements // inline (jsonDb element below). Component does not, so Item is used here // although this is not a visual element. Item { id: contactsJsonDbTestHelper property alias partition: jsonDb.partition function initTestHelper() { logDebug("initTestHelper()"); createSpyForJsonDb(); } function createContactToJsonDb(contact) { logDebug("createContactToJsonDb()"); contact["_type"] = "com.nokia.mt.contacts.Contact"; jsonDb.createAndSignal(contact); jsonDbSpy.wait(); } function removeContactFromJsonDb(contact) { logDebug("removeContactFromJsonDb(): remove contact id " + contact.contactId); var contactUuid = convertContactIdToJsonDbUuid(contact.contactId); removeContactWithUuidFromJsonDb(contactUuid); } function removeContactWithUuidFromJsonDb(contactUuid) { logDebug("removeContactWithUuidFromJsonDb(): contact uuid " + contactUuid); var query = '[?_type="com.nokia.mt.contacts.Contact"]' + '[?_uuid="' + contactUuid + '"]'; jsonDb.queryAndSignal(query); jsonDbSpy.wait(); var object = jsonDb.lastResult[0]; verify(object, "fetched given contact from jsondb"); jsonDb.removeAndSignal(object); jsonDbSpy.wait(); } // updates only the first name at the moment! function updateContactInJsonDb(contact, update) { var jsonUuid = convertContactIdToJsonDbUuid(contact.contactId); updateContactWithUuidInJsonDb(jsonUuid, update); } // updates only the first name at the moment! function updateContactWithUuidInJsonDb(contactUuid, update) { logDebug("updateContactWithUuidInJsonDb(): contact uuid " + contactUuid); var query = '[?_type="com.nokia.mt.contacts.Contact"]' + '[?_uuid="' + contactUuid + '"]'; jsonDb.queryAndSignal(query); jsonDbSpy.wait(); var object = jsonDb.lastResult[0]; object.name.firstName = update.name.firstName; jsonDb.updateAndSignal(object); jsonDbSpy.wait(); } function queryContactsInJsonDb() { logDebug("queryContactsInJsonDb()"); var query = '[?_type="com.nokia.mt.contacts.Contact"]'; jsonDb.queryAndSignal(query); jsonDbSpy.wait(); return jsonDb.lastResult; } function emptyContacts() { logDebug("emptyContacts()"); var query = '[?_type="com.nokia.mt.contacts.Contact"]'; jsonDb.queryAndSignal(query); jsonDbSpy.wait(); var contacts = jsonDb.lastResult; for (var i = 0; i < contacts.length; i++) { jsonDb.removeAndSignal(contacts[i]); jsonDbSpy.wait(); } } function convertJsonDbUuidAndStorageLocationToContactId(contactUuid, storageLocation) { return 'qtcontacts:jsondb::' + storageLocation + '/' + contactUuid; } function convertContactIdToJsonDbUuid(contactId) { var idParts = contactId.split(':'); var engineId = lastItemInArray(idParts); var engineIdParts = engineId.split('/'); var jsonDbUuid = lastItemInArray(engineIdParts); return jsonDbUuid; } function lastItemInArray(array) { return array[array.length - 1]; } SignalingJsonDb { id: jsonDb } property SignalSpy jsonDbSpy function createSpyForJsonDb() { logDebug("createSpyForJson()"); jsonDbSpy = Qt.createQmlObject( "import QtTest 1.0;" + "SignalSpy {" + "}", contactsJsonDbTestHelper); jsonDbSpy.target = jsonDb; jsonDbSpy.signalName = "operationFinished" } property bool debug: false function logDebug(message) { if (debug) console.log('ContactsJsonDbTestHelper.' + message); } } tests/auto/contacts/qmlcontacts/testcases/ContactsSavingTestCase.qml000066400000000000000000000106511233466112000264320ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 TestCase { name: "ContactsSavingTestCase" id: contactsSavingTestCase property SignalSpy spy property bool debug: false ContactsTestConfiguration { id: configuration } function getManagerUnderTest() { logDebug("getManagerUnderTest"); var managerName = configuration.getManagerUnderTest(); if (managerName.length > 0) console.log("ContactsSavingTestCase: Testing against '" + managerName + "' manager backend."); return managerName; } function initTestForModel(model) { logDebug("initTestForModel"); spy = Qt.createQmlObject( "import QtTest 1.0;" + "SignalSpy {" + "}", contactsSavingTestCase); spy.target = model; spy.signalName = "contactsChanged"; return spy; } function listenToContactsChanged() { logDebug("listenForContactsChanged"); spy.clear(); } function waitForContactsChanged() { logDebug("waitForContactsChanged"); spy.wait(); } function verifyNoContactsChangedReceived() { logDebug("verifyNoContactsChangedReceived"); wait(500); compare(spy.count, 0, "no contacts changed signal received"); } function initTestForTargetListeningToSignal(target, signalName) { logDebug("initTestForTargetListeningToSignal"); var spy = Qt.createQmlObject( "import QtTest 1.0;" + "SignalSpy {" + "}", contactsSavingTestCase); spy.target = target; spy.signalName = signalName; return spy; } function waitForTargetSignal(spy) { logDebug("waitForTargetSignal"); spy.wait(); } function emptyContacts(model) { logDebug("emptyContacts"); model.update(); spy.wait(); var count = model.contacts.length; for (var i = 0; i < count; i++) { var id = model.contacts[0].contactId; model.removeContact(id); if (!model.autoUpdate) model.update() spy.wait(); } compare(model.contacts.length, 0, "model is empty"); } function finishTestForModel(model) { logDebug("finishTestForModel"); model.autoUpdate = false; } function logDebug(message) { if (debug) console.log(message); } } tests/auto/contacts/qmlcontacts/testcases/ContactsSignalingTestCase.qml000066400000000000000000000056461233466112000271260ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 TestCase { name: "ContactsSignalingTestCase" id: contactsSignalingTestCase property SignalSpy spy property bool debug: false function initSignalingTest() { logDebug("initSignalingTest"); spy = Qt.createQmlObject("import QtTest 1.0;" + "SignalSpy {}", contactsSignalingTestCase); } function listenToSignalFromObject(signalName, object) { logDebug("listenToSignalFromObject"); spy.target = object; spy.signalName = signalName; spy.clear(); } function verifySignalReceived() { logDebug("verifySignalReceived"); spy.wait(); } function verifyNoSignalReceived() { logDebug("verifyNoSignalReceived"); wait(500); compare(spy.count, 0, "no signal received"); } function logDebug(message) { if (debug) console.log(message); } } tests/auto/contacts/qmlcontacts/testcases/ContactsTestConfiguration.qml000066400000000000000000000043151233466112000272160ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 Item { function getManagerUnderTest() { // Adjust this to run the tests against different manager backends // Supported values: // "jsondb" // "memory" // "" (selects the default backend) return ""; } } tests/auto/contacts/qmlcontacts/testcases/ContactsTestHelper.qml000066400000000000000000000110211233466112000256160ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 Item { id:myModel; property ContactModel model; property Contact myContact : Contact { id:myContact; Name { firstName: ""; } PhoneNumber { number: "" } } property Contact mycontact2 : Contact { id:mycontact2 Name { firstName: ""; } PhoneNumber { number: "" contexts:["Home"] subTypes:[PhoneNumber.Mobile] } Organization { name: "" assistantName:"" } } function addContacts(contactsData) { console.log("contactsTestHelper.addContacts: " + contactsData.length + " contacts"); for (var index=0; index < contactsData.length; index++){ var contactData = contactsData[index] myModel.myContact.name.firstName = contactData.firstName; myModel.myContact.name.lastName = contactData.lastName; myModel.myContact.phoneNumber.number = contactData.phoneNumber; model.saveContact (myModel.myContact) contactsChangedSpy.wait(); } } function addContactDetail(contactDetails) { myModel.mycontact2.name.firstName = contactDetails[0].firstName; myModel.mycontact2.name.lastName = contactDetails[0].lastName; myModel.mycontact2.organization.name = contactDetails[0].name; myModel.mycontact2.organization.assistantName = contactDetails[0].assistantName; myModel.mycontact2.organization.startDate = contactDetails[0].startDate; myModel.mycontact2.organization.logoUrl = contactDetails[0].logoUrl; myModel.mycontact2.phoneNumber.number = contactDetails[0].phoneNumber; myModel.mycontact2.phoneNumber.subTypes = [contactDetails[0].phoneSubType]; myModel.mycontact2.phoneNumber.contexts = [contactDetails[0].contexts]; model.saveContact(myModel.mycontact2) contactsChangedSpy.wait(); } function testContacts() { return model.contacts } function emptyContactsDb () { console.log("contactsTestHelper.emptyContactsDb"); var id; var temp = model.contacts.length; for (var i = 0; i < temp; i++) { id = model.contacts[0].contactId; model.removeContact(id); console.log("contactsTestHelper.emptyContactsDb: remove " + id); contactsChangedSpy.wait(); } if (model.contacts.length > 0) { console.log("contactsTestHelper.emptyContactsDb: Failed to empty Db"); } } } tests/auto/contacts/qmlcontacts/testcases/SignalingJsonDb.qml000066400000000000000000000067601233466112000250710ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 import QtJsonDb 1.0 Partition { id: jsonDb property alias partition: jsonDb.name function createAndSignal(object) { logDebug("createAndSignal()"); create(object, resultCallback); } function removeAndSignal(object) { logDebug("removeAndSignal()"); remove(object, resultCallback); } function updateAndSignal(object) { logDebug("updateAndSignal()"); update(object, resultCallback); } property variant queryObject function queryAndSignal(querystr) { logDebug("queryAndSignal()"); queryObject = createQuery(querystr, -1, {}, jsonDb); queryObject.finished.connect(queryResultCallback); queryObject.start(); } signal operationFinished property variant lastResult: {} function resultCallback(error, result) { if (error) { console.log("resultCallback(): error: code " + error.code + ": message: " + error.message); return; } logDebug("resultCallback(): result received: " + JSON.stringify(result)); lastResult = result.items; operationFinished(); } function queryResultCallback() { var result = queryObject.takeResults(); logDebug("queryResultCallback(): result received: " + JSON.stringify(result)); lastResult = result; operationFinished(); } property bool debug: false function logDebug(message) { if (debug) console.log('SignalingJsonDb.' + message); } } tests/auto/contacts/qmlcontacts/testcases/tst_contact_add_detail.qml000066400000000000000000000140561233466112000265320ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 TestCase { name: "ContactAddDetailTests" id: contactAddDetailTests Contact { id: contact0 } function test_contact_add_null_detail_emits_no_signal() { listenToSignalFromObject("contactChanged", contact0); contact0.addDetail(null); verifyNoSignalReceived(); } Contact { id: contact1 } Name { id: contact1Name } function test_contact_add_detail_emits_signal() { listenToSignalFromObject("contactChanged", contact1); contact1.addDetail(contact1Name); verifySignalReceived(); } Contact { id: contact2 Name { id: contact2Name } } function test_contact_add_the_same_detail_emits_no_signal() { listenToSignalFromObject("contactChanged", contact2); contact2.addDetail(contact2Name); verifyNoSignalReceived(); } Contact { id: contact3 PhoneNumber { id: contact3PhoneNumber1 } } PhoneNumber { id: contact3PhoneNumber2 } function test_contact_add_second_detail_of_the_same_type_emits_signal() { listenToSignalFromObject("contactChanged", contact3); contact3.addDetail(contact3PhoneNumber2); verifySignalReceived(); } Contact { id: contact4 Name { id: contact4Name } } PhoneNumber { id: contact4PhoneNumber } function test_contact_add_second_detail_of_the_different_type_emits_signal() { listenToSignalFromObject("contactChanged", contact4); contact4.addDetail(contact4PhoneNumber); verifySignalReceived(); } Contact { id: contact5 } Name { id: contact5Name firstName: "old" } function test_contact_modify_added_detail_emits_signal() { contact5.addDetail(contact5Name); var detail = contact5.detail(ContactDetail.Name); listenToSignalFromObject("contactChanged", contact5); detail.firstName = "new"; verifySignalReceived(); } Contact { id: contact6 } Name { id: contact6Name firstName: "old" } function test_contact_modify_detail_already_added_modifies_contact() { contact6.addDetail(contact6Name); contact6Name.firstName = "new"; var detail = contact6.detail(ContactDetail.Name); expectFail("", "implementation takes a copy of the detail"); compare(detail.firstName, "new"); } Contact { id: contact7 } Name { id: contact7Name firstName: "old" } function test_contact_modify_detail_already_added_emits_signal() { contact7.addDetail(contact7Name); listenToSignalFromObject("contactChanged", contact7); contact7Name.firstName = "new"; expectFail("", "implementation takes a copy of the detail"); verifySignalReceived(); } Contact { id: contact8_1 } Contact { id: contact8_2 } Name { id: contact8Name firstName: "old" } function test_contact_modify_detail_added_to_two_contacts_modifies_both() { contact8_1.addDetail(contact8Name); contact8_2.addDetail(contact8Name); var detail = contact8_1.detail(ContactDetail.Name); detail.firstName = "new"; detail = contact8_2.detail(ContactDetail.Name); expectFail("", "does not propagate to the second contact"); compare(detail.firstName, "new"); compare(contact8_2.name.firstName, "new"); } property SignalSpy spy function init() { spy = Qt.createQmlObject("import QtTest 1.0;" + "SignalSpy {}", contactAddDetailTests); } function listenToSignalFromObject(signalName, object) { spy.target = object; spy.signalName = signalName; spy.clear(); } function verifySignalReceived() { spy.wait(); } function verifyNoSignalReceived() { verify(spy.count == 0, "no signal was received"); } } tests/auto/contacts/qmlcontacts/testcases/tst_contact_addresses.qml000066400000000000000000000203501233466112000264270ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 TestCase { name: "ContactsAddressesTests" TestCase { name: "ContactsAddressesTests::NoAddress" Contact { id: contactWithoutAddress } Contact { id: anotherContactWithoutAddress } Contact { id: yetAnotherContactWithoutAddress } function test_addressOfContactWithoutAddressIsDefined() { verifyIsDefined(contactWithoutAddress.address); } function test_addressesCountOfAnotherContactWithoutAddressIsZero() { // we do not access the address property of the contact, // so in this case the expected length is 0 compare(anotherContactWithoutAddress.addresses.length, 0); } function test_addressesCountOfYetAnotherContactWithoutAddressIsOne() { // we first access the address property of the contact, // so in this case the expected length is 1 verifyIsDefined(yetAnotherContactWithoutAddress.address); compare(yetAnotherContactWithoutAddress.addresses.length, 1); } function test_addressOfContactWithoutAddressIsEmpty() { verifyIsUndefined(contactWithoutAddress.address.city); verifyIsUndefined(contactWithoutAddress.address.country); verifyIsUndefined(contactWithoutAddress.address.countryCode); verifyIsUndefined(contactWithoutAddress.address.county); verifyIsUndefined(contactWithoutAddress.address.district); verifyIsUndefined(contactWithoutAddress.address.postcode); verifyIsUndefined(contactWithoutAddress.address.state); verifyIsUndefined(contactWithoutAddress.address.street); } function test_addressSetStreetOfContactWithoutAddress() { contactWithoutAddress.address.street = "NoAddressStreet"; compare(contactWithoutAddress.address.street, "NoAddressStreet"); } } TestCase { name: "ContactsAddressesTests::OneAddress" Contact { id: contactWithOneAddress Address { street: "myAddress" } } function test_accessAddressThroughAddress() { compare(contactWithOneAddress.address.street, "myAddress"); } function test_addressCount() { compare(contactWithOneAddress.addresses.length, 1); } function test_accessAddressThroughAddresses() { compare(contactWithOneAddress.addresses[0].street, "myAddress"); } function test_accessAddressThroughDetails() { compare(contactWithOneAddress.details( ContactDetail.Address)[0].street, "myAddress"); } } TestCase { name: "ContactsAddressesTests::MultipleAddresses" Contact { id: contactWithAddresses Address { street: "myAddress1" } Address { street: "myAddress2" } } function test_accessAddressThroughAddress() { compare(contactWithAddresses.address.street, "myAddress1"); } function test_addressCount() { compare(contactWithAddresses.addresses.length, 2); } function test_accessAddressThroughAddresses() { compare(contactWithAddresses.addresses[0].street, "myAddress1"); compare(contactWithAddresses.addresses[1].street, "myAddress2"); } function test_accessAddressThroughDetails() { compare(contactWithAddresses.details( ContactDetail.Address)[0].street, "myAddress1"); compare(contactWithAddresses.details( ContactDetail.Address)[1].street, "myAddress2"); } } TestCase { name: "ContactsAddressesTests::DynamicallyAdded::MultipleAddresses" Contact { id: contact1 } Address { id: address1 street: "myAddress1" } Address { id: address2 street: "myAddress2" } function init() { contact1.clearDetails(); contact1.addDetail(address1); contact1.addDetail(address2); } function test_accessAddressThroughAddress() { compare(contact1.address.street, "myAddress1"); } function test_addressCount() { verify(contact1.addresses); compare(contact1.addresses.length, 2); } function test_accessAddressThroughAddresses() { compare(contact1.addresses[0].street, "myAddress1"); compare(contact1.addresses[1].street, "myAddress2"); } function test_accessAddressThroughDetails() { compare(contact1.details( ContactDetail.Address)[0].street, "myAddress1"); compare(contact1.details( ContactDetail.Address)[1].street, "myAddress2"); } function cleanup () { contact1.clearDetails(); } } TestCase { name: "ContactsAddressesTests::ModificationSignaling" Contact { id: contactWithAddress Address { street: "old" } } function test_changeToDetailThroughAddressesEmitsSignal() { listenToSignalFromObject("contactChanged", contactWithAddress); contactWithAddress.addresses[0].street = "new"; verifySignalReceived(); } function init() { spy = Qt.createQmlObject("import QtTest 1.0;" + "SignalSpy {}", this); } } property SignalSpy spy function listenToSignalFromObject(signalName, object) { spy.target = object; spy.signalName = signalName; spy.clear(); } function verifySignalReceived() { spy.wait(); } function verifyNoSignalReceived() { verify(spy.count == 0, "no signal was received"); } function verifyIsUndefined(object) { verify(!object, "Object " + object + " is undefined"); } function verifyIsDefined(object) { verify(object, "Object " + object + " is defined"); } } tests/auto/contacts/qmlcontacts/testcases/tst_contact_detail_access.qml000066400000000000000000000142211233466112000272350ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 TestCase { name: "ContactDetailAccessTests" id: contactDetailAccessTests TestCase { name: "ContactDetailAccessTests::ContactWithoutDetails" Contact { id: contact10 } function test_detailReturnsUndefined() { verifyIsUndefined(contact10.detail(ContactDetail.PhoneNumber)); } function test_detailsReturnsEmptyList() { compare(contact10.details(ContactDetail.PhoneNumber).length, 0); } } TestCase { name: "ContactDetailAccessTests::ContactWithOneDetail" Contact { id: contact20 PhoneNumber { id: contact20PhoneNumber number: "99999999" } } function test_detailReturnsOneDetail() { var detail = contact20.detail(ContactDetail.PhoneNumber); compare(detail.number, contact20PhoneNumber.number); } function test_detailsReturnsListOfLengthOne() { var details = contact20.details(ContactDetail.PhoneNumber); compare(details.length, 1); } function test_detailsReturnsListContainingTheDetail() { var details = contact20.details(ContactDetail.PhoneNumber); compare(details[0].number, contact20PhoneNumber.number); } } TestCase { name: "ContactDetailAccessTests::ContactWithDetailsOfDifferentType" Contact { id: contact30 Name { id: contact30Name firstName: "A" } PhoneNumber { id: contact30PhoneNumber number: "99999999" } } function test_detailReturnsOneDetail() { var detail = contact30.detail(ContactDetail.Name); compare(detail.firstName, contact30Name.firstName); } function test_detailReturnsTheOtherDetail() { var detail = contact30.detail(ContactDetail.PhoneNumber); compare(detail.number, contact30PhoneNumber.number); } function test_detailsReturnsListOfCorrentLengthForOneDetail() { var details = contact30.details(ContactDetail.Name); compare(details.length, 1); } function test_detailsReturnsListOfCorrentLengthForTheOtherDetail() { var details = contact30.details(ContactDetail.PhoneNumber); compare(details.length, 1); } function test_detailsReturnsListContainingTheCorrectDetailForOne() { var details = contact30.details(ContactDetail.Name); compare(details[0].firstName, contact30Name.firstName); } function test_detailsReturnsListContainingTheCorrectDetailForTheOther() { var details = contact30.details(ContactDetail.PhoneNumber); compare(details[0].number, contact30PhoneNumber.number); } } TestCase { name: "ContactDetailAccessTests::ContactWithMultipleDetails" Contact { id: contact40 PhoneNumber { id: contact40PhoneNumber1 number: "11111111" } PhoneNumber { id: contact40PhoneNumber2 number: "22222222" } } function test_detailReturnsTheFirstDetail() { var detail = contact40.detail(ContactDetail.PhoneNumber); compare(detail.number, contact40PhoneNumber1.number); } function test_detailsReturnsListOfCorrentLength() { var details = contact40.details(ContactDetail.PhoneNumber); compare(details.length, 2); } function test_detailsReturnsListContainingTheFirstDetail() { var details = contact40.details(ContactDetail.PhoneNumber); compare(details[0].number, contact40PhoneNumber1.number); } function test_detailsReturnsListContainingTheSecondDetail() { var details = contact40.details(ContactDetail.PhoneNumber); compare(details[1].number, contact40PhoneNumber2.number); } } function verifyIsUndefined(object) { verify(!object, "Object " + object + "is undefined"); } } tests/auto/contacts/qmlcontacts/testcases/tst_contact_emails.qml000066400000000000000000000170131233466112000257260ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 TestCase { name: "ContactsEmailsTests" TestCase { name: "ContactsEmailsTests::NoEmail" Contact { id: contactWithoutEmail } Contact { id: anotherContactWithoutEmail } Contact { id: yetAnotherContactWithoutEmail } function test_emailOfContactWithoutEmailIsDefined() { verifyIsDefined(contactWithoutEmail.email); } function test_emailOfContactWithoutEmailIsEmpty() { verifyIsUndefined(contactWithoutEmail.email.emailAddress); } function test_emailsCountOfAnotherContactWithoutEmailIsZero() { // we do not access the email property of the contact, // so in this case the expected length is 0 compare(anotherContactWithoutEmail.emails.length, 0); } function test_emailsCountOfYetAnotherContactWithoutEmailIsOne() { // we first access the email property of the contact, // so in this case the expected length is 1 verifyIsDefined(yetAnotherContactWithoutEmail.email); compare(yetAnotherContactWithoutEmail.emails.length, 1); } } TestCase { name: "ContactsEmailsTests::OneEmail" Contact { id: contactWithOneEmail EmailAddress { emailAddress: "a@ovi.com" } } function test_accessEmailThroughEmail() { compare(contactWithOneEmail.email.emailAddress, "a@ovi.com"); } function test_emailCount() { compare(contactWithOneEmail.emails.length, 1); } function test_accessEmailThroughEmails() { compare(contactWithOneEmail.emails[0].emailAddress, "a@ovi.com"); } function test_accessEmailThroughDetails() { compare(contactWithOneEmail.details( ContactDetail.Email)[0].emailAddress, "a@ovi.com"); } } TestCase { name: "ContactsEmailsTests::MultipleEmails" Contact { id: contactWithEmails EmailAddress { emailAddress: "a@ovi.com" } EmailAddress { emailAddress: "b@ovi.com" } } function test_accessEmailThroughEmail() { compare(contactWithEmails.email.emailAddress, "a@ovi.com"); } function test_emailCount() { compare(contactWithEmails.emails.length, 2); } function test_accessEmailThroughEmails() { compare(contactWithEmails.emails[0].emailAddress, "a@ovi.com"); compare(contactWithEmails.emails[1].emailAddress, "b@ovi.com"); } function test_accessEmailThroughDetails() { compare(contactWithEmails.details( ContactDetail.Email)[0].emailAddress, "a@ovi.com"); compare(contactWithEmails.details( ContactDetail.Email)[1].emailAddress, "b@ovi.com"); } } TestCase { name: "ContactsEmailsTests::DynamicallyAdded::MultipleEmails" Contact { id: contact1 } EmailAddress { id: emailAddress1 emailAddress: "a@ovi.com" } EmailAddress { id: emailAddress2 emailAddress: "b@ovi.com" } function init() { contact1.clearDetails(); contact1.addDetail(emailAddress1); contact1.addDetail(emailAddress2); } function test_accessEmailThroughEmail() { compare(contact1.email.emailAddress, "a@ovi.com"); } function test_emailCount() { verify(contact1.emails); compare(contact1.emails.length, 2); } function test_accessEmailThroughEmails() { compare(contact1.emails[0].emailAddress, "a@ovi.com"); compare(contact1.emails[1].emailAddress, "b@ovi.com"); } function test_accessEmailThroughDetails() { compare(contact1.details( ContactDetail.Email)[0].emailAddress, "a@ovi.com"); compare(contact1.details( ContactDetail.Email)[1].emailAddress, "b@ovi.com"); } function cleanup () { contact1.clearDetails(); } } TestCase { name: "ContactsEmailsTests::ModificationSignaling" Contact { id: contactWithEmailAddress EmailAddress { emailAddress: "a@a" } } function test_changeToDetailThroughEmailsEmitsSignal() { listenToSignalFromObject("contactChanged", contactWithEmailAddress); contactWithEmailAddress.emails[0].emailAddress = "b@b"; verifySignalReceived(); } function init() { spy = Qt.createQmlObject("import QtTest 1.0;" + "SignalSpy {}", this); } } property SignalSpy spy function listenToSignalFromObject(signalName, object) { spy.target = object; spy.signalName = signalName; spy.clear(); } function verifySignalReceived() { spy.wait(); } function verifyNoSignalReceived() { verify(spy.count == 0, "no signal was received"); } function verifyIsUndefined(object) { verify(!object, "Object " + object + " is undefined"); } function verifyIsDefined(object) { verify(object, "Object " + object + " is defined"); } } tests/auto/contacts/qmlcontacts/testcases/tst_contact_extendeddetails.qml000066400000000000000000000253671233466112000276350ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 TestCase { name: "ContactsExtendedDetailsTests" TestCase { name: "ContactsExtendedDetailsTests::NoExtendedDetail" Contact { id: contactWithoutExtendedDetail } Contact { id: anotherContactWithoutExtendedDetail } Contact { id: yetAnotherContactWithoutExtendedDetail } function test_ExtendedDetailOfContactWithoutExtendedDetailIsDefined() { verifyIsDefined(contactWithoutExtendedDetail.extendedDetail); } function test_ExtendedDetailNameOfContactWithoutExtendedDetailIsEmpty() { verifyIsUndefined(contactWithoutExtendedDetail.extendedDetail.name); } function test_ExtendedDetailDataOfContactWithoutExtendedDetailIsEmpty() { verifyIsUndefined(contactWithoutExtendedDetail.extendedDetail.data); } function test_ExtendedDetailsCountOfAnotherContactWithoutExtendedDetailIsZero() { // we do not access the ExtendedDetail property of the contact, // so in this case the expected length is 0 compare(anotherContactWithoutExtendedDetail.extendedDetails.length, 0); } function test_ExtendedDetailsCountOfYetAnotherContactWithoutExtendedDetailIsOne() { // we first access the ExtendedDetail property of the contact, // so in this case the expected length is 1 verifyIsDefined(yetAnotherContactWithoutExtendedDetail.extendedDetail); compare(yetAnotherContactWithoutExtendedDetail.extendedDetails.length, 1); } } TestCase { name: "ContactsExtendedDetailsTests::OneExtendedDetail" Contact { id: contactWithOneExtendedDetail ExtendedDetail { name: "First" data: 1 } } function test_accessExtendedDetailThroughExtendedDetail() { compare(contactWithOneExtendedDetail.extendedDetail.name, "First", "name"); compare(contactWithOneExtendedDetail.extendedDetail.data, 1, "data"); } function test_ExtendedDetailsCount() { compare(contactWithOneExtendedDetail.extendedDetails.length, 1, "extendedDetails length"); } function test_accessExtendedDetailThroughExtendedDetails() { compare(contactWithOneExtendedDetail.extendedDetails[0].name, "First", "name"); compare(contactWithOneExtendedDetail.extendedDetails[0].data, 1, "data"); } function test_accessExtendedDetailThroughDetails() { compare(contactWithOneExtendedDetail.details( ContactDetail.ExtendedDetail)[0].name, "First", "name"); compare(contactWithOneExtendedDetail.details( ContactDetail.ExtendedDetail)[0].data, 1, "data"); } } TestCase { name: "ContactsExtendedDetailsTests::MultipleExtendedDetails" Contact { id: contactWithExtendedDetails ExtendedDetail { name: "First" data: 1 } ExtendedDetail { name: "Second" data: 2 } } function test_accessExtendedDetailThroughExtendedDetail() { compare(contactWithExtendedDetails.extendedDetail.name, "First", "name"); compare(contactWithExtendedDetails.extendedDetail.data, 1, "name"); } function test_ExtendedDetailCount() { compare(contactWithExtendedDetails.extendedDetails.length, 2, "length"); } function test_accessExtendedDetailThroughExtendedDetails() { compare(contactWithExtendedDetails.extendedDetails[0].name, "First", "[0].name"); compare(contactWithExtendedDetails.extendedDetails[0].data, 1, "[0].data"); compare(contactWithExtendedDetails.extendedDetails[1].name, "Second", "[1].name"); compare(contactWithExtendedDetails.extendedDetails[1].data, 2, "[1].data"); } function test_accessExtendedDetailThroughDetails() { compare(contactWithExtendedDetails.details( ContactDetail.ExtendedDetail)[0].name, "First", "[0].name"); compare(contactWithExtendedDetails.details( ContactDetail.ExtendedDetail)[0].data, 1, "[0].data"); compare(contactWithExtendedDetails.details( ContactDetail.ExtendedDetail)[1].name, "Second", "[1].name"); compare(contactWithExtendedDetails.details( ContactDetail.ExtendedDetail)[1].data, 2, "[1].data"); } } TestCase { name: "ContactsExtendedDetailsTests::DynamicallyAdded::MultipleExtendedDetails" Contact { id: contact1 } ExtendedDetail { id: extendedDetail1 name: "First" data: 1 } ExtendedDetail { id: extendedDetail2 name: "Second" data: 2 } function init() { contact1.clearDetails(); contact1.addDetail(extendedDetail1); contact1.addDetail(extendedDetail2); } function test_accessExtendedDetailThroughExtendedDetail() { compare(contact1.extendedDetail.name, "First", "name"); compare(contact1.extendedDetail.data, 1, "data"); } function test_ExtendedDetailCount() { verify(contact1.extendedDetails); compare(contact1.extendedDetails.length, 2); } function test_accessExtendedDetailThroughExtendedDetails() { compare(contact1.extendedDetails[0].name, "First", "[0].name"); compare(contact1.extendedDetails[0].data, 1, "[0].data"); compare(contact1.extendedDetails[1].name, "Second", "[1].name"); compare(contact1.extendedDetails[1].data, 2, "[1].data"); } function test_accessExtendedDetailThroughDetails() { compare(contact1.details( ContactDetail.ExtendedDetail)[0].name, "First", "[0].name"); compare(contact1.details( ContactDetail.ExtendedDetail)[0].data, 1, "[0].data"); compare(contact1.details( ContactDetail.ExtendedDetail)[1].name, "Second", "[1].name"); compare(contact1.details( ContactDetail.ExtendedDetail)[1].data, 2, "[1].data"); } function cleanup () { contact1.clearDetails(); } } TestCase { name: "ContactsExtendedDetailsTests::ModificationSignaling" Contact { id: contactWithExtendedDetail ExtendedDetail { name: "First" data: 1 } } function test_changeToNameThroughExtendedDetailsEmitsSignal() { listenToSignalFromObject("contactChanged", contactWithExtendedDetail); contactWithExtendedDetail.extendedDetails[0].name = "FirstChanged"; verifySignalReceived(); } function test_changeToDataThroughExtendedDetailsEmitsSignal() { listenToSignalFromObject("contactChanged", contactWithExtendedDetail); contactWithExtendedDetail.extendedDetails[0].data = 5; verifySignalReceived(); } Contact { id: contactWithExtendedDetail2 ExtendedDetail { name: "First" data: 1 } } function test_noChangeToNameThroughExtendedDetailsEmitsNoSignal() { listenToSignalFromObject("contactChanged", contactWithExtendedDetail2); contactWithExtendedDetail2.extendedDetails[0].name = "First"; verifyNoSignalReceived(); } function test_noChangeToDataThroughExtendedDetailsEmitsNoSignal() { listenToSignalFromObject("contactChanged", contactWithExtendedDetail2); contactWithExtendedDetail2.extendedDetails[0].data = 1; verifyNoSignalReceived(); } function init() { spy = Qt.createQmlObject("import QtTest 1.0;" + "SignalSpy {}", this); } } property SignalSpy spy function listenToSignalFromObject(signalName, object) { spy.target = object; spy.signalName = signalName; spy.clear(); } function verifySignalReceived() { spy.wait(); } function verifyNoSignalReceived() { verify(spy.count == 0, "no signal was received"); } function verifyIsUndefined(object) { verify(!object, "Object " + object + " is undefined"); } function verifyIsDefined(object) { verify(object, "Object " + object + " is defined"); } } tests/auto/contacts/qmlcontacts/testcases/tst_contact_modification.qml000066400000000000000000000121061233466112000271170ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 TestCase { name: "ContactModificationTests" id: contactModificationTests TestCase { name: "ContactModificationTests::UpdateDetail" Contact { id: contact1 Name { id: contact1Name firstName: "old" } } function test_update_to_contact_detail_directly_sets_contact_as_modified() { contact1Name.firstName = "new"; verify(contact1.modified); } Contact { id: contact2 Name { id: contact2Name firstName: "old" } } function test_update_to_contact_detail_sets_contact_as_modified() { contact2.name.firstName = "new"; verify(contact2.modified); } } TestCase { name: "ContactModificationTests::ThroughDetailAccessors" Contact { id: contact50 Name { firstName: "old" } } function test_modification_through_detail_modifies_the_contact() { var detail = contact50.detail(ContactDetail.Name); detail.firstName = "new" detail = contact50.detail(ContactDetail.Name); compare(detail.firstName, "new"); } Contact { id: contact51 Name { firstName: "old" } } function test_modification_through_details_modifies_the_contact() { var details = contact51.details(ContactDetail.Name); details[0].firstName = "new" var detail = contact51.detail(ContactDetail.Name); compare(detail.firstName, "new"); } Contact { id: contact52 Name { firstName: "old" } } function test_modification_through_detail_emits_contact_changed() { var detail = contact52.detail(ContactDetail.Name); listenToSignalFromObject("contactChanged", contact52); detail.firstName = "new" verifySignalReceived(); } Contact { id: contact53 Name { firstName: "old" } } function test_modification_through_details_emits_contact_changed() { var details = contact53.details(ContactDetail.Name); listenToSignalFromObject("contactChanged", contact53); details[0].firstName = "new" verifySignalReceived(); } property SignalSpy spy function init() { spy = Qt.createQmlObject("import QtTest 1.0;" + "SignalSpy {}", this); } function listenToSignalFromObject(signalName, object) { spy.target = object; spy.signalName = signalName; spy.clear(); } function verifySignalReceived() { spy.wait(); } function verifyNoSignalReceived() { verify(spy.count == 0, "no signal was received"); } } } tests/auto/contacts/qmlcontacts/testcases/tst_contact_organizations.qml000066400000000000000000000203511233466112000273420ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 TestCase { name: "ContactsOrganizationsTests" TestCase { name: "ContactsOrganizationsTests::NoOrganization" Contact { id: contactWithoutOrganization } Contact { id: anotherContactWithoutOrganization } Contact { id: yetAnotherContactWithoutOrganization } function test_organizationOfContactWithoutOrganizationIsDefined() { verifyIsDefined(contactWithoutOrganization.organization); } function test_organizationOfContactWithoutOrganizationIsEmpty() { verifyIsUndefined(contactWithoutOrganization.organization.assistantName); compare(contactWithoutOrganization.organization.department.length, 0); verifyIsUndefined(contactWithoutOrganization.organization.location); compare(contactWithoutOrganization.organization.logoUrl, ""); verifyIsUndefined(contactWithoutOrganization.organization.name); verifyIsUndefined(contactWithoutOrganization.organization.role); verifyIsUndefined(contactWithoutOrganization.organization.title); } function test_organizationsCountOfAnotherContactWithoutOrganizationIsZero() { // we do not access the organization property of the contact, // so in this case the expected length is 0 compare(anotherContactWithoutOrganization.organizations.length, 0); } function test_organizationsCountOfYetAnotherContactWithoutOrganizationIsOne() { // we first access the organization property of the contact, // so in this case the expected length is 1 verifyIsDefined(yetAnotherContactWithoutOrganization.organization); compare(yetAnotherContactWithoutOrganization.organizations.length, 1); } } TestCase { name: "ContactsOrganizationsTests::OneOrganization" Contact { id: contactWithOneOrganization Organization { name: "myOrganization" } } function test_accessOrganizationThroughOrganization() { compare(contactWithOneOrganization.organization.name, "myOrganization"); } function test_OrganizationCount() { compare(contactWithOneOrganization.organizations.length, 1); } function test_accessOrganizationThroughOrganizations() { compare(contactWithOneOrganization.organizations[0].name, "myOrganization"); } function test_accessOrganizationThroughDetails() { compare(contactWithOneOrganization.details( ContactDetail.Organization)[0].name, "myOrganization"); } } TestCase { name: "ContactsOrganizationsTests::MultipleOrganizations" Contact { id: contactWithOrganizations Organization { name: "myOrganization1" } Organization { name: "myOrganization2" } } function test_accessOrganizationThroughOrganization() { compare(contactWithOrganizations.organization.name, "myOrganization1"); } function test_organizationCount() { compare(contactWithOrganizations.organizations.length, 2); } function test_accessOrganizationThroughOrganizations() { compare(contactWithOrganizations.organizations[0].name, "myOrganization1"); compare(contactWithOrganizations.organizations[1].name, "myOrganization2"); } function test_accessOrganizationThroughDetails() { compare(contactWithOrganizations.details( ContactDetail.Organization)[0].name, "myOrganization1"); } } TestCase { name: "ContactsOrganizationsTests::DynamicallyAdded::MultipleOrganizations" Contact { id: contact1 } Organization { id: organization1 name: "myOrganization1" } Organization { id: organization2 name: "myOrganization2" } function init() { contact1.clearDetails(); contact1.addDetail(organization1); contact1.addDetail(organization2); } function test_accessOrganizationThroughOrganization() { compare(contact1.organization.name, "myOrganization1"); } function test_organizationCount() { verify(contact1.organizations); compare(contact1.organizations.length, 2); } function test_accessOrganizationThroughOrganizations() { compare(contact1.organizations[0].name, "myOrganization1"); compare(contact1.organizations[1].name, "myOrganization2"); } function test_accessOrganizationThroughDetails() { compare(contact1.details( ContactDetail.Organization)[0].name, "myOrganization1"); } function cleanup () { contact1.clearDetails(); } } TestCase { name: "ContactsOrganizationsTests::ModificationSignaling" Contact { id: contactWithOrganization Organization { name: "old" } } function test_changeToDetailThroughOrganizationsEmitsSignal() { listenToSignalFromObject("contactChanged", contactWithOrganization); contactWithOrganization.organizations[0].name = "new"; verifySignalReceived(); } function init() { spy = Qt.createQmlObject("import QtTest 1.0;" + "SignalSpy {}", this); } } property SignalSpy spy function listenToSignalFromObject(signalName, object) { spy.target = object; spy.signalName = signalName; spy.clear(); } function verifySignalReceived() { spy.wait(); } function verifyNoSignalReceived() { verify(spy.count == 0, "no signal was received"); } function verifyIsUndefined(object) { verify(!object, "Object " + object + " is undefined"); } function verifyIsDefined(object) { verify(object, "Object " + object + " is defined"); } } tests/auto/contacts/qmlcontacts/testcases/tst_contact_phonenumbers.qml000066400000000000000000000176601233466112000271710ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 TestCase { name: "ContactsPhoneNumbersTests" TestCase { name: "ContactsPhoneNumbersTests::NoPhoneNumber" Contact { id: contactWithoutPhoneNumber } Contact { id: anotherContactWithoutPhoneNumber } Contact { id: yetAnotherContactWithoutPhoneNumber } function test_phoneNumberOfContactWithoutPhoneNumberIsDefined() { verifyIsDefined(contactWithoutPhoneNumber.phoneNumber); } function test_phoneNumberOfContactWithoutPhoneNumberIsEmpty() { verifyIsUndefined(contactWithoutPhoneNumber.phoneNumber.number); compare(contactWithoutPhoneNumber.phoneNumber.subTypes.length, 0); } function test_phoneNumbersCountOfAnotherContactWithoutPhoneNumberIsZero() { // we do not access the phoneNumber property of the contact, // so in this case the expected length is 0 compare(anotherContactWithoutPhoneNumber.phoneNumbers.length, 0); } function test_phoneNumbersCountOfYetAnotherContactWithoutPhoneNumberIsOne() { // we first access the phoneNumber property of the contact, // so in this case the expected length is 1 verifyIsDefined(yetAnotherContactWithoutPhoneNumber.phoneNumber); compare(yetAnotherContactWithoutPhoneNumber.phoneNumbers.length, 1); } } TestCase { name: "ContactsPhoneNumbersTests::OnePhoneNumber" Contact { id: contactWithOnePhoneNumber PhoneNumber { number: "1111111111" subTypes:[PhoneNumber.Mobile] } } function test_accessPhoneNumberThroughPhoneNumber() { compare(contactWithOnePhoneNumber.phoneNumber.number, "1111111111"); } function test_phoneNumberCount() { compare(contactWithOnePhoneNumber.phoneNumbers.length, 1); } function test_accessPhoneNumberThroughPhoneNumbers() { compare(contactWithOnePhoneNumber.phoneNumbers[0].number, "1111111111"); } function test_accessPhoneNumberThroughDetails() { compare(contactWithOnePhoneNumber.details( ContactDetail.PhoneNumber)[0].number, "1111111111"); } } TestCase { name: "ContactsPhoneNumbersTests::MultiplePhoneNumbers" Contact { id: contactWithPhoneNumbers PhoneNumber { number: "1111111111" subTypes:[PhoneNumber.Mobile] } PhoneNumber { number: "2222222222" subTypes:[PhoneNumber.Fax] } } function test_accessPhoneNumberThroughPhoneNumber() { compare(contactWithPhoneNumbers.phoneNumber.number, "1111111111"); } function test_phoneNumberCount() { compare(contactWithPhoneNumbers.phoneNumbers.length, 2); } function test_accessPhoneNumberThroughPhoneNumbers() { compare(contactWithPhoneNumbers.phoneNumbers[0].number, "1111111111"); compare(contactWithPhoneNumbers.phoneNumbers[1].number, "2222222222"); } function test_accessPhoneNumberThroughDetails() { compare(contactWithPhoneNumbers.details( ContactDetail.PhoneNumber)[0].number, "1111111111"); } } TestCase { name: "ContactsPhoneNumbersTests::DynamicallyAdded::MultiplePhoneNumbers" Contact { id: contact1 } PhoneNumber { id: phoneNumber1 number: "1111111111" subTypes:[PhoneNumber.Mobile] } PhoneNumber { id: phoneNumber2 number: "2222222222" subTypes:[PhoneNumber.Fax] } function init() { contact1.clearDetails(); contact1.addDetail(phoneNumber1); contact1.addDetail(phoneNumber2); } function test_accessPhoneNumberThroughPhoneNumber() { compare(contact1.phoneNumber.number, "1111111111"); } function test_phoneNumberCount() { verify(contact1.phoneNumbers); compare(contact1.phoneNumbers.length, 2); } function test_accessPhoneNumberThroughPhoneNumbers() { compare(contact1.phoneNumbers[0].number, "1111111111"); compare(contact1.phoneNumbers[1].number, "2222222222"); } function test_accessPhoneNumberThroughDetails() { compare(contact1.details( ContactDetail.PhoneNumber)[0].number, "1111111111"); } function cleanup () { contact1.clearDetails(); } } TestCase { name: "ContactsPhoneNumbersTests::ModificationSignaling" Contact { id: contactWithPhoneNumber PhoneNumber { number: "1234" } } function test_changeToDetailThroughPhoneNumbersEmitsSignal() { listenToSignalFromObject("contactChanged", contactWithPhoneNumber); contactWithPhoneNumber.phoneNumbers[0].number = "5678"; verifySignalReceived(); } function init() { spy = Qt.createQmlObject("import QtTest 1.0;" + "SignalSpy {}", this); } } property SignalSpy spy function listenToSignalFromObject(signalName, object) { spy.target = object; spy.signalName = signalName; spy.clear(); } function verifySignalReceived() { spy.wait(); } function verifyNoSignalReceived() { verify(spy.count == 0, "no signal was received"); } function verifyIsUndefined(object) { verify(!object, "Object " + object + " is undefined"); } function verifyIsDefined(object) { verify(object, "Object " + object + " is defined"); } } tests/auto/contacts/qmlcontacts/testcases/tst_contact_remove_detail.qml000066400000000000000000000122141233466112000272710ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 TestCase { name: "ContactRemoveDetailTests" id: contactRemoveDetailTests Contact { id: contact0 } function test_contact_remove_null_detail_emits_no_signal() { listenToSignalFromObject("contactChanged", contact0); contact0.removeDetail(null); verifyNoSignalReceived(); } Contact { id: contact2 Name { id: contact2Name } } function test_contact_remove_detail_emits_signal() { listenToSignalFromObject("contactChanged", contact2); contact2.removeDetail(contact2Name); verifySignalReceived(); } Contact { id: contact3 Name { id: contact3Name1 } } Name { id: contact3Name2 } function test_contact_remove_unrelated_detail_emits_no_signal() { listenToSignalFromObject("contactChanged", contact3); contact3.removeDetail(contact3Name2); verifyNoSignalReceived(); } Contact { id: contact4 Name { id: contact4Name } } PhoneNumber { id: contact4PhoneNumber } function test_contact_remove_unrelated_detail_of_the_different_type_emits_no_signal() { listenToSignalFromObject("contactChanged", contact4); contact4.removeDetail(contact4PhoneNumber); verifyNoSignalReceived(); } Contact { id: contact5 PhoneNumber { id: contact5PhoneNumber1 } PhoneNumber { id: contact5PhoneNumber2 } } function test_contact_remove_one_of_multiple_details_emits_signal() { listenToSignalFromObject("contactChanged", contact5); contact5.removeDetail(contact5PhoneNumber1); verifySignalReceived(); } Contact { id: contact6 PhoneNumber { id: contact6PhoneNumber1 } PhoneNumber { id: contact6PhoneNumber2 } } function test_contact_remove_multiple_details_emits_signal() { contact6.removeDetail(contact6PhoneNumber1); listenToSignalFromObject("contactChanged", contact6); contact6.removeDetail(contact6PhoneNumber2); verifySignalReceived(); } Contact { id: contact7 Name { id: contact7Name firstName: "old" } } function test_contact_modify_detail_already_removed_emits_no_signal() { var detail = contact7.detail(ContactDetail.Name); contact7.removeDetail(detail); listenToSignalFromObject("contactChanged", contact7); detail.firstName = "new"; verifyNoSignalReceived(); } property SignalSpy spy function init() { spy = Qt.createQmlObject("import QtTest 1.0;" + "SignalSpy {}", contactRemoveDetailTests); } function listenToSignalFromObject(signalName, object) { spy.target = object; spy.signalName = signalName; spy.clear(); } function verifySignalReceived() { spy.wait(); } function verifyNoSignalReceived() { verify(spy.count == 0, "no signal was received"); } } tests/auto/contacts/qmlcontacts/testcases/tst_contact_signals.qml000066400000000000000000000527331233466112000261240ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 TestCase { name: "ContactSignalsTests" id: contactSignalsTests Contact { id: contactWithAddress1 Address { street: "old" } } function test_contact_address_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithAddress1); contactWithAddress1.address.street = "new"; verifySignalReceived(); } Contact { id: contactWithAddress2 Address { street: "old" } } function test_contact_address_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithAddress2); contactWithAddress2.address.street = contactWithAddress2.address.street; verifyNoSignalReceived(); } Contact { id: contactWithAnniversary1 Anniversary { originalDate: "2011-10-26" } } function test_contact_anniversary_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithAnniversary1); contactWithAnniversary1.anniversary.originalDate = "2011-10-27"; verifySignalReceived(); } Contact { id: contactWithAnniversary2 Anniversary { originalDate: "2011-10-26" } } function test_contact_anniversary_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithAnniversary2); contactWithAnniversary2.anniversary.originalDate = contactWithAnniversary2.anniversary.originalDate; verifyNoSignalReceived(); } Contact { id: contactWithAvatar1 Avatar { imageUrl: "http://old" } } function test_contact_avatar_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithAvatar1); contactWithAvatar1.avatar.imageUrl = "http://new"; verifySignalReceived(); } Contact { id: contactWithAvatar2 Avatar { imageUrl: "http://old" } } function test_contact_avatar_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithAvatar2); contactWithAvatar2.avatar.imageUrl = contactWithAvatar2.avatar.imageUrl; verifyNoSignalReceived(); } Contact { id: contactWithBirthday1 Birthday { birthday: "2010-10-26" } } function test_contact_birthday_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithBirthday1); contactWithBirthday1.birthday.birthday = "2010-10-27"; verifySignalReceived(); } Contact { id: contactWithBirthday2 Birthday { birthday: "2010-10-26" } } function test_contact_birthday_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithBirthday2); contactWithBirthday2.birthday.birthday = contactWithBirthday2.birthday.birthday; verifyNoSignalReceived(); } Contact { id: contactWithEmailAddress1 EmailAddress { emailAddress: "a@a" } } function test_contact_email_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithEmailAddress1); contactWithEmailAddress1.email.emailAddress = "b@b"; verifySignalReceived(); } Contact { id: contactWithEmailAddress2 EmailAddress { emailAddress: "a@a" } } function test_contact_email_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithEmailAddress2); contactWithEmailAddress2.email.emailAddress = contactWithEmailAddress2.email.emailAddress; verifyNoSignalReceived(); } Contact { id: contactWithExtendedDetail1 ExtendedDetail { name: "extended1" data: "originaldata1" } } function test_contact_extended_detail_change_name_emits_signal() { listenToSignalFromObject("contactChanged", contactWithExtendedDetail1); contactWithExtendedDetail1.extendedDetail.name = "new name"; verifySignalReceived(); } function test_contact_extended_detail_change_data_emits_signal() { listenToSignalFromObject("contactChanged", contactWithExtendedDetail1); contactWithExtendedDetail1.extendedDetail.data = "new data"; verifySignalReceived(); } function test_contact_extended_detail_emits_no_signal_when_name_does_not_change() { listenToSignalFromObject("contactChanged", contactWithExtendedDetail1); contactWithExtendedDetail1.extendedDetail.name = contactWithExtendedDetail1.extendedDetail.name; verifyNoSignalReceived(); } function test_contact_extended_detail_emits_no_signal_when_data_does_not_change() { listenToSignalFromObject("contactChanged", contactWithExtendedDetail1); contactWithExtendedDetail1.extendedDetail.data = contactWithExtendedDetail1.extendedDetail.data; verifyNoSignalReceived(); } Contact { id: contactWithFavorite1 Favorite { favorite: false } } function test_contact_favorite_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithFavorite1); contactWithFavorite1.favorite.favorite = true; verifySignalReceived(); } Contact { id: contactWithFavorite2 Favorite { favorite: false } } function test_contact_favorite_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithFavorite2); contactWithFavorite2.favorite.favorite = contactWithFavorite2.favorite.favorite; verifyNoSignalReceived(); } Contact { id: contactWithGender1 Gender { gender: Gender.Male } } function test_contact_gender_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithGender1); contactWithGender1.gender.gender = Gender.Female; verifySignalReceived(); } Contact { id: contactWithGender2 Gender { gender: Gender.Male } } function test_contact_gender_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithGender2); contactWithGender2.gender.gender = contactWithGender2.gender.gender; verifyNoSignalReceived(); } Contact { id: contactWithLocation1 Location { latitude: 0.0 } } function test_contact_geolocation_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithLocation1); contactWithLocation1.geolocation.latitude = 1.0; verifySignalReceived(); } Contact { id: contactWithLocation2 Location { latitude: 0.0 } } function test_contact_geolocation_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithLocation2); contactWithLocation2.geolocation.latitude = contactWithLocation2.geolocation.latitude; verifyNoSignalReceived(); } Contact { id: contactWithGlobalPresence1 GlobalPresence { state: Presence.Unknown } } function test_contact_globalpresence_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithGlobalPresence1); contactWithGlobalPresence1.globalPresence.state = Presence.Available; verifySignalReceived(); } Contact { id: contactWithGlobalPresence2 GlobalPresence { state: Presence.Unknown } } function test_contact_globalpresence_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithGlobalPresence2); contactWithGlobalPresence2.globalPresence.state = contactWithGlobalPresence2.globalPresence.state; verifyNoSignalReceived(); } Contact { id: contactWithGuid1 Guid { guid: "0000" } } function test_contact_guid_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithGuid1); contactWithGuid1.guid.guid = "1111"; verifySignalReceived(); } Contact { id: contactWithGuid2 Guid { guid: "0000" } } function test_contact_guid_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithGuid2); contactWithGuid2.guid.guid = contactWithGuid2.guid.guid; verifyNoSignalReceived(); } Contact { id: contactWithHobby1 Hobby { hobby: "old" } } function test_contact_hobby_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithHobby1); contactWithHobby1.hobby.hobby = "new"; verifySignalReceived(); } Contact { id: contactWithHobby2 Hobby { hobby: "old" } } function test_contact_hobby_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithHobby2); contactWithHobby2.hobby.hobby = contactWithHobby2.hobby.hobby; verifyNoSignalReceived(); } Contact { id: contactWithName1 Name { firstName: "old" } } function test_contact_name_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithName1); contactWithName1.name.firstName = "new"; verifySignalReceived(); } Contact { id: contactWithName2 Name { firstName: "old" } } function test_contact_name_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithName2); contactWithName2.name.firstName = contactWithName2.name.firstName; verifyNoSignalReceived(); } Contact { id: contactWithNickname1 Nickname { nickname: "old" } } function test_contact_nickname_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithNickname1); contactWithNickname1.nickname.nickname = "new"; verifySignalReceived(); } Contact { id: contactWithNickname2 Nickname { nickname: "old" } } function test_contact_nickname_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithNickname2); contactWithNickname2.nickname.nickname = contactWithNickname2.nickname.nickname; verifyNoSignalReceived(); } Contact { id: contactWithDisplayLabel1 DisplayLabel { label: "old" } } function test_contact_displaylabel_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithDisplayLabel1); contactWithDisplayLabel1.displayLabel.label = "new"; verifySignalReceived(); } Contact { id: contactWithDisplayLabel2 DisplayLabel { label: "old" } } function test_contact_displaylabel_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithDisplayLabel2); contactWithDisplayLabel2.displayLabel.label = contactWithDisplayLabel2.displayLabel.label; verifyNoSignalReceived(); } Contact { id: contactWithNote1 Note { note: "old" } } function test_contact_note_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithNote1); contactWithNote1.note.note = "new"; verifySignalReceived(); } Contact { id: contactWithNote2 Note { note: "old" } } function test_contact_note_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithNote2); contactWithNote2.note.note = contactWithNote2.note.note; verifyNoSignalReceived(); } Contact { id: contactWithOnlineAccount1 OnlineAccount { accountUri: "http://old" } } function test_contact_onlineaccount_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithOnlineAccount1); contactWithOnlineAccount1.onlineAccount.accountUri = "http://new"; verifySignalReceived(); } Contact { id: contactWithOnlineAccount2 OnlineAccount { accountUri: "http://old" } } function test_contact_onlineaccount_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithOnlineAccount2); contactWithOnlineAccount2.onlineAccount.accountUri = contactWithOnlineAccount2.onlineAccount.accountUri; verifyNoSignalReceived(); } Contact { id: contactWithOrganization1 Organization { name: "old" } } function test_contact_organization_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithOrganization1); contactWithOrganization1.organization.name = "new"; verifySignalReceived(); } Contact { id: contactWithOrganization2 Organization { name: "old" } } function test_contact_organization_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithOrganization2); contactWithOrganization2.organization.name = contactWithOrganization2.organization.name; verifyNoSignalReceived(); } Contact { id: contactWithPhoneNumber1 PhoneNumber { number: "1234" } } function test_contact_phonenumber_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithPhoneNumber1); contactWithPhoneNumber1.phoneNumber.number = "5678"; verifySignalReceived(); } Contact { id: contactWithPhoneNumber2 PhoneNumber { number: "1234" } } function test_contact_phonenumber_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithPhoneNumber2); contactWithPhoneNumber2.phoneNumber.number = contactWithPhoneNumber2.phoneNumber.number; verifyNoSignalReceived(); } Contact { id: contactWithPresence1 Presence { state: Presence.Unknown } } function test_contact_presence_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithPresence1); contactWithPresence1.presence.state = Presence.Available; verifySignalReceived(); } Contact { id: contactWithPresence2 Presence { state: Presence.Unknown } } function test_contact_presence_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithPresence2); contactWithPresence2.presence.state = contactWithPresence2.presence.state; verifyNoSignalReceived(); } Contact { id: contactWithRingtone1 Ringtone { audioRingtoneUrl: "http://old" } } function test_contact_ringtone_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithRingtone1); contactWithRingtone1.ringtone.audioRingtoneUrl = "http://new"; verifySignalReceived(); } Contact { id: contactWithRingtone2 Ringtone { audioRingtoneUrl: "http://old" } } function test_contact_ringtone_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithRingtone2); contactWithRingtone2.ringtone.audioRingtoneUrl = contactWithRingtone2.ringtone.audioRingtoneUrl; verifyNoSignalReceived(); } Contact { id: contactWithSyncTarget1 SyncTarget { syncTarget: "old" } } function test_contact_synctarget_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithSyncTarget1); contactWithSyncTarget1.syncTarget.syncTarget = "new"; verifySignalReceived(); } Contact { id: contactWithSyncTarget2 SyncTarget { syncTarget: "old" } } function test_contact_synctarget_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithSyncTarget2); contactWithSyncTarget2.syncTarget.syncTarget = contactWithSyncTarget2.syncTarget.syncTarget; verifyNoSignalReceived(); } Contact { id: contactWithTag1 Tag { tag: "old" } } function test_contact_tag_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithTag1); contactWithTag1.tag.tag = "new"; verifySignalReceived(); } Contact { id: contactWithTag2 Tag { tag: "old" } } function test_contact_tag_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithTag2); contactWithTag2.tag.tag = contactWithTag2.tag.tag; verifyNoSignalReceived(); } Contact { id: contactWithTimestamp1 Timestamp { created: new Date(2012, 1, 1) } } function test_contact_timestamp_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithTimestamp1); contactWithTimestamp1.timestamp.created = new Date(2012, 1, 2); verifySignalReceived(); } Contact { id: contactWithTimestamp2 Timestamp { created: new Date(2012, 1, 1) } } function test_contact_timestamp_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithTimestamp2); contactWithTimestamp2.timestamp.created = contactWithTimestamp2.timestamp.created; verifyNoSignalReceived(); } Contact { id: contactWithUrl1 Url { url: "http://old" } } function test_contact_url_change_emits_signal() { listenToSignalFromObject("contactChanged", contactWithUrl1); contactWithUrl1.url.url = "http://new"; verifySignalReceived(); } Contact { id: contactWithUrl2 Url { url: "http://old" } } function test_contact_url_emits_no_signal_when_value_does_not_change() { listenToSignalFromObject("contactChanged", contactWithUrl2); contactWithUrl2.url.url = contactWithUrl2.url.url; verifyNoSignalReceived(); } property SignalSpy spy function init() { spy = Qt.createQmlObject("import QtTest 1.0;" + "SignalSpy {}", contactSignalsTests); } function listenToSignalFromObject(signalName, object) { spy.target = object; spy.signalName = signalName; spy.clear(); } function verifySignalReceived() { spy.wait(); } function verifyNoSignalReceived() { verify(spy.count == 0, "no signal was received"); } } tests/auto/contacts/qmlcontacts/testcases/tst_contact_urls.qml000066400000000000000000000164041233466112000254440ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 TestCase { name: "ContactsUrlsTests" TestCase { name: "ContactsUrlsTests::NoUrl" Contact { id: contactWithoutUrl } Contact { id: anotherContactWithoutUrl } Contact { id: yetAnotherContactWithoutUrl } function test_urlOfContactWithoutUrlIsDefined() { verifyIsDefined(contactWithoutUrl.url); } function test_urlOfContactWithoutUrlIsEmpty() { verifyIsUndefined(contactWithoutUrl.url.url); verifyIsUndefined(contactWithoutUrl.url.subType); } function test_urlsCountOfAnotherContactWithoutUrlIsZero() { // we do not access the url property of the contact, // so in this case the expected length is 0 compare(anotherContactWithoutUrl.urls.length, 0); } function test_urlsCountOfYetAnotherContactWithoutUrlIsOne() { // we first access the url property of the contact, // so in this case the expected length is 1 verifyIsDefined(yetAnotherContactWithoutUrl.url); compare(yetAnotherContactWithoutUrl.urls.length, 1); } } TestCase { name: "ContactsUrlsTests::OneUrl" Contact { id: contactWithOneUrl Url { url: "http://myUrl.com" } } function test_accessUrlThroughUrl() { compare(contactWithOneUrl.url.url, "http://myUrl.com"); } function test_UrlCount() { compare(contactWithOneUrl.urls.length, 1); } function test_accessUrlThroughUrls() { compare(contactWithOneUrl.urls[0].url, "http://myUrl.com"); } function test_accessUrlThroughDetails() { compare(contactWithOneUrl.details( ContactDetail.Url)[0].url, "http://myUrl.com"); } } TestCase { name: "ContactsUrlsTests::MultipleUrls" Contact { id: contactWithUrls Url { url: "http://myUrl.com" } Url { url: "http://myUrl2.com" } } function test_accessUrlThroughUrl() { compare(contactWithUrls.url.url, "http://myUrl.com"); } function test_urlCount() { compare(contactWithUrls.urls.length, 2); } function test_accessUrlThroughUrls() { compare(contactWithUrls.urls[0].url, "http://myUrl.com"); compare(contactWithUrls.urls[1].url, "http://myUrl2.com"); } function test_accessUrlThroughDetails() { compare(contactWithUrls.details( ContactDetail.Url)[0].url, "http://myUrl.com"); compare(contactWithUrls.details( ContactDetail.Url)[1].url, "http://myUrl2.com"); } } TestCase { name: "ContactsUrlsTests::DynamicallyAdded::MultipleUrls" Contact { id: contact1 } Url { id: url1 url: "http://myUrl.com" } Url { id: url2 url: "http://myUrl2.com" } function init() { contact1.clearDetails(); contact1.addDetail(url1); contact1.addDetail(url2); } function test_accessUrlThroughUrl() { compare(contact1.url.url, "http://myUrl.com"); } function test_urlCount() { verify(contact1.urls); compare(contact1.urls.length, 2); } function test_accessUrlThroughUrls() { compare(contact1.urls[0].url, "http://myUrl.com"); compare(contact1.urls[1].url, "http://myUrl2.com"); } function test_accessUrlThroughDetails() { compare(contact1.details( ContactDetail.Url)[0].url, "http://myUrl.com"); compare(contact1.details( ContactDetail.Url)[1].url, "http://myUrl2.com"); } function cleanup () { contact1.clearDetails(); } } TestCase { name: "ContactsUrlsTests::ModificationSignaling" Contact { id: contactWithUrl Url { url: "http://old" } } function test_changeToDetailThroughUrlsEmitsSignal() { listenToSignalFromObject("contactChanged", contactWithUrl); contactWithUrl.urls[0].url = "http://new"; verifySignalReceived(); } function init() { spy = Qt.createQmlObject("import QtTest 1.0;" + "SignalSpy {}", this); } } property SignalSpy spy function listenToSignalFromObject(signalName, object) { spy.target = object; spy.signalName = signalName; spy.clear(); } function verifySignalReceived() { spy.wait(); } function verifyNoSignalReceived() { verify(spy.count == 0, "no signal was received"); } function verifyIsUndefined(object) { verify(!object, "Object " + object + " is undefined"); } function verifyIsDefined(object) { verify(object, "Object " + object + " is defined"); } } tests/auto/contacts/qmlcontacts/testcases/tst_contactdetail.qml000066400000000000000000000413611233466112000255620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 TestCase { name: "ContactDetailsTests" Address { id: address } Anniversary { id: anniversary } Avatar { id: avatar } Birthday { id: birthday } ContactDetail { id: detailCommon } ContactDetail { id: detailContexts } DisplayLabel { id: displaylabel } EmailAddress { id: emailAddress } ExtendedDetail { id: extendedDetail; } Family { id: family } Favorite { id: favorite } Gender { id: gender } Location { id: location } GlobalPresence { id: globalpresence } Guid { id: guid } Hobby { id: hobby } Name { id: name } Nickname { id: nickname } Note { id: note } OnlineAccount { id: onlineAccount } Organization { id: organization } PhoneNumber { id: phonenumber } Presence { id: presence } Ringtone { id: ringtone } SyncTarget { id: synctarget } Tag { id: tag } Timestamp { id: timestamp } Type { id: type } Url { id: url } Version { id: version } property url myUrl: "http://nokia.com" property variant myEmptyVariant property variant myStringVariant: "String data in a variant" property variant myListVariant: [ 0, 1, 2, 3, 4, 5, 6, 7, "eight", "nine", "ten"] function test_address() { compare(address.street, "") address.street = "Kauppakatu" compare(address.street, "Kauppakatu") compare(address.locality, "") address.locality = "Tampere" compare(address.locality, "Tampere") compare(address.region, "") address.region = "Pirkanmaa" compare(address.region, "Pirkanmaa") compare(address.postcode, "") address.postcode = "33720" compare(address.postcode, "33720") compare(address.country, "") address.country = "Finland" compare(address.country, "Finland") compare(address.subTypes, []) address.subTypes = [Address.Postal] compare(address.subTypes, [Address.Postal]) compare(address.postOfficeBox, "") address.postOfficeBox = "510" compare(address.postOfficeBox, "510") } function test_anniversary() { compare(anniversary.calendarId, "") anniversary.calendarId = "myCalendarId" compare(anniversary.calendarId, "myCalendarId") //compare(anniversary.originalDate, "") var dateTime = new Date(2001, 5, 21) // 21 May 2001 14:13:09 anniversary.originalDate = dateTime compare(anniversary.originalDate, dateTime) compare(anniversary.event, "") anniversary.event = "myEvent" compare(anniversary.event, "myEvent") //compare(anniversary.subType, "") anniversary.subType = Anniversary.Wedding compare(anniversary.subType, Anniversary.Wedding) } function test_avatar() { compare(avatar.imageUrl, "") avatar.imageUrl = "http://nokia.com" compare(avatar.imageUrl.toString(), "http://nokia.com") compare(avatar.videoUrl, "") avatar.videoUrl = "http://nokia2.com" compare(avatar.videoUrl.toString(), "http://nokia2.com") } function test_birthday() { var dateTime = new Date(2001, 5, 21, 14, 13, 09) // 21 May 2001 14:13:09 //compare(birthday.birthday, "") birthday.birthday = dateTime; compare(birthday.birthday, dateTime) } function test_detail_common_properties() { compare(detailCommon.type, ContactDetail.Unknown, "type"); compare(detailCommon.detailUri, "", "detailUri"); compare(detailCommon.linkedDetailUris, [], "linkedDetailUris"); compare(detailCommon.fields, [], "fields"); compare(detailCommon.readOnly, false, "readOnly"); compare(detailCommon.removable, true, "removable"); } function test_detail_contexts() { compare(detailContexts.contexts, [], "contexts"); detailContexts.contexts = [ContactDetail.ContextHome]; compare(detailContexts.contexts, [ContactDetail.ContextHome], "context"); } function test_displayLabel() { compare(displaylabel.label, "") displaylabel.label = "exampleFullName"; compare(displaylabel.label, "exampleFullName") } function test_emailAddress() { compare(emailAddress.emailAddress, "") emailAddress.emailAddress = "ns@ovi.com"; compare(emailAddress.emailAddress, "ns@ovi.com") } function test_extendedDetail() { compare(extendedDetail.name, "", "Empty name") verify(!extendedDetail.data, "Empty data") extendedDetail.name = "MyDetail"; extendedDetail.data = "MyData"; compare(extendedDetail.name, "MyDetail", "Valid name (string data)") compare(extendedDetail.data, "MyData", "Valid data (string data)") extendedDetail.name = "MyNumber"; extendedDetail.data = 1; compare(extendedDetail.name, "MyNumber", "Valid name (int data)") compare(extendedDetail.data, 1, "Valid data (int data)") extendedDetail.name = "MyVariant"; extendedDetail.data = myStringVariant; compare(extendedDetail.name, "MyVariant", "Valid name (string variant data)") compare(extendedDetail.data, myStringVariant, "Valid data (string variant data)") extendedDetail.name = "MyListVariant"; extendedDetail.data = myListVariant; compare(extendedDetail.name, "MyListVariant", "Valid name (list variant data)") var actualData = extendedDetail.data; var expectedData = myListVariant; for (var j=0; j < expectedData.length;j++) { var actualItem = actualData[j]; var expectedItem = expectedData[j]; compare(actualItem, expectedItem, "Valid data (list variant), item #" + j) } } function test_family() { compare(family.spouse, "") family.spouse = "Kate"; compare(family.spouse, "Kate") compare(family.children, []) family.children = ["Mike"]; compare(family.children, ["Mike"]) } function test_favorite() { compare(favorite.favorite, false) favorite.favorite = true; compare(favorite.favorite, true) compare(favorite.index, 0) favorite.index = 1; compare(favorite.index, 1) } function test_gender() { compare(gender.gender, Gender.Female) gender.gender = Gender.Male; compare(gender.gender, Gender.Male) } function test_geolocation() { compare(location.label, "") location.label = "Finland"; compare(location.label, "Finland") compare(location.latitude, 0.0) location.latitude = 15.0; compare(location.latitude, 15.0) compare(location.longitude, 0.0) location.longitude = 20.0; compare(location.longitude, 20.0) compare(location.accuracy, 0.0) location.accuracy = 1.0; compare(location.accuracy, 1.0) compare(location.altitude, 0.0) location.altitude = 100.0; compare(location.altitude, 100.0) compare(location.altitudeAccuracy, 0.0) location.altitudeAccuracy = 2.0; compare(location.altitudeAccuracy, 2.0) compare(location.heading, 0.0) location.heading = 1.0; compare(location.heading, 1.0) compare(location.speed, 0.0) location.speed = 30.0; compare(location.speed, 30.0) var dateTime = new Date(2001, 5, 21, 14, 13, 09) // 21 May 2001 14:13:09 location.timestamp = dateTime; compare(location.timestamp, dateTime) } function test_globalpresence() { var dateTime = new Date(2001, 5, 21, 14, 13, 09) // 21 May 2001 14:13:09 globalpresence.timestamp = dateTime; compare(globalpresence.timestamp, dateTime) compare(globalpresence.nickname, "") globalpresence.nickname = "superman"; compare(globalpresence.nickname, "superman") compare(globalpresence.state, 0) globalpresence.state = Presence.Available; compare(globalpresence.state, Presence.Available); compare(globalpresence.stateText, "") globalpresence.stateText = "away..."; compare(globalpresence.stateText, "away...") compare(globalpresence.imageUrl, "") globalpresence.imageUrl = "http://nokia.com" compare(globalpresence.imageUrl.toString(), "http://nokia.com") compare(globalpresence.customMessage, "") globalpresence.customMessage = "Gone fishing"; compare(globalpresence.customMessage, "Gone fishing") } function test_guid() { compare(guid.guid, "") guid.guid = "myGuid"; compare(guid.guid, "myGuid") } function test_hobby() { compare(hobby.hobby, "") hobby.hobby = "biking"; compare(hobby.hobby, "biking") } function test_name() { compare(name.prefix, "") name.prefix = "Mr"; compare(name.prefix, "Mr") compare(name.firstName, "") name.firstName = "Paul"; compare(name.firstName, "Paul") compare(name.middleName, "") name.middleName = "Ron"; compare(name.middleName, "Ron") compare(name.lastName, "") name.lastName = "Walsh"; compare(name.lastName, "Walsh") compare(name.suffix, "") name.suffix = "First"; compare(name.suffix, "First") } function test_nickname() { compare(nickname.nickname, "") nickname.nickname = "piggy"; compare(nickname.nickname, "piggy") } function test_note() { compare(note.note, "") note.note = "see you"; compare(note.note, "see you") } function test_onlineaccount() { compare(onlineAccount.accountUri, "") onlineAccount.accountUri = "myUri"; compare(onlineAccount.accountUri, "myUri") compare(onlineAccount.serviceProvider, "") onlineAccount.serviceProvider = "myProvider"; compare(onlineAccount.serviceProvider, "myProvider") compare(onlineAccount.capabilities, []) onlineAccount.capabilities = ["myCapabilities"]; compare(onlineAccount.capabilities, ["myCapabilities"]) compare(onlineAccount.subTypes, []) onlineAccount.subTypes = [OnlineAccount.Sip]; compare(onlineAccount.subTypes, [OnlineAccount.Sip]) } function test_organization() { compare(organization.name, "") organization.name = "ACME"; compare(organization.name, "ACME") compare(organization.logoUrl, "") organization.logoUrl = "http://acme.com" compare(organization.logoUrl.toString(), "http://acme.com") compare(organization.department, []) organization.department = ["R&D"]; compare(organization.department, ["R&D"]) compare(organization.location, "") organization.location = "NY"; compare(organization.location, "NY") compare(organization.role, "") organization.role = "CEO"; compare(organization.role, "CEO") compare(organization.title, "") organization.title = "Dr"; compare(organization.title, "Dr") compare(organization.assistantName, "") organization.assistantName = "Pamela"; compare(organization.assistantName, "Pamela") } function test_phonenumber() { compare(phonenumber.number, "") phonenumber.number = "112"; compare(phonenumber.number , "112") compare(phonenumber.subTypes, []) phonenumber.subTypes = [PhoneNumber.Landline] compare(phonenumber.subTypes, [PhoneNumber.Landline]) } function test_presence() { var DateTime = new Date(2001, 5, 21) // 21 May 2001 presence.timestamp = DateTime; compare(presence.timestamp , DateTime) compare(presence.nickname, "") presence.nickname = "pearl"; compare(presence.nickname, "pearl") compare(presence.state, 0) presence.state = Presence.Available; compare(presence.state, Presence.Available) compare(presence.stateText, "") presence.stateText = "away..."; compare(presence.stateText, "away...") compare(presence.imageUrl, "") presence.imageUrl = "http://nokia.com" compare(presence.imageUrl.toString(), "http://nokia.com") compare(presence.customMessage, "") presence.customMessage = "Gone fishing"; compare(presence.customMessage, "Gone fishing") } function test_ringtone() { compare(ringtone.audioRingtoneUrl, "") ringtone.audioRingtoneUrl = "http://acme.com" compare(ringtone.audioRingtoneUrl.toString(), "http://acme.com") compare(ringtone.videoRingtoneUrl, "") ringtone.videoRingtoneUrl = "http://acme.com" compare(ringtone.videoRingtoneUrl.toString(), "http://acme.com") compare(ringtone.vibrationRingtoneUrl, "") ringtone.vibrationRingtoneUrl = "http://acme.com" compare(ringtone.vibrationRingtoneUrl.toString(), "http://acme.com") } function test_synctarget() { compare(synctarget.syncTarget, "") synctarget.syncTarget = "mySynctarget"; compare(synctarget.syncTarget, "mySynctarget") } function test_tag() { compare(tag.tag, "") tag.tag = "myTag"; compare(tag.tag, "myTag") } function test_timestamp() { var modifiedDateTime = new Date(2002, 5, 21) // 21 May 2001 timestamp.lastModified = modifiedDateTime; compare(timestamp.lastModified, modifiedDateTime) } function test_type() { compare(type.type, Type.Unspecified) type.type = Type.Group; compare(type.type, Type.Group) type.type = Type.Contact; compare(type.type, Type.Contact) } function test_url() { compare(url.url, "") url.url = "http://acme.com" compare(url.url.toString(), "http://acme.com") url.subType = Url.HomePage compare(url.subType, Url.HomePage) } function test_version() { compare(version.sequenceNumber, 0) compare(version.value(Version.SequenceNumber), undefined) version.sequenceNumber = 64 compare(version.sequenceNumber, 64) compare(version.value(Version.SequenceNumber), 64) compare(version.extendedVersion, "") compare(version.value(Version.ExtendedVersion), undefined) version.extendedVersion = "Qt rules" compare(version.extendedVersion, "Qt rules") compare(version.value(Version.ExtendedVersion), "Qt rules") } } tests/auto/contacts/qmlcontacts/testcases/tst_contactmodel_signals.qml000066400000000000000000000066661233466112000271510ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSignalingTestCase { name: "ContactModelSignalsTests" id: contactModelSignalsTests // This test case is a unit test for contact model // so it does not require updates to contacts. ContactModel { id: model autoUpdate: false } IdFilter { id: filter ids: [] } function test_settingTheFilterSendsSignal() { listenToSignalFromObject("filterChanged", model) model.filter = filter; verifySignalReceived(); } IdFilter { id: oldFilter ids: [] } IdFilter { id: newFilter ids: [] } function test_changingTheFilterSendsSignal() { model.filter = oldFilter; listenToSignalFromObject("filterChanged", model) model.filter = newFilter; verifySignalReceived(); } IdFilter { id: nonNullFilter ids: [] } function test_removingTheFilterSendsSignal() { model.filter = nonNullFilter; listenToSignalFromObject("filterChanged", model) model.filter = null; verifySignalReceived(); } IdFilter { id: theSameFilter ids: [] } function test_settingTheSameFilterDoesNotSendSignal() { model.filter = theSameFilter; listenToSignalFromObject("filterChanged", model) model.filter = theSameFilter; verifyNoSignalReceived(); } function init() { initSignalingTest(); } } tests/auto/contacts/qmlcontacts/testcases/tst_contactrelationship.qml000066400000000000000000000134241233466112000270200ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 TestCase { name: "ContactRelationship" Contact { id: contact1 Name { id: contact1Name firstName: "contact1" } } Contact { id: contact2 Name { id: contact2Name firstName: "contact2" } } function setAndVerifyRelationship(first, second, relationship) { relationship.first = first relationship.second = second compare(relationship.first.name.firstName, first.name.firstName) compare(relationship.second.name.firstName, second.name.firstName) } Relationship { id: relationshipHasMember type: Relationship.HasMember } Relationship { id: relationshipHasMemberTypeAsString type: "HasMember" } function test_hasMember() { compare(relationshipHasMember.type, "HasMember") compare(relationshipHasMemberTypeAsString.type, "HasMember") setAndVerifyRelationship(contact1, contact2, relationshipHasMember) setAndVerifyRelationship(contact1, contact2, relationshipHasMemberTypeAsString) } Relationship { id: relationshipAggregates type: Relationship.Aggregates } Relationship { id: relationshipAggregatesTypeAsString type: "Aggregates" } function test_aggregates() { compare(relationshipAggregates.type, "Aggregates") compare(relationshipAggregatesTypeAsString.type, "Aggregates") setAndVerifyRelationship(contact1, contact2, relationshipAggregates) } Relationship { id: relationshipIsSameAs type: Relationship.IsSameAs } Relationship { id: relationshipIsSameAsTypeAsString type: "IsSameAs" } function test_isSameAs() { compare(relationshipIsSameAs.type, "IsSameAs") compare(relationshipIsSameAsTypeAsString.type, "IsSameAs") setAndVerifyRelationship(contact1, contact2, relationshipIsSameAs) } Relationship { id: relationshipHasAssistant type: Relationship.HasAssistant } Relationship { id: relationshipHasAssistantTypeAsString type: "HasAssistant" } function test_hasAssistant() { compare(relationshipHasAssistant.type, "HasAssistant") compare(relationshipHasAssistantTypeAsString.type, "HasAssistant") setAndVerifyRelationship(contact1, contact2, relationshipHasAssistant) } Relationship { id: relationshipHasManager type: Relationship.HasManager } Relationship { id: relationshipHasManagerTypeAsString type: "HasManager" } function test_hasManager() { compare(relationshipHasManager.type, "HasManager") compare(relationshipHasManagerTypeAsString.type, "HasManager") setAndVerifyRelationship(contact1, contact2, relationshipHasManager) } Relationship { id: relationshipHasSpouse type: Relationship.HasSpouse } Relationship { id: relationshipHasSpouseTypeAsString type: "HasSpouse" } function test_hasSpouse() { compare(relationshipHasSpouse.type, "HasSpouse") compare(relationshipHasSpouseTypeAsString.type, "HasSpouse") setAndVerifyRelationship(contact1, contact2, relationshipHasSpouse) } Relationship { id: relationshipForNullContacts type: "HasSpouse" } function test_nullParticipants() { //just check we do not crash in these cases relationshipForNullContacts.first = null; relationshipForNullContacts.second = null; try { //next two lines will throw an exception, so we catch it) relationshipForNullContacts.second = undefined; relationshipForNullContacts.second = undefined; } catch(err) { } } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_clear_details_e2e.qml000066400000000000000000000120471233466112000301670ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSavingTestCase { name: "ContactsClearDetailsE2ETests" ContactModel { id: model manager: getManagerUnderTest() autoUpdate: true } // Tests Contact { id: contact1 Name { firstName: "1" } } function test_clear_details() { var contact = contact1; contact.clearDetails(); var detail = contact1.detail(ContactDetail.Name); verifyIsUndefined(detail); } Contact { id: contact2 Name { firstName: "2" } } function test_clear_details_of_contact_in_the_model() { var contact = contact2; model.saveContact(contact); waitForContactsChanged(); contact = model.contacts[0]; contact.clearDetails(); var detail = contact.detail(ContactDetail.Name); verifyIsUndefined(detail); } Contact { id: contact3 Name { firstName: "3" } } function test_clear_details_and_save_modified() { var contact = contact3; model.saveContact(contact); waitForContactsChanged(); contact = model.contacts[0]; contact.clearDetails(); contact.save(); waitForContactsChanged(); var detail = contact.detail(ContactDetail.Name); verifyIsUndefined(detail); } Contact { id: contact3_1 Name { firstName: "3_1" } } function test_clear_details_save_modified_and_refetch_from_model() { var contact = contact3_1; model.saveContact(contact); waitForContactsChanged(); contact = model.contacts[0]; contact.clearDetails(); contact.save(); waitForContactsChanged(); contact = model.contacts[0]; var detail = contact.detail(ContactDetail.Name); verifyIsUndefined(detail); } Contact { id: contact4 Name { firstName: "4" } } function test_clear_details_and_save_through_model() { var contact = contact4; model.saveContact(contact); waitForContactsChanged(); contact = model.contacts[0]; contact.clearDetails(); model.saveContact(contact); waitForContactsChanged(); contact = model.contacts[0]; var detail = contact.detail(ContactDetail.Name); verifyIsUndefined(detail); } // Init & teardown function initTestCase() { initTestForModel(model); waitForContactsChanged(); // The wait is needed so the model is populated // (e.g. with garbage left from previous test runs) // before cleanup() is called. emptyContacts(model); } function init() { initTestForModel(model); } function cleanup() { emptyContacts(model); } function cleanupTestCase() { emptyContacts(model); finishTestForModel(model); } // Helpers function verifyIsUndefined(object) { verify(!object, "Object " + object + " is undefined"); } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_details_saving_e2e.qml000066400000000000000000000663641233466112000304030ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSavingTestCase { name: "ContactsDetailsSavingE2ETests" id: contactsDetailsSavingE2ETests property Contact contact ContactModel { id: model manager: getManagerUnderTest() autoUpdate: true } // Tests Address { id: address } function test_address() { address.street = "Street" address.locality = "Locality" address.region = "Region" address.postcode = "Postcode" address.country = "Country" address.contexts = [ContactDetail.ContextHome] contact.addDetail(address) saveAndRefreshContact() var detail = contact.detail(ContactDetail.Address) compare(detail.street, "Street") compare(detail.locality, "Locality") compare(detail.region, "Region") compare(detail.postcode, "Postcode") compare(detail.country, "Country") compare(detail.contexts.length, 1, "contexts length") compare(detail.contexts[0], ContactDetail.ContextHome, "contexts") } Address { id: addressSanitizable } function test_addressCanBeSanitized() { addressSanitizable.street = " Street" addressSanitizable.locality = " Locality" addressSanitizable.region = " Region " addressSanitizable.postcode = " Postcode " addressSanitizable.country = " Country " contact.addDetail(addressSanitizable) saveAndRefreshContact() var detail = contact.detail(ContactDetail.Address) if (model.manager == "jsondb") { compare(detail.street, "Street") compare(detail.locality, "Locality") compare(detail.region, "Region") compare(detail.postcode, "Postcode") compare(detail.country, "Country") } else { // we do not expect other backends to sanitize the input by default compare(detail.street, " Street") compare(detail.locality, " Locality") compare(detail.region, " Region ") compare(detail.postcode, " Postcode ") compare(detail.country, " Country ") } } Address { id: addressNotSanitizable } function test_addressCanNotBeSanitized() { // If any of the address fields is longer than 50 chars it is invalid, thus the contact won't be saved addressNotSanitizable.street = " Street" addressNotSanitizable.locality = "" addressNotSanitizable.region = " Vvvvvvvvvveeeeeeeeeerrrrrrrrrryyyyyyyyyylongnameeeeeeeeee " addressNotSanitizable.postcode = " Postcode " addressNotSanitizable.country = " Country " contact.addDetail(addressNotSanitizable) if (model.manager == "jsondb") { // jsondb backend will remove blank spaces and other invalid characters // resulting this case into an invalid contact object. The model // should return here a BadArgument error. saveContactWithError("BadArgument") } else { saveAndRefreshContact() var detail = contact.detail(ContactDetail.Address) compare(detail.street, " Street") compare(detail.locality, "") compare(detail.region, " Vvvvvvvvvveeeeeeeeeerrrrrrrrrryyyyyyyyyylongnameeeeeeeeee ") compare(detail.postcode, " Postcode ") compare(detail.country, " Country ") } } Address { id: addresses1 street: "Street 1" } Address { id: addresses2 street: "Street 2" } function test_multipleAddresses() { contact.addDetail(addresses1) contact.addDetail(addresses2) saveAndRefreshContact() var details = contact.details(ContactDetail.Address) compare(details.length, 2, "details length"); if (details[0].street === addresses2.street) details.reverse(); // assuming detail order was not preserved compare(details[0].street, addresses1.street, "street 0") compare(details[1].street, addresses2.street, "street 1") } Avatar { id: avatar } function test_avatar() { avatar.imageUrl = "http://qt.nokia.com" contact.addDetail(avatar) saveAndRefreshContact() var detail = contact.detail(ContactDetail.Avatar) compare(detail.imageUrl, "http://qt.nokia.com") } Birthday { id: birthday } function test_birthday() { birthday.birthday = new Date(2012, 12, 01) contact.addDetail(birthday) saveAndRefreshContact() var detail = contact.detail(ContactDetail.Birthday) compare(new Date(detail.birthday), new Date(2012, 12, 01)) } DisplayLabel { id: displaylabel } function test_displaylabel() { displaylabel.label = "Dummy" contact.addDetail(displaylabel) saveAndRefreshContact() var detail = contact.detail(ContactDetail.DisplayLabel) compare(detail.label, "Dummy") } EmailAddress { id: emailaddress } function test_emailAddress() { emailaddress.emailAddress = "test@qt.nokia.com" emailaddress.contexts = [ContactDetail.ContextHome] contact.addDetail(emailaddress) saveAndRefreshContact() var detail = contact.detail(ContactDetail.Email) compare(detail.emailAddress, "test@qt.nokia.com") compare(detail.contexts.length, 1, "contexts length") compare(detail.contexts[0], ContactDetail.ContextHome, "contexts") } EmailAddress { id: emailaddressSanitizable } function test_test_emailAddressCanBeSanitized() { emailaddressSanitizable.emailAddress = " test@qt.nokia.com " emailaddressSanitizable.contexts = [ContactDetail.ContextHome] contact.addDetail(emailaddressSanitizable) saveAndRefreshContact() var detail = contact.detail(ContactDetail.Email) if (model.manager == "jsondb") { compare(detail.emailAddress, "test@qt.nokia.com") } else { // we do not expect other backends to sanitize the input by default compare(detail.emailAddress, " test@qt.nokia.com ") } } EmailAddress { id: emailaddressNotSanitizable } function test_emailAddressCanNotBeSanitized() { // If email address is longer than 126 chars it is invalid and won't be saved var veryLongEmailAddress for (var i = 0; i < 127; i++) { veryLongEmailAddress = "a" + veryLongEmailAddress } emailaddressNotSanitizable.emailAddress = veryLongEmailAddress emailaddressNotSanitizable.contexts = [ContactDetail.ContextHome] contact.addDetail(emailaddressNotSanitizable) if (model.manager == "jsondb") { saveContactWithError("BadArgument") } else { saveAndRefreshContact() var detail = contact.detail(ContactDetail.Email) compare(detail.emailAddress, veryLongEmailAddress) } } EmailAddress { id: emailAddresses1 emailAddress: "a@a" } EmailAddress { id: emailAddresses2 emailAddress: "b@b" } function test_multipleEmailAddresses() { contact.addDetail(emailAddresses1) contact.addDetail(emailAddresses2) saveAndRefreshContact() var details = contact.details(ContactDetail.Email) compare(details.length, 2, "details length"); if (details[0].emailAddress === emailAddresses2.emailAddress) details.reverse(); // assuming detail order was not preserved compare(details[0].emailAddress, emailAddresses1.emailAddress, "email address 0"); compare(details[1].emailAddress, emailAddresses2.emailAddress, "email address 1"); } Favorite { id: favorite } function test_favorite() { if (model.manager === "jsondb") skip("jsondb does not save favorite correctly"); favorite.favorite = true; favorite.index = 1; contact.addDetail(favorite); saveAndRefreshContact(); var detail = contact.detail(ContactDetail.Favorite); compare(detail.favorite, true); compare(detail.index, 1); } Gender { id: gender } function test_gender() { gender.gender = Gender.Male contact.addDetail(gender) saveAndRefreshContact() var detail = contact.detail(ContactDetail.Gender) compare(detail.gender, Gender.Male) } Guid { id: guid } function test_guid() { guid.guid = "TestGUID" contact.addDetail(guid) saveAndRefreshContact() var detail = contact.detail(ContactDetail.Guid) compare(detail.guid, "TestGUID") } Name { id: name } function test_name() { name.prefix = "Dr." name.firstName = "John" name.middleName = "B." name.lastName = "Doe" name.suffix = "Sr." name.contexts = [ContactDetail.ContextHome] contact.addDetail(name) saveAndRefreshContact() var detail = contact.detail(ContactDetail.Name) compare(detail.prefix, "Dr.") compare(detail.firstName, "John") compare(detail.middleName, "B.") compare(detail.lastName, "Doe") compare(detail.suffix, "Sr.") if (model.manager == 'jsondb') expectFail("", "contexts are not supported at the moment") compare(detail.contexts.length, 1, "contexts length") compare(detail.contexts[0], ContactDetail.ContextHome, "contexts") } Name { id: nameSanitizable } function test_nameCanBeSanitized() { nameSanitizable.prefix = " Mr. " nameSanitizable.firstName = " Matti-Tapio " nameSanitizable.middleName = "C. " nameSanitizable.lastName = " De Angelis" nameSanitizable.suffix = "Sr." contact.addDetail(nameSanitizable) saveAndRefreshContact() var detail = contact.detail(ContactDetail.Name) if (model.manager == "jsondb") { compare(detail.prefix, "Mr.") compare(detail.firstName, "Matti-Tapio") compare(detail.middleName, "C.") compare(detail.lastName, "De Angelis") compare(detail.suffix, "Sr.") } else { compare(detail.prefix, " Mr. ") compare(detail.firstName, " Matti-Tapio ") compare(detail.middleName, "C. ") compare(detail.lastName, " De Angelis") compare(detail.suffix, "Sr.") } } Name { id: nameNotSanitizable } function test_nameCanNotBeSanitized() { // If any of the name fields is longer than 50 chars it is invalid, thus the contact won't be saved nameNotSanitizable.prefix = "" nameNotSanitizable.firstName = "Matti-Tapio" nameNotSanitizable.middleName = "C. " nameNotSanitizable.lastName = "Vvvvvvvvvveeeeeeeeeerrrrrrrrrryyyyyyyyyylongnameeeeeeeeee" nameNotSanitizable.suffix = "Sr." contact.addDetail(nameNotSanitizable) if (model.manager == "jsondb") { saveContactWithError("BadArgument") } else { saveAndRefreshContact() var detail = contact.detail(ContactDetail.Name) compare(detail.prefix, "") compare(detail.firstName, "Matti-Tapio") compare(detail.middleName, "C. ") compare(detail.lastName, "Vvvvvvvvvveeeeeeeeeerrrrrrrrrryyyyyyyyyylongnameeeeeeeeee") compare(detail.suffix, "Sr.") } } Nickname { id: nickname } function test_nickname() { nickname.nickname = "Dummy" contact.addDetail(nickname) saveAndRefreshContact() var detail = contact.detail(ContactDetail.NickName) compare(detail.nickname, "Dummy") } Nickname { id: nicknameSanitizable } function test_nicknameCanBeSanitized() { nicknameSanitizable.nickname = " Dummy " contact.addDetail(nicknameSanitizable) saveAndRefreshContact() var detail = contact.detail(ContactDetail.NickName) if (model.manager == "jsondb") { compare(detail.nickname, "Dummy") } else { compare(detail.nickname, " Dummy ") } } Nickname { id: nicknameNotSanitizable } function test_nicknameCanNotBeSanitized() { // If nickname is longer than 50 chars it is invalid and won't be saved nicknameNotSanitizable.nickname = "Vvvvvvvvvveeeeeeeeeerrrrrrrrrryyyyyyyyyylongnameeeeeeeeee" contact.addDetail(nicknameNotSanitizable) if (model.manager == "jsondb") { saveContactWithError("BadArgument") } else { saveAndRefreshContact() var detail = contact.detail(ContactDetail.NickName) compare(detail.nickname, "Vvvvvvvvvveeeeeeeeeerrrrrrrrrryyyyyyyyyylongnameeeeeeeeee") } } Note { id: note } function test_note() { note.note = "Dummy" contact.addDetail(note) saveAndRefreshContact() var detail = contact.detail(ContactDetail.Note) compare(detail.note, "Dummy") } Note { id: noteSanitizable } function test_noteCanBeSanitized() { noteSanitizable.note = " Dummy " contact.addDetail(noteSanitizable) saveAndRefreshContact() var detail = contact.detail(ContactDetail.Note) if (model.manager == "jsondb") { compare(detail.note, "Dummy") } else { compare(detail.note, " Dummy ") } } Note { id: noteNotSanitizable } function test_noteCanNotBeSanitized() { // If the note is longer than 1000 chars it is invalid and won't be saved var veryLongNote for (var i = 0; i < 10001; i++) { veryLongNote = "a" + veryLongNote } noteNotSanitizable.note = veryLongNote contact.addDetail(noteNotSanitizable) if (model.manager == "jsondb") { saveContactWithError("BadArgument") } else { saveAndRefreshContact() var detail = contact.detail(ContactDetail.Note) compare(detail.note, veryLongNote) } } OnlineAccount { id: onlineaccount } function test_onlineAccount() { onlineaccount.accountUri = "Account URI"; contact.addDetail(onlineaccount); expectFail("", "Saving of Account URI is not supported at the moment"); verify(false); saveAndRefreshContact(); var detail = contact.detail(ContactDetail.OnlineAccount); expectFail("", "This is not supported at the moment"); verify(detail); compare(detail.accountUri, "Account URI"); } Organization { id: organization } function test_organization() { organization.name = "Name" organization.logoUrl = "http://qt.nokia.com" organization.department = "Department" organization.location = "Location" organization.role = "Role" organization.title = "Title" organization.assistantName = "Assistant name" organization.contexts = ["Home"] contact.addDetail(organization) saveAndRefreshContact() var detail = contact.detail(ContactDetail.Organization) compare(detail.name, "Name") compare(detail.logoUrl, "http://qt.nokia.com") compare(detail.department[0], "Department") if (model.manager == 'jsondb') expectFail("", "TODO: location is not working as expected at the moment"); compare(detail.location, "Location") if (model.manager == 'jsondb') expectFail("", "TODO: role is not working as expected at the moment"); compare(detail.role, "Role") compare(detail.title, "Title") compare(detail.assistantName, "Assistant name") if (model.manager == 'jsondb') expectFail("", "TODO: contexts are not working as expected at the moment"); compare(detail.contexts.length, 1, "contexts length") compare(detail.contexts[0], ContactDetail.ContextHome, "contexts") } Organization { id: organizationSanitizable } function test_organizationCanBeSanitized() { organizationSanitizable.name = " Company Name " contact.addDetail(organizationSanitizable) saveAndRefreshContact() var detail = contact.detail(ContactDetail.Organization) if (model.manager == "jsondb") { compare(detail.name, "Company Name") } else { compare(detail.name, " Company Name ") } } Organization { id: organizationNotSanitizable } function test_organizationCanNotBeSanitized() { // If any of the organization fields is longer than 50 chars it is invalid, thus the contact won't be saved organizationNotSanitizable.name = "Vvvvvvvvvveeeeeeeeeerrrrrrrrrryyyyyyyyyylongnameeeeeeeeee" contact.addDetail(organizationNotSanitizable) if (model.manager == "jsondb") { saveContactWithError("BadArgument") } else { saveAndRefreshContact() var detail = contact.detail(ContactDetail.Organization) compare(detail.name, "Vvvvvvvvvveeeeeeeeeerrrrrrrrrryyyyyyyyyylongnameeeeeeeeee") } } Organization { id: organizations1 name: "A" } Organization { id: organizations2 name: "B" } function test_multipleOrganizations() { contact.addDetail(organizations1); contact.addDetail(organizations2); saveAndRefreshContact(); var details = contact.details(ContactDetail.Organization); compare(details.length, 2, "details length"); if (details[0].name === organizations2.name) details.reverse(); // assuming detail order was not preserved compare(details[0].name, organizations1.name, "name 0"); compare(details[1].name, organizations2.name, "name 1"); } PhoneNumber { id: phonenumber } function test_phoneNumber() { phonenumber.subTypes = [ PhoneNumber.Landline, PhoneNumber.Mobile, PhoneNumber.Fax, PhoneNumber.Video, ] phonenumber.number = "1" phonenumber.contexts = [ContactDetail.ContextHome] contact.addDetail(phonenumber) saveAndRefreshContact() var detail = contact.detail(ContactDetail.PhoneNumber) compare(detail.number, "1") compare(detail.contexts.length, 1, "contexts length") compare(detail.contexts[0], ContactDetail.ContextHome, "contexts") if (model.manager == 'jsondb') { compare(detail.subTypes.length, 1, "jsondb phone number subtypes length") compare(detail.subTypes[0], phonenumber.subTypes[0]) } else { compare(detail.subTypes.length, phonenumber.subTypes.length) } } PhoneNumber { id: phonenumberOneSubtype } function test_phoneNumberOneSubtype() { phonenumberOneSubtype.subTypes = [ PhoneNumber.Mobile ] phonenumberOneSubtype.number = "1" contact.addDetail(phonenumberOneSubtype) saveAndRefreshContact() var detail = contact.detail(ContactDetail.PhoneNumber) compare(detail.number, "1") compare(detail.subTypes.length, phonenumberOneSubtype.subTypes.length) } PhoneNumber { id: phonenumberSanitizable } function test_phoneNumberCanBeSanitized() { phonenumberSanitizable.subTypes = [ PhoneNumber.Mobile ] phonenumberSanitizable.number = "+1 +(234)-56789abcdef*# " contact.addDetail(phonenumberSanitizable) saveAndRefreshContact() var detail = contact.detail(ContactDetail.PhoneNumber) if (model.manager == "jsondb") // jsondb backend will remove blank spaces and other invalid characters compare(detail.number, "+1(234)-56789abcd*#") else // we do not expect other backends to sanitize the input by default compare(detail.number, "+1 +(234)-56789abcdef*# ") compare(detail.subTypes.length, phonenumberSanitizable.subTypes.length) } PhoneNumber { id: phonenumberNotSanitizable } function test_phoneNumberCannotBeSanitized() { phonenumberNotSanitizable.subTypes = [ PhoneNumber.Mobile ] phonenumberNotSanitizable.number = " efghijk []/>>>>>" contact.addDetail(phonenumberNotSanitizable) var detail if (model.manager == "jsondb") { saveContactWithError("BadArgument") } else { saveAndRefreshContact() detail = contact.detail(ContactDetail.PhoneNumber) compare(detail.number, " efghijk []/>>>>>") compare(detail.subTypes.length, phonenumberNotSanitizable.subTypes.length) } } PhoneNumber { id: phonenumbers1 number: "1" } PhoneNumber { id: phonenumbers2 number: "2" } function test_multiplePhoneNumbers() { contact.addDetail(phonenumbers1); contact.addDetail(phonenumbers2); saveAndRefreshContact(); var details = contact.details(ContactDetail.PhoneNumber); compare(details.length, 2, "details length"); if (details[0].number === phonenumbers2.number) details.reverse(); // assuming detail order was not preserved compare(details[0].number, phonenumbers1.number, "number 0"); compare(details[1].number, phonenumbers2.number, "number 1"); } Ringtone { id: ringtone } function test_ringtone() { ringtone.audioRingtoneUrl = "http://qt.nokia.com" contact.addDetail(ringtone) saveAndRefreshContact() var detail = contact.detail(ContactDetail.Ringtone) compare(detail.audioRingtoneUrl, "http://qt.nokia.com") } SyncTarget { id: synctarget } function test_synctarget() { synctarget.syncTarget = "TestSyncTarget" contact.addDetail(synctarget) saveAndRefreshContact() var detail = contact.detail(ContactDetail.SyncTarget) compare(detail.syncTarget, "TestSyncTarget") } Url { id: url } function test_url() { url.url = "http://qt.nokia.com" url.contexts = [ContactDetail.ContextHome] contact.addDetail(url) saveAndRefreshContact() var detail = contact.detail(ContactDetail.Url) compare(detail.url, "http://qt.nokia.com") compare(detail.contexts.length, 1, "contexts length") compare(detail.contexts[0], ContactDetail.ContextHome, "contexts") } Url { id: urls1 url: "http://a" } Url { id: urls2 url: "http://b" } function test_multipleUrls() { contact.addDetail(urls1); contact.addDetail(urls2); saveAndRefreshContact(); var details = contact.details(ContactDetail.Url); compare(details.length, 2, "details length"); if (details[0].url === urls2.url) details.reverse(); // assuming detail order was not preserved compare(details[0].url, urls1.url, "url 0"); compare(details[1].url, urls2.url, "url 1"); } Version { id: version } function test_version() { version.sequenceNumber = 64 version.extendedVersion = "Qt rules" contact.addDetail(version) saveAndRefreshContact() var detail = contact.detail(ContactDetail.Version) verify(detail.sequenceNumber != undefined) if (model.manager == "jsondb") { compare(detail.sequenceNumber, 1, "sequenceNumber is always 1 for jsondb") compare(detail.extendedVersion.length, 10, "jsondb extended version field is 10 characters long") } } // Init & teardown function initTestCase() { initTestForModel(model); waitForContactsChanged(); // The wait is needed so the model is populated // (e.g. with garbage left from previous test runs) // before cleanup() is called. emptyContacts(model); } function init() { initTestForModel(model); contact = Qt.createQmlObject( "import QtContacts 5.0;" + "Contact {}", contactsDetailsSavingE2ETests) } function cleanup() { emptyContacts(model); } function cleanupTestCase() { emptyContacts(model); finishTestForModel(model); } function saveAndRefreshContact() { model.saveContact(contact) waitForContactsChanged(); compare(model.contacts.length, 1, "Unexpected amount of contacts in model after updating contact.") contact = model.contacts[0] } function saveContactWithError(errorMessage) { var errorSpy = initTestForTargetListeningToSignal(model, "errorChanged"); model.saveContact(contact); waitForTargetSignal(errorSpy); verifyNoContactsChangedReceived(); compare(model.contacts.length, 0, "Model should be empty after saving an invalid contact") compare(model.error, errorMessage, "At this point we should get this error \"" + errorMessage + "\" from the model.") } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_e2e.qml000066400000000000000000000175771233466112000253310ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 Rectangle { property bool start: false property SignalSpy contactsChangedSpy property ContactModel contactModel property Contact testContact TestCase { name: "ContactsTests" function waitForContactsChanged (expectedCount) { contactsChangedSpy.wait(); compare (contactsChangedSpy.count, expectedCount) } function test_addAndRemoveDetails() { //TODO verify this test case when we remove dynamic properties from the Api //currently removing details from the contact is not updated in contact model //TODO add a test case to remove the same detail N times and remove a detail which doesnt exist etc... var model = Qt.createQmlObject( "import QtContacts 5.0;" + "ContactModel {id:model;autoUpdate:true;onContactsChanged:{console.log(\"CONTACTS CHANGED!\")}}", testHelper); var tmp = Qt.createQmlObject( "import QtContacts 5.0;" + "Contact {Name{}}", testHelper); var spy2 = Qt.createQmlObject("import QtTest 1.0;" +"SignalSpy {id: theSpy;signalName: \"contactsChanged\";}", testHelper); contactsChangedSpy = spy2; contactsChangedSpy.target = model; contactsChangedSpy.clear() testHelper.model = model; waitForContactsChanged (1) testHelper.emptyContactsDb(); testContact = tmp; var phone = Qt.createQmlObject("import QtContacts 5.0;" + "PhoneNumber {number: '99999999'}", testContact); var nick = Qt.createQmlObject("import QtContacts 5.0;" + "Nickname {nickname: 'jack'}", testContact); var label = Qt.createQmlObject("import QtContacts 5.0;" + "DisplayLabel {label: 'Alice In Wonderland'}", testContact); var mail = Qt.createQmlObject("import QtContacts 5.0;" + "EmailAddress {emailAddress: 'joe.john@ovi.com'}", testContact); testContact.name.firstName = "Joe" testContact.name.lastName = "John" testContact.addDetail(phone) testContact.addDetail(nick) testContact.addDetail(label) testContact.addDetail(mail) contactsChangedSpy.clear() model.saveContact(testContact) waitForContactsChanged (1) testContact = model.contacts[0] compare(testContact.phoneNumber.number,"99999999") compare(testContact.nickname.nickname,"jack") compare(testContact.displayLabel.label,"Alice In Wonderland") compare(testContact.email.emailAddress,"joe.john@ovi.com") testContact.removeDetail(testContact.detail(ContactDetail.NickName)); testContact.removeDetail(testContact.detail(ContactDetail.DisplayLabel)); testContact.removeDetail(testContact.detail(ContactDetail.PhoneNumber)); testContact.removeDetail(testContact.detail(ContactDetail.Email)); contactsChangedSpy.clear() model.saveContact(testContact) waitForContactsChanged (1) testContact = model.contacts[0] verify(!testContact.detail(ContactDetail.NickName)) verify(!testContact.detail(ContactDetail.DisplayLabel)) verify(!testContact.detail(ContactDetail.PhoneNumber)) verify(!testContact.detail(ContactDetail.Email)) compare(model.contacts.length, 1) testHelper.emptyContactsDb(); model.autoUpdate = false; } property Component component property ContactsTestHelper testHelper function init() { component = Qt.createComponent("ContactsTestHelper.qml"); testHelper = component.createObject(top); if (testHelper == undefined) console.log("Unable to load component from " + name + " error is ", component.errorString()) verify(testHelper != undefined, 'Unable to load component ' + name); } function cleanup() { testHelper.destroy(); component.destroy(); } Contact { id: contact } function test_contact() { // empty Contact compare(contact.modified, false) // access Type console.log("type is: " + contact.type) compare(contact.type, Type.Contact) // Type is Contact and not Group // access contactId console.log("contactId is: " + JSON.stringify(contact.contactId) ) compare(contact.contactId, "qtcontacts:::") // access manager console.log("manager is: " + JSON.stringify(contact.manager)) compare(contact.manager, "") } Contact { id: contact2 } PhoneNumber { id: phoneNumber number: "99999999" } function test_contactAddDetail() { // add new Detail contact2.addDetail(phoneNumber) compare(contact2.phoneNumber.number, "99999999") } Contact { id: contact3 PhoneNumber { id: phoneNumber3 number: "99999999" } } function test_contactUpdateDetail() { // update existing Detail contact3.phoneNumber.number = "88888" compare(contact3.phoneNumber.number, "88888") } Contact { id: contact4 PhoneNumber { id: phoneNumber4 number: "99999999" } } function test_contactRemoveDetail() { // delete one existing Detail contact4.removeDetail(phoneNumber4); verify(!contact4.phoneNumber.number) } } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_export_import_details_e2e.qml000066400000000000000000000551111233466112000320130ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSavingTestCase { name: "ContactsExportImportDetailsE2ETests" ContactModel { id: model manager: "memory" autoUpdate: true } Contact { id: megaContact Address { street: "Street" locality: "Locality" region: "Region" postcode: "12345" country: "Country" contexts: [ContactDetail.ContextHome] } Address { street: "Street2" locality: "Locality2" region: "Region2" postcode: "123452" country: "Country2" contexts: [ContactDetail.ContextHome, ContactDetail.ContextWork] } Anniversary { calendarId: "Calendar id" originalDate: new Date("2012-01-01") event: "Event" subType: Anniversary.Employment } Avatar { imageUrl: "http://nokia.com/imageurl" } Birthday { birthday: new Date(2000, 1, 1, 12, 0, 0) } /* Display label is currently read-only DisplayLabel { label: "Display label" } */ EmailAddress { emailAddress: "emailaddress@ovi.com.invalid" contexts: [ContactDetail.ContextWork] } EmailAddress { emailAddress: "emailaddress2@ovi.com.invalid" contexts: [ContactDetail.ContextHome, ContactDetail.ContextWork] } ExtendedDetail { name: "Extended detail: string data" data: "string 1" } ExtendedDetail { name: "Extended detail: number data" data: 2 } ExtendedDetail { name: "Extended detail: date data" data: new Date(2000, 0, 2, 12, 13, 14) } ExtendedDetail { name: "Extended detail: array data" data: ["string 1", "string 2"] } ExtendedDetail { name: "Extended detail: object data" data: {"key 1": "string 1", "key 2": "string 2"} } Family { spouse: "Spouse" children: ["Kid A", "Kid B"] } Favorite { favorite: true index: 1 } Gender { gender: Gender.Male } Guid { guid: "Guid" } GlobalPresence { timestamp: new Date(2012, 1, 1, 12, 0, 0) nickname: "Nickname" stateText: "State text" imageUrl: "http://qt.nokia.com/url" customMessage: "Custom message" state: Presence.Away } Hobby { hobby: "Hobby" } Location { latitude: 1.1 longitude: 1.2 label: "Location" accuracy: 1 altitude: 1 altitudeAccuracy: 1 heading: 1 speed: 1 timestamp: new Date(2012, 1, 1, 12, 0, 0) } Name { prefix: "Prefix" firstName: "First" middleName: "Middle" lastName: "Last" suffix: "Suffix" } Nickname { nickname: "Nick" } Note { note: "Note" } OnlineAccount { accountUri: "http://qt.nokia.com/url" serviceProvider: "Service provider" capabilities: ["Capability"] subTypes: [OnlineAccount.Sip] } Organization { name: "Organization" logoUrl: "http://qt.nokia.com/logourl" department: "Department" location: "Location" title: "Title" role: "Role" assistantName: "Assistant" } Organization { name: "Organization2" logoUrl: "http://qt.nokia.com/logourl2" department: "Department2" location: "Location2" title: "Title2" role: "Role2" assistantName: "Assistant2" } PhoneNumber { subTypes: [ PhoneNumber.Landline, PhoneNumber.Mobile, PhoneNumber.Fax, PhoneNumber.Video, ] number: "12345" contexts: [ContactDetail.ContextHome] } PhoneNumber { subTypes: [ PhoneNumber.Landline, PhoneNumber.Mobile, PhoneNumber.Fax, PhoneNumber.Video, ] number: "54321" contexts: [ContactDetail.ContextHome, ContactDetail.ContextWork] } Presence { timestamp: new Date() nickname: "Nickname" stateText: "State text" imageUrl: "http://qt.nokia.com/url" customMessage: "Custom message" state: Presence.Away } Ringtone { audioRingtoneUrl: "http://qt.nokia.com/audioringtoneurl" } Tag { tag: "Tag" } Timestamp { created: new Date(2012, 1, 1, 12, 0, 0) lastModified: new Date(2012, 1, 1, 12, 0, 0) } Url { url: "http://qt.nokia.com/url" contexts: [ContactDetail.ContextWork] } Url { url: "http://qt.nokia.com/url2" contexts: [ContactDetail.ContextHome, ContactDetail.ContextWork] } Version { sequenceNumber: 1 extendedVersion: "Extended version" } } // Base for vcard file names. property string vcardFileNameBase: 'tst_contacts_export_import_details_e2e' property Contact importedContact // Init & teardown function initTestCase() { initTestForModel(model); waitForContactsChanged(); var signalSpy = initTestForTargetListeningToSignal(model, "exportCompleted"); // Save and fetch test contact. model.saveContact(megaContact); waitForContactsChanged(); // Export contacts to vcard file. var vcardFilePath = Qt.resolvedUrl(vcardFileNameBase + ".vcard"); model.exportContacts(vcardFilePath, ["Sync"]); waitForTargetSignal(signalSpy); // Empty all contacts and import just created vcard file. emptyContacts(model); listenToContactsChanged(); model.importContacts(vcardFilePath, ["Sync"]); waitForContactsChanged(); compare (model.error, "NoError", "importError"); // Check that imported contact has correct data. compare (model.contacts.length, 1, "import count"); importedContact = model.contacts[0]; } function cleanupTestCase() { emptyContacts(model); finishTestForModel(model); } // Tests function test_exportImportAddress() { compare(importedContact.addresses.length, 2, "Error importing/exporting multiple addresses"); var importedAddresses = importedContact.details(ContactDetail.Address); if (importedAddresses[0].street === megaContact.addresses[1].street) importedAddresses.reverse(); for (var i = 0; i < 2; i++) { compare(importedAddresses[i].street, megaContact.addresses[i].street, "street"); compare(importedAddresses[i].locality, megaContact.addresses[i].locality, "locality"); compare(importedAddresses[i].region, megaContact.addresses[i].region, "region"); compare(importedAddresses[i].postcode, megaContact.addresses[i].postcode, "postcode"); compare(importedAddresses[i].country, megaContact.addresses[i].country, "country"); verify(importedAddresses[i].contexts.length, "Contexts not imported/exported"); compareSets(importedAddresses[i].contexts, megaContact.addresses[i].contexts); } } function test_exportImportAnniversary() { compare(importedContact.anniversary.originalDate, megaContact.anniversary.originalDate, "anniversary"); expectFail("", "Anniversary calendar id is not exported at the moment.") compare(importedContact.anniversary.calendarId, megaContact.anniversary.calendarId, "calendar id"); expectFail("", "Anniversary event is not exported at the moment.") compare(importedContact.anniversary.event, megaContact.anniversary.event, "event"); expectFail("", "Anniversary subtype is not exported at the moment.") compare(importedContact.anniversary.subType, megaContact.anniversary.subType, "subtypes"); } function test_exportImportAvatar() { compare(importedContact.avatar.imageUrl, megaContact.avatar.imageUrl, "image url"); } function test_exportImportBirthday() { compare(importedContact.birthday.birthday, megaContact.birthday.birthday, "birthday"); } function test_exportImportEmail() { compare(importedContact.emails.length, 2, "Error importing/exporting multiple email addresses"); var importedEmails = importedContact.details(ContactDetail.Email); if (importedEmails[0].emailAddress === megaContact.emails[1].emailAddress) importedEmails.reverse(); for (var i = 0; i < 2; i++) { compare(importedEmails[i].emailAddress, megaContact.emails[i].emailAddress, "email address"); compareSets(importedEmails[i].contexts, megaContact.emails[i].contexts); } } function test_exportImportExtendedDetail() { compare(importedContact.extendedDetails.length, 5, "Error importing/exporting multiple extended details"); var importedExtendedDetails = importedContact.details(ContactDetail.ExtendedDetail); for (var i = 0; i < 4; i++) { compare(importedExtendedDetails[i].name, megaContact.extendedDetails[i].name, "extended detail name"); if (importedExtendedDetails[i].name === "Extended detail: date data") { // A date is exported as a string in local time without timezone specified. // QML/V8 javascript Date.parse cannot be used to parse it, // since it interprets a date string without timezone as using UTC. compare(importedExtendedDetails[i].data, Qt.formatDateTime(megaContact.extendedDetails[i].data, 'yyyy-MM-ddThh:mm:ss'), "extended detail data"); } else { compare(importedExtendedDetails[i].data, megaContact.extendedDetails[i].data, "extended detail data"); } } } function test_exportImportFamily() { compare(importedContact.family.spouse, megaContact.family.spouse, "spouse"); compareSets(importedContact.family.children, megaContact.family.children, "children"); } function test_exportImportFavorite() { compare(importedContact.favorite.favorite, megaContact.favorite.favorite, "favorite"); compare(importedContact.favorite.index, megaContact.favorite.index, "index"); } function test_exportImportGender() { compare(importedContact.gender.gender, megaContact.gender.gender, "gender"); } function test_exportImportGlobalPresence() { expectFail("", "Global presence is not exported at the moment."); compare(importedContact.globalPresence.timestamp, megaContact.globalPresence.timestamp, "timestamp"); expectFail("", "Global presence is not exported at the moment."); compare(importedContact.globalPresence.nickname, megaContact.globalPresence.nickname, "nickname"); expectFail("", "Global presence is not exported at the moment."); compare(importedContact.globalPresence.stateText, megaContact.globalPresence.stateText, "state text"); expectFail("", "Global presence is not exported at the moment."); compare(importedContact.globalPresence.imageUrl, megaContact.globalPresence.imageUrl, "image url"); expectFail("", "Global presence is not exported at the moment."); compare(importedContact.globalPresence.customMessage, megaContact.globalPresence.customMessage, "custom message"); expectFail("", "Global presence is not exported at the moment."); compare(importedContact.globalPresence.state, megaContact.globalPresence.state, "state"); } function test_exportImportGuid() { compare(importedContact.guid.guid, megaContact.guid.guid, "guid"); } function test_exportImportHobby() { expectFail("", "Hobby is not exported at the moment."); compare(importedContact.hobby.hobby, megaContact.hobby.hobby, "hobby"); } function test_exportImportLocation() { compare(importedContact.geolocation.latitude, megaContact.geolocation.latitude, "latitude"); compare(importedContact.geolocation.longitude, megaContact.geolocation.longitude, "longitude"); expectFail("", "Location label is not exported at the moment."); compare(importedContact.geolocation.label, megaContact.geolocation.label, "label"); expectFail("", "Location accuracy is not exported at the moment."); compare(importedContact.geolocation.accuracy, megaContact.geolocation.accuracy, "accuracy"); expectFail("", "Location altitude is not exported at the moment."); compare(importedContact.geolocation.altitude, megaContact.geolocation.altitude, "altitude"); expectFail("", "Location altitude accuracy is not exported at the moment."); compare(importedContact.geolocation.altitudeAccuracy, megaContact.geolocation.altitudeAccuracy, "altitudeAccuracy"); expectFail("", "Location heading is not exported at the moment."); compare(importedContact.geolocation.heading, megaContact.geolocation.heading, "heading"); expectFail("", "Location speed is not exported at the moment."); compare(importedContact.geolocation.speed, megaContact.geolocation.speed, "speed"); expectFail("", "Location timestamp is not exported at the moment."); compare(importedContact.geolocation.timestamp, megaContact.geolocation.timestamp, "timestamp"); } function test_exportImportName() { compare(importedContact.name.firstName, megaContact.name.firstName, "first name"); compare(importedContact.name.lastName, megaContact.name.lastName, "last name"); compare(importedContact.name.middleName, megaContact.name.middleName, "middle name"); compare(importedContact.name.prefix, megaContact.name.prefix, "prefix"); compare(importedContact.name.suffix, megaContact.name.suffix, "suffix"); } function test_exportImportNickname() { compare(importedContact.nickname.nickname, megaContact.nickname.nickname, "nickname"); } function test_exportImportNote() { compare(importedContact.note.note, megaContact.note.note, "note"); } function test_exportImportOnlineAccount() { compare(importedContact.onlineAccount.accountUri, megaContact.onlineAccount.accountUri, "account uri"); expectFail("", "Service provider is not exported at the moment."); compare(importedContact.onlineAccount.serviceProvider, megaContact.onlineAccount.serviceProvider, "service provider"); expectFail("", "Capabilities are not exported at the moment."); compare(importedContact.onlineAccount.capabilities[0], megaContact.onlineAccount.capabilities[0], "capabilities"); expectFail("", "Subtypes are not exported at the moment."); compare(importedContact.onlineAccount.subTypes[0], megaContact.onlineAccount.subTypes[0], "subtypes"); } function test_exportImportOrganization() { compare(importedContact.organizations.length, 2, "Error importing/exporting multiple organizations"); var importedOrganizations = importedContact.details(ContactDetail.Organization); if (importedOrganizations[0].name === megaContact.organizations[1].name) importedOrganizations.reverse(); for (var i = 0; i < 2; i++) { compare(importedOrganizations[i].name, megaContact.organizations[i].name, "name"); compare(importedOrganizations[i].logoUrl, megaContact.organizations[i].logoUrl, "logo url"); compare(importedOrganizations[i].department, megaContact.organizations[i].department, "department"); compare(importedOrganizations[i].title, megaContact.organizations[i].title, "title"); compare(importedOrganizations[i].role, megaContact.organizations[i].role, "role"); compare(importedOrganizations[i].assistantName, megaContact.organizations[i].assistantName, "assistant name"); expectFail("", "Organization location is not exported at the moment."); compare(importedOrganizations[i].location, megaContact.organizations[i].location, "location"); } } function test_exportImportPhoneNumber() { compare(importedContact.phoneNumbers.length, 2, "Error importing/exporting multiple phone numbers"); var importedPhoneNumbers = importedContact.details(ContactDetail.PhoneNumber); if (importedPhoneNumbers[0].number === megaContact.phoneNumbers[1].number) importedPhoneNumbers.reverse(); for (var i = 0; i < 2; i++) { compare(importedPhoneNumbers[i].number, megaContact.phoneNumbers[i].number, "phone number"); compareSets(importedPhoneNumbers[i].contexts, megaContact.phoneNumbers[i].contexts); compareSets(importedPhoneNumbers[i].subTypes, megaContact.phoneNumbers[i].subTypes); } } function test_exportImportPresence() { expectFail("", "Presence is not exported at the moment."); compare(importedContact.presence.timestamp, megaContact.presence.timestamp, "timestamp"); expectFail("", "Presence is not exported at the moment."); compare(importedContact.presence.nickname, megaContact.presence.nickname, "nickname"); expectFail("", "Presence is not exported at the moment."); compare(importedContact.presence.stateText, megaContact.presence.stateText, "state text"); expectFail("", "Presence is not exported at the moment."); compare(importedContact.presence.imageUrl, megaContact.presence.imageUrl, "image url"); expectFail("", "Presence is not exported at the moment."); compare(importedContact.presence.customMessage, megaContact.presence.customMessage, "custom message"); expectFail("", "Presence is not exported at the moment."); compare(importedContact.presence.state, megaContact.presence.state, "state"); } function test_exportImportRingtone() { compare(importedContact.ringtone.audioRingtoneUrl, megaContact.ringtone.audioRingtoneUrl); compare(importedContact.ringtone.audioRingtoneUrl, "http://qt.nokia.com/audioringtoneurl"); } function test_exportImportUrl() { compare(importedContact.urls.length, 2, "Error importing/exporting multiple urls"); var importedUrls = importedContact.details(ContactDetail.Url); if (importedUrls[0].url === megaContact.urls[1].url) importedUrls.reverse(); for (var i = 0; i < 2; i++) { compare(importedUrls[i].url, megaContact.urls[i].url, "url"); compareSets(importedUrls[i].contexts, megaContact.urls[i].contexts); } } function test_exportImportTag() { compare(importedContact.tag.tag, megaContact.tag.tag, "tag"); } function test_exportImportTimestamp() { // No convenience accessor exists for timestamp property! var importedTimestamp = importedContact.detail(ContactDetail.Timestamp); var originalTimestamp = megaContact.detail(ContactDetail.Timestamp); expectFail("", "Timestamps are not exported at the moment."); compare(importedTimestamp.created, originalTimestamp.created, "timestamp"); expectFail("", "Timestamps are not exported at the moment"); compare(importedTimestamp.lastModified, originalTimestamp.lastModified, "timestamp"); } function test_exportImportVersion() { compare(importedContact.version.sequenceNumber, megaContact.version.sequenceNumber, "sequence number"); compare(importedContact.version.extendedVersion, megaContact.version.extendedVersion, "extended version"); } function compareSets(a, b) { compare(a.length, b.length, "Different amount of items in compared sets"); for (var i = 0; i < a.length; i++) { var matches = 0; var matchesNeeded = 0; for (var j = 0; j < a.length; j++) { if (a[i] === a[j]) matchesNeeded++; } for (var j = 0; j < b.length; j++) { if (a[i] === b[j]) matches++; } compare(matches, matchesNeeded, "Items in sets don't match"); } } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_export_import_e2e.qml000066400000000000000000000122311233466112000303020ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSavingTestCase { name: "ContactsExportImportE2ETests" ContactModel { id: model manager: getManagerUnderTest() autoUpdate: true } Contact { id: testContact Name { firstName: "First" lastName: "Last" } PhoneNumber { contexts: ContactDetail.ContextOther number: "5874983729" } } // Base for vcard file names. property string vcardFileNameBase: 'tst_contacts_export_import_e2e_' function test_contactExportImportWithTinyContactExportSignaling() { var signalSpy = initTestForTargetListeningToSignal(model, "exportCompleted"); // Save and fetch test contact. model.saveContact(testContact); waitForContactsChanged(); // Export contacts to vcard file. var vcardFilePath = Qt.resolvedUrl(vcardFileNameBase + "1.vcard"); model.exportContacts(vcardFilePath, ["Sync"]); waitForTargetSignal(signalSpy); // Empty all contacts and import just created vcard file. emptyContacts(model); listenToContactsChanged(); model.importContacts(vcardFilePath, ["Sync"]); waitForContactsChanged(); compare (model.error, "NoError", "importError"); // Check that imported contact has correct data. compare (model.contacts.length, 1, "import count"); var importedContact = model.contacts[0]; compare(importedContact.name.firstName,"First", "imported contact first name"); compare(importedContact.name.lastName,"Last", "imported contact last name"); } function test_exportListOfDeclarativeContacts() { var signalSpy = initTestForTargetListeningToSignal(model, "exportCompleted"); // Export contacts to vcard file. var vcardFilePath = Qt.resolvedUrl(vcardFileNameBase + "contactsList.vcard"); var temp = new Array(1); temp[0] = testContact; model.exportContacts(vcardFilePath, ["Sync"], temp); waitForTargetSignal(signalSpy); // Empty all contacts and import just created vcard file. emptyContacts(model); listenToContactsChanged(); model.importContacts(vcardFilePath, ["Sync"]); waitForContactsChanged(); compare (model.error, "NoError", "importError"); // Check that imported contact has correct data. compare (model.contacts.length, 1, "import count"); var importedContact = model.contacts[0]; compare(importedContact.name.firstName,"First", "imported contact first name"); compare(importedContact.name.lastName,"Last", "imported contact last name"); } // Init & teardown function initTestCase() { initTestForModel(model); waitForContactsChanged(); // The wait is needed so the model is populated // (e.g. with garbage left from previous test runs) // before cleanup() is called. emptyContacts(model); } function init() { initTestForModel(model); } function cleanup() { emptyContacts(model); } function cleanupTestCase() { emptyContacts(model); finishTestForModel(model); } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_export_import_signaling_e2e.qml000066400000000000000000000227771233466112000323550ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSavingTestCase { name: "ContactsExportImportSignalingTest" ContactModel { id: model manager: getManagerUnderTest() autoUpdate: true onExportCompleted: { exportErrorCode = error; exportFileName = url; exportResults.append({"error": error, "url": exportFileName}); } onImportCompleted: { importErrorCode = error; importFileName = url; importResults.append({"error": error, "url": importFileName}); } } ListModel { id: exportResults } ListModel { id: importResults } Contact { id: testContact Name { firstName: "First" lastName: "Last" } PhoneNumber { contexts: ContactDetail.ContextOther number: "5874983729" } } // Helper properties for signal parameters testing. property int exportErrorCode property string exportFileName property int importErrorCode property string importFileName property string vcardFileNameBase: 'tst_contacts_export_import_signaling_e2e_' function test_successfulExportEmitsSignalProperly() { var signalSpy = initTestForTargetListeningToSignal(model, "exportCompleted"); // Save and export test contact. var vcardFilePath = Qt.resolvedUrl(vcardFileNameBase + "export_1.vcard"); model.saveContact(testContact); waitForContactsChanged(); model.exportContacts(vcardFilePath, ["Sync"]); // Check if export enters finished state properly. waitForTargetSignal(signalSpy); compare(exportErrorCode, ContactModel.ExportNoError, 'signal finished state error'); compare(exportFileName, vcardFilePath, 'signal finished state filename'); } function test_overlappingExportEmitsSignalWithError() { var signalSpy = initTestForTargetListeningToSignal(model, "exportCompleted"); // Save and export test contact. var vcardFilePath = Qt.resolvedUrl(vcardFileNameBase + "export_2.vcard"); var vcardFilePath2 = Qt.resolvedUrl(vcardFileNameBase + "export_3.vcard"); model.saveContact(testContact); waitForContactsChanged(); model.exportContacts(vcardFilePath, ["Sync"]); // Make immediate re-export which overlaps with the previous one and causes error. model.exportContacts(vcardFilePath2, ["Sync"]); // Wait for both exports completed. waitForTargetSignal(signalSpy); waitForTargetSignal(signalSpy); // It is possible that threads get scheduled so that both exports succeed. if ((exportResults.get(0).error === ContactModel.ExportNoError) && (exportResults.get(1).error === ContactModel.ExportNoError)) { console.log("Overlapping exports did not overlap. This may be ok but error on overlapping did not get tested!") } else { // We got overlapping but it is a bit complex to verify since we do not know for sure in which order signals did arrive. for (var i = 0; i < exportResults.count; i++) { if (exportResults.get(i).error === ContactModel.ExportNoError) { compare(exportResults.get(i).url, vcardFilePath, 'signal finished state filename'); } else { compare(exportResults.get(i).error, ContactModel.ExportNotReadyError, 'signal not ready state error'); compare(exportResults.get(i).url, vcardFilePath2, 'signal not ready state filename'); } } } } function test_successfulImportEmitsSignalProperly() { // Save and fetch test contact. model.saveContact(testContact); waitForContactsChanged(); // Export contacts to vcard file. var vcardFilePath = Qt.resolvedUrl(vcardFileNameBase + "import_1.vcard"); var signalSpy1 = initTestForTargetListeningToSignal(model, "exportCompleted"); model.exportContacts(vcardFilePath, ["Sync"]); waitForTargetSignal(signalSpy1); // Import contacts form vcard file just created. var signalSpy2 = initTestForTargetListeningToSignal(model, "importCompleted"); listenToContactsChanged(); model.importContacts(vcardFilePath, ["Sync"]); waitForContactsChanged(); // Check that import enters finished state properly. waitForTargetSignal(signalSpy2); compare(importErrorCode, ContactModel.ImportNoError, 'signal finished state error'); compare(importFileName, vcardFilePath, 'signal finished state filename'); } function test_overlappingImportEmitsSingalWithError() { // Save and fetch test contact. model.saveContact(testContact); waitForContactsChanged(); // Export contacts to two vcard files. var vcardFilePath = Qt.resolvedUrl(vcardFileNameBase + "import_2.vcard"); var vcardFilePath2 = Qt.resolvedUrl(vcardFileNameBase + "import_3.vcard"); var signalSpy1 = initTestForTargetListeningToSignal(model, "exportCompleted"); model.exportContacts(vcardFilePath, ["Sync"]); waitForTargetSignal(signalSpy1); model.exportContacts(vcardFilePath2, ["Sync"]); waitForTargetSignal(signalSpy1); // Import contacts form vcard file just created. var signalSpy2 = initTestForTargetListeningToSignal(model, "importCompleted"); listenToContactsChanged(); model.importContacts(vcardFilePath, ["Sync"]); // Make immediate second import request which should fail as reader is busy. model.importContacts(vcardFilePath2, ["Sync"]); // Wait for both imports to finish. waitForTargetSignal(signalSpy2); waitForTargetSignal(signalSpy2); // It is possible that threads get scheduled so that both imports succeed. if ((importResults.get(0).error === ContactModel.ImportNoError) && (importResults.get(1).error === ContactModel.ImportNoError)) { console.log("Overlapping imports did not overlap. This may be ok but error on overlapping did not get tested!") } else { // We got overlapping but it is a bit complex to verify since we do not know for sure in which order signals did arrive. for (var i = 0; i < importResults.count; i++) { if (importResults.get(i).error === ContactModel.ImportNoError) { compare(importResults.get(i).url, vcardFilePath, 'signal finished state filename'); } else { compare(importResults.get(i).error, ContactModel.ImportNotReadyError, 'signal not ready state error'); compare(importResults.get(i).url, vcardFilePath2, 'signal not ready state filename'); } } } } // Init & teardown function initTestCase() { initTestForModel(model); waitForContactsChanged(); // The wait is needed so the model is populated // (e.g. with garbage left from previous test runs) // before cleanup() is called. emptyContacts(model); } function init() { initTestForModel(model); // Initial values for helper properties. exportErrorCode = ContactModel.ExportNoError exportFileName = "" importErrorCode = ContactModel.ImportNoError importFileName = "" } function cleanup() { emptyContacts(model); exportResults.clear(); importResults.clear(); } function cleanupTestCase() { finishTestForModel(model); } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_extended_detail_e2e.qml000066400000000000000000000357441233466112000305270ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSavingTestCase { name: "ContactsExtendedDetailE2ETests" ContactModel { id: model manager: getManagerUnderTest() autoUpdate:true } property bool myBoolean: false Contact { id: contactForBoolean ExtendedDetail { name: "boolean" data: myBoolean } } function test_withBoolean() { var fetchedExtendedDetail = saveAndUpdate(contactForBoolean); compare(fetchedExtendedDetail.name, "boolean", "extendedDetail.name"); compare(fetchedExtendedDetail.data, myBoolean, "extendedDetail.data"); } property color myColor: "red" Contact { id: contactForColor ExtendedDetail { name: "color" data: myColor } } function test_withColor() { var fetchedExtendedDetail = saveAndUpdate(contactForColor); compare(fetchedExtendedDetail.name, "color", "extendedDetail.name"); compare(fetchedExtendedDetail.data, myColor, "extendedDetail.data"); } property date myDate: "2001-08-14" Contact { id: contactForDate ExtendedDetail { name: "date" data: myDate } } function test_withDate() { var fetchedExtendedDetail = saveAndUpdate(contactForDate); compare(fetchedExtendedDetail.name, "date", "extendedDetail.name"); if (model.manager == "jsondb") { // Jsondb backend converts dates to strings in ISO 8601 format containing // date and time in local time without timezone specified. compare(fetchedExtendedDetail.data, "2001-08-14T00:00:00", "extendedDetail.data"); } else { compare(fetchedExtendedDetail.data, myDate, "extendedDetail.data"); } } Contact { id: contactForDateAsString ExtendedDetail { name: "date" data: new Date(Date.UTC(2000, 0, 2, 12, 13, 14)).toISOString() } } function test_withDateAsString() { var fetchedExtendedDetail = saveAndUpdate(contactForDateAsString); compare(fetchedExtendedDetail.data, "2000-01-02T12:13:14.000Z", "data as string"); compare(Date.parse(fetchedExtendedDetail.data), Date.UTC(2000, 0, 2, 12, 13, 14), "data parsed to Date (in milliseconds)"); } property double myDouble: 4.5 Contact { id: contactForDouble ExtendedDetail { name: "double" data: myDouble } } function test_withDouble() { var fetchedExtendedDetail = saveAndUpdate(contactForDouble); compare(fetchedExtendedDetail.name, "double", "extendedDetail.name"); compare(fetchedExtendedDetail.data, myDouble, "extendedDetail.data"); } property int myInt: 42 Contact { id: contactForInt ExtendedDetail { name: "int" data: myInt } } function test_withInt() { var fetchedExtendedDetail = saveAndUpdate(contactForInt); compare(fetchedExtendedDetail.name, "int", "extendedDetail.name"); compare(fetchedExtendedDetail.data, myInt, "extendedDetail.data"); } property int myNegativeInt: -52 Contact { id: contactForNegativeInt ExtendedDetail { name: "negativeInt" data: myNegativeInt } } function test_withNegativeInt() { var fetchedExtendedDetail = saveAndUpdate(contactForNegativeInt); compare(fetchedExtendedDetail.name, "negativeInt", "extendedDetail.name"); compare(fetchedExtendedDetail.data, myNegativeInt, "extendedDetail.data"); } property real myReal: 4.4 Contact { id: contactForReal ExtendedDetail { name: "real" data: myReal } } function test_withReal() { var fetchedExtendedDetail = saveAndUpdate(contactForReal ); compare(fetchedExtendedDetail.name, "real", "extendedDetail.name"); compare(fetchedExtendedDetail.data, myReal, "extendedDetail.data"); } property real myNegativeReal: -4.5 Contact { id: contactForNegativeReal ExtendedDetail { name: "negativeReal" data: myNegativeReal } } function test_withNegReal() { var fetchedExtendedDetail = saveAndUpdate(contactForNegativeReal); compare(fetchedExtendedDetail.name, "negativeReal", "extendedDetail.name"); compare(fetchedExtendedDetail.data, myNegativeReal, "extendedDetail.data"); } property string myString: "Hello world!" Contact { id: contactForString ExtendedDetail { name: "string" data: myString } } function test_withString() { var fetchedExtendedDetail = saveAndUpdate(contactForString); compare(fetchedExtendedDetail.name, "string", "extendedDetail.name"); compare(fetchedExtendedDetail.data, myString, "extendedDetail.data"); } property url myUrl: "http://ovi.com" Contact { id: contactForUrl ExtendedDetail { name: "url" data: myUrl } } function test_withUrl() { var fetchedExtendedDetail = saveAndUpdate(contactForUrl); compare(fetchedExtendedDetail.name, "url", "extendedDetail.name"); compare(fetchedExtendedDetail.data, myUrl, "extendedDetail.data"); } property string stringForVariant: "StringForVariant" property variant myVariant: stringForVariant Contact { id: contactForVariant ExtendedDetail { name: "stringInVariant" data: myVariant } } function test_withVariant() { var fetchedExtendedDetail = saveAndUpdate(contactForVariant); compare(fetchedExtendedDetail.name, "stringInVariant", "extendedDetail.name"); compare(fetchedExtendedDetail.data, myVariant, "extendedDetail.data Variant"); compare(fetchedExtendedDetail.data, stringForVariant, "extendedDetail.data String"); } property variant myIntList: [ 1, 2, 3, 4, 5, 6, 7] Contact { id: contactForIntList ExtendedDetail { name: "intList" data: myIntList } } function test_withIntList() { var fetchedExtendedDetail = saveAndUpdate(contactForIntList); compare(fetchedExtendedDetail.name, "intList", "extendedDetail.name"); compare(fetchedExtendedDetail.data, myIntList, "extendedDetail.data"); } property variant myStringList:[ "one", "two", "three", "four"] Contact { id: contactForStringList ExtendedDetail { name: "stringList" data: myStringList } } function test_withStringList() { var fetchedExtendedDetail = saveAndUpdate(contactForStringList); compare(fetchedExtendedDetail.name, "stringList", "extendedDetail.name"); compare(fetchedExtendedDetail.data, myStringList, "extendedDetail.data"); } property variant myVariantList: [ "one", "two", 3, 4, 5, 6, 7] Contact { id: contactForVariantList ExtendedDetail { name: "variantList" data: myVariantList } } function test_withVariantList() { var fetchedExtendedDetail = saveAndUpdate(contactForVariantList); compare(fetchedExtendedDetail.name, "variantList", "extendedDetail.name"); compare(fetchedExtendedDetail.data, myVariantList, "extendedDetail.data"); } property variant myEmbeddedVariantLists: [["one", "two"], 3, 4, 5, [6, 7], 8, "nine", ["ten", 11]] Contact { id: contactForEmbeddedVariantLists ExtendedDetail { name: "embeddedVariantLists" data: myEmbeddedVariantLists } } function test_withEmbeddedVariantsList() { var fetchedExtendedDetail = saveAndUpdate(contactForEmbeddedVariantLists); compare(fetchedExtendedDetail.name, "embeddedVariantLists", "extendedDetail.name"); compare(fetchedExtendedDetail.data, myEmbeddedVariantLists, "extendedDetail.data"); } property variant myDeeplyEmbeddedVariantLists: [["one", "two", [3, 4, [5, 6], 7], 8], 9, "ten", [ "eleven", 12]] Contact { id: contactForDeeplyEmbeddedVariantLists ExtendedDetail { name: "deeplyEmbeddedVariantLists" data: myDeeplyEmbeddedVariantLists } } function test_withDeeplyEmbeddedVariantLists() { var fetchedExtendedDetail = saveAndUpdate(contactForDeeplyEmbeddedVariantLists); compare(fetchedExtendedDetail.name, "deeplyEmbeddedVariantLists", "extendedDetail.name"); compare(fetchedExtendedDetail.data, myDeeplyEmbeddedVariantLists, "extendedDetail.data"); } property variant myMap: {"key1": 1, "key2": 2, "key3": 3} Contact { id: contactForMap ExtendedDetail { name: "mapData" data: myMap } } function test_withMap() { var fetchedExtendedDetail = saveAndUpdate(contactForMap); compare(fetchedExtendedDetail.name, "mapData", "extendedDetail.name"); compare(fetchedExtendedDetail.data, myMap, "extendedDetail.data"); } Contact { id: contactWithThreeExtendedDetails ExtendedDetail { name: "First" data: 1 } ExtendedDetail { name: "Second" data: 2 } ExtendedDetail { name: "Third" data: 3 } } function test_extendedDetailsListProperty_multipleDetails() { var fetchedContact = saveAndUpdateContact(contactWithThreeExtendedDetails); var fetchedExtendedDetails = fetchedContact.extendedDetails; compare(fetchedExtendedDetails.length, 3, "extendedDetails.length"); compare(fetchedExtendedDetails[0], fetchedContact.extendedDetail, "extendedDetail == extendedDetails[0]"); compareExtendedDetails(fetchedExtendedDetails[0], contactWithThreeExtendedDetails.extendedDetails[0]); compareExtendedDetails(fetchedExtendedDetails[1], contactWithThreeExtendedDetails.extendedDetails[1]); compareExtendedDetails(fetchedExtendedDetails[2], contactWithThreeExtendedDetails.extendedDetails[2]); } Contact { id: contactWithOneExtendedDetail ExtendedDetail { name: "First" data: 1 } } function test_extendedDetailsListProperty_oneDetail() { var fetchedContact = saveAndUpdateContact(contactWithOneExtendedDetail); var fetchedExtendedDetails = fetchedContact.extendedDetails; compare(fetchedExtendedDetails.length, 1, "extendedDetails.length"); compare(fetchedExtendedDetails[0], fetchedContact.extendedDetail, "extendedDetail == extendedDetails[0]"); compareExtendedDetails(fetchedExtendedDetails[0], contactWithOneExtendedDetail.extendedDetail); } Contact { id: contactWithoutExtendedDetails } function test_extendedDetailsListProperty_noDetails() { var fetchedExtendedDetails = saveAndUpdateContact(contactWithoutExtendedDetails).extendedDetails; compare(fetchedExtendedDetails.length, 0, "extendedDetails.length"); verify(!fetchedExtendedDetails[0], "extendedDetails[0] is undefined"); } Contact { id: contactWithTwoExtendedDetails ExtendedDetail { name: "First" data: 1 } ExtendedDetail { name: "Second" data: 2 } } function test_extendedDetailsListProperty_removing_one_detail() { var fetchedExtendedDetails = saveAndUpdateContact(contactWithTwoExtendedDetails).extendedDetails; compare(fetchedExtendedDetails.length, 2, "extendedDetails.length"); contactWithTwoExtendedDetails.removeDetail(contactWithTwoExtendedDetails.extendedDetails[1]) compare(contactWithTwoExtendedDetails.extendedDetails.length, 1, "extendedDetails.length"); } // TODO: Test more QML and Javascript types. function saveAndUpdate(contact) { model.saveContact(contact); waitForContactsChanged(); model.update(); waitForContactsChanged(); return model.contacts[0].extendedDetail; } function saveAndUpdateContact(contact) { model.saveContact(contact); waitForContactsChanged(); model.update(); waitForContactsChanged(); return model.contacts[0]; } function compareExtendedDetails(actual, expected) { compare(actual.name, expected.name, "name") compare(actual.data, expected.data, "data") } // Init & teardown function initTestCase() { initTestForModel(model); waitForContactsChanged(); // The wait is needed so the model is populated // (e.g. with garbage left from previous test runs) // before cleanup() is called. emptyContacts(model); } function init() { initTestForModel(model); } function cleanup() { emptyContacts(model); } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_fetch_contacts_e2e.qml000066400000000000000000000230571233466112000303660ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSavingTestCase { name: "ContactsFetchContactsE2ETests" id: contactsFetchContactsE2ETests ContactModel { id: model manager: getManagerUnderTest() autoUpdate: true onContactsFetched: { lastTransactionId = requestId; lastContactsFetched = fetchedContacts; } } Contact { id: contact1 } Contact { id: contact2 } Contact { id: contact3 } property Contact nonExistingContact property int lastTransactionId property list lastContactsFetched // Tests function test_fetchExistingContact() { var id = model.contacts[0].contactId; var trid = model.fetchContacts([id]); waitForContactsFetched(); compare(lastTransactionId, trid, "transaction id"); compare(lastContactsFetched.length, 1, "contacts length"); compare(lastContactsFetched[0].contactId, model.contacts[0].contactId, "fetched contact id"); } function test_fetchMultipleExistingContacts() { var id1 = model.contacts[0].contactId; var id2 = model.contacts[1].contactId; model.fetchContacts([id1, id2]); waitForContactsFetched(); compare(lastContactsFetched.length, 2, "contacts length"); compare(lastContactsFetched[0].contactId, id1, "fetched contact id 1"); compare(lastContactsFetched[1].contactId, id2, "fetched contact id 2"); } function test_fetchNonExistingContact() { var id = nonExistingContact.contactId; var trid = model.fetchContacts([id]); waitForContactsFetched(); compare(lastContactsFetched.length, 0, "contacts length"); } function test_fetchBothExistingAndNonExistingContacts() { var id1 = model.contacts[0].contactId; var id2 = nonExistingContact.contactId; model.fetchContacts([id1, id2]); waitForContactsFetched(); expectFail("", "implementation returns an empty list of contacts") compare(lastContactsFetched.length, 1, "contacts length"); compare(lastContactsFetched[0].contactId, id1, "fetched contact id 1"); } function test_fetchDuplicateContactIds() { var id = model.contacts[0].contactId; var originalLength = model.contacts.length; var trid = model.fetchContacts([id,id]); waitForContactsFetched(); compare(lastContactsFetched.length, 2, "contacts length"); compare(lastContactsFetched[0].contactId, model.contacts[0].contactId, "fetched contact id 0"); compare(lastContactsFetched[1].contactId, model.contacts[0].contactId, "fetched contact id 1"); compare(model.contacts.length, originalLength, "model length"); } function test_fetchEmptyListOfContactIds() { listenToContactsFetched(); var trid = model.fetchContacts([]); verifyNoContactsFetchedReceived(); compare(trid, -1, "transaction id"); } Contact { id: contactWhichHasNullId } function test_fetchNullContactId() { var id = contactWhichHasNullId.contactId; var trid = model.fetchContacts([id]); waitForContactsFetched(); compare(lastContactsFetched.length, 0, "contacts length"); } function test_fetchInvalidContactId() { var id = "invalidContactId"; var trid = model.fetchContacts([id]); waitForContactsFetched(); compare(lastContactsFetched.length, 0, "contacts length"); } IdFilter { id: filter ids: [] } function test_filterAndFetchContactMatchingTheFilter() { var contact = model.contacts[0]; var id = contact.contactId; filter.ids = [id]; model.filter = filter; waitForContactsChanged(); model.fetchContacts([id]); waitForContactsFetched(); compare(lastContactsFetched.length, 1, "contacts length"); compare(lastContactsFetched[0].contactId, id, "fetched contact id"); } function test_filterAndFetchContactWhichDoesNotMatchTheFilter() { var id = model.contacts[0].contactId; var idOfAnotherContact = model.contacts[1].contactId; filter.ids = [id]; model.filter = filter; waitForContactsChanged(); model.fetchContacts([idOfAnotherContact]); waitForContactsFetched(); compare(lastContactsFetched.length, 1, "contacts length"); compare(lastContactsFetched[0].contactId, idOfAnotherContact, "fetched contact id"); } // Init & teardown function initTestCase() { initTestForModel(model); waitForContactsChanged(); // The wait is needed so the model is populated // (e.g. with garbage left from previous test runs) // before cleanup() is called. emptyContacts(model); addTestContactsToModel(); } Contact { id: contactFirstSavedAndThenRemoved Name { firstName: "contactFirstSavedAndThenRemoved" } } function addTestContactsToModel() { model.saveContact(contact1); waitForContactsChanged(); model.saveContact(contact2); waitForContactsChanged(); model.saveContact(contact3); waitForContactsChanged(); model.saveContact(contactFirstSavedAndThenRemoved); waitForContactsChanged(); for (var i = 0; i < model.contacts.length; i++) { if (model.contacts[i].name.firstName == contactFirstSavedAndThenRemoved.name.firstName) { nonExistingContact = model.contacts[i]; break; } } model.removeContact(nonExistingContact.contactId); waitForContactsChanged(); } function init() { initTestForModel(model); initContactsFetchSpy(); } function cleanup() { clearFilter(); } function cleanupTestCase() { initTestForModel(model); clearFilter(); emptyContacts(model); finishTestForModel(model); } function clearFilter() { if (model.filter) { model.filter = null; waitForContactsChanged(); } } property SignalSpy contactsFetchedSpy function initContactsFetchSpy() { contactsFetchedSpy = initTestForTargetListeningToSignal(model, "contactsFetched"); lastTransactionId = -99; // different from -1 (used as an error code in fetchContacts) lastContactsFetched = []; } function waitForContactsFetched() { waitForTargetSignal(contactsFetchedSpy); } function listenToContactsFetched() { listenToTargetSignal(contactsFetchedSpy); } function verifyNoContactsFetchedReceived() { verifyNoTargetSignalReceived(contactsFetchedSpy); } // Helpers // These will be later found from the base test case element function initTestForTargetListeningToSignal(target, signalName) { logDebug("initTestForTargetListeningToSignal"); var spy = Qt.createQmlObject( "import QtTest 1.0;" + "SignalSpy {" + "}", contactsFetchContactsE2ETests); spy.target = target; spy.signalName = signalName; return spy; } function waitForTargetSignal(spy) { logDebug("waitForTargetSignal"); spy.wait(); } function listenToTargetSignal(spy) { logDebug("listenToTargetSignal"); spy.clear(); } function verifyNoTargetSignalReceived(spy) { logDebug("verifyNoTargetSignalReceived"); wait(500); compare(spy.count, 0, "no target signal received"); } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_filtering_by_detail_e2e.qml000066400000000000000000000330521233466112000313720ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSavingTestCase { id: testcase name: "ContactsFilteringByDetailE2ETests" ContactModel { id: model manager: getManagerUnderTest() autoUpdate: true } Contact { id: contact1; Name { firstName: "A" } PhoneNumber { number: "1111111111" } } Contact { id: contact2; Name { firstName: "B" } PhoneNumber { number: "2222222222" } } Contact { id: contact3; Name { firstName: "John Joe" } PhoneNumber { number: "3333333333" } } function initTestCase() { initTestForModel(model); waitForContactsChanged(); // The wait is needed so the model is populated // (e.g. with garbage left from previous test runs) // before cleanup() is called. emptyContacts(model); model.saveContact(contact1); waitForContactsChanged(); model.saveContact(contact2); waitForContactsChanged(); model.saveContact(contact3); waitForContactsChanged(); compare(model.contacts.length, 3); } function createDetailFilter(detail, field, value, matchFlags) { var filter = Qt.createQmlObject( "import QtContacts 5.0;" + "DetailFilter {}", testcase); filter.detail = detail filter.field = field filter.value = value filter.matchFlags = matchFlags return filter; } function createPhoneNumberFilter(value, matchFlags) { return createDetailFilter(ContactDetail.PhoneNumber, PhoneNumber.Number, value, matchFlags); } function createFirstNameFilter(value, matchFlags) { return createDetailFilter(ContactDetail.Name, Name.FirstName, value, matchFlags); } function test_filterByDetail_data() { return [{ tag: "Phone number, match exactly, identical value", filter: createPhoneNumberFilter(contact1.phoneNumber.number, Filter.MatchExactly), matches: [contact1] }, { tag: "Phone number, match exactly, no match", filter: createPhoneNumberFilter("1111", Filter.MatchExactly), matches: [] }, { tag: "Phone number, match exactly, empty string", filter: createPhoneNumberFilter("", Filter.MatchExactly), matches: [] }, { tag: "Phone number, match contains, identical value", filter: createPhoneNumberFilter(contact1.phoneNumber.number, Filter.MatchContains), matches: [contact1] }, { tag: "Phone number, match contains, no match", filter: createPhoneNumberFilter(" ", Filter.MatchContains), matches: [] }, { tag: "Phone number, match contains, matching substring", filter: createPhoneNumberFilter("1111", Filter.MatchContains), matches: [contact1] }, { tag: "Phone number, match contains, empty string", filter: createPhoneNumberFilter("", Filter.MatchContains), matches: [contact1, contact2, contact3] }, { tag: "Phone number, match starts with, identical value", filter: createPhoneNumberFilter(contact1.phoneNumber.number, Filter.MatchStartsWith), matches: [contact1] }, { tag: "Phone number, match starts with, no match", filter: createPhoneNumberFilter(" ", Filter.MatchStartsWith), matches: [] }, { tag: "Phone number, match starts with, matching substring", filter: createPhoneNumberFilter("1111", Filter.MatchStartsWith), matches: [contact1] }, { tag: "Phone number, match starts with, empty string", filter: createPhoneNumberFilter("", Filter.MatchStartsWith), matches: [contact1, contact2, contact3] }, { tag: "Phone number, match ends with, identical value", filter: createPhoneNumberFilter(contact1.phoneNumber.number, Filter.MatchEndsWith), matches: [contact1] }, { tag: "Phone number, match ends with, no match", filter: createPhoneNumberFilter(" ", Filter.MatchEndsWith), matches: [] }, { tag: "Phone number, match ends with, matching substring", filter: createPhoneNumberFilter("1111", Filter.MatchEndsWith), matches: [contact1] }, { tag: "Phone number, match ends with, empty string", filter: createPhoneNumberFilter("", Filter.MatchEndsWith), matches: [contact1, contact2, contact3] }, { tag: "First name, match exactly, identical value", filter: createFirstNameFilter(contact1.name.firstName, Filter.MatchExactly), matches: [contact1] }, { tag: "First name, match exactly, no match", filter: createFirstNameFilter("C", Filter.MatchExactly), matches: [] }, { tag: "First name, match contains , matching substring", filter: createFirstNameFilter("Joe", Filter.MatchContains), matches: [contact3] }, { tag: "First name, match contains , caseInsensitive by default so matches", filter: createFirstNameFilter("joe", Filter.MatchContains), matches: [contact3] }, { tag: "First name, match exactly ,always case sensitive so no matches here", filter: createFirstNameFilter("john joe", Filter.MatchExactly), matches: [] }, { tag: "First name, match fixed string , identical value", filter: createFirstNameFilter("John Joe", Filter.MatchFixedString), matches: [contact3] }, { tag: "First name, match fixed string , no match", filter: createFirstNameFilter("John", Filter.MatchFixedString), matches: [] }, { tag: "First name, match fixed string, caseInsensitive by default so matches", filter: createFirstNameFilter("john joe", Filter.MatchFixedString), matches: [contact3] }, { tag: "First name, match fixed string and case sensitive, no match", filter: createFirstNameFilter("john joe", Filter.MatchFixedString | Filter.MatchCaseSensitive), matches: [] }, { tag: "First name, match starts with, caseInsensitive by default so matches", filter: createFirstNameFilter("john", Filter.MatchStartsWith), matches: [contact3] }, { tag: "First name, match starts with and match caseSensitive, matching substring", filter: createFirstNameFilter("John", Filter.MatchStartsWith | Filter.MatchCaseSensitive), matches: [contact3] }, { tag: "First name, match starts with and match caseSensitive, no match", filter: createFirstNameFilter("john", Filter.MatchStartsWith | Filter.MatchCaseSensitive), matches: [] }, { tag: "First name, match ends with and match caseSensitive, matching substring", filter: createFirstNameFilter("Joe", Filter.MatchEndsWith | Filter.MatchCaseSensitive), matches: [contact3] }, { tag: "First name, match ends with and match caseSensitive, no match", filter: createFirstNameFilter("joe", Filter.MatchEndsWith | Filter.MatchCaseSensitive), matches: [] }, { tag: "First name, match ends with, caseInsensitive by default so matches", filter: createFirstNameFilter("joe", Filter.MatchEndsWith), matches: [contact3] }, { tag: "First name, match fixedString and match ends with, matching substring", filter: createFirstNameFilter("John Joe", Filter.MatchFixedString | Filter.MatchEndsWith), matches: [contact3] }, { tag: "First name, match fixedString and match startsWith, matching substring", filter: createFirstNameFilter("John Joe", Filter.MatchFixedString | Filter.MatchStartsWith), matches: [contact3] }, { tag: "Last name, match exactly, no contact has a value for last name", filter: createDetailFilter(ContactDetail.Name, Name.LastName, "NoContactHasThisAsLastName", Filter.MatchExactly), matches: [] }, ] } function test_filterByDetail(data) { model.filter = data.filter; waitForContactsChanged(); compare(model.contacts.length, data.matches.length, data.tag); // Compare filtered set to expected set for (var i = 0; i < model.contacts.length; i++) { var match = false; for (var j = 0; j < data.matches.length; j++) { if (model.contacts[i].name.firstName == data.matches[j].name.firstName && model.contacts[i].phoneNumber.number == data.matches[j].phoneNumber.number) match = true; } compare(match, true); } } function test_settingMatchFlagDoesChangeTheContacts() { var filter = createPhoneNumberFilter("1111", Filter.MatchExactly); model.filter = filter; waitForContactsChanged(); compare(model.contacts.length, 0); model.filter.matchFlags = Filter.MatchContains; waitForContactsChanged(); compare(model.contacts.length, 1); model.filter.matchFlags = Filter.MatchStartsWith; waitForContactsChanged(); compare(model.contacts.length, 1); model.filter.matchFlags = Filter.MatchEndsWith; waitForContactsChanged(); compare(model.contacts.length, 1); } // Teardown function cleanup() { if (model.filter) { model.filter = null; waitForContactsChanged(); } } function cleanupTestCase() { emptyContacts(model); finishTestForModel(model); } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_filtering_e2e.qml000066400000000000000000000220571233466112000273610ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSavingTestCase { name: "ContactsFilteringTests" IdFilter { id: filter ids: [] } ContactModel { id: model manager: getManagerUnderTest() autoUpdate: true } SignalSpy { id: contactsFetchedSpy signalName: "contactsFetched" target: model } Contact { id: contact1; } Contact { id: contact2; } Contact { id: contact3; } // Clean and populate the database with test contacts function initTestCase() { initTestForModel(model); waitForContactsChanged(); // The wait is needed so the model is populated // (e.g. with garbage left from previous test runs) // before cleanup() is called. emptyContacts(model); model.saveContact(contact1); waitForContactsChanged(); model.saveContact(contact2); waitForContactsChanged(); model.saveContact(contact3); waitForContactsChanged(); } // Clean database function cleanupTestCase() { clearFilter(); emptyContacts(model); finishTestForModel(model); } // Clear filter function cleanup() { clearFilter(); compare (model.contacts.length, 3); } function clearFilter() { if (model.filter) { model.filter = null; waitForContactsChanged(); } } // Tests function test_dynamicIdFilterConstruction() { var newFilter = Qt.createQmlObject( "import QtContacts 5.0;" + "IdFilter { ids: ['" + model.contacts[0].contactId + "']" + "}", this); model.filter = newFilter; waitForContactsChanged(); compare(model.contacts.length, 1); } function test_filterById() { var id = model.contacts[0].contactId; filterById(id); } function filterById(id) { filter.ids = [id]; model.filter = filter; waitForContactsChanged(); compare (model.contacts.length, 1); compare(model.contacts[0].contactId, id); } function test_filterByIdOfContactInTheMiddle() { var id = model.contacts[1].contactId; filterById(id); } function test_filterByIdOfContactAtTheEnd() { var id = model.contacts[2].contactId; filterById(id); } function test_filterByMultipleIds() { var id1 = model.contacts[0].contactId; var id2 = model.contacts[1].contactId; filterByMultipleIds(id1, id2); } function filterByMultipleIds(id1, id2) { var id1 = model.contacts[0].contactId; var id2 = model.contacts[1].contactId; filter.ids = [id1, id2]; model.filter = filter; waitForContactsChanged(); compare (model.contacts.length, 2); compare(model.contacts[0].contactId, id1); compare(model.contacts[1].contactId, id2); } function test_filterByMultipleNonConsequtiveIds() { var id1 = model.contacts[0].contactId; var id2 = model.contacts[2].contactId; filterByMultipleIds(id1, id2); } function test_filterByMultipleIdsOfContactsAtTheEnd() { var id1 = model.contacts[1].contactId; var id2 = model.contacts[2].contactId; filterByMultipleIds(id1, id2); } function test_filterByNonExistingId() { filter.ids = ["foo bar"]; model.filter = filter; waitForContactsChanged();; compare (model.contacts.length, 0); } function test_filterByMultipleNonExistingIds() { filter.ids = ["foo", "bar", "baz", "qux"]; model.filter = filter; waitForContactsChanged();; compare (model.contacts.length, 0); } function test_filterByMixedExistingAndNonExistingIds() { var id = model.contacts[0].contactId; filter.ids = ["foo bar", id]; model.filter = filter; waitForContactsChanged(); compare (model.contacts.length, 1); compare(model.contacts[0].contactId, id); } function test_filterByEmptyIdList() { filter.ids = []; model.filter = filter; waitForContactsChanged(); compare (model.contacts.length, 0); } function test_filterByTwoOverlappingIds() { filter.ids = [model.contacts[0].contactId, model.contacts[0].contactId]; model.filter = filter; waitForContactsChanged(); compare (model.contacts.length, 1); } function test_filterByTwoCouplesOfOverlappingIds() { filter.ids = [model.contacts[0].contactId, model.contacts[0].contactId, model.contacts[1].contactId, model.contacts[1].contactId]; model.filter = filter; waitForContactsChanged(); compare (model.contacts.length, 2); } function test_filterByAlternatingOverlappingIds() { filter.ids = [model.contacts[0].contactId, model.contacts[1].contactId, model.contacts[0].contactId]; model.filter = filter; waitForContactsChanged(); compare (model.contacts.length, 2); } function test_filterMatchingContactLeavesItStillValid() { var contact = model.contacts[0]; var id = contact.contactId; filter.ids = [id]; model.filter = filter; waitForContactsChanged(); verify(contact, "contact is defined"); verify(contact.contactId, "contact id is defined"); verify(contact === model.contacts[0], "still contains the contact") } function test_filterOutContactLeavesItStillValid() { var contact = model.contacts[0]; var idOfAnotherContact = model.contacts[1].contactId; filter.ids = [idOfAnotherContact]; model.filter = filter; waitForContactsChanged(); verify(contact, "contact is defined"); verify(contact.contactId, "contact id is defined"); } function test_expandFilterLeavesContactsStillValid() { var contact = model.contacts[0]; var id = contact.contactId; var idOfAnotherContact = model.contacts[1].contactId; filter.ids = [id]; model.filter = filter; waitForContactsChanged(); contact = model.contacts[0]; filter.ids = [id, idOfAnotherContact]; model.filter = filter; waitForContactsChanged(); verify(contact, "contact is defined"); verify(contact.contactId, "contact id is defined"); verify(contact === model.contacts[0] || contact === model.contacts[1], "still contains the contact") } IdFilter { id: oldFilter ids: [] } IdFilter { id: newFilter ids: [] } function test_afterSettingANewFilterChangingTheOldFilterDoesNotChangeModel() { var id = model.contacts[0].contactId; oldFilter.ids = [id]; model.filter = oldFilter; waitForContactsChanged(); newFilter.ids = [id]; model.filter = newFilter; waitForContactsChanged(); listenToContactsChanged(); oldFilter.ids = []; // change the old filter verifyNoContactsChangedReceived(); } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_intersection_filter_e2e.qml000066400000000000000000000177031233466112000314530ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSavingTestCase { name: "ContactsIntersectionFilterTests" ContactModel { id: model manager: getManagerUnderTest() autoUpdate: true } IntersectionFilter { id: intersectionFilter } DetailFilter { id: detailFilter1 detail: ContactDetail.Name field: Name.FirstName value: "1" matchFlags: Filter.MatchExactly } DetailFilter { id: detailFilterB detail: ContactDetail.Name field: Name.LastName value: "B" matchFlags: Filter.MatchExactly } IdFilter { // contact id:s are set in initTestCase() id: idFilter_1B_2B_3B } IdFilter { // contact id:s are set in initTestCase() id: idFilter_2A_3A_1B_2B } Contact { id: contact1A Name { firstName: "1" lastName: "A" } } Contact { id: contact2A Name { firstName: "2" lastName: "A" } } Contact { id: contact3A Name { firstName: "3" lastName: "A" } } Contact { id: contact1B Name { firstName: "1" lastName: "B" } } Contact { id: contact2B Name { firstName: "2" lastName: "B" } } Contact { id: contact3B Name { firstName: "3" lastName: "B" } } function initTestCase() { initTestForModel(model); waitForContactsChanged(); // The wait is needed so the model is populated // (e.g. with garbage left from previous test runs) // before cleanup() is called. emptyContacts(model); saveContactsToModel(); setupIdFilter(); } function saveContactsToModel() { model.saveContact(contact1A); waitForContactsChanged(); model.saveContact(contact2A); waitForContactsChanged(); model.saveContact(contact3A); waitForContactsChanged(); model.saveContact(contact1B); waitForContactsChanged(); model.saveContact(contact2B); waitForContactsChanged(); model.saveContact(contact3B); waitForContactsChanged(); compare(model.contacts.length, 6); } function setupIdFilter() { idFilter_1B_2B_3B.ids = [findId("1", "B"), // contacts in model aren't guaranteed to be in the saving order, so find is needed findId("2", "B"), findId("3", "B")]; idFilter_2A_3A_1B_2B.ids = [findId("2", "A"), findId("3", "A"), findId("1", "B"), findId("2", "B")]; } function findId(firstName, lastName) { for (var i = 0; i < model.contacts.length; i++) { if (model.contacts[i].name.firstName == firstName && model.contacts[i].name.lastName == lastName) return model.contacts[i].contactId; } } function test_intersectionFilter_data() { return [{ tag: "Intersection of two detail filters", filters: [detailFilter1, detailFilterB], intersection: [contact1B] }, { tag: "Intersection of detail filter and id filter", filters: [detailFilterB, idFilter_2A_3A_1B_2B], intersection: [contact1B, contact2B] }, { tag: "Intersection of two id filters", filters: [idFilter_1B_2B_3B, idFilter_2A_3A_1B_2B], intersection: [contact1B, contact2B] }, { tag: "Intersection of two id filters and detail filter", filters: [detailFilter1, idFilter_1B_2B_3B, idFilter_2A_3A_1B_2B], intersection: [contact1B] }, { tag: "Intersection filter with empty filter set (manager: jsondb)", manager: "jsondb", filters: [], intersection: [contact1A, contact2A, contact3A, contact1B, contact2B, contact3B] }, { tag: "Intersection filter with empty filter set (manager: memory)", manager: "memory", filters: [], intersection: [] }, { tag: "Intersection filter with a single detail filter", filters: [detailFilter1], intersection: [contact1A, contact1B] } ]; } function test_intersectionFilter(data) { if (data.manager && data.manager !== model.manager) skip("Test not applicable to this manager"); intersectionFilter.filters = data.filters; model.filter = intersectionFilter; waitForContactsChanged(); compareContacts(model.contacts, data.intersection) } function compareContacts(set1, set2) { // Compare filtered set to expected set. // All contacts in sets are assumed to have unique firstName and lastName detail pairs. compare(set1.length, set2.length); for (var i = 0; i < set1.length; i++) { var match = false; for (var j = 0; j < set2.length; j++) { if (set1[i].name.firstName == set2[j].name.firstName && set1[i].name.lastName == set2[j].name.lastName) match = true; } compare(match, true); } } function cleanup() { if (model.filter) { model.filter = null; waitForContactsChanged(); } } function cleanupTestCase() { emptyContacts(model); finishTestForModel(model); } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_jsondb_details_saving_e2e.qml000066400000000000000000000063441233466112000317320ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsJsonDbTestCase { name: "ContactsJsonDbDetailsSavingE2ETests" id: contactsJsonDbDetailsSavingE2ETests ContactModel { id: model manager: "jsondb" autoUpdate: true } // Tests Contact { id: contactWithBirthday Birthday { birthday: new Date(2012,0,2) } } function test_saveBirthday() { initTestForModel(model); model.saveContact(contactWithBirthday); waitForContactsChanged(); var contacts = queryContactsInJsonDb(); verify(contacts, "contacts is defined"); compare(contacts.length, 1, "contact present"); compare(contacts[0].details.birthday, "2012-01-02", "birthday matches"); } // Init & teardown function initTestCase() { waitForModelToBeReady(model); cleanupContacts(); initJsonDbAccess(); } function init() { cleanupContacts(); } function cleanup() { cleanupContacts(); } function cleanupTestCase() { cleanupContacts(); } function waitForModelToBeReady(model) { initTestForModel(model); waitForContactsChanged(); } function cleanupContacts() { emptyContacts(model); } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_jsondb_partitions_e2e.qml000066400000000000000000000373001233466112000311260ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSavingTestCase { name: "ContactsJsonDbPartitionsE2ETests" id: contactsJsonDbPartitionsE2ETests property ContactModel model function createModel() { model = Qt.createQmlObject( 'import QtContacts 5.0;' + 'ContactModel {' + 'manager: "jsondb";' + 'autoUpdate: true;' + '}', contactsJsonDbPartitionsE2ETests); } function destroyModel() { if (model) { model.autoUpdate = false; model.destroy(); } } // Partitions under test ContactsJsonDbPartitions { id: jsonDbPartitions } property alias defaultPartition: jsonDbPartitions.userPartition property alias testPartition: jsonDbPartitions.systemPartition // Tests Contact { id: contactInTheDefaultPartition Name { firstName: "contactInTheDefaultPartition" } } function test_fetchContactWithoutSpecifyingPartition() { createAndInitModelForPartition(); // does not set partition createContactToPartition({name: {firstName: "contactFromDefaultPartition"}}, defaultPartition); waitForContactsChanged(); compare(model.contacts.length, 1, "model has a contact"); compare(model.contacts[0].name.firstName, "contactFromDefaultPartition", "detail values match"); } function test_fetchContactFromTheDefaultPartitionWhenThereAreContactsInAnotherPartition() { createAndInitModelForPartition(defaultPartition); // create a contact to the other partition (does not change the model) listenToContactsChanged(); createContactToPartition({}, testPartition); verifyNoContactsChangedReceived(); // create a contact to and fetch it from the default partition createContactToPartition({name: {firstName: "contactFromDefaultPartition"}}, defaultPartition); waitForContactsChanged(); compare(model.contacts.length, 1, "model has a contact"); compare(model.contacts[0].name.firstName, "contactFromDefaultPartition", "detail values match"); } function test_fetchContactsToModelFromGivenPartition() { createAndInitModelForPartition(testPartition); createContactToPartition({name: {firstName: "contactFromGivenPartition"}}, testPartition); waitForContactsChanged(); compare(model.contacts.length, 1, "model has a contact"); compare(model.contacts[0].name.firstName, "contactFromGivenPartition", "detail values match"); } function test_createNewContactAndSaveItToDefaultPartition() { createAndInitModelForPartition(defaultPartition); model.saveContact(contactInTheDefaultPartition); waitForContactsChanged(); var contacts = queryContactsInPartition(defaultPartition); verify(contacts, "contacts is defined"); compare(contacts.length, 1, "partition has a contact"); compare(model.contacts[0].name.firstName, contactInTheDefaultPartition.name.firstName, "detail values match"); } Contact { id: contactInTheTargetPartition Name { firstName: "contactInTheTargetPartition" } } function test_createNewContactAndSaveItToGivenPartition() { createAndInitModelForPartition(testPartition); model.saveContact(contactInTheTargetPartition, testPartition.storageLocation); waitForContactsChanged(); var contacts = queryContactsInPartition(testPartition); verify(contacts, "contacts is defined"); compare(contacts.length, 1, "partition has a contact"); compare(model.contacts[0].name.firstName, contactInTheTargetPartition.name.firstName, "detail values match"); } function test_createNewContactAndSaveItToPartitionWhichIsNotInTheModel() { createAndInitModelForPartition(defaultPartition); model.saveContact(contactInTheTargetPartition, testPartition.storageLocation); // the completion of the save does not signal through the model since it does not change wait(500); var contacts = queryContactsInPartition(testPartition); verify(contacts, "contacts is defined"); compare(contacts.length, 1, "partition has a contact"); compare(contacts[0].name.firstName, contactInTheTargetPartition.name.firstName, "detail values match"); } function test_removeContactFromGivenPartition() { createAndInitModelForPartition(testPartition); createContactToPartition({name: {firstName: "contactToBeRemoved"}}, testPartition); waitForContactsChanged(); compare(model.contacts.length, 1, "guard: model has a contact"); model.removeContact(model.contacts[0].contactId); waitForContactsChanged(); var contacts = queryContactsInPartition(testPartition); verify(contacts, "contacts is is defined"); compare(contacts.length, 0, "partition is empty"); } function test_fetchContactFromGivenPartitionChangeItAndSaveBackToTheSamePartition() { createAndInitModelForPartition(testPartition); createContactToPartition({name: {firstName: "contactToBeChanged"}}, testPartition); waitForContactsChanged(); compare(model.contacts.length, 1, "guard: model has a contact"); var contact = model.contacts[0]; var newFirstName = "contactWithNewFirstName"; contact.name.firstName = newFirstName; model.saveContact(contact); waitForContactsChanged(); var contacts = queryContactsInPartition(testPartition); compare(contacts.length, 1, "partition has a contact"); compare(model.contacts[0].name.firstName, newFirstName, "first name"); } function test_updateContactInAnotherPartitionDoesNotChangeModel() { createAndInitModelForPartition(defaultPartition); createContactToPartition({name: {firstName: "old"}}, testPartition); compare(model.contacts.length, 0, "guard: model does not see the contact"); var contacts = queryContactsInPartition(testPartition); compare(contacts.length, 1, "target partition has a contact"); listenToContactsChanged(); updateContactWithUuidInTargetPartition(contacts[0]["_uuid"], {name: {firstName: "new"}}, testPartition); verifyNoContactsChangedReceived(); compare(model.contacts.length, 0, "model has not changed"); } function test_removeContactInAnotherPartitionDoesNotChangeModel() { createAndInitModelForPartition(defaultPartition); createContactToPartition({}, testPartition); var contacts = queryContactsInPartition(testPartition); compare(contacts.length, 1, "target partition has a contact"); listenToContactsChanged(); removeContactWithUuidFromPartition(contacts[0]["_uuid"], testPartition); verifyNoContactsChangedReceived(); } function test_setStorageLocationToGivenPartitionShouldFetchContacts() { createAndInitModelForPartition(defaultPartition); createContactToPartition({name: {firstName: "contactFromGivenPartition"}}, testPartition); // the completion of the save does not signal through the model since it does not change wait(500); model.storageLocations = testPartition.storageLocation; waitForContactsChanged(); compare(model.contacts.length, 1, "model has a contact"); compare(model.contacts[0].name.firstName, "contactFromGivenPartition", "detail values match"); } function test_setIncorrectStorageLocationRaisesError() { createAndInitModelForPartition(testPartition); model.storageLocations = 0; compare(model.error, "BadArgument", "error"); } function test_setMultipleStorageLocationsRaisesError() { createAndInitModelForPartition(testPartition); model.storageLocations = ContactModel.UserDataStorage | ContactModel.SystemStorage; compare(model.error, "NotSupported", "error"); } function test_fetchContactsFromThePartitionInTheModel() { createAndInitModelForPartition(defaultPartition); initContactsFetchTestForModel(model); createContactToPartition({}, defaultPartition); waitForContactsChanged(); compare(model.contacts.length, 1, "guard: model has a contact"); var id = model.contacts[0].contactId; model.fetchContacts([id]); waitForContactsFetched(); compare(lastContactsFetched.length, 1, "contacts length"); compare(lastContactsFetched[0].contactId, id, "contact id"); } function test_fetchContactsFromPartitionNotInTheModel() { createAndInitModelForPartition(defaultPartition); initContactsFetchTestForModel(model); createContactToPartition({}, testPartition); // the completion of the save does not signal through the model since it does not change wait(500); var id = queryContactIdOfTheContactOnPartition(testPartition); model.fetchContacts([id]); waitForContactsFetched(); compare(lastContactsFetched.length, 1, "contacts length"); compare(lastContactsFetched[0].contactId, id, "contact id"); } function test_fetchContactsFromMultiplePartitions() { createAndInitModelForPartition(defaultPartition); initContactsFetchTestForModel(model); createContactToPartition({}, defaultPartition); waitForContactsChanged(); createContactToPartition({}, testPartition); // the completion of the save does not signal through the model since it does not change wait(500); var id1 = queryContactIdOfTheContactOnPartition(defaultPartition); var id2 = queryContactIdOfTheContactOnPartition(testPartition); model.fetchContacts([id1, id2]); waitForContactsFetched(); compare(lastContactsFetched.length, 2, "contacts length"); compare(lastContactsFetched[0].contactId, id1, "contact id 1"); compare(lastContactsFetched[1].contactId, id2, "contact id 2"); } property SignalSpy contactsFetchedSpy property list lastContactsFetched function onContactsFetched(requestId, fetchedContacts) { logDebug("onContactsFetched" + ": fetchedContacts " + JSON.stringify(fetchedContacts)); lastContactsFetched = fetchedContacts; } function initContactsFetchTestForModel(model) { contactsFetchedSpy = initTestForTargetListeningToSignal(model, "contactsFetched"); lastContactsFetched = []; model.contactsFetched.connect(onContactsFetched); } function waitForContactsFetched() { waitForTargetSignal(contactsFetchedSpy); } // Init & teardown function initTestCase() { initJsonDbAccess(); } function init() { cleanupContacts(); } function cleanup() { destroyModel(); cleanupContacts(); } function cleanupTestCase() { cleanupContacts(); } function cleanupContacts() { emptyContactsInPartition(defaultPartition); emptyContactsInPartition(testPartition); } // Helpers function createAndInitModelForPartition(partition) { if (partition === defaultPartition) { createModelForPartition(defaultPartition); } else if (partition === testPartition) { createModelForPartition(testPartition); waitForModelToBeReady(model); // change to another partition } else if (!partition) { createModelForPartition(); // does not set partition } else { fail("Failed to initialize partition for the model under test.") } initTestForModel(model); } function createModelForPartition(partition) { createModel(); waitForModelToBeReady(model); if (partition) model.storageLocations = partition.storageLocation; } function waitForModelToBeReady(model) { initTestForModel(model); waitForContactsChanged(); } function emptyContactsInPartition(partition) { partition.testHelper.emptyContacts(); } function createContactToPartition(contact, partition) { partition.testHelper.createContactToJsonDb(contact); } function queryContactsInPartition(partition) { return partition.testHelper.queryContactsInJsonDb(); } function updateContactWithUuidInTargetPartition(contactUuid, update, partition) { partition.testHelper.updateContactWithUuidInJsonDb(contactUuid, update); } function removeContactWithUuidFromPartition(contactUuid, partition) { partition.testHelper.removeContactWithUuidFromJsonDb(contactUuid); } function queryContactIdOfTheContactOnPartition(partition) { var contacts = queryContactsInPartition(partition); compare(contacts.length, 1, "guard: partition has one contact"); var uuid = contacts[0]["_uuid"]; return convertJsonDbUuidOnPartitionToContactId(uuid, partition); } function convertJsonDbUuidOnPartitionToContactId(uuid, partition) { return partition.testHelper.convertJsonDbUuidAndStorageLocationToContactId(uuid, partition.storageLocation); } ContactsJsonDbTestHelper { id: jsonDbTestHelperForDefaultPartition } ContactsJsonDbTestHelper { id: jsonDbTestHelperForTestPartition } function initJsonDbAccess() { jsonDbTestHelperForDefaultPartition.partition = defaultPartition.name; jsonDbTestHelperForDefaultPartition.initTestHelper(); defaultPartition.testHelper = jsonDbTestHelperForDefaultPartition; jsonDbTestHelperForTestPartition.partition = testPartition.name; jsonDbTestHelperForTestPartition.initTestHelper(); testPartition.testHelper = jsonDbTestHelperForTestPartition; } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_jsondb_to_model_notification_e2e.qml000066400000000000000000000337721233466112000333130ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsJsonDbTestCase { name: "ContactsJsonDbToModelNotificationE2ETests" id: contactsJsonDbToModelNotificationE2ETests property ContactModel model function createModel() { model = Qt.createQmlObject( 'import QtContacts 5.0;' + 'ContactModel {' + 'manager: "jsondb";' + 'autoUpdate: true;' + '}', contactsJsonDbToModelNotificationE2ETests); } function destroyModel() { model.autoUpdate = false; model.destroy(); } function test_createContactShouldUpdateModel() { initTestForModel(model); createContactToJsonDb({}); waitForContactsChanged(); compare(model.contacts.length, 1, "model has a contact"); } function test_createContactPassesDetailsToModel() { initTestForModel(model); createContactToJsonDb({name: {firstName: "Test"} }); waitForContactsChanged(); verify(model.contacts[0].name, "name exists"); compare(model.contacts[0].name.firstName, "Test", "first name"); } function test_createContactWhenAutoUpdateIsOff() { initTestForModel(model); model.autoUpdate = false; listenToContactsChanged(); createContactToJsonDb({}); verifyNoContactsChangedReceived(); compare(model.contacts.length, 0, "model should be empty now"); emptyContacts(model); } Contact { id: contactToBeRemoved } function test_removeContactShouldUpdateModel() { initTestForModel(model); model.saveContact(contactToBeRemoved); waitForContactsChanged(); var contact = model.contacts[0]; verify(contact); removeContactFromJsonDb(contact); waitForContactsChanged(); compare(model.contacts.length, 0, "model is empty"); } Contact { id: contactToBeRemovedWhenAutoUpdateIsOff } function test_removeContactWhenAutoUpdateIsOff() { initTestForModel(model); model.autoUpdate = false; model.saveContact(contactToBeRemovedWhenAutoUpdateIsOff); model.update(); waitForContactsChanged(); var contact = model.contacts[0]; listenToContactsChanged(); removeContactFromJsonDb(contact); verifyNoContactsChangedReceived(); compare(model.contacts.length, 1, "model has a contact"); emptyContacts(model); } Contact { id: contactToBeUpdated Name { firstName: "old" } } function test_updateContactShouldUpdateModel() { initTestForModel(model); model.saveContact(contactToBeUpdated); waitForContactsChanged(); var contact = model.contacts[0]; updateContactInJsonDb(contact, {name: {firstName: "new"}}); waitForContactsChanged(); compare(model.contacts.length, 1, "model is not empty"); compare(model.contacts[0].name.firstName, "new", "first name"); } Contact { id: contactToBeUpdatedWhenAutoUpdateIsOff Name { firstName: "old" } } function test_updateContactWhenAutoUpdateIsOff() { initTestForModel(model); model.autoUpdate = false; model.saveContact(contactToBeUpdatedWhenAutoUpdateIsOff); model.update(); waitForContactsChanged(); listenToContactsChanged(); var contact = model.contacts[0]; updateContactInJsonDb(contact, {name: {firstName: "new"}}); verifyNoContactsChangedReceived(); //model is not updated so the firstName must be still the "old" compare(model.contacts[0].name.firstName, "old", "first name"); emptyContacts(model); } SortOrder { id: sortByFirstName detail: ContactDetail.Name field: Name.FirstName direction: Qt.AscendingOrder } Contact { id: sorting_creating_secondContact Name { firstName: "B" } } function test_createContactSortsContactsInModel() { initTestForModel(model); model.sortOrders = [sortByFirstName]; waitForContactsChanged(); model.saveContact(sorting_creating_secondContact); waitForContactsChanged(); createContactToJsonDb({name: {firstName: "A"}}); waitForContactsChanged(); compare(model.contacts.length, 2, "contacts length"); compare(model.contacts[0].name.firstName, "A", "first contact"); compare(model.contacts[1].name.firstName, "B", "second contact"); } Contact { id: sorting_updating_firstContact Name { firstName: "B" } } Contact { id: sorting_updating_secondContact Name { firstName: "C" } } function test_updateContactDetailSortsContactsInModel() { initTestForModel(model); model.sortOrders = [sortByFirstName]; waitForContactsChanged(); model.saveContact(sorting_updating_firstContact); waitForContactsChanged(); model.saveContact(sorting_updating_secondContact); waitForContactsChanged(); var contact = model.contacts[1]; compare(contact.name.firstName, "C", "second contact first name"); updateContactInJsonDb(contact, {name: {firstName: "A"}}); waitForContactsChanged(); compare(model.contacts.length, 2, "contacts length"); compare(model.contacts[0].name.firstName, "A", "first contact"); compare(model.contacts[1].name.firstName, "B", "second contact"); } Contact { id: sorting_removing_firstContact Name { firstName: "A" } } Contact { id: sorting_removing_secondContact Name { firstName: "B" } } Contact { id: sorting_removing_thirdContact Name { firstName: "C" } } function test_removeContactSortsContactsInModel() { initTestForModel(model); model.sortOrders = [sortByFirstName]; waitForContactsChanged(); model.saveContact(sorting_removing_thirdContact); waitForContactsChanged(); model.saveContact(sorting_removing_secondContact); waitForContactsChanged(); model.saveContact(sorting_removing_firstContact); waitForContactsChanged(); var contact = model.contacts[1]; removeContactFromJsonDb(contact); waitForContactsChanged(); compare(model.contacts.length, 2, "contacts length"); compare(model.contacts[0].name.firstName, "A", "first contact"); compare(model.contacts[1].name.firstName, "C", "second contact"); } DetailFilter { id: filterByFirstName detail: ContactDetail.Name field: Name.FirstName matchFlags: Filter.MatchExactly } function test_createContactMatchingTheFilter() { initTestForModel(model); filterByFirstName.value = "A"; model.filter = filterByFirstName; waitForContactsChanged(); createContactToJsonDb({name: {firstName: "A"}}); waitForContactsChanged(); compare(model.contacts.length, 1, "contacts length"); compare(model.contacts[0].name.firstName, "A", "first name"); } function test_createContactNotMatchingTheFilter() { initTestForModel(model); filterByFirstName.value = "A"; model.filter = filterByFirstName; waitForContactsChanged(); createContactToJsonDb({name: {firstName: "B"}}); compare(model.contacts.length, 0, "contacts length"); } Contact { id: filtering_removing_matchingContact Name { firstName: "A" } } function test_removeContactMatchingTheFilter() { initTestForModel(model); filterByFirstName.value = "A"; model.filter = filterByFirstName; waitForContactsChanged(); model.saveContact(filtering_removing_matchingContact); waitForContactsChanged(); var contact = model.contacts[0]; removeContactFromJsonDb(contact); waitForContactsChanged(); compare(model.contacts.length, 0, "contacts length"); } Contact { id: filtering_removing_nonmatchingContact Name { firstName: "B" } } function test_removeContactNotMatchingTheFilter() { initTestForModel(model); model.saveContact(filtering_removing_nonmatchingContact); waitForContactsChanged(); var id = model.contacts[0].contactId; filterByFirstName.value = "A"; model.filter = filterByFirstName; waitForContactsChanged(); compare(model.contacts.length, 0, "contacts length"); var count = spy.count; removeContactFromJsonDb({contactId: id}); wait(500); compare(spy.count, count, "spy does not receive a signal"); } Contact { id: filtering_updating_nonmatchingContact Name { firstName: "B" } } function test_updateContactDetailToMatchTheFilter() { initTestForModel(model); model.saveContact(filtering_updating_nonmatchingContact); waitForContactsChanged(); var id = model.contacts[0].contactId; filterByFirstName.value = "A"; model.filter = filterByFirstName; waitForContactsChanged(); compare(model.contacts.length, 0, "model is empty"); updateContactInJsonDb({contactId: id}, {name: {firstName: "A"}}); waitForContactsChanged(); compare(model.contacts.length, 1, "model is not empty"); compare(model.contacts[0].name.firstName, "A", "first name"); } Contact { id: filtering_updating_matchingContact Name { firstName: "A" } } function test_updateContactDetailToNotMatchTheFilter() { initTestForModel(model); model.saveContact(filtering_updating_matchingContact); waitForContactsChanged(); var id = model.contacts[0].contactId; filterByFirstName.value = "A"; model.filter = filterByFirstName; waitForContactsChanged(); compare(model.contacts.length, 1, "model is not empty"); updateContactInJsonDb({contactId: id}, {name: {firstName: "B"}}); waitForContactsChanged(); compare(model.contacts.length, 0, "model is empty"); } function initTestCase() { cleanupContacts(); } function cleanupTestCase() { cleanupContacts(); } function init() { initJsonDbAccess(); createModel(); waitForModelToBeReady(model); } function waitForModelToBeReady(model) { initTestForModel(model); waitForContactsChanged(); } function cleanup() { cleanupContacts(); destroyModel(); } function cleanupContacts() { var modelForCleanup = Qt.createQmlObject( 'import QtContacts 5.0;' + 'ContactModel {' + 'manager: "jsondb";' + 'autoUpdate: true;' + '}', contactsJsonDbToModelNotificationE2ETests); waitForModelToBeReady(modelForCleanup); emptyContacts(modelForCleanup); modelForCleanup.autoUpdate = false; modelForCleanup.destroy(); } function compareContactArrays(actual, expected) { compare(actual.length, expected.length, "length"); for (var i = 0; i < expected.length; i++) { compareContacts(actual[i], expected[i]); } } function compareContacts(actual, expected) { if (expected.name) { compare(actual.name.firstName, expected.name.firstName, 'name.firstName'); compare(actual.name.lastName, expected.name.lastName, 'name.lastName'); } if (expected.email) { compare(actual.email.emailAddress, expected.email.emailAddress, 'email.emailAddress'); } } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_model_to_model_notification_e2e.qml000066400000000000000000000205151233466112000331230ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSavingTestCase { name: "ContactsModelToModelNotificationE2ETests" id: contactsModelToModelNotificationE2ETests ContactModel { id: modifiedModel manager: getManagerUnderTest() autoUpdate: false // otherwise this model will distract the test helper functions } ContactModel { id: notifiedModel manager: getManagerUnderTest() autoUpdate:true } Contact { id: contact } function test_createContactShouldUpdateOtherModels() { initTestForModel(notifiedModel); modifiedModel.saveContact(contact); waitForContactsChanged(); compare(notifiedModel.contacts.length, 1, "contacts updated in the notified model"); } Contact { id: contactWithDetailToBeVerified Name { firstName: "contactWithDetailToBeVerified" } } function test_createContactPassesDetailsToOtherModels() { initTestForModel(notifiedModel); modifiedModel.saveContact(contactWithDetailToBeVerified); waitForContactsChanged(); compareContactArrays(notifiedModel.contacts, [contactWithDetailToBeVerified], "contacts updated in the notified model"); } Contact { id: firstOfMultipleContacts } Contact { id: secondOfMultipleContacts } function test_createMultipleContactsShouldUpdateOtherModels() { initTestForModel(notifiedModel); emptyContacts(notifiedModel); modifiedModel.saveContact(firstOfMultipleContacts); modifiedModel.saveContact(secondOfMultipleContacts); waitForContactsChanged(); if (notifiedModel.contacts.length < 2) { waitForContactsChanged(); } compare(notifiedModel.contacts.length, 2, "contacts updated in the notified model"); } Contact { id: contactToBeUpdated Name { firstName: "old" } } function test_updateContactShouldUpdateOtherModels() { initTestForModel(modifiedModel); modifiedModel.saveContact(contactToBeUpdated); modifiedModel.update(); waitForContactsChanged(); var contact = modifiedModel.contacts[0]; initTestForModel(notifiedModel); if (notifiedModel.contacts.length < 1) waitForContactsChanged(); compare(notifiedModel.contacts.length, 1, "guard: contact present in the notified model"); contact.name.firstName = "new"; modifiedModel.saveContact(contact); waitForContactsChanged(); compare(notifiedModel.contacts[0].name.firstName, "new", "first name"); } Contact { id: contactWithNoDetails } Name { id: detailForContactWithNoDetails firstName: "detail" } function pending_updateContactByAddingDetail() { initTestForModel(modifiedModel); modifiedModel.saveContact(contactWithNoDetails); waitForContactsChanged(); initTestForModel(notifiedModel); compareContactArrays(notifiedModel.contacts, [contactWithNoDetails], "contact present in the notified model"); initTestForModel(notifiedModel); contactWithNoDetails.addDetail(detailForContactWithNoDetails); modifiedModel.saveContact(contactWithNoDetails); waitForContactsChanged(); compareContactArrays(notifiedModel.contacts, [contactWithDetail], "contact updated in the notified model"); } Contact { id: contactWithDetail Name { id: detailOfContactWithDetail firstName: "detail" } } function pending_updateContactByRemovingDetail() { initTestForModel(modifiedModel); modifiedModel.saveContact(contactWithDetail); waitForContactsChanged(); initTestForModel(notifiedModel); compareContactArrays(notifiedModel.contacts, [contactWithDetail], "contact present in the notified model"); initTestForModel(notifiedModel); contactWithDetail.removeDetail(detailOfContactWithDetail); modifiedModel.saveContact(contactWithDetail); waitForContactsChanged(); compareContactArrays(notifiedModel.contacts, [contactWithDetail], "contact updated in the notified model"); } function test_removeContactShouldUpdateOtherModel() { initTestForModel(modifiedModel); modifiedModel.saveContact(contact); modifiedModel.update(); waitForContactsChanged(); var id = modifiedModel.contacts[0].contactId; initTestForModel(notifiedModel); if (notifiedModel.contacts.length < 1) waitForContactsChanged(); compare(notifiedModel.contacts.length, 1, "guard: contact present in the notified model"); modifiedModel.removeContact(id); waitForContactsChanged(); compare(notifiedModel.contacts.length, 0, "contacts removed from the notified model"); } function initTestCase() { initTestForModel(notifiedModel); waitForContactsChanged(); emptyContacts(notifiedModel); } function init() { if (notifiedModel.manager == 'memory') skip("memory backend does not support this at the moment"); initTestForModel(notifiedModel); emptyContacts(notifiedModel); } function cleanup() { initTestForModel(notifiedModel); emptyContacts(notifiedModel); } function cleanupTestCase() { initTestForModel(notifiedModel); emptyContacts(notifiedModel); finishTestForModel(notifiedModel); finishTestForModel(modifiedModel); } function compareContactArrays(actual, expected, message) { compare(actual.length, expected.length, message + ": length"); for (var i = 0; i < expected.length; i++) { compareContacts(actual[i], expected[i], message); } } function compareContacts(actual, expected, message) { if (expected.name) { compare(actual.name.firstName, expected.name.firstName, message + ': name.firstName'); compare(actual.name.lastName, expected.name.lastName, message + ': name.lastName'); } if (expected.email) { compare(actual.email.emailAddress, expected.email.emailAddress, message + ': email.emailAddress'); } } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_remove_contacts_e2e.qml000066400000000000000000000112001233466112000305550ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSavingTestCase { name: "ContactsRemoveContactsE2ETests" id: contactsRemoveContactsE2ETests ContactModel { id: model manager: getManagerUnderTest() autoUpdate: true } Contact { id: firstContact } Contact { id: secondContact } function init() { initTestForModel(model); emptyContacts(model); model.saveContact(firstContact); waitForContactsChanged(); model.saveContact(secondContact); waitForContactsChanged(); } function cleanup() { emptyContacts(model); } // Tests function test_removeEmptyList() { model.removeContacts([]); wait(500); compare(model.contacts.length, 2, "contacts.length"); } function test_removeInvalidId() { model.removeContacts(["invalid"]); wait(500); compare(model.contacts.length, 2, "contacts.length"); } function test_removeOneContact() { var id1 = model.contacts[0].contactId; var id2 = model.contacts[1].contactId; model.removeContacts([id1]); waitForContactsChanged(); compare(model.contacts.length, 1, "contacts.length"); compare(model.contacts[0].contactId, id2, "id of the second contact"); } function test_removeMultipleContacts() { var id1 = model.contacts[0].contactId; var id2 = model.contacts[1].contactId; model.removeContacts([id1, id2]); waitForContactsChanged(); if (model.contacts.length > 0) waitForContactsChanged(); compare(model.contacts.length, 0, "contacts is empty"); } function test_saveRemovedContactFails() { var id1 = model.contacts[0].contactId; var contactToFirstRemoveAndThenSave = model.contacts[0]; model.removeContacts([id1]); waitForContactsChanged(); var errorSpy = initTestForTargetListeningToSignal(model, "errorChanged"); model.saveContact(contactToFirstRemoveAndThenSave); waitForTargetSignal(errorSpy); compare(model.error, "DoesNotExist", "model.error"); } // Init & teardown function initTestCase() { initTestForModel(model); waitForContactsChanged(); // The wait is needed so the model is populated // (e.g. with garbage left from previous test runs) // before cleanup() is called. emptyContacts(model); } function cleanupTestCase() { emptyContacts(model); finishTestForModel(model); } // Helpers function verifyIsUndefined(object) { verify(!object, "Object " + object + " is undefined"); } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_remove_detail_e2e.qml000066400000000000000000000116421233466112000302130ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSavingTestCase { id: testcase name: "ContactsRemoveDetailE2ETests" ContactModel { id: model manager: getManagerUnderTest() autoUpdate: true } // Tests Contact { id: contact1 Name { firstName: "1" } } function test_remove_detail() { contact1.removeDetail(contact1.detail(ContactDetail.Name)) verifyIsUndefined(contact1.detail(ContactDetail.Name)) } Contact { id: contact2 Name { firstName: "2" } } function test_remove_detail_of_contact_in_the_model() { model.saveContact(contact2) waitForContactsChanged() var contact = model.contacts[0] contact.removeDetail(contact.detail(ContactDetail.Name)) verifyIsUndefined(contact.detail(ContactDetail.Name)); } Contact { id: contact3 Name { firstName: "3" } } function test_remove_detail_and_save_modified() { model.saveContact(contact3) waitForContactsChanged() var contact = model.contacts[0] contact.removeDetail(contact.detail(ContactDetail.Name)) contact.save() waitForContactsChanged() verifyIsUndefined(contact.detail(ContactDetail.Name)) } Contact { id: contact3_1 Name { firstName: "3_1" } } function test_remove_detail_save_modified_and_refetch_from_model() { model.saveContact(contact3_1) waitForContactsChanged() var contact = model.contacts[0] contact.removeDetail(contact.detail(ContactDetail.Name)) contact.save() waitForContactsChanged() contact = model.contacts[0] verifyIsUndefined(contact.detail(ContactDetail.Name)) } Contact { id: contact4 Name { firstName: "4" } } function test_remove_detail_and_save_through_model() { model.saveContact(contact4) waitForContactsChanged() var contact = model.contacts[0] contact.removeDetail(contact.detail(ContactDetail.Name)) model.saveContact(contact) waitForContactsChanged() contact = model.contacts[0] verifyIsUndefined(contact.detail(ContactDetail.Name)) } // Init & teardown function initTestCase() { initTestForModel(model); waitForContactsChanged(); // The wait is needed so the model is populated // (e.g. with garbage left from previous test runs) // before cleanup() is called. emptyContacts(model); } function init() { initTestForModel(model); } function cleanup() { emptyContacts(model); } function cleanupTestCase() { emptyContacts(model); finishTestForModel(model); } // Helpers function verifyIsUndefined(object) { verify(!object, "Object " + object + " is undefined"); } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_save_contact_e2e.qml000066400000000000000000000133371233466112000300500ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSavingTestCase { name: "ContactsSaveContactE2ETests" id: contactsSaveContactE2ETests ContactModel { id: model manager: getManagerUnderTest() autoUpdate: true } // Tests Contact { id: singleContact } function test_saveContact() { model.saveContact(singleContact); waitForContactsChanged(); compare(model.contacts.length, 1, "contacts.length"); } Contact { id: contactWithDetail Name { firstName: "Joe" } } function test_saveContactSavesDetails() { model.saveContact(contactWithDetail); waitForContactsChanged(); compare(model.contacts[0].name.firstName, contactWithDetail.name.firstName, "contact name"); } function test_saveDynamicallyCreatedContact() { var contact = Qt.createQmlObject( 'import QtContacts 5.0;' + 'Contact {' + ' Name {' + ' firstName: "A"' + ' }' + '}', contactsSaveContactE2ETests); model.saveContact(contact); waitForContactsChanged(); compare(model.contacts.length, 1, "contacts.length"); compare(model.contacts[0].name.firstName, contact.name.firstName, 'contact name'); } Contact { id: firstOfMultipleContacts Name { firstName: "A" } } Contact { id: secondOfMultipleContacts Name { firstName: "B" } } function test_saveMultipleContacts() { model.saveContact(firstOfMultipleContacts); waitForContactsChanged(); model.saveContact(secondOfMultipleContacts); waitForContactsChanged(); compare(model.contacts.length, 2, "contacts.length"); compare(model.contacts[0].name.firstName, firstOfMultipleContacts.name.firstName, 'contacts[0].name.firstName'); compare(model.contacts[1].name.firstName, secondOfMultipleContacts.name.firstName, 'contacts[1].name.firstName'); } Contact { id: contactToSaveMultipleTimes } function test_saveTheSameContactMultipleTimes() { model.saveContact(contactToSaveMultipleTimes); waitForContactsChanged(); model.saveContact(contactToSaveMultipleTimes); waitForContactsChanged(); compare(model.contacts.length, 2, "contacts.length"); verify(model.contacts[0].contactId !== model.contacts[1].contactId, "contact ids are different"); } Contact { id: contactToSaveMultipleTimesWithoutWaitingForTheModel } function test_saveTheSameContactMultipleTimesWithoutWaitingForTheModel() { model.saveContact(contactToSaveMultipleTimes); model.saveContact(contactToSaveMultipleTimes); waitForContactsChanged(); if (model.contacts.length < 2) waitForContactsChanged(); compare(model.contacts.length, 2, "contacts.length"); verify(model.contacts[0].contactId !== model.contacts[1].contactId, "contact ids are different"); } // Init & teardown function initTestCase() { initTestForModel(model); waitForContactsChanged(); // The wait is needed so the model is populated // (e.g. with garbage left from previous test runs) // before cleanup() is called. emptyContacts(model); } function init() { initTestForModel(model); } function cleanup() { emptyContacts(model); } function cleanupTestCase() { emptyContacts(model); finishTestForModel(model); } // Helpers function verifyIsUndefined(object) { verify(!object, "Object " + object + " is undefined"); } } tests/auto/contacts/qmlcontacts/testcases/tst_contacts_sorting_e2e.qml000066400000000000000000000225521233466112000270630ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 ContactsSavingTestCase { name: "ContactsSortingTests" ContactModel { id: model manager: getManagerUnderTest() autoUpdate:true } SortOrder { id: sortOrderByFirstName detail: ContactDetail.Name field: Name.FirstName direction: Qt.AscendingOrder } Contact { id: contactWithFirstName1 Name { firstName: "A" } } Contact { id: contactWithFirstName2 Name { firstName: "B" } } function test_sortByFirstName() { model.sortOrders = [sortOrderByFirstName]; waitForContactsChanged(); model.saveContact(contactWithFirstName2); waitForContactsChanged(); model.saveContact(contactWithFirstName1); waitForContactsChanged(); compareContactArrays(model.contacts, [contactWithFirstName1, contactWithFirstName2]); } SortOrder { id: sortOrderByLastName detail: ContactDetail.Name field: Name.LastName direction: Qt.AscendingOrder } Contact { id: contactWithLastName1 Name { lastName: "A" } } Contact { id: contactWithLastName2 Name { lastName: "B" } } function test_sortByLastName() { model.sortOrders = [sortOrderByLastName]; waitForContactsChanged(); model.saveContact(contactWithLastName2); waitForContactsChanged(); model.saveContact(contactWithLastName1); waitForContactsChanged(); compareContactArrays(model.contacts, [contactWithLastName1, contactWithLastName2]); } SortOrder { id: sortOrderByEmailAddress detail: ContactDetail.Email field: EmailAddress.EmailAddress direction: Qt.AscendingOrder } Contact { id: contactWithEmailAddress1 EmailAddress { emailAddress: "a@a" } } Contact { id: contactWithEmailAddress2 EmailAddress { emailAddress: "b@b" } } function test_sortByEmail() { model.sortOrders = [sortOrderByEmailAddress]; waitForContactsChanged(); model.saveContact(contactWithEmailAddress2); waitForContactsChanged(); model.saveContact(contactWithEmailAddress1); waitForContactsChanged(); compareContactArrays(model.contacts, [contactWithEmailAddress1, contactWithEmailAddress2]); } Contact { id: contactWithFirstAndLastName1 Name { lastName: "A" firstName: "E" } } Contact { id: contactWithFirstAndLastName2 Name { lastName: "B" firstName: "C" } } Contact { id: contactWithFirstAndLastName3 Name { lastName: "B" firstName: "D" } } function test_sortByLastAndFirstName() { skip("Backends do not support this at the moment.") model.sortOrders = [sortOrderByFirstName, sortOrderByLastName]; waitForContactsChanged(); model.saveContact(contactWithFirstAndLastName3); waitForContactsChanged(); model.saveContact(contactWithFirstAndLastName2); waitForContactsChanged(); model.saveContact(contactWithFirstAndLastName1); waitForContactsChanged(); compareContactArrays(model.contacts, [contactWithFirstAndLastName1, contactWithFirstAndLastName2, contactWithFirstAndLastName3]); } SortOrder { id: sortOrderInDefaultOrder detail: ContactDetail.Name field: Name.FirstName } Contact { id: contactInDefaultOrder1 Name { firstName: "A" } } Contact { id: contactInDefaultOrder2 Name { firstName: "B" } } function test_sortInDefaultOrder() { model.sortOrders = [sortOrderInDefaultOrder]; waitForContactsChanged(); model.saveContact(contactInDefaultOrder2); waitForContactsChanged(); model.saveContact(contactInDefaultOrder1); waitForContactsChanged(); compareContactArrays(model.contacts, [contactInDefaultOrder1, contactInDefaultOrder2]); } SortOrder { id: sortOrderInAscendingOrder detail: ContactDetail.Name field: Name.FirstName direction: Qt.AscendingOrder } Contact { id: contactInAscendingOrder1 Name { firstName: "A" } } Contact { id: contactInAscendingOrder2 Name { firstName: "B" } } function test_sortInAscendingOrder() { model.sortOrders = [sortOrderInAscendingOrder]; waitForContactsChanged(); model.saveContact(contactInAscendingOrder2); waitForContactsChanged(); model.saveContact(contactInAscendingOrder1); waitForContactsChanged(); compareContactArrays(model.contacts, [contactInAscendingOrder1, contactInAscendingOrder2]); } SortOrder { id: sortOrderInDescendingOrder detail: ContactDetail.Name field: Name.FirstName direction: Qt.DescendingOrder } Contact { id: contactInDescendingOrder1 Name { firstName: "B" } } Contact { id: contactInDescendingOrder2 Name { firstName: "A" } } function test_sortInDescendingOrder() { model.sortOrders = [sortOrderInDescendingOrder]; waitForContactsChanged(); model.saveContact(contactInDescendingOrder2); waitForContactsChanged(); model.saveContact(contactInDescendingOrder1); waitForContactsChanged(); compareContactArrays(model.contacts, [contactInDescendingOrder1, contactInDescendingOrder2]); } // Init & teardown function initTestCase() { initTestForModel(model); waitForContactsChanged(); // The wait is needed so the model is populated // (e.g. with garbage left from previous test runs) // before cleanup() is called. emptyContacts(model); } function init() { initTestForModel(model); emptyContacts(model); } function cleanup() { if (model.sortOrders.length > 0) { model.sortOrders = []; waitForContactsChanged(); } emptyContacts(model); } function cleanupTestCase() { finishTestForModel(model); } // Helper functions function compareContactArrays(actual, expected) { compare(actual.length, expected.length, "length"); for (var i = 0; i < expected.length; i++) { compareContacts(actual[i], expected[i], "index " + i); } } function compareContacts(actual, expected, message) { if (expected.name) { compare(actual.name.firstName, expected.name.firstName, message + ': name.firstName'); compare(actual.name.lastName, expected.name.lastName, message + ': name.lastName'); } if (expected.email) { compare(actual.email.emailAddress, expected.email.emailAddress, message + ': email.emailAddress'); } } } tests/auto/contacts/qmlcontacts/tst_qmlcontacts.cpp000066400000000000000000000037531233466112000232720ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include QUICK_TEST_MAIN(contacts) tests/auto/jsondbprocess.h000066400000000000000000000107601233466112000162210ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef JSONDBPROCESS_H #define JSONDBPROCESS_H #include #include #include #include #include // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // class JsonDbProcess { public: JsonDbProcess() { } bool start(const QString &partitionsFilePath = QString(), bool killAllJsonDb = true) { if (killAllJsonDb) { if (system("killall jsondb") != 0) { qWarning() << Q_FUNC_INFO << "Unable to kill running jsondb instances."; } } QFileInfo partitionsFileInfo(partitionsFilePath); // Start new process QString jsondbPath = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/jsondb"; if (!QFileInfo(jsondbPath).exists()) { qWarning() << Q_FUNC_INFO << "Cannot find JsonDb binaries."; return false; } QStringList args; if (!partitionsFilePath.isEmpty()) args << "-config-path" << partitionsFileInfo.absolutePath(); args << "-reject-stale-updates"; m_process.start(jsondbPath, args); if (!m_process.waitForStarted()) { qWarning() << Q_FUNC_INFO << m_process.errorString(); return false; } else { // Wait is needed so we get to know if the process was immediately exited QTest::qWait(100); // The process was immediately exited if (m_process.state() == QProcess::NotRunning) { // Maybe older JsonDb version is in use and -config-path argument is invalid? // TODO: remove this and the above wait later when it's safe m_process.start(jsondbPath, QStringList() << partitionsFileInfo.absolutePath()); if (!m_process.waitForStarted()) { qWarning() << Q_FUNC_INFO << m_process.errorString(); return false; } } } return true; } void terminate() { if (m_process.state() == QProcess::NotRunning) return; m_process.terminate(); if (m_process.state() != QProcess::NotRunning && !m_process.waitForFinished()) { qWarning() << Q_FUNC_INFO << "JsonDb did not terminate cleanly. Killing."; m_process.kill(); } } private: QProcess m_process; }; #endif // JSONDBPROCESS_H tests/auto/organizer/000077500000000000000000000000001233466112000151665ustar00rootroot00000000000000tests/auto/organizer/organizer.pro000066400000000000000000000005241233466112000177110ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS += \ qorganizercollection \ qorganizeritem \ qorganizeritemasync \ qorganizeritemdetail \ qorganizeritemdetails \ qorganizeritemfilter \ qorganizeritemsortorder \ qorganizermanager \ qorganizermanagerdetails \ qorganizere2e qtHaveModule(qmltest): SUBDIRS += qmlorganizer tests/auto/organizer/qmlorganizer/000077500000000000000000000000001233466112000177005ustar00rootroot00000000000000tests/auto/organizer/qmlorganizer/qmlorganizer.pro000066400000000000000000000003231233466112000231320ustar00rootroot00000000000000TEMPLATE=app TARGET=tst_qmlorganizer QT += versit organizer CONFIG += qmltestcase CONFIG += console SOURCES += tst_qmlorganizer.cpp importFiles.files = testcases importFiles.path = . DEPLOYMENT += importFiles tests/auto/organizer/qmlorganizer/testcases/000077500000000000000000000000001233466112000216765ustar00rootroot00000000000000tests/auto/organizer/qmlorganizer/testcases/CheckJsonDb.qml000066400000000000000000000072461233466112000245370ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/organizer import QtQuick 2.0 import QtTest 1.0 import QtJsonDb 1.0 Item { name: "A First thing to run" id: firstThingie // NOTE: Since Organizer jsondb-backend expects to have User- and System-partitions, // we must ensure they are there before testing. signal partitionsCreated property int creationRequestAmount: 0 Partition { id: systemPartition } JsonDbListModel { id: partitionModel query: "[?_type=\"Partition\"]" roleNames: ["name", "_uuid"] partition: systemPartition } SignalSpy { id: partitionsCreatedSpy target: firstThingie signalName: "partitionsCreated" } function check_db() { var neededPartitions = ["com.nokia.mt.System", "com.nokia.mt.User"]; for (var n=0;n expect_count) { do { organizerChangedSpy.wait(signalWaitTime); count ++; verify(__model.itemCount >= expect_count) verify(count <= 10) } while (__model.itemCount > expect_count) } } else if (Number(waitModelSignalType) == collectionChange) { if (__model.collections.length < expect_count) { do { organizerCollectionChangedSpy.wait(signalWaitTime); count ++; verify(__model.collections.length <= expect_count) verify(count <= 10) } while (__model.collections.length < expect_count) } else if (__model.collections.length > expect_count) { do { organizerCollectionChangedSpy.wait(signalWaitTime); count ++; verify(__model.collections.length >= expect_count) verify(count <= 10) } while (__model.collections.length > expect_count) } } } function getManagerList() { var testManagers = ["memory", "jsondb"]; var model = Qt.createQmlObject( "import QtOrganizer 5.0; OrganizerModel {}" , testUtility); var managerlist = []; for (var i in testManagers) { if (model.availableManagers.indexOf(testManagers[i]) != -1) { managerlist.push(testManagers[i]); } } model.autoUpdate = false; model.destroy(); return managerlist; } function getManagerListData() { return utility.getManagerList().map(function(x) { return {tag: x+" backend", managerToBeTested: x} }) } function createModel(managerName) { var model = Qt.createQmlObject( "import QtOrganizer 5.0;" + "OrganizerModel {" + " manager: \"qtorganizer:" + managerName + ":id=qml\";" + " startPeriod:'2009-01-01';" + " endPeriod:'2012-12-31';" + " autoUpdate:true; }" , testUtility); init(model); waitModelChange(); empty_calendar(); return model; } function create_spy(targetObj, signalName) { var spy = Qt.createQmlObject( "import QtTest 1.0 \nSignalSpy {}", testUtility); spy.target = targetObj; spy.signalName = signalName; return spy; } function empty_calendar(log) { var ids = []; var removeIds = []; var i; var j; for (i = 0; i < __model.itemCount; i++) { if (__model.items[i].itemType == Type.EventOccurrence || __model.items[i].itemType == Type.TodoOccurrence) ids.push(__model.items[i].parentId) if (__model.items[i].itemId !== "qtorganizer:::") ids.push(__model.items[i].itemId) } // remove duplicates for (i = 0; i < ids.length; i++) { for (j = 0; j < removeIds.length; j++) { if (ids[i] == removeIds[j]) break; } if (j == removeIds.length) removeIds.push(ids[i]) } if (log != undefined) { console.log("items count :" + __model.itemCount); console.log("items :" + removeIds); } if (removeIds.length > 0) { clearModelChanged() __model.removeItems(removeIds) if (!__model.autoUpdate) __model.update() waitModelChanged(500); } compare(__model.items.length, 0) empty_calendar_collections(log); } function empty_calendar_collections(log) { if (__model.collections.length > 1) { var setAutoUpdate = __model.autoUpdate; __model.autoUpdate = false; for (var i = 0; i < __model.collections.length; ++i) { var collId = __model.collections[i].collectionId; if (collId != __model.defaultCollection().collectionId) { __model.removeCollection(collId); } } __model.autoUpdate = setAutoUpdate; __model.update(); } } function outputDetail(eventDetail) { console.log("========================================="); if (eventDetail == undefined) { console.log("detail is undefined"); return; } console.log(eventDetail); console.log("secondsBeforeStart: " + eventDetail.secondsBeforeStart); console.log("repetitionCount: " + eventDetail.repetitionCount); console.log("repetitionDelay: " + eventDetail.repetitionDelay); if (eventDetail.reminderType == 2) { //audibleReminder type console.log("dataUrl: " + eventDetail.dataUrl); } else if (eventDetail.reminderType == 1) { //VisualReminder type console.log("message: " + eventDetail.message); console.log("dataUrl: " + eventDetail.dataUrl); } else if (eventDetail.reminderType == 3) { //EmailReminder type console.log("subject: " + eventDetail.subject); console.log("body: " + eventDetail.body); //recipients list var recipients = eventDetail.recipients; console.log("recipients: " + recipients); //attachments list var attachments = eventDetail.attachments; console.log("recipients: " + attachments); } } function outputEvent(event) { console.log("--------------------------------------------"); console.log("organizer item id: " + event.itemId); console.log("audibleReminder: "); //AudibleReminder var eventDetail = event.detail(Detail.AudibleReminder); outputDetail(eventDetail); eventDetail = event.detail(Detail.EmailReminder); outputDetail(eventDetail); eventDetail = event.detail(Detail.VisualReminder); outputDetail(eventDetail); } function compareReminderDetails(detail1, detail2, log) { if (log != undefined) { outputDetail(detail1); outputDetail(detail2); } //Reminder common compare if (detail1 == undefined) { verify(detail2 == undefined); return; } else { verify(detail2 != undefined); } verify(detail1.reminderType == detail2.reminderType); var detail1RepCount = detail1.repetitionCount; var detail2RepCount = detail2.repetitionCount; compare(detail1RepCount, detail2RepCount); var detail1RepDelay = detail1.repetitionDelay; var detail2RepDelay = detail2.repetitionDelay; compare(detail1RepDelay, detail2RepDelay); var detail2SecBeforeStart = detail2.secondsBeforeStart; var detail1SecBeforeStart = detail1.secondsBeforeStart; compare(detail1SecBeforeStart, detail2SecBeforeStart); if (detail1.reminderType == 2) { //audibleReminder type var detail1DataUrl = detail1.dataUrl; var detail2DataUrl = detail2.dataUrl; compare(detail1DataUrl, detail2DataUrl); } else if (detail1.reminderType == 1) { //VisualReminder type var detail1Message = detail1.message; var detail2Message = detail2.message; compare(detail1Message, detail2Message); var detail1DataUrl = detail1.dataUrl; var detail2DataUrl = detail2.dataUrl; compare(detail1DataUrl, detail2DataUrl); } else if (detail1.reminderType == 3) { //EmailReminder type var detail1Subject = detail1.subject; var detail2Subject = detail2.subject; compare(detail1Subject, detail2Subject); var detail1Body = detail1.body; var detail2Body = detail2.body; compare(detail1Body, detail2Body); var detail1Recipients = detail1.recipients; var detail2Recipients = detail2.recipients; verify(compareList(detail1Recipients, detail2Recipients)); var detail1Attachments = detail1.attachments; var detail2Attachments = detail2.attachments; verify(compareList(detail1Attachments, detail2Attachments)) } } function compareEvent(event1, event2, log) { if (log != undefined) { outputEvent(event1); outputEvent(event2); console.log("compareEvent"); } //Reminder compare compareReminderDetails(event1.detail(Detail.AudibleReminder), event2.detail(Detail.AudibleReminder)); compareReminderDetails(event1.detail(Detail.VisualReminder), event2.detail(Detail.VisualReminder)); compareReminderDetails(event1.detail(Detail.EmailReminder), event2.detail(Detail.EmailReminder)); } function compareList(a, b) { if (a == b) { return true; } if (a.length != b.length) return false; for (var key in a) { if (a[key] != b[key]) { console.log("Failed! List values are not the same :" + a[key] + " is not equal to" + b[key]); return false; } } return true; } function addItemsToModel(ctrStrings, parent) { for (var i=0;i 0) { organizerModel.removeItems(ids); } for (var i = 0; i < organizerModel.collections.length; ++i) { var collId = organizerModel.collections[i].collectionId; if (collId != organizerModel.defaultCollection().collectionId) { organizerModel.removeCollection(collId); } } organizerModel.autoUpdate = true; organizerModel.update(); } // COLLECTION ELEMENT API function create_collection() { console.log("");//to print out test tags for every data set var ctorString = "import QtQuick 2.0 \n" + "import QtOrganizer 5.0\n" + "Collection {\n" + "}\n" var newCollection = Qt.createQmlObject(ctorString, collectionTests); verify(newCollection != undefined, 'Collection creation failed'); return newCollection; } // SignalSpy { // id: spy // signalName: "valueChanged" // } // There is currently some problem with static // SignalSpy and changing the target (QTBUG-21083). // As a workaround recreating the spy dynamicly. function create_spy(targetObj, signalName) { var spy = Qt.createQmlObject( "import QtTest 1.0 \nSignalSpy {}", collectionTests); spy.target = targetObj; spy.signalName = signalName; return spy; } // creation function test_creation_data() { return [ {tag: "empty definition", code: "import QtQuick 2.0 \n" + "import QtOrganizer 5.0\n" + "Collection {\n" + "}\n" }, {tag: "full definition", code: "import QtQuick 2.0 \n" + "import QtOrganizer 5.0\n" + "Collection {\n" + "id: testCollection\n" + "collectionId: 'my collection id (setting might not be supported by the backend)'\n" + "name: 'My test collection'\n" + "description: 'Here we are testing our awesome collection!'\n" + "color: 'green'\n" + "image: 'my/url/somewhere'\n" + "}\n" } ] } function test_creation(data) { console.log("");//to print out test tags for every data set var newCollection = Qt.createQmlObject(data.code, collectionTests); verify(newCollection != undefined, 'Collection creation failed'); //verify(newCollection.parent == collectionTests); } // name API function test_name_data() { return [ {tag: "empty name", testValue: ""}, {tag: "string name", testValue: "my name"}, {tag: "long string name", testValue: "here goes the long string name but what to have here? Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed posuere interdum sem. Quisque ligula eros ullamcorper quis, lacinia quis facilisis sed sapien. Mauris varius diam vitae arcu. Sed arcu lectus auctor vitae, consectetuer et venenatis eget velit. Sed augue orci, lacinia eu tincidunt et eleifend nec lacus. Donec ultricies nisl ut felis, suspendisse potenti. Lorem ipsum ligula ut hendrerit mollis, ipsum erat vehicula risus, eu suscipit sem libero nec erat. Aliquam erat volutpat. Sed congue augue vitae neque. Nulla consectetuer porttitor pede. Fusce purus morbi tortor magna condimentum vel, placerat id blandit sit amet tortor. Mauris sed libero. Suspendisse facilisis nulla in lacinia laoreet, lorem velit accumsan velit vel mattis libero nisl et sem. Proin interdum maecenas massa turpis sagittis in, interdum non lobortis vitae massa. Quisque purus lectus, posuere eget imperdiet nec sodales id arcu. Vestibulum elit pede dictum eu, viverra non tincidunt eu ligula. Nam molestie nec tortor. Donec placerat leo sit amet velit. Vestibulum id justo ut vitae massa. Proin in dolor mauris consequat aliquam. Donec ipsum, vestibulum ullamcorper venenatis augue. Aliquam tempus nisi in auctor vulputate, erat felis pellentesque augue nec, pellentesque lectus justo nec erat. Aliquam et nisl. Quisque sit amet dolor in justo pretium condimentum. Vivamus placerat lacus vel vehicula scelerisque, dui enim adipiscing lacus sit amet sagittis, libero enim vitae mi. In neque magna posuere, euismod ac tincidunt tempor est. Ut suscipit nisi eu purus. Proin ut pede mauris eget ipsum. Integer vel quam nunc commodo consequat. Integer ac eros eu tellus dignissim viverra. Maecenas erat aliquam erat volutpat. Ut venenatis ipsum quis turpis. Integer cursus scelerisque lorem. Sed nec mauris id quam blandit consequat. Cras nibh mi hendrerit vitae, dapibus et aliquam et magna. Nulla vitae elit. Mauris consectetuer odio vitae augue."} ] } function test_name(data) { var newCollection = create_collection(); var spy = create_spy(newCollection, "valueChanged"); // change newCollection.name = data.testValue; compare(spy.count, 1); compare(newCollection.name, data.testValue); // change without change newCollection.name = data.testValue; compare(spy.count, 1); compare(newCollection.name, data.testValue); } // description API function test_description_data() { return [ {tag: "empty desc", testValue: ""}, {tag: "string desc", testValue: "here goes the string description"}, {tag: "long string desc", testValue: "here goes the long string description but what to have here? Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed posuere interdum sem. Quisque ligula eros ullamcorper quis, lacinia quis facilisis sed sapien. Mauris varius diam vitae arcu. Sed arcu lectus auctor vitae, consectetuer et venenatis eget velit. Sed augue orci, lacinia eu tincidunt et eleifend nec lacus. Donec ultricies nisl ut felis, suspendisse potenti. Lorem ipsum ligula ut hendrerit mollis, ipsum erat vehicula risus, eu suscipit sem libero nec erat. Aliquam erat volutpat. Sed congue augue vitae neque. Nulla consectetuer porttitor pede. Fusce purus morbi tortor magna condimentum vel, placerat id blandit sit amet tortor. Mauris sed libero. Suspendisse facilisis nulla in lacinia laoreet, lorem velit accumsan velit vel mattis libero nisl et sem. Proin interdum maecenas massa turpis sagittis in, interdum non lobortis vitae massa. Quisque purus lectus, posuere eget imperdiet nec sodales id arcu. Vestibulum elit pede dictum eu, viverra non tincidunt eu ligula. Nam molestie nec tortor. Donec placerat leo sit amet velit. Vestibulum id justo ut vitae massa. Proin in dolor mauris consequat aliquam. Donec ipsum, vestibulum ullamcorper venenatis augue. Aliquam tempus nisi in auctor vulputate, erat felis pellentesque augue nec, pellentesque lectus justo nec erat. Aliquam et nisl. Quisque sit amet dolor in justo pretium condimentum. Vivamus placerat lacus vel vehicula scelerisque, dui enim adipiscing lacus sit amet sagittis, libero enim vitae mi. In neque magna posuere, euismod ac tincidunt tempor est. Ut suscipit nisi eu purus. Proin ut pede mauris eget ipsum. Integer vel quam nunc commodo consequat. Integer ac eros eu tellus dignissim viverra. Maecenas erat aliquam erat volutpat. Ut venenatis ipsum quis turpis. Integer cursus scelerisque lorem. Sed nec mauris id quam blandit consequat. Cras nibh mi hendrerit vitae, dapibus et aliquam et magna. Nulla vitae elit. Mauris consectetuer odio vitae augue."} ] } function test_description(data) { var newCollection = create_collection(); var spy = create_spy(newCollection, "valueChanged"); // change newCollection.description = data.testValue; compare(spy.count, 1); compare(newCollection.description, data.testValue); // change without change newCollection.description = data.testValue; compare(spy.count, 1); compare(newCollection.description, data.testValue); } // color API function test_color_data() { return [ {tag: "black", testValue: "black", colorCode: "#000000"}, {tag: "red", testValue: "red", colorCode: "#ff0000"} ] } function test_color(data) { var newCollection = create_collection(); var spy = create_spy(newCollection, "valueChanged"); // change newCollection.color = data.testValue; compare(spy.count, 1); compare(newCollection.color.toString(), data.colorCode.toString()); // change without change newCollection.color = data.testValue; compare(spy.count, 1); compare(newCollection.color.toString(), data.colorCode.toString()); } // image API function test_image_data() { return [ {tag: "empty path", testValue: ""}, {tag: "string path", testValue: "/my/path/"} ] } function test_image(data) { var newCollection = create_collection(); var spy = create_spy(newCollection, "valueChanged"); // change newCollection.image = data.testValue; compare(spy.count, 1); var pathString = newCollection.image.toString(); verify(pathString.search(data.testValue) != -1); // change without change newCollection.image = data.testValue; compare(spy.count, 1); compare(newCollection.image.toString(), pathString); } // metadata API function test_metadata_data() { return [ {tag: "string key", testKey: "myKey", testValue: "my awesome key"} ] } function test_metadata(data) { var newCollection = create_collection(); var spy = create_spy(newCollection, "valueChanged"); // change newCollection.setExtendedMetaData(data.testKey, data.testValue); compare(spy.count, 1); compare(newCollection.extendedMetaData(data.testKey).toString(), data.testValue); // change without change newCollection.setExtendedMetaData(data.testKey, data.testValue); compare(spy.count, 1); compare(newCollection.extendedMetaData(data.testKey).toString(), data.testValue); } // ITEM COLLECTION API function backend_plugin_available(model, pluginName) { var managerAvailable = false; for (var i=0;i 0) { organizerModel.removeItems(ids) } organizerModel.update() verify(organizerModel.itemCount === 0, 'empty_calendar failed') } function saveTestEvent() { modelChangedSpy.clear() var detailEvent = create_testobject( "import QtQuick 2.0\n" + "import QtOrganizer 5.0 \n" + " Event {\n" + " displayLabel:\"Event 1\"\n" + " description:\"Event 1 description\"\n" + " startDateTime:new Date('2011-10-25')\n" + " endDateTime:new Date('2011-10-26')\n" + " }") organizerModel.saveItem(detailEvent) modelChangedSpy.wait() verify(organizerModel.itemCount === 1, 'Save item check failed.') } function test_organizerImportExportSignaling_data() { return utility.getManagerListData(); } function test_organizerImportExportSignaling(data) { organizerModel.manager = data.managerToBeTested modelChangedSpy.wait() // Save test Event. saveTestEvent() // Export even to vcal file. var exportModelChangedSpy = create_spy(organizerModel, "exportCompleted") var icalFilePath = Qt.resolvedUrl( "tst_organizer_versit_export_import_e2e.ical") organizerModel.exportItems(icalFilePath, ["Sync"]) exportModelChangedSpy.wait() // Check if export enters finished state properly. compare(exportErrorCode, OrganizerModel.ExportNoError, 'exportCompleted signal gave error during export.') compare(exportFileName, icalFilePath, 'exportCompleted signal reported incorrect filename compared to the given name.') compare(organizerModel.error, "NoError", "exportError found") // Empty all items and import just created icalFilePath file. empty_calendar() var importModelChangedSpy = create_spy(organizerModel, "importCompleted") organizerModel.importItems(icalFilePath, ["Sync"]) importModelChangedSpy.wait() compare(importErrorCode, OrganizerModel.ImportNoError, 'importCompleted signal gave error during import.') compare(importFileName, icalFilePath, 'importCompleted signal reported incorrect filename compared to the given name.') compare(organizerModel.error, "NoError", "importError found") // Check that imported items have correct data. compare(organizerModel.itemCount, 1, "Versit Import Count Check Failed.") var importedEvent = organizerModel.items[0] compare(importedEvent.description, "Event 1 description", "imported description different from the exported description.") compare(importedEvent.displayLabel, "Event 1", "imported event label different to exported label.") } function test_overlappingExportEmitsSignalWithError_data() { return utility.getManagerListData(); } function test_overlappingExportEmitsSignalWithError(data) { organizerModel.manager = data.managerToBeTested // Export items to two ical files which we will use for testing purposes.... var icalFilePath1 = Qt.resolvedUrl("export_3.ical") var icalFilePath2 = Qt.resolvedUrl("export_4.ical") // Save test Event. saveTestEvent() // Make immediate re-export which overlaps with the previous one and causes error. // We should get immediate signal with error showing the not ready yet. var exportModelChangedSpy = create_spy(organizerModel, "exportCompleted") organizerModel.exportItems(icalFilePath1, ["Sync"]) organizerModel.exportItems(icalFilePath2, ["Sync"]) exportModelChangedSpy.wait() compare(exportErrorCode, OrganizerModel.ExportNotReadyError, 'Overlapping export should have given an error.') compare(exportFileName, icalFilePath2, 'Overlapping export incorrect file path.') // Check if the first export enters finished state properly. exportModelChangedSpy.wait() compare(exportErrorCode, OrganizerModel.ExportNoError, 'Exported item gave an unexpected error.') compare(exportFileName, icalFilePath1, 'Exported item was different than expected.') } function test_overlappingImportEmitsSignalWithError_data() { return utility.getManagerListData(); } function test_overlappingImportEmitsSignalWithError(data) { organizerModel.manager = data.managerToBeTested // Save test Event. saveTestEvent() // Export items to two ical files which we will use for testing purposes.... var icalFilePath1 = Qt.resolvedUrl("import_1.ical") var icalFilePath2 = Qt.resolvedUrl("import_2.ical") var exportModelChangedSpy = create_spy(organizerModel, "exportCompleted") organizerModel.exportItems(icalFilePath1, ["Sync"]) exportModelChangedSpy.wait() organizerModel.exportItems(icalFilePath2, ["Sync"]) exportModelChangedSpy.wait() // Import Events form ical file just created. modelChangedSpy.clear() var importModelChangedSpy = create_spy(organizerModel, "importCompleted") organizerModel.importItems(icalFilePath1, ["Sync"]) // Make immediate second import request which should fail as reader is busy. organizerModel.importItems(icalFilePath2, ["Sync"]) // Check that import enters finished state properly. importModelChangedSpy.wait() compare(importErrorCode, OrganizerModel.ImportNotReadyError, 'Overlapping import error state incorrect.') compare(importFileName, icalFilePath2, 'Overlapping import file path incorrect.') // Check that the first import enters finished state properly. importModelChangedSpy.wait()// import finished modelChangedSpy.wait()// imported items also now in model compare(importFileName, icalFilePath1, 'Imported item failed filename.') compare(importErrorCode, OrganizerModel.ImportNoError, 'Imported Item failed.') } // Memory model for detail exporting test OrganizerModel { id: memoryModel manager: "memory" startPeriod: '2009-01-01' endPeriod: '2012-12-31' autoUpdate: true } SignalSpy { id: memoryModelSpy target: memoryModel signalName: "modelChanged" } function test_organizerImportExportItemDetails() { memoryModelSpy.wait() // Signal from model initialization memoryModel.saveItem(megaEvent) memoryModelSpy.wait() var exportModelChangedSpy = create_spy(memoryModel, "exportCompleted") memoryModel.exportItems(Qt.resolvedUrl("tst_organizer_versit_export_import_e2e_megaevent.ical"), ["Sync"]) exportModelChangedSpy.wait() memoryModel.removeItem(memoryModel.items[0]) memoryModelSpy.wait() memoryModel.importItems(Qt.resolvedUrl("tst_organizer_versit_export_import_e2e_megaevent.ical"), ["Sync"]) memoryModelSpy.wait() compare(memoryModel.itemCount, 1) var importedEvent = memoryModel.items[0] // Verify imported details. Todo: verify more details when more details are supported compare(importedEvent.startDateTime, megaEvent.startDateTime) compare(importedEvent.endDateTime, megaEvent.endDateTime) compare(importedEvent.displayLabel, megaEvent.displayLabel) compare(importedEvent.priority, megaEvent.priority) compare(importedEvent.description, megaEvent.description) compare(importedEvent.details(Detail.ExtendedDetail).length, 4) compare(importedEvent.details(Detail.ExtendedDetail)[0].name, megaEvent.details(Detail.ExtendedDetail)[0].name) compare(importedEvent.details(Detail.ExtendedDetail)[0].data, megaEvent.details(Detail.ExtendedDetail)[0].data) compare(importedEvent.details(Detail.ExtendedDetail)[1].name, megaEvent.details(Detail.ExtendedDetail)[1].name) compare(importedEvent.details(Detail.ExtendedDetail)[1].data, megaEvent.details(Detail.ExtendedDetail)[1].data) compare(importedEvent.details(Detail.ExtendedDetail)[2].name, megaEvent.details(Detail.ExtendedDetail)[2].name) compare(importedEvent.details(Detail.ExtendedDetail)[2].data, megaEvent.details(Detail.ExtendedDetail)[2].data) compare(importedEvent.details(Detail.ExtendedDetail)[3].name, megaEvent.details(Detail.ExtendedDetail)[3].name) compare(importedEvent.details(Detail.ExtendedDetail)[3].data, megaEvent.details(Detail.ExtendedDetail)[3].data) compare(importedEvent.detail(Detail.Comment).comment, megaEvent.detail(Detail.Comment).comment) compare(importedEvent.detail(Detail.AudibleReminder).repetitionCount, megaEvent.detail(Detail.AudibleReminder).repetitionCount) compare(importedEvent.detail(Detail.AudibleReminder).repetitionDelay, megaEvent.detail(Detail.AudibleReminder).repetitionDelay) compare(importedEvent.detail(Detail.AudibleReminder).secondsBeforeStart, megaEvent.detail(Detail.AudibleReminder).secondsBeforeStart) compare(importedEvent.detail(Detail.AudibleReminder).dataUrl, megaEvent.detail(Detail.AudibleReminder).dataUrl) compare(importedEvent.detail(Detail.EmailReminder).repetitionCount, megaEvent.detail(Detail.EmailReminder).repetitionCount) compare(importedEvent.detail(Detail.EmailReminder).repetitionDelay, megaEvent.detail(Detail.EmailReminder).repetitionDelay) compare(importedEvent.detail(Detail.EmailReminder).secondsBeforeStart, megaEvent.detail(Detail.EmailReminder).secondsBeforeStart) compare(importedEvent.detail(Detail.EmailReminder).body, megaEvent.detail(Detail.EmailReminder).body) compare(importedEvent.detail(Detail.EmailReminder).subject, megaEvent.detail(Detail.EmailReminder).subject) compare(importedEvent.detail(Detail.EmailReminder).recipients, megaEvent.detail(Detail.EmailReminder).recipients) compare(importedEvent.detail(Detail.EmailReminder).attachments, megaEvent.detail(Detail.EmailReminder).attachments) compare(importedEvent.detail(Detail.VisualReminder).repetitionCount, megaEvent.detail(Detail.VisualReminder).repetitionCount) compare(importedEvent.detail(Detail.VisualReminder).repetitionDelay, megaEvent.detail(Detail.VisualReminder).repetitionDelay) compare(importedEvent.detail(Detail.VisualReminder).secondsBeforeStart, megaEvent.detail(Detail.VisualReminder).secondsBeforeStart) compare(importedEvent.detail(Detail.VisualReminder).message, megaEvent.detail(Detail.VisualReminder).message) compare(importedEvent.detail(Detail.VisualReminder).dataUrl, megaEvent.detail(Detail.VisualReminder).dataUrl) expectFail("", "During import the timestamp information gets overwritten with current datetime") compare(importedEvent.detail(Detail.Timestamp).created, megaEvent.detail(Detail.Timestamp).created) expectFail("", "During import the timestamp information gets overwritten with current datetime") compare(importedEvent.detail(Detail.Timestamp).lastModified, megaEvent.detail(Detail.Timestamp).lastModified) compare(importedEvent.detail(Detail.Version).version, megaEvent.detail(Detail.Version).version) compare(importedEvent.detail(Detail.Version).extendedVersion, megaEvent.detail(Detail.Version).extendedVersion) memoryModel.removeItem(memoryModel.items[0]) memoryModelSpy.wait() } } tests/auto/organizer/qmlorganizer/testcases/tst_organizeraudiblereminder.qml000066400000000000000000000215321233466112000303620ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtOrganizer 5.0 Rectangle { id: test; QOrganizerTestUtility { id: utility } TestCase { name: "test_audibleReminder" function test_audibleReminder_data() { return [ { tag: " no properties", code: "import QtOrganizer 5.0;" + "AudibleReminder {" + "}" }, { tag: " 1 properties", code: "import QtOrganizer 5.0;" + "AudibleReminder {" + " repetitionCount: 3; }" }, { tag: " 2 properties", code: "import QtOrganizer 5.0;" + "AudibleReminder {" + " repetitionCount: 3; " + " repetitionDelay: 30;}" }, { tag: " 3 properties", code: "import QtOrganizer 5.0;" + "AudibleReminder {" + " repetitionCount: 0; " + " secondsBeforeStart: 100;}" }, { tag: " 4 properties", code: "import QtOrganizer 5.0;" + "AudibleReminder {" + " repetitionCount: 3; " + " repetitionDelay: 30; " + " secondsBeforeStart: 40; " + " dataUrl: \"www.nokia.com\" }" }, ] } function test_audibleReminder(data) { var list = utility.getManagerList(); if (list.length < 0) { console.log("No manager to test"); return; } //Test all manager backends for (var i = 0; i < list.length; i ++) { var managerName = list[i]; var debugFlag = 1; console.log("AudibleReminder test start! :" + managerName); var model = Qt.createQmlObject( "import QtOrganizer 5.0;" + "OrganizerModel {" + " manager: \"qtorganizer:" + managerName + ":id=qml\";" + " startPeriod:'2009-01-01';" + " endPeriod:'2012-12-31';" + " autoUpdate:true; }" , test); var event = Qt.createQmlObject( "import QtOrganizer 5.0;" + "Event { " + " id:event;" + " displayLabel: \"organizer qml reminder test event\"; " + " description: \"This is a new reminder event!!!\"; " + " startDateTime: '2010-12-12'; " + " endDateTime: '2010-12-13'; }" , test); var audibleReminderDetail = Qt.createQmlObject(data.code , event); utility.init(model); utility.waitModelChange(); utility.empty_calendar(); //------Create and save the detail test------// utility.debug("Create and save the detail test", debugFlag); audibleReminderDetail.dataUrl = "http://www.test0.com"; event.setDetail(audibleReminderDetail); if (managerName == "jsondb") { // custom fields allowed in JsonDb for audible reminder // simple test here, since already fully tested in C++ var extendedDetail = Qt.createQmlObject( "import QtOrganizer 5.0;" + "ExtendedDetail {" + " name: \"reminder\";" + " data: \{" + " Qt: \"Everywhere\";" + " Url: \"http://www.qt-project.org/\";" + " }" + "}" , test); event.setDetail(extendedDetail); } model.saveItem(event); //Let's wait for the model to be up-to-date utility.waitModelChange(1); compare(model.itemCount, 1); var fetchlist = model.items; var savedEvent = fetchlist[0]; verify(savedEvent != undefined); verify(savedEvent.detail(Detail.AudibleReminder) != undefined); utility.compareReminderDetails(audibleReminderDetail, savedEvent.detail(Detail.AudibleReminder)); //------update the detail test------// var savedEventDetail = savedEvent.detail(Detail.AudibleReminder); savedEventDetail.dataUrl = "http://www.test222.com"; savedEventDetail.secondsBeforeStart = 300; savedEventDetail.repetitionCount = 0; audibleReminderDetail.dataUrl = "http://www.test222.com"; audibleReminderDetail.secondsBeforeStart = 300; audibleReminderDetail.repetitionCount = 0; savedEvent.setDetail(savedEventDetail); model.saveItem(savedEvent); utility.organizerChangedSpy.wait(); //no new event created compare(model.itemCount, 1) fetchlist = model.items; var updatedEvent = fetchlist[0]; var updatedEventDetail = updatedEvent.detail(Detail.AudibleReminder); verify(updatedEvent != undefined); utility.compareReminderDetails(audibleReminderDetail, updatedEventDetail); //------remove the detail test------// utility.debug("Remove the detail test", debugFlag); var removeEventDetail = updatedEvent.detail(Detail.AudibleReminder); updatedEvent.removeDetail(removeEventDetail); model.saveItem(updatedEvent); utility.organizerChangedSpy.wait(); //no new event created compare(model.itemCount, 1) fetchlist = model.items; var detailRemovedEvent = fetchlist[0]; var detailRemovedEventDetailList = detailRemovedEvent.details(Detail.AudibleReminder); if (detailRemovedEventDetailList.length > 0) utility.outputDetail(detailRemovedEvent.detail(Detail.AudibleReminder)); verify(detailRemovedEventDetailList.length == 0) verify(removeEventDetail != undefined) utility.empty_calendar(); utility.organizerChangedSpy.clear(); utility.organizerChangedSpy.destroy(); model.destroy(); event.destroy(); audibleReminderDetail.destroy(); } } } } tests/auto/organizer/qmlorganizer/testcases/tst_organizercollectionfilter.qml000066400000000000000000000164011233466112000305670ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtOrganizer 5.0 Rectangle { id: test; QOrganizerTestUtility { id: utility } Collection { id: testCollection name: 'My collection filter test' description: 'collection filter test' } TestCase { name: "Organizer collection filter test" function test_collectionFilter_data() { return [ { tag: "properties", code: "import QtOrganizer 5.0;CollectionFilter{}" }, ] } function test_collectionFilter(data) { var list = utility.getManagerList(); if (list.length < 0) { console.log("No manager to test"); return; } //Test all manager backends for (var i = 0; i < list.length; i ++) { var managerName = list[i]; var debugFlag = 0; console.log("test_collectionFilter test start! :" + managerName); var model = Qt.createQmlObject( "import QtOrganizer 5.0;" + "OrganizerModel {" + " manager: \"qtorganizer:" + managerName + ":id=qml\";" + " startPeriod:'2009-01-01';" + " endPeriod:'2012-12-31';" + " autoUpdate:true; }" , test); utility.init(model); utility.waitModelChange(); utility.empty_calendar(); var collectionFilter = Qt.createQmlObject(data.code, test); var event = Qt.createQmlObject( "import QtOrganizer 5.0;" + "Event { " + " displayLabel: \"organizer collection filter test event\"; " + " description: \"organizer collection filter test event\"; " + " startDateTime: '2010-12-12'; " + " endDateTime: '2010-12-13'; }" , test); //------Create and save the detail test------// var collectionLength = model.collections.length; model.saveCollection(testCollection) //Let's wait for the model to be up-to-date utility.waitModelChange(collectionLength + 1, utility.collectionChange); //we should have more than default collection now utility.debug("New collection length :" + model.collections.length, debugFlag); var savedCollection = model.collections[model.collections.length - 1]; utility.debug("New collection id :" + savedCollection.collectionId, debugFlag); event.collectionId = savedCollection.collectionId; model.saveItem(event); utility.waitModelChange(1, utility.itemChange); var fetchlist = model.items; compare(model.itemCount, 1) var savedEvent = fetchlist[0]; utility.debug("New event collection id :" + savedEvent.collectionId, debugFlag); compare(savedEvent.collectionId, event.collectionId); //Change collection filter id collectionFilter.ids = [event.collectionId]; model.filter = collectionFilter; utility.waitModelChange(1); compare(model.itemCount, 1) //default collection utility.debug("default collection id :" + model.defaultCollection().collectionId, debugFlag); var modelCollectionFilter = model.filter; modelCollectionFilter.ids = [model.defaultCollection().collectionId]; utility.waitModelChange(0); compare(model.itemCount, 0) //save event to default collection event.collectionId = model.defaultCollection().collectionId; model.saveItem(event); utility.waitModelChange(1); //empty ides: modelCollectionFilter.ids = []; utility.waitModelChange(0); compare(model.itemCount, 0) //remove collection filter or set empty filter model.filter = null; utility.waitModelChange(2); compare(model.itemCount, 2); //reset back filter collectionFilter = Qt.createQmlObject(data.code, test); collectionFilter.ids = [event.collectionId]; model.filter = collectionFilter; utility.waitModelChange(1); compare(model.itemCount, 1); //set more collection ids modelCollectionFilter = model.filter modelCollectionFilter.ids = [model.defaultCollection().collectionId, savedCollection.collectionId]; utility.waitModelChange(2); compare(model.itemCount, 2); //One invalid collection id modelCollectionFilter.ids = [model.defaultCollection().collectionId, "12345666666",savedCollection.collectionId]; utility.waitModelChange(2); compare(model.itemCount, 2); utility.empty_calendar(); } } } } tests/auto/organizer/qmlorganizer/testcases/tst_organizere2e.qml000066400000000000000000000236261233466112000257100ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtOrganizer 5.0 TestCase { id: test name: "OrganizerE2ETests" property int spyWaitDelay: 250 QOrganizerTestUtility { id: utility } OrganizerModel { id: model startPeriod:'2009-01-01'; endPeriod:'2012-12-31'; } SignalSpy { id: spyManagerChanged signalName: "managerChanged" target: model } SignalSpy { id: spyModelChanged signalName: "modelChanged" target: model } function cleanup() { model.manager = "" } function test_megaitems_data() { return [{ managers: utility.getManagerList(), definitions: { "Event": { "Type": {}, "Guid": {}, "DisplayLabel": { "label": "'dummy'" }, "Description": { "description": "'dummy'" }, "Comment": { "comment": "'dummy'" }, "Tag": { "tag": "'dummy'" }, "ExtendedDetail": { "name": "'dummy'", "data": "'dummy'" }, "EventTime": { "startDateTime": "'2012-01-01'", "endDateTime": "'2012-01-01'" }, "Priority": { "priority": "Priority.Medium" }, "Location": { "label": "'dummy'", "longitude": "1.2", "latitude": "3.4", }, "AudibleReminder": { "dataUrl": "'url'" }, "EventAttendee": { "name": "'dummy'", "emailAddress": "'new.attendee@qt.com'", "attendeeId": "'123444455555'", "participationStatus": "EventAttendee.StatusAccepted", "participationRole": "EventAttendee.RoleRequiredParticipant" }, "EventRsvp": { "organizerName": "'Oswald Organizer'", "organizerEmail": "'ossy@waldo.com'", "participationStatus": "EventAttendee.StatusDelegated", "participationRole": "EventAttendee.RoleChairperson", "responseRequirement": "EventRsvp.ResponseRequired", "responseDeadline": "'2012-10-10'", "responseDate": "'2012-01-01'" } }, "Todo": { "Type": {}, "Guid": {}, "DisplayLabel": { "label": "'dummy'" }, "Description": { "description": "'dummy'" }, "Comment": { "comment": "'dummy'" }, "Tag": { "tag": "'dummy'" }, "ExtendedDetail": { "name": "'dummy'", "data": "'dummy'" }, "TodoTime": { "startDateTime": "'2012-01-01'", "dueDateTime": "'2012-01-02'" }, "Priority": { "priority": "Priority.Medium" }, "AudibleReminder": { "dataUrl": "'url'" } } } }] } function test_megaitems(data) { var qmlItems = createQMLItemsFromHash(data.definitions) for (var i in data.managers) { console.log("Testing "+data.managers[i]+" backend") model.manager = data.managers[i] spyManagerChanged.wait(spyWaitDelay) cleanDatabase() for (var j in qmlItems) { model.saveItem(qmlItems[j]) spyModelChanged.wait(spyWaitDelay) } compare(model.itemCount, qmlItems.length, "Items were not successfully saved.") compareViewToModel(qmlItems, model) cleanDatabase() } } // Helper functions function cleanDatabase() { var ids = model.itemIds() spyModelChanged.clear() if (ids.length > 0) { model.removeItems(ids) spyModelChanged.wait(spyWaitDelay) } compare(model.itemIds().length, 0) } function createQMLItemsFromHash(data) { var qmlItems = [] for (var itemDef in data) { var item = Qt.createQmlObject( "import QtOrganizer 5.0;" + itemDef + "{id: item" + itemDef.toLowerCase() + "}", test) for (var detailDef in data[itemDef]) { // Don't override the type if (detailDef == "Type") continue var s = "import QtOrganizer 5.0;" + detailDef + "{" + "id: detail" + detailDef.toLowerCase() + "; " for (var fieldDef in data[itemDef][detailDef]) { s += fieldDef + ": " + data[itemDef][detailDef][fieldDef] + "; " } s += "}" var detail = Qt.createQmlObject(s, test) var success = item.setDetail(detail) } qmlItems.push(item) } return qmlItems } function compareViewToModel(qmlItems, model) { for (var i = 0; i < qmlItems.length; i++) { var match = false for (var j = 0; j < model.itemCount; j++) { if (qmlItems[i].itemType == model.items[j].itemType) { for (var k = 0; k < qmlItems[i].itemDetails.length; k++) { match = false for (var l = 0; l < model.items[j].itemDetails.length; l++) { if (qmlItems[i].itemDetails[k].type == model.items[j].itemDetails[l].type) { match = true; var originalDetail = qmlItems[i].itemDetails[k]; var modelDetail = model.items[j].itemDetails[l]; var fieldType = originalDetail.type; // 10 is big enough number to go through all the fields of invidual detail for (var m = 0; m < 10; m++) { fieldType++; var originalFieldValue = originalDetail.value(fieldType); var modelFieldValue = modelDetail.value(fieldType); if (originalFieldValue == undefined) { if (fieldType == Guid.FieldGuid) { // exceptions, where model is updating the fields so they differ from original test set continue; } verify(modelFieldValue == undefined) break;// all fields for this detail have been checked } compare(modelFieldValue, originalFieldValue) } break; } } compare(match, true) } break; } } compare(match, true) } } } tests/auto/organizer/qmlorganizer/testcases/tst_organizeremailreminder.qml000066400000000000000000000216061233466112000300460ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtOrganizer 5.0 Rectangle { id: test; QOrganizerTestUtility { id: utility } TestCase { name: "Email Reminder" function test_emailReminder_data() { return [ { tag: " no properties", code: "import QtOrganizer 5.0;" + "EmailReminder {" + "}" }, { tag: " 1 properties", code: "import QtOrganizer 5.0;" + "EmailReminder {" + " repetitionCount: 3; }" }, { tag: " 2 properties", code: "import QtOrganizer 5.0;" + "EmailReminder {" + " repetitionCount: 3; " + " repetitionDelay: 30;}" }, { tag: " 3 properties", code: "import QtOrganizer 5.0;" + "EmailReminder {" + " repetitionCount: -1; " + " secondsBeforeStart: 100;" + " attachments : [\"1attachments\", \"2attachments\", \"3attachments\"];}" }, { tag: " 6 properties", code: "import QtOrganizer 5.0;" + "EmailReminder {" + " repetitionCount: 3; " + " repetitionDelay: 30; " + " secondsBeforeStart: 40; " + " body: \"email body........not a long one\"; " + " recipients: [\"one@email.com\", \"two@email.com\", \"three@email.com\"];" + " attachments : [\"1attachments\", \"2attachments\", \"3attachments\"];" + " subject: \"email subject\";}" }, ] } function test_emailReminder(data) { var list = utility.getManagerList(); if (list.length < 0) { console.log("No manager to test"); return; } //Test all manager backends for (var i = 0; i < list.length; i ++) { var managerName = list[i]; if (managerName == "jsondb")//jsondb backend does not support email reminder return; console.log("Email reminder test start! :" + managerName); var model = Qt.createQmlObject( "import QtOrganizer 5.0;" + "OrganizerModel {" + " manager: \"qtorganizer:" + managerName + ":id=qml\";" + " startPeriod:'2009-01-01';" + " endPeriod:'2012-12-31';" + " autoUpdate:true; }" , test); var event = Qt.createQmlObject( "import QtOrganizer 5.0;" + "Event { " + " id:event;" + " displayLabel: \"organizer qml reminder test event\"; " + " description: \"This is a new reminder event!!!\"; " + " startDateTime: '2010-12-12'; " + " endDateTime: '2010-12-13'; }" , test); var emailReminderDetail = Qt.createQmlObject(data.code, event); utility.init(model); utility.empty_calendar(); //------Create and save the detail test------// emailReminderDetail.repetitionCount = -1; event.setDetail(emailReminderDetail); model.saveItem(event); //Wait for the model to be up-to-date utility.waitModelChange(1); compare(model.itemCount, 1) var fetchlist = model.items; compare(model.itemCount, 1) var savedEvent = fetchlist[0]; var detail = savedEvent.detail(Detail.EmailReminder); verify(savedEvent != undefined) verify(detail != undefined) utility.compareReminderDetails(emailReminderDetail, detail); //------update the detail test------// var savedEventDetail = savedEvent.detail(Detail.EmailReminder); savedEventDetail.subject = "email subject"; savedEventDetail.body = "Hello! mail body!!!"; savedEventDetail.recipients = ["arecipients", "brecipients", "crecipients"]; savedEventDetail.attachments = ["aattachments", "battachments", "cattachments"]; savedEventDetail.secondsBeforeStart = 300; savedEventDetail.repetitionCount = -1; emailReminderDetail.subject = "email subject"; emailReminderDetail.body = "Hello! mail body!!!"; emailReminderDetail.recipients = ["arecipients", "brecipients", "crecipients"]; emailReminderDetail.attachments = ["aattachments", "battachments", "cattachments"]; emailReminderDetail.secondsBeforeStart = 300; emailReminderDetail.repetitionCount = -1; savedEvent.setDetail(savedEventDetail); model.saveItem(savedEvent); utility.organizerChangedSpy.wait(); //no new event created compare(model.itemCount, 1) fetchlist = model.items; var updatedEvent = fetchlist[0]; var updatedEventDetail = updatedEvent.detail(Detail.EmailReminder); verify(updatedEvent != undefined) verify(updatedEventDetail != undefined) utility.compareReminderDetails(emailReminderDetail, updatedEventDetail); //------remove the detail test------// var removeEventDetail = updatedEvent.detail(Detail.EmailReminder); updatedEvent.removeDetail(removeEventDetail); model.saveItem(updatedEvent); utility.organizerChangedSpy.wait(); //no new event created compare(model.itemCount, 1) fetchlist = model.items; var detailRemovedEvent = fetchlist[0]; var detailRemovedEventDetailList = detailRemovedEvent.details(Detail.EmailReminder); if (detailRemovedEventDetailList.length > 0) utility.outputDetail(detailRemovedEvent.detail(Detail.EmailReminder)); verify(detailRemovedEventDetailList.length == 0) verify(removeEventDetail != undefined) utility.organizerChangedSpy.clear(); utility.organizerChangedSpy.destroy(); model.destroy(); event.destroy(); emailReminderDetail.destroy(); // utility.empty_calendar(); } } } } tests/auto/organizer/qmlorganizer/testcases/tst_organizerintersectionfilter.qml000066400000000000000000000171331233466112000311450ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtOrganizer 5.0 Rectangle { id: test; QOrganizerTestUtility { id: utility } Collection { id: testCollection name: 'My collection filter' description: 'IntersectionFilter test' } CollectionFilter { id: collectionFilter } IdFilter { id: idFilter } TestCase { name: "Organizer intersection filter test" function test_intersectionFilter_data() { return [ { tag: "properties", code: "import QtOrganizer 5.0;" + "IntersectionFilter{\n" + " id:intersectionFilter" + "}\n" }, ] } function test_intersectionFilter(data) { var list = utility.getManagerList(); if (list.length < 0) { console.log("No manager to test"); return; } //Test all manager backends for (var i = 0; i < list.length; i ++) { var managerName = list[i]; var debugFlag = 0; console.log("test_intersectionFilter test start! :" + managerName); var model = Qt.createQmlObject( "import QtOrganizer 5.0;" + "OrganizerModel {" + " manager: \"qtorganizer:" + managerName + ":id=qml\";" + " startPeriod:'2009-01-01';" + " endPeriod:'2012-12-31';" + " autoUpdate:true; }" , test); var intersectionFilter = Qt.createQmlObject(data.code, test); var event = Qt.createQmlObject( "import QtOrganizer 5.0;" + "Event { " + " id:event;" + " displayLabel: \"organizer intersection filter test event\"; " + " description: \"organizer intersection filter test event\"; " + " startDateTime: '2010-12-12'; " + " endDateTime: '2010-12-13'; }" , test); utility.init(model); utility.waitModelChange(); utility.empty_calendar(); //------prepare filter data: save event and collection------// var collectionLegnth = model.collections.length model.saveCollection(testCollection) //Let's wait for the model to be up-to-date utility.waitModelChange(collectionLegnth + 1, utility.collectionChange); compare(model.collections.length, collectionLegnth + 1) //we should have more than default collection now var savedCollection = model.collections[model.collections.length - 1]; model.saveItem(event); utility.waitModelChange(1); compare(model.itemCount, 1) //event with new collection id event.collectionId = savedCollection.collectionId; model.saveItem(event); utility.waitModelChange(2); compare(model.itemCount, 2) var fetchlist = model.items; var idEventId; var collectionEventId; if (fetchlist[1].collectionId == savedCollection.collectionId) { idEventId = fetchlist[0].itemId; collectionEventId = fetchlist[1].itemId; } else if (fetchlist[0].collectionId == savedCollection.collectionId) { idEventId = fetchlist[1].itemId; collectionEventId = fetchlist[0].itemId; } else { console.log("Something Wrong!"); } utility.debug("Single filter", debugFlag); idFilter.ids = [idEventId]; //Single filter intersectionFilter.filters = [idFilter]; model.filter = intersectionFilter; utility.waitModelChange(1); compare(model.itemCount, 1) //Change collection filter id collectionFilter.ids = [savedCollection.collectionId]; //Duoble filters model.filter.filters = [idFilter, collectionFilter] utility.debug("Duoble filter", debugFlag); utility.waitModelChange(0); compare(model.itemCount, 0) //Double filter 2 //Change collection filter id collectionFilter.ids = [savedCollection.collectionId, model.defaultCollection().collectionId]; model.filter.filters = [idFilter, collectionFilter] utility.debug("Duoble filter 2", debugFlag); utility.waitModelChange(1); compare(model.itemCount, 1) //Double filter 3 //Change collection filter id collectionFilter.ids = [savedCollection.collectionId, model.defaultCollection().collectionId]; idFilter.ids = [idEventId, collectionEventId]; utility.debug("Duoble filter ~3", debugFlag); model.filter.filters = [idFilter, collectionFilter] utility.debug("Duoble filter 3" + collectionFilter.ids, debugFlag); utility.waitModelChange(2); compare(model.itemCount, 2) utility.debug("Test over!", debugFlag); utility.empty_calendar(); } } } } tests/auto/organizer/qmlorganizer/testcases/tst_organizeritem.qml000066400000000000000000000370041233466112000261660ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtOrganizer 5.0 TestCase { name: "OrganizerItemTests" id: organizerItemTests property int waitTime : 200 QOrganizerTestUtility { id: utility } SignalSpy { id: itemChangedSpy signalName: "itemChanged" } OrganizerItem { id: item } Event { id: event } EventOccurrence { id: eventOccurrence } Journal { id: journal } Note { id: note } Todo { id: todo } TodoOccurrence { id: todoOccurrence } Comment { id: comment comment: "Code Less" } EventAttendee { id: eventAttendee } ExtendedDetail { id: extendedDetail name: "extendedDetail1" data: "data 1" } function cleanup() { itemChangedSpy.clear() itemChangedSpy.target = null } function test_todoOccurrence() { itemChangedSpy.target = todoOccurrence; var count = 0; compare(todoOccurrence.itemType, Type.TodoOccurrence) compare(todoOccurrence.allDay, false) todoOccurrence.allDay = true; itemChangedSpy.wait(waitTime); compare(todoOccurrence.allDay, true) compare(itemChangedSpy.count, ++count) todoOccurrence.allDay = false; itemChangedSpy.wait(waitTime); compare(todoOccurrence.allDay, false) compare(itemChangedSpy.count, ++count) todoOccurrence.percentageComplete = 89 compare(todoOccurrence.percentageComplete, 89) skip('TODO should be fixed conversion between local time and UTC to avoid a double conversion') var originalDate = new Date("2008-12-28") todoOccurrence.originalDate = originalDate compare(todoOccurrence.originalDate, originalDate) var startDateTime = new Date("1991-08-25T20:57:08Z") todoOccurrence.startDateTime = startDateTime compare(todoOccurrence.startDateTime, startDateTime) var dueDateTime = new Date("2008-12-28 15:22:33 GMT+0300") todoOccurrence.dueDateTime = dueDateTime compare(todoOccurrence.dueDateTime, dueDateTime) var finishedDateTime = new Date("1995-05-20 11:22:33 GMT+0200") todoOccurrence.finishedDateTime = finishedDateTime compare(todoOccurrence.finishedDateTime, finishedDateTime) todoOccurrence.priority = Priority.VeryHigh compare(todoOccurrence.priority, Priority.VeryHigh) todoOccurrence.status = TodoProgress.InProgress compare(todoOccurrence.status, TodoProgress.InProgress) todoOccurrence.parentId = todo.itemId compare(todoOccurrence.parentId, todo.itemId) } function test_todo() { compare(todo.itemType, Type.Todo) todo.allDay = true; compare(todo.allDay, true) todo.percentageComplete = 64 compare(todo.percentageComplete, 64) var startDateTime = new Date("1991-08-25 20:57:08 GMT+0000") todo.startDateTime = startDateTime compare(todo.startDateTime, startDateTime) var dueDateTime = new Date("2008-12-28 15:22:33 GMT+0300") todo.dueDateTime = dueDateTime compare(todo.dueDateTime, dueDateTime) var finishedDateTime = new Date("1995-05-20 11:22:33 GMT+0200") todo.finishedDateTime = finishedDateTime compare(todo.finishedDateTime, finishedDateTime) todo.priority = Priority.VeryHigh compare(todo.priority, Priority.VeryHigh) todo.status = TodoProgress.InProgress compare(todo.status, TodoProgress.InProgress) // recurrence var recurrenceDates = new Array() recurrenceDates[0] = new Date("2005-06-28") recurrenceDates[1] = new Date("2005-12-19") compare(todo.details(Detail.Recurrence).length, 0) todo.recurrence.recurrenceDates = recurrenceDates compare(todo.details(Detail.Recurrence).length, 1) compare(todo.recurrence.recurrenceDates.length, 2) } function test_note() { compare(note.itemType, Type.Note) } function test_journal() { compare(journal.itemType, Type.Journal) var dateTime = new Date("1991-08-25 20:57:08 GMT+0000") journal.dateTime = dateTime compare(journal.dateTime, dateTime) } function test_eventOccurrence() { itemChangedSpy.target = eventOccurrence var count = 0; compare(eventOccurrence.allDay, false) eventOccurrence.allDay = true; itemChangedSpy.wait(waitTime); compare(eventOccurrence.allDay, true) compare(itemChangedSpy.count, ++count) eventOccurrence.allDay = false; itemChangedSpy.wait(waitTime); compare(eventOccurrence.allDay, false) compare(itemChangedSpy.count, ++count) compare(eventOccurrence.itemType, Type.EventOccurrence) eventOccurrence.parentId = event.itemId; itemChangedSpy.wait(waitTime); compare(eventOccurrence.parentId, event.itemId) compare(itemChangedSpy.count, ++count) skip('TODO should be fixed conversion between local time and UTC to avoid a double conversion') var originalDate = new Date("2008-12-28") eventOccurrence.originalDate = originalDate itemChangedSpy.wait(waitTime); compare(eventOccurrence.originalDate, utility.toUTCMidnight(originalDate)) compare(itemChangedSpy.count, ++count) var startDateTime = new Date("1991-08-25 20:57:08 GMT+0000") eventOccurrence.startDateTime = startDateTime itemChangedSpy.wait(waitTime); compare(eventOccurrence.startDateTime, startDateTime) compare(itemChangedSpy.count, ++count) var endDateTime = new Date("1995-05-20 11:22:33 GMT+0200") eventOccurrence.endDateTime = endDateTime itemChangedSpy.wait(waitTime); compare(eventOccurrence.endDateTime, endDateTime) compare(itemChangedSpy.count, ++count) eventOccurrence.priority = Priority.VeryHigh itemChangedSpy.wait(waitTime); compare(eventOccurrence.priority, Priority.VeryHigh) compare(itemChangedSpy.count, ++count) eventOccurrence.location = "Tampere" itemChangedSpy.wait(waitTime); compare(eventOccurrence.location, "Tampere") compare(itemChangedSpy.count, ++count) } function test_event() { itemChangedSpy.target = event; var count = 0; compare(event.itemType, Type.Event); event.allDay = true; itemChangedSpy.wait(waitTime); compare(event.allDay, true) compare(itemChangedSpy.count, ++count) //Following format does not work: GMT+0000 is not accepted by javascript //event.startDateTime = "1991-08-25 20:57:08 GMT+0000" var startDateTime = new Date("1991-08-25 20:57:08 GMT+0000") event.startDateTime = startDateTime itemChangedSpy.wait(waitTime); compare(event.startDateTime, startDateTime) compare(itemChangedSpy.count, ++count) var endDateTime = new Date("1995-05-20 11:22:33 GMT+0200") event.endDateTime = endDateTime itemChangedSpy.wait(waitTime); compare(event.endDateTime, endDateTime) compare(itemChangedSpy.count, ++count) event.priority = Priority.VeryHigh itemChangedSpy.wait(waitTime); compare(event.priority, Priority.VeryHigh) compare(itemChangedSpy.count, ++count) event.location = "Tampere" itemChangedSpy.wait(waitTime); compare(event.location, "Tampere") compare(itemChangedSpy.count, ++count) // recurrence var recurrenceDates = new Array() recurrenceDates[0] = new Date("2005-06-28") recurrenceDates[1] = new Date("2005-12-19") compare(event.details(Detail.Recurrence).length, 0) event.recurrence.recurrenceDates = recurrenceDates itemChangedSpy.wait(waitTime); compare(event.details(Detail.Recurrence).length, 1) compare(event.recurrence.recurrenceDates.length, 2) compare(itemChangedSpy.count, ++count) // attendee eventAttendee.name = "new attendee" eventAttendee.emailAddress = "new.attendee@qt.com" eventAttendee.attendeeId = "123444455555" eventAttendee.participationStatus = EventAttendee.StatusAccepted eventAttendee.participationRole = EventAttendee.RoleRequiredParticipant event.setDetail(eventAttendee) itemChangedSpy.wait(waitTime); compare(event.details(Detail.EventAttendee).length, 1) compare(event.detail(Detail.EventAttendee).name, "new attendee") compare(event.detail(Detail.EventAttendee).emailAddress, "new.attendee@qt.com") compare(event.detail(Detail.EventAttendee).participationStatus, EventAttendee.StatusAccepted) compare(event.detail(Detail.EventAttendee).participationRole, EventAttendee.RoleRequiredParticipant) compare(itemChangedSpy.count, ++count) // add one more dynamic created attendee var eventAttendee2 = utility.create_testobject("import QtTest 1.0;import QtOrganizer 5.0;" + "EventAttendee {}" , organizerItemTests); event.setDetail(eventAttendee2) itemChangedSpy.wait(waitTime); compare(event.details(Detail.EventAttendee).length, 2) compare(itemChangedSpy.count, ++count) // remove one attendee event.removeDetail(eventAttendee) itemChangedSpy.wait(waitTime); compare(event.details(Detail.EventAttendee).length, 1) compare(itemChangedSpy.count, ++count) // remove last attendee event.removeDetail(eventAttendee2) itemChangedSpy.wait(waitTime); compare(event.details(Detail.EventAttendee).length, 0) compare(itemChangedSpy.count, ++count) // attenddees property event.attendees = [eventAttendee]; itemChangedSpy.wait(waitTime); compare(event.attendees.length, 1) compare(itemChangedSpy.count, ++count) // attendees append test event.attendees = [eventAttendee, eventAttendee2]; itemChangedSpy.wait(waitTime); compare(event.attendees.length, 2) count += 3;//clear + 2 * append signals compare(itemChangedSpy.count, count) event.attendees = [eventAttendee2]; itemChangedSpy.wait(waitTime); compare(event.attendees.length, 1) count += 2;//clear + append signals compare(itemChangedSpy.count, count) // attendees clear event.attendees = []; itemChangedSpy.wait(waitTime); compare(event.attendees.length, 0) count += 1;//clear compare(itemChangedSpy.count, count) } function test_item() { itemChangedSpy.target = item var count = 0; // empty OrganizerItem compare(item.modified, false) // access Description item.description = "Qt Open Governance" itemChangedSpy.wait(waitTime); compare(item.description, "Qt Open Governance") compare(item.detail(Detail.Description).description, "Qt Open Governance") compare(item.details(Detail.Description).length, 1) compare(itemChangedSpy.count, ++count) // access DisplayLabel item.displayLabel = "http://qt-project.org/" itemChangedSpy.wait(waitTime); compare(item.displayLabel, "http://qt-project.org/") compare(item.detail(Detail.DisplayLabel).label, "http://qt-project.org/") compare(item.details(Detail.DisplayLabel).length, 1) compare(itemChangedSpy.count, ++count) // access Guid item.guid = "b7dd2d61-fcb3-1170-e314-899ac5e91c7c" itemChangedSpy.wait(waitTime); compare(item.guid, "b7dd2d61-fcb3-1170-e314-899ac5e91c7c") compare(item.detail(Detail.Guid).guid, "b7dd2d61-fcb3-1170-e314-899ac5e91c7c") compare(item.details(Detail.Guid).length, 1) compare(itemChangedSpy.count, ++count) // add new Detail item.setDetail(comment) itemChangedSpy.wait(waitTime); compare(item.detail(Detail.Comment).comment, "Code Less") compare(item.details(Detail.Comment).length, 1) compare(item.itemDetails.length, 4) compare(itemChangedSpy.count, ++count) // update existing Detail comment.comment = "Create More" item.setDetail(comment) itemChangedSpy.wait(waitTime); compare(item.detail(Detail.Comment).comment, "Create More") compare(item.details(Detail.Comment).length, 1) compare(itemChangedSpy.count, ++count) // delete one existing Detail item.removeDetail(comment) itemChangedSpy.wait(waitTime); compare(item.details(Detail.Comment).length, 0) compare(itemChangedSpy.count, ++count) // delete one no-existing Detail item.removeDetail(comment) compare(item.details(Detail.Comment).length, 0) compare(itemChangedSpy.count, count) // remove all existing Details item.clearDetails(); itemChangedSpy.wait(waitTime); compare(item.itemDetails.length, 0) compare(itemChangedSpy.count, ++count) // remove all existing Details again will not get any signal item.clearDetails(); compare(item.itemDetails.length, 0) compare(itemChangedSpy.count, count) // extended detail item.setDetail(extendedDetail) itemChangedSpy.wait(waitTime); compare(item.details(Detail.ExtendedDetail).length, 1) compare(item.detail(Detail.ExtendedDetail).name, "extendedDetail1") compare(item.detail(Detail.ExtendedDetail).data, "data 1") compare(itemChangedSpy.count, ++count) } } tests/auto/organizer/qmlorganizer/testcases/tst_organizeritemdetail.qml000066400000000000000000001075001233466112000273500ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtOrganizer 5.0 TestCase { name: "ItemDetailTests" id: organizerItemDetailTests property int waitTime : 200 QOrganizerTestUtility { id: utility } EventTime { id: eventTime } Comment { id: comment } Description { id: description } DisplayLabel { id: displayLabel } Guid { id: guid } Location { id: location } Parent { id: parent } Priority { id: priority } Tag { id: tag } Timestamp {//id "timestamp" conflict something id: timestamp1 } Type { id: type } JournalTime { id: journalTime } TodoProgress { id: todoProgress } TodoTime { id: todoTime } Reminder { id: reminder } AudibleReminder { id: audibleReminder } EmailReminder { id: emailReminder } VisualReminder { id: visualReminder } Recurrence { id: recurrence } ExtendedDetail { id: extendedDetail } EventAttendee { id: eventAttendee } EventRsvp { id: eventRsvp } Classification { id: classification } Version { id: version } function test_version() { compare(version.type, Detail.Version) compare(version.version, 0) compare(version.value(Version.FieldVersion), undefined) version.version = 64 compare(version.version, 64) compare(version.value(Version.FieldVersion), 64) compare(version.extendedVersion, "") compare(version.value(Version.FieldExtendedVersion), undefined) version.extendedVersion = "Qt rules" compare(version.extendedVersion, "Qt rules") compare(version.value(Version.FieldExtendedVersion), "Qt rules") } function test_extendedDetail() { compare(extendedDetail.type, Detail.ExtendedDetail) compare(extendedDetail.name, "") compare(extendedDetail.value(ExtendedDetail.FieldName), undefined) extendedDetail.name = "Qt" compare(extendedDetail.name, "Qt") compare(extendedDetail.value(ExtendedDetail.FieldName), "Qt") compare(extendedDetail.data, undefined) compare(extendedDetail.value(ExtendedDetail.FieldData), undefined) extendedDetail.data = "Everywhere" compare(extendedDetail.data, "Everywhere"); compare(extendedDetail.value(ExtendedDetail.FieldData), "Everywhere") } function test_recurrence() { } function test_recurrenceDateArrays_data() { return [ {tag: "basic date object", testValue: [new Date(2012, 2, 16, 11, 00, 00)]}, {tag: "date object from string", testValue: [new Date("January 1, 1986")]}, {tag: "date object from ISO date", testValue: [new Date('2014-01-01')]}, {tag: "datetime object from string", testValue: [new Date("October 13, 1975 11:13:00")]}, // {tag: "date string", testValue: ['2013-01-01']}, // TODO fix conversion to UTC from string // {tag: "datetime string", testValue: ['2013-10-23T23:55:00']}, // TODO fix conversion to UTC from string {tag: "datetime string ISO 8601 Z", testValue: ['1997-07-16T19:20:30.45Z']}, {tag: "datetime string ISO 8601 +01", testValue: ['1997-07-16T19:20:30.45+01:00']}, {tag: "datetime string ISO 8601 +10", testValue: ['1997-07-16T19:20:30.45+10:00']}, {tag: "datetime string ISO 8601 -01", testValue: ['1997-07-16T19:20:30.45-01:00']}, {tag: "datetime string ISO 8601 -10", testValue: ['1997-07-16T19:20:30.45-10:00']} ] } function test_recurrenceDateArrays(data) { console.log()//print the separate cases var detailChangedSpy = utility.create_testobject("import QtTest 1.0;SignalSpy{}", organizerItemDetailTests) var tempRecurrence = utility.create_testobject("import QtOrganizer 5.0; Recurrence{}", organizerItemDetailTests) detailChangedSpy.target = tempRecurrence; detailChangedSpy.signalName = "detailChanged" var testDate = (typeof data.testValue[0] == 'string') ? new Date(data.testValue[0]) : data.testValue[0]; var testDateUTCMidnight = utility.toUTCMidnight(testDate);; if (isNaN(testDate.getTime())) { warn("test \"" + data.tag + "\" contains incorrect date"); return; } // recurrenceDates tempRecurrence.recurrenceDates = data.testValue detailChangedSpy.wait(waitTime) compare(tempRecurrence.recurrenceDates[0], testDateUTCMidnight) var rdates = tempRecurrence.value(Recurrence.FieldRecurrenceDates); compare(rdates[0], testDateUTCMidnight); compare(detailChangedSpy.count, 1) // exceptionDates tempRecurrence.setValue(Recurrence.FieldExceptionDates, data.testValue) detailChangedSpy.wait(waitTime) compare(tempRecurrence.exceptionDates[0], testDateUTCMidnight) var edates = tempRecurrence.value(Recurrence.FieldExceptionDates); compare(edates[0], testDateUTCMidnight); compare(detailChangedSpy.count, 2) } function test_visualReminder() { compare(visualReminder.type, Detail.VisualReminder) compare(visualReminder.reminderType, Reminder.VisualReminder) compare(visualReminder.repetitionCount, 0) compare(visualReminder.value(VisualReminder.FieldRepetitionCount), undefined) compare(visualReminder.repetitionDelay, 0) compare(visualReminder.value(VisualReminder.FieldRepetitionDelay), undefined) compare(visualReminder.secondsBeforeStart, 0) compare(visualReminder.value(VisualReminder.FieldSecondsBeforeStart), undefined) compare(visualReminder.message, "") compare(visualReminder.value(VisualReminder.FieldMessage), undefined) compare(visualReminder.dataUrl, "") compare(visualReminder.value(VisualReminder.FieldDataUrl), undefined) visualReminder.repetitionCount = 19 compare(visualReminder.repetitionCount, 19) compare(visualReminder.value(VisualReminder.FieldRepetitionCount), 19) visualReminder.repetitionDelay = 89 compare(visualReminder.repetitionDelay, 89) compare(visualReminder.value(VisualReminder.FieldRepetitionDelay), 89) visualReminder.secondsBeforeStart = 64 compare(visualReminder.secondsBeforeStart, 64) compare(visualReminder.value(VisualReminder.FieldSecondsBeforeStart), 64) visualReminder.message = "Qt Open Governance" compare(visualReminder.message, "Qt Open Governance") compare(visualReminder.value(VisualReminder.FieldMessage), "Qt Open Governance") visualReminder.dataUrl = "http://qt-project.org/" compare(visualReminder.dataUrl, "http://qt-project.org/") compare(visualReminder.value(VisualReminder.FieldDataUrl), "http://qt-project.org/") } function test_emailReminder() { compare(emailReminder.type, Detail.EmailReminder) compare(emailReminder.reminderType, Reminder.EmailReminder) compare(emailReminder.repetitionCount, 0) compare(emailReminder.value(EmailReminder.FieldRepetitionCount), undefined) compare(emailReminder.repetitionDelay, 0) compare(emailReminder.value(EmailReminder.FieldRepetitionDelay), undefined) compare(emailReminder.secondsBeforeStart, 0) compare(emailReminder.value(EmailReminder.FieldSecondsBeforeStart), undefined) compare(emailReminder.body, "") compare(emailReminder.value(EmailReminder.FieldBody), undefined) compare(emailReminder.subject, "") compare(emailReminder.value(EmailReminder.FieldSubject), undefined) compare(emailReminder.recipients.length, 0) compare(emailReminder.value(EmailReminder.FieldRecipients), undefined) compare(emailReminder.attachments. length, 0) compare(emailReminder.value(EmailReminder.FieldAttachments), undefined) emailReminder.repetitionCount = 19 compare(emailReminder.repetitionCount, 19) compare(emailReminder.value(EmailReminder.FieldRepetitionCount), 19) emailReminder.repetitionDelay = 89 compare(emailReminder.repetitionDelay, 89) compare(emailReminder.value(EmailReminder.FieldRepetitionDelay), 89) emailReminder.secondsBeforeStart = 64 compare(emailReminder.secondsBeforeStart, 64) compare(emailReminder.value(EmailReminder.FieldSecondsBeforeStart), 64) emailReminder.body = "Qt - Cross-platform application and UI framework" compare(emailReminder.body, "Qt - Cross-platform application and UI framework") compare(emailReminder.value(EmailReminder.FieldBody), "Qt - Cross-platform application and UI framework") emailReminder.subject = "Qt!" compare(emailReminder.subject, "Qt!") compare(emailReminder.value(EmailReminder.FieldSubject), "Qt!") emailReminder.recipients = ["Berlin", "Brisbane", "Oslo", "Tampere"] compare(emailReminder.recipients, ["Berlin", "Brisbane", "Oslo", "Tampere"]) compare(emailReminder.value(EmailReminder.FieldRecipients), ["Berlin", "Brisbane", "Oslo", "Tampere"]) emailReminder.attachments = [1, 2, "345"] compare(emailReminder.attachments, [1, 2, "345"]) compare(emailReminder.value(EmailReminder.FieldAttachments), [1, 2, "345"]) } function test_audibleReminder() { compare(audibleReminder.type, Detail.AudibleReminder) compare(audibleReminder.reminderType, Reminder.AudibleReminder) compare(audibleReminder.repetitionCount, 0) compare(audibleReminder.value(AudibleReminder.FieldRepetitionCount), undefined) compare(audibleReminder.repetitionDelay, 0) compare(audibleReminder.value(AudibleReminder.FieldRepetitionDelay), undefined) compare(audibleReminder.secondsBeforeStart, 0) compare(audibleReminder.value(AudibleReminder.FieldSecondsBeforeStart), undefined) compare(audibleReminder.dataUrl, "") compare(audibleReminder.value(AudibleReminder.FieldDataUrl), undefined) audibleReminder.repetitionCount = 19 compare(audibleReminder.repetitionCount, 19) compare(audibleReminder.value(AudibleReminder.FieldRepetitionCount), 19) audibleReminder.repetitionDelay = 89 compare(audibleReminder.repetitionDelay, 89) compare(audibleReminder.value(AudibleReminder.FieldRepetitionDelay), 89) audibleReminder.secondsBeforeStart = 64 compare(audibleReminder.secondsBeforeStart, 64) compare(audibleReminder.value(AudibleReminder.FieldSecondsBeforeStart), 64) audibleReminder.dataUrl = "http://qt-project.org/" compare(audibleReminder.dataUrl, "http://qt-project.org/") compare(audibleReminder.value(AudibleReminder.FieldDataUrl), "http://qt-project.org/") } function test_reminder() { compare(reminder.type, Detail.Reminder) compare(reminder.reminderType, Reminder.NoReminder) compare(reminder.repetitionCount, 0) compare(reminder.value(Reminder.FieldRepetitionCount), undefined) compare(reminder.repetitionDelay, 0) compare(reminder.value(Reminder.FieldRepetitionDelay), undefined) compare(reminder.secondsBeforeStart, 0) compare(reminder.value(Reminder.FieldSecondsBeforeStart), undefined) reminder.repetitionCount = 19 compare(reminder.repetitionCount, 19) compare(reminder.value(Reminder.FieldRepetitionCount), 19) reminder.repetitionDelay = 89 compare(reminder.repetitionDelay, 89) compare(reminder.value(Reminder.FieldRepetitionDelay), 89) reminder.secondsBeforeStart = 64 compare(reminder.secondsBeforeStart, 64) compare(reminder.value(Reminder.FieldSecondsBeforeStart), 64) } function test_todoTime() { compare(todoTime.type, Detail.TodoTime) compare(todoTime.allDay, false) compare(todoTime.value(TodoTime.FieldAllDay), undefined) todoTime.allDay = true compare(todoTime.allDay, true) compare(todoTime.value(TodoTime.FieldAllDay), true) compare(todoTime.value(TodoTime.FieldStartDateTime), undefined) var startDateTime = new Date("1991-08-25 20:57:08 GMT+0000") todoTime.startDateTime = startDateTime compare(todoTime.startDateTime, startDateTime) compare(todoTime.value(TodoTime.FieldStartDateTime), startDateTime) compare(todoTime.value(TodoTime.FieldDueDateTime), undefined) var dueDateTime = new Date("1995-05-20 11:22:33 GMT+0200") todoTime.dueDateTime = dueDateTime compare(todoTime.dueDateTime, dueDateTime) compare(todoTime.value(TodoTime.FieldDueDateTime), dueDateTime) } function test_todoProgress() { compare(todoProgress.type, Detail.TodoProgress) compare(todoProgress.value(TodoProgress.FieldFinishedDateTime), undefined) var finishedDateTime = new Date("1991-08-25 20:57:08 GMT+0000") todoProgress.finishedDateTime = finishedDateTime compare(todoProgress.finishedDateTime, finishedDateTime) compare(todoProgress.value(TodoProgress.FieldFinishedDateTime), finishedDateTime) compare(todoProgress.value(TodoProgress.FieldPercentageComplete), undefined) todoProgress.percentageComplete = 64 compare(todoProgress.percentageComplete, 64) compare(todoProgress.value(TodoProgress.FieldPercentageComplete), 64) compare(todoProgress.value(TodoProgress.FieldStatus), undefined) todoProgress.status = TodoProgress.InProgress compare(todoProgress.status, TodoProgress.InProgress) compare(todoProgress.value(TodoProgress.FieldStatus), TodoProgress.InProgress) } function test_journalTime() { compare(journalTime.type, Detail.JournalTime) compare(journalTime.value(JournalTime.FieldEntryDateTime), undefined) var entryDateTime = new Date("1991-08-25 20:57:08 GMT+0000") journalTime.entryDateTime = entryDateTime compare(journalTime.entryDateTime, entryDateTime) compare(journalTime.value(JournalTime.FieldEntryDateTime), journalTime.entryDateTime) } function test_type() { compare(type.type, Detail.ItemType) compare(type.value(Type.FieldType), undefined) type.itemType = Type.TodoOccurrence compare(type.itemType, Type.TodoOccurrence) } function test_timestamp() { compare(timestamp1.type, Detail.Timestamp) compare(timestamp1.value(Timestamp.FieldCreated), undefined) var timestamp = new Date("1991-08-25 20:57:08 GMT+0000") timestamp.created = timestamp compare(timestamp.created, timestamp) compare(timestamp1.value(Timestamp.FieldLastModified), undefined) var lastModified = new Date("1995-05-20 11:22:33 GMT+0200") timestamp.lastModified = lastModified compare(timestamp.lastModified, lastModified) } function test_tag() { compare(tag.type, Detail.Tag) compare(tag.value(Tag.FieldTag), undefined) tag.tag = "Qt" compare(tag.tag, "Qt") compare(tag.value(Tag.FieldTag), "Qt") } function test_priority() { compare(priority.type, Detail.Priority) compare(priority.value(Priority.FieldPriority), undefined) priority.priority = Priority.Medium compare(priority.priority, Priority.Medium) compare(priority.value(Priority.FieldPriority), Priority.Medium) } function test_parent() { compare(parent.type, Detail.Parent) skip('TODO should be fixed conversion between local time and UTC to avoid a double conversion') compare(parent.value(Parent.FieldOriginalDate), undefined) var originalDate = new Date("2008-12-28") parent.originalDate = originalDate compare(parent.originalDate, originalDate) compare(parent.value(Parent.FieldOriginalDate), originalDate) var originalDate2 = new Date("2008-01-01") var originalDate2UTC = utility.toUTCMidnight(originalDate2) parent.setValue(Parent.FieldOriginalDate, originalDate2) compare(parent.originalDate, originalDate2UTC) compare(parent.value(Parent.FieldOriginalDate), originalDate2UTC) parent.setValue(Parent.FieldOriginalDate, "2008-01-01") compare(parent.originalDate, originalDate2UTC) compare(parent.value(Parent.FieldOriginalDate), originalDate2UTC) } function test_location() { compare(location.type, Detail.Location) compare(location.value(Location.FieldLatitude), undefined) location.latitude = 1.2 compare(location.latitude, 1.2) compare(location.value(Location.FieldLatitude), 1.2) compare(location.value(Location.FieldLongitude), undefined) location.longitude = 3.4 compare(location.longitude, 3.4) compare(location.value(Location.FieldLongitude), 3.4) compare(location.value(Location.FieldLabel), undefined) location.label = "Zion" compare(location.label, "Zion") compare(location.value(Location.FieldLabel), "Zion") } function test_guid() { compare(guid.type, Detail.Guid) compare(guid.value(Guid.FieldGuid), undefined) guid.guid = "b7dd2d61-fcb3-1170-e314-899ac5e91c7c" compare(guid.guid, "b7dd2d61-fcb3-1170-e314-899ac5e91c7c") compare(guid.value(Guid.FieldGuid), "b7dd2d61-fcb3-1170-e314-899ac5e91c7c") } function test_displayLabel() { compare(displayLabel.type, Detail.DisplayLabel) compare(displayLabel.value(DisplayLabel.FieldLabel), undefined) displayLabel.label = "Deploy Everywhere" compare(displayLabel.label, "Deploy Everywhere") compare(displayLabel.value(DisplayLabel.FieldLabel), "Deploy Everywhere") } function test_description() { compare(description.type, Detail.Description) compare(description.value(Description.FieldDescription), undefined) description.description = "Create More" compare(description.description, "Create More") compare(description.value(Description.FieldDescription), "Create More") } function test_comment() { compare(comment.type, Detail.Comment) compare(comment.value(Comment.FieldComment), undefined) comment.comment = "Code Less" compare(comment.comment, "Code Less") compare(comment.value(Comment.FieldComment), "Code Less") } function test_eventTime() { compare(eventTime.type, Detail.EventTime) compare(eventTime.value(EventTime.FieldAllDay), undefined) eventTime.allDay = true compare(eventTime.allDay, true) compare(eventTime.value(EventTime.FieldAllDay), true) compare(eventTime.value(EventTime.FieldStartDateTime), undefined) var startDateTime = new Date("1991-08-25 20:57:08 GMT+0000") eventTime.startDateTime = startDateTime compare(eventTime.startDateTime, startDateTime) compare(eventTime.value(EventTime.FieldStartDateTime), startDateTime) compare(eventTime.value(EventTime.FieldEndDateTime), undefined) var endDateTime = new Date("1995-05-20 11:22:33 GMT+0200") eventTime.endDateTime = endDateTime compare(eventTime.endDateTime, endDateTime) compare(eventTime.value(EventTime.FieldEndDateTime), endDateTime) } function test_attendeeDetail() { var detailChangedSpy = utility.create_testobject("import QtTest 1.0;" + "SignalSpy {}" , organizerItemDetailTests); detailChangedSpy.target = eventAttendee; detailChangedSpy.signalName = "detailChanged"; var count = 0; compare(eventAttendee.type, Detail.EventAttendee) eventAttendee.name = "new attendee" detailChangedSpy.wait(waitTime) compare(eventAttendee.name, "new attendee") compare(detailChangedSpy.count, ++count) eventAttendee.emailAddress = "new.attendee@qt.com" detailChangedSpy.wait(waitTime) compare(eventAttendee.emailAddress, "new.attendee@qt.com") compare(detailChangedSpy.count, ++count) eventAttendee.attendeeId = "123444455555" detailChangedSpy.wait(waitTime) compare(eventAttendee.attendeeId, "123444455555") compare(detailChangedSpy.count, ++count) eventAttendee.participationStatus = EventAttendee.StatusAccepted detailChangedSpy.wait(waitTime) compare(eventAttendee.participationStatus, EventAttendee.StatusAccepted) compare(detailChangedSpy.count, ++count) eventAttendee.participationRole = EventAttendee.RoleRequiredParticipant detailChangedSpy.wait(waitTime) compare(eventAttendee.participationRole, EventAttendee.RoleRequiredParticipant) compare(detailChangedSpy.count, ++count) // set same value eventAttendee.participationRole = EventAttendee.RoleRequiredParticipant compare(eventAttendee.participationRole, EventAttendee.RoleRequiredParticipant) // no signal has been emited compare(detailChangedSpy.count, count) } function test_rsvpGeneralTesting() { var detailChangedSpy = utility.create_testobject("import QtTest 1.0;" + "SignalSpy {}" , organizerItemDetailTests); detailChangedSpy.target = eventRsvp; detailChangedSpy.signalName = "detailChanged"; var count = 0; compare(eventRsvp.type, Detail.EventRsvp) // default value checks compare(eventRsvp.organizerName, "") compare(eventRsvp.organizerEmail, "") compare(eventRsvp.participationStatus, EventAttendee.StatusUnknown) compare(eventRsvp.participationRole, EventAttendee.RoleUnknown) compare(eventRsvp.responseRequirement, EventRsvp.ResponseNotRequired) // no change tests are here to minimize the delays of testing // organizerName - no change on value eventRsvp.organizerName = eventRsvp.organizerName compare(eventRsvp.organizerName, eventRsvp.organizerName) compare(detailChangedSpy.count, count) // organizerEmail - no change on value eventRsvp.organizerEmail = eventRsvp.organizerEmail compare(eventRsvp.organizerEmail, eventRsvp.organizerEmail) compare(detailChangedSpy.count, count) // participationStatus - no change on value eventRsvp.participationStatus = eventRsvp.participationStatus compare(eventRsvp.participationStatus, eventRsvp.participationStatus) compare(detailChangedSpy.count, count) // participationRole - no change on value eventRsvp.participationRole = eventRsvp.participationRole compare(eventRsvp.participationRole, eventRsvp.participationRole) compare(detailChangedSpy.count, count) // responseRequirement - no change on value eventRsvp.responseRequirement = eventRsvp.responseRequirement compare(eventRsvp.responseRequirement, eventRsvp.responseRequirement) compare(detailChangedSpy.count, count) } function test_rsvpStringProperties_data() { return [ {tag: "string", testValue: "short string"}, {tag: "long string", testValue: "here goes the long string name but what to have here? Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed posuere interdum sem. Quisque ligula eros ullamcorper quis, lacinia quis facilisis sed sapien. Mauris varius diam vitae arcu. Sed arcu lectus auctor vitae, consectetuer et venenatis eget velit. Sed augue orci, lacinia eu tincidunt et eleifend nec lacus. Donec ultricies nisl ut felis, suspendisse potenti. Lorem ipsum ligula ut hendrerit mollis, ipsum erat vehicula risus, eu suscipit sem libero nec erat. Aliquam erat volutpat. Sed congue augue vitae neque. Nulla consectetuer porttitor pede. Fusce purus morbi tortor magna condimentum vel, placerat id blandit sit amet tortor. Mauris sed libero. Suspendisse facilisis nulla in lacinia laoreet, lorem velit accumsan velit vel mattis libero nisl et sem. Proin interdum maecenas massa turpis sagittis in, interdum non lobortis vitae massa. Quisque purus lectus, posuere eget imperdiet nec sodales id arcu. Vestibulum elit pede dictum eu, viverra non tincidunt eu ligula. Nam molestie nec tortor. Donec placerat leo sit amet velit. Vestibulum id justo ut vitae massa. Proin in dolor mauris consequat aliquam. Donec ipsum, vestibulum ullamcorper venenatis augue. Aliquam tempus nisi in auctor vulputate, erat felis pellentesque augue nec, pellentesque lectus justo nec erat. Aliquam et nisl. Quisque sit amet dolor in justo pretium condimentum. Vivamus placerat lacus vel vehicula scelerisque, dui enim adipiscing lacus sit amet sagittis, libero enim vitae mi. In neque magna posuere, euismod ac tincidunt tempor est. Ut suscipit nisi eu purus. Proin ut pede mauris eget ipsum. Integer vel quam nunc commodo consequat. Integer ac eros eu tellus dignissim viverra. Maecenas erat aliquam erat volutpat. Ut venenatis ipsum quis turpis. Integer cursus scelerisque lorem. Sed nec mauris id quam blandit consequat. Cras nibh mi hendrerit vitae, dapibus et aliquam et magna. Nulla vitae elit. Mauris consectetuer odio vitae augue."}, {tag: "number", testValue: 123}, ] } function test_rsvpStringProperties(data) { var detailChangedSpy = utility.create_testobject("import QtTest 1.0;SignalSpy{}", organizerItemDetailTests) var tempEventRsvp = utility.create_testobject("import QtOrganizer 5.0; EventRsvp{}", organizerItemDetailTests) detailChangedSpy.target = tempEventRsvp detailChangedSpy.signalName = "detailChanged" // organizerName tempEventRsvp.organizerName = data.testValue detailChangedSpy.wait(waitTime) compare(tempEventRsvp.organizerName, data.testValue.toString()) compare(detailChangedSpy.count, 1) // organizerEmail tempEventRsvp.organizerEmail = data.testValue detailChangedSpy.wait(waitTime) compare(tempEventRsvp.organizerEmail, data.testValue.toString()) compare(detailChangedSpy.count, 2) } function test_rsvpDateProperties_data() { return [ {tag: "basic date object", testValue: new Date(2012, 2, 16, 11, 00, 00)}, {tag: "date object from string", testValue: new Date("January 1, 1986")}, {tag: "date object from ISO date", testValue: new Date('2014-01-01')}, {tag: "datetime object from string", testValue: new Date("October 13, 1975 11:13:00")}, // {tag: "date string", testValue: '2013-01-01'}, // TODO test fails for TZ=EET // {tag: "datetime string", testValue: '2013-10-23T23:55:00'}, // TODO test fails for TZ=HST {tag: "datetime string ISO 8601 Z", testValue: '1997-07-16T19:20:30.45Z'}, {tag: "datetime string ISO 8601 +01", testValue: '1997-07-16T19:20:30.45+01:00'}, {tag: "datetime string ISO 8601 +10", testValue: '1997-07-16T19:20:30.45+10:00'}, {tag: "datetime string ISO 8601 -01", testValue: '1997-07-16T19:20:30.45-01:00'}, {tag: "datetime string ISO 8601 -10", testValue: '1997-07-16T19:20:30.45-10:00'} ] } function test_rsvpDateProperties(data) { console.log()//print the separate cases var detailChangedSpy = utility.create_testobject("import QtTest 1.0;SignalSpy{}", organizerItemDetailTests) var tempEventRsvp = utility.create_testobject("import QtOrganizer 5.0; EventRsvp{}", organizerItemDetailTests) detailChangedSpy.target = tempEventRsvp detailChangedSpy.signalName = "detailChanged" var testDate = (typeof data.testValue == 'string') ? new Date(data.testValue) : data.testValue; var testDateUTCMidnight = utility.toUTCMidnight(testDate); if (isNaN(testDate.getTime())) { warn("test \"" + data.tag + "\" contains incorrect date"); return; } // responseDeadline tempEventRsvp.responseDeadline = data.testValue detailChangedSpy.wait(waitTime) compare(tempEventRsvp.value(EventRsvp.FieldResponseDeadline), testDateUTCMidnight); compare(tempEventRsvp.responseDeadline.toString(), testDateUTCMidnight.toString()) compare(detailChangedSpy.count, 1) // responseDate tempEventRsvp.setValue(EventRsvp.FieldResponseDate, data.testValue); detailChangedSpy.wait(waitTime) compare(tempEventRsvp.responseDate.toString(), testDateUTCMidnight.toString()) compare(tempEventRsvp.value(EventRsvp.FieldResponseDate), testDateUTCMidnight); compare(detailChangedSpy.count, 2) } function test_rsvpParticipationStatus_data() { return [ {tag: "StatusUnknown", testValue: EventAttendee.StatusUnknown}, {tag: "StatusAccepted", testValue: EventAttendee.StatusAccepted}, {tag: "StatusDeclined", testValue: EventAttendee.StatusDeclined}, {tag: "StatusTentative", testValue: EventAttendee.StatusTentative}, {tag: "StatusDelegated", testValue: EventAttendee.StatusDelegated}, {tag: "StatusInProcess", testValue: EventAttendee.StatusInProcess}, {tag: "StatusCompleted", testValue: EventAttendee.StatusCompleted}, {tag: "non existing - number", testValue: 123}, ] } function test_rsvpParticipationStatus(data) { console.log()//print the separate cases var detailChangedSpy = utility.create_testobject("import QtTest 1.0;SignalSpy{}", organizerItemDetailTests) var tempEventRsvp = utility.create_testobject("import QtOrganizer 5.0; EventRsvp{}", organizerItemDetailTests) detailChangedSpy.target = tempEventRsvp detailChangedSpy.signalName = "detailChanged" var defaultValue = EventAttendee.StatusUnknown == data.testValue ? true : false; tempEventRsvp.participationStatus = data.testValue if (!defaultValue) detailChangedSpy.wait(waitTime) compare(detailChangedSpy.count, defaultValue ? 0 : 1) compare(tempEventRsvp.participationStatus, data.testValue) } function test_rsvpParticipationRole_data() { return [ {tag: "RoleUnknown", testValue: EventAttendee.RoleUnknown}, {tag: "RoleOrganizer", testValue: EventAttendee.RoleOrganizer}, {tag: "RoleChairperson", testValue: EventAttendee.RoleChairperson}, {tag: "RoleHost", testValue: EventAttendee.RoleHost}, {tag: "RoleRequiredParticipant", testValue: EventAttendee.RoleRequiredParticipant}, {tag: "RoleOptionalParticipant", testValue: EventAttendee.RoleOptionalParticipant}, {tag: "RoleNonParticipant", testValue: EventAttendee.RoleNonParticipant}, {tag: "non existing - number", testValue: 123}, ] } function test_rsvpParticipationRole(data) { console.log()//print the separate cases var detailChangedSpy = utility.create_testobject("import QtTest 1.0;SignalSpy{}", organizerItemDetailTests) var tempEventRsvp = utility.create_testobject("import QtOrganizer 5.0; EventRsvp{}", organizerItemDetailTests) detailChangedSpy.target = tempEventRsvp detailChangedSpy.signalName = "detailChanged" var defaultValue = EventAttendee.RoleUnknown == data.testValue ? true : false; tempEventRsvp.participationRole = data.testValue if (!defaultValue) detailChangedSpy.wait(waitTime) compare(detailChangedSpy.count, defaultValue ? 0 : 1) compare(tempEventRsvp.participationRole, data.testValue) } function test_rsvpResponseRequirement_data() { return [ {tag: "ResponseNotRequired", testValue: EventRsvp.ResponseNotRequired}, {tag: "ResponseRequired", testValue: EventRsvp.ResponseRequired}, {tag: "non existing - number", testValue: 123}, ] } function test_rsvpResponseRequirement(data) { console.log()//print the separate cases var detailChangedSpy = utility.create_testobject("import QtTest 1.0;SignalSpy{}", organizerItemDetailTests) var tempEventRsvp = utility.create_testobject("import QtOrganizer 5.0; EventRsvp{}", organizerItemDetailTests) detailChangedSpy.target = tempEventRsvp detailChangedSpy.signalName = "detailChanged" var defaultValue = EventRsvp.ResponseNotRequired == data.testValue ? true : false; tempEventRsvp.responseRequirement = data.testValue if (!defaultValue) detailChangedSpy.wait(waitTime) compare(detailChangedSpy.count, defaultValue ? 0 : 1) compare(tempEventRsvp.responseRequirement, data.testValue) } function test_classificationGeneralTesting() { var detailChangedSpy = utility.create_testobject("import QtTest 1.0;SignalSpy{}", organizerItemDetailTests); detailChangedSpy.target = classification; detailChangedSpy.signalName = "detailChanged"; compare(classification.type, Detail.Classification) // default value check compare(classification.classification, Classification.AccessPublic) // no change on value classification.classification = classification.classification compare(detailChangedSpy.count, 0) } function test_classification_data() { return [ {tag: "AccessPrivate", testValue: Classification.AccessPrivate}, {tag: "AccessConfidential", testValue: Classification.AccessConfidential} ] } function test_classification(data) { var detailChangedSpy = utility.create_testobject("import QtTest 1.0;SignalSpy{}", organizerItemDetailTests); var tempClassification = utility.create_testobject("import QtOrganizer 5.0; Classification{}", organizerItemDetailTests) detailChangedSpy.target = tempClassification; detailChangedSpy.signalName = "detailChanged"; // classification tempClassification.classification = data.testValue detailChangedSpy.wait(waitTime) compare(tempClassification.classification, data.testValue) compare(detailChangedSpy.count, 1) } } tests/auto/organizer/qmlorganizer/testcases/tst_organizeritems.qml000066400000000000000000000114271233466112000263520ustar00rootroot00000000000000 /**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtOrganizer 5.0 Rectangle { id: test; QOrganizerTestUtility { id: utility } Event { id: event } Event { id:newEvent1 displayLabel: "NewEvent1" description: "This is a new created event 1" startDateTime: '2010-12-12' endDateTime: '2010-12-13' } Event { id:newEvent2 displayLabel: "NewEvent2" description: "This is a new created event 2" startDateTime: '2010-12-13' endDateTime: '2010-12-14' } TestCase { name: "OrganizerItemTests" function test_addRemoveEvent() { var list = utility.getManagerList(); if (list.length < 0) { console.log("No manager to test"); return; } //Test all manager backends for (var i = 0; i < list.length; i ++) { var managerName = list[i]; console.log(managerName); var model = utility.createModel(managerName); utility.init(model); utility.empty_calendar() //------model save event------// model.saveItem(newEvent1); //make sure event is saved utility.waitModelChange(1); verify(model.itemCount === 1); var ids = model.itemIds(); verify(ids.length === 1); var item = model.item(ids[0]); //verify DisplayLabel and description verify(item.displayLabel === "NewEvent1"); verify(item.description === "This is a new created event 1"); //------modify saved event------// item.displayLabel = "EditedEvent1"; model.saveItem(item); //make sure exist event saved/updated and no duplicated items saved utility.waitModelChange(1); //console.log(model.itemCount); verify(model.itemCount === 1); var item2 = model.item(ids[0]); verify(item2.displayLabel === "EditedEvent1"); //------delete event------// model.removeItem(ids[0]); utility.waitModelChange(0); verify(model.itemCount === 0); //delete invalid items and no crash model.removeItem(ids[0]); utility.waitModelChange(0); verify(model.itemCount === 0); //remove a list of event model.saveItem(newEvent2); utility.waitModelChange(1); model.saveItem(newEvent1); utility.waitModelChange(2); var deletList = model.itemIds(); model.removeItems(deletList); utility.waitModelChange(0); } } } } tests/auto/organizer/qmlorganizer/testcases/tst_organizermodel.qml000066400000000000000000001411421233466112000263270ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/organizer import QtQuick 2.0 import QtTest 1.0 import QtOrganizer 5.0 TestCase { name: "ModelTests" id:modelTests property var signalWaitTime : 300 QOrganizerTestUtility { id: utility } property Rectangle rect: Rectangle { id:myRectangle } function test_componentCreation_data() { return [ // OrganizerModel {tag: "No properties", code: "import QtQuick 2.0\n" + "import QtOrganizer 5.0 \n" + " OrganizerModel {\n" + " }" }, {tag: "Only id property", code: "import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + " OrganizerModel {\n" + " id:organizerModelId\n" + " }\n" }, {tag: "Valuetype properties", code: "import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + " OrganizerModel {\n" + " id:organizerModelId\n" + " manager:'memory'\n" + " startPeriod:'2010-08-12T13:22:01'\n" + " endPeriod:'2010-09-12T13:22:01'\n" + " }\n" }, {tag: "With filter", code: "import QtOrganizer 5.0\n" + "OrganizerModel {\n" + " id:organizerModelId\n" + " filter:DetailFieldFilter{\n" + " id:filter\n" + " field:EventTime.FieldStartDateTime\n" + " value:'2010-08-12T13:22:01'\n" + " }\n" + "}\n" }, {tag: "With invalid filter", code: "import QtOrganizer 5.0\n" + "OrganizerModel {\n" + " id:organizerModelId\n" + " filter:InvalidFilter{\n" + " id:filter\n" + " }\n" + "}\n" }, {tag: "With range filter", code: "import QtOrganizer 5.0\n" + "OrganizerModel {\n" + " id:organizerModelId\n" + " filter:DetailRangeFilter{\n" + " id:filter\n" + " field:EventTime.FieldStartDateTime\n" + " min:'2010-08-12T13:22:01'\n" + " max:'2010-09-12T13:22:01'\n" + " }\n" + "}\n" }, {tag: "With collection filter", code: "import QtOrganizer 5.0\n" + "OrganizerModel {\n" + " id:organizerModelId\n" + " filter:CollectionFilter{\n" + " id:filter\n" + " ids:['1234', '456', '90']\n" + " }\n" + "}\n" }, {tag: "With intersection filter", code: "import QtOrganizer 5.0\n" + "OrganizerModel {\n" + " id:organizerModelId\n" + " filter:IntersectionFilter {\n" + " id:filter\n" + " filters:[\n" + " DetailFieldFilter{\n" + " id:filter1\n" + " field:EventTime.FieldStartDateTime\n" + " value:'2010-08-12T13:22:01'\n" + " },\n" + " DetailRangeFilter{\n" + " id:filter2\n" + " field:EventTime.FieldStartDateTime\n" + " min:'2010-08-12T13:22:01'\n" + " max:'2010-09-12T13:22:01'\n" + " }\n" + " ]\n" + " }\n" + "}\n" }, {tag: "With fetchHint", code: "import QtOrganizer 5.0\n" + "OrganizerModel {\n" + " id:organizerModelId\n" + " fetchHint:FetchHint {\n" + " id:hint\n" + " optimizationHints:FetchHint.AllRequired\n" + " }\n" + "}\n" }, // Organizer Items {tag: "Base organizer item", code: "import QtOrganizer 5.0\n" + "OrganizerItem {\n" + "}\n" }, {tag: "Base organizer item: only id", code: "import QtOrganizer 5.0\n" + "OrganizerItem {\n" + " id:organizerItem\n" + "}\n" }, {tag: "Base organizer item: Valuetype properties", code: "import QtOrganizer 5.0\n" + "OrganizerItem {\n" + " id:organizerItem\n" + " displayLabel:'test item'\n" + " description:'item description'\n" + " guid:'1112232133'\n" + "}\n" }, {tag: "Base organizer item: default property", code: "import QtOrganizer 5.0\n" + "OrganizerItem {\n" + " id:organizerItem\n" + " DisplayLabel {\n" + " label:'test item'\n" + " }\n" + " Description {\n" + " description:'item description'\n" + " }\n" + " Guid{\n" + " guid:'111223213'\n" + " }\n" + "}\n" }, //Event {tag: "Organizer event", code: "import QtOrganizer 5.0\n" + "Event {\n" + "}\n" }, /*{tag: "Organizer event:Valuetype properties", code: "import QtOrganizer 5.0 \n" + "Event {\n" + " id:organizerEvent\n" + " displayLabel:'meeting'\n" + " startDateTime:'2010-08-12T13:00:00'\n" + " endDateTime:'2010-08-12T15:00:00'\n" + " allDay:false\n" + " location:'office'\n" + " Location {\n" + " label:'53 Brandl st'\n" + " latitude:-27.579570\n" + " longitude:153.10031\n" + " }\n" + " priority:Priority.Low\n" + " recurrence.recurrenceRules:[\n" + " RecurrenceRule {\n" + " }\n" + " ]\n" + " recurrence.recurrenceDates:[]\n" + " recurrence.exceptionDates:[]\n" + "}\n" },*/ /* {tag: "", code: "" }, */ ] } function test_componentCreation(data) { try{ var obj = Qt.createQmlObject(data.code, myRectangle, "dynamicSnippet1"); verify(obj != undefined, "Unable to load script for " + data.tag); obj.destroy(); console.log("Completed test on '" + data.tag + "'") } catch (errorObject) { console.log("For code " + data.code + " Error was seen is " + errorObject ); } } function checkDetails(events) { var testDataEvents = test_addEvent_data()[0].events var foundEvent = false var testDataEvent = undefined for (var index = 0; index < testDataEvents.length; index++){ testDataEvent = testDataEvents[index] foundEvent = false console.log("Checking event " + testDataEvent.subject) for (var index2 = 0; index2 < events.length; index2++){ var actualEvent = events[index2]; if (testDataEvent.subject == actualEvent.description){ foundEvent = true compare(actualEvent.type, testDataEvent.type) compare(actualEvent.startDateTime.toString(), testDataEvent.startDateTime.toString()) compare(actualEvent.endDateTime.toString(), testDataEvent.endDateTime.toString()) if (testDataEvent.repeat != undefined){ verify(actualEvent.recurrence != undefined, "Expected recurrance Element to be present for event") verify(actualEvent.recurrence.recurrenceRules.length == 1, "Expected there to be one RecurranceRule") var recurranceRule = actualEvent.recurrence.recurrenceRules[0] // first rule only switch (event.repeat){ case RecurrenceRule.Daily: case RecurrenceRule.Weekly: case RecurrenceRule.Monthly: case RecurrenceRule.Yearly: compare(recurranceRule.frequency, RecurrenceRule.Weekly) for (var dayIndex=0; dayIndex < testDataEvent.repeatDays; dayIndex++){ verify(recurranceRule.daysOfWeek.indexOf(testDataEvent.repeatDays[dayIndex]) != -1, "Expected event to recurrence on day " + testDataEvent.repeatDays[dayIndex]) } compare(recurranceRule.limit.toString(), testDataEvent.limit.toString()) break default: if (event.repeat != RecurrenceRule.Invalid) console.log("Unsupported repeat for Event actual:" + actualEvent.reoccurance + " expected:" + testDataEvent.repeat); } } break } } //verify(foundEvent, "Did not find event " + testDataEvent.subject) } console.log("Done checking Event") } // test data is handled by addEvent.qml function addEvents() function test_addEvent_data() { return [ {tag: "Event set 1", events:[ {tag: "Event 1", type: Type.Event, subject: "Event 1", description: "starts 2010-12-09 8AM finishes 5PM", startDateTime: new Date(2010, 12, 9, 8, 0), endDateTime: new Date(2010, 12, 9, 17, 0), }, {tag: "Event 2", type: Type.Event, subject: "Event 2", description: "starts 2010-12-08 8AM finishes 1PM", startDateTime: new Date(2010, 12, 8, 8, 0), endDateTime: new Date(2010, 12, 8, 13, 0), }, {tag: "Event 3", type: Type.Event, subject: "Event 3", description: "starts a month from 2010-12-08 at 11AM finish at 2PM", startDateTime: new Date(2010, 12, 8, 11, 0), endDateTime: new Date(2010, 12, 8, 14, 0), }, {tag: "Event 4", type: Type.Event, subject: "Event 4", description: "starts after Event 3 and finishes 4PM", startDateTime: new Date(2010, 12, 8, 14, 0), endDateTime: new Date(2010, 12, 8, 16, 0), }, {tag: "Event 5", type: Type.Event, subject: "Event 5", description: "starts after Event 4 and finishes 5PM", startDateTime: new Date(2010, 12, 8, 16, 0), endDateTime: new Date(2010, 12, 8, 17, 0), }, {tag: "Event 6", type: Type.Event, subject: "Event 6", description: "starts 2010-12-10 at 11AM finishing 1PM, repeating for 4 weeks", startDateTime: new Date(2010, 12, 10, 11, 0), endDateTime: new Date(2010, 12, 10, 13, 0), repeat: RecurrenceRule.Weekly, repeatDays: [Qt.Friday], repeatCount: 4 }, ] } ] } function test_addEvent(data) { var component = Qt.createComponent("addEvent.qml") var obj = component.createObject(top) if (obj == undefined) console.log("Unable to load component from " + name + " error is ", component.errorString()) verify(obj != undefined, 'Unable to load component ' + name) obj.addEvents(data.events) var items = obj.testEvents() checkDetails(items) if (items.length == 0) console.log("No records added") obj.destroy() component.destroy() } function test_organizermodel_error_data() { return utility.getManagerListData(); } function test_organizermodel_error(data) { var organizerChangedSpy = utility.create_testobject("import QtTest 1.0; SignalSpy {}", modelTests); // Create and check that backend for the tests is available var organizerModel = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "OrganizerModel {\n" + " manager: '" + data.managerToBeTested + "'\n" + "}\n", modelTests); organizerChangedSpy.target = organizerModel; organizerChangedSpy.signalName = "modelChanged"; organizerChangedSpy.wait(); organizerModel.removeCollection(organizerModel.defaultCollection().collectionId); wait(signalWaitTime);// how to utilise SignalSpy to check signal is _not_ emitted? compare(organizerModel.error, "PermissionsError"); } function test_organizermodel_fetchitemsbyid_data() { return utility.getManagerListData(); } function test_organizermodel_fetchitemsbyid(data) { var organizerModel = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "OrganizerModel {\n" + " manager: '" + data.managerToBeTested + "'\n" + "}\n", modelTests); compare(organizerModel.fetchItems([]), -1) var spy = Qt.createQmlObject( "import QtTest 1.0 \nSignalSpy {}", modelTests); spy.target = organizerModel; spy.signalName = "itemsFetched"; verify(organizerModel.fetchItems(["invalid-id"]) >= 0) spy.wait() compare(spy.count, 1) } function fetchItemsPopulation_data() { var event1 = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "Event {\n" + " displayLabel: \"event1:\"\n" + " id: event1\n" + " startDateTime: new Date(2011, 11, 8, 13, 55)\n" + " endDateTime: new Date(2011, 11, 8, 14, 07)\n" + "}\n", modelTests); var event2 = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "Event {\n" + " displayLabel: \"event2:\"\n" + " id: event2\n" + " startDateTime: new Date(2011, 11, 8, 14, 11)\n" + " endDateTime: new Date(2011, 11, 8, 14, 15)\n" + "}\n", modelTests); var event3 = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "Event {\n" + " displayLabel: \"event3:\"\n" + " id: event3\n" + " startDateTime: new Date(2011, 11, 9, 14, 25, 0)\n" + " endDateTime: new Date(2011, 11, 9, 14, 45)\n" + "}\n", modelTests); var event4 = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "Event {\n" + " displayLabel: \"event4:\"\n" + " id: event4\n" + " startDateTime: new Date(2011, 11, 10, 14, 11)\n" + " endDateTime: new Date(2011, 11, 10, 14, 45)\n" + "}\n", modelTests); var event5 = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "Event {\n" + " displayLabel: \"event5:\"\n" + " id: event5\n" + " startDateTime: new Date(2011, 12, 11, 14, 55)\n" + " endDateTime: new Date(2011, 12, 11, 15, 05)\n" + "}\n", modelTests); return [ { tag: "Event set 1", events:[ event1, event2, event3, event4, event5, ] } ] } function test_organizermodel_fetchitems_data() { return utility.getManagerListData(); } function test_organizermodel_fetchitems(data) { var organizerModel = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "OrganizerModel {\n" + " manager: '" + data.managerToBeTested + "'\n" + " id:fetchItemsTestModel\n" + " autoUpdate:true\n " + " property list testFetchedItems: [OrganizerItem{},OrganizerItem{}]\n" + " onItemsFetched: {\n" + " testFetchedItems = fetchedItems;\n" + " }\n" + "}\n", modelTests); var modelChangedSpy = Qt.createQmlObject("import QtTest 1.0; SignalSpy{}", modelTests) modelChangedSpy.target = organizerModel modelChangedSpy.signalName = "modelChanged" var modelItemsFetchedSpy = Qt.createQmlObject("import QtTest 1.0; SignalSpy{}", modelTests) modelItemsFetchedSpy.target = organizerModel modelItemsFetchedSpy.signalName = "itemsFetched" var startDate = new Date(1977, 12, 9, 8, 0); var endDate = new Date(2022, 12, 9, 8, 0); organizerModel.startPeriod = startDate; organizerModel.endPeriod = endDate; // during initialisation only one modelChanged allowed modelChangedSpy.wait(); compare(modelChangedSpy.count, 1) //Cleanup utility.init(organizerModel) utility.empty_calendar(); compare(organizerModel.items.length, 0); // Store some test data.... var testDataSet = fetchItemsPopulation_data()[0] //Event set 1 console.log("Test Set:" + testDataSet.tag) console.log(" Saving " + testDataSet.events.length + " events...") for (var index = 0; index < testDataSet.events.length; index++){ modelChangedSpy.clear(); console.log("saving event:" + testDataSet.events[index]); organizerModel.saveItem( testDataSet.events[index]); modelChangedSpy.wait(); compare(modelChangedSpy.count, 1) } console.log("Orgnizer itemcount = " + organizerModel.items.length); compare(organizerModel.items.length, testDataSet.events.length); // 1. Only fetch events with start/end dates before any stored events modelItemsFetchedSpy.clear(); startDate = new Date(1977, 11, 9, 8, 0); endDate = new Date(2010, 11, 9, 8, 0); verify(organizerModel.fetchItems( startDate, endDate ) != -1); modelItemsFetchedSpy.wait(); compare(modelItemsFetchedSpy.count, 1); compare(organizerModel.fetchItems.length,0); // 2. Nothing fetched if start+end dates are after any stored events modelItemsFetchedSpy.clear(); startDate = new Date(2012, 11, 9, 8, 0); endDate = new Date(2099, 11, 9, 8, 0); verify( organizerModel.fetchItems( startDate, endDate ) != -1); modelItemsFetchedSpy.wait(); compare(modelItemsFetchedSpy.count, 1); compare(organizerModel.fetchItems.length,0); // 3. All events fetched modelItemsFetchedSpy.clear(); startDate = new Date(1977, 11, 9, 8, 0); endDate = new Date(2099, 11, 9, 8, 0); verify( organizerModel.fetchItems( startDate, endDate ) != -1); modelItemsFetchedSpy.wait(); compare(modelItemsFetchedSpy.count, 1) compare(organizerModel.testFetchedItems.length, 5); // 4. Only items within start+end fetched modelItemsFetchedSpy.clear(); startDate = new Date(2011, 11, 8, 14, 10); endDate = new Date(2011, 11, 10, 20, 0); verify(organizerModel.fetchItems( startDate, endDate ) != -1); modelItemsFetchedSpy.wait(); compare(modelItemsFetchedSpy.count, 1) compare(organizerModel.testFetchedItems.length, 3); // 5. Filtering var testFilterDisplayLabel = Qt.createQmlObject("import QtOrganizer 5.0; DetailFieldFilter{}", organizerModel) testFilterDisplayLabel.detail = Detail.DisplayLabel testFilterDisplayLabel.field = DisplayLabel.FieldLabel testFilterDisplayLabel.value = "event2:" testFilterDisplayLabel.matchFlags = DetailFilter.MatchContains modelItemsFetchedSpy.clear(); startDate = new Date(1977, 11, 9, 8, 0); endDate = new Date(2099, 11, 9, 8, 0); verify( organizerModel.fetchItems( startDate, endDate, testFilterDisplayLabel ) != -1); modelItemsFetchedSpy.wait(); compare(modelItemsFetchedSpy.count, 1) compare(organizerModel.testFetchedItems.length, 1); // 6. maxcount is not available in Memory model var testDefaultFilter = Qt.createQmlObject("import QtOrganizer 5.0; Filter{}", organizerModel) if (data.managerToBeTested != "memory") { startDate = new Date(1977, 11, 9, 8, 0); endDate = new Date(2099, 11, 9, 8, 0); modelItemsFetchedSpy.clear(); verify( organizerModel.fetchItems( startDate, endDate, testDefaultFilter, 2) != -1); modelItemsFetchedSpy.wait(); compare(modelItemsFetchedSpy.count, 1) compare(organizerModel.testFetchedItems.length, 2); } // 7. Sorting var sortAscending = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + " SortOrder {\n" + " detail: Detail.DisplayLabel\n" + " field: DisplayLabel.FieldLabel\n" //field: DisplayLabelField.FieldLabel\n" + " id: testAscendingOrder\n" + " direction: Qt.AscendingOrder\n" + "}\n", organizerModel); var sortDescending = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + " SortOrder {\n" + " detail: Detail.DisplayLabel\n" + " field: DisplayLabel.FieldLabel\n" //field: DisplayLabelField.FieldLabel\n" + " id: testDecendingOrder\n" + " direction: Qt.DescendingOrder\n" + "}\n", organizerModel); modelItemsFetchedSpy.clear(); startDate = new Date(2011, 11, 8, 14, 10); endDate = new Date(2011, 11, 10, 20, 0); modelItemsFetchedSpy.clear(); verify( organizerModel.fetchItems( startDate, endDate, testDefaultFilter, -1, [sortAscending] ) != -1); modelItemsFetchedSpy.wait(); compare(modelItemsFetchedSpy.count, 1); compare(organizerModel.testFetchedItems.length, 3); var expectedSortOrder = [ "event2:", "event3:", "event4:"] compare(expectedSortOrder.length, organizerModel.testFetchedItems.length); for (var j = 0; j < organizerModel.testFetchedItems.length; j++) { verify(organizerModel.testFetchedItems[j].displayLabel === expectedSortOrder[j], "FetchItems Ascending Sort Order Incorrect."); } modelItemsFetchedSpy.clear(); verify( organizerModel.fetchItems( startDate, endDate, testDefaultFilter, -1, [sortDescending] ) != -1); modelItemsFetchedSpy.wait(); compare(modelItemsFetchedSpy.count, 1); compare(organizerModel.testFetchedItems.length, 3); compare(expectedSortOrder.length, organizerModel.testFetchedItems.length); for ( j = 0; j < organizerModel.testFetchedItems.length; j++) { verify(organizerModel.testFetchedItems[j].displayLabel == expectedSortOrder[2-j], "FetchItems Decending Sort Order Incorrect."); } // 8. fetch hints var fetchhint = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + " FetchHint {\n" + " id:hint\n" + " optimizationHints:FetchHint.AllRequired\n" + "}\n", organizerModel); modelItemsFetchedSpy.clear(); verify( organizerModel.fetchItems( startDate, endDate, testFilterDisplayLabel, -1, [sortDescending], fetchhint ) != -1); modelItemsFetchedSpy.wait(); compare(modelItemsFetchedSpy.count, 1) compare(organizerModel.testFetchedItems.length, 1); // 9. storage locations and maxcount are not available in Memory model if (data.managerToBeTested != "memory") { modelItemsFetchedSpy.clear(); organizerModel.fetchItems( startDate, endDate, testFilterDisplayLabel, -1, [sortDescending], fetchhint , OrganizerModel.SystemStorage); modelItemsFetchedSpy.wait(); compare(modelItemsFetchedSpy.count, 1) compare(organizerModel.testFetchedItems.length, 0); } //Cleanup utility.empty_calendar(); compare(organizerModel.items.length, 0); } function test_organizermodel_containsitems_data() { return utility.getManagerListData(); } function test_organizermodel_containsitems(data) { var organizerModel = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "OrganizerModel {\n" + " manager: '" + data.managerToBeTested + "'\n" + " startPeriod: new Date(2011, 12, 8, 14, 0)\n" + " endPeriod: new Date(2011, 12, 8, 16, 0)\n" + "}\n", modelTests); utility.init(organizerModel) utility.waitModelChange() utility.empty_calendar() var event1 = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "Event {\n" + " startDateTime: new Date(2011, 12, 8, 13, 55)\n" + " endDateTime: new Date(2011, 12, 8, 14, 07)\n" + "}\n", modelTests); var event2 = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "Event {\n" + " startDateTime: new Date(2011, 12, 8, 14, 11)\n" + " endDateTime: new Date(2011, 12, 8, 14, 15)\n" + "}\n", modelTests); var todo3 = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "Todo {\n" + " startDateTime: new Date(2011, 12, 8, 14, 25, 0)\n" + "}\n", modelTests); var todo4 = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "Todo {\n" + " dueDateTime: new Date(2011, 12, 8, 14, 45)\n" + "}\n", modelTests); var event5 = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "Event {\n" + " startDateTime: new Date(2011, 12, 8, 14, 55)\n" + " endDateTime: new Date(2011, 12, 8, 15, 05)\n" + "}\n", modelTests); organizerModel.saveItem(event1); utility.waitModelChange() organizerModel.saveItem(event2); utility.waitModelChange() organizerModel.saveItem(todo3); utility.waitModelChange() organizerModel.saveItem(todo4); utility.waitModelChange() organizerModel.saveItem(event5); utility.waitModelChange() compare(organizerModel.items.length, 5); var containsItems = organizerModel.containsItems(new Date(2011, 12, 8, 14, 0), new Date(2011, 12, 8, 16, 0), 600); compare(containsItems.length, 12); compare(containsItems[0], true); compare(containsItems[1], true); compare(containsItems[2], true); compare(containsItems[3], false); compare(containsItems[4], true); compare(containsItems[5], true); compare(containsItems[6], true); compare(containsItems[7], false); compare(containsItems[8], false); compare(containsItems[9], false); compare(containsItems[10], false); compare(containsItems[11], false); } function test_organizermodel_containsitems2_data() { return utility.getManagerListData(); } function test_organizermodel_containsitems2(data) { var organizerModel = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "OrganizerModel {\n" + " manager: '" + data.managerToBeTested + "'\n" + " startPeriod: new Date(2011, 12, 7)\n" + " endPeriod: new Date(2011, 12, 9)\n" + "}\n", modelTests); utility.init(organizerModel) utility.waitModelChange() utility.empty_calendar() var event00 = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "Event {\n" + " startDateTime: new Date(2011, 12, 7, 11)\n" + " endDateTime: new Date(2011, 12, 8, 0, 30)\n" + "}\n", modelTests); var event0 = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "Todo {\n" + " startDateTime: new Date(2011, 12, 8, 1)\n" + "}\n", modelTests); var event1 = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "Event {\n" + " startDateTime: new Date(2011, 12, 8, 3)\n" + " endDateTime: new Date(2011, 12, 8, 3, 30)\n" + "}\n", modelTests); var event2 = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "Event {\n" + " startDateTime: new Date(2011, 12, 8, 5)\n" + " endDateTime: new Date(2011, 12, 8, 6)\n" + "}\n", modelTests); var event3 = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "Event {\n" + " startDateTime: new Date(2011, 12, 8, 8)\n" + " endDateTime: new Date(2011, 12, 8, 10)\n" + "}\n", modelTests); var event4 = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "Event {\n" + " startDateTime: new Date(2011, 12, 8, 11, 30)\n" + " endDateTime: new Date(2011, 12, 8, 12)\n" + "}\n", modelTests); var event5 = utility.create_testobject("import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "Event {\n" + " startDateTime: new Date(2011, 12, 8, 13)\n" + " endDateTime: new Date(2011, 12, 8, 13, 30)\n" + "}\n", modelTests); compare(organizerModel.items.length, 0); organizerModel.saveItem(event00); utility.waitModelChange() organizerModel.saveItem(event0); utility.waitModelChange() organizerModel.saveItem(event1); utility.waitModelChange() organizerModel.saveItem(event2); utility.waitModelChange() organizerModel.saveItem(event3); utility.waitModelChange() organizerModel.saveItem(event4); utility.waitModelChange() organizerModel.saveItem(event5); utility.waitModelChange() compare(organizerModel.items.length, 7); var containsItems = organizerModel.containsItems(new Date(2011, 12, 8), new Date(2011, 12, 8, 13), 3600); compare(containsItems.length, 13); compare(containsItems[0], true); compare(containsItems[1], true); compare(containsItems[2], false); compare(containsItems[3], true); compare(containsItems[4], false); compare(containsItems[5], true); compare(containsItems[6], false); compare(containsItems[7], false); compare(containsItems[8], true); compare(containsItems[9], true); compare(containsItems[10], false); compare(containsItems[11], true); compare(containsItems[12], false); } function modelChangedSignalTestItems() { return [ // events "import QtOrganizer 5.0 \n" + " Event {\n" + " startDateTime:'2011-10-25'\n" + " endDateTime:'2011-10-26'\n" + " allDay: false\n" + " }", "import QtOrganizer 5.0 \n" + " Event {\n" + " startDateTime:'2011-10-26'\n" + " endDateTime:'2011-10-27'\n" + " allDay: true\n" + " }" ] } // to test various usecases for modelChanged-signal function test_modelChangedSignal() { var managerlist = utility.getManagerList(); if (managerlist.length < 0) { console.log("No manager to test"); return; } for (var i = 0; i < managerlist.length; i ++) { var filter = Qt.createQmlObject("import QtOrganizer 5.0; DetailFieldFilter{}", modelTests) filter.detail = Detail.EventTime filter.field = EventTime.FieldAllDay filter.value = true var model = Qt.createQmlObject( "import QtOrganizer 5.0;" + "OrganizerModel {" + " manager: \"qtorganizer:" + managerlist[i] + ":id=qml\";" + " startPeriod:'2009-01-01';" + " endPeriod:'2012-12-31';" + " autoUpdate:true; }" , modelTests); console.log("## Testing plugin: " + managerlist[i]); var modelChangedSpy = Qt.createQmlObject("import QtTest 1.0; SignalSpy{}", modelTests) modelChangedSpy.target = model modelChangedSpy.signalName = "modelChanged" // during initialisation only one modelChanged allowed modelChangedSpy.wait(signalWaitTime); compare(modelChangedSpy.count, 1) // prepare for rest of cases utility.init(model) utility.empty_calendar() utility.addItemsToModel(modelChangedSignalTestItems(), modelTests) compare(model.itemCount, 2) // after filterchange only one modelChanged allowed modelChangedSpy.clear() model.filter = filter modelChangedSpy.wait(signalWaitTime); compare(modelChangedSpy.count, 1) compare(model.itemCount, 1) // after manual update only one modelChanged allowed model.autoUpdate = false modelChangedSpy.clear() model.filter = null model.update() modelChangedSpy.wait(signalWaitTime); compare(modelChangedSpy.count, 1) compare(model.itemCount, 2) utility.empty_calendar() } } function test_updateMethods() { var managerlist = utility.getManagerList(); if (managerlist.length < 0) { console.log("No manager to test"); return; } for (var i = 0; i < managerlist.length; i ++) { var organizerModel = Qt.createQmlObject( "import QtOrganizer 5.0;" + "OrganizerModel {" + " manager: '" + managerlist[i] + "'\n" + " startPeriod:'2009-01-01'\n" + " endPeriod:'2012-12-31'\n" + "}" , modelTests); console.log("## Testing plugin: " + managerlist[i]); utility.init(organizerModel); utility.waitModelChange(); utility.empty_calendar(); var event1 = utility.create_testobject( "import QtOrganizer 5.0\n" + "Event {\n" + " startDateTime: new Date(2011, 12, 7, 11)\n" + " endDateTime: new Date(2011, 12, 8, 0, 30)\n" + "}\n", modelTests); var event2 = utility.create_testobject( "import QtOrganizer 5.0\n" + "Event {\n" + " startDateTime: new Date(2011, 13, 7, 11)\n" + " endDateTime: new Date(2011, 13, 8, 0, 30)\n" + "}\n", modelTests); var collection1 = utility.create_testobject("import QtQuick 2.0 \n" + "import QtOrganizer 5.0\n" + "Collection {\n" + "id: coll1\n" + "}\n", modelTests); var collection2 = utility.create_testobject("import QtQuick 2.0 \n" + "import QtOrganizer 5.0\n" + "Collection {\n" + "id: coll1\n" + "}\n", modelTests); var modelChangedSpy = Qt.createQmlObject("import QtTest 1.0; SignalSpy{}", modelTests) modelChangedSpy.target = organizerModel modelChangedSpy.signalName = "modelChanged" var collectionsChangedSpy = Qt.createQmlObject("import QtTest 1.0; SignalSpy{}", modelTests) collectionsChangedSpy.target = organizerModel collectionsChangedSpy.signalName = "collectionsChanged" organizerModel.autoUpdate = false; // starting point compare(organizerModel.items.length, 0); compare(organizerModel.collections.length, 1); // updateItems() - should update only items organizerModel.saveItem(event1); organizerModel.saveCollection(collection1); wait(signalWaitTime) compare(modelChangedSpy.count, 0) compare(collectionsChangedSpy.count, 0) compare(organizerModel.items.length, 0) compare(organizerModel.collections.length, 1) organizerModel.updateItems(); modelChangedSpy.wait(signalWaitTime) compare(modelChangedSpy.count, 1) compare(collectionsChangedSpy.count, 0) compare(organizerModel.items.length, 1)//+1 compare(organizerModel.collections.length, 1)// collection change not visible, only default collection visible // updateCollections() - should update only collections modelChangedSpy.clear(); collectionsChangedSpy.clear(); organizerModel.saveItem(event2); // - there is saved unseen collection from updateItems()-test wait(signalWaitTime) compare(modelChangedSpy.count, 0) compare(collectionsChangedSpy.count, 0) organizerModel.updateCollections(); collectionsChangedSpy.wait(signalWaitTime) compare(modelChangedSpy.count, 0) compare(collectionsChangedSpy.count, 1) compare(organizerModel.items.length, 1)// event2 not visible, only event1 saved in updateItems()-test visible compare(organizerModel.collections.length, 2)//+1 // update() - should update both modelChangedSpy.clear(); collectionsChangedSpy.clear(); // - there is saved unseen item from updateCollections()-test organizerModel.saveCollection(collection2); wait(signalWaitTime) compare(modelChangedSpy.count, 0) compare(collectionsChangedSpy.count, 0) organizerModel.update(); modelChangedSpy.wait(signalWaitTime) compare(modelChangedSpy.count, 1) compare(collectionsChangedSpy.count, 1) compare(organizerModel.items.length, 2)//+1 compare(organizerModel.collections.length, 3)//+1 modelChangedSpy.destroy(); collectionsChangedSpy.destroy(); organizerModel.destroy(); } } function test_updateMethodsStartWithAutoupdateFalse() { if (utility.getManagerList().indexOf("jsondb") === -1) skip("Cannot run tests for jsondb backend. No plugin available!"); var organizerModel = Qt.createQmlObject( "import QtOrganizer 5.0;" + "OrganizerModel {" + " manager: 'jsondb'\n" + " startPeriod:'2009-01-01'\n" + " endPeriod:'2012-12-31'\n" + " autoUpdate: false\n" + "}" , modelTests); console.log("## Testing only jsondb-plugin"); var modelChangedSpy = Qt.createQmlObject("import QtTest 1.0; SignalSpy{}", modelTests) modelChangedSpy.target = organizerModel modelChangedSpy.signalName = "modelChanged" var collectionsChangedSpy = Qt.createQmlObject("import QtTest 1.0; SignalSpy{}", modelTests) collectionsChangedSpy.target = organizerModel collectionsChangedSpy.signalName = "collectionsChanged" // After test_updateMethods()-test there should be // 2 items and 2 collections (+ default collection) // on the jsondb. They're just not visible, since // autoUpdate is false. compare(organizerModel.items.length, 0); compare(organizerModel.collections.length, 0); // Check we see still only collections after updating collections organizerModel.updateCollections(); collectionsChangedSpy.wait(signalWaitTime) compare(organizerModel.items.length, 0); compare(organizerModel.collections.length, 3);// 2 + default collection // Save there item with other collection than default collection var event = utility.create_testobject( "import QtOrganizer 5.0\n" + "Event {\n" + " startDateTime: new Date(2011, 12, 7, 11)\n" + " endDateTime: new Date(2011, 12, 8, 0, 30)\n" + "}\n", modelTests); for (var i = 0; i < organizerModel.collections.length; ++i) { var collId = organizerModel.collections[i].collectionId; if (collId != organizerModel.defaultCollection().collectionId) { event.collectionId = collId; } } organizerModel.saveItem(event); // Create collection filter and check that only the item with that collection is visible var collectionFilter = Qt.createQmlObject("import QtOrganizer 5.0;CollectionFilter{}", modelTests); collectionFilter.ids = [event.collectionId]; organizerModel.filter = collectionFilter; organizerModel.updateItems(); modelChangedSpy.wait(signalWaitTime) compare(organizerModel.items.length, 1);// 1 with that collection // Lastly check we get everything if filter is reset organizerModel.filter = null; organizerModel.updateItems(); modelChangedSpy.wait(signalWaitTime) compare(organizerModel.items.length, 3); // all items // cleanup utility.init(organizerModel); utility.empty_calendar(); collectionFilter.destroy(); modelChangedSpy.destroy(); collectionsChangedSpy.destroy(); organizerModel.destroy(); } function test_collectionsSaveRemove_data() { return utility.getManagerListData(); } function test_collectionsSaveRemove(data) { var model = utility.createModel(data.managerToBeTested); var event1 = utility.create_testobject( "import QtOrganizer 5.0\n" + "Event {\n" + " startDateTime: new Date(2011, 12, 7, 11)\n" + " endDateTime: new Date(2011, 12, 8, 0, 30)\n" + "}\n", modelTests); var collection1 = utility.create_testobject("import QtQuick 2.0 \n" + "import QtOrganizer 5.0\n" + "Collection {\n" + "id: coll1\n" + "}\n", modelTests); var view = utility.create_testobject( "import QtQuick 2.0\n" + "ListView {\n" + " width:100; height: 1000;\n" + " delegate: Text{ text: name }\n" + "}\n", modelTests); var modelChangedSpy = utility.create_testobject("import QtTest 1.0; SignalSpy{}", modelTests) modelChangedSpy.target = model modelChangedSpy.signalName = "modelChanged" var collectionsChangedSpy = utility.create_testobject("import QtTest 1.0; SignalSpy{}", modelTests) collectionsChangedSpy.target = model collectionsChangedSpy.signalName = "collectionsChanged" // starting point compare(model.items.length, 0); compare(model.collections.length, 1); // add collections var ncoll = 10; for (var i=1; i<=ncoll; ++i) { collection1.name = 'collection ' + i model.saveCollection(collection1); collectionsChangedSpy.wait(signalWaitTime); event1.collectionId = model.collections[i].collectionId; model.saveItem(event1); modelChangedSpy.wait(signalWaitTime); } compare(model.collections.length, ncoll+1); compare(model.itemCount, ncoll); view.model = model.collections; verify(view.contentHeight > 0, 'view content is empty'); compare(view.count, ncoll+1); model.removeCollection(model.collections[ncoll].collectionId); collectionsChangedSpy.wait(signalWaitTime); view.model = model.collections; compare(model.collections.length, ncoll); compare(model.itemCount, ncoll-1); compare(view.count, ncoll); view.destroy() collection1.destroy() event1.destroy() model.destroy(); } function test_modelsSharingFilterAndFetchHint() { // ensure model won't delete filter and fetchHint it doesn't own var view = utility.create_testobject( "import QtQuick 2.0\n" + "import QtOrganizer 5.0\n" + "ListView {\n" + " model:2;\n" + " width:100; height: 1000;\n" + " property var theFilter: DetailRangeFilter {}\n" + " property var theFetchHint: FetchHint {}\n" + " delegate: Item { OrganizerModel { filter: theFilter; fetchHint: theFetchHint } }\n" + "}\n", modelTests); } } tests/auto/organizer/qmlorganizer/testcases/tst_organizermodelupdate.qml000066400000000000000000001432561233466112000275420ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtOrganizer 5.0 // TODO: ideas for additional tests: // change existing normal event currently not in model to be a recurring event // put autoupdate off, add an item (no changes to model), put it on --> model should be updated // put autoupdate off, change start and end dates (no changes to model) and put it back on --> model updated // put autoupdate off, change start and end dates and filter (e.g. collection) (no changes) and put it back on --> model updated // put autoupdate off, add item in the range of new start and end times, change start and end dates (no changes to model) and put it back on --> model updated // put autoupdate off, change start and end dates many times (no changes to model) and put it back on --> model updated // add sortorder and remove it, check the model TestCase { id: test name: "OrganizerRecurrenceTests" property int spyWaitDelay: 700 property variant fetchedItem QOrganizerTestUtility { id: utility } OrganizerModel { id: model onItemsFetched : { test.fetchedItem = fetchedItems[0] } } SignalSpy { id: spyManagerChanged signalName: "managerChanged" target: model } SignalSpy { id: modelChangedSpy signalName: "modelChanged" target: model } SignalSpy { id: fetchSpy signalName: "itemsFetched" target: model } function cleanup() { model.manager = "" } function test_changeTimePeriod_data() { return [{ managers: utility.getManagerList(), definitions: [ { event : { "displayLabel" : "event0", "start" : new Date('2011-10-25T15:00:00'), "end" : new Date('2011-10-10T16:00:00'), "recurrenceDates": [], "exceptionDates": [] }, affectsModel: false }, { event : { "displayLabel" : "recevent1", "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [], "exceptionDates": [] }, rrule: { "frequency": RecurrenceRule.Daily, "limit": new Date('2012-01-03'), "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, affectsModel: true }, { event : { "displayLabel" : "event1", "start" : new Date('2012-01-02T15:00:00'), "end" : new Date('2012-01-02T16:00:00'), "recurrenceDates": [], "exceptionDates": [] }, affectsModel: true }, { event : { "displayLabel" : "event2", "start" : new Date('2012-01-03T11:00:00'), "end" : new Date('2012-01-03T18:00:00'), "recurrenceDates": [], "exceptionDates": [] }, affectsModel: true }, { event : { "displayLabel" : "recevent2", "start" : new Date('2011-01-01T16:00:00'), "end" : new Date('2011-01-01T17:00:00'), "recurrenceDates": [], "exceptionDates": [] }, rrule: { "frequency": RecurrenceRule.Monthly, "limit": 15, "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, affectsModel: true }, { event : { "displayLabel" : "event3", "start" : new Date('2012-05-15T09:00:00'), "end" : new Date('2012-05-15T10:00:00'), "recurrenceDates": [], "exceptionDates": [] }, affectsModel: false }, { event : { "displayLabel" : "recevent3", "start" : new Date('2012-06-03T20:00:00'), "end" : new Date('2012-06-03T21:00:00'), "recurrenceDates": [], "exceptionDates": [] }, rrule: { "frequency": RecurrenceRule.Monthly, "limit": 4, "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, affectsModel: false } ], results: [ {label: "recevent2", start: new Date('2011-12-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-01T14:00:00')}, {label: "recevent2", start: new Date('2012-01-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-02T14:00:00')}, {label: "event1", start: new Date('2012-01-02T15:00:00')}, {label: "event2", start: new Date('2012-01-03T11:00:00')}, {label: "recevent1", start: new Date('2012-01-03T14:00:00')}, {label: "recevent2", start: new Date('2012-02-01T16:00:00')}, {label: "recevent2", start: new Date('2012-03-01T16:00:00')} ], timePeriods: [ { start: new Date('2012-01-03T00:00:00'), autoUpdate: true, results: [ {label: "event2", start: new Date('2012-01-03T11:00:00')}, {label: "recevent1", start: new Date('2012-01-03T14:00:00')}, {label: "recevent2", start: new Date('2012-02-01T16:00:00')}, {label: "recevent2", start: new Date('2012-03-01T16:00:00')} ] }, { end: new Date('2012-07-25T15:00:00'), autoUpdate: true, results: [ {label: "event2", start: new Date('2012-01-03T11:00:00')}, {label: "recevent1", start: new Date('2012-01-03T14:00:00')}, {label: "recevent2", start: new Date('2012-02-01T16:00:00')}, {label: "recevent2", start: new Date('2012-03-01T16:00:00')}, {label: "event3", start: new Date('2012-05-15T09:00:00')}, {label: "recevent3", start: new Date('2012-06-03T20:00:00')}, {label: "recevent3", start: new Date('2012-07-03T20:00:00')} ] }, { start: new Date('2011-09-25T15:00:00'), end: new Date('2012-12-25T15:00:00'), autoUpdate: false, results: [ {label: "recevent2", start: new Date('2011-10-01T16:00:00')}, {label: "event0", start: new Date('2011-10-25T15:00:00')}, {label: "recevent2", start: new Date('2011-11-01T16:00:00')}, {label: "recevent2", start: new Date('2011-12-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-01T14:00:00')}, {label: "recevent2", start: new Date('2012-01-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-02T14:00:00')}, {label: "event1", start: new Date('2012-01-02T15:00:00')}, {label: "event2", start: new Date('2012-01-03T11:00:00')}, {label: "recevent1", start: new Date('2012-01-03T14:00:00')}, {label: "recevent2", start: new Date('2012-02-01T16:00:00')}, {label: "recevent2", start: new Date('2012-03-01T16:00:00')}, {label: "event3", start: new Date('2012-05-15T09:00:00')}, {label: "recevent3", start: new Date('2012-06-03T20:00:00')}, {label: "recevent3", start: new Date('2012-07-03T20:00:00')}, {label: "recevent3", start: new Date('2012-08-03T20:00:00')}, {label: "recevent3", start: new Date('2012-09-03T20:00:00')} ] } ] }] } // initialize db with normal and recurring items, some are in mode time period some not // change time period and check correct items are in model function test_changeTimePeriod(data) { skip("TODO Currently fails"); var j = 0; for (var i in data.managers) { console.log("Testing "+data.managers[i]+" backend") model.manager = data.managers[i]; model.autoUpdate = true; model.startPeriod = new Date('2011-12-01'); model.endPeriod = new Date('2012-04-30'); spyManagerChanged.wait(spyWaitDelay) cleanDatabase(); compare(model.itemCount, 0, "Model not empty") for (j = 0; j < data.definitions.length; j++) { var testEvent = createTestItemFromData(data.definitions[j]); model.saveItem(testEvent); if (data.definitions[j].affectsModel) modelChangedSpy.wait(spyWaitDelay) } compareResultDatesToModel(data.results, model); for (j = 0; j < data.timePeriods.length; j++) { model.autoUpdate = data.timePeriods[j].autoUpdate; if (data.timePeriods[j].start !== undefined) { model.startPeriod = data.timePeriods[j].start; if (model.autoUpdate) modelChangedSpy.wait(spyWaitDelay); } if (data.timePeriods[j].end !== undefined) { model.endPeriod = data.timePeriods[j].end; } if (!model.autoUpdate) { model.update(); } modelChangedSpy.wait(spyWaitDelay); compareResultDatesToModel(data.timePeriods[j].results, model); } model.autoUpdate = true; cleanDatabase(); compare(model.itemCount, 0, "Model not empty") } } function test_modifyParentItems_data() { return [{ managers: utility.getManagerList(), definitions: [ { event : { "displayLabel" : "event0", "start" : new Date('2011-10-25T15:00:00'), "end" : new Date('2011-10-10T16:00:00'), "recurrenceDates": [], "exceptionDates": [] }, affectsModel: false }, { event : { "displayLabel" : "recevent1", "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [], "exceptionDates": [] }, rrule: { "frequency": RecurrenceRule.Daily, "limit": new Date('2012-01-03'), "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, affectsModel: true }, { event : { "displayLabel" : "event1", "start" : new Date('2012-01-02T15:00:00'), "end" : new Date('2012-01-02T16:00:00'), "recurrenceDates": [], "exceptionDates": [] }, affectsModel: true }, { event : { "displayLabel" : "event2", "start" : new Date('2012-01-03T11:00:00'), "end" : new Date('2012-01-03T18:00:00'), "recurrenceDates": [], "exceptionDates": [] }, affectsModel: true }, { event : { "displayLabel" : "recevent2", "start" : new Date('2011-01-01T16:00:00'), "end" : new Date('2011-01-01T17:00:00'), "recurrenceDates": [], "exceptionDates": [] }, rrule: { "frequency": RecurrenceRule.Monthly, "limit": 15, "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, affectsModel: true }, { event : { "displayLabel" : "event3", "start" : new Date('2012-05-15T09:00:00'), "end" : new Date('2012-05-15T10:00:00'), "recurrenceDates": [], "exceptionDates": [] }, affectsModel: false }, { event : { "displayLabel" : "recevent3", "start" : new Date('2012-06-03T20:00:00'), "end" : new Date('2012-06-03T21:00:00'), "recurrenceDates": [], "exceptionDates": [] }, rrule: { "frequency": RecurrenceRule.Monthly, "limit": 4, "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, affectsModel: false } ], results: [ {label: "recevent2", start: new Date('2011-12-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-01T14:00:00')}, {label: "recevent2", start: new Date('2012-01-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-02T14:00:00')}, {label: "event1", start: new Date('2012-01-02T15:00:00')}, {label: "event2", start: new Date('2012-01-03T11:00:00')}, {label: "recevent1", start: new Date('2012-01-03T14:00:00')}, {label: "recevent2", start: new Date('2012-02-01T16:00:00')}, {label: "recevent2", start: new Date('2012-03-01T16:00:00')} ], modifications: { addRule: { rrule: { "frequency": RecurrenceRule.Daily, "limit": 3, "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, results: [ {label: "recevent2", start: new Date('2011-12-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-01T14:00:00')}, {label: "recevent2", start: new Date('2012-01-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-02T14:00:00')}, {label: "event1", start: new Date('2012-01-02T15:00:00')}, {label: "event2", start: new Date('2012-01-03T11:00:00')}, {label: "recevent1", start: new Date('2012-01-03T14:00:00')}, {label: "event1", start: new Date('2012-01-03T15:00:00')}, {label: "event1", start: new Date('2012-01-04T15:00:00')}, {label: "recevent2", start: new Date('2012-02-01T16:00:00')}, {label: "recevent2", start: new Date('2012-03-01T16:00:00')} ] }, modifyRule1: { rrule: { "frequency": RecurrenceRule.Monthly, "limit": 3, "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, results: [ {label: "recevent2", start: new Date('2011-12-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-01T14:00:00')}, {label: "recevent2", start: new Date('2012-01-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-02T14:00:00')}, {label: "event1", start: new Date('2012-01-02T15:00:00')}, {label: "event2", start: new Date('2012-01-03T11:00:00')}, {label: "recevent1", start: new Date('2012-01-03T14:00:00')}, {label: "recevent2", start: new Date('2012-02-01T16:00:00')}, {label: "event1", start: new Date('2012-02-02T15:00:00')}, {label: "recevent2", start: new Date('2012-03-01T16:00:00')}, {label: "event1", start: new Date('2012-03-02T15:00:00')} ] }, modifyRule2: { rrule: { "frequency": RecurrenceRule.Weekly, "limit": 3, "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, results: [ {label: "recevent2", start: new Date('2011-12-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-01T14:00:00')}, {label: "recevent2", start: new Date('2012-01-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-02T14:00:00')}, {label: "event1", start: new Date('2012-01-02T15:00:00')}, {label: "event2", start: new Date('2012-01-03T11:00:00')}, {label: "recevent1", start: new Date('2012-01-03T14:00:00')}, {label: "event1", start: new Date('2012-01-09T15:00:00')}, {label: "event1", start: new Date('2012-01-16T15:00:00')}, {label: "recevent2", start: new Date('2012-02-01T16:00:00')}, {label: "recevent2", start: new Date('2012-03-01T16:00:00')} ] }, removeRule: { results: [ {label: "recevent2", start: new Date('2011-12-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-01T14:00:00')}, {label: "recevent2", start: new Date('2012-01-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-02T14:00:00')}, {label: "event1", start: new Date('2012-01-02T15:00:00')}, {label: "event2", start: new Date('2012-01-03T11:00:00')}, {label: "recevent1", start: new Date('2012-01-03T14:00:00')}, {label: "recevent2", start: new Date('2012-02-01T16:00:00')}, {label: "recevent2", start: new Date('2012-03-01T16:00:00')} ] } } }] } // initialize model and modify one normal item: // first add a daily recurrence rule then modify recurrence rule from daily to monthly, // then to weekly and finally remove rule function test_modifyParentItems(data) { var j = 0; for (var i in data.managers) { console.log("Testing "+data.managers[i]+" backend") model.manager = data.managers[i]; model.startPeriod = new Date('2011-12-01'); model.endPeriod = new Date('2012-04-30'); model.autoUpdate = true; spyManagerChanged.wait(spyWaitDelay) cleanDatabase(); compare(model.itemCount, 0, "Model not empty") for (j = 0; j < data.definitions.length; j++) { var testEvent = createTestItemFromData(data.definitions[j]); model.saveItem(testEvent); if (data.definitions[j].affectsModel) modelChangedSpy.wait(spyWaitDelay) } compareResultDatesToModel(data.results, model); // addRule, start modifying event1 by adding daily recurrence rule // need to fetch the event we are going to modify separately, because as soon // as we make it a recurring event, it will be deleted from the model fetchSpy.clear(); model.fetchItems([model.items[4].itemId]); fetchSpy.wait(spyWaitDelay); var testItem = test.fetchedItem; var newRule = createTestRuleFromData(data.modifications.addRule); testItem.recurrence.recurrenceRules = [newRule]; model.saveItem(testItem); modelChangedSpy.wait(spyWaitDelay); compareResultDatesToModel(data.modifications.addRule.results, model); // modifyRule1, change rule to monthly var modRule1 = createTestRuleFromData(data.modifications.modifyRule1); testItem.recurrence.recurrenceRules = [modRule1]; model.saveItem(testItem); modelChangedSpy.wait(spyWaitDelay); compareResultDatesToModel(data.modifications.modifyRule1.results, model); // modifyRule2, change rule to weekly var modRule2 = createTestRuleFromData(data.modifications.modifyRule2); testItem.recurrence.recurrenceRules = [modRule2]; model.saveItem(testItem); modelChangedSpy.wait(spyWaitDelay); compareResultDatesToModel(data.modifications.modifyRule2.results, model); // removeRule testItem.recurrence.recurrenceRules = []; model.saveItem(testItem); modelChangedSpy.wait(spyWaitDelay); compareResultDatesToModel(data.modifications.removeRule.results, model); // clean db model.startPeriod = new Date('2011-01-01'); model.endPeriod = new Date('2012-08-30'); modelChangedSpy.wait(spyWaitDelay); cleanDatabase(); compare(model.itemCount, 0, "Model not empty") } } function test_exceptionOccurrences_data() { return [{ managers: utility.getManagerList(), definitions: [ { event : { "displayLabel" : "event0", "start" : new Date('2011-10-25T15:00:00'), "end" : new Date('2011-10-10T16:00:00'), "recurrenceDates": [], "exceptionDates": [] }, affectsModel: true }, { event : { "displayLabel" : "recevent1", "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [], "exceptionDates": [] }, rrule: { "frequency": RecurrenceRule.Daily, "limit": new Date('2012-01-03'), "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, affectsModel: true }, { event : { "displayLabel" : "event1", "start" : new Date('2012-01-02T15:00:00'), "end" : new Date('2012-01-02T16:00:00'), "recurrenceDates": [], "exceptionDates": [] }, affectsModel: true }, { event : { "displayLabel" : "event2", "start" : new Date('2012-01-03T11:00:00'), "end" : new Date('2012-01-03T18:00:00'), "recurrenceDates": [], "exceptionDates": [] }, affectsModel: true }, { event : { "displayLabel" : "recevent2", "start" : new Date('2011-01-01T16:00:00'), "end" : new Date('2011-01-01T17:00:00'), "recurrenceDates": [], "exceptionDates": [] }, rrule: { "frequency": RecurrenceRule.Monthly, "limit": 15, "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, affectsModel: true }, { event : { "displayLabel" : "event3", "start" : new Date('2012-05-15T09:00:00'), "end" : new Date('2012-05-15T10:00:00'), "recurrenceDates": [], "exceptionDates": [] }, affectsModel: false }, { event : { "displayLabel" : "recevent3", "start" : new Date('2012-06-03T20:00:00'), "end" : new Date('2012-06-03T21:00:00'), "recurrenceDates": [], "exceptionDates": [] }, rrule: { "frequency": RecurrenceRule.Monthly, "limit": 4, "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, affectsModel: false } ], results: [ {label: "recevent2", start: new Date('2011-10-01T16:00:00')}, {label: "event0", start: new Date('2011-10-25T15:00:00')}, {label: "recevent2", start: new Date('2011-11-01T16:00:00')}, {label: "recevent2", start: new Date('2011-12-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-01T14:00:00')}, {label: "recevent2", start: new Date('2012-01-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-02T14:00:00')}, {label: "event1", start: new Date('2012-01-02T15:00:00')}, {label: "event2", start: new Date('2012-01-03T11:00:00')}, {label: "recevent1", start: new Date('2012-01-03T14:00:00')}, {label: "recevent2", start: new Date('2012-02-01T16:00:00')}, {label: "recevent2", start: new Date('2012-03-01T16:00:00')} ], modifications: { addRule: { rrule: { "frequency": RecurrenceRule.Monthly, "limit": new Date('2012-05-31'), "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, results: [ {label: "recevent2", start: new Date('2011-10-01T16:00:00')}, {label: "event0", start: new Date('2011-10-25T15:00:00')}, {label: "recevent2", start: new Date('2011-11-01T16:00:00')}, {label: "event0", start: new Date('2011-11-25T15:00:00')}, {label: "recevent2", start: new Date('2011-12-01T16:00:00')}, {label: "event0", start: new Date('2011-12-25T15:00:00')}, {label: "recevent1", start: new Date('2012-01-01T14:00:00')}, {label: "recevent2", start: new Date('2012-01-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-02T14:00:00')}, {label: "event1", start: new Date('2012-01-02T15:00:00')}, {label: "event2", start: new Date('2012-01-03T11:00:00')}, {label: "recevent1", start: new Date('2012-01-03T14:00:00')}, {label: "event0", start: new Date('2012-01-25T15:00:00')}, {label: "recevent2", start: new Date('2012-02-01T16:00:00')}, {label: "event0", start: new Date('2012-02-25T15:00:00')}, {label: "recevent2", start: new Date('2012-03-01T16:00:00')}, {label: "event0", start: new Date('2012-03-25T15:00:00')}, {label: "event0", start: new Date('2012-04-25T15:00:00')} ] }, addException: { results: [ {label: "recevent2", start: new Date('2011-10-01T16:00:00')}, {label: "event0", start: new Date('2011-10-25T15:00:00')}, {label: "recevent2", start: new Date('2011-11-01T16:00:00')}, {label: "event0", start: new Date('2011-11-25T15:00:00')}, {label: "recevent2", start: new Date('2011-12-01T16:00:00')}, {label: "event0", start: new Date('2011-12-25T15:00:00')}, {label: "recevent1", start: new Date('2012-01-01T14:00:00')}, {label: "recevent2", start: new Date('2012-01-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-02T14:00:00')}, {label: "event1", start: new Date('2012-01-02T15:00:00')}, {label: "event2", start: new Date('2012-01-03T11:00:00')}, {label: "recevent1", start: new Date('2012-01-03T14:00:00')}, {label: "recevent2", start: new Date('2012-02-01T16:00:00')}, {label: "event0", start: new Date('2012-02-02T15:00:00')}, {label: "event0", start: new Date('2012-02-25T15:00:00')}, {label: "recevent2", start: new Date('2012-03-01T16:00:00')}, {label: "event0", start: new Date('2012-03-25T15:00:00')}, {label: "event0", start: new Date('2012-04-25T15:00:00')} ] }, removeException: { results: [ {label: "recevent2", start: new Date('2011-10-01T16:00:00')}, {label: "event0", start: new Date('2011-10-25T15:00:00')}, {label: "recevent2", start: new Date('2011-11-01T16:00:00')}, {label: "event0", start: new Date('2011-11-25T15:00:00')}, {label: "recevent2", start: new Date('2011-12-01T16:00:00')}, {label: "event0", start: new Date('2011-12-25T15:00:00')}, {label: "recevent1", start: new Date('2012-01-01T14:00:00')}, {label: "recevent2", start: new Date('2012-01-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-02T14:00:00')}, {label: "event1", start: new Date('2012-01-02T15:00:00')}, {label: "event2", start: new Date('2012-01-03T11:00:00')}, {label: "recevent1", start: new Date('2012-01-03T14:00:00')}, {label: "recevent2", start: new Date('2012-02-01T16:00:00')}, {label: "event0", start: new Date('2012-02-25T15:00:00')}, {label: "recevent2", start: new Date('2012-03-01T16:00:00')}, {label: "event0", start: new Date('2012-03-25T15:00:00')}, {label: "event0", start: new Date('2012-04-25T15:00:00')} ] }, addException2: { results: [ {label: "recevent2", start: new Date('2011-10-01T16:00:00')}, {label: "event0", start: new Date('2011-10-25T15:00:00')}, {label: "recevent2", start: new Date('2011-11-01T16:00:00')}, {label: "event0", start: new Date('2011-11-25T15:00:00')}, {label: "modifiedrecevent2", start: new Date('2011-12-01T16:00:00')}, {label: "event0", start: new Date('2011-12-25T15:00:00')}, {label: "recevent1", start: new Date('2012-01-01T14:00:00')}, {label: "recevent2", start: new Date('2012-01-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-02T14:00:00')}, {label: "event1", start: new Date('2012-01-02T15:00:00')}, {label: "event2", start: new Date('2012-01-03T11:00:00')}, {label: "recevent1", start: new Date('2012-01-03T14:00:00')}, {label: "recevent2", start: new Date('2012-02-01T16:00:00')}, {label: "event0", start: new Date('2012-02-25T15:00:00')}, {label: "recevent2", start: new Date('2012-03-01T16:00:00')}, {label: "event0", start: new Date('2012-03-25T15:00:00')}, {label: "event0", start: new Date('2012-04-25T15:00:00')} ] }, removeGeneratedOccurrence: { results: [ {label: "event0", start: new Date('2011-10-25T15:00:00')}, {label: "recevent2", start: new Date('2011-11-01T16:00:00')}, {label: "event0", start: new Date('2011-11-25T15:00:00')}, {label: "modifiedrecevent2", start: new Date('2011-12-01T16:00:00')}, {label: "event0", start: new Date('2011-12-25T15:00:00')}, {label: "recevent1", start: new Date('2012-01-01T14:00:00')}, {label: "recevent2", start: new Date('2012-01-01T16:00:00')}, {label: "recevent1", start: new Date('2012-01-02T14:00:00')}, {label: "event1", start: new Date('2012-01-02T15:00:00')}, {label: "event2", start: new Date('2012-01-03T11:00:00')}, {label: "recevent1", start: new Date('2012-01-03T14:00:00')}, {label: "recevent2", start: new Date('2012-02-01T16:00:00')}, {label: "event0", start: new Date('2012-02-25T15:00:00')}, {label: "recevent2", start: new Date('2012-03-01T16:00:00')}, {label: "event0", start: new Date('2012-03-25T15:00:00')}, {label: "event0", start: new Date('2012-04-25T15:00:00')} ] }, removeParent: { results: [ {label: "event0", start: new Date('2011-10-25T15:00:00')}, {label: "event0", start: new Date('2011-11-25T15:00:00')}, {label: "event0", start: new Date('2011-12-25T15:00:00')}, {label: "recevent1", start: new Date('2012-01-01T14:00:00')}, {label: "recevent1", start: new Date('2012-01-02T14:00:00')}, {label: "event1", start: new Date('2012-01-02T15:00:00')}, {label: "event2", start: new Date('2012-01-03T11:00:00')}, {label: "recevent1", start: new Date('2012-01-03T14:00:00')}, {label: "event0", start: new Date('2012-02-25T15:00:00')}, {label: "event0", start: new Date('2012-03-25T15:00:00')}, {label: "event0", start: new Date('2012-04-25T15:00:00')} ] }, removeParent2: { results: [ {label: "recevent1", start: new Date('2012-01-01T14:00:00')}, {label: "recevent1", start: new Date('2012-01-02T14:00:00')}, {label: "event1", start: new Date('2012-01-02T15:00:00')}, {label: "event2", start: new Date('2012-01-03T11:00:00')}, {label: "recevent1", start: new Date('2012-01-03T14:00:00')} ] } } }] } // initialize model (store one normal event to variable and change model timePeriod so that stored event is not part of the model anymore) // modify stored item by adding a daily recurrence rule // then add exception, modify event time to move occurrence to different index // then remove exception occurrence // create another exception occurrence with different displayLabel // remove one generated occurrence // remove whole parent item function test_exceptionOccurrences(data) { var j = 0; for (var i in data.managers) { console.log("Testing "+data.managers[i]+" backend") model.manager = data.managers[i]; model.startPeriod = new Date('2011-10-01'); model.endPeriod = new Date('2012-04-30'); model.autoUpdate = true; spyManagerChanged.wait(spyWaitDelay) cleanDatabase(); compare(model.itemCount, 0, "Model not empty") for (j = 0; j < data.definitions.length; j++) { var testItem = createTestItemFromData(data.definitions[j]); model.saveItem(testItem); if (data.definitions[j].affectsModel) modelChangedSpy.wait(spyWaitDelay) } compareResultDatesToModel(data.results, model); var testEvent = model.items[1]; var testEventId = testEvent.itemId; // addRule, start modifying event0 by adding monthly recurrence rule var newRule = createTestRuleFromData(data.modifications.addRule); testEvent.recurrence.recurrenceRules = [newRule]; model.saveItem(testEvent); modelChangedSpy.wait(spyWaitDelay); compareResultDatesToModel(data.modifications.addRule.results, model); // addException, modify generated occurrence and save it var xoccurrence = model.items[12]; xoccurrence.startDateTime = new Date('2012-02-02T15:00:00'); xoccurrence.endDateTime = new Date('2012-02-02T16:00:00'); model.saveItem(xoccurrence); modelChangedSpy.wait(spyWaitDelay); compareResultDatesToModel(data.modifications.addException.results, model); // removeException var xoccurrenceRemove = model.items[13]; model.removeItem(xoccurrenceRemove); modelChangedSpy.wait(spyWaitDelay); compareResultDatesToModel(data.modifications.removeException.results, model); // addException2, change displayLabel var xoccurrence2 = model.items[4]; xoccurrence2.displayLabel = "modifiedrecevent2"; model.saveItem(xoccurrence2); modelChangedSpy.wait(spyWaitDelay); compareResultDatesToModel(data.modifications.addException2.results, model); // removeGeneratedOccurrence var occurrenceRemove = model.items[0]; model.removeItem(occurrenceRemove); modelChangedSpy.wait(spyWaitDelay); compareResultDatesToModel(data.modifications.removeGeneratedOccurrence.results, model); // removeParent // handle an occurrence of recevent2 var parentsOccurrence = model.items[1]; //find out parent id var parentId = parentsOccurrence.parentId; model.removeItem(parentId); modelChangedSpy.wait(spyWaitDelay); compareResultDatesToModel(data.modifications.removeParent.results, model); // removeParent2 // handle an occurrence of event0 var parentsOccurrence2 = model.items[0]; //find out parent id var parentId2 = parentsOccurrence2.parentId; model.removeItem(parentId2); modelChangedSpy.wait(spyWaitDelay); compareResultDatesToModel(data.modifications.removeParent2.results, model); // clean db model.startPeriod = new Date('2011-01-01'); model.endPeriod = new Date('2012-08-30'); modelChangedSpy.wait(spyWaitDelay); cleanDatabase(); compare(model.itemCount, 0, "Model not empty") } } // Helper functions function cleanDatabase() { var ids = []; var removeIds = []; var i; var j; var parentId; for (i = 0; i < model.itemCount; i++) { if (model.items[i].itemType == Type.EventOccurrence || model.items[i].itemType == Type.TodoOccurrence) ids.push(model.items[i].parentId) if (model.items[i].itemId !== "qtorganizer:::") ids.push(model.items[i].itemId) } // remove duplicates for (i = 0; i < ids.length; i++) { for (j = 0; j < removeIds.length; j++) { if (ids[i] == removeIds[j]) break; } if (j == removeIds.length) removeIds.push(ids[i]) } modelChangedSpy.clear() if (removeIds.length > 0) { model.removeItems(removeIds) modelChangedSpy.wait(spyWaitDelay) } compare(model.items.length, 0) } function createTestItemFromData(data) { var testEvent = Qt.createQmlObject("import QtOrganizer 5.0; Event { }", test); var testRule = Qt.createQmlObject("import QtOrganizer 5.0; RecurrenceRule { }", test); if (data.rrule !== undefined) { testRule.frequency = data.rrule.frequency; testRule.limit = data.rrule.limit; testRule.interval = data.rrule.interval; testRule.daysOfWeek = data.rrule.daysOfWeek; testRule.daysOfMonth = data.rrule.daysOfMonth; testRule.daysOfYear = data.rrule.daysOfYear; testRule.monthsOfYear = data.rrule.monthsOfYear; testRule.positions = data.rrule.positions; testRule.firstDayOfWeek = data.rrule.firstDayOfWeek; testEvent.recurrence.recurrenceRules = [testRule]; } testEvent.displayLabel = data.event.displayLabel; testEvent.startDateTime = new Date(data.event.start); testEvent.endDateTime = new Date(data.event.end); testEvent.recurrence.recurrenceDates = data.event.recurrenceDates; testEvent.recurrence.exceptionDates = data.event.exceptionDates; return testEvent; } function createTestRuleFromData(data) { var testRule = Qt.createQmlObject("import QtOrganizer 5.0; RecurrenceRule { }", test); testRule.frequency = data.rrule.frequency; testRule.limit = data.rrule.limit; testRule.interval = data.rrule.interval; testRule.daysOfWeek = data.rrule.daysOfWeek; testRule.daysOfMonth = data.rrule.daysOfMonth; testRule.daysOfYear = data.rrule.daysOfYear; testRule.monthsOfYear = data.rrule.monthsOfYear; testRule.positions = data.rrule.positions; testRule.firstDayOfWeek = data.rrule.firstDayOfWeek; return testRule; } function compareResultDatesToModel(results, model) { compare(model.itemCount, results.length, "Item count is wrong.") for (var i = 0; i < results.length; i++) { var itemDisplayLabel = model.items[i].displayLabel; var itemStart = model.items[i].startDateTime; compare(itemDisplayLabel, results[i].label, "Item displayLabel is not correct"); compare(itemStart, results[i].start, "Item start date is not correct") } } } tests/auto/organizer/qmlorganizer/testcases/tst_organizerrecurrence.qml000066400000000000000000001052721233466112000273700ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtOrganizer 5.0 TestCase { id: test name: "OrganizerRecurrenceTests" QOrganizerTestUtility { id: utility } OrganizerModel { id: model autoUpdate:true startPeriod:'2009-01-01' endPeriod:'2014-12-31' } Event { id: testEvent } Todo { id: testTodo } RecurrenceRule { id: testRule } RecurrenceRule { id: testXRule } SignalSpy { id: spyManagerChanged signalName: "managerChanged" target: model } SignalSpy { id: spyModelChanged signalName: "modelChanged" target: model } function cleanup() { model.manager = "" } function test_recurrenceDates_data() { return [ { tag: "Event with two recurrence dates", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [new Date('2012-01-02'), new Date('2012-01-03')], "exceptionDates": [], "recurrenceRules": [], "exceptionRules": [] }, results: [new Date('2012-01-01T14:00:00'), new Date('2012-01-02T14:00:00'), new Date('2012-01-03T14:00:00')] }, { tag: "Event with recurrence date before event start date", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [new Date('2011-01-01')], "exceptionDates": [], "recurrenceRules": [], "exceptionRules": [] }, results: [new Date('2012-01-01T14:00:00')] }, { tag: "Event outside model range with occurrences inside model range", managers: utility.getManagerList(), definitions: { "start" : new Date('2008-01-01T14:00:00'), "end" : new Date('2008-01-01T15:00:00'), "recurrenceDates": [new Date('2012-01-02'), new Date('2012-01-03')], "exceptionDates": [], "recurrenceRules": [], "exceptionRules": [] }, results: [new Date('2012-01-02T14:00:00'), new Date('2012-01-03T14:00:00')] }, { tag: "Event inside model range with occurrences outside model range", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [new Date('2020-01-02'), new Date('2020-01-03')], "exceptionDates": [], "recurrenceRules": [], "exceptionRules": [] }, results: [new Date('2012-01-01T14:00:00')] }, { tag: "Overlapping recurrence dates", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [new Date('2012-01-02'), new Date('2020-01-02')], "exceptionDates": [], "recurrenceRules": [], "exceptionRules": [] }, results: [new Date('2012-01-01T14:00:00'), new Date('2012-01-02T14:00:00')] } ] } function test_recurrenceDates(data) { runTest(data) } function test_recurrenceRules_data() { return [ { tag: "Daily recurrence, limit to 3", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [], "exceptionDates": [], "recurrenceRules": [testRule], "exceptionRules": [] }, rrule: { "frequency": RecurrenceRule.Daily, "limit": 3, "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, results: [new Date('2012-01-01T14:00:00'), new Date('2012-01-02T14:00:00'), new Date('2012-01-03T14:00:00')] }, { tag: "Daily recurrence, limit to 3, interval of 2 days", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [], "exceptionDates": [], "recurrenceRules": [testRule], "exceptionRules": [] }, rrule: { "frequency": RecurrenceRule.Daily, "limit": 3, "interval": 2, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, results: [new Date('2012-01-01T14:00:00'), new Date('2012-01-03T14:00:00'), new Date('2012-01-05T14:00:00')] }, { tag: "Daily recurrence, limit to date", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [], "exceptionDates": [], "recurrenceRules": [testRule], "exceptionRules": [] }, rrule: { "frequency": RecurrenceRule.Daily, "limit": new Date('2012-01-05'), "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, results: [new Date('2012-01-01T14:00:00'), new Date('2012-01-02T14:00:00'), new Date('2012-01-03T14:00:00'), new Date('2012-01-04T14:00:00'), new Date('2012-01-05T14:00:00')] }, { tag: "Daily recurrence, limit to 6, Mondays, Wednesdays and Saturdays only", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [], "exceptionDates": [new Date('2012-01-04T14:00:00'), new Date('2012-01-09T14:00:00')], "recurrenceRules": [testRule], "exceptionRules": [] }, rrule: { "frequency": RecurrenceRule.Daily, "limit": 6, "interval": 1, "daysOfWeek": [Qt.Monday, Qt.Wednesday, Qt.Saturday], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, results: [new Date('2012-01-02T14:00:00'), new Date('2012-01-07T14:00:00'), new Date('2012-01-11T14:00:00'), new Date('2012-01-14T14:00:00')] }, { tag: "Daily recurrence, limit to 4, days of month: 1, 2, 10, 11", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [], "exceptionDates": [], "recurrenceRules": [testRule], "exceptionRules": [] }, rrule: { "frequency": RecurrenceRule.Daily, "limit": 4, "interval": 1, "daysOfWeek": [], "daysOfMonth": [1, 2, 10, 11], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, results: [new Date('2012-01-01T14:00:00'), new Date('2012-01-02T14:00:00'), new Date('2012-01-10T14:00:00'), new Date('2012-01-11T14:00:00')] }, // Weekly recurrences { tag: "Weekly recurrence, limit by date, biweekly", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [], "exceptionDates": [], "recurrenceRules": [testRule], "exceptionRules": [] }, rrule: { "frequency": RecurrenceRule.Weekly, "limit": new Date('2012-02-05'), "interval": 2, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, results: [new Date('2012-01-01T14:00:00'), new Date('2012-01-15T14:00:00'), new Date('2012-01-29T14:00:00')] }, // Monthly recurrences { tag: "Monthly recurrence, limit of 6, February, May, December only", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [], "exceptionDates": [], "recurrenceRules": [testRule], "exceptionRules": [] }, rrule: { "frequency": RecurrenceRule.Monthly, "limit": 6, "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [RecurrenceRule.February, RecurrenceRule.May, RecurrenceRule.December], "positions": [], "firstDayOfWeek": Qt.Monday }, results: [new Date('2012-02-01T14:00:00'), new Date('2012-05-01T14:00:00'), new Date('2012-12-01T14:00:00'), new Date('2013-02-01T14:00:00'), new Date('2013-05-01T14:00:00'), new Date('2013-12-01T14:00:00')] }, { tag: "Monthly recurrence, limit of 6, positions 1, 2, -1", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [], "exceptionDates": [], "recurrenceRules": [testRule], "exceptionRules": [] }, rrule: { "frequency": RecurrenceRule.Monthly, "limit": 6, "interval": 1, "daysOfWeek": [Qt.Monday, Qt.Tuesday, Qt.Wednesday, Qt.Thursday, Qt.Friday, Qt.Saturday, Qt.Sunday], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [1, 2, -1], "firstDayOfWeek": Qt.Monday }, results: [new Date('2012-01-01T14:00:00'), new Date('2012-01-02T14:00:00'), new Date('2012-01-31T14:00:00'), new Date('2012-02-01T14:00:00'), new Date('2012-02-02T14:00:00'), new Date('2012-02-29T14:00:00')] }, { tag: "Monthly recurrence, limit of 6, position 31", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [], "exceptionDates": [], "recurrenceRules": [testRule], "exceptionRules": [] }, rrule: { "frequency": RecurrenceRule.Monthly, "limit": 6, "interval": 1, "daysOfWeek": [Qt.Monday, Qt.Tuesday, Qt.Wednesday, Qt.Thursday, Qt.Friday, Qt.Saturday, Qt.Sunday], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [31], "firstDayOfWeek": Qt.Monday }, results: [new Date('2012-01-31T14:00:00'), new Date('2012-03-31T14:00:00'), new Date('2012-05-31T14:00:00'), new Date('2012-07-31T14:00:00'), new Date('2012-08-31T14:00:00'), new Date('2012-10-31T14:00:00')] }, // Yearly recurrences { tag: "Yearly recurrence, limit to 4, two exception dates ", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [], "exceptionDates": [new Date('2012-01-04T14:00:00'), new Date('2012-01-09T14:00:00')], "recurrenceRules": [testRule], "exceptionRules": [] }, rrule: { "frequency": RecurrenceRule.Yearly, "limit": 4, "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, results: [new Date('2012-01-01T14:00:00'), new Date('2013-01-01T14:00:00'), new Date('2014-01-01T14:00:00')] }, { tag: "Yearly recurrence, limit to 6, May, July, August, December only", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [], "exceptionDates": [], "recurrenceRules": [testRule], "exceptionRules": [] }, rrule: { "frequency": RecurrenceRule.Yearly, "limit": 6, "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [RecurrenceRule.May, RecurrenceRule.July, RecurrenceRule.August, RecurrenceRule.December], "positions": [2, -2], "firstDayOfWeek": Qt.Monday }, results: [new Date('2012-07-01T14:00:00'), new Date('2012-08-01T14:00:00'), new Date('2013-07-01T14:00:00'), new Date('2013-08-01T14:00:00'), new Date('2014-07-01T14:00:00'), new Date('2014-08-01T14:00:00')] } ] } function test_recurrenceRules(data) { runTest(data) } function test_exceptionDates_data() { return [ { tag: "Exception dates, two matching exception dates", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T23:00:00'), "end" : new Date('2012-01-02T01:00:00'), "recurrenceDates": [new Date('2012-01-02'), new Date('2012-01-03'), new Date('2012-01-04')], "exceptionDates": [new Date('2012-01-02'), new Date('2012-01-04')], "recurrenceRules": [], "exceptionRules": [] }, results: [new Date('2012-01-01T23:00:00'), new Date('2012-01-03T23:00:00')] }, { tag: "Exception dates, two non-matching exception dates", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [], "exceptionDates": [new Date('2012-01-02'), new Date('2012-01-03')], "recurrenceRules": [], "exceptionRules": [] }, results: [new Date('2012-01-01T14:00:00')] } ] } function test_exceptionDates(data) { runTest(data) } function test_exceptionRules_data() { return [ { tag: "Daily recurrence with matching daily exceptions", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [new Date('2012-05-18')], "exceptionDates": [], "recurrenceRules": [testRule], "exceptionRules": [testXRule] }, rrule: { "frequency": RecurrenceRule.Daily, "limit": 3, "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, xrule: { "frequency": RecurrenceRule.Daily, "limit": 3, "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, results: [new Date('2012-05-18T14:00:00')] }, { tag: "Daily recurrence, weekly exceptions on Monday and Sunday", managers: utility.getManagerList(), definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [], "exceptionDates": [], "recurrenceRules": [testRule], "exceptionRules": [testXRule] }, rrule: { "frequency": RecurrenceRule.Daily, "limit": 10, "interval": 1, "daysOfWeek": [], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, xrule: { "frequency": RecurrenceRule.Weekly, "limit": 10, "interval": 1, "daysOfWeek": [Qt.Monday, Qt.Sunday], "daysOfMonth": [], "daysOfYear": [], "monthsOfYear": [], "positions": [], "firstDayOfWeek": Qt.Monday }, results: [new Date('2012-01-03T14:00:00'), new Date('2012-01-04T14:00:00'), new Date('2012-01-05T14:00:00'), new Date('2012-01-06T14:00:00'), new Date('2012-01-07T14:00:00'), new Date('2012-01-10T14:00:00')] }] } function test_exceptionRules(data) { runTest(data) } /* Todo, how this should behave? Currently there will be no occurrences, despite the recurrenceDate function test_invalidRecurrence_data() { return [ { tag: "Invalid recurrence, no matching dates for rule", managers: ["jsondb", "memory"], definitions: { "start" : new Date('2012-01-01T14:00:00'), "end" : new Date('2012-01-01T15:00:00'), "recurrenceDates": [new Date('2012-01-01T14:00:00')], "exceptionDates": [], "recurrenceRules": [testRule], "exceptionRules": [] }, rrule: { "frequency": RecurrenceRule.Monthly, "limit": 5, "interval": 1, "daysOfWeek": [], "daysOfMonth": [31], "daysOfYear": [], "monthsOfYear": [RecurrenceRule.February, RecurrenceRule.June, RecurrenceRule.April, RecurrenceRule.September, RecurrenceRule.November], "positions": [], "firstDayOfWeek": Qt.Monday }, results: [new Date('2012-01-01T14:00:00')] }] } function test_invalidRecurrence(data) { runTest(data); }*/ function test_recurrenceRulesMaxLimit() { var managers = utility.getManagerList(); for (var i in managers) { console.log("Testing "+managers[i]+" backend"); model.manager = managers[i]; spyManagerChanged.wait() cleanDatabase(); testRule.frequency = RecurrenceRule.Daily; testRule.interval = 1; testRule.limit = null; testRule.daysOfWeek = []; testRule.daysOfMonth = []; testRule.daysOfYear = []; testRule.monthsOfYear = []; testRule.positions = []; testEvent.recurrence.recurrenceRules = [testRule]; model.saveItem(testEvent) spyModelChanged.wait(); compare(model.itemCount, 50); // Default max limit is 50 cleanDatabase(); } } function test_recurrenceRulesUnion() { var managers = utility.getManagerList(); for (var i in managers) { console.log("Testing "+managers[i]+" backend"); model.manager = managers[i]; spyManagerChanged.wait() cleanDatabase(); testRule.frequency = RecurrenceRule.Daily; testRule.interval = 3; testRule.limit = 3; testRule.daysOfWeek = []; testRule.daysOfMonth = []; testRule.daysOfYear = []; testRule.monthsOfYear = []; testRule.positions = []; // Not used for exception this time... testXRule.frequency = RecurrenceRule.Daily; testXRule.interval = 2; testXRule.limit = 4; testXRule.daysOfWeek = []; testXRule.daysOfMonth = []; testXRule.daysOfYear = []; testXRule.monthsOfYear = []; testXRule.positions = []; testEvent.recurrence.recurrenceDates = []; testEvent.recurrence.exceptionDates = []; testEvent.recurrence.recurrenceRules = [testRule, testXRule]; testEvent.recurrence.exceptionRules = []; testEvent.startDateTime = new Date('2012-01-01T14:00:00'); testEvent.endDateTime = new Date('2012-01-01T16:00:00'); model.saveItem(testEvent) spyModelChanged.wait(); compareResultDatesToModel([new Date('2012-01-01T14:00:00'), new Date('2012-01-03T14:00:00'), new Date('2012-01-04T14:00:00'), new Date('2012-01-05T14:00:00'), new Date('2012-01-07T14:00:00'), ],model); cleanDatabase(); } } // Helper functions function runTest(data) { for (var i in data.managers) { console.log("Testing "+data.managers[i]+" backend") model.manager = data.managers[i] spyManagerChanged.wait() cleanDatabase() compare(model.itemCount, 0, "Model not empty") populateTestItemsFromData(data); model.saveItem(testEvent) spyModelChanged.wait() compareResultDatesToModel(data.results, model) cleanDatabase() compare(model.itemCount, 0, "Model not empty") populateTestItemsFromData(data); model.saveItem(testTodo) spyModelChanged.wait() compareResultDatesToModel(data.results, model) cleanDatabase() } } function cleanDatabase() { var ids = []; //model.itemIds(); var removeIds = []; var i; var j; var parentId; for (i = 0; i < model.itemCount; i++) { if (model.items[i].itemType == Type.EventOccurrence || model.items[i].itemType == Type.TodoOccurrence) ids.push(model.items[i].parentId) else ids.push(model.items[i].itemId) } // remove duplicates for (i = 0; i < ids.length; i++) { for (j = 0; j < removeIds.length; j++) { if (ids[i] == removeIds[j]) break; } if (j == removeIds.length) removeIds.push(ids[i]) } spyModelChanged.clear() if (ids.length > 0) { model.removeItems(removeIds) spyModelChanged.wait() } compare(model.itemIds().length, 0) } function populateTestItemsFromData(data) { if (data.rrule !== undefined) { testRule.frequency = data.rrule.frequency; testRule.limit = data.rrule.limit; testRule.interval = data.rrule.interval; testRule.daysOfWeek = data.rrule.daysOfWeek; testRule.daysOfMonth = data.rrule.daysOfMonth; testRule.daysOfYear = data.rrule.daysOfYear; testRule.monthsOfYear = data.rrule.monthsOfYear; testRule.positions = data.rrule.positions; testRule.firstDayOfWeek = data.rrule.firstDayOfWeek; } if (data.xrule !== undefined) { testXRule.frequency = data.xrule.frequency; testXRule.limit = data.xrule.limit; testXRule.interval = data.xrule.interval; testXRule.daysOfWeek = data.xrule.daysOfWeek; testXRule.daysOfMonth = data.xrule.daysOfMonth; testXRule.daysOfYear = data.xrule.daysOfYear; testXRule.monthsOfYear = data.xrule.monthsOfYear; testXRule.positions = data.xrule.positions; testXRule.firstDayOfWeek = data.xrule.firstDayOfWeek; } testEvent.startDateTime = new Date(data.definitions.start); testTodo.startDateTime = new Date(data.definitions.start); testEvent.endDateTime = new Date(data.definitions.end); testTodo.dueDateTime = new Date(data.definitions.end); testEvent.recurrence.recurrenceDates = data.definitions.recurrenceDates; testTodo.recurrence.recurrenceDates = data.definitions.recurrenceDates; testEvent.recurrence.recurrenceRules = data.definitions.recurrenceRules; testTodo.recurrence.recurrenceRules = data.definitions.recurrenceRules; testEvent.recurrence.exceptionDates = data.definitions.exceptionDates; testTodo.recurrence.exceptionDates = data.definitions.exceptionDates; testEvent.recurrence.exceptionRules = data.definitions.exceptionRules; testTodo.recurrence.exceptionRules = data.definitions.exceptionRules; } function compareResultDatesToModel(results, model) { compare(model.itemCount, results.length, "Occurrence count is wrong."); for (var i = 0; i < results.length; i++) { var itemStart = model.items[i].startDateTime; compare(itemStart, results[i], " Occurrence start date is not correct"); } } } tests/auto/organizer/qmlorganizer/testcases/tst_organizerunionfilter.qml000066400000000000000000000167011233466112000275670ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtOrganizer 5.0 Rectangle { id: test; QOrganizerTestUtility { id: utility } Collection { id: testCollection name: 'My collection filter' description: 'unionFilter test' } CollectionFilter { id: collectionFilter } IdFilter { id: idFilter } TestCase { name: "Organizer union filter test" function test_unionFilter_data() { return [ { tag: "properties", code: "import QtOrganizer 5.0;" + "UnionFilter{\n" + " id:unionFilter" + "}\n" }, ] } function test_unionFilter(data) { var list = utility.getManagerList(); if (list.length < 0) { console.log("No manager to test"); return; } //Test all manager backends for (var i = 0; i < list.length; i ++) { var managerName = list[i]; var debugFlag = 0; console.log("test_unionFilter test start! :" + managerName); var model = Qt.createQmlObject( "import QtOrganizer 5.0;" + "OrganizerModel {" + " manager: \"qtorganizer:" + managerName + ":id=qml\";" + " startPeriod:'2009-01-01';" + " endPeriod:'2012-12-31';" + " autoUpdate:true; }" , test); var unionFilter = Qt.createQmlObject(data.code, test); var event = Qt.createQmlObject( "import QtOrganizer 5.0;" + "Event { " + " id:event;" + " displayLabel: \"organizer union filter test event\"; " + " description: \"organizer union filter test event\"; " + " startDateTime: '2010-12-12'; " + " endDateTime: '2010-12-13'; }" , test); utility.init(model); utility.waitModelChange(); utility.empty_calendar(); //------prepare filter data: save event and collection------// var collectionLegnth = model.collections.length model.saveCollection(testCollection) //Let's wait for the model to be up-to-date utility.waitModelChange(collectionLegnth + 1, utility.collectionChange); //we should have more than default collection now var savedCollection = model.collections[model.collections.length - 1]; model.saveItem(event); utility.waitModelChange(1); compare(model.itemCount, 1) //event with new collection id event.collectionId = savedCollection.collectionId; model.saveItem(event); utility.waitModelChange(2); compare(model.itemCount, 2) var fetchlist = model.items; var idEventId; var collectionEventId; if (fetchlist[1].collectionId == savedCollection.collectionId) { idEventId = fetchlist[0].itemId; collectionEventId = fetchlist[1].itemId; } else if (fetchlist[0].collectionId == savedCollection.collectionId) { idEventId = fetchlist[1].itemId; collectionEventId = fetchlist[0].itemId; } else { console.log("Something Wrong!"); } utility.debug("Single filter", debugFlag); idFilter.ids = [idEventId]; //Single filter unionFilter.filters = [idFilter]; model.filter = unionFilter; utility.waitModelChange(1); compare(model.itemCount, 1) //Change collection filter id collectionFilter.ids = [savedCollection.collectionId]; //Duoble filters model.filter.filters = [idFilter, collectionFilter] utility.debug("Duoble filter", debugFlag); utility.waitModelChange(2); compare(model.itemCount, 2) //Double filter 2 //Change collection filter id collectionFilter.ids = [savedCollection.collectionId, model.defaultCollection().collectionId]; model.filter.filters = [idFilter, collectionFilter] utility.debug("Duoble filter 2", debugFlag); utility.waitModelChange(2); compare(model.itemCount, 2) //Double filter 3 //Change collection filter id collectionFilter.ids = [savedCollection.collectionId, model.defaultCollection().collectionId]; idFilter.ids = [idEventId, collectionEventId]; utility.debug("Duoble filter ~3", debugFlag); model.filter.filters = [idFilter, collectionFilter] utility.debug("Duoble filter 3" + collectionFilter.ids, debugFlag); utility.waitModelChange(2); compare(model.itemCount, 2) utility.debug("Test over!", debugFlag); utility.empty_calendar(); } } } } tests/auto/organizer/qmlorganizer/testcases/tst_organizervisualreminder.qml000066400000000000000000000234431233466112000302630ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtOrganizer 5.0 Rectangle { id: test; QOrganizerTestUtility { id: utility } TestCase { name: "VisualReminder" function test_visualReminder_data() { return [ { tag: " no properties", code: "import QtOrganizer 5.0;" + "VisualReminder {" + "}" }, { tag: " 1 properties", code: "import QtOrganizer 5.0;" + "VisualReminder {" + " repetitionCount: 3; }" }, { tag: " 2 properties", code: "import QtOrganizer 5.0;" + "VisualReminder {" + " repetitionCount: 3; " + " repetitionDelay: 30;}" }, { tag: " 3 properties", code: "import QtOrganizer 5.0;" + "VisualReminder {" + " repetitionCount: 0; " + " secondsBeforeStart: 100;}" }, { tag: " 4 properties", code: "import QtOrganizer 5.0;" + "VisualReminder {" + " repetitionCount: 3; " + " repetitionDelay: 30; " + " secondsBeforeStart: 40; " + " dataUrl: \"www.nokia.com\" }" }, { tag: " 5 properties", code: "import QtOrganizer 5.0;" + "VisualReminder {" + " repetitionCount: 3; " + " repetitionDelay: 30; " + " secondsBeforeStart: 40; " + " message: \"visual reminder Message\"; " + " dataUrl: \"www.nokia.com\" }" }, ] } function test_visualReminder(data) { var debugFlag = 0; var list = utility.getManagerList(); if (list.length < 0) { console.log("No manager to test"); return; } //Test all manager backends for (var i = 0; i < list.length; i ++) { var managerName = list[i]; if (managerName == "jsondb")//jsondb backend does not support visual reminder return; console.log("VisualReminder test start! :" + managerName); var model = Qt.createQmlObject( "import QtOrganizer 5.0;" + "OrganizerModel {" + " manager: \"qtorganizer:" + managerName + ":id=qml\";" + " startPeriod:'2009-01-01';" + " endPeriod:'2012-12-31';" + " autoUpdate:true; }" , test); var visualReminderEvent = Qt.createQmlObject( "import QtOrganizer 5.0;" + "Event { " + " id:event;" + " displayLabel: \"organizer qml reminder test event\"; " + " description: \"This is a new reminder event!!!\"; " + " startDateTime: '2010-12-12'; " + " endDateTime: '2010-12-13'; }" , test); var visualReminderDetail = Qt.createQmlObject(data.code , visualReminderEvent); utility.init(model); utility.empty_calendar(); //------Create and save the detail test------// utility.debug("Create and save the detail test", debugFlag); visualReminderDetail.dataUrl = "http://www.test0.com"; visualReminderEvent.setDetail(visualReminderDetail); if (managerName == "jsondb") { // custom fields allowed in JsonDb for audible reminder // simple test here, since already fully tested in C++ var extendedDetail = Qt.createQmlObject( "import QtOrganizer 5.0;" + "ExtendedDetail {" + " name: \"reminder\";" + " data: \{" + " Qt: \"Everywhere\";" + " Url: \"http://www.qt-project.org/\";" + " }" + "}" , test); visualReminderEvent.addDetail(extendedDetail); } model.saveItem(visualReminderEvent); //Let's wait for the model to be up-to-date utility.waitModelChange(1); compare(model.itemCount, 1) var fetchVisuallist = model.items; var savedVisualEvent = fetchVisuallist[0]; verify(savedVisualEvent != undefined); verify(savedVisualEvent.detail(Detail.VisualReminder) != undefined); utility.compareReminderDetails(visualReminderDetail, savedVisualEvent.detail(Detail.VisualReminder)); //------update the details test------// var savedEventDetail = savedVisualEvent.detail(Detail.VisualReminder); savedEventDetail.dataUrl = "http://www.test222.com"; savedEventDetail.message = "visual reminder message"; savedEventDetail.secondsBeforeStart = 300; savedEventDetail.repetitionCount = 0; visualReminderDetail.dataUrl = "http://www.test222.com"; visualReminderDetail.message = "visual reminder message"; visualReminderDetail.secondsBeforeStart = 300; visualReminderDetail.repetitionCount = 0; savedVisualEvent.setDetail(savedEventDetail); model.saveItem(savedVisualEvent); utility.organizerChangedSpy.wait(); //no new event created compare(model.itemCount, 1) fetchVisuallist = model.items; var updatedEvent = fetchVisuallist[0]; verify(updatedEvent != undefined); verify(updatedEvent.detail(Detail.VisualReminder) != undefined); var updatedEventDetail = updatedEvent.detail(Detail.VisualReminder); utility.compareReminderDetails(savedEventDetail, updatedEventDetail); utility.compareReminderDetails(visualReminderDetail, updatedEventDetail); //------remove the details test------// var removeEventDetail = updatedEvent.detail(Detail.VisualReminder); updatedEvent.removeDetail(removeEventDetail); model.saveItem(updatedEvent); utility.organizerChangedSpy.wait(); //no new event created compare(model.itemCount, 1) fetchVisuallist = model.items; var detailRemovedEvent = fetchVisuallist[0]; var detailRemovedEventDetailList = detailRemovedEvent.details(Detail.VisualReminder); if (detailRemovedEventDetailList.length > 0) { var detailRemovedEventDetail = detailRemovedEvent.detail(Detail.VisualReminder); utility.outputEvent(detailRemovedEvent) } verify(detailRemovedEventDetailList.length == 0); verify(removeEventDetail != undefined) utility.organizerChangedSpy.clear(); utility.organizerChangedSpy.destroy(); model.destroy(); visualReminderEvent.destroy(); visualReminderDetail.destroy(); // utility.empty_calendar(); } } } } tests/auto/organizer/qmlorganizer/tst_qmlorganizer.cpp000066400000000000000000000037571233466112000240240ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include QUICK_TEST_MAIN(qmlorganizer) tests/auto/organizer/qorganizercollection/000077500000000000000000000000001233466112000214235ustar00rootroot00000000000000tests/auto/organizer/qorganizercollection/qorganizercollection.pro000066400000000000000000000001751233466112000264050ustar00rootroot00000000000000include(../../auto.pri) QT += organizer SOURCES += tst_qorganizercollection.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/organizer/qorganizercollection/tst_qorganizercollection.cpp000066400000000000000000000436361233466112000272720ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include //TESTED_COMPONENT=src/organizer QTORGANIZER_USE_NAMESPACE class tst_QOrganizerCollection: public QObject { Q_OBJECT public: tst_QOrganizerCollection(); virtual ~tst_QOrganizerCollection(); private slots: void metaData(); void compare(); void idLessThan(); void idHash(); void idStringFunctions(); void hash(); void datastream(); void traits(); void idTraits(); void localIdTraits(); }; tst_QOrganizerCollection::tst_QOrganizerCollection() { } tst_QOrganizerCollection::~tst_QOrganizerCollection() { } void tst_QOrganizerCollection::metaData() { QOrganizerCollection c; QVERIFY(c.metaData().isEmpty()); c.setExtendedMetaData(QString(QStringLiteral("test")), 5); QCOMPARE(c.extendedMetaData(QString(QStringLiteral("test"))).toInt(), 5); QMap mdm; mdm.insert(QOrganizerCollection::KeyName, QString(QStringLiteral("test2"))); c.setMetaData(mdm); QCOMPARE(c.metaData(), mdm); QCOMPARE(c.metaData(QOrganizerCollection::KeyName).toString(), QString(QStringLiteral("test2"))); } class BasicCollectionLocalId : public QOrganizerCollectionEngineId { public: BasicCollectionLocalId(const QString& managerUri, uint id) : m_managerUri(managerUri), m_id(id) {} bool isEqualTo(const QOrganizerCollectionEngineId* other) const { if (m_managerUri == static_cast(other)->m_managerUri) return m_id == static_cast(other)->m_id; return false; } bool isLessThan(const QOrganizerCollectionEngineId* other) const { if (m_managerUri == static_cast(other)->m_managerUri) return m_id < static_cast(other)->m_id; return m_managerUri < static_cast(other)->m_managerUri; } QString managerUri() const { static const QString uri(QStringLiteral("qtorganizer:basic:")); return uri; } QOrganizerCollectionEngineId* clone() const { BasicCollectionLocalId* cloned = new BasicCollectionLocalId(m_managerUri, m_id); return cloned; } QDebug& debugStreamOut(QDebug& dbg) const { return dbg << m_managerUri << m_id; } QString toString() const { return m_managerUri + QString("::") + QString::number(m_id); } uint hash() const { return m_id; } private: QString m_managerUri; uint m_id; }; QOrganizerCollectionId makeId(const QString& managerUri, uint id) { return QOrganizerCollectionId(new BasicCollectionLocalId(managerUri, id)); } void tst_QOrganizerCollection::compare() { QOrganizerCollection c, c2; QVERIFY(c == c2); c.setExtendedMetaData(QStringLiteral("test"), 5); QVERIFY(c != c2); c2.setExtendedMetaData(QStringLiteral("test"), 5); QVERIFY(c == c2); QMap mdm; mdm.insert(QOrganizerCollection::KeyName, QStringLiteral("test2")); c.setMetaData(mdm); QVERIFY(c != c2); c2.setMetaData(mdm); QVERIFY(c == c2); c2 = QOrganizerCollection(); QVERIFY(c != c2); c2 = c; QVERIFY(c == c2); c.setId(makeId(QStringLiteral("a"), 1)); QVERIFY(c != c2); c2.setId(makeId(QStringLiteral("a"), 1)); QVERIFY(c == c2); c.setId(makeId(QStringLiteral("b"), 1)); QVERIFY(c != c2); c2.setId(c.id()); QVERIFY(c == c2); c.setId(makeId(QStringLiteral("b"), 2)); } void tst_QOrganizerCollection::idLessThan() { // TODO: review tests QOrganizerCollectionId id1(makeId("a", 1)); QOrganizerCollectionId id2(makeId("a", 1)); QVERIFY(!(id1 < id2)); QVERIFY(!(id2 < id1)); QVERIFY(id1 == id2); QOrganizerCollectionId id3(makeId("a", 2)); QOrganizerCollectionId id4(makeId("b", 1)); QOrganizerCollectionId id5(makeId(QString(), 2)); QVERIFY(id1 < id3); QVERIFY(!(id3 < id1)); QVERIFY(id1 < id4); QVERIFY(!(id4 < id1)); QVERIFY(id3 < id4); QVERIFY(!(id4 < id3)); QVERIFY(id5 < id1); QVERIFY(!(id1 < id5)); } void tst_QOrganizerCollection::idHash() { // TODO: review tests QOrganizerCollectionId id1(makeId(QString(), 1)); QOrganizerCollectionId id2(makeId(QString(), 1)); QOrganizerCollectionId id3(makeId(QString(), 2)); QOrganizerCollectionId id4(makeId("a", 1)); QVERIFY(qHash(id1) == qHash(id2)); QVERIFY(qHash(id1) != qHash(id3)); // note that the hash function is dependent on the id type // in BasicCollectionLocalId, the hash function ignores the managerUri. QSet set; set.insert(id1); set.insert(id2); set.insert(id3); set.insert(id4); QCOMPARE(set.size(), 3); } void tst_QOrganizerCollection::idStringFunctions() { // TODO: review test QOrganizerCollectionId id1(makeId("a", 1)); QOrganizerCollectionId id2(makeId("a", 1)); QOrganizerCollectionId id3(makeId("b", 1)); QOrganizerCollectionId id4(makeId("a", 2)); QVERIFY(qHash(id1) == qHash(id2)); QVERIFY(qHash(id1) != qHash(id4)); // note that the toString and fromString functions are // engine and id specific. This test merely checks that // the API is hooked up correctly. QVERIFY(id1.toString() == id2.toString()); QVERIFY(id1.toString() != id3.toString()); QVERIFY(id1.toString() != id4.toString()); QVERIFY(id3.toString() != id4.toString()); // this should "work" -- string of the correct format QString prebuiltidstring = QString("qtorganizer") + QString(":") + QString("a") + QString("::") + QString::number(2); QOrganizerCollectionId rebuiltid = QOrganizerCollectionId::fromString(prebuiltidstring); // QVERIFY(rebuiltid == id4); // -- this requires a working backend. // this string has the right format and one parameter, but requires a working backend prebuiltidstring = QString("qtorganizer") + QString(":") + QString("a") + QString(":") + QString("key=value") + QString(":") + QString::number(2); rebuiltid = QOrganizerCollectionId::fromString(prebuiltidstring); // QVERIFY(rebuiltid == id4); // -- this requires a working backend. // this string has the right format and some parameters, but requires a working backend prebuiltidstring = QString("qtorganizer") + QString(":") + QString("a") + QString(":") + QString("key=value&key2=value2") + QString(":") + QString::number(2); rebuiltid = QOrganizerCollectionId::fromString(prebuiltidstring); // QVERIFY(rebuiltid == id4); // -- this requires a working backend. // this string has the right format but misses the value for a parameter prebuiltidstring = QString("qtorganizer") + QString(":") + QString("a") + QString(":") + QString("key=value&key2=") + QString(":") + QString::number(2); rebuiltid = QOrganizerCollectionId::fromString(prebuiltidstring); // QVERIFY(rebuiltid == id4); // -- this requires a working backend. // this string misses a field (the parameters) prebuiltidstring = QString("qtorganizer") + QString(":") + QString("a") + QString(":") + QString::number(2); rebuiltid = QOrganizerCollectionId::fromString(prebuiltidstring); QVERIFY(rebuiltid == QOrganizerCollectionId()); // invalid so should be null. // this string misses two fields (params plus manager uri) prebuiltidstring = QString("qtorganizer") + QString(":") + QString::number(2); rebuiltid = QOrganizerCollectionId::fromString(prebuiltidstring); QVERIFY(rebuiltid == QOrganizerCollectionId()); // invalid so should be null. // this string misses the prefix (qtorganizer) prebuiltidstring = QString("notorganizer") + QString(":") + QString("a") + QString("::") + QString::number(2); rebuiltid = QOrganizerCollectionId::fromString(prebuiltidstring); QVERIFY(rebuiltid == QOrganizerCollectionId()); // invalid so should be null. // this string misses the manager uri prebuiltidstring = QString("notorganizer") + QString(":::") + QString::number(2); rebuiltid = QOrganizerCollectionId::fromString(prebuiltidstring); QVERIFY(rebuiltid == QOrganizerCollectionId()); // invalid so should be null. } void tst_QOrganizerCollection::hash() { // TODO: review tests QOrganizerCollectionId id(makeId("a", 1)); QOrganizerCollection c1; c1.setId(id); c1.setExtendedMetaData("key", "value"); QOrganizerCollection c2; c2.setId(id); c2.setExtendedMetaData("key", "value"); QOrganizerCollection c3; c3.setId(id); c3.setExtendedMetaData("key", "another value"); QOrganizerCollection c4; // no details c4.setId(id); QOrganizerCollection c5; c5.setId(id); c5.setExtendedMetaData("key", "value"); QVERIFY(qHash(c1) == qHash(c2)); QVERIFY(qHash(c1) != qHash(c3)); QVERIFY(qHash(c1) != qHash(c4)); QVERIFY(qHash(c1) == qHash(c5)); } void tst_QOrganizerCollection::datastream() { // collection datastreaming QByteArray buffer; QOrganizerCollection collectionIn; collectionIn.setExtendedMetaData("key", "value"); QOrganizerCollection collectionOut; QOrganizerCollectionId originalId; // first, stream an item with a complete id { QDataStream stream1(&buffer, QIODevice::WriteOnly); QOrganizerManager om("memory"); QVERIFY(om.saveCollection(&collectionIn)); // fill in its ID originalId = collectionIn.id(); stream1 << collectionIn; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> collectionOut; QCOMPARE(collectionOut, collectionIn); // can use QCOMPARE for collections, since no detail keys. } // second, stream an item with an id with the mgr uri set, local id null { QDataStream stream1(&buffer, QIODevice::WriteOnly); collectionIn.setId(QOrganizerCollectionId()); stream1 << collectionIn; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> collectionOut; QCOMPARE(collectionOut, collectionIn); // can use QCOMPARE for collections, since no detail keys. } /* TODO: Review tests // third, stream an item with an id with the mgr uri null, local id set { QDataStream stream1(&buffer, QIODevice::WriteOnly); QOrganizerCollectionId modifiedId = originalId; modifiedId.setManagerUri(QString()); // this will clear the local id! modifiedId.setId(originalId.localId()); // so reset it and make sure nothing bad happens. collectionIn.setId(modifiedId); stream1 << collectionIn; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> collectionOut; QVERIFY(collectionOut.metaData() == collectionIn.metaData()); QVERIFY(collectionOut.id() != collectionIn.id()); // no manager uri of input :. won't be serialized. }*/ // fourth, stream an item with a null id { QDataStream stream1(&buffer, QIODevice::WriteOnly); collectionIn.setId(QOrganizerCollectionId()); stream1 << collectionIn; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> collectionOut; QVERIFY(collectionOut.metaData() == collectionIn.metaData()); QVERIFY(collectionOut.id() == collectionIn.id()); // should both be null ids. } // id datastreaming buffer.clear(); QOrganizerCollectionId inputId; QOrganizerCollectionId outputId; // first, stream the whole id (mgr uri set, local id set) { inputId = originalId; QString serializedId = inputId.toString(); outputId = QOrganizerCollectionId::fromString(serializedId); QCOMPARE(inputId, outputId); inputId = originalId; buffer.clear(); QDataStream stream1(&buffer, QIODevice::WriteOnly); stream1 << inputId; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> outputId; QCOMPARE(inputId, outputId); } /* TODO: review test // second, stream a partial id (mgr uri null, local id set) { inputId.setManagerUri(QString()); inputId.setId(originalId.localId()); buffer.clear(); QDataStream stream1(&buffer, QIODevice::WriteOnly); stream1 << inputId; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> outputId; // because the manager uri is null, we cannot stream it in. QVERIFY(outputId.isNull()); QVERIFY(!inputId.isNull()); } // third, stream a partial id (mgr uri set, local id null). { inputId.setManagerUri(originalId.managerUri()); inputId.setId(QOrganizerCollectionId()); buffer.clear(); QDataStream stream1(&buffer, QIODevice::WriteOnly); stream1 << inputId; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> outputId; QCOMPARE(inputId, outputId); }*/ // fourth, stream a null id { inputId = QOrganizerCollectionId(); QString serializedId = inputId.toString(); outputId = QOrganizerCollectionId::fromString(serializedId); QCOMPARE(inputId, outputId); inputId = QOrganizerCollectionId(); buffer.clear(); QDataStream stream1(&buffer, QIODevice::WriteOnly); stream1 << inputId; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> outputId; QCOMPARE(inputId, outputId); } /* TODO: review test // fifth, stream an id after changing it's manager uri. { inputId.setManagerUri(originalId.managerUri()); inputId.setId(originalId.localId()); inputId.setManagerUri("test manager uri"); // should clear the local id. QVERIFY(inputId.localId() == QOrganizerCollectionId()); buffer.clear(); QDataStream stream1(&buffer, QIODevice::WriteOnly); stream1 << inputId; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> outputId; QCOMPARE(inputId, outputId); } // sixth, stream an id after changing it's manager uri and resetting the local id. // this should cause great problems, because the manager doesn't exist so it shouldn't // be able to deserialize. Make sure it's handled gracefully. { inputId.setManagerUri(originalId.managerUri()); inputId.setManagerUri("test manager uri"); // should clear the local id. inputId.setId(originalId.localId()); buffer.clear(); QDataStream stream1(&buffer, QIODevice::WriteOnly); stream1 << inputId; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> outputId; QVERIFY(outputId.isNull()); } */ } void tst_QOrganizerCollection::traits() { QVERIFY(sizeof(QOrganizerCollection) == sizeof(void *)); QVERIFY(QTypeInfo::isComplex); QVERIFY(!QTypeInfo::isStatic); QVERIFY(!QTypeInfo::isLarge); QVERIFY(!QTypeInfo::isPointer); QVERIFY(!QTypeInfo::isDummy); } void tst_QOrganizerCollection::idTraits() { QVERIFY(sizeof(QOrganizerCollectionId) == sizeof(void *)); QVERIFY(QTypeInfo::isComplex); QVERIFY(!QTypeInfo::isStatic); QVERIFY(!QTypeInfo::isLarge); QVERIFY(!QTypeInfo::isPointer); QVERIFY(!QTypeInfo::isDummy); } void tst_QOrganizerCollection::localIdTraits() { QVERIFY(sizeof(QOrganizerCollectionId) == sizeof(void *)); QVERIFY(QTypeInfo::isComplex); // unlike QContactLocalId (int typedef), we have a ctor QVERIFY(!QTypeInfo::isStatic); QVERIFY(!QTypeInfo::isLarge); QVERIFY(!QTypeInfo::isPointer); QVERIFY(!QTypeInfo::isDummy); } QTEST_MAIN(tst_QOrganizerCollection) #include "tst_qorganizercollection.moc" tests/auto/organizer/qorganizere2e/000077500000000000000000000000001233466112000177435ustar00rootroot00000000000000tests/auto/organizer/qorganizere2e/partitions.json000066400000000000000000000001211233466112000230240ustar00rootroot00000000000000[ { "name" : "com.nokia.mt.User" }, { "name" : "com.nokia.mt.System" } ] tests/auto/organizer/qorganizere2e/qorganizere2e.pro000066400000000000000000000003151233466112000232410ustar00rootroot00000000000000include(../../auto.pri) QT += organizer organizer-private qtHaveModule(jsondb): QT += jsondb SOURCES += tst_qorganizere2e.cpp HEADERS += ../../jsondbprocess.h DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/organizer/qorganizere2e/tst_qorganizere2e.cpp000066400000000000000000000444551233466112000241320ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include "../../jsondbprocess.h" QTORGANIZER_USE_NAMESPACE class tst_QOrganizerE2E : public QObject { Q_OBJECT private slots: void initTestCase(); void cleanupTestCase(); void testMegaItem_data() { addManager(); } void testMegaItem(); void testUnsupportedItemType_data() { addManager(); } void testUnsupportedItemType(); void testInvalidParentId_data() { addManager(); } void testInvalidParentId(); private: void addManager(); QOrganizerItem createItem(QOrganizerItemType::ItemType itemType); QOrganizerItemDetail createDetail(QOrganizerItemDetail::DetailType detailType); // pair for existing items QMap > existingItems; JsonDbProcess jsondbProcess; }; void tst_QOrganizerE2E::initTestCase() { // Start JsonDb daemon if needed if (QOrganizerManager::availableManagers().contains("jsondb")) { QString partitions_json = QFINDTESTDATA("partitions.json"); QVERIFY2(!partitions_json.isEmpty(), "partitions.json file is missing"); QVERIFY2(jsondbProcess.start(partitions_json), "Failed to start JsonDb process"); } // back-up all existing items QStringList availableManagers(QOrganizerManager::availableManagers()); foreach (const QString &manager, availableManagers) { QOrganizerManager organizerManager(manager); QList items(organizerManager.items()); if (items.size() > 0) { existingItems.insert(manager, items); QList itemIds(organizerManager.itemIds()); organizerManager.removeItems(itemIds); } } } void tst_QOrganizerE2E::cleanupTestCase() { // remove all items generated when running tests QStringList availableManagers(QOrganizerManager::availableManagers()); foreach (const QString &manager, availableManagers) { QOrganizerManager organizerManager(manager); organizerManager.removeItems(organizerManager.itemIds()); } // restore all existing items QMap >::iterator i = existingItems.begin(); while (i != existingItems.end()) { QOrganizerManager organizerManager(i.key()); organizerManager.saveItems(&(i.value())); ++i; } if (QOrganizerManager::availableManagers().contains("jsondb")) jsondbProcess.terminate(); } void tst_QOrganizerE2E::testMegaItem() { QFETCH(QString, managerName); QOrganizerManager organizerManager(managerName); QList supportedItemTypes(organizerManager.supportedItemTypes()); foreach (QOrganizerItemType::ItemType itemType, supportedItemTypes) { QOrganizerItem referenceItem = createItem(itemType); QCOMPARE(referenceItem.type(), itemType); QList supportedDetails(organizerManager.supportedItemDetails(itemType)); foreach (QOrganizerItemDetail::DetailType supportedDetail, supportedDetails) { QOrganizerItemDetail detail = createDetail(supportedDetail); if (detail.type() != supportedDetail) continue; QVERIFY(referenceItem.saveDetail(&detail)); } // set parent for occurrence if (itemType == QOrganizerItemType::TypeEventOccurrence || itemType == QOrganizerItemType::TypeTodoOccurrence) { bool parentItemAvailable(false); QList items(organizerManager.items()); foreach (const QOrganizerItem &item, items) { if ((itemType == QOrganizerItemType::TypeEventOccurrence && item.type() == QOrganizerItemType::TypeEvent) || (itemType == QOrganizerItemType::TypeTodoOccurrence && item.type() == QOrganizerItemType::TypeTodo)) { QOrganizerItemParent parent; parent.setOriginalDate(QDate(1996, 9, 24)); parent.setParentId(item.id()); QVERIFY(referenceItem.saveDetail(&parent)); parentItemAvailable = true; break; } } if (!parentItemAvailable) continue; } // so that the referenceItem stays the same QOrganizerItem itemToSave(referenceItem); QVERIFY(organizerManager.saveItem(&itemToSave)); QOrganizerItem itemRead = organizerManager.item(itemToSave.id()); // special details QCOMPARE(itemRead.type(), referenceItem.type()); if (organizerManager.supportedItemDetails(itemType).contains(QOrganizerItemDetail::TypeGuid)) QCOMPARE(itemRead.details(QOrganizerItemDetail::TypeGuid).size(), 1); if (organizerManager.supportedItemDetails(itemType).contains(QOrganizerItemDetail::TypeTimestamp)) QCOMPARE(itemRead.details(QOrganizerItemDetail::TypeTimestamp).size(), 1); QCOMPARE(itemRead.details(QOrganizerItemDetail::TypeReminder).size(), 0); // all details should saved and loaded correctly foreach (const QOrganizerItemDetail &detail, referenceItem.details()) QCOMPARE(itemRead.detail(detail.type()), detail); } } void tst_QOrganizerE2E::testUnsupportedItemType() { QFETCH(QString, managerName); QOrganizerManager organizerManager(managerName); QList supportedItemTypes(organizerManager.supportedItemTypes()); if (!supportedItemTypes.contains(QOrganizerItemType::TypeEvent)) { QOrganizerItem event = createItem(QOrganizerItemType::TypeEvent); QVERIFY(!organizerManager.saveItem(&event)); } if (!supportedItemTypes.contains(QOrganizerItemType::TypeEventOccurrence)) { QOrganizerItem eventOccurence = createItem(QOrganizerItemType::TypeEventOccurrence); QVERIFY(!organizerManager.saveItem(&eventOccurence)); } if (!supportedItemTypes.contains(QOrganizerItemType::TypeTodo)) { QOrganizerItem todo = createItem(QOrganizerItemType::TypeTodo); QVERIFY(!organizerManager.saveItem(&todo)); } if (!supportedItemTypes.contains(QOrganizerItemType::TypeTodoOccurrence)) { QOrganizerItem todoOccurence = createItem(QOrganizerItemType::TypeTodoOccurrence); QVERIFY(!organizerManager.saveItem(&todoOccurence)); } if (!supportedItemTypes.contains(QOrganizerItemType::TypeJournal)) { QOrganizerItem journal = createItem(QOrganizerItemType::TypeJournal); QVERIFY(!organizerManager.saveItem(&journal)); } if (!supportedItemTypes.contains(QOrganizerItemType::TypeNote)) { QOrganizerItem note = createItem(QOrganizerItemType::TypeNote); QVERIFY(!organizerManager.saveItem(¬e)); } } void tst_QOrganizerE2E::testInvalidParentId() { QFETCH(QString, managerName); QOrganizerManager organizerManager(managerName); QList supportedItemTypes(organizerManager.supportedItemTypes()); foreach (QOrganizerItemType::ItemType itemType, supportedItemTypes) { if (itemType == QOrganizerItemType::TypeEventOccurrence || itemType == QOrganizerItemType::TypeTodoOccurrence) { QOrganizerItem item = createItem(itemType); QCOMPARE(item.type(), itemType); QOrganizerItemParent parent; parent.setParentId(QOrganizerItemId()); QVERIFY(item.saveDetail(&parent)); QVERIFY(!organizerManager.saveItem(&item)); } } } void tst_QOrganizerE2E::addManager() { QTest::addColumn("managerName"); QStringList availableManagers = QOrganizerManager::availableManagers(); availableManagers.removeAll("invalid"); availableManagers.removeAll("skeleton"); foreach (const QString &manager, availableManagers) QTest::newRow(manager.toLatin1().constData()) << manager; } QOrganizerItem tst_QOrganizerE2E::createItem(QOrganizerItemType::ItemType itemType) { if (itemType == QOrganizerItemType::TypeEvent) return QOrganizerEvent(); else if (itemType == QOrganizerItemType::TypeEventOccurrence) return QOrganizerEventOccurrence(); else if (itemType == QOrganizerItemType::TypeTodo) return QOrganizerTodo(); else if (itemType == QOrganizerItemType::TypeTodoOccurrence) return QOrganizerTodoOccurrence(); else if (itemType == QOrganizerItemType::TypeJournal) return QOrganizerJournal(); else if (itemType == QOrganizerItemType::TypeNote) return QOrganizerNote(); return QOrganizerItem(); } QOrganizerItemDetail tst_QOrganizerE2E::createDetail(QOrganizerItemDetail::DetailType detailType) { if (detailType == QOrganizerItemDetail::TypeEventTime) { QOrganizerEventTime eventTime; eventTime.setAllDay(false); eventTime.setStartDateTime(QDateTime::fromString(QStringLiteral("1991-08-25T20:57:08+00:00"), Qt::ISODate)); eventTime.setEndDateTime(QDateTime::fromString(QStringLiteral("1995-05-20T11:22:33+02:00"), Qt::ISODate)); return eventTime; } else if (detailType == QOrganizerItemDetail::TypeJournalTime) { QOrganizerJournalTime journalTime; journalTime.setEntryDateTime(QDateTime::fromString(QStringLiteral("1991-08-25T20:57:08+00:00"), Qt::ISODate)); return journalTime; } else if (detailType == QOrganizerItemDetail::TypeTodoTime) { QOrganizerTodoTime todoTime; todoTime.setAllDay(true); todoTime.setStartDateTime(QDateTime::fromString(QStringLiteral("1991-08-25T20:57:08+00:00"), Qt::ISODate)); todoTime.setDueDateTime(QDateTime::fromString(QStringLiteral("1995-05-20T11:22:33+02:00"), Qt::ISODate)); return todoTime; } else if (detailType == QOrganizerItemDetail::TypeTodoProgress) { QOrganizerTodoProgress todoProgress; todoProgress.setFinishedDateTime(QDateTime::fromString(QStringLiteral("1995-05-20T11:22:33+02:00"), Qt::ISODate)); todoProgress.setPercentageComplete(64); todoProgress.setStatus(QOrganizerTodoProgress::StatusInProgress); return todoProgress; } else if (detailType == QOrganizerItemDetail::TypeReminder) { // do nothing, because noboday should directly use this } else if (detailType == QOrganizerItemDetail::TypeAudibleReminder) { QOrganizerItemAudibleReminder audibleReminder; audibleReminder.setDataUrl(QUrl::fromLocalFile(QStringLiteral("some_random_path"))); audibleReminder.setRepetition(6, 4); audibleReminder.setSecondsBeforeStart(1989); return audibleReminder; } else if (detailType == QOrganizerItemDetail::TypeVisualReminder) { QOrganizerItemVisualReminder visualReminder; visualReminder.setDataUrl(QUrl::fromLocalFile(QStringLiteral("yet_another_path"))); visualReminder.setMessage(QStringLiteral("Qt!!")); visualReminder.setRepetition(6, 4); visualReminder.setSecondsBeforeStart(1989); return visualReminder; } else if (detailType == QOrganizerItemDetail::TypeEmailReminder) { QOrganizerItemEmailReminder emailReminder; emailReminder.setContents(QStringLiteral("Qt Rocks!!"), QStringLiteral("Qt - Cross-platform application and UI framework"), QVariantList() << QVariant(QStringLiteral("Code once")) << QVariant(QStringLiteral("Create more")) << QVariant(QStringLiteral("Deploy everywhere"))); emailReminder.setRecipients(QStringList() << QStringLiteral("Berlin") << QStringLiteral("Brisbane") << QStringLiteral("Oslo") << QStringLiteral("Tampere")); emailReminder.setRepetition(6, 4); emailReminder.setSecondsBeforeStart(1989); return emailReminder; } else if (detailType == QOrganizerItemDetail::TypeComment) { QOrganizerItemComment comment; comment.setComment(QStringLiteral("Qt Everywhere!")); return comment; } else if (detailType == QOrganizerItemDetail::TypeDescription) { QOrganizerItemDescription description; description.setDescription(QStringLiteral("Qt is cute!")); return description; } else if (detailType == QOrganizerItemDetail::TypeDisplayLabel) { QOrganizerItemDisplayLabel displayLabel; displayLabel.setLabel(QStringLiteral("Qt - Cross-platform application and UI framework")); return displayLabel; } else if (detailType == QOrganizerItemDetail::TypeGuid) { // do nothing, because it should be set by the back-end engine } else if (detailType == QOrganizerItemDetail::TypeLocation) { QOrganizerItemLocation location; location.setLabel(QStringLiteral("In the middle of nowhere")); location.setLatitude(19.84); location.setLongitude(6.4); return location; } else if (detailType == QOrganizerItemDetail::TypeParent) { // do nothing, because it's handled specially } else if (detailType == QOrganizerItemDetail::TypePriority) { QOrganizerItemPriority priority; priority.setPriority(QOrganizerItemPriority::HighPriority); return priority; } else if (detailType == QOrganizerItemDetail::TypeRecurrence) { QOrganizerItemRecurrence recurrence; recurrence.setRecurrenceDates(QSet() << QDate(2005, 6, 28) << QDate(2005, 12, 19) << QDate(2006, 10, 4) << QDate(2007, 5, 30) << QDate(2008, 5, 6) << QDate(2009, 3, 3) << QDate(2009, 12, 1) << QDate(2010, 9, 21)); QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Yearly); recurrenceRule.setInterval(1); recurrenceRule.setLimit(QOrganizerRecurrenceRule::NoLimit); recurrenceRule.setMonthsOfYear(QSet() << QOrganizerRecurrenceRule::September); recurrenceRule.setDaysOfMonth(QSet() << 24); recurrence.setRecurrenceRules(QSet() << recurrenceRule); recurrence.setExceptionDates(QSet() << QDate(2008, 9, 24) << QDate(2009, 9, 24) << QDate(2010, 9, 24)); QOrganizerRecurrenceRule exceptionRule; exceptionRule.setFrequency(QOrganizerRecurrenceRule::Yearly); exceptionRule.setInterval(2); exceptionRule.setLimit(QOrganizerRecurrenceRule::NoLimit); exceptionRule.setMonthsOfYear(QSet() << QOrganizerRecurrenceRule::September); exceptionRule.setDaysOfMonth(QSet() << 24); recurrence.setExceptionRules(QSet() << exceptionRule); return recurrence; } else if (detailType == QOrganizerItemDetail::TypeTimestamp) { // do nothing, because it should be maintained by the back-end engine } else if (detailType == QOrganizerItemDetail::TypeItemType) { // do nothing, because it should not be changed } else if (detailType == QOrganizerItemDetail::TypeTag) { QOrganizerItemTag tag; tag.setTag(QStringLiteral("Qt Open Governance")); } else if (detailType == QOrganizerItemDetail::TypeExtendedDetail) { QOrganizerItemExtendedDetail extendedDetail; extendedDetail.setName(QStringLiteral("My-Stupid-Extended-Detail")); extendedDetail.setData(QVariantList() << QVariant(QStringLiteral("Code once")) << QVariant(QStringLiteral("Create more")) << QVariant(QStringLiteral("Deploy everywhere"))); return extendedDetail; } else if (detailType == QOrganizerItemDetail::TypeEventAttendee) { QOrganizerEventAttendee attendee; attendee.setName(QStringLiteral("people")); attendee.setAttendeeId(QStringLiteral("123456")); attendee.setEmailAddress(QStringLiteral("people@nokia.com")); attendee.setParticipationRole(QOrganizerEventAttendee::RoleRequiredParticipant); attendee.setParticipationStatus(QOrganizerEventAttendee::StatusAccepted); return attendee; } else if (detailType == QOrganizerItemDetail::TypeVersion) { // do nothing, because it should be maintained by the back-end engine } return QOrganizerItemDetail(); } QTEST_MAIN(tst_QOrganizerE2E) #include "tst_qorganizere2e.moc" tests/auto/organizer/qorganizeritem/000077500000000000000000000000001233466112000202265ustar00rootroot00000000000000tests/auto/organizer/qorganizeritem/qorganizeritem.pro000066400000000000000000000001671233466112000240140ustar00rootroot00000000000000include(../../auto.pri) QT += organizer SOURCES += tst_qorganizeritem.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/organizer/qorganizeritem/tst_qorganizeritem.cpp000066400000000000000000001431141233466112000246700ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include //TESTED_COMPONENT=src/organizer QTORGANIZER_USE_NAMESPACE class tst_QOrganizerItem: public QObject { Q_OBJECT public: tst_QOrganizerItem(); virtual ~tst_QOrganizerItem(); private slots: void details(); void displayLabel(); void description(); void comments(); void tags(); void type(); void emptiness(); void idLessThan(); void idHash(); void idStringFunctions(); void hash(); void datastream(); void traits(); void idTraits(); void localIdTraits(); void debugOutput(); void event(); void todo(); void journal(); void note(); void eventOccurrence(); void todoOccurrence(); void testDebugStreamOut(); void itemsCompare(); }; tst_QOrganizerItem::tst_QOrganizerItem() { } tst_QOrganizerItem::~tst_QOrganizerItem() { } void tst_QOrganizerItem::details() { // Check that detail keys are unique, regardless of order of initialisation // First, construct the detail first, then the contact QOrganizerItemComment note; note.setComment("Example Note"); QOrganizerItem keyTest; QVERIFY(keyTest.saveDetail(¬e)); QList allDetails = keyTest.details(); QList detailKeys; foreach (const QOrganizerItemDetail& det, allDetails) { int currKey = det.key(); QVERIFY(!detailKeys.contains(currKey)); detailKeys.append(currKey); } // Second, construct the detail after the contact has been constructed QOrganizerItemLocation adr; adr.setLabel("this is a test address"); QVERIFY(keyTest.saveDetail(&adr)); allDetails = keyTest.details(); detailKeys.clear(); foreach (const QOrganizerItemDetail& det, allDetails) { int currKey = det.key(); QVERIFY(!detailKeys.contains(currKey)); detailKeys.append(currKey); } // now test for default construction sanity QOrganizerItem oi; // Test there are no details (apart from type) by default QVERIFY(oi.isEmpty() == true); QCOMPARE(oi.details().count(), 1); QCOMPARE(oi.details(QOrganizerItemDetail::TypeLocation).count(), 0); QCOMPARE(oi.details(QOrganizerItemDetail::TypeLocation).count(), 0); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QCOMPARE(oi.details(QOrganizerItemDetail::TypeLocation).count(), 0); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QCOMPARE(oi.details(QOrganizerItemDetail::TypeLocation).count(), 0); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); // Test retrieving the default details (type) QList details = oi.details(); QVERIFY(details.at(0).type() == QOrganizerItemDetail::TypeItemType); QOrganizerItemDetail detail = oi.detail(QOrganizerItemDetail::TypeExtendedDetail); QVERIFY(detail.isEmpty()); // retrieve the first detail using the empty definition name accessor. detail = oi.detail(); QVERIFY(detail == details.at(0)); QVERIFY(oi.details(QOrganizerItemDetail::TypeExtendedDetail).count() == 0); // Add a detail QOrganizerItemLocation a; a.setLabel("another test address label"); QVERIFY(oi.saveDetail(&a)); QVERIFY(oi.isEmpty() == false); QVERIFY(oi.details().count() == 2); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 1); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 1); QVERIFY(!oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QVERIFY(!oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QCOMPARE(static_cast(oi.detail(QOrganizerItemDetail::TypeLocation)), a); // Remove detail QVERIFY(oi.removeDetail(&a)); QCOMPARE(oi.details().count(), 1); QVERIFY(oi.isEmpty() == true); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 0); QCOMPARE(oi.details(QOrganizerItemDetail::TypeLocation).count(), 0); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); // Try removing it again QVERIFY(!oi.removeDetail(&a)); // Add again, and remove a different way (retrieved copy) QVERIFY(oi.saveDetail(&a)); QVERIFY(oi.isEmpty() == false); QCOMPARE(oi.details().count(), 2); QOrganizerItemLocation a2 = oi.detail(QOrganizerItemDetail::TypeLocation); QCOMPARE(a, a2); QVERIFY(oi.removeDetail(&a2)); QCOMPARE(oi.details().count(), 1); QVERIFY(oi.isEmpty() == true); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 0); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 0); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QCOMPARE(a, a2); // Add again again, and remove a different way (base class) QVERIFY(oi.saveDetail(&a)); QCOMPARE(oi.details().count(), 2); QOrganizerItemDetail a3 = oi.detail(QOrganizerItemDetail::TypeLocation); QVERIFY(a == a3); QVERIFY(oi.removeDetail(&a3)); QCOMPARE(oi.details().count(), 1); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 0); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 0); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QVERIFY(a == a3); // now we want to add multiple details of the same type, and test that retrieval works correctly. a2 = QOrganizerItemLocation(); a2.setLabel("22222"); a2.setValue(100, QVariant("22222-2")); // non-existing field oi.saveDetail(&a); oi.saveDetail(&a2); QCOMPARE(oi.details().count(), 3); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 2); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 2); QVERIFY(!oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QVERIFY(!oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QCOMPARE(static_cast(oi.detail(QOrganizerItemDetail::TypeLocation)), a); QVERIFY(oi.removeDetail(&a2)); // Now try adding a detail to multiple contacts QOrganizerItem oi2; QVERIFY(oi2.isEmpty() == true); QVERIFY(oi.saveDetail(&a)); QVERIFY(oi2.saveDetail(&a)); QVERIFY(oi2.isEmpty() == false); QCOMPARE(oi.details().count(), 2); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 1); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 1); QVERIFY(!oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QVERIFY(!oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QCOMPARE(static_cast(oi.detail(QOrganizerItemDetail::TypeLocation)), a); QCOMPARE(oi2.details().count(), 2); QVERIFY(oi2.details(QOrganizerItemDetail::TypeLocation).count() == 1); QVERIFY(oi2.details(QOrganizerItemDetail::TypeLocation).count() == 1); QVERIFY(!oi2.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QVERIFY(!oi2.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QCOMPARE(static_cast(oi2.detail(QOrganizerItemDetail::TypeLocation)), a); // Now try removing it from one QVERIFY(oi.removeDetail(&a)); // Make sure it's gone from the first contact QVERIFY(oi.isEmpty() == true); QCOMPARE(oi.details().count(), 1); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 0); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 0); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); // but not the second QVERIFY(oi2.isEmpty() == false); QCOMPARE(oi2.details().count(), 2); QVERIFY(oi2.details(QOrganizerItemDetail::TypeLocation).count() == 1); QVERIFY(oi2.details(QOrganizerItemDetail::TypeLocation).count() == 1); QVERIFY(!oi2.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QVERIFY(!oi2.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QCOMPARE(static_cast(oi2.detail(QOrganizerItemDetail::TypeLocation)), a); // Now remove it from the second as well QVERIFY(oi2.removeDetail(&a)); // Make sure it's gone from both QCOMPARE(oi.details().count(), 1); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 0); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 0); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QCOMPARE(oi2.details().count(), 1); QVERIFY(oi2.details(QOrganizerItemDetail::TypeLocation).count() == 0); QVERIFY(oi2.details(QOrganizerItemDetail::TypeLocation).count() == 0); QVERIFY(oi2.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QVERIFY(oi2.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); // add a, add b, remove a, add a, remove b, remove a QVERIFY(oi.saveDetail(&a)); QVERIFY(oi2.saveDetail(&a)); QVERIFY(oi.removeDetail(&a)); QVERIFY(oi.saveDetail(&a)); QVERIFY(oi2.removeDetail(&a)); QVERIFY(oi.removeDetail(&a)); // Now add a detail with the same values twice QOrganizerItemLocation one; QOrganizerItemLocation two; one.setLabel("12345"); two.setLabel("12345"); // add it once QVERIFY(oi.saveDetail(&one)); QCOMPARE(oi.details().count(), 2); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 1); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 1); QVERIFY(!oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QVERIFY(!oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QCOMPARE(static_cast(oi.detail(QOrganizerItemDetail::TypeLocation)), one); // add it twice QVERIFY(oi.saveDetail(&two)); QCOMPARE(oi.details().count(), 3); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 2); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 2); QVERIFY(!oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QVERIFY(!oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QCOMPARE(static_cast(oi.detail(QOrganizerItemDetail::TypeLocation)), one); QCOMPARE(static_cast(oi.details(QOrganizerItemDetail::TypeLocation)[0]), one); QCOMPARE(static_cast(oi.details(QOrganizerItemDetail::TypeLocation)[1]), two); // Remove it once QVERIFY(oi.removeDetail(&one)); QCOMPARE(oi.details().count(), 2); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 1); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 1); QVERIFY(!oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QVERIFY(!oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QCOMPARE(static_cast(oi.detail(QOrganizerItemDetail::TypeLocation)), two); // Remove it twice QVERIFY(oi.removeDetail(&two)); QCOMPARE(oi.details().count(), 1); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 0); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 0); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation).isEmpty()); // Null pointer tests QVERIFY(oi.saveDetail(0) == false); QVERIFY(oi.removeDetail(0) == false); // Reference tests... QOrganizerItemDetail& ref = one; QVERIFY(oi.saveDetail(&one)); QVERIFY(ref == one); one.setLabel("56678"); QVERIFY(oi.saveDetail(&one)); QVERIFY(ref == one); // Retrieve the detail again and modify it QOrganizerItemLocation three = oi.detail(QOrganizerItemDetail::TypeLocation); QVERIFY(ref == three); QVERIFY(one == three); three.setLabel("542343"); QVERIFY(oi.saveDetail(&three)); // Now see if we got any updates to ref/one QVERIFY(ref == one); QVERIFY(ref != three); // test saving of a detail with an empty field. QOrganizerItemLocation four; four.setLabel(""); oi.saveDetail(&four); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).count() == 2); QVERIFY(!four.values().isEmpty()); // an empty qstring is not invalid; make sure it exists in the detail. // ensure that clearing a contact's details works correctly QOrganizerItemPriority priorityDetail; priorityDetail.setPriority(QOrganizerItemPriority::VeryHighPriority); oi.saveDetail(&priorityDetail); QCOMPARE(oi.detail(QOrganizerItemDetail::TypePriority).value(QOrganizerItemPriority::FieldPriority).toInt(), static_cast(QOrganizerItemPriority::VeryHighPriority)); QVERIFY(oi.details().size() > 0); QVERIFY(!oi.isEmpty()); QOrganizerItemId oldId = oi.id(); oi.clearDetails(); QCOMPARE(oi.details().size(), 1); // always has an item type. QCOMPARE(oi.detail(QOrganizerItemDetail::TypePriority).value(QOrganizerItemPriority::FieldPriority).toString(), QString()); QVERIFY(oi.isEmpty()); QCOMPARE(oi.id(), oldId); // id shouldn't change. } void tst_QOrganizerItem::displayLabel() { QOrganizerItem oi; QString label = oi.displayLabel(); QVERIFY(label.isEmpty()); oi.setDisplayLabel("Test Event Display Label"); QVERIFY(oi.displayLabel() == QString("Test Event Display Label")); /* Remove the detail via removeDetail */ QOrganizerItemDisplayLabel old; QCOMPARE(oi.details().count(), 2); // type + label QVERIFY(!oi.removeDetail(&old)); // should fail. QVERIFY(oi.isEmpty() == false); QVERIFY(oi.details().count() == 2); // it should not be removed! QOrganizerItemDisplayLabel displayLabel; displayLabel.setLabel("test label"); oi.setDisplayLabel(displayLabel.label()); QVERIFY(oi.displayLabel() == displayLabel.label()); /* Test self assign */ oi.operator =(oi); QVERIFY(oi.details().count() == 2); QVERIFY(oi.isEmpty() == false); } void tst_QOrganizerItem::description() { QOrganizerItem oi; QString description = oi.description(); QVERIFY(description.isEmpty()); oi.setDescription("Test Event Description"); QVERIFY(oi.description() == QString("Test Event Description")); /* Remove the detail via removeDetail */ QOrganizerItemDescription old; QCOMPARE(oi.details().count(), 2); // type + description QVERIFY(!oi.removeDetail(&old)); // should fail. QVERIFY(oi.isEmpty() == false); QVERIFY(oi.details().count() == 2); // it should not be removed! QOrganizerItemDescription descr; descr.setDescription("test description"); oi.setDescription(descr.description()); QVERIFY(oi.description() == descr.description()); /* Test self assign */ oi.operator =(oi); QVERIFY(oi.details().count() == 2); QVERIFY(oi.isEmpty() == false); } void tst_QOrganizerItem::comments() { QOrganizerItem oi; QStringList comments = oi.comments(); QVERIFY(comments.isEmpty()); oi.addComment("test comment"); QCOMPARE(oi.comments().size(), 1); QVERIFY(oi.details().count() == 2); QVERIFY(oi.comments().at(0) == QString("test comment")); oi.addComment("another test comment"); QCOMPARE(oi.comments().size(), 2); QVERIFY(oi.details().count() == 3); QVERIFY(oi.comments().contains(QString("test comment"))); QVERIFY(oi.comments().contains(QString("another test comment"))); oi.clearComments(); QVERIFY(oi.comments().size() == 0); QVERIFY(oi.details().count() == 1); // should have a type detail left. QOrganizerItemComment comment; comment.setComment("yet another test comment"); oi.saveDetail(&comment); QCOMPARE(oi.comments().size(), 1); QVERIFY(oi.details().count() == 2); QVERIFY(oi.comments().at(0) == QString("yet another test comment")); oi.removeDetail(&comment); QVERIFY(oi.comments().size() == 0); QVERIFY(oi.details().count() == 1); // should have a type detail left. oi.clearComments(); oi.setComments(QStringList() << "comment 1" << "comment 2"); QVERIFY(oi.comments().size() == 2); QVERIFY(oi.details().count() == 3); /* Test self assign */ oi.operator =(oi); QVERIFY(oi.details().count() == 3); QVERIFY(oi.isEmpty() == false); } void tst_QOrganizerItem::tags() { QOrganizerItem item; QVERIFY(item.tags().isEmpty()); item.addTag("tag 1"); QStringList tags; tags.append("tag 1"); QCOMPARE(item.tags(), tags); QList tagDetails = item.details(QOrganizerItemDetail::TypeTag); QCOMPARE(tagDetails.size(), 1); QCOMPARE(tagDetails.first().value(QOrganizerItemTag::FieldTag).toString(), QString(QStringLiteral("tag 1"))); item.clearTags(); QVERIFY(item.tags().isEmpty()); QVERIFY(item.details(QOrganizerItemDetail::TypeTag).isEmpty()); tags.append("tag 2"); // tags is now "tag 1", "tag 2" item.setTags(tags); QCOMPARE(item.tags(), tags); tagDetails = item.details(QOrganizerItemDetail::TypeTag); QCOMPARE(tagDetails.size(), 2); QCOMPARE(tagDetails.at(0).value(QOrganizerItemTag::FieldTag).toString(), QString(QStringLiteral("tag 1"))); QCOMPARE(tagDetails.at(1).value(QOrganizerItemTag::FieldTag).toString(), QString(QStringLiteral("tag 2"))); } void tst_QOrganizerItem::debugOutput() { QOrganizerCollection c; QOrganizerItem item; QOrganizerItemDetail d; // Test that these can be used as the first argument to qDebug() qDebug() << item.id(); qDebug() << item.collectionId(); qDebug() << item; qDebug() << d; qDebug() << c; // And that other things can come after them (return type) qDebug() << item.id() << "id"; qDebug() << item.collectionId() << "collection id"; qDebug() << item << "item"; qDebug() << d << "detail"; qDebug() << c << "collection"; // And for kicks, that other things can come first qDebug() << " " << item.id(); qDebug() << " " << item.collectionId(); qDebug() << " " << item; qDebug() << " " << d; qDebug() << " " << c; } void tst_QOrganizerItem::type() { QOrganizerItem oi; QVERIFY(oi.isEmpty() == true); // ensure that the default type is the QOrganizerItemType::TypeUnknown type QVERIFY(oi.type() == QOrganizerItemType::TypeUndefined); // now set it to be an event via the type mutator, and test that it works QOrganizerItemType eventType; eventType.setType(QOrganizerItemType::TypeEvent); oi.setType(eventType.type()); QVERIFY(oi.type() == QOrganizerItemType::TypeEvent); // set it back to a note, via the string mutator oi.setType(QOrganizerItemType::TypeNote); QVERIFY(oi.type() == QOrganizerItemType::TypeNote); QVERIFY(oi.isEmpty() == true); // type doesn't affect emptiness } void tst_QOrganizerItem::emptiness() { QOrganizerItem oi; QVERIFY(oi.isEmpty() == true); oi.setType(QOrganizerItemType::TypeNote); QVERIFY(oi.type() == QOrganizerItemType::TypeNote); QVERIFY(oi.isEmpty() == true); // type doesn't affect emptiness } class BasicItemLocalId : public QOrganizerItemEngineId { public: BasicItemLocalId(const QString& managerUri, uint id) : m_managerUri(managerUri), m_id(id) {} bool isEqualTo(const QOrganizerItemEngineId* other) const { if (m_managerUri == static_cast(other)->m_managerUri) return m_id == static_cast(other)->m_id; return false; } bool isLessThan(const QOrganizerItemEngineId* other) const { if (m_managerUri == static_cast(other)->m_managerUri) return m_id < static_cast(other)->m_id; return m_managerUri < static_cast(other)->m_managerUri; } QString managerUri() const { static const QString uri(QStringLiteral("qtorganizer:basic:")); return uri; } QOrganizerItemEngineId* clone() const { BasicItemLocalId* cloned = new BasicItemLocalId(m_managerUri, m_id); return cloned; } QDebug& debugStreamOut(QDebug& dbg) const { return dbg << m_managerUri << m_id; } QString toString() const { return m_managerUri + QString("::") + QString::number(m_id); } uint hash() const { return m_id; } private: QString m_managerUri; uint m_id; }; QOrganizerItemId makeId(const QString& managerUri, uint id) { return QOrganizerItemId(new BasicItemLocalId(managerUri, id)); } void tst_QOrganizerItem::idLessThan() { // TODO: review test QOrganizerItemId id1(makeId("a", 1)); QOrganizerItemId id2(makeId("a", 1)); QVERIFY(!(id1 < id2)); QVERIFY(!(id2 < id1)); QOrganizerItemId id3(makeId("a", 2)); QOrganizerItemId id4(makeId("b", 1)); QOrganizerItemId id5(makeId(QString(), 2)); // no Uri specified. QVERIFY(id1 < id3); QVERIFY(!(id3 < id1)); QVERIFY(id1 < id4); QVERIFY(!(id4 < id1)); QVERIFY(id3 < id4); QVERIFY(!(id4 < id3)); QVERIFY(id5 < id1); QVERIFY(!(id1 < id5)); } void tst_QOrganizerItem::idHash() { // TODO: review test QOrganizerItemId id1(makeId("a", 1)); QOrganizerItemId id2(makeId("a", 1)); QOrganizerItemId id3(makeId("b", 1)); QOrganizerItemId id4(makeId("a", 2)); QVERIFY(qHash(id1) == qHash(id2)); QVERIFY(qHash(id1) != qHash(id4)); // note that the hash function is dependent on the id type // in BasicCollectionLocalId, the hash function ignores the managerUri. QSet set; set.insert(id1); set.insert(id2); set.insert(id3); set.insert(id4); QCOMPARE(set.size(), 3); } void tst_QOrganizerItem::idStringFunctions() { // TODO: review test QOrganizerItemId id1(makeId("a", 1)); QOrganizerItemId id2(makeId("a", 1)); QOrganizerItemId id3(makeId("b", 1)); QOrganizerItemId id4(makeId("a", 2)); QVERIFY(qHash(id1) == qHash(id2)); QVERIFY(qHash(id1) != qHash(id4)); // note that the toString and fromString functions are // engine and id specific. This test merely checks that // the API is hooked up correctly. QVERIFY(id1.toString() == id2.toString()); QVERIFY(id1.toString() != id3.toString()); QVERIFY(id1.toString() != id4.toString()); QVERIFY(id3.toString() != id4.toString()); // this should "work" -- string of the correct format QString prebuiltidstring = QString("qtorganizer") + QString(":") + QString("a") + QString("::") + QString::number(2); QOrganizerItemId rebuiltid = QOrganizerItemId::fromString(prebuiltidstring); // QVERIFY(rebuiltid == id4); // -- this requires a working backend. // this string has the right format and one parameter, but requires a working backend prebuiltidstring = QString("qtorganizer") + QString(":") + QString("a") + QString(":") + QString("key=value") + QString(":") + QString::number(2); rebuiltid = QOrganizerItemId::fromString(prebuiltidstring); // QVERIFY(rebuiltid == id4); // -- this requires a working backend. // this string has the right format and some parameters, but requires a working backend prebuiltidstring = QString("qtorganizer") + QString(":") + QString("a") + QString(":") + QString("key=value&key2=value2") + QString(":") + QString::number(2); rebuiltid = QOrganizerItemId::fromString(prebuiltidstring); // QVERIFY(rebuiltid == id4); // -- this requires a working backend. // this string has the right format but misses the value for a parameter prebuiltidstring = QString("qtorganizer") + QString(":") + QString("a") + QString(":") + QString("key=value&key2=") + QString(":") + QString::number(2); rebuiltid = QOrganizerItemId::fromString(prebuiltidstring); // QVERIFY(rebuiltid == id4); // -- this requires a working backend. // this string misses a field (the parameters) prebuiltidstring = QString("qtorganizer") + QString(":") + QString("a") + QString(":") + QString::number(2); rebuiltid = QOrganizerItemId::fromString(prebuiltidstring); QVERIFY(rebuiltid == QOrganizerItemId()); // invalid so should be null. // this string misses two fields (params plus manager uri) prebuiltidstring = QString("qtorganizer") + QString(":") + QString::number(2); rebuiltid = QOrganizerItemId::fromString(prebuiltidstring); QVERIFY(rebuiltid == QOrganizerItemId()); // invalid so should be null. // this string misses the prefix (qtorganizer) prebuiltidstring = QString("notorganizer") + QString(":") + QString("a") + QString("::") + QString::number(2); rebuiltid = QOrganizerItemId::fromString(prebuiltidstring); QVERIFY(rebuiltid == QOrganizerItemId()); // invalid so should be null. // this string misses the manager uri prebuiltidstring = QString("notorganizer") + QString(":::") + QString::number(2); rebuiltid = QOrganizerItemId::fromString(prebuiltidstring); QVERIFY(rebuiltid == QOrganizerItemId()); // invalid so should be null. } void tst_QOrganizerItem::hash() { // TODO: review test QOrganizerItemId id(makeId("a", 1)); QOrganizerItem oi1; oi1.setId(id); QOrganizerItemDetail detail1(QOrganizerItemDetail::TypeComment); detail1.setValue(QOrganizerItemComment::FieldComment, "value"); oi1.saveDetail(&detail1); QOrganizerItem oi2; oi2.setId(id); oi2.saveDetail(&detail1); QOrganizerItem oi3; oi3.setId(id); QOrganizerItemDetail detail3(QOrganizerItemDetail::TypeComment); detail3.setValue(QOrganizerItemComment::FieldComment, "another value"); oi3.saveDetail(&detail3); QOrganizerItem oi4; // no details oi4.setId(id); QOrganizerItem oi5; oi5.setId(id); oi5.saveDetail(&detail1);qDebug()<<__LINE__; QVERIFY(qHash(oi1) == qHash(oi2));qDebug()<<__LINE__; QVERIFY(qHash(oi1) != qHash(oi3));qDebug()<<__LINE__; QVERIFY(qHash(oi1) != qHash(oi4));qDebug()<<__LINE__; QVERIFY(qHash(oi1) == qHash(oi5));qDebug()<<__LINE__; } void tst_QOrganizerItem::datastream() { // item datastreaming QByteArray buffer; QOrganizerEvent itemIn; itemIn.addComment("test comment"); QOrganizerItem itemOut; QOrganizerItemId originalId; QOrganizerCollectionId originalCollectionId; // first, stream an item with a complete id { QDataStream stream1(&buffer, QIODevice::WriteOnly); QOrganizerManager om("memory"); QVERIFY(om.saveItem(&itemIn)); // fill in its ID originalId = itemIn.id(); originalCollectionId = itemIn.collectionId(); stream1 << itemIn; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> itemOut; //QCOMPARE(itemOut, itemIn); // can't do QCOMPARE because detail keys get changed. QVERIFY(itemOut.details() == itemIn.details()); QVERIFY(itemOut.id() == itemIn.id()); } // TODO : review tests // second, stream an item with an id with the mgr uri set, local id null /* { QDataStream stream1(&buffer, QIODevice::WriteOnly); QOrganizerItemId modifiedId = originalId; modifiedId.setLocalId(QOrganizerItemLocalId()); itemIn.setId(modifiedId); stream1 << itemIn; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> itemOut; //QCOMPARE(itemOut, itemIn); // can't do QCOMPARE because detail keys get changed. QVERIFY(itemOut.details() == itemIn.details()); QVERIFY(itemOut.id() == itemIn.id()); } // third, stream an item with an id with the mgr uri null, local id set { QDataStream stream1(&buffer, QIODevice::WriteOnly); QOrganizerItemId modifiedId = originalId; modifiedId.setManagerUri(QString()); // this will clear the local id! modifiedId.setLocalId(originalId.localId()); // so reset it and make sure things don't fall over. itemIn.setId(modifiedId); stream1 << itemIn; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> itemOut; //QCOMPARE(itemOut, itemIn); // can't do QCOMPARE because detail keys get changed. QVERIFY(itemOut.details() == itemIn.details()); QVERIFY(itemOut.id() != itemIn.id()); // in this case, with null mgr uri, the id doesn't get serialized. }*/ // fourth, stream an item with null ids { QDataStream stream1(&buffer, QIODevice::WriteOnly); itemIn.setId(QOrganizerItemId()); itemIn.setCollectionId(QOrganizerCollectionId()); stream1 << itemIn; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> itemOut; //QCOMPARE(itemOut, itemIn); // can't do QCOMPARE because detail keys get changed. QVERIFY(itemOut.details() == itemIn.details()); QVERIFY(itemOut.id() == itemIn.id()); } // fifth, stream an item with a collection id { QDataStream stream1(&buffer, QIODevice::WriteOnly); itemIn.setId(QOrganizerItemId()); itemIn.setCollectionId(originalCollectionId); stream1 << itemIn; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> itemOut; //QCOMPARE(itemOut, itemIn); // can't do QCOMPARE because detail keys get changed. QVERIFY(itemOut.details() == itemIn.details()); QVERIFY(itemOut.collectionId() == itemIn.collectionId()); QVERIFY(itemOut.id() == itemIn.id()); } // id datastreaming buffer.clear(); QOrganizerItemId inputId; QOrganizerItemId outputId; // first, stream the whole id (mgr uri set, local id set) { inputId = originalId; QString serializedId = inputId.toString(); outputId = QOrganizerItemId::fromString(serializedId); QCOMPARE(inputId, outputId); inputId = originalId; buffer.clear(); QDataStream stream1(&buffer, QIODevice::WriteOnly); stream1 << inputId; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> outputId; QCOMPARE(inputId, outputId); } // TODO : review tests // second, stream a partial id (mgr uri null, local id set) /* { inputId.setManagerUri(QString()); inputId.setLocalId(originalId.localId()); buffer.clear(); QDataStream stream1(&buffer, QIODevice::WriteOnly); stream1 << inputId; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> outputId; // because the manager uri is null, we cannot stream it in. QVERIFY(outputId.isNull()); QVERIFY(!inputId.isNull()); } // third, stream a partial id (mgr uri set, local id null). { inputId.setManagerUri(originalId.managerUri()); inputId.setLocalId(QOrganizerItemLocalId()); buffer.clear(); QDataStream stream1(&buffer, QIODevice::WriteOnly); stream1 << inputId; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> outputId; QCOMPARE(inputId, outputId); }*/ // fourth, stream a null id { inputId = QOrganizerItemId(); QString serializedId = inputId.toString(); outputId = QOrganizerItemId::fromString(serializedId); QCOMPARE(inputId, outputId); inputId = QOrganizerItemId(); buffer.clear(); QDataStream stream1(&buffer, QIODevice::WriteOnly); stream1 << inputId; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> outputId; QCOMPARE(inputId, outputId); } // TODO : review tests // fifth, stream an id after changing it's manager uri string. /* { inputId.setManagerUri(originalId.managerUri()); inputId.setLocalId(originalId.localId()); inputId.setManagerUri("test manager uri"); // should clear the local id. QVERIFY(inputId.localId() == QOrganizerItemLocalId()); buffer.clear(); QDataStream stream1(&buffer, QIODevice::WriteOnly); stream1 << inputId; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> outputId; QCOMPARE(inputId, outputId); } // sixth, stream an id after changing it's manager uri string, and resetting the local id. // this should cause great problems, because the manager doesn't exist so it shouldn't // be able to deserialize. Make sure it's handled gracefully. { inputId.setManagerUri(originalId.managerUri()); inputId.setManagerUri("test manager uri"); // should clear the local id. inputId.setLocalId(originalId.localId()); buffer.clear(); QDataStream stream1(&buffer, QIODevice::WriteOnly); stream1 << inputId; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); stream2 >> outputId; QVERIFY(outputId.isNull()); }*/ } void tst_QOrganizerItem::traits() { QCOMPARE(sizeof(QOrganizerItem), sizeof(void *)); QTypeInfo ti; QVERIFY(ti.isComplex); QVERIFY(!ti.isStatic); QVERIFY(!ti.isLarge); QVERIFY(!ti.isPointer); QVERIFY(!ti.isDummy); } void tst_QOrganizerItem::idTraits() { QVERIFY(sizeof(QOrganizerItemId) == sizeof(void *)); QTypeInfo ti; QVERIFY(ti.isComplex); QVERIFY(!ti.isStatic); QVERIFY(!ti.isLarge); QVERIFY(!ti.isPointer); QVERIFY(!ti.isDummy); } void tst_QOrganizerItem::localIdTraits() { QVERIFY(sizeof(QOrganizerItemId) == sizeof(void *)); QTypeInfo ti; QVERIFY(ti.isComplex); // unlike QContactLocalId (int typedef), we have a ctor QVERIFY(!ti.isStatic); QVERIFY(!ti.isLarge); QVERIFY(!ti.isPointer); QVERIFY(!ti.isDummy); } void tst_QOrganizerItem::event() { QOrganizerEvent testEvent; QCOMPARE(testEvent.type(), QOrganizerItemType::TypeEvent); testEvent.setLocation("test address"); QCOMPARE(testEvent.location(), QString("test address")); testEvent.setStartDateTime(QDateTime(QDate::currentDate())); QCOMPARE(testEvent.startDateTime(), QDateTime(QDate::currentDate())); testEvent.setEndDateTime(QDateTime(QDate::currentDate().addDays(1))); QCOMPARE(testEvent.endDateTime(), QDateTime(QDate::currentDate().addDays(1))); QVERIFY(!testEvent.isAllDay()); // default to not all day testEvent.setAllDay(true); QVERIFY(testEvent.isAllDay()); testEvent.setPriority(QOrganizerItemPriority::VeryHighPriority); QCOMPARE(testEvent.priority(), QOrganizerItemPriority::VeryHighPriority); testEvent.setPriority(QOrganizerItemPriority::VeryLowPriority); QCOMPARE(testEvent.priority(), QOrganizerItemPriority::VeryLowPriority); QSet rdates; testEvent.setRecurrenceDate(QDate::currentDate()); QCOMPARE(testEvent.recurrenceDates(), QSet() << QDate::currentDate()); rdates << QDate::currentDate() << QDate::currentDate().addDays(3) << QDate::currentDate().addDays(8); testEvent.setRecurrenceDates(rdates); QCOMPARE(testEvent.recurrenceDates(), rdates); QSet exdates; testEvent.setExceptionDate(QDate::currentDate()); QCOMPARE(testEvent.exceptionDates(), QSet() << QDate::currentDate()); exdates << QDate::currentDate().addDays(3); testEvent.setExceptionDates(exdates); QCOMPARE(testEvent.exceptionDates(), exdates); QSet rrules; QOrganizerRecurrenceRule rrule; QVERIFY(rrule.limitType() == QOrganizerRecurrenceRule::NoLimit); QVERIFY(rrule.limitCount() == -1); QVERIFY(rrule.limitDate().isNull()); rrule.setLimit(1); QVERIFY(rrule.limitType() == QOrganizerRecurrenceRule::CountLimit); QVERIFY(rrule.limitCount() == 1); QVERIFY(rrule.limitDate().isNull()); rrule.setLimit(-1); QVERIFY(rrule.limitType() == QOrganizerRecurrenceRule::NoLimit); QVERIFY(rrule.limitCount() == -1); QVERIFY(rrule.limitDate().isNull()); rrule.setLimit(0); QVERIFY(rrule.limitType() == QOrganizerRecurrenceRule::CountLimit); QVERIFY(rrule.limitCount() == 0); QVERIFY(rrule.limitDate().isNull()); rrule.setLimit(-100); QVERIFY(rrule.limitType() == QOrganizerRecurrenceRule::NoLimit); QVERIFY(rrule.limitCount() == -1); QVERIFY(rrule.limitDate().isNull()); rrule.setLimit(QDate()); QVERIFY(rrule.limitType() == QOrganizerRecurrenceRule::NoLimit); QVERIFY(rrule.limitCount() == -1); QVERIFY(rrule.limitDate().isNull()); rrule.setLimit(QDate(2010, 10, 6)); QVERIFY(rrule.limitType() == QOrganizerRecurrenceRule::DateLimit); QVERIFY(rrule.limitCount() == -1); QVERIFY(rrule.limitDate() == QDate(2010, 10, 6)); rrule.setLimit(QDate(2010, 13, 34)); QVERIFY(rrule.limitType() == QOrganizerRecurrenceRule::NoLimit); QVERIFY(rrule.limitCount() == -1); QVERIFY(rrule.limitDate() == QDate()); rrule.setLimit(2); rrule.setFrequency(QOrganizerRecurrenceRule::Daily); rrules << rrule; testEvent.setRecurrenceRule(rrule); QVERIFY(testEvent.recurrenceRules() == rrules); QVERIFY(testEvent.recurrenceRule() == rrule); testEvent.setRecurrenceRule(rrule); QVERIFY(testEvent.recurrenceRules() == rrules); QVERIFY(testEvent.recurrenceRule() == rrule); QSet exrules; QOrganizerRecurrenceRule exrule; exrule.setLimit(1); exrule.setFrequency(QOrganizerRecurrenceRule::Weekly); exrules << exrule; testEvent.setExceptionRules(exrules); QVERIFY(testEvent.exceptionRules() == exrules); QVERIFY(testEvent.exceptionRule() == exrule); testEvent.setExceptionRule(exrule); QVERIFY(testEvent.exceptionRules() == exrules); QVERIFY(testEvent.exceptionRule() == exrule); } void tst_QOrganizerItem::todo() { QOrganizerTodo testTodo; QCOMPARE(testTodo.type(), QOrganizerItemType::TypeTodo); QCOMPARE(testTodo.status(), QOrganizerTodoProgress::StatusNotStarted); testTodo.setStatus(QOrganizerTodoProgress::StatusInProgress); QCOMPARE(testTodo.status(), QOrganizerTodoProgress::StatusInProgress); QCOMPARE(testTodo.progressPercentage(), 0); testTodo.setProgressPercentage(50); QCOMPARE(testTodo.progressPercentage(), 50); testTodo.setStatus(QOrganizerTodoProgress::StatusComplete); QCOMPARE(testTodo.progressPercentage(), 50); // XXX TODO: should this update automatically? QDateTime currentDateTime = QDateTime::currentDateTime(); testTodo.setStartDateTime(currentDateTime); QCOMPARE(testTodo.startDateTime(), currentDateTime); testTodo.setDueDateTime(QDateTime(QDate::currentDate())); QCOMPARE(testTodo.dueDateTime(), QDateTime(QDate::currentDate())); testTodo.setFinishedDateTime(QDateTime(QDate::currentDate().addDays(1))); QCOMPARE(testTodo.finishedDateTime(), QDateTime(QDate::currentDate().addDays(1))); QVERIFY(testTodo.isAllDay() == false); // default should not be all day event. testTodo.setAllDay(true); QVERIFY(testTodo.isAllDay() == true); testTodo.setAllDay(false); QVERIFY(testTodo.isAllDay() == false); testTodo.setPriority(QOrganizerItemPriority::VeryHighPriority); QCOMPARE(testTodo.priority(), QOrganizerItemPriority::VeryHighPriority); testTodo.setPriority(QOrganizerItemPriority::VeryLowPriority); QCOMPARE(testTodo.priority(), QOrganizerItemPriority::VeryLowPriority); QSet rdates; rdates << QDate::currentDate() << QDate::currentDate().addDays(3) << QDate::currentDate().addDays(8); testTodo.setRecurrenceDates(rdates); QCOMPARE(testTodo.recurrenceDates(), rdates); QSet exdates; exdates << QDate::currentDate().addDays(3); testTodo.setExceptionDates(exdates); QCOMPARE(testTodo.exceptionDates(), exdates); QSet rrules; QOrganizerRecurrenceRule rrule; rrule.setLimit(2); rrule.setFrequency(QOrganizerRecurrenceRule::Daily); rrules << rrule; testTodo.setRecurrenceRules(rrules); QVERIFY(testTodo.recurrenceRules() == rrules); QVERIFY(testTodo.recurrenceRule() == rrule); testTodo.setRecurrenceRule(rrule); QVERIFY(testTodo.recurrenceRules() == rrules); QVERIFY(testTodo.recurrenceRule() == rrule); QSet exrules; QOrganizerRecurrenceRule exrule; exrule.setLimit(1); exrule.setFrequency(QOrganizerRecurrenceRule::Weekly); exrules << exrule; testTodo.setExceptionRules(exrules); QVERIFY(testTodo.exceptionRules() == exrules); QVERIFY(testTodo.exceptionRule() == exrule); testTodo.setExceptionRule(exrule); QVERIFY(testTodo.exceptionRules() == exrules); QVERIFY(testTodo.exceptionRule() == exrule); } void tst_QOrganizerItem::journal() { QOrganizerJournal testJournal; QCOMPARE(testJournal.type(), QOrganizerItemType::TypeJournal); QDateTime currDateTime = QDateTime::currentDateTime(); testJournal.setDateTime(currDateTime); QCOMPARE(testJournal.dateTime(), currDateTime); } void tst_QOrganizerItem::note() { QOrganizerNote testNote; QCOMPARE(testNote.type(), QOrganizerItemType::TypeNote); } void tst_QOrganizerItem::eventOccurrence() { QOrganizerEventOccurrence testEventOccurrence; QCOMPARE(testEventOccurrence.type(), QOrganizerItemType::TypeEventOccurrence); testEventOccurrence.setLocation("test address"); QCOMPARE(testEventOccurrence.location(), QString("test address")); testEventOccurrence.setStartDateTime(QDateTime(QDate::currentDate())); QCOMPARE(testEventOccurrence.startDateTime(), QDateTime(QDate::currentDate())); testEventOccurrence.setEndDateTime(QDateTime(QDate::currentDate().addDays(1))); QCOMPARE(testEventOccurrence.endDateTime(), QDateTime(QDate::currentDate().addDays(1))); testEventOccurrence.setPriority(QOrganizerItemPriority::VeryHighPriority); QCOMPARE(testEventOccurrence.priority(), QOrganizerItemPriority::VeryHighPriority); testEventOccurrence.setPriority(QOrganizerItemPriority::VeryLowPriority); QCOMPARE(testEventOccurrence.priority(), QOrganizerItemPriority::VeryLowPriority); // the parent id and original date time must be tested properly in the manager unit test // but we will test the API mutator/accessor functions here. QOrganizerItemId id; testEventOccurrence.setParentId(id); QCOMPARE(testEventOccurrence.parentId(), id); QDateTime originalDateTime = QDateTime::currentDateTime(); testEventOccurrence.setOriginalDate(originalDateTime.date()); QCOMPARE(testEventOccurrence.originalDate(), originalDateTime.date()); } void tst_QOrganizerItem::todoOccurrence() { QOrganizerTodoOccurrence testTodoOccurrence; QCOMPARE(testTodoOccurrence.type(), QOrganizerItemType::TypeTodoOccurrence); QCOMPARE(testTodoOccurrence.status(), QOrganizerTodoProgress::StatusNotStarted); testTodoOccurrence.setStatus(QOrganizerTodoProgress::StatusInProgress); QCOMPARE(testTodoOccurrence.status(), QOrganizerTodoProgress::StatusInProgress); QCOMPARE(testTodoOccurrence.progressPercentage(), 0); testTodoOccurrence.setProgressPercentage(50); QCOMPARE(testTodoOccurrence.progressPercentage(), 50); testTodoOccurrence.setStatus(QOrganizerTodoProgress::StatusComplete); QCOMPARE(testTodoOccurrence.progressPercentage(), 50); // XXX TODO: should this update automatically? testTodoOccurrence.setStartDateTime(QDateTime(QDate::currentDate())); QCOMPARE(testTodoOccurrence.startDateTime(), QDateTime(QDate::currentDate())); testTodoOccurrence.setDueDateTime(QDateTime(QDate::currentDate())); QCOMPARE(testTodoOccurrence.dueDateTime(), QDateTime(QDate::currentDate())); testTodoOccurrence.setFinishedDateTime(QDateTime(QDate::currentDate().addDays(1))); QCOMPARE(testTodoOccurrence.finishedDateTime(), QDateTime(QDate::currentDate().addDays(1))); testTodoOccurrence.setPriority(QOrganizerItemPriority::VeryHighPriority); QCOMPARE(testTodoOccurrence.priority(), QOrganizerItemPriority::VeryHighPriority); testTodoOccurrence.setPriority(QOrganizerItemPriority::VeryLowPriority); QCOMPARE(testTodoOccurrence.priority(), QOrganizerItemPriority::VeryLowPriority); // the parent id and original date time must be tested properly in the manager unit test // but we will test the API mutator/accessor functions here. QOrganizerItemId id; testTodoOccurrence.setParentId(id); QCOMPARE(testTodoOccurrence.parentId(), id); QDateTime originalDateTime = QDateTime::currentDateTime(); testTodoOccurrence.setOriginalDate(originalDateTime.date()); QCOMPARE(testTodoOccurrence.originalDate(), originalDateTime.date()); } void tst_QOrganizerItem::testDebugStreamOut() { QOrganizerRecurrenceRule rrule; // Testing the empty case QTest::ignoreMessage(QtDebugMsg, "QOrganizerRecurrenceRule(frequency=0,interval=1,no limit,daysOfWeek=\"\",daysOfMonth=\"\",daysOfYear=\"\",monthsOfYear=\"\",positions=\"\",firstDayOfWeek=1)"); qDebug() << rrule; // Testing a completely filled case rrule.setLimit(1); rrule.setFrequency(QOrganizerRecurrenceRule::Weekly); rrule.setInterval(10); rrule.setLimit(20); QSet days; days << Qt::Thursday << Qt::Friday; rrule.setDaysOfWeek(days); QSet daysM; daysM << 1 << 2 << 3; rrule.setDaysOfMonth(daysM); QSet daysY; daysY << 5 << 6 << 7 << 8; rrule.setDaysOfYear(daysY); QSet months; months << QOrganizerRecurrenceRule::January << QOrganizerRecurrenceRule::February; rrule.setMonthsOfYear(months); QSet pos; pos << -1; rrule.setPositions(pos); Qt::DayOfWeek firstDay; firstDay = Qt::Tuesday; rrule.setFirstDayOfWeek(firstDay); QSKIP("QTBUG-25382: Assumes QSet is an ordered collection, which it's not"); QTest::ignoreMessage(QtDebugMsg, "QOrganizerRecurrenceRule(frequency=2,interval=10,limitCount=20,daysOfWeek=\"4 5 \",daysOfMonth=\"1 2 3 \",daysOfYear=\"5 6 7 8 \",monthsOfYear=\"1 2 \",positions=\"-1 \",firstDayOfWeek=2)"); qDebug() << rrule; } void tst_QOrganizerItem::itemsCompare() { QOrganizerItemDisplayLabel displayLabel; displayLabel.setLabel("test label"); QOrganizerItemDescription descr; descr.setDescription("test description"); QOrganizerItemComment comment; comment.setComment("yet another test comment"); QOrganizerItemLocation location; location.setLabel("another test address label"); QOrganizerEventAttendee a; a.setAttendeeId("123456"); a.setEmailAddress("people@qt-project.org"); a.setName("people"); QOrganizerEventAttendee a1; a1.setAttendeeId("777777"); a1.setName("people1"); a1.setEmailAddress("people1@qt-project.org"); QOrganizerEventAttendee a2; a2.setAttendeeId("9jjjkkk"); a2.setName("people2"); a2.setEmailAddress("people1@qt-project.org"); QOrganizerEventAttendee a3; a2.setAttendeeId("ppppp"); a2.setName("people3"); a2.setEmailAddress("people3@qt-project.org"); QOrganizerEventTime etr; etr.setStartDateTime(QDateTime(QDate::currentDate())); etr.setEndDateTime(QDateTime(QDate::currentDate().addDays(1))); QOrganizerItem item1; QOrganizerItem item2; item1.saveDetail(&displayLabel); item1.saveDetail(&descr); item1.saveDetail(&comment); item1.saveDetail(&location); item1.saveDetail(&etr); item2.saveDetail(&etr); item2.saveDetail(&location); item2.saveDetail(&comment); item2.saveDetail(&descr); QVERIFY(item1 != item2); item2.saveDetail(&displayLabel); QVERIFY(item1 == item2); // Identical details compare test // Same type details but different amount item1.addTag("TestTag1"); item1.addTag("TestTag2"); item1.saveDetail(&a); item2.addTag("TestTag1"); item2.saveDetail(&a); item2.saveDetail(&a1); QVERIFY(item1 != item2); // Make they same but different orders item1.saveDetail(&a1); item2.addTag("TestTag2"); QVERIFY(item1 == item2); item1.saveDetail(&a2); item2.saveDetail(&a3); QVERIFY(item1 != item2); item1.saveDetail(&a3); item2.saveDetail(&a2); QVERIFY(item1 == item2); } QTEST_MAIN(tst_QOrganizerItem) #include "tst_qorganizeritem.moc" tests/auto/organizer/qorganizeritemasync/000077500000000000000000000000001233466112000212645ustar00rootroot00000000000000tests/auto/organizer/qorganizeritemasync/maliciousplugin/000077500000000000000000000000001233466112000244705ustar00rootroot00000000000000tests/auto/organizer/qorganizeritemasync/maliciousplugin/malicious.json000066400000000000000000000000461233466112000273500ustar00rootroot00000000000000{ "Keys": [ "maliciousplugin" ] } tests/auto/organizer/qorganizeritemasync/maliciousplugin/maliciousplugin.cpp000066400000000000000000000223501233466112000304020ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "maliciousplugin_p.h" #include #include #include #include #include #include #ifndef MALICIOUSPLUGINTARGET #define MALICIOUSPLUGINTARGET organizer_maliciousplugin #endif #ifndef MALICIOUSPLUGINNAME #define MALICIOUSPLUGINNAME maliciousplugin #endif #define makestr(x) (#x) #define makename(x) makestr(x) QTORGANIZER_USE_NAMESPACE class MaliciousThreadObject : public QObject { Q_OBJECT public: MaliciousThreadObject() {} public slots: void activateRequest(QOrganizerAbstractRequest *req) { mutex.lock(); if (activeRequests.contains(req)) QOrganizerManagerEngine::updateRequestState(req, QOrganizerAbstractRequest::ActiveState); mutex.unlock(); } void finishRequest(QOrganizerAbstractRequest *req) { QOrganizerManager::Error errorResult = QOrganizerManager::NoError; QMap errorMap; QList idResult; QList itemResult; QList collectionResult; mutex.lock(); if (activeRequests.contains(req)) { switch (req->type()) { case QOrganizerAbstractRequest::ItemSaveRequest: QOrganizerManagerEngine::updateItemSaveRequest(static_cast(req), itemResult, errorResult, errorMap, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::ItemFetchRequest: QOrganizerManagerEngine::updateItemFetchRequest(static_cast(req), itemResult, errorResult, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::ItemIdFetchRequest: QOrganizerManagerEngine::updateItemIdFetchRequest(static_cast(req), idResult, errorResult, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::ItemFetchByIdRequest: QOrganizerManagerEngine::updateItemFetchByIdRequest(static_cast(req), itemResult, errorResult, errorMap, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::ItemFetchForExportRequest: QOrganizerManagerEngine::updateItemFetchForExportRequest(static_cast(req), itemResult, errorResult, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::ItemOccurrenceFetchRequest: QOrganizerManagerEngine::updateItemOccurrenceFetchRequest(static_cast(req), itemResult, errorResult, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::ItemRemoveRequest: QOrganizerManagerEngine::updateItemRemoveRequest(static_cast (req), errorResult, errorMap, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::ItemRemoveByIdRequest: QOrganizerManagerEngine::updateItemRemoveByIdRequest(static_cast (req), errorResult, errorMap, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::CollectionSaveRequest: QOrganizerManagerEngine::updateCollectionSaveRequest(static_cast (req), collectionResult, errorResult, errorMap, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::CollectionFetchRequest: QOrganizerManagerEngine::updateCollectionFetchRequest(static_cast(req), collectionResult, errorResult, QOrganizerAbstractRequest::FinishedState); break; case QOrganizerAbstractRequest::CollectionRemoveRequest: QOrganizerManagerEngine::updateCollectionRemoveRequest(static_cast(req), errorResult, errorMap, QOrganizerAbstractRequest::FinishedState); break; default: break; } } activeRequests.remove(req); mutex.unlock(); } public: QMutex mutex; QSet activeRequests; }; class MaliciousThread : public QThread { Q_OBJECT public: MaliciousThread(); ~MaliciousThread(); QObject *eventLoopQuitHack; }; MaliciousThread::MaliciousThread() { eventLoopQuitHack = new QObject; eventLoopQuitHack->moveToThread(this); connect(eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection); } MaliciousThread::~MaliciousThread() { eventLoopQuitHack->deleteLater(); wait(); } MaliciousAsyncManagerEngine::MaliciousAsyncManagerEngine() : QOrganizerManagerEngine() { thread = new MaliciousThread(); threadObject = new MaliciousThreadObject(); threadObject->moveToThread(thread); connect(this, SIGNAL(doStartRequest(QOrganizerAbstractRequest*)), threadObject, SLOT(activateRequest(QOrganizerAbstractRequest*)), Qt::BlockingQueuedConnection); connect(this, SIGNAL(doFinishRequest(QOrganizerAbstractRequest*)), threadObject, SLOT(finishRequest(QOrganizerAbstractRequest*)), Qt::BlockingQueuedConnection); thread->start(); } MaliciousAsyncManagerEngine::~MaliciousAsyncManagerEngine() { delete thread; delete threadObject; } QString MaliciousAsyncManagerEngine::managerName() const { return QString(makename(MALICIOUSPLUGINNAME)); } bool MaliciousAsyncManagerEngine::startRequest(QOrganizerAbstractRequest *req) { threadObject->mutex.lock(); threadObject->activeRequests.insert(req); threadObject->mutex.unlock(); // Spawn a thread to do stuff on another thread emit doStartRequest(req); emit doFinishRequest(req); return true; } bool MaliciousAsyncManagerEngine::cancelRequest(QOrganizerAbstractRequest *req) { updateRequestState(req, QOrganizerAbstractRequest::CanceledState); QOrganizerManagerEngine::cancelRequest(req); return true; } void MaliciousAsyncManagerEngine::requestDestroyed(QOrganizerAbstractRequest *req) { threadObject->mutex.lock(); threadObject->activeRequests.remove(req); threadObject->mutex.unlock(); QOrganizerManagerEngine::requestDestroyed(req); } QString MaliciousEngineFactory::managerName() const { return QString(makename(MALICIOUSPLUGINNAME)); } QOrganizerManagerEngine *MaliciousEngineFactory::engine(const QMap ¶meters, QOrganizerManager::Error *error) { Q_UNUSED(parameters); *error = QOrganizerManager::NoError; return new MaliciousAsyncManagerEngine(); } QOrganizerItemEngineId *MaliciousEngineFactory::createItemEngineId(const QMap ¶meters, const QString &engineIdString) const { Q_UNUSED(parameters); Q_UNUSED(engineIdString); return 0; } QOrganizerCollectionEngineId *MaliciousEngineFactory::createCollectionEngineId(const QMap ¶meters, const QString &engineIdString) const { Q_UNUSED(parameters); Q_UNUSED(engineIdString); return 0; } #include "maliciousplugin.moc" tests/auto/organizer/qorganizeritemasync/maliciousplugin/maliciousplugin.pro000066400000000000000000000004071233466112000304170ustar00rootroot00000000000000PLUGIN_TYPE = organizer load(qt_plugin) QT += core organizer DEFINES += MALICIOUSPLUGINTARGET=organizer_maliciousplugin DEFINES += MALICIOUSPLUGINNAME=maliciousplugin HEADERS += maliciousplugin_p.h SOURCES += maliciousplugin.cpp OTHER_FILES += malicious.json tests/auto/organizer/qorganizeritemasync/maliciousplugin/maliciousplugin_p.h000066400000000000000000000206711233466112000303720ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERMALICIOUSPLUGIN_P_H #define QORGANIZERMALICIOUSPLUGIN_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include QT_FORWARD_DECLARE_CLASS(QThread) QTORGANIZER_USE_NAMESPACE class MaliciousThreadObject; class MaliciousAsyncManagerEngine : public QOrganizerManagerEngine { Q_OBJECT public: MaliciousAsyncManagerEngine(); ~MaliciousAsyncManagerEngine(); QString managerName() const; bool startRequest(QOrganizerAbstractRequest *req); bool cancelRequest(QOrganizerAbstractRequest *req); QMap managerParameters() const { return QMap(); } int managerVersion() const { return 0; } // items QList items(const QList &itemIds, const QOrganizerItemFetchHint &fetchHint, QMap *errorMap, QOrganizerManager::Error *error) { return QOrganizerManagerEngine::items(itemIds, fetchHint, errorMap, error); } QList items(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error) { return QOrganizerManagerEngine::items(filter, startDateTime, endDateTime, maxCount, sortOrders, fetchHint, error); } QList itemIds(const QOrganizerItemFilter &filter, const QDateTime &startDateTime, const QDateTime &endDateTime, const QList &sortOrders, QOrganizerManager::Error *error) { return QOrganizerManagerEngine::itemIds(filter, startDateTime, endDateTime, sortOrders, error); } QList itemOccurrences(const QOrganizerItem &parentItem, const QDateTime &startDateTime, const QDateTime &endDateTime, int maxCount, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error) { return QOrganizerManagerEngine::itemOccurrences(parentItem, startDateTime, endDateTime, maxCount, fetchHint, error); } QList itemsForExport(const QDateTime &startDateTime, const QDateTime &endDateTime, const QOrganizerItemFilter &filter, const QList &sortOrders, const QOrganizerItemFetchHint &fetchHint, QOrganizerManager::Error *error) { return QOrganizerManagerEngine::itemsForExport(startDateTime, endDateTime, filter, sortOrders, fetchHint, error); } bool saveItems(QList *items, const QList &detailMask, QMap *errorMap, QOrganizerManager::Error *error) { return QOrganizerManagerEngine::saveItems(items, detailMask, errorMap, error); } bool removeItems(const QList &itemIds, QMap *errorMap, QOrganizerManager::Error *error) { return QOrganizerManagerEngine::removeItems(itemIds, errorMap, error); } bool removeItems(const QList *items, QMap *errorMap, QOrganizerManager::Error *error) { return QOrganizerManagerEngine::removeItems(items, errorMap, error); } // collections QOrganizerCollection defaultCollection(QOrganizerManager::Error *error) { return QOrganizerManagerEngine::defaultCollection(error); } QOrganizerCollection collection(const QOrganizerCollectionId &collectionId, QOrganizerManager::Error *error) { return QOrganizerManagerEngine::collection(collectionId, error); } QList collections(QOrganizerManager::Error *error) { return QOrganizerManagerEngine::collections(error); } bool saveCollection(QOrganizerCollection *collection, QOrganizerManager::Error *error) { return QOrganizerManagerEngine::saveCollection(collection, error); } bool removeCollection(const QOrganizerCollectionId &collectionId, QOrganizerManager::Error *error) { return QOrganizerManagerEngine::removeCollection(collectionId, error); } /* Asynchronous Request Support */ void requestDestroyed(QOrganizerAbstractRequest *req); bool waitForRequestFinished(QOrganizerAbstractRequest *req, int msecs) { return QOrganizerManagerEngine::waitForRequestFinished(req, msecs); } QList supportedFilters() const { return QOrganizerManagerEngine::supportedFilters(); } QList supportedItemTypes() const { return QOrganizerManagerEngine::supportedItemTypes(); } QList supportedItemDetails(QOrganizerItemType::ItemType itemType) const { return QOrganizerManagerEngine::supportedItemDetails(itemType); } signals: void doStartRequest(QOrganizerAbstractRequest *req); void doFinishRequest(QOrganizerAbstractRequest *req); private: QThread *thread; MaliciousThreadObject *threadObject; }; class MaliciousEngineFactory : public QOrganizerManagerEngineFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QOrganizerManagerEngineFactoryInterface" FILE "malicious.json") public: QString managerName() const; QOrganizerManagerEngine *engine(const QMap ¶meters, QOrganizerManager::Error *error); QOrganizerItemEngineId *createItemEngineId(const QMap ¶meters, const QString &engineIdString) const; QOrganizerCollectionEngineId *createCollectionEngineId(const QMap ¶meters, const QString &engineIdString) const; private: MaliciousAsyncManagerEngine mame; }; #endif // QORGANIZERMALICIOUSPLUGIN_P_H tests/auto/organizer/qorganizeritemasync/qorganizeritemasync.pro000066400000000000000000000001041233466112000260770ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS += maliciousplugin \ unittest tests/auto/organizer/qorganizeritemasync/unittest/000077500000000000000000000000001233466112000231435ustar00rootroot00000000000000tests/auto/organizer/qorganizeritemasync/unittest/partitions.json000066400000000000000000000001211233466112000262240ustar00rootroot00000000000000[ { "name" : "com.nokia.mt.User" }, { "name" : "com.nokia.mt.System" } ] tests/auto/organizer/qorganizeritemasync/unittest/tst_qorganizeritemasync.cpp000066400000000000000000003767471233466112000306670ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include "../../qorganizermanagerdataholder.h" //QOrganizerManagerDataHolder #include "../../../jsondbprocess.h" Q_DECLARE_METATYPE(QTORGANIZER_PREPEND_NAMESPACE(QOrganizerAbstractRequest::State)) QTORGANIZER_USE_NAMESPACE /* Define an innocuous request (fetch ie doesn't mutate) to "fill up" any queues */ #define FILL_QUEUE_WITH_FETCH_REQUESTS(manager) QOrganizerItemFetchRequest fqifr1, fqifr2, fqifr3; \ fqifr1.setManager(manager); fqifr1.start(); \ fqifr2.setManager(manager); fqifr2.start(); \ fqifr3.setManager(manager); fqifr3.start(); //TESTED_COMPONENT=src/organizer // Unfortunately the plumbing isn't in place to allow cancelling requests at arbitrary points // in their processing. So we do multiple loops until things work out.. or not #define MAX_OPTIMISTIC_SCHEDULING_LIMIT 100 // Thread capable QThreadSignalSpy (to avoid data races with count/appendArgS) class QThreadSignalSpy: public QObject { public: QThreadSignalSpy(QObject *obj, const char *aSignal) { QMutexLocker m(&lock); #ifdef Q_CC_BOR const int memberOffset = QObject::staticMetaObject.methodCount(); #else static const int memberOffset = QObject::staticMetaObject.methodCount(); #endif Q_ASSERT(obj); Q_ASSERT(aSignal); if (((aSignal[0] - '0') & 0x03) != QSIGNAL_CODE) { qWarning("QThreadSignalSpy: Not a valid signal, use the SIGNAL macro"); return; } QByteArray ba = QMetaObject::normalizedSignature(aSignal + 1); const QMetaObject *mo = obj->metaObject(); int sigIndex = mo->indexOfMethod(ba.constData()); if (sigIndex < 0) { qWarning("QThreadSignalSpy: No such signal: '%s'", ba.constData()); return; } if (!QMetaObject::connect(obj, sigIndex, this, memberOffset, Qt::DirectConnection, 0)) { qWarning("QThreadSignalSpy: QMetaObject::connect returned false. Unable to connect."); return; } sig = ba; initArgs(mo->method(sigIndex)); } inline bool isValid() const { return !sig.isEmpty(); } inline QByteArray signal() const { return sig; } int qt_metacall(QMetaObject::Call call, int methodId, void **a) { methodId = QObject::qt_metacall(call, methodId, a); if (methodId < 0) return methodId; if (call == QMetaObject::InvokeMetaMethod) { if (methodId == 0) { appendArgs(a); } --methodId; } return methodId; } // The QList API we actually use int count() const { QMutexLocker m(&lock); return savedArgs.count(); } void clear() { QMutexLocker m(&lock); savedArgs.clear(); } private: void initArgs(const QMetaMethod &member) { QList params = member.parameterTypes(); for (int i = 0; i < params.count(); ++i) { int tp = QMetaType::type(params.at(i).constData()); if (tp == QMetaType::Void) qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.", params.at(i).constData()); args << tp; } } void appendArgs(void **a) { QMutexLocker m(&lock); QList list; for (int i = 0; i < args.count(); ++i) { QMetaType::Type type = static_cast(args.at(i)); list << QVariant(type, a[i + 1]); } savedArgs.append(list); } // the full, normalized signal name QByteArray sig; // holds the QMetaType types for the argument list of the signal QList args; mutable QMutex lock; // Different API QList< QVariantList> savedArgs; }; class tst_QOrganizerItemAsync : public QObject { Q_OBJECT public: tst_QOrganizerItemAsync(); virtual ~tst_QOrganizerItemAsync(); public slots: void initTestCase(); void cleanupTestCase(); private: void addManagers(QStringList includes = QStringList()); // add standard managers to the data private slots: void testDestructor(); void testDestructor_data() { addManagers(QStringList(QString("maliciousplugin"))); } void itemFetch(); void itemFetch_data() { addManagers(); } void itemFetchById(); void itemFetchById_data() { addManagers(); } void itemIdFetch(); void itemIdFetch_data() { addManagers(); } void itemOccurrenceFetch(); void itemOccurrenceFetch_data() { addManagers(); } void itemFetchForExport(); void itemFetchForExport_data() { addManagers(); } void itemRemove(); void itemRemove_data() { addManagers(); } void itemRemoveById(); void itemRemoveById_data() { addManagers(); } void itemSave(); void itemSave_data() { addManagers(); } void itemListSave(); void itemListSave_data() { addManagers(); } #if defined(QT_NO_JSONDB) void itemPartialSave(); void itemPartialSave_data() { addManagers(); } #endif void collectionFetch(); void collectionFetch_data() { addManagers(); } void collectionRemove(); void collectionRemove_data() { addManagers(); } void collectionSave(); void collectionSave_data() { addManagers(); } void testQuickDestruction(); void testQuickDestruction_data() { addManagers(QStringList(QString("maliciousplugin"))); } void threadDelivery(); void threadDelivery_data() { addManagers(QStringList(QString("maliciousplugin"))); } void testDebugStreamOut(); protected slots: void resultsAvailableReceived(); private: bool compareItemLists(QList lista, QList listb); bool compareItems(QOrganizerItem ca, QOrganizerItem cb); bool containsIgnoringTimestamps(const QList& list, const QOrganizerItem& c); bool compareIgnoringTimestamps(const QOrganizerItem& ca, const QOrganizerItem& cb); bool containsIgnoringDetailKeys(const QList& list, const QOrganizerItem& c); bool compareIgnoringDetailKeys(const QOrganizerItem& ca, const QOrganizerItem& cb); bool detailListContainsDetailIgnoringDetailKeys(const QList& dets, const QOrganizerItemDetail& det); bool containsAllCollectionIds(const QList& target, const QList& ids); QOrganizerManager* prepareModel(const QString& uri); Qt::HANDLE m_mainThreadId; Qt::HANDLE m_resultsAvailableSlotThreadId; QScopedPointer managerDataHolder; JsonDbProcess jsondbProcess; }; tst_QOrganizerItemAsync::tst_QOrganizerItemAsync() { // ensure we can load all of the plugins we need to. QString path = QCoreApplication::applicationDirPath() + QStringLiteral("/dummyplugin/plugins"); QCoreApplication::addLibraryPath(path); qRegisterMetaType(); } tst_QOrganizerItemAsync::~tst_QOrganizerItemAsync() { } void tst_QOrganizerItemAsync::initTestCase() { // Start JsonDb daemon if needed if (QOrganizerManager::availableManagers().contains("jsondb")) { QString partitions_json = QFINDTESTDATA("partitions.json"); QVERIFY2(!partitions_json.isEmpty(), "partitions.json file is missing"); QVERIFY2(jsondbProcess.start(partitions_json), "Failed to start JsonDb process"); } managerDataHolder.reset(new QOrganizerManagerDataHolder()); } void tst_QOrganizerItemAsync::cleanupTestCase() { managerDataHolder.reset(0); if (QOrganizerManager::availableManagers().contains("jsondb")) jsondbProcess.terminate(); } bool tst_QOrganizerItemAsync::compareItemLists(QList lista, QList listb) { // NOTE: This compare is item order insensitive. // Remove matching items foreach (QOrganizerItem a, lista) { foreach (QOrganizerItem b, listb) { if (compareItems(a, b)) { lista.removeOne(a); listb.removeOne(b); break; } } } return (lista.count() == 0 && listb.count() == 0); } bool tst_QOrganizerItemAsync::compareItems(QOrganizerItem ca, QOrganizerItem cb) { // NOTE: This compare is item detail order insensitive. if (ca.id() != cb.id()) return false; QList aDetails = ca.details(); QList bDetails = cb.details(); // Remove matching details foreach (QOrganizerItemDetail ad, aDetails) { foreach (QOrganizerItemDetail bd, bDetails) { if (ad == bd) { ca.removeDetail(&ad); cb.removeDetail(&bd); break; } // Special handling for timestamp if (ad.type() == QOrganizerItemDetail::TypeTimestamp && bd.type() == QOrganizerItemDetail::TypeTimestamp) { QOrganizerItemTimestamp at = static_cast(ad); QOrganizerItemTimestamp bt = static_cast(bd); if (at.created().toString() == bt.created().toString() && at.lastModified().toString() == bt.lastModified().toString()) { ca.removeDetail(&ad); cb.removeDetail(&bd); break; } } } } return (ca == cb); } bool tst_QOrganizerItemAsync::containsIgnoringTimestamps(const QList& list, const QOrganizerItem& c) { QList cl = list; QOrganizerItem a(c); for (int i = 0; i < cl.size(); i++) { QOrganizerItem b(cl.at(i)); if (compareIgnoringTimestamps(a, b)) return true; } return false; } bool tst_QOrganizerItemAsync::compareIgnoringTimestamps(const QOrganizerItem& ca, const QOrganizerItem& cb) { // Compares two items, ignoring any timestamp details QOrganizerItem a(ca); QOrganizerItem b(cb); QList aDetails = a.details(); QList bDetails = b.details(); // They can be in any order, so loop // First remove any matches, and any timestamps foreach (QOrganizerItemDetail d, aDetails) { foreach (QOrganizerItemDetail d2, bDetails) { if (d == d2) { a.removeDetail(&d); b.removeDetail(&d2); break; } if (d.type() == QOrganizerItemDetail::TypeTimestamp) a.removeDetail(&d); if (d2.type() == QOrganizerItemDetail::TypeTimestamp) b.removeDetail(&d2); } } if (a == b) return true; return false; } bool tst_QOrganizerItemAsync::containsIgnoringDetailKeys(const QList& list, const QOrganizerItem& c) { QList cl = list; QOrganizerItem a(c); for (int i = 0; i < cl.size(); i++) { QOrganizerItem b(cl.at(i)); if (compareIgnoringDetailKeys(a, b)) return true; } return false; } bool tst_QOrganizerItemAsync::compareIgnoringDetailKeys(const QOrganizerItem& ca, const QOrganizerItem& cb) { // Compares two items, ignoring any timestamp details QOrganizerItem a(ca); QOrganizerItem b(cb); QList aDetails = a.details(); QList bDetails = b.details(); // They can be in any order, so loop // First remove any matches, and any timestamps foreach (QOrganizerItemDetail d, aDetails) { foreach (QOrganizerItemDetail d2, bDetails) { if (d == d2) { a.removeDetail(&d); b.removeDetail(&d2); break; } // equality without checking the detail key values. if (d.type() == d2.type() && d.values() == d2.values()) { a.removeDetail(&d); b.removeDetail(&d2); break; } // and we have to ignore timestamps if (d.type() == QOrganizerItemDetail::TypeTimestamp) a.removeDetail(&d); if (d2.type() == QOrganizerItemDetail::TypeTimestamp) b.removeDetail(&d2); } } if (a == b) return true; // now compare the details, ignoring the keys. aDetails = a.details(); bDetails = b.details(); foreach (QOrganizerItemDetail d, aDetails) { bool foundCurrentDetail = false; foreach (QOrganizerItemDetail d2, bDetails) { if (d.type() == d2.type() && d.values() == d2.values()) { foundCurrentDetail = true; } if (d.type() == d2.type() && d.type() == QOrganizerItemDetail::TypeParent) { // XXX TODO: fix this properly in code. At the moment, doing d.values() == d2.values() doesn't work for ParentItem details. QOrganizerItemParent p1 = d; QOrganizerItemParent p2 = d2; if (p1.parentId() == p2.parentId()) { foundCurrentDetail = true; } } } if (!foundCurrentDetail) { return false; } } return true; } bool tst_QOrganizerItemAsync::detailListContainsDetailIgnoringDetailKeys(const QList& dets, const QOrganizerItemDetail& det) { bool foundCurrentDetail = false; foreach (const QOrganizerItemDetail d2, dets) { if (det == d2) { foundCurrentDetail = true; } if (det.type() == d2.type() && det.values() == d2.values()) { foundCurrentDetail = true; } if (det.type() == d2.type() && det.type() == QOrganizerItemDetail::TypeParent) { // XXX TODO: fix this properly in code. At the moment, doing d.values() == d2.values() doesn't work for ParentItem details. QOrganizerItemParent p1 = det; QOrganizerItemParent p2 = d2; if (p1.parentId() == p2.parentId()) { foundCurrentDetail = true; } } } if (!foundCurrentDetail) { return false; } return true; } bool tst_QOrganizerItemAsync::containsAllCollectionIds(const QList& target, const QList& ids) { bool containsAllIds = true; foreach(QOrganizerCollectionId id, ids) { if (!target.contains(id)) { containsAllIds = false; break; } } return containsAllIds; } void tst_QOrganizerItemAsync::testDestructor() { QFETCH(QString, uri); QOrganizerManager* cm = prepareModel(uri); QOrganizerItemFetchRequest* req = new QOrganizerItemFetchRequest; req->setManager(cm); QOrganizerManager* cm2 = prepareModel(uri); QOrganizerItemFetchRequest* req2 = new QOrganizerItemFetchRequest; req2->setManager(cm2); // first, delete manager then request delete cm; delete req; // second, delete request then manager delete req2; delete cm2; } void tst_QOrganizerItemAsync::itemFetch() { QFETCH(QString, uri); QScopedPointer oim(prepareModel(uri)); QOrganizerItemFetchRequest ifr; QVERIFY(ifr.type() == QOrganizerAbstractRequest::ItemFetchRequest); // initial state - not started, no manager. QVERIFY(!ifr.isActive()); QVERIFY(!ifr.isFinished()); QVERIFY(!ifr.start()); QVERIFY(!ifr.cancel()); QVERIFY(!ifr.waitForFinished()); // "all items" retrieval QOrganizerItemFilter fil; ifr.setManager(oim.data()); QCOMPARE(ifr.manager(), oim.data()); QVERIFY(!ifr.isActive()); QVERIFY(!ifr.isFinished()); QVERIFY(!ifr.cancel()); QVERIFY(!ifr.waitForFinished()); qRegisterMetaType("QOrganizerItemFetchRequest*"); QThreadSignalSpy spy(&ifr, SIGNAL(stateChanged(QOrganizerAbstractRequest::State))); ifr.setFilter(fil); QCOMPARE(ifr.filter(), fil); QVERIFY(!ifr.cancel()); // not started QVERIFY(ifr.start()); //QVERIFY(ifr.isFinished() || !ifr.start()); // already started. // thread scheduling means this is untestable QVERIFY((ifr.isActive() && ifr.state() == QOrganizerAbstractRequest::ActiveState) || ifr.isFinished()); QVERIFY(ifr.waitForFinished()); QVERIFY(ifr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList mitems = oim->items(); QList items = ifr.items(); QCOMPARE(mitems.size(), items.size()); for (int i = 0; i < items.size(); i++) { QVERIFY(containsIgnoringDetailKeys(mitems, items.at(i))); } // asynchronous detail field filtering QOrganizerItemDetailFieldFilter dfil; dfil.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); ifr.setFilter(dfil); QVERIFY(ifr.filter() == dfil); QVERIFY(!ifr.cancel()); // not started QVERIFY(ifr.start()); QVERIFY((ifr.isActive() && ifr.state() == QOrganizerAbstractRequest::ActiveState) || ifr.isFinished()); //QVERIFY(ifr.isFinished() || !ifr.start()); // already started. // thread scheduling means this is untestable QVERIFY(ifr.waitForFinished()); QVERIFY(ifr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); mitems = oim->items(QDateTime(), QDateTime(), dfil); items = ifr.items(); QEXPECT_FAIL("mgr='jsondb'","Jsondb backend does not support filtering based only on the detail/field and not value.", Continue); QVERIFY(!mitems.isEmpty()); QCOMPARE(mitems.size(), items.size()); for (int i = 0; i < items.size(); i++) { QVERIFY(containsIgnoringDetailKeys(mitems, items.at(i))); } #if defined(QT_NO_JSONDB) // sort order QOrganizerItemSortOrder sortOrder; sortOrder.setDetail(QOrganizerItemDetail::TypePriority, QOrganizerItemPriority::FieldPriority); QList sorting; sorting.append(sortOrder); ifr.setFilter(fil); ifr.setSorting(sorting); QCOMPARE(ifr.sorting(), sorting); QVERIFY(!ifr.cancel()); // not started QVERIFY(ifr.start()); QVERIFY((ifr.isActive() && ifr.state() == QOrganizerAbstractRequest::ActiveState) || ifr.isFinished()); //QVERIFY(ifr.isFinished() || !ifr.start()); // already started. // thread scheduling means this is untestable QVERIFY(ifr.waitForFinished()); QVERIFY(ifr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); mitems = oim->items(QDateTime(), QDateTime(), QOrganizerItemFilter(), -1, sorting); items = ifr.items(); QCOMPARE(mitems.size(), items.size()); for (int i = 0; i < items.size(); i++) { QVERIFY(containsIgnoringDetailKeys(mitems, items.at(i))); } // restrictions sorting.clear(); ifr.setFilter(fil); ifr.setSorting(sorting); QOrganizerItemFetchHint fetchHint; fetchHint.setDetailTypesHint(QList() << QOrganizerItemDetail::TypeDescription); ifr.setFetchHint(fetchHint); QCOMPARE(ifr.fetchHint().detailTypesHint(), QList() << QOrganizerItemDetail::TypeDescription); QVERIFY(!ifr.cancel()); // not started QVERIFY(ifr.start()); QVERIFY((ifr.isActive() && ifr.state() == QOrganizerAbstractRequest::ActiveState) || ifr.isFinished()); //QVERIFY(ifr.isFinished() || !ifr.start()); // already started. // thread scheduling means this is untestable QVERIFY(ifr.waitForFinished()); QVERIFY(ifr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); mitems = oim->items(QDateTime(), QDateTime(), QOrganizerItemFilter(), -1, sorting); items = ifr.items(); QCOMPARE(mitems.size(), items.size()); for (int i = 0; i < mitems.size(); i++) { // create a item from the restricted data only (id + display label) QOrganizerItem currFull = mitems.at(i); QOrganizerItem currRestricted; // in prepare model, the items are all events. if (currFull.type() == QOrganizerItemType::TypeEvent) { currRestricted = QOrganizerEvent(); // therefore, the returned items will either be events } else if (currFull.type() == QOrganizerItemType::TypeEventOccurrence) { currRestricted = QOrganizerEventOccurrence(); // or event occurrences. } currRestricted.setId(currFull.id()); QList descriptions = currFull.details(QOrganizerItemDetail::TypeDescription); foreach (const QOrganizerItemDescription& description, descriptions) { QOrganizerItemDescription descr = description; if (!descr.isEmpty()) { currRestricted.saveDetail(&descr); } } // now find the item in the retrieved list which our restricted item mimics QOrganizerItem retrievedRestricted; bool found = false; foreach (const QOrganizerItem& retrieved, items) { if (retrieved.id() == currRestricted.id()) { retrievedRestricted = retrieved; found = true; } } QVERIFY(found); // must exist or fail. // ensure that the item is the same (except synth fields) QList retrievedDetails = retrievedRestricted.details(); QList expectedDetails = currRestricted.details(); foreach (const QOrganizerItemDetail& det, expectedDetails) { // ignore backend synthesised details // again, this requires a "default item details" function to work properly. if (det.type() == QOrganizerItemDetail::TypeTimestamp) continue; // everything else in the expected item should be in the retrieved one. QVERIFY(detailListContainsDetailIgnoringDetailKeys(retrievedDetails, det)); } } // cancelling sorting.clear(); ifr.setFilter(fil); ifr.setSorting(sorting); ifr.setFetchHint(QOrganizerItemFetchHint()); int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. while (true) { QVERIFY(!ifr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); QVERIFY(ifr.start()); if (!ifr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. spy.clear(); ifr.waitForFinished(); sorting.clear(); ifr.setFilter(fil); ifr.setSorting(sorting); ifr.setFetchHint(QOrganizerItemFetchHint()); ifr.setFetchHint(QOrganizerItemFetchHint()); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } continue; } // if we get here, then we are cancelling the request. QVERIFY(ifr.waitForFinished()); QVERIFY(ifr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); break; } // restart, and wait for progress after cancel. while (true) { QVERIFY(!ifr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); QVERIFY(ifr.start()); if (!ifr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. ifr.waitForFinished(); sorting.clear(); ifr.setFilter(fil); ifr.setSorting(sorting); ifr.setFetchHint(QOrganizerItemFetchHint()); bailoutCount -= 1; spy.clear(); if (!bailoutCount) { //qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } continue; } ifr.waitForFinished(); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); QVERIFY(!ifr.isActive()); QVERIFY(ifr.state() == QOrganizerAbstractRequest::CanceledState); break; } #endif } void tst_QOrganizerItemAsync::itemFetchById() { QFETCH(QString, uri); QScopedPointer oim(prepareModel(uri)); // XXX TODO: fetchbyid request for items as well as items!!! QOrganizerItemFetchByIdRequest ifr; QVERIFY(ifr.type() == QOrganizerAbstractRequest::ItemFetchByIdRequest); // initial state - not started, no manager. QVERIFY(!ifr.isActive()); QVERIFY(!ifr.isFinished()); QVERIFY(!ifr.start()); QVERIFY(!ifr.cancel()); QVERIFY(!ifr.waitForFinished()); // get all item ids QList itemIds(oim->itemIds()); // "all items" retrieval ifr.setManager(oim.data()); ifr.setIds(itemIds); QCOMPARE(ifr.manager(), oim.data()); QVERIFY(!ifr.isActive()); QVERIFY(!ifr.isFinished()); QVERIFY(!ifr.cancel()); QVERIFY(!ifr.waitForFinished()); qRegisterMetaType("QOrganizerItemFetchByIdRequest*"); QThreadSignalSpy spy(&ifr, SIGNAL(stateChanged(QOrganizerAbstractRequest::State))); QVERIFY(!ifr.cancel()); // not started QVERIFY(ifr.start()); //QVERIFY(ifr.isFinished() || !ifr.start()); // already started. // thread scheduling means this is untestable QVERIFY((ifr.isActive() && ifr.state() == QOrganizerAbstractRequest::ActiveState) || ifr.isFinished()); QVERIFY(ifr.waitForFinished()); QVERIFY(ifr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList items = ifr.items(); QCOMPARE(itemIds.size(), items.size()); for (int i = 0; i < itemIds.size(); i++) { QOrganizerItem curr = oim->item(itemIds.at(i)); QVERIFY(items.at(i) == curr); } // save empty list QList itemIdList; QOrganizerItemFetchByIdRequest ifr1; ifr1.setManager(oim.data()); ifr1.setIds(itemIdList); ifr1.start(); ifr1.waitForFinished(); QVERIFY(ifr1.isFinished()); QVERIFY(ifr1.error() == QOrganizerManager::NoError); // XXX TODO: fetchbyid request for items as well as items!!! } void tst_QOrganizerItemAsync::itemIdFetch() { QFETCH(QString, uri); QScopedPointer oim(prepareModel(uri)); QOrganizerItemIdFetchRequest ifr; QVERIFY(ifr.type() == QOrganizerAbstractRequest::ItemIdFetchRequest); // initial state - not started, no manager. QVERIFY(!ifr.isActive()); QVERIFY(!ifr.isFinished()); QVERIFY(!ifr.start()); QVERIFY(!ifr.cancel()); QVERIFY(!ifr.waitForFinished()); // "all items" retrieval QOrganizerItemFilter fil; ifr.setManager(oim.data()); QCOMPARE(ifr.manager(), oim.data()); QVERIFY(!ifr.isActive()); QVERIFY(!ifr.isFinished()); QVERIFY(!ifr.cancel()); QVERIFY(!ifr.waitForFinished()); qRegisterMetaType("QOrganizerItemIdFetchRequest*"); QThreadSignalSpy spy(&ifr, SIGNAL(stateChanged(QOrganizerAbstractRequest::State))); ifr.setFilter(fil); QCOMPARE(ifr.filter(), fil); QVERIFY(!ifr.cancel()); // not started QVERIFY(ifr.start()); QVERIFY((ifr.isActive() &&ifr.state() == QOrganizerAbstractRequest::ActiveState) || ifr.isFinished()); //QVERIFY(ifr.isFinished() || !ifr.start()); // already started. // thread scheduling means this is untestable QVERIFY(ifr.waitForFinished()); QVERIFY(ifr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList itemIds = oim->itemIds(); QList result = ifr.itemIds(); QCOMPARE(itemIds, result); // asynchronous detail field filtering QOrganizerItemDetailFieldFilter dfil; dfil.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); ifr.setFilter(dfil); QVERIFY(ifr.filter() == dfil); QVERIFY(!ifr.cancel()); // not started QVERIFY(ifr.start()); QVERIFY((ifr.isActive() && ifr.state() == QOrganizerAbstractRequest::ActiveState) || ifr.isFinished()); //QVERIFY(ifr.isFinished() || !ifr.start()); // already started. // thread scheduling means this is untestable QVERIFY(ifr.waitForFinished()); QVERIFY(ifr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); itemIds = oim->itemIds(QDateTime(), QDateTime(), dfil); result = ifr.itemIds(); QEXPECT_FAIL("mgr='jsondb'","Jsondb backend does not support filtering based only on the detail/field and not value.", Continue); QVERIFY(!itemIds.isEmpty()); QCOMPARE(itemIds, result); #if defined(QT_NO_JSONDB) // sort order QOrganizerItemSortOrder sortOrder; sortOrder.setDetail(QOrganizerItemDetail::TypePriority, QOrganizerItemPriority::FieldPriority); QList sorting; sorting.append(sortOrder); ifr.setFilter(fil); ifr.setSorting(sorting); QCOMPARE(ifr.sorting(), sorting); QVERIFY(!ifr.cancel()); // not started QVERIFY(ifr.start()); QVERIFY((ifr.isActive() && ifr.state() == QOrganizerAbstractRequest::ActiveState) || ifr.isFinished()); //QVERIFY(ifr.isFinished() || !ifr.start()); // already started. // thread scheduling means this is untestable QVERIFY(ifr.waitForFinished()); QVERIFY(ifr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); itemIds = oim->itemIds(QDateTime(), QDateTime(), QOrganizerItemFilter(), sorting); result = ifr.itemIds(); QCOMPARE(itemIds, result); // cancelling sorting.clear(); ifr.setFilter(fil); ifr.setSorting(sorting); int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. while (true) { QVERIFY(!ifr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); QVERIFY(ifr.start()); if (!ifr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. ifr.waitForFinished(); sorting.clear(); ifr.setFilter(fil); ifr.setSorting(sorting); bailoutCount -= 1; spy.clear(); if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } continue; } // if we get here, then we are cancelling the request. QVERIFY(ifr.waitForFinished()); QVERIFY(ifr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); break; } // restart, and wait for progress after cancel. while (true) { QVERIFY(!ifr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); QVERIFY(ifr.start()); if (!ifr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. ifr.waitForFinished(); sorting.clear(); ifr.setFilter(fil); ifr.setSorting(sorting); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } continue; } ifr.waitForFinished(); QVERIFY(ifr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); break; } #endif } void tst_QOrganizerItemAsync::itemOccurrenceFetch() { QFETCH(QString, uri); QScopedPointer oim(prepareModel(uri)); QOrganizerItemOccurrenceFetchRequest ifr; QVERIFY(ifr.type() == QOrganizerAbstractRequest::ItemOccurrenceFetchRequest); // retrieve a parent event from the backend. QOrganizerItem parent; bool foundParent = false; QList allItems = oim->itemsForExport(); for (int i = 0; i < allItems.size(); ++i) { QOrganizerItem curr = allItems.at(i); if (curr.type() == QOrganizerItemType::TypeEvent) { QOrganizerEvent evt = curr; if (evt.recurrenceDates().size() > 0) { parent = evt; foundParent = true; } } } if (!foundParent) { QSKIP("Manager has no valid recurring events; skipping."); } // initial state - not started, no manager. QVERIFY(!ifr.isActive()); QVERIFY(!ifr.isFinished()); QVERIFY(!ifr.start()); QVERIFY(!ifr.cancel()); QVERIFY(!ifr.waitForFinished()); // "all items" retrieval ifr.setManager(oim.data()); QCOMPARE(ifr.manager(), oim.data()); QVERIFY(!ifr.isActive()); QVERIFY(!ifr.isFinished()); QVERIFY(!ifr.cancel()); QVERIFY(!ifr.waitForFinished()); qRegisterMetaType("QOrganizerItemFetchRequest*"); QThreadSignalSpy spy(&ifr, SIGNAL(stateChanged(QOrganizerAbstractRequest::State))); ifr.setParentItem(parent); QCOMPARE(ifr.parentItem(), parent); QVERIFY(!ifr.cancel()); // not started QVERIFY(ifr.start()); //QVERIFY(ifr.isFinished() || !ifr.start()); // already started. // thread scheduling means this is untestable QVERIFY((ifr.isActive() && ifr.state() == QOrganizerAbstractRequest::ActiveState) || ifr.isFinished()); QVERIFY(ifr.waitForFinished()); QVERIFY(ifr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList itemOccs = oim->itemOccurrences(parent, QDateTime(), QDateTime(), -1); QList items = ifr.itemOccurrences(); QCOMPARE(items.size(), itemOccs.size()); for (int i = 0; i < items.size(); ++i) { QVERIFY(containsIgnoringDetailKeys(itemOccs, items.at(i))); } // do it again, make sure it doesn't mutate the result set. ifr.setParentItem(parent); QVERIFY(ifr.parentItem() == parent); QVERIFY(!ifr.cancel()); // not started QVERIFY(ifr.start()); QVERIFY((ifr.isActive() && ifr.state() == QOrganizerAbstractRequest::ActiveState) || ifr.isFinished()); //QVERIFY(ifr.isFinished() || !ifr.start()); // already started. // thread scheduling means this is untestable QVERIFY(ifr.waitForFinished()); QVERIFY(ifr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); itemOccs = oim->itemOccurrences(parent, QDateTime(), QDateTime(), -1); items = ifr.itemOccurrences(); QCOMPARE(items.size(), itemOccs.size()); for (int i = 0; i < items.size(); ++i) { QVERIFY(containsIgnoringDetailKeys(itemOccs, items.at(i))); } // // restrictions // ifr.setParentItem(parent); // QOrganizerItemFetchHint fetchHint; // fetchHint.setDetailTypesHint(QList() << QOrganizerItemDetail::TypeDescription); // ifr.setFetchHint(fetchHint); // QCOMPARE(ifr.fetchHint().detailTypesHint(), QList() << QOrganizerItemDetail::TypeDescription); // QVERIFY(!ifr.cancel()); // not started // QVERIFY(ifr.start()); // QVERIFY((ifr.isActive() && ifr.state() == QOrganizerAbstractRequest::ActiveState) || ifr.isFinished()); // //QVERIFY(ifr.isFinished() || !ifr.start()); // already started. // thread scheduling means this is untestable // QVERIFY(ifr.waitForFinished()); // QVERIFY(ifr.isFinished()); // QVERIFY(spy.count() >= 1); // active + finished progress signals // spy.clear(); // itemOccs = oim->itemOccurrences(parent, QDateTime(), QDateTime(), -1); // items = ifr.itemOccurrences(); // QCOMPARE(itemOccs.size(), items.size()); // for (int i = 0; i < itemOccs.size(); i++) { // // create a item from the restricted data only (id + display label) // QOrganizerItem currFull = itemOccs.at(i); // QOrganizerItem currRestricted; // in prepare model, the items are all events. // if (currFull.type() == QOrganizerItemType::TypeEvent) { // currRestricted = QOrganizerEvent(); // therefore, the returned items will either be events // } else if (currFull.type() == QOrganizerItemType::TypeEventOccurrence) { // currRestricted = QOrganizerEventOccurrence(); // or event occurrences. // } // currRestricted.setId(currFull.id()); // QList descriptions = currFull.details(); // foreach (const QOrganizerItemDescription& description, descriptions) { // QOrganizerItemDescription descr = description; // if (!descr.isEmpty()) { // currRestricted.saveDetail(&descr); // } // } // // now find the item in the retrieved list which our restricted item mimics // QOrganizerItem retrievedRestricted; // bool found = false; // foreach (const QOrganizerItem& retrieved, items) { // if (retrieved.id() == currRestricted.id()) { // retrievedRestricted = retrieved; // found = true; // } // } // QVERIFY(found); // must exist or fail. // // ensure that the item is the same (except synth fields) // QList retrievedDetails = retrievedRestricted.details(); // QList expectedDetails = currRestricted.details(); // foreach (const QOrganizerItemDetail& det, expectedDetails) { // // ignore backend synthesised details // // again, this requires a "default item details" function to work properly. // if (det.type() == QOrganizerItemDetail::TypeTimestamp) { // continue; // } // // everything else in the expected item should be in the retrieved one. // QVERIFY(detailListContainsDetailIgnoringDetailKeys(retrievedDetails, det)); // } // } // // cancelling // ifr.setParentItem(parent); // ifr.setFetchHint(QOrganizerItemFetchHint()); // int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. // while (true) { // QVERIFY(!ifr.cancel()); // not started // FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); // QVERIFY(ifr.start()); // if (!ifr.cancel()) { // // due to thread scheduling, async cancel might be attempted // // after the request has already finished.. so loop and try again. // spy.clear(); // ifr.waitForFinished(); // ifr.setParentItem(parent); // ifr.setFetchHint(QOrganizerItemFetchHint()); // bailoutCount -= 1; // if (!bailoutCount) { //// qWarning("Unable to test cancelling due to thread scheduling!"); // bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // break; // } // continue; // } // // if we get here, then we are cancelling the request. // QVERIFY(ifr.waitForFinished()); // QVERIFY(ifr.isCanceled()); // QVERIFY(spy.count() >= 1); // active + cancelled progress signals // spy.clear(); // break; // } // // restart, and wait for progress after cancel. // while (true) { // QVERIFY(!ifr.cancel()); // not started // FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); // QVERIFY(ifr.start()); // if (!ifr.cancel()) { // // due to thread scheduling, async cancel might be attempted // // after the request has already finished.. so loop and try again. // ifr.waitForFinished(); // ifr.setParentItem(parent); // ifr.setFetchHint(QOrganizerItemFetchHint()); // bailoutCount -= 1; // spy.clear(); // if (!bailoutCount) { // //qWarning("Unable to test cancelling due to thread scheduling!"); // bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // break; // } // continue; // } // ifr.waitForFinished(); // QVERIFY(spy.count() >= 1); // active + cancelled progress signals // spy.clear(); // QVERIFY(!ifr.isActive()); // QVERIFY(ifr.state() == QOrganizerAbstractRequest::CanceledState); // break; // } } void tst_QOrganizerItemAsync::itemFetchForExport() { QFETCH(QString, uri); QScopedPointer oim(prepareModel(uri)); QOrganizerItemFetchForExportRequest ifr; QVERIFY(ifr.type() == QOrganizerAbstractRequest::ItemFetchForExportRequest); // initial state - not started, no manager. QVERIFY(!ifr.isActive()); QVERIFY(!ifr.isFinished()); QVERIFY(!ifr.start()); QVERIFY(!ifr.cancel()); QVERIFY(!ifr.waitForFinished()); // "all items" retrieval QOrganizerItemFilter fil; ifr.setManager(oim.data()); QCOMPARE(ifr.manager(), oim.data()); QVERIFY(!ifr.isActive()); QVERIFY(!ifr.isFinished()); QVERIFY(!ifr.cancel()); QVERIFY(!ifr.waitForFinished()); qRegisterMetaType("QOrganizerItemFetchRequest*"); QThreadSignalSpy spy(&ifr, SIGNAL(stateChanged(QOrganizerAbstractRequest::State))); ifr.setFilter(fil); QCOMPARE(ifr.filter(), fil); QVERIFY(!ifr.cancel()); // not started QVERIFY(ifr.start()); //QVERIFY(ifr.isFinished() || !ifr.start()); // already started. // thread scheduling means this is untestable QVERIFY((ifr.isActive() && ifr.state() == QOrganizerAbstractRequest::ActiveState) || ifr.isFinished()); QVERIFY(ifr.waitForFinished()); QVERIFY(ifr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList itemsfe = oim->itemsForExport(QDateTime(), QDateTime(), fil); QList items = ifr.items(); QCOMPARE(itemsfe.size(), items.size()); for (int i = 0; i < itemsfe.size(); i++) { QOrganizerItem curr = itemsfe.at(i); QVERIFY(items.contains(curr)); } #if defined(QT_NO_JSONDB) // asynchronous detail field filtering QOrganizerItemDetailFieldFilter dfil; dfil.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); ifr.setFilter(dfil); QVERIFY(ifr.filter() == dfil); QVERIFY(!ifr.cancel()); // not started QVERIFY(ifr.start()); QVERIFY((ifr.isActive() && ifr.state() == QOrganizerAbstractRequest::ActiveState) || ifr.isFinished()); //QVERIFY(ifr.isFinished() || !ifr.start()); // already started. // thread scheduling means this is untestable QVERIFY(ifr.waitForFinished()); QVERIFY(ifr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); itemsfe = oim->itemsForExport(QDateTime(), QDateTime(), dfil); items = ifr.items(); QCOMPARE(itemsfe.size(), items.size()); for (int i = 0; i < itemsfe.size(); i++) { QOrganizerItem curr = itemsfe.at(i); QVERIFY(items.contains(curr)); } #endif // sort order QOrganizerItemSortOrder sortOrder; sortOrder.setDetail(QOrganizerItemDetail::TypePriority, QOrganizerItemPriority::FieldPriority); QList sorting; sorting.append(sortOrder); ifr.setFilter(fil); ifr.setSorting(sorting); ifr.setStartDate(QDateTime()); ifr.setEndDate(QDateTime()); QCOMPARE(ifr.sorting(), sorting); QVERIFY(!ifr.cancel()); // not started QVERIFY(ifr.start()); QVERIFY((ifr.isActive() && ifr.state() == QOrganizerAbstractRequest::ActiveState) || ifr.isFinished()); //QVERIFY(ifr.isFinished() || !ifr.start()); // already started. // thread scheduling means this is untestable QVERIFY(ifr.waitForFinished()); QVERIFY(ifr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); itemsfe = oim->itemsForExport(QDateTime(), QDateTime(), fil, sorting); items = ifr.items(); QCOMPARE(itemsfe.size(), items.size()); for (int i = 0; i < itemsfe.size(); i++) { QOrganizerItem curr = itemsfe.at(i); QVERIFY(items.contains(curr)); } #if defined(QT_NO_JSONDB) // restrictions sorting.clear(); ifr.setFilter(fil); ifr.setSorting(sorting); QOrganizerItemFetchHint fetchHint; fetchHint.setDetailTypesHint(QList() << QOrganizerItemDetail::TypeDescription); ifr.setFetchHint(fetchHint); QCOMPARE(ifr.fetchHint().detailTypesHint(), QList() << QOrganizerItemDetail::TypeDescription); QVERIFY(!ifr.cancel()); // not started QVERIFY(ifr.start()); QVERIFY((ifr.isActive() && ifr.state() == QOrganizerAbstractRequest::ActiveState) || ifr.isFinished()); //QVERIFY(ifr.isFinished() || !ifr.start()); // already started. // thread scheduling means this is untestable QVERIFY(ifr.waitForFinished()); QVERIFY(ifr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); itemsfe = oim->itemsForExport(QDateTime(), QDateTime(), QOrganizerItemFilter(), sorting); items = ifr.items(); QCOMPARE(itemsfe.size(), items.size()); for (int i = 0; i < itemsfe.size(); i++) { // create a item from the restricted data only (id + display label) QOrganizerItem currFull = itemsfe.at(i); QOrganizerEvent currRestricted; // in prepare model, the item types were "Event" currRestricted.setId(currFull.id()); QList descriptions = currFull.details(QOrganizerItemDetail::TypeDescription); foreach (const QOrganizerItemDescription& description, descriptions) { QOrganizerItemDescription descr = description; if (!descr.isEmpty()) currRestricted.saveDetail(&descr); } // now find the item in the retrieved list which our restricted item mimics QOrganizerItem retrievedRestricted; bool found = false; foreach (const QOrganizerItem& retrieved, items) { if (retrieved.id() == currRestricted.id()) { retrievedRestricted = retrieved; found = true; } } QVERIFY(found); // must exist or fail. // ensure that the item is the same (except synth fields) QList retrievedDetails = retrievedRestricted.details(); QList expectedDetails = currRestricted.details(); foreach (const QOrganizerItemDetail& det, expectedDetails) { // ignore backend synthesised details // again, this requires a "default item details" function to work properly. if (det.type() == QOrganizerItemDetail::TypeTimestamp) continue; // everything else in the expected item should be in the retrieved one. QVERIFY(retrievedDetails.contains(det)); } } // cancelling sorting.clear(); ifr.setFilter(fil); ifr.setSorting(sorting); ifr.setFetchHint(QOrganizerItemFetchHint()); int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. while (true) { QVERIFY(!ifr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); QVERIFY(ifr.start()); if (!ifr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. spy.clear(); ifr.waitForFinished(); sorting.clear(); ifr.setFilter(fil); ifr.setSorting(sorting); ifr.setFetchHint(QOrganizerItemFetchHint()); ifr.setFetchHint(QOrganizerItemFetchHint()); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } continue; } // if we get here, then we are cancelling the request. QVERIFY(ifr.waitForFinished()); QVERIFY(ifr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); break; } // restart, and wait for progress after cancel. while (true) { QVERIFY(!ifr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); QVERIFY(ifr.start()); if (!ifr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. ifr.waitForFinished(); sorting.clear(); ifr.setFilter(fil); ifr.setSorting(sorting); ifr.setFetchHint(QOrganizerItemFetchHint()); bailoutCount -= 1; spy.clear(); if (!bailoutCount) { //qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } continue; } ifr.waitForFinished(); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); QVERIFY(!ifr.isActive()); QVERIFY(ifr.state() == QOrganizerAbstractRequest::CanceledState); break; } #endif } void tst_QOrganizerItemAsync::itemRemove() { QFETCH(QString, uri); QScopedPointer oim(prepareModel(uri)); qRegisterMetaType >("QList"); QOrganizerItemRemoveRequest irr; QVERIFY(irr.type() == QOrganizerAbstractRequest::ItemRemoveRequest); // initial state - not started, no manager. QVERIFY(!irr.isActive()); QVERIFY(!irr.isFinished()); QVERIFY(!irr.start()); QVERIFY(!irr.cancel()); QVERIFY(!irr.waitForFinished()); // fill manager with test data QOrganizerTodo testTodo1; QOrganizerItemDisplayLabel label; label.setLabel("Test todo 1"); testTodo1.saveDetail(&label); QVERIFY(oim->saveItem(&testTodo1)); testTodo1.setId(QOrganizerItemId()); label.setLabel("Test todo 2"); testTodo1.saveDetail(&label); QOrganizerItemComment comment; comment.setComment("todo comment"); testTodo1.saveDetail(&comment); QVERIFY(oim->saveItem(&testTodo1)); QList allItems(oim->items()); QVERIFY(!allItems.isEmpty()); QOrganizerItem removableItem(allItems.first()); // specific item set irr.setItem(removableItem); QVERIFY(irr.items() == QList() << removableItem); // remove empty list QList itemList; QOrganizerItemRemoveRequest irr1; irr1.setManager(oim.data()); irr1.setItems(itemList); irr1.start(); irr1.waitForFinished(); QVERIFY(irr1.isFinished()); QVERIFY(irr1.error() == QOrganizerManager::NoError); // specific item removal via detail field filter int originalCount = oim->itemIds().size(); QOrganizerItemDetailFieldFilter dfil; dfil.setDetail(QOrganizerItemDetail::TypeComment, QOrganizerItemComment::FieldComment); irr.setItems(oim->items(QDateTime(), QDateTime(), dfil)); irr.setManager(oim.data()); QCOMPARE(irr.manager(), oim.data()); QVERIFY(!irr.isActive()); QVERIFY(!irr.isFinished()); QVERIFY(!irr.cancel()); QVERIFY(!irr.waitForFinished()); qRegisterMetaType("QOrganizerItemRemoveRequest*"); QThreadSignalSpy spy(&irr, SIGNAL(stateChanged(QOrganizerAbstractRequest::State))); QVERIFY(!irr.cancel()); // not started QEXPECT_FAIL("mgr='jsondb'","Jsondb backend does not support filtering based only on the detail/field and not value.", Continue); QVERIFY(!oim->items(QDateTime(), QDateTime(), dfil).isEmpty()); QVERIFY(irr.start()); QVERIFY((irr.isActive() &&irr.state() == QOrganizerAbstractRequest::ActiveState) || irr.isFinished()); //QVERIFY(irr.isFinished() || !irr.start()); // already started. // thread scheduling means this is untestable QVERIFY(irr.waitForFinished()); QVERIFY(irr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QEXPECT_FAIL("mgr='jsondb'","Jsondb backend does not support filtering based only on the detail/field and not value.", Continue); QCOMPARE(oim->itemIds().size(), originalCount - 1); QVERIFY(oim->itemIds(QDateTime(), QDateTime(), dfil).isEmpty()); // remove all items irr.setManager(oim.data()); irr.setItems(oim->items()); QVERIFY(!irr.isActive()); QVERIFY(irr.isFinished()); QVERIFY(irr.waitForFinished()); QVERIFY(!irr.cancel()); // not started QVERIFY(irr.start()); // This removes test events and also initial events which include one recurring event // without start and end dates (with recurrence date list) QVERIFY((irr.isActive() && irr.state() == QOrganizerAbstractRequest::ActiveState) || irr.isFinished()); //QVERIFY(irr.isFinished() || !irr.start()); // already started. // thread scheduling means this is untestable QVERIFY(irr.waitForFinished()); QVERIFY(irr.isFinished()); QCOMPARE(oim->items().size(), 0); // no items should be left. QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); // // cancelling // QOrganizerTodo temp; // QOrganizerItemDescription description; // description.setDescription("Should not be removed"); // temp.saveDetail(&description); // oim->saveItem(&temp); // QCOMPARE(oim->itemIds().size(), 1); // irr.setItemIds(oim->itemIds()); // int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. // while (true) { // QVERIFY(!irr.cancel()); // not started // FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); // QVERIFY(spy.count() == 0); // QVERIFY(irr.start()); // if (!irr.cancel()) { // // due to thread scheduling, async cancel might be attempted // // after the request has already finished.. so loop and try again. // irr.waitForFinished(); // temp.setId(QOrganizerItemId()); // if (!oim->saveItem(&temp)) { // QSKIP("Unable to save temporary item for remove request cancellation test!"); // } // irr.setItemIds(oim->itemIds()); // bailoutCount -= 1; // if (!bailoutCount) { //// QSKIP("Unable to test cancelling due to thread scheduling!"); // bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // break; // } // spy.clear(); // continue; // } // // if we get here, then we are cancelling the request. // QVERIFY(irr.waitForFinished()); // QVERIFY(irr.isCanceled()); // QCOMPARE(oim->itemIds().size(), 1); // QCOMPARE(oim->itemIds(), irr.itemIds()); // QVERIFY(spy.count() >= 1); // active + cancelled progress signals // spy.clear(); // break; // } // // restart, and wait for progress after cancel. // while (true) { // QVERIFY(!irr.cancel()); // not started // FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); // QVERIFY(irr.start()); // if (!irr.cancel()) { // // due to thread scheduling, async cancel might be attempted // // after the request has already finished.. so loop and try again. // irr.waitForFinished(); // temp.setId(QOrganizerItemId()); // oim->saveItem(&temp); // irr.setItemIds(oim->itemIds()); // bailoutCount -= 1; // if (!bailoutCount) { //// qWarning("Unable to test cancelling due to thread scheduling!"); // bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // break; // } // spy.clear(); // continue; // } // irr.waitForFinished(); // QVERIFY(irr.isCanceled()); // QCOMPARE(oim->itemIds().size(), 1); // QCOMPARE(oim->itemIds(), irr.itemIds()); // QVERIFY(spy.count() >= 1); // active + cancelled progress signals // spy.clear(); // break; // } } void tst_QOrganizerItemAsync::itemRemoveById() { QFETCH(QString, uri); QScopedPointer oim(prepareModel(uri)); qRegisterMetaType >("QList"); QOrganizerItemRemoveByIdRequest irr; QVERIFY(irr.type() == QOrganizerAbstractRequest::ItemRemoveByIdRequest); // initial state - not started, no manager. QVERIFY(!irr.isActive()); QVERIFY(!irr.isFinished()); QVERIFY(!irr.start()); QVERIFY(!irr.cancel()); QVERIFY(!irr.waitForFinished()); // fill manager with test data QOrganizerTodo testTodo1; QOrganizerItemDisplayLabel label; label.setLabel("Test todo 1"); testTodo1.saveDetail(&label); QVERIFY(oim->saveItem(&testTodo1)); testTodo1.setId(QOrganizerItemId()); label.setLabel("Test todo 2"); testTodo1.saveDetail(&label); QOrganizerItemComment comment; comment.setComment("todo comment"); testTodo1.saveDetail(&comment); QVERIFY(oim->saveItem(&testTodo1)); QList allIds(oim->itemIds()); QVERIFY(!allIds.isEmpty()); QOrganizerItemId removableId(allIds.first()); // specific item set irr.setItemId(removableId); QVERIFY(irr.itemIds() == QList() << removableId); // remove empty list QList itemIdList; QOrganizerItemRemoveByIdRequest irr1; irr1.setManager(oim.data()); irr1.setItemIds(itemIdList); irr1.start(); irr1.waitForFinished(); QVERIFY(irr1.isFinished()); QVERIFY(irr1.error() == QOrganizerManager::NoError); // specific item removal via detail field filter int originalCount = oim->itemIds().size(); QOrganizerItemDetailFieldFilter dfil; dfil.setDetail(QOrganizerItemDetail::TypeComment, QOrganizerItemComment::FieldComment); irr.setItemIds(oim->itemIds(QDateTime(), QDateTime(), dfil)); irr.setManager(oim.data()); QCOMPARE(irr.manager(), oim.data()); QVERIFY(!irr.isActive()); QVERIFY(!irr.isFinished()); QVERIFY(!irr.cancel()); QVERIFY(!irr.waitForFinished()); qRegisterMetaType("QOrganizerItemRemoveByIdRequest*"); QThreadSignalSpy spy(&irr, SIGNAL(stateChanged(QOrganizerAbstractRequest::State))); QVERIFY(!irr.cancel()); // not started QEXPECT_FAIL("mgr='jsondb'","Jsondb backend does not support filtering based only on the detail/field and not value.", Continue); QVERIFY(!oim->itemIds(QDateTime(), QDateTime(), dfil).isEmpty()); QVERIFY(irr.start()); QVERIFY((irr.isActive() &&irr.state() == QOrganizerAbstractRequest::ActiveState) || irr.isFinished()); //QVERIFY(irr.isFinished() || !irr.start()); // already started. // thread scheduling means this is untestable QVERIFY(irr.waitForFinished()); QVERIFY(irr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QEXPECT_FAIL("mgr='jsondb'","Jsondb backend does not support filtering based only on the detail/field and not value.", Continue); QCOMPARE(oim->itemIds().size(), originalCount - 1); QVERIFY(oim->itemIds(QDateTime(), QDateTime(), dfil).isEmpty()); // remove all items //// dfil.setDetail(QOrganizerItemDetail::TypeDisplayLabel); // delete everything. //#if !defined(QT_NO_JSONDB) // qRegisterMetaType("QOrganizerItemRemoveRequest*"); //#endif irr.setManager(oim.data()); irr.setItemIds(oim->itemIds()); QVERIFY(!irr.isActive()); QVERIFY(irr.isFinished()); QVERIFY(irr.waitForFinished()); QVERIFY(!irr.cancel()); // not started QVERIFY(irr.start()); QVERIFY((irr.isActive() && irr.state() == QOrganizerAbstractRequest::ActiveState) || irr.isFinished()); //QVERIFY(irr.isFinished() || !irr.start()); // already started. // thread scheduling means this is untestable QVERIFY(irr.waitForFinished()); QVERIFY(irr.isFinished()); QCOMPARE(oim->itemIds().size(), 0); // no items should be left. QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); // // cancelling // QOrganizerTodo temp; // QOrganizerItemDescription description; // description.setDescription("Should not be removed"); // temp.saveDetail(&description); // oim->saveItem(&temp); // QCOMPARE(oim->itemIds().size(), 1); // irr.setItemIds(oim->itemIds()); // int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. // while (true) { // QVERIFY(!irr.cancel()); // not started // FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); // QVERIFY(spy.count() == 0); // QVERIFY(irr.start()); // if (!irr.cancel()) { // // due to thread scheduling, async cancel might be attempted // // after the request has already finished.. so loop and try again. // irr.waitForFinished(); // temp.setId(QOrganizerItemId()); // if (!oim->saveItem(&temp)) { // QSKIP("Unable to save temporary item for remove request cancellation test!"); // } // irr.setItemIds(oim->itemIds()); // bailoutCount -= 1; // if (!bailoutCount) { //// QSKIP("Unable to test cancelling due to thread scheduling!"); // bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // break; // } // spy.clear(); // continue; // } // // if we get here, then we are cancelling the request. // QVERIFY(irr.waitForFinished()); // QVERIFY(irr.isCanceled()); // QCOMPARE(oim->itemIds().size(), 1); // QCOMPARE(oim->itemIds(), irr.itemIds()); // QVERIFY(spy.count() >= 1); // active + cancelled progress signals // spy.clear(); // break; // } // // restart, and wait for progress after cancel. // while (true) { // QVERIFY(!irr.cancel()); // not started // FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); // QVERIFY(irr.start()); // if (!irr.cancel()) { // // due to thread scheduling, async cancel might be attempted // // after the request has already finished.. so loop and try again. // irr.waitForFinished(); // temp.setId(QOrganizerItemId()); // oim->saveItem(&temp); // irr.setItemIds(oim->itemIds()); // bailoutCount -= 1; // if (!bailoutCount) { //// qWarning("Unable to test cancelling due to thread scheduling!"); // bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // break; // } // spy.clear(); // continue; // } // irr.waitForFinished(); // QVERIFY(irr.isCanceled()); // QCOMPARE(oim->itemIds().size(), 1); // QCOMPARE(oim->itemIds(), irr.itemIds()); // QVERIFY(spy.count() >= 1); // active + cancelled progress signals // spy.clear(); // break; // } } void tst_QOrganizerItemAsync::itemSave() { QFETCH(QString, uri); QScopedPointer oim(prepareModel(uri)); QOrganizerItemSaveRequest isr; QVERIFY(isr.type() == QOrganizerAbstractRequest::ItemSaveRequest); // initial state - not started, no manager. QVERIFY(!isr.isActive()); QVERIFY(!isr.isFinished()); QVERIFY(!isr.start()); QVERIFY(!isr.cancel()); QVERIFY(!isr.waitForFinished()); // save a new item int originalCount = oim->itemIds().size(); QOrganizerTodo testTodo; QOrganizerItemDescription description; description.setDescription("Test todo"); testTodo.saveDetail(&description); QList saveList; saveList << testTodo; isr.setManager(oim.data()); QCOMPARE(isr.manager(), oim.data()); QVERIFY(!isr.isActive()); QVERIFY(!isr.isFinished()); QVERIFY(!isr.cancel()); QVERIFY(!isr.waitForFinished()); qRegisterMetaType("QOrganizerItemSaveRequest*"); QThreadSignalSpy spy(&isr, SIGNAL(stateChanged(QOrganizerAbstractRequest::State))); isr.setItem(testTodo); QCOMPARE(isr.items(), saveList); QVERIFY(!isr.cancel()); // not started QVERIFY(isr.start()); QVERIFY((isr.isActive() && isr.state() == QOrganizerAbstractRequest::ActiveState) || isr.isFinished()); //QVERIFY(isr.isFinished() || !isr.start()); // already started. // thread scheduling means this is untestable QVERIFY(isr.waitForFinished()); QVERIFY(isr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList expected = isr.items(); QCOMPARE(expected.size(), 1); QList result; result << oim->item(expected.first().id()); //some backends add extra fields, so this doesn't work: //QCOMPARE(result, expected); // XXX: really, we should use isSuperset() from tst_QOrganizerManager, but this will do for now: QVERIFY(result.first().detail(QOrganizerItemDetail::TypeDescription) == description); QCOMPARE(oim->itemIds().size(), originalCount + 1); // update a previously saved item QOrganizerItemPriority priority = result.first().detail(QOrganizerItemDetail::TypePriority); priority.setPriority(QOrganizerItemPriority::LowestPriority); testTodo = result.first(); testTodo.saveDetail(&priority); saveList.clear(); saveList << testTodo; isr.setItems(saveList); QCOMPARE(isr.items(), saveList); QVERIFY(!isr.cancel()); // not started QVERIFY(isr.start()); QVERIFY((isr.isActive() && isr.state() == QOrganizerAbstractRequest::ActiveState) || isr.isFinished()); //QVERIFY(isr.isFinished() || !isr.start()); // already started. // thread scheduling means this is untestable QVERIFY(isr.waitForFinished()); QVERIFY(isr.error() == QOrganizerManager::NoError); // if this fails, it means that the backend doesn't support Priority... QVERIFY(isr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); expected = isr.items(); result.clear(); result << oim->item(expected.first().id()); QVERIFY(compareItemLists(result, expected)); // save empty list QList itemList; QOrganizerItemSaveRequest isr1; isr1.setManager(oim.data()); isr1.setItems(itemList); isr1.start(); isr1.waitForFinished(); QVERIFY(isr1.isFinished()); QVERIFY(isr1.error() == QOrganizerManager::NoError); //here we can't compare the whole item details, testTodo would be updated by async call because we just use QThreadSignalSpy to receive signals. //QVERIFY(containsIgnoringTimestamps(result, testTodo)); // XXX: really, we should use isSuperset() from tst_QOrganizerManager, but this will do for now: QVERIFY(result.first().detail(QOrganizerItemDetail::TypePriority).value(QOrganizerItemPriority::FieldPriority) == priority.priority()); QCOMPARE(oim->itemIds().size(), originalCount + 1); #if defined(QT_NO_JSONDB) // cancelling QOrganizerItem temp = testTodo; temp.setDisplayLabel("should not get saved"); saveList.clear(); saveList << temp; isr.setItems(saveList); int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. while (true) { QVERIFY(!isr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); QVERIFY(isr.start()); if (!isr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. isr.waitForFinished(); saveList = isr.items(); if (oim->itemIds().size() > (originalCount + 1) && !oim->removeItem(saveList.at(0).id())) { QSKIP("Unable to remove saved item to test cancellation of item save request"); } saveList.clear(); saveList << temp; isr.setItems(saveList); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } spy.clear(); continue; } // if we get here, then we are cancelling the request. QVERIFY(isr.waitForFinished()); QVERIFY(isr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); // verify that the changes were not saved expected.clear(); QList allItems = oim->itemIds(); for (int i = 0; i < allItems.size(); i++) { expected.append(oim->item(allItems.at(i))); } QVERIFY(!expected.contains(temp)); QCOMPARE(oim->itemIds().size(), originalCount + 1); break; } // restart, and wait for progress after cancel. while (true) { QVERIFY(!isr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); QVERIFY(isr.start()); if (!isr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. isr.waitForFinished(); saveList = isr.items(); if (oim->itemIds().size() > (originalCount + 1) && !oim->removeItem(saveList.at(0).id())) { QSKIP("Unable to remove saved item to test cancellation of item save request"); } saveList.clear(); saveList << temp; isr.setItems(saveList); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } spy.clear(); continue; } isr.waitForFinished(); // now wait until finished (if it hasn't already). QVERIFY(isr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); // verify that the changes were not saved expected.clear(); QList allItems = oim->itemIds(); for (int i = 0; i < allItems.size(); i++) { expected.append(oim->item(allItems.at(i))); } QVERIFY(!expected.contains(temp)); QCOMPARE(oim->itemIds().size(), originalCount + 1); break; } #endif } void tst_QOrganizerItemAsync::itemListSave() { QFETCH(QString, uri); QScopedPointer oim(prepareModel(uri)); QOrganizerItemSaveRequest isr; QVERIFY(isr.type() == QOrganizerAbstractRequest::ItemSaveRequest); // item with invalid collection id cannot be saved QOrganizerCollection collection; oim->saveCollection(&collection); QOrganizerCollectionId invalidCollectionId = collection.id(); oim->removeCollection(invalidCollectionId); // initial state - not started, no manager. QVERIFY(!isr.isActive()); QVERIFY(!isr.isFinished()); QVERIFY(!isr.start()); QVERIFY(!isr.cancel()); QVERIFY(!isr.waitForFinished()); // save a list of items where one in the middle is invalid QOrganizerEvent e0, e1, e2, e3, e4; e0.setDisplayLabel("Test Event 0"); e1.setDisplayLabel("Test Event 1"); e2.setDisplayLabel("Test Event 2"); e3.setDisplayLabel("Test Event 3"); e4.setDisplayLabel("Test Event 4"); e2.setCollectionId(invalidCollectionId); QList saveList; saveList << e0 << e1 << e2 << e3 << e4; isr.setManager(oim.data()); QCOMPARE(isr.manager(), oim.data()); QVERIFY(!isr.isActive()); QVERIFY(!isr.isFinished()); QVERIFY(!isr.cancel()); QVERIFY(!isr.waitForFinished()); qRegisterMetaType("QOrganizerItemSaveRequest*"); QThreadSignalSpy spy(&isr, SIGNAL(stateChanged(QOrganizerAbstractRequest::State))); isr.setItems(saveList); QVERIFY(!isr.cancel()); // not started QVERIFY(isr.start()); QVERIFY((isr.isActive() && isr.state() == QOrganizerAbstractRequest::ActiveState) || isr.isFinished()); //QVERIFY(isr.isFinished() || !isr.start()); // already started. // thread scheduling means this is untestable QVERIFY(isr.waitForFinished()); QVERIFY(isr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList expected = isr.items(); QCOMPARE(expected.size(), 5); QVERIFY(isr.error() != QOrganizerManager::NoError); QVERIFY(isr.errorMap().contains(2)); QVERIFY(!expected[0].id().isNull()); QVERIFY(!expected[1].id().isNull()); QVERIFY(expected[2].id().isNull()); // Should not have been saved QVERIFY(!expected[3].id().isNull()); QVERIFY(!expected[4].id().isNull()); QVERIFY(oim->item(expected[0].id()).displayLabel() == expected[0].displayLabel()); QVERIFY(oim->item(expected[1].id()).displayLabel() == expected[1].displayLabel()); QVERIFY(oim->item(expected[3].id()).displayLabel() == expected[3].displayLabel()); QVERIFY(oim->item(expected[4].id()).displayLabel() == expected[4].displayLabel()); // save a list of items where all items are invalid QOrganizerTodo t0, t1; t0.setDisplayLabel("Test Todo 0"); t1.setDisplayLabel("Test Todo 1"); t0.setCollectionId(invalidCollectionId); t1.setCollectionId(invalidCollectionId); QList invalidSaveList; invalidSaveList << t0 << t1; isr.setManager(oim.data()); QCOMPARE(isr.manager(), oim.data()); QVERIFY(!isr.isInactive()); QVERIFY(!isr.isActive()); QVERIFY(isr.isFinished()); QVERIFY(!isr.cancel()); QVERIFY(isr.waitForFinished()); //qRegisterMetaType("QOrganizerItemSaveRequest*"); isr.setItems(invalidSaveList); QVERIFY(!isr.cancel()); // not started QVERIFY(isr.start()); QVERIFY((isr.isActive() && isr.state() == QOrganizerAbstractRequest::ActiveState) || isr.isFinished()); //QVERIFY(isr.isFinished() || !isr.start()); // already started. // thread scheduling means this is untestable QVERIFY(isr.waitForFinished()); QVERIFY(isr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); expected = isr.items(); QCOMPARE(expected.size(), 2); QVERIFY(isr.error() != QOrganizerManager::NoError); QVERIFY(isr.errorMap().contains(0)); QVERIFY(isr.errorMap().contains(1)); QVERIFY(expected[0].id().isNull()); QVERIFY(expected[1].id().isNull()); } #if defined(QT_NO_JSONDB) void tst_QOrganizerItemAsync::itemPartialSave() { // QFETCH(QString, uri); // QScopedPointer oim(prepareModel(uri)); // // XXX TODO: partial save for organizer items as well as items!!! // QList items(oim->items()); // QList originalItems(items); // QCOMPARE(items.count(), 3); // QOrganizerItemId aId = items[0].id(); // QOrganizerItemId bId = items[1].id(); // QOrganizerItemId cId = items[2].id(); // // Test 1: saving a item with a changed detail masked out does nothing // QOrganizerItemPriority priority(items[0].detail()); // priority.setPriority(QOrganizerItemPriority::LowPriority); // items[0].saveDetail(&priority); // QOrganizerItemSaveRequest isr; // isr.setManager(oim.data()); // isr.setItems(items); // isr.setDefinitionMask(QStringList(QOrganizerItemEmailAddress::DefinitionName)); // qRegisterMetaType("QOrganizerItemSaveRequest*"); // QThreadSignalSpy spy(&isr, SIGNAL(stateChanged(QOrganizerAbstractRequest::State))); // QVERIFY(isr.start()); // QVERIFY((isr.isActive() && isr.state() == QOrganizerAbstractRequest::ActiveState) || isr.isFinished()); // QVERIFY(isr.waitForFinished()); // QVERIFY(isr.isFinished()); // QVERIFY(spy.count() >= 1); // active + finished progress signals // spy.clear(); // QCOMPARE(isr.error(), QOrganizerManager::NoError); // QVERIFY(isr.errorMap().isEmpty()); // items[0] = oim->item(aId); // QCOMPARE(items[0].detail().priority(), // originalContacts[0].detail().priority()); // // Test 2: saving a item with a changed detail in the mask changes it // QOrganizerItemEmailAddress email; // email.setEmailAddress("me@example.com"); // items[1].saveDetail(&email); // isr.setItems(items); // isr.setDefinitionMask(QStringList(QOrganizerItemEmailAddress::DefinitionName)); // QVERIFY(isr.start()); // QVERIFY(isr.waitForFinished()); // QCOMPARE(isr.error(), QOrganizerManager::NoError); // QVERIFY(isr.errorMap().isEmpty()); // items[1] = oim->item(bId); // QCOMPARE(items[1].detail().emailAddress(), QString("me@example.com")); // // 3) Remove an email address and a phone number // QCOMPARE(items[1].details().count(), 1); // QCOMPARE(items[1].details().count(), 1); // QVERIFY(items[1].removeDetail(&email)); // phn = items[1].detail(); // QVERIFY(items[1].removeDetail(&priority)); // QVERIFY(items[1].details().count() == 0); // QVERIFY(items[1].details().count() == 0); // isr.setItems(items); // isr.setDefinitionMask(QStringList(QOrganizerItemEmailAddress::DefinitionName)); // QVERIFY(isr.start()); // QVERIFY(isr.waitForFinished()); // QCOMPARE(isr.error(), QOrganizerManager::NoError); // QVERIFY(isr.errorMap().isEmpty()); // items[1] = oim->item(bId); // QCOMPARE(items[1].details().count(), 0); // QCOMPARE(items[1].details().count(), 1); // // 4 - New item, no details in the mask // QOrganizerItem newContact; // newContact.saveDetail(&email); // newContact.saveDetail(&priority); // items.append(newContact); // isr.setItems(items); // isr.setDefinitionMask(QStringList(QOrganizerItemOnlineAccount::DefinitionName)); // QVERIFY(isr.start()); // QVERIFY(isr.waitForFinished()); // QCOMPARE(isr.error(), QOrganizerManager::NoError); // QVERIFY(isr.errorMap().isEmpty()); // items = isr.items(); // QCOMPARE(items.size()-1, 3); // Just check that we are dealing with the item at index 3 // QOrganizerItemId dId = items[3].id(); // items[3] = oim->item(dId); // QVERIFY(items[3].details().count() == 0); // not saved // QVERIFY(items[3].details().count() == 0); // not saved // // 5 - New item, some details in the mask // QVERIFY(newContact.id() == 0); // QVERIFY(newContact.details().count() == 1); // QVERIFY(newContact.details().count() == 1); // items.append(newContact); // isr.setItems(items); // isr.setDefinitionMask(QStringList(QOrganizerItemPriority::DefinitionName)); // QVERIFY(isr.start()); // QVERIFY(isr.waitForFinished()); // QCOMPARE(isr.error(), QOrganizerManager::NoError); // QVERIFY(isr.errorMap().isEmpty()); // items = isr.items(); // QCOMPARE(items.size()-1, 4); // Just check that we are dealing with the item at index 4 // QOrganizerItemId eId = items[4].id(); // items[4] = oim->item(eId); // QCOMPARE(items[4].details().count(), 0); // not saved // QCOMPARE(items[4].details().count(), 1); // saved // // 6) Have a bad manager uri in the middle followed by a save error // QOrganizerItemId id3(items[3].id()); // QOrganizerItemId badId(id3); // badId.setManagerUri(QString()); // items[3].setId(badId); // QOrganizerItemDetail badDetail("BadDetail"); // badDetail.setValue("BadField", "BadValue"); // items[4].saveDetail(&badDetail); // isr.setItems(items); // isr.setDefinitionMask(QStringList("BadDetail")); // QVERIFY(isr.start()); // QVERIFY(isr.waitForFinished()); // QVERIFY(isr.error() != QOrganizerManager::NoError); // QMap errorMap(isr.errorMap()); // QCOMPARE(errorMap.count(), 2); // QCOMPARE(errorMap[3], QOrganizerManager::DoesNotExistError); // QCOMPARE(errorMap[4], QOrganizerManager::InvalidDetailError); // // 7) Have a non existing item in the middle followed by a save error // badId = id3; // badId.setLocalId(987234); // something nonexistent (hopefully) // items[3].setId(badId); // isr.setItems(items); // isr.setDefinitionMask(QStringList("BadDetail")); // QVERIFY(isr.start()); // QVERIFY(isr.waitForFinished()); // QVERIFY(isr.error() != QOrganizerManager::NoError); // errorMap = isr.errorMap(); // QCOMPARE(errorMap.count(), 2); // QCOMPARE(errorMap[3], QOrganizerManager::DoesNotExistError); // QCOMPARE(errorMap[4], QOrganizerManager::InvalidDetailError); // // XXX TODO: partial save for organizer items as well as items!!! } #endif void tst_QOrganizerItemAsync::collectionFetch() { QFETCH(QString, uri); QScopedPointer oim(prepareModel(uri)); QOrganizerCollectionFetchRequest cfr; QVERIFY(cfr.type() == QOrganizerAbstractRequest::CollectionFetchRequest); // initial state - not started, no manager. QVERIFY(!cfr.isActive()); QVERIFY(!cfr.isFinished()); QVERIFY(!cfr.start()); QVERIFY(!cfr.cancel()); QVERIFY(!cfr.waitForFinished()); // retrieve all collections. cfr.setManager(oim.data()); QCOMPARE(cfr.manager(), oim.data()); QVERIFY(!cfr.isActive()); QVERIFY(!cfr.isFinished()); QVERIFY(!cfr.cancel()); QVERIFY(!cfr.waitForFinished()); qRegisterMetaType("QOrganizerCollectionFetchRequest*"); QThreadSignalSpy spy(&cfr, SIGNAL(stateChanged(QOrganizerAbstractRequest::State))); QVERIFY(!cfr.cancel()); // not started QVERIFY(cfr.start()); //QVERIFY(cfr.isFinished() || !cfr.start()); // already started. // thread scheduling means this is untestable QVERIFY((cfr.isActive() && cfr.state() == QOrganizerAbstractRequest::ActiveState) || cfr.isFinished()); QVERIFY(cfr.waitForFinished()); QVERIFY(cfr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList syncCols = oim->collections(); QList cols = cfr.collections(); QCOMPARE(cols.size(), syncCols.size()); for (int i = 0; i < cols.size(); i++) { QOrganizerCollection curr = cols.at(i); QVERIFY(syncCols.contains(curr)); } #if defined(QT_NO_JSONDB) // cancelling int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. while (true) { QVERIFY(!cfr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); QVERIFY(cfr.start()); if (!cfr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. spy.clear(); cfr.waitForFinished(); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } continue; } // if we get here, then we are cancelling the request. QVERIFY(cfr.waitForFinished()); QVERIFY(cfr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); break; } // restart, and wait for progress after cancel. while (true) { QVERIFY(!cfr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); QVERIFY(cfr.start()); if (!cfr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. cfr.waitForFinished(); bailoutCount -= 1; spy.clear(); if (!bailoutCount) { //qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } continue; } cfr.waitForFinished(); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); QVERIFY(!cfr.isActive()); QVERIFY(cfr.state() == QOrganizerAbstractRequest::CanceledState); break; } #endif } void tst_QOrganizerItemAsync::collectionRemove() { QFETCH(QString, uri); QScopedPointer oim(prepareModel(uri)); QOrganizerCollectionRemoveRequest crr; QVERIFY(crr.type() == QOrganizerAbstractRequest::CollectionRemoveRequest); // initial state - not started, no manager. QVERIFY(!crr.isActive()); QVERIFY(!crr.isFinished()); QVERIFY(!crr.start()); QVERIFY(!crr.cancel()); QVERIFY(!crr.waitForFinished()); // specific collection set QOrganizerCollectionId removeId = oim->collections().last().id(); if (oim->defaultCollection().id() == removeId) removeId = oim->collections().first().id(); crr.setCollectionId(removeId); QVERIFY(crr.collectionIds() == QList() << removeId); int originalCount = oim->collections().size(); crr.setManager(oim.data()); QCOMPARE(crr.manager(), oim.data()); QVERIFY(!crr.isActive()); QVERIFY(!crr.isFinished()); QVERIFY(!crr.cancel()); QVERIFY(!crr.waitForFinished()); qRegisterMetaType("QOrganizerCollectionRemoveRequest*"); QThreadSignalSpy spy(&crr, SIGNAL(stateChanged(QOrganizerAbstractRequest::State))); QVERIFY(!crr.cancel()); // not started QVERIFY(crr.start()); QVERIFY((crr.isActive() &&crr.state() == QOrganizerAbstractRequest::ActiveState) || crr.isFinished()); //QVERIFY(crr.isFinished() || !crr.start()); // already started. // thread scheduling means this is untestable QVERIFY(crr.waitForFinished()); QVERIFY(crr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QCOMPARE(oim->collections().size(), originalCount - 1); // should have removed that particular collection. QVERIFY(crr.error() == QOrganizerManager::NoError); QVERIFY(crr.errorMap().isEmpty()); // remove all collections QList allCollectionIds; QList allCollections = oim->collections(); for (int i = 0; i < allCollections.size(); ++i) allCollectionIds << allCollections.at(i).id(); crr.setCollectionIds(allCollectionIds); QVERIFY(!crr.cancel()); // not started QVERIFY(crr.start()); QVERIFY((crr.isActive() && crr.state() == QOrganizerAbstractRequest::ActiveState) || crr.isFinished()); //QVERIFY(crr.isFinished() || !crr.start()); // already started. // thread scheduling means this is untestable QVERIFY(crr.waitForFinished()); QVERIFY(crr.isFinished()); QVERIFY(oim->collections().size() >= 1); // at least one collection must be left, since default collection cannot be removed. QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); // remove empty list QList collectionIdList; QOrganizerCollectionRemoveRequest crr1; crr1.setManager(oim.data()); crr1.setCollectionIds(collectionIdList); crr1.start(); crr1.waitForFinished(); QVERIFY(crr1.isFinished()); QVERIFY(crr1.error() == QOrganizerManager::NoError); #if defined(QT_NO_JSONDB) // cancelling QOrganizerCollection temp; temp.setMetaData(QOrganizerCollection::KeyDescription, "Should not be removed!"); oim->saveCollection(&temp); crr.setCollectionId(temp.id()); int collectionCount = oim->collections().size(); int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. while (true) { QVERIFY(!crr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); QVERIFY(spy.count() == 0); QVERIFY(crr.start()); if (!crr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. crr.waitForFinished(); temp.setId(QOrganizerCollectionId()); if (!oim->saveCollection(&temp)) { QSKIP("Unable to save temporary item for remove request cancellation test!"); } crr.setCollectionId(temp.id()); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } spy.clear(); continue; } // if we get here, then we are cancelling the request. QVERIFY(crr.waitForFinished()); QVERIFY(crr.isCanceled()); QCOMPARE(oim->collections().size(), collectionCount); // temp collection should not have been removed QList removeCollectionIds; QList removeCollections = oim->collections(); for (int i = 0; i < removeCollections.size(); ++i) removeCollectionIds << removeCollections.at(i).id(); QVERIFY(containsAllCollectionIds(removeCollectionIds, crr.collectionIds())); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); break; } // restart, and wait for progress after cancel. while (true) { QVERIFY(!crr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); QVERIFY(crr.start()); if (!crr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. crr.waitForFinished(); temp.setId(QOrganizerCollectionId()); if (!oim->saveCollection(&temp)) { QSKIP("Unable to save temporary item for remove request cancellation test!"); } crr.setCollectionId(temp.id()); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } spy.clear(); continue; } crr.waitForFinished(); QVERIFY(crr.isCanceled()); QCOMPARE(oim->collections().size(), collectionCount); // temp collection should not have been removed QList removeCollectionIds; QList removeCollections = oim->collections(); for (int i = 0; i < removeCollections.size(); ++i) removeCollectionIds << removeCollections.at(i).id(); QVERIFY(containsAllCollectionIds(removeCollectionIds, crr.collectionIds())); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); break; } // now clean up our temp collection. oim->removeCollection(temp.id()); #endif } void tst_QOrganizerItemAsync::collectionSave() { QFETCH(QString, uri); QScopedPointer oim(prepareModel(uri)); QOrganizerCollectionSaveRequest csr; QVERIFY(csr.type() == QOrganizerAbstractRequest::CollectionSaveRequest); // initial state - not started, no manager. QVERIFY(!csr.isActive()); QVERIFY(!csr.isFinished()); QVERIFY(!csr.start()); QVERIFY(!csr.cancel()); QVERIFY(!csr.waitForFinished()); // save a new item int originalCount = oim->collections().size(); QOrganizerCollection testCollection; testCollection.setMetaData(QOrganizerCollection::KeyDescription, "test description"); testCollection.setMetaData(QOrganizerCollection::KeyName, "New collection"); QList saveList; saveList << testCollection; csr.setManager(oim.data()); QCOMPARE(csr.manager(), oim.data()); QVERIFY(!csr.isActive()); QVERIFY(!csr.isFinished()); QVERIFY(!csr.cancel()); QVERIFY(!csr.waitForFinished()); qRegisterMetaType("QOrganizerCollectionSaveRequest*"); QThreadSignalSpy spy(&csr, SIGNAL(stateChanged(QOrganizerAbstractRequest::State))); csr.setCollection(testCollection); QCOMPARE(csr.collections(), saveList); QVERIFY(!csr.cancel()); // not started QVERIFY(csr.start()); QVERIFY((csr.isActive() && csr.state() == QOrganizerAbstractRequest::ActiveState) || csr.isFinished()); //QVERIFY(csr.isFinished() || !csr.start()); // already started. // thread scheduling means this is untestable QVERIFY(csr.waitForFinished()); QVERIFY(csr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); QList expected = csr.collections(); QCOMPARE(expected.size(), 1); QList result; result << oim->collection(csr.collections().at(0).id()); // find the saved one, compare. foreach (const QOrganizerCollection &col, result) QVERIFY(col.id() == expected.at(0).id()); // update a previously saved collection QVERIFY(!result.isEmpty()); // make sure that we were able to retrieve the required collection. testCollection = result.first(); testCollection.setMetaData(QOrganizerCollection::KeyName, "test name"); saveList.clear(); saveList << testCollection; csr.setCollections(saveList); QCOMPARE(csr.collections(), saveList); QVERIFY(!csr.cancel()); // not started QVERIFY(csr.start()); QVERIFY((csr.isActive() && csr.state() == QOrganizerAbstractRequest::ActiveState) || csr.isFinished()); //QVERIFY(csr.isFinished() || !csr.start()); // already started. // thread scheduling means this is untestable QVERIFY(csr.waitForFinished()); QVERIFY(csr.isFinished()); QVERIFY(spy.count() >= 1); // active + finished progress signals spy.clear(); expected = csr.collections(); result.clear(); result = oim->collections(); // find the saved one, compare. foreach (const QOrganizerCollection& col, result) { if (col.id() == expected.at(0).id()) { QVERIFY(col == expected.at(0)); // XXX TODO: if we change the semantic so that save merely updates the id...? } } QCOMPARE(oim->collections().size(), originalCount + 1); // ie shouldn't have added an extra one (would be +2) QVERIFY(csr.error() == QOrganizerManager::NoError); QVERIFY(csr.errorMap().isEmpty()); // save empty list QList collectionList; QOrganizerCollectionSaveRequest csr1; csr1.setManager(oim.data()); csr1.setCollections(collectionList); csr1.start(); csr1.waitForFinished(); QVERIFY(csr1.isFinished()); QVERIFY(csr1.error() == QOrganizerManager::NoError); #if defined(QT_NO_JSONDB) // cancelling QOrganizerCollection temp; temp.setMetaData(testCollection.metaData()); temp.setExtendedMetaData("test", "shouldn't be saved"); saveList.clear(); saveList << temp; csr.setCollections(saveList); int bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; // attempt to cancel 40 times. If it doesn't work due to threading, bail out. while (true) { QVERIFY(!csr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); QVERIFY(csr.start()); if (!csr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. csr.waitForFinished(); saveList = csr.collections(); if (oim->collections().size() > (originalCount + 1) && !oim->removeCollection(saveList.at(0).id())) { QSKIP("Unable to remove saved collection to test cancellation of collection save request"); } saveList.clear(); saveList << temp; csr.setCollections(saveList); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } spy.clear(); continue; } // if we get here, then we are cancelling the request. QVERIFY(csr.waitForFinished()); QVERIFY(csr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); // verify that the changes were not saved expected.clear(); QList allCollections = oim->collections(); QVERIFY(!allCollections.contains(temp)); // should NOT contain it since it was cancelled. QCOMPARE(allCollections.size(), originalCount + 1); break; } // restart, and wait for progress after cancel. while (true) { QVERIFY(!csr.cancel()); // not started FILL_QUEUE_WITH_FETCH_REQUESTS(oim.data()); QVERIFY(csr.start()); if (!csr.cancel()) { // due to thread scheduling, async cancel might be attempted // after the request has already finished.. so loop and try again. csr.waitForFinished(); saveList = csr.collections(); if (oim->collections().size() > (originalCount + 1) && !oim->removeCollection(saveList.at(0).id())) { QSKIP("Unable to remove saved item to test cancellation of item save request"); } saveList.clear(); saveList << temp; csr.setCollections(saveList); bailoutCount -= 1; if (!bailoutCount) { // qWarning("Unable to test cancelling due to thread scheduling!"); bailoutCount = MAX_OPTIMISTIC_SCHEDULING_LIMIT; break; } spy.clear(); continue; } csr.waitForFinished(); // now wait until finished (if it hasn't already). QVERIFY(csr.isCanceled()); QVERIFY(spy.count() >= 1); // active + cancelled progress signals spy.clear(); // verify that the changes were not saved expected.clear(); QList allCollections = oim->collections(); QVERIFY(!allCollections.contains(temp)); QCOMPARE(oim->collections().size(), originalCount + 1); break; } #endif } void tst_QOrganizerItemAsync::testQuickDestruction() { QFETCH(QString, uri); // in this test, we create a manager, fire off a request, and delete the manager, all in quick succession // this is to test for segfaults etc. for (int i = 0; i < 10; i++) { QOrganizerItemFetchRequest ifr; QOrganizerManager *cm = prepareModel(uri); ifr.setManager(cm); ifr.start(); delete cm; } // in this test, we create a manager, fire off a request, delete the request, then delete the manager, all in quick succession // this is to test for segfaults, etc. for (int i = 0; i < 10; i++) { QOrganizerItemFetchRequest *ifr = new QOrganizerItemFetchRequest; QOrganizerManager *cm = prepareModel(uri); ifr->setManager(cm); ifr->start(); delete ifr; delete cm; } // in this test, we create a manager, fire off a request, delete the manager, then delete the request, all in quick succession // this is to test for segfaults, etc. for (int i = 0; i < 10; i++) { QOrganizerItemFetchRequest *ifr = new QOrganizerItemFetchRequest; QOrganizerManager *cm = prepareModel(uri); ifr->setManager(cm); ifr->start(); delete cm; delete ifr; } // in this test, we create a manager, fire off a request, and delete the request, all in quick succession // this is to test for segfaults, etc. QOrganizerManager *cm = prepareModel(uri); for (int i = 0; i < 10; i++) { QOrganizerItemFetchRequest *ifr = new QOrganizerItemFetchRequest; ifr->setManager(cm); ifr->start(); delete ifr; } delete cm; } void tst_QOrganizerItemAsync::threadDelivery() { QFETCH(QString, uri); QScopedPointer oim(prepareModel(uri)); m_mainThreadId = oim->thread()->currentThreadId(); m_resultsAvailableSlotThreadId = m_mainThreadId; // now perform a fetch request and check that the progress is delivered to the correct thread. QOrganizerItemFetchRequest *req = new QOrganizerItemFetchRequest; req->setManager(oim.data()); connect(req, SIGNAL(resultsAvailable()), this, SLOT(resultsAvailableReceived())); req->start(); int totalWaitTime = 0; QTest::qWait(1); // force it to process events at least once. while (req->state() != QOrganizerAbstractRequest::FinishedState) { // ensure that the progress signal was delivered to the main thread. QCOMPARE(m_mainThreadId, m_resultsAvailableSlotThreadId); QTest::qWait(5); // spin until done totalWaitTime += 5; // break after 30 seconds. if (totalWaitTime > 30000) { delete req; QSKIP("Asynchronous request not complete after 30 seconds!"); } } // ensure that the progress signal was delivered to the main thread. QCOMPARE(m_mainThreadId, m_resultsAvailableSlotThreadId); delete req; } void tst_QOrganizerItemAsync::testDebugStreamOut() { // QOrganizerItemFetchHint fetchHint; // // Testing the empty case // QTest::ignoreMessage(QtDebugMsg, "QOrganizerItemFetchHint(detailDefinitionsHint=() ,optimizationHints=0)"); // qDebug() << fetchHint; // // Testing the completely filled case // fetchHint.setDetailDefinitionsHint(QStringList(QOrganizerItemDescription::DefinitionName)); // fetchHint.setOptimizationHints(QOrganizerItemFetchHint::NoBinaryBlobs); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerItemFetchHint(detailDefinitionsHint=(\"Description\") ,optimizationHints=4)"); // qDebug() << fetchHint; // // Testing QOrganizerItemSaveRequest // QOrganizerItemSaveRequest isr; // // Testing the empty case // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemSaveRequest(\n* items=() ,\n* definitionMask=() ,\n* errorMap=QMap() \n))"); // qDebug() << isr; // // Testing the filled-in case // isr.setDefinitionMask(QStringList("BadDetail")); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemSaveRequest(\n* items=() ,\n* definitionMask=(\"BadDetail\") ,\n* errorMap=QMap() \n))"); // qDebug() << isr; // // Testing QOrganizerItemSaveRequest // QOrganizerItemFetchRequest ifr; // // Testing the empty case // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemFetchRequest(\n* items=() ,\n* filter=QOrganizerItemFilter((null)),\n* sorting=() ,\n* startDate=QDateTime(\"\") ,\n* endDate=QDateTime(\"\") ,\n* fetchHint=QOrganizerItemFetchHint(detailDefinitionsHint=() ,optimizationHints=0),\n* maxCount=-1\n))"); // qDebug() << ifr; // // Testing the filled-in case // QOrganizerItemFilter fil; // ifr.setFilter(fil); // QOrganizerItemSortOrder sortOrder; // sortOrder.setDetailDefinitionName(QOrganizerItemPriority::DefinitionName, QOrganizerItemPriority::FieldPriority); // QList sorting; // sorting.append(sortOrder); // ifr.setSorting(sorting); // fetchHint.setDetailDefinitionsHint(QStringList(QOrganizerItemDescription::DefinitionName)); // ifr.setFetchHint(fetchHint); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemFetchRequest(\n* items=() ,\n* filter=QOrganizerItemFilter((null)),\n* sorting=(QOrganizerItemSortOrder(detailDefinitionName=\"Priority\",detailFieldName=\"Priority\",blankPolicy=1,direction=0,caseSensitivity=1)) ,\n* startDate=QDateTime(\"\") ,\n* endDate=QDateTime(\"\") ,\n* fetchHint=QOrganizerItemFetchHint(detailDefinitionsHint=(\"Description\") ,optimizationHints=4),\n* maxCount=-1\n))"); // qDebug() << ifr; // // Testing QOrganizerItemFetchForExportRequest // QOrganizerItemFetchForExportRequest ifer; // // Testing the empty case // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemFetchForExportRequest(\n* items=() ,\n* filter=QOrganizerItemFilter((null)),\n* sorting=() ,\n* startDate=QDateTime(\"\") ,\n* endDate=QDateTime(\"\") ,\n* fetchHint=QOrganizerItemFetchHint(detailDefinitionsHint=() ,optimizationHints=0)\n))"); // qDebug() << ifer; // // Testing the filled-in case // ifer.setFilter(fil); // ifer.setSorting(sorting); // ifer.setStartDate(QDateTime()); // ifer.setEndDate(QDateTime()); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemFetchForExportRequest(\n* items=() ,\n* filter=QOrganizerItemFilter((null)),\n* sorting=(QOrganizerItemSortOrder(detailDefinitionName=\"Priority\",detailFieldName=\"Priority\",blankPolicy=1,direction=0,caseSensitivity=1)) ,\n* startDate=QDateTime(\"\") ,\n* endDate=QDateTime(\"\") ,\n* fetchHint=QOrganizerItemFetchHint(detailDefinitionsHint=() ,optimizationHints=0)\n))"); // qDebug() << ifer; // // Testing QOrganizerItemFetchByIdRequest // QOrganizerItemFetchByIdRequest ifbidr; // // Testing the empty case // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemFetchByIdRequest(\n* items=() ,\n* ids=() ,\n* fetchHint=QOrganizerItemFetchHint(detailDefinitionsHint=() ,optimizationHints=0),\n* errorMap=QMap() \n))"); // qDebug() << ifbidr; // // Test the filled-in case // fetchHint.setDetailDefinitionsHint(QStringList(QOrganizerItemDescription::DefinitionName)); // ifbidr.setFetchHint(fetchHint); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemFetchByIdRequest(\n* items=() ,\n* ids=() ,\n* fetchHint=QOrganizerItemFetchHint(detailDefinitionsHint=(\"Description\") ,optimizationHints=4),\n* errorMap=QMap() \n))"); // qDebug() << ifbidr; // // Testing QOrganizerItemOccurrenceFetchRequest // QOrganizerItemOccurrenceFetchRequest iofr; // // Testing the empty case // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemOccurrenceFetchRequest(\n* itemOccurrences=() ,\n* parentItem=QOrganizerItem(QOrganizerItemId((null))) in collection(QOrganizerCollectionId((null))) \n QOrganizerItemDetail(name=\"Type\", key=201754, \"Type\"=QVariant(QString, \"Note\") ),\n* startDate=QDateTime(\"\") ,\n* endDate=QDateTime(\"\") ,\n* fetchHint=QOrganizerItemFetchHint(detailDefinitionsHint=() ,optimizationHints=0),\n* maxOccurrences=-1\n))"); // qDebug() << iofr; // // Testing the filled-in case // QOrganizerItem parent; // iofr.setParentItem(parent); // iofr.setFetchHint(fetchHint); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemOccurrenceFetchRequest(\n* itemOccurrences=() ,\n* parentItem=QOrganizerItem(QOrganizerItemId((null))) in collection(QOrganizerCollectionId((null))) \n QOrganizerItemDetail(name=\"Type\", key=201756, \"Type\"=QVariant(QString, \"Note\") ),\n* startDate=QDateTime(\"\") ,\n* endDate=QDateTime(\"\") ,\n* fetchHint=QOrganizerItemFetchHint(detailDefinitionsHint=(\"Description\") ,optimizationHints=4),\n* maxOccurrences=-1\n))"); // qDebug() << iofr; // // Testing QOrganizerItemRemoveRequest // QOrganizerItemRemoveRequest irr; // // Testing the empty case // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemRemoveRequest(itemIds=() ,errorMap=QMap() \n))"); // qDebug() << irr; // // Testing the filled-in case // QString mgr = "memory"; // QMap params; // params.insert("id", "tst_QOrganizerManager"); // QString uri = QOrganizerManager::buildUri(mgr, params); // QScopedPointer oim(prepareModel(uri)); // qRegisterMetaType >("QList"); // QVERIFY(irr.type() == QOrganizerAbstractRequest::ItemRemoveRequest); // // initial state - not started, no manager. // QVERIFY(!irr.isActive()); // QVERIFY(!irr.isFinished()); // QVERIFY(!irr.start()); // QVERIFY(!irr.cancel()); // QVERIFY(!irr.waitForFinished()); // // fill manager with test data // QOrganizerTodo testTodo1; // QOrganizerItemDisplayLabel label; // label.setLabel("Test todo 1"); // testTodo1.saveDetail(&label); // QVERIFY(oim->saveItem(&testTodo1)); // testTodo1.setId(QOrganizerItemId()); // label.setLabel("Test todo 2"); // testTodo1.saveDetail(&label); // QOrganizerItemComment comment; // comment.setComment("todo comment"); // testTodo1.saveDetail(&comment); // QVERIFY(oim->saveItem(&testTodo1)); // QList allIds(oim->itemIds()); // QVERIFY(!allIds.isEmpty()); // QOrganizerItemId removableId(allIds.first()); // // specific item set // irr.setItemId(removableId); // QVERIFY(irr.itemIds() == QList() << removableId); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemRemoveRequest(itemIds=(QOrganizerItemId(QOrganizerItemMemoryEngineId(1, 2,\"qtorganizer:memory:id=tst_QOrganizerManager\"))) ,errorMap=QMap() \n))"); // qDebug() << irr; // // Testing ItemIdFetchRequest // // Testing the empty case // QOrganizerItemIdFetchRequest iidfr; // QVERIFY(iidfr.type() == QOrganizerAbstractRequest::ItemIdFetchRequest); // // initial state - not started, no manager. // QVERIFY(!iidfr.isActive()); // QVERIFY(!iidfr.isFinished()); // QVERIFY(!iidfr.start()); // QVERIFY(!iidfr.cancel()); // QVERIFY(!iidfr.waitForFinished()); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemIdFetchRequest(\n* itemIds=() ,\n* filter=QOrganizerItemFilter((null)),\n* sorting=() ,\n* startDate=QDateTime(\"\") ,\n* endDate=QDateTime(\"\") \n))"); // qDebug() << iidfr; // // Testing the filled-in case // // "all items" retrieval // iidfr.setManager(oim.data()); // QCOMPARE(iidfr.manager(), oim.data()); // QVERIFY(!iidfr.isActive()); // QVERIFY(!iidfr.isFinished()); // QVERIFY(!iidfr.cancel()); // QVERIFY(!iidfr.waitForFinished()); // qRegisterMetaType("QOrganizerItemIdFetchRequest*"); // QThreadSignalSpy spy(&iidfr, SIGNAL(stateChanged(QOrganizerAbstractRequest::State))); // iidfr.setFilter(fil); // QCOMPARE(iidfr.filter(), fil); // QVERIFY(!iidfr.cancel()); // not started // QVERIFY(iidfr.start()); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemIdFetchRequest(\n* itemIds=(QOrganizerItemId(QOrganizerItemMemoryEngineId(1, 2,\"qtorganizer:memory:id=tst_QOrganizerManager\")), QOrganizerItemId(QOrganizerItemMemoryEngineId(1, 3,\"qtorganizer:memory:id=tst_QOrganizerManager\")), QOrganizerItemId(QOrganizerItemMemoryEngineId(1, 4,\"qtorganizer:memory:id=tst_QOrganizerManager\")), QOrganizerItemId(QOrganizerItemMemoryEngineId(1, 5,\"qtorganizer:memory:id=tst_QOrganizerManager\")), QOrganizerItemId(QOrganizerItemMemoryEngineId(1, 6,\"qtorganizer:memory:id=tst_QOrganizerManager\"))) ,\n* filter=QOrganizerItemFilter((null)),\n* sorting=() ,\n* startDate=QDateTime(\"\") ,\n* endDate=QDateTime(\"\") \n))"); // qDebug() << iidfr; // // Testing QOrganizerItemDetailDefinitionFetchRequest // // Testing the empty case // QOrganizerItemDetailDefinitionFetchRequest dfr; // QVERIFY(dfr.type() == QOrganizerAbstractRequest::DetailDefinitionFetchRequest); // QVERIFY(dfr.itemType() == QString(QLatin1String(QOrganizerItemType::TypeNote))); // ensure ctor sets item type correctly. // dfr.setItemType(QOrganizerItemType::TypeEvent); // QVERIFY(dfr.itemType() == QString(QLatin1String(QOrganizerItemType::TypeEvent))); // // initial state - not started, no manager. // QVERIFY(!dfr.isActive()); // QVERIFY(!dfr.isFinished()); // QVERIFY(!dfr.start()); // QVERIFY(!dfr.cancel()); // QVERIFY(!dfr.waitForFinished()); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemDetailDefinitionFetchRequest(\n* definitionNames=() ,\n* definitions=QMap() ,\n* itemType=\"Event\",\n* errorMap=QMap() \n))"); // qDebug() << dfr; // // Note: testing of the filled-in case has been removed due to issues (expected-message string gets truncated after 1000 characters) // // Testing QOrganizerItemDetailDefinitionSaveRequest // // Testing the empty case // QOrganizerItemDetailDefinitionSaveRequest dsr; // QVERIFY(dsr.type() == QOrganizerAbstractRequest::DetailDefinitionSaveRequest); // QVERIFY(dsr.itemType() == QString(QLatin1String(QOrganizerItemType::TypeNote))); // ensure ctor sets item type correctly // dsr.setItemType(QOrganizerItemType::TypeEvent); // QVERIFY(dsr.itemType() == QString(QLatin1String(QOrganizerItemType::TypeEvent))); // // initial state - not started, no manager. // QVERIFY(!dsr.isActive()); // QVERIFY(!dsr.isFinished()); // QVERIFY(!dsr.start()); // QVERIFY(!dsr.cancel()); // QVERIFY(!dsr.waitForFinished()); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemDetailDefinitionSaveRequest(\n* definitions=() ,\n* itemType=\"Event\",\n* errorMap=QMap() \n))"); // qDebug() << dsr; // // Testing the filled-in case // // save a new detail definition // int originalCount = oim->detailDefinitions(QOrganizerItemType::TypeEvent).keys().size(); // QOrganizerItemDetailDefinition testDef; // testDef.setName("TestDefinitionId"); // QMap fields; // QOrganizerItemDetailFieldDefinition f; // f.setDataType(QVariant::String); // fields.insert("TestDefinitionField", f); // testDef.setFields(fields); // QList saveList; // saveList << testDef; // dsr.setManager(oim.data()); // QCOMPARE(dsr.manager(), oim.data()); // QVERIFY(!dsr.isActive()); // QVERIFY(!dsr.isFinished()); // QVERIFY(!dsr.cancel()); // QVERIFY(!dsr.waitForFinished()); // qRegisterMetaType("QOrganizerItemDetailDefinitionSaveRequest*"); // dsr.setDefinition(testDef); // QCOMPARE(dsr.definitions(), saveList); // QVERIFY(!dsr.cancel()); // not started // QVERIFY(dsr.start()); // QVERIFY((dsr.isActive() && dsr.state() == QOrganizerAbstractRequest::ActiveState) || dsr.isFinished()); // //QVERIFY(dsr.isFinished() || !dsr.start()); // already started. // thread scheduling means this is untestable // QVERIFY(dsr.waitForFinished()); // QVERIFY(dsr.isFinished()); // QVERIFY(spy.count() >= 1); // active + finished progress signals // spy.clear(); // QList expected; // expected << oim->detailDefinition("TestDefinitionId", QOrganizerItemType::TypeEvent); // QList result = dsr.definitions(); // QCOMPARE(expected, result); // QVERIFY(expected.contains(testDef)); // QCOMPARE(oim->detailDefinitions(QOrganizerItemType::TypeEvent).values().size(), originalCount + 1); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemDetailDefinitionSaveRequest(\n* definitions=(QOrganizerItemDetailDefinition(name=\"TestDefinitionId\",isUnique=false,isEmpty=false,fields=QMap((\"TestDefinitionField\", QOrganizerItemDetailFieldDefinition(dataType=10,allowableValues=() ))) )) ,\n* itemType=\"Event\",\n* errorMap=QMap() \n))"); // qDebug() << dsr; // // Testing QOrganizerItemDetailDefinitionRemoveRequest // // Testing the empty case // QOrganizerItemDetailDefinitionRemoveRequest drr; // QVERIFY(drr.type() == QOrganizerAbstractRequest::DetailDefinitionRemoveRequest); // QVERIFY(drr.itemType() == QString(QLatin1String(QOrganizerItemType::TypeNote))); // ensure ctor sets item type correctly. // drr.setItemType(QOrganizerItemType::TypeEvent); // drr.setDefinitionNames(QStringList()); // QVERIFY(drr.itemType() == QString(QLatin1String(QOrganizerItemType::TypeEvent))); // // initial state - not started, no manager. // QVERIFY(!drr.isActive()); // QVERIFY(!drr.isFinished()); // QVERIFY(!drr.start()); // QVERIFY(!drr.cancel()); // QVERIFY(!drr.waitForFinished()); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemDetailDefinitionRemoveRequest(\n* definitionNames=() ,\n* itemType=\"Event\",\n* errorMap=QMap() \n))"); // qDebug() << drr; // // Testing the filled-in case // // specific definition removal // QStringList removeIds; // removeIds << oim->detailDefinitions(QOrganizerItemType::TypeEvent).keys().first(); // drr.setDefinitionName(oim->detailDefinitions(QOrganizerItemType::TypeEvent).keys().first()); // drr.setManager(oim.data()); // QCOMPARE(drr.manager(), oim.data()); // QVERIFY(!drr.isActive()); // QVERIFY(!drr.isFinished()); // QVERIFY(!drr.cancel()); // QVERIFY(!drr.waitForFinished()); // qRegisterMetaType("QOrganizerItemDetailDefinitionRemoveRequest*"); // QVERIFY(drr.definitionNames() == removeIds); // QVERIFY(!drr.cancel()); // not started // QVERIFY(drr.start()); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerItemDetailDefinitionRemoveRequest(\n* definitionNames=(\"AudibleReminder\") ,\n* itemType=\"Event\",\n* errorMap=QMap() \n))"); // qDebug() << drr; // // Testing QOrganizerCollectionFetchRequest // // Testing the empty case // QOrganizerCollectionFetchRequest cfr; // QVERIFY(cfr.type() == QOrganizerAbstractRequest::CollectionFetchRequest); // // initial state - not started, no manager. // QVERIFY(!cfr.isActive()); // QVERIFY(!cfr.isFinished()); // QVERIFY(!cfr.start()); // QVERIFY(!cfr.cancel()); // QVERIFY(!cfr.waitForFinished()); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerCollectionFetchRequest(collections=() ))"); // qDebug() << cfr; // // Testing the filled-in case // // retrieve all collections. // cfr.setManager(oim.data()); // QCOMPARE(cfr.manager(), oim.data()); // QVERIFY(!cfr.isActive()); // QVERIFY(!cfr.isFinished()); // QVERIFY(!cfr.cancel()); // QVERIFY(!cfr.waitForFinished()); // qRegisterMetaType("QOrganizerCollectionFetchRequest*"); // QVERIFY(!cfr.cancel()); // not started // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerCollectionFetchRequest(collections=() ))"); // qDebug() << cfr; // // Testing QOrganizerCollectionRemoveRequest // // Testing the empty case // QOrganizerCollectionRemoveRequest crr; // QVERIFY(crr.type() == QOrganizerAbstractRequest::CollectionRemoveRequest); // // initial state - not started, no manager. // QVERIFY(!crr.isActive()); // QVERIFY(!crr.isFinished()); // QVERIFY(!crr.start()); // QVERIFY(!crr.cancel()); // QVERIFY(!crr.waitForFinished()); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerCollectionRemoveRequest(collectionIds=() ,errorMap=QMap() ))"); // qDebug() << crr; // //Testing the filled-in case // // specific collection set // QOrganizerCollectionId removeId = oim->collections().last().id(); // crr.setCollectionId(removeId); // QVERIFY(crr.collectionIds() == QList() << removeId); // crr.setManager(oim.data()); // QCOMPARE(crr.manager(), oim.data()); // QVERIFY(!crr.isActive()); // QVERIFY(!crr.isFinished()); // QVERIFY(!crr.cancel()); // QVERIFY(!crr.waitForFinished()); // qRegisterMetaType("QOrganizerCollectionRemoveRequest*"); // QVERIFY(!crr.cancel()); // not started // QVERIFY(crr.start()); // QVERIFY((crr.isActive() &&crr.state() == QOrganizerAbstractRequest::ActiveState) || crr.isFinished()); // //QVERIFY(crr.isFinished() || !crr.start()); // already started. // thread scheduling means this is untestable // QVERIFY(crr.waitForFinished()); // QVERIFY(crr.isFinished()); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerCollectionRemoveRequest(collectionIds=(QOrganizerCollectionId(QOrganizerCollectionMemoryEngineId(2,\"qtorganizer:memory:id=tst_QOrganizerManager\"))) ,errorMap=QMap() ))"); // qDebug() << crr; // // Testing QOrganizerCollectionSaveRequest // // Testing the empty case // QOrganizerCollectionSaveRequest csr; // QVERIFY(csr.type() == QOrganizerAbstractRequest::CollectionSaveRequest); // // initial state - not started, no manager. // QVERIFY(!csr.isActive()); // QVERIFY(!csr.isFinished()); // QVERIFY(!csr.start()); // QVERIFY(!csr.cancel()); // QVERIFY(!csr.waitForFinished()); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerCollectionSaveRequest(collections=() ,errorMap=QMap() ))"); // qDebug() << csr; // // Testing the filled-in case // // save a new item // QOrganizerCollection testCollection; // testCollection.setMetaData("description", "test description"); // testCollection.setMetaData(QOrganizerCollection::KeyName, "New collection"); // //saveList << testCollection; // csr.setManager(oim.data()); // QCOMPARE(csr.manager(), oim.data()); // QVERIFY(!csr.isActive()); // QVERIFY(!csr.isFinished()); // QVERIFY(!csr.cancel()); // QVERIFY(!csr.waitForFinished()); // qRegisterMetaType("QOrganizerCollectionSaveRequest*"); // csr.setCollection(testCollection); // QTest::ignoreMessage(QtDebugMsg, "QOrganizerAbstractRequest(QOrganizerCollectionSaveRequest(collections=(QOrganizerCollection(id=QOrganizerCollectionId((null)), \"Name\"=QVariant(QString, \"New collection\") , \"description\"=QVariant(QString, \"test description\") )) ,errorMap=QMap() ))"); // qDebug() << csr; } void tst_QOrganizerItemAsync::resultsAvailableReceived() { QOrganizerItemFetchRequest *req = qobject_cast(QObject::sender()); Q_ASSERT(req); m_resultsAvailableSlotThreadId = req->thread()->currentThreadId(); } void tst_QOrganizerItemAsync::addManagers(QStringList stringlist) { QTest::addColumn("uri"); // retrieve the list of available managers QStringList managers = QOrganizerManager::availableManagers(); // remove ones that we know will not pass if (!stringlist.contains("invalid")) managers.removeAll("invalid"); if (!stringlist.contains("maliciousplugin")) managers.removeAll("maliciousplugin"); if (!stringlist.contains("testdummy")) managers.removeAll("testdummy"); if (!stringlist.contains("skeleton")) managers.removeAll("skeleton"); foreach(QString mgr, managers) { QMap params; QTest::newRow(QString("mgr='%1'").arg(mgr).toLatin1().constData()) << QOrganizerManager::buildUri(mgr, params); if (mgr == "memory") { params.insert("id", "tst_QOrganizerManager"); QTest::newRow(QString("mgr='%1', params").arg(mgr).toLatin1().constData()) << QOrganizerManager::buildUri(mgr, params); } } } QOrganizerManager* tst_QOrganizerItemAsync::prepareModel(const QString& managerUri) { QOrganizerManager* oim = QOrganizerManager::fromUri(managerUri); // XXX TODO: ensure that this is the case: // there should be no items in the database. #if defined(QT_NO_JSONDB) QList toRemove = oim->itemIds(); foreach (const QOrganizerItemId& removeId, toRemove) oim->removeItem(removeId); #endif QOrganizerEvent a, b, c; a.setDisplayLabel("event a"); b.setDisplayLabel("event b"); c.setDisplayLabel("event c"); QOrganizerItemDescription aDescriptionDetail; aDescriptionDetail.setDescription("A Description"); a.saveDetail(&aDescriptionDetail); QOrganizerItemDescription bDescriptionDetail; bDescriptionDetail.setDescription("B Description"); b.saveDetail(&bDescriptionDetail); QOrganizerItemDescription cDescriptionDetail; cDescriptionDetail.setDescription("C Description"); c.saveDetail(&cDescriptionDetail); QOrganizerItemType aTypeDetail; aTypeDetail.setType(QOrganizerItemType::TypeEvent); a.saveDetail(&aTypeDetail); QOrganizerItemType bTypeDetail; bTypeDetail.setType(QOrganizerItemType::TypeEvent); b.saveDetail(&bTypeDetail); QOrganizerItemType cTypeDetail; cTypeDetail.setType(QOrganizerItemType::TypeEvent); c.saveDetail(&cTypeDetail); QOrganizerItemPriority priority; priority.setPriority(QOrganizerItemPriority::HighestPriority); c.saveDetail(&priority); priority.setPriority(QOrganizerItemPriority::VeryHighPriority); b.saveDetail(&priority); priority.setPriority(QOrganizerItemPriority::HighPriority); a.saveDetail(&priority); QOrganizerItemLocation loc; loc.setLabel("test location label"); a.saveDetail(&loc); QSet recurrenceDates; QDate currentDate = QDate::currentDate(); recurrenceDates << currentDate << currentDate.addDays(2) << currentDate.addDays(4); b.setRecurrenceDates(recurrenceDates); oim->saveItem(&a); oim->saveItem(&b); oim->saveItem(&c); QOrganizerCollection testCollection; testCollection.setMetaData(QOrganizerCollection::KeyName, "Test Collection"); testCollection.setMetaData(QOrganizerCollection::KeyDescription, "test collection"); oim->saveCollection(&testCollection); return oim; // TODO: cleanup once test is complete } QTEST_MAIN(tst_QOrganizerItemAsync) #include "tst_qorganizeritemasync.moc" tests/auto/organizer/qorganizeritemasync/unittest/unittest.pro000066400000000000000000000004141233466112000255430ustar00rootroot00000000000000include(../../../auto.pri) TARGET = tst_qorganizeritemasync QT += organizer qtHaveModule(jsondb): QT += jsondb SOURCES += tst_qorganizeritemasync.cpp HEADERS += ../../qorganizermanagerdataholder.h ../../../jsondbprocess.h DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/organizer/qorganizeritemdetail/000077500000000000000000000000001233466112000214115ustar00rootroot00000000000000tests/auto/organizer/qorganizeritemdetail/qorganizeritemdetail.pro000066400000000000000000000001751233466112000263610ustar00rootroot00000000000000include(../../auto.pri) QT += organizer SOURCES += tst_qorganizeritemdetail.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/organizer/qorganizeritemdetail/tst_qorganizeritemdetail.cpp000066400000000000000000000521011233466112000272310ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include //TESTED_COMPONENT=src/organizer QTORGANIZER_USE_NAMESPACE class tst_QOrganizerItemDetail : public QObject { Q_OBJECT public: tst_QOrganizerItemDetail(); virtual ~tst_QOrganizerItemDetail(); public slots: void init(); void cleanup(); private slots: void classHierarchy(); void assignment(); void templates(); void values(); void hash(); void datastream(); void traits(); void keys(); }; tst_QOrganizerItemDetail::tst_QOrganizerItemDetail() { } tst_QOrganizerItemDetail::~tst_QOrganizerItemDetail() { } void tst_QOrganizerItemDetail::init() { } void tst_QOrganizerItemDetail::cleanup() { } /* Test class that doesn't do the right thing */ class NonMacroCustomDetail : public QOrganizerItemDetail { public: NonMacroCustomDetail() : QOrganizerItemDetail(QOrganizerItemDetail::TypeUndefined) {} void doAssign(const QOrganizerItemDetail& other) {assign(other, QOrganizerItemDetail::TypeUndefined);} NonMacroCustomDetail(const QOrganizerItemDetail& other) : QOrganizerItemDetail(other, QOrganizerItemDetail::TypeUndefined) {} }; class NonMacroCustomDetail2 : public QOrganizerItemDetail { public: NonMacroCustomDetail2() : QOrganizerItemDetail(QOrganizerItemDetail::TypeUndefined) {} void doAssign(const QOrganizerItemDetail& other) {assign(other, QOrganizerItemDetail::TypeUndefined);} NonMacroCustomDetail2(const QOrganizerItemDetail& other) : QOrganizerItemDetail(other, QOrganizerItemDetail::TypeUndefined) {} }; void tst_QOrganizerItemDetail::classHierarchy() { QOrganizerItemDetail f1; QOrganizerItemDetail f2; QVERIFY(f1.isEmpty()); QVERIFY(f2.isEmpty()); QOrganizerItemPriority p1; p1.setPriority(QOrganizerItemPriority::VeryHighPriority); QVERIFY(!p1.isEmpty()); QVERIFY(p1.type() == QOrganizerItemDetail::TypePriority); QOrganizerItemComment m1; m1.setComment("Bob"); QVERIFY(!m1.isEmpty()); QVERIFY(m1.type() == QOrganizerItemDetail::TypeComment); QVERIFY(p1 != m1); QVERIFY(f1 == f2); f1 = p1; // f1 is a priority QVERIFY(f1 == p1); f1 = f1; // assign to itself QVERIFY(f1 == f1); QVERIFY(f1 == p1); QVERIFY(f1 != f2); QVERIFY(p1 != f2); p1 = p1; // assign leaf class to itself QVERIFY(p1 == p1); QVERIFY(f1 == p1); QVERIFY(p1 == f1); f2 = f1; // f2 = f1 = priority QVERIFY(f1 == f2); QVERIFY(f2 == f1); QVERIFY(f2 == p1); QVERIFY(f1 == p1); f1 = m1; // f1 = name, f2 = priority QVERIFY(f1 == m1); QVERIFY(f1 != f2); QVERIFY(f2 == p1); QOrganizerItemPriority p2(f2); // p2 = f2 = priority QVERIFY(p1 == p2); QVERIFY(p1 == f2); QCOMPARE(p2.priority(), p1.priority()); QCOMPARE(p2.priority(), QOrganizerItemPriority::VeryHighPriority); p2 = p1; // priority to priority QVERIFY(p1 == p2); QVERIFY(p1 == f2); QCOMPARE(p2.priority(), p1.priority()); QCOMPARE(p2.priority(), QOrganizerItemPriority::VeryHighPriority); p2.setPriority(QOrganizerItemPriority::VeryLowPriority); // NOTE: implicitly shared, this has caused a detach so p1 != 2 QVERIFY(p1 != p2); QVERIFY(p1 == f2); QVERIFY(p2 != f2); QCOMPARE(p2.priority(), QOrganizerItemPriority::VeryLowPriority); QCOMPARE(p1.priority(), QOrganizerItemPriority::VeryHighPriority); /* Bad assignment */ p2 = m1; // assign a comment to a priority QVERIFY(p2 != m1); QVERIFY(p2.type() == QOrganizerItemDetail::TypePriority); QVERIFY(p2.isEmpty()); /* copy ctor */ QOrganizerItemComment m2(m1); QVERIFY(m2 == m1); /* another bad assignment */ m2 = p2; // priority to a comment QVERIFY(m2 != m1); QVERIFY(m2.type() == QOrganizerItemDetail::TypeComment); QVERIFY(m2.isEmpty()); /* Copy ctor from valid type */ QOrganizerItemDetail f3(p2); QVERIFY(f3 == p2); QVERIFY(f3.type() == QOrganizerItemDetail::TypePriority); /* Copy ctor from invalid type */ QOrganizerItemPriority p3(m1); QVERIFY(p3 != m1); QVERIFY(p3.type() == QOrganizerItemDetail::TypePriority); QVERIFY(p3.isEmpty()); /* Copy ctore from invalid type, through base type */ f3 = m1; QOrganizerItemPriority p4(f3); QVERIFY(p4 != f3); QVERIFY(p4.type() == QOrganizerItemDetail::TypePriority); QVERIFY(p4.isEmpty()); /* Try a reference */ p1.setPriority(QOrganizerItemPriority::VeryLowPriority); QOrganizerItemDetail& ref = p1; QVERIFY(p1.priority() == QOrganizerItemPriority::VeryLowPriority); QVERIFY(p1.value(QOrganizerItemPriority::FieldPriority).toInt() == static_cast(QOrganizerItemPriority::VeryLowPriority)); QVERIFY(ref.value(QOrganizerItemPriority::FieldPriority).toInt() == static_cast(QOrganizerItemPriority::VeryLowPriority)); QVERIFY(p1 == ref); QVERIFY(ref == p1); /* Try changing the original */ p1.setPriority(QOrganizerItemPriority::MediumPriority); QVERIFY(p1.priority() == QOrganizerItemPriority::MediumPriority); QVERIFY(p1.value(QOrganizerItemPriority::FieldPriority).toInt() == static_cast(QOrganizerItemPriority::MediumPriority)); QVERIFY(ref.value(QOrganizerItemPriority::FieldPriority).toInt() == static_cast(QOrganizerItemPriority::MediumPriority)); QVERIFY(p1 == ref); QVERIFY(ref == p1); /* Try changing the reference */ ref.setValue(QOrganizerItemPriority::FieldPriority, QOrganizerItemPriority::LowPriority); QVERIFY(p1.priority() == QOrganizerItemPriority::LowPriority); QVERIFY(p1.value(QOrganizerItemPriority::FieldPriority).toInt() == static_cast(QOrganizerItemPriority::LowPriority)); QVERIFY(ref.value(QOrganizerItemPriority::FieldPriority).toInt() == static_cast(QOrganizerItemPriority::LowPriority)); QVERIFY(p1 == ref); QVERIFY(ref == p1); /* Random other test */ NonMacroCustomDetail md; QVERIFY(md.type() == QOrganizerItemDetail::TypeUndefined); QVERIFY(md.setValue(1, "value")); QVERIFY(!md.isEmpty()); md.doAssign(md); // self assignment QVERIFY(!md.isEmpty()); QVERIFY(md.value(1) == "value"); QOrganizerItemDetail mdv; mdv = md; QVERIFY(mdv.type() == QOrganizerItemDetail::TypeUndefined); QVERIFY(mdv.value(1) == "value"); md = mdv; QVERIFY(md.type() == QOrganizerItemDetail::TypeUndefined); QVERIFY(md.value(1) == "value"); NonMacroCustomDetail2 md2; QVERIFY(md2.setValue(1, "value")); QVERIFY(md2.type() == QOrganizerItemDetail::TypeUndefined); QVERIFY(md2.value(1) == "value"); md2.doAssign(md); QVERIFY(md2 == md); md2 = md; QVERIFY(md.type() == QOrganizerItemDetail::TypeUndefined); QVERIFY(md.value(1) == "value"); // Self assignment md2.doAssign(md2); QVERIFY(md2.type() == QOrganizerItemDetail::TypeUndefined); QVERIFY(md2.value(1) == "value"); md.doAssign(md2); QVERIFY(md == md2); // Assigning something else QOrganizerItemPriority pn; pn.setPriority(QOrganizerItemPriority::LowestPriority); md2.doAssign(pn); QVERIFY(md2.isEmpty()); QVERIFY(md2.type() == QOrganizerItemDetail::TypeUndefined); NonMacroCustomDetail mdb(pn); QVERIFY(mdb.isEmpty()); QVERIFY(mdb.type() == QOrganizerItemDetail::TypeUndefined); NonMacroCustomDetail2 md2b(pn); QVERIFY(md2b.isEmpty()); QVERIFY(md2b.type() == QOrganizerItemDetail::TypeUndefined); } void tst_QOrganizerItemDetail::assignment() { QOrganizerItemPriority p1, p2; p1.setPriority(QOrganizerItemPriority::LowPriority); p2.setPriority(QOrganizerItemPriority::HighPriority); QVERIFY(p1 != p2); p1 = p2; QVERIFY(p1 == p2); QOrganizerItemComment c1; c1.setComment("test comment"); QVERIFY(c1 != p1); c1 = p1; QVERIFY(c1 != p1); // assignment across types shouldn't work QVERIFY(c1.comment() == QString()); // should reset the detail QCOMPARE(c1, QOrganizerItemComment()); } void tst_QOrganizerItemDetail::templates() { QOrganizerItem c; QOrganizerItemPriority p1, p2; p1.setPriority(QOrganizerItemPriority::HighPriority); p2.setPriority(QOrganizerItemPriority::HighestPriority); QVERIFY(c.saveDetail(&p1)); QVERIFY(c.saveDetail(&p2)); QList l = c.details(QOrganizerItemDetail::TypePriority); QCOMPARE(l.count(), 2); QCOMPARE(QOrganizerItemPriority(l.at(0)), p1); QCOMPARE(QOrganizerItemPriority(l.at(1)), p2); QList l2 = c.details(QOrganizerItemDetail::TypePriority); QCOMPARE(l2.count(), 2); QCOMPARE(static_cast(l2.at(0)), p1); QCOMPARE(static_cast(l2.at(1)), p2); } void tst_QOrganizerItemDetail::values() { QOrganizerItemDetail p; QCOMPARE(p.values(), (QMap())); QDateTime dt = QDateTime::currentDateTime(); QTime t = dt.time(); t.setHMS(t.hour(), t.minute(), t.second(), 0); // milliseconds don't round trip through ISODate dt.setTime(t); QDate d = dt.date(); QDateTime ddt(d); // DateTime version of a Date (QTime()) p.setValue(101, "This is a string"); p.setValue(102, d); p.setValue(103, dt); p.setValue(104, (int)6); p.setValue(105, d.toString(Qt::ISODate)); p.setValue(106, dt.toString(Qt::ISODate)); // Test the setter that takes a QString p.setValue(107, "123"); // and the setter that takes a QL1C p.setValue(QOrganizerItemPriority::FieldPriority, QVariant::fromValue(static_cast(QOrganizerItemPriority::ExtremelyHighPriority))); /* Presence test */ QVERIFY(p.hasValue(101)); QVERIFY(p.hasValue(102)); QVERIFY(p.hasValue(103)); QVERIFY(p.hasValue(104)); QVERIFY(p.hasValue(105)); QVERIFY(p.hasValue(106)); QVERIFY(p.hasValue(107)); QVERIFY(p.hasValue(QOrganizerItemPriority::FieldPriority)); QVERIFY(!p.hasValue(666)); QVERIFY(p.hasValue(QOrganizerItemPriority::FieldPriority)); /* Variant accessor */ QCOMPARE(p.value(101), QVariant(QString("This is a string"))); QCOMPARE(p.value(102), QVariant(d)); QCOMPARE(p.value(103), QVariant(dt)); QCOMPARE(p.value(104), QVariant((int)6)); QCOMPARE(p.value(105), QVariant(d.toString(Qt::ISODate))); QCOMPARE(p.value(106), QVariant(dt.toString(Qt::ISODate))); QCOMPARE(p.value(107), QVariant(QString("123"))); QCOMPARE(p.value(QOrganizerItemPriority::FieldPriority).toInt(), static_cast(QOrganizerItemPriority::ExtremelyHighPriority)); /* Typed accessors */ QCOMPARE(p.value(101), QString("This is a string")); QCOMPARE(p.value(102), d.toString(Qt::ISODate)); QCOMPARE(p.value(103), dt.toString(Qt::ISODate)); QCOMPARE(p.value(104), QString("6")); QCOMPARE(p.value(105), d.toString(Qt::ISODate)); QCOMPARE(p.value(106), dt.toString(Qt::ISODate)); QCOMPARE(p.value(107), QString("123")); QCOMPARE(p.value(QOrganizerItemPriority::FieldPriority), static_cast(QOrganizerItemPriority::ExtremelyHighPriority)); /* Now individual original types */ QCOMPARE(p.value(102), d); QCOMPARE(p.value(103), dt); QCOMPARE(p.value(104), 6); /* Now cross types that should fail */ QDate id; QDateTime idt; QCOMPARE(p.value(101), id); QCOMPARE(p.value(104), id); QCOMPARE(p.value(107), id); QCOMPARE(p.value(101), idt); QCOMPARE(p.value(104), idt); QCOMPARE(p.value(107), idt); QCOMPARE(p.value(102), 0); QCOMPARE(p.value(103), 0); QCOMPARE(p.value(101), 0); QCOMPARE(p.value(105), 0); QCOMPARE(p.value(106), 0); /* Cross types that should work.. */ QCOMPARE(p.value(107), 123); QCOMPARE(p.value(105), d); QCOMPARE(p.value(106), dt); QCOMPARE(p.value(103), d); QCOMPARE(p.value(106), d); QCOMPARE(p.value(102), ddt); QCOMPARE(p.value(105), ddt); /* Now set everything again */ QMap emptyValues; QMap values = p.values(); QList keys = values.keys(); foreach (int key, keys) QVERIFY(p.setValue(key, QVariant())); QCOMPARE(p.values(), emptyValues); QVERIFY(p.values().count() == 0); QVERIFY(!p.hasValue(101)); QVERIFY(!p.hasValue(102)); QVERIFY(!p.hasValue(103)); QVERIFY(!p.hasValue(104)); QVERIFY(!p.hasValue(105)); QVERIFY(!p.hasValue(106)); QVERIFY(!p.hasValue(107)); QVERIFY(!p.hasValue(666)); QVERIFY(p.value(101).toString() == QString()); QVERIFY(p.value(101) == QVariant()); values.insert(101, "This is a string"); values.insert(102, d); values.insert(103, dt); values.insert(104, (int)6); values.insert(105, d.toString(Qt::ISODate)); values.insert(106, dt.toString(Qt::ISODate)); values.insert(107, "123"); values.insert(101, QString("This is a string")); /* Set values */ keys = values.keys(); foreach (int key, keys) QVERIFY(p.setValue(key, values.value(key))); /* Now repeat the tests with our bulk set map */ QVERIFY(p.hasValue(101)); QVERIFY(p.hasValue(102)); QVERIFY(p.hasValue(103)); QVERIFY(p.hasValue(104)); QVERIFY(p.hasValue(105)); QVERIFY(p.hasValue(106)); QVERIFY(p.hasValue(107)); QVERIFY(!p.hasValue(666)); /* String accessors */ QCOMPARE(p.value(101).toString(), QString("This is a string")); QCOMPARE(p.value(102).toString(), d.toString(Qt::ISODate)); QCOMPARE(p.value(103).toString(), dt.toString(Qt::ISODate)); QCOMPARE(p.value(104).toString(), QString("6")); QCOMPARE(p.value(105).toString(), d.toString(Qt::ISODate)); QCOMPARE(p.value(106).toString(), dt.toString(Qt::ISODate)); QCOMPARE(p.value(107).toString(), QString("123")); /* Typed accessors, string first */ QCOMPARE(p.value(101), QString("This is a string")); QCOMPARE(p.value(102), d.toString(Qt::ISODate)); QCOMPARE(p.value(103), dt.toString(Qt::ISODate)); QCOMPARE(p.value(104), QString("6")); QCOMPARE(p.value(105), d.toString(Qt::ISODate)); QCOMPARE(p.value(106), dt.toString(Qt::ISODate)); QCOMPARE(p.value(107), QString("123")); /* Now individual original types */ QCOMPARE(p.value(102), d); QCOMPARE(p.value(103), dt); QCOMPARE(p.value(104), 6); /* Now cross types that should fail */ QCOMPARE(p.value(101), id); QCOMPARE(p.value(104), id); QCOMPARE(p.value(107), id); QCOMPARE(p.value(101), idt); QCOMPARE(p.value(104), idt); QCOMPARE(p.value(107), idt); QCOMPARE(p.value(101), 0); QCOMPARE(p.value(102), 0); QCOMPARE(p.value(103), 0); QCOMPARE(p.value(105), 0); QCOMPARE(p.value(106), 0); /* Cross types that should work.. */ QCOMPARE(p.value(107), 123); QCOMPARE(p.value(105), d); QCOMPARE(p.value(106), dt); QCOMPARE(p.value(103), d); QCOMPARE(p.value(106), d); QCOMPARE(p.value(102), ddt); QCOMPARE(p.value(105), ddt); /* Reset again */ values = p.values(); keys = values.keys(); foreach (int key, keys) QVERIFY(p.setValue(key, QVariant())); QCOMPARE(p.values(), emptyValues); /* Check that we can add a null variant */ //QVERIFY(p.setValue("nullvariant", QVariant())); //QVERIFY(p.hasValue("nullvariant")); //QCOMPARE(p.value("nullvariant"), QString()); //QCOMPARE(p.value("nullvariant"), QVariant()); //QVERIFY(p.removeValue("nullvariant")); //QVERIFY(p.values().count() == 0); /* Check that adding a value, then setting it to null updates it */ //QVERIFY(p.setValue("string", QString("string value"))); //QCOMPARE(p.values().count(), 1); //QCOMPARE(p.value("string"), QString("string value")); //QVERIFY(p.setValue("string", QVariant())); //QCOMPARE(p.values().count(), 1); //QVERIFY(p.hasValue("string")); //QVERIFY(p.removeValue("string")); //QCOMPARE(p.values().count(), 0); /* See if adding a null QString triggers the same behaviour */ //QVERIFY(p.setValue("string", QString("string value"))); //QCOMPARE(p.values().count(), 1); //QCOMPARE(p.value("string"), QString("string value")); //QVERIFY(p.setValue("string", QString())); //QCOMPARE(p.values().count(), 1); //QVERIFY(p.hasValue("string")); //QVERIFY(p.removeValue("string")); //QCOMPARE(p.values().count(), 0); /* Check adding a null value removes the field */ p.setValue(101, "stringvalue"); QVERIFY(p.values().contains(101)); QVERIFY(p.value(101) == QString("stringvalue")); p.setValue(101, QVariant()); QVERIFY(!p.values().contains(101)); /* Check adding a field whose value is an empty string */ p.setValue(101, ""); QVERIFY(p.values().contains(101)); QVERIFY(p.value(101) == QString("")); /* Check accessing a missing value */ QCOMPARE(p.value(666).toString(), QString()); QVERIFY(p.setValue(666, "changed my mind")); QCOMPARE(p.value(666).toString(), QString("changed my mind")); /* Check removing a missing value */ QVERIFY(!p.removeValue(777)); QVERIFY(!p.removeValue(QOrganizerItemComment::FieldComment)); p.setValue(107, "555"); p.setValue(QOrganizerItemPriority::FieldPriority, "1234"); /* Check removing a real value */ QVERIFY(p.removeValue(101)); QVERIFY(p.removeValue(107)); QVERIFY(p.removeValue(QOrganizerItemPriority::FieldPriority)); } void tst_QOrganizerItemDetail::hash() { QOrganizerItemDetail detail1(QOrganizerItemDetail::TypeComment); detail1.setValue(QOrganizerItemComment::FieldComment, "value"); QOrganizerItemDetail detail2(QOrganizerItemDetail::TypeComment); detail2.setValue(QOrganizerItemComment::FieldComment, "value"); QOrganizerItemDetail detail3(QOrganizerItemDetail::TypeComment); detail3.setValue(QOrganizerItemComment::FieldComment, "different value"); QVERIFY(qHash(detail1) == qHash(detail2)); QVERIFY(qHash(detail1) != qHash(detail3)); QSet set; set.insert(detail1); set.insert(detail2); set.insert(detail3); QCOMPARE(set.size(), 2); } void tst_QOrganizerItemDetail::datastream() { QByteArray buffer; QDataStream stream1(&buffer, QIODevice::WriteOnly); QOrganizerItemDetail detailIn(QOrganizerItemDetail::TypeComment); detailIn.setValue(QOrganizerItemComment::FieldComment, "value1"); stream1 << detailIn; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); QOrganizerItemDetail detailOut; stream2 >> detailOut; QCOMPARE(detailOut, detailIn); } void tst_QOrganizerItemDetail::traits() { QCOMPARE(sizeof(QOrganizerItemDetail), sizeof(void *)); QTypeInfo ti; QVERIFY(ti.isComplex); QVERIFY(!ti.isStatic); QVERIFY(!ti.isLarge); QVERIFY(!ti.isPointer); QVERIFY(!ti.isDummy); } void tst_QOrganizerItemDetail::keys() { QOrganizerItemDetail d; QOrganizerItemDetail d2; QVERIFY(d.key() != d2.key()); d = d2; QVERIFY(d.key() == d2.key()); d.resetKey(); QVERIFY(d.key() != d2.key()); } QTEST_MAIN(tst_QOrganizerItemDetail) #include "tst_qorganizeritemdetail.moc" tests/auto/organizer/qorganizeritemdetails/000077500000000000000000000000001233466112000215745ustar00rootroot00000000000000tests/auto/organizer/qorganizeritemdetails/qorganizeritemdetails.pro000066400000000000000000000001761233466112000267300ustar00rootroot00000000000000include(../../auto.pri) QT += organizer SOURCES += tst_qorganizeritemdetails.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/organizer/qorganizeritemdetails/tst_qorganizeritemdetails.cpp000066400000000000000000001163701233466112000276100ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include //TESTED_COMPONENT=src/organizer QTORGANIZER_USE_NAMESPACE class tst_QOrganizerItemDetails : public QObject { Q_OBJECT public: tst_QOrganizerItemDetails(); virtual ~tst_QOrganizerItemDetails(); public slots: void init(); void cleanup(); private slots: void compare(); // leaf class testing void description(); void displayLabel(); void guid(); void location(); void parent(); void priority(); void recurrence(); void tag(); void timestamp(); void type(); void todoProgress(); // time range void eventTime(); void journalTime(); void todoTime(); // reminders void reminder(); void audibleReminder(); void emailReminder(); void visualReminder(); void attendee(); void rsvp(); void classification(); void version(); }; tst_QOrganizerItemDetails::tst_QOrganizerItemDetails() { } tst_QOrganizerItemDetails::~tst_QOrganizerItemDetails() { } void tst_QOrganizerItemDetails::init() { } void tst_QOrganizerItemDetails::cleanup() { } void tst_QOrganizerItemDetails::compare() { QOrganizerItemRecurrence r, r2; QVERIFY(r == r2); QVERIFY(QOrganizerItemDetail() != r); QOrganizerRecurrenceRule testRule; testRule.setFrequency(QOrganizerRecurrenceRule::Weekly); testRule.setLimit(4); QSet rrules; rrules << testRule; QOrganizerRecurrenceRule exrule; exrule.setFrequency(QOrganizerRecurrenceRule::Daily); testRule.setLimit(12); QSet exrules; exrules << exrule; QSet rdates, exdates; rdates << QDate(2010, 10, 13); exdates << QDate(2010, 11, 17) << QDate(2010, 11, 20); r.setRecurrenceRules(rrules); QVERIFY(r != r2); r2.setRecurrenceRules(rrules); QVERIFY(r == r2); r.setExceptionRules(exrules); QVERIFY(r != r2); r2.setExceptionRules(exrules); QVERIFY(r == r2); r.setRecurrenceDates(rdates); QVERIFY(r != r2); r2.setRecurrenceDates(rdates); QVERIFY(r == r2); r.setExceptionDates(exdates); QVERIFY(r != r2); r2.setExceptionDates(exdates); QVERIFY(r == r2); QOrganizerEvent e, e2; QCOMPARE(e.details(QOrganizerItemDetail::TypeRecurrence).size(), 0); QVERIFY(e == e2); // now save QVERIFY(e.saveDetail(&r)); QCOMPARE(e.details(QOrganizerItemDetail::TypeRecurrence).size(), 1); QVERIFY(e.detail(QOrganizerItemDetail::TypeRecurrence) == r); QVERIFY(e != e2); QVERIFY(e2.saveDetail(&r2)); QCOMPARE(e2.details(QOrganizerItemDetail::TypeRecurrence).size(), 1); QVERIFY(e2.detail(QOrganizerItemDetail::TypeRecurrence) == r2); QVERIFY(e == e2); // update exdates << QDate(2010, 10, 17); r.setExceptionDates(exdates); QVERIFY(r != r2); QVERIFY(e.detail(QOrganizerItemDetail::TypeRecurrence) != r); QVERIFY(e.detail(QOrganizerItemDetail::TypeRecurrence) == r2); QVERIFY(e.saveDetail(&r)); QVERIFY(e.detail(QOrganizerItemDetail::TypeRecurrence) == r); QVERIFY(e.detail(QOrganizerItemDetail::TypeRecurrence) != r2); QVERIFY(e != e2); // remove. QVERIFY(e.removeDetail(&r)); QVERIFY(e != e2); QVERIFY(e2.removeDetail(&r2)); QVERIFY(e == e2); QVERIFY(e.saveDetail(&r)); e2 = QOrganizerEvent(); QVERIFY(e != e2); e2 = e; QVERIFY(e == e2); } void tst_QOrganizerItemDetails::description() { QOrganizerItemDescription d1; QOrganizerItem oi; QVERIFY(d1.description().isEmpty()); QVERIFY(d1.value(QOrganizerItemDescription::FieldDescription).isNull()); d1.setValue(QOrganizerItemDescription::FieldDescription, "Test"); QVERIFY(d1.value(QOrganizerItemDescription::FieldDescription) == QString("Test")); QVERIFY(d1.description() == QString("Test")); QOrganizerItemDescription d2; d2.setValue(QOrganizerItemDescription::FieldDescription, "Test 2"); // test property add -- description is unique. QVERIFY(oi.saveDetail(&d2)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeDescription).count(), 1); // count should be 1 QVERIFY(oi.saveDetail(&d1)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeDescription).count(), 1); // count should be 1 // test property update d1.setDescription("Test 3"); QVERIFY(oi.saveDetail(&d1)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeDescription).count(), 1); // count should be 1 QVERIFY(oi.detail(QOrganizerItemDetail::TypeDescription).value(QOrganizerItemDescription::FieldDescription) == QString("Test 3")); // test property remove QVERIFY(oi.removeDetail(&d1)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeDescription).count(), 0); } void tst_QOrganizerItemDetails::displayLabel() { QOrganizerItemDisplayLabel d1; QOrganizerItem oi; QVERIFY(d1.label().isEmpty()); QVERIFY(d1.value(QOrganizerItemDisplayLabel::FieldLabel).isNull()); d1.setValue(QOrganizerItemDisplayLabel::FieldLabel, "Test"); QVERIFY(d1.value(QOrganizerItemDisplayLabel::FieldLabel) == QString("Test")); QVERIFY(d1.label() == QString("Test")); QOrganizerItemDisplayLabel d2; d2.setValue(QOrganizerItemDisplayLabel::FieldLabel, "Test 2"); // test property add - should update. QVERIFY(oi.saveDetail(&d2)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeDisplayLabel).count(), 1); QVERIFY(oi.saveDetail(&d1)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeDisplayLabel).count(), 1); // test property update - should update d1.setLabel("Test 3"); QVERIFY(oi.saveDetail(&d1)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeDisplayLabel).count(), 1); QVERIFY(oi.detail(QOrganizerItemDetail::TypeDisplayLabel).value(QOrganizerItemDisplayLabel::FieldLabel) == QString("Test 3")); // test property remove QVERIFY(oi.removeDetail(&d1)); // can remove display label. it's unique but not required. QCOMPARE(oi.details(QOrganizerItemDetail::TypeDisplayLabel).count(), 0); } void tst_QOrganizerItemDetails::guid() { QOrganizerItem oi; QOrganizerItemGuid g1, g2; // test property set g1.setGuid("1234"); QCOMPARE(g1.guid(), QString("1234")); QCOMPARE(g1.value(QOrganizerItemGuid::FieldGuid).toString(), QString("1234")); // test property add QVERIFY(oi.saveDetail(&g1)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeGuid).count(), 1); QCOMPARE(QOrganizerItemGuid(oi.details(QOrganizerItemDetail::TypeGuid).value(0)).guid(), g1.guid()); // test property update g1.setValue(101,"label1"); g1.setGuid("12345"); QVERIFY(oi.saveDetail(&g1)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeGuid).value(0).value(101).toString(), QString("label1")); QCOMPARE(oi.details(QOrganizerItemDetail::TypeGuid).value(0).value(QOrganizerItemGuid::FieldGuid).toString(), QString("12345")); // test property remove QVERIFY(oi.removeDetail(&g1)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeGuid).count(), 0); QVERIFY(oi.saveDetail(&g2)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeGuid).count(), 1); QVERIFY(oi.removeDetail(&g2)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeGuid).count(), 0); QVERIFY(oi.removeDetail(&g2) == false); QCOMPARE(oi.details(QOrganizerItemDetail::TypeGuid).count(), 0); } void tst_QOrganizerItemDetails::location() { QOrganizerItemLocation l1; QOrganizerItem oi; QVERIFY(l1.isEmpty()); l1.setLatitude(89.999); QVERIFY(!l1.isEmpty()); QVERIFY(l1.latitude() == 89.999); l1.setValue(QOrganizerItemLocation::FieldLatitude, -89.12345); QVERIFY(l1.value(QOrganizerItemLocation::FieldLatitude) == -89.12345); l1.setLatitude(-90.12345); l1.setLatitude(91); QVERIFY(l1.latitude() == -89.12345); l1.setLongitude(179); QVERIFY(l1.longitude() == 179); l1.setValue(QOrganizerItemLocation::FieldLongitude, -179.54321); QVERIFY(l1.value(QOrganizerItemLocation::FieldLongitude) == -179.54321); l1.setLongitude(180.1); l1.setLongitude(-180.1); QVERIFY(l1.longitude() == -179.54321); l1.setLabel("test"); QVERIFY(l1.label() == QString("test")); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).size() == 0); QVERIFY(oi.saveDetail(&l1)); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).size() == 1); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation) == l1); l1.setLabel("test 2"); QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation) != l1); QVERIFY(oi.saveDetail(&l1)); // update. QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).size() == 1); // should update, not add another. QVERIFY(oi.detail(QOrganizerItemDetail::TypeLocation) == l1); oi.removeDetail(&l1); QVERIFY(oi.details(QOrganizerItemDetail::TypeLocation).size() == 0); } void tst_QOrganizerItemDetails::parent() { QOrganizerItemParent p; QOrganizerItem oi; // hrm, how to test parent local id? QVERIFY(p.isEmpty()); p.setOriginalDate(QDate(2010, 10, 13)); QVERIFY(p.originalDate() == QDate(2010, 10, 13)); QVERIFY(!p.isEmpty()); QVERIFY(oi.saveDetail(&p)); QVERIFY(oi.details(QOrganizerItemDetail::TypeParent).size() == 1); QVERIFY(oi.detail(QOrganizerItemDetail::TypeParent) == p); p.setOriginalDate(QDate(2010, 10, 14)); QVERIFY(oi.detail(QOrganizerItemDetail::TypeParent) != p); QVERIFY(oi.saveDetail(&p)); // update QVERIFY(oi.detail(QOrganizerItemDetail::TypeParent) == p); QVERIFY(oi.removeDetail(&p)); QVERIFY(oi.details(QOrganizerItemDetail::TypeParent).size() == 0); } void tst_QOrganizerItemDetails::priority() { QOrganizerItemPriority p; QOrganizerItem oi; QVERIFY(p.isEmpty()); p.setValue(QOrganizerItemPriority::FieldPriority, QOrganizerItemPriority::VeryHighPriority); QVERIFY(p.priority() == QOrganizerItemPriority::VeryHighPriority); QVERIFY(oi.saveDetail(&p)); QVERIFY(oi.details(QOrganizerItemDetail::TypePriority).size() == 1); QVERIFY(oi.detail(QOrganizerItemDetail::TypePriority) == p); p.setPriority(QOrganizerItemPriority::VeryLowPriority); QVERIFY(oi.detail(QOrganizerItemDetail::TypePriority) != p); QVERIFY(oi.saveDetail(&p)); QVERIFY(oi.details(QOrganizerItemDetail::TypePriority).size() == 1); // update shouldn't add another detail. QVERIFY(oi.detail(QOrganizerItemDetail::TypePriority) == p); QVERIFY(oi.removeDetail(&p)); QVERIFY(oi.details(QOrganizerItemDetail::TypePriority).size() == 0); } void tst_QOrganizerItemDetails::recurrence() { QOrganizerItemRecurrence r; QOrganizerEvent e; QOrganizerRecurrenceRule testRule; testRule.setFrequency(QOrganizerRecurrenceRule::Weekly); testRule.setLimit(4); QSet rrules; rrules << testRule; QOrganizerRecurrenceRule exrule; exrule.setFrequency(QOrganizerRecurrenceRule::Daily); testRule.setLimit(12); QSet exrules; exrules << exrule; QSet rdates, exdates; rdates << QDate(2010, 10, 13); exdates << QDate(2010, 11, 17) << QDate(2010, 11, 20); QVERIFY(e.details(QOrganizerItemDetail::TypeRecurrence).size() == 0); r.setRecurrenceRules(rrules); QVERIFY(r.recurrenceRules() == rrules); QVERIFY(r.recurrenceDates().isEmpty()); QVERIFY(r.exceptionRules().isEmpty()); r.setExceptionRules(exrules); QVERIFY(r.recurrenceRules() == rrules); QVERIFY(r.exceptionRules() == exrules); QVERIFY(r.exceptionDates().isEmpty()); r.setRecurrenceDates(rdates); QVERIFY(r.recurrenceDates() == rdates); QVERIFY(r.exceptionDates().isEmpty()); QVERIFY(r.recurrenceRules() == rrules); QVERIFY(r.exceptionRules() == exrules); r.setExceptionDates(exdates); QVERIFY(r.recurrenceDates() == rdates); QVERIFY(r.exceptionDates() == exdates); QVERIFY(r.recurrenceRules() == rrules); QVERIFY(r.exceptionRules() == exrules); // now save. QVERIFY(e.saveDetail(&r)); QVERIFY(e.details(QOrganizerItemDetail::TypeRecurrence).size() == 1); QVERIFY(e.detail(QOrganizerItemDetail::TypeRecurrence) == r); // update exdates << QDate(2010, 10, 17); r.setExceptionDates(exdates); QVERIFY(e.detail(QOrganizerItemDetail::TypeRecurrence) != r); QVERIFY(e.saveDetail(&r)); QVERIFY(e.detail(QOrganizerItemDetail::TypeRecurrence) == r); QVERIFY(e.details(QOrganizerItemDetail::TypeRecurrence).size() == 1); // should update, not add another. // remove. QVERIFY(e.removeDetail(&r)); QVERIFY(e.details(QOrganizerItemDetail::TypeRecurrence).size() == 0); } void tst_QOrganizerItemDetails::tag() { QOrganizerItem oi; QOrganizerItemTag t1, t2; // test property set t1.setTag("red"); QCOMPARE(t1.tag(), QString("red")); QCOMPARE(t1.value(QOrganizerItemTag::FieldTag).toString(), QString("red")); // test property add QVERIFY(oi.saveDetail(&t1)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeTag).count(), 1); QCOMPARE(QOrganizerItemTag(oi.details(QOrganizerItemDetail::TypeTag).value(0)).tag(), t1.tag()); QVERIFY(oi.saveDetail(&t2)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeTag).count(), 2); // test property update t1.setValue(101,"label1"); t1.setTag("green"); QVERIFY(oi.saveDetail(&t1)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeTag).value(0).value(101).toString(), QString("label1")); QCOMPARE(oi.details(QOrganizerItemDetail::TypeTag).value(0).value(QOrganizerItemTag::FieldTag).toString(), QString("green")); // test property remove QVERIFY(oi.removeDetail(&t1)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeTag).count(), 1); QVERIFY(oi.removeDetail(&t2)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeTag).count(), 0); QVERIFY(oi.removeDetail(&t2) == false); QCOMPARE(oi.details(QOrganizerItemDetail::TypeTag).count(), 0); } void tst_QOrganizerItemDetails::timestamp() { QOrganizerItem oi; QOrganizerItemTimestamp t1, t2; QDateTime modified = QDateTime::currentDateTime(); QDateTime created = modified.addSecs(-43); // test property set t1.setCreated(created); QCOMPARE(t1.created(), created); QCOMPARE(t1.value(QOrganizerItemTimestamp::FieldCreated).toDateTime(), created); // test property add QVERIFY(oi.saveDetail(&t1)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeTimestamp).count(), 1); QCOMPARE(QOrganizerItemTimestamp(oi.details(QOrganizerItemDetail::TypeTimestamp).value(0)).created(), t1.created()); // test property update t1.setValue(101,"label1"); t1.setLastModified(modified); QVERIFY(oi.saveDetail(&t1)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeTimestamp).value(0).value(101).toString(), QString("label1")); QCOMPARE(oi.details(QOrganizerItemDetail::TypeTimestamp).value(0).value(QOrganizerItemTimestamp::FieldCreated).toDateTime(), created); QCOMPARE(oi.details(QOrganizerItemDetail::TypeTimestamp).value(0).value(QOrganizerItemTimestamp::FieldLastModified).toDateTime(), modified); // test property remove QVERIFY(oi.removeDetail(&t1)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeTimestamp).count(), 0); t2.setCreated(created.addSecs(15)); QVERIFY(oi.saveDetail(&t2)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeTimestamp).count(), 1); QVERIFY(oi.removeDetail(&t2)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeTimestamp).count(), 0); QVERIFY(oi.removeDetail(&t2) == false); QCOMPARE(oi.details(QOrganizerItemDetail::TypeTimestamp).count(), 0); } void tst_QOrganizerItemDetails::todoProgress() { QOrganizerTodoProgress p; QOrganizerTodo t; QVERIFY(p.status() == QOrganizerTodoProgress::StatusNotStarted); QVERIFY(p.isEmpty()); p.setStatus(QOrganizerTodoProgress::StatusInProgress); QVERIFY(!p.isEmpty()); QVERIFY(p.status() == QOrganizerTodoProgress::StatusInProgress); p.setPercentageComplete(50); QVERIFY(p.percentageComplete() == 50); p.setPercentageComplete(200); // this should fail QVERIFY(p.percentageComplete() == 50); // value should remain unchanged. p.setFinishedDateTime(QDateTime(QDate(2010, 10, 13))); QVERIFY(p.finishedDateTime() == QDateTime(QDate(2010, 10, 13))); QVERIFY(p.status() != QOrganizerTodoProgress::StatusComplete); // XXX TODO: should this be automatic? p.setPercentageComplete(100); QVERIFY(p.percentageComplete() == 100); QVERIFY(p.status() != QOrganizerTodoProgress::StatusComplete); // XXX TODO: should this be automatic? // add QVERIFY(t.saveDetail(&p)); QVERIFY(t.details(QOrganizerItemDetail::TypeTodoProgress).size() == 1); QVERIFY(t.detail(QOrganizerItemDetail::TypeTodoProgress) == p); // update p.setStatus(QOrganizerTodoProgress::StatusComplete); QVERIFY(p.status() == QOrganizerTodoProgress::StatusComplete); QVERIFY(t.detail(QOrganizerItemDetail::TypeTodoProgress) != p); QVERIFY(t.saveDetail(&p)); QVERIFY(t.details(QOrganizerItemDetail::TypeTodoProgress).size() == 1); // update, not add another QVERIFY(t.detail(QOrganizerItemDetail::TypeTodoProgress) == p); // remove QVERIFY(t.removeDetail(&p)); QVERIFY(t.details(QOrganizerItemDetail::TypeTodoProgress).size() == 0); } void tst_QOrganizerItemDetails::type() { QOrganizerItem oi; QOrganizerItemType t1, t2; QOrganizerEvent e; QOrganizerNote n; QOrganizerTodo t; QOrganizerJournal j; QOrganizerEventOccurrence eo; QOrganizerTodoOccurrence to; QVERIFY(e.type() == QOrganizerItemType::TypeEvent); QVERIFY(n.type() == QOrganizerItemType::TypeNote); QVERIFY(t.type() == QOrganizerItemType::TypeTodo); QVERIFY(j.type() == QOrganizerItemType::TypeJournal); QVERIFY(eo.type() == QOrganizerItemType::TypeEventOccurrence); QVERIFY(to.type() == QOrganizerItemType::TypeTodoOccurrence); // test property set t1.setType(QOrganizerItemType::TypeEvent); QCOMPARE(t1.type(), QOrganizerItemType::TypeEvent); QCOMPARE(static_cast(t1.value(QOrganizerItemType::FieldType).toInt()), QOrganizerItemType::TypeEvent); // test property add QVERIFY(oi.saveDetail(&t1)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeItemType).count(), 1); QCOMPARE(QOrganizerItemType(oi.details(QOrganizerItemDetail::TypeItemType).value(0)).type(), t1.type()); // test property update t1.setType(QOrganizerItemType::TypeTodo); QVERIFY(oi.saveDetail(&t1)); QCOMPARE(static_cast(oi.details(QOrganizerItemDetail::TypeItemType).value(0).value(QOrganizerItemType::FieldType).toInt()), QOrganizerItemType::TypeTodo); // test property remove QVERIFY(!oi.removeDetail(&t1)); // cannot remove type QCOMPARE(oi.details(QOrganizerItemDetail::TypeItemType).count(), 1); t2.setType(QOrganizerItemType::TypeNote); QVERIFY(oi.saveDetail(&t2)); // overwrites t1 QCOMPARE(oi.details(QOrganizerItemDetail::TypeItemType).count(), 1); QVERIFY(!oi.removeDetail(&t2)); // cannot remove type - "succeeds" but count remains unchanged QCOMPARE(oi.details(QOrganizerItemDetail::TypeItemType).count(), 1); QVERIFY(!oi.removeDetail(&t2)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeItemType).count(), 1); } void tst_QOrganizerItemDetails::eventTime() { QOrganizerEventTime r; QOrganizerEvent e; QVERIFY(r.isEmpty()); QVERIFY(!r.isAllDay()); r.setStartDateTime(QDateTime(QDate(2010, 10, 13))); QVERIFY(r.startDateTime() == QDateTime(QDate(2010, 10, 13))); QVERIFY(!r.isAllDay()); r.setAllDay(true); QVERIFY(r.isAllDay()); r.setEndDateTime(QDateTime(QDate(2010, 10, 14))); QVERIFY(r.endDateTime() == QDateTime(QDate(2010, 10, 14))); // add QOrganizerEventTime r2; r2.setStartDateTime(QDateTime(QDate(2010, 10, 23))); r2.setEndDateTime(QDateTime(QDate(2010, 10, 24))); QVERIFY(e.saveDetail(&r)); QVERIFY(e.details(QOrganizerItemDetail::TypeEventTime).size() == 1); QVERIFY(e.detail(QOrganizerItemDetail::TypeEventTime) == r); QVERIFY(e.saveDetail(&r2)); // note that we don't enforce uniqueness in organizeritem; that's done by the manager! QVERIFY(e.details(QOrganizerItemDetail::TypeEventTime).size() == 2); QVERIFY(e.removeDetail(&r2)); QVERIFY(e.details(QOrganizerItemDetail::TypeEventTime).size() == 1); QVERIFY(e.detail(QOrganizerItemDetail::TypeEventTime) == r); // update r.setAllDay(false); QVERIFY(e.detail(QOrganizerItemDetail::TypeEventTime) != r); QVERIFY(e.saveDetail(&r)); QVERIFY(e.detail(QOrganizerItemDetail::TypeEventTime) == r); QVERIFY(e.details(QOrganizerItemDetail::TypeEventTime).size() == 1); // remove QVERIFY(e.removeDetail(&r)); QVERIFY(e.details(QOrganizerItemDetail::TypeEventTime).size() == 0); } void tst_QOrganizerItemDetails::journalTime() { QOrganizerJournalTime r; QOrganizerJournal j; QVERIFY(r.isEmpty()); r.setEntryDateTime(QDateTime(QDate(2010, 10, 13))); QVERIFY(r.entryDateTime() == QDateTime(QDate(2010, 10, 13))); // add QOrganizerJournalTime r2; r2.setEntryDateTime(QDateTime(QDate(2010, 10, 23))); QVERIFY(j.saveDetail(&r)); QVERIFY(j.details(QOrganizerItemDetail::TypeJournalTime).size() == 1); QVERIFY(j.detail(QOrganizerItemDetail::TypeJournalTime) == r); QVERIFY(j.saveDetail(&r2)); // note that we don't enforce uniqueness in organizeritem; that's done by the manager! QVERIFY(j.details(QOrganizerItemDetail::TypeJournalTime).size() == 2); QVERIFY(j.removeDetail(&r2)); QVERIFY(j.details(QOrganizerItemDetail::TypeJournalTime).size() == 1); QVERIFY(j.detail(QOrganizerItemDetail::TypeJournalTime) == r); // update r.setEntryDateTime(QDateTime(QDate(2010, 11, 05))); QVERIFY(j.detail(QOrganizerItemDetail::TypeJournalTime) != r); QVERIFY(j.saveDetail(&r)); QVERIFY(j.detail(QOrganizerItemDetail::TypeJournalTime) == r); QVERIFY(j.details(QOrganizerItemDetail::TypeJournalTime).size() == 1); // remove QVERIFY(j.removeDetail(&r)); QVERIFY(j.details(QOrganizerItemDetail::TypeJournalTime).size() == 0); } void tst_QOrganizerItemDetails::todoTime() { QOrganizerTodoTime r; QOrganizerTodo t; QVERIFY(r.isEmpty()); QVERIFY(!r.isAllDay()); r.setStartDateTime(QDateTime(QDate(2010, 10, 13))); QVERIFY(r.startDateTime() == QDateTime(QDate(2010, 10, 13))); QVERIFY(!r.isAllDay()); r.setAllDay(true); QVERIFY(r.isAllDay()); r.setDueDateTime(QDateTime(QDate(2010, 10, 14))); QVERIFY(r.dueDateTime() == QDateTime(QDate(2010, 10, 14))); // add QOrganizerTodoTime r2; r2.setStartDateTime(QDateTime(QDate(2010, 10, 23))); r2.setDueDateTime(QDateTime(QDate(2010, 10, 24))); QVERIFY(t.saveDetail(&r)); QVERIFY(t.details(QOrganizerItemDetail::TypeTodoTime).size() == 1); QVERIFY(t.detail(QOrganizerItemDetail::TypeTodoTime) == r); QVERIFY(t.saveDetail(&r2)); // note that we don't enforce uniqueness in organizeritem; that's done by the manager! QVERIFY(t.details(QOrganizerItemDetail::TypeTodoTime).size() == 2); QVERIFY(t.removeDetail(&r2)); QVERIFY(t.details(QOrganizerItemDetail::TypeTodoTime).size() == 1); QVERIFY(t.detail(QOrganizerItemDetail::TypeTodoTime) == r); // update r.setAllDay(false); QVERIFY(t.detail(QOrganizerItemDetail::TypeTodoTime) != r); QVERIFY(t.saveDetail(&r)); QVERIFY(t.detail(QOrganizerItemDetail::TypeTodoTime) == r); QVERIFY(t.details(QOrganizerItemDetail::TypeTodoTime).size() == 1); // remove QVERIFY(t.removeDetail(&r)); QVERIFY(t.details(QOrganizerItemDetail::TypeTodoTime).size() == 0); } void tst_QOrganizerItemDetails::reminder() { QOrganizerItemReminder r; QOrganizerItem oi; QVERIFY(r.isEmpty()); QVERIFY(r.reminderType() == QOrganizerItemReminder::NoReminder); QVERIFY(r.secondsBeforeStart() == 0); QVERIFY(r.repetitionCount() == 0); QVERIFY(r.repetitionDelay() == 0); r.setSecondsBeforeStart(45); QVERIFY(!r.isEmpty()); r.removeValue(QOrganizerItemReminder::FieldSecondsBeforeStart); QVERIFY(r.isEmpty()); r.setRepetition(3, 100); QVERIFY(r.repetitionCount() == 3); QVERIFY(r.repetitionDelay() == 100); QVERIFY(!r.isEmpty()); r.setSecondsBeforeStart(30); // reminder, 30 seconds before the item is due to start. QVERIFY(r.secondsBeforeStart() == 30); // add QVERIFY(oi.details(QOrganizerItemDetail::TypeReminder).size() == 0); QVERIFY(oi.saveDetail(&r)); QVERIFY(oi.details(QOrganizerItemDetail::TypeReminder).size() == 1); QVERIFY(oi.detail(QOrganizerItemDetail::TypeReminder) == r); // update r.setSecondsBeforeStart(300); QVERIFY(oi.detail(QOrganizerItemDetail::TypeReminder) != r); QVERIFY(oi.saveDetail(&r)); QVERIFY(oi.details(QOrganizerItemDetail::TypeReminder).size() == 1); // should update, not add another QVERIFY(oi.detail(QOrganizerItemDetail::TypeReminder) == r); // remove QVERIFY(oi.removeDetail(&r)); QVERIFY(oi.details(QOrganizerItemDetail::TypeReminder).size() == 0); } void tst_QOrganizerItemDetails::audibleReminder() { QOrganizerItemAudibleReminder r; QOrganizerItem oi; QVERIFY(r.isEmpty()); QVERIFY(r.reminderType() == QOrganizerItemReminder::AudibleReminder); QVERIFY(r.secondsBeforeStart() == 0); QVERIFY(r.repetitionCount() == 0); QVERIFY(r.repetitionDelay() == 0); r.setSecondsBeforeStart(45); QVERIFY(!r.isEmpty()); r.removeValue(QOrganizerItemReminder::FieldSecondsBeforeStart); QVERIFY(r.isEmpty()); QOrganizerItemReminder baseR; baseR = r; QVERIFY(baseR.isEmpty()); baseR.setSecondsBeforeStart(50); QVERIFY(baseR.secondsBeforeStart() == 50); QVERIFY(r.secondsBeforeStart() == 0); QVERIFY(!baseR.isEmpty()); QVERIFY(r.isEmpty()); r.setRepetition(3, 100); QVERIFY(r.repetitionCount() == 3); QVERIFY(r.repetitionDelay() == 100); QVERIFY(!r.isEmpty()); r.setSecondsBeforeStart(30); // reminder, 30 seconds before the item is due to start. QVERIFY(r.secondsBeforeStart() == 30); // add QVERIFY(oi.details(QOrganizerItemDetail::TypeAudibleReminder).size() == 0); QVERIFY(oi.saveDetail(&r)); QVERIFY(oi.details(QOrganizerItemDetail::TypeAudibleReminder).size() == 1); QVERIFY(oi.detail(QOrganizerItemDetail::TypeAudibleReminder) == r); // update r.setSecondsBeforeStart(300); r.setDataUrl(QUrl("http://www.test.com")); QVERIFY(r.dataUrl() == QUrl("http://www.test.com")); QVERIFY(oi.detail(QOrganizerItemDetail::TypeAudibleReminder) != r); QVERIFY(oi.saveDetail(&r)); QVERIFY(oi.details(QOrganizerItemDetail::TypeAudibleReminder).size() == 1); // should update, not add another QVERIFY(oi.detail(QOrganizerItemDetail::TypeAudibleReminder) == r); // remove QVERIFY(oi.removeDetail(&r)); QVERIFY(oi.details(QOrganizerItemDetail::TypeAudibleReminder).size() == 0); } void tst_QOrganizerItemDetails::emailReminder() { QOrganizerItemEmailReminder r; QOrganizerItem oi; QVERIFY(r.isEmpty()); QVERIFY(r.reminderType() == QOrganizerItemReminder::EmailReminder); QVERIFY(r.secondsBeforeStart() == 0); QVERIFY(r.repetitionCount() == 0); QVERIFY(r.repetitionDelay() == 0); r.setRepetition(3, 100); QVERIFY(r.repetitionCount() == 3); QVERIFY(r.repetitionDelay() == 100); QVERIFY(!r.isEmpty()); r.setSecondsBeforeStart(30); // reminder, 30 seconds before the item is due to start. QVERIFY(r.secondsBeforeStart() == 30); // add QVERIFY(oi.details(QOrganizerItemDetail::TypeEmailReminder).size() == 0); QVERIFY(oi.saveDetail(&r)); QVERIFY(oi.details(QOrganizerItemDetail::TypeEmailReminder).size() == 1); QVERIFY(oi.detail(QOrganizerItemDetail::TypeEmailReminder) == r); // update r.setSecondsBeforeStart(300); r.setContents("subject", "body", QVariantList()); r.setRecipients(QStringList() << "recipient" << "other recipient"); QVERIFY(r.subject() == QString("subject")); QVERIFY(r.body() == QString("body")); QVERIFY(r.attachments() == QVariantList()); QVERIFY(r.recipients() == (QStringList() << "recipient" << "other recipient")); QVERIFY(oi.detail(QOrganizerItemDetail::TypeEmailReminder) != r); QVERIFY(oi.saveDetail(&r)); QVERIFY(oi.details(QOrganizerItemDetail::TypeEmailReminder).size() == 1); // should update, not add another QVERIFY(oi.detail(QOrganizerItemDetail::TypeEmailReminder) == r); // remove QVERIFY(oi.removeDetail(&r)); QVERIFY(oi.details(QOrganizerItemDetail::TypeEmailReminder).size() == 0); } void tst_QOrganizerItemDetails::visualReminder() { QOrganizerItemVisualReminder r; QOrganizerItem oi; QVERIFY(r.isEmpty()); QVERIFY(r.reminderType() == QOrganizerItemReminder::VisualReminder); QVERIFY(r.secondsBeforeStart() == 0); QVERIFY(r.repetitionCount() == 0); QVERIFY(r.repetitionDelay() == 0); r.setRepetition(3, 100); QVERIFY(r.repetitionCount() == 3); QVERIFY(r.repetitionDelay() == 100); QVERIFY(!r.isEmpty()); r.setSecondsBeforeStart(30); // reminder, 30 seconds before the item is due to start. QVERIFY(r.secondsBeforeStart() == 30); // add QVERIFY(oi.details(QOrganizerItemDetail::TypeVisualReminder).size() == 0); QVERIFY(oi.saveDetail(&r)); QVERIFY(oi.details(QOrganizerItemDetail::TypeVisualReminder).size() == 1); QVERIFY(oi.detail(QOrganizerItemDetail::TypeVisualReminder) == r); // update r.setSecondsBeforeStart(300); r.setMessage("test"); r.setDataUrl(QUrl("http://www.test.com")); QVERIFY(r.message() == QString("test")); QVERIFY(r.dataUrl() == QUrl("http://www.test.com")); QVERIFY(oi.detail(QOrganizerItemDetail::TypeVisualReminder) != r); QVERIFY(oi.saveDetail(&r)); QVERIFY(oi.details(QOrganizerItemDetail::TypeVisualReminder).size() == 1); // should update, not add another QVERIFY(oi.detail(QOrganizerItemDetail::TypeVisualReminder) == r); // remove QVERIFY(oi.removeDetail(&r)); QVERIFY(oi.details(QOrganizerItemDetail::TypeVisualReminder).size() == 0); } void tst_QOrganizerItemDetails::attendee() { QOrganizerEventAttendee a; QOrganizerItem oi; QVERIFY(a.isEmpty()); QVERIFY(a.name().isEmpty()); QVERIFY(a.emailAddress().isEmpty()); QVERIFY(a.attendeeId().isEmpty()); QVERIFY(a.participationRole() == 0); QVERIFY(a.participationStatus() == 0); a.setAttendeeId("123456"); QVERIFY(a.attendeeId() == QString("123456")); a.setEmailAddress("people@nokia.com"); QVERIFY(a.emailAddress() == QString("people@nokia.com")); a.setName("people"); QVERIFY(a.name() == QString("people")); a.setParticipationRole(QOrganizerEventAttendee::RoleRequiredParticipant); QVERIFY(a.participationRole() == QOrganizerEventAttendee::RoleRequiredParticipant); a.setParticipationStatus(QOrganizerEventAttendee::StatusAccepted); QVERIFY(a.participationStatus() == QOrganizerEventAttendee::StatusAccepted); // add QVERIFY(oi.details(QOrganizerItemDetail::TypeEventAttendee).size() == 0); QVERIFY(oi.saveDetail(&a)); QVERIFY(oi.details(QOrganizerItemDetail::TypeEventAttendee).size() == 1); QVERIFY(oi.detail(QOrganizerItemDetail::TypeEventAttendee) == a); // update a.setAttendeeId("54321"); a.setName("newpeople"); a.setEmailAddress("newpeople@nokia.com"); QVERIFY(oi.detail(QOrganizerItemDetail::TypeEventAttendee) != a); QVERIFY(oi.saveDetail(&a)); QVERIFY(oi.details(QOrganizerItemDetail::TypeEventAttendee).size() == 1); QVERIFY(oi.detail(QOrganizerItemDetail::TypeEventAttendee) == a); // add one more attendee QOrganizerEventAttendee a1; a1.setAttendeeId("777777"); a1.setName("people1"); a1.setEmailAddress("people1@nokia.com"); QVERIFY(oi.saveDetail(&a1)); QVERIFY(oi.details(QOrganizerItemDetail::TypeEventAttendee).size() == 2); // remove QVERIFY(oi.removeDetail(&a)); QVERIFY(oi.details(QOrganizerItemDetail::TypeEventAttendee).size() == 1); QVERIFY(oi.removeDetail(&a1)); QVERIFY(oi.details(QOrganizerItemDetail::TypeEventAttendee).size() == 0); } void tst_QOrganizerItemDetails::rsvp() { QOrganizerEventRsvp rsvp; QOrganizerItem oi; QVERIFY(rsvp.isEmpty()); QVERIFY(rsvp.organizerName().isEmpty()); QVERIFY(rsvp.organizerEmail().isEmpty()); QVERIFY(rsvp.responseDate().isNull()); QVERIFY(!rsvp.responseDate().isValid()); QVERIFY(rsvp.responseDeadline().isNull()); QVERIFY(!rsvp.responseDeadline().isValid()); QVERIFY(rsvp.participationRole() == 0); QVERIFY(rsvp.participationStatus() == 0); QVERIFY(rsvp.responseRequirement() == 0); rsvp.setOrganizerName("Donald Duck"); QVERIFY(rsvp.organizerName() == QString("Donald Duck")); rsvp.setOrganizerEmail("don@duck.com"); QVERIFY(rsvp.organizerEmail() == QString("don@duck.com")); QDate testDate(2010, 10, 10); rsvp.setResponseDate(testDate); QVERIFY(!rsvp.responseDate().isNull()); QVERIFY(rsvp.responseDate().isValid()); QCOMPARE(testDate, rsvp.responseDate()); rsvp.setResponseDeadline(testDate); QVERIFY(!rsvp.responseDeadline().isNull()); QVERIFY(rsvp.responseDeadline().isValid()); QCOMPARE(testDate, rsvp.responseDeadline()); rsvp.setParticipationRole(QOrganizerEventAttendee::RoleOrganizer); QVERIFY(rsvp.participationRole() == QOrganizerEventAttendee::RoleOrganizer); rsvp.setParticipationStatus(QOrganizerEventAttendee::StatusAccepted); QVERIFY(rsvp.participationStatus() == QOrganizerEventAttendee::StatusAccepted); rsvp.setResponseRequirement(QOrganizerEventRsvp::ResponseRequired); QVERIFY(rsvp.responseRequirement() == QOrganizerEventRsvp::ResponseRequired); // add QCOMPARE(0, oi.details(QOrganizerItemDetail::TypeEventRsvp).size()); QVERIFY(oi.saveDetail(&rsvp)); QCOMPARE(1, oi.details(QOrganizerItemDetail::TypeEventRsvp).size()); QVERIFY(rsvp == oi.detail(QOrganizerItemDetail::TypeEventRsvp)); // update rsvp.setOrganizerName("Mickey Mouse"); rsvp.setOrganizerEmail("mick@mouse.com"); QDate testDate2(2011, 11, 11); rsvp.setResponseDate(testDate2); rsvp.setResponseDeadline(testDate2); rsvp.setParticipationRole(QOrganizerEventAttendee::RoleChairperson); rsvp.setParticipationStatus(QOrganizerEventAttendee::StatusDelegated); rsvp.setResponseRequirement(QOrganizerEventRsvp::ResponseNotRequired); QVERIFY(oi.detail(QOrganizerItemDetail::TypeEventRsvp) != rsvp); QVERIFY(oi.saveDetail(&rsvp)); QCOMPARE(1, oi.details(QOrganizerItemDetail::TypeEventRsvp).size()); QVERIFY(oi.detail(QOrganizerItemDetail::TypeEventRsvp) == rsvp); // add another rsvp - not supported by all the backends QOrganizerEventRsvp rsvp2; rsvp2.setOrganizerName("Goofy"); QVERIFY(oi.saveDetail(&rsvp2)); QCOMPARE(2, oi.details(QOrganizerItemDetail::TypeEventRsvp).size()); // remove QVERIFY(oi.removeDetail(&rsvp2)); QCOMPARE(1, oi.details(QOrganizerItemDetail::TypeEventRsvp).size()); QVERIFY(oi.removeDetail(&rsvp)); QCOMPARE(0, oi.details(QOrganizerItemDetail::TypeEventRsvp).size()); } void tst_QOrganizerItemDetails::classification() { QOrganizerItemClassification classification; QOrganizerItem oi; QVERIFY(classification.isEmpty()); // setters/getters classification.setClassification(QOrganizerItemClassification::AccessPublic); QVERIFY(classification.classification() == QOrganizerItemClassification::AccessPublic); classification.setClassification(QOrganizerItemClassification::AccessPrivate); QVERIFY(classification.classification() == QOrganizerItemClassification::AccessPrivate); classification.setClassification(QOrganizerItemClassification::AccessConfidential); QVERIFY(classification.classification() == QOrganizerItemClassification::AccessConfidential); // add QCOMPARE(0, oi.details(QOrganizerItemDetail::TypeClassification).size()); QVERIFY(oi.saveDetail(&classification)); QCOMPARE(1, oi.details(QOrganizerItemDetail::TypeClassification).size()); QCOMPARE(classification, static_cast(oi.detail(QOrganizerItemDetail::TypeClassification))); // update classification.setValue(QOrganizerItemClassification::FieldClassification, QOrganizerItemClassification::AccessPrivate); QVERIFY(oi.detail(QOrganizerItemDetail::TypeClassification) != classification); QVERIFY(oi.saveDetail(&classification)); QCOMPARE(1, oi.details(QOrganizerItemDetail::TypeClassification).size()); QVERIFY(oi.detail(QOrganizerItemDetail::TypeClassification) == classification); // try adding another, amount of details should stay the same QOrganizerItemClassification classification2; classification2.setClassification(QOrganizerItemClassification::AccessConfidential); QVERIFY(oi.saveDetail(&classification2)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeClassification).size(), 1); // remove QVERIFY(oi.removeDetail(&classification2)); QCOMPARE(oi.details(QOrganizerItemDetail::TypeClassification).size(), 0); } void tst_QOrganizerItemDetails::version() { QOrganizerItemVersion version; QCOMPARE(version.type(), QOrganizerItemDetail::TypeVersion); QVERIFY(!version.hasValue(QOrganizerItemVersion::FieldVersion)); QVERIFY(!version.hasValue(QOrganizerItemVersion::FieldExtendedVersion)); version.setVersion(64); QCOMPARE(version.version(), 64); QVERIFY(version.hasValue(QOrganizerItemVersion::FieldVersion)); QVERIFY(!version.hasValue(QOrganizerItemVersion::FieldExtendedVersion)); QByteArray extendedVersion("Qt rules!"); version.setExtendedVersion(extendedVersion); QCOMPARE(version.extendedVersion(), extendedVersion); QVERIFY(version.hasValue(QOrganizerItemVersion::FieldVersion)); QVERIFY(version.hasValue(QOrganizerItemVersion::FieldExtendedVersion)); } QTEST_MAIN(tst_QOrganizerItemDetails) #include "tst_qorganizeritemdetails.moc" tests/auto/organizer/qorganizeritemfilter/000077500000000000000000000000001233466112000214345ustar00rootroot00000000000000tests/auto/organizer/qorganizeritemfilter/qorganizeritemfilter.pro000066400000000000000000000001751233466112000264270ustar00rootroot00000000000000include(../../auto.pri) QT += organizer SOURCES += tst_qorganizeritemfilter.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/organizer/qorganizeritemfilter/tst_qorganizeritemfilter.cpp000066400000000000000000001552611233466112000273120ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include //TESTED_COMPONENT=src/organizer QTORGANIZER_USE_NAMESPACE Q_DECLARE_METATYPE(QOrganizerItem) Q_DECLARE_METATYPE(QOrganizerItemFilter) Q_DECLARE_METATYPE(QOrganizerItemDetailFieldFilter) class tst_QOrganizerItemFilter : public QObject { Q_OBJECT public: tst_QOrganizerItemFilter(); virtual ~tst_QOrganizerItemFilter(); public slots: void init(); void cleanup(); private slots: void classHierarchy(); void intersectionFilter(); void unionFilter(); void detailFilter(); void detailFieldFilter(); void detailRangeFilter(); void boringFilters(); void idListFilter(); void collectionFilter(); void canonicalizedFilter(); void canonicalizedFilter_data(); void testFilter(); void testFilter_data(); void datastream(); void datastream_data(); void traits(); void testDebugStreamOut(); void testDebugStreamOut_data(); }; class BasicItemLocalId : public QOrganizerItemEngineId { public: BasicItemLocalId(uint id) : m_id(id) {} bool isEqualTo(const QOrganizerItemEngineId* other) const { return m_id == static_cast(other)->m_id; } bool isLessThan(const QOrganizerItemEngineId* other) const { return m_id < static_cast(other)->m_id; } QOrganizerItemEngineId* clone() const { BasicItemLocalId* cloned = new BasicItemLocalId(m_id); return cloned; } QString managerUri() const { static const QString uri(QStringLiteral("qtorganizer:basicItem:")); return uri; } QDebug& debugStreamOut(QDebug& dbg) const { return dbg << m_id; } QString toString() const { return QString::number(m_id); } uint hash() const { return m_id; } private: uint m_id; }; class BasicCollectionLocalId : public QOrganizerCollectionEngineId { public: BasicCollectionLocalId(uint id) : m_id(id) {} bool isEqualTo(const QOrganizerCollectionEngineId* other) const { return m_id == static_cast(other)->m_id; } bool isLessThan(const QOrganizerCollectionEngineId* other) const { return m_id < static_cast(other)->m_id; } QOrganizerCollectionEngineId* clone() const { BasicCollectionLocalId* cloned = new BasicCollectionLocalId(m_id); return cloned; } QString managerUri() const { static const QString uri(QStringLiteral("qtorganizer:basicCollection:")); return uri; } QDebug& debugStreamOut(QDebug& dbg) const { return dbg << m_id; } QString toString() const { return QString::number(m_id); } uint hash() const { return m_id; } private: uint m_id; }; QOrganizerItemId makeId(uint id) { return QOrganizerItemId(new BasicItemLocalId(id)); } QOrganizerCollectionId makeCId(uint id) { return QOrganizerCollectionId(new BasicCollectionLocalId(id)); } tst_QOrganizerItemFilter::tst_QOrganizerItemFilter() { } tst_QOrganizerItemFilter::~tst_QOrganizerItemFilter() { } void tst_QOrganizerItemFilter::init() { } void tst_QOrganizerItemFilter::cleanup() { } void tst_QOrganizerItemFilter::classHierarchy() { /* Test "casting" up and down the hierarchy */ QOrganizerItemDetailFieldFilter df; QVERIFY(df.type() == QOrganizerItemFilter::DetailFieldFilter); df.setDetail(QOrganizerItemDetail::TypeUndefined, 101); df.setValue(42); QOrganizerItemFilter f = df; QVERIFY(f.type() == QOrganizerItemFilter::DetailFieldFilter); QOrganizerItemDetailFieldFilter df2 = f; QVERIFY(df2.type() == QOrganizerItemFilter::DetailFieldFilter); QVERIFY(df2.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(df2.detailField() == 101); QVERIFY(df2.value() == 42); /* Now try to check if we dangle pointers at all */ { QOrganizerItemFilter f2 = df2; } QVERIFY(df2.type() == QOrganizerItemFilter::DetailFieldFilter); QVERIFY(df2.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(df2.detailField() == 101); QVERIFY(df2.value() == 42); { QOrganizerItemDetailFieldFilter sdf2 = df2; sdf2.setDetail(QOrganizerItemDetail::TypeComment, QOrganizerItemComment::FieldComment); QVERIFY(sdf2.detailType() == QOrganizerItemDetail::TypeComment); QVERIFY(df2.detailType() == QOrganizerItemDetail::TypeUndefined); } QVERIFY(df2.type() == QOrganizerItemFilter::DetailFieldFilter); QVERIFY(df2.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(df2.detailField() == 101); QVERIFY(df2.value() == 42); /* Try creating a default filter and making sure we don't break */ QOrganizerItemFilter deff, deff2; QVERIFY(deff.type() == QOrganizerItemFilter::DefaultFilter); QVERIFY(deff == deff); QVERIFY(deff == deff2); QVERIFY(deff != df2); QVERIFY(df2 != deff); QOrganizerItemFilter fdeff = deff; QVERIFY(fdeff.type() == QOrganizerItemFilter::DefaultFilter); QVERIFY(fdeff == deff); QVERIFY(fdeff == deff2); /* Now some "invalid" filters */ QOrganizerItemInvalidFilter iff, iff2; QVERIFY(iff.type() == QOrganizerItemFilter::InvalidFilter); QVERIFY(iff == iff); QVERIFY(iff == iff2); QVERIFY(iff != df2); QVERIFY(df2 != iff); QOrganizerItemFilter fiff = iff; QVERIFY(fiff.type() == QOrganizerItemFilter::InvalidFilter); QVERIFY(fiff == iff); QVERIFY(fiff == iff2); /* Now test some "cross casting" */ } void tst_QOrganizerItemFilter::intersectionFilter() { /* Test boolean ops */ QOrganizerItemDetailFieldFilter df; df.setDetail(QOrganizerItemDetail::TypeUndefined, -1); QOrganizerItemDetailFieldFilter df2; df2.setDetail(QOrganizerItemDetail::TypeComment, QOrganizerItemComment::FieldComment); QOrganizerItemDetailFieldFilter df3; df3.setDetail(QOrganizerItemDetail::TypeDescription, QOrganizerItemDescription::FieldDescription); QOrganizerItemIntersectionFilter bf; bf << df << df2; QOrganizerItemFilter f = df & df2; QVERIFY(bf == f); QOrganizerItemFilter f2 = bf & df3; QVERIFY(f2.type() == QOrganizerItemFilter::IntersectionFilter); QOrganizerItemIntersectionFilter bf2 = f2; QVERIFY(bf2 == f2); QCOMPARE(bf2.filters().count(), 2); QVERIFY(bf2.filters().at(0) == bf); QVERIFY(bf2.filters().at(1) == df3); f2 = df3 & bf; QVERIFY(f2.type() == QOrganizerItemFilter::IntersectionFilter); bf2 = f2; QVERIFY(bf2 == f2); QCOMPARE(bf2.filters().count(), 2); QVERIFY(bf2.filters().at(0) == df3); QVERIFY(bf2.filters().at(1) == bf); /* Save this list */ QList filterList = bf2.filters(); f2 = df & df2 & df3; QVERIFY(f2.type() == QOrganizerItemFilter::IntersectionFilter); bf2 = f2; QVERIFY(bf2 == f2); QCOMPARE(bf2.filters().count(), 2); QVERIFY(bf2.filters().at(0) == (df & df2)); QVERIFY(bf2.filters().at(1) == df3); /* Self assignment should do nothing */ bf2 = bf2; QVERIFY(bf2 == f2); /* Test set filter */ bf2.setFilters(filterList); QCOMPARE(bf2.filters().count(), 2); QVERIFY(bf2.filters().at(0) == df3); QVERIFY(bf2.filters().at(1) == bf); /* Test remove */ bf2.remove(bf); QCOMPARE(bf2.filters().count(), 1); QVERIFY(bf2.filters().at(0) == df3); /* Double remove, should do nothing */ bf2.remove(bf); QCOMPARE(bf2.filters().count(), 1); QVERIFY(bf2.filters().at(0) == df3); /* Append/prepend */ QOrganizerItemIntersectionFilter bf3; bf3.append(df); QVERIFY(bf3.filters().count() == 1); bf3.prepend(df2); QVERIFY(bf3.filters().count() == 2); QVERIFY(bf3.filters().at(0) == df2); QVERIFY(bf3.filters().at(1) == df); bf3.append(df3); QVERIFY(bf3.filters().count() == 3); QVERIFY(bf3.filters().at(0) == df2); QVERIFY(bf3.filters().at(1) == df); QVERIFY(bf3.filters().at(2) == df3); bf3.prepend(df3); QVERIFY(bf3.filters().count() == 4); QVERIFY(bf3.filters().at(0) == df3); QVERIFY(bf3.filters().at(1) == df2); QVERIFY(bf3.filters().at(2) == df); QVERIFY(bf3.filters().at(3) == df3); /* Clear */ bf3.clear(); QVERIFY(bf3.filters().isEmpty()); } void tst_QOrganizerItemFilter::unionFilter() { /* Test boolean ops */ QOrganizerItemDetailFieldFilter df; df.setDetail(QOrganizerItemDetail::TypeUndefined, -1); QOrganizerItemDetailFieldFilter df2; df2.setDetail(QOrganizerItemDetail::TypeComment, QOrganizerItemComment::FieldComment); QOrganizerItemDetailFieldFilter df3; df3.setDetail(QOrganizerItemDetail::TypeDescription, QOrganizerItemDescription::FieldDescription); QOrganizerItemUnionFilter bf; bf << df << df2; QOrganizerItemFilter f = df | df2; QVERIFY(bf == f); QOrganizerItemFilter f2 = bf | df3; QVERIFY(f2.type() == QOrganizerItemFilter::UnionFilter); QOrganizerItemUnionFilter bf2 = f2; QVERIFY(bf2 == f2); QCOMPARE(bf2.filters().count(), 3); QVERIFY(bf2.filters().at(0) == df); QVERIFY(bf2.filters().at(1) == df2); QVERIFY(bf2.filters().at(2) == df3); f2 = df3 | bf; QVERIFY(f2.type() == QOrganizerItemFilter::UnionFilter); bf2 = f2; QVERIFY(bf2 == f2); QCOMPARE(bf2.filters().count(), 3); QVERIFY(bf2.filters().at(0) == df3); QVERIFY(bf2.filters().at(1) == df); QVERIFY(bf2.filters().at(2) == df2); /* Save this list */ QList filterList = bf2.filters(); f2 = df | df2 | df3; QVERIFY(f2.type() == QOrganizerItemFilter::UnionFilter); bf2 = f2; QVERIFY(bf2 == f2); QCOMPARE(bf2.filters().count(), 3); QVERIFY(bf2.filters().at(0) == df); QVERIFY(bf2.filters().at(1) == df2); QVERIFY(bf2.filters().at(2) == df3); /* Self assignment should do nothing */ bf2 = bf2; QVERIFY(bf2 == f2); /* Test set filter */ bf2.setFilters(filterList); QCOMPARE(bf2.filters().count(), 3); QVERIFY(bf2.filters().at(0) == df3); QVERIFY(bf2.filters().at(1) == df); QVERIFY(bf2.filters().at(2) == df2); /* Test remove */ bf2.remove(df); QCOMPARE(bf2.filters().count(), 2); QVERIFY(bf2.filters().at(0) == df3); QVERIFY(bf2.filters().at(1) == df2); /* Double remove, should do nothing */ bf2.remove(df); QCOMPARE(bf2.filters().count(), 2); QVERIFY(bf2.filters().at(0) == df3); QVERIFY(bf2.filters().at(1) == df2); /* Append/prepend */ QOrganizerItemUnionFilter bf3; bf3.append(df); QVERIFY(bf3.filters().count() == 1); bf3.prepend(df2); QVERIFY(bf3.filters().count() == 2); QVERIFY(bf3.filters().at(0) == df2); QVERIFY(bf3.filters().at(1) == df); bf3.append(df3); QVERIFY(bf3.filters().count() == 3); QVERIFY(bf3.filters().at(0) == df2); QVERIFY(bf3.filters().at(1) == df); QVERIFY(bf3.filters().at(2) == df3); bf3.prepend(df3); QVERIFY(bf3.filters().count() == 4); QVERIFY(bf3.filters().at(0) == df3); QVERIFY(bf3.filters().at(1) == df2); QVERIFY(bf3.filters().at(2) == df); QVERIFY(bf3.filters().at(3) == df3); /* Clear */ bf3.clear(); QVERIFY(bf3.filters().isEmpty()); } void tst_QOrganizerItemFilter::detailFilter() { QOrganizerItemDetailFilter df; QVERIFY(df.type() == QOrganizerItemFilter::DetailFilter); QVERIFY(df.detail().type() == QOrganizerItemDetail::TypeUndefined); QVERIFY(df.detail().isEmpty()); QOrganizerItemComment comment; df.setDetail(comment); QVERIFY(df.detail().isEmpty()); QVERIFY(df.detail().type() == QOrganizerItemDetail::TypeComment); QVERIFY(!df.detail().values().keys().contains(QOrganizerItemComment::FieldComment)); QVERIFY(df.detail().value(QOrganizerItemComment::FieldComment).isNull()); comment.setComment(QStringLiteral("TestComment")); df.setDetail(comment); QVERIFY(!df.detail().isEmpty()); QCOMPARE(df.detail().type(), QOrganizerItemDetail::TypeComment); QVERIFY(df.detail().values().keys().contains(QOrganizerItemComment::FieldComment)); QCOMPARE(df.detail().value(QOrganizerItemComment::FieldComment).toString(), QString(QStringLiteral("TestComment"))); /* Test op= */ QOrganizerItemFilter f = df; QVERIFY(f == df); QOrganizerItemDetailFilter df2 = f; QVERIFY(df2 == df); QCOMPARE(df2.detail(), df.detail()); /* Self assignment should do nothing */ df2 = df2; QVERIFY(df2 == df); /* Some cross casting */ QOrganizerItemDetailRangeFilter rf; /* Directly */ df2 = rf; QCOMPARE(df2.type(), QOrganizerItemFilter::DetailFilter); QCOMPARE(df2.detail().type(), QOrganizerItemDetail::TypeUndefined); QVERIFY(df2.detail().isEmpty()); /* reset it */ df2 = df; QCOMPARE(df2.detail().type(), QOrganizerItemDetail::TypeComment); QVERIFY(df2.detail().values().keys().contains(QOrganizerItemComment::FieldComment)); /* Through base class */ f = rf; df2 = f; QCOMPARE(df2.detail().type(), QOrganizerItemDetail::TypeUndefined); QVERIFY(df2.detail().isEmpty()); /* Now test copy ctor */ QOrganizerItemDetailFilter df3(rf); QVERIFY(df3.type() == QOrganizerItemFilter::DetailFilter); QCOMPARE(df3.detail().type(), QOrganizerItemDetail::TypeUndefined); QVERIFY(df3.detail().isEmpty()); /* reset it */ df3 = df; QCOMPARE(df3.detail().type(), QOrganizerItemDetail::TypeComment); QVERIFY(df3.detail().values().keys().contains(QOrganizerItemComment::FieldComment)); /* Now test copy ctor through base class */ QOrganizerItemDetailFilter df4(f); QCOMPARE(df4.type(), QOrganizerItemFilter::DetailFilter); QCOMPARE(df4.detail().type(), QOrganizerItemDetail::TypeUndefined); QVERIFY(df4.detail().isEmpty()); /* reset it */ df4 = df; QCOMPARE(df4.detail().type(), QOrganizerItemDetail::TypeComment); QVERIFY(df4.detail().values().keys().contains(QOrganizerItemComment::FieldComment)); } void tst_QOrganizerItemFilter::detailFieldFilter() { QOrganizerItemDetailFieldFilter df; QVERIFY(df.type() == QOrganizerItemFilter::DetailFieldFilter); QVERIFY(df.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(df.detailField() == -1); QVERIFY(df.matchFlags() == 0); QVERIFY(df.value().isNull()); df.setDetail(QOrganizerItemDetail::TypeComment, QOrganizerItemComment::FieldComment); QVERIFY(df.detailType() == QOrganizerItemDetail::TypeComment); QVERIFY(df.detailField() == QOrganizerItemComment::FieldComment); QVERIFY(df.matchFlags() == 0); QVERIFY(df.value().isNull()); df.setDetail(QOrganizerItemDetail::TypeDescription, QOrganizerItemDescription::FieldDescription); QVERIFY(df.detailType() == QOrganizerItemDetail::TypeDescription); QVERIFY(df.detailField() == QOrganizerItemDescription::FieldDescription); QVERIFY(df.matchFlags() == 0); QVERIFY(df.value().isNull()); df.setMatchFlags(QOrganizerItemFilter::MatchExactly); QVERIFY(df.matchFlags() == QOrganizerItemFilter::MatchExactly); df.setValue(5); QVERIFY(df.value() == 5); df.setValue("String value"); QVERIFY(df.value() == "String value"); /* Test op= */ QOrganizerItemFilter f = df; QVERIFY(f == df); QOrganizerItemDetailFieldFilter df2 = f; QVERIFY(df2 == df); QVERIFY(df2.detailType() == QOrganizerItemDetail::TypeDescription); QVERIFY(df2.detailField() == QOrganizerItemDescription::FieldDescription); /* Self assignment should do nothing */ df2 = df2; QVERIFY(df2 == df); /* Some cross casting */ QOrganizerItemDetailRangeFilter rf; /* Directly */ df2 = rf; QVERIFY(df2.type() == QOrganizerItemFilter::DetailFieldFilter); QVERIFY(df2.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(df2.detailField() == -1); QVERIFY(df2.value().isNull()); /* reset it */ df2 = df; QVERIFY(df2.detailType() == QOrganizerItemDetail::TypeDescription); QVERIFY(df2.detailField() == QOrganizerItemDescription::FieldDescription); /* Through base class */ f = rf; df2 = f; QVERIFY(df2.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(df2.detailField() == -1); QVERIFY(df2.value().isNull()); /* Now test copy ctor */ QOrganizerItemDetailFieldFilter df3(rf); QVERIFY(df3.type() == QOrganizerItemFilter::DetailFieldFilter); QVERIFY(df3.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(df3.detailField() == -1); QVERIFY(df3.value().isNull()); /* reset it */ df3 = df; QVERIFY(df3.detailType() == QOrganizerItemDetail::TypeDescription); QVERIFY(df3.detailField() == QOrganizerItemDescription::FieldDescription); /* Now test copy ctor through base class */ QOrganizerItemDetailFieldFilter df4(f); QVERIFY(df4.type() == QOrganizerItemFilter::DetailFieldFilter); QVERIFY(df4.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(df4.detailField() == -1); QVERIFY(df4.value().isNull()); /* reset it */ df4 = df; QVERIFY(df4.detailType() == QOrganizerItemDetail::TypeDescription); QVERIFY(df4.detailField() == QOrganizerItemDescription::FieldDescription); } void tst_QOrganizerItemFilter::detailRangeFilter() { QOrganizerItemDetailRangeFilter rf; QVERIFY(rf.type() == QOrganizerItemFilter::DetailRangeFilter); QVERIFY(rf.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(rf.detailField() == -1); QVERIFY(rf.matchFlags() == 0); QVERIFY(rf.minValue().isNull()); QVERIFY(rf.maxValue().isNull()); QVERIFY(rf.rangeFlags() == (QOrganizerItemDetailRangeFilter::ExcludeUpper | QOrganizerItemDetailRangeFilter::IncludeLower)); rf.setDetail(QOrganizerItemDetail::TypeEventTime, QOrganizerEventTime::FieldStartDateTime); QVERIFY(rf.detailType() == QOrganizerItemDetail::TypeEventTime); QVERIFY(rf.detailField() == QOrganizerEventTime::FieldStartDateTime); QVERIFY(rf.matchFlags() == 0); QVERIFY(rf.minValue().isNull()); QVERIFY(rf.maxValue().isNull()); QVERIFY(rf.rangeFlags() == (QOrganizerItemDetailRangeFilter::ExcludeUpper | QOrganizerItemDetailRangeFilter::IncludeLower)); rf.setDetail(QOrganizerItemDetail::TypeEventTime, QOrganizerEventTime::FieldStartDateTime); QVERIFY(rf.detailType() == QOrganizerItemDetail::TypeEventTime); QVERIFY(rf.detailField() == QOrganizerEventTime::FieldStartDateTime); QVERIFY(rf.matchFlags() == 0); QVERIFY(rf.minValue().isNull()); QVERIFY(rf.maxValue().isNull()); QVERIFY(rf.rangeFlags() == (QOrganizerItemDetailRangeFilter::ExcludeUpper | QOrganizerItemDetailRangeFilter::IncludeLower)); rf.setMatchFlags(QOrganizerItemFilter::MatchExactly); QVERIFY(rf.matchFlags() == QOrganizerItemFilter::MatchExactly); rf.setMatchFlags(QOrganizerItemFilter::MatchCaseSensitive); QVERIFY(rf.matchFlags() == QOrganizerItemFilter::MatchCaseSensitive); // Contains is not allowed rf.setMatchFlags(QOrganizerItemFilter::MatchCaseSensitive | QOrganizerItemFilter::MatchContains); QVERIFY(rf.matchFlags() == QOrganizerItemFilter::MatchCaseSensitive); rf.setMatchFlags(QOrganizerItemFilter::MatchEndsWith); QVERIFY(rf.matchFlags() == QOrganizerItemFilter::MatchExactly); // 0 rf.setRange(5, 10); QVERIFY(rf.minValue() == 5); QVERIFY(rf.maxValue() == 10); QVERIFY(rf.rangeFlags() == (QOrganizerItemDetailRangeFilter::ExcludeUpper | QOrganizerItemDetailRangeFilter::IncludeLower)); rf.setRange(QVariant(), 11); QVERIFY(rf.minValue().isNull()); QVERIFY(rf.maxValue() == 11); QVERIFY(rf.rangeFlags() == (QOrganizerItemDetailRangeFilter::ExcludeUpper | QOrganizerItemDetailRangeFilter::IncludeLower)); rf.setRange(6, QVariant()); QVERIFY(rf.minValue() == 6); QVERIFY(rf.maxValue().isNull()); QVERIFY(rf.rangeFlags() == (QOrganizerItemDetailRangeFilter::ExcludeUpper | QOrganizerItemDetailRangeFilter::IncludeLower)); rf.setRange(QVariant(), QVariant()); QVERIFY(rf.minValue().isNull()); QVERIFY(rf.maxValue().isNull()); QVERIFY(rf.rangeFlags() == (QOrganizerItemDetailRangeFilter::ExcludeUpper | QOrganizerItemDetailRangeFilter::IncludeLower)); rf.setRange(5, 10, QOrganizerItemDetailRangeFilter::ExcludeLower); QVERIFY(rf.minValue() == 5); QVERIFY(rf.maxValue() == 10); QVERIFY(rf.rangeFlags() == (QOrganizerItemDetailRangeFilter::ExcludeUpper | QOrganizerItemDetailRangeFilter::ExcludeLower)); rf.setRange(QVariant(), 11, QOrganizerItemDetailRangeFilter::IncludeUpper); QVERIFY(rf.minValue().isNull()); QVERIFY(rf.maxValue() == 11); QVERIFY(rf.rangeFlags() == (QOrganizerItemDetailRangeFilter::IncludeUpper | QOrganizerItemDetailRangeFilter::IncludeLower)); rf.setRange(6, QVariant(), QOrganizerItemDetailRangeFilter::ExcludeLower | QOrganizerItemDetailRangeFilter::IncludeUpper); QVERIFY(rf.minValue() == 6); QVERIFY(rf.maxValue().isNull()); QVERIFY(rf.rangeFlags() == (QOrganizerItemDetailRangeFilter::IncludeUpper | QOrganizerItemDetailRangeFilter::ExcludeLower)); rf.setRange(QVariant(), QVariant(), QOrganizerItemDetailRangeFilter::ExcludeUpper | QOrganizerItemDetailRangeFilter::IncludeLower); QVERIFY(rf.minValue().isNull()); QVERIFY(rf.maxValue().isNull()); QVERIFY(rf.rangeFlags() == (QOrganizerItemDetailRangeFilter::ExcludeUpper | QOrganizerItemDetailRangeFilter::IncludeLower)); /* Test op= */ QOrganizerItemFilter f = rf; QVERIFY(f == rf); QOrganizerItemDetailRangeFilter rf2 = f; QVERIFY(rf2 == rf); rf2 = rf; QVERIFY(rf2 == f); /* Self assignment should do nothing */ rf2 = rf2; QVERIFY(rf2 == rf); } void tst_QOrganizerItemFilter::boringFilters() { QOrganizerItemFilter all; QVERIFY(all.type() == QOrganizerItemFilter::DefaultFilter); QOrganizerItemInvalidFilter invalid; QVERIFY(invalid.type() == QOrganizerItemFilter::InvalidFilter); QVERIFY(all != invalid); QVERIFY(!(all == invalid)); /* Test op= */ QOrganizerItemFilter f = all; QVERIFY(f == all); QOrganizerItemFilter f2; f2 = f; QVERIFY(f2 == all); /* Self assignment should do nothing */ f2 = f2; QVERIFY(f2 == all); /* InvalidFilter, op= */ QOrganizerItemInvalidFilter inv2 = invalid; QVERIFY(inv2 == invalid); QOrganizerItemInvalidFilter inv3; inv3 = inv2; QVERIFY(inv3 == invalid); inv3 = inv3; QVERIFY(inv3 == invalid); inv3 = all; QVERIFY(inv3 == invalid); // won't be all } void tst_QOrganizerItemFilter::idListFilter() { QOrganizerItemIdFilter idf; QVERIFY(idf.type() == QOrganizerItemFilter::IdFilter); QVERIFY(idf.ids().count() == 0); QList ids; ids << makeId(5) << makeId(6) << makeId(17); idf.setIds(ids); QVERIFY(idf.ids() == ids); idf.setIds(QList()); QVERIFY(idf.ids().count() == 0); QOrganizerItemId singleId = makeId(12); idf.insert(singleId); QVERIFY(idf.ids().contains(singleId)); idf.remove(singleId); QVERIFY(!idf.ids().contains(singleId)); QList allIds = idf.ids(); idf.remove(singleId); // remove again QVERIFY(idf.ids() == allIds); idf.clear(); QVERIFY(idf.ids().isEmpty()); idf.setIds(allIds); /* Test op= */ idf.setIds(ids); QOrganizerItemFilter f = idf; QVERIFY(f == idf); QOrganizerItemIdFilter idf2 = f; QVERIFY(idf2 == idf); QVERIFY(idf2.ids() == ids); idf2 = idf; QVERIFY(idf2 == f); /* Self assignment should do nothing */ idf2 = idf2; QVERIFY(idf2 == idf); QOrganizerItemDetailFieldFilter dfil; QOrganizerItemIdFilter idf3(dfil); QVERIFY(idf3.type() == QOrganizerItemFilter::IdFilter); // should be a blank id list filter QOrganizerItemIdFilter idf4(idf); QVERIFY(idf4 == idf); // should be a copy of idf. idf = dfil; // now assign. QVERIFY(idf == idf3); // again, should be a blank id list filter. idf = idf3; idf.setIds(ids); // force a detach } void tst_QOrganizerItemFilter::collectionFilter() { QOrganizerItemCollectionFilter icf; QVERIFY(icf.collectionIds().isEmpty()); QOrganizerCollectionId id1 = makeCId(5); QOrganizerCollectionId id2 = makeCId(6); QOrganizerCollectionId id3 = makeCId(7); QOrganizerCollectionId id4 = makeCId(12); QSet ids; ids << id1 << id2 << id3; icf.setCollectionIds(ids); QVERIFY(icf.collectionIds() == ids); icf.setCollectionId(id4); ids.clear(); ids << id4; QVERIFY(icf.collectionIds() == ids); QOrganizerItemCollectionFilter icf2; icf2 = icf; QVERIFY(icf2.collectionIds() == ids); QOrganizerItemFilter fil; fil = icf; QVERIFY(fil.type() == QOrganizerItemFilter::CollectionFilter); QOrganizerItemCollectionFilter icf3(fil); QVERIFY(fil.type() == QOrganizerItemFilter::CollectionFilter); QVERIFY(icf3.collectionIds() == ids); } void tst_QOrganizerItemFilter::canonicalizedFilter() { QFETCH(QOrganizerItemFilter, in); QFETCH(QOrganizerItemFilter, expected); QOrganizerItemFilter out = QOrganizerManagerEngine::canonicalizedFilter(in); QCOMPARE(out, expected); } void tst_QOrganizerItemFilter::canonicalizedFilter_data() { QTest::addColumn("in"); QTest::addColumn("expected"); QOrganizerItemDetailFieldFilter detailFilter1; detailFilter1.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); detailFilter1.setValue("1"); detailFilter1.setMatchFlags(QOrganizerItemFilter::MatchContains); QOrganizerItemDetailFieldFilter detailFilter2; detailFilter2.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); detailFilter2.setValue("2"); detailFilter2.setMatchFlags(QOrganizerItemFilter::MatchContains); QOrganizerItemInvalidFilter invalidFilter; QOrganizerItemFilter defaultFilter; { QTest::newRow("Normal detail filter") << static_cast(detailFilter1) << static_cast(detailFilter1); } { QOrganizerItemIntersectionFilter qcif; qcif << detailFilter1; qcif << detailFilter2; QTest::newRow("Normal intersection filter") << static_cast(qcif) << static_cast(qcif); } { QOrganizerItemUnionFilter qcuf; qcuf << detailFilter1; qcuf << detailFilter2; QTest::newRow("Normal intersection filter") << static_cast(qcuf) << static_cast(qcuf); } { QOrganizerItemIntersectionFilter qcif; QTest::newRow("Empty intersection") << static_cast(qcif) << static_cast(defaultFilter); } { QOrganizerItemUnionFilter qcuf; QTest::newRow("Empty union") << static_cast(qcuf) << static_cast(invalidFilter); } { QOrganizerItemIntersectionFilter qcif; qcif << detailFilter1; QTest::newRow("Single entry intersection filter") << static_cast(qcif) << static_cast(detailFilter1); } { QOrganizerItemUnionFilter qcuf; qcuf << detailFilter1; QTest::newRow("Single entry union filter") << static_cast(qcuf) << static_cast(detailFilter1); } { QOrganizerItemIntersectionFilter qcif; qcif << invalidFilter; qcif << detailFilter1; qcif << detailFilter2; QTest::newRow("Intersection with invalid") << static_cast(qcif) << static_cast(invalidFilter); } { QOrganizerItemIntersectionFilter qcif; qcif << defaultFilter; qcif << detailFilter1; qcif << detailFilter2; QOrganizerItemIntersectionFilter expected; expected << detailFilter1; expected << detailFilter2; QTest::newRow("Intersection with default") << static_cast(qcif) << static_cast(expected); } { QOrganizerItemUnionFilter qcuf; qcuf << invalidFilter; qcuf << detailFilter1; qcuf << detailFilter2; QOrganizerItemUnionFilter expected; expected << detailFilter1; expected << detailFilter2; QTest::newRow("Union with invalid") << static_cast(qcuf) << static_cast(expected); } { QOrganizerItemUnionFilter qcuf; qcuf << defaultFilter; qcuf << detailFilter1; qcuf << detailFilter2; QTest::newRow("Union with default") << static_cast(qcuf) << static_cast(defaultFilter); } { QOrganizerItemIdFilter qclif; QTest::newRow("Empty local id filter") << static_cast(qclif) << static_cast(invalidFilter); } { QOrganizerItemIdFilter qclif; qclif.setIds(QList() << makeId(1) << makeId(2)); QTest::newRow("Normal local id filter") << static_cast(qclif) << static_cast(qclif); } { QOrganizerItemDetailRangeFilter qcdrf; qcdrf.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); QOrganizerItemDetailFieldFilter expected; expected.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); QTest::newRow("Null valued range filter") << static_cast(qcdrf) << static_cast(expected); } { QOrganizerItemDetailRangeFilter qcdrf; qcdrf.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); qcdrf.setRange(QStringLiteral("a"), QStringLiteral("a")); qcdrf.setMatchFlags(QOrganizerItemFilter::MatchFixedString); QOrganizerItemDetailFieldFilter expected; expected.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); expected.setValue(QStringLiteral("a")); expected.setMatchFlags(QOrganizerItemFilter::MatchFixedString); QTest::newRow("Equal valued range filter") << static_cast(qcdrf) << static_cast(expected); } { QOrganizerItemDetailRangeFilter qcdrf; qcdrf.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); qcdrf.setRange(QStringLiteral("a"), QStringLiteral("a"), QOrganizerItemDetailRangeFilter::ExcludeLower | QOrganizerItemDetailRangeFilter::ExcludeUpper); qcdrf.setMatchFlags(QOrganizerItemFilter::MatchFixedString); QTest::newRow("Equal valued range filter with excluded bounds") << static_cast(qcdrf) << static_cast(invalidFilter); } { QOrganizerItemDetailRangeFilter qcdrf; qcdrf.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); qcdrf.setRange(QStringLiteral("a"), QStringLiteral("b")); qcdrf.setMatchFlags(QOrganizerItemFilter::MatchFixedString); QTest::newRow("Normal range filter") << static_cast(qcdrf) << static_cast(qcdrf); } { QOrganizerItemDetailRangeFilter qcdrf; qcdrf.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); qcdrf.setRange(QVariant(QVariant::String), QVariant(QVariant::String)); // null bounds qcdrf.setMatchFlags(QOrganizerItemFilter::MatchFixedString); QOrganizerItemDetailFieldFilter qcdf; qcdf.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); qcdf.setMatchFlags(QOrganizerItemFilter::MatchFixedString); qcdf.setValue(QVariant(QVariant::String)); QTest::newRow("Null valued range filter") << static_cast(qcdrf) << static_cast(qcdf); } { QOrganizerItemDetailRangeFilter qcdrf; qcdrf.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); qcdrf.setRange(QVariant(QVariant::String), QStringLiteral("a")); // min is null qcdrf.setMatchFlags(QOrganizerItemFilter::MatchFixedString); QTest::newRow("One sided range filter") << static_cast(qcdrf) << static_cast(qcdrf); } { QOrganizerItemDetailRangeFilter qcdrf; QTest::newRow("Empty range filter") << static_cast(qcdrf) << static_cast(invalidFilter); } { QOrganizerItemDetailFieldFilter qcdf; QTest::newRow("Empty detail filter") << static_cast(qcdf) << static_cast(invalidFilter); } } void tst_QOrganizerItemFilter::testFilter() { QFETCH(QOrganizerItem, item); QFETCH(QOrganizerItemDetailFieldFilter, filter); QFETCH(bool, expected); QCOMPARE(QOrganizerManagerEngine::testFilter(filter, item), expected); } void tst_QOrganizerItemFilter::testFilter_data() { QTest::addColumn("item"); QTest::addColumn("filter"); QTest::addColumn("expected"); // XXX TODO: other detail types (comment, description, ...) { QOrganizerItem item; QOrganizerItemLocation name; name.setLabel("test location"); item.saveDetail(&name); QOrganizerItemDetailFieldFilter filter; filter.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); filter.setMatchFlags(QOrganizerItemFilter::MatchContains); filter.setValue("test location"); QTest::newRow("QOrganizerItemLocation::match location") << item << filter << true; filter.setValue("ocati"); QTest::newRow("QOrganizerItemLocation::match substring") << item << filter << true; filter.setValue("foo"); QTest::newRow("QOrganizerItemLocation::match negative") << item << filter << false; } { QOrganizerItem item; item.setDisplayLabel(QStringLiteral("foo")); QOrganizerItemDetailFieldFilter filter; filter.setDetail(QOrganizerItemDetail::TypeDisplayLabel, QOrganizerItemDisplayLabel::FieldLabel); filter.setMatchFlags(QOrganizerItemFilter::MatchContains); filter.setValue("foo"); QTest::newRow("QOrganizerItemDisplayLabel::match positive") << item << filter << true; filter.setValue("o"); QTest::newRow("QOrganizerItemDisplayLabel::match positive substring") << item << filter << true; filter.setValue("bar"); QTest::newRow("QOrganizerItemDisplayLabel::match negative") << item << filter << false; } { QOrganizerItem item; QOrganizerItemPriority priority; priority.setPriority(QOrganizerItemPriority::VeryHighPriority); item.saveDetail(&priority); QOrganizerItemDetailFieldFilter filter; filter.setDetail(QOrganizerItemDetail::TypePriority, QOrganizerItemPriority::FieldPriority); filter.setMatchFlags(QOrganizerItemFilter::MatchContains); filter.setValue(QOrganizerItemPriority::VeryHighPriority); QTest::newRow("QOrganizerItemPriority::match positive") << item << filter << true; filter.setValue(QOrganizerItemPriority::VeryLowPriority); QTest::newRow("QOrganizerItemPhoneNumber::match negative") << item << filter << false; } } void tst_QOrganizerItemFilter::datastream() { QFETCH(QOrganizerItemFilter, filterIn); QByteArray buffer; QDataStream stream1(&buffer, QIODevice::WriteOnly); stream1 << filterIn; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); QOrganizerItemFilter filterOut; stream2 >> filterOut; QCOMPARE(filterOut, filterIn); } void tst_QOrganizerItemFilter::datastream_data() { QTest::addColumn("filterIn"); { QOrganizerItemFilter filter; QTest::newRow("default") << filter; } { QOrganizerItemDetailFieldFilter filter; filter.setDetail(QOrganizerItemDetail::TypeComment, QOrganizerItemComment::FieldComment); filter.setMatchFlags(QOrganizerItemFilter::MatchEndsWith); filter.setValue("ski"); QTest::newRow("detail") << (QOrganizerItemFilter)filter; } { QOrganizerItemIntersectionFilter filter; QTest::newRow("intersection") << (QOrganizerItemFilter)filter; } { QOrganizerItemInvalidFilter filter; QTest::newRow("invalid") << (QOrganizerItemFilter)filter; } { QOrganizerItemUnionFilter filter; QTest::newRow("union") << (QOrganizerItemFilter)filter; } // NOTE: LocalIdFilter streaming is not supported } void tst_QOrganizerItemFilter::traits() { QCOMPARE(sizeof(QOrganizerItemFilter), sizeof(void *)); QTypeInfo ti; QVERIFY(ti.isComplex); QVERIFY(!ti.isStatic); QVERIFY(!ti.isLarge); QVERIFY(!ti.isPointer); QVERIFY(!ti.isDummy); } void tst_QOrganizerItemFilter::testDebugStreamOut() { QFETCH(QOrganizerItemFilter, filterIn); QFETCH(QString, messageExpected); QTest::ignoreMessage(QtDebugMsg, messageExpected.toUtf8()); qDebug() << filterIn; } void tst_QOrganizerItemFilter::testDebugStreamOut_data() { QTest::addColumn("filterIn"); QTest::addColumn("messageExpected"); { QOrganizerItemFilter filter; QTest::newRow("default") << filter << "QOrganizerItemFilter((null))"; } { QOrganizerItemCollectionFilter filter; QOrganizerCollectionId id1 = makeCId(5); QOrganizerCollectionId id2 = makeCId(6); QOrganizerCollectionId id3 = makeCId(7); QOrganizerCollectionId id4 = makeCId(12); QSet ids; ids << id1 << id2 << id3; filter.setCollectionIds(ids); // Testing method setCollectionIds // FIXME: QTBUG-25382: Assumes QSet is an ordered collection, which it's not // QTest::newRow("collection") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemCollectionFilter(collectionIds=QSet(QOrganizerCollectionId(5), QOrganizerCollectionId(6), QOrganizerCollectionId(7))))"; filter.setCollectionId(id2); // Testing method setCollectionId (and the related clearing of the collection) QTest::newRow("collection") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemCollectionFilter(collectionIds=QSet(QOrganizerCollectionId(6))))"; filter.setCollectionId(id4); // Testing again method setCollectionId (and the related clearing of the collection) QTest::newRow("collection") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemCollectionFilter(collectionIds=QSet(QOrganizerCollectionId(12))))"; ids.clear(); ids << id4; // Testing again method setCollectionIds QTest::newRow("collection") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemCollectionFilter(collectionIds=QSet(QOrganizerCollectionId(12))))"; QOrganizerItemCollectionFilter filter2; filter2 = filter; // Testing again method setCollectionIds on the copied filter QTest::newRow("collection") << (QOrganizerItemFilter)filter2 << "QOrganizerItemFilter(QOrganizerItemCollectionFilter(collectionIds=QSet(QOrganizerCollectionId(12))))"; QOrganizerItemFilter fil; fil = filter; // Testing that the assignment/conversion went fine QTest::newRow("collection") << (QOrganizerItemFilter)fil << "QOrganizerItemFilter(QOrganizerItemCollectionFilter(collectionIds=QSet(QOrganizerCollectionId(12))))"; QOrganizerItemCollectionFilter filter3(fil); QTest::newRow("collection") << (QOrganizerItemFilter)filter3 << "QOrganizerItemFilter(QOrganizerItemCollectionFilter(collectionIds=QSet(QOrganizerCollectionId(12))))"; } { QOrganizerItemDetailFieldFilter filter; filter.setDetail(QOrganizerItemDetail::TypeComment, QOrganizerItemComment::FieldComment); filter.setMatchFlags(QOrganizerItemFilter::MatchEndsWith); filter.setValue("ski"); QTest::newRow("detail") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemDetailFieldFilter(detailType=200,detailField=201,value=QVariant(QString, \"ski\") ,matchFlags=3))"; } { QOrganizerItemDetailRangeFilter filter; // Testing the empty fields QTest::newRow("detailRange") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=0,detailField=-1,minValue=QVariant(Invalid) ,maxValue=QVariant(Invalid) ,matchFlags=0,rangeFlags=0))"; // Testing the method setDetail filter.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); QTest::newRow("detailRange") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=700,detailField=703,minValue=QVariant(Invalid) ,maxValue=QVariant(Invalid) ,matchFlags=0,rangeFlags=0))"; // Testing the method rangeFlags filter.setMatchFlags(QOrganizerItemFilter::MatchExactly); QTest::newRow("detailRange") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=700,detailField=703,minValue=QVariant(Invalid) ,maxValue=QVariant(Invalid) ,matchFlags=0,rangeFlags=0))"; // Testing the method matchFlags filter.setMatchFlags(QOrganizerItemFilter::MatchCaseSensitive); QTest::newRow("detailRange") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=700,detailField=703,minValue=QVariant(Invalid) ,maxValue=QVariant(Invalid) ,matchFlags=16,rangeFlags=0))"; // Contains is not allowed filter.setMatchFlags(QOrganizerItemFilter::MatchCaseSensitive | QOrganizerItemFilter::MatchContains); QTest::newRow("detailRange") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=700,detailField=703,minValue=QVariant(Invalid) ,maxValue=QVariant(Invalid) ,matchFlags=16,rangeFlags=0))"; filter.setMatchFlags(QOrganizerItemFilter::MatchEndsWith); QTest::newRow("detailRange") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=700,detailField=703,minValue=QVariant(Invalid) ,maxValue=QVariant(Invalid) ,matchFlags=0,rangeFlags=0))"; // Testing the minValue and maxValue filter.setRange(5, 10); QTest::newRow("detailRange") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=700,detailField=703,minValue=QVariant(int, 5) ,maxValue=QVariant(int, 10) ,matchFlags=0,rangeFlags=0))"; // Testing the setRange filter.setRange(QVariant(), 11); QTest::newRow("detailRange") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=700,detailField=703,minValue=QVariant(Invalid) ,maxValue=QVariant(int, 11) ,matchFlags=0,rangeFlags=0))"; filter.setRange(6, QVariant()); QTest::newRow("detailRange") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=700,detailField=703,minValue=QVariant(int, 6) ,maxValue=QVariant(Invalid) ,matchFlags=0,rangeFlags=0))"; filter.setRange(QVariant(), QVariant()); QTest::newRow("detailRange") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=700,detailField=703,minValue=QVariant(Invalid) ,maxValue=QVariant(Invalid) ,matchFlags=0,rangeFlags=0))"; filter.setRange(5, 10, QOrganizerItemDetailRangeFilter::ExcludeLower); QTest::newRow("detailRange") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=700,detailField=703,minValue=QVariant(int, 5) ,maxValue=QVariant(int, 10) ,matchFlags=0,rangeFlags=2))"; // * filter.setRange(QVariant(), 11, QOrganizerItemDetailRangeFilter::IncludeUpper); QTest::newRow("detailRange") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=700,detailField=703,minValue=QVariant(Invalid) ,maxValue=QVariant(int, 11) ,matchFlags=0,rangeFlags=1))"; filter.setRange(6, QVariant(), QOrganizerItemDetailRangeFilter::ExcludeLower | QOrganizerItemDetailRangeFilter::IncludeUpper); QTest::newRow("detailRange") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=700,detailField=703,minValue=QVariant(int, 6) ,maxValue=QVariant(Invalid) ,matchFlags=0,rangeFlags=3))"; filter.setRange(QVariant(), QVariant(), QOrganizerItemDetailRangeFilter::ExcludeUpper | QOrganizerItemDetailRangeFilter::IncludeLower); QTest::newRow("detailRange") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=700,detailField=703,minValue=QVariant(Invalid) ,maxValue=QVariant(Invalid) ,matchFlags=0,rangeFlags=0))"; // Test op= QOrganizerItemFilter f = filter; QTest::newRow("detailRange") << (QOrganizerItemFilter)f << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=700,detailField=703,minValue=QVariant(Invalid) ,maxValue=QVariant(Invalid) ,matchFlags=0,rangeFlags=0))"; QOrganizerItemDetailRangeFilter filter2 = f; QTest::newRow("detailRange") << (QOrganizerItemFilter)filter2 << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=700,detailField=703,minValue=QVariant(Invalid) ,maxValue=QVariant(Invalid) ,matchFlags=0,rangeFlags=0))"; filter2 = filter; QTest::newRow("detailRange") << (QOrganizerItemFilter)filter2 << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=700,detailField=703,minValue=QVariant(Invalid) ,maxValue=QVariant(Invalid) ,matchFlags=0,rangeFlags=0))"; // Self assignment should do nothing filter2 = filter2; QTest::newRow("detailRange") << (QOrganizerItemFilter)filter2 << "QOrganizerItemFilter(QOrganizerItemDetailRangeFilter(detailType=700,detailField=703,minValue=QVariant(Invalid) ,maxValue=QVariant(Invalid) ,matchFlags=0,rangeFlags=0))"; } { // Testing creation of an empty filter QOrganizerItemIdFilter filter; QTest::newRow("Id") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemIdFilter(ids=()))"; // Testing the method setIds QList ids; ids << makeId(5) << makeId(6) << makeId(17); filter.setIds(ids); QTest::newRow("Id") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemIdFilter(ids=(QOrganizerItemId(5), QOrganizerItemId(6), QOrganizerItemId(17))))"; // Resetting the list of Ids filter.setIds(QList()); QTest::newRow("Id") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemIdFilter(ids=()))"; // Testing the method insert QOrganizerItemId singleId = makeId(12); filter.insert(singleId); QTest::newRow("Id") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemIdFilter(ids=(QOrganizerItemId(12))))"; // Testing the method remove filter.remove(singleId); QTest::newRow("Id") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemIdFilter(ids=()))"; QList allIds = filter.ids(); filter.remove(singleId); // remove again filter.clear(); filter.setIds(allIds); QTest::newRow("Id") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemIdFilter(ids=()))"; // Test op= filter.setIds(ids); QOrganizerItemFilter f = filter; QTest::newRow("Id") << (QOrganizerItemFilter)f << "QOrganizerItemFilter(QOrganizerItemIdFilter(ids=(QOrganizerItemId(5), QOrganizerItemId(6), QOrganizerItemId(17))))"; QOrganizerItemIdFilter filter2 = f; QTest::newRow("Id") << (QOrganizerItemFilter)filter2 << "QOrganizerItemFilter(QOrganizerItemIdFilter(ids=(QOrganizerItemId(5), QOrganizerItemId(6), QOrganizerItemId(17))))"; filter2 = filter; QTest::newRow("Id") << (QOrganizerItemFilter)filter2 << "QOrganizerItemFilter(QOrganizerItemIdFilter(ids=(QOrganizerItemId(5), QOrganizerItemId(6), QOrganizerItemId(17))))"; // Self assignment should do nothing filter2 = filter2; QTest::newRow("Id") << (QOrganizerItemFilter)filter2 << "QOrganizerItemFilter(QOrganizerItemIdFilter(ids=(QOrganizerItemId(5), QOrganizerItemId(6), QOrganizerItemId(17))))"; QOrganizerItemDetailFieldFilter dfil; QOrganizerItemIdFilter filter3(dfil); QTest::newRow("Id") << (QOrganizerItemFilter)filter3 << "QOrganizerItemFilter(QOrganizerItemIdFilter(ids=()))"; QOrganizerItemIdFilter filter4(filter); QTest::newRow("Id") << (QOrganizerItemFilter)filter4 << "QOrganizerItemFilter(QOrganizerItemIdFilter(ids=(QOrganizerItemId(5), QOrganizerItemId(6), QOrganizerItemId(17))))"; filter = dfil; // now assign. QTest::newRow("Id") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemIdFilter(ids=()))"; QTest::newRow("Id") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemIdFilter(ids=()))"; filter = filter3; filter.setIds(ids); // force a detach QTest::newRow("Id") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemIdFilter(ids=(QOrganizerItemId(5), QOrganizerItemId(6), QOrganizerItemId(17))))"; } { // Test empty filter QOrganizerItemIntersectionFilter filter; QTest::newRow("intersection") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemIntersectionFilter(filters=()))"; // Test boolean ops QOrganizerItemDetailFieldFilter filter1; filter1.setDetail(QOrganizerItemDetail::TypeComment, QOrganizerItemComment::FieldComment); QOrganizerItemDetailFieldFilter filter2; filter2.setDetail(QOrganizerItemDetail::TypeDescription, QOrganizerItemDescription::FieldDescription); QOrganizerItemDetailFieldFilter filter3; filter3.setDetail(QOrganizerItemDetail::TypeDisplayLabel, QOrganizerItemDisplayLabel::FieldLabel); filter << filter1 << filter2; QTest::newRow("intersection") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemIntersectionFilter(filters=(QOrganizerItemFilter(QOrganizerItemDetailFieldFilter(detailType=200,detailField=201,value=QVariant(Invalid) ,matchFlags=0)), QOrganizerItemFilter(QOrganizerItemDetailFieldFilter(detailType=300,detailField=301,value=QVariant(Invalid) ,matchFlags=0)))))"; } { QOrganizerItemInvalidFilter filter; QTest::newRow("invalid") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemInvalidFilter())"; } { QOrganizerItemUnionFilter filter; QTest::newRow("union") << (QOrganizerItemFilter)filter << "QOrganizerItemFilter(QOrganizerItemUnionFilter(filters=()))"; // Test boolean ops QOrganizerItemDetailFieldFilter df; df.setDetail(QOrganizerItemDetail::TypeComment, QOrganizerItemComment::FieldComment); QOrganizerItemDetailFieldFilter df2; df2.setDetail(QOrganizerItemDetail::TypeDescription, QOrganizerItemDescription::FieldDescription); QOrganizerItemDetailFieldFilter df3; df3.setDetail(QOrganizerItemDetail::TypeDisplayLabel, QOrganizerItemDisplayLabel::FieldLabel); QOrganizerItemUnionFilter bf; bf << df << df2; QTest::newRow("union") << (QOrganizerItemFilter)bf << "QOrganizerItemFilter(QOrganizerItemUnionFilter(filters=(QOrganizerItemFilter(QOrganizerItemDetailFieldFilter(detailType=200,detailField=201,value=QVariant(Invalid) ,matchFlags=0)), QOrganizerItemFilter(QOrganizerItemDetailFieldFilter(detailType=300,detailField=301,value=QVariant(Invalid) ,matchFlags=0)))))"; } } QTEST_MAIN(tst_QOrganizerItemFilter) #include "tst_qorganizeritemfilter.moc" tests/auto/organizer/qorganizeritemsortorder/000077500000000000000000000000001233466112000221725ustar00rootroot00000000000000tests/auto/organizer/qorganizeritemsortorder/qorganizeritemsortorder.pro000066400000000000000000000001261233466112000277170ustar00rootroot00000000000000include(../../auto.pri) QT += organizer SOURCES += tst_qorganizeritemsortorder.cpp tests/auto/organizer/qorganizeritemsortorder/tst_qorganizeritemsortorder.cpp000066400000000000000000000471131233466112000306020ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include //TESTED_COMPONENT=src/organizer QTORGANIZER_USE_NAMESPACE Q_DECLARE_METATYPE(QOrganizerItem) Q_DECLARE_METATYPE(QOrganizerItemSortOrder) class tst_QOrganizerItemSortOrder : public QObject { Q_OBJECT public slots: void init(); void cleanup(); private slots: void traits(); void sortObject(); void compareItem_data(); void compareItem(); void datastream_data(); void datastream(); void debugstream_data(); void debugstream(); }; void tst_QOrganizerItemSortOrder::init() { } void tst_QOrganizerItemSortOrder::cleanup() { } void tst_QOrganizerItemSortOrder::traits() { QTypeInfo ti; QVERIFY(ti.isComplex); QVERIFY(!ti.isStatic); QVERIFY(!ti.isLarge); QVERIFY(!ti.isPointer); QVERIFY(!ti.isDummy); QCOMPARE(uint(ti.sizeOf), uint(sizeof(void *))); } void tst_QOrganizerItemSortOrder::sortObject() { QOrganizerItemSortOrder sortorder; /* Defaults */ QVERIFY(sortorder.blankPolicy() == QOrganizerItemSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(sortorder.detailField() == -1); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.caseSensitivity() == Qt::CaseSensitive); QVERIFY(!sortorder.isValid()); QVERIFY(sortorder == QOrganizerItemSortOrder()); /* Blank Policy */ sortorder.setBlankPolicy(QOrganizerItemSortOrder::BlanksFirst); QVERIFY(sortorder.blankPolicy() == QOrganizerItemSortOrder::BlanksFirst); QVERIFY(sortorder.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(sortorder.detailField() == -1); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.caseSensitivity() == Qt::CaseSensitive); QVERIFY(!sortorder.isValid()); QVERIFY(sortorder != QOrganizerItemSortOrder()); sortorder.setBlankPolicy(QOrganizerItemSortOrder::BlanksLast); QVERIFY(sortorder.blankPolicy() == QOrganizerItemSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(sortorder.detailField() == -1); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.caseSensitivity() == Qt::CaseSensitive); QVERIFY(!sortorder.isValid()); QVERIFY(sortorder == QOrganizerItemSortOrder()); /* Direction */ sortorder.setDirection(Qt::DescendingOrder); QVERIFY(sortorder.direction() == Qt::DescendingOrder); QVERIFY(sortorder.blankPolicy() == QOrganizerItemSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(sortorder.detailField() == -1); QVERIFY(sortorder.caseSensitivity() == Qt::CaseSensitive); QVERIFY(!sortorder.isValid()); QVERIFY(sortorder != QOrganizerItemSortOrder()); sortorder.setDirection(Qt::AscendingOrder); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.blankPolicy() == QOrganizerItemSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(sortorder.detailField() == -1); QVERIFY(sortorder.caseSensitivity() == Qt::CaseSensitive); QVERIFY(!sortorder.isValid()); QVERIFY(sortorder == QOrganizerItemSortOrder()); /* Case sensitivity */ sortorder.setCaseSensitivity(Qt::CaseInsensitive); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.blankPolicy() == QOrganizerItemSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(sortorder.detailField() == -1); QVERIFY(sortorder.caseSensitivity() == Qt::CaseInsensitive); QVERIFY(!sortorder.isValid()); QVERIFY(sortorder != QOrganizerItemSortOrder()); sortorder.setCaseSensitivity(Qt::CaseSensitive); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.blankPolicy() == QOrganizerItemSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(sortorder.detailField() == -1); QVERIFY(sortorder.caseSensitivity() == Qt::CaseSensitive); QVERIFY(!sortorder.isValid()); QVERIFY(sortorder == QOrganizerItemSortOrder()); /* Definitions */ sortorder.setDetail(QOrganizerItemDetail::TypeUndefined, -1); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.blankPolicy() == QOrganizerItemSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(sortorder.detailField() == -1); QVERIFY(!sortorder.isValid()); QVERIFY(sortorder == QOrganizerItemSortOrder()); sortorder.setDetail(QOrganizerItemDetail::TypeComment, -1); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.blankPolicy() == QOrganizerItemSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QOrganizerItemDetail::TypeComment); QVERIFY(sortorder.detailField() == -1); QVERIFY(sortorder.isValid()); sortorder.setDetail(QOrganizerItemDetail::TypeComment, QOrganizerItemComment::FieldComment); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.blankPolicy() == QOrganizerItemSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QOrganizerItemDetail::TypeComment); QVERIFY(sortorder.detailField() == QOrganizerItemComment::FieldComment); QVERIFY(sortorder.isValid()); sortorder.setDetail(QOrganizerItemDetail::TypeUndefined, 101); QVERIFY(sortorder.direction() == Qt::AscendingOrder); QVERIFY(sortorder.blankPolicy() == QOrganizerItemSortOrder::BlanksLast); QVERIFY(sortorder.detailType() == QOrganizerItemDetail::TypeUndefined); QVERIFY(sortorder.detailField() == 101); QVERIFY(!sortorder.isValid()); /* Copy ctor */ sortorder.setDetail(QOrganizerItemDetail::TypeComment, QOrganizerItemComment::FieldComment); sortorder.setBlankPolicy(QOrganizerItemSortOrder::BlanksFirst); sortorder.setDirection(Qt::DescendingOrder); QVERIFY(sortorder.direction() == Qt::DescendingOrder); QVERIFY(sortorder.blankPolicy() == QOrganizerItemSortOrder::BlanksFirst); QVERIFY(sortorder.detailType() == QOrganizerItemDetail::TypeComment); QVERIFY(sortorder.detailField() == QOrganizerItemComment::FieldComment); QVERIFY(sortorder.isValid()); QOrganizerItemSortOrder other(sortorder); QVERIFY(other.direction() == Qt::DescendingOrder); QVERIFY(other.blankPolicy() == QOrganizerItemSortOrder::BlanksFirst); QVERIFY(other.detailType() == QOrganizerItemDetail::TypeComment); QVERIFY(other.detailField() == QOrganizerItemComment::FieldComment); QVERIFY(other.isValid()); QVERIFY(other == sortorder); QVERIFY(!(other != sortorder)); other.setDetail(QOrganizerItemDetail::TypeDescription, QOrganizerItemDescription::FieldDescription); QVERIFY(other != sortorder); other.setDetail(QOrganizerItemDetail::TypeComment, 2); QVERIFY(other != sortorder); /* Assignment operator */ QOrganizerItemSortOrder another; another = other; QVERIFY(another.direction() == Qt::DescendingOrder); QVERIFY(another.blankPolicy() == QOrganizerItemSortOrder::BlanksFirst); QVERIFY(another.detailType() == QOrganizerItemDetail::TypeComment); QVERIFY(another.detailField() == 2); QVERIFY(another.isValid()); QVERIFY(another == other); QVERIFY(!(other != another)); /* Self assignment */ another = another; QVERIFY(another.direction() == Qt::DescendingOrder); QVERIFY(another.blankPolicy() == QOrganizerItemSortOrder::BlanksFirst); QVERIFY(another.detailType() == QOrganizerItemDetail::TypeComment); QVERIFY(another.detailField() == 2); QVERIFY(another.isValid()); QVERIFY(another == other); QVERIFY(!(other != another)); } void tst_QOrganizerItemSortOrder::compareItem_data() { QTest::addColumn("item1"); QTest::addColumn("item2"); QTest::addColumn("sortOrder"); QTest::addColumn("expected"); QOrganizerItem emptyItem; { QOrganizerItem item1; item1.setDescription(QLatin1String("description")); item1.setDisplayLabel(QLatin1String("My label")); item1.setGuid(QLatin1String("98765")); QOrganizerItem item2; item2.setDescription(QLatin1String("DESCRIPTION")); item2.setDisplayLabel(QLatin1String("label")); item2.setGuid(QLatin1String("12345")); QOrganizerItemLocation name; name.setLabel("test location"); item2.saveDetail(&name); QOrganizerItemSortOrder sortOrder; QTest::newRow("empty sortorder") << item1 << item2 << sortOrder << 0; sortOrder.setDetail(QOrganizerItemDetail::TypeDescription, QOrganizerItemDescription::FieldDescription); QTest::newRow("empty items") << emptyItem << emptyItem << sortOrder << 0; QTest::newRow("equals") << item1 << item1 << sortOrder << 0; QTest::newRow("equals") << item2 << item2 << sortOrder << 0; sortOrder.setBlankPolicy(QOrganizerItemSortOrder::BlanksFirst); { sortOrder.setDetail(QOrganizerItemDetail::TypeLocation, -1); QTest::newRow("blanks first, field presence") << emptyItem << item2 << sortOrder << -1; QTest::newRow("blanks first, field presence") << item1 << item2 << sortOrder << -1; sortOrder.setDetail(QOrganizerItemDetail::TypeDescription, QOrganizerItemDescription::FieldDescription); QTest::newRow("blanks first") << emptyItem << item2 << sortOrder << -1; QTest::newRow("blanks first") << item2 << emptyItem << sortOrder << 1; sortOrder.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); QTest::newRow("blanks first") << emptyItem << item2 << sortOrder << -1; sortOrder.setDetail(QOrganizerItemDetail::TypeTag, QOrganizerItemTag::FieldTag); QTest::newRow("blanks first") << item1 << item2 << sortOrder << 0; } sortOrder.setBlankPolicy(QOrganizerItemSortOrder::BlanksLast); { sortOrder.setDetail(QOrganizerItemDetail::TypeLocation, -1); QTest::newRow("blanks last, field presence") << emptyItem << item2 << sortOrder << 1; QTest::newRow("blanks last, field presence") << item1 << item2 << sortOrder << 1; sortOrder.setDetail(QOrganizerItemDetail::TypeDescription, QOrganizerItemDescription::FieldDescription); QTest::newRow("blanks last") << emptyItem << item2 << sortOrder << 1; QTest::newRow("blanks last") << item2 << emptyItem << sortOrder << -1; sortOrder.setDetail(QOrganizerItemDetail::TypeLocation, QOrganizerItemLocation::FieldLabel); QTest::newRow("blanks first") << emptyItem << item2 << sortOrder << 1; sortOrder.setDetail(QOrganizerItemDetail::TypeTag, QOrganizerItemTag::FieldTag); QTest::newRow("blanks first") << item1 << item2 << sortOrder << 0; } sortOrder.setDirection(Qt::AscendingOrder); sortOrder.setCaseSensitivity(Qt::CaseSensitive); { sortOrder.setDetail(QOrganizerItemDetail::TypeDescription, QOrganizerItemDescription::FieldDescription); QTest::newRow("asc, cs") << item1 << item2 << sortOrder << 1; QTest::newRow("asc, cs") << item2 << item1 << sortOrder << -1; sortOrder.setCaseSensitivity(Qt::CaseInsensitive); QTest::newRow("asc, ci") << item1 << item2 << sortOrder << 0; sortOrder.setDetail(QOrganizerItemDetail::TypeDisplayLabel, QOrganizerItemDisplayLabel::FieldLabel); QTest::newRow("asc, ci") << item1 << item2 << sortOrder << 1; QTest::newRow("asc, ci") << item2 << item1 << sortOrder << -1; } sortOrder.setDirection(Qt::DescendingOrder); { sortOrder.setCaseSensitivity(Qt::CaseSensitive); sortOrder.setDetail(QOrganizerItemDetail::TypeDescription, QOrganizerItemDescription::FieldDescription); QTest::newRow("desc, cs") << item1 << item2 << sortOrder << -1; QTest::newRow("desc, cs") << item2 << item1 << sortOrder << 1; sortOrder.setCaseSensitivity(Qt::CaseInsensitive); QTest::newRow("desc, ci") << item1 << item2 << sortOrder << 0; sortOrder.setDetail(QOrganizerItemDetail::TypeDisplayLabel, QOrganizerItemDisplayLabel::FieldLabel); QTest::newRow("desc, ci") << item1 << item2 << sortOrder << -1; QTest::newRow("desc, ci") << item2 << item1 << sortOrder << 1; } } { // ensure non-existence is treated just like blank-ness QOrganizerItem item1; QOrganizerEventTime time1; time1.setStartDateTime(QDateTime::currentDateTime()); time1.setEndDateTime(QDateTime::currentDateTime().addDays(1)); item1.saveDetail(&time1); QOrganizerItem item2; QOrganizerEventTime time2; time2.setStartDateTime(QDateTime::currentDateTime()); item2.saveDetail(&time2); QOrganizerItemSortOrder sortOrder; sortOrder.setDetail(QOrganizerItemDetail::TypeEventTime, QOrganizerEventTime::FieldEndDateTime); sortOrder.setDirection(Qt::AscendingOrder); sortOrder.setCaseSensitivity(Qt::CaseInsensitive); sortOrder.setBlankPolicy(QOrganizerItemSortOrder::BlanksFirst); QTest::newRow("non-existence vs blank-ness, blanks first") << item1 << item2 << sortOrder << 1; QTest::newRow("non-existence vs blank-ness, blanks first") << emptyItem << item1 << sortOrder << -1; QTest::newRow("non-existence vs blank-ness, blanks first") << emptyItem << item2 << sortOrder << 0; sortOrder.setBlankPolicy(QOrganizerItemSortOrder::BlanksLast); QTest::newRow("non-existence vs blank-ness, blanks last") << item1 << item2 << sortOrder << -1; QTest::newRow("non-existence vs blank-ness, blanks last") << emptyItem << item1 << sortOrder << 1; QTest::newRow("non-existence vs blank-ness, blanks last") << emptyItem << item2 << sortOrder << 0; } } void tst_QOrganizerItemSortOrder::compareItem() { QFETCH(QOrganizerItem, item1); QFETCH(QOrganizerItem, item2); QFETCH(QOrganizerItemSortOrder, sortOrder); QFETCH(int, expected); int actual = QOrganizerManagerEngine::compareItem(item1, item2, (QList() << sortOrder)); actual = qBound(-1, actual, 1); QCOMPARE(actual, expected); } void tst_QOrganizerItemSortOrder::datastream_data() { QTest::addColumn("sortOrderIn"); { QOrganizerItemSortOrder sortOrder; QTest::newRow("default") << sortOrder; } { QOrganizerItemSortOrder sortOrder; sortOrder.setBlankPolicy(QOrganizerItemSortOrder::BlanksFirst); sortOrder.setDirection(Qt::DescendingOrder); sortOrder.setCaseSensitivity(Qt::CaseInsensitive); QTest::newRow("invalid") << sortOrder; } { QOrganizerItemSortOrder sortOrder; sortOrder.setDetail(QOrganizerItemDetail::TypeDescription, QOrganizerItemDescription::FieldDescription); QTest::newRow("detail") << sortOrder; } { QOrganizerItemSortOrder sortOrder; sortOrder.setDetail(QOrganizerItemDetail::TypeDescription, QOrganizerItemDescription::FieldDescription); sortOrder.setBlankPolicy(QOrganizerItemSortOrder::BlanksFirst); sortOrder.setDirection(Qt::DescendingOrder); sortOrder.setCaseSensitivity(Qt::CaseInsensitive); QTest::newRow("complete") << sortOrder; } } void tst_QOrganizerItemSortOrder::datastream() { QFETCH(QOrganizerItemSortOrder, sortOrderIn); QByteArray buffer; QDataStream stream1(&buffer, QIODevice::WriteOnly); stream1 << sortOrderIn; QVERIFY(buffer.size() > 0); QDataStream stream2(buffer); QOrganizerItemSortOrder sortOrderOut; stream2 >> sortOrderOut; QCOMPARE(sortOrderOut, sortOrderIn); } void tst_QOrganizerItemSortOrder::debugstream_data() { QTest::addColumn("sortOrder"); QTest::addColumn("messageExpected"); { QOrganizerItemSortOrder sortOrder; QTest::newRow("default") << sortOrder << "QOrganizerItemSortOrder(detailType=0,detailField=-1,blankPolicy=1,direction=0,caseSensitivity=1)"; } { QOrganizerItemSortOrder sortOrder; sortOrder.setBlankPolicy(QOrganizerItemSortOrder::BlanksFirst); sortOrder.setDirection(Qt::DescendingOrder); sortOrder.setCaseSensitivity(Qt::CaseInsensitive); QTest::newRow("invalid") << sortOrder << "QOrganizerItemSortOrder(detailType=0,detailField=-1,blankPolicy=0,direction=1,caseSensitivity=0)"; } { QOrganizerItemSortOrder sortOrder; sortOrder.setDetail(QOrganizerItemDetail::TypeDescription, QOrganizerItemDescription::FieldDescription); QTest::newRow("detail") << sortOrder << "QOrganizerItemSortOrder(detailType=300,detailField=301,blankPolicy=1,direction=0,caseSensitivity=1)"; } { QOrganizerItemSortOrder sortOrder; sortOrder.setDetail(QOrganizerItemDetail::TypeDescription, QOrganizerItemDescription::FieldDescription); sortOrder.setBlankPolicy(QOrganizerItemSortOrder::BlanksFirst); sortOrder.setDirection(Qt::DescendingOrder); sortOrder.setCaseSensitivity(Qt::CaseInsensitive); QTest::newRow("complete") << sortOrder << "QOrganizerItemSortOrder(detailType=300,detailField=301,blankPolicy=0,direction=1,caseSensitivity=0)"; } } void tst_QOrganizerItemSortOrder::debugstream() { QFETCH(QOrganizerItemSortOrder, sortOrder); QFETCH(QString, messageExpected); QTest::ignoreMessage(QtDebugMsg, messageExpected.toUtf8()); qDebug() << sortOrder; } QTEST_MAIN(tst_QOrganizerItemSortOrder) #include "tst_qorganizeritemsortorder.moc" tests/auto/organizer/qorganizermanager/000077500000000000000000000000001233466112000207025ustar00rootroot00000000000000tests/auto/organizer/qorganizermanager/partitions.json000066400000000000000000000001211233466112000237630ustar00rootroot00000000000000[ { "name" : "com.nokia.mt.User" }, { "name" : "com.nokia.mt.System" } ] tests/auto/organizer/qorganizermanager/qorganizermanager.pro000066400000000000000000000003621233466112000251410ustar00rootroot00000000000000include(../../auto.pri) QT += organizer organizer-private qtHaveModule(jsondb): QT += jsondb SOURCES += tst_qorganizermanager.cpp HEADERS += ../qorganizermanagerdataholder.h ../../jsondbprocess.h DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/organizer/qorganizermanager/tst_qorganizermanager.cpp000066400000000000000000007005421233466112000260240ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/organizer #include #include #include #include #include "../qorganizermanagerdataholder.h" #include "../../jsondbprocess.h" #include #include #include QTORGANIZER_USE_NAMESPACE // to get QFETCH to work with the template expression... typedef QMap tst_QOrganizerManager_QStringMap; Q_DECLARE_METATYPE(tst_QOrganizerManager_QStringMap) Q_DECLARE_METATYPE(QList) /* A class that no backend can support */ class UnsupportedMetatype { int foo; }; Q_DECLARE_METATYPE(UnsupportedMetatype) Q_DECLARE_METATYPE(QOrganizerItem) Q_DECLARE_METATYPE(QOrganizerManager::Error) Q_DECLARE_METATYPE(QList) class tst_QOrganizerManager : public QObject { Q_OBJECT public: tst_QOrganizerManager(); virtual ~tst_QOrganizerManager(); private: void dumpOrganizerItemDifferences(const QOrganizerItem& a, const QOrganizerItem& b); void dumpOrganizerItem(const QOrganizerItem &c); void dumpOrganizerItems(QOrganizerManager *cm); bool isSuperset(const QOrganizerItem& ca, const QOrganizerItem& cb); QList removeAllDefaultDetails(const QList& details); void addManagers(); // add standard managers to the data QScopedPointer managerDataHolder; JsonDbProcess jsondbProcess; public slots: void initTestCase(); void cleanupTestCase(); private slots: void doDump(); void doDump_data() {addManagers();} /* Special test with special data */ void uriParsing(); void recurrenceWithGenerator(); void todoRecurrenceWithGenerator(); void dateRange(); /* Tests that are run on all managers */ void metadata(); void nullIdOperations(); void add(); void saveRecurrence(); void persistence(); void addExceptions(); void modifyRecurrence(); void addExceptionsWithGuid(); void update(); void remove(); void batch(); void observerDeletion(); void signalEmission(); void detailOrders(); void itemType(); void collections(); void dataSerialization(); void itemFetch(); void todoItemFetch(); void itemFetchV2(); void itemFilterFetch(); void spanOverDays(); void incompleteTodoTime(); void recurrence(); void idComparison(); void emptyItemManipulation(); void partialSave(); /* Tests that take no data */ void errorStayingPut(); void ctors(); void invalidManager(); void memoryManager(); void changeSet(); void fetchHint(); void testFilterFunction(); void testReminder(); void testIntersectionFilter(); void testNestCompoundFilter(); void testUnionFilter(); void testItemOccurrences(); /* Special test with special data */ void uriParsing_data(); void recurrenceWithGenerator_data(); void todoRecurrenceWithGenerator_data(); void dateRange_data(); /* Tests that are run on all managers */ void metadata_data() {addManagers();} void nullIdOperations_data() {addManagers();} void add_data() {addManagers();} void saveRecurrence_data() {addManagers();} void persistence_data() {addManagers();} void addExceptions_data() {addManagers();} void addExceptionsWithGuid_data() {addManagers();} void modifyRecurrence_data() {addManagers();} void update_data() {addManagers();} void remove_data() {addManagers();} void batch_data() {addManagers();} void signalEmission_data() {addManagers();} void detailOrders_data() {addManagers();} void itemType_data() {addManagers();} void collections_data() {addManagers();} void dataSerialization_data() {addManagers();} void itemFetch_data() {addManagers();} void todoItemFetch_data() {addManagers();} void itemFetchV2_data() {addManagers();} void itemFilterFetch_data() {addManagers();} void spanOverDays_data() {addManagers();} void incompleteTodoTime_data() {addManagers();} void recurrence_data() {addManagers();} void idComparison_data() {addManagers();} void testReminder_data() {addManagers();} void testIntersectionFilter_data() {addManagers();} void testNestCompoundFilter_data() {addManagers();} void testUnionFilter_data() {addManagers();} void emptyItemManipulation_data() {addManagers();} void partialSave_data() {addManagers();} void testItemOccurrences_data(){addManagers();} void testTags_data() { addManagers(); } void testTags(); void testExtendedDetail_data() { addManagers(); } void testExtendedDetail(); void testAttendee_data() { addManagers(); } void testAttendee(); void testRsvp_data() { addManagers(); } void testRsvp(); void testClassification_data() { addManagers(); } void testClassification(); void testVersion_data() { addManagers(); } void testVersion(); }; class BasicItemLocalId : public QOrganizerItemEngineId { public: BasicItemLocalId(uint id) : m_id(id) {} bool isEqualTo(const QOrganizerItemEngineId* other) const { return m_id == static_cast(other)->m_id; } bool isLessThan(const QOrganizerItemEngineId* other) const { return m_id < static_cast(other)->m_id; } QOrganizerItemEngineId* clone() const { BasicItemLocalId* cloned = new BasicItemLocalId(m_id); return cloned; } QString managerUri() const { static const QString uri(QStringLiteral("qtorganizer:basicItem:")); return uri; } QDebug& debugStreamOut(QDebug& dbg) const { return dbg << m_id; } QString toString() const { return QString::number(m_id); } uint hash() const { return m_id; } private: uint m_id; }; class BasicCollectionLocalId : public QOrganizerCollectionEngineId { public: BasicCollectionLocalId(uint id) : m_id(id) {} bool isEqualTo(const QOrganizerCollectionEngineId* other) const { return m_id == static_cast(other)->m_id; } bool isLessThan(const QOrganizerCollectionEngineId* other) const { return m_id < static_cast(other)->m_id; } QOrganizerCollectionEngineId* clone() const { BasicCollectionLocalId* cloned = new BasicCollectionLocalId(m_id); return cloned; } QString managerUri() const { static const QString uri(QStringLiteral("qtorganizer:basicCollection:")); return uri; } QDebug& debugStreamOut(QDebug& dbg) const { return dbg << m_id; } QString toString() const { return QString::number(m_id); } uint hash() const { return m_id; } private: uint m_id; }; QOrganizerItemId makeItemId(uint id) { return QOrganizerItemId(new BasicItemLocalId(id)); } QOrganizerCollectionId makeCollectionId(uint id) { return QOrganizerCollectionId(new BasicCollectionLocalId(id)); } tst_QOrganizerManager::tst_QOrganizerManager() { } tst_QOrganizerManager::~tst_QOrganizerManager() { } void tst_QOrganizerManager::initTestCase() { // Start JsonDb daemon if needed if (QOrganizerManager::availableManagers().contains("jsondb")) { QString partitions_json = QFINDTESTDATA("partitions.json"); QVERIFY2(!partitions_json.isEmpty(), "partitions.json file is missing"); QVERIFY2(jsondbProcess.start(partitions_json), "Failed to start JsonDb process"); } managerDataHolder.reset(new QOrganizerManagerDataHolder()); /* Make sure these other test plugins are NOT loaded by default */ // These are now removed from the list of managers in addManagers() QVERIFY(!QOrganizerManager::availableManagers().contains("testdummy")); QVERIFY(!QOrganizerManager::availableManagers().contains("teststaticdummy")); } void tst_QOrganizerManager::cleanupTestCase() { managerDataHolder.reset(0); if (QOrganizerManager::availableManagers().contains("jsondb")) jsondbProcess.terminate(); } void tst_QOrganizerManager::dumpOrganizerItemDifferences(const QOrganizerItem& ca, const QOrganizerItem& cb) { // Try to narrow down the differences QOrganizerItem a(ca); QOrganizerItem b(cb); // Check the display label QCOMPARE(a.displayLabel(), b.displayLabel()); // Now look at the rest QList aDetails = a.details(); QList bDetails = b.details(); // They can be in any order, so loop // First remove any matches foreach(QOrganizerItemDetail d, aDetails) { foreach(QOrganizerItemDetail d2, bDetails) { if(d == d2) { a.removeDetail(&d); b.removeDetail(&d2); break; } } } // Now dump the extra details that were unmatched in A (note that DisplayLabel and Type are always present). aDetails = a.details(); bDetails = b.details(); foreach(QOrganizerItemDetail d, aDetails) { if (d.type() != QOrganizerItemDetail::TypeDisplayLabel && d.type() != QOrganizerItemDetail::TypeItemType) qDebug() << "A item had extra detail:" << d.type() << d.values(); } // and same for B foreach(QOrganizerItemDetail d, bDetails) { if (d.type() != QOrganizerItemDetail::TypeDisplayLabel && d.type() != QOrganizerItemDetail::TypeItemType) qDebug() << "B item had extra detail:" << d.type() << d.values(); } // now test specifically the display label and the type if (a.displayLabel() != b.displayLabel()) { qDebug() << "A item display label =" << a.displayLabel(); qDebug() << "B item display label =" << b.displayLabel(); } if (a.type() != b.type()) { qDebug() << "A item type =" << a.type(); qDebug() << "B item type =" << b.type(); } } bool tst_QOrganizerManager::isSuperset(const QOrganizerItem& ca, const QOrganizerItem& cb) { // returns true if item ca is a superset of item cb // we use this test instead of equality because dynamic information // such as presence/location, and synthesised information such as // display label and (possibly) type, may differ between a item // in memory and the item in the managed store. QOrganizerItem a(ca); QOrganizerItem b(cb); QList aDetails = a.details(); QList bDetails = b.details(); // They can be in any order, so loop // First remove any matches foreach(QOrganizerItemDetail d, aDetails) { foreach(QOrganizerItemDetail d2, bDetails) { if(d == d2) { a.removeDetail(&d); b.removeDetail(&d2); break; } } } // Second remove any superset matches (eg, backend adds a field) aDetails = a.details(); bDetails = b.details(); foreach (QOrganizerItemDetail d, aDetails) { foreach (QOrganizerItemDetail d2, bDetails) { if (d.type() == d2.type()) { bool canRemove = true; QMap d2map = d2.values(); foreach (int key, d2map.keys()) { if (d.value(key) != d2.value(key)) { // d can have _more_ keys than d2, // but not _less_; and it cannot // change the value. canRemove = false; } } if (canRemove) { // if we get to here, we can remove the details. a.removeDetail(&d); b.removeDetail(&d2); break; } } } } // check for item type updates if (a.type() != QOrganizerItemType::TypeUndefined) if (b.type() != QOrganizerItemType::TypeUndefined) if (a.type() != b.type()) return false; // nonempty type is different. // Now check to see if b has any details remaining; if so, a is not a superset. // Note that the DisplayLabel and Type can never be removed. if (b.details().size() > 2 || (b.details().size() == 2 && (b.details().value(0).type() != QOrganizerItemDetail::TypeDisplayLabel || b.details().value(1).type() != QOrganizerItemDetail::TypeItemType))) return false; return true; } void tst_QOrganizerManager::dumpOrganizerItem(const QOrganizerItem& item) { QOrganizerManager m; qDebug() << "OrganizerItem: " << item.id() << "(" << item.displayLabel() << ")"; QList details = item.details(); foreach(QOrganizerItemDetail d, details) { qDebug() << " " << d.type() << ":"; qDebug() << " Vals:" << d.values(); } } void tst_QOrganizerManager::dumpOrganizerItems(QOrganizerManager *cm) { QList ids = cm->itemIds(); qDebug() << "There are" << ids.count() << "items in" << cm->managerUri(); foreach(QOrganizerItemId id, ids) { QOrganizerItem c = cm->item(id); dumpOrganizerItem(c); } } void tst_QOrganizerManager::uriParsing_data() { QTest::addColumn("uri"); QTest::addColumn("good"); // is this a good uri or not QTest::addColumn("manager"); QTest::addColumn >("parameters"); QMap inparameters; inparameters.insert("foo", "bar"); inparameters.insert("bazflag", QString()); inparameters.insert("bar", "glob"); QMap inparameters2; inparameters2.insert("this has spaces", QString()); inparameters2.insert("and& an", " &"); inparameters2.insert("and an ", "=quals"); QTest::newRow("built") << QOrganizerManager::buildUri("manager", inparameters) << true << "manager" << inparameters; QTest::newRow("built with escaped parameters") << QOrganizerManager::buildUri("manager", inparameters2) << true << "manager" << inparameters2; QTest::newRow("no scheme") << "this should not split" << false << QString() << tst_QOrganizerManager_QStringMap(); QTest::newRow("wrong scheme") << "invalidscheme:foo bar" << false << QString() << tst_QOrganizerManager_QStringMap(); QTest::newRow("right scheme, no colon") << "qtorganizer" << false << QString() << tst_QOrganizerManager_QStringMap(); QTest::newRow("no manager, colon, no params") << "qtorganizer::" << false << "manager" << tst_QOrganizerManager_QStringMap(); QTest::newRow("yes manager, no colon, no params") << "qtorganizer:manager" << true << "manager" << tst_QOrganizerManager_QStringMap(); QTest::newRow("yes manager, yes colon, no params") << "qtorganizer:manager:" << true << "manager"<< tst_QOrganizerManager_QStringMap(); QTest::newRow("yes params") << "qtorganizer:manager:foo=bar&bazflag=&bar=glob" << true << "manager" << inparameters; QTest::newRow("yes params but misformed") << "qtorganizer:manager:foo=bar&=gloo&bar=glob" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 2") << "qtorganizer:manager:=&=gloo&bar=glob" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 3") << "qtorganizer:manager:==" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 4") << "qtorganizer:manager:&&" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 5") << "qtorganizer:manager:&goo=bar" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 6") << "qtorganizer:manager:goo&bar" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 7") << "qtorganizer:manager:goo&bar&gob" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 8") << "qtorganizer:manager:==&&==&goo=bar" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 9") << "qtorganizer:manager:foo=bar=baz" << false << "manager" << inparameters; QTest::newRow("yes params but misformed 10") << "qtorganizer:manager:foo=bar=baz=glob" << false << "manager" << inparameters; QTest::newRow("no manager but yes params") << "qtorganizer::foo=bar&bazflag=&bar=glob" << false << QString() << inparameters; QTest::newRow("no manager or params") << "qtorganizer::" << false << QString() << inparameters; QTest::newRow("no manager or params or colon") << "qtorganizer:" << false << QString() << inparameters; } void tst_QOrganizerManager::addManagers() { QTest::addColumn("uri"); QStringList managers = QOrganizerManager::availableManagers(); /* Known one that will not pass */ managers.removeAll("invalid"); managers.removeAll("skeleton"); managers.removeAll("testdummy"); managers.removeAll("teststaticdummy"); managers.removeAll("maliciousplugin"); foreach(QString mgr, managers) { QMap params; QTest::newRow(QString("mgr='%1'").arg(mgr).toLatin1().constData()) << QOrganizerManager::buildUri(mgr, params); if (mgr == "memory") { params.insert("id", "tst_QOrganizerManager"); QTest::newRow(QString("mgr='%1', params").arg(mgr).toLatin1().constData()) << QOrganizerManager::buildUri(mgr, params); } } } void tst_QOrganizerManager::metadata() { // ensure that the backend is publishing its metadata (name / parameters / uri) correctly QFETCH(QString, uri); QScopedPointer cm(new QOrganizerManager("memory")); QVERIFY(QOrganizerManager::buildUri(cm->managerName(), cm->managerParameters()) == cm->managerUri()); } void tst_QOrganizerManager::nullIdOperations() { QFETCH(QString, uri); QScopedPointer cm(new QOrganizerManager("memory")); QVERIFY(!cm->removeItem(QOrganizerItemId())); QVERIFY(cm->error() == QOrganizerManager::DoesNotExistError); QOrganizerItem c = cm->item(QOrganizerItemId()); QVERIFY(c.id() == QOrganizerItemId()); QVERIFY(c.isEmpty()); QVERIFY(cm->error() == QOrganizerManager::DoesNotExistError); /* TODO: rewrite tests using toString() / fromString() // test that streaming null ids doesn't crash. { QOrganizerItemId nullId; QByteArray buffer; QDataStream outBufferStream(&buffer, QIODevice::WriteOnly); outBufferStream << nullId; QVERIFY(buffer.length() > 0); QDataStream inBufferStream(buffer); QOrganizerItemId id; inBufferStream >> id; QVERIFY(id == nullId); } { QOrganizerItemId nullid; QByteArray buffer; QDataStream outBufferStream(&buffer, QIODevice::WriteOnly); outBufferStream << nullid; QVERIFY(buffer.length() > 0); // cannot stream in local ids; must stream in entire ids. //QDataStream inBufferStream(buffer); //QOrganizerItemId id; //inBufferStream >> id; //QVERIFY(id == nullid); } { QOrganizerCollectionId nullId; QByteArray buffer; QDataStream outBufferStream(&buffer, QIODevice::WriteOnly); outBufferStream << nullId; QVERIFY(buffer.length() > 0); QDataStream inBufferStream(buffer); QOrganizerCollectionId id; inBufferStream >> id; QVERIFY(id == nullId); } { QOrganizerCollectionId nullLocalId; QByteArray buffer; QDataStream outBufferStream(&buffer, QIODevice::WriteOnly); outBufferStream << nullLocalId; QVERIFY(buffer.length() > 0); // cannot stream in local ids; must stream in entire ids. //QDataStream inBufferStream(buffer); //QOrganizerCollectionLocalId id; //inBufferStream >> id; //QVERIFY(id == nullLocalId); } */ } void tst_QOrganizerManager::uriParsing() { QFETCH(QString, uri); QFETCH(bool, good); QFETCH(QString, manager); QFETCH(tst_QOrganizerManager_QStringMap, parameters); QString outmanager; QMap outparameters; if (good) { /* Good split */ /* Test splitting */ QVERIFY(QOrganizerManager::parseUri(uri, 0, 0)); // no out parms // 1 out param QVERIFY(QOrganizerManager::parseUri(uri, &outmanager, 0)); QCOMPARE(manager, outmanager); QVERIFY(QOrganizerManager::parseUri(uri, 0, &outparameters)); QCOMPARE(parameters, outparameters); outmanager.clear(); outparameters.clear(); QVERIFY(QOrganizerManager::parseUri(uri, &outmanager, &outparameters)); QCOMPARE(manager, outmanager); QCOMPARE(parameters, outparameters); } else { /* bad splitting */ outmanager.clear(); outparameters.clear(); QVERIFY(QOrganizerManager::parseUri(uri, 0, 0) == false); QVERIFY(QOrganizerManager::parseUri(uri, &outmanager, 0) == false); QVERIFY(outmanager.isEmpty()); QVERIFY(QOrganizerManager::parseUri(uri, 0, &outparameters) == false); QVERIFY(outparameters.isEmpty()); /* make sure the in parameters don't change with a bad split */ outmanager = manager; outparameters = parameters; QVERIFY(QOrganizerManager::parseUri(uri, &outmanager, 0) == false); QCOMPARE(manager, outmanager); QVERIFY(QOrganizerManager::parseUri(uri, 0, &outparameters) == false); QCOMPARE(parameters, outparameters); } } void tst_QOrganizerManager::ctors() { /* test the different ctors to make sure we end up with the same uri */ QVERIFY(QOrganizerManager::availableManagers().count() >= 1); // invalid, and probably something else QVERIFY(QOrganizerManager::availableManagers().contains("invalid")); QString defaultStore = QOrganizerManager::availableManagers().value(0); qDebug() << "Available managers:" << QOrganizerManager::availableManagers(); QMap randomParameters; randomParameters.insert("something", "old"); randomParameters.insert("something...", "new"); randomParameters.insert("something ", "borrowed"); randomParameters.insert(" something", "blue"); QObject parent; QOrganizerManager cm; // default QOrganizerManager cm2(defaultStore); QOrganizerManager cm3(defaultStore, QMap()); //QOrganizerManager cm4(cm.managerUri()); // should fail QScopedPointer cm5(QOrganizerManager::fromUri(QOrganizerManager::buildUri(defaultStore, QMap()))); QScopedPointer cm6(QOrganizerManager::fromUri(cm.managerUri())); // uri is not a name; should fail. QScopedPointer cm9(QOrganizerManager::fromUri(QString(), &parent)); QVERIFY(cm9->parent() == &parent); /* OLD TEST WAS THIS: */ //QCOMPARE(cm.managerUri(), cm2.managerUri()); //QCOMPARE(cm.managerUri(), cm3.managerUri()); //QCOMPARE(cm.managerUri(), cm5->managerUri()); //QCOMPARE(cm.managerUri(), cm6->managerUri()); //QCOMPARE(cm.managerUri(), cm9->managerUri()); /* NEW TEST IS THIS: Test that the names of the managers are the same */ QCOMPARE(cm.managerName(), cm2.managerName()); QCOMPARE(cm.managerName(), cm3.managerName()); QCOMPARE(cm.managerName(), cm5->managerName()); QCOMPARE(cm.managerName(), cm6->managerName()); QCOMPARE(cm.managerName(), cm9->managerName()); //QVERIFY(cm.managerUri() != cm4.managerUri()); /* Test that we get invalid stores when we do silly things */ QOrganizerManager em("non existent"); QOrganizerManager em2("non existent", QMap()); QOrganizerManager em3("memory", randomParameters); /* Also invalid, since we don't have one of these anyway */ QScopedPointer em4(QOrganizerManager::fromUri("invalid uri")); QScopedPointer em5(QOrganizerManager::fromUri(QOrganizerManager::buildUri("nonexistent", QMap()))); QScopedPointer em6(QOrganizerManager::fromUri(em3.managerUri())); /* * Sets of stores that should be equivalent: * - 1, 2, 4, 5 * - 3, 6 */ /* First some URI testing for equivalent stores */ QVERIFY(em.managerUri() == em2.managerUri()); QVERIFY(em.managerUri() == em5->managerUri()); QVERIFY(em.managerUri() == em4->managerUri()); QVERIFY(em2.managerUri() == em4->managerUri()); QVERIFY(em2.managerUri() == em5->managerUri()); QVERIFY(em4->managerUri() == em5->managerUri()); QVERIFY(em3.managerUri() == em6->managerUri()); /* Test the stores that should not be the same */ QVERIFY(em.managerUri() != em3.managerUri()); QVERIFY(em.managerUri() != em6->managerUri()); /* now the components */ QCOMPARE(em.managerName(), QString("invalid")); QCOMPARE(em2.managerName(), QString("invalid")); QCOMPARE(em3.managerName(), QString("memory")); QCOMPARE(em4->managerName(), QString("invalid")); QCOMPARE(em5->managerName(), QString("invalid")); QCOMPARE(em6->managerName(), QString("memory")); QCOMPARE(em.managerParameters(), tst_QOrganizerManager_QStringMap()); QCOMPARE(em2.managerParameters(), tst_QOrganizerManager_QStringMap()); QCOMPARE(em4->managerParameters(), tst_QOrganizerManager_QStringMap()); QCOMPARE(em5->managerParameters(), tst_QOrganizerManager_QStringMap()); QCOMPARE(em3.managerParameters(), em6->managerParameters()); // memory engine discards the given params, replaces with id. // Finally test the platform specific engines are actually the defaults #if !defined(QT_NO_JSONDB) QCOMPARE(defaultStore, QString::fromLatin1("jsondb")); #else QCOMPARE(defaultStore, QString("invalid")); #endif } void tst_QOrganizerManager::doDump() { // Only do this if it has been explicitly selected if (QCoreApplication::arguments().contains("doDump")) { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); dumpOrganizerItems(cm.data()); } } Q_DECLARE_METATYPE(QVariant) void tst_QOrganizerManager::add() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); // Use note & todo item depending on backend support QOrganizerItemType::ItemType type; if (cm->supportedItemTypes().contains(QOrganizerItemType::TypeNote)) type = QOrganizerItemType::TypeNote; else if (cm->supportedItemTypes().contains(QOrganizerItemType::TypeTodo)) type = QOrganizerItemType::TypeTodo; else QSKIP("This manager does not support note or todo item"); QOrganizerItem item; item.setType(type); item.setDisplayLabel("This is a note"); item.setDescription("This note is a particularly notey note"); int currCount = cm->itemIds().count(); QVERIFY(cm->saveItem(&item)); QVERIFY(cm->error() == QOrganizerManager::NoError); QVERIFY(!item.id().managerUri().isEmpty()); QVERIFY(!item.id().isNull()); QCOMPARE(cm->itemIds().count(), currCount+1); QOrganizerItem added = cm->item(item.id()); QVERIFY(added.id() == item.id()); if (!isSuperset(added, item)) { // XXX TODO: fix the isSuperset so that it ignores timestamps. //dumpOrganizerItems(cm.data()); //dumpOrganizerItemDifferences(added, note); QCOMPARE(added, item); } // now try adding an item that does not exist in the database with non-zero id QOrganizerItem nonexistentItem; nonexistentItem.setType(type); nonexistentItem.setDisplayLabel("Another note."); nonexistentItem.setDescription("This is `another note`'s description"); QVERIFY(cm->saveItem(&nonexistentItem)); // should work QVERIFY(cm->removeItem(nonexistentItem.id())); // now nonexistentItem has an id which does not exist QEXPECT_FAIL("mgr='jsondb'", "Expect fail due to Jsondb change", Continue); QVERIFY(!cm->saveItem(&nonexistentItem)); // hence, should fail QEXPECT_FAIL("mgr='jsondb'", "Expect fail due to Jsondb change", Continue); QCOMPARE(cm->error(), QOrganizerManager::DoesNotExistError); nonexistentItem.setId(QOrganizerItemId()); QVERIFY(cm->saveItem(&nonexistentItem)); // after setting id to zero, should save QVERIFY(cm->removeItem(nonexistentItem.id())); // now try adding a "megaevent" // - get list of all definitions supported by the manager // - add one detail of each definition to a item // - save the item // - read it back // - ensure that it's the same. #if defined(QT_NO_JSONDB) // QOrganizerEvent megaevent; // QMap defmap = cm->detailDefinitions(QOrganizerItemType::TypeEvent); // QList defs = defmap.values(); // foreach (const QOrganizerItemDetailDefinition def, defs) { // // This is probably read-only // if (def.name() == QOrganizerItemTimestamp::DefinitionName) // continue; // // otherwise, create a new detail of the given type and save it to the item // QOrganizerItemDetail det(def.name()); // QMap fieldmap = def.fields(); // QStringList fieldKeys = fieldmap.keys(); // foreach (const QString& fieldKey, fieldKeys) { // // get the field, and check to see that it's not constrained. // QOrganizerItemDetailFieldDefinition currentField = fieldmap.value(fieldKey); // // Attempt to create a worthy value // if (!currentField.allowableValues().isEmpty()) { // // we want to save a value that will be accepted. // if (currentField.dataType() == QVariant::StringList) // det.setValue(fieldKey, QStringList() << currentField.allowableValues().first().toString()); // else if (currentField.dataType() == QVariant::List) // det.setValue(fieldKey, QVariantList() << currentField.allowableValues().first()); // else // det.setValue(fieldKey, currentField.allowableValues().first()); // } else { // // any value of the correct type will be accepted // bool savedSuccessfully = false; // QVariant dummyValue = QVariant(fieldKey); // try to get some unique string data // if (currentField.dataType() < static_cast(QVariant::UserType)) { // QVariant::Type type = static_cast(currentField.dataType()); // // It is not a user-defined type // if (dummyValue.canConvert(type)) { // savedSuccessfully = dummyValue.convert(type); // if (savedSuccessfully) { // // we have successfully created a (supposedly) valid field for this detail. // det.setValue(fieldKey, dummyValue); // continue; // } // } // // nope, couldn't save the string value (test); try a date. // dummyValue = QVariant(QDate::currentDate()); // if (dummyValue.canConvert(type)) { // savedSuccessfully = dummyValue.convert(type); // if (savedSuccessfully) { // // we have successfully created a (supposedly) valid field for this detail. // det.setValue(fieldKey, dummyValue); // continue; // } // } // // nope, couldn't convert a string or a date - try the integer value (42) // dummyValue = QVariant(42); // if (dummyValue.canConvert(type)) { // savedSuccessfully = dummyValue.convert(type); // if (savedSuccessfully) { // // we have successfully created a (supposedly) valid field for this detail. // det.setValue(fieldKey, dummyValue); // continue; // } // } // } // // if we get here, we don't know what sort of value can be saved... // } // } // if (!det.isEmpty()) // megaevent.saveDetail(&det); // } // QVERIFY(cm->saveItem(&megaevent)); // must be able to save since built from definitions. // QOrganizerItem retrievedMegaitem = cm->item(megaevent.id()); // if (!isSuperset(retrievedMegaitem, megaevent)) { // dumpOrganizerItemDifferences(megaevent, retrievedMegaitem); // } // now a item with many details of a particular definition // if the detail is not unique it should then support minumum of two of the same kind // const int nrOfdetails = 2; // XXX TODO. #endif } void tst_QOrganizerManager::saveRecurrence() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); QOrganizerEvent event; event.setDisplayLabel(QStringLiteral("meeting")); event.setStartDateTime(QDateTime(QDate(2010, 1, 1), QTime(11, 0, 0))); event.setEndDateTime(QDateTime(QDate(2010, 1, 1), QTime(12, 0, 0))); QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Weekly); rrule.setLimit(QDate(2011, 1, 1)); rrule.setInterval(2); rrule.setDaysOfWeek(QSet() << Qt::Monday << Qt::Tuesday); rrule.setDaysOfMonth(QSet() << 1 << 2); rrule.setDaysOfYear(QSet() << 1 << 2); rrule.setMonthsOfYear(QSet() << QOrganizerRecurrenceRule::January << QOrganizerRecurrenceRule::February); rrule.setWeeksOfYear(QSet() << 1 << 2); rrule.setFirstDayOfWeek(Qt::Tuesday); //this is disabled because certain backend doesn't support it. //rrule.setPositions(QSet() << 1 << 2); event.setRecurrenceRule(rrule); event.setExceptionRule(rrule); QSet rdates; rdates << QDate(2010, 1, 4) << QDate(2010, 4, 1); event.setRecurrenceDates(rdates); event.setExceptionDates(rdates); QVERIFY(cm->saveItem(&event)); QOrganizerEvent savedEvent(cm->item(event.id())); QCOMPARE(event.recurrenceRule(), savedEvent.recurrenceRule()); QCOMPARE(event.exceptionRule(), savedEvent.exceptionRule()); QCOMPARE(event.recurrenceDates(), savedEvent.recurrenceDates()); QCOMPARE(event.exceptionDates(), savedEvent.exceptionDates()); } void tst_QOrganizerManager::persistence() { // Test that changes in one manager are visible from another QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); if (cm->managerName() == "memory") return; // memory engine is not persistent cm->removeItems(cm->itemIds()); QScopedPointer cm2(QOrganizerManager::fromUri(uri)); QCOMPARE(cm->items().size(), 0); // Add an event QOrganizerEvent event; event.setDisplayLabel(QStringLiteral("meeting")); event.setStartDateTime(QDateTime(QDate(2010, 1, 1), QTime(11, 0, 0))); event.setEndDateTime(QDateTime(QDate(2010, 1, 1), QTime(12, 0, 0))); QVERIFY(cm->saveItem(&event)); QTest::qWait(500); QCOMPARE(cm->items().size(), 1); QCOMPARE(cm2->items().size(), 1); // Remove the event cm->removeItems(cm->itemIds()); QTest::qWait(500); QCOMPARE(cm->items().size(), 0); QCOMPARE(cm2->items().size(), 0); //#if 0 // This is disabled because it'll fail on managers that don't support collections // Remove all non-default collections QList collections(cm->collections()); QOrganizerCollectionId defaultCollectionId(cm->defaultCollection().id()); foreach (const QOrganizerCollection &col, collections) { QOrganizerCollectionId id(col.id()); if (id != defaultCollectionId) cm->removeCollection(id); } QTest::qWait(500); QCOMPARE(cm2->collections().size(), cm->collections().size()); QOrganizerCollection collection; collection.setMetaData(QOrganizerCollection::KeyName, QStringLiteral("test collection")); QVERIFY(cm->saveCollection(&collection)); QTest::qWait(500); QCOMPARE(cm2->collections().size(), cm->collections().size()); //#endif } void tst_QOrganizerManager::addExceptions() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); QOrganizerEvent event; event.setDisplayLabel(QStringLiteral("meeting")); event.setStartDateTime(QDateTime(QDate(2010, 1, 1), QTime(11, 0, 0))); event.setEndDateTime(QDateTime(QDate(2010, 1, 1), QTime(12, 0, 0))); QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Weekly); rrule.setLimit(3); event.setRecurrenceRule(rrule); QVERIFY(cm->saveItem(&event)); QVERIFY(!event.id().isNull()); event = cm->item(event.id()); // the guid must be set so when it is exported to iCalendar, the relationship can be represented QVERIFY(!event.guid().isEmpty()); QList items = cm->itemOccurrences(event, QDateTime(QDate(2010, 1, 1), QTime(0, 0, 0)), QDateTime(QDate(2010, 2, 1), QTime(0, 0, 0))); QCOMPARE(items.size(), 3); QOrganizerItem secondItem = items.at(1); QCOMPARE(secondItem.type(), QOrganizerItemType::TypeEventOccurrence); QOrganizerEventOccurrence secondEvent = static_cast(secondItem); // not sure this is the best way... QCOMPARE(secondEvent.startDateTime(), QDateTime(QDate(2010, 1, 8), QTime(11, 0, 0))); QCOMPARE(secondEvent.id(), QOrganizerItemId()); QCOMPARE(secondEvent.parentId(), event.id()); // save a change to an occurrence's detail (ie. create an exception) secondEvent.setDisplayLabel(QStringLiteral("seminar")); QVERIFY(cm->saveItem(&secondEvent)); event = cm->item(event.id()); // reload the event to pick up any changed exception dates. items = cm->itemOccurrences(event, QDateTime(QDate(2010, 1, 1), QTime(0, 0, 0)), QDateTime(QDate(2010, 2, 1), QTime(0, 0, 0))); QCOMPARE(items.size(), 3); // shouldn't change the count. // save a change to an occurrence's time QOrganizerEventOccurrence thirdEvent = static_cast(items.at(2)); QCOMPARE(thirdEvent.id(), QOrganizerItemId()); QCOMPARE(thirdEvent.parentId(), event.id()); thirdEvent.setStartDateTime(QDateTime(QDate(2010, 1, 15), QTime(13, 0, 0))); thirdEvent.setEndDateTime(QDateTime(QDate(2010, 1, 15), QTime(14, 0, 0))); QVERIFY(cm->saveItem(&thirdEvent)); event = cm->item(event.id()); // reload the event to pick up any changed exception dates. items = cm->itemOccurrences(event, QDateTime(QDate(2010, 1, 1), QTime(0, 0, 0)), QDateTime(QDate(2010, 2, 1), QTime(0, 0, 0))); QCOMPARE(items.size(), 3); // shouldn't change the count. QOrganizerItem firstItem; bool foundFirst = false; bool foundSecond = false; bool foundThird = false; foreach (const QOrganizerItem& item, items) { if (item.id().isNull()) { foundFirst = true; firstItem = item; } if (item.id() == secondEvent.id()) { foundSecond = true; secondEvent = item; } if (item.id() == thirdEvent.id()) { foundThird = true; thirdEvent = item; } } // check that saving an exception doesn't change other items QVERIFY(foundFirst); // there should still be one "generated" occurrence QCOMPARE(firstItem.displayLabel(), QStringLiteral("meeting")); // and it should have the original label. // item occurrences which are not exceptions should have zero id QVERIFY(firstItem.id().isNull()); // the exception's changes have been persisted QVERIFY(foundSecond); QCOMPARE(secondEvent.displayLabel(), QStringLiteral("seminar")); // item occurrences which are persisted exceptions should have non-zero id QVERIFY(!secondEvent.id().isNull()); QVERIFY(foundThird); QCOMPARE(thirdEvent.startDateTime(), QDateTime(QDate(2010, 1, 15), QTime(13, 0, 0))); QCOMPARE(thirdEvent.endDateTime(), QDateTime(QDate(2010, 1, 15), QTime(14, 0, 0))); QVERIFY(!thirdEvent.id().isNull()); // remove persisted exception, it should not modify parent item's exception dates cm->removeItem(secondEvent.id()); event = cm->item(event.id()); // reload the event to pick up any changed exception dates. items = cm->itemOccurrences(event, QDateTime(QDate(2010, 1, 1), QTime(0, 0, 0)), QDateTime(QDate(2010, 2, 1), QTime(0, 0, 0))); QCOMPARE(items.size(), 2); // shouldn't change the count. // remove the parent item, persisted exceptions should be removed also cm->removeItem(event.id()); QVERIFY(cm->item(thirdEvent.id()).isEmpty()); } void tst_QOrganizerManager::modifyRecurrence() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); QOrganizerEvent event; event.setDisplayLabel(QStringLiteral("recurring meeting")); event.setStartDateTime(QDateTime(QDate(2010, 1, 1), QTime(11, 0, 0))); event.setEndDateTime(QDateTime(QDate(2010, 1, 1), QTime(12, 0, 0))); QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Yearly); rrule.setLimit(QDate(2014, 1, 1)); event.setRecurrenceRule(rrule); QVERIFY(cm->saveItem(&event)); QVERIFY(!event.id().isNull()); event = cm->item(event.id()); // the guid must be set so when it is exported to iCalendar, the relationship can be represented QVERIFY(!event.guid().isEmpty()); QList items = cm->itemOccurrences(event); QCOMPARE(items.size(), 5); QOrganizerItem secondItem = items.at(1); QCOMPARE(secondItem.type(), QOrganizerItemType::TypeEventOccurrence); QOrganizerEventOccurrence secondEvent = static_cast(secondItem); // not sure this is the best way... QCOMPARE(secondEvent.startDateTime(), QDateTime(QDate(2011, 1, 1), QTime(11, 0, 0))); QCOMPARE(secondEvent.id(), QOrganizerItemId()); QCOMPARE(secondEvent.parentId(), event.id()); // save a change to an occurrence's detail (ie. create an exception) secondEvent.setDisplayLabel(QStringLiteral("exceptional display label")); QVERIFY(cm->saveItem(&secondEvent)); event = cm->item(event.id()); // reload the event to pick up any changed exception dates. items = cm->itemOccurrences(event); QCOMPARE(items.size(), 5); // shouldn't change the count. // save a change to an occurrence's time QOrganizerEventOccurrence thirdEvent = static_cast(items.at(2)); QCOMPARE(thirdEvent.id(), QOrganizerItemId()); QCOMPARE(thirdEvent.parentId(), event.id()); thirdEvent.setStartDateTime(QDateTime(QDate(2012, 1, 2), QTime(15, 0, 0))); thirdEvent.setEndDateTime(QDateTime(QDate(2012, 1, 2), QTime(16, 0, 0))); QVERIFY(cm->saveItem(&thirdEvent)); event = cm->item(event.id()); // reload the event to pick up any changed exception dates. items = cm->itemOccurrences(event); QCOMPARE(items.size(), 5); // shouldn't change the count. QCOMPARE(items[0].id(), QOrganizerItemId()); QCOMPARE(items[1].id(), secondEvent.id()); QCOMPARE(items[2].id(), thirdEvent.id()); QCOMPARE(items[3].id(), QOrganizerItemId()); QCOMPARE(items[4].id(), QOrganizerItemId()); // modify recurrence rule rrule.setLimit(QDate(2011, 1, 1)); event.setRecurrenceRule(rrule); QVERIFY(cm->saveItem(&event)); items = cm->itemOccurrences(event); QCOMPARE(items.size(), 2); // only one generated and one exception should be left // use different function to fetch events items = cm->items(); QCOMPARE(items.size(), 2); // only one generated and one exception should be left } /*test ItemOccurrences function maxCount parameters*/ void tst_QOrganizerManager::testItemOccurrences() { //Get the uri value QFETCH (QString, uri); //Create the QOrganizerManager QScopedPointer cm (QOrganizerManager::fromUri (uri)); //Create the weekly recurrence event data that is used by the test QOrganizerEvent event; event.setDisplayLabel (QStringLiteral ("meeting")); event.setStartDateTime (QDateTime (QDate (2010, 1, 1), QTime (11, 0, 0))); event.setEndDateTime (QDateTime (QDate (2010, 1, 1), QTime (12, 0, 0))); //Create weekly recurrence rule and count limit is 3 QOrganizerRecurrenceRule rrule; rrule.setFrequency (QOrganizerRecurrenceRule::Weekly); rrule.setLimit (3); event.setRecurrenceRule (rrule); //Save the event QVERIFY (cm->saveItem (&event)); //Varify the event data guid QVERIFY (!event.id().isNull ()); event = cm->item (event.id ()); //The guid(Globally unique identifier) must be set so when it is exported to iCalendar, the relationship can be represented QVERIFY (!event.guid().isEmpty ()); //Use default parameter value to fetch QList items = cm->itemOccurrences( event, //parantItem QDateTime (QDate (2010, 1, 1), QTime (0, 0, 0)), //start date QDateTime (QDate (2010, 2, 1), QTime (0, 0, 0)) //end date ); //The result should be same as rrule's limitation 3 QCOMPARE(items.size(), 3); //Assign maxCount negative value to get same result as default value QList items2 = cm->itemOccurrences( event, //parantItem QDateTime (QDate (2010, 1, 1), QTime (0, 0, 0)), //start date QDateTime (QDate (2010, 2, 1), QTime (0, 0, 0)), //end date -5 //maxCount ); //The result should be same as rrule's limitation QCOMPARE(items2.size(), 3); //Assign maxCount bigger value to get same result as default value QList items3 = cm->itemOccurrences( event, QDateTime (QDate (2010, 1, 1), QTime (0, 0, 0)), QDateTime (QDate (2010, 2, 1), QTime (0, 0, 0)), 4 ); //The result should be same as rrule's limitation since the maxCount is bigger than the actually data QCOMPARE(items3.size(), 3); /*------------------------------------------------------------------------ Create 2nd Daily event data, now We have Weekly and Daily 2 different events and make the situation more complex. maxCount is also smaller than the actual item data. */ //Create 2nd Daily event data and limitation is 20 QOrganizerEvent event2; event2.setDisplayLabel (QStringLiteral ("meeting2")); event2.setStartDateTime (QDateTime (QDate (2010, 1, 1), QTime (11, 0, 0))); event2.setEndDateTime (QDateTime (QDate (2010, 1, 1), QTime (12, 0, 0))); rrule.setFrequency (QOrganizerRecurrenceRule::Daily); rrule.setLimit (20); event2.setRecurrenceRule (rrule); QVERIFY (cm->saveItem (&event2)); event2 = cm->item (event2.id ()); // The guid must be set so when it is exported to iCalendar, the relationship can be represented QVERIFY (!event2.guid().isEmpty ()); //Use default maxCount parameter value to fetch QList items4 = cm->itemOccurrences( event2, QDateTime (QDate (2010, 1, 1), QTime (0, 0, 0)), QDateTime (QDate (2010, 2, 1), QTime (0, 0, 0)) ); //The result should be same as rrule's limitation 20 QCOMPARE (items4.size (), 20); //Normal daily event items fetch with 5 max count limitation QList items5 = cm->itemOccurrences( event2, QDateTime (QDate (2010, 1, 1), QTime (0, 0, 0)), QDateTime (QDate (2010, 2, 1), QTime (0, 0, 0)), 5 ); //The result should be same as maxCount 5 which is smaller than event rrule's limitation 20 QCOMPARE (items5.size (), 5); //Normal daily event items fetch with 0 max count limitation QList items6 = cm->itemOccurrences( event2, QDateTime (QDate (2010, 1, 1), QTime (0, 0, 0)), QDateTime (QDate (2010, 2, 1), QTime (0, 0, 0)), 0 ); //The result should be 0 QCOMPARE (items6.size (), 0); } void tst_QOrganizerManager::addExceptionsWithGuid() { // It should be possible to save an exception that has at least an originalDate and either a // guid or a parentId. If guid and parentId are both specified, the client should // ensure they are consistent and the manager should fail if they are not. If only one of the // guid or parentId are specified, the manager should generate the other one. // This test case tests all of this. QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); // Set up some recurring items QOrganizerEvent christmas; christmas.setGuid("christmas"); christmas.setStartDateTime(QDateTime(QDate(2009, 12, 25), QTime(0, 0, 0))); christmas.setEndDateTime(QDateTime(QDate(2009, 12, 26), QTime(0, 0, 0))); christmas.setDisplayLabel(QStringLiteral("Christmas")); QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Yearly); christmas.setRecurrenceRule(rrule); QVERIFY(cm->saveItem(&christmas)); QVERIFY(!christmas.id().managerUri().isEmpty()); QVERIFY(!christmas.id().isNull()); QOrganizerEvent newYearsDay; newYearsDay.setGuid("newyear"); newYearsDay.setStartDateTime(QDateTime(QDate(2010, 1, 1), QTime(0, 0, 0))); newYearsDay.setEndDateTime(QDateTime(QDate(2010, 1, 2), QTime(0, 0, 0))); newYearsDay.setDisplayLabel(QStringLiteral("New Years Day")); newYearsDay.setRecurrenceRule(rrule); QVERIFY(cm->saveItem(&newYearsDay)); QOrganizerTodo report; report.setGuid("report"); report.setDueDateTime(QDateTime(QDate(2010, 1, 11), QTime(0, 0, 0))); report.setDisplayLabel(QStringLiteral("Report")); QVERIFY(cm->saveItem(&report)); // The tests: // exception with no guid or parentId fails QOrganizerEventOccurrence exception; exception.setOriginalDate(QDate(2010, 12, 25)); exception.setStartDateTime(QDateTime(QDate(2010, 12, 25), QTime(0, 0, 0))); exception.setEndDateTime(QDateTime(QDate(2010, 12, 26), QTime(0, 0, 0))); exception.setDisplayLabel(QStringLiteral("Xmas")); // if (cm->detailDefinitions(QOrganizerItemType::TypeEventOccurrence).contains(QOrganizerItemComment::DefinitionName)) // exception.addComment(QLatin1String("With the in-laws")); QVERIFY(!cm->saveItem(&exception)); QCOMPARE(cm->error(), QOrganizerManager::InvalidOccurrenceError); // exception with invalid guid fails exception.setId(QOrganizerItemId()); exception.setGuid(QStringLiteral("halloween")); QVERIFY(!cm->saveItem(&exception)); QCOMPARE(cm->error(), QOrganizerManager::InvalidOccurrenceError); // with the guid set, it should work exception.setId(QOrganizerItemId()); exception.setGuid(QStringLiteral("christmas")); QVERIFY(cm->saveItem(&exception)); QVERIFY(!exception.id().isNull()); QOrganizerEventOccurrence savedException = cm->item(exception.id()); QCOMPARE(savedException.parentId(), christmas.id()); // parentId should be set by manager // with the id, guid and the parentId all set and consistent, it should work exception = savedException; QVERIFY(cm->saveItem(&exception)); savedException = cm->item(exception.id()); QCOMPARE(savedException.parentId(), christmas.id()); // Make a fresh exception object on a fresh date to avoid clashing with the previously saved one // can't set parentId to a non-event QOrganizerEventOccurrence exception2; exception2.setOriginalDate(QDate(2011, 12, 25)); exception2.setStartDateTime(QDateTime(QDate(2011, 12, 25), QTime(0, 0, 0))); exception2.setEndDateTime(QDateTime(QDate(2011, 12, 26), QTime(0, 0, 0))); exception2.setDisplayLabel(QStringLiteral("XMas")); // if (cm->detailDefinitions(QOrganizerItemType::TypeEventOccurrence).contains(QOrganizerItemComment::DefinitionName)) // exception2.addComment(QLatin1String("With the in-laws")); exception2.setParentId(report.id()); // report is not an event QVERIFY(!cm->saveItem(&exception2)); QCOMPARE(cm->error(), QOrganizerManager::InvalidOccurrenceError); // can't set guid to a non-event exception2.setGuid(QStringLiteral("report")); exception2.setParentId(QOrganizerItemId()); QVERIFY(!cm->saveItem(&exception2)); QCOMPARE(cm->error(), QOrganizerManager::InvalidOccurrenceError); // can't make the guid inconsistent with the parentId exception2.setParentId(christmas.id()); exception2.setGuid(QStringLiteral("newyear")); QVERIFY(!cm->saveItem(&exception2)); QCOMPARE(cm->error(), QOrganizerManager::InvalidOccurrenceError); // with just the parentId set to a valid parent, it should work exception2.setGuid(QStringLiteral("")); QVERIFY(cm->saveItem(&exception2)); savedException = cm->item(exception2.id()); QCOMPARE(savedException.parentId(), christmas.id()); QCOMPARE(savedException.guid(), QStringLiteral("christmas")); // guid should be set by manager // Make a fresh exception object on a fresh date to avoid clashing with the previously saved one // exception without originalDate fails QOrganizerEventOccurrence exception3; exception3.setStartDateTime(QDateTime(QDate(2012, 12, 25), QTime(0, 0, 0))); exception3.setEndDateTime(QDateTime(QDate(2012, 12, 26), QTime(0, 0, 0))); exception3.setDisplayLabel(QStringLiteral("XMas")); // if (cm->detailDefinitions(QOrganizerItemType::TypeEventOccurrence).contains(QOrganizerItemComment::DefinitionName)) // exception3.addComment(QLatin1String("With the in-laws")); exception3.setParentId(christmas.id()); exception3.setGuid(QStringLiteral("christmas")); QVERIFY(!cm->saveItem(&exception3)); QCOMPARE(cm->error(), QOrganizerManager::InvalidOccurrenceError); // with original date, guid and parentId set and consistent, and id=0, it should work exception3.setOriginalDate(QDate(2012, 12, 25)); QVERIFY(cm->saveItem(&exception3)); QVERIFY(!exception3.id().isNull()); savedException = cm->item(exception3.id()); QCOMPARE(savedException.parentId(), christmas.id()); } void tst_QOrganizerManager::update() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); // Use note & todo item depending on backend support QOrganizerItemType::ItemType type; if (cm->supportedItemTypes().contains(QOrganizerItemType::TypeNote)) type = QOrganizerItemType::TypeNote; else if (cm->supportedItemTypes().contains(QOrganizerItemType::TypeTodo)) type = QOrganizerItemType::TypeTodo; else QSKIP("This manager does not support note or todo item"); // Save a new item first QOrganizerItem item; item.setType(type); item.setDisplayLabel("Yet another note"); item.setDescription("Surprisingly, this note is not a particularly notey note"); QVERIFY(cm->saveItem(&item)); QVERIFY(cm->error() == QOrganizerManager::NoError); // Update name QOrganizerItemDescription descr = item.detail(QOrganizerItemDetail::TypeDescription); descr.setDescription("This note is now slightly noteworthy"); item.saveDetail(&descr); QVERIFY(cm->saveItem(&item)); QVERIFY(cm->error() == QOrganizerManager::NoError); descr.setDescription("This is a very noteworthy note"); item.saveDetail(&descr); QVERIFY(cm->saveItem(&item)); QVERIFY(cm->error() == QOrganizerManager::NoError); QOrganizerItem updated = cm->item(item.id()); QOrganizerItemDescription updatedDescr = updated.detail(QOrganizerItemDetail::TypeDescription); QCOMPARE(updatedDescr, descr); /* Create a recurring event, update an occurrence and save (should persist as an exceptional occurrence) */ cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one QOrganizerEvent recEvent; recEvent.setDescription("a recurring event"); recEvent.setStartDateTime(QDateTime(QDate(2010, 10, 20), QTime(8, 0, 0))); recEvent.setEndDateTime(QDateTime(QDate(2010, 10, 20), QTime(10, 0, 0))); QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Daily); rrule.setLimit(3); recEvent.setRecurrenceRule(rrule); QVERIFY(cm->saveItem(&recEvent)); int persistentCount = cm->itemsForExport().size(); QCOMPARE(persistentCount, 1); // just the parent QList items = cm->items(); QCOMPARE(items.size(), 3); bool foundException = false; QOrganizerEventOccurrence exception; foreach (const QOrganizerEventOccurrence& curr, items) { if (curr.startDateTime() == QDateTime(QDate(2010, 10, 21), QTime(8, 0, 0))) { exception = curr; foundException = true; break; } } QVERIFY(foundException); exception.setDescription("different description"); QVERIFY(cm->saveItem(&exception)); persistentCount = cm->itemsForExport().size(); QCOMPARE(persistentCount, 2); // parent plus one exception items = cm->items(); QCOMPARE(items.size(), 3); foreach (const QOrganizerEventOccurrence& curr, items) { if (curr.startDateTime() == QDateTime(QDate(2010, 10, 21), QTime(8, 0, 0))) { QVERIFY(!curr.id().isNull()); QCOMPARE(curr.description(), QStringLiteral("different description")); } else { QVERIFY(curr.id().isNull()); } } /* Save a non-updated occurrence - should still persist as an exceptional occurrence */ QOrganizerEventOccurrence secondException; foreach (const QOrganizerEventOccurrence& curr, items) { if (curr.startDateTime() == QDateTime(QDate(2010, 10, 22), QTime(8, 0, 0))) { secondException = curr; foundException = true; break; } } QVERIFY(foundException); QVERIFY(cm->saveItem(&secondException)); // no changes, but should save as an exception anyway. persistentCount = cm->itemsForExport().size(); QCOMPARE(persistentCount, 3); // parent plus two exceptions items = cm->items(); QCOMPARE(items.size(), 3); foreach (const QOrganizerEventOccurrence& curr, items) { if (curr.startDateTime() == QDateTime(QDate(2010, 10, 20), QTime(8, 0, 0))) { QVERIFY(curr.id().isNull()); // only the first occurrence is not an exception. } else { QVERIFY(!curr.id().isNull()); // we have two exceptions this time } } } void tst_QOrganizerManager::remove() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); // for fetch results QList items; // for removeItems request QList itemList; // Use note & todo item depending on backend support QOrganizerItemType::ItemType type; if (cm->supportedItemTypes().contains(QOrganizerItemType::TypeNote)) type = QOrganizerItemType::TypeNote; else if (cm->supportedItemTypes().contains(QOrganizerItemType::TypeTodo)) type = QOrganizerItemType::TypeTodo; else QSKIP("This manager does not support note or todo item"); // Save a new item first QOrganizerItem item; item.setType(type); item.setDisplayLabel("Not another note"); item.setDescription("Yes, another note!"); QVERIFY(cm->saveItem(&item)); QVERIFY(cm->error() == QOrganizerManager::NoError); QVERIFY(item.id() != QOrganizerItemId()); // Remove the created item const int itemCount = cm->itemIds().count(); QVERIFY(cm->removeItem(item.id())); QCOMPARE(cm->itemIds().count(), itemCount - 1); QVERIFY(cm->item(item.id()).isEmpty()); QCOMPARE(cm->error(), QOrganizerManager::DoesNotExistError); //delete item not exist QVERIFY(!cm->removeItem(item.id())); QCOMPARE(cm->error(), QOrganizerManager::DoesNotExistError); //delete item id is empty QOrganizerItemId id; QVERIFY(!cm->removeItem(id)); QCOMPARE(cm->error(), QOrganizerManager::DoesNotExistError); // delete an event by item (not id) cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one QCOMPARE(cm->items().size(), 0); QOrganizerEvent event1; event1.setDisplayLabel("first event"); QOrganizerEvent event2; event1.setDisplayLabel("second event"); QOrganizerEvent event3; event1.setDisplayLabel("third event"); itemList << event1 << event2 << event3; QVERIFY(cm->saveItems(&itemList)); QVERIFY(cm->error() == QOrganizerManager::NoError); items = cm->items(); QCOMPARE(cm->items().size(), 3); QVERIFY(cm->removeItem(&(items[2]))); items = cm->items(); QCOMPARE(cm->items().size(), 2); QVERIFY(cm->removeItems(&items)); items = cm->items(); QCOMPARE(cm->items().size(), 0); /* Create a recurring event, save an exception, remove the recurring event should remove all children occurrences incl. persisted exceptions. */ cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one QOrganizerEvent recEvent; recEvent.setDescription("a recurring event"); recEvent.setStartDateTime(QDateTime(QDate(2010, 10, 20), QTime(8, 0, 0))); recEvent.setEndDateTime(QDateTime(QDate(2010, 10, 20), QTime(10, 0, 0))); QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Daily); rrule.setLimit(3); recEvent.setRecurrenceRule(rrule); QVERIFY(cm->saveItem(&recEvent)); items = cm->items(); QCOMPARE(items.size(), 3); QOrganizerEventOccurrence exception = items.at(1); exception.setStartDateTime(QDateTime(QDate(2010, 10, 21), QTime(7, 0, 0))); QVERIFY(cm->saveItem(&exception)); items = cm->items(); QCOMPARE(items.size(), 3); QCOMPARE(cm->itemsForExport().size(), 2); QVERIFY(cm->removeItem(recEvent.id())); QCOMPARE(cm->itemsForExport().size(), 0); QCOMPARE(cm->items().size(), 0); /* Create a recurring event, save an exception, remove the saved exception should remove the persisted exception, but the exdate should remain in the parent */ cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one recEvent.setId(QOrganizerItemId()); recEvent.setDescription("a recurring event"); recEvent.setStartDateTime(QDateTime(QDate(2010, 10, 20), QTime(8, 0, 0))); recEvent.setEndDateTime(QDateTime(QDate(2010, 10, 20), QTime(10, 0, 0))); rrule.setFrequency(QOrganizerRecurrenceRule::Daily); rrule.setLimit(3); recEvent.setRecurrenceRule(rrule); QVERIFY(cm->saveItem(&recEvent)); items = cm->items(); QCOMPARE(items.size(), 3); exception = items.at(1); exception.setStartDateTime(QDateTime(QDate(2010, 10, 21), QTime(7, 0, 0))); QVERIFY(cm->saveItem(&exception)); items = cm->items(); QCOMPARE(items.size(), 3); QCOMPARE(cm->itemsForExport().size(), 2); QVERIFY(cm->removeItem(exception.id())); QCOMPARE(cm->itemsForExport().size(), 1); // only parent remains as persistent items = cm->items(); QCOMPARE(items.size(), 2); // the exception date remains in parent, so only 2 occurrences are generated. // Create a recurring event, remove a generated occurrence should add an exdate in the parent QOrganizerItem itemOccurrence = items[0]; cm->removeItem(&itemOccurrence); items = cm->items(); QCOMPARE(items.size(), 1); // Remove both parent item and generated occurrence in the same request // --> request should fail because itemList.clear(); itemList << recEvent << items[0]; QVERIFY(cm->removeItems(&itemList)); items = cm->items(); QCOMPARE(cm->items().size(), 0); } void tst_QOrganizerManager::batch() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); /* First test null pointer operations */ QVERIFY(!cm->saveItems(NULL)); QVERIFY(cm->error() == QOrganizerManager::BadArgumentError); QVERIFY(!cm->removeItems(QList())); QVERIFY(cm->error() == QOrganizerManager::BadArgumentError); // Use note & todo item depending on backend support QOrganizerItemType::ItemType type; if (cm->supportedItemTypes().contains(QOrganizerItemType::TypeNote)) type = QOrganizerItemType::TypeNote; else if (cm->supportedItemTypes().contains(QOrganizerItemType::TypeTodo)) type = QOrganizerItemType::TypeTodo; else QSKIP("This manager does not support note or todo item"); QOrganizerItem a; QOrganizerItem b; QOrganizerItem c; a.setType(type); b.setType(type); c.setType(type); /* Now add 3 items, all valid */ QOrganizerItemDisplayLabel da; da.setValue(QOrganizerItemDisplayLabel::FieldLabel, "XXXXXX A Note"); a.saveDetail(&da); QOrganizerItemDisplayLabel db; db.setValue(QOrganizerItemDisplayLabel::FieldLabel, "XXXXXX B Note"); b.saveDetail(&db); QOrganizerItemDisplayLabel dc; dc.setValue(QOrganizerItemDisplayLabel::FieldLabel, "XXXXXX C Note"); c.saveDetail(&dc); QList items; items << a << b << c; QMap errorMap; // Add one dummy error to test if the errors are reset errorMap.insert(0, QOrganizerManager::NoError); QVERIFY(cm->saveItems(&items)); QVERIFY(cm->error() == QOrganizerManager::NoError); errorMap = cm->errorMap(); QVERIFY(errorMap.count() == 0); /* Make sure our items got updated too */ QVERIFY(items.count() == 3); QVERIFY(items.at(0).id() != QOrganizerItemId()); QVERIFY(items.at(1).id() != QOrganizerItemId()); QVERIFY(items.at(2).id() != QOrganizerItemId()); QVERIFY(items.at(0).detail(QOrganizerItemDetail::TypeDisplayLabel) == da); QVERIFY(items.at(1).detail(QOrganizerItemDetail::TypeDisplayLabel) == db); QVERIFY(items.at(2).detail(QOrganizerItemDetail::TypeDisplayLabel) == dc); /* Retrieve again */ a = cm->item(items.at(0).id()); b = cm->item(items.at(1).id()); c = cm->item(items.at(2).id()); QVERIFY(items.at(0).detail(QOrganizerItemDetail::TypeDisplayLabel) == da); QVERIFY(items.at(1).detail(QOrganizerItemDetail::TypeDisplayLabel) == db); QVERIFY(items.at(2).detail(QOrganizerItemDetail::TypeDisplayLabel) == dc); /* Now make an update to them all */ QOrganizerItemDescription descr; descr.setDescription("This note looks slightly shifty"); QVERIFY(items[0].saveDetail(&descr)); descr.setDescription("This note is definitely up to no good"); QVERIFY(items[1].saveDetail(&descr)); descr.setDescription("This note is a terrible note"); QVERIFY(items[2].saveDetail(&descr)); QVERIFY(cm->saveItems(&items)); QVERIFY(cm->error() == QOrganizerManager::NoError); errorMap = cm->errorMap(); QVERIFY(errorMap.count() == 0); /* Retrieve them and check them again */ a = cm->item(items.at(0).id()); b = cm->item(items.at(1).id()); c = cm->item(items.at(2).id()); QVERIFY(items.at(0).detail(QOrganizerItemDetail::TypeDisplayLabel) == da); QVERIFY(items.at(1).detail(QOrganizerItemDetail::TypeDisplayLabel) == db); QVERIFY(items.at(2).detail(QOrganizerItemDetail::TypeDisplayLabel) == dc); QVERIFY(a.details(QOrganizerItemDetail::TypeDescription).count() == 1); QVERIFY(b.details(QOrganizerItemDetail::TypeDescription).count() == 1); QVERIFY(c.details(QOrganizerItemDetail::TypeDescription).count() == 1); QVERIFY(a.details(QOrganizerItemDetail::TypeDescription).at(0).value(QOrganizerItemDescription::FieldDescription) == "This note looks slightly shifty"); QVERIFY(b.details(QOrganizerItemDetail::TypeDescription).at(0).value(QOrganizerItemDescription::FieldDescription) == "This note is definitely up to no good"); QVERIFY(c.details(QOrganizerItemDetail::TypeDescription).at(0).value(QOrganizerItemDescription::FieldDescription) == "This note is a terrible note"); /* Now delete them all */ QList ids; ids << a.id() << b.id() << c.id(); QVERIFY(cm->removeItems(ids)); errorMap = cm->errorMap(); QVERIFY(errorMap.count() == 0); QVERIFY(cm->error() == QOrganizerManager::NoError); /* Make sure the items really don't exist any more */ QVERIFY(cm->item(a.id()).id() == QOrganizerItemId()); QVERIFY(cm->item(a.id()).isEmpty()); QVERIFY(cm->error() == QOrganizerManager::DoesNotExistError); QVERIFY(cm->item(b.id()).id() == QOrganizerItemId()); QVERIFY(cm->item(b.id()).isEmpty()); QVERIFY(cm->error() == QOrganizerManager::DoesNotExistError); QVERIFY(cm->item(c.id()).id() == QOrganizerItemId()); QVERIFY(cm->item(c.id()).isEmpty()); QVERIFY(cm->error() == QOrganizerManager::DoesNotExistError); /* Now try removing with all invalid ids (e.g. the ones we just removed) */ ids.clear(); ids << a.id() << b.id() << c.id(); QVERIFY(!cm->removeItems(ids)); QVERIFY(cm->error() == QOrganizerManager::DoesNotExistError); errorMap = cm->errorMap(); QVERIFY(errorMap.count() == 3); QVERIFY(errorMap.values().at(0) == QOrganizerManager::DoesNotExistError); QVERIFY(errorMap.values().at(1) == QOrganizerManager::DoesNotExistError); QVERIFY(errorMap.values().at(2) == QOrganizerManager::DoesNotExistError); /* Try adding some new ones again, this time one with an error */ items.clear(); a.setId(QOrganizerItemId()); b.setId(QOrganizerItemId()); c.setId(QOrganizerItemId()); items << a << b << c; // /* Make B the bad guy */ // QOrganizerItemDetail bad("does not exist and will break if you add it"); // bad.setValue("This is also bad", "Very bad"); // b.saveDetail(&bad); // QVERIFY(!cm->saveItems(&items)); // since we don't setCollectionId() in any of the items, they go in default collection. // /* We can't really say what the error will be.. maybe bad argument, maybe invalid detail */ // QVERIFY(cm->error() != QOrganizerManager::NoError); // /* It's permissible to fail all the adds, or to add the successful ones */ // errorMap = cm->errorMap(); // QVERIFY(errorMap.count() > 0); // QVERIFY(errorMap.count() <= 3); // // A might have gone through // if (errorMap.keys().contains(0)) { // QVERIFY(errorMap.value(0) != QOrganizerManager::NoError); // QVERIFY(items.at(0).id() == QOrganizerItemId()); // } else { // QVERIFY(items.at(0).id() != QOrganizerItemId()); // } // // B should have failed // QVERIFY(errorMap.value(1) == QOrganizerManager::InvalidDetailError); // QVERIFY(items.at(1).id() == QOrganizerItemId()); // // C might have gone through // if (errorMap.keys().contains(2)) { // QVERIFY(errorMap.value(2) != QOrganizerManager::NoError); // QVERIFY(items.at(2).id() == QOrganizerItemId()); // } else { // QVERIFY(items.at(2).id() != QOrganizerItemId()); // } // /* Fix up B and re save it */ // QVERIFY(items[1].removeDetail(&bad)); // QVERIFY(cm->saveItems(&items)); // errorMap = cm->errorMap(); // QVERIFY(errorMap.count() == 0); // QVERIFY(cm->error() == QOrganizerManager::NoError); // Save and remove a fourth item. Store the id. QOrganizerItem d; d.setType(type); d.setDisplayLabel("XXXXXX D Note"); QVERIFY(cm->saveItem(&d)); QOrganizerItemId removedId = d.id(); QVERIFY(cm->removeItem(removedId)); /* Now delete 3 items, but with one bad argument */ ids.clear(); ids << items.at(0).id(); ids << removedId; ids << items.at(2).id(); QVERIFY(!cm->removeItems(ids)); QVERIFY(cm->error() != QOrganizerManager::NoError); /* Again, the backend has the choice of either removing the successful ones, or not */ errorMap = cm->errorMap(); QVERIFY(errorMap.count() > 0); QVERIFY(errorMap.count() <= 3); // A might have gone through if (errorMap.keys().contains(0)) { QVERIFY(errorMap.value(0) != QOrganizerManager::NoError); QVERIFY(items.at(0).id() == QOrganizerItemId()); } else { QVERIFY(items.at(0).id() != QOrganizerItemId()); } /* B should definitely have failed */ QVERIFY(errorMap.value(1) == QOrganizerManager::DoesNotExistError); QVERIFY(ids.at(1) == removedId); // A might have gone through if (errorMap.keys().contains(2)) { QVERIFY(errorMap.value(2) != QOrganizerManager::NoError); QVERIFY(items.at(2).id() == QOrganizerItemId()); } else { QVERIFY(items.at(2).id() != QOrganizerItemId()); } } void tst_QOrganizerManager::invalidManager() { /* Create an invalid manager */ QOrganizerManager manager("this should never work"); QVERIFY(manager.managerName() == "invalid"); /* Now test that all the operations fail */ QVERIFY(manager.itemIds().count() == 0); QVERIFY(manager.error() == QOrganizerManager::NotSupportedError); QOrganizerItem foo; QOrganizerItemDisplayLabel dl; dl.setLabel("some label"); foo.saveDetail(&dl); QVERIFY(manager.saveItem(&foo) == false); QVERIFY(manager.error() == QOrganizerManager::NotSupportedError); QVERIFY(foo.id() == QOrganizerItemId()); QVERIFY(manager.itemIds().count() == 0); QVERIFY(manager.item(foo.id()).id() == QOrganizerItemId()); QVERIFY(manager.item(foo.id()).isEmpty()); QVERIFY(manager.error() == QOrganizerManager::NotSupportedError); QVERIFY(manager.removeItem(foo.id()) == false); QVERIFY(manager.error() == QOrganizerManager::NotSupportedError); QMap errorMap; errorMap.insert(0, QOrganizerManager::NoError); QVERIFY(!manager.saveItems(0)); errorMap = manager.errorMap(); QVERIFY(errorMap.count() == 0); QVERIFY(manager.error() == QOrganizerManager::BadArgumentError); /* filters */ QOrganizerItemFilter f; // matches everything QOrganizerItemDetailFieldFilter dff; dff.setDetail(QOrganizerItemDetail::TypeDisplayLabel, QOrganizerItemDisplayLabel::FieldLabel); QVERIFY(manager.itemIds(QDateTime(), QDateTime(), QOrganizerItemFilter()).count() == 0); QVERIFY(manager.error() == QOrganizerManager::NotSupportedError); QVERIFY(manager.itemIds(QDateTime(), QDateTime(), dff).count() == 0); QVERIFY(manager.error() == QOrganizerManager::NotSupportedError); QVERIFY(manager.itemIds(QDateTime(), QDateTime(), f | f).count() == 0); QVERIFY(manager.error() == QOrganizerManager::NotSupportedError); QVERIFY(manager.itemIds(QDateTime(), QDateTime(), dff | dff).count() == 0); QVERIFY(manager.error() == QOrganizerManager::NotSupportedError); QVERIFY(manager.supportedFilters().size() == 0); QList list; list << foo; QVERIFY(!manager.saveItems(&list)); errorMap = manager.errorMap(); QVERIFY(errorMap.count() == 0); QVERIFY(manager.error() == QOrganizerManager::NotSupportedError); QVERIFY(!manager.removeItems(QList())); errorMap = manager.errorMap(); QVERIFY(errorMap.count() == 0); QVERIFY(manager.error() == QOrganizerManager::BadArgumentError); QList idlist; idlist << foo.id(); QVERIFY(!manager.removeItems(idlist)); errorMap = manager.errorMap(); QVERIFY(errorMap.count() == 0); QVERIFY(manager.error() == QOrganizerManager::NotSupportedError); /* Collections */ QOrganizerCollection testCollection; testCollection.setExtendedMetaData("test", "example"); QVERIFY(!manager.saveCollection(&testCollection)); QVERIFY(manager.error() == QOrganizerManager::NotSupportedError || manager.error() == QOrganizerManager::InvalidCollectionError); QVERIFY(!manager.removeCollection(testCollection.id())); QVERIFY(manager.error() == QOrganizerManager::NotSupportedError || manager.error() == QOrganizerManager::DoesNotExistError); QVERIFY(manager.defaultCollection() == QOrganizerCollection()); QVERIFY(manager.error() == QOrganizerManager::NotSupportedError); QVERIFY(manager.collections().isEmpty()); QVERIFY(manager.error() == QOrganizerManager::NotSupportedError); /* Requests */ QOrganizerItemFetchRequest ifr; QOrganizerItemFetchForExportRequest ifer; QOrganizerItemSaveRequest isr; QOrganizerItemRemoveRequest irr; QOrganizerItemRemoveByIdRequest irbir; QOrganizerCollectionFetchRequest cfr; QOrganizerCollectionSaveRequest csr; QOrganizerCollectionRemoveRequest crr; ifr.setManager(&manager); QVERIFY(!ifr.start()); QVERIFY(!ifr.cancel()); ifr.waitForFinished(); //QVERIFY(ifr.error() == QOrganizerManager::NotSupportedError); // XXX TODO: if start fails, should be not supported error... ifer.setManager(&manager); QVERIFY(!ifer.start()); QVERIFY(!ifer.cancel()); ifer.waitForFinished(); //QVERIFY(ifer.error() == QOrganizerManager::NotSupportedError); // XXX TODO: if start fails, should be not supported error... isr.setManager(&manager); isr.setItem(foo); QVERIFY(!isr.start()); QVERIFY(!isr.cancel()); isr.waitForFinished(); //QVERIFY(isr.error() == QOrganizerManager::NotSupportedError); // XXX TODO: if start fails, should be not supported error... irr.setManager(&manager); irr.setItem(foo); QVERIFY(!irr.start()); QVERIFY(!irr.cancel()); irr.waitForFinished(); //QVERIFY(irr.error() == QOrganizerManager::NotSupportedError); // XXX TODO: if start fails, should be not supported error... irbir.setManager(&manager); irbir.setItemId(foo.id()); qDebug() << foo.id(); QVERIFY(!irbir.start()); QVERIFY(!irbir.cancel()); irbir.waitForFinished(); //QVERIFY(irr.error() == QOrganizerManager::NotSupportedError); // XXX TODO: if start fails, should be not supported error... cfr.setManager(&manager); QVERIFY(!cfr.start()); QVERIFY(!cfr.cancel()); cfr.waitForFinished(); //QVERIFY(cfr.error() == QOrganizerManager::NotSupportedError); // XXX TODO: if start fails, should be not supported error... csr.setManager(&manager); csr.setCollection(testCollection); QVERIFY(!csr.start()); QVERIFY(!csr.cancel()); csr.waitForFinished(); //QVERIFY(csr.error() == QOrganizerManager::NotSupportedError); // XXX TODO: if start fails, should be not supported error... crr.setManager(&manager); crr.setCollectionId(testCollection.id()); QVERIFY(!crr.start()); QVERIFY(!crr.cancel()); crr.waitForFinished(); //QVERIFY(crr.error() == QOrganizerManager::NotSupportedError); // XXX TODO: if start fails, should be not supported error... } void tst_QOrganizerManager::memoryManager() { QMap params; QOrganizerManager m1("memory"); params.insert("random", "shouldNotBeUsed"); QOrganizerManager m2("memory", params); params.insert("id", "shouldBeUsed"); QOrganizerManager m3("memory", params); QOrganizerManager m4("memory", params); params.insert("id", QString("")); QOrganizerManager m5("memory", params); // add a item to each of m1, m2, m3 QOrganizerEvent c; QOrganizerItemDisplayLabel c1dl; c1dl.setLabel("c1dl"); c.saveDetail(&c1dl); QVERIFY(m1.saveItem(&c)); c.setId(QOrganizerItemId()); QOrganizerItem c2; QOrganizerItemDisplayLabel c2dl = c2.detail(QOrganizerItemDetail::TypeDisplayLabel); c2 = c; c2dl.setLabel("c2dl"); c2.saveDetail(&c2dl); c2.setCollectionId(QOrganizerCollectionId()); c.setCollectionId(QOrganizerCollectionId()); QVERIFY(m2.saveItem(&c2)); // save c2 first; c will be given a higher id QVERIFY(m2.saveItem(&c)); // save c to m2 c.setId(QOrganizerItemId()); c1dl.setLabel("c3dl"); c.saveDetail(&c1dl); c.setCollectionId(QOrganizerCollectionId()); QVERIFY(m3.saveItem(&c)); /* test that m1 != m2 != m3 and that m3 == m4 */ // check the counts are correct - especially note m4 and m3. QCOMPARE(m1.itemIds().count(), 1); QCOMPARE(m2.itemIds().count(), 2); QCOMPARE(m3.itemIds().count(), 1); QCOMPARE(m4.itemIds().count(), 1); QCOMPARE(m5.itemIds().count(), 0); // remove c2 from m2 - ensure that this doesn't affect any other manager. m2.removeItem(c2.id()); QCOMPARE(m1.itemIds().count(), 1); QCOMPARE(m2.itemIds().count(), 1); QCOMPARE(m3.itemIds().count(), 1); QCOMPARE(m4.itemIds().count(), 1); QCOMPARE(m5.itemIds().count(), 0); // check that the items contained within are different. // note that in the m1->m2 case, only the id will be different! QVERIFY(m1.item(m1.itemIds().at(0)) != m2.item(m2.itemIds().at(0))); QVERIFY(m1.item(m1.itemIds().at(0)) != m3.item(m3.itemIds().at(0))); QVERIFY(m2.item(m2.itemIds().at(0)) != m3.item(m3.itemIds().at(0))); QVERIFY(m3.item(m3.itemIds().at(0)) == m4.item(m4.itemIds().at(0))); // now, we should be able to remove from m4, and have m3 empty QVERIFY(m4.removeItem(c.id())); QCOMPARE(m3.itemIds().count(), 0); QCOMPARE(m4.itemIds().count(), 0); QCOMPARE(m5.itemIds().count(), 0); } void tst_QOrganizerManager::recurrenceWithGenerator_data() { QTest::addColumn("uri"); QTest::addColumn("generatorDate"); QTest::addColumn("recurrenceRule"); QTest::addColumn("exceptionRule"); QTest::addColumn >("recurrenceDates"); QTest::addColumn >("exceptionDates"); QTest::addColumn("startDate"); QTest::addColumn("endDate"); QTest::addColumn >("occurrenceDates"); QStringList managers = QOrganizerManager::availableManagers(); /* Known one that will not pass */ managers.removeAll("invalid"); managers.removeAll("testdummy"); managers.removeAll("teststaticdummy"); managers.removeAll("maliciousplugin"); managers.removeAll("skeleton"); QOrganizerRecurrenceRule exrule; QSet rdates; QSet exdates; foreach(QString mgr, managers) { QString managerUri = QOrganizerManager::buildUri(mgr, QMap()); { QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Weekly); rrule.setLimit(QDate(2010, 1, 22)); QTest::newRow(QString("mgr=%1, weekly recurrence").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 1) << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2010, 1, 20) // stops at the 15th because the query end date is the 20th << (QList() << QDate(2010, 1, 1) << QDate(2010, 1, 8) << QDate(2010, 1, 15)); // change the end date of the query to 2010-02-01 QTest::newRow(QString("mgr=%1, weekly recurrence, end date is inclusive").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 1) << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2010, 2, 1) << (QList() << QDate(2010, 1, 1) << QDate(2010, 1, 8) << QDate(2010, 1, 15) << QDate(2010, 1, 22)); // Now let's fiddle with the recurrence end date and see what happens rrule.setLimit(QDate(2010, 1, 23)); QTest::newRow(QString("mgr=%1, weekly recurrence, end date observed (+1)").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 1) << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2010, 2, 1) // now stop on the 22nd << (QList() << QDate(2010, 1, 1) << QDate(2010, 1, 8) << QDate(2010, 1, 15) << QDate(2010, 1, 22)); rrule.setLimit(QDate(2010, 1, 21)); QTest::newRow(QString("mgr=%1, weekly recurrence, end date observed (-1)").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 1) << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2010, 2, 1) << (QList() << QDate(2010, 1, 1) << QDate(2010, 1, 8) << QDate(2010, 1, 15)); rrule.setLimit(QDate()); rrule.setLimit(2); QTest::newRow(QString("mgr=%1, weekly recurrence, count").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 1) << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2010, 2, 1) << (QList() << QDate(2010, 1, 1) << QDate(2010, 1, 8)); } { QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Daily); rrule.setLimit(QDate(2010, 1, 5)); QTest::newRow(QString("mgr=%1, daily").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 1) << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2015, 1, 1) << (QList() << QDate(2010, 1, 1) << QDate(2010, 1, 2) << QDate(2010, 1, 3) << QDate(2010, 1, 4) << QDate(2010, 1, 5)); rrule.setInterval(3); QTest::newRow(QString("mgr=%1, daily, interval").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 1) << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2015, 1, 1) << (QList() << QDate(2010, 1, 1) << QDate(2010, 1, 4)); } { QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Weekly); rrule.setFirstDayOfWeek(Qt::Monday); rrule.setDaysOfWeek(QSet() << Qt::Friday << Qt::Saturday << Qt::Sunday); rrule.setLimit(QDate(2010, 1, 27)); QTest::newRow(QString("mgr=%1, weekly, days of week").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 1) << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2015, 1, 1) << (QList() << QDate(2010, 1, 1) << QDate(2010, 1, 2) << QDate(2010, 1, 3) << QDate(2010, 1, 8) << QDate(2010, 1, 9) << QDate(2010, 1, 10) << QDate(2010, 1, 15) << QDate(2010, 1, 16) << QDate(2010, 1, 17) << QDate(2010, 1, 22) << QDate(2010, 1, 23) << QDate(2010, 1, 24)); rrule.setInterval(3); QTest::newRow(QString("mgr=%1, weekly, days of week, interval").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 1) << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2015, 1, 1) << (QList() << QDate(2010, 1, 1) << QDate(2010, 1, 2) << QDate(2010, 1, 3) << QDate(2010, 1, 22) << QDate(2010, 1, 23) << QDate(2010, 1, 24)); } { QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Monthly); rrule.setDaysOfMonth(QSet() << 1 << 10); rrule.setLimit(QDate(2010, 4, 15)); QTest::newRow(QString("mgr=%1, monthly recurrence").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 1) << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2015, 1, 1) << (QList() << QDate(2010, 1, 1) << QDate(2010, 1, 10) << QDate(2010, 2, 1) << QDate(2010, 2, 10) << QDate(2010, 3, 1) << QDate(2010, 3, 10) << QDate(2010, 4, 1) << QDate(2010, 4, 10)); rrule.setInterval(3); QTest::newRow(QString("mgr=%1, monthly recurrence, interval").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 1) << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2015, 1, 1) << (QList() << QDate(2010, 1, 1) << QDate(2010, 1, 10) << QDate(2010, 4, 1) << QDate(2010, 4, 10)); } { QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Yearly); rrule.setDaysOfYear(QSet() << 1 << 32); rrule.setLimit(QDate(2012, 3, 15)); QTest::newRow(QString("mgr=%1, yearly recurrence").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 1) << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2015, 1, 1) << (QList() << QDate(2010, 1, 1) << QDate(2010, 2, 1) << QDate(2011, 1, 1) << QDate(2011, 2, 1) << QDate(2012, 1, 1) << QDate(2012, 2, 1)); rrule.setLimit(QDate(2013, 3, 15)); rrule.setInterval(3); QTest::newRow(QString("mgr=%1, yearly recurrence, interval").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 1) << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2015, 1, 1) << (QList() << QDate(2010, 1, 1) << QDate(2010, 2, 1) << QDate(2013, 1, 1) << QDate(2013, 2, 1)); } { QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Yearly); rrule.setMonthsOfYear(QSet() << QOrganizerRecurrenceRule::January << QOrganizerRecurrenceRule::March); rrule.setLimit(QDate(2011, 3, 15)); QTest::newRow(QString("mgr=%1, yearly recurrence, by month").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 1) << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2015, 1, 1) << (QList() << QDate(2010, 1, 1) << QDate(2010, 3, 1) << QDate(2011, 1, 1) << QDate(2011, 3, 1)); // The day-of-month should be inferred from the day-of-month of the original event } { QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Yearly); rrule.setWeeksOfYear(QSet() << 1 << 4); rrule.setDaysOfWeek(QSet() << Qt::Thursday); rrule.setLimit(QDate(2011, 3, 15)); QTest::newRow(QString("mgr=%1, yearly recurrence, by week").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 7) // this is the first day of week 1 << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2015, 1, 1) << (QList() << QDate(2010, 1, 7) << QDate(2010, 1, 28) << QDate(2011, 1, 6) << QDate(2011, 1, 27)); } { QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Yearly); rrule.setMonthsOfYear(QSet() << QOrganizerRecurrenceRule::April); rrule.setDaysOfWeek(QSet() << Qt::Sunday); rrule.setPositions(QSet() << 1); QTest::newRow(QString("mgr=%1, yearly recurrence, first Sunday of April").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 4, 4) // this is the first Sunday of April 2010 << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2015, 1, 1) << (QList() << QDate(2010, 4, 4) << QDate(2011, 4, 3) << QDate(2012, 4, 1) << QDate(2013, 4, 7) << QDate(2014, 4, 6)); } { QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Yearly); rrule.setMonthsOfYear(QSet() << QOrganizerRecurrenceRule::October); rrule.setDaysOfWeek(QSet() << Qt::Sunday); rrule.setPositions(QSet() << -1); QTest::newRow(QString("mgr=%1, yearly recurrence, last Sunday of October").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 10, 31) // this is the last Sunday of October 2010 << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2015, 1, 1) << (QList() << QDate(2010, 10, 31) << QDate(2011, 10, 30) << QDate(2012, 10, 28) << QDate(2013, 10, 27) << QDate(2014, 10, 26)); } { QOrganizerRecurrenceRule rrule; // empty QSet rdates; rdates << QDate(2010, 1, 5) << QDate(2010, 1, 8); QTest::newRow(QString("mgr=%1, rdates").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 1) << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2015, 1, 1) << (QList() << QDate(2010, 1, 1) << QDate(2010, 1, 5) << QDate(2010, 1, 8)); } { QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Daily); QSet exdates; exdates << QDate(2010, 1, 2) << QDate(2010, 1, 3); QTest::newRow(QString("mgr=%1, exdates").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 1) << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2010, 1, 5) << (QList() << QDate(2010, 1, 1) << QDate(2010, 1, 4) << QDate(2010, 1, 5)); } { QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Daily); QOrganizerRecurrenceRule exrule; exrule.setFrequency(QOrganizerRecurrenceRule::Monthly); exrule.setDaysOfMonth(QSet() << 2 << 3); QTest::newRow(QString("mgr=%1, exrule").arg(mgr).toLatin1().constData()) << managerUri << QDate(2010, 1, 1) << rrule << exrule << rdates << exdates << QDate(2010, 1, 1) << QDate(2010, 1, 5) << (QList() << QDate(2010, 1, 1) << QDate(2010, 1, 4) << QDate(2010, 1, 5)); } { QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Weekly); rrule.setLimit(8); rrule.setDaysOfWeek(QSet() << Qt::Monday << Qt::Tuesday << Qt::Wednesday); QOrganizerRecurrenceRule exrule; QTest::newRow(QString("mgr=%1, weekly, count, days of week").arg(mgr).toLatin1().constData()) << managerUri << QDate(2014, 1, 22) << rrule << exrule << rdates << exdates << QDate(2014, 1, 1) << QDate(2014, 5, 1) << (QList() << QDate(2014, 1, 22) << QDate(2014, 1, 27) << QDate(2014, 1, 28) << QDate(2014, 1, 29) << QDate(2014, 2, 3) << QDate(2014, 2, 4) << QDate(2014, 2, 5) << QDate(2014, 2, 10)); } } } void tst_QOrganizerManager::recurrenceWithGenerator() { QFETCH(QString, uri); QFETCH(QDate, generatorDate); QFETCH(QOrganizerRecurrenceRule, recurrenceRule); QFETCH(QOrganizerRecurrenceRule, exceptionRule); QFETCH(QSet, recurrenceDates); QFETCH(QSet, exceptionDates); QFETCH(QDate, startDate); QFETCH(QDate, endDate); QFETCH(QList, occurrenceDates); QScopedPointer cm(QOrganizerManager::fromUri(uri)); QOrganizerEvent event; event.setDisplayLabel("event"); event.setStartDateTime(QDateTime(generatorDate, QTime(11, 0, 0))); event.setEndDateTime(QDateTime(generatorDate, QTime(11, 30, 0))); event.setRecurrenceRule(recurrenceRule); event.setExceptionRule(exceptionRule); event.setRecurrenceDates(recurrenceDates); event.setExceptionDates(exceptionDates); if (cm->saveItem(&event)) { QList items = cm->itemOccurrences(event, QDateTime(startDate, QTime(0, 0, 0)), QDateTime(endDate, QTime(23, 59, 59, 999))); QList actualDates; for (int i = 0; i < items.size(); i++) { QOrganizerItem item = items.at(i); QCOMPARE(item.type(), QOrganizerItemType::TypeEventOccurrence); QDate occurrenceDate = item.detail(QOrganizerItemDetail::TypeEventTime).value(QOrganizerEventTime::FieldStartDateTime).toDateTime().date(); //QCOMPARE(occurrenceDate, occurrenceDates.at(i)); actualDates << occurrenceDate; } if (actualDates != occurrenceDates) { qDebug() << "Actual: " << actualDates; qDebug() << "Expected: " << occurrenceDates; QCOMPARE(actualDates, occurrenceDates); } } else { // Allow backend specific limitations QCOMPARE(cm->error(), QOrganizerManager::NotSupportedError); qWarning() << "The event not supported by the backend"; } } void tst_QOrganizerManager::todoRecurrenceWithGenerator_data() { recurrenceWithGenerator_data(); } // This is just a copy of recurrenceWithGenerator, but for todos, not events void tst_QOrganizerManager::todoRecurrenceWithGenerator() { QFETCH(QString, uri); QFETCH(QDate, generatorDate); QFETCH(QOrganizerRecurrenceRule, recurrenceRule); QFETCH(QOrganizerRecurrenceRule, exceptionRule); QFETCH(QSet, recurrenceDates); QFETCH(QSet, exceptionDates); QFETCH(QDate, startDate); QFETCH(QDate, endDate); QFETCH(QList, occurrenceDates); QScopedPointer cm(QOrganizerManager::fromUri(uri)); QOrganizerTodo todo; todo.setDisplayLabel("todo"); todo.setStartDateTime(QDateTime(generatorDate, QTime(11, 0, 0))); todo.setDueDateTime(QDateTime(generatorDate, QTime(11, 30, 0))); todo.setRecurrenceRule(recurrenceRule); todo.setExceptionRule(exceptionRule); todo.setRecurrenceDates(recurrenceDates); todo.setExceptionDates(exceptionDates); if (cm->saveItem(&todo)) { QList items = cm->itemOccurrences(todo, QDateTime(startDate, QTime(0, 0, 0)), QDateTime(endDate, QTime(23, 59, 59, 999))); QList actualDates; for (int i = 0; i < items.size(); i++) { QOrganizerItem item = items.at(i); QCOMPARE(item.type(), QOrganizerItemType::TypeTodoOccurrence); QDate occurrenceDate = item.detail(QOrganizerItemDetail::TypeTodoTime).value(QOrganizerTodoTime::FieldStartDateTime).toDateTime().date(); //QCOMPARE(occurrenceDate, occurrenceDates.at(i)); actualDates << occurrenceDate; } if (actualDates != occurrenceDates) { qDebug() << "Actual: " << actualDates; qDebug() << "Expected: " << occurrenceDates; QCOMPARE(actualDates, occurrenceDates); } } else { // Allow backend specific limitations QCOMPARE(cm->error(), QOrganizerManager::NotSupportedError); qWarning() << "The todo not supported by the backend"; } } void tst_QOrganizerManager::observerDeletion() { QOrganizerManager *manager = new QOrganizerManager("memory"); QOrganizerEvent c; QVERIFY(manager->saveItem(&c)); QOrganizerItemId id = c.id(); QOrganizerItemObserver *observer = new QOrganizerItemObserver(manager, id); Q_UNUSED(observer) delete manager; delete observer; // Test for bug MOBILITY-2566 - that QOrganizerItemObserver doesn't crash when it is // destroyed after the associated QOrganizerManager } void tst_QOrganizerManager::signalEmission() { QTest::qWait(500); // clear the signal queue QFETCH(QString, uri); QScopedPointer m1(QOrganizerManager::fromUri(uri)); qRegisterMetaType("QOrganizerItemId"); qRegisterMetaType >("QList"); QSignalSpy spyAdded(m1.data(), SIGNAL(itemsAdded(QList))); QSignalSpy spyModified(m1.data(), SIGNAL(itemsChanged(QList))); QSignalSpy spyRemoved(m1.data(), SIGNAL(itemsRemoved(QList))); QSignalSpy spyChanged(m1.data(), SIGNAL(dataChanged())); QList args; QList arg; QOrganizerTodo todo; QList batchAdd; QList batchRemove; int addSigCount = 0; // the expected signal counts. int modSigCount = 0; int remSigCount = 0; // verify add emits signal added QOrganizerItemDisplayLabel nc; nc.setLabel("label me this"); QVERIFY(todo.saveDetail(&nc)); QVERIFY(m1->saveItem(&todo)); QOrganizerItemId cid = todo.id(); QTRY_VERIFY(spyAdded.count() > addSigCount); ++addSigCount; args = spyAdded.takeFirst(); --addSigCount; arg = args.first().value >(); QVERIFY(arg.count() == 1); QCOMPARE(QOrganizerItemId(arg.at(0)), cid); QScopedPointer todo1Observer(new QOrganizerItemObserver(m1.data(), cid)); QScopedPointer spyObserverModified1(new QSignalSpy(todo1Observer.data(), SIGNAL(itemChanged()))); QScopedPointer spyObserverRemoved1(new QSignalSpy(todo1Observer.data(), SIGNAL(itemRemoved()))); // verify save modified emits signal changed nc.setLabel("label me that"); QVERIFY(todo.saveDetail(&nc)); QVERIFY(m1->saveItem(&todo)); QTRY_VERIFY(spyModified.count() > modSigCount); ++modSigCount; QTRY_VERIFY(spyObserverModified1->count() > 0); args = spyModified.takeFirst(); --modSigCount; arg = args.first().value >(); QVERIFY(arg.count() == 1); QCOMPARE(QOrganizerItemId(arg.at(0)), cid); // verify remove emits signal removed QVERIFY(m1->removeItem(todo.id())); QTRY_VERIFY(spyRemoved.count() > remSigCount); QTRY_VERIFY(spyObserverRemoved1->count() > 0); args = spyRemoved.takeFirst(); --remSigCount; arg = args.first().value >(); QVERIFY(arg.count() == 1); QCOMPARE(QOrganizerItemId(arg.at(0)), cid); // verify multiple adds works as advertised QOrganizerTodo todo2, todo3; QOrganizerItemDisplayLabel nc2, nc3; nc2.setLabel("Mark"); nc3.setLabel("Garry"); QVERIFY(todo2.saveDetail(&nc2)); QVERIFY(todo3.saveDetail(&nc3)); QVERIFY(m1->saveItem(&todo2)); QVERIFY(m1->saveItem(&todo3)); QTRY_COMPARE(spyModified.count(), modSigCount); QTRY_VERIFY(spyAdded.count() > addSigCount); addSigCount = spyAdded.count(); spyObserverModified1->clear(); spyObserverRemoved1->clear(); QScopedPointer todo2Observer(new QOrganizerItemObserver(m1.data(), todo2.id())); QScopedPointer todo3Observer(new QOrganizerItemObserver(m1.data(), todo3.id())); QScopedPointer spyObserverModified2(new QSignalSpy(todo2Observer.data(), SIGNAL(itemChanged()))); QScopedPointer spyObserverModified3(new QSignalSpy(todo3Observer.data(), SIGNAL(itemChanged()))); QScopedPointer spyObserverRemoved2(new QSignalSpy(todo2Observer.data(), SIGNAL(itemRemoved()))); QScopedPointer spyObserverRemoved3(new QSignalSpy(todo3Observer.data(), SIGNAL(itemRemoved()))); // verify multiple modifies works as advertised nc2.setLabel("M."); QVERIFY(todo2.saveDetail(&nc2)); QVERIFY(m1->saveItem(&todo2)); nc2.setLabel("Mark"); nc3.setLabel("G."); QVERIFY(todo2.saveDetail(&nc2)); QVERIFY(todo3.saveDetail(&nc3)); QVERIFY(m1->saveItem(&todo2)); QVERIFY(m1->saveItem(&todo3)); QTRY_VERIFY(spyModified.count() > modSigCount); modSigCount = spyModified.count(); QTRY_VERIFY(spyObserverModified2->count() > 0); QTRY_VERIFY(spyObserverModified3->count() > 0); QCOMPARE(spyObserverModified1->count(), 0); // verify multiple removes works as advertised m1->removeItem(todo3.id()); m1->removeItem(todo2.id()); QTRY_VERIFY(spyRemoved.count() > remSigCount); remSigCount = spyRemoved.count(); QTRY_VERIFY(spyObserverRemoved2->count() > 0); QTRY_VERIFY(spyObserverRemoved3->count() > 0); QCOMPARE(spyObserverRemoved1->count(), 0); QVERIFY(!m1->removeItem(todo.id())); // not saved. /* Now test the batch equivalents */ spyAdded.clear(); spyModified.clear(); spyRemoved.clear(); /* Batch adds - set ids to zero so add succeeds. */ todo.setId(QOrganizerItemId()); todo2.setId(QOrganizerItemId()); todo3.setId(QOrganizerItemId()); batchAdd << todo << todo2 << todo3; QMap errorMap; QVERIFY(m1->saveItems(&batchAdd)); errorMap = m1->errorMap(); QVERIFY(batchAdd.count() == 3); todo = batchAdd.at(0); todo2 = batchAdd.at(1); todo3 = batchAdd.at(2); // We want to see one itemsAdded signal listing the id's for all three items QTRY_COMPARE(spyAdded.count(), 1); { QList sigids = spyAdded.takeFirst().at(0).value >(); QVERIFY(sigids.contains(todo.id())); QVERIFY(sigids.contains(todo2.id())); QVERIFY(sigids.contains(todo3.id())); } QTRY_COMPARE(spyModified.count(), 0); todo1Observer.reset(new QOrganizerItemObserver(m1.data(), todo.id())); todo2Observer.reset(new QOrganizerItemObserver(m1.data(), todo2.id())); todo3Observer.reset(new QOrganizerItemObserver(m1.data(), todo3.id())); spyObserverModified1.reset(new QSignalSpy(todo1Observer.data(), SIGNAL(itemChanged()))); spyObserverModified2.reset(new QSignalSpy(todo2Observer.data(), SIGNAL(itemChanged()))); spyObserverModified3.reset(new QSignalSpy(todo3Observer.data(), SIGNAL(itemChanged()))); spyObserverRemoved1.reset(new QSignalSpy(todo1Observer.data(), SIGNAL(itemRemoved()))); spyObserverRemoved2.reset(new QSignalSpy(todo2Observer.data(), SIGNAL(itemRemoved()))); spyObserverRemoved3.reset(new QSignalSpy(todo3Observer.data(), SIGNAL(itemRemoved()))); QTRY_COMPARE(spyRemoved.count(), 0); /* Batch modifies */ QOrganizerItemDisplayLabel modifiedName = todo.detail(QOrganizerItemDetail::TypeDisplayLabel); modifiedName.setLabel("Modified number 1"); modifiedName = todo2.detail(QOrganizerItemDetail::TypeDisplayLabel); modifiedName.setLabel("Modified number 2"); modifiedName = todo3.detail(QOrganizerItemDetail::TypeDisplayLabel); modifiedName.setLabel("Modified number 3"); batchAdd.clear(); batchAdd << todo << todo2 << todo3; QVERIFY(m1->saveItems(&batchAdd)); errorMap = m1->errorMap(); // We want to see one itemsChanged signal listing the id's for all three items. QTRY_COMPARE(spyModified.count(), 1); { QList sigids = spyModified.takeFirst().at(0).value >(); QVERIFY(sigids.contains(todo.id())); QVERIFY(sigids.contains(todo2.id())); QVERIFY(sigids.contains(todo3.id())); } QTRY_COMPARE(spyObserverModified1->count(), 1); QTRY_COMPARE(spyObserverModified2->count(), 1); QTRY_COMPARE(spyObserverModified3->count(), 1); /* Batch removes */ batchRemove << todo.id() << todo2.id() << todo3.id(); QVERIFY(m1->removeItems(batchRemove)); errorMap = m1->errorMap(); // We want to see one itemsRemoved signal listing the id's for all three items. QTRY_COMPARE(spyRemoved.count(), 1); { QList sigids = spyRemoved.takeFirst().at(0).value >(); QVERIFY(sigids.contains(todo.id())); QVERIFY(sigids.contains(todo2.id())); QVERIFY(sigids.contains(todo3.id())); } QTRY_COMPARE(spyObserverRemoved1->count(), 1); QTRY_COMPARE(spyObserverRemoved2->count(), 1); QTRY_COMPARE(spyObserverRemoved3->count(), 1); QTRY_COMPARE(spyAdded.count(), 0); QTRY_COMPARE(spyModified.count(), 0); /* Now some cross manager testing */ if (m1->managerName() == QStringLiteral("memory")) QSKIP("Not supported by memory back-end"); spyAdded.clear(); spyModified.clear(); spyRemoved.clear(); spyChanged.clear(); // verify that signals are emitted for modifications made to other managers (same id). QScopedPointer m2(QOrganizerManager::fromUri(uri)); // add one new item QOrganizerEvent newEvent; newEvent.setStartDateTime(QDateTime::currentDateTime()); newEvent.setEndDateTime(QDateTime::currentDateTime()); newEvent.setDisplayLabel(QStringLiteral("a new event")); QVERIFY(m2->saveItem(&newEvent)); QTRY_VERIFY(spyChanged.count() || (spyAdded.count() > 0)); spyAdded.clear(); spyModified.clear(); spyRemoved.clear(); spyChanged.clear(); // modify the item newEvent.setEndDateTime(QDateTime::currentDateTime().addDays(1)); QVERIFY(m2->saveItem(&newEvent)); QTRY_VERIFY(spyChanged.count() || (spyModified.count() > 0)); spyAdded.clear(); spyModified.clear(); spyRemoved.clear(); spyChanged.clear(); // remove the item QVERIFY(m2->removeItem(newEvent.id())); QTRY_VERIFY(spyChanged.count() || (spyRemoved.count() > 0)); spyAdded.clear(); spyModified.clear(); spyRemoved.clear(); spyChanged.clear(); // add several new items QOrganizerEvent moreEvents[10]; for (int i = 0; i < 10; ++i) {; moreEvents[i].setStartDateTime(QDateTime::currentDateTime()); moreEvents[i].setEndDateTime(QDateTime::currentDateTime()); moreEvents[i].setDisplayLabel(QStringLiteral("yet another event")); QVERIFY(m2->saveItem(&moreEvents[i])); } QTRY_VERIFY(spyChanged.count() || (spyAdded.count() > 0)); spyAdded.clear(); spyModified.clear(); spyRemoved.clear(); spyChanged.clear(); // modify several items for (int i = 0; i < 10; ++i) { moreEvents[i].setEndDateTime(QDateTime::currentDateTime().addDays(i + 1)); QVERIFY(m2->saveItem(&moreEvents[i])); } QTRY_VERIFY(spyChanged.count() || (spyModified.count() > 0)); spyAdded.clear(); spyModified.clear(); spyRemoved.clear(); spyChanged.clear(); // remove several items for (int i = 0; i < 10; ++i) QVERIFY(m2->removeItem(moreEvents[i].id())); QTRY_VERIFY(spyChanged.count() || (spyRemoved.count() > 0)); spyChanged.clear(); QSignalSpy spyCollectionAdded(m1.data(), SIGNAL(collectionsAdded(QList))); QSignalSpy spyCollectionModified(m1.data(), SIGNAL(collectionsChanged(QList))); QSignalSpy spyCollectionRemoved(m1.data(), SIGNAL(collectionsRemoved(QList))); // add one collection QOrganizerCollection newCollection; newCollection.setMetaData(QOrganizerCollection::KeyName, QVariant(QStringLiteral("a new collection"))); QVERIFY(m2->saveCollection(&newCollection)); QTRY_VERIFY(spyChanged.count() || (spyCollectionAdded.count() > 0)); spyCollectionAdded.clear(); spyCollectionModified.clear(); spyCollectionRemoved.clear(); spyChanged.clear(); // modify one collection newCollection.setMetaData(QOrganizerCollection::KeyName, QVariant(QStringLiteral("a new collection, modified"))); QVERIFY(m2->saveCollection(&newCollection)); QTRY_VERIFY(spyChanged.count() || (spyCollectionModified.count() > 0)); spyCollectionAdded.clear(); spyCollectionModified.clear(); spyCollectionRemoved.clear(); spyChanged.clear(); // remove one collection QVERIFY(m2->removeCollection(newCollection.id())); QTRY_VERIFY(spyChanged.count() || (spyCollectionRemoved.count() > 0)); } void tst_QOrganizerManager::errorStayingPut() { /* Make sure that when we clone a manager, we don't clone the error */ QMap params; params.insert("id", "error isolation test"); QOrganizerManager m1("memory",params); QVERIFY(m1.error() == QOrganizerManager::NoError); /* Remove an invalid item to get an error */ QVERIFY(m1.removeItem(QOrganizerItemId()) == false); QVERIFY(m1.error() == QOrganizerManager::DoesNotExistError); /* Create a new manager with hopefully the same backend */ QOrganizerManager m2("memory", params); QVERIFY(m1.error() == QOrganizerManager::DoesNotExistError); QVERIFY(m2.error() == QOrganizerManager::NoError); /* Cause an error on the other ones and check the first is not affected */ m2.saveItems(0); QVERIFY(m1.error() == QOrganizerManager::DoesNotExistError); QVERIFY(m2.error() == QOrganizerManager::BadArgumentError); QOrganizerItem c; QOrganizerItemDetail d(QOrganizerItemDetail::TypeUndefined); d.setValue(101, 5); c.saveDetail(&d); // QVERIFY(m1.saveItem(&c) == false); // QVERIFY(m1.error() == QOrganizerManager::InvalidDetailError); QVERIFY(m2.error() == QOrganizerManager::BadArgumentError); } void tst_QOrganizerManager::changeSet() { QOrganizerItemId id; QOrganizerItemChangeSet changeSet; QVERIFY(changeSet.addedItems().isEmpty()); QVERIFY(changeSet.changedItems().isEmpty()); QVERIFY(changeSet.removedItems().isEmpty()); changeSet.insertAddedItem(id); QVERIFY(!changeSet.addedItems().isEmpty()); QVERIFY(changeSet.changedItems().isEmpty()); QVERIFY(changeSet.removedItems().isEmpty()); QVERIFY(changeSet.addedItems().contains(id)); changeSet.clearAddedItems(); changeSet.insertAddedItems(QList() << id); changeSet.insertChangedItem(id); changeSet.insertChangedItems(QList() << id); QVERIFY(changeSet.changedItems().size() == 1); // set, should only be added once. QVERIFY(!changeSet.addedItems().isEmpty()); QVERIFY(!changeSet.changedItems().isEmpty()); QVERIFY(changeSet.removedItems().isEmpty()); QVERIFY(changeSet.changedItems().contains(id)); changeSet.clearChangedItems(); QVERIFY(changeSet.changedItems().isEmpty()); changeSet.insertRemovedItems(QList() << id); QVERIFY(changeSet.removedItems().contains(id)); changeSet.clearRemovedItems(); QVERIFY(changeSet.removedItems().isEmpty()); QVERIFY(changeSet.dataChanged() == false); QOrganizerItemChangeSet changeSet2; changeSet2 = changeSet; QVERIFY(changeSet.addedItems() == changeSet2.addedItems()); changeSet.emitSignals(0); changeSet2.clearAddedItems(); QVERIFY(changeSet2.addedItems().isEmpty()); changeSet2.insertAddedItems(changeSet.addedItems().toList()); QVERIFY(changeSet.addedItems() == changeSet2.addedItems()); changeSet2.clearAll(); QVERIFY(changeSet.addedItems() != changeSet2.addedItems()); QOrganizerItemChangeSet changeSet3(changeSet2); QVERIFY(changeSet.addedItems() != changeSet3.addedItems()); QVERIFY(changeSet2.addedItems() == changeSet3.addedItems()); changeSet.setDataChanged(true); QVERIFY(changeSet.dataChanged() == true); QVERIFY(changeSet.dataChanged() != changeSet2.dataChanged()); QVERIFY(changeSet.dataChanged() != changeSet3.dataChanged()); changeSet.emitSignals(0); QOrganizerCollectionId colId; QOrganizerCollectionChangeSet colChangeSet; QVERIFY(colChangeSet.addedCollections().isEmpty()); QVERIFY(colChangeSet.changedCollections().isEmpty()); QVERIFY(colChangeSet.removedCollections().isEmpty()); colChangeSet.insertAddedCollection(colId); QVERIFY(!changeSet.addedItems().isEmpty()); QVERIFY(colChangeSet.changedCollections().isEmpty()); QVERIFY(colChangeSet.removedCollections().isEmpty()); QVERIFY(colChangeSet.addedCollections().contains(colId)); colChangeSet.clearAddedCollections(); colChangeSet.insertAddedCollections(QList() << colId); colChangeSet.insertChangedCollection(colId); colChangeSet.insertChangedCollections(QList() << colId); QVERIFY(colChangeSet.changedCollections().size() == 1); // set, should only be added once. QVERIFY(!colChangeSet.addedCollections().isEmpty()); QVERIFY(!colChangeSet.changedCollections().isEmpty()); QVERIFY(colChangeSet.removedCollections().isEmpty()); QVERIFY(colChangeSet.changedCollections().contains(colId)); colChangeSet.clearChangedCollections(); QVERIFY(colChangeSet.changedCollections().isEmpty()); colChangeSet.insertRemovedCollections(QList() << colId); QVERIFY(colChangeSet.removedCollections().contains(colId)); colChangeSet.clearRemovedCollections(); QVERIFY(colChangeSet.removedCollections().isEmpty()); QVERIFY(colChangeSet.dataChanged() == false); QOrganizerCollectionChangeSet colChangeSet2; colChangeSet2 = colChangeSet; QVERIFY(colChangeSet.addedCollections() == colChangeSet2.addedCollections()); colChangeSet.emitSignals(0); colChangeSet2.clearAddedCollections(); QVERIFY(colChangeSet2.addedCollections().isEmpty()); colChangeSet2.insertAddedCollections(colChangeSet.addedCollections().toList()); QVERIFY(colChangeSet.addedCollections() == colChangeSet2.addedCollections()); colChangeSet2.clearAll(); QVERIFY(colChangeSet.addedCollections() != colChangeSet2.addedCollections()); QOrganizerCollectionChangeSet colChangeSet3(colChangeSet2); QVERIFY(colChangeSet.addedCollections() != colChangeSet3.addedCollections()); QVERIFY(colChangeSet2.addedCollections() == colChangeSet3.addedCollections()); colChangeSet.setDataChanged(true); QVERIFY(colChangeSet.dataChanged() == true); QVERIFY(colChangeSet.dataChanged() != colChangeSet2.dataChanged()); QVERIFY(colChangeSet.dataChanged() != colChangeSet3.dataChanged()); colChangeSet.emitSignals(0); } void tst_QOrganizerManager::fetchHint() { QOrganizerItemFetchHint hint; hint.setOptimizationHints(QOrganizerItemFetchHint::NoBinaryBlobs); QCOMPARE(hint.optimizationHints(), QOrganizerItemFetchHint::NoBinaryBlobs); } void tst_QOrganizerManager::testFilterFunction() { QOrganizerEvent item; item.setId(makeItemId(10)); item.setStartDateTime(QDateTime(QDate(2010,10,10), QTime(10,10))); item.setEndDateTime(QDateTime(QDate(2010,10,10), QTime(12,10))); item.setDisplayLabel("test"); item.setCollectionId(makeCollectionId(1)); // Test for QOrganizerItemFilter::InvalidFilter QOrganizerItemInvalidFilter fif; QVERIFY(!QOrganizerManagerEngine::testFilter(fif, item)); // Test for QOrganizerItemFilter::IdFilter: QOrganizerItemIdFilter fidf; fidf.setIds(QList() << makeItemId(10)); QVERIFY(QOrganizerManagerEngine::testFilter(fidf, item)); // test for nonexistent id fidf.setIds(QList() << makeItemId(11)); QVERIFY(!QOrganizerManagerEngine::testFilter(fidf, item)); // Test for QOrganizerItemFilter::DefaultFilter: QOrganizerItemFilter fdf; QVERIFY(fdf.type() == QOrganizerItemFilter::DefaultFilter); QVERIFY(QOrganizerManagerEngine::testFilter(fdf, item)); // Test for QOrganizerItemFilter::OrganizerItemDetailFieldFilter: QOrganizerItemDetailFieldFilter fdef; fdef.setDetail(QOrganizerItemDetail::TypeDisplayLabel, QOrganizerItemDisplayLabel::FieldLabel); fdef.setValue("invalid"); // test for nonexistent label QVERIFY(!QOrganizerManagerEngine::testFilter(fdef, item)); fdef.setValue("test"); // test for existent label QVERIFY(QOrganizerManagerEngine::testFilter(fdef, item)); // Test for QOrganizerItemFilter::OrganizerItemDetailRangeFilter: QOrganizerItemDetailRangeFilter fdrf; fdrf.setDetail(QOrganizerItemDetail::TypeEventTime, QOrganizerEventTime::FieldStartDateTime); fdrf.setRange(QDateTime(QDate(2010,10,9)), QDateTime(QDate(2010,10,11))); // test for a valid range QVERIFY(QOrganizerManagerEngine::testFilter(fdrf, item)); fdrf.setRange(QDateTime(QDate(2010,10,11)), QDateTime(QDate(2010,10,12))); // test for item not in the range QVERIFY(!QOrganizerManagerEngine::testFilter(fdrf, item)); // Test for QOrganizerItemFilter::IntersectionFilter: QOrganizerItemIntersectionFilter oiif; oiif.setFilters(QList() << fif << fdf); // check for an invalid filter and default filter intersection QVERIFY(!QOrganizerManagerEngine::testFilter(oiif, item)); oiif.setFilters(QList() << fdef << fdf); // check for a detail filter and default filter intersection QVERIFY(QOrganizerManagerEngine::testFilter(oiif, item)); // Test for QOrganizerItemFilter::UnionFilter: QOrganizerItemUnionFilter oiuf; oiuf.setFilters(QList() << fif << fdf); // check for an invalid filter and default filter union QVERIFY(QOrganizerManagerEngine::testFilter(oiuf, item)); oiuf.setFilters(QList() << fdef << fdf); // check for a detail filter and default filter union QVERIFY(QOrganizerManagerEngine::testFilter(oiuf, item)); // Test for QOrganizerItemFilter::CollectionFilter: QOrganizerItemCollectionFilter oicf; oicf.setCollectionId(makeCollectionId(1)); // check for existing collection id QVERIFY(QOrganizerManagerEngine::testFilter(oicf, item)); oicf.setCollectionId(makeCollectionId(2)); // check for nonexisting collection id QVERIFY(!QOrganizerManagerEngine::testFilter(oicf, item)); } void tst_QOrganizerManager::dataSerialization() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); QOrganizerEvent event; event.setDisplayLabel("event"); event.setStartDateTime(QDateTime(QDate(2010, 9, 9), QTime(11, 0, 0))); event.setEndDateTime(QDateTime(QDate(2010, 9, 9), QTime(11, 30, 0))); if (cm->saveItem(&event)) { QByteArray buffer; QDataStream outBufferStream(&buffer, QIODevice::WriteOnly); outBufferStream << event.id().toString(); QVERIFY(buffer.length() > 0); QDataStream inBufferStream(buffer); QString inString; inBufferStream >> inString; QOrganizerItemId id = QOrganizerItemId::fromString(inString); QVERIFY(id == event.id()); } } void tst_QOrganizerManager::itemFilterFetch() { // Some of the tests present on itemFetch()-tests, but this test extends the cases a bit // Preparations QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); QOrganizerItemExtendedDetail extDetail; QOrganizerItemComment comment; cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one for (int i=0; i<6; i++) { QOrganizerEvent event; event.setDisplayLabel(QString("event %1").arg(i)); event.setStartDateTime(QDateTime(QDate(2010, 9, 9), QTime(11, 0, 0))); event.setEndDateTime(QDateTime(QDate(2010, 9, 9), QTime(11, 30, 0))); if (i==0) { // 1 event.setPriority(QOrganizerItemPriority::VeryLowPriority); } else if (i==1) { // 2 comment.setComment("my comment"); event.saveDetail(&comment); } else if (i==2) { // 3 QOrganizerItemDisplayLabel label; label.setLabel("my 3rd event!"); event.saveDetail(&label); } else if (i==3) { // 4 QOrganizerItemDisplayLabel label; label.setLabel("my 4th event!"); event.saveDetail(&label); } else if (i==4) { // 5 event.setEndDateTime(QDateTime(QDate(2010, 10, 10), QTime(11, 0, 0))); } else if (i==5) { // 6 extDetail.setName("DetailOfMine"); extDetail.setData(42); event.saveDetail(&extDetail); } QVERIFY(cm->saveItem(&event)); } QCOMPARE(cm->items().count(), 6); // DetailFieldFilter Checks QOrganizerItemDetailFieldFilter dFieldFilter; // 1 dFieldFilter.setDetail(QOrganizerItemDetail::TypePriority, QOrganizerItemPriority::FieldPriority); dFieldFilter.setValue(QOrganizerItemPriority::VeryHighPriority); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFieldFilter).count(), 0); dFieldFilter.setValue(QOrganizerItemPriority::VeryLowPriority); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFieldFilter).count(), 1); // 2 dFieldFilter.setDetail(QOrganizerItemDetail::TypeComment, QOrganizerItemComment::FieldComment); dFieldFilter.setValue("my comment"); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFieldFilter).count(), 1); // 3-4 dFieldFilter.setDetail(QOrganizerItemDetail::TypeDisplayLabel, QOrganizerItemDisplayLabel::FieldLabel); dFieldFilter.setValue("my 3rd event!"); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFieldFilter).count(), 1); dFieldFilter.setMatchFlags(QOrganizerItemFilter::MatchEndsWith); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFieldFilter).count(), 1); dFieldFilter.setValue("event!"); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFieldFilter).count(), 2); dFieldFilter.setValue("event"); dFieldFilter.setMatchFlags(QOrganizerItemFilter::MatchContains); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFieldFilter).count(), cm->items().count()); dFieldFilter.setValue("my"); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFieldFilter).count(), 2); dFieldFilter.setMatchFlags(QOrganizerItemFilter::MatchStartsWith); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFieldFilter).count(), 2); dFieldFilter.setValue("event"); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFieldFilter).count(), 4); // 5 dFieldFilter.setMatchFlags(QOrganizerItemFilter::MatchExactly); dFieldFilter.setDetail(QOrganizerItemDetail::TypeEventTime, QOrganizerEventTime::FieldEndDateTime); dFieldFilter.setValue(QDateTime(QDate(2010, 10, 10), QTime(11, 0, 0))); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFieldFilter).count(), 1); // 6 dFieldFilter.setDetail(QOrganizerItemDetail::TypeExtendedDetail, QOrganizerItemExtendedDetail::FieldName); dFieldFilter.setValue("DetailOfMine"); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFieldFilter).count(), 1); // DetailFilter Checks if (cm->managerName() == QStringLiteral("memory")) { QOrganizerItemDetailFilter dFilter; // 1 QOrganizerItemPriority priority; priority.setPriority(QOrganizerItemPriority::VeryHighPriority); dFilter.setDetail(priority); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFilter).count(), 0); priority.setPriority(QOrganizerItemPriority::VeryLowPriority); dFilter.setDetail(priority); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFilter).count(), 1); // 2 dFilter.setDetail(comment); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFilter).count(), 1); // 3-4 QOrganizerItemDisplayLabel displayLabel; displayLabel.setLabel(QStringLiteral("my 3rd event!")); dFilter.setDetail(displayLabel); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFilter).count(), 1); displayLabel.setLabel(QStringLiteral("my non existing event!")); dFilter.setDetail(displayLabel); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFilter).count(), 0); // 5 QOrganizerEventTime eventTime; eventTime.setEndDateTime(QDateTime(QDate(2010, 10, 10), QTime(11, 0, 0))); eventTime.setStartDateTime(QDateTime(QDate(2010, 9, 9), QTime(11, 0, 0))); dFilter.setDetail(eventTime); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFilter).count(), 1); // 6 dFilter.setDetail(extDetail); QCOMPARE(cm->items(QDateTime(), QDateTime(), dFilter).count(), 1); } } void tst_QOrganizerManager::itemFetch() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one QOrganizerEvent event; event.setDisplayLabel("event"); event.setStartDateTime(QDateTime(QDate(2010, 9, 9), QTime(11, 0, 0))); event.setEndDateTime(QDateTime(QDate(2010, 9, 9), QTime(11, 30, 0))); QVERIFY(cm->saveItem(&event)); QOrganizerEvent recEvent; recEvent.setDisplayLabel("daily event"); recEvent.setStartDateTime(QDateTime(QDate(2010, 9, 1), QTime(16, 0, 0))); recEvent.setEndDateTime(QDateTime(QDate(2010, 9, 1), QTime(16, 30, 0))); QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Daily); rrule.setLimit(QDate(2010, 9, 10)); recEvent.setRecurrenceRule(rrule); QVERIFY(cm->saveItem(&recEvent)); //fetch all recurrences QList items = cm->items(QDateTime(QDate(2010, 9, 8)), QDateTime(QDate(2010, 9, 12))); QCOMPARE(items.count(), 4); // should return event + 3 x occurrencesOfRecEvent //fetch only the originating items items = cm->itemsForExport(QDateTime(QDate(2010, 9, 8)), QDateTime(QDate(2010, 9, 12)), QOrganizerItemFilter(), QList(), QOrganizerItemFetchHint()); QCOMPARE(items.count(), 2); // test semantics of items(): // first - save event with multiple occurrences; call items() -- should get back just occurrences. cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one rrule.setLimit(QDate(2010, 9, 3)); recEvent.setRecurrenceRule(rrule); recEvent.setId(QOrganizerItemId()); cm->saveItem(&recEvent); items = cm->items(); QCOMPARE(items.count(), 3); foreach (const QOrganizerItem &item, items) QVERIFY(item.type() == QOrganizerItemType::TypeEventOccurrence); // second - the same situation, but giving a time span that only covers the first day - should get back a single occurrence. items = cm->items(QDateTime(QDate(2010, 9, 1), QTime(15, 0, 0)), QDateTime(QDate(2010, 9, 1), QTime(18, 0, 0))); QCOMPARE(items.count(), 1); foreach (const QOrganizerItem& item, items) { QVERIFY(item.type() == QOrganizerItemType::TypeEventOccurrence); } // third - save event with no recurrence; call items() -- should get back that parent, not an occurrence. cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one recEvent.setRecurrenceRules(QSet()); // clear rrule. recEvent.setId(QOrganizerItemId()); cm->saveItem(&recEvent); items = cm->items(); QCOMPARE(items.count(), 1); foreach (const QOrganizerItem &item, items) QVERIFY(item.type() == QOrganizerItemType::TypeEvent); // fourth - the same situation, but giving a time span. should still get back the parent. items = cm->items(QDateTime(QDate(2010, 9, 1), QTime(15, 0, 0)), QDateTime(QDate(2010, 9, 1), QTime(18, 0, 0))); QCOMPARE(items.count(), 1); foreach (const QOrganizerItem &item, items) QVERIFY(item.type() == QOrganizerItemType::TypeEvent); // test semantics of itemsForExport(): // first - save event with multiple occurrences; call ife() -- get back that parent cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one recEvent.setRecurrenceRule(rrule); recEvent.setId(QOrganizerItemId()); cm->saveItem(&recEvent); items = cm->itemsForExport(); QCOMPARE(items.count(), 1); foreach (const QOrganizerItem& item, items) { QVERIFY(item.type() == QOrganizerItemType::TypeEvent); } // second - call items, resave only the first occurrence as an exception, // call ife() -- get back parent + exception items = cm->items(); QCOMPARE(items.size(), 3); // 3 occurrences. int eventCount = 0; int eventOccurrenceCount = 0; foreach (const QOrganizerItem& item, items) { if (item.type() == QOrganizerItemType::TypeEventOccurrence) { if (eventOccurrenceCount == 0) { QOrganizerEventOccurrence exception(item); exception.setDisplayLabel("exception"); QVERIFY(cm->saveItem(&exception)); } eventOccurrenceCount++; } else if (item.type() == QOrganizerItemType::TypeEvent) { eventCount++; } } QCOMPARE(eventOccurrenceCount, 3); QCOMPARE(eventCount, 0); items = cm->items(); // reload items after saving exception QCOMPARE(items.size(), 3); // saving the exception shouldn't have added more items. items = cm->itemsForExport(); QCOMPARE(items.count(), 2); eventCount = 0; eventOccurrenceCount = 0; foreach (const QOrganizerItem& item, items) { if (item.type() == QOrganizerItemType::TypeEvent) { eventCount += 1; } else if (item.type() == QOrganizerItemType::TypeEventOccurrence) { eventOccurrenceCount += 1; } QVERIFY(!item.id().isNull()); // should NEVER be null, since that would be a generated occurrence. } QCOMPARE(eventCount, 1); QCOMPARE(eventOccurrenceCount, 1); //make a parent filter and test item count QOrganizerItemDetailFieldFilter dff; dff.setDetail(QOrganizerItemDetail::TypeParent, QOrganizerItemParent::FieldParentId); dff.setValue(QVariant::fromValue(recEvent.id())); QCOMPARE(cm->items(QDateTime(), QDateTime(), dff).count(), 3); QCOMPARE(cm->itemsForExport(QDateTime(), QDateTime(), dff).count(), 2); // third, have all occurrences persisted items = cm->items(); QCOMPARE(items.size(), 3); // should be three occurrences foreach (const QOrganizerItem& item, items) { if (item.type() == QOrganizerItemType::TypeEventOccurrence) { QOrganizerEventOccurrence exception(item); exception.setDisplayLabel("exception"); QVERIFY(cm->saveItem(&exception)); } } items = cm->itemsForExport(); QCOMPARE(items.size(), 4); // parent + 3 persisted exceptions eventCount = 0; eventOccurrenceCount = 0; foreach (const QOrganizerItem& item, items) { if (item.type() == QOrganizerItemType::TypeEvent) { eventCount += 1; } else if (item.type() == QOrganizerItemType::TypeEventOccurrence) { eventOccurrenceCount += 1; } QVERIFY(!item.id().isNull()); // should NEVER be null, since that would be a generated occurrence. } QCOMPARE(eventCount, 1); QCOMPARE(eventOccurrenceCount, 3); // fourth, all occurrences persisted, time period excludes parent item, but it's still fetched items = cm->itemsForExport(QDateTime(QDate(2010, 9, 2)), QDateTime(QDate(2010, 9, 4))); QCOMPARE(items.size(), 3); // parent + 2 persisted exceptions eventCount = 0; eventOccurrenceCount = 0; foreach (const QOrganizerItem& item, items) { if (item.type() == QOrganizerItemType::TypeEvent) { eventCount += 1; } else if (item.type() == QOrganizerItemType::TypeEventOccurrence) { eventOccurrenceCount += 1; } QVERIFY(!item.id().isNull()); // should NEVER be null, since that would be a generated occurrence. } QCOMPARE(eventCount, 1); QCOMPARE(eventOccurrenceCount, 2); } // This is mostly a copy of itemFetch(), but for todos void tst_QOrganizerManager::todoItemFetch() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); QList items; cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one QOrganizerTodo todoStartDueDate; todoStartDueDate.setDisplayLabel("todo startdue"); todoStartDueDate.setStartDateTime(QDateTime(QDate(2011, 3, 27), QTime(11, 0, 0))); todoStartDueDate.setDueDateTime(QDateTime(QDate(2011, 3, 29), QTime(11, 30, 0))); QVERIFY(cm->saveItem(&todoStartDueDate)); QOrganizerTodo todoStartDate; todoStartDate.setDisplayLabel("todo start"); todoStartDate.setStartDateTime(QDateTime(QDate(2011, 3, 25), QTime(11, 0, 0))); QVERIFY(cm->saveItem(&todoStartDate)); QOrganizerTodo todoDueDate; todoDueDate.setDisplayLabel("todo due"); todoDueDate.setDueDateTime(QDateTime(QDate(2011, 3, 25), QTime(11, 30, 0))); QVERIFY(cm->saveItem(&todoDueDate)); QOrganizerTodo todoNoDate; todoNoDate.setDisplayLabel("todo nodate"); QVERIFY(cm->saveItem(&todoNoDate)); items = cm->items(); QCOMPARE(items.count(), 4); items = cm->items(QDateTime(), QDateTime(QDate(2011, 3, 25), QTime(13, 0, 0))); QCOMPARE(items.count(), 2); items = cm->items(QDateTime(), QDateTime(QDate(2011, 3, 25), QTime(11, 15, 0))); QCOMPARE(items.count(), 1); items = cm->items(QDateTime(QDate(2011, 3, 27), QTime(10, 0, 0)), QDateTime()); QCOMPARE(items.count(), 1); items = cm->items(QDateTime(QDate(2011, 3, 29), QTime(13, 0, 0)), QDateTime()); QCOMPARE(items.count(), 0); items = cm->items(QDateTime(QDate(2011, 3, 26), QTime(11, 0, 0)), QDateTime(QDate(2011, 3, 28), QTime(11, 0, 0))); QCOMPARE(items.count(), 1); items = cm->items(QDateTime(QDate(2011, 3, 28), QTime(11, 0, 0)), QDateTime(QDate(2011, 3, 30), QTime(11, 0, 0))); QCOMPARE(items.count(), 1); items = cm->items(QDateTime(QDate(2011, 3, 28), QTime(11, 0, 0)), QDateTime(QDate(2011, 3, 28), QTime(11, 0, 0))); QCOMPARE(items.count(), 1); cm->removeItems(cm->itemIds()); QOrganizerTodo todo; todo.setDisplayLabel("todo"); todo.setStartDateTime(QDateTime(QDate(2010, 9, 9), QTime(11, 0, 0))); todo.setDueDateTime(QDateTime(QDate(2010, 9, 9), QTime(11, 30, 0))); QVERIFY(cm->saveItem(&todo)); QOrganizerTodo recTodo; recTodo.setDisplayLabel("daily todo"); recTodo.setStartDateTime(QDateTime(QDate(2010, 9, 1), QTime(16, 0, 0))); recTodo.setDueDateTime(QDateTime(QDate(2010, 9, 1), QTime(16, 30, 0))); QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Daily); rrule.setLimit(QDate(2010, 9, 10)); recTodo.setRecurrenceRule(rrule); QVERIFY(cm->saveItem(&recTodo)); //fetch all recurrences items = cm->items(QDateTime(QDate(2010, 9, 8)), QDateTime(QDate(2010, 9, 12))); QCOMPARE(items.count(), 4); // should return todo + 3 x occurrencesOfRecTodo //fetch only the originating items items = cm->itemsForExport(QDateTime(QDate(2010, 9, 8)), QDateTime(QDate(2010, 9, 12)), QOrganizerItemFilter(), QList(), QOrganizerItemFetchHint()); QCOMPARE(items.count(), 2); // test semantics of items(): // first - save todo with multiple occurrences; call items() -- should get back just occurrences. cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one rrule.setLimit(QDate(2010, 9, 3)); recTodo.setRecurrenceRule(rrule); recTodo.setId(QOrganizerItemId()); cm->saveItem(&recTodo); items = cm->items(); QCOMPARE(items.count(), 3); foreach (const QOrganizerItem& item, items) { QVERIFY(item.type() == QOrganizerItemType::TypeTodoOccurrence); } // second - the same situation, but giving a time span that only covers the first day - should get back a single occurrence. items = cm->items(QDateTime(QDate(2010, 9, 1), QTime(15, 0, 0)), QDateTime(QDate(2010, 9, 1), QTime(18, 0, 0))); QCOMPARE(items.count(), 1); foreach (const QOrganizerItem& item, items) { QVERIFY(item.type() == QOrganizerItemType::TypeTodoOccurrence); } // third - save event with no recurrence; call items() -- should get back that parent, not an occurrence. cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one recTodo.setRecurrenceRules(QSet()); // clear rrule. recTodo.setId(QOrganizerItemId()); cm->saveItem(&recTodo); items = cm->items(); QCOMPARE(items.count(), 1); foreach (const QOrganizerItem& item, items) { QVERIFY(item.type() == QOrganizerItemType::TypeTodo); } // fourth - the same situation, but giving a time span. should still get back the parent. items = cm->items(QDateTime(QDate(2010, 9, 1), QTime(15, 0, 0)), QDateTime(QDate(2010, 9, 1), QTime(18, 0, 0))); QCOMPARE(items.count(), 1); foreach (const QOrganizerItem& item, items) { QVERIFY(item.type() == QOrganizerItemType::TypeTodo); } // test semantics of itemsForExport(): // first - save event with multiple occurrences; call ife() -- get back that parent cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one recTodo.setRecurrenceRule(rrule); recTodo.setId(QOrganizerItemId()); cm->saveItem(&recTodo); items = cm->itemsForExport(); QCOMPARE(items.count(), 1); foreach (const QOrganizerItem& item, items) { QVERIFY(item.type() == QOrganizerItemType::TypeTodo); } // second - call items, resave only the first occurrence as an exception, // call ife() -- get back parent + exception items = cm->items(); int todoCount = 0; int todoOccurrenceCount = 0; foreach (const QOrganizerItem& item, items) { if (item.type() == QOrganizerItemType::TypeTodoOccurrence) { if (todoOccurrenceCount == 0) { QOrganizerTodoOccurrence exception(item); exception.setDisplayLabel("exception"); QVERIFY(cm->saveItem(&exception)); } todoOccurrenceCount++; } else if (item.type() == QOrganizerItemType::TypeTodo) { todoCount++; } } QCOMPARE(todoOccurrenceCount, 3); QCOMPARE(todoCount, 0); items = cm->itemsForExport(); QCOMPARE(items.count(), 2); todoCount = 0; todoOccurrenceCount = 0; foreach (const QOrganizerItem& item, items) { if (item.type() == QOrganizerItemType::TypeTodo) { todoCount += 1; } else if (item.type() == QOrganizerItemType::TypeTodoOccurrence) { todoOccurrenceCount += 1; } QVERIFY(!item.id().isNull()); // should NEVER be null, since that would be a generated occurrence. } QCOMPARE(todoCount, 1); QCOMPARE(todoOccurrenceCount, 1); // third, have all occurrences persisted items = cm->items(); foreach (const QOrganizerItem& item, items) { if (item.type() == QOrganizerItemType::TypeTodoOccurrence) { QOrganizerTodoOccurrence exception(item); exception.setDisplayLabel("exception"); QVERIFY(cm->saveItem(&exception)); } } items = cm->itemsForExport(); QCOMPARE(items.size(), 4); todoCount = 0; todoOccurrenceCount = 0; foreach (const QOrganizerItem& item, items) { if (item.type() == QOrganizerItemType::TypeTodo) { todoCount += 1; } else if (item.type() == QOrganizerItemType::TypeTodoOccurrence) { todoOccurrenceCount += 1; } QVERIFY(!item.id().isNull()); // should NEVER be null, since that would be a generated occurrence. } QCOMPARE(todoCount, 1); QCOMPARE(todoOccurrenceCount, 3); } void tst_QOrganizerManager::itemFetchV2() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one QOrganizerEvent event1; event1.setDisplayLabel("event1"); event1.setStartDateTime(QDateTime(QDate(2010, 1, 1), QTime(11, 0, 0))); event1.setEndDateTime(QDateTime(QDate(2010, 1, 1), QTime(11, 30, 0))); QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Daily); rrule.setLimit(QDate(2010, 1, 2)); event1.setRecurrenceRule(rrule); QVERIFY(cm->saveItem(&event1)); QOrganizerEvent event2; event2.setDisplayLabel("event2"); event2.setStartDateTime(QDateTime(QDate(2010, 1, 1), QTime(13, 0, 0))); event2.setEndDateTime(QDateTime(QDate(2010, 1, 1), QTime(13, 30, 0))); rrule = QOrganizerRecurrenceRule(); rrule.setFrequency(QOrganizerRecurrenceRule::Daily); rrule.setLimit(QDate(2010, 1, 2)); event2.setRecurrenceRule(rrule); QVERIFY(cm->saveItem(&event2)); // Get items without a maxCount, check that they're date sorted QList items = cm->items(QDateTime(QDate(2010, 1, 1), QTime(0, 0, 0)), QDateTime()); QCOMPARE(items.size(), 4); QCOMPARE(items[0].displayLabel(), QStringLiteral("event1")); QCOMPARE(items[1].displayLabel(), QStringLiteral("event2")); QCOMPARE(items[2].displayLabel(), QStringLiteral("event1")); QCOMPARE(items[3].displayLabel(), QStringLiteral("event2")); // Get the next 3 items from 2010-02-01 items = cm->items(QDateTime(QDate(2010, 1, 1), QTime(0, 0, 0)), QDateTime(), // no end date limit QOrganizerItemFilter(), 3); // maxCount QCOMPARE(items.size(), 3); QCOMPARE(items[0].displayLabel(), QStringLiteral("event1")); QCOMPARE(items[1].displayLabel(), QStringLiteral("event2")); QCOMPARE(items[2].displayLabel(), QStringLiteral("event1")); } void tst_QOrganizerManager::spanOverDays() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one QOrganizerEvent event; event.setDisplayLabel("huge event"); event.setStartDateTime(QDateTime(QDate(2010, 8, 9), QTime(11, 0, 0))); event.setEndDateTime(QDateTime(QDate(2010, 8, 11), QTime(11, 30, 0))); QVERIFY(cm->saveItem(&event)); // just fetch one day from the event QList items = cm->items(QDateTime(QDate(2010, 8, 9)), QDateTime(QDate(2010, 8, 9), QTime(23,59,59))); QCOMPARE(items.count(), 1); // fetch the next day items = cm->items(QDateTime(QDate(2010, 8, 10), QTime(0,0,0)), QDateTime(QDate(2010, 8, 10), QTime(23,59,59))); QCOMPARE(items.count(), 1); // fetch the last day items = cm->items(QDateTime(QDate(2010, 8, 11), QTime(0,0,0)), QDateTime(QDate(2010, 11, 10), QTime(23,59,59))); QCOMPARE(items.count(), 1); // fetch an interval starting before the event until infinity items = cm->items(QDateTime(QDate(2010, 8, 5), QTime(0,0,0)), QDateTime()); QCOMPARE(items.count(), 1); // fetch an interval ending after the event items = cm->items(QDateTime(), QDateTime(QDate(2010, 12, 10), QTime(23,59,59))); QCOMPARE(items.count(), 1); // fetch an interval starting before the event and ending at almost end of the year items = cm->items(QDateTime(QDate(2010, 8, 5), QTime(0,0,0)), QDateTime(QDate(2010, 12, 10), QTime(23,59,59))); QCOMPARE(items.count(), 1); // fetch an interval ending in the middle of the event items = cm->items(QDateTime(), QDateTime(QDate(2010, 8, 10), QTime(23,59,59))); QCOMPARE(items.count(), 1); // fetch an interval starting from the middle of the event until infinity items = cm->items(QDateTime(QDate(2010, 8, 10), QTime(0,0,0)), QDateTime()); QCOMPARE(items.count(), 1); cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one QOrganizerEvent event2; QOrganizerRecurrenceRule rrule; event2.setDisplayLabel("huge recurring event"); event2.setStartDateTime(QDateTime(QDate(2010, 8, 9), QTime(11, 0, 0))); event2.setEndDateTime(QDateTime(QDate(2010, 8, 11), QTime(11, 30, 0))); rrule.setFrequency(QOrganizerRecurrenceRule::Weekly); rrule.setLimit(QDate(2010, 9, 2)); event2.setRecurrenceRule(rrule); QVERIFY(cm->saveItem(&event2)); items = cm->items(QDateTime(QDate(2010, 8, 8)), QDateTime(QDate(2010, 9, 3))); QCOMPARE(items.count(), 4); QCOMPARE(static_cast(items[0]).startDateTime(), QDateTime(QDate(2010, 8, 9), QTime(11, 0, 0))); QCOMPARE(static_cast(items[0]).endDateTime(), QDateTime(QDate(2010, 8, 11), QTime(11, 30, 0))); QCOMPARE(static_cast(items[1]).startDateTime(), QDateTime(QDate(2010, 8, 16), QTime(11, 0, 0))); QCOMPARE(static_cast(items[1]).endDateTime(), QDateTime(QDate(2010, 8, 18), QTime(11, 30, 0))); QCOMPARE(static_cast(items[2]).startDateTime(), QDateTime(QDate(2010, 8, 23), QTime(11, 0, 0))); QCOMPARE(static_cast(items[2]).endDateTime(), QDateTime(QDate(2010, 8, 25), QTime(11, 30, 0))); QCOMPARE(static_cast(items[3]).startDateTime(), QDateTime(QDate(2010, 8, 30), QTime(11, 0, 0))); QCOMPARE(static_cast(items[3]).endDateTime(), QDateTime(QDate(2010, 9, 1), QTime(11, 30, 0))); cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one QOrganizerTodo todo; QOrganizerRecurrenceRule rrule2; todo.setDisplayLabel("huge overlapping recurring todo"); todo.setStartDateTime(QDateTime(QDate(2010, 8, 9), QTime(11, 0, 0))); todo.setDueDateTime(QDateTime(QDate(2010, 8, 11), QTime(11, 30, 0))); rrule2.setFrequency(QOrganizerRecurrenceRule::Daily); rrule2.setLimit(4); todo.setRecurrenceRule(rrule2); QVERIFY(cm->saveItem(&todo)); items = cm->items(QDateTime(QDate(2010, 8, 8)), QDateTime(QDate(2010, 9, 3))); QCOMPARE(items.count(), 4); QCOMPARE(static_cast(items[0]).startDateTime(), QDateTime(QDate(2010, 8, 9), QTime(11, 0, 0))); QCOMPARE(static_cast(items[0]).dueDateTime(), QDateTime(QDate(2010, 8, 11), QTime(11, 30, 0))); QCOMPARE(static_cast(items[1]).startDateTime(), QDateTime(QDate(2010, 8, 10), QTime(11, 0, 0))); QCOMPARE(static_cast(items[1]).dueDateTime(), QDateTime(QDate(2010, 8, 12), QTime(11, 30, 0))); QCOMPARE(static_cast(items[2]).startDateTime(), QDateTime(QDate(2010, 8, 11), QTime(11, 0, 0))); QCOMPARE(static_cast(items[2]).dueDateTime(), QDateTime(QDate(2010, 8, 13), QTime(11, 30, 0))); QCOMPARE(static_cast(items[3]).startDateTime(), QDateTime(QDate(2010, 8, 12), QTime(11, 0, 0))); QCOMPARE(static_cast(items[3]).dueDateTime(), QDateTime(QDate(2010, 8, 14), QTime(11, 30, 0))); } void tst_QOrganizerManager::incompleteTodoTime() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one QOrganizerTodo todo; QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Daily); rrule.setLimit(4); todo.setDisplayLabel("recurring todo without start date"); todo.setDueDateTime(QDateTime(QDate(2010, 8, 11), QTime(11, 30, 0))); todo.setRecurrenceRule(rrule); QVERIFY(cm->saveItem(&todo)); QList items = cm->items(QDateTime(QDate(2010, 8, 8)), QDateTime(QDate(2010, 9, 3))); QCOMPARE(items.count(), 4); QCOMPARE(static_cast(items[0]).dueDateTime(), QDateTime(QDate(2010, 8, 11), QTime(11, 30, 0))); QCOMPARE(static_cast(items[1]).dueDateTime(), QDateTime(QDate(2010, 8, 12), QTime(11, 30, 0))); QCOMPARE(static_cast(items[2]).dueDateTime(), QDateTime(QDate(2010, 8, 13), QTime(11, 30, 0))); QCOMPARE(static_cast(items[3]).dueDateTime(), QDateTime(QDate(2010, 8, 14), QTime(11, 30, 0))); // same test as previous, but with date limit cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one QOrganizerTodo todo2; todo2.setDisplayLabel("recurring todo without start date with date limit"); todo2.setDueDateTime(QDateTime(QDate(2010, 8, 11), QTime(11, 30, 0))); QOrganizerRecurrenceRule rrule2; rrule2.setFrequency(QOrganizerRecurrenceRule::Daily); rrule2.setLimit(QDate(2010, 8, 13)); todo2.setRecurrenceRule(rrule2); QVERIFY(cm->saveItem(&todo2)); items = cm->items(QDateTime(QDate(2010, 8, 8)), QDateTime(QDate(2010, 9, 3))); QCOMPARE(items.count(), 3); QCOMPARE(static_cast(items[0]).dueDateTime(), QDateTime(QDate(2010, 8, 11), QTime(11, 30, 0))); QCOMPARE(static_cast(items[1]).dueDateTime(), QDateTime(QDate(2010, 8, 12), QTime(11, 30, 0))); QCOMPARE(static_cast(items[2]).dueDateTime(), QDateTime(QDate(2010, 8, 13), QTime(11, 30, 0))); cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one QOrganizerTodo todo3; todo3.setDisplayLabel("recurring todo without start and due date"); todo3.setRecurrenceRule(rrule2); QVERIFY(cm->saveItem(&todo3)); items = cm->items(); QCOMPARE(items.count(), 0); items = cm->items(QDateTime(QDate(2010, 8, 8)), QDateTime(QDate(2010, 9, 3))); QCOMPARE(items.count(), 6); QCOMPARE(static_cast(items[0]).originalDate(), QDate(2010, 8, 8)); QCOMPARE(static_cast(items[1]).originalDate(), QDate(2010, 8, 9)); QCOMPARE(static_cast(items[2]).dueDateTime(), QDateTime()); QCOMPARE(static_cast(items[3]).dueDateTime(), QDateTime()); QCOMPARE(static_cast(items[4]).originalDate(), QDate(2010, 8, 12)); QCOMPARE(static_cast(items[5]).originalDate(), QDate(2010, 8, 13)); cm->removeItem(todo3.id()); } void tst_QOrganizerManager::recurrence() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); QOrganizerEvent event; event.setDisplayLabel("recurrent event"); event.setStartDateTime(QDateTime(QDate(2012, 8, 9), QTime(11, 0, 0))); event.setEndDateTime(QDateTime(QDate(2012, 8, 9), QTime(11, 30, 0))); // first, test count limiting. QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Daily); rrule.setLimit(3); event.setRecurrenceRule(rrule); QVERIFY(cm->saveItem(&event)); { // Fetch all events with occurrences QList items = cm->items(QDateTime(QDate(2012, 8, 9)), QDateTime(QDate(2012, 8, 12), QTime(23,59,59))); QCOMPARE(items.count(), 3); // Fetch events for the first day items = cm->items(QDateTime(QDate(2012, 8, 9), QTime(0,0,0)), QDateTime(QDate(2012, 8, 9), QTime(23,59,59))); QCOMPARE(items.count(), 1); // Fetch events for the second day items = cm->items(QDateTime(QDate(2012, 8, 10), QTime(0,0,0)), QDateTime(QDate(2012, 8, 10), QTime(23,59,59))); QCOMPARE(items.count(), 1); // Create an exception on the second day QOrganizerEventOccurrence ex = static_cast(items.at(0)); ex.setStartDateTime(QDateTime(QDate(2012, 8, 10), QTime(10, 30, 0))); QVERIFY(cm->saveItem(&ex)); // Fetch again the events for the second day items = cm->items(QDateTime(QDate(2012, 8, 10), QTime(0,0,0)), QDateTime(QDate(2012, 8, 10), QTime(23,59,59))); QCOMPARE(items.count(), 1); QOrganizerItem item = items.at(0); QVERIFY(!item.id().isNull()); QVERIFY(item.type() == QOrganizerItemType::TypeEventOccurrence); // Add a normal event to the first day QOrganizerEvent event2; event2.setDisplayLabel("event"); event2.setStartDateTime(QDateTime(QDate(2012, 8, 9), QTime(15, 0, 0))); event2.setEndDateTime(QDateTime(QDate(2012, 8, 9), QTime(16, 0, 0))); QVERIFY(cm->saveItem(&event2)); // Fetch the whole period again items = cm->items(QDateTime(QDate(2012, 8, 9)), QDateTime(QDate(2012, 8, 12), QTime(23,59,59))); QCOMPARE(items.count(), 4); foreach(QOrganizerItem item, items) { // check if the item is the recurrence exception if (item.id() == ex.id()) { QOrganizerEventOccurrence exc = static_cast(item); QCOMPARE(exc.guid(), ex.guid()); QCOMPARE(exc.startDateTime(), ex.startDateTime()); QCOMPARE(exc.endDateTime(), ex.endDateTime()); } else if (item.id() == event2.id()) { // check if the item is the normal event QOrganizerEvent ev = static_cast(item); QCOMPARE(ev.guid(), event2.guid()); QCOMPARE(ev.startDateTime(), event2.startDateTime()); QCOMPARE(ev.endDateTime(), event2.endDateTime()); } else { // item must be event occurrence type and has to be a generated one QVERIFY(item.type() == QOrganizerItemType::TypeEventOccurrence); QVERIFY(item.id().isNull()); } } // Fetch events on a day where the recurrence is no longer valid items = cm->items(QDateTime(QDate(2012, 8, 12), QTime(0,0,0)), QDateTime(QDate(2012, 8, 12), QTime(23,59,59))); QCOMPARE(items.count(), 0); } //test for unlimited count limit //for bug:MOBILITY-2125 cm->removeItems(cm->itemIds()); event.setId(QOrganizerItemId()); rrule.setLimit(INT_MAX); rrule.setFrequency(QOrganizerRecurrenceRule::Weekly); rrule.setInterval(4); rrule.setDaysOfWeek(QSet() << Qt::Friday); event.setEndDateTime(QDateTime(QDate(2013, 8, 9), QTime(11, 30, 0))); event.setRecurrenceRule(rrule); QVERIFY(cm->saveItem(&event)); { // Fetch all events with occurrences QList items = cm->items(QDateTime(QDate(2012, 8, 9)), QDateTime(QDate(2013, 8, 12), QTime(23,59,59))); QVERIFY(items.count() > 1); } // second, test date limit. The results should be the same as the count limit, if the limit date is the 11th. cm->removeItems(cm->itemIds()); // empty the calendar to prevent the previous test from interfering this one QOrganizerRecurrenceRule rrule2; rrule2.setFrequency(QOrganizerRecurrenceRule::Daily); rrule2.setLimit(QDate(2012, 8, 11)); event.setRecurrenceRule(rrule2); event.setId(QOrganizerItemId()); QVERIFY(cm->saveItem(&event)); { // Fetch all events with occurrences QList items = cm->items(QDateTime(QDate(2012, 8, 9)), QDateTime(QDate(2012, 8, 12), QTime(23,59,59))); QCOMPARE(items.count(), 3); // Fetch events for the first day items = cm->items(QDateTime(QDate(2012, 8, 9), QTime(0,0,0)), QDateTime(QDate(2012, 8, 9), QTime(23,59,59))); QCOMPARE(items.count(), 1); // Fetch events for the second day items = cm->items(QDateTime(QDate(2012, 8, 10), QTime(0,0,0)), QDateTime(QDate(2012, 8, 10), QTime(23,59,59))); QCOMPARE(items.count(), 1); // Create an exception on the second day QOrganizerEventOccurrence ex = static_cast(items.at(0)); ex.setStartDateTime(QDateTime(QDate(2012, 8, 10), QTime(10, 30, 0))); QVERIFY(cm->saveItem(&ex)); // Fetch again the events for the second day items = cm->items(QDateTime(QDate(2012, 8, 10), QTime(0,0,0)), QDateTime(QDate(2012, 8, 10), QTime(23,59,59))); QCOMPARE(items.count(), 1); QOrganizerItem item = items.at(0); QVERIFY(!item.id().isNull()); QVERIFY(item.type() == QOrganizerItemType::TypeEventOccurrence); // Add a normal event to the first day QOrganizerEvent event2; event2.setDisplayLabel("event"); event2.setStartDateTime(QDateTime(QDate(2012, 8, 9), QTime(15, 0, 0))); event2.setEndDateTime(QDateTime(QDate(2012, 8, 9), QTime(16, 0, 0))); QVERIFY(cm->saveItem(&event2)); // Fetch the whole period again items = cm->items(QDateTime(QDate(2012, 8, 9)), QDateTime(QDate(2012, 8, 12), QTime(23,59,59))); QCOMPARE(items.count(), 4); foreach(QOrganizerItem item, items) { // check if the item is the recurrence exception if (item.id() == ex.id()) { QOrganizerEventOccurrence exc = static_cast(item); QCOMPARE(exc.guid(), ex.guid()); QCOMPARE(exc.startDateTime(), ex.startDateTime()); QCOMPARE(exc.endDateTime(), ex.endDateTime()); } else if (item.id() == event2.id()) { // check if the item is the normal event QOrganizerEvent ev = static_cast(item); QCOMPARE(ev.guid(), event2.guid()); QCOMPARE(ev.startDateTime(), event2.startDateTime()); QCOMPARE(ev.endDateTime(), event2.endDateTime()); } else { // item must be event occurrence type and has to be a generated one QVERIFY(item.type() == QOrganizerItemType::TypeEventOccurrence); QVERIFY(item.id().isNull()); } } } } void tst_QOrganizerManager::idComparison() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); // Can we run this test? if (!cm->supportedItemTypes().contains(QOrganizerItemType::TypeJournal)) { QSKIP("Backend not compatible with this test"); // TODO: The test should be refactored so it could run on all platforms } // delete all collections in the database so that we know there can be no interference from previous test runs. QList allCollections = cm->collections(); for (int i = 0; i < allCollections.size(); ++i) { QOrganizerCollectionId currentId = allCollections.at(i).id(); if (currentId != cm->defaultCollection().id()) { cm->removeCollection(currentId); } } // step one: make a few items and collections to save (and harvest their ids) QOrganizerEvent e1; e1.setDescription("test event one"); e1.setDisplayLabel("event one"); e1.setStartDateTime(QDateTime::currentDateTime()); e1.setEndDateTime(QDateTime::currentDateTime().addSecs(3600)); QOrganizerEvent e2; e2.setDescription("test event two"); e2.setDisplayLabel("event two"); e2.setStartDateTime(QDateTime::currentDateTime().addDays(1)); e2.setEndDateTime(QDateTime::currentDateTime().addDays(1).addSecs(1800)); QOrganizerTodo t1; QOrganizerRecurrenceRule r1; r1.setFrequency(QOrganizerRecurrenceRule::Weekly); t1.setDisplayLabel("todo one"); t1.setDescription("test todo one"); t1.setDueDateTime(QDateTime::currentDateTime().addDays(5)); t1.setRecurrenceRule(r1); QOrganizerCollection c1; c1.setMetaData(QOrganizerCollection::KeyName, "IdComparisonTest"); // step two: save and harvest the ids QVERIFY(cm->saveItem(&e1)); QVERIFY(cm->saveItem(&e2)); QVERIFY(cm->saveItem(&t1)); QVERIFY(cm->saveCollection(&c1)); QOrganizerItemId e1id = e1.id(); QOrganizerItemId e2id = e2.id(); QOrganizerItemId t1id = t1.id(); QOrganizerCollectionId c1id = c1.id(); // step three: make some basic ids as controlled data for our unit test QOrganizerItemId biid1 = makeItemId(5); QOrganizerItemId biid2 = makeItemId(12); QOrganizerCollectionId bcid1 = makeCollectionId(5); QOrganizerCollectionId bcid2 = makeCollectionId(17); QOrganizerCollectionId bcid3 = makeCollectionId(15); // finally: do the testing. QVERIFY(biid1 != biid2); QVERIFY(bcid1 != bcid2); QVERIFY(e1id != e2id); QVERIFY(e1id != t1id); QList idList; idList << e1id << t1id << biid1; QVERIFY(!idList.contains(biid2)); QVERIFY(!idList.contains(e2id)); QVERIFY(idList.contains(e1id)); QVERIFY(idList.contains(t1id)); QVERIFY(idList.contains(biid1)); QVERIFY(bcid1 < bcid2); QVERIFY(bcid3 < bcid2); QVERIFY(((e1id < e2id) || (e2id < e1id)) && (e1id != e2id)); // now we do some tests which might be unstable QVERIFY(bcid1 < c1id); // collectionIds: the first comparison should be manager uri, and bcid manager uri is early in the alphabet. QVERIFY(!(c1id < bcid1)); // collectionIds: ensure that less-than is consistent no matter which is on LHS or RHS. QVERIFY(biid2 < e1id); // itemIds: first comparison should be manager uri, and biid manager uri is early in the alphabet. QVERIFY(!(e1id < biid2)); // itemIds: ensure that less-than is consistent no matter which is on LHS or RHS. // null testing QOrganizerItemId n1; QOrganizerItemId n2; QVERIFY(n1 == n2); // both null means they're equal QVERIFY(!(n1 < n2)); // not less than QVERIFY(!(n2 < n1)); // and not less than with LHS/RHS swapped. QVERIFY(n1 < biid1); // null id is always less than any other id QVERIFY(!(biid1 < n1)); QVERIFY(n2 < e2id); QVERIFY(!(e2id < n2)); QVERIFY(!idList.contains(n2)); idList << n1; QVERIFY(idList.contains(n2)); // any two null ids are equal, so .contains should work. QMap testMap; testMap.insert(e1id, 1); testMap.insert(biid1, 12); testMap.insert(biid2, 11); testMap.insert(t1id, 6); testMap.insert(n1, 12); QCOMPARE(testMap.value(e1id), 1); QCOMPARE(testMap.value(n2), 12); // again, n1 == n2. QCOMPARE(testMap.value(biid1), 12); QCOMPARE(testMap.value(biid2), 11); QCOMPARE(testMap.value(t1id), 6); QCOMPARE(testMap.value(QOrganizerItemId()), 12); // again, n1 == null QVERIFY(testMap.size() == 5); testMap.remove(QOrganizerItemId()); QVERIFY(testMap.size() == 4); QVERIFY(testMap.value(QOrganizerItemId()) != 12); // removed this entry. } void tst_QOrganizerManager::emptyItemManipulation() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); QOrganizerItem i; QOrganizerEvent e; QOrganizerTodo t; // attempt to save an empty item if (cm->saveItem(&i)) { // if the backend allowed us to save it, it should definitely allow us to remove it again. QVERIFY(cm->removeItem(i.id())); } else { // if the backend didn't allow us to save it, there should be nothing to remove. QVERIFY(!cm->removeItem(i.id())); } // attempt to save an empty event. if (cm->saveItem(&e)) { // if the backend allowed us to save it, it should definitely allow us to remove it again. QVERIFY(cm->removeItem(e.id())); } else { // if the backend didn't allow us to save it, there should be nothing to remove. QVERIFY(!cm->removeItem(e.id())); } // attempt to save an empty event. if (cm->saveItem(&t)) { // if the backend allowed us to save it, it should definitely allow us to remove it again. QVERIFY(cm->removeItem(t.id())); } else { // if the backend didn't allow us to save it, there should be nothing to remove. QVERIFY(!cm->removeItem(t.id())); } // now attempt to remove some invalid ids. QOrganizerItemId invalidId; QVERIFY(!cm->removeItem(invalidId)); // null id invalidId = makeItemId(50); QVERIFY(!cm->removeItem(invalidId)); // id from different manager } void tst_QOrganizerManager::partialSave() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); if (cm->managerName() == QStringLiteral("jsondb")) QSKIP("Partial save is not supported by JsonDb!"); QList items; QOrganizerEvent event = QOrganizerEvent(); event.setDisplayLabel("One"); event.setStartDateTime(QDateTime(QDate(2010, 12, 25), QTime(1, 0, 0))); event.setEndDateTime(QDateTime(QDate(2010, 12, 25), QTime(1, 30, 0))); event.setDescription("One description"); items.append(event); event = QOrganizerEvent(); event.setDisplayLabel("Two"); event.setStartDateTime(QDateTime(QDate(2010, 12, 25), QTime(2, 0, 0))); event.setEndDateTime(QDateTime(QDate(2010, 12, 25), QTime(2, 30, 0))); event.setDescription("Two description"); items.append(event); event = QOrganizerEvent(); event.setDisplayLabel("Three"); event.setStartDateTime(QDateTime(QDate(2010, 12, 25), QTime(3, 0, 0))); event.setEndDateTime(QDateTime(QDate(2010, 12, 25), QTime(3, 30, 0))); items.append(event); event = QOrganizerEvent(); event.setDisplayLabel("Four"); event.setStartDateTime(QDateTime(QDate(2010, 12, 25), QTime(4, 0, 0))); event.setEndDateTime(QDateTime(QDate(2010, 12, 25), QTime(4, 30, 0))); items.append(event); // First save these items QVERIFY(cm->saveItems(&items)); QList originalItems = items; items[0].setDescription("One changed description"); // 0) empty mask == full save QVERIFY(cm->saveItems(&items)); // That should have updated everything QOrganizerItem a = cm->item(originalItems[0].id()); QVERIFY(a.description() == "One changed description"); // 1) Change the description for b, mask it out items[1].setDescription("Two changed description"); QVERIFY(cm->saveItems(&items, QList() << QOrganizerItemDetail::TypeEventTime)); QVERIFY(cm->errorMap().isEmpty()); QOrganizerItem b = cm->item(originalItems[1].id()); QCOMPARE(b.description(), QString("Two description")); // 2) save a modified detail in the mask items[1].setDescription("Two changed description"); QVERIFY(cm->saveItems(&items, QList() << QOrganizerItemDetail::TypeDescription)); QVERIFY(cm->errorMap().isEmpty()); b = cm->item(originalItems[1].id()); QCOMPARE(b.description(), QString("Two changed description")); // 3) Remove a description QOrganizerItemDescription desc = items[1].detail(QOrganizerItemDetail::TypeDescription); QVERIFY(items[1].removeDetail(&desc)); // Mask it out, so it shouldn't work. QVERIFY(cm->saveItems(&items, QList() << QOrganizerItemDetail::TypeEventTime)); QVERIFY(cm->errorMap().isEmpty()); b = cm->item(originalItems[1].id()); QCOMPARE(b.details(QOrganizerItemDetail::TypeDescription).count(), 1); // Now include it in the mask QVERIFY(cm->saveItems(&items, QList() << QOrganizerItemDetail::TypeDescription)); QVERIFY(cm->errorMap().isEmpty()); b = cm->item(originalItems[1].id()); QCOMPARE(b.details(QOrganizerItemDetail::TypeDescription).count(), 0); // 4 - New item, no details in the mask QOrganizerItem newItem = originalItems[3]; newItem.setId(QOrganizerItemId()); items.append(newItem); // this is items[4] QVERIFY(cm->saveItems(&items, QList() << QOrganizerItemDetail::TypeTag)); QVERIFY(cm->errorMap().isEmpty()); QVERIFY(!items[4].id().isNull()); // Saved b = cm->item(items[4].id()); QCOMPARE(b.details(QOrganizerItemDetail::TypeDisplayLabel).count(), 0); // not saved QCOMPARE(b.details(QOrganizerItemDetail::TypeEventTime).count(), 0); // not saved // 5 - New item, some details in the mask newItem = originalItems[2]; newItem.setId(QOrganizerItemId()); items.append(newItem); // this is items[5] QVERIFY(cm->saveItems(&items, QList() << QOrganizerItemDetail::TypeDisplayLabel)); QVERIFY(cm->errorMap().isEmpty()); QVERIFY(!items[5].id().isNull()); // Saved b = cm->item(items[5].id()); QCOMPARE(b.details(QOrganizerItemDetail::TypeDisplayLabel).count(), 1); QCOMPARE(b.details(QOrganizerItemDetail::TypeEventTime).count(), 0); // not saved // 6 Have a non existing item in the middle followed by a save error cm->removeItem(items[4].id()); QOrganizerItemDetail badDetail(QOrganizerItemDetail::TypeUndefined); badDetail.setValue(101, "BadValue"); items[5].saveDetail(&badDetail); QVERIFY(!cm->saveItems(&items, QList() << QOrganizerItemDetail::TypeUndefined)); QMap errorMap = cm->errorMap(); // QCOMPARE(errorMap.count(), 2); QCOMPARE(errorMap[4], QOrganizerManager::DoesNotExistError); // QCOMPARE(errorMap[5], QOrganizerManager::InvalidDetailError); } void tst_QOrganizerManager::dateRange() { QFETCH(QOrganizerItem, item); QFETCH(QDateTime, startPeriod); QFETCH(QDateTime, endPeriod); QFETCH(bool, result); QCOMPARE(QOrganizerManagerEngine::isItemBetweenDates(item, startPeriod, endPeriod), result); } void tst_QOrganizerManager::dateRange_data() { QTest::addColumn("item"); QTest::addColumn("startPeriod"); QTest::addColumn("endPeriod"); QTest::addColumn("result"); QOrganizerEvent ev; ev.setStartDateTime(QDateTime(QDate(2010, 10, 10), QTime(10,0,0))); ev.setEndDateTime(QDateTime(QDate(2010, 10, 12), QTime(11,0,0))); QTest::newRow("event - month") << QOrganizerItem(ev) << QDateTime(QDate(2010,10,1)) << QDateTime(QDate(2010,10,31)) << true; QTest::newRow("event - first day") << QOrganizerItem(ev) << QDateTime(QDate(2010,10,10)) << QDateTime(QDate(2010,10,10), QTime(23,59,59)) << true; QTest::newRow("event - second day") << QOrganizerItem(ev) << QDateTime(QDate(2010,10,11)) << QDateTime(QDate(2010,10,11), QTime(23,59,59)) << true; QTest::newRow("event - last day") << QOrganizerItem(ev) << QDateTime(QDate(2010,10,12)) << QDateTime(QDate(2010,10,12), QTime(23,59,59)) << true; QTest::newRow("event - undefined period") << QOrganizerItem(ev) << QDateTime() << QDateTime() << true; QTest::newRow("event - undefined start") << QOrganizerItem(ev) << QDateTime() << QDateTime(QDate(2010,10,11)) << true; QTest::newRow("event - undefined end") << QOrganizerItem(ev) << QDateTime(QDate(2010,10,11)) << QDateTime() << true; QTest::newRow("event - before") << QOrganizerItem(ev) << QDateTime(QDate(2010,10,8)) << QDateTime(QDate(2010,10,9)) << false; QTest::newRow("event - after") << QOrganizerItem(ev) << QDateTime(QDate(2010,10,13)) << QDateTime(QDate(2010,10,14)) << false; QOrganizerTodo todo; todo.setStartDateTime(QDateTime(QDate(2010, 10, 10), QTime(10,0,0))); todo.setDueDateTime(QDateTime(QDate(2010, 10, 12), QTime(11,0,0))); QTest::newRow("todo - month") << QOrganizerItem(todo) << QDateTime(QDate(2010,10,1)) << QDateTime(QDate(2010,10,31)) << true; QTest::newRow("todo - first day") << QOrganizerItem(todo) << QDateTime(QDate(2010,10,10)) << QDateTime(QDate(2010,10,10), QTime(23,59,59)) << true; QTest::newRow("todo - second day") << QOrganizerItem(todo) << QDateTime(QDate(2010,10,11)) << QDateTime(QDate(2010,10,11), QTime(23,59,59)) << true; QTest::newRow("todo - last day") << QOrganizerItem(todo) << QDateTime(QDate(2010,10,12)) << QDateTime(QDate(2010,10,12), QTime(23,59,59)) << true; QTest::newRow("todo - undefined period") << QOrganizerItem(todo) << QDateTime() << QDateTime() << true; QTest::newRow("todo - undefined start") << QOrganizerItem(todo) << QDateTime() << QDateTime(QDate(2010,10,11)) << true; QTest::newRow("todo - undefined end") << QOrganizerItem(todo) << QDateTime(QDate(2010,10,11)) << QDateTime() << true; QTest::newRow("todo - before") << QOrganizerItem(todo) << QDateTime(QDate(2010,10,8)) << QDateTime(QDate(2010,10,9)) << false; QTest::newRow("todo - after") << QOrganizerItem(todo) << QDateTime(QDate(2010,10,13)) << QDateTime(QDate(2010,10,14)) << false; todo.setDueDateTime(QDateTime()); QTest::newRow("todo missing due date - undefined start") << QOrganizerItem(todo) << QDateTime() << QDateTime(QDate(2010,10,11)) << true; QTest::newRow("todo missing due date - undefined end") << QOrganizerItem(todo) << QDateTime(QDate(2010,10,10)) << QDateTime() << true; todo.setStartDateTime(QDateTime()); todo.setDueDateTime(QDateTime(QDate(2010, 10, 12), QTime(11,0,0))); QTest::newRow("todo missing start date - undefined start") << QOrganizerItem(todo) << QDateTime() << QDateTime(QDate(2010,10,13)) << true; QTest::newRow("todo missing start date - undefined end") << QOrganizerItem(todo) << QDateTime(QDate(2010,10,11)) << QDateTime() << true; QOrganizerJournal journal; journal.setDateTime(QDateTime(QDate(2010, 10, 10), QTime(10,0,0))); QTest::newRow("journal - month") << QOrganizerItem(journal) << QDateTime(QDate(2010,10,1)) << QDateTime(QDate(2010,10,31)) << true; QTest::newRow("journal - first day") << QOrganizerItem(journal) << QDateTime(QDate(2010,10,10)) << QDateTime(QDate(2010,10,10), QTime(23,59,59)) << true; QTest::newRow("journal - second day") << QOrganizerItem(journal) << QDateTime(QDate(2010,10,11)) << QDateTime(QDate(2010,10,11), QTime(23,59,59)) << false; QTest::newRow("journal - undefined period") << QOrganizerItem(journal) << QDateTime() << QDateTime() << true; QTest::newRow("journal - undefined start") << QOrganizerItem(journal) << QDateTime() << QDateTime(QDate(2010,10,11)) << true; QTest::newRow("journal - undefined end") << QOrganizerItem(journal) << QDateTime(QDate(2010,10,10)) << QDateTime() << true; QTest::newRow("journal - before") << QOrganizerItem(journal) << QDateTime(QDate(2010,10,8)) << QDateTime(QDate(2010,10,9)) << false; QTest::newRow("journal - after") << QOrganizerItem(journal) << QDateTime(QDate(2010,10,13)) << QDateTime(QDate(2010,10,14)) << false; QOrganizerNote note; QTest::newRow("note") << QOrganizerItem(note) << QDateTime(QDate(2010,10,1)) << QDateTime() << false; QTest::newRow("note - undefined period") << QOrganizerItem(note) << QDateTime() << QDateTime() << true; } QList tst_QOrganizerManager::removeAllDefaultDetails(const QList& details) { QList newlist; foreach (const QOrganizerItemDetail d, details) { if (d.type() != QOrganizerItemDetail::TypeDisplayLabel && d.type() != QOrganizerItemDetail::TypeItemType && d.type() != QOrganizerItemDetail::TypeTimestamp) { newlist << d; } } return newlist; } void tst_QOrganizerManager::detailOrders() { QFETCH(QString, uri); QScopedPointer cm(QOrganizerManager::fromUri(uri)); QOrganizerEvent a; // comments QOrganizerItemComment comment1, comment2, comment3; comment1.setComment("11111111"); comment2.setComment("22222222"); comment3.setComment("33333333"); a.saveDetail(&comment1); a.saveDetail(&comment2); a.saveDetail(&comment3); QVERIFY(cm->saveItem(&a)); a = cm->item(a.id()); QList details = a.details(QOrganizerItemDetail::TypeComment); QVERIFY(details.count() == 3); comment2 = a.details(QOrganizerItemDetail::TypeComment).at(1); QVERIFY(a.removeDetail(&comment2)); QVERIFY(cm->saveItem(&a)); a = cm->item(a.id()); details = a.details(QOrganizerItemDetail::TypeComment); QVERIFY(details.count() == 2); a.saveDetail(&comment2); QVERIFY(cm->saveItem(&a)); a = cm->item(a.id()); details = a.details(QOrganizerItemDetail::TypeComment); QVERIFY(details.count() == 3); //addresses { QOrganizerItemLocation address1, address2, address3; address1.setLabel("Brandl St"); address3 = address2 = address1; a.saveDetail(&address1); a.saveDetail(&address2); a.saveDetail(&address3); QVERIFY(cm->saveItem(&a)); a = cm->item(a.id()); QList details = a.details(QOrganizerItemDetail::TypeLocation); QVERIFY(details.count() == 1); // 1 location - they're unique // Detail keys for the moment are not persistent through an item save / fetch address3 = details.at(0); QVERIFY(a.removeDetail(&address3)); // remove the most recent. QVERIFY(cm->saveItem(&a)); a = cm->item(a.id()); details = a.details(QOrganizerItemDetail::TypeLocation); QVERIFY(details.count() == 0); // unique, remove one means none left. a.saveDetail(&address2); QVERIFY(cm->saveItem(&a)); a = cm->item(a.id()); details = a.details(QOrganizerItemDetail::TypeLocation); QVERIFY(details.count() == 1); // add one back. } } void tst_QOrganizerManager::itemType() { // XXX TODO! } void tst_QOrganizerManager::collections() { // XXX TODO: break test into smaller sub-tests (per operation). QFETCH(QString, uri); QScopedPointer oim(QOrganizerManager::fromUri(uri)); // delete all collections in the database so that we know there can be no interference from previous test runs. QList allCollections = oim->collections(); for (int i = 0; i < allCollections.size(); ++i) { QOrganizerCollectionId currentId = allCollections.at(i).id(); if (currentId != oim->defaultCollection().id()) { oim->removeCollection(currentId); } } QOrganizerCollection c1, c2, c3, sc1; c1.setMetaData(QOrganizerCollection::KeyName, "Test One"); c1.setMetaData(QOrganizerCollection::KeyDescription, "This collection is for testing purposes."); c1.setExtendedMetaData("key", "value"); c2.setMetaData(QOrganizerCollection::KeyName, "Test Two"); c2.setMetaData(QOrganizerCollection::KeyColor, QColor(Qt::blue)); // c3 doesn't have any meta-data, just an id. QOrganizerEvent i1, i2, i3, i4, i5; i1.setDisplayLabel("one"); i2.setDisplayLabel("two"); i2.setDescription("this is the second item"); i3.setDisplayLabel("three"); i4.setDisplayLabel("four"); i4.setGuid(QUuid::createUuid().toString()); i5.setDisplayLabel("five"); // i5.setLocation("test location address"); // modify default collection if (oim->managerName() != "memory") // modifying default collection is not allowed in memory engine { int initialCollectionCount = oim->collections().size(); QOrganizerCollection defaultCollection = oim->defaultCollection(); QOrganizerCollectionId defaultCollectionId = oim->defaultCollection().id(); defaultCollection.setMetaData(QOrganizerCollection::KeyName, "NewName"); QCOMPARE(defaultCollection.id(), defaultCollectionId); QVERIFY(oim->saveCollection(&defaultCollection)); int finalCollectionCount = oim->collections().size(); QCOMPARE(finalCollectionCount, initialCollectionCount); QCOMPARE(oim->defaultCollection().metaData(QOrganizerCollection::KeyName).toString(), QString("NewName")); QCOMPARE(defaultCollection.id(), defaultCollectionId); } // first test { // save a collection QVERIFY(c1.id().isNull()); // should have a null id to start with. QVERIFY(oim->saveCollection(&c1)); QVERIFY(!c1.id().isNull()); // should have been set by the save operation QVERIFY(oim->collections().contains(c1)); sc1 = oim->collection(c1.id()); QVERIFY(sc1.metaData(QOrganizerCollection::KeyName) == "Test One"); QVERIFY(sc1.metaData(QOrganizerCollection::KeyDescription) == "This collection is for testing purposes."); QVERIFY(sc1.extendedMetaData("key") == "value"); // save an item in that collection QOrganizerItemCollectionFilter fil; fil.setCollectionId(c1.id()); i1.setCollectionId(c1.id()); QVERIFY(oim->saveItem(&i1)); QVERIFY(i1.collectionId() == c1.id()); QList c1Items = oim->items(QDateTime(), QDateTime(), fil); int itemIndex = -1; for (int i = 0; i < c1Items.count(); i++) { if (c1Items.at(i).id() == i1.id()) { itemIndex = i; break; } } QVERIFY(itemIndex >= 0); QVERIFY(oim->items(QDateTime(), QDateTime(), fil).contains(i1) || isSuperset(c1Items.at(itemIndex), i1)); fil.setCollectionId(oim->defaultCollection().id()); QVERIFY(!oim->items(QDateTime(), QDateTime(), fil).contains(i1)); // it should not be in the default collection. } // second test { // save multiple collections. // XXX TODO: batch save for collections? int originalColCount = oim->collections().count(); QVERIFY(oim->saveCollection(&c2)); QVERIFY(oim->saveCollection(&c3)); QVERIFY(oim->collections().count() == (originalColCount + 2)); // save i5 in c3 as a canary value. i5.setCollectionId(c3.id()); // save multiple items in collection c2 QList saveList; i2.setCollectionId(c2.id()); i3.setCollectionId(c2.id()); i4.setCollectionId(c2.id()); saveList << i2 << i3 << i4 << i5; int originalItemCount = oim->items().count(); QVERIFY(oim->saveItems(&saveList)); i2 = saveList.at(0); // update from save list because manager might have added details / set ids etc. i3 = saveList.at(1); i4 = saveList.at(2); i5 = saveList.at(3); QList fetchedItems = oim->items(); QCOMPARE(fetchedItems.count(), originalItemCount + 4); //fails here QVERIFY(fetchedItems.contains(i2)); // these three should have been added QVERIFY(fetchedItems.contains(i3)); QVERIFY(fetchedItems.contains(i4)); QVERIFY(fetchedItems.contains(i5)); // i5 should not have been removed. // update a collection shouldn't remove its items. c2.setMetaData(QOrganizerCollection::KeyName, "Test Two Updated"); QVERIFY(oim->saveCollection(&c2)); fetchedItems = oim->items(); QVERIFY(fetchedItems.contains(i2)); // no items should have been removed QVERIFY(fetchedItems.contains(i3)); // nor should they have changed collection. QVERIFY(fetchedItems.contains(i4)); QVERIFY(fetchedItems.contains(i5)); // exceptions must be saved in the same collection as their parent. QOrganizerEvent recurringEvent; recurringEvent.setDescription("A recurring test event parent."); // recurringEvent.setLocation("Some Location"); recurringEvent.setStartDateTime(QDateTime(QDate(2010,10,5), QTime(10,30))); recurringEvent.setEndDateTime(QDateTime(QDate(2010,10,5), QTime(11,30))); QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Weekly); rrule.setLimit(5); // count limited. recurringEvent.setRecurrenceRule(rrule); recurringEvent.setCollectionId(c2.id()); QVERIFY(oim->saveItem(&recurringEvent)); recurringEvent = oim->item(recurringEvent.id()); // reload it. QVERIFY(recurringEvent.collectionId() == c2.id()); QList occ(oim->itemOccurrences(recurringEvent, QDateTime(), QDateTime())); QVERIFY(occ.size() == 5); QOrganizerEventOccurrence someException = occ.at(2); // there should be five, so this shouldn't segfault. // someException.setLocation("Other Location"); someException.setCollectionId(c3.id()); // different to parent. QVERIFY(!oim->saveItem(&someException)); // shouldn't work. someException.setCollectionId(c2.id()); // same as parent. QVERIFY(oim->saveItem(&someException)); // should work. // remove a collection, removes its items. QVERIFY(oim->removeCollection(c2.id())); fetchedItems = oim->items(); QCOMPARE(fetchedItems.count(), originalItemCount + 1); // i5 should remain, i2->i4 should be removed. QVERIFY(!fetchedItems.contains(i2)); // these three should have been removed QVERIFY(!fetchedItems.contains(i3)); QVERIFY(!fetchedItems.contains(i4)); QVERIFY(!fetchedItems.contains(recurringEvent)); // the parent QVERIFY(!fetchedItems.contains(someException)); // and exceptions too. QVERIFY(fetchedItems.contains(i5)); // i5 should not have been removed. // attempt to save an item in a non-existent collection should fail. i2.setId(QOrganizerItemId()); // reset Id so save can succeed... i2.setCollectionId(c2.id()); QVERIFY(!oim->saveItem(&i2)); fetchedItems = oim->items(); QVERIFY(!fetchedItems.contains(i2)); // shouldn't have been added. QVERIFY(fetchedItems.contains(i5)); // i5 should not have been removed. } } void tst_QOrganizerManager::testReminder() { QFETCH(QString, uri); QScopedPointer oim(QOrganizerManager::fromUri(uri)); /*audible reminder test*/ QOrganizerItemAudibleReminder audioReminder; QOrganizerEvent oi; oi.setDisplayLabel("test reminder"); audioReminder.setRepetition(3, 100); QVERIFY(oi.saveDetail(&audioReminder)); oi.setStartDateTime(QDateTime::currentDateTime()); QVERIFY(oim->saveItem(&oi)); QList fetchedItems = oim->items(); //After adding StartDateTime detail the item compare function does not work. Need fix later //QVERIFY(fetchedItems.contains(oi)); foreach (QOrganizerItem item, fetchedItems) { if (oi.id() == item.id()) QVERIFY(item.detail(QOrganizerItemDetail::TypeAudibleReminder) == audioReminder); } //Test SecondsBeforeStart properties audioReminder.setSecondsBeforeStart(30); // reminder, 30 seconds before the item is due to start. QVERIFY(oi.saveDetail(&audioReminder)); QVERIFY(oim->saveItem (&oi)); fetchedItems = oim->items(); //After adding StartDateTime detail the item compare function does not work. Need fix later //QVERIFY(fetchedItems.contains(oi)); foreach (QOrganizerItem item, fetchedItems) { if (oi.id() == item.id()) QVERIFY(item.detail(QOrganizerItemDetail::TypeAudibleReminder) == audioReminder); } // update audioReminder.setSecondsBeforeStart(300); audioReminder.setDataUrl(QUrl("http://www.test.com")); QVERIFY(audioReminder.dataUrl() == QUrl("http://www.test.com")); QVERIFY(oi.detail(QOrganizerItemDetail::TypeAudibleReminder) != audioReminder); QVERIFY(oi.saveDetail(&audioReminder)); QVERIFY(oi.details(QOrganizerItemDetail::TypeAudibleReminder).size() == 1); // should update, not add another QVERIFY(oi.detail(QOrganizerItemDetail::TypeAudibleReminder) == audioReminder); oim->saveItem (&oi); fetchedItems = oim->items(); // After adding StartDateTime detail the item compare function does not work. Need fix later //QVERIFY(fetchedItems.contains(oi)); foreach (QOrganizerItem item, fetchedItems) { if (oi.id() == item.id()) QVERIFY(item.detail(QOrganizerItemDetail::TypeAudibleReminder) == audioReminder); } // remove QVERIFY(oi.removeDetail(&audioReminder)); QVERIFY(oi.details(QOrganizerItemDetail::TypeAudibleReminder).size() == 0); oim->saveItem (&oi); fetchedItems = oim->items(); // After adding StartDateTime detail the item compare function does not work. Need fix later //QVERIFY(fetchedItems.contains(oi)); foreach (QOrganizerItem item, fetchedItems) { if (oi.id() == item.id()) QVERIFY(item.detail(QOrganizerItemDetail::TypeAudibleReminder) == oi.detail(QOrganizerItemDetail::TypeAudibleReminder)); } if ("qtorganizer:jsondb:" == uri)// jsondb backend does not support email reminder and visual reminder return; /*Email reminder test*/ QOrganizerItemEmailReminder emailReminder; QOrganizerEvent emailEvent; QVERIFY(emailReminder.reminderType() == QOrganizerItemReminder::EmailReminder); emailReminder.setRepetition(3, 100); QVERIFY(emailEvent.saveDetail(&emailReminder)); QVERIFY(oim->saveItem (&emailEvent)); fetchedItems = oim->items(); QVERIFY(fetchedItems.contains(emailEvent)); emailReminder.setSecondsBeforeStart(30); // reminder, 30 seconds before the item is due to start. QVERIFY(emailReminder.secondsBeforeStart() == 30); QVERIFY(emailEvent.saveDetail(&emailReminder)); QVERIFY(oim->saveItem (&emailEvent)); fetchedItems = oim->items(); QVERIFY(fetchedItems.contains(emailEvent)); // update emailReminder.setSecondsBeforeStart(300); emailReminder.setContents("subject", "body", QVariantList()); emailReminder.setRecipients(QStringList() << "recipient" << "other recipient"); QVERIFY(emailReminder.subject() == QString("subject")); QVERIFY(emailReminder.body() == QString("body")); QVERIFY(emailReminder.attachments() == QVariantList()); QVERIFY(emailReminder.recipients() == (QStringList() << "recipient" << "other recipient")); QVERIFY(emailEvent.detail(QOrganizerItemDetail::TypeEmailReminder) != emailReminder); QVERIFY(emailEvent.saveDetail(&emailReminder)); oim->saveItem (&emailEvent); fetchedItems = oim->items(); // dumpOrganizerItem (emailEvent); // foreach (QOrganizerItem item, fetchedItems) { // if (emailEvent.id() == item.id()) // dumpOrganizerItem (item); // } QVERIFY(fetchedItems.contains(emailEvent)); // remove QVERIFY(emailEvent.removeDetail(&emailReminder)); QVERIFY(emailEvent.details(QOrganizerItemDetail::TypeEmailReminder).size() == 0); oim->saveItem (&emailEvent); fetchedItems = oim->items(); QVERIFY(fetchedItems.contains(emailEvent)); /*Visual reminder test*/ QOrganizerItemVisualReminder visualReminder; QOrganizerEvent visualEvent; QVERIFY(visualReminder.reminderType() == QOrganizerItemReminder::VisualReminder); visualReminder.setRepetition(3, 100); QVERIFY(visualEvent.saveDetail(&visualReminder)); QVERIFY(oim->saveItem (&visualEvent)); fetchedItems = oim->items(); QVERIFY(fetchedItems.contains(visualEvent)); visualReminder.setSecondsBeforeStart(30); // reminder, 30 seconds before the item is due to start. QVERIFY(visualReminder.secondsBeforeStart() == 30); QVERIFY(visualEvent.saveDetail(&visualReminder)); QVERIFY(oim->saveItem (&visualEvent)); fetchedItems = oim->items(); QVERIFY(fetchedItems.contains(visualEvent)); // update visualReminder.setSecondsBeforeStart(300); visualReminder.setMessage("test"); visualReminder.setDataUrl(QUrl("http://www.test.com")); QVERIFY(visualReminder.message() == QString("test")); QVERIFY(visualReminder.dataUrl() == QUrl("http://www.test.com")); QVERIFY(visualEvent.detail(QOrganizerItemDetail::TypeVisualReminder) != visualReminder); QVERIFY(visualEvent.saveDetail(&visualReminder)); oim->saveItem (&visualEvent); fetchedItems = oim->items(); QVERIFY(fetchedItems.contains(visualEvent)); // remove QVERIFY(visualEvent.removeDetail(&visualReminder)); QVERIFY(visualEvent.details(QOrganizerItemDetail::TypeVisualReminder).size() == 0); oim->saveItem (&visualEvent); fetchedItems = oim->items(); QVERIFY(fetchedItems.contains(visualEvent)); /*Event with more reminder*/ QOrganizerEvent bigEvent; bigEvent.saveDetail(&audioReminder); bigEvent.saveDetail(&visualReminder); bigEvent.saveDetail(&emailReminder); oim->saveItem (&bigEvent); fetchedItems = oim->items(); QVERIFY(fetchedItems.contains(bigEvent)); // remove QVERIFY(bigEvent.removeDetail(&visualReminder)); oim->saveItem (&bigEvent); fetchedItems = oim->items(); QVERIFY(fetchedItems.contains(bigEvent)); } void tst_QOrganizerManager::testIntersectionFilter() { QFETCH(QString, uri); QScopedPointer mgr(QOrganizerManager::fromUri(uri)); // initialize db mgr->removeItems(mgr->itemIds()); QCOMPARE(mgr->items().count(), 0); //prepare filter data QOrganizerCollection c1; QOrganizerCollection c2; mgr->saveCollection(&c1); mgr->saveCollection(&c2); QOrganizerItem event1; event1.setType(QOrganizerItemType::TypeEvent); QOrganizerItem event2; event2.setType(QOrganizerItemType::TypeEvent); event1.setCollectionId(c1.id()); event2.setCollectionId(c2.id()); mgr->saveItem(&event1); mgr->saveItem(&event2); //Test intersection filter with 2 different collection filter QOrganizerItemCollectionFilter cf1; cf1.setCollectionId(c1.id()); QOrganizerItemCollectionFilter cf2; cf2.setCollectionId(c2.id()); QOrganizerItemIntersectionFilter isf; isf.append(cf1); isf.append(cf2); QList itemList = mgr->items(QDateTime(), QDateTime(), cf1); QCOMPARE(itemList.size(), 1); QCOMPARE(itemList.at(0), event1); itemList = mgr->items(QDateTime(), QDateTime(), isf); QCOMPARE(itemList.size(), 0); //Test intersection filter with 2 same collection filter isf.remove(cf2); cf2.setCollectionId(c1.id()); isf.append(cf2); itemList = mgr->items(QDateTime(), QDateTime(), isf); QCOMPARE(itemList.size(), 1); //Test intersection filter with 2 collection filter and one of them is bigger than another isf.remove(cf2); QSet collectionList; collectionList << c1.id() << c2.id(); cf2.setCollectionIds(collectionList); isf.append(cf2); isf.append(cf1); itemList = mgr->items(QDateTime(), QDateTime(), isf); QCOMPARE(itemList.size(), 1); //Bad case: one empty filter isf.remove(cf2); cf2.setCollectionId(QOrganizerCollectionId()); isf.append(cf2); QCOMPARE(isf.filters().size(), 2); itemList = mgr->items(QDateTime(), QDateTime(), isf); QCOMPARE(itemList.size(), 0); //QOrganizerItemIdFilter test QOrganizerItemIdFilter idFilter; QList ids; QOrganizerItem event3; event3.setType(QOrganizerItemType::TypeEvent); mgr->saveItem(&event3); ids << event1.id() << event3.id(); idFilter.setIds(ids); itemList = mgr->items(QDateTime(), QDateTime(), idFilter); QCOMPARE(itemList.size(), 2); if (itemList.at(0).id() == event1.id()){ QCOMPARE(itemList.at(0), event1); QCOMPARE(itemList.at(1), event3); } else { QCOMPARE(itemList.at(1), event1); QCOMPARE(itemList.at(0), event3); } //a bad id inside the list // NOTE: empty id in id filter will match all generated occurrences QOrganizerItem badEvent; ids.prepend(badEvent.id()); idFilter.setIds(ids); itemList = mgr->items(QDateTime(), QDateTime(), idFilter); QCOMPARE(itemList.size(), 2); //intersection filter contains QOrganizerItemIdFilter and QOrganizerItemCollectionFilter cf2.setCollectionIds(collectionList); isf.clear(); isf.append(cf2);//event1 event2 isf.append(idFilter);//event1 event3 itemList = mgr->items(QDateTime(), QDateTime(), isf); QCOMPARE(itemList.size(), 1); QCOMPARE(itemList.at(0), event1);//expect event1 //3 filters intersection ids.clear(); ids.append(event1.id()); idFilter.setIds(ids); isf.append(idFilter);//event1 itemList = mgr->items(QDateTime(), QDateTime(), isf); QCOMPARE(itemList.size(), 1); //empty intersection ids.clear(); ids.append(event3.id()); idFilter.setIds(ids); isf.append(idFilter);//event3 itemList = mgr->items(QDateTime(), QDateTime(), isf); QCOMPARE(itemList.size(), 0); } void tst_QOrganizerManager::testNestCompoundFilter() { QFETCH(QString, uri); QScopedPointer mgr(QOrganizerManager::fromUri(uri)); //simple case: nest intersection filter //prepare filter data QOrganizerCollection c1; QOrganizerCollection c2; mgr->saveCollection(&c1); mgr->saveCollection(&c2); QOrganizerItem event1; event1.setType(QOrganizerItemType::TypeEvent); QOrganizerItem event2; event2.setType(QOrganizerItemType::TypeEvent); event1.setCollectionId(c1.id()); event2.setCollectionId(c2.id()); mgr->saveItem(&event1); mgr->saveItem(&event2); //Test intersection filter with 2 different collection filter QOrganizerItemCollectionFilter cf1; cf1.setCollectionId(c1.id()); QOrganizerItemCollectionFilter cf2; cf2.setCollectionId(c2.id()); QOrganizerItemIntersectionFilter isf; isf.append(cf1); QOrganizerItemIntersectionFilter isfLevel1; isfLevel1.append(isf); QList itemList = mgr->items(QDateTime(), QDateTime(), isfLevel1); QCOMPARE(itemList.size(), 1); QOrganizerItemIntersectionFilter isfLevel2; isfLevel2.append(isfLevel1); itemList = mgr->items(QDateTime(), QDateTime(), isfLevel2); QCOMPARE(itemList.size(), 1); QOrganizerItemIntersectionFilter isfLevel3; isfLevel3.append(isfLevel2); itemList = mgr->items(QDateTime(), QDateTime(), isfLevel3); QCOMPARE(itemList.size(), 1); // union filter nest in interseion filter QOrganizerItemUnionFilter unf; unf.append(cf2); unf.append(cf1); isfLevel1.append(unf); itemList = mgr->items(QDateTime(), QDateTime(), isfLevel1); QCOMPARE(itemList.size(), 1); isfLevel2.append(unf); itemList = mgr->items(QDateTime(), QDateTime(), isfLevel2); QCOMPARE(itemList.size(), 1); QList idList; idList << event1.id() << event2.id(); QOrganizerItemIdFilter idf; idf.setIds(idList); isfLevel3.append(idf); itemList = mgr->items(QDateTime(), QDateTime(), isfLevel3); QCOMPARE(itemList.size(), 1); QOrganizerItemUnionFilter unfLevel4; unfLevel4.append(isfLevel3); unfLevel4.append(idf); itemList = mgr->items(QDateTime(), QDateTime(), unfLevel4); QCOMPARE(itemList.size(), 2); //attach each other isfLevel3.append(unfLevel4); itemList = mgr->items(QDateTime(), QDateTime(), isfLevel3); QCOMPARE(itemList.size(), 1); //actual use case test int count = mgr->items().size(); QOrganizerTodo todo; todo.setDisplayLabel("myTodo"); todo.setCollectionId(c1.id()); mgr->saveItem(&todo); itemList = mgr->items(); QCOMPARE(itemList.size(), ++count); //collection QOrganizerItemCollectionFilter collectionFilter; collectionFilter.setCollectionId(c1.id()); itemList = mgr->items(QDateTime(), QDateTime(), collectionFilter); QCOMPARE(itemList.size(), 2); //event QOrganizerItemDetailFieldFilter detailFieldFilter; detailFieldFilter.setDetail(QOrganizerItemDetail::TypeItemType, QOrganizerItemType::FieldType); detailFieldFilter.setValue(QOrganizerItemType::TypeEvent); itemList = mgr->items(QDateTime(), QDateTime(), detailFieldFilter); QVERIFY(itemList.size() >= 2); //event + collection QOrganizerItemIntersectionFilter intersFilter; intersFilter.append(detailFieldFilter); intersFilter.append(collectionFilter); itemList = mgr->items(QDateTime(), QDateTime(), intersFilter); QCOMPARE(itemList.size(), 1); //filter event + collection + id idList << event1.id() << event2.id() << todo.id(); QOrganizerItemIdFilter idf1; idf1.setIds(idList); QOrganizerItemIntersectionFilter intersFilter2; intersFilter2.append(idf1); intersFilter2.append(intersFilter); itemList = mgr->items(QDateTime(), QDateTime(), intersFilter); QCOMPARE(itemList.size(), 1); QCOMPARE(itemList[0].id(), event1.id()); //case 2: myTodo or event + collection + id QOrganizerItemDetailFieldFilter detailFieldFilter2; detailFieldFilter2.setDetail(QOrganizerItemDetail::TypeDisplayLabel, QOrganizerItemDisplayLabel::FieldLabel); detailFieldFilter2.setValue("myTodo"); itemList = mgr->items(QDateTime(), QDateTime(), detailFieldFilter2); QCOMPARE(itemList.size(), 1); QOrganizerItemUnionFilter unf2; unf2.append(detailFieldFilter2); unf2.append(detailFieldFilter); itemList = mgr->items(QDateTime(), QDateTime(), unf2); QVERIFY(itemList.size() >= 3); //event or myTodo + collection QOrganizerItemIntersectionFilter intersFilter3; intersFilter3.append(unf2); intersFilter3.append(collectionFilter); itemList = mgr->items(QDateTime(), QDateTime(), intersFilter3); QCOMPARE(itemList.size(), 2); // ... + id QOrganizerItemIntersectionFilter intersFilter4; intersFilter4.append(idf1); intersFilter4.append(intersFilter3); itemList = mgr->items(QDateTime(), QDateTime(), intersFilter4); QCOMPARE(itemList.size(), 2); QVERIFY(itemList.contains(event1)); QVERIFY(itemList.contains(todo)); } void tst_QOrganizerManager::testUnionFilter() { QFETCH(QString, uri); QScopedPointer mgr(QOrganizerManager::fromUri(uri)); //prepare filter data QOrganizerCollection c1; QOrganizerCollection c2; mgr->saveCollection(&c1); mgr->saveCollection(&c2); QOrganizerItem event1; event1.setType(QOrganizerItemType::TypeEvent); QOrganizerItem event2; event2.setType(QOrganizerItemType::TypeEvent); event1.setCollectionId(c1.id()); event2.setCollectionId(c2.id()); mgr->saveItem(&event1); mgr->saveItem(&event2); //Test union filter with 2 different collection filter QOrganizerItemCollectionFilter cf1; cf1.setCollectionId(c1.id()); QOrganizerItemCollectionFilter cf2; cf2.setCollectionId(c2.id()); QOrganizerItemUnionFilter unf; unf.append(cf1); unf.append(cf2); QList itemList = mgr->items(QDateTime(), QDateTime(), cf1); QCOMPARE(itemList.size(), 1); QCOMPARE(itemList.at(0), event1); itemList = mgr->items(QDateTime(), QDateTime(), unf); QCOMPARE(itemList.size(), 2); //Test union filter with 2 same collection filter unf.remove(cf2); cf2.setCollectionId(c1.id()); unf.append(cf2); itemList = mgr->items(QDateTime(), QDateTime(), unf); QCOMPARE(itemList.size(), 1); //Test union filter with 2 collection filter and one of them is biger than another unf.remove(cf2); QSet collectionList; collectionList << c1.id() << c2.id(); cf2.setCollectionIds(collectionList); unf.append(cf2); unf.append(cf1); itemList = mgr->items(QDateTime(), QDateTime(), unf); QCOMPARE(itemList.size(), 2); //Bad case: one empty filter unf.remove(cf2); cf2.setCollectionId(QOrganizerCollectionId()); unf.append(cf2); QCOMPARE(unf.filters().size(), 2); itemList = mgr->items(QDateTime(), QDateTime(), unf); QCOMPARE(itemList.size(), 1); //union filter contains QOrganizerItemIdFilter and QOrganizerItemCollectionFilter QList ids; QOrganizerItemIdFilter idFilter; QOrganizerItem event3; event3.setType(QOrganizerItemType::TypeEvent); mgr->saveItem(&event3); ids << event3.id(); idFilter.setIds(ids); cf2.setCollectionIds(collectionList); unf.clear(); unf.append(cf2);//event1 event2 unf.append(idFilter);// event3 itemList = mgr->items(QDateTime(), QDateTime(), unf); QCOMPARE(itemList.size(), 3);//expect event1 event2 event3 //3 filters union QOrganizerItem event4; event4.setType(QOrganizerItemType::TypeEvent); mgr->saveItem(&event4); ids.clear(); ids.append(event4.id()); idFilter.setIds(ids); unf.append(idFilter);//event4 itemList = mgr->items(QDateTime(), QDateTime(), unf); QCOMPARE(itemList.size(), 4);//expect event1 event2 event3 event4 } void tst_QOrganizerManager::testTags() { QFETCH(QString, uri); QScopedPointer mgr(QOrganizerManager::fromUri(uri)); // save & load QOrganizerEvent event; event.setTags(QStringList() << QString::fromLatin1("Tag1") << QString::fromLatin1("Tag2")); event.addTag(QString::fromLatin1("Tag3")); QVERIFY(mgr->saveItem(&event)); QOrganizerItemId id = event.id(); QOrganizerItem item = mgr->item(id); QVERIFY(item.tags().size() == 3); QVERIFY(item.tags().contains(QString::fromLatin1("Tag1"))); QVERIFY(item.tags().contains(QString::fromLatin1("Tag2"))); QVERIFY(item.tags().contains(QString::fromLatin1("Tag3"))); // update item.addTag(QString::fromLatin1("Tag4"));QVERIFY(mgr->saveItem(&event)); QList details = item.details(QOrganizerItemDetail::TypeTag); QOrganizerItemTag tag = details.at(1); tag.setTag(QString::fromLatin1("Tag222")); item.saveDetail(&tag); item.removeDetail(&details[2]); QVERIFY(mgr->saveItem(&item)); QOrganizerItemId id2 = item.id(); QOrganizerItem item2 = mgr->item(id2); QVERIFY(item2.tags().size() == 3); QVERIFY(item2.tags().contains(QString::fromLatin1("Tag1"))); QVERIFY(item2.tags().contains(QString::fromLatin1("Tag222"))); QVERIFY(item2.tags().contains(QString::fromLatin1("Tag4"))); } void tst_QOrganizerManager::testExtendedDetail() { QFETCH(QString, uri); QScopedPointer mgr(QOrganizerManager::fromUri(uri)); QOrganizerEvent event; // simple string QOrganizerItemExtendedDetail basicString; basicString.setName(QStringLiteral("basic-string")); basicString.setData(QString(QStringLiteral("Qt Everywhere"))); event.saveDetail(&basicString); QVERIFY(mgr->saveItem(&event)); event = mgr->item(event.id()); basicString = event.detail(QOrganizerItemDetail::TypeExtendedDetail); QCOMPARE(basicString.name(), QStringLiteral("basic-string")); QCOMPARE(basicString.data().toString(), QStringLiteral("Qt Everywhere")); // simple list QOrganizerItemExtendedDetail basicList; basicList.setName(QStringLiteral("basic-list")); QVariantList basicListData; basicListData << QString(QStringLiteral("data 1")) << QString(QStringLiteral("data 2")) << QVariant(3); basicList.setData(basicListData); event.saveDetail(&basicList); QVERIFY(mgr->saveItem(&event)); event = mgr->item(event.id()); QList extendedDetails = event.details(QOrganizerItemDetail::TypeExtendedDetail); QCOMPARE(extendedDetails.size(), 2); foreach (const QOrganizerItemExtendedDetail &extendedDetail, extendedDetails) { if (extendedDetail.name() == QStringLiteral("basic-list")) { QVariantList data = extendedDetail.data().toList(); QCOMPARE(data.size(), 3); QCOMPARE(data.at(0).toString(), QStringLiteral("data 1")); QCOMPARE(data.at(1).toString(), QStringLiteral("data 2")); QCOMPARE(data.at(2).toInt(), 3); break; } } // simple map QOrganizerItemExtendedDetail basicMap; basicMap.setName(QStringLiteral("basic map")); QVariantMap basicMapData; basicMapData.insert(QString(QStringLiteral("key1")), QString(QStringLiteral("data-1"))); basicMapData.insert(QString(QStringLiteral("key-2")), QString(QStringLiteral("data2"))); basicMapData.insert(QString(QStringLiteral("key_3")), QVariant(1989)); basicMap.setData(basicMapData); event.saveDetail(&basicMap); QVERIFY(mgr->saveItem(&event)); event = mgr->item(event.id()); extendedDetails = event.details(QOrganizerItemDetail::TypeExtendedDetail); QCOMPARE(extendedDetails.size(), 3); foreach (const QOrganizerItemExtendedDetail &extendedDetail, extendedDetails) { if (extendedDetail.name() == QStringLiteral("basic map")) { QVariantMap data = extendedDetail.data().toMap(); QCOMPARE(data.size(), 3); QCOMPARE(data.value(QStringLiteral("key1")).toString(), QStringLiteral("data-1")); QCOMPARE(data.value(QStringLiteral("key-2")).toString(), QStringLiteral("data2")); QCOMPARE(data.value(QStringLiteral("key_3")).toInt(), 1989); break; } } // map inside a list QOrganizerItemExtendedDetail mapInList; mapInList.setName(QStringLiteral("map in list")); mapInList.setData(QVariantList() << QString(QStringLiteral("Qt is cute")) << basicMapData); event.saveDetail(&mapInList); QVERIFY(mgr->saveItem(&event)); event = mgr->item(event.id()); extendedDetails = event.details(QOrganizerItemDetail::TypeExtendedDetail); QCOMPARE(extendedDetails.size(), 4); foreach (const QOrganizerItemExtendedDetail &extendedDetail, extendedDetails) { if (extendedDetail.name() == QStringLiteral("map in list")) { QVariantList data = extendedDetail.data().toList(); QCOMPARE(data.size(), 2); QCOMPARE(data.at(0).toString(), QStringLiteral("Qt is cute")); QVariantMap map = data.at(1).toMap(); QCOMPARE(map.value(QStringLiteral("key1")).toString(), QStringLiteral("data-1")); QCOMPARE(map.value(QStringLiteral("key-2")).toString(), QStringLiteral("data2")); QCOMPARE(map.value(QStringLiteral("key_3")).toInt(), 1989); break; } } QVERIFY(mgr->removeItem(event.id())); // for JsonDb backend, we allow custom fields for reminder, rsvp, and location if (mgr->managerName() == QString(QStringLiteral("jsondb"))) { QOrganizerItemAudibleReminder audibleReminder; audibleReminder.setSecondsBeforeStart(1989); audibleReminder.setRepetition(6, 4); audibleReminder.setDataUrl(QUrl(QString(QStringLiteral("http://www.qt-project.org/")))); QOrganizerItemExtendedDetail extendedDetailForReminder; extendedDetailForReminder.setName(QString(QStringLiteral("reminder"))); QVariantMap data; data.insert(QString(QStringLiteral("Qt")), QString(QStringLiteral("Everywhere"))); data.insert(QString(QStringLiteral("URL")), QUrl(QString(QStringLiteral("http://www.qt-project.org/")))); extendedDetailForReminder.setData(data); QOrganizerEvent eventForReminder; eventForReminder.setStartDateTime(QDateTime::fromString(QString(QStringLiteral("2012-02-01T00:11:22")), Qt::ISODate)); eventForReminder.saveDetail(&audibleReminder); eventForReminder.saveDetail(&extendedDetailForReminder); QVERIFY(mgr->saveItem(&eventForReminder)); QOrganizerItem fetchedItem = mgr->items(QList() << eventForReminder.id()).at(0); QVERIFY(fetchedItem == eventForReminder); QOrganizerEventRsvp rsvp; rsvp.setOrganizerName(QString(QStringLiteral("Qt"))); QOrganizerItemExtendedDetail extendedDetailForRsvp; extendedDetailForRsvp.setName(QString(QStringLiteral("rsvp"))); extendedDetailForRsvp.setData(data); QOrganizerEvent eventForRsvp; eventForRsvp.saveDetail(&rsvp); eventForRsvp.saveDetail(&extendedDetailForRsvp); QVERIFY(mgr->saveItem(&eventForRsvp)); fetchedItem = mgr->items(QList() << eventForRsvp.id()).at(0); QVERIFY(fetchedItem == eventForRsvp); QOrganizerItemLocation location; location.setLatitude(19.84); QOrganizerItemExtendedDetail extendedDetailForLocation; extendedDetailForLocation.setName(QString(QStringLiteral("location"))); extendedDetailForLocation.setData(data); QOrganizerEvent eventForLocation; eventForLocation.saveDetail(&location); eventForLocation.saveDetail(&extendedDetailForLocation); QVERIFY(mgr->saveItem(&eventForLocation)); fetchedItem = mgr->items(QList() << eventForLocation.id()).at(0); QVERIFY(fetchedItem == eventForLocation); QOrganizerEvent eventForAll; eventForAll.setStartDateTime(QDateTime::fromString(QString(QStringLiteral("2012-02-01T00:11:22")), Qt::ISODate)); eventForAll.saveDetail(&audibleReminder); eventForAll.saveDetail(&extendedDetailForReminder); eventForAll.saveDetail(&rsvp); eventForAll.saveDetail(&extendedDetailForRsvp); eventForAll.saveDetail(&location); eventForAll.saveDetail(&extendedDetailForLocation); QVERIFY(mgr->saveItem(&eventForAll)); fetchedItem = mgr->items(QList() << eventForAll.id()).at(0); QVERIFY(fetchedItem == eventForAll); } } void tst_QOrganizerManager::testAttendee() { QFETCH(QString, uri); QScopedPointer mgr(QOrganizerManager::fromUri(uri)); QOrganizerEvent event; // Save item and verify QOrganizerEventAttendee attendee; attendee.setName("people"); attendee.setAttendeeId("123456"); attendee.setEmailAddress("people@nokia.com"); attendee.setParticipationRole(QOrganizerEventAttendee::RoleRequiredParticipant); attendee.setParticipationStatus(QOrganizerEventAttendee::StatusAccepted); event.saveDetail(&attendee); QVERIFY(mgr->saveItem(&event)); QOrganizerItemId id = event.id(); QOrganizerItem item = mgr->item(id); QVERIFY(item.details(QOrganizerItemDetail::TypeEventAttendee).count() == 1); QVERIFY(item == event);//This will compare all details and their values // Update attendee.setName("newpeople"); attendee.setAttendeeId("54321"); attendee.setEmailAddress("newpeople@nokia.com"); event.saveDetail(&attendee); QVERIFY(item != event); QVERIFY(mgr->saveItem(&event)); item = mgr->item(id); QVERIFY(item.details(QOrganizerItemDetail::TypeEventAttendee).count() == 1); QVERIFY(item == event);//This will compare all details and their values // Add one more attendee QOrganizerEventAttendee a1; a1.setAttendeeId("777777"); a1.setName("people1"); a1.setEmailAddress("people1@nokia.com"); event.saveDetail(&a1); QVERIFY(item != event); QVERIFY(mgr->saveItem(&event)); item = mgr->item(id); QVERIFY(item.details(QOrganizerItemDetail::TypeEventAttendee).count() == 2); // Remove QVERIFY(event.removeDetail(&attendee)); QVERIFY(event.details(QOrganizerItemDetail::TypeEventAttendee).size() == 1); QVERIFY(event.removeDetail(&a1)); QVERIFY(event.details(QOrganizerItemDetail::TypeEventAttendee).size() == 0); QVERIFY(mgr->saveItem(&event)); item = mgr->item(id); QVERIFY(item.details(QOrganizerItemDetail::TypeEventAttendee).count() == 0); } void tst_QOrganizerManager::testRsvp() { QFETCH(QString, uri); QScopedPointer mgr(QOrganizerManager::fromUri(uri)); QOrganizerEvent event; // Save item and verify QOrganizerEventRsvp rsvp; rsvp.setOrganizerName("Donald Duck"); rsvp.setOrganizerEmail("don@duck.com"); rsvp.setResponseDate(QDate(2010, 10, 10)); rsvp.setResponseDeadline(QDate(2010, 11, 11)); rsvp.setParticipationRole(QOrganizerEventAttendee::RoleOrganizer); rsvp.setParticipationStatus(QOrganizerEventAttendee::StatusAccepted); rsvp.setResponseRequirement(QOrganizerEventRsvp::ResponseRequired); QVERIFY(event.saveDetail(&rsvp)); QVERIFY(mgr->saveItem(&event)); QOrganizerItemId id = event.id(); QOrganizerItem item = mgr->item(id); QCOMPARE(1, item.details(QOrganizerItemDetail::TypeEventRsvp).count()); QVERIFY(item == event);//This will compare all details and their values // Update rsvp.setOrganizerName("Mickey Mouse"); rsvp.setOrganizerEmail("mick@mouse.com"); rsvp.setResponseDate(QDate(2011, 11, 11)); rsvp.setResponseDeadline(QDate(2011, 12, 12)); rsvp.setParticipationRole(QOrganizerEventAttendee::RoleChairperson); rsvp.setParticipationStatus(QOrganizerEventAttendee::StatusDelegated); rsvp.setResponseRequirement(QOrganizerEventRsvp::ResponseNotRequired); QVERIFY(event.saveDetail(&rsvp)); QVERIFY(mgr->saveItem(&event)); item = mgr->item(id); QCOMPARE(1, event.details(QOrganizerItemDetail::TypeEventRsvp).size()); QVERIFY(item == event);//This will compare all details and their values // Remove QVERIFY(event.removeDetail(&rsvp)); QCOMPARE(0, event.details(QOrganizerItemDetail::TypeEventRsvp).size()); QVERIFY(mgr->saveItem(&event)); item = mgr->item(id); QVERIFY(item.details(QOrganizerItemDetail::TypeEventRsvp).count() == 0); } void tst_QOrganizerManager::testClassification() { QFETCH(QString, uri); QScopedPointer mgr(QOrganizerManager::fromUri(uri)); if (!mgr->supportedItemDetails(QOrganizerItemType::TypeEvent).contains(QOrganizerItemDetail::TypeClassification)) QSKIP("Classification -detail not supported by this backend."); // Save item and verify QOrganizerEvent event; QOrganizerItemClassification classification; classification.setClassification(QOrganizerItemClassification::AccessPrivate); QVERIFY(event.saveDetail(&classification)); QVERIFY(mgr->saveItem(&event)); QOrganizerItemId id = event.id(); QOrganizerItem item = mgr->item(id); QEXPECT_FAIL("mgr='jsondb'", "No support on jsondb backend yet", Abort); QCOMPARE(1, item.details(QOrganizerItemDetail::TypeClassification).count()); QVERIFY(item == event);//This will compare all details and their values // Update classification.setClassification(QOrganizerItemClassification::AccessConfidential); QVERIFY(event.saveDetail(&classification)); QVERIFY(mgr->saveItem(&event)); item = mgr->item(id); QCOMPARE(1, event.details(QOrganizerItemDetail::TypeClassification).size()); QVERIFY(item == event);//This will compare all details and their values // Remove QVERIFY(event.removeDetail(&classification)); QCOMPARE(0, event.details(QOrganizerItemDetail::TypeClassification).size()); QVERIFY(mgr->saveItem(&event)); item = mgr->item(id); QVERIFY(item.details(QOrganizerItemDetail::TypeClassification).count() == 0); } void tst_QOrganizerManager::testVersion() { QFETCH(QString, uri); QScopedPointer mgr(QOrganizerManager::fromUri(uri)); if (!mgr->supportedItemDetails(QOrganizerItemType::TypeEvent).contains(QOrganizerItemDetail::TypeVersion)) QSKIP("Version detail not supported by this backend."); QOrganizerEvent event; QVERIFY(event.detail(QOrganizerItemDetail::TypeVersion).isEmpty()); QVERIFY(mgr->saveItem(&event)); QOrganizerItemVersion version = event.detail(QOrganizerItemDetail::TypeVersion); QVERIFY(!version.isEmpty()); event.setDisplayLabel("Qt rules!"); QVERIFY(mgr->saveItem(&event)); QOrganizerItemVersion version2 = event.detail(QOrganizerItemDetail::TypeVersion); QVERIFY(!version2.isEmpty()); QVERIFY((version2.version() > version.version()) || (version2.extendedVersion() != version.extendedVersion())); } #if defined(QT_NO_JSONDB) class errorSemanticsTester : public QObject { Q_OBJECT; public: bool initialErrorWasDoesNotExist; bool slotErrorWasBadArgument; QOrganizerManager* mManager; errorSemanticsTester(QOrganizerManager* manager) : initialErrorWasDoesNotExist(false), slotErrorWasBadArgument(false), mManager(manager) { connect(manager, SIGNAL(itemsAdded(QList)), this, SLOT(handleAdded())); } public slots: void handleAdded() { // Make sure the initial error state is correct initialErrorWasDoesNotExist = mManager->error() == QOrganizerManager::DoesNotExistError; // Now force a different error mManager->removeItems(QList()); slotErrorWasBadArgument = mManager->error() == QOrganizerManager::BadArgumentError; // and return } }; #endif QTEST_MAIN(tst_QOrganizerManager) #include "tst_qorganizermanager.moc" tests/auto/organizer/qorganizermanagerdataholder.h000066400000000000000000000100311233466112000230760ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QORGANIZERITEMMANAGERDATAHOLDER_H #define QORGANIZERITEMMANAGERDATAHOLDER_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include QTORGANIZER_USE_NAMESPACE class QOrganizerManagerDataHolder { public: QOrganizerManagerDataHolder() { QStringList managerNames = QOrganizerManager::availableManagers(); foreach(const QString& mgr, managerNames) { // Don't bother with these if (mgr == "memory" || mgr == "invalid" || mgr == "testdummy" || mgr == "maliciousplugin" || mgr == "skeleton") continue; QMap params; QString mgrUri = QOrganizerManager::buildUri(mgr, params); QOrganizerManager* cm = QOrganizerManager::fromUri(mgrUri); if (cm) { qDebug() << "Saving items for" << mgrUri; QList items = cm->itemsForExport(QDateTime(), QDateTime(), QOrganizerItemFilter()); savedItems.insert(cm->managerName(),items); QList ids; foreach(const QOrganizerItem& item, items) ids.append(item.id()); cm->removeItems(ids); delete cm; } } } ~QOrganizerManagerDataHolder() { foreach(const QString& mgrUri, savedItems.keys()) { QOrganizerManager* cm = QOrganizerManager::fromUri(mgrUri); if (cm) { qDebug() << "Restoring items for" << mgrUri; QList items = savedItems.value(mgrUri); cm->saveItems(&items); // XXX this doesn't restore relationships.. delete cm; } } } private: QMap > savedItems; }; #endif tests/auto/organizer/qorganizermanagerdetails/000077500000000000000000000000001233466112000222505ustar00rootroot00000000000000tests/auto/organizer/qorganizermanagerdetails/partitions.json000066400000000000000000000001211233466112000253310ustar00rootroot00000000000000[ { "name" : "com.nokia.mt.User" }, { "name" : "com.nokia.mt.System" } ] tests/auto/organizer/qorganizermanagerdetails/qorganizermanagerdetails.pro000066400000000000000000000003471233466112000300600ustar00rootroot00000000000000include(../../auto.pri) QT += organizer qtHaveModule(jsondb): QT += jsondb SOURCES += tst_qorganizermanagerdetails.cpp HEADERS += ../qorganizermanagerdataholder.h ../../jsondbprocess.h DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/organizer/qorganizermanagerdetails/tst_qorganizermanagerdetails.cpp000066400000000000000000000127461233466112000307420ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include "../qorganizermanagerdataholder.h" #include "../../jsondbprocess.h" QTORGANIZER_USE_NAMESPACE class tst_QOrganizerManagerDetails : public QObject { Q_OBJECT private: void addManagers(); bool saveAndLoadItem(QOrganizerManager *manager, QOrganizerItem &original, QOrganizerItem &loaded); void saveAndVerifyItem(QOrganizerManager *manager, QOrganizerItem &original); QScopedPointer managerDataHolder; JsonDbProcess jsondbProcess; private slots: void initTestCase(); void cleanupTestCase(); void testRecurrenceRule(); void testRecurrenceRule_data() {addManagers();} private slots: }; void tst_QOrganizerManagerDetails::addManagers() { QTest::addColumn("uri"); QStringList managers = QOrganizerManager::availableManagers(); /* Known one that will not pass */ managers.removeAll("invalid"); managers.removeAll("skeleton"); managers.removeAll("maliciousplugin"); foreach(QString mgr, managers) { QMap params; QTest::newRow(QString("mgr='%1'").arg(mgr).toLatin1().constData()) << QOrganizerManager::buildUri(mgr, params); } } void tst_QOrganizerManagerDetails::initTestCase() { // Start JsonDb daemon if needed if (QOrganizerManager::availableManagers().contains("jsondb")) { QString partitions_json = QFINDTESTDATA("partitions.json"); QVERIFY2(!partitions_json.isEmpty(), "partitions.json file is missing"); QVERIFY2(jsondbProcess.start(partitions_json), "Failed to start JsonDb process"); } managerDataHolder.reset(new QOrganizerManagerDataHolder()); } void tst_QOrganizerManagerDetails::cleanupTestCase() { managerDataHolder.reset(0); if (QOrganizerManager::availableManagers().contains("jsondb")) jsondbProcess.terminate(); } bool tst_QOrganizerManagerDetails::saveAndLoadItem(QOrganizerManager *manager, QOrganizerItem &original, QOrganizerItem &loaded) { // Save item if(manager->saveItem(&original) == false) return false; // Check the id if(original.id().isNull()) return false; // Load same item from database loaded = manager->item(original.id()); if(manager->error()) return false; // TODO: Ignore some details which are not relevant and will mess // up direct comparison between two items. return true; } void tst_QOrganizerManagerDetails::saveAndVerifyItem(QOrganizerManager *manager, QOrganizerItem &original) { QOrganizerItem loaded; QVERIFY(saveAndLoadItem(manager, original, loaded)); if (original != loaded) { qDebug() << "expected: " << original; qDebug() << "loaded: " << loaded; QCOMPARE(loaded.details().count(), original.details().count()); #if defined(QT_NO_JSONDB) QCOMPARE(loaded, original); #endif } } void tst_QOrganizerManagerDetails::testRecurrenceRule() { QFETCH(QString, uri); QScopedPointer manager(QOrganizerManager::fromUri(uri)); QOrganizerEvent event; event.setDisplayLabel("recurring event"); event.setStartDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 5))); event.setEndDateTime(QDateTime(QDate(2010, 1, 2), QTime(4, 4, 5))); QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Daily); rrule.setLimit(QDate(2010, 1, 10)); event.setRecurrenceRule(rrule); saveAndVerifyItem(manager.data(), event); } QTEST_MAIN(tst_QOrganizerManagerDetails) #include "tst_qorganizermanagerdetails.moc" tests/auto/versit/000077500000000000000000000000001233466112000145025ustar00rootroot00000000000000tests/auto/versit/qvcard21writer/000077500000000000000000000000001233466112000173625ustar00rootroot00000000000000tests/auto/versit/qvcard21writer/qvcard21writer.pro000066400000000000000000000003141233466112000227620ustar00rootroot00000000000000include(../../auto.pri) QT += versit versit-private HEADERS += tst_qvcard21writer.h SOURCES += tst_qvcard21writer.cpp CONFIG+=insignificant_test # QTBUG-25382 DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/versit/qvcard21writer/tst_qvcard21writer.cpp000066400000000000000000000424121233466112000236430ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/versit #include "tst_qvcard21writer.h" #ifdef QT_BUILD_INTERNAL #include #endif #include #include #include #include #include // This says "NOKIA" in Katakana const QString KATAKANA_NOKIA(QString::fromUtf8("\xe3\x83\x8e\xe3\x82\xad\xe3\x82\xa2")); QTVERSIT_USE_NAMESPACE Q_DECLARE_METATYPE(QVersitProperty) // Because the QFETCH macro balks on the comma in QMultiHash typedef QMultiHash StringHash; Q_DECLARE_METATYPE(StringHash) #ifdef QT_BUILD_INTERNAL void tst_QVCard21Writer::init() { mWriter = new QVCard21Writer(QVersitDocument::VCard21Type); mWriter->setCodec(QTextCodec::codecForName("ISO_8859-1")); } void tst_QVCard21Writer::cleanup() { delete mWriter; } void tst_QVCard21Writer::testEncodeVersitProperty() { QFETCH(QVersitProperty, property); QFETCH(QByteArray, expectedResult); QFETCH(QByteArray, codec); QTextCodec* textCodec = QTextCodec::codecForName(codec); QByteArray encodedProperty; QBuffer buffer(&encodedProperty); mWriter->setDevice(&buffer); mWriter->setCodec(textCodec); buffer.open(QIODevice::WriteOnly); mWriter->encodeVersitProperty(property); if (encodedProperty != expectedResult) { qDebug() << "Encoded: " << encodedProperty; qDebug() << "Expected: " << expectedResult; QVERIFY(false); } } void tst_QVCard21Writer::testEncodeVersitProperty_data() { QTest::addColumn("property"); QTest::addColumn("expectedResult"); QTest::addColumn("codec"); QVersitProperty property; QByteArray expectedResult; QByteArray codec("ISO-8859_1"); // normal case property.setName(QString::fromLatin1("FN")); property.setValue(QString::fromLatin1("John Citizen")); property.setValueType(QVersitProperty::PlainType); expectedResult = "FN:John Citizen\r\n"; QTest::newRow("No parameters") << property << expectedResult << codec; // Structured N - escaping should happen for semicolons, not for commas property.setName(QStringLiteral("N")); property.setValue(QStringList() << QStringLiteral("La;st") // needs to be backslash escaped << QStringLiteral("Fi,rst") << QStringLiteral("Mi:ddle") << QStringLiteral("Pr\\efix") // needs to be QP encoded << QStringLiteral("Suffix")); property.setValueType(QVersitProperty::CompoundType); expectedResult = "N;ENCODING=QUOTED-PRINTABLE:La\\;st;Fi,rst;Mi:ddle;Pr=5Cefix;Suffix\r\n"; QTest::newRow("N property") << property << expectedResult << codec; // Structured N - there was a bug where if two fields had to be escaped, // two ENCODING parameters were added property.setName(QStringLiteral("N")); property.setValue(QStringList() << QStringLiteral("La\\st") << QStringLiteral("Fi\\rst") << QString() << QString() << QString()); property.setValueType(QVersitProperty::CompoundType); expectedResult = "N;ENCODING=QUOTED-PRINTABLE:La=5Cst;Fi=5Crst;;;\r\n"; QTest::newRow("N property, double-encoded") << property << expectedResult << codec; // Structured N - one field needs to be encoded in UTF-8 while the other doesn't // correct behaviour is to encode the whole thing in UTF-8 property.setName(QStringLiteral("N")); property.setValue(QStringList() << QString::fromUtf8("\xE2\x82\xAC") // euro sign << QString::fromLatin1("\xA3") // pound sign (upper Latin-1) << QString() << QString() << QString()); property.setValueType(QVersitProperty::CompoundType); expectedResult = "N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=E2=82=AC;=C2=A3;;;\r\n"; QTest::newRow("N property, double-encoded") << property << expectedResult << codec; // Structured CATEGORIES - escaping should happen for commas, not semicolons property.setName(QStringLiteral("CATEGORIES")); property.setValue(QStringList() << QStringLiteral("re;d") << QStringLiteral("gr,een") << QStringLiteral("bl:ue")); property.setValueType(QVersitProperty::ListType); expectedResult = "CATEGORIES:re;d,gr\\,een,bl:ue\r\n"; QTest::newRow("CATEGORIES property") << property << expectedResult << codec; // With parameter(s). No special characters in the value. // -> No need to Quoted-Printable encode the value. expectedResult = "TEL;HOME:123\r\n"; property.setName(QString::fromLatin1("TEL")); property.setValue(QString::fromLatin1("123")); property.insertParameter(QString::fromLatin1("TYPE"),QString::fromLatin1("HOME")); QTest::newRow("With parameters, plain value") << property << expectedResult << codec; expectedResult = "EMAIL;HOME;ENCODING=QUOTED-PRINTABLE:john.citizen=40example.com\r\n"; property.setName(QString::fromLatin1("EMAIL")); property.setValue(QString::fromLatin1("john.citizen@example.com")); QTest::newRow("With parameters, special value") << property << expectedResult << codec; // AGENT property with parameter expectedResult = "AGENT;X-PARAMETER=VALUE:\r\n\ BEGIN:VCARD\r\n\ VERSION:2.1\r\n\ FN:Secret Agent\r\n\ END:VCARD\r\n\ \r\n"; property.setParameters(QMultiHash()); property.setName(QString::fromLatin1("AGENT")); property.setValue(QString()); property.insertParameter(QString::fromLatin1("X-PARAMETER"),QString::fromLatin1("VALUE")); QVersitDocument document(QVersitDocument::VCard21Type); document.setComponentType(QStringLiteral("VCARD")); QVersitProperty embeddedProperty; embeddedProperty.setName(QString(QString::fromLatin1("FN"))); embeddedProperty.setValue(QString::fromLatin1("Secret Agent")); document.addProperty(embeddedProperty); property.setValue(QVariant::fromValue(document)); QTest::newRow("AGENT property") << property << expectedResult << codec; // Value is base64 encoded. // Check that the extra folding and the line break are added QByteArray value("value"); expectedResult = "Springfield.HOUSE.PHOTO;ENCODING=BASE64:\r\n " + value.toBase64() + "\r\n\r\n"; QStringList groups(QString::fromLatin1("Springfield")); groups.append(QString::fromLatin1("HOUSE")); property.setGroups(groups); property.setParameters(QMultiHash()); property.setName(QString::fromLatin1("PHOTO")); property.setValue(value); QTest::newRow("base64 encoded") << property << expectedResult << codec; // Characters other than ASCII: // Note: KATAKANA_NOKIA is defined as: QString::fromUtf8("\xe3\x83\x8e\xe3\x82\xad\xe3\x82\xa2") // The expected behaviour is to convert to UTF8, then encode with quoted-printable. // Because the result overflows one line, it should be split onto two lines using a // quoted-printable soft line break (EQUALS-CR-LF). (Note: Versit soft line breaks // (CR-LF-SPACE) are not supported by the native Symbian vCard importers). expectedResult = "ORG;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=E3=83=8E=E3=82=AD=E3=82=A2=E3=\r\n" "=83=8E=E3=82=AD=E3=82=A2\r\n"; property = QVersitProperty(); property.setName(QStringLiteral("ORG")); property.setValue(KATAKANA_NOKIA + KATAKANA_NOKIA); QTest::newRow("non-ASCII 1") << property << expectedResult << codec; expectedResult = "ORG;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:a=E3=83=8E=E3=82=AD=E3=82=A2=E3=\r\n" "=83=8E=E3=82=AD=E3=82=A2\r\n"; property = QVersitProperty(); property.setName(QStringLiteral("ORG")); property.setValue("a" + KATAKANA_NOKIA + KATAKANA_NOKIA); QTest::newRow("non-ASCII 2") << property << expectedResult << codec; expectedResult = "ORG;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:aa=E3=83=8E=E3=82=AD=E3=82=A2=\r\n" "=E3=83=8E=E3=82=AD=E3=82=A2\r\n"; property = QVersitProperty(); property.setName(QStringLiteral("ORG")); property.setValue("aa" + KATAKANA_NOKIA + KATAKANA_NOKIA); QTest::newRow("non-ASCII 3") << property << expectedResult << codec; // In Shift-JIS codec. QTextCodec* jisCodec = QTextCodec::codecForName("Shift-JIS"); expectedResult = jisCodec->fromUnicode( QStringLiteral("ORG:") + KATAKANA_NOKIA + QStringLiteral("\r\n")); property = QVersitProperty(); property.setName(QStringLiteral("ORG")); property.setValue(KATAKANA_NOKIA); QTest::newRow("JIS codec") << property << expectedResult << QByteArray("Shift-JIS"); } void tst_QVCard21Writer::testEncodeParameters() { QFETCH(StringHash, parameters); QFETCH(QByteArray, expected); QByteArray encodedParameters; QBuffer buffer(&encodedParameters); mWriter->setDevice(&buffer); buffer.open(QIODevice::WriteOnly); // No parameters mWriter->encodeParameters(parameters); if (encodedParameters != expected) { qDebug() << "Encoded: " << encodedParameters; qDebug() << "Expected: " << expected; QVERIFY(false); } } void tst_QVCard21Writer::testEncodeParameters_data() { QTest::addColumn< QMultiHash >("parameters"); QTest::addColumn("expected"); QMultiHash parameters; QTest::newRow("No parameters") << parameters << QByteArray(""); parameters.insert(QStringLiteral("TYPE"), QString::fromLatin1("HOME")); QTest::newRow("One TYPE parameter") << parameters << QByteArray(";HOME"); // HOME should appear before VOICE because it is more "important" and some vCard // parsers may ignore everything after the first TYPE parameters.insert(QStringLiteral("TYPE"), QString::fromLatin1("VOICE")); QTest::newRow("Two TYPE parameters") << parameters << QByteArray(";HOME;VOICE"); parameters.clear(); parameters.insert(QStringLiteral("ENCODING"), QString::fromLatin1("8BIT")); QTest::newRow("One ENCODING parameter") << parameters << QByteArray(";ENCODING=8BIT"); parameters.insert(QString::fromLatin1("X-PARAM"),QString::fromLatin1("VALUE")); QTest::newRow("Two parameters") << parameters << QByteArray(";X-PARAM=VALUE;ENCODING=8BIT"); parameters.clear(); parameters.insert(QStringLiteral("TYPE"), QStringLiteral("VOICE")); parameters.insert(QStringLiteral("TYPE"), QStringLiteral("CELL")); parameters.insert(QStringLiteral("TYPE"), QStringLiteral("MODEM")); parameters.insert(QStringLiteral("TYPE"), QStringLiteral("CAR")); parameters.insert(QStringLiteral("TYPE"), QStringLiteral("VIDEO")); parameters.insert(QStringLiteral("TYPE"), QStringLiteral("FAX")); parameters.insert(QStringLiteral("TYPE"), QStringLiteral("BBS")); parameters.insert(QStringLiteral("TYPE"), QStringLiteral("PAGER")); parameters.insert(QStringLiteral("TYPE"), QStringLiteral("HOME")); parameters.insert(QStringLiteral("TYPE"), QStringLiteral("WORK")); // Ensure CELL and FAX are at the front because they are "more important" and some vCard // parsers may ignore everything after the first TYPE // Ensure WORK and HOME come next. // Besides these conditions, there are no other ordering constraints. The data here is simply // what the writer produces (as dictated by its internal data structures). QTest::newRow("TYPE parameters order") << parameters << QByteArray(";CELL;FAX;WORK;HOME;MODEM;CAR;VIDEO;BBS;PAGER;VOICE"); } void tst_QVCard21Writer::testEncodeGroupsAndName() { QVersitProperty property; QByteArray result; QBuffer buffer(&result); mWriter->setDevice(&buffer); buffer.open(QIODevice::WriteOnly); // No groups property.setName(QString::fromLatin1("name")); QByteArray expected("NAME"); mWriter->encodeGroupsAndName(property); QCOMPARE(result, expected); // One group mWriter->writeCrlf(); // so it doesn't start folding buffer.close(); result.clear(); buffer.open(QIODevice::WriteOnly); property.setGroups(QStringList(QString::fromLatin1("group"))); expected = "group.NAME"; mWriter->encodeGroupsAndName(property); QCOMPARE(result, expected); // Two groups mWriter->writeCrlf(); // so it doesn't start folding buffer.close(); result.clear(); buffer.open(QIODevice::WriteOnly); QStringList groups(QString::fromLatin1("group1")); groups.append(QString::fromLatin1("group2")); property.setGroups(groups); expected = "group1.group2.NAME"; mWriter->encodeGroupsAndName(property); QCOMPARE(result, expected); } void tst_QVCard21Writer::testQuotedPrintableEncode() { QByteArray encodedBytes; // Nothing to encode QString nothingToEncode(QStringLiteral("nothing to encode")); QVERIFY(!mWriter->quotedPrintableEncode(nothingToEncode)); // Special characters QString inputOutput(QStringLiteral("\n")); QVERIFY(mWriter->quotedPrintableEncode(inputOutput)); QCOMPARE(inputOutput, QStringLiteral("=0A")); inputOutput = QStringLiteral("\r"); QVERIFY(mWriter->quotedPrintableEncode(inputOutput)); QCOMPARE(inputOutput, QStringLiteral("=0D")); inputOutput = QStringLiteral("!"); QVERIFY(mWriter->quotedPrintableEncode(inputOutput)); QCOMPARE(inputOutput, QStringLiteral("=21")); inputOutput = QStringLiteral("\""); QVERIFY(mWriter->quotedPrintableEncode(inputOutput)); QCOMPARE(inputOutput, QStringLiteral("=22")); inputOutput = QStringLiteral("#"); QVERIFY(mWriter->quotedPrintableEncode(inputOutput)); QCOMPARE(inputOutput, QStringLiteral("=23")); inputOutput = QStringLiteral("$"); QVERIFY(mWriter->quotedPrintableEncode(inputOutput)); QCOMPARE(inputOutput, QStringLiteral("=24")); inputOutput = QStringLiteral("="); QVERIFY(mWriter->quotedPrintableEncode(inputOutput)); QCOMPARE(inputOutput, QStringLiteral("=3D")); inputOutput = QStringLiteral("@"); QVERIFY(mWriter->quotedPrintableEncode(inputOutput)); QCOMPARE(inputOutput, QStringLiteral("=40")); inputOutput = QStringLiteral("["); QVERIFY(mWriter->quotedPrintableEncode(inputOutput)); QCOMPARE(inputOutput, QStringLiteral("=5B")); inputOutput = QStringLiteral("\\"); QVERIFY(mWriter->quotedPrintableEncode(inputOutput)); QCOMPARE(inputOutput, QStringLiteral("=5C")); inputOutput = QStringLiteral("]"); QVERIFY(mWriter->quotedPrintableEncode(inputOutput)); QCOMPARE(inputOutput, QStringLiteral("=5D")); inputOutput = QStringLiteral("^"); QVERIFY(mWriter->quotedPrintableEncode(inputOutput)); QCOMPARE(inputOutput, QStringLiteral("=5E")); inputOutput = QStringLiteral("`"); QVERIFY(mWriter->quotedPrintableEncode(inputOutput)); QCOMPARE(inputOutput, QStringLiteral("=60")); inputOutput = QStringLiteral("{"); QVERIFY(mWriter->quotedPrintableEncode(inputOutput)); QCOMPARE(inputOutput, QStringLiteral("=7B")); inputOutput = QStringLiteral("|"); QVERIFY(mWriter->quotedPrintableEncode(inputOutput)); QCOMPARE(inputOutput, QStringLiteral("=7C")); inputOutput = QStringLiteral("}"); QVERIFY(mWriter->quotedPrintableEncode(inputOutput)); QCOMPARE(inputOutput, QStringLiteral("=7D")); inputOutput = QStringLiteral("~"); QVERIFY(mWriter->quotedPrintableEncode(inputOutput)); QCOMPARE(inputOutput, QStringLiteral("=7E")); } #endif QTEST_MAIN(tst_QVCard21Writer) tests/auto/versit/qvcard21writer/tst_qvcard21writer.h000066400000000000000000000051141233466112000233060ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef tst_QVCARD21WRITER_H #define tst_QVCARD21WRITER_H #include #include QT_BEGIN_NAMESPACE_VERSIT class QVCard21Writer; QT_END_NAMESPACE_VERSIT QTVERSIT_USE_NAMESPACE class tst_QVCard21Writer : public QObject { Q_OBJECT private slots: // Tests #ifdef QT_BUILD_INTERNAL void init(); void cleanup(); void testEncodeVersitProperty(); void testEncodeVersitProperty_data(); void testEncodeParameters(); void testEncodeParameters_data(); void testEncodeGroupsAndName(); void testQuotedPrintableEncode(); #endif private: // Data QVCard21Writer* mWriter; }; #endif // tst_QVCARD21WRITER_H tests/auto/versit/qvcard30writer/000077500000000000000000000000001233466112000173625ustar00rootroot00000000000000tests/auto/versit/qvcard30writer/qvcard30writer.pro000066400000000000000000000003051233466112000227620ustar00rootroot00000000000000include(../../auto.pri) QT += versit versit-private DEFINES += QT_ASCII_CAST_WARNINGS HEADERS += tst_qvcard30writer.h SOURCES += tst_qvcard30writer.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/versit/qvcard30writer/tst_qvcard30writer.cpp000066400000000000000000000300021233466112000236330ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/versit #include "tst_qvcard30writer.h" #ifdef QT_BUILD_INTERNAL #include #endif #include #include #include #include #include // This says "NOKIA" in Katakana encoded with UTF-8 const QString KATAKANA_NOKIA(QString::fromUtf8("\xe3\x83\x8e\xe3\x82\xad\xe3\x82\xa2")); QTVERSIT_USE_NAMESPACE Q_DECLARE_METATYPE(QVersitProperty) #ifdef QT_BUILD_INTERNAL void tst_QVCard30Writer::init() { mWriter = new QVCard30Writer(QVersitDocument::VCard30Type); mWriter->setCodec(QTextCodec::codecForName("UTF-8")); } void tst_QVCard30Writer::cleanup() { delete mWriter; } void tst_QVCard30Writer::testEncodeVersitProperty() { QFETCH(QVersitProperty, property); QFETCH(QByteArray, expectedResult); QByteArray encodedProperty; QBuffer buffer(&encodedProperty); mWriter->setDevice(&buffer); buffer.open(QIODevice::WriteOnly); mWriter->encodeVersitProperty(property); QCOMPARE(encodedProperty, expectedResult); } void tst_QVCard30Writer::testEncodeVersitProperty_data() { QTest::addColumn("property"); QTest::addColumn("expectedResult"); QVersitProperty property; QByteArray expectedResult; // No parameters expectedResult = "FN:John Citizen\r\n"; property.setName(QString::fromLatin1("FN")); property.setValue(QString::fromLatin1("John Citizen")); QTest::newRow("No parameters") << property << expectedResult; // With parameter(s) expectedResult = "TEL;TYPE=HOME:123\r\n"; property.setName(QString::fromLatin1("TEL")); property.setValue(QString::fromLatin1("123")); property.insertParameter(QString::fromLatin1("TYPE"),QString::fromLatin1("HOME")); QTest::newRow("With parameters, plain value") << property << expectedResult; // normal FN property is backslash escaped property.clear(); property.setName(QStringLiteral("FN")); property.setValue(QStringLiteral(";,:\\")); // semicolons, commas and backslashes are escaped (not colons, as per RFC2426) expectedResult = "FN:\\;\\,:\\\\\r\n"; QTest::newRow("FN property") << property << expectedResult; // Structured N property.setName(QStringLiteral("N")); property.setValue(QStringList() << QStringLiteral("La;st") // needs to be backslash escaped << QStringLiteral("Fi,rst") << QStringLiteral("Mi:ddle") << QStringLiteral("Pr\\efix") // needs to be QP encoded << QStringLiteral("Suffix")); property.setValueType(QVersitProperty::CompoundType); expectedResult = "N:La\\;st;Fi\\,rst;Mi:ddle;Pr\\\\efix;Suffix\r\n"; QTest::newRow("N property") << property << expectedResult; // Structured CATEGORIES property.setName(QStringLiteral("CATEGORIES")); property.setValue(QStringList() << QStringLiteral("re;d") << QStringLiteral("gr,een") << QStringLiteral("bl:ue") << QStringLiteral("ye\\llow")); property.setValueType(QVersitProperty::ListType); expectedResult = "CATEGORIES:re\\;d,gr\\,een,bl:ue,ye\\\\llow\r\n"; QTest::newRow("CATEGORIES property") << property << expectedResult; // Convert X-NICKNAME to NICKNAME expectedResult = "NICKNAME:Jack\r\n"; property.setParameters(QMultiHash()); property.setName(QString::fromLatin1("X-NICKNAME")); property.setValue(QString::fromLatin1("Jack")); QTest::newRow("NICKNAME property") << property << expectedResult; // Convert X-IMPP to IMPP; expectedResult = "IMPP:msn:msn-address\r\n"; property.setParameters(QMultiHash()); property.setName(QString::fromLatin1("X-IMPP")); property.setValue(QString::fromLatin1("msn:msn-address")); QTest::newRow("IMPP property") << property << expectedResult; // AGENT property expectedResult = "AGENT:BEGIN:VCARD\\nVERSION:3.0\\nFN:Secret Agent\\nEND:VCARD\\n\r\n"; property.setName(QString::fromLatin1("AGENT")); property.setValue(QString()); QVersitDocument document(QVersitDocument::VCard30Type); document.setComponentType(QStringLiteral("VCARD")); QVersitProperty embeddedProperty; embeddedProperty.setName(QString(QString::fromLatin1("FN"))); embeddedProperty.setValue(QString::fromLatin1("Secret Agent")); document.addProperty(embeddedProperty); property.setValue(QVariant::fromValue(document)); QTest::newRow("AGENT property") << property << expectedResult; // Value is base64 encoded. QByteArray value("value"); expectedResult = "Springfield.HOUSE.PHOTO;ENCODING=b:" + value.toBase64() + "\r\n"; QStringList groups(QString::fromLatin1("Springfield")); groups.append(QString::fromLatin1("HOUSE")); property.setGroups(groups); property.setParameters(QMultiHash()); property.setName(QString::fromLatin1("PHOTO")); property.setValue(value); QTest::newRow("base64 encoded") << property << expectedResult; // Characters other than ASCII: expectedResult = "ORG:" + KATAKANA_NOKIA.toUtf8() + "\r\n"; property = QVersitProperty(); property.setName(QStringLiteral("ORG")); property.setValue(KATAKANA_NOKIA); QTest::newRow("non-ASCII") << property << expectedResult; // No CHARSET and QUOTED-PRINTABLE parameters expectedResult = "EMAIL:john@" + KATAKANA_NOKIA.toUtf8() + ".com\r\n"; property = QVersitProperty(); property.setName(QStringLiteral("EMAIL")); property.setValue(QString::fromLatin1("john@%1.com").arg(KATAKANA_NOKIA)); QTest::newRow("special chars") << property << expectedResult; } void tst_QVCard30Writer::testEncodeParameters() { QByteArray encodedParameters; QBuffer buffer(&encodedParameters); mWriter->setDevice(&buffer); buffer.open(QIODevice::WriteOnly); QString typeParameterName(QString::fromLatin1("TYPE")); QString encodingParameterName(QString::fromLatin1("ENCODING")); // No parameters QMultiHash parameters; mWriter->encodeParameters(parameters); QCOMPARE(encodedParameters, QByteArray("")); // One TYPE parameter parameters.insert(typeParameterName,QString::fromLatin1("HOME")); mWriter->writeCrlf(); // so it doesn't start folding buffer.close(); encodedParameters.clear(); buffer.open(QIODevice::WriteOnly); mWriter->encodeParameters(parameters); QCOMPARE(encodedParameters, QByteArray(";TYPE=HOME")); // Two TYPE parameters parameters.insert(typeParameterName,QString::fromLatin1("VOICE")); mWriter->writeCrlf(); // so it doesn't start folding buffer.close(); encodedParameters.clear(); buffer.open(QIODevice::WriteOnly); mWriter->encodeParameters(parameters); QCOMPARE(encodedParameters, QByteArray(";TYPE=VOICE,HOME")); // One ENCODING parameter parameters.clear(); parameters.insert(encodingParameterName,QString::fromLatin1("8BIT")); mWriter->writeCrlf(); // so it doesn't start folding buffer.close(); encodedParameters.clear(); buffer.open(QIODevice::WriteOnly); mWriter->encodeParameters(parameters); QCOMPARE(encodedParameters, QByteArray(";ENCODING=8BIT")); // Two parameters parameters.insert(QString::fromLatin1("X-PARAM"),QString::fromLatin1("VALUE")); mWriter->writeCrlf(); // so it doesn't start folding buffer.close(); encodedParameters.clear(); buffer.open(QIODevice::WriteOnly); mWriter->encodeParameters(parameters); QSKIP("QTBUG-25382: Assumes QHash is ordered, which it's not"); QCOMPARE(encodedParameters, QByteArray(";X-PARAM=VALUE;ENCODING=8BIT")); // Parameter with characters that require backslash escaping parameters.clear(); parameters.insert(QString::fromLatin1("X-P;ARAM"),QString::fromLatin1("VA,LUE")); mWriter->writeCrlf(); // so it doesn't start folding buffer.close(); encodedParameters.clear(); buffer.open(QIODevice::WriteOnly); mWriter->encodeParameters(parameters); QCOMPARE(encodedParameters, QByteArray(";X-P\\;ARAM=VA\\,LUE")); } void tst_QVCard30Writer::testBackSlashEscape() { // Empty string QString input; QVCard30Writer::backSlashEscape(&input); QCOMPARE(input,QString()); // Nothing to escape in the string input = QString::fromLatin1("Nothing to escape"); QVCard30Writer::backSlashEscape(&input); QCOMPARE(input,QString::fromLatin1("Nothing to escape")); // Line break in the beginning input = QString::fromLatin1("\r\n input"); QVCard30Writer::backSlashEscape(&input); QCOMPARE(input,QString::fromLatin1("\\n input")); // Line break in the end input = QString::fromLatin1("input\r\n"); QVCard30Writer::backSlashEscape(&input); QCOMPARE(input,QString::fromLatin1("input\\n")); // Semicolon in the beginning input = QString::fromLatin1(";input"); QVCard30Writer::backSlashEscape(&input); QCOMPARE(input,QString::fromLatin1("\\;input")); // Semicolon in the end input = QString::fromLatin1("input;"); QVCard30Writer::backSlashEscape(&input); QCOMPARE(input,QString::fromLatin1("input\\;")); // Comma in the beginning input = QString::fromLatin1(",input"); QVCard30Writer::backSlashEscape(&input); QCOMPARE(input,QString::fromLatin1("\\,input")); // Comma in the end input = QString::fromLatin1("input,"); QVCard30Writer::backSlashEscape(&input); QCOMPARE(input,QString::fromLatin1("input\\,")); // Backslash in the beginning input = QString::fromLatin1("\\input"); QVCard30Writer::backSlashEscape(&input); QCOMPARE(input,QString::fromLatin1("\\\\input")); // Backslash in the end input = QString::fromLatin1("input\\"); QVCard30Writer::backSlashEscape(&input); QCOMPARE(input,QString::fromLatin1("input\\\\")); // Line break, semicolon, backslash and comma in the middle of the string input = QString::fromLatin1("Escape these \r\n ; , \\ "); QVCard30Writer::backSlashEscape(&input); QCOMPARE(input, QString::fromLatin1("Escape these \\n \\; \\, \\\\ ")); } #endif QTEST_MAIN(tst_QVCard30Writer) tests/auto/versit/qvcard30writer/tst_qvcard30writer.h000066400000000000000000000047761233466112000233230ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef tst_QVCARD30WRITER_H #define tst_QVCARD30WRITER_H #include #include QT_BEGIN_NAMESPACE_VERSIT class QVCard30Writer; QT_END_NAMESPACE_VERSIT QTVERSIT_USE_NAMESPACE class tst_QVCard30Writer : public QObject { Q_OBJECT private slots: // Tests #ifdef QT_BUILD_INTERNAL void init(); void cleanup(); void testEncodeVersitProperty(); void testEncodeVersitProperty_data(); void testEncodeParameters(); void testBackSlashEscape(); #endif private: // Data QVCard30Writer* mWriter; }; #endif // tst_QVCARD30WRITER_H tests/auto/versit/qversit/000077500000000000000000000000001233466112000161775ustar00rootroot00000000000000tests/auto/versit/qversit/qversit.pro000066400000000000000000000003371233466112000204210ustar00rootroot00000000000000include(../../auto.pri) QT += contacts organizer versit versitorganizer versit-private DEFINES += TESTDATA_DIR=\\\"$$PWD/\\\" HEADERS += tst_qversit.h SOURCES += tst_qversit.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/versit/qversit/testdata_ics/000077500000000000000000000000001233466112000206465ustar00rootroot00000000000000tests/auto/versit/qversit/testdata_ics/2010-FIFA-WorldCup.ics000066400000000000000000000455651233466112000242070ustar00rootroot00000000000000BEGIN:VCALENDAR METHOD:PUBLISH VERSION:2.0 X-WR-CALNAME:2010 FIFA World Cup PRODID:-//Apple Inc.//iCal 4.0.1//EN X-WR-CALDESC:This calendar lists the 2010 FIFA World Cup matches in Sout h Africa. I put them in at Central Time\, but hopefully it is accurate f or your time zone. Due to the way Google Calendar works\, it may not tra nslate time zones correctly. X-APPLE-CALENDAR-COLOR:#F57802 X-WR-TIMEZONE:Europe/Amsterdam CALSCALE:GREGORIAN BEGIN:VTIMEZONE TZID:Europe/Berlin BEGIN:DAYLIGHT TZOFFSETFROM:+0100 RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU DTSTART:19810329T020000 TZNAME:GMT+02:00 TZOFFSETTO:+0200 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU DTSTART:19961027T030000 TZNAME:GMT+01:00 TZOFFSETTO:+0100 END:STANDARD END:VTIMEZONE BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100615T210000Z UID:hmnchtpq4rud81o4k6gpuv54eg@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (G): Brazil vs. Korea DPR DTSTART:20100615T183000Z CREATED:20091204T194743Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100625T210000Z UID:gati2cmgnvhgodn4eik9otmf78@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (H): Chile vs. Spain DTSTART:20100625T183000Z CREATED:20091204T200720Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100612T163000Z UID:pbfdsbhoku39f73cou4fpapo0o@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup (B): Korea Rep. vs. Greece DTSTART:20100612T140000Z CREATED:20091204T192743Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100613T163000Z UID:lui0vns2i71k8dv6p3t3tp2b6c@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup (D): Germany vs. Australia DTSTART:20100613T140000Z CREATED:20091204T194156Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100625T163000Z UID:0dvhsppbl68pdn6hgkptn7utrs@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup (G): Korea DPR vs. Cote d Ivoire DTSTART:20100625T140000Z CREATED:20091204T200657Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100616T140000Z UID:h713317l5n6mg79ef2ho6ooe24@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (H): Honduras vs. Chile DTSTART:20100616T113000Z CREATED:20091204T194813Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100615T163000Z UID:6q5mg61oi4d41b7upvnevtsec8@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (G): Cote d Ivoire vs. Portugal DTSTART:20100615T140000Z CREATED:20091204T194713Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100622T163000Z UID:kc820t9qbks6qdog868evlcvvs@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (A): France vs. South Africa DTSTART:20100622T140000Z CREATED:20091204T200002Z END:VEVENT BEGIN:VEVENT ATTENDEE;CN="2010 FIFA World Cup";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED;RO LE=REQ-PARTICIPANT:mailto:hioolh3fpnqg16f0ef1r7p1hsg@group.calendar.goog le.com DTEND:20100623T163000Z TRANSP:OPAQUE UID:eke563hinr6439bp946p463640@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup: USA vs Algeria DTSTART:20100623T140000Z CREATED:20091204T191533Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100620T163000Z UID:nl517e60q2cq24qr6civm1pfn8@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (F): Italy vs. New Zealand DTSTART:20100620T140000Z CREATED:20091204T195709Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100622T210000Z UID:5ng2ieeeqqj35q8orip2mqu3h0@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (B): Nigeria vs. Korea Rep. DTSTART:20100622T183000Z CREATED:20091204T200022Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100611T210000Z UID:pjnr8akalph5v8bdpm2va2df04@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (A): Uruguay vs France DTSTART:20100611T183000Z CREATED:20091204T192513Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100613T140000Z UID:1a12u7vtvd8d1ss5gh5pmijue0@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup (C): Algeria vs. Slovenia DTSTART:20100613T113000Z CREATED:20091204T192827Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100612T140000Z UID:vqmnje9vsv821u4dfrlvg218uo@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup (B): Argentina vs Nigeria DTSTART:20100612T113000Z CREATED:20091204T192558Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100623T210000Z UID:kf7eu8r2beq50tm0ecpp7jqhag@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (D): Ghana vs. Germany DTSTART:20100623T183000Z CREATED:20091204T200251Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100614T140000Z UID:rt3c7adkl2up69g5c34m7k9atk@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup (E): Netherlands vs. Denmark DTSTART:20100614T113000Z CREATED:20091204T194336Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100618T140000Z UID:gh6981i2sgkq35ltgd2rivh5co@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (D): Germany vs. Serbia DTSTART:20100618T113000Z CREATED:20091204T195330Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100624T210000Z UID:seulqgt7idfh46eno606gic7l8@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup (E): Cameroon vs. Netherlands DTSTART:20100624T183000Z CREATED:20091204T200521Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100614T163000Z UID:n47qnuit99a6n46mvfemaljso0@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (E): Japan vs. Cameroon DTSTART:20100614T140000Z CREATED:20091204T194531Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100619T210000Z UID:jl83ksobjroqi7536mpsrf9fic@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (E): Cameroon vs. Denmark DTSTART:20100619T183000Z CREATED:20091204T195551Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100615T140000Z UID:fcdrhvksfl422qndh4s9m69f9g@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (F): New Zealand vs. Slovakia DTSTART:20100615T113000Z CREATED:20091204T194647Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100618T210000Z UID:cmje1gbghumejv823ea4hr1p4k@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (C): England vs. Algeria DTSTART:20100618T183000Z CREATED:20091204T195433Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100622T163000Z UID:vliddlfau4o4fc3lcrle8cvha4@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup (A): Mexico vs. Uruguay DTSTART:20100622T140000Z CREATED:20091204T195948Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100619T140000Z UID:tq9isk66uhus4r0b9qek6ubcq8@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (D): Ghana vs. Australia DTSTART:20100619T113000Z CREATED:20091204T195501Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100619T163000Z UID:hs26ltdpulb4inuf0j7u6eemng@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (E): Netherlands vs. Japan DTSTART:20100619T140000Z CREATED:20091204T195525Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100625T210000Z UID:nc8qunlc3aeqh5jajha2s94to4@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup (H): Switzerland vs. Honduras DTSTART:20100625T183000Z CREATED:20091204T200741Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100625T163000Z UID:r56dccttfcm5j0cqgoeoj87gdk@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (G): Portugal vs. Brazil DTSTART:20100625T140000Z CREATED:20091204T200545Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100620T140000Z UID:8u0fqct4g668h84fl53lhtipqc@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (F): Slovakia vs. Paraguay DTSTART:20100620T113000Z CREATED:20091204T195643Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100617T140000Z UID:fj5glmmrh711claboqv2iqv7f4@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (A): France vs. Mexico DTSTART:20100617T113000Z CREATED:20091204T195040Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100621T163000Z UID:krlomofbnafvbe1t5utkvnn72s@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (H): Chile vs. Switzerland DTSTART:20100621T140000Z CREATED:20091204T195857Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100620T210000Z UID:e73g6rnfsoveeri8efjf23em2o@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (G): Brazil vs. Cote d Ivoire DTSTART:20100620T183000Z CREATED:20091204T195745Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100611T163000Z UID:l1r85n07sjfpitbdd8nb03q240@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup (A): South Africa vs Mexico DTSTART:20100611T140000Z CREATED:20091204T192253Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100621T140000Z UID:9faliohjdtit8dfvapht5rehlk@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (G): Portugal vs. Korea DPR DTSTART:20100621T113000Z CREATED:20091204T195833Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100621T210000Z UID:du879ivuckqf7heimudpne98ok@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (H): Spain vs. Honduras DTSTART:20100621T183000Z CREATED:20091204T195922Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100624T163000Z UID:bdaju0gptblq192kq7tlm2lovs@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (F): Slovakia vs. Italy DTSTART:20100624T140000Z CREATED:20091204T200359Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100622T210000Z UID:tqdehpl20dk44hbmpvq29mf308@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup (B): Greece vs. Argentina DTSTART:20100622T183000Z CREATED:20091204T200100Z END:VEVENT BEGIN:VEVENT ATTENDEE;CN="2010 FIFA World Cup";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED;RO LE=REQ-PARTICIPANT:mailto:hioolh3fpnqg16f0ef1r7p1hsg@group.calendar.goog le.com DTEND:20100618T163000Z TRANSP:OPAQUE UID:7294evill0f2kmf60s5j0675uk@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup (C): USA vs Slovenia DTSTART:20100618T140000Z CREATED:20091204T191447Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100614T210000Z UID:9ttktmpdg31dlr8abd9v3iiukg@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (F): Italy vs. Paraguay DTSTART:20100614T183000Z CREATED:20091204T194611Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100623T210000Z UID:ooit2jpoglhn85rl4fmtcomkak@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup (D): Australia vs. Serbia DTSTART:20100623T183000Z CREATED:20091204T200311Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100616T163000Z UID:iuken2fi8savbqpob6rhkaqd38@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (H): Spain vs. Switzerland DTSTART:20100616T140000Z CREATED:20091204T194842Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100624T163000Z UID:lbo6b6k3jv7rgf3iklkiu4idms@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup (F): Paraguay vs. New Zealand DTSTART:20100624T140000Z CREATED:20091204T200438Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100613T210000Z UID:biso0e872fhqbkill5dic5k6d4@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup (D): Serbia vs. Ghana DTSTART:20100613T183000Z CREATED:20091204T194235Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100616T210000Z UID:f2gbe8mpn5bddqmklhl3182t80@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (A): South Africa vs. Uruguay DTSTART:20100616T183000Z CREATED:20091204T195018Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100623T163000Z UID:lva6u92a5d3kem7s5qenhetljg@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup (C): Slovenia vs. England DTSTART:20100623T140000Z CREATED:20091204T200216Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100617T210000Z UID:69qb8sui1hcspdupd1dgjp43sk@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (B): Argentina vs. Korea Rep. DTSTART:20100617T183000Z CREATED:20091204T195140Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100617T163000Z UID:j21vnoucpt6bncjfn897j1h29k@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (B): Greece vs. Nigeria DTSTART:20100617T140000Z CREATED:20091204T195102Z END:VEVENT BEGIN:VEVENT TRANSP:OPAQUE DTEND:20100624T210000Z UID:j80jm97ei9ab4i6p8iiro0squg@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:0 SUMMARY:World Cup (E): Denmark vs. Japan DTSTART:20100624T183000Z CREATED:20091204T200503Z END:VEVENT BEGIN:VEVENT ATTENDEE;CN="2010 FIFA World Cup";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED;RO LE=REQ-PARTICIPANT:mailto:hioolh3fpnqg16f0ef1r7p1hsg@group.calendar.goog le.com DTEND:20100612T210000Z TRANSP:OPAQUE UID:chgemsmh5eks3lb03nat8hmapg@google.com DTSTAMP:20091204T224514Z LOCATION: DESCRIPTION: STATUS:CONFIRMED SEQUENCE:2 SUMMARY:World Cup (C): USA vs ENG DTSTART:20100612T183000Z CREATED:20091204T191346Z END:VEVENT BEGIN:VEVENT CREATED:20091205T080346Z UID:64084E04-0DA9-4B33-95D1-79AC69B94288 DTEND;TZID=Europe/Berlin:20100629T223000 TRANSP:OPAQUE SUMMARY:match 56: 1H - 2G DTSTART;TZID=Europe/Berlin:20100629T203000 DTSTAMP:20091205T110824Z SEQUENCE:4 END:VEVENT BEGIN:VEVENT CREATED:20091205T080215Z UID:ADE0C036-E788-402A-BFE2-C4A6D4ADB6AA DTEND;TZID=Europe/Berlin:20100627T223000 TRANSP:OPAQUE SUMMARY:match 52: 1B - 2A DTSTART;TZID=Europe/Berlin:20100627T203000 DTSTAMP:20091205T110800Z SEQUENCE:6 END:VEVENT BEGIN:VEVENT CREATED:20091205T080346Z UID:692EB017-69AE-4C9A-8DA9-216A5EB446DF DTEND;TZID=Europe/Berlin:20100628T180000 TRANSP:OPAQUE SUMMARY:match 53: 1E - 2F DTSTART;TZID=Europe/Berlin:20100628T160000 DTSTAMP:20091205T110807Z SEQUENCE:4 END:VEVENT BEGIN:VEVENT CREATED:20091205T080924Z UID:D8544EAB-AEF7-45F5-9AA7-C483158C1226 DTEND;TZID=Europe/Berlin:20100703T180000 TRANSP:OPAQUE SUMMARY:QF match 59: winner 52 - winner 51 DTSTART;TZID=Europe/Berlin:20100703T160000 DTSTAMP:20091205T111536Z SEQUENCE:4 END:VEVENT BEGIN:VEVENT CREATED:20091205T081300Z UID:E7DA6125-613C-472B-B113-BE0AA0B6940B DTEND;TZID=Europe/Berlin:20100710T223000 TRANSP:OPAQUE SUMMARY:Match for third place: loser 61 - loser 62 DTSTART;TZID=Europe/Berlin:20100710T203000 DTSTAMP:20091205T111111Z SEQUENCE:5 END:VEVENT BEGIN:VEVENT CREATED:20091205T080355Z UID:F0C2ACA5-40A0-4487-B1E7-C4D514FD8CC4 DTEND;TZID=Europe/Berlin:20100628T223000 TRANSP:OPAQUE SUMMARY:match 54: 1G - 2H DTSTART;TZID=Europe/Berlin:20100628T203000 DTSTAMP:20091205T110813Z SEQUENCE:3 END:VEVENT BEGIN:VEVENT CREATED:20091205T080138Z UID:A17719FE-9F77-4522-AD5B-1FC4AF0C2076 DTEND;TZID=Europe/Berlin:20100626T223000 TRANSP:OPAQUE SUMMARY:match 50: 1C - 2D DTSTART;TZID=Europe/Berlin:20100626T203000 DTSTAMP:20091205T110748Z SEQUENCE:4 END:VEVENT BEGIN:VEVENT CREATED:20091205T080346Z UID:88C6B3D2-1FC9-4665-9DF4-3D8F82AC6831 DTEND;TZID=Europe/Berlin:20100629T180000 TRANSP:OPAQUE SUMMARY:match 55: 1F - 2E DTSTART;TZID=Europe/Berlin:20100629T160000 DTSTAMP:20091205T110819Z SEQUENCE:4 END:VEVENT BEGIN:VEVENT CREATED:20091205T080208Z UID:DAB31292-A166-4A51-9B40-23FE4FD46B84 DTEND;TZID=Europe/Berlin:20100627T180000 TRANSP:OPAQUE SUMMARY:match 51: 1D - 2C DTSTART;TZID=Europe/Berlin:20100627T160000 DTSTAMP:20091205T110754Z SEQUENCE:4 END:VEVENT BEGIN:VEVENT CREATED:20091205T081354Z UID:42F0FFA6-08E4-4256-BB27-63FB6F23F47A DTEND;TZID=Europe/Berlin:20100711T223000 TRANSP:OPAQUE SUMMARY:Final: winner 61 - winner 62 DTSTART;TZID=Europe/Berlin:20100711T203000 DTSTAMP:20091205T111003Z SEQUENCE:4 END:VEVENT BEGIN:VEVENT CREATED:20091205T081147Z UID:0386496E-F7C9-42D8-B46B-BC1E9AFCAFD7 DTEND;TZID=Europe/Berlin:20100706T223000 TRANSP:OPAQUE SUMMARY:SF match 61: winner 57 - winner 58 DTSTART;TZID=Europe/Berlin:20100706T203000 DTSTAMP:20091205T111658Z SEQUENCE:7 END:VEVENT BEGIN:VEVENT CREATED:20091205T081216Z UID:6F1C10EF-9A3A-49D6-BF5F-4154556DF7BE DTEND;TZID=Europe/Berlin:20100707T223000 TRANSP:OPAQUE SUMMARY:SF match 62: winner 59 - winner 60 DTSTART;TZID=Europe/Berlin:20100707T203000 DTSTAMP:20091205T111603Z SEQUENCE:4 END:VEVENT BEGIN:VEVENT CREATED:20091205T080124Z UID:B6B00EAA-EC2D-4F79-88A0-D37016C93195 DTEND;TZID=Europe/Berlin:20100626T180000 TRANSP:OPAQUE SUMMARY:match 49: 1A - 2B DTSTART;TZID=Europe/Berlin:20100626T160000 DTSTAMP:20091205T110736Z SEQUENCE:6 END:VEVENT BEGIN:VEVENT CREATED:20091205T080713Z UID:B40893C6-C237-453C-8044-49CBDDD257BE DTEND;TZID=Europe/Berlin:20100702T180000 TRANSP:OPAQUE SUMMARY:QF match 57: winner 53 - winner 54 DTSTART;TZID=Europe/Berlin:20100702T160000 DTSTAMP:20091205T111239Z SEQUENCE:7 END:VEVENT BEGIN:VEVENT CREATED:20091205T080809Z UID:BF5EEDB3-DF11-46C7-A70D-CFFDDC3A4B9B DTEND;TZID=Europe/Berlin:20100702T223000 TRANSP:OPAQUE SUMMARY:QF match 58: winner 49 - winner 50 DTSTART;TZID=Europe/Berlin:20100702T203000 DTSTAMP:20091205T111521Z SEQUENCE:6 END:VEVENT BEGIN:VEVENT CREATED:20091205T080950Z UID:E8B14DB7-8260-4127-9F70-0394B56C8E93 DTEND;TZID=Europe/Berlin:20100703T223000 TRANSP:OPAQUE SUMMARY:match 60: match 55 - match 56 DTSTART;TZID=Europe/Berlin:20100703T203000 DTSTAMP:20091205T110908Z SEQUENCE:3 END:VEVENT END:VCALENDAR tests/auto/versit/qversit/testdata_ics/2010-India-Holidays.ics000066400000000000000000000111111233466112000245170ustar00rootroot00000000000000BEGIN:VCALENDAR VERSION:2.0 METHOD:PUBLISH X-WR-CALNAME:India 2010 Holidays BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100102 DTSTART;VALUE=DATE:20100101 SEQUENCE:0 SUMMARY:New Year's Day TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100114 DTSTART;VALUE=DATE:20100114 SEQUENCE:0 SUMMARY:Pongal TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100126 DTSTART;VALUE=DATE:20100126 SEQUENCE:0 SUMMARY:Republic Day TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100212 DTSTART;VALUE=DATE:20100212 SEQUENCE:3 SUMMARY:Maha Shivaratri TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100227 DTSTART;VALUE=DATE:20100227 SEQUENCE:0 SUMMARY:Prophet's Birthday TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100301 DTSTART;VALUE=DATE:20100301 SEQUENCE:1 SUMMARY:Holi TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100402 DTSTART;VALUE=DATE:20100402 SEQUENCE:1 SUMMARY:Good Friday TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100713 DTSTART;VALUE=DATE:20100713 SEQUENCE:1 SUMMARY:Rath Yatra TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100815 DTSTART;VALUE=DATE:20100815 SEQUENCE:0 SUMMARY:Independence Day TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100823 DTSTART;VALUE=DATE:20100823 SEQUENCE:0 SUMMARY:Onam TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100824 DTSTART;VALUE=DATE:20100824 SEQUENCE:0 SUMMARY:Raksha Bandhan TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100902 DTSTART;VALUE=DATE:20100902 SEQUENCE:0 SUMMARY:Janmashtami TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100911 DTSTART;VALUE=DATE:20100911 SEQUENCE:0 SUMMARY:Vinayaka Chaturthi TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100911 DTSTART;VALUE=DATE:20100911 SEQUENCE:1 SUMMARY:Eid-ul-Fitar TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20101002 DTSTART;VALUE=DATE:20101002 SEQUENCE:0 SUMMARY:Mathatma Gandhi Jayanti TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20101017 DTSTART;VALUE=DATE:20101017 SEQUENCE:0 SUMMARY:Dussehra/Dasara TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20101105 DTSTART;VALUE=DATE:20101105 SEQUENCE:0 SUMMARY:Diwali TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20101117 DTSTART;VALUE=DATE:20101117 SEQUENCE:0 SUMMARY:Bakri Id/Eid ul-Adha TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20101217 DTSTART;VALUE=DATE:20101217 SEQUENCE:0 SUMMARY:Muharram TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20101225 DTSTART;VALUE=DATE:20101225 SEQUENCE:0 SUMMARY:Christmas TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT END:VCALENDAR tests/auto/versit/qversit/testdata_ics/2010-US-Holidays.ics000066400000000000000000000121051233466112000240260ustar00rootroot00000000000000BEGIN:VCALENDAR VERSION:2.0 METHOD:PUBLISH X-WR-CALNAME:US 2010 Holidays BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20060117 DTSTART;VALUE=DATE:20060116 PRIORITY:5 RRULE:FREQ=YEARLY;BYDAY=MO;BYMONTH=1;BYSETPOS=3 SEQUENCE:0 SUMMARY:Martin Luther King\, Jr. Day TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100405 DTSTART;VALUE=DATE:20100404 PRIORITY:5 SEQUENCE:0 SUMMARY:Easter TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20030121 DTSTART;VALUE=DATE:20030120 PRIORITY:5 RRULE:FREQ=YEARLY;COUNT=3;BYDAY=MO;BYMONTH=1;BYSETPOS=3 SEQUENCE:0 SUMMARY:Martin Luther King\, Jr's Day TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20101012 DTSTART;VALUE=DATE:20101011 PRIORITY:5 SEQUENCE:0 SUMMARY:Columbus Day TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20101126 DTSTART;VALUE=DATE:20101125 PRIORITY:5 SEQUENCE:0 SUMMARY:Thanksgiving TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20061101 DTSTART;VALUE=DATE:20061031 PRIORITY:5 RRULE:FREQ=YEARLY;BYMONTHDAY=31;BYMONTH=10 SEQUENCE:0 SUMMARY:Halloween TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100509 DTSTART;VALUE=DATE:20100509 PRIORITY:5 SEQUENCE:0 SUMMARY:Mother's Day TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100620 DTSTART;VALUE=DATE:20100620 PRIORITY:5 SEQUENCE:0 SUMMARY:Father's Day TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100906 DTSTART;VALUE=DATE:20100906 PRIORITY:5 SEQUENCE:0 SUMMARY:Labor Day TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100102 DTSTART;VALUE=DATE:20100101 PRIORITY:5 SEQUENCE:0 SUMMARY:New Year's Day TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20021226 DTSTART;VALUE=DATE:20021225 PRIORITY:5 RRULE:FREQ=YEARLY;BYMONTHDAY=25;BYMONTH=12 SEQUENCE:0 SUMMARY:Christmas TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20021112 DTSTART;VALUE=DATE:20021111 PRIORITY:5 RRULE:FREQ=YEARLY;BYMONTHDAY=11;BYMONTH=11 SEQUENCE:0 SUMMARY:Veteran's Day TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20020215 DTSTART;VALUE=DATE:20020214 PRIORITY:5 RRULE:FREQ=YEARLY;BYMONTHDAY=14;BYMONTH=2 SEQUENCE:0 SUMMARY:Valentine's Day TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100601 DTSTART;VALUE=DATE:20100531 PRIORITY:5 SEQUENCE:1 SUMMARY:Memorial Day TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20020102 DTSTART;VALUE=DATE:20020101 PRIORITY:5 RRULE:FREQ=YEARLY;COUNT=4;BYMONTHDAY=1;BYMONTH=1 SEQUENCE:2 SUMMARY:New Year's TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20020122 DTSTART;VALUE=DATE:20020121 PRIORITY:5 RRULE:FREQ=YEARLY;COUNT=1;BYDAY=MO;BYMONTH=1;BYSETPOS=3 SEQUENCE:2 SUMMARY:Martin Luther King\, Jr's Davy TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20020705 DTSTART;VALUE=DATE:20020704 PRIORITY:5 RRULE:FREQ=YEARLY;BYMONTHDAY=4;BYMONTH=7 SEQUENCE:1 SUMMARY:Independence Day TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20090217 DTSTART;VALUE=DATE:20090216 PRIORITY:5 SEQUENCE:0 SUMMARY;LANGUAGE=en-us:Washington’s Birthday TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT BEGIN:VEVENT CLASS:PUBLIC DTEND;VALUE=DATE:20100216 DTSTART;VALUE=DATE:20100215 PRIORITY:5 SEQUENCE:0 SUMMARY;LANGUAGE=en-us:Presidents' Day TRANSP:TRANSPARENT DESCRIPTION:Visit http://www.calendarlabs.com for any type of calendar needs. END:VEVENT END:VCALENDAR tests/auto/versit/qversit/testdata_ics/Asiacup2010.ics000066400000000000000000000061471233466112000232460ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//ESPN Cricinfo//Series Fixtures//EN VERSION:2.0 METHOD:PUBLISH BEGIN:VEVENT DESCRIPTION:Follow every ball of every international match live on Cricinfo.com.\n\nGet all the latest Asia Cup news at: http://www.cricinfo.com/asia2010/content/series/424694.html SUMMARY:1st Match Sri Lanka v Pakistan DTSTART:20100615T090000Z DTEND:20100615T170000Z LOCATION:Rangiri Dambulla International Stadium TRANSP:OPAQUE UID:455231_0 BEGIN:VALARM TRIGGER:-PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM END:VEVENT BEGIN:VEVENT DESCRIPTION:Follow every ball of every international match live on Cricinfo.com.\n\nGet all the latest Asia Cup news at: http://www.cricinfo.com/asia2010/content/series/424694.html SUMMARY:2nd Match Bangladesh v India DTSTART:20100616T090000Z DTEND:20100616T170000Z LOCATION:Rangiri Dambulla International Stadium TRANSP:OPAQUE UID:455232_0 BEGIN:VALARM TRIGGER:-PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM END:VEVENT BEGIN:VEVENT DESCRIPTION:Follow every ball of every international match live on Cricinfo.com.\n\nGet all the latest Asia Cup news at: http://www.cricinfo.com/asia2010/content/series/424694.html SUMMARY:3rd Match Sri Lanka v Bangladesh DTSTART:20100618T090000Z DTEND:20100618T170000Z LOCATION:Rangiri Dambulla International Stadium TRANSP:OPAQUE UID:455233_0 BEGIN:VALARM TRIGGER:-PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM END:VEVENT BEGIN:VEVENT DESCRIPTION:Follow every ball of every international match live on Cricinfo.com.\n\nGet all the latest Asia Cup news at: http://www.cricinfo.com/asia2010/content/series/424694.html SUMMARY:4th Match India v Pakistan DTSTART:20100619T090000Z DTEND:20100619T170000Z LOCATION:Rangiri Dambulla International Stadium TRANSP:OPAQUE UID:455234_0 BEGIN:VALARM TRIGGER:-PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM END:VEVENT BEGIN:VEVENT DESCRIPTION:Follow every ball of every international match live on Cricinfo.com.\n\nGet all the latest Asia Cup news at: http://www.cricinfo.com/asia2010/content/series/424694.html SUMMARY:5th Match Bangladesh v Pakistan DTSTART:20100621T090000Z DTEND:20100621T170000Z LOCATION:Rangiri Dambulla International Stadium TRANSP:OPAQUE UID:455235_0 BEGIN:VALARM TRIGGER:-PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM END:VEVENT BEGIN:VEVENT DESCRIPTION:Follow every ball of every international match live on Cricinfo.com.\n\nGet all the latest Asia Cup news at: http://www.cricinfo.com/asia2010/content/series/424694.html SUMMARY:6th Match Sri Lanka v India DTSTART:20100622T090000Z DTEND:20100622T170000Z LOCATION:Rangiri Dambulla International Stadium TRANSP:OPAQUE UID:455236_0 BEGIN:VALARM TRIGGER:-PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM END:VEVENT BEGIN:VEVENT DESCRIPTION:Follow every ball of every international match live on Cricinfo.com.\n\nGet all the latest Asia Cup news at: http://www.cricinfo.com/asia2010/content/series/424694.html SUMMARY:Final TBD v TBD DTSTART:20100624T090000Z DTEND:20100624T170000Z LOCATION:Rangiri Dambulla International Stadium TRANSP:OPAQUE UID:455237_0 BEGIN:VALARM TRIGGER:-PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM END:VEVENT END:VCALENDAR tests/auto/versit/qversit/testdata_ics/FinlandHolidays.ics000066400000000000000000000573451233466112000244340ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 BEGIN:VTIMEZONE TZID:Europe/Helsinki X-LIC-LOCATION:Europe/Helsinki BEGIN:DAYLIGHT TZOFFSETFROM:+0200 TZOFFSETTO:+0300 TZNAME:EEST DTSTART:19700329T030000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0300 TZOFFSETTO:+0200 TZNAME:EET DTSTART:19701025T040000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 END:STANDARD END:VTIMEZONE BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20070314T203413Z DTSTAMP:20080102T191704Z UID:uuid1173902329173 SUMMARY:Laskiasisunnuntai DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070218 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070219 END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20070314T203413Z DTSTAMP:20080102T191704Z UID:uuid1173902521400 SUMMARY:Pitkäperjantai DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070406 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070407 END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20070314T203413Z DTSTAMP:20080102T191704Z UID:uuid1173902679858 SUMMARY:Palmusunnuntai DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070401 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070402 END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20070314T203413Z DTSTAMP:20080102T191704Z UID:uuid1173902703892 SUMMARY:Pääsiäispäivä DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070408 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070409 END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20070314T203413Z DTSTAMP:20080102T191704Z UID:uuid1173902720596 SUMMARY:2. pääsiäispäivä DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070409 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070410 END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20070314T203413Z DTSTAMP:20080102T191704Z UID:uuid1173902797747 SUMMARY:Helatorstai DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070517 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070518 END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20070314T203413Z DTSTAMP:20080102T191704Z UID:uuid1173902826809 SUMMARY:Helluntaipäivä DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070527 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070528 END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20070314T203413Z DTSTAMP:20080102T191704Z UID:uuid1173902901066 SUMMARY:Juhannuspäivä DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070623 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070624 END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20070314T203413Z DTSTAMP:20080102T191704Z UID:uuid1173903578279 SUMMARY:Pyhäinpäivä DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20071103 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20071104 END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20070314T203413Z DTSTAMP:20080102T191704Z UID:uuid1173904211991 SUMMARY:Kaatuneiden muistopäivä DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070520 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070521 END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20080102T175934Z DTSTAMP:20080102T191704Z UID:uuid1173903924447 SUMMARY:Syyspäivän tasaus DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070923 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070924 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20080102T181707Z DTSTAMP:20080102T191704Z UID:uuid1173903838634 SUMMARY:Talvipäivänseisaus DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20071222 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20071223 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T182142Z LAST-MODIFIED:20080102T182210Z DTSTAMP:20080102T191704Z UID:6a6d8ec1-cc31-4f5d-b29b-680ff180a592 SUMMARY:Laskiaissunnuntai DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20080203 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20080204 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T182838Z LAST-MODIFIED:20080102T182904Z DTSTAMP:20080102T191704Z UID:d8d0678f-288a-482d-b771-29357737c979 SUMMARY:Pääsiäispäivä DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20080323 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20080324 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T182915Z LAST-MODIFIED:20080102T182937Z DTSTAMP:20080102T191704Z UID:479bcd2a-d3a3-4d54-8945-098d10df2f18 SUMMARY:Pitkäperjantai DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20080321 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20080322 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T183032Z LAST-MODIFIED:20080102T183115Z DTSTAMP:20080102T191704Z UID:c81db456-f410-458c-af6c-39d2cc812a46 SUMMARY:2. pääsiäispäivä DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20080324 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20080325 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T183148Z LAST-MODIFIED:20080102T183207Z DTSTAMP:20080102T191704Z UID:473447c1-5d9e-4dd6-9f9f-4c664afcec94 SUMMARY:Helatorstai DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20080501 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20080502 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T183326Z LAST-MODIFIED:20080102T183425Z DTSTAMP:20080102T191704Z UID:d1f13d2f-1c7c-4a74-8ee8-62ac9efcd1aa SUMMARY:Helluntaipäivä DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20080511 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20080512 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20080102T184134Z DTSTAMP:20080102T191704Z UID:uuid1173904035186 SUMMARY:Kesäpäivän seisaus DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070621 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070622 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T184148Z LAST-MODIFIED:20080102T184206Z DTSTAMP:20080102T191704Z UID:260ccb4f-3cdd-4a47-884f-201f9d7f551a SUMMARY:Juhannuspäivä DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20080621 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20080622 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T184210Z LAST-MODIFIED:20080102T184229Z DTSTAMP:20080102T191704Z UID:8fd5014e-8686-40d5-abbe-322f06922624 SUMMARY:Juhannusaatto DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20080620 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20080621 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T184246Z LAST-MODIFIED:20080102T184308Z DTSTAMP:20080102T191704Z UID:b9214b26-1db8-4f02-9ecd-e4feabe8152b SUMMARY:Kesäpäivänseisaus DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20080621 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20080622 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T184635Z LAST-MODIFIED:20080102T184658Z DTSTAMP:20080102T191704Z UID:de4fd81a-c953-4484-84bc-7bf490f4153f SUMMARY:Syyspäiväntasaus DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20080922 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20080923 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T185139Z LAST-MODIFIED:20080102T185200Z DTSTAMP:20080102T191704Z UID:0fb158c5-3029-45cd-9ca5-2846f9d171bd SUMMARY:Mikkelinpäivä DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20081005 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20081006 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T185425Z LAST-MODIFIED:20080102T185446Z DTSTAMP:20080102T191704Z UID:ed561567-f9ba-49cd-9546-80f08b34ed17 SUMMARY:Mikkelinpäivä DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20071104 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20071105 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T185548Z LAST-MODIFIED:20080102T185628Z DTSTAMP:20080102T191704Z UID:640ec471-4970-419f-9bcb-f8a00211067b SUMMARY:Pyhäinpäivä DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20081101 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20081102 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T190903Z LAST-MODIFIED:20080102T190924Z DTSTAMP:20080102T191704Z UID:4137184c-d07d-42be-a8ab-e2ea07bea95c SUMMARY:1. adventtisunnuntai DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20081130 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20081201 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T190952Z LAST-MODIFIED:20080102T191016Z DTSTAMP:20080102T191704Z UID:7bd114fe-0c02-485e-b161-9c25815f42b2 SUMMARY:2. adventtisunnuntai DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20081207 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20081208 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T191050Z LAST-MODIFIED:20080102T191117Z DTSTAMP:20080102T191704Z UID:74b1e961-6048-4fce-9088-ceaf4a0bc5ed SUMMARY:3. adventtisunnuntai DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20081214 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20081215 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T191141Z LAST-MODIFIED:20080102T191213Z DTSTAMP:20080102T191704Z UID:b725f491-bc76-40b1-8782-b4f9f42d6085 SUMMARY:4. adventtisunnuntai DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20081221 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20081222 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T055853Z LAST-MODIFIED:20081223T055912Z DTSTAMP:20081223T055912Z UID:68a4aa5e-a290-498d-a820-1f30d03d2c46 SUMMARY:Talvipäivänseisaus DTSTART;VALUE=DATE:20081221 DTEND;VALUE=DATE:20081222 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T055853Z LAST-MODIFIED:20081223T060348Z DTSTAMP:20081223T060348Z UID:23717c45-cee8-48c6-ac74-1d009a77d7be SUMMARY:Talvipäivänseisaus DTSTART;VALUE=DATE:20091221 DTEND;VALUE=DATE:20091222 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T055853Z LAST-MODIFIED:20081223T060411Z DTSTAMP:20081223T060411Z UID:04339833-a55f-4d76-87c8-7d8afdda9c5f SUMMARY:Talvipäivänseisaus DTSTART;VALUE=DATE:20101222 DTEND;VALUE=DATE:20101223 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T055853Z LAST-MODIFIED:20081223T060417Z DTSTAMP:20081223T060417Z UID:b0a03de9-57ba-4cc2-89f2-54b0144273a8 SUMMARY:Talvipäivänseisaus DTSTART;VALUE=DATE:20111222 DTEND;VALUE=DATE:20111223 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T055853Z LAST-MODIFIED:20081223T060422Z DTSTAMP:20081223T060422Z UID:1f243a4e-1731-4970-b973-a84f79a23089 SUMMARY:Talvipäivänseisaus DTSTART;VALUE=DATE:20121221 DTEND;VALUE=DATE:20121222 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T182142Z LAST-MODIFIED:20081223T060731Z DTSTAMP:20081223T060731Z UID:093c59dc-3859-493f-84d1-1fe26fe47aab SUMMARY:Laskiaissunnuntai DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20090222 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20090223 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T061128Z LAST-MODIFIED:20081223T061140Z DTSTAMP:20081223T061140Z UID:f7a90fca-1dcf-4bc3-b02a-f56eb2910377 SUMMARY:Kevätpäiväntasaus DTSTART;VALUE=DATE:20090320 DTEND;VALUE=DATE:20090321 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T061337Z LAST-MODIFIED:20081223T061349Z DTSTAMP:20081223T061349Z UID:123d57f6-b87a-4485-835d-47f7c81bca3a SUMMARY:Palmusunnuntai DTSTART;VALUE=DATE:20090405 DTEND;VALUE=DATE:20090406 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T061416Z LAST-MODIFIED:20081223T061427Z DTSTAMP:20081223T061427Z UID:10e9309a-3934-4aba-9fba-4aaf6b0bc67d SUMMARY:Pitkäperjantai DTSTART;VALUE=DATE:20090410 DTEND;VALUE=DATE:20090411 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T061501Z LAST-MODIFIED:20081223T061516Z DTSTAMP:20081223T061516Z UID:8d7ce620-450a-4859-bcaa-935d854e518e SUMMARY:Pääsiäispäivä DTSTART;VALUE=DATE:20090412 DTEND;VALUE=DATE:20090413 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T061559Z LAST-MODIFIED:20081223T061617Z DTSTAMP:20081223T061617Z UID:dc013c35-1101-4416-8673-9182b8bf9c20 SUMMARY:2. pääsiäispäivä DTSTART;VALUE=DATE:20090413 DTEND;VALUE=DATE:20090414 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T061645Z LAST-MODIFIED:20081223T061659Z DTSTAMP:20081223T061659Z UID:1f0b491b-3793-434d-8377-342dbaebb0f3 SUMMARY:Helatorstai DTSTART;VALUE=DATE:20090521 DTEND;VALUE=DATE:20090522 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T061758Z LAST-MODIFIED:20081223T061815Z DTSTAMP:20081223T061815Z UID:fc1ee8c9-e7af-4680-9020-e651faa5e737 SUMMARY:Helluntaipäivä DTSTART;VALUE=DATE:20090531 DTEND;VALUE=DATE:20090601 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T061843Z LAST-MODIFIED:20081223T061900Z DTSTAMP:20081223T061900Z UID:f2fea8cb-4a70-482a-8a06-a57d1c302bc7 SUMMARY:Juhannuspäivä DTSTART;VALUE=DATE:20090620 DTEND;VALUE=DATE:20090621 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T061914Z LAST-MODIFIED:20081223T061928Z DTSTAMP:20081223T061928Z UID:4fd08064-8ba1-4d55-9a33-0b0d83d3e4eb SUMMARY:Juhannusaatto DTSTART;VALUE=DATE:20090619 DTEND;VALUE=DATE:20090620 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T062023Z LAST-MODIFIED:20081223T062031Z DTSTAMP:20081223T062031Z UID:f6ef8660-0a66-4c4f-9abb-42d254226517 SUMMARY:Kesäpäivänseisaus DTSTART;VALUE=DATE:20090621 DTEND;VALUE=DATE:20090622 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T062213Z LAST-MODIFIED:20081223T062228Z DTSTAMP:20081223T062228Z UID:910e4390-9ca2-4de5-b81f-72ab5e3a2862 SUMMARY:Syyspäiväntasaus DTSTART;VALUE=DATE:20090923 DTEND;VALUE=DATE:20090924 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T062255Z LAST-MODIFIED:20081223T062307Z DTSTAMP:20081223T062307Z UID:9889cac6-0bea-4554-a8cc-112e56cc3fed SUMMARY:Pyhäinpäivä DTSTART;VALUE=DATE:20091031 DTEND;VALUE=DATE:20091101 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T062505Z LAST-MODIFIED:20081223T062525Z DTSTAMP:20081223T062525Z UID:4b25fdc1-2e44-402b-a2eb-2c3b8db60487 SUMMARY:1. adventtisunnuntai DTSTART;VALUE=DATE:20091129 DTEND;VALUE=DATE:20091130 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T062505Z LAST-MODIFIED:20081223T062717Z DTSTAMP:20081223T062717Z UID:65367f72-be7f-4175-85f5-be7ffde97160 SUMMARY:2. adventtisunnuntai DTSTART;VALUE=DATE:20091206 DTEND;VALUE=DATE:20091207 TRANSP:TRANSPARENT X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20081223T062505Z LAST-MODIFIED:20081223T062742Z DTSTAMP:20081223T062742Z UID:ba253a68-41ae-4fd8-a1f2-459fcbd8bae6 SUMMARY:3. adventtisunnuntai DTSTART;VALUE=DATE:20091213 DTEND;VALUE=DATE:20091214 TRANSP:TRANSPARENT X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20081223T062505Z LAST-MODIFIED:20081223T062801Z DTSTAMP:20081223T062801Z UID:b5f3b99c-767c-4ce5-98d3-c1fe513101f0 SUMMARY:4. adventtisunnuntai DTSTART;VALUE=DATE:20091220 DTEND;VALUE=DATE:20091221 TRANSP:TRANSPARENT X-MOZ-GENERATION:2 END:VEVENT BEGIN:VEVENT CREATED:20081223T063018Z LAST-MODIFIED:20081223T063029Z DTSTAMP:20081223T063029Z UID:e9f868fd-2d3c-4a7c-9d04-4e14cded7214 SUMMARY:Laskiaistiistai DTSTART;VALUE=DATE:20090224 DTEND;VALUE=DATE:20090225 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20081223T063537Z LAST-MODIFIED:20081223T063551Z DTSTAMP:20081223T063551Z UID:24e26456-1a18-437a-b29b-94a02e45ba5d SUMMARY:Mikkelinpäivä DTSTART;VALUE=DATE:20091004 DTEND;VALUE=DATE:20091005 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20091205T210752Z LAST-MODIFIED:20091205T211016Z DTSTAMP:20091205T211016Z UID:ee87e625-440f-4025-b006-b807dc1428e0 SUMMARY:Pääsiäispäivä DTSTART;VALUE=DATE:20100404 DTEND;VALUE=DATE:20100405 TRANSP:TRANSPARENT SEQUENCE:1 X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20091205T211107Z LAST-MODIFIED:20091205T211128Z DTSTAMP:20091205T211128Z UID:a6103548-e601-41c1-aba9-3b00c5d47268 SUMMARY:2. pääsiäispäivä DTSTART;VALUE=DATE:20100405 DTEND;VALUE=DATE:20100406 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20091205T211203Z LAST-MODIFIED:20091205T211219Z DTSTAMP:20091205T211219Z UID:8df89a50-9709-4a3f-a057-923cc0278b17 SUMMARY:Helatorstai DTSTART;VALUE=DATE:20100513 DTEND;VALUE=DATE:20100514 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20091205T211343Z LAST-MODIFIED:20091205T211408Z DTSTAMP:20091205T211408Z UID:487748a6-a423-491f-af1a-f664b52ca3f3 SUMMARY:Helluntaipäivä DTSTART;VALUE=DATE:20100523 DTEND;VALUE=DATE:20100524 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20091205T211556Z LAST-MODIFIED:20091205T211621Z DTSTAMP:20091205T211621Z UID:fae1a689-1345-4ff7-8f36-959e734be6ea SUMMARY:Juhannuspäivä DTSTART;VALUE=DATE:20100626 DTEND;VALUE=DATE:20100627 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20091205T211644Z LAST-MODIFIED:20091205T211701Z DTSTAMP:20091205T211701Z UID:73c8c5a1-a2f7-445a-9b3a-0b74bd39e578 SUMMARY:Juhannusatto DTSTART;VALUE=DATE:20100625 DTEND;VALUE=DATE:20100626 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20091205T211944Z LAST-MODIFIED:20091205T212010Z DTSTAMP:20091205T212010Z UID:a7f5c87d-628a-4029-98fc-c8b0e15bc09b SUMMARY:Syyspäiväntasaus DTSTART;VALUE=DATE:20100923 DTEND;VALUE=DATE:20100924 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20091205T212137Z LAST-MODIFIED:20091205T212158Z DTSTAMP:20091205T212158Z UID:8091b7ea-570f-4f8f-8796-aae8c63837ad SUMMARY:Pyhäinpäivä DTSTART;VALUE=DATE:20101106 DTEND;VALUE=DATE:20101107 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20091205T212525Z LAST-MODIFIED:20091205T212550Z DTSTAMP:20091205T212550Z UID:c6613547-5d80-4d43-a5fb-5bf6fe7aa9ad SUMMARY:1. adventtisunnuntai DTSTART;VALUE=DATE:20101128 DTEND;VALUE=DATE:20101129 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20091205T212642Z LAST-MODIFIED:20091205T212706Z DTSTAMP:20091205T212706Z UID:d123ccec-8f0c-4af3-b5d8-4f0f9c005272 SUMMARY:2. adventtisunnuntai DTSTART;VALUE=DATE:20101205 DTEND;VALUE=DATE:20101206 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20091205T212726Z LAST-MODIFIED:20091205T212739Z DTSTAMP:20091205T212739Z UID:2c0fade3-abde-4def-bf06-07d647ee2c6c SUMMARY:3. adventtisunnuntai DTSTART;VALUE=DATE:20101212 DTEND;VALUE=DATE:20101213 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20091205T212757Z LAST-MODIFIED:20091205T212840Z DTSTAMP:20091205T212840Z UID:4984470b-7937-4a9b-b177-ea94e159f33f SUMMARY:4. adventtisunnuntai DTSTART;VALUE=DATE:20101219 DTEND;VALUE=DATE:20101220 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20091205T210632Z LAST-MODIFIED:20091205T213127Z DTSTAMP:20091205T213127Z UID:232de986-a010-4031-aa4d-57c3e7c366c9 SUMMARY:Palmusunnuntai DTSTART;VALUE=DATE:20100328 DTEND;VALUE=DATE:20100329 TRANSP:TRANSPARENT SEQUENCE:1 X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20091205T210732Z LAST-MODIFIED:20091205T213340Z DTSTAMP:20091205T213340Z UID:76f0ed00-fbd3-4e9f-b2d2-aeb6a3fa9e09 SUMMARY:Pitkäperjantai DTSTART;VALUE=DATE:20100402 DTEND;VALUE=DATE:20100403 TRANSP:TRANSPARENT X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20091205T213715Z LAST-MODIFIED:20091205T213737Z DTSTAMP:20091205T213737Z UID:3e3395ad-dd56-457e-ba78-5f80e062bd52 SUMMARY:Kesäpäivänseisaus DTSTART;VALUE=DATE:20100621 DTEND;VALUE=DATE:20100622 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20091205T213820Z LAST-MODIFIED:20091205T213837Z DTSTAMP:20091205T213837Z UID:167c0769-12c2-4f8a-a0a8-a7a263bf82fb SUMMARY:Kevätpäiväntasaus DTSTART;VALUE=DATE:20100320 DTEND;VALUE=DATE:20100321 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20091205T213930Z LAST-MODIFIED:20091205T213946Z DTSTAMP:20091205T213946Z UID:484e386b-6fdc-4a2d-b012-909afa64ffa0 SUMMARY:Laskiaissunnuntai DTSTART;VALUE=DATE:20100214 DTEND;VALUE=DATE:20100215 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20091205T214030Z LAST-MODIFIED:20091205T214050Z DTSTAMP:20091205T214050Z UID:be93f747-8a3c-4b28-a366-2292e75cebb3 SUMMARY:Laskiaistiistai DTSTART;VALUE=DATE:20100216 DTEND;VALUE=DATE:20100217 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20080102T172837Z DTSTAMP:20080102T191704Z UID:uuid1173904352072 SUMMARY:Uudenvuoden päivä RRULE:FREQ=YEARLY DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070101 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070102 END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20080102T172853Z DTSTAMP:20080102T191704Z UID:uuid1173904370248 SUMMARY:Loppiainen RRULE:FREQ=YEARLY DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070106 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070107 END:VEVENT BEGIN:VEVENT CREATED:20080102T173716Z LAST-MODIFIED:20080102T173733Z DTSTAMP:20080102T191704Z UID:3efb2264-6af2-4a14-a884-bd782c4db846 SUMMARY:Ystävänpäivä RRULE:FREQ=YEARLY DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070214 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070215 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20080102T174336Z DTSTAMP:20080102T191704Z UID:uuid1173904290944 SUMMARY:Kesäaika alkaa RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070325 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070326 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T174620Z LAST-MODIFIED:20080102T174908Z DTSTAMP:20080102T191704Z UID:91312106-7a42-47e7-bb08-932df2e5bb8e SUMMARY:Vapunaatto RRULE:FREQ=YEARLY DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070430 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070501 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20080102T175025Z DTSTAMP:20080102T191704Z UID:uuid1173902771209 SUMMARY:Vappu RRULE:FREQ=YEARLY DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070501 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070502 END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20080102T175328Z DTSTAMP:20080102T191704Z UID:uuid1173904176169 SUMMARY:Äitienpäivä RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=5 DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20070513 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20070514 END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20080102T180508Z DTSTAMP:20080102T191704Z UID:uuid1173903515119 SUMMARY:Kesäaika päättyy RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20071028 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20071029 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20080102T181136Z DTSTAMP:20080102T191704Z UID:uuid1173903607401 SUMMARY:Isänpäivä RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=11 DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20071111 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20071112 END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20080102T181439Z DTSTAMP:20080102T191704Z UID:uuid1173903722337 SUMMARY:Itsenäisyyspäivä RRULE:FREQ=YEARLY DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20071206 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20071207 END:VEVENT BEGIN:VEVENT CREATED:20080102T181622Z LAST-MODIFIED:20080102T181654Z DTSTAMP:20080102T191704Z UID:7e288df4-770c-4899-a220-9da337943e5a SUMMARY:Lucian päivä RRULE:FREQ=YEARLY DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20071213 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20071214 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T181745Z LAST-MODIFIED:20080102T181820Z DTSTAMP:20080102T191704Z UID:9cd77dfc-1995-43f0-9bcf-7a40f87363c7 SUMMARY:Jouluaatto RRULE:FREQ=YEARLY DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20071224 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20071225 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20080102T181900Z LAST-MODIFIED:20080102T182004Z DTSTAMP:20080102T191704Z UID:c46a14c1-034a-4b37-85f0-5c4a42a31db7 SUMMARY:Uudenvuodenaatto RRULE:FREQ=YEARLY DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20071231 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20080101 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20080102T191259Z DTSTAMP:20080102T191704Z UID:uuid1173903783765 SUMMARY:Joulupäivä RRULE:FREQ=YEARLY DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20071225 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20071226 END:VEVENT BEGIN:VEVENT CREATED:20070314T203413Z LAST-MODIFIED:20080102T191326Z DTSTAMP:20080102T191704Z UID:uuid1173903799728 SUMMARY:Tapaninpäivä RRULE:FREQ=YEARLY DTSTART;VALUE=DATE;TZID=Europe/Helsinki:20071226 DTEND;VALUE=DATE;TZID=Europe/Helsinki:20071227 END:VEVENT END:VCALENDAR tests/auto/versit/qversit/testdata_ics/IndiaHolidays.ics000066400000000000000000001205631233466112000240760ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN VERSION:2.0 X-WR-CALNAME:IndiaHolidays X-WR-TIMEZONE:America/Los_Angeles BEGIN:VTIMEZONE TZID:America/Dawson X-LIC-LOCATION:America/Dawson BEGIN:DAYLIGHT TZOFFSETFROM:-0800 TZOFFSETTO:-0700 TZNAME:PDT DTSTART:19700308T020000 RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3 END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:-0700 TZOFFSETTO:-0800 TZNAME:PST DTSTART:19701101T020000 RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11 END:STANDARD END:VTIMEZONE BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20081223T231126Z DTSTAMP:20051221T145035Z UID:2002-01-01-1 SUMMARY:New Year's Day STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Restricted Holiday DTSTART;VALUE=DATE:20090101 DTEND;VALUE=DATE:20090102 CLASS:PUBLIC X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:3 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T031644Z DTSTAMP:20100131T031644Z UID:2002-01-21-1 SUMMARY:Guru Gobind Singh Jayanti STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Public Holiday DTSTART;VALUE=DATE:20100105 DTEND;VALUE=DATE:20100106 CLASS:PUBLIC DESCRIPTION:The 10th and the final guru of the Sikh faith was born on thi s day in the year 1666. His birthday generally falls in December or Januar y or sometimes twice within a year as it is calculated according to Hindu Bikrami calendar based on moon-year. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:3 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20081223T231119Z DTSTAMP:20051221T134735Z UID:2002-01-13-1 SUMMARY:Bhogi STATUS:CONFIRMED RRULE:FREQ=YEARLY EXDATE;VALUE=DATE:20110113 CATEGORIES:Festival DTSTART;VALUE=DATE:20090113 DTEND;VALUE=DATE:20090114 CLASS:PUBLIC X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:3 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20081223T231109Z DTSTAMP:20051221T134735Z UID:2002-01-14-1 SUMMARY:Sankranti STATUS:CONFIRMED RRULE:FREQ=YEARLY EXDATE;VALUE=DATE:20110114 CATEGORIES:Restricted Holiday DTSTART;VALUE=DATE:20090114 DTEND;VALUE=DATE:20090115 CLASS:PUBLIC DESCRIPTION:This holy day marks the commencement of the Sun's northern cou rse in the Heavens\, known as the Uttaraayana patha. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:4 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20081223T231057Z DTSTAMP:20051221T134735Z UID:2002-01-15-1 SUMMARY:Pongal STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Restricted Holiday DTSTART;VALUE=DATE:20090115 DTEND;VALUE=DATE:20090116 CLASS:PUBLIC DESCRIPTION:Usually comes a day after Sankranti. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T142234Z LAST-MODIFIED:20081226T162838Z DTSTAMP:20071219T142234Z UID:2002-01-15-1 SUMMARY:Pongal STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20110115 DTSTART;VALUE=DATE:20080116 DTEND;VALUE=DATE:20080117 CLASS:PUBLIC END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20081223T231304Z DTSTAMP:20051221T145035Z UID:2002-01-26-1 SUMMARY:Republic Day STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:National Holiday DTSTART;VALUE=DATE:20090126 DTEND;VALUE=DATE:20090127 CLASS:PUBLIC DESCRIPTION:On this day in 1950 India became a Sovereign Democratic Republ ic with a constitution to guide her destiny. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20081223T231254Z DTSTAMP:20030102T193611Z UID:902705443 SUMMARY:Netaji's birthday STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Miscellaneous DTSTART;VALUE=DATE:20090123 DTEND;VALUE=DATE:20090124 CLASS:PUBLIC DESCRIPTION:Netaji Subhash Chandra Bose was born on this day in the year 1 897 in Cuttack\, Orissa\, India. Netaji was a fierce and popular leader i n the political scene in pre-independence India. He founded the Indian Na tional Arm. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20081223T231315Z DTSTAMP:20051222T031701Z UID:2002-01-30-1 SUMMARY:Mahatma's martyrdom day STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:National Holiday DTSTART;VALUE=DATE:20090130 DTEND;VALUE=DATE:20090131 CLASS:PUBLIC DESCRIPTION:On this day in 1948 a Hindu fanatic\, Nathuram Godse who oppos ed the mahatma's program of tolerance for all creeds and religion assassin ated the mahatma. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T142014Z LAST-MODIFIED:20081226T162838Z DTSTAMP:20071219T142014Z UID:2002-01-30-1 SUMMARY:Mahatma's martyrdom day STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20150130 DTSTART;VALUE=DATE:20080130 DTEND;VALUE=DATE:20080131 CLASS:PUBLIC END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T033321Z DTSTAMP:20100131T033321Z UID:41e41b70-7235-11da-8b29-b8edd2384ed6 SUMMARY:Nanakshahi New Year STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Festival DTSTART;VALUE=DATE:20100314 DTEND;VALUE=DATE:20100315 CLASS:PUBLIC DESCRIPTION:Sikh New Year's Day from the Nanakshahi calendar. X-MOZILLA-RECUR-DEFAULT-UNITS:years SEQUENCE:1 X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T032252Z DTSTAMP:20100131T032252Z UID:2002-05-25-1 SUMMARY:ID-I-Milad-un-Nabi (Barah-Wafat) STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:National Holiday DTSTART;VALUE=DATE:20100226 DTEND;VALUE=DATE:20100227 CLASS:PUBLIC DESCRIPTION:The Prophet was born on the twelfth day of Rabi-ul-Awwal\, the third month of the Muslim year. His death anniversary also falls on the s ame day\, the word 'barah' standing for the twelve days of the Prophet's s ickness. This event is subject to the appearance of the Moon. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:3 SEQUENCE:2 END:VEVENT BEGIN:VEVENT CREATED:20081226T170605Z LAST-MODIFIED:20100131T032252Z DTSTAMP:20100131T032252Z UID:2002-05-25-1 SUMMARY:ID-I-Milad-un-Nabi (Barah-Wafat) STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20110226 CATEGORIES:National Holiday DTSTART;VALUE=DATE:20110215 DTEND;VALUE=DATE:20110216 CLASS:PUBLIC DESCRIPTION:The Prophet was born on the twelfth day of Rabi-ul-Awwal\, the third month of the Muslim year. His death anniversary also falls on the s ame day\, the word 'barah' standing for the twelve days of the Prophet's s ickness. This event is subject to the appearance of the Moon. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T040251Z DTSTAMP:20100131T040251Z UID:2002-04-14-1 SUMMARY:Puthandu STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Tamil Nadu State Holiday DTSTART;VALUE=DATE:20100414 DTEND;VALUE=DATE:20100415 CLASS:PUBLIC DESCRIPTION:This marks the beginning of the Tamil new year. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T032609Z DTSTAMP:20100131T032609Z UID:31970fe0-7236-11da-86c4-893c642964fe SUMMARY:Gudi Padwa STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Event DTSTART;VALUE=DATE:20100316 DTEND;VALUE=DATE:20100317 CLASS:PUBLIC X-MOZILLA-ALARM-DEFAULT-LENGTH:1 DESCRIPTION:This marks the beginning of the Marathi new year. X-MOZILLA-ALARM-DEFAULT-UNITS:days X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20081226T171903Z DTSTAMP:20051221T152653Z UID:ae54cc40-7236-11da-aaaa-92d722977743 SUMMARY:Dr. Bhimrao Ramji Ambedkar's birthday STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Public Holiday DTSTART;VALUE=DATE:18910415 DTEND;VALUE=DATE:18910416 CLASS:PUBLIC X-MOZILLA-ALARM-DEFAULT-LENGTH:1 DESCRIPTION:Dr. B.R. Ambedkar has been called the most prominent Indian Un touchable leader of the 20th century. He helped spark a revival of Buddhis m in India\, a movement which is now known as neo-Buddhism. X-MOZILLA-ALARM-DEFAULT-UNITS:days X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20081226T171554Z LAST-MODIFIED:20081226T171903Z DTSTAMP:20081226T171554Z UID:ae54cc40-7236-11da-aaaa-92d722977743 SUMMARY:Dr. Bhimrao Ramji Ambedkar's birthday STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20090415 CATEGORIES:Public Holiday DTSTART;VALUE=DATE:20090414 DTEND;VALUE=DATE:20090415 CLASS:PUBLIC X-MOZILLA-ALARM-DEFAULT-LENGTH:1 DESCRIPTION:Dr. B.R. Ambedkar has been called the most prominent Indian Un touchable leader of the 20th century. He helped spark a revival of Buddhis m in India\, a movement which is now known as neo-Buddhism. X-MOZILLA-ALARM-DEFAULT-UNITS:days X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20080112T043907Z DTSTAMP:20051222T033320Z UID:2002-08-15-1 SUMMARY:Independence Day STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:National Holiday DTSTART;VALUE=DATE:20080815 DTEND;VALUE=DATE:20080816 CLASS:PUBLIC TRANSP:TRANSPARENT DESCRIPTION:On this day in 1947\, India got her independence. X-MOZILLA-RECUR-DEFAULT-UNITS:years END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T032743Z DTSTAMP:20100131T032743Z UID:362908a0-2f52-11d8-ae5b-92fe3e55f869 SUMMARY:Parsi New Year STATUS:CONFIRMED RRULE:FREQ=YEARLY EXDATE;VALUE=DATE:20120819 CATEGORIES:Festival DTSTART;VALUE=DATE:20100819 DTEND;VALUE=DATE:20100820 CLASS:PUBLIC DESCRIPTION:Parsis are descendants from Persian immigrants of the A.D. 600 -900 who fled from Persia\, now Iran. They are the followers of the ancien t Persian religion known as Zoroastrianism\, living in India\, Iran\, and Pakistan. X-MOZILLA-RECUR-DEFAULT-UNITS:years SEQUENCE:1 X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20080112T043907Z DTSTAMP:20051222T033320Z UID:2002-10-02-1 SUMMARY:Gandhi Jayanti STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:National Holiday DTSTART;VALUE=DATE:20021002 DTEND;VALUE=DATE:20021003 CLASS:PUBLIC DESCRIPTION:Mohandas Karamchand Gandhi\, the great Mahatma was born on thi s day in the year 1869. X-MOZILLA-RECUR-DEFAULT-UNITS:years END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T033252Z DTSTAMP:20100131T033252Z UID:2002-03-25-1 SUMMARY:Muharram STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:National Holiday DTSTART;VALUE=DATE:20101207 DTEND;VALUE=DATE:20101208 CLASS:PUBLIC DESCRIPTION:Islamic new year. Depending on the visibility of the Moon\, th is event may occur one day later or earlier. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:3 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T142021Z LAST-MODIFIED:20100131T033252Z DTSTAMP:20100131T033252Z UID:2002-03-25-1 SUMMARY:Muharram STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20121206 DTSTART;VALUE=DATE:20121125 DTEND;VALUE=DATE:20121126 CLASS:PUBLIC END:VEVENT BEGIN:VEVENT CREATED:20081223T224235Z LAST-MODIFIED:20100131T033252Z DTSTAMP:20100131T033252Z UID:2002-03-25-1 SUMMARY:Muharram STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20131207 CATEGORIES:National Holiday DTSTART;VALUE=DATE:20131114 DTEND;VALUE=DATE:20131115 CLASS:PUBLIC DESCRIPTION:Islamic new year. Depending on the visibility of the Moon\, th is event may occur one day later or earlier. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T033440Z DTSTAMP:20100131T033440Z UID:2002-03-12-1 SUMMARY:Maha Shivaratri STATUS:CONFIRMED RRULE:FREQ=YEARLY EXDATE;VALUE=DATE:20110212 CATEGORIES:Restricted Holiday DTSTART;VALUE=DATE:20100212 DTEND;VALUE=DATE:20100213 CLASS:PUBLIC DESCRIPTION:This festival is observed to commemorate the night when Lord S hiva\, the God of destruction\, danced his celestial dance\, the Tandav an d his wedding anniversary. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T032642Z DTSTAMP:20100131T032642Z UID:2002-03-29-1 SUMMARY:Ugadi STATUS:CONFIRMED RRULE:FREQ=YEARLY EXDATE;VALUE=DATE:20110317 CATEGORIES:A.P. State Public Holiday DTSTART;VALUE=DATE:20100316 DTEND;VALUE=DATE:20100317 CLASS:PUBLIC DESCRIPTION:Telugu new year's day. It is believed that the creator of the Hindu pantheon Lord Brahma started creation on this day. Also known as Cha itra Suddha Padhyami. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20081226T171819Z DTSTAMP:20051221T134735Z UID:2002-04-25-1 SUMMARY:Mahavir Jayanti STATUS:CONFIRMED RRULE:FREQ=YEARLY EXDATE;VALUE=DATE:20100419 CATEGORIES:National Holiday DTSTART;VALUE=DATE:20080418 DTEND;VALUE=DATE:20080419 CLASS:PUBLIC DESCRIPTION:Birthday of Mahavir\, the 24th Tirtankhara \, founder of Jaini sm. The Digambaras believe that he was born in the year 615 BC X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20081226T171554Z LAST-MODIFIED:20081226T171819Z DTSTAMP:20081226T171554Z UID:2002-04-25-1 SUMMARY:Mahavir Jayanti STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20090418 CATEGORIES:National Holiday DTSTART;VALUE=DATE:20090407 DTEND;VALUE=DATE:20090408 CLASS:PUBLIC DESCRIPTION:Birthday of Mahavir\, the 24th Tirtankhara \, founder of Jaini sm. The Digambaras believe that he was born in the year 615 BC X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T031025Z DTSTAMP:20100131T031025Z UID:cf7417b0-7236-11da-936b-8c894bc3e4c3 SUMMARY:Good Friday STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Public Holiday DTSTART;VALUE=DATE:20100402 DTEND;VALUE=DATE:20100403 CLASS:PUBLIC X-MOZILLA-ALARM-DEFAULT-LENGTH:1 X-MOZILLA-ALARM-DEFAULT-UNITS:days X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T031103Z DTSTAMP:20100131T031103Z UID:2002-05-26-1 SUMMARY:Buddha Purnima STATUS:CONFIRMED RRULE:FREQ=YEARLY EXDATE;VALUE=DATE:20110529 CATEGORIES:National Holiday DTSTART;VALUE=DATE:20100528 DTEND;VALUE=DATE:20100529 CLASS:PUBLIC DESCRIPTION:Buddha was born on a full moon night in the month of Vaisakh i n 563 B.C. This day comes in the month of April or May. He achieved enligh tenment as well as nirvana on the same date. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20081226T172109Z LAST-MODIFIED:20100131T031103Z DTSTAMP:20100131T031103Z UID:2002-05-26-1 SUMMARY:Buddha Purnima STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20110528 CATEGORIES:National Holiday DTSTART;VALUE=DATE:20110517 DTEND;VALUE=DATE:20110518 CLASS:PUBLIC DESCRIPTION:Buddha was born on a full moon night in the month of Vaisakh i n 563 B.C. This day comes in the month of April or May. He achieved enligh tenment as well as nirvana on the same date. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T033849Z DTSTAMP:20100131T033849Z UID:2002-12-06-1 SUMMARY:Idu'l Fitr STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:National Holiday DTSTART;VALUE=DATE:20100910 DTEND;VALUE=DATE:20100911 CLASS:PUBLIC DESCRIPTION:Also known as Ramzan. Coming with the new moon\, this festival marks the end of Ramzan\, the ninth month of the Muslim year. The holy Ko ran was also revealed this month. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T031335Z DTSTAMP:20100131T031335Z UID:2002-11-04-1 SUMMARY:Deepawali STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Festival DTSTART;VALUE=DATE:20101105 DTEND;VALUE=DATE:20101106 CLASS:PUBLIC DESCRIPTION:Also called as Diwali commemorates Lord Rama's return to his k ingdom Ayodhya after completing his 14-year exile. Also marks the beginnin g of the Marwari new year. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:3 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20081226T173244Z LAST-MODIFIED:20100131T031335Z DTSTAMP:20100131T031335Z UID:2002-11-04-1 SUMMARY:Deepawali STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20111105 CATEGORIES:Festival DTSTART;VALUE=DATE:20111025 DTEND;VALUE=DATE:20111026 CLASS:PUBLIC DESCRIPTION:Also called as Diwali commemorates Lord Rama's return to his k ingdom Ayodhya after completing his 14-year exile. Also marks the beginnin g of the Marwari new year. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T031410Z DTSTAMP:20100131T031410Z UID:2002-11-19-1 SUMMARY:Guru Nanak's Jayanti STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:National Holiday DTSTART;VALUE=DATE:20101121 DTEND;VALUE=DATE:20101122 CLASS:PUBLIC DESCRIPTION:Guru Nanak Dev\, the founder of the Sikh faith\, was born in t he month of Kartik(October/November)\, in 1469 A.D. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20081226T173911Z LAST-MODIFIED:20100131T031410Z DTSTAMP:20100131T031410Z UID:2002-11-19-1 SUMMARY:Guru Nanak's Jayanti STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20111121 CATEGORIES:National Holiday DTSTART;VALUE=DATE:20111110 DTEND;VALUE=DATE:20111111 CLASS:PUBLIC DESCRIPTION:Guru Nanak Dev\, the founder of the Sikh faith\, was born in t he month of Kartik(October/November)\, in 1469 A.D. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T034007Z DTSTAMP:20100131T034007Z UID:b9e79b50-7235-11da-8266-8565e4d16d93 SUMMARY:Holi STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Restricted Holiday DTSTART;VALUE=DATE:20100301 DTEND;VALUE=DATE:20100302 CLASS:PUBLIC DESCRIPTION:This exuberant spring festival celebrating the destruction of the \n demon-King Hiranyakashipu and of the evil Holika by Narasimha\, the \n half-man half-Lion incarnation of Lord Visnu. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T142520Z LAST-MODIFIED:20100131T034007Z DTSTAMP:20100131T034007Z UID:b9e79b50-7235-11da-8266-8565e4d16d93 SUMMARY:Holi STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20110302 DTSTART;VALUE=DATE:20110302 DTEND;VALUE=DATE:20110303 CLASS:PUBLIC DESCRIPTION:This exuberant spring festival celebrating the destruction of the \n demon-King Hiranyakashipu and of the evil Holika by Narasimha\, the \n half-man half-Lion incarnation of Lord Vishnu. END:VEVENT BEGIN:VEVENT CREATED:20081226T170644Z LAST-MODIFIED:20100131T034007Z DTSTAMP:20100131T034007Z UID:b9e79b50-7235-11da-8266-8565e4d16d93 SUMMARY:Holi STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20120301 CATEGORIES:Restricted Holiday DTSTART;VALUE=DATE:20120219 DTEND;VALUE=DATE:20120220 CLASS:PUBLIC DESCRIPTION:This exuberant spring festival celebrating the destruction of the \n demon-King Hiranyakashipu and of the evil Holika by Narasimha\, the \n half-man half-Lion incarnation of Lord Visnu. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T030931Z DTSTAMP:20100131T030931Z UID:2002-04-21-1 SUMMARY:Sri Ram navami STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Festival DTSTART;VALUE=DATE:20100324 DTEND;VALUE=DATE:20100325 CLASS:PUBLIC DESCRIPTION:The birthday of Sri Rama\, the 7th avatar of Vishnu. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20081226T171554Z LAST-MODIFIED:20100131T030931Z DTSTAMP:20100131T030931Z UID:2002-04-21-1 SUMMARY:Sri Ram navami STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20130324 CATEGORIES:Festival DTSTART;VALUE=DATE:20130313 DTEND;VALUE=DATE:20130314 CLASS:PUBLIC DESCRIPTION:The birthday of Sri Rama\, the 7th avatar of Vishnu. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T032826Z DTSTAMP:20100131T032826Z UID:2002-08-22-1 SUMMARY:Raksha Bandhan STATUS:CONFIRMED RRULE:FREQ=YEARLY EXDATE;VALUE=DATE:20110825 CATEGORIES:Festival DTSTART;VALUE=DATE:20100824 DTEND;VALUE=DATE:20100825 CLASS:PUBLIC DESCRIPTION:Raksha Bandhan is celebrated in India to honour the sea God Va runa. However\, at most places\, it celebrates the love of a brother for h is sister. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20081226T172157Z LAST-MODIFIED:20100131T032826Z DTSTAMP:20100131T032826Z UID:2002-08-22-1 SUMMARY:Raksha Bandhan STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20110824 CATEGORIES:Festival DTSTART;VALUE=DATE:20110813 DTEND;VALUE=DATE:20110814 CLASS:PUBLIC DESCRIPTION:Raksha Bandhan is celebrated in India to honour the sea God Va runa. However\, at most places\, it celebrates the love of a brother for h is sister. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T031216Z DTSTAMP:20100131T031216Z UID:2002-08-30-1 SUMMARY:Janmashtami STATUS:TENTATIVE RRULE:FREQ=YEARLY EXDATE;VALUE=DATE:20110903 CATEGORIES:Festival DTSTART;VALUE=DATE:20100902 DTEND;VALUE=DATE:20100903 CLASS:PUBLIC DESCRIPTION:Lord Krishna's brithday. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20081226T172157Z LAST-MODIFIED:20100131T031216Z DTSTAMP:20100131T031216Z UID:2002-08-30-1 SUMMARY:Janmashtami STATUS:TENTATIVE RECURRENCE-ID;VALUE=DATE:20110902 CATEGORIES:Festival DTSTART;VALUE=DATE:20110819 DTEND;VALUE=DATE:20110820 CLASS:PUBLIC DESCRIPTION:Lord Krishna's brithday. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T032857Z DTSTAMP:20100131T032857Z UID:2002-09-10-1 SUMMARY:Ganesh Chaturthi STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Festival DTSTART;VALUE=DATE:20100911 DTEND;VALUE=DATE:20100912 CLASS:PUBLIC DESCRIPTION:This festival marked the birthday of Lord Ganesh. Ganpati is o ne of the most popular deities. He is worshipped by both Shivaites and Vai shnavites. Even Buddhists and Jains have respect for Ganpati. He is consid ered to be an avatar of both Shiva and Vishnu. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20081226T172157Z LAST-MODIFIED:20100131T032857Z DTSTAMP:20100131T032857Z UID:2002-09-10-1 SUMMARY:Ganesh Chaturthi STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20110911 CATEGORIES:Festival DTSTART;VALUE=DATE:20110831 DTEND;VALUE=DATE:20110901 CLASS:PUBLIC DESCRIPTION:This festival marked the birthday of Lord Ganesh. Ganpati is o ne of the most popular deities. He is worshipped by both Shivaites and Vai shnavites. Even Buddhists and Jains have respect for Ganpati. He is consid ered to be an avatar of both Shiva and Vishnu. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T034255Z DTSTAMP:20100131T034255Z UID:179ec0c0-722d-11da-8c69-f4215b177f2b SUMMARY:Maha Saptami [Dussehra] STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Festival DTSTART;VALUE=DATE:20081006 DTEND;VALUE=DATE:20081007 CLASS:PUBLIC DESCRIPTION:Prayers are offered to Kolabou\, Lord Ganesha’s wife. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:3 END:VEVENT BEGIN:VEVENT CREATED:20081226T172959Z LAST-MODIFIED:20081226T173131Z DTSTAMP:20081226T172959Z UID:179ec0c0-722d-11da-8c69-f4215b177f2b SUMMARY:Maha Saptami [Dussehra] STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20091006 CATEGORIES:Festival DTSTART;VALUE=DATE:20090925 DTEND;VALUE=DATE:20090926 CLASS:PUBLIC DESCRIPTION:Prayers are offered to Kolabou\, Lord Ganesha’s wife. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20100131T034022Z LAST-MODIFIED:20100131T034255Z DTSTAMP:20100131T034255Z UID:179ec0c0-722d-11da-8c69-f4215b177f2b SUMMARY:Maha Saptami [Dussehra] STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20081006 CATEGORIES:Festival DTSTART;VALUE=DATE:20101008 DTEND;VALUE=DATE:20101009 SEQUENCE:2 CLASS:PUBLIC DESCRIPTION:Prayers are offered to Kolabou\, Lord Ganesha’s wife. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:3 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20081226T173221Z DTSTAMP:20051007T142104Z UID:2002-10-07-1 SUMMARY:Navaratri [Dussehra] STATUS:TENTATIVE RRULE:FREQ=YEARLY CATEGORIES:Festival DTSTART;VALUE=DATE:20080930 DTEND;VALUE=DATE:20081001 CLASS:PUBLIC DESCRIPTION:A 10 day festival associated with vanquishing demons\, in par ticular Rama's victory over Ravana in the Ramayana\, and Durga's victory o ver the buffalo-headed Mahishasura. Ends with the Vijaya Dasami day. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 END:VEVENT BEGIN:VEVENT CREATED:20081226T172959Z LAST-MODIFIED:20081226T173221Z DTSTAMP:20081226T172959Z UID:2002-10-07-1 SUMMARY:Navaratri [Dussehra] STATUS:TENTATIVE RECURRENCE-ID;VALUE=DATE:20090930 CATEGORIES:Festival DTSTART;VALUE=DATE:20090919 DTEND;VALUE=DATE:20090920 CLASS:PUBLIC TRANSP:TRANSPARENT DESCRIPTION:A 10 day festival associated with vanquishing demons\, in par ticular Rama's victory over Ravana in the Ramayana\, and Durga's victory o ver the buffalo-headed Mahishasura. Ends with the Vijaya Dasami day. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T034307Z DTSTAMP:20100131T034307Z UID:2002-10-12-1 SUMMARY:Sri Saraswati Avahana [Dussehra] STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Festival DTSTART;VALUE=DATE:20081006 DTEND;VALUE=DATE:20081007 CLASS:PUBLIC DESCRIPTION:Prayers are offered to the Godess of wisdom\, Sri Saraswati. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:3 END:VEVENT BEGIN:VEVENT CREATED:20081226T172959Z LAST-MODIFIED:20081226T173135Z DTSTAMP:20081226T172959Z UID:2002-10-12-1 SUMMARY:Sri Saraswati Avahana [Dussehra] STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20091006 CATEGORIES:Festival DTSTART;VALUE=DATE:20090925 DTEND;VALUE=DATE:20090926 CLASS:PUBLIC DESCRIPTION:Prayers are offered to the Godess of wisdom\, Sri Saraswati. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20100131T034022Z LAST-MODIFIED:20100131T034307Z DTSTAMP:20100131T034307Z UID:2002-10-12-1 SUMMARY:Sri Saraswati Avahana [Dussehra] STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20081006 CATEGORIES:Festival DTSTART;VALUE=DATE:20101008 DTEND;VALUE=DATE:20101009 SEQUENCE:2 CLASS:PUBLIC DESCRIPTION:Prayers are offered to the Godess of wisdom\, Sri Saraswati. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:3 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T034313Z DTSTAMP:20100131T034313Z UID:2002-10-13-1 SUMMARY:Sri Durga Ashtami [Dussehra] STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Festival DTSTART;VALUE=DATE:20081007 DTEND;VALUE=DATE:20081008 CLASS:PUBLIC DESCRIPTION:Godess Durga is considered to be a divine mother who exists in all beings in the form of intelligence\, mercy\, beauty and is a consort of Lord Shiva. Prayers are offered to the goddess to bestow upon man all w ealth\, auspiciousness\, prosperity\, knowledge (both sacred and secular)\ , and all other potent powers. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:3 END:VEVENT BEGIN:VEVENT CREATED:20081226T172959Z LAST-MODIFIED:20081226T173015Z DTSTAMP:20081226T172959Z UID:2002-10-13-1 SUMMARY:Sri Durga Ashtami [Dussehra] STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20091007 CATEGORIES:Festival DTSTART;VALUE=DATE:20090926 DTEND;VALUE=DATE:20090927 CLASS:PUBLIC DESCRIPTION:Godess Durga is considered to be a divine mother who exists in all beings in the form of intelligence\, mercy\, beauty and is a consort of Lord Shiva. Prayers are offered to the goddess to bestow upon man all w ealth\, auspiciousness\, prosperity\, knowledge (both sacred and secular)\ , and all other potent powers. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20100131T034022Z LAST-MODIFIED:20100131T034313Z DTSTAMP:20100131T034313Z UID:2002-10-13-1 SUMMARY:Sri Durga Ashtami [Dussehra] STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20081007 CATEGORIES:Festival DTSTART;VALUE=DATE:20101009 DTEND;VALUE=DATE:20101010 SEQUENCE:2 CLASS:PUBLIC DESCRIPTION:Godess Durga is considered to be a divine mother who exists in all beings in the form of intelligence\, mercy\, beauty and is a consort of Lord Shiva. Prayers are offered to the goddess to bestow upon man all w ealth\, auspiciousness\, prosperity\, knowledge (both sacred and secular)\ , and all other potent powers. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:3 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T034321Z DTSTAMP:20100131T034321Z UID:2002-10-14-1 SUMMARY:Sri Maha Navami [Dussehra] STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Festival DTSTART;VALUE=DATE:20081008 DTEND;VALUE=DATE:20081009 CLASS:PUBLIC DESCRIPTION:The culmination of the Navratri festival. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:3 END:VEVENT BEGIN:VEVENT CREATED:20081226T172959Z LAST-MODIFIED:20081226T173115Z DTSTAMP:20081226T172959Z UID:2002-10-14-1 SUMMARY:Sri Maha Navami [Dussehra] STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20091008 CATEGORIES:Festival DTSTART;VALUE=DATE:20090927 DTEND;VALUE=DATE:20090928 CLASS:PUBLIC DESCRIPTION:The culmination of the Navratri festival. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20100131T034022Z LAST-MODIFIED:20100131T034321Z DTSTAMP:20100131T034321Z UID:2002-10-14-1 SUMMARY:Sri Maha Navami [Dussehra] STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20081008 CATEGORIES:Festival DTSTART;VALUE=DATE:20101010 DTEND;VALUE=DATE:20101011 SEQUENCE:2 CLASS:PUBLIC DESCRIPTION:The culmination of the Navratri festival. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:3 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T034328Z DTSTAMP:20100131T034328Z UID:2002-10-15-1 SUMMARY:Vijaya Dasami [Dussehra] STATUS:CONFIRMED RRULE:FREQ=YEARLY EXDATE;VALUE=DATE:20091010 CATEGORIES:Festival DTSTART;VALUE=DATE:20081009 DTEND;VALUE=DATE:20081010 CLASS:PUBLIC DESCRIPTION:Also very well known as Dussehra. The occasion marks the trium ph of Lord Rama over the demon king\, Ravana\, the victory of good over ev il. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:3 END:VEVENT BEGIN:VEVENT CREATED:20081226T172959Z LAST-MODIFIED:20081226T173119Z DTSTAMP:20081226T172959Z UID:2002-10-15-1 SUMMARY:Vijaya Dasami [Dussehra] STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20091009 CATEGORIES:Festival DTSTART;VALUE=DATE:20090928 DTEND;VALUE=DATE:20090929 CLASS:PUBLIC DESCRIPTION:Also very well known as Dussehra. The occasion marks the trium ph of Lord Rama over the demon king\, Ravana\, the victory of good over ev il. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20100131T034022Z LAST-MODIFIED:20100131T034328Z DTSTAMP:20100131T034328Z UID:2002-10-15-1 SUMMARY:Vijaya Dasami [Dussehra] STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20081009 CATEGORIES:Festival DTSTART;VALUE=DATE:20101011 DTEND;VALUE=DATE:20101012 SEQUENCE:2 CLASS:PUBLIC DESCRIPTION:Also very well known as Dussehra. The occasion marks the trium ph of Lord Rama over the demon king\, Ravana\, the victory of good over ev il. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:3 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T034603Z DTSTAMP:20100131T034603Z UID:5efd3780-7233-11da-88ad-9f93f56d9df3 SUMMARY:Maharishi Dayanand Saraswati Jayanti STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Restricted Holiday DTSTART;VALUE=DATE:20100208 DTEND;VALUE=DATE:20100209 CLASS:PUBLIC DESCRIPTION:The founder of the Arya Samaj. The sage who sought to restore to Hinduism its natural radiance and wisdom. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20081223T225242Z LAST-MODIFIED:20100131T034603Z DTSTAMP:20100131T034603Z UID:5efd3780-7233-11da-88ad-9f93f56d9df3 SUMMARY:Maharishi Dayanand Saraswati Jayanti STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20120209 CATEGORIES:Restricted Holiday DTSTART;VALUE=DATE:20120219 DTEND;VALUE=DATE:20120220 CLASS:PUBLIC DESCRIPTION:The founder of the Arya Samaj. The sage who sought to restore to Hinduism its natural radiance and wisdom. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T041245Z DTSTAMP:20100131T041245Z UID:cef395e0-83b1-11db-9a83-a573279bff77 SUMMARY:Lunar Eclipse [total] STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Event DTSTART:20100806T131000 DTEND:20100806T142300 CLASS:PUBLIC X-MOZILLA-ALARM-DEFAULT-LENGTH:1 X-MOZILLA-ALARM-DEFAULT-UNITS:days X-MOZ-GENERATION:5 SEQUENCE:3 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T035638Z DTSTAMP:20100131T035638Z UID:7ed9f200-2f52-11d8-b2cb-913852cf0eb2 SUMMARY:Onam STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Holidays DTSTART;VALUE=DATE:20100907 DTEND;VALUE=DATE:20100908 CLASS:PUBLIC DESCRIPTION:Onam is celebrated after the memory of King Mahabali. The King ruled Kerala\, Southern India long time ago. Onam symbolizes the joyful r ule of the King and the happiness that the people had under his rule. The people also have the belief that during Onam\, the King returns to Kerala to visit his people. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20081226T172806Z LAST-MODIFIED:20100131T035638Z DTSTAMP:20100131T035638Z UID:7ed9f200-2f52-11d8-b2cb-913852cf0eb2 SUMMARY:Onam STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20110907 CATEGORIES:Holidays DTSTART;VALUE=DATE:20110828 DTEND;VALUE=DATE:20110829 CLASS:PUBLIC DESCRIPTION:Onam is celebrated after the memory of King Mahabali. The King ruled Kerala\, Southern India long time ago. Onam symbolizes the joyful r ule of the King and the happiness that the people had under his rule. The people also have the belief that during Onam\, the King returns to Kerala to visit his people. X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T034336Z DTSTAMP:20100131T034336Z UID:7818d420-7237-11da-be73-d94cdcdce3bf SUMMARY:Maharishi Valmiki's birthday STATUS:CONFIRMED RRULE:FREQ=YEARLY EXDATE;VALUE=DATE:20091015 CATEGORIES:Restricted Holiday DTSTART;VALUE=DATE:20081014 DTEND;VALUE=DATE:20081015 CLASS:PUBLIC X-MOZILLA-ALARM-DEFAULT-LENGTH:1 DESCRIPTION:Valmiki began to compose the great eternal song and poem of th e life of Sri Rama-The Ramayana. Valmiki's Ramayana is the very first poem in Sanskrit. Therefore\, it is called the Adikavya or the \"The first poe m\" and Valmiki is also known as the Adikavi\, which means \"The first poe t\". X-MOZILLA-ALARM-DEFAULT-UNITS:days X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:4 END:VEVENT BEGIN:VEVENT CREATED:20081226T173244Z LAST-MODIFIED:20081226T173404Z DTSTAMP:20081226T173244Z UID:7818d420-7237-11da-be73-d94cdcdce3bf SUMMARY:Maharishi Valmiki's birthday STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20091014 CATEGORIES:Restricted Holiday DTSTART;VALUE=DATE:20091004 DTEND;VALUE=DATE:20091005 CLASS:PUBLIC X-MOZILLA-ALARM-DEFAULT-LENGTH:1 DESCRIPTION:Valmiki began to compose the great eternal song and poem of th e life of Sri Rama-The Ramayana. Valmiki's Ramayana is the very first poem in Sanskrit. Therefore\, it is called the Adikavya or the \"The first poe m\" and Valmiki is also known as the Adikavi\, which means \"The first poe t\". X-MOZILLA-ALARM-DEFAULT-UNITS:days X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20100131T034022Z LAST-MODIFIED:20100131T034336Z DTSTAMP:20100131T034336Z UID:7818d420-7237-11da-be73-d94cdcdce3bf SUMMARY:Maharishi Valmiki's birthday STATUS:CONFIRMED RECURRENCE-ID;VALUE=DATE:20081014 CATEGORIES:Restricted Holiday DTSTART;VALUE=DATE:20101016 DTEND;VALUE=DATE:20101017 SEQUENCE:2 CLASS:PUBLIC X-MOZILLA-ALARM-DEFAULT-LENGTH:1 DESCRIPTION:Valmiki began to compose the great eternal song and poem of th e life of Sri Rama-The Ramayana. Valmiki's Ramayana is the very first poem in Sanskrit. Therefore\, it is called the Adikavya or the \"The first poe m\" and Valmiki is also known as the Adikavi\, which means \"The first poe t\". X-MOZILLA-ALARM-DEFAULT-UNITS:days X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:4 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T040032Z DTSTAMP:20100131T040032Z UID:9ada5cc0-8471-11db-b773-eb975c5415ab SUMMARY:Id ul Zuha [Bakrid] STATUS:CONFIRMED CATEGORIES:National Holiday DTSTART;VALUE=DATE:20101117 DTEND;VALUE=DATE:20101118 CLASS:PUBLIC X-MOZILLA-ALARM-DEFAULT-LENGTH:1 DESCRIPTION:A muslim festival also known as the feast of the sacrifice. Si gnifies the commemoration of the ordeals of Prophet Ibrahim. Usually falls on the last month of the islamic calendar. Depending on the visibility of the Moon\, this event may occur one day later or earlier. X-MOZILLA-ALARM-DEFAULT-UNITS:days SEQUENCE:1 X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20100131T035500Z DTSTAMP:20100131T035500Z UID:ef00eb20-846f-11db-ad56-bf7312a0508a SUMMARY:Solar Eclipse [annular] STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:Event DTSTART:20100117T150100 DTEND:20100117T154000 CLASS:PUBLIC X-MOZILLA-ALARM-DEFAULT-LENGTH:1 X-MOZILLA-ALARM-DEFAULT-UNITS:days X-MOZILLA-RECUR-DEFAULT-UNITS:years X-MOZ-GENERATION:3 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20071219T141846Z LAST-MODIFIED:20080112T043907Z DTSTAMP:20051221T155235Z UID:2002-12-25-1 SUMMARY:Christmas STATUS:CONFIRMED RRULE:FREQ=YEARLY CATEGORIES:National Holiday DTSTART;VALUE=DATE:20071225 DTEND;VALUE=DATE:20071226 CLASS:PUBLIC X-MOZILLA-RECUR-DEFAULT-UNITS:years END:VEVENT BEGIN:VEVENT CREATED:20071222T175806Z LAST-MODIFIED:20100131T035848Z DTSTAMP:20100131T035848Z UID:0509197b-e862-4b73-837d-98d23d4d0587 SUMMARY:Bohag Bihu [Assamese New Year] RRULE:FREQ=YEARLY CATEGORIES:Event DTSTART;VALUE=DATE;TZID=America/Dawson:20100415 DTEND;VALUE=DATE;TZID=America/Dawson:20100416 TRANSP:TRANSPARENT DESCRIPTION:This marks the beginning of the Assamese new year. SEQUENCE:1 X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071222T180739Z LAST-MODIFIED:20100131T040216Z DTSTAMP:20100131T040216Z UID:13794675-3cb6-4e3b-9581-d72514278e90 SUMMARY:Gujarati New Year RRULE:FREQ=YEARLY DTSTART;VALUE=DATE;TZID=America/Dawson:20101107 DTEND;VALUE=DATE;TZID=America/Dawson:20101108 TRANSP:TRANSPARENT DESCRIPTION:This marks the beginning of the Gujarati new year. X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20081226T173244Z LAST-MODIFIED:20100131T040216Z DTSTAMP:20100131T040216Z UID:13794675-3cb6-4e3b-9581-d72514278e90 SUMMARY:Gujarati New Year RECURRENCE-ID;VALUE=DATE;TZID=America/Dawson:20111107 DTSTART;VALUE=DATE;TZID=America/Dawson:20111027 DTEND;VALUE=DATE;TZID=America/Dawson:20111028 TRANSP:TRANSPARENT DESCRIPTION:This marks the beginning of the Gujarati new year. X-MOZ-GENERATION:1 END:VEVENT BEGIN:VEVENT CREATED:20071222T182315Z LAST-MODIFIED:20100131T040940Z DTSTAMP:20100131T040940Z UID:e4d23f27-a4e3-479e-a25b-d905e1d27f67 SUMMARY:Losoong [Sikkimese New Year] RRULE:FREQ=YEARLY CATEGORIES:Event DTSTART;VALUE=DATE;TZID=America/Dawson:20101212 DTEND;VALUE=DATE;TZID=America/Dawson:20101213 TRANSP:TRANSPARENT DESCRIPTION:This marks the beginning of the Sikkimese new year.\n X-MOZ-GENERATION:2 SEQUENCE:1 END:VEVENT BEGIN:VEVENT CREATED:20081226T174015Z LAST-MODIFIED:20100131T040940Z DTSTAMP:20100131T040940Z UID:e4d23f27-a4e3-479e-a25b-d905e1d27f67 SUMMARY:Losoong [Sikkimese New Year] RECURRENCE-ID;VALUE=DATE;TZID=America/Dawson:20111212 CATEGORIES:Event DTSTART;VALUE=DATE;TZID=America/Dawson:20111229 DTEND;VALUE=DATE;TZID=America/Dawson:20111230 TRANSP:TRANSPARENT DESCRIPTION:This marks the beginning of the Sikkimese new year.\n X-MOZ-GENERATION:1 END:VEVENT END:VCALENDAR tests/auto/versit/qversit/testdata_ics/india2010.ics000066400000000000000000001143721233466112000227450ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Google Inc//Google Calendar 70.9054//EN VERSION:2.0 CALSCALE:GREGORIAN METHOD:PUBLISH X-WR-CALNAME:Hindu Vedic Calendar for India for year 2010 X-WR-TIMEZONE:UTC X-WR-CALDESC:Hindu Vedic Calendar for India for year 2010 BEGIN:VEVENT DTSTART;VALUE=DATE:20100918 DTEND;VALUE=DATE:20100919 DTSTAMP:20091226T024227Z UID:CSVConvert4f6296729d82e2f84e298777dc5747d2 CREATED:19000101T120000Z DESCRIPTION:Parivartini Ekadashi LAST-MODIFIED:20091226T023919Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Parivartini Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100901 DTEND;VALUE=DATE:20100902 DTSTAMP:20091226T024227Z UID:CSVConvert0027bbaa4de98a07c61f243d899d938a CREATED:19000101T120000Z DESCRIPTION:Janmasthami LAST-MODIFIED:20091226T023919Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Janmasthami TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100919 DTEND;VALUE=DATE:20100920 DTSTAMP:20091226T024227Z UID:CSVConvert02a3af83db2276f6f8d4441b26b4af3d CREATED:19000101T120000Z DESCRIPTION:Vaman Jayanthi LAST-MODIFIED:20091226T023918Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Vaman Jayanthi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100209 DTEND;VALUE=DATE:20100210 DTSTAMP:20091226T024227Z UID:CSVConvert6c15b7119e7484215731068ae2233d7c CREATED:19000101T120000Z DESCRIPTION:Vijaya Ekadashi LAST-MODIFIED:20091226T023923Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Vijaya Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100126 DTEND;VALUE=DATE:20100127 DTSTAMP:20091226T024227Z UID:CSVConverta5688f1d38526d13088cfb5cdabcfe0a CREATED:19000101T120000Z DESCRIPTION:Bhishma Ekadashi LAST-MODIFIED:20091226T023923Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Bhishma Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100415 DTEND;VALUE=DATE:20100416 DTSTAMP:20091226T024227Z UID:CSVConvert64748b751568ae962c41b29e48b5f961 CREATED:19000101T120000Z DESCRIPTION:Bihag Bohu LAST-MODIFIED:20091226T023922Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Bihag Bohu TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100726 DTEND;VALUE=DATE:20100727 DTSTAMP:20091226T024227Z UID:CSVConvert67303e2ab833ad95652c9cda387d0095 CREATED:19000101T120000Z DESCRIPTION:Guru Purnima LAST-MODIFIED:20091226T023920Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Guru Purnima TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100608 DTEND;VALUE=DATE:20100609 DTSTAMP:20091226T024227Z UID:CSVConvert7268125fa988f7ed2f4e87d8b6ae37d5 CREATED:19000101T120000Z DESCRIPTION:Apara Ekadashi LAST-MODIFIED:20091226T023921Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Apara Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101104 DTEND;VALUE=DATE:20101105 DTSTAMP:20091226T024227Z UID:CSVConvert5b4f1939fe0e00b00d8ac245587cc0f8 CREATED:19000101T120000Z DESCRIPTION:Kaali Chaudash LAST-MODIFIED:20091226T023917Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Kaali Chaudash TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100831 DTEND;VALUE=DATE:20100901 DTSTAMP:20091226T024227Z UID:CSVConvert1a74406371968c1df87f9f1b02f6cd95 CREATED:19000101T120000Z DESCRIPTION:Randhan chhath (Guj) LAST-MODIFIED:20091226T023919Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Randhan chhath (Guj) TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100910 DTEND;VALUE=DATE:20100911 DTSTAMP:20091226T024227Z UID:CSVConvert440b75441d50a966a59809ea6752c65a CREATED:19000101T120000Z DESCRIPTION:Samaveda Upakarma LAST-MODIFIED:20091226T023919Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Samaveda Upakarma TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101015 DTEND;VALUE=DATE:20101016 DTSTAMP:20091226T024227Z UID:CSVConvertc415f660e5e6f09b7dcd65b269e11b10 CREATED:19000101T120000Z DESCRIPTION:Durgasthami LAST-MODIFIED:20091226T023917Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Durgasthami TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101016 DTEND;VALUE=DATE:20101017 DTSTAMP:20091226T024227Z UID:CSVConvertb47a39f6f238061124889b4257bb2376 CREATED:19000101T120000Z DESCRIPTION:Maha Navami LAST-MODIFIED:20091226T023917Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Maha Navami TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100330 DTEND;VALUE=DATE:20100331 DTSTAMP:20091226T024227Z UID:CSVConverte349f2d07b99f65a8a3705a757735fff CREATED:19000101T120000Z DESCRIPTION:Hanuman Jayanthi LAST-MODIFIED:20091226T023922Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Hanuman Jayanthi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101111 DTEND;VALUE=DATE:20101112 DTSTAMP:20091226T024227Z UID:CSVConvertc44b8ac52937c85ad63b619ce87c1fd7 CREATED:19000101T120000Z DESCRIPTION:Chhath (Bihar) LAST-MODIFIED:20091226T023916Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Chhath (Bihar) TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100420 DTEND;VALUE=DATE:20100421 DTSTAMP:20091226T024227Z UID:CSVConvertd5fb7f8943482f8619b292c56da8e23b CREATED:19000101T120000Z DESCRIPTION:Ramanuja Jayanthi LAST-MODIFIED:20091226T023921Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Ramanuja Jayanthi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100526 DTEND;VALUE=DATE:20100527 DTSTAMP:20091226T024227Z UID:CSVConvert208a3eb97b264b1447d1e659c2be2b8b CREATED:19000101T120000Z DESCRIPTION:Narsimha Jayanthi LAST-MODIFIED:20091226T023921Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Narsimha Jayanthi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100524 DTEND;VALUE=DATE:20100525 DTSTAMP:20091226T024227Z UID:CSVConvertb286a8ab811b59eb9bd3e960a62533ed CREATED:19000101T120000Z DESCRIPTION:Mohini Ekadashi LAST-MODIFIED:20091226T023921Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Mohini Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100924 DTEND;VALUE=DATE:20100925 DTSTAMP:20091226T024227Z UID:CSVConvertd22469622b58d42b8cbac229013fa332 CREATED:19000101T120000Z DESCRIPTION:Pitru Paksha / Shraddha Starts LAST-MODIFIED:20091226T023918Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Pitru Paksha / Shraddha Starts TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101105 DTEND;VALUE=DATE:20101106 DTSTAMP:20091226T024227Z UID:CSVConvert48eb6a29a87d72f0c64aff070144a5be CREATED:19000101T120000Z DESCRIPTION:Lakshami Pujan LAST-MODIFIED:20091226T023917Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Lakshami Pujan TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101221 DTEND;VALUE=DATE:20101222 DTSTAMP:20091226T024227Z UID:CSVConvert996c7c26aad437ee8c535d94242f4b64 CREATED:19000101T120000Z DESCRIPTION:Dattatreya Jayanthi LAST-MODIFIED:20091226T023915Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Dattatreya Jayanthi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100318 DTEND;VALUE=DATE:20100319 DTSTAMP:20091226T024227Z UID:CSVConverte0b2370fe4fbe3f492b39be35ece3518 CREATED:19000101T120000Z DESCRIPTION:Gauri Tritiya LAST-MODIFIED:20091226T023922Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Gauri Tritiya TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100910 DTEND;VALUE=DATE:20100911 DTSTAMP:20091226T024227Z UID:CSVConvertd5655a1fc622bb37b639857e5d206f31 CREATED:19000101T120000Z DESCRIPTION:Haritatalika Teej / Kevada Teej LAST-MODIFIED:20091226T023919Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Haritatalika Teej / Kevada Teej TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100823 DTEND;VALUE=DATE:20100824 DTSTAMP:20091226T024227Z UID:CSVConverte5e576260c6f4b7cfe5b5303df4d6f4c CREATED:19000101T120000Z DESCRIPTION:Rigaveda Upakarma LAST-MODIFIED:20091226T023919Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Rigaveda Upakarma TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100316 DTEND;VALUE=DATE:20100317 DTSTAMP:20091226T024227Z UID:CSVConvert7798df89f6c5920a83bfc3d5560b35aa CREATED:19000101T120000Z DESCRIPTION:Gudi Padwa / Cheti Chand LAST-MODIFIED:20091226T023923Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Gudi Padwa / Cheti Chand TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101107 DTEND;VALUE=DATE:20101108 DTSTAMP:20091226T024227Z UID:CSVConverted2204abe11f10990aa73738c7d31965 CREATED:19000101T120000Z DESCRIPTION:Bali Pratipada / Gowardhan Puja LAST-MODIFIED:20091226T023916Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Bali Pratipada / Gowardhan Puja TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101103 DTEND;VALUE=DATE:20101104 DTSTAMP:20091226T024227Z UID:CSVConvert992964ba00a80765b1400bc972c1af19 CREATED:19000101T120000Z DESCRIPTION:Vaaga Baaras LAST-MODIFIED:20091226T023917Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Vaaga Baaras TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100326 DTEND;VALUE=DATE:20100327 DTSTAMP:20091226T024227Z UID:CSVConvert63e76860e9a08e05d316408ee4ccaead CREATED:19000101T120000Z DESCRIPTION:Kamada Ekadashi LAST-MODIFIED:20091226T023922Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Kamada Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100318 DTEND;VALUE=DATE:20100319 DTSTAMP:20091226T024227Z UID:CSVConvert102d8a945e6ceb84a89bff3115b55bca CREATED:19000101T120000Z DESCRIPTION:Matysa Jayanthi LAST-MODIFIED:20091226T023922Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Matysa Jayanthi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101017 DTEND;VALUE=DATE:20101018 DTSTAMP:20091226T024227Z UID:CSVConvert079ea785bd7fff7792bdcd48e2c6eb3d CREATED:19000101T120000Z DESCRIPTION:Vijaya Dashami/Dussera/Dashera LAST-MODIFIED:20091226T023917Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Vijaya Dashami/Dussera/Dashera TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100410 DTEND;VALUE=DATE:20100411 DTSTAMP:20091226T024227Z UID:CSVConvertf50182a5e0663b8089ffc0a7b1be63a5 CREATED:19000101T120000Z DESCRIPTION:Varuthini Ekadashi LAST-MODIFIED:20091226T023922Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Varuthini Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101121 DTEND;VALUE=DATE:20101122 DTSTAMP:20091226T024227Z UID:CSVConvert5d2e41b99b9f739a1524e7885bf3f4bc CREATED:19000101T120000Z DESCRIPTION:Kartiki Poornima LAST-MODIFIED:20091226T023916Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Kartiki Poornima TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100316 DTEND;VALUE=DATE:20100317 DTSTAMP:20091226T024227Z UID:CSVConvert79eace7b3add65f4da65ba82ccef586a CREATED:19000101T120000Z DESCRIPTION:Chaitra Navaratri LAST-MODIFIED:20091226T023923Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Chaitra Navaratri TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100814 DTEND;VALUE=DATE:20100815 DTSTAMP:20091226T024227Z UID:CSVConvert4a4c2113036d25fbbdbb7303030f5985 CREATED:19000101T120000Z DESCRIPTION:Naag Panchami LAST-MODIFIED:20091226T023920Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Naag Panchami TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101109 DTEND;VALUE=DATE:20101110 DTSTAMP:20091226T024227Z UID:CSVConvert647ea13ea74213b20a00bbf748068f9d CREATED:19000101T120000Z DESCRIPTION:Nagual Chavithi LAST-MODIFIED:20091226T023916Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Nagual Chavithi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101105 DTEND;VALUE=DATE:20101106 DTSTAMP:20091226T024227Z UID:CSVConvert87117ac1a7ef7dd8489f647a9c7814de CREATED:19000101T120000Z DESCRIPTION:Narak Chaturdashi LAST-MODIFIED:20091226T023917Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Narak Chaturdashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100728 DTEND;VALUE=DATE:20100729 DTSTAMP:20091226T024227Z UID:CSVConvert8f70b8e0301972a7bfd55955ea17639b CREATED:19000101T120000Z DESCRIPTION:Jayaparvati vrat ends LAST-MODIFIED:20091226T023920Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Jayaparvati vrat ends TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101201 DTEND;VALUE=DATE:20101202 DTSTAMP:20091226T024227Z UID:CSVConverte143316db927cf3318bd28e01d84a6f8 CREATED:19000101T120000Z DESCRIPTION:Uttpatthi Ekadashi LAST-MODIFIED:20091226T023916Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Uttpatthi Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100922 DTEND;VALUE=DATE:20100923 DTSTAMP:20091226T024227Z UID:CSVConverta26f454197725fb61e5f0d0b72b4b19f CREATED:19000101T120000Z DESCRIPTION:Anant Chaturdashi LAST-MODIFIED:20091226T023918Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Anant Chaturdashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101103 DTEND;VALUE=DATE:20101104 DTSTAMP:20091226T024227Z UID:CSVConvert364a43dfcf51aa06dfcc8519ef0032c3 CREATED:19000101T120000Z DESCRIPTION:Dhanatrayodashi / Dhan Teras LAST-MODIFIED:20091226T023917Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Dhanatrayodashi / Dhan Teras TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100625 DTEND;VALUE=DATE:20100626 DTSTAMP:20091226T024227Z UID:CSVConvert54250287eec7bd301ac3fe2723f9b4ed CREATED:19000101T120000Z DESCRIPTION:Vata Savitri Puja LAST-MODIFIED:20091226T023921Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Vata Savitri Puja TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101217 DTEND;VALUE=DATE:20101218 DTSTAMP:20091226T024227Z UID:CSVConvert37de9c6ea6be6447f9a082d0c932f677 CREATED:19000101T120000Z DESCRIPTION:Geeta Jayanthi LAST-MODIFIED:20091226T023915Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Geeta Jayanthi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100806 DTEND;VALUE=DATE:20100807 DTSTAMP:20091226T024227Z UID:CSVConvert0d55f7228275cb31b61fd2bf01379a7e CREATED:19000101T120000Z DESCRIPTION:Kamika Ekadashi LAST-MODIFIED:20091226T023920Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Kamika Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100113 DTEND;VALUE=DATE:20100114 DTSTAMP:20091226T024227Z UID:CSVConvertb3d7daf6dee9267e6b11e7282156426a CREATED:19000101T120000Z DESCRIPTION:Bhogi / Lohri LAST-MODIFIED:20091226T023924Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Bhogi / Lohri TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101007 DTEND;VALUE=DATE:20101008 DTSTAMP:20091226T024227Z UID:CSVConvert201bec45b033105c8846df42b8418fe2 CREATED:19000101T120000Z DESCRIPTION:Sarvapitru Amavasya LAST-MODIFIED:20091226T023918Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Sarvapitru Amavasya TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100415 DTEND;VALUE=DATE:20100416 DTSTAMP:20091226T024227Z UID:CSVConvert7a73ac6bae648ecc963a40561d9fc8c8 CREATED:19000101T120000Z DESCRIPTION:Adhika Mala Masa begins LAST-MODIFIED:20091226T023922Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Adhika Mala Masa begins TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101008 DTEND;VALUE=DATE:20101009 DTSTAMP:20091226T024227Z UID:CSVConvert18f80af5e85e2e9e5112b884aae636d5 CREATED:19000101T120000Z DESCRIPTION:Sharad Navaratri begins LAST-MODIFIED:20091226T023918Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Sharad Navaratri begins TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100910 DTEND;VALUE=DATE:20100911 DTSTAMP:20091226T024227Z UID:CSVConvert4bdd64b9ca3cf8b3ee88a1f08d5cb376 CREATED:19000101T120000Z DESCRIPTION:Swarna Gauri Vratam LAST-MODIFIED:20091226T023918Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Swarna Gauri Vratam TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100329 DTEND;VALUE=DATE:20100330 DTSTAMP:20091226T024227Z UID:CSVConvertb8ebefd37aef2a947fdc9c546b3b2efb CREATED:19000101T120000Z DESCRIPTION:Panguni Uttitram LAST-MODIFIED:20091226T023922Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Panguni Uttitram TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101222 DTEND;VALUE=DATE:20101223 DTSTAMP:20091226T024227Z UID:CSVConvert899e64d7067f5b830aaccfed066e837e CREATED:19000101T120000Z DESCRIPTION:Arudra Darshanam LAST-MODIFIED:20091226T023915Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Arudra Darshanam TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100824 DTEND;VALUE=DATE:20100825 DTSTAMP:20091226T024227Z UID:CSVConvert8415894a39c3fe06e978d9de3a2412c0 CREATED:19000101T120000Z DESCRIPTION:Shravani Purnima LAST-MODIFIED:20091226T023919Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Shravani Purnima TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100516 DTEND;VALUE=DATE:20100517 DTSTAMP:20091226T024227Z UID:CSVConverteb957b483c40eb199b149ba918847964 CREATED:19000101T120000Z DESCRIPTION:Akshay Tritiyai LAST-MODIFIED:20091226T023921Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Akshay Tritiyai TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101102 DTEND;VALUE=DATE:20101103 DTSTAMP:20091226T024227Z UID:CSVConvert4b0b0f2f418a1fb5290f6a970d15726e CREATED:19000101T120000Z DESCRIPTION:Rama Ekadashi LAST-MODIFIED:20091226T023917Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Rama Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100212 DTEND;VALUE=DATE:20100213 DTSTAMP:20091226T024227Z UID:CSVConvertc226726630d629c8f6cb4032a4012454 CREATED:19000101T120000Z DESCRIPTION:Mahashivaratri LAST-MODIFIED:20091226T023923Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Mahashivaratri TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101110 DTEND;VALUE=DATE:20101111 DTSTAMP:20091226T024227Z UID:CSVConvert1b859228261602e788f6138c3a0d9d35 CREATED:19000101T120000Z DESCRIPTION:Labh Panchami LAST-MODIFIED:20091226T023916Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Labh Panchami TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101107 DTEND;VALUE=DATE:20101108 DTSTAMP:20091226T024227Z UID:CSVConverta8e78127bee7a03f0fcf3675a6e2ad19 CREATED:19000101T120000Z DESCRIPTION:Gujarati New Year LAST-MODIFIED:20091226T023918Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Gujarati New Year TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100924 DTEND;VALUE=DATE:20100925 DTSTAMP:20091226T024227Z UID:CSVConvertf4f2aca647e09f84e63f50f5585b0311 CREATED:19000101T120000Z DESCRIPTION:Mahalaya Maha Parva Starts LAST-MODIFIED:20091226T023918Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Mahalaya Maha Parva Starts TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101117 DTEND;VALUE=DATE:20101118 DTSTAMP:20091226T024227Z UID:CSVConvert8ae6b1ef88441415b7be38abdb1542e8 CREATED:19000101T120000Z DESCRIPTION:Chaturmasa Ends LAST-MODIFIED:20091226T023916Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Chaturmasa Ends TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100812 DTEND;VALUE=DATE:20100813 DTSTAMP:20091226T024227Z UID:CSVConvert512519df1dcdc8af580cc1830e3b2d6f CREATED:19000101T120000Z DESCRIPTION:Hariyali Teej LAST-MODIFIED:20091226T023920Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Hariyali Teej TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100723 DTEND;VALUE=DATE:20100724 DTSTAMP:20091226T024227Z UID:CSVConvertf13cf7e53ad9e9ec096a5f82559ad541 CREATED:19000101T120000Z DESCRIPTION:Jayaparvati Vrat starts LAST-MODIFIED:20091226T023920Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Jayaparvati Vrat starts TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100130 DTEND;VALUE=DATE:20100131 DTSTAMP:20091226T024227Z UID:CSVConvertdbb79c5f099a47269dee888d33223fef CREATED:19000101T120000Z DESCRIPTION:Thai Poosam LAST-MODIFIED:20091226T023923Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Thai Poosam TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100314 DTEND;VALUE=DATE:20100315 DTSTAMP:20091226T024227Z UID:CSVConvertd63eff48a75b7a284d02a9719c390a3e CREATED:19000101T120000Z DESCRIPTION:Karadyan Nonbu LAST-MODIFIED:20091226T023923Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Karadyan Nonbu TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101023 DTEND;VALUE=DATE:20101024 DTSTAMP:20091226T024227Z UID:CSVConvert2d1924c9f9017f4f9591c9a97ce9340b CREATED:19000101T120000Z DESCRIPTION:Sharad Poornima LAST-MODIFIED:20091226T023917Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Sharad Poornima TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100527 DTEND;VALUE=DATE:20100528 DTSTAMP:20091226T024227Z UID:CSVConvertf8b027534f36cb964fa6e1e26b7b89a7 CREATED:19000101T120000Z DESCRIPTION:Kuma Jayanthi LAST-MODIFIED:20091226T023921Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Kuma Jayanthi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100824 DTEND;VALUE=DATE:20100825 DTSTAMP:20091226T024227Z UID:CSVConvertca7cd27986322168e2a8fe11ec62d9ff CREATED:19000101T120000Z DESCRIPTION: Yajur Upamakarma LAST-MODIFIED:20091226T023919Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY: Yajur Upamakarma TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101030 DTEND;VALUE=DATE:20101031 DTSTAMP:20091226T024227Z UID:CSVConvert00289d2e5d3a6a74c4b3245a680550ed CREATED:19000101T120000Z DESCRIPTION:Ahoi Asthami LAST-MODIFIED:20091226T023917Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Ahoi Asthami TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100708 DTEND;VALUE=DATE:20100709 DTSTAMP:20091226T024227Z UID:CSVConvert2249683ebb138c3fe5ca72edfb275942 CREATED:19000101T120000Z DESCRIPTION:Yogini Ekadashi LAST-MODIFIED:20091226T023921Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Yogini Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100225 DTEND;VALUE=DATE:20100226 DTSTAMP:20091226T024227Z UID:CSVConvert10307a2df238470a1d261e85e864a2f9 CREATED:19000101T120000Z DESCRIPTION:Amalaki Ekadashi LAST-MODIFIED:20091226T023923Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Amalaki Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100328 DTEND;VALUE=DATE:20100329 DTSTAMP:20091226T024227Z UID:CSVConvert7108395f855499720fcfcc957e7daa9d CREATED:19000101T120000Z DESCRIPTION:Mahavir Jayanti LAST-MODIFIED:20091226T023922Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Mahavir Jayanti TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100824 DTEND;VALUE=DATE:20100825 DTSTAMP:20091226T024227Z UID:CSVConvert5d9c2651aaa89f68c4f0bc3d203e2e66 CREATED:19000101T120000Z DESCRIPTION:Raksha Bandhan LAST-MODIFIED:20091226T023919Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Raksha Bandhan TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100831 DTEND;VALUE=DATE:20100901 DTSTAMP:20091226T024227Z UID:CSVConvertc639e02f58f559d1eac975982cbb6752 CREATED:19000101T120000Z DESCRIPTION:Shitala Satam LAST-MODIFIED:20091226T023919Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Shitala Satam TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100904 DTEND;VALUE=DATE:20100905 DTSTAMP:20091226T024227Z UID:CSVConvert6baaf8b21faebd5225b7c1540e4b88d2 CREATED:19000101T120000Z DESCRIPTION:Aja Ekadashi LAST-MODIFIED:20091226T023919Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Aja Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100713 DTEND;VALUE=DATE:20100714 DTSTAMP:20091226T024227Z UID:CSVConvert33e71d8784bf97789e34dae764de5653 CREATED:19000101T120000Z DESCRIPTION:Jagannath Rathyatra LAST-MODIFIED:20091226T023920Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Jagannath Rathyatra TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100316 DTEND;VALUE=DATE:20100317 DTSTAMP:20091226T024227Z UID:CSVConvertc35a800cb3c208b13dd3a73a651568c7 CREATED:19000101T120000Z DESCRIPTION:Yugadi LAST-MODIFIED:20091226T023923Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Yugadi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101103 DTEND;VALUE=DATE:20101104 DTSTAMP:20091226T024227Z UID:CSVConvert9b1d89a3a6f1df189ceb11dc1d270099 CREATED:19000101T120000Z DESCRIPTION:Govatsa Dwadashi LAST-MODIFIED:20091226T023917Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Govatsa Dwadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101018 DTEND;VALUE=DATE:20101019 DTSTAMP:20091226T024227Z UID:CSVConvertcef56414ffd6c018de7c66d3e0489417 CREATED:19000101T120000Z DESCRIPTION:Pasankusa Ekadashi LAST-MODIFIED:20091226T023917Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Pasankusa Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100122 DTEND;VALUE=DATE:20100123 DTSTAMP:20091226T024227Z UID:CSVConvert67020f3187f17a3a9090b00786361617 CREATED:19000101T120000Z DESCRIPTION:Ratha Sapthami LAST-MODIFIED:20091226T023924Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Ratha Sapthami TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100912 DTEND;VALUE=DATE:20100913 DTSTAMP:20091226T024227Z UID:CSVConvert622a85c53d68dd54c59fa15143915605 CREATED:19000101T120000Z DESCRIPTION:Rishi Panchami / Bhaiya Panchami LAST-MODIFIED:20091226T023918Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Rishi Panchami / Bhaiya Panchami TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100823 DTEND;VALUE=DATE:20100824 DTSTAMP:20091226T024227Z UID:CSVConvertb8daddd35285b645d02a31c907e319c1 CREATED:19000101T120000Z DESCRIPTION:Onam LAST-MODIFIED:20091226T023920Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Onam TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100228 DTEND;VALUE=DATE:20100301 DTSTAMP:20091226T024227Z UID:CSVConvert555e214c673b48729df1a341dc1ae6c3 CREATED:19000101T120000Z DESCRIPTION:Holi / Dhuleti LAST-MODIFIED:20091226T023923Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Holi / Dhuleti TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100812 DTEND;VALUE=DATE:20100813 DTSTAMP:20091226T024227Z UID:CSVConvert35686cc11286f169bb46dac59abe7351 CREATED:19000101T120000Z DESCRIPTION:Andal Jayanthi LAST-MODIFIED:20091226T023920Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Andal Jayanthi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100110 DTEND;VALUE=DATE:20100111 DTSTAMP:20091226T024227Z UID:CSVConvert96ef440a962049db7a6da825503b6f09 CREATED:19000101T120000Z DESCRIPTION:Sattila Ekadashi LAST-MODIFIED:20091226T023924Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Sattila Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100228 DTEND;VALUE=DATE:20100301 DTSTAMP:20091226T024227Z UID:CSVConvert499f9cc5b0529003c6f5153c8893d306 CREATED:19000101T120000Z DESCRIPTION:Holika Dahan LAST-MODIFIED:20091226T023923Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Holika Dahan TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100324 DTEND;VALUE=DATE:20100325 DTSTAMP:20091226T024227Z UID:CSVConvert744450004d0818af0dd0aba7cb483458 CREATED:19000101T120000Z DESCRIPTION:Sri Rama Navami LAST-MODIFIED:20091226T023922Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Sri Rama Navami TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100514 DTEND;VALUE=DATE:20100515 DTSTAMP:20091226T024227Z UID:CSVConvert06c22d6c1954923d39499edea0d5aa49 CREATED:19000101T120000Z DESCRIPTION:Adhika masa ends LAST-MODIFIED:20091226T023921Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Adhika masa ends TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101106 DTEND;VALUE=DATE:20101107 DTSTAMP:20091226T024227Z UID:CSVConvert2be9a179d4e738fdd53ede83fc8f8e0a CREATED:19000101T120000Z DESCRIPTION:Diwali LAST-MODIFIED:20091226T023917Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Diwali TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100120 DTEND;VALUE=DATE:20100121 DTSTAMP:20091226T024227Z UID:CSVConvertef591f9ce5c7d011319b1b89c9dc5e57 CREATED:19000101T120000Z DESCRIPTION:Vasant Panchami LAST-MODIFIED:20091226T023924Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Vasant Panchami TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100622 DTEND;VALUE=DATE:20100623 DTSTAMP:20091226T024227Z UID:CSVConvert6c8a535e53b032deb5ab1b2ef1a46591 CREATED:19000101T120000Z DESCRIPTION:Nirjala Ekadashi (Vaishnava) LAST-MODIFIED:20091226T023921Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Nirjala Ekadashi (Vaishnava) TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100820 DTEND;VALUE=DATE:20100821 DTSTAMP:20091226T024227Z UID:CSVConvert851332acd43eca785be5a51caa17acd8 CREATED:19000101T120000Z DESCRIPTION:Varalakshami Vratam LAST-MODIFIED:20091226T023920Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Varalakshami Vratam TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101117 DTEND;VALUE=DATE:20101118 DTSTAMP:20091226T024227Z UID:CSVConvertb212d5174d5e389e23e8535cb267ad7b CREATED:19000101T120000Z DESCRIPTION:Uthana Ekadashi LAST-MODIFIED:20091226T023916Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Uthana Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101007 DTEND;VALUE=DATE:20101008 DTSTAMP:20091226T024227Z UID:CSVConvertf8d5149383a3519d943c5b78e0983bac CREATED:19000101T120000Z DESCRIPTION:Mahalaya Maha-Amavasya LAST-MODIFIED:20091226T023918Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Mahalaya Maha-Amavasya TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100114 DTEND;VALUE=DATE:20100115 DTSTAMP:20091226T024227Z UID:CSVConverte67714b0410c81d34cca576d2378e441 CREATED:19000101T120000Z DESCRIPTION:Pongal LAST-MODIFIED:20091226T023924Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Pongal TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101211 DTEND;VALUE=DATE:20101212 DTSTAMP:20091226T024227Z UID:CSVConvert2677b35a35408997051b6e9bcca8ebe8 CREATED:19000101T120000Z DESCRIPTION:Kuke Shasthi LAST-MODIFIED:20091226T023915Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Kuke Shasthi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101026 DTEND;VALUE=DATE:20101027 DTSTAMP:20091226T024227Z UID:CSVConvert1077a3bb71d3d83c7f3e81e67e736a78 CREATED:19000101T120000Z DESCRIPTION:Karwa Chauth LAST-MODIFIED:20091226T023917Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Karwa Chauth TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101231 DTEND;VALUE=DATE:20110101 DTSTAMP:20091226T024227Z UID:CSVConvert93b6b6af734e651e6bb701196fead55f CREATED:19000101T120000Z DESCRIPTION:Saphala Ekadashi LAST-MODIFIED:20091226T023915Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Saphala Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100911 DTEND;VALUE=DATE:20100912 DTSTAMP:20091226T024227Z UID:CSVConvert9c93d8d566a8f38010d1321830a0e267 CREATED:19000101T120000Z DESCRIPTION:Ganesh Chaturthi LAST-MODIFIED:20091226T023918Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Ganesh Chaturthi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101118 DTEND;VALUE=DATE:20101119 DTSTAMP:20091226T024227Z UID:CSVConvert9d556038ade874d102a7f35bc592aca0 CREATED:19000101T120000Z DESCRIPTION:Tulasi Vivaha LAST-MODIFIED:20091226T023916Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Tulasi Vivaha TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100830 DTEND;VALUE=DATE:20100831 DTSTAMP:20091226T024227Z UID:CSVConvert821127609ec50f61ab61816b11e84827 CREATED:19000101T120000Z DESCRIPTION:Naag Paancham (Guj) LAST-MODIFIED:20091226T023919Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Naag Paancham (Guj) TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100622 DTEND;VALUE=DATE:20100623 DTSTAMP:20091226T024227Z UID:CSVConvertc51d6717f4f2da5f583f4e4d66b1c406 CREATED:19000101T120000Z DESCRIPTION:Nirjala Ekadashi (Smarta) LAST-MODIFIED:20091226T023921Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Nirjala Ekadashi (Smarta) TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100518 DTEND;VALUE=DATE:20100519 DTSTAMP:20091226T024227Z UID:CSVConvert6fd296142e2d692497c55de3644f9e14 CREATED:19000101T120000Z DESCRIPTION:Sankara Jayanthi LAST-MODIFIED:20091226T023921Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Sankara Jayanthi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100415 DTEND;VALUE=DATE:20100416 DTSTAMP:20091226T024227Z UID:CSVConvert8171c1b8b8ceff5d608e14a17ecc3499 CREATED:19000101T120000Z DESCRIPTION:Pohela Boisakh LAST-MODIFIED:20091226T023922Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Pohela Boisakh TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101216 DTEND;VALUE=DATE:20101217 DTSTAMP:20091226T024227Z UID:CSVConvert122a2115294b842dab282b6946ed0325 CREATED:19000101T120000Z DESCRIPTION:Dhanurmasa Starts LAST-MODIFIED:20091226T023915Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Dhanurmasa Starts TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100721 DTEND;VALUE=DATE:20100722 DTSTAMP:20091226T024227Z UID:CSVConvert393b19b21a404c296f417d74447fb6be CREATED:19000101T120000Z DESCRIPTION:Chaturmasa starts LAST-MODIFIED:20091226T023920Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Chaturmasa starts TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100415 DTEND;VALUE=DATE:20100416 DTSTAMP:20091226T024227Z UID:CSVConvert9f89ff9020464705e2f325b15b715ef1 CREATED:19000101T120000Z DESCRIPTION:Vishu Kani LAST-MODIFIED:20091226T023922Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Vishu Kani TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101111 DTEND;VALUE=DATE:20101112 DTSTAMP:20091226T024227Z UID:CSVConvertd236c0c4f5838145031daaa80c78965b CREATED:19000101T120000Z DESCRIPTION:Skanda Shasthi LAST-MODIFIED:20091226T023916Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Skanda Shasthi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100414 DTEND;VALUE=DATE:20100415 DTSTAMP:20091226T024227Z UID:CSVConvert9a9cb5575d3da938833517a2b5e18dd2 CREATED:19000101T120000Z DESCRIPTION:Baisakhi / Orrisa New Year LAST-MODIFIED:20091226T023922Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Baisakhi / Orrisa New Year TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100827 DTEND;VALUE=DATE:20100828 DTSTAMP:20091226T024227Z UID:CSVConvert0b8693884dd2fdf8edf40082b0488526 CREATED:19000101T120000Z DESCRIPTION:Kajari Teej LAST-MODIFIED:20091226T023919Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Kajari Teej TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100828 DTEND;VALUE=DATE:20100829 DTSTAMP:20091226T024227Z UID:CSVConvert6defada2142b1923b1fe65e8c2a0cfb1 CREATED:19000101T120000Z DESCRIPTION:Bolchauth LAST-MODIFIED:20091226T023919Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Bolchauth TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101217 DTEND;VALUE=DATE:20101218 DTSTAMP:20091226T024227Z UID:CSVConvertc0f26c322aaca0f8262f36723d95dea7 CREATED:19000101T120000Z DESCRIPTION:Mokshada/Vaikuntha Ekadashi LAST-MODIFIED:20091226T023915Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Mukkoti / Vaikuntha Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101108 DTEND;VALUE=DATE:20101109 DTSTAMP:20091226T024227Z UID:CSVConvertecf72d686cd5d5b675ddafc904506373 CREATED:19000101T120000Z DESCRIPTION:Bhaiya Duj / Yama Dwitiya LAST-MODIFIED:20091226T023917Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Bhaiya Duj / Yama Dwitiya TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100820 DTEND;VALUE=DATE:20100821 DTSTAMP:20091226T024227Z UID:CSVConvert8b5f9b4f81eb0c097bf199a5a9fdef6b CREATED:19000101T120000Z DESCRIPTION:Putrada Ekadashi LAST-MODIFIED:20091226T023920Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Putrada Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100721 DTEND;VALUE=DATE:20100722 DTSTAMP:20091226T024227Z UID:CSVConvert5feb5ac7c13928c497f81d8f35c41e10 CREATED:19000101T120000Z DESCRIPTION:Shayana Ekadasi LAST-MODIFIED:20091226T023920Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Shayana Ekadasi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101217 DTEND;VALUE=DATE:20101218 DTSTAMP:20091226T024227Z UID:CSVConvertf73a85b020f5030068db75e91b25ee16 CREATED:19000101T120000Z DESCRIPTION:Mokshada/Vaikuntha Ekadashi LAST-MODIFIED:20091226T023915Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Mokshada/Vaikuntha Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20101004 DTEND;VALUE=DATE:20101005 DTSTAMP:20091226T024227Z UID:CSVConvertb3b4606bf5c489a57211adcec9ac2f10 CREATED:19000101T120000Z DESCRIPTION:Indira Ekadashi LAST-MODIFIED:20091226T023918Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Indira Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100114 DTEND;VALUE=DATE:20100115 DTSTAMP:20091226T024227Z UID:CSVConvert68dea9b5d35599bff3172d831aaf4bd8 CREATED:19000101T120000Z DESCRIPTION:Makar Samkaranti LAST-MODIFIED:20091226T023924Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Makar Samkaranti TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100810 DTEND;VALUE=DATE:20100811 DTSTAMP:20091226T024227Z UID:CSVConvertcf4f52662e7873dc5d64fed15270e366 CREATED:19000101T120000Z DESCRIPTION:Diwaso LAST-MODIFIED:20091226T023920Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Diwaso TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100414 DTEND;VALUE=DATE:20100415 DTSTAMP:20091226T024227Z UID:CSVConvertb6b1c8528236db1ce2eada21fa2ce73e CREATED:19000101T120000Z DESCRIPTION:Tamil New Year LAST-MODIFIED:20091226T023922Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Tamil New Year TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100311 DTEND;VALUE=DATE:20100312 DTSTAMP:20091226T024227Z UID:CSVConvertd7e20921329ff932a687f50c81d3c825 CREATED:19000101T120000Z DESCRIPTION:Papamochini Ekadashi LAST-MODIFIED:20091226T023923Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Papamochini Ekadashi TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;VALUE=DATE:20100527 DTEND;VALUE=DATE:20100528 DTSTAMP:20091226T024227Z UID:CSVConvert4a515871154980cdcaca85c0b8be3b13 CREATED:19000101T120000Z DESCRIPTION:Vaikasi Vikasam LAST-MODIFIED:20091226T023921Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Vaikasi Vikasam TRANSP:OPAQUE END:VEVENT END:VCALENDAR tests/auto/versit/qversit/testdata_ics/worldt2009.ics000066400000000000000000000546761233466112000232160ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Microsoft Corporation//Outlook 9.0 MIMEDIR//EN VERSION:2.0 METHOD:PUBLISH BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090605T163000Z DTEND:20090605T203000Z LOCATION:Lord's, London TRANSP:TRANSPARENT SEQUENCE:0 UID:odi1 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 1st Match, Group B - England v Netherlands PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090605T163000Z DTEND:20090605T203000Z FREEBUSY:20090605T163000Z/20090605T203000Z FREEBUSY:20090605T163000Z/20090605T203000Z FREEBUSY:20090605T163000Z/20090605T203000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090606T090000Z DTEND:20090606T130000Z LOCATION:Kennington Oval, London TRANSP:TRANSPARENT SEQUENCE:0 UID:odi2 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 2nd Match, Group D - New Zealand v Scotland PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090606T090000Z DTEND:20090606T130000Z FREEBUSY:20090606T090000Z/20090606T130000Z FREEBUSY:20090606T090000Z/20090606T130000Z FREEBUSY:20090606T090000Z/20090606T130000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090606T123000Z DTEND:20090606T163000Z LOCATION:Kennington Oval, London TRANSP:TRANSPARENT SEQUENCE:0 UID:odi3 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 3rd Match, Group C - Australia v West Indies PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090606T123000Z DTEND:20090606T163000Z FREEBUSY:20090606T123000Z/20090606T163000Z FREEBUSY:20090606T123000Z/20090606T163000Z FREEBUSY:20090606T123000Z/20090606T163000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090606T163000Z DTEND:20090606T203000Z LOCATION:Trent Bridge, Nottingham TRANSP:TRANSPARENT SEQUENCE:0 UID:odi4 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 4th Match, Group A - Bangladesh v India PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090606T163000Z DTEND:20090606T203000Z FREEBUSY:20090606T163000Z/20090606T203000Z FREEBUSY:20090606T163000Z/20090606T203000Z FREEBUSY:20090606T163000Z/20090606T203000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090607T123000Z DTEND:20090607T163000Z LOCATION:Kennington Oval, London TRANSP:TRANSPARENT SEQUENCE:0 UID:odi5 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 5th Match, Group D - Scotland v South Africa PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090607T123000Z DTEND:20090607T163000Z FREEBUSY:20090607T123000Z/20090607T163000Z FREEBUSY:20090607T123000Z/20090607T163000Z FREEBUSY:20090607T123000Z/20090607T163000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090607T163000Z DTEND:20090607T203000Z LOCATION:Kennington Oval, London TRANSP:TRANSPARENT SEQUENCE:0 UID:odi6 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 6th Match, Group B - England v Pakistan PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090607T163000Z DTEND:20090607T203000Z FREEBUSY:20090607T163000Z/20090607T203000Z FREEBUSY:20090607T163000Z/20090607T203000Z FREEBUSY:20090607T163000Z/20090607T203000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090608T123000Z DTEND:20090608T163000Z LOCATION:Trent Bridge, Nottingham TRANSP:TRANSPARENT SEQUENCE:0 UID:odi7 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 7th Match, Group A - Bangladesh v Ireland PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090608T123000Z DTEND:20090608T163000Z FREEBUSY:20090608T123000Z/20090608T163000Z FREEBUSY:20090608T123000Z/20090608T163000Z FREEBUSY:20090608T123000Z/20090608T163000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090608T163000Z DTEND:20090608T203000Z LOCATION:Trent Bridge, Nottingham TRANSP:TRANSPARENT SEQUENCE:0 UID:odi8 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 8th Match, Group C - Australia v Sri Lanka PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090608T163000Z DTEND:20090608T203000Z FREEBUSY:20090608T163000Z/20090608T203000Z FREEBUSY:20090608T163000Z/20090608T203000Z FREEBUSY:20090608T163000Z/20090608T203000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090609T123000Z DTEND:20090609T163000Z LOCATION:Lord's, London TRANSP:TRANSPARENT SEQUENCE:0 UID:odi9 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 9th Match, Group B - Pakistan v Netherlands PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090609T123000Z DTEND:20090609T163000Z FREEBUSY:20090609T123000Z/20090609T163000Z FREEBUSY:20090609T123000Z/20090609T163000Z FREEBUSY:20090609T123000Z/20090609T163000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090609T163000Z DTEND:20090609T203000Z LOCATION:Lord's, London TRANSP:TRANSPARENT SEQUENCE:0 UID:odi10 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 10th Match, Group D - New Zealand v South Africa PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090609T163000Z DTEND:20090609T203000Z FREEBUSY:20090609T163000Z/20090609T203000Z FREEBUSY:20090609T163000Z/20090609T203000Z FREEBUSY:20090609T163000Z/20090609T203000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090610T123000Z DTEND:20090610T163000Z LOCATION:Trent Bridge, Nottingham TRANSP:TRANSPARENT SEQUENCE:0 UID:odi11 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 11th Match, Group C - Sri Lanka v West Indies PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090610T123000Z DTEND:20090610T163000Z FREEBUSY:20090610T123000Z/20090610T163000Z FREEBUSY:20090610T123000Z/20090610T163000Z FREEBUSY:20090610T123000Z/20090610T163000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090610T163000Z DTEND:20090610T203000Z LOCATION:Trent Bridge, Nottingham TRANSP:TRANSPARENT SEQUENCE:0 UID:odi12 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 12th Match, Group A - India v Ireland PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090610T163000Z DTEND:20090610T203000Z FREEBUSY:20090610T163000Z/20090610T203000Z FREEBUSY:20090610T163000Z/20090610T203000Z FREEBUSY:20090610T163000Z/20090610T203000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090611T123000Z DTEND:20090611T163000Z LOCATION:Trent Bridge, Nottingham TRANSP:TRANSPARENT SEQUENCE:0 UID:odi13 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 13th Match, Group F - TBC v TBC PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090611T123000Z DTEND:20090611T163000Z FREEBUSY:20090611T123000Z/20090611T163000Z FREEBUSY:20090611T123000Z/20090611T163000Z FREEBUSY:20090611T123000Z/20090611T163000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090611T163000Z DTEND:20090611T203000Z LOCATION:Trent Bridge, Nottingham TRANSP:TRANSPARENT SEQUENCE:0 UID:odi14 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 14th Match, Group E - TBC v TBC PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090611T163000Z DTEND:20090611T203000Z FREEBUSY:20090611T163000Z/20090611T203000Z FREEBUSY:20090611T163000Z/20090611T203000Z FREEBUSY:20090611T163000Z/20090611T203000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090612T123000Z DTEND:20090612T163000Z LOCATION:Lord's, London TRANSP:TRANSPARENT SEQUENCE:0 UID:odi15 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 15th Match, Group F - TBC v TBC PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090612T123000Z DTEND:20090612T163000Z FREEBUSY:20090612T123000Z/20090612T163000Z FREEBUSY:20090612T123000Z/20090612T163000Z FREEBUSY:20090612T123000Z/20090612T163000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090612T163000Z DTEND:20090612T203000Z LOCATION:Lord's, London TRANSP:TRANSPARENT SEQUENCE:0 UID:odi16 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 16th Match, Group E - TBC v TBC PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090612T163000Z DTEND:20090612T203000Z FREEBUSY:20090612T163000Z/20090612T203000Z FREEBUSY:20090612T163000Z/20090612T203000Z FREEBUSY:20090612T163000Z/20090612T203000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090613T123000Z DTEND:20090613T163000Z LOCATION:Kennington Oval, London TRANSP:TRANSPARENT SEQUENCE:0 UID:odi17 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 17th Match, Group E - TBC v TBC PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090613T123000Z DTEND:20090613T163000Z FREEBUSY:20090613T123000Z/20090613T163000Z FREEBUSY:20090613T123000Z/20090613T163000Z FREEBUSY:20090613T123000Z/20090613T163000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090613T163000Z DTEND:20090613T203000Z LOCATION:Kennington Oval, London TRANSP:TRANSPARENT SEQUENCE:0 UID:odi18 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 18th Match, Group F - TBC v TBC PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090613T163000Z DTEND:20090613T203000Z FREEBUSY:20090613T163000Z/20090613T203000Z FREEBUSY:20090613T163000Z/20090613T203000Z FREEBUSY:20090613T163000Z/20090613T203000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090614T123000Z DTEND:20090614T163000Z LOCATION:Lord's, London TRANSP:TRANSPARENT SEQUENCE:0 UID:odi19 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 19th Match, Group F - TBC v TBC PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090614T123000Z DTEND:20090614T163000Z FREEBUSY:20090614T123000Z/20090614T163000Z FREEBUSY:20090614T123000Z/20090614T163000Z FREEBUSY:20090614T123000Z/20090614T163000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090614T163000Z DTEND:20090614T203000Z LOCATION:Lord's, London TRANSP:TRANSPARENT SEQUENCE:0 UID:odi20 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 20th Match, Group E - TBC v TBC PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090614T163000Z DTEND:20090614T203000Z FREEBUSY:20090614T163000Z/20090614T203000Z FREEBUSY:20090614T163000Z/20090614T203000Z FREEBUSY:20090614T163000Z/20090614T203000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090615T123000Z DTEND:20090615T163000Z LOCATION:Kennington Oval, London TRANSP:TRANSPARENT SEQUENCE:0 UID:odi21 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 21st Match, Group E - TBC v TBC PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090615T123000Z DTEND:20090615T163000Z FREEBUSY:20090615T123000Z/20090615T163000Z FREEBUSY:20090615T123000Z/20090615T163000Z FREEBUSY:20090615T123000Z/20090615T163000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090615T163000Z DTEND:20090615T203000Z LOCATION:Kennington Oval, London TRANSP:TRANSPARENT SEQUENCE:0 UID:odi22 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 22nd Match, Group F - TBC v TBC PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090615T163000Z DTEND:20090615T203000Z FREEBUSY:20090615T163000Z/20090615T203000Z FREEBUSY:20090615T163000Z/20090615T203000Z FREEBUSY:20090615T163000Z/20090615T203000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090616T123000Z DTEND:20090616T163000Z LOCATION:Trent Bridge, Nottingham TRANSP:TRANSPARENT SEQUENCE:0 UID:odi23 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 23rd Match, Group F - TBC v TBC PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090616T123000Z DTEND:20090616T163000Z FREEBUSY:20090616T123000Z/20090616T163000Z FREEBUSY:20090616T123000Z/20090616T163000Z FREEBUSY:20090616T123000Z/20090616T163000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090616T163000Z DTEND:20090616T203000Z LOCATION:Trent Bridge, Nottingham TRANSP:TRANSPARENT SEQUENCE:0 UID:odi24 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 24th Match, Group E - TBC v TBC PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090616T163000Z DTEND:20090616T203000Z FREEBUSY:20090616T163000Z/20090616T203000Z FREEBUSY:20090616T163000Z/20090616T203000Z FREEBUSY:20090616T163000Z/20090616T203000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090618T163000Z DTEND:20090618T203000Z LOCATION:Trent Bridge, Nottingham TRANSP:TRANSPARENT SEQUENCE:0 UID:odi25 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 1st Semi-Final - TBC v TBC PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090618T163000Z DTEND:20090618T203000Z FREEBUSY:20090618T163000Z/20090618T203000Z FREEBUSY:20090618T163000Z/20090618T203000Z FREEBUSY:20090618T163000Z/20090618T203000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090619T163000Z DTEND:20090619T203000Z LOCATION:Kennington Oval, London TRANSP:TRANSPARENT SEQUENCE:0 UID:odi26 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: 2nd Semi-Final - TBC v TBC PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090619T163000Z DTEND:20090619T203000Z FREEBUSY:20090619T163000Z/20090619T203000Z FREEBUSY:20090619T163000Z/20090619T203000Z FREEBUSY:20090619T163000Z/20090619T203000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT BEGIN:VEVENT ORGANIZER:MAILTO:unknown DTSTART:20090621T140000Z DTEND:20090621T180000Z LOCATION:Lord's, London TRANSP:TRANSPARENT SEQUENCE:0 UID:odi27 DTSTAMP:20060928T150023Z DESCRIPTION:Follow every ball of every international match live on Cricinfo.com: http://www.cricinfo.com \n\nGet all the latest ICC World Twenty20, 2009 news at: http://www.cricinfo.com/db/ARCHIVE/2009/ICC-WORLD-2020/ SUMMARY: Final - TBC v TBC PRIORITY:9 CLASS:PUBLIC BEGIN:VALARM TRIGGER:PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM BEGIN:VFREEBUSY ORGANIZER:MAILTO:unknown DTSTART:20090621T140000Z DTEND:20090621T180000Z FREEBUSY:20090621T140000Z/20090621T180000Z FREEBUSY:20090621T140000Z/20090621T180000Z FREEBUSY:20090621T140000Z/20090621T180000Z URL:http://www.cricinfo.com END:VFREEBUSY END:VEVENT END:VCALENDARtests/auto/versit/qversit/testdata_vcf/000077500000000000000000000000001233466112000206465ustar00rootroot00000000000000tests/auto/versit/qversit/testdata_vcf/AAB4-MultipleAll.vcf000066400000000000000000042166601233466112000242560ustar00rootroot00000000000000BEGIN:VCARD VERSION:3.0 N:;;;; FN:Apple Computer Inc. ORG:Apple Computer Inc.; TEL;type=MAIN;type=pref:1-800-MY-APPLE item1.ADR;type=WORK;type=pref:;;1 Infinite Loop;Cupertino;CA;95014;United States item1.X-ABADR:us item2.URL;type=pref:http\://www.apple.com item2.X-ABLabel:_$!<HomePage>!$_ PHOTO;BASE64: TU0AKgAAAyiAP+BQOCQWDQeEQmFQuGQ2HQ+IRGJROKRWLReMRmNRuOR2PR+QSGRSOPPyTOZ1OxjM 1pLZfsVtuBxySaTWHvF5vVkNBqqlZrlTrFbqqfthvOKbUmlQN4PN6L1jMugrep1NZLtguR0uul12 RvZ8PhhstoVWhWZbr5jsywPivW+PzFxqhZUCz3ehz9st+Z3C/Rp6vd8MFks60Whis1ovd8vm/4+L ul2O5YrpgYehMBkM2mvTIZ+LOJzOm0ZVgM5qtmcvbQa1+696PZ76vXv2Eu14PJmNNsNNtN7JO7a0 yncF2u9423Wx+TPx0ZNlNFqrthslar5iddiLxispmtRsc93Pl9PuB7Xyvt3vJ575vVBlrVesOrsF ZLxgy5i2NoN1xnMxjHOWiLVmWaRrFcW5eroXDMKooS6FyV5cF8zRmmubpwG4cJyJWaT6wjB0HKIX MLHUdx4QGhbcHk7hlREvEYQeW8GQZGUbxioTtHIdB1uHFTVl4YkXxzGccSNIsjyUvDCGdALlubAx rSXJEqypK8ilyYRjq1HzXs+2seHWWBcl/LErSTNM0TWtBcGCYzjnjFR/vEY5nGnG01TPPc9KFCZf NEdM5oGaBrm27U+TZPtFUYYhmGi9NBoEX61xJRNL0WtBWFsXijKRSSB0RTNR0ZTBbvqcrJVAgb61 NV1SPkYZ1OPVaBNNV9S1JGRZl4YTxVqf9eGFXFiTVBJem8cZz2BIRlTzXNoWLGcIu+bFgTsacyTN XVuWjWDsTjUBsG6cLqGTaV0LxBhkulJ8VRY3ZsRDbt02hEl4uU1zXnAcpz21euAQgur+Vmd7ltW+ GA29ha0F0YZkTmaRsG5eeGXpi9GSlOb1nnYWFXrTZeYLUDUGzj+MRknZq0jSSnHrNxjZPi0qvrE8 U2AgVknOVZaF1mUzp8XL3ZwgramgaxtZ/btHGjfOiIKtsLaVRhbmAYtw6ehacHrqVFwZhxkSlQpt 2qtRmUtReYHOdZ26yiLAnwaOJvqVZal2+5g5VLrYnu9LauatrxXi7RWwVvF445t3FcXxnG8dx/Ic jyXJ8pyvLIIgIAANAQAAAwAAAAEAMAAAAQEAAwAAAAEAMAAAAQIAAwAAAAMAAAPKAQMAAwAAAAEA BQAAAQYAAwAAAAEAAgAAAREABAAAAAEAAAAIARUAAwAAAAEAAwAAARYABAAAAAEAAADjARcABAAA AAEAAAMgARoABQAAAAEAAAPQARsABQAAAAEAAAPYARwAAwAAAAEAAQAAASgAAwAAAAEAAgAAAAAA AAAIAAgACAAK/IAAACcQAAr8gAAAJxA= X-ABShowAs:COMPANY X-ABUID:C5C50103-C86C-40BB-8864-909A089EB390\:ABPerson END:VCARD BEGIN:VCARD VERSION:3.0 N:Lastname;Firstname;;; FN:Firstname Lastname ORG:Company; EMAIL;type=INTERNET;type=WORK;type=pref:work@email item1.EMAIL;type=INTERNET:other email item1.X-ABLabel:_$!<Other>!$_ item2.EMAIL;type=INTERNET:custom@email item2.X-ABLabel:custom email label TEL;type=WORK;type=pref:work phone TEL;type=WORK:work phone 2 TEL;type=CELL:mobile phone TEL;type=WORK;type=FAX:work fax item3.TEL:other phone item3.X-ABLabel:_$!<Other>!$_ item4.TEL:custom phone item4.X-ABLabel:custom label item5.ADR;type=WORK;type=pref:;;Work Address;Work City;Work State;Work ZIP;Work Country item5.X-ABADR:us X-AIM;type=WORK;type=pref:workaim X-JABBER;type=HOME;type=pref:Jabber X-JABBER;type=WORK:workjabber item6.X-MSN;type=pref:othermsn item6.X-ABLabel:_$!<Other>!$_ X-YAHOO;type=HOME;type=pref:homeyahoo X-ICQ;type=WORK;type=pref:workicq PHOTO;BASE64: TU0AKgADAAhIMyBIMyBIMidELiNDKxc/KBVAKRY/KBVALBZGMRpIMx5KNSBKNylOOixPOSdIMiFD LxhEMBlEKxlFLBo6LBY5KxU5JQ47Jw87KhU+LRdELh9IMiNNNSlQOSxVPi5ROytPOS1POS1PODFT OzRKQDA9MyRHKRZJKxdJMyJOOCZKOCVFMiBAMyVAMyVAMydFOCtMQDVHPDFFNyU/MSA8LyFGOSpP QC5WRzRQPzBJOSo8MiM4Lh8/LyFVRDReTEVlU0xnT0FkTT9hT0NjUUVhSTxdRjlJNSBDLxpHNy5T QTlYQzJMNydUOSVVOiZRNyNOMyBKLBpIKhhDLxpFMRxGMRxFMBtOPihQQCpMOSZHNCJMNSJWPytW QTNWQTNTPjFKNypMMiJNMyNTQTNcSjxiT0ZfTURYRS9MOSRUQC9bRzVbSjhNPStHOCY/MB9ENR5K PCRYQThXQDdROy5POSxGOiVEOCNKOy1TQzRNPS9AMSREMCRHMydELyBJNCVDLxhFMRpJNyZEMSFF MSRALSA5LB48LyFGOjhOQT9VSUBYTURTQzdNPTFJOylDNCM/Mx83Kxc6LyhEOTFRRj5XTERYRz5J OTBBMSNMOyxOOi9OOi9KMy9MNDBENSxFNy1IODJMOzVMRTxTTENUSj5GPTFGNylGNylAOSo8NCZG LytROjVQPjVTQDhNQDxOQT1OPixJOihFNC9JOTNJPz5KQD9YRUlfTFBcSUxFMzU9JBpGLCJINzdR Pz9aRkhcSEpOOzlFMjBIOTpaSUpeTElcSUdROTRNNDBDLSJDLSJBLRpELxxOOChQOipTOzRWPjhW RT9UQz1POjVNODNJMi5IMS1NPDdYR0FfSUFNODBBLy1RPjxWQ0NOOzs9JyE+KCJALCVBLSZIMiNH MSI9Lhk/MBtOOSlVPy9YQThcRTtbQzdVPTFUOS1WOy9VPylRPCZMNSk+KR1FMCtQOzVPOy5MOCtR OCVWPClcQTBUOilHOCRGNyNKNSRGMSBHMh1UPihPNCFGLBlKNSZPOipMMiREKx0/JhJAJxNEKhZH LRhIMBxNNCBQOB9NNBxHNylKOixJNSlEMCRHMR5KNCFGMB0/Khc8KQ88KQ9GKBZKLBpGMB1BLBlF LxxKNCFHOi1PQTRXRDJWQzFQQCxMPChWOzBYPTJTQTVKOi5HKhtKLR5NOidNOidJPCxFOChDNSZE NydDLyJDLyJMPC5PPzFNNSlIMSU7MSE7MSFKOylURDFWPzNTPDBFLyNFLyM3MCVEPTFaTkZhVU1i UEBkU0NfTkViUEdcUUNXTT5INSNALhxOOi1XQzVbRTRQOytYOzFcPjRYQTFTPCxQNCVNMSJKNChM NSlFMCFDLh9OPS5XRjdNOSxJNSlJOiRTQyxWRjpRQTVPPy1KOylBLiFEMCNQPzlfTkdjU09eTkpX QzRNOStMSDVVUT5bSUBQPzdIOig8Lh1GMiVVQDJdPzVaPDJROytUPS1KOitIOClIPzVMQzlIOSs9 LiFKNChMNSlKNCFKNCFBLxtHNCBNPC5EMyZFMB89KRg+LyE9LiA7NS9AOzRRRj5aTkZRRzlKQDJD PS43MSM9Kh4+Kx88LCZDMixRRUNVSEZMOjhDMS9HLSVQNS1UPTNTPDJQPDFJNSs9NCk/NytBNy9G OzNEQztJSEBORTlFPDBBMSNBMSM7My47My5BMStGNS9MQDVQRTpVRj5TRDxMPjFFOCtGMiZGMiZF MjJKODhNRUFPR0RJPjM+MylFLiVKMypNOzVXRT9cTE1XR0hFNTI8LSo9MzBPRUFWSUdUR0VNOS5H MylEMCM/LB9BLRZMNx9UPi5VPy9VQzpTQDhROjNNNS9MMSdOMylNNTJONzNTQDteTEZVQDNFMSVF Ny1TRDpTPjNHMyk9Jxs+KBxFKxhJLxxQMiFRMyJDMyVIOSpVQDNWQTRXQTpXQTpYQTFUPS1TPS1W QDBVPTFQOS1KMydDLCBNNTFTOzdJOi5GNytOOixXQzRaRDNPOipQOylTPStOOChVPi5cRDdfRzpb PCpPMSBQNyRUOidIMBo6Iw9DJg9FKBFGKhZOMRxPOSdROylNOSNOOiRHNylOPS9MNSZNNydONCRP NSVMMxtMMxtFMRhFMRhMMCFMMCFKNSJJNCFJNCFKNSJNOS5VQDVWSDtTRThNRTJHPy1MPC5OPjBN PjdGODBBLCBAKx9UPS1WPy9KQDBEOipGNyhHOClMMSlGLCRQOi5aQzdTOzRONzBFMCFELyBHMiNU Pi5WRTdTQTNHLhxEKxlDLSJJMyhUSEBYTUVhVkdfVUZfT0BjU0RfUEZeT0VQPzFDMiVJPC1QQzNW RTlWRTlVQz1aR0FdRT5YQDpUPS1TPCxMPCpNPStEMCZGMihTPDBeRztcRDdROi1HOCpWRjheTENc SUBFOjE8MSlBLiFGMiVMQDVXTEBnTU1lTExhRTpWOzBQSTNaUzxTTEFIQThEMyU+LiBNNy1WPzVe QDdYOzFcRjVbRTRUPzFPOy1NRDpKQThGNyVENCNTPDBUPTFQOyVOOSNPOCxTOy9TPTVNODBILCU/ JB0/KxxDLh9BMyxBMyxTPTVXQTpPPzFRQTNIPzM4LyRDKR9FKyFELypPOjRWRUhUQ0ZMNy9FMClB KyJPOC5VQzxUQTtNOzRBMCo/MiZAMyc9MTFFOTlMRD5ORkBOPjBHOCpALSBALSBGNCxEMipDMipE MytQPjVWRDtaRTpRPTJJOixFNShBMSNBMSNBMChJOC9UPTxTPDtOOi1MOCtQOSxVPTBQPjxWREFW QUZVQEVIOjI4KiM3JiRNOzlWTU5KQUNONy1MNCtIMx5FMBtMMiBYPitRQTVWRjpXRj1RQDhROjBO Ny1QODFTOjNPODNXPztfT0BfT0BKPS4/MiRJNDBYQz5WPytKNCE5JxY8KhhGMhdPOx9UPSlVPipT Oy9XPzNTQzdWRjpRQDRKOi5WOzBWOzBYQTVeRztUQz1MOzU+LyQ9LiNJMypMNSxMOy1OPS9URDhR QTVPPTRINy5KOylMPCpTQDpcSUNkU0NUQzNKNSRDLh1PNyBPNyA/Lg4+LQ0+KA87JQ1BLBlIMh9K NCFROydNNyVJMyJGNB5MOiNMMx9ONSFPOSdOOCZIOSNHOCJNNyNPOSVVPi5UPS1TPyxTPyxRPCpO OSdJNC1WQDldTD1YRzlURDhOPjJFOytHPS1QOSxTOy5POCtPOCtPQC5QQS9VPjFNNypJOSpMOyxN Ny1GMCdIMS5cREBYQDpTOzRNNydJMyRGMSJMNydUQzdWRTlVOi5XPDBNNyVGMB9JOTBaSD9qVlRq VlRkVEdjU0ZhUUpfUElTQTxHNzFANS5MQDlWSUdYTElUSUhVSklcSkRcSkRcRzxUPzRROytTPCxK NChNNypUQTleTENTQzdMPDBIQDFRSTpbTERaSkNRQTVENCk/MSA+MB9RPTBeSTxaT05dU1FcSURW RD5USkBaUEZWRz9KPDRAMSBDMyJKNS5UPjddQzFeRDJdRjpcRTlXQzhRPTJOPjJNPTFMOy1RQDJT PTVTPTVXQDRYQTVQPC9TPjFMQDhJPjVHMiM+KhtBLRhELxpFMB1JNCFJPC1QQzNMQzpPRj1QQTtH OTJAKh5AKh5ALzFPPT9XRERPPDxFNSRDMyJFMRxNOSNYPDlXOzhROTRONTFHMylINCpAMixDNC5J ODFQPjhTRDxHOTFAKBpFLB5GMCVFLyRELSdMNC5OPDVWRD1OQDNJPC9BOCc+NCRELyhFMClEMyhG NSpKOi5PPjJROi5YQDReSDhYQzJTQzdTQzdYPz9VPDxGNys5Kh83JyJJOTNNRUFGPjtQOjBQOjBI Mh9HMR5QOjBbRDpWREFYRkRYSDxURDhXQTxVPzpNOzlQPjxWRT9aSENcTj1YSjpNODNNODNOPDVO PDVOOSdGMSBBKRdNMyFRPS9TPjBTPjNTPjNVOi9XPDFURTtPQDdFOCg/MiNONzNXPzxbTEVdTkdW RDtJOC88LRw7LBtFLiNJMidQNS1cQDhUQzpOPTRHNzBEMy1ENDVJOjtaRkpiTlNaSTdMPCpIMyJB LRxPPi9NPC1DNRY7LhA7Kgo8Kws/LBZALRZKNSJOOSVMMx1ELBZALBlNOCRNOChKNSZKNSJMNyNJ OiRHOCJNOStPOy1QPC9RPTBOPTRNPDNPPjBNPC5KOixTQTNYRTNXRDJMPi9KPS5JOixMPC5NNSlQ OSxOOCxTPDBMQTFKQDBQPzBHNyhGMiZJNSlKNCtGMCdHLzFXPkBaR0BUQTtPOihMNyVJNCFIMyBJ OTJVRD1VQS5QPSpMNyNIMyBHMSZVPjJfTUpjUE5kT0FkT0FeU0lcUEdaPD1QMzRAMCNJOStUREBb SkdVTENVTENdTUBcTD9XSTxVRzpOPS5PPi9IOCpMOy1PQz5dUExPQy9EOCVEPCtNRTNeU0laTkVT PjBKNylBMSNDMiRMOyxXRjdYSk1cTlBaSENYR0FUSUZbUE1aRkpKODw+LiBAMCJHOCxPPzNVRDVb STtcRD9bQz5UQzpPPjVMNSxKNCtQPjVUQTlRQTVURDhVRDVUQzRPPzNQQDRKPzdDOC9HMh88KBY/ LBZBLhdJOCFNOyRGPC5JPzFFPjVNRj1ORzFJQy1JNSlDLyNBMi9MPDlTQDpNOzRHOCJHOCJMOCJP OyVVPjRVPjRTOzFROjBKOCNBLxs8LSBAMSRHNS9NOzROPzVIOjBHMiFHMiFBLCBFLyNJMyhNNytP OzBVQDVKQTVHPjJFOi9DOC1GNSpEMyhHMyZHMyZJOitRQTJbSD9iT0ZaSkRTRD1NPzBKPS5OPDNN OzJEMCY9KiA+LCpINTNKOjFIOC9KMy9MNDBENCdENCdMNzJaRD9YRkZXRUVWRERUQUFUPjlPOjRN PD9OPUBTQD5RPz1VRTdNPS9HMCRIMSVHOi1HOi1GNyNFNSJJLxhRNx9XQDNVPjFTOzdTOzdRPS9a RTdWSDtKPTA/MRw5KxZQOjteR0hcUUNWTD1TQzdOPjJGMyM9KxtEKiJPNCxYRUVjT09YR0FAMCs4 Khk3KRg5LypMQTxbST1bST1RQTVJOi5GMhc+KxFKNyxNOS5HMxo8KRE8KBA8KBA+KxZALRhJNR5N OSFMNyFGMRw9LBZFMx1HMydFMSVNOStPOy1HNyhGNSdFNSpGNytJOS1KOi5EOS5ANStKOi5JOS1M PTdMPTdKOitMOyxIOSpGNyhFNSpFNSpFNClMOy9MPC5OPjBNPzJOQDNIOyxFOClEMyhHNytNNytO OCxIMjFPOThTRkRQREFPRTdKQDJJOStJOStMOzRQPzlPQTROQDNNOzJHNS1GNB5HNR9YRkBeTEZd R0FdR0FiUUFhUEBeRDRONCZGLytKMy9MPztYTEdVSUBVSUBdTUleTkpYSUNVRj9NPTFOPjJDNSlK PTBUQURjUFNYRkBNOzVHOTFRQztbU1FRSUhOPDVJODFJMyJMNSQ+LyFJOitaT0BbUEFbSUBaSD9d TEVhT0hdRz9NODA8Lhs+MB1EOS5KPzRURTtaSkBcSEZbR0VQRTxGOzJBKydJMi5UQzdUQzdVRzpV RzpQQDFTQzNVRDtXRj1KQTVDOi5JMRlAKRJALhJINRhQOydRPChKPDJJOzFIPTJMQDVNRTJMRDFK OTBBMCg/MSpHOTFPOzBPOzBFNSBJOiRPOy1UPzFWRD5QPjlXPzlWPjhRPSVGMhs+LRc/LhhEMixJ ODFKOjNKOjNFNShDMyZBMSREMyZDMyZHOCpMPi5PQTFRQDpKOjNKOjRHNzFFNy9DNC1ENCdGNylP PjVaSD9bTk5bTk5JRTpDPjNDOC8/NCxGOSxDNSlALh5BLx9HNytHNytINzBKOTJHNS9HNS9ENS4/ MSo3LSxNQ0FbSUBXRj1WRT9UQz1QOjBOOC5OPDdRPzpRQzxTRD1TQzdMPDBMNSpHMSZAMR5FNSJI NB9TPihVPixeRzRiT0hYRj9VPTdUPDVTQzdXRztPQz4/My89KSRBLShTSEVdU09aR0FUQTxPPzxJ OjdKNSQ/Kxo9Ky1VQURbSEZVQ0BIOCwyIxgvIBg6KiI8MjFPRURjSkdbQz9TPCxAKxw0Ig88KRVN MSBTNyVPNRpKMRZFKhA/JQw/KBNGLhhJNB9NOCJHOCQ6KxgzJg83KRE+KRZGMB1HOCJGNyFGNylF NShBMic+LyRHMylGMig8LR8+LyFDMiRHNyhNODNQOzdOOi1MOCtHMSBIMiE/LyQ+LiM+NCVBOChD OypDOypMPCpKOylGMyE/LRtBMiFKOylKOylHOCZNOSxJNSlKOy9QQDRPRDlIPTJOOCtMNSlKOixN PC5OOi1POy5OPTRIOC9NNSpJMidVMSxcODJVRDheTUBYUUVVTkFXSTlNPy9BOCc8MiJIPTJXTEBN R0NIQz5TTUpTTUpcSEZPPDpTPTVXQTpQOzNMNy9QPjVbSD9YRj1NOzJIOjBYST9aUUxVTUdQQCxJ OiZGOCBGOCA4LiE+NCdJPzxMQT5aR0FeTEZkVFNdTUxURzNFOSY/MBs4KRU9MCFIOytbR0VdSUdX RERTPz9IOS1HOCxAKyJKNCtVRTlhUERaTDxVRzhTRTRWSDhbSj5bSj5URDVMPC5FMxs/LhZKNx9W QSlbRjhdSDpRRzlGPC5NOzRRPzlQRT1OQztPOjRDLik9Lhk7LBdGMSJMNydINChRPTBWQDlVPzhU QTtTQDpQPTtRPjxQOi5NNytFMBs+KhY4KRxAMSRGOC5HOS9BMiVAMSRAMSA+Lx45KhxGNyhJPC9K PTBPOjJTPTVNPDVGNS9HNytAMCU+MClJOzNUSD9YTURWRT5KOjM7LCE+LyRAMR4/MB1INCdJNShO PS9KOixENSxDNCtFNCxFNCxBMiFBMiFFNCk9LSI0Ly09ODVWRD5XRT9TQTtRQDpPOjJJNC1BLiJG MiZPOjVRPDhMNzFMNzFFMiA7KRcxIxM+Lx5PODFcRD1lT0dhSkNaSTtHOCpEOCVNQC1MRjdRTDxN RDo+NSw9LitGNzNRR0RRR0RRQztQQTpPQTRHOi1ELxY9KRFEMCZWQTdTQTtNPDVILx8/Jxc6JBk+ KB1FLjNYQEZYTUFIPTJHMh88KBY9LBZRPyhRNyFQNSBWORlTNRZGLg9AKQtFLBRHLhZNLyBTNCVH OSc9Lx40JQs5KQ89KBlELh8/MRw/MRxIOSVMPChGNSo7KyBALBtALBs6Kxg9LhtHMCRONypMNzJO OTRRPTJRPTJINB9GMh08LyE+MSNBNSNHOyhGPCtHPSxOOypKOCdGMx8/LRlAMCJHNyhNOidOOyhK OitHNyhGNylNPS9QOS9XPzVVPi5ROytVPipTPChPPCtTPy5PPzFMPC5IOClIOClILyFJMCJJPC9U RjlfTkVhT0ZYTUFVST5DPy86Nyc+MyxOQztKRUBMRkFRSkFWT0ZVSERMPztHPDNQRTxRQDpIODFQ QDRYSDxVPTdMNC5IOy5XSTxXU0hWUUdWQTdTPjNHOipIOys8MSk+Mys+Mi5MPztaR0dfTU1pUVNk TU5VRzhGOSo+MBs3KRU+MiBPQy9cSUdVQ0BPQDlKPDRFNShENCdFLiNPOCxaTUpeUU9RSkFORz5O QDNVRzpaTD5XSTxbRjtRPTJBOSNHPihRRDRXSTpWSUVXSkZQQzVOQDNNOzJQPjVRQDpPPjhPPClD MB4/KxZIMx5GNSpFNClJNC9RPDdVSUBaTkVUQzxNPDVFOTRKPjpKPDRHOTE+LRc6KRRIMyJKNSRE NS5BMyxIMyRFMCFBLRY9KRM5KhlGNyVMPC5MPC5OPDNOPDNJOi5BMidFMSVGMiZENztPQUZXSEBT RDxJOiw7LB85Jxc9KxtELiNNNytQQzVQQzVOPTdNPDVHMydEMCREMCREMCRDLyNALSE9Kxs+LBw8 LSI/MCVHOzdOQT1VQ0BPPTtFMytALydBMSZEMyhINzRHNTNENCdAMSQ9KxkyIRAyJR47LSZRPDhf SUVbT0ZWSkFMPjE3Kh49MCRKPTBTQTVUQzdJOTA+LiZAMydBNChKPzhTRz9OQzpJPjVPPi9QPzBI OSVAMR5ENSxQQThNPjdGODBEMhxBMBpAKiZAKiZMOjxXRUdRQTVENCk/MSA8Lh0/MiZMPjFUMyVa OSpiQCZdPCJWQCZUPiRPNB1KMBlPLR5cOSlTPS1FMCFAKRJAKRI8KBRELxpFMh5FMh5NPStOPixA NCI0KRc9JhE+JxJAKBZAKBZGKx5MMCNMNSpUPTFOPS5HNyg7Lxk1KhU6Jxs8KR1DMyVJOitDPCdI QSxKOyxIOSpGMCQ9KBxDLyJGMiVJOSpNPC1IPjBDOStGNSdFNCZMNzFTPThUPzJVQDNVRDRQPzBV PixVPixOPi9KOyxJOiRIOSNFOSZANCI8MiVIPjBbSEFhTkdWSkFUSD9HPjREOzE9OS5FQDVORTtQ Rz1VRUFaSUZWRT5PPjhOPjJQQDRQPzFQPzFKQzFUTDpbPjlMMCtAOCxMQzdQT0lTUUxYPzxWPTpN NzVQOjlNODNELys7LSRJOzFeRUlkSk9eSk9aRkpKRUA9ODM9MCQ4Kx9ENyhQQzNXTkRQRz1GPCxD OSlGNCxEMipFLyRTPDBYTkpcUU5UR0NQRD9QPjlXRT9cSUNYRj9URTtOPzVFOi9MQDVXR0ZdTUxb TkxXSkhMPTNHOS9KOjNKOjNOPTFIOCxALh4+LBxFMSRNOStIOCxBMSZGMSpQOzNQSkRbVU5NRDtB OTBGNTBFNC9GNS9FNC48MB44LBpGOSpGOSo7Mio3LiY8LRw8LRw8KhY9Kxc9LR9IOClQPC5RPS9O PDNKOTBENydBNCVDLSRHMShBNzpKP0NUSTtMQTM/OCc1Lh44KRs6Kx0/LylJOTJRPzlOPDVINChD LyNBKx9ELSFDMB4+LBo/KR4/KR45KhY6KxZBLR5FMCFJPjNRRjtVQT9KODVELSpAKic8Myg8MyhB Myo9LyY9MCI6LR81IxYzIRU1JiBAMCpPPDxaRkZWTDtJPy9GMCU6JRo6LCZJOzRUPTNTPDJKMydH MCRIMyROOSlTQTVTQTVRPS9QPC5GOSFGOSE9LiA3KBo+MSVNPzJNPixDNCM9KRM7JxE7JCBGLipR PDdPOjRPOS1GMCU4KBs5KRw/Li5NOztHLh5WPCthRTBlSTRjTDlbRDFPOyVBLhlKMh5ROSRWOS9W OS9MMiBFLBpEMRZFMhdFNSBJOiROPzlNPjhNNyVFLx5DKxdELBhBLBk/KhdFMCFNOChMOShNOilO PixIOSc+LBg8KhY7IxI8JBM3JRJDMBxIMyJOOSdbQC9VOypMMiBBKRdBLCBDLSFJNDBOOTRJQCxA OCRFMiA+LBpBMSRKOixQPDFXQzhUQTxVQz1dRz9cRj5WRjFTQy5OOypINSVIOSNENB89MCQ8LyNH Oj5URkpQST9TTEFURTtJOzFENClKOy9QPzlUQzxXREFbR0VURDhPPzNNOSxPOy5UPS1TPCxKQDJR RzlVPTdQOTJJODJTQDtRTkpQTUlVRD5OPThPOTpPOTpMOTdDMC43LSA6MCNNP0FcTlBdSkhXRUNJ PTk9MS07MSI1LB0+Mi5JPTlWRTlTQTVKOixGNShKNTFHMi5GMSxXQTxbSURfTkhRQzxOPzlNOzRT QDpXPzxdRUFYRkRUQT9KOjRQPzpYTEleUU9cSkVXRkBIOCk/LyFJLydOMytNNy1IMilALSFFMSVN NTJPODRKOjRAMCtFLipMNDBVSU1dUVVTQTxHNzFMMCtEKSRAKyI/KiE4MB8/OCZPQTJIOyw8LSAz JRg3Khs3Khs4Khk5Kxo7KB5INCpQOTVXPzxKOjFJOTBHOitAMyU9LShAMCtMOjhXRUNeTElNOzlA Lh49Kxs9Kh0/LB9MNC5QOTJMNzFGMSxELx5ALBtALBtIMyJGMCE+KRo7JRk7JRk7JhY+KRhMNyVP OihRQTNVRTdTQDtHNTA8LyE3Khw8Jxs8Jxs7KhM4JxA7KBI7KBI4JA07Jw83KRY+MB1HNS9TQDpU QT9INzRIKiBAIxlBMSRNPC5VOi9PNCpELyA+Khs/LCJKNyxOPDNNOzJNOStGMiVEMhpBMBg/LR0+ LBxBMiFGNyVMMx1IMBpFKRZDJxVFLyBMNSZOPS5KOitJMS1BKiY4JRk9Kh5EMCRHMydGLhZTOiFh STRnTzpnSkFeQzpNNyo/Kh5ALxdEMhpVOypcQTBWOS5ILCJKNx9JNR5NNydQOipNPDVPPjhNMidG LCFHLB1GKxxAKRZFLRlFKyBNMidMNydTPS1UOyJQOB9GMSA+Khk9JQw5IQk7IAs+Iw5FMRpNOSFe QTFdQDBRQyY/MRY4Jg06KA8+LyxBMi9FNyU/MSBELxw+KhdBKxJGLxZIOClQPzBPPjJRQDRXRT9X RT9WRz1VRjxTQTNJOStGOCZBMyJALSA6Jxo+MytMQDhQST9VTkRYQztMNy9BMSlHNy5NOjpUQEBU QTtUQTtaQzdUPTFJOC9NOzJUPzJRPTBOPy1TRDFRPi1QPSxNOzVTQDtaTU1TRkZNQD5BNTNJMS1I MCxBMic7LCE5MBs/NyFTRz9VSUFcRURTPDtGNSo+LiM7LCE9LiM/MiZFOCtMOShMOShJNCVOOSlM Oy9FNClDLilVPzpTRUxURk1JPT1DNzdENSRDNCNHNTNRPz1TQTtRQDpJOzRJOzRQPT9cSEpVSEhR RUVGMh1EMBtMMCFQNCVINSM+LBo+KRhFLx5ROy9VPjJNPTFENCk9LCdJODJVR05YSlFWQDlHMitJ LB8/IxYzHw84IxM3LiVGPTNUPzROOi8+KRg1IRE6JRk9KBw6LBc4KhY8JiBGLylTOjdWPTpQOS1O NytKNClGMCVELyhELyhUOz1iSEpaSENGNTBDLhY8KBA/KBVIMBxTOShROCdMMiBJMB5MMx1MMx1K NSZPOipKNClELiM8KRw5Jhk5KxhIOiZWRTVUQzNVPTdVPTdMOzJEMys8KhYzIg8+JhU+JhVDKA8/ JQxFKQpBJgdBKhVDKxZBLBlOOCRNODBVPzhKOTlGNDQ+Khs+KhtKMR9QNyRQOTJJMixBLCM6JRw9 KBxIMiZGNSdGNSdHMydINChDNyQ+MiA/LRs+LBpHMCVHMCVGLyNGLyNBJxhFKhtDLSRGMCdJNC9M NzFHMCVGLyQ6JBk7JRo8Kho/LR0+KhlOOSdYRzhfTj5lTUldRUFOOCxBLCE7LhY/MhlUPTBcRThW PzVOOC5TNyVVOSdXOi9UNyxQPC9RPTBJNCVALB1KLR5KLR5DKxRAKRJAKh9HMCVQNyRXPSpcQSdW PCJJOiRENB9EKxFAKA8+KxE1Iwo8JxtROy5bRDdfSDtbTDlHOSc7Kw83Jws9Lh0+Lx5FMiJGMyND LhY9KRE8JQ8/KBFFNyVKPCpMNyNOOSVPOzBUPzRTRz9TRz9UQzRQPzFKOylENCNFLiJAKh5DMyJN PStVRj9aSkRVQ0BKOTdDMiRDMiRHMydMOCtOOi9UPzRXQDdXQDdIOCxGNSpNOS5QPDFQQC5URDFR QDJRQDJOPj9OPj9USkxRSElORTlDOi5DLR5NNydENx85LBY1LB8/NShWRERVQ0NYQz5QOzc/MB87 LBs9KxlALhw8LyA9MCFIMyRMNydINSVNOilOOyZGMx9DOC1JPjNOQEVTRUlNPTFFNSo9MRtANB5H OCpPPzFPQTJNPzBMPTdJOzRKPDVTRD1RRUNRRUNHMylKNyxPOCxMNClENyg7LiA6Kxg+LxxHOCxM PDBHOS9DNCs/MStMPTdaSExbSU1YRDVNOStHLxs7JBEyIxY4KBo7MzBKQz9QPzNFNCk7IRE8IhJF KBdJLBtGLhZELBVELh1IMiFQNS1UOTBQOjBMNSxONy1NNSxHMylMOC1XRERdSUlUPzJFMSU+Kw89 Kg9FMSVUPzJYRDlWQTdUPSlUPSlPOSdQOihQOzNRPDRKMiw9JiA5JxY8KhhFPDBRSDxcSj5VRDhR Oi5POCxFNyNBMyA8Khg5JxZBKhNBKhNJLxpMMRxJNRhDLxNGLR9ILyFNOCRWQCxYQTRXQDNNOzVI NzFDLhdELxhOOSdRPCpMOShHNCRELx46JhY8JxhDLR5FNCdHNylMOCtNOSxIOiZENSJBLRw8KBc+ KRZAKxg+KRpGMCFDKxdELBg/KiFHMShVOi9XPDFQNyZKMSE/Khs6JRY4JRhDLyI6JRNHMR5RPS9e STthSkVbRT9POiZHMh8/LRNBLxVOOi1YRDdYQDNROi1PMyZRNShWOjRUODJQPSxQPSxOOyZHNCBH LxlKMhxGMRxBLRg+KxFDLxVHMR5WPytcRjVXQTFGPi1KQzFMOCBFMRpEMBY6Jw47KRdRPitfSD5n T0VlTkRXQDdFMxk4Jw88Iw9AJxNIMBxNNCBHMBVBKxA9JQ5AKBBDLhtIMyBHMSVKNChHNS9KOTJR PzlOPDVUPzJTPjFRPS9OOixPNClGLCFELyhPOjJXR0hYSElRRUVMPz9ANydANydEMyVHNyhROy9a QzdaQzlXQDdPOy5JNSlNNypQOi1RQTJWRjdbRT1YQztRQDtOPThORkBTSkVYSDlNPS5JLxhPNB1K Nx1INBs7Mh87Mh9TQTlRQDhTPChPOSVMMx9IMBxALBk+Khc9Lhk/MBtPOSdUPStPQC5TRDFWQDlP OjJIOSpGNyhQPj5VQ0NUOzdONTFBMSNBMSNKNTFUPjpTQTlPPjVOPTdIODFDOTNHPThOQDNPQTRJ OzNNPjdRPDRNODBBMiE9Lh04KRs6Kx09LiFHOCpGOSpHOitJODJRPzpWRT5VRD1XQzVTPjFKNCND LRw/Khc9KBY+NzNKQz9QQDRBMic/Iw1EJxBJLRZVOCBROB5KMRhFLRlMMx9POCtUPC9QQDRMPDBI OjJFNy9HOitJPC1QRz5RSD9OPTRAMCg7KhVINyBWRTlaSDxXRj1VRDtUPzFPOy1IOyxOQDFPPThP PThKMy1AKiQ6JxFEMBlTST9aUEZcSj5UQzdMNydJNCVNNyNKNCE/KxxELyBNNB5MMx1POSdTPCpR OClNMyVIMyJKNSRUPzJbRjlYQztUPjdMOjFKOTBMMx1UOyRbQC1aPyxQOjBIMilDMCA9Kxs+KR5B LCFHMSBNNyVTPCpROylPOiZNOCRIMBxBKhY4KRU6KxZELBhTOiVQPSpINSNHMiFQOylYPCxaPS1Q OipFLyA6JRM3IhA8IxhMMSZBKBRJLxpWQDBfSTliTT5dSDpVPipOOCRJMBdDKhJNNC5YPzlcRThX QDNXPSxQNyZVOC5VOC5NPC5PPjBRQStNPSdMNx9NOCBOOCRPOSVHMhtDLhdGMSpUPjdbQzxYQDpU PTNVPjRTPy5KOCdNNRtJMhhKMhxVPCVeTEVjUElfTUdaR0FNPSlAMR4/JQ5AJg9IMRdTOyBJNRpD LxVBKBJEKhRFLB5MMiREMCNEMCNHMSZMNSpPODFPODFTQTVVRDhYQS1WPytROytFLyBFMSdPOzBX QD9YQUBWQ0VPPD5FNCdEMyZGNyVKOylOPzVURTtXRj9YR0BWRjhIOStENSJJOydRQDRaSDxhTD5c RzpWRz1MPTNQPzdcSkFeSTxYRDdUOilUOilQOydNOCRPPCtINSVRQTNNPS9QOipTPCxXPSxUOilN MyFILx1HMh1IMx5TPyxTPyxWSkFWSkFWQDlPOjJENSRHOSdUPzRXQzhPPzFIOStBMidAMSZNNTFY QDxUQzpQPzdOPTFMOy9GNS1KOjFKQzNKQzNWQTdXQzhRQDROPTFMNSJFLxxBLBk8JxU/LCBHMydH NylKOixOOC5ROzFRRDRPQTJOPjJPPzNMOCJJNSBMMx9HLxtJOTJOPTdNPS5DMyU+LAxHNBNPPylX RzBUPipOOSVMMiJKMSFOOixUPzFXQTpRPDRMOjNHNS9IOjJNPjdOREBOREBOPjBBMiU8MyBGPSlW Rz9WRz9VRj5OPzhQQDFPPzBUPTNVPjRVPTdTOzRMLh9GKRo1LRhIPylYUUdTTEFWPzNTPDBPOSdU PStUPC9QOSxMMR5NMh9OOx9PPCBRPTBRPTBJNSlGMiZNNytOOCxUPjlcRkBXPzlTOzRKNyxMOC1O OixaRTddRTldRTlYPDdMMCtALRg8KRU8JhpGLyNNNypUPTBaQzJaQzJWQzFRPi1HMBZBKxI4KRw1 JxpGNyhXRzhXQTFQOytXQC5aQzBdQzFVOypHMCRDLCA9JBI6IQ9FKyFQNStOMBhYOiFcRDdhSDtd TDxVRDRRPi1NOilKNSJGMR5JLCpOMC5POjRTPThXQDBQOipQOihROylNPDBPPjJUQzNUQzNQPSpQ PSpTPjFTPjFNPSdFNSBMNCtTOzFWRD1WRD1UPTFVPjJPQDdOPzVRPitTPyxRPitVQS5fUUReUENj TUdeSENJPjNFOi9DLhdBLRZTPihYRC1RPitMOSZIMBhHLxdFLyNIMiY/MCI+LyE/MCJDMyVFOjFH PDNWRTleTUBdTTVURC1RPilGMx9GMiZOOi1QPzlRQDpRPzlTQDpHOipENydIOiZPQCxYQDRfRzth SUhjTEpVSERKPjpIOSpKOyxQRjhXTT5dT0FXSTxUQzpMOzJTPDJhST9hTkdaR0BUQTlTQDhUQzNR QDFPPTdJODFOPTRQPzdQQDFVRTVdTThWRjFaQzBMNSRJMCBONCRORDNORDNPSkBNSD5PQy9IPClA MSNRQTJaRTpYRDlRRzlKQDJJNStINCpPOjRbRT9URjVRRDNOQDFMPi9GNShHNylKQTVORTlbST1a SDxRQzBNPixHNyhEMyVFKRVFKRVAKxhPOSVROylPOSdQOytOOSlMPCpQQC5WQTRXQzVPPSZPPSZM PChGNyNKNCtPOS9MOCtKNypINSFWQy1aT0BYTj9WPzJROy5ONypQOSxPPzFURDVURjdPQTJIOjJF Ny9INzFINzFQPTtTPz1OPipOPipTRDFYSTdTRzxPRDlUQzpTQTlYRDdXQzVaQzlXQDdWRDtUQTlA Lxk5KBNHPDFYTUFcUEdVSUBRQS9NPStTQzdVRTlWQDlRPDRQOipPOSlQPidQPidOPS9IOCpKMydM NChONy1POC5WQDxcRkFUPzRNOS5JNzROOzlOOztUQEBWRD1WRD1TPjFBLiJBLRhALBdBLx9KOCdU PjdbRT1cSj5YRztVQDNNOSw/LBQ4JQ4yJhc5LB1NRjpVTkFRQDRNPDBYQTVYQTVTPjFDLyM9KRY/ Kxg5Jg86Jw9DLyVOOi9PMyJXOylbRjleSTxaRDxOOTFHMydGMiZHMSVELiJAKh48JhpBKiZTOjVU QC9VQTBOPixMPCpKNypNOSxOPjBPPzFOPjBPPzFNRDpKQThNPStOPixPOyVUPylVQzpVQzpRPzdQ PjVPQDpPQDpQQTpOPzhIQC9ORjRcSUNkUUpdUExYTEdORjRHPy5FPCRBOSFPPzFXRzlVRTdRQTNU PStROylNOilGMyNDMCA+LBw/MB1JOiZRPTBUPzJeTUdhT0leVEVdU0RXSDNNPipEOixEOixRPDRV PzhVQDVRPTJFOCtGOSxKPTBWSDtfSD5jTEFdSkpdSkpTRkZGOjpGNS1QPzdbTEReT0dYUUdQST9K QThJQDdOPzlRQzxhTkdbSEFbRT9hSkVYTj9RRzlWQ0NKODhDMiVQPzFNSD5UT0VaTkVWSkFUQzM+ LiBELSFONypQRjhWTD1YRj9XRT5IPytAOCRHNS1VQzpYRkBWRD5RR0RJPzxIPTJFOi9JPjdcUEhc SkFRQDhIPzNIPzNIOSpGNyhGOzJQRTxcSkVYR0FQQThMPTNHNylJOStGLBZEKhREMBdQPCJXQTFU Pi5TPy5UQC9NPC5HNylKOTBPPTRUPzRVQDVOPTdFNC5HNzBKOjNNOzJNOzJQPzlfTkdfUEhbTERW RTVQPzBTPjNXQzhYRDdeSTxUSTtUSTtKOjNFNC5IMS1IMS1JPjVQRTxTQzRbSjxiTkxjT01UQzxM OzRPRDxUSEBXTERTRz9RPzlRPzlWRkNOPjtJMidHMCVXRUdeTE5YSUNURT5QQDJPPzFURjlURjlU RT5NPjhPOjRMNzFPOzBOOi9QOi1JMydOOCRROydPOSxPOSxTPDtQOjlJODJFMy5HNDRJNzdMNTdV Pj9aQ0FWPz5TOC1NMihPMSBQMiFNOStRPS9bRENbRENQQDJJOixFMSVDLyNAKRI7JA4/LyJNPC5V Rj9TRD1ROi5POCxXPzJTOy5JKxZBJA9AJxFBKBI+JQ9ILhdTPS1UPi5KOylTQzBVRDtVRDtTPjBE MCNBLCBBLCA+Khk/Kxo+KA89Jw9ALB1IMyRJQTJMRDROPTdIODFENCFGNyNJNCVIMyRHNylNPC5J PjNHPDFOOixRPS9KPCpNPixTQzRURDVQPzBMOyxJOzNMPTVQPzlRQDpHPjJHPjJWSkNcUEhYVElP SkBKQDBGPCxFPCZFPCZNQzRTSDpRRzVMQTBXQSlTPSVMOyxFNCZHMh1MNyFIOSNRQStVRDhUQzdb TEVcTUZaUEdYT0ZUSTtRRzlMPChNPSlJOTBPPjVOPi9KOyxEOipEOipMQDhUSD9bSEZWREFXRT9W RD5UPzJKNyo/MiRTRTVeU0paTkZUSEBQRT1OPzVOPzVGPDdJPzpXR0ZWRkVbSEFeTEVaTkZUSEBX SEFFNzA4Lh9HPS1USEBbT0deSkpYRUVUPDA8JhtFKhtVOSlaQ0FaQ0FXQzhQPDFIOStJOixIODFW RT5YRUNVQT9WRkdTQ0RKQDtIPjlGPj1VTUxcTD1PPzFNPjRKPDJHOCpFNShGOzBQRTpaSUhVRURM PjFIOy5JOSpJOSpIMBxHLxtINSVVQTBWRTlXRjpUQTtUQTtMQTNFOy1JPC1MPi9QQDRRQTVOPTRJ OTBEOipEOipQPjhTQDpURENaSUhbSkdVRUFQPzNOPTFVPTFYQDRbRjleSTxaSEFXRj9NPTFDMyhF NClJOS1JPjdMQDlVRD5hT0lpT09eRUVNOzROPDVVR0laTE5VSUFQRT1PPjVVRDtWRT5NPDVJNDBM NzJYQztXQTpRPzdTQDhRPDdRPDdVQDJVQDJTQDpKOTJNNTJMNDFKNylKNylNOCZNOCZPOSdOOCZP OCxPOCxTOy5ONypKNCtNNy1HNS9HNS9EMCNPOy1PPTdNOzRTOCxTOCxbPCphQS9YRDlRPTJTQTxT QTxNOidBLx1FKxZILhlGLxRKMxdTPDBXQDRWPjpTOzdONCRPNSVPOCtKMydILRNFKhBBKRdGLRtF MCFOOSlXQzVdSDtINChTPjFXQS9YQzBRPSdEMBs5JhA6JxFBLBlDLRpELBhDKxdBLCBIMiZIPDhQ RD9TPThIMy4+LxxFNSJJMyJIMiFDLSFELiJJNStMOC1NOChPOipKPChJOydRQDhRQDhNOzJGNCxB MSZFNClRPi1RPi1NOS5KNyxQRT1YTUVbUEpPRT9GPSlDOiZGNSdHNyhNOzVTQDtTQDhTQDhWQy9R PitMPDBKOy9MOCtUPzJRPzdXRTxTQ0RRQUNVSEZaTUpaTkVWSkFVRjxWRz1QPzBJOSpEOipHPS1H PjRDOjBGNyhDMyVPPz5aSUhaRkhTP0FRPjxRPjxPOSVIMh9EMi1XRT9XTlFWTVBQQTtOPzlHPDFJ PjNGOzBFOi9TRkRRRUNaSkRXSEFWSkFXTENUSkFFPDM1MyZAPjBaRk1hTVRiSUZaQT5OOC4+KSBB LiRYRDlbRENaQ0FUQTtOPDVQOSxTOy5JPjVTRz5VQT9RPjxPPT9QPkBHPzxGPjtKQD9VSklbSEFR PzlTQTNRQDJGPTFBOS1KPjpVSERcSkRVRD1PPzFPPzFOPS9JOStGMB1IMh9NOSxbRjlXTENVSUBU RENWRkVOPjtIOTVFOTdJPTtTQTlUQzpQPzNMOy9DOC1ANStNPT5UREVTRkZVSEhYRkRUQT9QPzBO PS5UOzdWPTlWQDxdR0NbSEZWREFOPDdHNTBGNS1JOTBJODJQPjlaRk1hTVRiSklTPDtKOTdOPDpW SEpYSk1VRj9TRD1WQDlaRDxTQTNOPS9MOy9OPTFVPixUPStUOS1TOCxVOjFaPjVWPy1VPixNPC5H NylFMiJGMyNJOixKOy1OPy1QQS9QQC5OPixNNSxPOC5WPShVPCdTPC9ROy5FNCdEMyY/LB9JNShN PDBOPTFVPTFWPjJjQDdjQDdVPTNPOC5RQDFPPi9ONR1NNBxPNyBYPyhRPCxVPy9UPTBQOi1QNSpQ NSpaPS1aPS1TPCxMNSZPNyBPNyBNOidOOyhNPC5PPjBaQzlfSD5GMSBPOihaQCtcQy1YPyhTOiM+ LRQ5KA9FLRlMMx9JNB9IMx5EKx1KMSNPOjJaRDxTPjNFMSdEMR9INSNNNSpIMSY9LCQ/LiZIMSVN NSlKOCVKOCVFOSZIPClOPzVNPjRJOyM/MRo7KBI4JQ9ALB1FMCFJMyJHMSBFPztTTUhYTEdPQz5K PitANCJELx5GMSBPOy5VQDNVPy9YQzJVQDVOOi9NNy1NNy1ROjVWPjpTQD5UQT9PQ0NPQ0NRQDtU Qz1XRjpaSDxWRjhVRTdTQzdKOy9FNyVFNyVEOzI/Ny5BMyJAMiFKOzpaSUhaQT1ONzJJOi5KOy9J NyZHNCRELyhUPjdbTU9bTU9NRDtHPjVANS1EOTA/Ny5EOzJXPj5VPDxUQTtTQDpVRD5bSUReTElR Pz1BNy5EOTBYQUBiSklcRkFUPjpMMCNGKx5JOS1WRTlQRD9OQT1OPTFNPDBNNS9QOTJUPjlWQDtT PTVRPDRMNTdPOTpHPjRKQThNQDxUR0NaRkZaRkZaST1YSDxERjo7PTFJPTlPQz5eRz1YQThWQTNV QDJRPS9QPC5KNSJOOSVPQTRYSj1VUEZTTkRWRkdWRkdOPThGNTBIODJTQTxYSDxTQzdOOypJNyZE Myg9LSJGNzNQQD1XRTxWRDtbRTJYQzBXQzVTPjFNNytNNytTPDtcRURcSUxXRUdNOzJHNS1JNShJ NShMMzNcQ0NiUFZfTlRRPDhHMi5JPTlUR0NYRkZWRERYRDlaRTpdRz9fSUFXQDdQOjBROy5TPC9W PjFVPTBdQzNbQDFiQDFhPzBbQDFYPi9VQDNNOSw+LRY9LBVROydWPytYRDVWQTNUQzNPPi9ROi5Y QDRdRTlbQzdUPTNOOC5FNSRBMiFAKBpGLR9QNStTOC1WPjRaQThlRUddPT9MNzJNODNROy9POS1R PCxdRzdfTj5hTz9dRjlVPjFRPi1TPy5UQC9WQzFiSERdRD9bQzVTOy5UPixWQC5bRTRaRDNRQTVX RztdR0FWQDtDLhZPOiBbQC9eRDJcQCxYPSlJNCM/KxpBLRhPOiRNPC1GNSdGMx9GMx9VPjFcRThR QDFFNCZHNR9OPCVPOCtIMSVBLR5DLh9EMCNKNylOOyhRPitMRDRGPi9KQz1MRD5NPiZDNB0+Jg09 JQw5KBM9LBZELx5DLh1HPDRRRj5XSEBVRj5QPzNFNClDLhtIMyBOOypUQC9TPyxRPitTPDBKNClG MihKNyxOPDpRPz1VQT9QPTtMPDBOPjJHPDRFOjJPPTtPPTtaRTpbRjtURDFQQC5GOSpFOClBMyxA Mis8MiM7MSJQPj5aR0dWQTRMOCtIOSdKOylJOi5HOCw+MSNQQzNRTEdTTUhWRTxPPjVGOC5AMilB OS9EOzFROjNQOTJPPTRPPTRYQDpaQTtRR0RKQD1BOzI+OC9OPzlXSEFRRDdKPTBJNyRINSNTQDhY Rj1TRz9NQTpHPDNGOzJIMy9MNzJPPTdVQzxUSThNQzFGODBFNy9JOTBKOjFNPDdVRD5aRkZhTU1X TUdWTEZGQz0/PDdHPDRNQTpWRjpURDhXRjpWRTlPQTJHOitKNypWQTRbTERbTERWUE5RTElTRkFT RkFKQTlAOC9KOjRXRkBdTD9WRTlMPC1JOitGLyNDLCBJNyZTPy5URDhXRztiTT5fSjxeRjlXPzJI OCpKOixNPDdUQz1cTEhVRUFKOTBEMipJNSlKNypWOTpiREVlUVFdSUlHNTBEMi1NPT5YSElcSkFW RTxbST1eTUBjTUdeSENYQThQOjBUPi5XQTFbRT1dRz9jSj5jSj5dTD9XRjpdRD9dRD9YRDlQPDE9 Kxs6KBhPOihYQzBYRzhVRDRURTJRQzBTPjNeST5iT0ZdSkFOQDNDNSlGMSBALBs8JBY8JBZELSJN NSpXQTpeSEBhSERVPTlFNCdGNShKPDVPQDpXRj1fTkVlU01eTEZUPzRMOC1WQTNdSDpXTT5YTj9j UEldSkRXQDdWPzVOPi9VRTVhST9YQThHMydVQDNYQTVQOi5BKhNONR1cRi9eSDFYRTFTPyxKNyFH Mx5FMBlPOiJMPi9JPC1GOSpFOClQQDRYSDxURTBGOCRHNCBNOiVNOidINSM+LBhDMBxELx5HMiFQ OihYQS9URDRPPzBQRz5WTURNRC9BOSVAJxE/JhA8Jg4+KA8/Kxg9KRY+NC9MQTxWRz9VRj5QPC5H MyZALhpGMx9JOStPPjBOPS5MOyxQOi5KNClDLh1NOCZMOy9OPTFUQzdPPjJJOStJOStIMjFIMjFJ ODFOPDVVQzpXRTxYSDVURDFHPS9EOixJNSlHMydFNClFNClMPztQRD9VQDJOOixKOitPPi9OPixN PStENyhNPzBHRztOTkFaSkNTRDxKOjFAMChKNypOOi1JOS1NPDBPPzNNPTFTQTVVRDhOQDNNPzJH PjRBOS9HPjVRSD9NQzFJPy5MOyxQPzBTQTtaSEFVRD1OPTdMQTFHPS1GMCVMNSpEPDlQSEVbUUdP RjxGOSxBNChDNzJGOjVHPDRRRj5cUEdhVUxaTUpUR0VNPDNIOC9FPjVHQDhVSUFUSEBURT1TRDxV PzpQOzVGPjtQSEVVTkVXUEdaT05YTk1bRjtVQDVPPjVBMSlPPTdbSEFkTEdaQT1RPzdQPjVJMiZH MCROOi1WQTRXRkBbSURhUUpeT0haSkNRQztNOStOOixQPzlXRj9cT01RRUNDNC5FNzBDOC9FOjFQ PkBVQ0VaTUpNQD5FNC5JOTJMQTxQRkBXSTxWSDtWTUNdVElfTE5XREZJPjdIPTVUQzdaSDxbSj5c TD9dSkFeTENXRzlTQzRiREdoSU1aRkRKODU6Kx04KRtJPC9TRThcSjxYRzlTQzNURDRcRkBeSENf SURfSURRRDRGOSpONCZGLR87JxM5JRFBLCNMNSxXRj1bSUBbRjtQPDE9NCE5MB09OipHRDNXRUNe TElfR0RROjdJOS1JOS1WREFeTElfTUpeTEleTENaRz5RPTJRPTJNQTpWSkNYRj1PPTRHMSVNNypJ OStIOCpAKRJONR1eQTFoSjpeSDVXQS9VPylRPCZKNx9RPSVQQDROPjJGOSpGOSpKPTBVRzpVRi9I OiRFNx9GOCBMNyFGMRxBLBtAKxpELxxHMh9UOTBdQTlPRjpORTlORTlWTUBWTDtNQzJQMiNHKhs+ KxVALRZGLR1GLR0/LyRMOy9TQzdXRztVQSxKOCNMOShNOilVQDVVQDVbPzdWOzJQPC9POy5IMh9D LRpGMiVPOy1OQDFIOyxGNS0+LiY8MiU8MiVHOSNOPylVRDhcSj5jSkBiST9PQDdJOzFKOixFNCdI MilMNSxIPDpOQT9UQC9OOypKOy1PPzFQOi5POS1IOClOPS5NSDtNSDtXRUVVQ0NKPzRBNyxHNytN PDBKOixKOixRQDJRQDJXRzhYSDlVQDNVQDNNQC1GOidIOS1TQzdXRjdVRDRUQTlUQTlUQzxcSkRU RjlRRDdPRTRNQzJJOStMOy1JRD9TTUhWUElMRj9OOixKNyk3MSM4MiRHPThTSENcU0hcU0hcUEdT Rz5JPjNBNyw/Ny1GPTNMREBTSkdWTUBQRztUQzpNPDNJPENWSE9aUEZVTEFaTU1eUVFdSkFMOjFE NClDMyhPQDpeT0hqUFBcQ0NVPztbRUBQPzBGNSdJOTNPPjlPRUFYTkphUUpeT0hUTEZIQDtKOTBM OjFUQUFXRUVPSD9KRDtDMStBMCpEMytIOC9JODpKOTtTPz9QPT1OOi1KNypMOzJVRDteTUBeTUBb UUdeVUpYQUBQOjlKOjRMOzVVPzhdRz9cTkBeUENkTkhcRkBUQC1WQy9iUFRfTlFUQEVHNDk9LSJD MidJPzFRRzlWTD1USTtJQDhNRDtVSEhXSkpdRkVbRENRQDpKOjNPPCdNOiVIMxw6JhBBLCNTPDJc RzpbRjlWRTdJOStBMyBAMh9IOy5WSDtfTE5dSUxVQTBHNCQ/MiZPQTRaQ0RdRkddREZTOjxROTJO NS9KNypTPjFWRkVURENXQDNTPC9MMRxILhlHMydMOCtFLhVONxxcRDdkTD5bRjtVQDVTPjBRPS9T PjNTPjNWPzVUPTNMOCpFMSREMipMOjFXQTFPOipMNyNMNyNKNCVDLR5AKxhDLRpFLxxHMR5ROTRb QT1URT5MPTdIOCxUQzdUTUNPSD5PPCdKOCNKNxtMOBxONCRNMyNJMilONy1QPzlXRj9PPzBNPS5N PDBQPzNTQzdRQTVXPzNTOy9RPCxPOipJMidBKyBBMiFKOylOPS5MOyxIOClAMCI7Mh84LxxAMiFK PCpXQTxeSENkTEdkTEdPQDlPQDlPPThMOjRJMi9IMS5FOTROQT1RQDFRQDFKQC9JPy5RPS9OOixI PTVHPDRIPzNPRjpMRj9IQzxKPDVGODFHOS9KPDJKOixQPzFUQzNVRDRWSDtaTD5YRj1QPjVMPC5I OStTOy5YQDNYSDpYSDpWRz9VRj5WRT5eTUZaSUZUREBNRDtJQDhKOjNKOjNORUZRSElTTENMRTxM PypEOCM+NSI8MyA+NC9PRT9aTU1fU1NYTExNQEBKPDJFNy1ANStBNyxNOzlXRUNYTUFUSD1TRThO QDNVQEVaRUlYTElWSUdXTE1dUVNdTUxMPDtDMiVFNCdKRUBWUExiUEdVRDtUQz1WRT9NPixFNyVE MyZJOStPQURdT1FjU1RiUVNVSEZJPTtHOTFMPTVUQURVQ0VRRUVJPT1FLyNGMCRHNS1JOC9FNC5G NS9OPTRKOjFGMiVGMiVINzBUQTtfT0xiUU5jU1FcTEpPPTRGNCxEMyZJOStYQz1hSkVhUUpeT0he Rj9WPjhOPThYR0FfU05XSkZQPjVHNS1ILShJLilOPjJXRztWRjhRQTNKOyxOPi9WSUVWSUVYRUNV QT9QPzNOPTFNPSlMPChEMR09KxdBMiRMPC1VRTlQQDROPTRHNy5EMyhEMyhMPz9YTExiTkxUQD5I NyBBMBpGNytOPjJXQD9XQD9TOC9NMipIMSZHMCVFMClMNy9RPzlTQDpTPStJNCM/KRBBKxJJNSlT PjFFMBlTPSVYSDVfTzxRQDhIOC9IOCpMOy1NPDNPPjVTQDpTQDpJOihDMyJBLiJGMiZQNSpQNSpR NyNQNSJKNylFMSQ8KRU9KhY/Kh9FLyRMOTdYRUNVST5JPjNDLidQOzNOR0dOR0dQQzVPQTRQOylT PStVPytUPipOOixRPS9RRDdPQTRPQTFQQzJTPjNVQDVWRTdRQDJUPSlTPChQPC9NOSxKOitGNSdB MiFHOCZKOydJOiZPOSxKNChDMB5BLx0/MCNHOCpWRD1fTUZjUUVhT0NPPjVPPjVPPjlJOTNFMiBE MR9FNSdQQDFURDVVRTdTRThQQzVUQzNKOitIPTJGOzBMPTVQQTpIRDpHQzlJOzFFNy1KOixIOCpP Oy5XQzVXSEBaSkNaTkZYTUVVRzhNPzBMPC1MPC1VRDRdTDxeTURcSkFVRj9XSEFWRkNcTEhdSkhX RUNOQT1MPztOPzlMPTdRQUBURENRQUNUREVPQTRNPzJMOSRHNCA/MiRURjddUUlhVU1RSUZIQD1K PChJOydDNyA+MhxJOTNYR0FdUUlYTUVUSTtQRjhYR0FbSURWTEhUSUZVTlBYUVRXTE1KP0BENCdF NShJQzpUTURcSkVVRD5VSUBVSUBOPixGNyU+MB1FNyNVRUZlVVZlVVZYSElUOztQODhJODJMOjRO PDpRPz1UPTxNNzVINCdNOStOPy1PQC5HNR9FMx1FNyVDNCNENCE/MB1GNzVTQ0FbTk5hVFRfTExW Q0NIOjBBMyo/MShKPDJdSU5jT1RnT1NcRUhROjVROjVRR0RaT0xiTEZWQDtNOilINSVILiNILiNT PTVbRT1VQDVQPDFRPTBUPzJXR0ZRQUBQOzNTPTVKOitJOSpKNS5IMyxDLh1ELx5AMydHOi1QPzBR QDFRPz1FMzFGMiZINChPQDpYSUNYRDlINCpFMBtDLhlJOitMPC1WQDxUPjpNOS5KNyxFNSJDMyBE MyVFNCZIOStMPC5OPTRJOTBALBlIMyBeRjxpUEZJMyBbRC9bT0ZaTkVQRDA9MR8zJhI8LhlJOStQ PzFUQTlUQTlNPS9FNSg/LCA9Kh5GKxxVOSlUPylVQCpVPipOOCRFMx1BMBpFNSdFNSdIPTRUSD9U SkBORTtHMCpELSdKPzhPRDxTQTlVRDtVQDNYRDdaQzVXQDNJQDdGPTNKPzRMQDVOQDFPQTJWPzJY QTRaQzJVPi5TPCxUPS1QPSxNOilJPC1FOClFMB1HMh9HNyhNPC1QOi1POSxPNSVONCRHNCJKOCVX Rj1hT0ZeT0dbTERTQzdURDhPP0BNPT5EMyY+LiFENyhPQTJWRz9bTERYTUVPRDxOQDFDNSdGOC5G OC5OPThQPzpPRDxOQztKOjRGNTBJNStNOS5XPjhfRj9aT0lYTkhVSEhVSEhORDNMQTFOPS9UQzRY SEdiUVBdTUlXR0RKQDtNQz1XTUdfVU9fTUpWREFQPzlRQDpOPzVKPDJRPDdUPjlTQD5VQ0BYPz9V PDxUPS1ROytMOzRbSUNaTU1YTExPRjpMQzdRPCxOOSlAMyQ+MSJJRD9YU05hUE9YSEdVSUBUSD9c SkVfTkhYTkpVSkdYTU5XTE1bTk5NQEBFOCtHOi1OQztUSEBORTlPRjpWTUBVTD9PPzFGNylAMSNK OyxeSkpoVFRfT1BTQ0RRNDVPMjNHNDJJNzRNPDdTQTxNODBIMyxFNClPPjJaSDpVRDVNOR9BLhZA LB1ELyBHMCdELSREOjlPRURYTk1dU1FaR0lRP0FJOTJBMStHODlXR0hjTlNkT1RfQ0BUODVQOTJW PjhTSkdWTkpYQztNODBPODNKMy9MNSlNNypXPDFaPjNVPTBTOy5UPjdTPTVMOzJHNy5HMSJJMyRH Mh9HMh9ALSFDLyM8LRpDMyBHOSFHOSFTOy5XPzJPPi9GNSdFMSVMOCtQQDRPPzNJOyc+MB1MMiBR OCVRPS9RPS9RPzpNOzVKMixJMStJMiZHMCRINSVKOCdJOi5IOS1OOi1POy5POThdRkVoUE9qU1FM NyVcRjNdSkVeTEZURDFENCM6KBU4JhM8KhhEMR9TOzRbQzxJPy5ANyZDMRs4JxI7JhdKNCVWRjda STpaRC9XQS1QPSFKOBxMOyxKOitNPjdRQztUR0NTRkFMNC5BKyVINy5KOTBQQDJURDVaQzdeRzta SDpVRDVGPzU/OS9JOzFMPTNNPTFPPzNVPjJaQzdYRTNUQC9XQC5QOihMNyVNOCZIOSpFNSdIMyBN OCRVPjFYQTRWQTNTPjBMPChKOydOOi1XQzVdUUZdUUZWUEBPSTpQQzJTRTRPQDpNPjhENyg9MCJD MStTQDpVT0pWUExUUE1HREBGPi0+NyZDMyhHOCxKPDVPQDpTPz1PPDpKOjFFNCxBMSlJOTBUQzxd TEVXVExPTERPRURUSUhPPTRINy5FNSpVRTlaTk9eU1RUR0NNQDxBOjRHPzpXTUddU01cSkVVRD5U PDlTOzhJPjVJPjVQPjhPPTdOPzlQQTtUQT9RPz1OPDdOPDdNQEBYTExYSElXR0hPPjJOPTFQPzFM Oy0/NCo8MSdDPj9WUVNfUVRXSUxQR0hRSElYTU5eU1RVT0hOSEFPRT9QRkBWSkNMQDlFNC5GNS9M PDtMPDtKPzhMQDlVRTlVRTlRQDRHNytEMyVQPzBdSUliTk5YSEdNPTxOOC5IMilFNSpFNSpIOS1N PTFKOjFHNy4/OzFJRTtRRj5TRz9QOydJNCFMNSRQOihQOCNMMx9JODhVQ0NbSEpcSUxXPzxQOTVH OTJDNC5FOTdTRkRfSExfSExfQz9cPzxdRT5eRj9WSUdUR0VNOSxJNSlRNyxaPjNbRDNaQzJcRTRa QzJWOS5VOC1VPTNUPDJMOShJNyZJNyZMOShMOSRINSFHMCdHMCdGMCVMNSpTPS1UPi5YPi9WPC1R Oy5MNSlIMyBMNyNNPC1KOitHOCJIOSNRPitUQC1eRDRbQDFOPTFJOS1HMitGMSpIMStHMCpKNypO Oi1OPjJNPTFTPjFTPjFXPkNiSE1kT1RlUFVNOStdSDpVSUFVSUFUQC9KOCdELRQ+KA85KxU6LBZP OjRYQz1PPytJOiZENBI4KQk5KBNDMRtVPzpfSURbRjlaRThTQy5PPytRPi1QPSxPPi9RQDFUPjla RD5QPzFHNylFMSQ/LB9HOCxKOy9XQDdeRz1aUEZRSD5JPy5HPSxNNytMNSpIOCpPPjBRQDJXRjha Rz5WRDtcRjNUPixMNyNOOSVHOCZIOSdMPSlPQCxXRj1cSkFXTT5USTtORjdIQDFRQztURT1WSkNe U0pWTjxPRzVEPTNJQzlKPzhJPjc9OCk1MCJDMTFYRkZbVVBWUExQTEFIRDpIPClDNyRHMiNPOipQ PjVTQDhOPDpMOjhFOytDOSlFNCxHNy5QREFYTElTTUZMRj9HQEBNRkZHPDNEOTBENDFWRkNhVFRe UVFQQD1KOzhHOTFOPzhRUEhVVExVSEhOQUFOODtPOTxIPTRKPzdNPjdNPjdNPjhNPjhNQDxJPTlO PDpPPTtOPDpWREFaSEFUQzxQPzFUQzRURTJNPixENypAMydGPUNWTVNeUU9XSkhMRUdNRkhVSUpe U1RWT0VMRTtMPTNOPzVHQDdAOjA/LydBMSlHNTVGNDRINy5KOTBPQDdQQThTPjBOOixMNzFUPjlU RENVRURRQDtJOTNHMydHMydHOCZFNSRIOS1OPjJNPzJJPC9KPjpOQT1WREFVQ0BURDhTQzdXRjda SDlaRjJWQy9NPTpRQT5URENOPj1OPDNJOC89NSdAOSpGPjlNRT9VRUFaSUZoT0xnTkpkU0xfTkdf TUZYRj9MOyxPPi9bSUNhT0heVUheVUhiUERdTD9dRTheRjlUPDBTOy9IOClNPC1cPDJnRjxeQTNX Oy1KNyxEMCZEMy1IODFQPjVXRTxaRz5UQTlRPDdMNzFHNCJFMiBKOitMOyxRPS9QPC5URTtbTEFh Tz9bSTpTQTVIOCxEMyZDMiVJNSlINChNPDBQPzNUPjdTPTVXQTFaRDNTQENYRkhjT01kUE5FNSRP Py1URDhWRjpPPytNPSlFMx1FMx08MRk+MxtMNy9QOzNQPSpPPClEORU5Lgw7JhRHMR5XQDdfSD5W Sj9USD1URjlRRDdPQTFNPy9KPS5JPC1TPThVPzpKQThHPjRGNyhBMiRBLx1EMR9OOi9XQzhaTUpa TUpRRTFOQS5KOCVBLx1FMRxNOSNQQDRWRjpdTENcSkFeSjlYRTNTOSZGLRtBLx1INSNOQDNQQzVc SUlaR0dUREBPPzxIQDtJQTxJPzxUSUZbT0ReU0dMSjdGRTE+OCxEPTFHPDRIPTVFOi8+Myk6NDBO SERbTkxXSkhTQTtMOzRHOis/MiRAMiFOPy1RPzlMOjNJODJINzFBOis9NSdHNytKOi5UR0NbTkla RD9TPTlNPTxNPTxFNCxEMytBODdVSkldUVVbT1NNPTxFNTRAPDJIRDpRUE1UU09PQ0BGOjhGMzFK ODVOPTRQPzdKPDVHOTJFOjJIPTVMOzVOPThHPThIPjlOPThWRT9YRztTQTVUQTlaRz5VSUBUSD9O PDpDMS9JQERXTlFcUEhTRz9MQDlNQTpQRERbTk5UT0RJRTpMOy9KOi5BMiVBMiVALydBMChEMyhD MidENyhJPC1MPi9OQDFQQDFOPi9OPDdQPjlQPzlRQDpMPDBJOi5DMiVGNShJOixIOStJPjdNQTpQ QTtOPzlMPTdRQzxXRUVXRUVWSkNXTERcTEhdTUldSkVcSURbQz5aQT1RQDhPPjVNPixGOCY8MiI/ NSVKPDVTRD1QRkBYTkhnVldnVldkVU5eT0hfT0NcTD9XSD5fUEZoVFhlUVZrWFNtWlRfU1BXSkhc Sj5aSDxVPy9RPCxKNylUPzFXRT5jUElaRD5MNzFPNzBNNC5JNC9RPDdYR0BhT0hYTElPQ0BNOzRI NzBBMidGNytGNylJOixVRDtdTENiUVBhUE9XSTpURjdVPjRPOS9KNylFMSRMOShNOilPPTdTQDpU PDVUPDVXQDRUPTFKOzxTQ0RhT0llVE5ELx5GMSBJMyhNNytNOSxPOy5JNShGMiVEMR1FMh5JMiZO NypROi1TOy5JOCFFMx1BMSNGNSdQPzdbSUBWSkFWSkFVRj5TRDxMRDRHPzBFNy1FNy1GOzNKPzhM QzlMQzlHPjREOzFDNSc/MiQ/NSZPRTRdSkVeTEZURDVNPS9IOClBMSM/KxNBLRVJOi5VRTldTkRe T0VfUURbTT9POiJJNB1OMx5KMBtNPC5XRjhfTkVUQzpMOy1NPC5HPjVIPzdOQUFXSkpbTkxcT01T ST9MQzlBMyBENSJFOCtENypFNSpFNSpGNzNVRUFTRkZVSEhROzpMNTRINTNFMjBEMytOPTRMOjhF MzFGMS1GMS1AMilBMypMNSpROy9VRj9aSkRUPDVPODFMOy1MOy1ENyhBNCZFPztXUU1XUU9QSkhJ OC9EMipAOTNORkBYTkpYTkpKQTU+NSpIMStNNS9MOzRNPDVNPS9KOy1OPS9QPzFTPTlRPDhJPTlH OzdNPDVWRT5XRTxYRj1aSD9cSkFXTUlPRUFFODw9MDRIPT5USElWSUlOQUFJPTlHOzdMPDtVRURc TUNXSD5UPipKNSJHMSZIMidIMSVJMiZEMSFEMSFGNCxOPDNRQzlWRz1VRTlURDhTPjNQPDFPPzNQ QDRQQDJOPjBMNy9POjJUPjlUPjlTRUdURkhQQD1NPTpKOzhOPjtUREVWRkdXSkZVSERVSEZXSkhY TEdbTklfSDxdRjpXRztRQTVNPy9HOipGNyhDMyVMOzJYRz5XTkReVUpkWlZiV1ReUFNXSUxcSkFe TURjU1FjU1FqU1ZuVlpvVlNtVFBbTklXSkZfTURbSD9RPTBPOy5RPDRcRj5YTURbT0ZVPztMNzJR OzFMNSxJNzRRPjxVQ0NaR0dVRj5PQDlIOy5BNChBLSlJNDBINzFUQTxdSU5hTVFjTUhbRUBRPi1T Py5aQThROjBOOChKNCVMNSlQOi1QPjVRPzdRPi1QPSxROzFQOjBKOjNUQzxhVFRfU1NBKhVBKhVA LBY/KxVJMB5PNSNHMxpHMxpGMR5HMh9GMCRMNSlTNStYOzBPPCtNOilPOy5TPjFUQTlfTUReTUdc SkVYR0FWRT9ORkVGPj1DOC89MipANDBHOzdQPzlTQTtOPzlNPjhDPDA+OCxAMydMPjFXRT9cSURW RTdQPzFJPSpEOCU+LRQ9LBNANCJRRTFiUU5kVFBhVE9bTklWRjFNPSlNOCBHMhtKOjNWRT5bT0dP RDxGPi1BOilFOytEOipOPTdWRT5UR0VcT01WSkNPRDxMNydIMyREMSFEMSFDMyBGNyNNPDBTQTVQ RD9UR0NJPC9ENypFNC9FNC9KNTFMNzJJNC9IMy5FMSdFMSdAMCJEMyVKNCtNNy1URDhYSDxUQC1R PitMPyxMPyxAPjNAPjNKRkVWUVBVUUxKR0FGMSpHMitEOjdOREBaSEFXRj9OPzVGOC5GMihFMSdA OC5IPzVPQC5RQzBVRDVTQTNPPT1JODhGPTRGPTRJOjtPP0BYRj1eTENhT0lfTkhXTUlMQT5ALy06 KSdDOTVOREBRRUNMPz1GQDpAOzRIPT5PREVbSUBWRTxYRDVQPC5OOixOOixQOS1ONytHNyhKOitK ODVOOzlXRT5fTUZaSD9RQDhOOTNNODJNPTFQQDRTPTVRPDRUPTxROzpPPDxRPj5YRUdYRUdTQz9O PjtJPTtKPjxOQT1PQz5USD1QRTpQQD1RQT5UR0dYTExWSkFYTURYSEdPPz5PPz5KOzpBNy5ANS1P PzxfT0xeWEhfWklkVlhfUVRWSUVNQDxRQT5WRkNjUEpoVU9qVlZuWlpkUUxaR0FURDhdTUBjTUdc RkBUPDVVPTdaRD9dR0NbSEFaR0BaQzVYQTRRQDJNPC5HOCxRQTVVQ0NXRUVTQDpMOjNMNSxFLyZD LCFJMidNOz1eTE5jT1RfTFBYRDlTPjNRQDFWRTVcRD9ROjVRNytUOS1aQzJbRDNUQzRUQzRRPCxR PCxROy5OOCtKOTdYRkReUFNbTU9EKRhAJhY+JQ88Iw4/Jw9FLBRELxpFMBtHMhtHMhtGMyFGMyFP NCpXPDFNPTFMPDBUPzFXQzRXSD5eT0VfUEhcTUVVSUFUSEBOSERKRUBBODI6MCs/NC1BNy9HNy5K OjFHPDFNQTdKQThIPzVMOzJIOC9PPkFWRUhaQTtaQTtTPyxMOSZQNyRILx1BMSRRQDJfTExqVlZi VVVbTk5aST1QQDROOSlOOSlOPzlRQzxXTENTRz5MQTNFOy08Mys5MChDMzBQQD1RR0FWTEZUSEBT Rz9QPzlJOTJBMBpBMBpAMh9FNyNOPTFOPTFPPTtQPjxGOzJBNy4+NS09NCxANStGOzBQOjBPOS9I MyRDLh8+LSU/LiZDLyVFMSdRRDdVRzpWRjNVRTJMQzlIPzVFOzVIPjlNQUNVSUpYU05VT0pEMyhE MyhDOTNHPThUQzxTQTtNPy9FOCg/NSg8MiU6NStFQDVOSDlXUUFaUEdUSkFJQzc9NytBOitDOyw/ NTJDOTVQRz5bUUheU1RXTE1RQDtGNTA/KR4+KB0/OCdMRDJPQDpOPzlFPzlAOzRHPDRNQTpUR0dV SEhVRURWRkVRQTVTQzdTQzNOPi9MPSdGOCJPOjVWQDxbR0dlUVFaTU1MPz9IOCpJOStNPzBQQzNX QDdaQzlXQTpTPTVNOzlRPz1TQDtVQz1PPjJNPDBNPDVPPjhNQDxNQDxQPzlRQDpTQDhVQzpVR0xX SU5TT0xYVVFYUE9NRURUQUFPPT1KOy1IOStWRERnVFRjVUdcTkBUR0NOQT1DOi4/NytJOT5VREln VFRoVVVnU1NfTExRQTNJOixUSD1cUEVkTEdcRD9XPzVfRz1eRkFcRD9dRT5bQzxdRz9fSUFTRTVM Pi9KPTBWSDtYRUVXRERTOzdONzJQNSpOMyhIMSVPOCtUQTxdSkVYRz5RQDhPQTFURjVXRzhVRTVV PjJNNytXOytdQDBiRDlhQzhaQzBXQC5WPShWPShRPTBQPC9OPTdaSEFdTkdYSUNKNCNELh1DKxdA KRY/KxZBLRhFMB9HMiFINSFHNCBJNB9MNyFQOSxUPC9PQDlOPzhPPjhRQDpXTERYTUVXUEdWT0ZU TUNQST9WSj9RRjtGOSw1KR08KRxALSBFOClGOSpOPTRTQTlRQDpOPTdRPzdNOzJOOzlTPz1XQzhX QzhYQTVTPDBRPSdMOCJFMSVPOy5dUE5nWldiVlpeU1ZbSENWRD5ROy9UPTFPPzxTQz9TTENTTENV Qz1QPjlDODA5LidDLhtMNyNOPTdTQTtQRz5PRj1NQDxHOzdJMydNNypOOSlQOytQPzdNPDNPPTdO PDVJOS1GNSpDMipEMytAMSNKOyxVPy1YQzBUPS1JMyRBKx9BKx9ALSBHMyZORztRSj5YT0NYT0NR RUBHOzdKOTtKOTtJOT5VRElcSUlaR0dKPDJENSxENSxKPDJMPTdPQDpHPzxDOzhDOyxAOSpBNTNT RkRaTUpdUE5cU0lXTkVNRDpGPTNGOSxENyo7MTBBODdMR0hYVFVcU1RRSElJPC8+MSVAKBZEKxlF OCtRRDdPPjlPPjlGOjhHOzlMOzVHNzFJPj9PREVUQEdXREpORkBORkBUSEBWSkNQPzBJOSpUPTxh SUhjT09lUVFUQz1HNzFKPDJQQThYRzthT0NeTENcSUBWRT5MOzRFPDNKQTlPRDlRRjtQQzNNPzBQ PjlPPThNOzlPPTtPPTtPPTtQPjlUQTxRREZVR0lRSk1WT1FYTVBTR0pVRD5TQTxPOS9OOC5YSk1j VVdYTEdPQz5NOzRNOzRHOi1HOi1QPT1hTU1jVVpkVltcSUxRP0FPPjhQPzlUSkBbUUdhTEBdSD1k SD9pTURhSkVeSENdSUdYRUNeSU5lUFVXRj9OPTdTRD1dTkdeTElaR0VWPjRROjBNNSxKMypJNC9Q OzVQPjxRPz1QPzNVRDhdTDxfTj5VSjpKQDBNNyVPOSdXQDRcRTlbRDhcRTlVRTJWRjNcRjVbRTRU RDRTQzNRPTJXQzhcSURbSENTOy9KMyhHMx5GMh1HLxlHLxlGMSJKNSZOOSdRPCpUOilUOilUPS1Y QTFTPDJQOjBOPzVOPzVYR0FaSENbTkxaTUpXTkRWTUNeTENaRz5KPCg6LBk/KxZGMRxFOCtFOCtQ PC5TPjBRQDhPPjVOPDVMOjNKOTJOPDVQQS9URTJTRDFOPy1DRS1DRS1NPDVQPzlbTU9kVlhjVVdd T1FaSUhXR0ZXQDRUPTFOPTdRQDpORz5UTURUSD9OQzpHQDRDPDBDNyBDNyBMOyxKOitMPTNPQDdO QztKPzhQPDFRPTJROjBWPjRTQDpPPTdOPjBMPC5GOidANCJFMSRDLyI7MyJIQC5XRzheTj5VRTdM PC5JMCJILyFDMB5INSNWRz9eT0dbUE1YTkpVRUFIOTVDOTNFOzVMOjpUQUFQREFUR0VMPC5HOCpM NSxROzFMOzRRQDpPPz5MPDtIODJEMy5IOThVRURVSkVbUEpeVE5bUEpQQTpPQDlKPDJDNCs8LSpD MzBOQ0RYTU5YT1BNREVKOitFNCZBLRpGMR5JQDdUSkBPPjlNPDdEODNEODNINzBHNS9BOjdHPzxM QEROQ0ZRRUNTRkRRSElUSkxTQTtJOTJOR0dYUVFnUVZfSk9PPDxGMzNOQENYSk1dUExiVVBjU09c TEhTQTlJOTBFOzVNQz1ORTxRSD9PRzhMRDRKPjpMPztPPTtRPz1OPj1MPDtKOzxMPD1TQENVQ0VR SUZQSEVTQ0RTQ0RVRD5TQTxGPjlIQDtaSE5fTlReR0hVPj9MOTdOOzlIOS1KOy9QP0VfTlRcVVda U1VXRUVPPT1PPjVTQTlaR0BcSUNjSj5tVEd1VVB0VE9pT0hfRj9eRUBhR0NpTVZuUVtUQzxJOTJN REVbUVNcVFNWTk1XRTxQPjVKOjFBMSlGLylMNC5HOTFKPDRVRj5hUUlnVU5iUElbRT1VPzhOOSNT PSdcPTljRD9eRz1fSD5bRjtcRzxjTkBhTD5dTDxaSDlPOjJRPDRYRkRYRkRXQTpQOzNMOSRHNCBP NyBVPCVOOixPOy1MPStMPStTPCxXQDBcQDRkSDxVRDhNPDBMOzJKOjFPQDpRQzxWSUlaTU1dUUlY TUVaTkZWSkNJQCxBOSVAMRxHOCJKOy1NPS9UPzFUPzFQPzdOPTRIOjBJOzFOOypTPy5QQzNVRzhY TDhVSDRPRzVPRzVQPjxTQD5XSUxdT1FeT0hdTkdbTklVSERWPjhROjNMOy1OPS9RPzlXRT5USkBP RjxJRDJKRTNEOCU/MyFDNB9DNB9JOixOPjBNPjRMPTNNPDVOPTdOODdPOThMQDlOQztRRDNKPS1J OSpJOSpMNSlDLSE/OClJQTJaSENiUEpcSUNUQTtKMyc+KBxDLCFQOS1XSUxdT1FcTlBYSk1OPzVD NCtHOitJPC1MOzROPTdRQDpRQDpURDhRQTVXQzVWQTRTQDhRPzdWQDxQOzdOOzlFMjBHOTJNPjhV QzxbSEFXSkhTRkRIQTlJQzo/Ois9OCk6Lyg/NC1OQ0RYTU5XR0ZQQD9NOSxMOCtMNydQOytRR0ZP RURJOzFMPTNQPjVOPDNJOi5HOCxHOS9JOzFJOjdNPTpORD5VSkVYT1NQR0pMPD1MPD1TTUpcVlRo UFRdRklNPT5JOjtORk1YUFdeVVZeVVZYSk1URkhPPTdKOTJJOjlTQ0FVRj5bTERUSD9OQzpQPzlR QDpQPzlNPDVJNzdKODhKOTdOPDpQPTtUQD5TQTtNPDVTPDtUPTxTP0RQPUFFPD1IP0BWRU1YR09U R0VNQD5JOTJOPTdKOjFKOjFKP0NUSExYU05XUU1VRj9OPzlOPjBRQTNVRDtdTENjUE5qV1V1XVxv V1ZkTEFeRjxeSERqVE9yWlhqU1FNPDBMOy9NSElaVVZeVlNYUE1TRDxRQztTPCxPOSlMNSlKNChR OzFbRDpdSk1lU1ViT09XRUVaQzlVPjRMNSZVPi5dPjxjREFiSEFlTEVdSkFhTkVkT0RfSj9jSj1Y QDNNOS5POzBXRj9YR0BcPzpcPzpVPjFQOi1UPzJXQzVVRDRUQzNRQS9QQC5VQTBWQzFeRjliSTxR RDRKPS5MMiRJMCJIOCxMOy9PPzxYSEVeTEZjUEphUE1fT0xMQTFFOytIOSdJOihMOy9PPjJQPzFT QTNHPjRDOjBFOi9JPjNPOSxXQDNVRj5dTkZdVEpXTkVXRj1VRDtVRUFWRkNVSklaT05bSklYSEdW SUdRRUNQQD9JOjlDOStBOCpGNCxOPDNPRjxMQzlIPTRGOzJDNSc+MSNALxdJOB9NPSlOPipOQDNI Oy5JOi5MPDBKODhHNDRKOTdQPjxPPjBOPS9JOixHOCpNNypHMSVEMipWRDtYTElbTkxaSENVRD5N OS4/LCJIMStUPDVXSVBbTVRcSE1VQUZMOzJKOjFFOy1IPjBNOzVOPDdUPjpQOzdQPzpcSkViUEpj UUxfTz9YSDlQPjVHNS1JNC9JNC9DNC1GODBPPjVVRDtTRkFMPztEPDlEPDlBOy8/OS07MidAOCxN PTpVRUFbRT9VPzpXPzNVPTFQPzFTQTNXSkhPQ0BHNylMOy1VPTNWPjRFPDJBOS9DOShFOypJOi5O PjJUREVWRkdUR0dPQ0NPPDpUQD5eVE5fVU9kUU9RPz1IOz1IOz1PSE1YUVZaUFFRSElQQD1MPDlK OTJMOjNIPzdPRj1eTURlVEpTTD9MRTlMQDhJPjVMOyxJOSpNODBPOjJQPzdRQDhTQTlVRDtTQDpO PDVMOjNMOjNPPDpPPDpIPTVMQDlTRUlURkpQRT1OQztRQDRQPzNOPjJOPjJOPTdYR0BeU0lbT0ZX SEBNPjdMPTVPQDlTSENcUUxnW1xoXF1vVl5nTlZaQTtWPjhaSExrWl1rWFteTE5NPjdKPDRPRU1d U1tlVVRcTEpWRDtXRTxUQzdOPTFGNCxJOC9KOTtWREZbSEhfTU1dRkVWPz5aRDxQOzNHNytUQzdc Qz5aQDxdRUBcRD9WRz9YSUFlTkRoUEZlRz1TNSxGMSpKNS5XQEFYQUNcQDRfRDhaRDNWQDBWRTdc SjxYSDpVRTdTRTVRRDRWRTdXRjhhSTxlTkBWRz9MPTVKLyBILR5ALRhEMBtNPDVRQDpYRkRjUE5n VlNlVVFbRT1OOTFKNxtPOx9UQC9YRTNYQThXQDdJQTJBOitBMidJOi5QPDFbRjteTUdlVE5hVlNa T0xVR0lNP0FQQD9TQ0FPQ0NYTExbSklYSEdWRTxUQzpOPzlHOTJBMyxBMyw/MSpDNC1GOzNGOzNI OCxEMyhBMyBENSJHMh9MNyNQQDJVRTdNPy9HOipKOCVNOidFNShAMSRJOC9RPzdTPjNVQDVMPCpJ OihHMydKNypJOTJcSkRXT0xQSEVYQDpWPjhOOCtIMiZTPjNYRDlaTk9YTU5YQz5TPTlIOCxMOy9J OzFJOzFJOzNHOTFMOTdOOzlJPj9aTk9qWFxoVlpiU0haSkBOPixENCNHMSZHMSZEMyVIOClTQTJY RzhXRTxPPTRHPjJFPDBGOSpDNSdBMyJFNyVJOjdTQz9fR0BeRj9dTUBcTD9XSTpXSTpXTT5NQzRJ NC1QOzNVPjRUPTNFOi9ANStHPS1KQDBOPTRQPzdUQURYRkhURENRQUBPRT9WTEZcVFBdVVFbUUdJ QDdFOTlKPj5URkpVR0xUREVOPj9JODFGNC5KOjRKOjRJREFQSkheVFBjWFVVT0pKRUBJPjVIPTRK Oy1MPC5TQDpTQDpVRD5XRkBYRz5aSD9UQTtPPTdOPDVJODFFNzBIOjNHOS9JOzFTP0ZXREpWRT5W RT5aRDxbRT1aRThYRDdaRjRhTTtcUEhbT0daSkROPzlOPDVRPzlWTEpcUVBnV2FpWmNtU1ViSEpP PjhRQDpbSU1nVVhkVFBYSEVTQENUQURcSFFkUFpkUE5cSEZURTJVRjNTQzdOPjJFNzBHOTJOPj1R QUBaR0FbSENWRD5TQDtUQTlPPTRMOCtVQDNbQzlYQDdXQTxVPzpQPzdXRj1iTk5hTU1hQDVPMCZD LCFJMidUPjpRPDhYQTFdRjVbRjtcRzxbST1eTUBdSkFaRz5YRDVXQzRYRDldSD1hT0NjUUVdTEVU QzxOOChFLyA9LiBAMSNHNS9NOzRVQT9hTUpjVlFcT0pcSDdPPCtOOSVVPytbRDphST9bR0VWQ0BP QDdKPDJGNShIOCpRQDJaSDpkUVFnVFRjUFNbSEpQRT1FOjJFNC9HNzFGOC5RQzlaRkZcSEhYQDpW PjhTPjNJNStBLCNFLyY8LCE9LSJBLCBFLyNJMydIMiY9MR89MR9EMixHNS9VRD1bSUNQQDFIOSpF MB9HMiFFMx1HNR9JOCFVQytYQy5aRC9TPjBMOCpDNSlHOi1PPjlaSENWTUNNRDpTPDJROzFVQDNY RDdWRz1URTtcUU5YTkpUPjdJNC1JODFMOjNOPzVKPDJHOi1HOi1OOTNJNC9FOzpUSUhkVl1nWF9i VVBbTklTPypMOSRMOCJINB9FNSpMPDBcTUNfUEZaTkVTRz5JPSpFOSZHMyZHMyZNNydNNydJOzFU RTtdUE5dUE5WUExWUExaTUhdUExWTEhMQT5PNzBXPjhWQDlTPTVNPDdJOTNTQTlVRDtRPzdRPzdP RDtTRz5RRj5QRT1RRUVXSkpcVFNYUE9aST1MPDBNOzRMOjNJQEFKQUNPPjVNPDNOPS9MOy1NOjhO OzlVQ0VbSEpfT1BjU1RfVExUSEBOPDNPPTRTPThTPThRRj1QRTxUR0NTRkFTRkFUR0NWRT5PPjhI NzFHNTBBMy1IOjNJOTJMOzRWQUZXQ0ddRkVfSEddSkFdSkFfSUReSENcSUBdSkFhT0ldTEZfTElV QT9PPjhQPzlUR0VcT01oU1poU1piSEVbQT5ROTlXPj5aTlFhVVhiUElbSUNWQ0VcSEpfSk9iTVFh TkhaR0FUPzJXQzVTOzFMNCs/MSpGODBVPztYQz5cRD1aQTtUQzpUQzpXQDRROy9XPS5cQTJcQTJd QzNYQDpYQDpXRT9fTUdiUEdWRTxVOStKLyJDLCFFLiNMNTdQOjtVRTJbSjhdSThdSTheTENfTURe SEBcRj5aSDxcSj5aRD5eSENfTkdiUElhUERYSDxNPC1BMSM9LSJGNSpANStDOC1NOzRVQzxeTkpf T0xbTTxQQzJPRTdPRTdWRDtbSD9eRkFaQT1TQzdQQDRKOi5JOS1NQD5WSUdeUVFcT09cTEpXR0ZX QDRROy9HOipENydHNylRQDJYR0BdTEVURDhQQDRVPTBKMydMMCNGKx4/KxZELxpKMR9MMiBOOChO OChFNSc/MCJAMCVPPjJVSUBcUEdVSjpKQDBIMyJJNCNINB9OOiRTRC9aSjVfTkFfTkFORDVDOStD MyhDMyhKPzhYTUVYSDxURDhRPCxWQDBbSEFhTkdeU0pcUEhfVExcUEhbRzVRPi1QPC9POy5MQDhM QDhPOzBNOS5MOjNGNC5INTxWQ0leUFdfUVhhU1VeUFNdRz9XQTpPQTJGOSpEOzJKQTliVldfVFVX UU1MRkFFPSs9NSREMCZHMylJNyZNOilNPjdWRz9iUVNfT1BXTk9XTk9YSEddTUxaTkVQRTxWQTdb RjtYRkROPDpNODNPOjVUSD9TRz5UQzpVRDtORz5QSUBORkBPR0FVSEZVSEZcSUdbSEZaSENOPThN Ny1NNy1MPDlNPTpPPzNRQTVRQTNQQDJNOz1NOz1VO0FbQEdfTFNkUFdnUEpYQz1KPDJMPTNQOjlV Pj1VRUFTQz9OQzpNQTlQSkhTTUpTQDpNOzRHMitFMClHMi1MNzFMOjNMOjNWQ0lVQUhWRERbSEhd TUleTkpeTUdcSkVeTElfTUpdTU5YSEldTUxXR0ZUQD5QPTtWQ0deSk9iUFRhT1NcRkFUPjpUOzdb QT1eSlFjT1ZkUE5fTElYQz5aRD9bSEpeTE5kTEhbQz9TQTVUQzdUPjdMNy9GODBNPjdbQz9YQD1b Qz9cREBXRT5UQTtXPzlVPTdfRDhiRjpfSj1kT0FeSERhSkZkTkhlT0lnUUZdSD1MOCJHMx5JLyRO MyhPNzNUOzhRQy5VRjFcRjNhSjhcSkRcSkRYRz5VRDtTRz5WSkFbSD9eTENfUEZhUUddUUhVSUBT Qy5KOydFLyNDLSE/LydAMChFNCdPPjBQREFbTkxaTkVXTENVSUFVSUFdRT5dRT5dRjxaQzlbRT1Y QztNOStKNylMPD1OPj9TSEVTSEVXSD5VRjxTQyxOPihMPC5MPC5WOy9dQTVeSENiTEZaST1WRjpR Oy5NNypKMh5DKxdJNB1RPCRPPClRPitUPzJWQTRPPi9MOyxFMiBUQC1XTENcUEdVSUFMQDlJPSpG OidIOiZMPSlcSkFjUUhlTk1lTk1VRDtPPjVJOitENCZFQDdQTEFcSjtYRzhfSD5nT0VhUFFfT1Bf U1BcT01hVUxdUUhYTj9TSDpRRDRRRDRWPjhXPzlYQThUPTNNOS5JNStKOTlTQEBRR0RVSkdeUU9h VFFlTk1hSUhRTEdEPjo/NzhIP0BfVldaUFFTSklTSklMPi89MCJAMSBBMiFGNSpMOy9QPjxeTEli VVNdUE5YU05TTUhYUE9eVlVeVE5RR0FYRkZeTExRSElEOzxDMzBKOzhXR0hWRkdYR0FaSENPSUdP SUdTRkFUR0NXR0RXR0RMRkFNR0NOQ0RHPD1DMipDMipINzBMOjNTPTVbRT1UQzxUQzxNRDpEOzFI OTpNPT5YRUliTlNcTUZPQDpHNS1BMChINzFOPDdNPjhKPDVKPDRRQztORENPRURGPCw8MiNELSFF LiJKNCVQOipPPThNOzVOPj9PP0BOQENTRUdbR0dcSEhhTU9iTlBiUVNfT1BYRUlXREhPR0RQSEVT Qz9NPTpORUhYT1NeUFVcTlNTRz9OQztWQDtXQTxaSE5fTlReSk9fTFBcSUdXRUNTRUlaTFBfSUFY QztYQztYQztURjlKPTBNOzROPDVcRD9dRUBbRENcRURWPT1TOjpVPTdWPjhVRjxcTUNpU05rVVBu UE5rTkxoTk5qUFBoU0deST5QPCZINB9VOC1fQTddRT5TOzRQOylWQC5bRzNiTjpfTkdcSkRbRT9X QTxTRDxTRDxWRz9cTUVcT0pdUExbSEZWREFfQTdhQzhYQTFNNydBLhZDLxZPOSdROylQPjhbSEFd SkhbSEZXR0ZaSUhdSkVfTUdeTUZXRj9QPjhQPjhOOCtQOi1PPTdOPDVRPzlUQTtYSDpVRTdTQzNR QTJOPjJQQDRXQDdbRDpdSkRfTUZfVEpbT0ZKQzFJQTBKOitBMSNNPipWRzJcTDlbSjheTURaSD9V RTdURDVPPCtTPy5YRztfTkFWRT9MOzVGOCZFNyVIOCxVRDhcUU5iV1RkVFBiUU5YRz5NPDNEMR9D MB5AOC9ORTxbSUBeTURfVlpiWFxhVVhYTVBYSElYSElYUEpXT0lcT0pXSkZdRz9kTkZeUENfUURb TERXSEBbRT1RPDRQPjVRPzdORz5TTENbUUdiWE5tXF1kVFVWT09GPz9BOjlNRUReUU9bTkxWSUVX SkZMPjFDNSk+MBtAMh1GNShRQDJWRUhfTlFbU01XT0lWTkpWTkpcU1RfVldeVlVWTk1eSk9hTVFW TEhKQD1GMzhRPkNRRUNQREFRR0FRR0FPR0ZNRURMREBNRUFQRkBRR0FKRUBJRD9KPjpGOjVIOC9H Ny5OPjJURDhaR0BiT0hcTEpRQUBIOy5GOSxIPzVMQzlWQ0dcSE1KQz9DOzhFMys8KyNBMidGNytM Oy9OPTFOOzlRPjxKOTc/LixDLhs9KRZFLyBJMyRGNyhKOyxOPDdNOzVOPTdOPTdNQz1ORD5YRUNb R0VeSVBlUFdjTlVjTlVXRUVWRERTRkFWSUVVQ0BTQD5TR0hXTE1YSk9YSk9bQTtWPTdRPzlTQDpW Q0lhTVReTVViUFhYSElUREVVREdWRUheRj9aQTtcSUNcSUNYRj1OPDNNNC5QODFXRUNaR0VWRD1R PzlNOjhINTNNNTJROjdWQUZjTlNqU1ZrVFdrU1hkTFFbSEhjUFBiUERWRTlTPjBdSDprTkhvUUxh TEBPOzBQPSxXRDJaSDpiUEFkU0lfTkVcRj5YQztTQTJPPi9URTtYST9bTkleUU1aTUhTRkFYRDdX QzVUQzxQPzlNOR9QPCJQOytTPS1WQDtcRkBUR0NUR0NVRURbSklaTUpcT01bSEpXRUdQPzFIOCpM NydNOChQOTRTOzdQPzdTQTlURjdURjdWSkFUSD9RQDhQPzdURDhXRzthT0hkU0xfU05cT0pUSEBO QztFPDBFPDBMRTtQST9cTUNbTEFiUUViUUVdTz5cTj1XRjdRQDFfR0NjSkZUQzpJOTBFLyRGMCVK Oy9aST1bVFRiW1teVVZbUVNVSEZFOTdHMx5FMRw6LyhIPTVWSEpdT1FbWl5VVFhcTEpWRkVKRDpN RjxaSkNcTUVcT0pYTEdfSUVoUU1eVE5fVU9eTExXRUVRQzlNPjRVPzhTPTVKQz1UTEZfT0xpWFVl X1thW1ZWU09EQD0+OC5HQDdUTEpTSklYRkBaR0FOPixKOylFNyFFNyFENS5RQztXTlFXTlFYUE9W Tk1XUFBUTU1dUVNeU1ReU1ZcUFRfU1BbTkxRSkFFPjVIOThRQUBRSD9PRj1TRkFTRkFNRUFKQz9N Q0FRR0ZUSEBUSEBQQThPQDdOPTRMOzJKPDJJOzFUR0NbTkldTUxfT05XSEBMPTVFNy1GOC5JPjdN QTpQRT1OQztMQDVEOS5AMiE9Lx48LyBBNCVJOitJOitNOzJOPDNJNCVDLh9EMBtINB9HOSFMPSVE PypBPShKPDRMPTVTPThXQTxVRj5RQztbSEZcSUddSU5dSU5hSFBjSlNbSEFXRT5VQ0BUQT9TPz1U QD5XRUNbSEZbSEZaR0VYQztWQDlUQzpUQzpUQ0ZaSExaTFBcTlNXR0hVRUZYQUNaQ0RcRzxcRzxf TEliTkxYRj1INy5BMypKPDJUR0VbTkxbRT9TPThIOy5GOSxGNSpOPTFXRkBiUEpqVlRqVlRlTlFd RkldSk1hTlBdSkRXRT5bSENkUUxrUFNlSk1dRjVUPS1UPzJXQzVXRj1hT0ZnU1NkUFBaSENTQTxR QDJPPjBTRDpYST9bSUNhT0heTk9WRkdQQTpRQztQQD1UREBYQzJVPy9UPjdUPjdUQD5VQT9PRUFR R0RaQ0ZfSExeSk1iTlBdSkFYRj1QPzNKOi5KOitIOClNPC1TQTJQQTpRQztVRjxbTEFdTEVeTUZa SENUQz1RQzlVRjxdTEVjUUpfU05fU05bTEVTRD1HPSxMQTBRSD5USkBYTUFaTkNiUERhT0NdTkRa SkBTQzNRQTJeTUBdTD9aR0BKOTJGLyRHMCVGODBaSkNbVFZfWFtaU1NXUFBQSENAOTNHMiFBLRw3 LCVIPTVVSkVeVE5WV1BRU0xVRD1RQDpGQzBJRjNRQzxWR0BXSkhVSEZhTVFkUFVfUVRkVlheUU9V SEZNPDVOPTdRPz1PPTtIRDlOST5eTk9rW1xkX2NeWl1VT0pBPDhDNCNMPStUSD9VSUBaSD9aSD9T QzRQQDJPQTRKPTBGOjVQRD9VT01VT01VTUxTSklUTEpRSUhUTEpUTEpYTVBXTE9aUEdVTENVRTlM PDBMQDlTRz9XTENXTENRRj5RRj5QRkBORD5QQD9WRkVdSkhfTUpTRThPQTROPDxMOjpMPDtURENb TU9eUFNiUVBaSUhOPTFMOy9MOjFNOzJQOzNQOzNKPzdHPDNBNCVAMyQ/MB0/MB1DMidHNytKOy1N PS9TPDJROzFPOihPOihPOSxPOSxNPC5RQDJKQzNKQzNPQDpOPzlVPztWQDxUQz1WRT9cSkVdTEZk TkljTUhhTU9hTU9WSUlUR0dWRkNTQz9PPjJNPDBWQ0NbR0deRkNdRUFWRD1aR0BXRztTQzdPQDlT RDxYRkZbSEhfSUVaRD9UQTxRPzpWRTxhT0ZtVlFqVE9aRDxIMyxHPDRMQDlYTExdUFBhSERTOzdJ NyJMOSRKNSRQOylYRj9hTkdqVE5oUUxcSURTQDtaSENdTEZbSEhaR0deTEVjUEllTkReRz1YQTFX QDBTQTJRQDFTSEdcUVBfU1BeUU9UREBKOzhPPjBRQDJPPjJQPzNXREFjT01eU0pTRz9PQ0BMPz1Q QD9VRUReTkFdTUBbSkdVRUFTQTtNPDVPPzFURDVaQT1fR0NhTU1hTU1iUElaSEFOQzpPRDtQQDFO Pi9URDVVRTdXRj9bSUNdUUhfVEpfVExfVExbSUNTQTtPRDlXTEBaTkNiVkpeU0pfVExYSUFPQDlI PylHPihQQThURTtXTENXTENhUEFeTj9aSENYR0FRQDhWRTxbTkleUU1dTD9RQDROOi1EMCQ/OS9R SkBbVVBdV1NYTElWSUdMRTtDPDJEMSE8Kho0KxtEOilUSUZeVFBVSklRR0ZTQDtRPzpOQzhQRTpO QzpRRj1VSUFUSEBXSUxcTlBaTUpiVVNdU09USUZUQzpRQDhPPzFNPS9FPjJHQDRXTE1fVFVnXGRe VFxRTUFDPjNQPCZXQyxXRzldTT5bTEFaSkBURDhRQTVRQDRPPjJQPzlTQTtTSUBXTkVTSklRSUhR RUBUR0NRRj5PRDxPRURVSklYTUVVSUFRPzdRPzdJRD9UTkldUExcT0pPRDxOQztQP0NRQERUREVY SElYUE1YUE1XTUlMQT5OOzlPPDpKPjpaTUhiVVVhVFRbSU1KOj1JNCVJNCVPPCtQPSxVPTBUPC9J OTBHNy4+LyI/MCNBLBtGMB9JNShOOixMOyxQPzBYOzNaPDRWOS9aPDJTPDBUPTFTQDtUQTxQQThQ QThRQzxQQTtRQztPQDlQQD9VRURXSkpbTk5kTkZjTUVlTE5lTE5WTEpUSUhVRj9PQDpKOTNNOzVX RUVaR0dbQz5YQDxYRj1bSD9bSURVRD5TQTxYR0FbSklYSEddRz9YQztPPDpUQD5bTklnWlVoVU5k UUpdRjpPOS1RQDRXRjpcUUxfVU9cTD9TQzdOOSdQOylOOCRNNyNUQD5jT01kUU9cSUdXQEFTPD1Y QztXQTpRQDhVRDtYST9dTkRhTkhaR0FhST9qU0hRQDRNPDBNSD5RTUNYUEpTSkVQPjhHNS9KNChP OSxKPTBHOi1QPjxaR0VYTUVPRDxOOCxIMidINzFPPThXTkVeVUxYT1BUSkxVRzpNPzJPPjJRQDRT Qz9UREBWSkFYTURdU0BYTjxNSDtNSDtRRDRRRDRYST9dTkRiSEhhR0ddUExfU05dVlZbVFRaTk9U SElXRj9hT0hcUEViVkpdTkRXSD5WPjhTOzREOilGPCtIQTlPSD9VRD5XRkBaT0BcUUNcSUdbSEZY QUNcRUZaVFFeWFZfVUZRRzlPQTJHOitBPi5PTDtcVUxbVEpXRj1QPzdKOy1ENCdEMR87KRc4LxxE OydRRkdfVFVVSEZOQT9NPDNOPTRVPj1WPz5NRDtPRj1RRjtPRDlJRD1QSkRYUE1hWFVbU1FTSklO QT9OQT9VRDtRQDhMQTxNQz1XSU5eUFVkXWJdVltTRzxMQDVdRzdkTj1cVERfV0deTUZaSEFQRTxO QzpQSDlRSTpURT1QQTpUR0NYTEdTTENORz5NPjdQQTpPPzNPPzNQRkNUSUZWSkFVSUBRRjtPRDlH RkBUU01dU09YTkpRQDpKOjNNPT5RQUNNQz9XTUlbTkxcT01aSUZKOzhGNSpJOS1KP0NfVFdiWFxb UVVRQDpDMixINChJNSlTQzROPjBRPi1NOilFMCFALB1FLRlHLxtELhtJMyBKOylOPixQPC5TPjBX QDRYQTVUPTFVPjJWPzNVPjJTQDpUQTtOQztMQDlPPjhQPzlOQT1OQT1TQz9WRkNVSUpXTE1iUEph T0ldUE5eUU9bTk5aTU1YRkZRPz9OPzlNPjhdREZeRUdaRD5XQTxcSEZeSkhWRkNUREBaRkRcSEZW TEpUSUhYR0FTQTxONThXPkBdUE5kV1VnT05jTEpaRTpRPTJRQzxYSUNiVkpjV0xcTD9YSDxNOCRE LxxDMyJGNyVORD5dU01hT0hcSkRUPjdOOTFTOy9WPjJOPTRRQDhYRj1cSUBaR0VYRkRlUVRtWFtO PDVINzBNPDBVRDhXTENXTENaQT1PODNJMydHMSVGOidIPClGPTFUSj5USD9OQzpQNyhMMiRGMCFM NSZXRUNeTElXTE1WSkxQRjRIPi1HNy5IOC9KOTNRPzpURDRWRjdVTjhUTTdPSThNRzVURjlYSj1a TUpbTkxfTU9aR0leTkpjU09aV1ZYVlVYT1BRSElWTEheVFBdVlZdVlZXTEBJPjNNPC5JOStFOClH OitOPzVQQThUQzpaSD9XTERbT0deTEldSkhVQUhYRUxcU1RiWFpbV09NSUFQQDJMPC5IQThTTEFa UEdXTkVKRC5BOyZIOSVHOCRFNBhAMBU9LiNJOi5RRUVWSUlTRDpKPDJHPS9HPS9QQTtPQDpMQzdP RjpQQThNPjRFPThPR0FYUVFfWFhcVlRTTUpTQ0FXR0ZTRz5OQzpQRz5KQTlMRkRWUE5iWlhcVFNY SDxcTD9eTUZqWFFfVVFdU09iU0xbTEVXRj1XRj1YT0NbUUVUTUBMRTlTSENWTEZRSD9ORTxNPDdN PDdHPjJJQDRQRkNTSEVaSENeTUdbTEVRQzxOST9XU0hYTUVVSUFNQTlDOC9NPTpTQz9MREBPR0RT SkdWTkpdTENUQzpRPTJQPDFXQ05iTVhhVFRVSEhOOSdGMSBIOStOPjBVRDtRQDhOPi9IOSpELyA/ KxxBLRhELxpBMBZKOR5OPS5TQTJUPjdWQDlXRztaST1WRDtTQDhXQzVVQDNYRj1TQDhJPjNFOi9D ODBGOzNMQDlNQTpRQzxVRj9RRUBYTEdeUU9fU1BfU05eUU1bT1BbT1BcSUlVQ0NUQTxVQz1iSEVj SUZhSEVfR0ReSkpdSUlWRD5aR0FfTkhbSURWREFVQ0BRQDtOPThFMjRTP0FeSlFlUVhkUE5dSUdR QDRPPjJWQ0NcSEhlWlBjV05cSkFTQTlIMh9FLxxJMydQOi1XRT5fTUZbSj5YSDxJPy5EOilOOSlR PCxVPzhaRDxbQz5bQz5XREZcSEpiVFtfUVhGODFDNC5IOCpNPC5QRjhQRjhbQUFWPT1KOi5GNSpD NSZHOipNPjRQQThTQzdWRjpTPjFMOCtMMiJPNSVTPz1XREFWTURTSUBaRjROOypDOiZAOCRROytU PS1VPy9WQDBVSjlVSjlYST9XSD5URjlURjlWTEZbUEpeTkpcTEhcT0pbTklWVU9aWFNaUFRQR0pR RkdYTU5aVVRVUE9TQzRJOixENB9DMx5IMiZIMiZPOy1WQTNVPzhWQDlXREheSk9aUFFXTk9NQEBO QUFWTVBcU1ZWTkpKQz9POzBMOC1MRD5TSkVfUElYSUNMRS9DPCdQPSpWQy9PPylOPihIOjJNPjdX RTxYRj1TQzNJOitEPCtHPy5PRTdTSDpMRDJKQzFKQTlIPzdGPj1QSEdWVFdcWl1cTlBWSEpPR0ZR SUhGPzdJQzpMPTVJOzNJRD9TTUhkWlZdU09bT0dkWFBjWltnXV5hVVhbT1NfT05dTUxfUEheT0dc U0hbUUdbTEVYSUNUR0VXSkhVSUFMQDlMQDlJPjdHPjRJQDdPQ0BVSEZdSkhoVVNVUEVKRjtWRz1W Rz1VRj5RQztGOzJEOTBKOTdOPDpMQT5NQz9XTUdfVU9dUUZYTUFRQDpQPzlXSVBdT1ZcSkRQPzlH Mh1NOCJPPjVbSUBXTERPRDxJOC9EMio7Kx03Jxk+JxRELBhJMypXQDdYRj1WRDtUPjpXQT1eTUZf TkddTD9bST1dRT5cRD1dRT5WPjhQOylNOCZIOClHNyhHPDRKPzhQPzlRQDpRQztYSUFWSUdYTElf U05hVE9dVFVbUVNdSU5XREhVRj9QQTtYRkZeTExlTk9jTE1bSEhUQUFVQ0BbSEZdSkVYRkBTQDhU QTlRQTVMPDBHMDVYQEZnUVhqVVxhTkdcSUNUPzRRPTJVQ0NeTExiU0piU0pdTD9TQTVOOypQPSxR PTJVQDVfRkNiSEVaSENcSkVTQTVNPDBNQC1OQS5XSEBeT0dpVEZjTkBdQ0VcQURbSU9dTFFTPyxN OidPOCtTOy5RPzlVQzxVRDtWRTxPPjhKOjNGOzBGOzBOQDNKPTBTQTlVRDtQPzNNPDBMNydPOipQ PjhVQzxaRkRWQ0BTQTlKOjFGOSxFOCtOOypUQC9XRDJYRTNURjVTRTRXRkBaSENaSkBVRjxVSUBa TkVcUUxdU01dVEpbUUhcVk9eWFFcU1RPRkdRQUBWRkVXSkZbTklVQTBPPCtINB1FMRpJMyBHMR5J OS1UQzdWQTdYRDlWRT9fTkhfV1RaUU5QQTtKPDVXSUxdT1FYSEVQQD1PPjJNPDBOREBRR0RcTE1a SUpURjlTRThVRzpYSj1bSj5bSj5UQTlQPjVURDhQQDROPjJMPDBBOCpIPjBOQzhXTEBXRjhRQDJT Rz9MQDlJQERPRklWUE5cVlRfT0xdTUlWUExaVE9QRz5IPzdIOjJIOjJMRUdRSk1eVVZbUVNbU1Fk XFtqXmJnW15eVFNYTk1eUVFiVVViVVNjVlRdU01bUEpeTkpbSkdUSUZWTEhRSkBNRjxRQzxOPzlM PDlKOzhQRz1aUEZhVlBlW1VWT0ZMRTxRQTVTQzdURT5QQTtKPDRKPDRFNy1IOjBKOzpJOjlUSUhf VVRcUVBVSklNQz1MQTxQRkVWTEpYSj1RRDdOQDBRRDNVSEZbTkxTTENJQzpJNyZALh49KBdBLBtB LRVFMBdNNzVaQ0FbR0VUQD5TPTlYQz5eTE5iT1FjTkBhTD5YRz5XRj1XRj9TQTtXOjBUNy1JOStA MCNAMilJOzFQPjVVQzpXRj1XRj1VRUZcTE1jVlRnWldiVk5eU0pbUE9TSEdUQT9RPz1OQ0ZXTE9j U1RiUVNhSkZdR0NdSUxeSk1dR0FXQTxTQzdQQDRVQDVPOzBINzlXRUdoT1VoT1VWRz9PQDlPPjlO PThNPjhXSEFcTUNbTEFdRz9RPDRKOi5OPTFPPjhWRT5iSkxlTk9dSUxdSUxVPzhNODBMQzlWTUNl VkxqW1BpVk1jUEdaR0FUQTxcSEheSkpYRTFbRzNYQTVXQDRROjBQOS9QPzFTQTNQPzBQPzBNQzRJ PzFJPy5FOypKPTBOQDNPPzNMPDBPPCdPPCdPQTFTRTRYRkBWRD5QPzlJOTJNNy1OOC5MOjFWRDth T0ZcSkFRRDRTRTVUREBXR0RVTkVUTURWRz9YSUFXTUdbUEpaUUxcVE5dVU9eVlBeV05RSkFUQzpU QzpcSkFfTkVcSjtWRTVPQC5KPCpMOiFJOB9MPTNURTtWRDtVQzpRQzlcTUNeVlNcVFBRRDdHOi1Q RERXSkpbR0VaRkRTRDpQQThOQztOQztUR0NXSkZUSEBWSkNbTEFdTkRXTERbT0dXSD5MPTNRQzBW RzRIQTVDPDBDOStJPzFTRz9aTkZcTUVURT1WRkNQQD1ORD5USURXTERaTkZbTkxeUU9XUFBbVFRV SUFJPjdJOzNHOTFNPD9dTE9jV1tjV1teWl1kX2NoXmRiWF5cT0pUR0NYTU5hVVZhU1dhU1deU0lb T0ZbUE9YTk1TSklTSklPRDxPRDxMQzdGPTFIOTpHODlNSj5YVkliXFVhW1RaSD9OPTRPPTRTQDhT RDpQQThPQDdNPjRKPDJJOzFEOixDOStMPDlaSUZUTU9PSEpKQz1FPThMPDlPPzxVRD1UQzxRRjtb T0RWUExUTklWRz9QQTpQOipDLR47JRk8JhpJNSBUPylWRTxYRz5cSUBUQTlQQDRTQzddTFFkU1hf U05bTklPR0FPR0FaSkRaSkRbRDdUPTBMPCpENCNFOy1MQTNYQztcRj5YSUFVRj5RR0ZVSkliUU5l VVFnVlNjU09dU01VSkVTRkFTRkFVR0lbTU9hVFRiVVVlUEVlUEVdTkdbTEVaRz5RPzdUPzRXQzha QzlTPDJQNzdaPz9hR0NfRkFaRDxVPzhQPT1KODhFNy1KPDJYSj1YSj1bQzxYQDpQOi1VPjFUQT9a R0VfTkdiUElfTUZhTkdWQTNPOy1QRD9bTklqU1RrVFVlTExfRkZYRUVXREReTEZfTUdhT0ZhT0ZX UU9XUU9VQz1MOjRKOixKOixTPjNWQTdRRzlUSTtMRTlGPzNHOipJPCxPPzFPPzFPQTFNPy9RRDNW SDhVSERUR0NRQztKPDRKNSJKNSJMOTlWQ0NfT0xlVVFdTEVaSEFTQDtUQTxUSURUSURVSUFTRz9K SUFOTUVVTE1WTU5eTk1fT05dUUhVSUBTRDxRQztXRjpfTkFiUEBhTz9cTUNXSD5URjlNPzJPRTNY TjxfUEZVRjxURTtXSD5dU01fVU9XSTxENypJOzRVRj9fTj9jUUNeU0laTkVRRj5IPTVHPzBTSjtd UUhbT0ZiU0piU0phUUpeT0hXTzxRSTdTRzxbT0RQTTxFQTFEOS5IPTJXT0lcVE5bTkxUR0VURT1M PTVPPzxTQz9RR0RYTkpYU1BeWFZdWFdXU1FVRD1MOzRKQTVEOy9KPzdTRz5WTU5cU1RdU1tlW2Nn VVthT1VTQTtQPzlWSE9dT1ZcU1RbUVNaUEdWTURQSkhWUE5WSkNQRT1ORD5NQz1JQzpBOzI/NC1B Ny9NSUZdWlZnX19dVlZWRT5PPjhQRT1VSUFRSD5NRDpOQzpOQzpRPTBKNypBNR9GOiNKOjRUQz1R RUVRRUVKQDtANzFINzBOPDVWRkNbSkdfT0xnVlNfU1BXSkhaRThWQTRRPSdKNyFBMh1GNyFUQzdY RztYSUFURT1VRjxURTtPPjBWRTdeTVBoVlpiUU5iUU5USUZTSEVXSkhYTElbST1PPjJJOSpKOitU RT1VRj5WRDtbSD9XSEFWR0BURkhWSEpcTlBdT1FjU1RkVFVfTU1WRERWRz9aSkNbSEpeTE5dUFBh VFRjUUpeTUZdTUlYSEVYSDxVRTldRjxjTEFfSDVbRDFWQTdUPzRbRT9dR0FfRkFYPztNMipNMipM OjFQPjVWRTdYRzlXQDRaQzddQDtdQDteRkNkTEhjT09lUVFjTkBhTD5VRTBTQy5bSEZcSUdhTUpi TkxdSUdXREFWPz5cRURdSkFiT0ZhTk5eTExUVFRUVFRYRkRRPz1KOixOPS9RPi1WQzFbSUBbSUBa ST1OPjJJOylHOSdMOy1TQTNWRjhVRTdXRj1XRj1bSURaSENXRzlQQDJPOyFOOiBNODBUPjdbTkll WFRqVE9kTklXRT9UQTxVQ0BTQD5PQURPQURIQ0BJREFRRkdUSEleSERcRkFYR0BWRT5RQztTRDxU RDhXRzteTUZiUElhVUxdUUhdTkZWRz9VSjxhVkdnVklcTD9RSDxTST1hTkdkUUpdSDpKNylJNDBb RUBdUExkV1NjXF5cVVdTSEdDOThAOShPRzVkU0llVEpnVlNqWlZoVFRiTk5bTklcT0pbT1BfVFVd V0dPSTpHPzBNRTVWTU5cU1RYSUNQQTtNOzRQPjhNPjROPzVKP0BUSEldVlhfWFteVlNXT0xRQTNK Oy1FPS5FPS5GPjlIQDtPR0ZXT05bUE9jWFddU09TSEVIPzVJQDdbSU1nVVhdUVNbT1BUR0dUR0dW SUlbTk5XSD5OPzVRQTVPPzNGQTc+Oi9BNChIOy5RSkpeV1dnX2JbVFZTRThQQzVaTk9bT1BWREFT QD5VPzhWQDlVQS5JNyRJNCFPOiZQPDFUPzRRQDhQPzdNPjdJOzNPODRROjdaSExjUVVkUFVjT1Re SERaRD9WRD1VQzxQQCxNPSlJPy5NQzFbSURbSURaSkRXSEFVST5TRzxOPjJXRztkU1ZpV1trVFNl Tk1RQztRQztUSUhRR0ZTQTtNPDVNOzJRPzdaRkRXREFURDhWRjpUR0NUR0NWRUhcSk5eSk9iTlNk U1ZlVFdeT0hURT5YRkBcSURbSEheTExiUFRiUFRhVU1cUEhjUElfTUZYRj9dSkRpUEZnTkRdTTpY SDVXRj1YRz5aSENcSkVdSUdaRkROOixJNShTOzRWPjhYQTVbRDhXQDRcRTllRz9kRj5jTUVnUEhp VVdlUVRjTUdfSURYQztXQTpXRUVcSUleTUdaSENcQz5YPztVPztcRkFlTExpT09WTEZaT0leUFNd T1FcSUNXRT5VPy9RPCxNPC1VRDReTUdeTUdeTUZTQTtJPCxGOSlMPC1URDRVRjxVRjxXSD5VRjxW SkNYTUVYSj1XSTxaRC9VPytRQzBRQzBaSkNhUUltUVFuU1NkTEhiSUZXRj1VRDtQQD9NPTxJPjdN QTpTQD5WREFVSjxVSjxXTTxXTTxfSDxcRTlWRDtWRDtaR0BeTEVlVVFiUU5dVVFYUE1YT0VhV01t W1RhT0hWRz9RQztjUElkUUpYSTdHOSdJNSlbRjlcUU5jWFVkXGNaUVhQRTxBNy5IOCpaSDpjW1pq YmFpW11rXV9jVlRfU1BaTk9bT1BfTlZoVl5jWFVWTEhTQTtYR0BfUVRhU1VWRz9IOjJIOSpPPzBJ OzFMPTNNQz1ORD5aUFFbUVNbT0dUSEBNPjRNPjRHPDFEOS5AOjBGPzVVQT9dSUdhVlBjWFNYT0VN RDpDQDRIRjpYUVFeV1deTk1bSklRPkNWQ0dhSkZfSUVYRzhUQzNXRjdWRTVRRDNMPi5EMytKOjFP TUxXVVRfWlVUTklOQDNURjlcUFFaTk9bSENWRD5XQTpbRT1TQzNNPS5QQDFRQTJQPzdPPjVOPTFN PDBNPC1NPC1JNC1QOzNeSkpiTk5dRkdbREVVPzpVPzpVPztXQT1XRzlRQTNTQzRWRjhdTkddTkdd TUlaSUZcTUZcTUZXR0RYSEVoVlxpV11vVVVlTExQQzVOQDNPRj1KQTlOPDVNOzRPPjJUQzdbRT9b RT9RRj1QRTxQREFUR0VYTExaTU1fTU9lU1VkVlhhU1VhUE1YSEVfRkhjSUxdSkpfTU1dU1FfVVRh WFVdVVFbU0NYUEBcSURdSkVrUU5oTkpaSTtWRjhWRz9bTEReTEVfTUZbT0dXTERWQTdUPzRRPTJU PzRbRDhcRTlYRj1eTENkTkhkTkhiVk5hVU1iVVNfU1BcTE1aSUpVRDtUQzpQRkBPRT9XSEBWRz9T QzRPPzFQPjlVQz1iSE1kSk9bTEFhUUdfUEldTkdcTkBbTT9XSTpNPzBQQC5VRTJbSEZcSUdbTEVT RD1PPjJNPDBPQy9VSDRVST5XTEBUTUBRSj5YRj1cSUBeTUdeTUddSDtWQTRRRzVRRzVXSEBaSkNe TExfTU1iT1FiT1FbTklQRD9MQDlJPjdIPjBHPS9KPTBXSTxWUURWUURbT0RcUEVdTkdbTEVaSkBW Rz1aSEFcSkRfU05iVVBjVlRbTkxTSUBeVUxnVlVhUE9WRz9WRz9bT0daTkZWRjhIOStBOSFTSTBe UFNjVVdeW1dQTUlPQy9IPClFPDJdVElnV2FwYWprXWJoWl5iVVNcT01fT05cTEpfUVRoWlxdVlZT TExPRT9QRkBYTk1eVFNYTURMQDhHOi1OQDNOPDdNOzVBPzRIRjtTTEFXUEZdTT5XRzlTQDhOPDNE PTQ9Ny4+OytHRDNVRUZaSUpbVFZbVFZVTEFGPTNKRUBTTUhWT09WT09VSERQRD9MQUBTSEdcSkRa SEFURTtWRz1jUEpfTUdbTT1PQTJENCdJOixNSUFXVExdTkRURTtIQDFRSTpcVVVXUFBRQzlQQThR RzlWTD1QSDlQSDlVRjxXSD5YST9URTtPOy5OOi1NPS5NPS5JOzNQQTpYSUFVRj5PPzFQQDJKQThK QThVQzpaRz5YSDxYSDxdTENfTkVkU0xkU0xeU0paTkZaSUpXR0hYRkRaR0VhTVRlUVhjUVVfTlFX RzhQQDFRQDJOPS9POS9QOjBQPzNUQzdTQTNUQzROQzpQRTxWSUVXSkZXTUxXTUxeUVFiVVVjVVdi VFZdUFBaTU1iSkxlTk9hTU9lUVRbUVNjWltqWlZkVFBdTDxbSTpeSERjTUhpT1FlTE5WRjhVRTdb TEFcTUNeTENeTENcSkFdTENdRjpWPzNVPjFXQDNkTEFqUUdlU01jUEpnU1NjT09eVFNdU1FcT0pY TEdbSklXR0ZTRDxOPzhUQTlWRDtlST5oTEBlSjdbQC1OOSlUPi5bQUZiSE1eU0peU0pdU0RiV0hl WlBfVEpYUEpQSENPPzBQQDFbSEpdSk1dTkZVRj5QQD1MPDlTRTRVRzdXU0hYVElUTUBPSDxTRTVW SDlbU01cVE5cTUNURTtQRTxTRz5PRT9PRT9VRUFaSUZaTFBbTVFXT0xQSEVJQTxEPDdJOzFJOzFM PztUR0NaT0xaT0xVT0pVT0pYTEddUExcUEVXTEBcSj5cSj5YTkhbUEphT0ldTEZVSUFfVExkWlRe VE5XRj1eTURPSUdKRUNHPjVFPDNHPy5YUD5nVlVpWFdjWFNdU01ORTtHPjRHPD1fVFVuXF9zYWRy ZGRpXFxeWltTTk9VSkleVFNeVVZhV1hbTkxRRUNJQTBIQC9TSUBYT0ZYTEdQRD9OPTdUQzxVQUFK ODhBOy9NRjpbU0NcVERfTkVXRj1NPDdJOTNAOC48Myo/NTJMQT5UTU9YUVRbVldVUFFFREA9PDlK RERTTExXTUdUSURQRz1NRDpNQz9RR0RVTENQRz5PRUFVSkdiVVVhVFRaVERKRTVDOyxEPC1WRz1e T0VbTEFTRDpJQDRWTUBcW1dTUU5RRjtRRjtWTD1aT0BaUT9aUT9eUU1fU05XUEdRSkFPPzFNPS9Q PzdQPzdNQD5OQT9RRzlORDVNPihOPylNQzRRRzlYST9cTUNdTENfTkVkUU9nVFFlVVFnVlNdV1BW UElYSEdVRURXSEBWRz9eR0phSU1cSE9dSVBcSjtcSjtfSURVPzpTPjFPOy5OPDNRPzdXRTxVQzpQ RTpRRjtbSEhdSkpYTk1aT05hVFFiVVNiVFhjVVpdUE5bTkxfTUphTkxbSklcTEpjUVppV19rVFNo UE9hSD5lTUNqU1FqU1FlTlFdRklWQTdWQTdaRz5eTENeTUdaSENeTT1bSTpRRDNMPi5TPjBeSTtr VkpuWE1pUVBoUE9iT0laR0FdSkheTElfUEldTkdaSENVRD5TQTVPPjJdRD1fRj9rT0RyVUlqTkFf RDhVQDVXQzheSERhSkZiTlNlUVZhVE9oW1ZpYVtlXVdfWFhUTU1KQzNNRTVeTE5hTlBdUUhWSkFT QTtRQDpRRDNURjVTTk1XU1FbT0ZUSD9NRjBORzFRTEdbVVBeVUpVTEFQQTtURT5ORTxPRj1URjlV RzpXRkBbSURVTkVRSkFMQzdHPjJFOjFIPTRPRDxVSUFcTEhaSUZRTUNTTkRUSTtaT0BcUEVcUEVa TkVYTURaTUhaTUheTUdaSENRSElbUVNhWFVdVVFjUEljUElPRkdHPj9OPzhMPTVMQzldVEloXF1k WFphVUxeU0lPSD5DPDJKOzxdTU5lXVpqYl5nX19jXFxYWFtNTU9OR0lcVVdfVlpcU1ZVRTlMPDBD PCVIQSpTRz5VSUBUSEBQRT1RQDtQPzpQPkBNOz1DOi5RSDxYVElfW1BhVU1aTkZGPzNBOy8/MiRE NyhFOzhJPzxVUFFaVVZXU1RKRkdDPzhAPTVQRD9WSUVXSEFVRj9OSERMRkFKQz1RSURYTUVVSUFR R0FaT0lhV1hiWFpaWFBKSUFJPy9IPi5WRD1hTkdfTUpcSUdPSUVYU05eXVVRUEhQST1PSDxbUD9c UUBeU0leU0lfU1BcT01aSkNWRz9QQzNURjdUQz1VRD5RQT5TQz9TRDpURTtTQzNRQTJWR0BbTEVe TVBeTVBhUE9lVVRqWltqWltpXV5lWltfWldYU1BaR0VWREFeTEVaR0BaQ0FaQ0FdRklfSExhT0hi UElcTUVVRj5NPTFIOS1KPTBRRDdYRz5bSUBXTERYTUVbSEhdSkpbSkdbSkdbTk5cT09hT1NhT1Ni UU5bSkddSk1aR0lUR0VUR0VlVFprWl9oU1dnUVZnTUhqUExqV1doVVVfSElbREVWQDtUPjlVQ0Bb SEZfTUdbSENYSUFURT1TRDpRQzlVRDtiUEdqWk1qWk1lT0pkTklcTUVURT1cSUxdSk1kUU9hTkxf Sj9aRTpRPi1UQC9cSURlU01uVldrVFVhT0NaSDxTQTlVRDtbSENcSURjUFNoVVdoV1hrW1xnWF1l V1xfUVRVR0lNRC9RSDNdTEZhT0leTURaSD9VRDtTQTlRRDRURjdQTkNWVEhdTkRYST9QSDdNRTNR Rj5dUUljUUpcSkRKPjxMPz1MQzdPRjpRSDxRSDxYSDxbSj5WT0ZRSkFNRUFJQT5FPChGPSlNRDpR SD5YT0VWTUNPRjpORTlPRjxTST9dT0FfUURXUEdTTENbSURcSkVYSEdVRURQSEVVTUlcVE5dVU9h UE9hUE9TRkZNQEBRPDhRPDhUSUheVFNpXFxjVlZbSUNcSkRWTURNRDtMPD1XR0hdV1VqZGJlZGFf XltdXldPUElPRkdaUFFlVVRfT05QRy5IPydGOiVOQSxURjlQQzVVRD1TQTtXQTpRPDROPzhJOzNF PzlOSEFcVlRnYV5iXV5aVVZJQDhFPDNGMihINCpJOzROPzlTR0pYTVBTSUpKQUNHPThFOzVPPjhV RD1WQ0BaRkRUR0dQRERPRT9VSkVcTEpdTUxWTkpaUU5kWlZnXFhnVlNXR0ROPzVVRjxeTUZiUElk UE5jT01WTU5bUVNhW1hXUU9XSD5cTUNdVElfVkxeU0pcUEheT0hhUUpeTT1XRjdaRThdSDtbRjtW QTdQPzlVRD1USD9TRz5TRDpVRjxbSEhcSUleTk9iUVNhVFRnWlpqXGNuX2drXWRpW2JlW1pdU1FY Rz5eTURhTkdcSUNaRTpUPzRYRDdiTT9nVE1lU0xiT0haR0BPOy5NOSxOPjBWRjhfTkFjUUVdSkFa Rz5YRUNbR0VbR0VbR0VaSExYR0peSk9bR0xbSEZXRUNbSENXRT9QQTpYSUFeUVFhVFRjU1RhUFFd TEZkU01tXFhlVVFhR0daQEBQPjlWRD5WSUVdUExoUUlkTkZVRURURENXRUNWREFaR0FkUUxiV0Ze VENhTkVdSkFTQzdPPzNUQEBbR0djUUpfTkdfRz1aQThVPjFUPTBYSEliUVNoVVVkUVFdRzJWQCxP PTRTQDhcRURfSEdkUFdjT1ZlWFhpXFxqWFxqWFxnVFFYRkRTQTJTQTJbSUReTUdkU0RiUEFdTENX Rj1UPjdXQTpTTEFUTUNeT0VaSkBTRTVPQTJWRTxeTURlU01fTUdPQDlKPDRKQzNORjdRSDxVTD9Y Tj1YTj1YUUhXUEdVTEFORTtJPCxMPi5USD9XTENaTkVaTkVMRjdKRTVOQzpPRDtbSkdbSkdbTkxY TElWSUVVSERVQ0BTQD5QRkBXTUdbWlRbWlReUU9eUU9RQzxOPzlNPjdKPDRTSklfV1ZfWlNcVk9W TkpXT0xbTklXSkZQRkNVSkdhVVtpXWNlX1tjXVhcXVhTVE9ORkVUTEplTk1hSUhVSjlTSDdWSTVU RzNbSj5WRjpYRztbST1cRTtUPTNMOjhNOzlNQUVQRUhiWFxpX2NkYmVdW15PQ0BMPz1NOilINSVN PDdRQDtUSExWSk5VSEZOQT9HPjVFPDNQQTpTRDxYR0FcSkVVRj9VRj9RREZWSEpaTk9dUVNaU1Nb VFRhW1RiXFVqWFNXRkBQRkNaT0xrWFNrWFNtWFZpVVNcVFNbU1FcVk9bVU5cTD9jU0ZiW1BfWE5b UUhbUUhnU1BoVFFiUUVeTkFqT09pTk5eST5VQDVTQDhWRDtXTkVWTURUR0NWSUVXSkpUR0dWSUdd UE5iVlpoXF9rX2VqXmRnU1xpVV5kVFBjU09iU0xhUUpnVE1hTkddSD1WQTddSkRpVk9tXFtnVlVq TUpdQD5POSVMNSJaR0dnVFRqV1BkUUpcSjxXRjhWQ0BWQ0BbRUBdR0NaRkhaRkhcSE1dSU5hTU1a RkZUQzxVRD1WRD5YRkBaTU1fU1NeUVFbTk5aTk9lWltrW1xiUVNeQTxaPThWREFaR0VeUU9pXFpp W01fUURVRjxTRDpVRURTQ0FYRkRiT01hUERYSDxaSDxXRjpPPjBPPjBUQTxeTEZnVE1eTEVYQzJU Pi5QOjBUPTNXRkleTVBkUU9hTkxfQStWOSNPOjJUPjddQD1iRUFfSlFkT1ZnU1VpVVdqWFxqWFxl VE1cSkRWRjhPPzFPR0RWTkpjVExnV09hVFFWSUdXPjhWPTdQQDFVRTVbT0ZbT0ZYSj1TRThPRjxW TUNiT0hkUUpXQzhPOzBQPDFWQTdVSUFbT0dbUUdbUUdbU09aUU5cTEhaSUZTQzNPPzBRRj1XTENY TUVaTkZOQzpHPDNPPjhQPzlXRkBbSURcSURcSURYQz1YQz1TRD1QQTtWREFeTEleV1peV1piVldb T1BVRj9JOzRGOzBIPTJVRj9hUUpfXFZeW1VcUU5dU09hU1VdT1FOSEFPSUNiT1pqV2JlWl1kWFxh WlpWT09NQz9USUZdTUxdTUxcUEhdUUljWlBeVUxfTkhaSENeT0dlVk5eU0lTRz5NPjdNPjdJQERK QUVfWFtlXmFdW1pbWFdPQDdIOjBMOyxPPi9TQTVRQDRUR0dTRkZORkNKQz9JPjVFOjFRQTVWRjpW SkFXTENTSUBUSkFQRUZUSElWSE9bTVRcUU5hVlNlW1dkWlZdUUlQRT1USUZdU09pWFVrW1dpWlNj VE1bUE1eVFBdVU9dVU9WTURlXFNpYV1kXFhaT0xYTkpnWlplWFhkV1VkV1VqVltkUFVdRD1VPDVT RDxYSUFeT0hbTEVYSUNXSEFWRz9TRDxVRURaSUhlV1xpW19oXmJhV1tbT1BcUFFhUE9hUE9eUU9j VlRpXFxeUVFYR0FVRD5cT01oW1hqXF5iVFZdRz9RPDRMPjFXSTxkWFpoXF1kV1NXSkZaTD5WSDtP PTdQPjhURTtVRjxVQUFVQUFVREdYR0paR0lTQENRPzdTQDhWQDtUPjlVSEhhVFRfTUpeTElnUVZq VVpjUEpTQDtYQDpbQzxeSkhoVFFnVVttW2FpWlNeT0hYQTRXQDNQPzpQPzpWRERdSkpdTkRXSD5a SDxVRDhPPzFPPzFWRD5dSkVfUEldTkdRQTJQQDFTPjFVQDNbSEpkUVRkUUpdSkRfRTFWPClTOjNa QDpbQz5eRkFcTEpdTUxfTU9jUFNjV1hlWltjVlFbTklbSjxXRzlbRT1eSEBiUU5nVlNlVVFbSkdY QztVPzhKPS5OQDFUTURYUUhdTkRcTUNYR0BaSEFTTENYUUhVSjxPRTdOPTdTQTtYSEdfT05cUEha TkZXTE1VSUpcSUddSkhaSDxRQDRRSTpTSjtQST9TTEFOQzpHPDNOPjJVRTlXRkBbSURcSkVcSkVb TEFVRjxQRztMQzdQQ0VYSk1eV1paU1VbUE9TSEdTRz5MQDhPPjJJOS1RQDhXRj1cVVVbVFRYTkpc UU5fVFViVldQSUBNRj1aTk9kWFpkW15lXF9hV1tQR0pIQD9KQ0FcTEpjU1FiVVBjVlFiWFpbUVNa R0VdSkhdV1NfWlVeWFRaVE9PRjpGPTFBOjRHPzpcTlBrXV9jW1pVTUxRRTFNQC1RQzBRQzBVRTlO PjJNQDxOQT1NRDtKQTlHPS9FOy1QQDRaST1dTkRcTUNUSkFRSD9MRkRKRUNPRURQRkVRT05bWFdl XF1fVldaSkNTRDxbTkxnWldlVVRiUVBVUEZVUEZaT0xcUU5cVFNfV1ZhV1toXmJoYWFjXFxcT09Y TExjV1hjV1hjXFxkXV1kW15dVFdYR0BYR0BeTk9kVFVhUE1aSUZPSD9TTENRRj5OQztRQUBaSUhl VFpwXmRpXFddUExbSURcSkVaTU1cT09jUVdoVlxlXF9bUVVaR0VWREFXTE1jV1hjWlBeVUxYSDpR QTNTSEdfVVRpXV5hVVZfT05XR0ZXSD5QQThKOjFNPDNQOzNTPTVWOjhWOjhTPz9TPz9WPjpTOzdR Oy9POS1WPTdaQDpaRkReSkheTURhT0ZlTk9jTE1hRTxdQTldR0NiTEdhU1VoWlxrV1xzXmNrWlRi UEpeRjlaQTRQPjhVQzxYRUVcSEhbTT9aTD5aSjVXSDNURC9QQCxYQz5cRkFYTUVXTERXRjdVRDRX QzVbRjleUVFjVlZcUEVWSj9eRzpaQzVVPjRROzFOQT9VSEZbSEZcSUdaTkVdUUhbV1ReW1dkWlRc UUxXTENUSD9aRTpfSj9jUEpoVU9nVU9fTkhcSkVVRD5TQDhTQDhXR0RcTEhbTklcT0pdTENdTENU TUNaU0hQTD5OSTxOQzpPRDtWRUpcSlBeUU1cT0pUTURPSD9XRj9cSkRXRTxTQDhWSDlbTT1YSUFa SkNUSTtNQzRTQDhVQzpVRj5aSkNVTUlVTUlWSUVVSERRSTpNRTVQREFbTkxaVVRRTUxPR0FMRD5T RDpRQzlRQTVKOy9PPzNQQDRWT0ZXUEdUTkdaVE1cVVdiW11aT0lQRkBaTlFoXF9nX19oYWFYV1FI R0FBOjRFPThRSURbU01nVlNoV1RdVVRaUVBYRkRcSUdaVVRcV1ZdWFdYVFNURT1MPTVBOy9DPDBY TUVpXVVqWlZdTUlQRDBQRDBRRzdUSTlURjdMPi9MPTdNPjhRQzlNPjRGPSdEOyVVRj5eT0dYUEpX T0lUTklQSkZMRD5JQTxHPzpNRT9PTU5VU1RhWlpbVFRTRkFNQDxVSkldU1FdUUlYTUVVTD9YT0NU T0VXU0hYUVRcVVdiW11pYmRrXlxnWldXTkRUSkBcT01hVFFkXFtlXVxiXV5bVlddTE9fTlFjV11h VVteT0dRQztRRj1WSkFTRDxQQTpVRUZeTk9lWl1rX2NlW1pXTUxQQTpURT1aSExbSU1aU1dhWl5p W2JeUFdWREFUQT9XSkhjVlReV05XUEdORztMRTlPSEhXUFBkVFVeTk9YSEVTQz9TQTVNPDBOPS9O PS9POCtTOy5VOStWOixQOzNTPTVTPjFPOy5UPTBUPTBaQT1dRUBWSkFXTENbSUNaSEFaR0BbSEFe RT5jSUNeTkpiUU5jV1hkWFpoW1tpXFxrW1djU09iSjhYQS9OQDNMPjFTRkFVSERWT0NWT0NcTj1W SDhQPShRPilYQzteSEBbSUNbSUNdRjxeRz1aRD5dR0FaT0lcUUxXRztVRTlaRTpVQDVTQTNQPzFR RUNbTkxWRkNYSEVYTUFbT0RdVk1hWlBlWFZeUU9YT0VTST9XRjhdTD1YTUVeU0pjU09iUU5dTkdV Rj9XQTxXQTxVQ0NXRUVbR0xcSE1jTUdnUEpcVUpaU0hUTjxRTDpOSDlNRzhVRURXR0ZYTUVaTkZV SjpRRzdTRz5WSkFWRT9VRD5aRz5iT0ZcUEhXTERQSUBKRDtQQTtOPzlWREFhTkxdV1NaVE9WSUdW SUdVRzpTRThUSkFcU0laU1NOR0dPRjxORTtPRDxTRz9YTURWSkFURjdVRzhVTEFYT0VRTkpXVFBa V1heXF1YVVFQTUlaTU1qXV1iXmRhXWNYVElFQDc4Mx09OSJQSkZcVlFjWFViV1RXT0xbU09YSEVe TkphVlVfVVRdU09TSEVOQDFOQDE6OSVAPytTTUZiXFVkW1BWTUNMPi9RRDRVRzpWSDtQRC5QRC5I OStIOStMPTNKPDJFPS5JQTJVTE1dVFVbVldXU1RRSExPRklPRURMQUBGQUNMR0hQTE1aVVZbVlVV UE9TRThPQTRTSEVcUU5WTU5TSUpUSkFUSkFVTENbUUhWT1RcVVpfW1poY2JnXFZhVlBWTUBRSDxV SUBfVEpnW15kWFxkWlhdU1FbTU9jVVdnW1xhVVZcSDdWQzFTRTVXSTpRSTdORjNUREVfT1BlXF9p X2NkU01UQz1KPzhMQDlRQUNcTE1bVlVfW1poV1hhUFFURT1RQztPR0ZVTUxVTUdWTkhUSEBQRT1V SEhXSkpbSEZVQ0BPPjhMOzRMOy1MOy1QOi5ROy9TOy5WPjFYQyxYQyxPPzFRQTNUPzRTPjNaQThc RDpfRkFfRkFaST1bSj5YRz5WRTxWRDtdSkFjUEpkUUxhVFFfU1BiVVNfU1BfVVFkWlZpXFpkV1Ve TT5RQDJJOihOPixURT1XSEBXUEdbVEpjUUNbSTtWQTNWQTNeRkFhSERdSkhdSkhdSkFaRz5VRD1b SUNbSUBcSkFYRTFYRTFaRTpVQDVTQzNURDRWSUdfU1BQPjVTQDhYSUFbTERdUExiVVBjV1hcUFFX SkZTRkFURT1WRz9RTEVYU0xhVlBeVE5dSkpWRERTQTxQPzpRQzxURT5aRThbRjlfSUFhSkNbUUdd VElcU0haUEZQRz5NRDtWSkNaTkZcT0pbTklXSEBWRz9TSkVTSkVXTERXTERhT0loVlBfVkxcU0hT UEVNSj9PRzhQSDlXRjpiUERkWlRlW1VaUU5RSUZTQDpaR0BWUE5cVlReV1dUTU1RSkBPSD5YT0Zb UUhkVkheUENfSj1dSDtVTENYT0ZUT05aVVRaV1tbWFxQUE5OTkxbSU1hT1NeW2FfXGJYTVBHPD89 MyM8MiJNR0NdV1NlWFhkV1dfV1RdVVFiTlNiTlNoVVVjUFBaSD9RQDhQQzVKPTA/PitDQS5NTkdd XldfWE9QSUBNPjhVRj9eTUZdTEVaSkBRQzlJPC9HOi1JOzRJOzRIPj1QRkVTUFFXVVZcVVdRSk1T QTlRQDhNQD5NQD5KQDtORD5RR0ZbUE9aU1dRSk9QQzVQQzVTSjtWTj5YTkhWTEZQSENPR0FVSkVV SkVRTElWUE5dVlhoYWNoXF1fVFVPSDxORztUTklhW1ZqX15oXVxiUU5RQT5UREVeTk9fVVReVFNb STtdTD1fVEpfVEpVSjpQRjVWREFdSkhbVFReV1daSkROPzlMOjhOPDpRQEZeTVNeVlVhWFdoVVVh Tk5aRz5XRTxQRz1TST9aTUhXSkZUQz1UQz1aSkRaSkRdTUBYSDxNOSxMOCtFNCdGNShROjBXPzVc RURhSUhbTERaSkNQRjhQRjhYQztaRDxeRkFkTEdtUVFpTk5fTUZbSEFbQzxYQDpbREVhSUplU1Bl U1BjU1FjU1FdUExcT0piT01kUU9tW15pV1thT0NRQDRMNSJPOSVRQDpcSkRjV1hpXV5pVVNcSEZf Sj1hTD5eTEZcSURYTEdXSkZcSUBWRDtRRj1USD9bSD9dSkFaSTRXRzJcRj5aRDxRQDhWRTxaR0Vh TkxPOy5VQDNYSEVaSUZcUVBfVVRfVldaUFFYTElQREFOPjBOPjBRSD9YT0ZhT0hiUEleTUZYR0BV RjxOPzVOPi9PPzBWQTNbRjhcTUVaSkNaSkRfUEliUElfTkdPRDxOQztUTUNWT0VfT0xdTUlVRj5U RT1VTENVTENVTENcU0llVVFnVlNiWlhdVVRVTkVPSD9MRjRNRzVTSjtdVUVfXVFiX1RWVFNKSEdR PzpYRkBaTk9hVVZkWFpXTE1VST5YTUFdV1NdV1NeV01dVkxiVk1dUUhVTUdVTUdUTkxcVlRbVldY VFVTU1BJSUdQRUZcUFFiVlpeU1ZaSUhHODc/Lyc/LydJQEFXTk9tWlNtWlNnXV5iWFpcUFReU1Ze U0laTkVbSjVXRzJVRTdNPS89OCdGQC9UTEhcVFBcUEdVSUBTQDtWRD5hT0hiUElaUEZTST9MPypG OiVANylGPC5GPT5NREVcUFReU1ZYVElJRTtMPyhMPyhJPjdKPzhNPjhPQDpQSENaUUxbU09TSkdR QzlURTtUSkBYT0VbVEpYUUhaTkZXTERRSURNRT9OR0dVTk5dVFdlXF9nVVtfTlRMRD5PR0FXUFBf WFhjW1pjW1pfT1BUREVTR0haTk9iVVNhVFFkTklpU05jW1djW1dbUUhORTxUQzxaSEFbT1BbT1BU R0NQRD9UQzpXRj1dTUxkVFNlW1piV1ZiV1ReVFBcSUdaR0VYRz5aSD9dTkdaSkRVRj9XSEFdTUBj U0ZnVUZhT0BVQS5OOyhGOCZIOihRPDdbRT9eTEleTElbSENaR0FXRj9XRj9YSUNaSkRjTlNrVlty WFVuVVFkU0xcSkRaQTtaQTtjTE1lTk9kVUpkVUphUUdeT0ViUEphT0lkUU9lU1BpV1tpV1tiT0Zc SUBWPjFROi1UQEdeSlFpV11tW2FnWlpYTExcSUNeTEVeTkpaSUZaSkRXSEFaSUZXR0RaSD9WRTxc RkFdR0NdSkFdSkFkSkReRT5TQDpYRj9dSU5jT1RPPjBUQzRWREFbSEZhUE1oV1RiV1ZdU1FVTUdO RkBPQTJMPi9RPzdaRz5bSENdSkVhUERdTUBXTERVSUFRQDFPPi9QRTpYTUFaTUhXSkZbSEZdSkhf TkhcSkVVRjxURTtWTEZeVE5eVE5YTkhURTtTRDpQRztUSj5XUUpcVk9iVVVpXFxlXF1hV1hdT0FV RzpORDNPRTROST5cV0xjX1pjX1paWFNJSENTQz9QQD1WT1FfWFtiVVNaTUpdTEZjUUxeV1piW11l W1doXVpnXV5iWFpUTkdUTkdVT01YU1BhVlVdU1FVTUdPR0FMSENUUEpTTUhTTUhaSD9PPjVENCc/ MCNBPDhTTUhpWFpwX2FnXWFdVFdVSUpVSUpcUEdYTURfTz9fTz9aSjhTRDFBOCdMQTBVTENbUUhd TT5XRzlVQzpcSUBbT0ZfVEpdTUlhUE1XRzlNPS9DNyRHOyhHPTxNQ0FbSEhdSkpYT0NKQTVHPy1F PStIOjBKPDJNPjhPQDpWRkVbSklVTUdTSkVXSEFcTUZdVEpdVEpbVFRbVFReUFNXSUxQRkBMQTxQ REFTRkRWT09eV1dhU1VbTU9NRURRSUhWT09bVFRhW1ZdV1NeUU1TRkFWRUhdTE9fVExiVk5tWFhu WlpnXV5nXV5bTEVQQTtOQzpUSD9fTUdhTkhdTkdXSEFXSkhiVVNlW1dlW1diXFdiXFdeXFtaV1Ze Tk1aSUhcSURdSkVaSkRYSUNUQz1WRT9fT0xuXVpwXlhoVlBhTz9VRDRQPSxRPi1UQz1aSENdSkVe TEZdRz9aRDxXRT5XRT5WSUdcT01nU1xrV2FuVlVuVlVoWFFfUEldSUljT09qWFxtW15oXFReU0pe TENfTURkUFBjT09iUVNlVVZkVFNiUVBhTkVdSkFaRTdRPS9PREVWSkxfVFpnW2FiUVBWRkVbSENe TEZcSkRaSEFXSEFWR0BWREFWREFhRTpdQTdcRD9aQT1eSENlT0lkTEVdRT5RQztXSEBbTU9WSEpJ Oi5OPjJTQDhYRj1iUEplVE5oWFFlVk9eUU1YTEdNQzFJPy5UPzJbRjlfRDtnSkFbTEFaSkBaT0xW TEhVQzxUQTtQRkBbUEpfU1NeUVFcT09aTU1YTUVWSkNTSDpTSDpWSUleUVFeVE5YTkhTRDpTRDpN SDtPSj1YUE1dVVFqVltyXWJrXlxpXFpqVUlkT0RVRjxRQzlRSkpaU1NdW1xhXl9dWE5WUUdRTUFN SD1RT05fXVxjW1VaUUxaUU5eVlNdVlheV1piWlhnXl1lY2RjYWJWUUdUT0VfU1NeUVFfUVRdT1FQ TEBRTUFPT0BQUEFcU0lWTURVSUBTRz5PQy9EOCVFPDJWTUNfW1xoY2RoXl9aUFFXRj1XRj1hUUpe T0hjV05iVk1eT0dTRDxFOCtKPTBTRDxdTkZcTj5XSTpYRDdYRDdXTEBhVUlhUE9iUVBUT0VFQDdH OitGOSpDPDJGPzVOQzpUSD9YSUFVRj5WSDtPQTRMOyxNPC1QRz5PRj1TSkVRSURXTkRTST9UR0Nd UExiVVNiVVNbUVVaUFRbT1BVSUpTRkZPQ0NMPztQRD9USUZbUE1dUE5bTkxQSkhOSEZTTExYUVFa V1ZYVlVkUFBfTExbTEVeT0hbU01cVE5pW11tXmFiXFpXUU9YR0FUQz1TRz5WSkFhUUpjVE1hUUpf UElnVV1tW2NrXWJvYWVtX1tpXFdjWl1dVFdhTk5hTk5bSURcSkVYTUVaTkZYRkhbSEpoWmFzZGt1 ZGVvXl9oVkdYRzlYRi5aRy9VQ0NdSkpeTEZeTEZdRT5aQTtXQD9aQ0FbR0xkUFVkU1hkU1hjUFBl U1NiVVBlWFRpVlhoVVdoXF9jV1tlU01fTUdkSkplTExnUVZoU1dnVlVpWFdnV1BfUElcSUBUQTlR Pi1QPSxUQzxdTEVpVlhoVVdlTUhhSERaR0FYRkBXRTxXRTxWQ0BWQ0BYPztbQT1nRDtoRTxkSkdk SkdfTkdfTkdeTj5VRTVVPjRWPzVfRj9YPzlOPDNPPTRRPTJWQTdYTURdUUhhVlBiV1FlVUheTkFR RzdNQzJXQzRhTD1lUEFpVEVfUEZeT0VdUUlbT0ddTEZfTkhXTUlfVVFnWFtjVVdaUUxVTUdXTUdU SURPRjxMQzlRRj5cUEhbUE1XTUlWR0BURT5NRjpQST1eTVBjUVVkWlhoXVxqYlxoX1poVU9kUUxa SkRURT5VTlBYUVRiVlpoXF9jVlFaTUhUSTtWTD1VTUlfV1RjWl1dVFdeVVhfVlpjV1hhVVZdVlZl Xl5fX11hYV5cUU5YTkpYT1BcU1RbVFRXUFBKSUFNTERTTkRWUUdYUUhXUEdaSUhdTUxVTD9DOi5F OCtYSj1YXVxfZGNqX15XTUxRRDNVRzdeVE5eVE5jVVdfUVReTElTQD5FOClJPC1TRDxeT0deTEVa R0BXRjhXRjhcUUNpXk9kWlRhVlBVTkFKRDhKPTBKPTBJPC9KPTBQQzNaTDxeTUZfTkddTEVUQzxM QzdNRDhTSUBaUEdTTkNOST5USTtVSjxdTkZhUUlXVVZVU1RYTU5YTU5aTUhYTEdVSERNQDxJPTlO QT1QRD9XSkZUSURTSENKRT5MRj9PRkdQR0hXUFBdVlZfU1BaTUpWSkNTRz9QSkRVT0hkWlRlW1Vk V1NYTEdVRD1RQDpVRj5eT0deVUxfVk1fU1BfU1BoVFtvW2JyXGFwW19qVlRnU1BiVVVfU1NhT0hf TkddTUxhUE9eTk1eTk1aR0lcSUxhXF1qZWdwZGVpXV5qVUdlUENkTjtjTTpiSklhSUhjTkBjTkBe RjxbQzlaQT5cREBXSkpYTExbR0dYRUVbTkleUU1nVlVlVVRjUUxiUEpkVFNiUVBhTkVnVEpnVlNl VVFnWF1nWF1pW11nWFtlVVFbSkdUPTNQOjBUPTNbRDpYSElhUFFpVlhjUFNkTUNiSkBdRT5cRD1b RDhbRDhbQz5bQz5fQTplRz9qTUlpTEhkSkpjSUleSkhbR0VXSDNURTBVPjRWPzVVQzpUQTlTQTxR QDtRPzlTQDpUREVaSUpXT0lbU01kVkhfUURdTUBYSDxVRDtjUUhqXVhrXlplWFRhVE9dUUlfVExc T0pdUExYUE9hWFdnW15iVlpbUE1VSkdVTENRSD9RRjtOQzhNQzRXTT5cSUdcSUdaR0FUQTxRQztX SEBeTE5lU1VkWlZpXltqY2NlXl5jV09bT0dWSkNVSUFWTEpdU1FfVldkW1xcVlFaVE9VTEFPRjxP QURaTE5fVlpnXWFhWlxhWlxkVltiVFhbVlVnYmFoZ2FjYlxdVEleVUpbVVNaVFFXVE5WU01OSEFQ SkRYTExdUFBcVE5eVlBeUFNhU1VbVU5IQzxDPS5QSjtcXFxjY2NvXl1XR0ZRQzlXSD5lWl9oXGJl W1pXTUxaR0dVQ0NKOi5NPDBNPzJYSj1dSkhcSUdYTUVaTkZdVVRuZWRpYV1jW1dWT0VTTEFQRTpO QzhNPDNKOjFTRDxcTUVhVU1iVk5eVFNUSUhJRTtMRz1USUZdU09UTURQSUBNRDtPRj1dTkdjVE1U U0pRUEhQRkVPRURRSD9RSD9ORztJQzdMPTVOPzhPPzxRQT5NQTpPRDxMQDlOQztRQzxRQzxbTkxd UE5dUUlYTUVXSkZQRD9OSEFXUUpoWl5kVltkUE5YRUNPRTdRRzlaSkRiU0xeVUxeVUxdVEpeVUxl Wl9oXGJkWF5jV11jT09fTExYT0VXTkRaTkNaTkNdU09jWFVfTkhYR0FXRUVbSEhbVFZoYWNuYmVn W15pUVBqU1FpV05kU0lnT0VnT0VoUERiSj5cRDdbQzVaRD5YQz1TRD1URT5VRDhWRTlYTEleUU9o VFRlUVFeTElhTkxkUVFkUVFkUU9qV1VrW1dtXFhuW11nVFZlWFhkV1djVlFaTUhcQDhbPzdcRD9h SERdSUlhTU1kVFBiUU5nUEhlT0dlTUhiSUViRjtlST5fRz1iST9hSD5jSkBfR0NkTEdiSUZhSEVd SUldSUlcTj1XSTlTOzhWPjtXQTpdRz9OPi9QQDFNPjRKPDJMPD1PP0BVREdcSk5jUEpiT0ldTENd TENbTEVpWlNvYmJwY2NqXV1iVVVaTUpbTkxiUVNfT1BdT1RjVVplWl1hVVhfTU1bSEhVRUFUREBR Rj5MQDlMPTNURTtcTE1dTU5aSD9XRj1VRD1XRj9cTEpfT05eT0hoWFFrXWJuX2ReWFRQSkZTRDxU RT1TRz5YTURaTk9jV1hfWldaVFFTTkNKRjtFPz1QSkhbVFZnX2JjW2JjW2JjVVddT1FaU1NlXl5p YmJiW1tbT0dcUEhdU1FcUVBVTk5WT09PQ0BRRUNUQ0ZXRklTTlFXU1ZdVlZdVlZhXVVTT0dNSUFT T0dfXV5kYmNpXFdUR0NRREheUFVuX2RpW19dUUlXTERcSkVWRT9NPDNOPTRTSUBWTURWT0VUTUNX TE1cUFFkWmJqX2hvYWNtXmFcV0lVUENYST9VRjxPPTROPDNRSDxbUUVkXFhnXltfV1FRSURNRDhQ RztYUE1cVFBUTDxMRDRFPjRKRDpWTEZeVE5aVE9RTEdMRj9HQTtKQDtQRkBQRz5JQDhQQDFPPzBK OjFMOzJIOC9JOTBHPS9JPzFPPTtTQD5XR0ZbSklWSUdTRkRURENVRURRR0ZdU1FoXGJjV11eTE5Y RkhTRz5XTENaT0xfVVFdVVRcVFNbUE1cUU5hVF5iVV9eVVhbUVVdSkpYRkZUSEBWSkNaTkNaTkNf V1ZfV1ZaTUhTRkFTRD1VRj9dVFdpX2NqWFxlVFdoVVdpVlhiWlRfV1FlVE1lVE1qUU5kTEhdSDtX QzVRQDpTQTtTQTtUQzxURENaSUhaTU1dUFBkTUxjTEpeSjlcSDdhTUplUU9qVlhrV1pqVlRqVlRp UVBnT05hTk5eTExdT0FWSDtiSUNnTkdpUElnTkdcSkVeTUdhUE9lVVRtVlBtVlBpU01hSkVkQzVp RzpjRzxkSD1hSEFiSUNcQ0dfRkpkR0RnSUZiTk5lUVFdTD1VRDVWPjhXPzlhSEVrU09OPS5MOyxJ OTBGNS1HNTVKOTlQPkBVQ0VXR0RbSkdcSkVdTEZhUE1pWFVpXV5rX2FqWltlVVZXT05YUE9eTk1f T05eTk1jU1FeVVZeVVZcUEhWSkNRRj5QRT1MRD5HPzpMPTVOPzhQQ0dWSE1dTkdWR0BWRD1UQTtX SU5cTlNXT05dVVRrWl1wXmJcWlhPTUxRQzlNPjRKPS5URjdYTUVfVExeWFZXUU9WSj9PRDlGPDlN Qz9UTVFfWF1hWlxjXF5dVlhWT1FYVFVkX2FjYWJYVldaTkZaTkZaTU1bTk5QTEpOSUhKPjpHOzdN RDtNRDtQSkhWUE5eVlViWlhhWlpcVVVTTExXUFBiX2NiX2NlVVZTQ0RWSlBiVlxtXmNnWF1fT0Nc TD9fTkFeTUBRQDhOPTRUSkFaUEdbT0ZXTENTSkdaUU5jXl1nYmFtY2RuZGVfW01cV0leU0lUSD9R PzdWRDtbVVNiXFpkX15hXFtdTkZURT1RQzlWRz1dU01cUUxQSDVHPy1KPzdRRj1USURfVU9cU0lV TENJRD1BPDVJOzRQQTtRQDhUQzpYRztRQDRMOSRKOCNDNyJBNSFEPCpIQC5QQThTRDpTRD1RQzxN Q0FNQ0FQPj5WRERYTVBiVlplVFpiUFZcSkVYR0FcTUZiU0xiVVNkV1VhVFFbTkxTSEVUSUZUTU1W T09cUFRaTlFXSkhWSUdYRztcSj5YTEdbTkleV1dfWFhhTUpXREFRQUBaSUhjV1tnW15nVVtoVlxl U1VnVFZpXFplWFZfU1NfU1NlUVRhTU9XQTxQOzVQPjlUQTxWRDtUQTlVQz1aR0FbT0dbT0diT0le TEZcRTlbRDhiT0hnVE1qV1prWFtrVFVtVVZqVE9iTEddSUlcSEhcSEhfTExnVFZuW11rXFFhUUdY R0FYR0FhTU9lUVRpU05oUU1iSj5eRztjRT1kRj5iRj1hRTxdSDtdSDtcQ0dbQUZfSD5jTEFjUFNj UFNhST9UPTNUOzdcQz5iTlBqVlhMPChHOCRKOi5HNytJNC9POjRKPTBNPzJWRDtbSD9dSkRhTkdh UFFnVldqXV1pXFxiVVBeUU1bTkxXSkhdTUxhUE9iT0ZkUUhcVFNbU1FRTUFNSD1PPjVQPzdMRTxE PTRKOjNNPDVPPkFWRUhcTEhcTEhYSUFVRj5WTVBdVFddVVRcVFNjV1tpXWFkXFhWTkpOPS9JOStO PS9RQDJYTEljVlReWFRYU05fTkVVRDtNPDBUQzdUTEhdVVFiW11iW11dVlhVTlBaU1NkXV1nXl1e VlVbT0ZbT0ZhUE9bSklPR0FMRD5KPDJMPTNOQDFQQzNTRz9YTUVaUU5eVlNeV1dcVVVUTklbVVBj WltjWltjU09UREBaTUpkV1VuWlxlUVRlVE1lVE1kVUpjVElXSEFVRj9aTkVdUUhYT0NTST1TRkFa TUhhWFdrY2JvY2lwZGptYVdjV05bUUhRSD9VRD5hT0llXF9iWFxcWFVUUE1URTtTRDpQRz1YT0Vk WFBcUEhOQDFFOClRQzxVRj9QSkhcVlReUU9YTElGPjs9NTJFOjFHPDNTQTlcSkFbSTtTQTNOPydJ OyNDNRxDNRxNPS9XRzleTUBdTD9VRD1PPjhOPj1URENQQ0VURkhWTVBdVFdfU1BdUE5iTUFnUUZo VVNqV1VtWF1nU1djUE5aR0VQRD9QRD9ORkVTSklYSk1XSUxWTEpWTEpcSkVeTUdYTVBbT1NiVlpj V1tpUVBiSklUREBRQT5eSkpjT09iVVViVVVjUFBnVFRrV1dnU1NeTVBiUFRjUUxdTEZWPjhPODFR OzFWPzVaRDxYQztWRD5bSENbTkxdUE5iUU5hUE1dR0NfSUVqV1VvXFpyXlxuW1htU1NqUFBkTVBn T1NoTlBkSk1hTU9kUFNlV1poWlxpX1VcU0hUSD1TRzxeSERiTEdhST9lTkRoTTtpTjxiSEFfRj9h ST9hST9eRztdRjpeRUFfRkNeSEBdRz9fTExiTk5eSEBVPzhTPTleSERoVFFqVlRQQCpMPCZNOilH NCRJNC1OOTFIOSpNPS5TQzNdTT1dSkVfTUdWTk1YUE9iVVVnWlphV05aUEdUREBTQz9TSkdVTUlb T0ZiVk1iUU5hUE1bTERRQztXRjpXRjpVRjxTRDpRQzlPQDdRQT5WRkNWTkhaUUxdTkdeT0haU1Vf WFtnW1xqXl9rXWJrXWJhWlBWT0ZRQTNKOy1PPCtRPi1OSEReWFRoW1hhVFFQSkREPjhHPDNKPzdN SUFTT0deV1dcVVVbU01aUUxcU0llXFNpXFdhVE9bUUVYT0NdVU9XT0lUSD9TRz5MPi5RRDNURDhY SDxcTEhfT0xeVlNhWFVdVFVYT1BWSkFcUEdhVFFjVlRfTkhWRT9QTUdaVlBiW1tdVlZjVlRtX11q XVtpXFpbTklaTUhdTkZfUEhaUERPRjpMRzxPSj9iWFxtY2dvZWtyaG5uZWJoX1xnV1BRQzxYRUxp VVxkW2FhV11VT0hWUElXTTxYTj1cTEhnVlNuW1VhTkhQQCxQQCxTRkFbTkldU09dU09kUFNhTU9I QDtAOTNGNytKOy9TSUBYT0ZbUEFWTD1TSTRUSjVVRTBQQCxbSUNlVE1hU0VcTkBUQzpUQzpXRkBb SURYSElXR0hUSkxXTk9YTkpbUE1jV09vY1tuX2JoWlxfVFVaTk9aTUhRRUBUQz1RQDtRQT5TQz9T R0hWSkxXSU5YSk9YTElaTUpcUFFbT1BjVlRoW1hqWlhkVFNWSkNQRT1WRT9YR0FbT0deU0pjT1Fn U1VnU1BkUE5hTU1hTU1fTkFXRjpVPjRYQTheRT5fRj9aTkVWSkFWRz9TRDxVSERXSkZcUEhcUEhc SUdlU1BqXF5qXF5nXFZkWlRlUU9nU1BfTU1kUVFiT0hjUEllTE5tU1VwXV1yXl5yX1ZhT0ZRRzVM QTBRPzpaR0FlTUhuVVBtVUhqU0ZjSUVdRD9hSEFjSkRaSDxYRztWRD1YRj9bRDpaQzlbSURcSkVb SD9RPzdUPjpdR0NlU0xlU0xbSTpQPzBRPitOOyhKOy9JOi5IPzNJQDRURT1dTkZeT0hdTkdWSkNY TUVjUFNnVFZfVVRXTUxYQz1XQTxTRDxURT1cSUdkUU9qVlhpVVdeUU1XSkZbSUBYRz5YT0VdVElf TUZaR0BXRj1cSkFaSUZbSkdaUUxbU01dVFdjWl1kXWJoYWVvY2dtYWRoW1ZWSUVOQDNJPC9RQy5W RzJMTURTVEpYV1RbWlZNSklDQD9HPjVKQTlKQz1PR0FcU1ZcU1ZfU05aTUhXVFBaVlNjWlBeVUxe VUxdVEpcVFBbU09aSkBTRDpNRDhTST1XRj9fTkdeVlNdVVFbVFRbVFRaT0xTSEVRSD9XTkVcUFFc UFFbTEVWR0BUSkBdVElhV1hhV1heVVtpX2VpW19kVltRTElTTUpXUUFbVUVXVUhKSDxJQzlRSkBa V1tlY2doYWNqY2VvYWVtXmNiVVVRRUVbR05kUFdfWF1bVFhVTUdYUEpdUUhfVEpdU09kWlZoV1Zd TUxVST5aTkNfVU9hVlBaT0lYTkhdT1ReUFVQST9GPzVFOClHOitKQz9WTkpfVExdUUlWUURXU0Vb U0BYUD5dUE5jVlRjU0ZYSDxVRTdYSDphTkViT0ZaTkZXTERRT0RTUEVdU1FlW1pqYWRvZWlnYmFh XFtaUU5XT0xYTEdVSERTQDtQPjlRQDpPPjhPQDpWR0BbSklcTEpYTk1aT05XTk9bUVNcVFBeVlNk WFpeU1RbTEVWR0BTQTlRQDhYRj9eTEViTlBfTE5lTUllTUlbSEhaR0dbSEFbSEFaSDxeTUBnT05l Tk1cUEhWSkNYSj1TRThVRj5YSUFbTERbTERcUVBhVlVjW1pkXFtiXVNdWE5cVFBcVFBfT0xiUU5i T0ZhTkVcSE9lUVhpWmNvX2l0XVdjTUdUQC1OOyhKPDVTRD1hTkxoVVNiUUNdTT5bQzxWPjhYQztY QztYQztYQztaQTtbQzxXQTpXQTpeRkFhSEReSTxWQTRTQDtaR0FeTUZeTUZaSkBVRjxYRTNUQC9O PjBMPC5JQDhKQTlUSUZdU09jU09iUU5aSkNYSUFeTUdiUEpiUU5eTkpfTExVQUFRQztURT1aR0Fk UUxqVltrV1xiVldeU1RfT05hUE9eWFZjXVtiU0xbTEVbT0ZcUEdbT0RWSj9UUEhUUEhYT1VcU1he Xl5iYmJoYmpkXmdjWFVWTEhPQTFOQDBRRDRVRzhRTT9XU0VbVkxYVElOST9FQDdHOTJJOzRMPTdU RT5YTk1aT05bUEpXTUdVT01YU1BiV1FhVlBlVk9oWFFjWFVeVFBTRkRQREFQST9UTUNaTkVhVUxi WlRcVE5dVVRaUVBYTEdTRkFUSkBaUEZYVFNWUVBXT0lQSENVTD9eVUhlW1doXVpiXV5pZGViVVNY TElOST5TTkNcVUxeV05YU0xWUElPSUNQSkRWVFddW15iW11rZGdwYmdpW19bT1BVSUpaRkpeSk9c VVVcVVVcU0ldVEpcVUpcVUpXT05cVFNcVlFbVVBeU1RjV1hkXFteVlVTTENVTkVeU1ZdUVVUTkdQ SkRIRTRFQTFORkVaUVBfV1FeVlBfWlNeWFFeWFZbVVNdV1VcVlRcU0ZPRjpURTtdTkRkU0xfTkdX TEBRRjtRSD9USkFaT1dlW2NvZWlqYWRlWFRhVE9fUEhbTERbTERXSEBUQzpRQDhNPDBNPDBTRTha TD5eTURcSkFcTEpcTEpcT09dUFBcUU5cUU5fVFdaTlFYSEVTQz9NPzJMPjFQPkBbSEpfT0xhUE1f TUdcSURcRj5cRj5XRj9bSUNdTUliUU5qV1doVVViT09cSUlcSUNYRj9XSEFbTEVdTUleTkpfU1Nf U1NkWlhnXFtfWlNdV1BaUVBaUVBhTlBjUFNiT0lhTkhhTFBnUVZkWmdvZHJtXF1eTk9RRCtKPSVK OTJWRD1iUU5kVFBiUUNcTD1dRjxXQDdbRDhYQTVbQz5bQz5bSEFYRj9aRThaRThdRUFfR0RXSDVT RDFQPzNYRztdSkFfTURaSkNWRz9VRDhTQTVQPzdOPTRJPC1MPi9TRz5fVEprWFhnVFRaTUhTRkFX SkhaTUpaT0laT0lcT01YTElYSUNVRj9YSEVkVFBnXWNlXGJkWlhiV1ZjU09kVFBhWFdjW1pkW1Bj Wk9iWlZeVlNdVElWTUNNSj5NSj5PTEZPTEZWV1BdXlddYmNbX2FiVVVaTU1RQTNVRTdUSTtYTj9d U0FeVENYVEhWUUZRSDxEOy9IOSdKOylMPz1QREFXSkpcT09YVElVUEZOTEBWVEhfV1RfV1RoW1ht X11lXl5cVVVRSkFIQTlUSUReVE5jVlRjVlRlW1djWFVhWFdYUE9XTERVSUFaUERjWk1iWFpdVFVd TUlYSEVWTURiWE9lYWRqZWllYV9bVlVaTDxVRzhKSDxUUUVhW1RhW1RdVVRdVVRXSD5VRjxWTU5Y T1BeWltoY2RpZ2heXF1dUExTRkFTRD1bTEVXUEZbVElkVFBiUU5eU0lcUEdTSklVTUxbUE9fVVRj V1hlWltdV1BVT0hTSUBWTUReTEleTElUTEZWTkhRTEdQSkZWTU5fVldiVVNhVFFjV09kWFBkWlRe VE5dVlZbVFRaU0lKRDtUSkFcU0lhVU1aTkZWRTxTQTlRPzpVQz1UUVNeXF1pX2NnXWFkVFNiUVBa T0lbUEpcT0pXSkZVST5QRTpTQTJWRTVeUU9fU1BjUFBbSEhYRUVYRUVXRUNYRkRXTERXTERXTUdV SkVaSkNURT1NQTdNQTdPQ0BVSEZhTUpeSkhhSkZeSERbRjhbRjhUSD9YTURjV05qXlVnVFFjUE5f TUpdSkhfTElbR0VYQz1aRD5aR0BeTEVhTU1lUVFnVlVnVlViVk1fVEpdTUxaSUheSk1fTE5eTEZh TkhlT0pqVE9pV11yX2VqX15eVFNcTDRURC1VPTdbQzxeTEZhTkhlTz5kTj1jSUNlTEVoU0VeSTxc Rj5dRz9bSD9aRz5bRjtaRTpWRDtXRTxTPS1RPCxROi5XPzNdSD1hTEBaSkNaSkNXRzlTQzRTQDpP PTdPOy5POy5TRThdT0FpV1tpV1thVE9XSkZVSkVWTEZXTE1YTU5cSkRdTEVeTk1cTEpdT1FjVVdj XF5jXF5jV1hkWFplVVRnVlVoV1hpWFpjWFVlW1dkWFxkWFxfVVRaT05USkFPRj1ORTxRSD9UUEhc WFBfXV5fXV5nVldbSkxYRTNdSThhU0VlV0lhXlNfXVFaVk5WU0pTTDFHQCdUPSlaQy5QSDlRSTpY TUVcUEhaUU5cVFBUTURXUEdfXFhkYV1nZGNnZGNnXltcVFBPSTpIQzNWSUdkV1VkWFpqXl9hXFti XVxfVVRYTk1YTUVbT0dfV1ZlXVxpXl1lW1phU1dbTVFVTE1dVFVpY2ltZ21oYWFdVlZcSkFUQzpK RjtUT0RdXFhhX1xhWlxYUVRXRj1VRDtRRUVYTExdWl9lYmhoZG1dWmJXTkVORTxPRj1XTkVfVEpl WlBrWlRpV1FiVVBYTEdTRDpYST9eUU9iVVNkWFphVVZeU0lVSUBWSDtYSj1hT0lhT0laTk9YTU5W T1FWT1FeU1RkWFpfVU9hVlBkXVRlXlVoVVNiT01bT1BfVFVaUUxVTUdVUUlYVU1eVFNaT05TRkRT RkRXRTxVQzpeVFNnXFtpYmJlXl5hVFFaTUpXVE5cWFNYUUdVTkRXTkFXTkFcSUdiT01kW2FiWF5k VFBeTkpXRT5VQzxVRj9XSEFVSkVVSkVYTEdXSkZcSUNXRT5VRjxRQzlRQzlYST9oU0doU0dkU0Zd TD9TRC9URTBRSj5bVEdkXVRkXVRpV1BjUUpdTUlhUE1dSkVXRT9aQThbQzlcSEZfTElhTUpkUE5j VE1jVE1iUEpiUEpfTUdbSENcTEpcTEpeTExkUVFoVU9uW1VtW150YmV1Y2ltW2FrVkddSDpUPTBV PjFXR0RkVFBuW1R3Y1x0YV5wXVt0XE5pUURfSURbRT9bRT9cRkBWQDtXQTxYRDlUPzRPNSVTOShU OitbQDFdRz9eSEBkTkZiTERcSjxVRDVaQzlWPzVQOydOOSVTQTJdTDxnWldoW1hpWlFhUUlWSkFR Rj1VSERaTUhhT0ZhT0ZdTU5dTU5YT1BdVFVlWltlWlthVFRhVFRjU1FlVVRnVlVnVlVjWFVjWFVn VVhpV1tiVVVdUFBYRkhWREZOSkdNSUZOSkdaVlNfXV5jYWJkV1NeUU1fT0BkVEVnVlNpWFVqX15l W1pbVElbVElcTj1dTz5qU0FqU0FdU0FcUUBbUUhbUUhfVFVhVVZbT0ZcUEdeXVdpaGJpaWdnZ2Rn WlVjVlFWTUNPRjxaSExtW15qYmlpYWheYV9aXFtfVVFaT0xdTkdlVk9kW1xoXl9jXl1cV1ZdVFdY T1NUT1BbVldrZGltZWppXV5hVVZaTUhVSERJRzxUUUZfW1piXVxfXltWVVFVTD9NRDhORTtbUUdf W15lYWRkXGNcVFtVTUxIQD9JQzpQSUBeVlBqYlxtX1tqXVhhWlBXUEdTQzNXRzhdV1VfWldnXFtk WlhiVERYSjtWTUBcU0ZlVE5oVlBcUVBaT05aTE5dT1FdVFVhV1hhXVdhXVdfXlhfXlhiT0lcSURe TExiT09fT05nVlVnXVNnXVNhVFFcT01VUEZUT0VYST9XSD5lV1xrXWJtYWJlWltbU1FXT05cV1Zf W1pdVklXUERWT0NVTkFcT09kV1djXGFiW19fVVFbUE1XRztWRjpXRTxYRj1VRUFUREBVRURbSklj UElcSUNaRThVQDNQRkBdU01oW1ZrXlpiWlRbU01XRzlURDVWTEhiV1RlW1pkWlhoUUliTEReTkpi UU5fSj9VQDVaQTVeRjphTkhkUUxiT0hhTkdcT01iVVNnU1NkUFBcSURaR0FcTEhiUU5hUE9nVlVq V1FrWFNtWFtwXF5zXmNvW19qWE9dTENaPy5hRjRkUVF1YmJ1ZGV1ZGV3Y2F1Yl9zYlNnVkdeTEZW RD5WQDlVPzhXOTRUNTFWOCpTNCdFMB9QOylVPjFXQDNfSDxfSDxiUUVhUERjTD9iSj5jTj9aRTdU RC1PPylaSDpfTj9nWldlWFZoWFBoWFBcTUNVRjxaTD5cTkBjU09jU09dUVVcUFRWUExcVlFjW1dh WFViVVBeUU1iU0xiU0xjVlRlWFZlWlFhVU1lWFZlWFZiVk5cUEhVSkdQRkNHR0dFRUVERkVRVFNe WltkX2FkXFZjW1VoXVxqX15tXmFqXF5vXVdrWlRlWlBoXFNfV1ZjW1pyYV1yYV1pXltlW1djWFNe VE5fWFtdVlhUTEZbU01pXl11amlpaWtkZGdYVU9bV1FUVEhKSj9USk5jWl1rYmhpX2VeXl5YWFhV UE9VUE9eVE5kWlRkXV1nX19eW1dXVFBdWFdUT05VUE9cV1ZpYmRoYWNhV1hVTE1QSkZWUExRR0FR R0FUU01cW1VfXFZbV1FeT0dVRj5QRkBbUEpiVFhlV1xjVVddT1FcTEpOPj1JPz5USUhjXFxqY2Nk YmFcWlhhVlBWTEZVRj5YSUFcWlhiX15nXV5kW1xfVEhjV0xiV1RqX1xkY19jYl5eWFFQSkRVRUFc TEhXT05eVlVlV1xrXWJvZGNnXFtcTD1YSDpcSURhTkhdU01kWlRoYl1nYVxnWlpfU1NaVE1YU0xY T0ZcU0loWl5qXGFhXFtcV1ZaT0lbUEpeV1dhWlpeVlBWTkhYTkhaT0liUFZhT1VdVFViWFpeVFNY Tk1YTj9VSjxeSEBeSEBVQz1UQTxVSEZbTkxnT05jTEpWRjpURDhaTFBlV1xtZWhtZWhtX19kV1dY TURUSD9eUFNnWFtuW11qV1pkUUxjUEpkUVFjUFBdTD9aSDxfSj9jTkNqVE9qVE9oT0NjSj5YR0pc Sk5cT01eUU9dTEVbSUNdSUdhTUpdUExjVlFkWFBnW1NqV1drWFhqWFxqWFxkV1VeUU9pUUdyWk9v Y2R0aGlzYl5yYV1tYlxuY11uXlRdTkRdRD9YPztNOilQPSxQOihMNSRIMh9JMyBTPCxbRDNeSDhd RzdkT0BoU0RfTkViUEdjUUVhT0NlVEdfTkFbTT9XSTxeT0hhUUpiVVNlWFZlXlRoYVZnWEpfUURi Sj5kTUBiUFRpV1teWltcV1hcVUxeV05iWlRiWlRdVVRXT05YTkheVE5dVU9fV1FeV05bVEphV01i WE5nV01fUEZYTkhUSURJSENHRkBOQ0ZWSk5fVFVlWltnXV5qYWJtY2dpX2NpX2FlXF1nXVBnXVBr Xl5tX19nX2RqY2hpYmdqY2hnX2RjXGFiVlpdUVVeVFxfVV1aT05hVlVnWF1zZGlrZ2pjXmJcUU5d U09YUEpUTEZWT1FjXF5qYmlkXGNcV1hXU1RWT1FWT1FaVFFfWldhWlpiW1taVE9YU05aUFFXTk9V UU5cWFVlXl5jXFxdV1NRTEdPR0FUTEZOQztPRDxUTkdeWFFeV05dVk1OSkNOSkNQTUlTT0xjT1Rl UVZfVU9WTEZNRUFHPzxGPDtPRURaUVhkXGNlXF9fVlpcUFFXTE1TSklRSUhfW15qZWllXmFdVlhh VlNjWFVeV1ptZWhyaWVqYl5hT0ZRQDhOQT1QRD9TSEdVSkldVV5nXmhqYWJeVVZaUERUSj5WRz9b TERdUExjVlFhYWFhYWFkWF5eU1hdUFBdUFBdSkhfTUpjVlZjVlZeVE5dU01dUUldUUlcVVddVlhd UUlYTUVaT0lbUEpeUVFeUVFeUU1iVVBYUE1WTkpcTUNfUEZjUUxcSkVTRz5QRTxVR0ldT1FiUU5f T0xaSEFVRD1USk5lXF9vaGpqY2VpXltcUU5VRj5YSUFkVFBtXFhrWFZoVVNhTk5eTExkUE5iTkxi SkBhST9eSkhiTkxqU1ZqU1ZkTEhiSUZdSUdeSkheTEleTElbTEVeT0hiTEZhSkVeT0dnV09nV09l Vk5nVE5pVlBnVVhnVVhjVVdjVVdvXFxzX19zYmNwX2FuXFVuXFVuY11tYlxrXU9cTkBYQTRTPC9K NCNNNyVOOCRPOSVONypTOy5dRTtkTEFlUEVoU0doVU9kUUxiTUFkT0RkTEVlTUZjU09hUE1hVE9b TkldUE5fU1BdT1RjVVpkXFhrY19tXFhkVFBkVUpjVEllVFdtW15pXFxjVlZiVVBiVVBjWl9lXGJk VlhYSk1YTEdaTUhcU0heVUpeVlBdVU9iVk5jV09oVVVoVVViVk5eU0pUT0VPSkBTSEdVSklYVVFh XVpjXmJlYWRtYWdoXGJoXVplW1dkXFhpYV1qYWJpX2FoXWVpXmdlYmhkYWdoYWNhWlxaVVZXU1Rk VV5kVV5fVlpeVVhlWl1wZGhpZ2heXF1eT0haSkRaTkZaTkZYU05jXVhnY2lfXGJfUVRaTE5YTElb TkxbVVBaVE9hV1hhV1heVVZdVFVWT09cVVVWUVVaVVhjWl1lXF9eWk9QTEFOSTxOSTxOQzpQRTxX TUdiV1FhVlNcUU5MRkRPSUdUSUhRR0ZeTk9jU1ReUD9URjVOQzhJPjNNPTxQQD9WSk5fVFdiVVNf U1BYRkRcSUdRSkpVTk5fW1xpZGVqXF5hU1VaUVBdVVRfWFtwaWtwa2FnYldcSDRTPyxIQDFMRDRM QTxNQz1UUFhjX2hkW15aUFRYTURVSUBRRjtVST5eTExoVVVoXmJhV1teUFNbTU9YTElXSkhaR0dc SUldUUlfVExfVVFdU09bUE9hVlViWFxhV1teTExbSEhdTUxiUVBdTkdeT0hhUE1eTkpdT0FdT0Ff TkVjUUhiVVBfU05aT0xWTEhcSUxeTE5hVE9jVlFdT0FaTD5YUE9kXFtwZGhtYWRrWFZdSkhVRj5c TUVnVlVtXFtoV1hjU1RaTkZXTERfTEliTkxiSUVeRkFdRkVjTEpnUVtnUVtlUVFeSkpiTERfSUFc SUddSkhdUFBdUFBfTEliTkxfTU1iT09jUEpjUEphTkhiT0ljUE5jUE5oVVdrWFtyXV90X2JvW1hr V1VuVlVtVVRwWlRrVU9oU0VbRjlXQTpRPDRQNyZWPCtaQzJbRDNcRTtdRjxhSkZkTklrVFdzW15y V1xvVVpfSUFfSUFfTUdhTkhcT09cT09eTk9XR0hYSEdXR0ZUTEpaUVBjV1htYWJuX2JnWFtnXFhq X1xnXWNoXmRnWF1jVVpiVFZkVlhkW2FpX2VqV1VeTEleTURaSD9WSkFcUEddUUhfVEplWFRlWFRp WFpoV1hiWlheVlVXT0xUTEhYTk1XTUxUU0pXVk5jW1pjW1plWFZkV1ViV1RiV1RhVlVoXVxoYWFf WFhhVVheU1ZjV1thVVhfVlpbUVVUUVNTUFFeU1hlWl9fV2FdVV5jX2hnY2tuaW1lYWRcUEhTRz9d UE5hVFFjWlBnXVRpYmdkXWJjVVdbTU9YTk1XTUxcU1RdVFVqX15pXl1kXV9bVFZWUE5WUE5WUE5b VVNlXF1lXF1oW1hXSkhUSkFORTxPRjxaUEZiT01lU1BfVFdbT1NRQUNTQ0RQREROQUFYTUVdUUld TjlVRjFURDRPPzBJOzNKPDRVSkVYTkheUU1aTUhXRkBXRkBVSUBYTURbVFRpYmJlXFNbUUhXTUdd U01eXF9raW1waWlkXV1YRDlVQDVQPzNTQTVQPjhUQTtXU1ZhXF9jWFdbUE9RSD9ORTxVRDtcSkFi UEpnVU9kV1VfU1BXTERVSUFTRkFVSERXTERXTERaTUpeUU9iV1FhVlBiV1RkWlZpV1tpV1tjU09e TkpfU1BfU1BcTUVcTUViUEpeTUdeTENfTURlVVRpWFdjWFVjWFVeVlNXT0xaSkRbTEVdV1BdV1Bd TkdYSUNcUFRhVVhqXV1qXV1nVU5eTUZWSUVhVE9uX2JqXF5oV1ZiUVBYTURYTUReTEVhTkdkT0Rd SD1cRURjTEpiTlNjT1RhT0ZeTURiSUNhSEFaR0FfTUdkVU5iU0xhTkheTEZdTEZcSkVeTURfTkVh UE9hUE9jT09iTk5nT1BqU1RvVFRyVlZrUVFrUVFtU1NqUFBoT0VqUUdnUUZiTUFfRztdRTlfQzRi RTdfRzpiSTxjSkZiSUVeSk1lUVRtVVh7Y2d7YV1wVlNfTkhdTEZYRj1bSD9bTkxaTUpdSkpWRERU RT5WR0BVTUxWTk1iVFhpW19uYmhtYWdlXVxnXl1jW2RhWGJhVVheU1ZbU01fV1FnXV5qYWJrXFRj VExhTkVcSUBYSUNXSEFaSEFeTUZoVVduW11nW2FkWF5cVVVYUVFaTkZWSkNbTEVbTEVbVkxeWk9o XVxhVlVfU1NfU1NdU01eVE5kVltpW19qXF5cTlBeTkpeTkpfUVRhU1VbUE1TSEVTTUhTTUhXUU9d V1VcU1RbUVNiW11jXF5qYWJlXF1hV01YT0VhVE9kV1NnYldnYldpYV1rY19kXlxeWFZRT05OTEpY TVNoXGJrYmVuZGhiXVxYVFNUUEpVUUxdUExkV1NqYlxoX1poW1ZeUU1cTD9QQDRPSDxYUUVkVFBk VFBfU1BbTkxVTENORTxPPT9RP0FWSkFdUUhnU0BkUD5iUEFcSjxKQTVJQDRWTD1aT0BbUUdXTkRX TERTRz9WSDtWSDtYTVBkWFxhWFVXT0xaSkNdTkZkYWloZG1oY2JeWlhbSTtaSDpaQTRaQTRUQzRW RTdbTU9iVFZfVExXTERQQzVTRThcTUNjVElpV1FoVlBeVlBcVE5WT0VTTEFWRz9WRz9USEBWSkNf U1BoW1htX11rXlxjWltiWFppV1tqWFxqXlVjV05fVk1eVUxcSkRbSUNdTUxcTEpfTElnU1BqWltr W1xiV1RfVVFdUUlaTkZUSj5WTUBdV1BbVU5aUEZVTEFaSE5kU1hlXVplXVppVlRhTkxXT05eVlVl XF1lXF1nV09hUUlYTUVYTUVjT09lUVFnU1BiTkxiSklkTUxjUEpfTUdfTz9iUUFkTkZiTEReTEVj UElkU01jUUxiUEddTENaSEFdTEVfU05lWFRkW1FaUEdhSEVhSEVdSkVhTkhrUUprUUpnTUhnTUhr TEdtTUhoTEBtUEVuVUpuVUptUERvU0ZtVUdqU0VjTkNlUEVjSkdhSEVhTVFlUVZqVVpuWF1yVFBn SUZfU05XSkZVQzpXRTxbSURaSENWSj9USD1TQTxUQz1QSUlVTk5eV1plXmFpX2FqYWJkYmFlY2Jl XWlfV2NjVlRhVFFdVU9fV1FjXFxnX19tXFtoV1ZjWFNdU01aTkZVSUFXRT9dSkVdT1FkVlhiW1tb VFRUTkxUTkxaT0xaT0xiTkxnU1BkWlZlW1dkXV1bVFRWTk1XT05bT0deU0pjV1hpXV5lXVpYUE1Y TEdbTklYUEpXT0lUTURQSUBUSEBVSUFTTUpXUU9aUU5XT0xbVFRdVlZoW1hrXlxoXVpkWlZjW1pk XFtiWlhkXFtkXFhoX1xjZF9iY15XUEZRSkBRSExfVlptZWpwaW5kYVtcWFNWUUdTTkRlVVRqWlhu ZWJtZGFqYl5kXFhhT0hUQzxUTEZfV1FjW1dkXFhhVlNcUU5aVUlNSD1VRD1PPjhVTUdcVE5rX1Rq XlNnXVNfVkxTTT1QSjteU0ljV05fVkxaUEZTTUZQSkRVRzhXSTpUTEpcVFNdV1VbVVNbTklfU05q ZWdlYWJqXVhjVlFiUUNiUUNjUUFiUEBYSjpTRTRcSUdhTkxaTkZRRj5VRD1dTEVoXl9rYmNpX1Zo XlViWlZhWFVdWE5YVElbTERWRz9USkBWTUNlXF9vZWlvZWttY2loXF1hVVZhWF9nXmVtZGFlXVpk VU1fUEhfTURdSkFWTURbUUhdU09lW1dpXFxqXV1lWFRdUExdTkdYSUNTSUBaUEdfVVRdU1FcTUZY SUNcTE1nVldoYl9kXlxkUU9dSkhYSk9eUFVeVVheVVhfUEZeT0VdTEVeTUZlUVFjT09fTUdhTkhk SkZlTEdnUEhpU0pqW1NpWlFvV1ZrVFNkUE5iTkxfT05jU1FkVUpeT0VeTUZnVU5pV11uXGJpXl1f VVRfTkVcSkFhSEFkTEVlUEVoU0dkTTxlTj1tTkNzVEhuVkxyWk96X1iAZV5+Y197YV10YVduW1Fp VlBiT0liSUZeRkNdSUxeSk1jSU5kSk9lTT9hSDtcUVBWTEpYRDlWQTdWRkVWRkVPSD9NRj1RQzxR QzxQRkBUSURRT05dW1pjXl9oY2RjYWJkYmNiWFxcU1ZeVFBfVVFcVFBdVVFiW1tkXV1vXl1wX15p YV1iWlZdVEpWTURbTERhUUlYU0xeWFFbWlRVVE5TSkdPR0RRTUxXU1FkV1dqXV1nX2JpYmRnY19d WlZTT0lOSkVTTUpYU1BhWlxnX2JkW1xeVVZeUVFcT09WTkhRSURVSkVUSURVSEZWSUdXTUxWTEpX SkhYTElbUE9hVlVtWlxtWlxiW11nX2JkXV1iW1tiWlheVlVbVVNfWldbW11dXV9bVEpYUUhXUU9h W1hrZ2pqZWldXlpcXVhcWFNRTkhaVFFkXlxnZGhraW1uaW1iXWFnU1NkUFBYVVFkYV1oYWFnX19j W1pdVVRaU1NTTExbSklWRkVXVVhkYmVqaGlraWpraF9hXVVWU0FXVENlW1VlW1ViWlRXT0lRTkhU UEpeT0VhUUdVTUlbU09hV1tdVFdWU01YVU9dUVVkWFxjVlRkV1VoYVRoYVRvX1VpWk9XTzxTSjhd TkddTkdaT0xaT0xYTExnWlpuaG5rZWtkXlxeWFZeWFZiXFphW1ZbVVBaUUxNRT9USURdU01lY2dt am5vZ25oX2djV11iVlxjXWVlX2htY2RpX2FuVlVrVFNjUUxcSkVUSElYTU5jVVxrXWRuYmNoXF1h VU1dUUleTUdaSENdR0FlT0loVFZoVFZjTEphSUhcUFFlWltnXl1hWFdpUElkTEViUFZoVlxlWFhd UFBeTENcSUBaST1eTkFhT0lfTkhjSkZjSkZjUEdqV05uXldtXVZwXmJuXF93XmJtVVhpUE1pUE1i TkxlUU9kVU5kVU5kU0llVEptXF1rW1xlWFRhVE9WTURUSkFdTEZhT0lkU0xlVE1rU0x1XFV/YVuE ZV+EamWEamV+amR9aWN7Z2l4Y2VyXlxtWldtU05jSUVdRTlYQDRaQT1cRD9eRUBjSUVkTTxhSTlk UE5eSkheST5YRDlYTElXSkhPRUFQRkNRQztQQTpURTtVRjxTTUpfWldnXGRlW2NjWl9nXWNkV1dh VFReTk9fT1BdU1FfVVRjWltnXV5vXWFyX2NpYVtkXFZjWFNhVlBlVEpoVk1bVU5cVk9dVlZWT09W TEpVSklTTk9XU1ReVVhlXF9pXmdpXmdlZGFhX1xTVEpISUBRRklYTVBYWFtfX2JfXV5YVlddVlZb VFRXUEdORz5MRzxKRjtQRkBTSENRTElRTElTTUhRTEdWTU5bUVNlVVRqWlhiWlhoX15kXV1cVVVW T0ZVTkVXU1RXU1RcVVphWl5eU1ReU1RfV1ZtZGNvaGprZGdkXFthWFdaV1ZPTUxaVFFjXVtoaGpp aWtnZGheXF9fWlVeWFRfWlVpY15oYWFlXl5dV1NcVlFWTVBXTlFYTkhYTkhfXV5raWprZ2hpZGVi XFpdV1VfU05iVVBnXFhpXltjW1pbU1FYTkheVE5eV01hWk9cUVBbUE9dUVVdUVVbVVBaVE9YTk1h VlVfWFhkXV1zZWNwY2FuZFtjWlBhUEReTkFdVElYT0VWSkxXTE1bT1BlWltnX2RlXmNoW1tjVlZc WltbWFphVlNfVVFbT0RVST5WTURkW1FyZWlyZWltY2dkW15iVFtjVVxcWGFfXGRrYmVrYmVyYWJq WltdUE5VSEZVREdcSk5jW2dqYm5wYmRpW11iUElfTkdfTURiT0ZeTUddTEZiT01jUE5kUU9nVFFj Wl1jWl1jW1ddVVFoVU5pVk9pVFhqVVpnU1VhTU9iSj5eRztcTD9hUEReT0hiU0xkUUxpVlBrW1py YV90Z2d0Z2d1YWh1YWh5YWJuVldtVEdtVEdoUU1pU05lVVFnVlNoVkdqWElrW1poV1ZnVU9hT0lU TEpVTUxUTEpUTEphUE1pWFVyXl53Y2N+ZWl+ZWl7aGV9aWd+ZWd+ZWd+aG14YmdzXFZrVU9lTkFe RztfRTViRzhlTUNlTUNfSDteRzpdRTldRTliT0hcSUNbSUBYRz5bTEVbTEVYRkZVQ0NRQDtTQTxO Rz1TTEFVTUxcVFNhU1dkVltjWltnXV5oW1hhVFFcUFRXTE9aTlFdUVVjVVdoWlxuX2JyY2VpW11o WlxpXltkWlZvXl1zYmFdW1pYVlVcVFNaUVBVTUxQSEdQSkZVT0pUTEphWFdkYmVlY2dkYmVfXWFX VExKRz9RQzxWR0BOTEpcWlhaX1pTWFNYVU9YVU9VTkVORz5PRDlPRDlKRDpKRDpORkNORkNMRUVK RERQSkhWUE5eVE5nXFZlXVdlXVdiVk1XTENXTT5aT0BbUVNbUVNfVldeVVZfU1teUVpcUFZvY2lu aW1oY2doXF1lWltfWFtcVVdeWl1fW15oZWlua29oYWFiW1tdVVRhWFdhWlxnX2JpX2NjWl1hVlVb UE9XTE1dUVNYU0xWUElfX11oaGVkYmNfXV5hWFNeVlBdU09kWlZyZWlzZ2pjYV9VU1FYU05fWlVi X2NkYmVeVFBbUE1YT0ZbUUhaVE1VT0hUSURWTEZbW1tiYmJwZGhvY2dnYVpdV1BeU0phVU1kWkpe VEVaR0VaR0VVSklfVVRnXFhkWlZnV1BiU0xjUVVfTlFbUUVXTkFbU0NiWkljW1dpYV1yZWltYWRi W11eV1pbVFZcVVddVlthWl5oXmJrYmVrY2JjW1peU0pWSkNUR0NaTUhiWmNrY21vXl9pWFpkVFBl VVFoV1RoV1RhUFFcTE1dUUlfVExlWFZoW1heV1dfWFhlWFhiVVVnWldpXFpnWFtlV1phVFFeUU9n TkdnTkdlVEdoVkllWFZjVlRjVlRpXFpuZGhwZ2p0X2lyXWdvW19vW19uXFNnVUxrVkprVkpqV1Fo VU9qV1VnVFFrVkpwW09pWFpqWltnVU9dTEZTSUBUSkFTST9aUEZjUVVwXmJ4ZGd3Y2V3Yl93Yl90 ZF11ZV55ZGd6ZWh5ZGdvW11tVlFpU05fSDteRzplTUNrU0hpVEhkT0RbSTtYRzldRjpeRztfUEhd TkZeTURdTENhUE1dTUlfTUdbSENWREFWREFNSD5RTUNUSkBXTkRXTERbT0diWE9tY1pqYl5jW1dh WlxbVFZYT1NYT1NbUVNiWFprX2FwZGVzXmVvW2JqYWJrYmN1aGhzZWVrX2NkWFxiVFZfUVRXT05P R0ZRSD9TSUBPR0FUTEZaU1VhWlxjW2RdVV5aTUhRRUBWSTNQRC5JRzxQTkNWVlRUVFFhT1NhT1Nf UEZcTUNTSDpPRTdQRz1PRjxQRTxNQTlKQUNKQUNRR0ZdU1FbWFdjYV9nZGhdW15eVUxVTENWTEhd U09fVlpfVlpdVlZdVlZeU1hfVFpcU1ZqYWRoZGphXWNfVlphV1tiW11dVlhbVldfW1xpZ2hoZWdn XV5eVVZaT0xaT0xaUU5hWFVoXl9iWFpjVlRbTkxbR0xdSU5TTExVTk5fX19paWlhYV5aWldfV1Rc VFBhWlpnX19wZ2huZGVfXVxVU1FVTk5kXV1tZ21qZGpeUU1YTEdYT0ZbUUhXU0hTTkRPSUNTTUZa V1tnZGhrYmNoXl9dVEpeVUxeWlhnYmFrXlpbTklTQTtTQTtVSkVcUUxjWFVlW1diWlZhWFViVVNc T01WVEhcWk5nY19pZWJrZ2hvamtrY2plXWReW1VXVE5XU0dcV0xiV1FjWFNjV1hrX2FwY2NtX19j XFNYUUhWTEZiV1FuYmVuYmVyZF9rXlppXltqX1xoXVxkWlhhVU1dUUlfVVRlW1poXl9lXF1fV1Re VlNkWE9nW1FpX2NrYmVlWFhfU1NjVlRkV1VoUE9qU1FtWlNyXlduYV5nWldkWlhrYV9uZGVtY2Rq V1plU1VkU1hpV11qXVtqXVtyXl5yXl5uWlxpVVdoVVNpVlRuW1VvXFZpW2JiVFtdTkdRQzxNQTpT Rz9eTENnVEptXFt0Y2J5ZGd0X2JuXVpuXVpwX1x1ZGF5ZGRyXV1pVlhnVFZrVU1tVk5fSUFiTERi TUFkT0RiUUNcTD1bST1YRzthTEBnUUZdUUleU0pjUEliT0hhUE1hUE1fT1BfT1BaSENaSENTSENV SkVUSUhXTUxYTU5cUFFoXVxuY2JqYWJnXV5iXVxbVlVcVFNbU1FWVFVeXF1qYWRuZGhzYmNwX2Fu YmNzZ2h0amtwZ2hzX19tWlppVlRhTkxbT0dYTUVRSD9RSD9QRztTST1WSkxeU1RhVVhfVFdcUEhY TUVVSUBUSD9NRjpKRDhRTEVTTUZcTEpfT05hU0ViVEZbUUhUSkFUR0VVSEZXR0ZVRURUSUZQRkNR R0ZeVFNiXGJqZGpoZGpcWF5VVFBWVVFeV1peV1pkWFxjV1taU1VbVFZfVFVfVFViVlxnW2FlYWRi XWFdVFVdVFVeV1pYUVRVVVVaWlplYmhkYWdeXF9UUVVPTkpRUE1cT0pfU05jWltjWltjW1pcVFNj T09iTk5PSkxaVVZiX2NnZGhdWlRXVE5aVFFcVlRiXVxlYV9rY2pkXGNdVlZYUVFbUE9lW1pvYmJu YWFfVEpbT0ZhVUlhVUlaUEdWTURPSj9TTkNfW1pkX15lXVdhWFNYTkhcUUxYVlpoZWlpYVtXT0lW SDhXSTlXTkFdVEdhXFtlYV9oYWFnX19hV1hcU1RaVlNhXVpqaGlraWpybXBybXBqZGppY2ljW1VY UEpbVEpjXFNlXlViW1FhWFNlXVdyX2N3ZGhtXmFfUVRcT01lWFZtXmFuX2JvZGNvZGNtYWJrX2Fn XFtiV1ZkVUpiU0hjWl1qYWRpX2VlXGJfW1BbVkxjW1dtZGF0aGttYWRkVFNfT05jVVdjVVdnVldr W1x1ZGV4Z2hyZ2VuY2JrYmNtY2RtY1piWE9fUElhUUpjVlZpXFxvXF5rWFtvWl5vWl5nVU9lVE5j UE5oVVNpV1tuXF9pW19iVFhcT0pVSERVRj9XSEFjT01lUU9tXFtuXVxwV1BrU0xuVU5yWFFtWlRv XFZuW1FpVk1lVVFoV1RrWFZnVFFdTTpeTjtlUENlUENiUUViUUVhUERjU0ZpWFVpWFVfT0xfT0xi UEliUElkVFBkVFBiUVNeTk9cU0lbUUhiT01hTkxeTkpfT0xaTlFdUVVtYWRvY2drYmVrYmVnYV5e WFZdV1BdV1BeV1pjXF5nX2JqY2VoXl9lXF1oYl9vaWdza25vaGprW1poV1ZjVlFcT0pcUUxdU01Q TEFRTUNaTUhbTklXTE1bT1BYTU5YTU5eVE5eVE5YU0xXUUpUSTtTSDpXRj1bSUBYT0NaUERaV0pe XE9cV1ZWUVBQSkhOSEZUSEBRRj5USD1TRzxVRj5fUEhhWF9nXmVeWlhWUVBPT01UVFFcVVdcVVde U1RdUVNVSkdXTUlaUVBbU1FdVFdhV1tiWFxoXmJcVVVYUVFUU01NTEZWU09VUU5eV1xkXWJhWlxX UFNNSklQTk1fT1BiUVNoWlxqXF5lX11hW1heUU9eUU9WTEpfVVRjXVtjXVtfU05XSkZTTk9YVFVe WlhkX15jWl1iWFxeWFZbVVNbUVViWFxwZGVuYmNdV1NcVlFpYV1jW1dhTkxcSUdXSEFaSkReW1Nj X1djXE9dVklVT0hVT0haV1ZkYmFpXVRcUEddTENjUUhnVU9qWFNiW19kXWJqX2hrYWljXF5dVlhb VVNfWldtZG5uZW90a3hzandvZG1uY2trW1dkVFBoWlxrXV9nYV5jXVtfWldoYl90am5zaW1uWldi TkxbT1NhVVhuYmV0aGtwYmRuX2JwX15qWlhjWFNeVE5jVE1oWFFrYmV0am5qXmJiVlpfVU9nXFZt Y2d0am51ZGVtXF1cUEhcUEhhVVhfVFdhV11rYmh1aW14a294a21yZWdyZ2VtYmFlXFNfVk1dUUlj V09uXVpvXltrXlxqXVttWF9oVFtiUEpkU01nVE5kUUxoVlpvXWFtWlxnVFZkTkZdRz9aSDpYRzlc SkVfTkhlU1BlU1BqUUptVE1vVU5zWFFuVU5tVE1qWExpV0puXVxtXFtwVlhrUVRjTkBjTkBnUURq VUdrUU1tU05rVFNyWlh1X2RzXWJfTU1dSkpdTkZfUEhiT01kUU9nU1NjT09bU09YUE1eUU9eUU9j U1FlVVRkVFVpWFpnX2JrZGdqZGpoYmhlXVphWFVeVlNfV1RhVFRoW1tkX2NnYmVlXF1jWltiXFpo Yl9tZ21uaG5pXltiV1RjWFdfVVRYT1BaUFFaU0lYUUhYUUhYUUhdTUlYSEVVRDhaSDxcUU5nXFhl XF1kW1xhUUleT0ddTENeTURfVkxbUUdYV1FeXVdfW1xXU1RORkBHPzpRQzlQQThTRz5XTENNR0BO SEFWTVNiWF5hVlBVSkVNTEhPTkpYUVRXUFNQRkBQRkBORTlQRztMRz1OST9UTEpYUE9eWlheWlhd VVFYUE1RSkFORz5USUZVSkdaU1NbVFRfWFhcVVVVTUlQSEVaTUpeUU9jW1dlXVpqXVtoW1hcUUxc UUxaT0lWTEZiVVNiVVNhUFFdTU5UTkxWUE5aUFFeVVZqWFNpV1FqW1RiU0xcVVdfWFtvZWluZGhe XF1kYmNoaGhcXFxWTEhOREBWR0BbTEVfWFhjXFxuYVxpXFdeWk9aVUpeWlhkX15rXlpnWlVnV1Br XFVtXFtlVVRdWFpjXl9qX2hoXWVnVldiUVNaUUxeVlBnYmVtaGttZGtqYmlwZ21zaW9uYWFtX19p YmdrZGlqXmRnW2FkXWJtZWpzanJzanJkXVRTTENYTVBoXF9uZGpyaG5uZWRtZGNwX15uXVxtXVZn V1BiWlhqYmFza25waWtkWlhbUE9YU05fWlVkY2hlZGlrYV1jWFVjU1FlVVRhU1dfUVZkVV5rXGVy ZWt3anB1aWpwZGVuX2JqXF5pWFVoV1RpWFVpWFVrW1pvXl1tX11uYV5tWF1oVFhjSkZfR0NeSERf SUVjUFBuW1twWlFpU0plST5eQzhXQzVbRjlWRjpbSj5pUExrU05uV09zXFR1Ylx1Ylx1Xlh1Xlhw XVdtWlRnU1duWl5zWFtuVFZkUUpkUUprU05vVlFrUVFtU1NuVlVvV1ZvXFpwXVthTUpYRUNcSUNh TkddUExhVE9fU1BeUU9hUE1eTkpcTlBcTlBiTlNkUFVnVVhtW15jX2VqZ21yZWtuYmhpXFpiVVNd VVFYUE1hTlBqV1ppX2VtY2lkW1xkW1xnW15tYWRoZGpraG5uZV9oX1piXlteW1dfT1BbSkxfTUpj UE5jU0ZlVUhhUERbSj5XRzJXRzJbVVBnYVxoXmRpX2VkV1ViVVNkWFBoXFRqXVhfU05cVVVdVlZc U1RTSUpPQDlKPDRRQTVRQTVQRkBUSURURT1VRj5WSkxfVFVfVExWSkNVSkdXTUlbVVNXUU9RSD5R SD5NRDtJQDhKQTlRSD9PSkBTTkReVFBcUU5YV1FVVE5UT0VPSkBTSEdYTk1cUVBcUVBfUVRkVlhY UUhTTENjUUhlVEpoXVpuY19lXVxiWlheVUxbUUhcT0pUR0NdU09iV1RdV1NdV1NbUUhaUEddTUlk VFBqWlZvXltvXVdpV1FdWlZbV1RkXGNrY2pjYV9tamlubmtiYl9VTkFNRjpRSD5bUUdnXWNtY2l1 amlwZWRpXltjWFVeV1phWlxuX2JrXV9vYmJzZWVyYV9pWFdkWFxtYWRoY2RnYmNlWFZhVFFlVk9r XFVpX2NvZWlqY2VoYWNuZ2lyam1taGtuaW1uZW1tZGtlWl1kWFxoXWpuY3BvZ3BuZW9iW1FVTkVd UFBrXl5yY2hwYmdtYmFtYmFqX15tYmFtXFhrW1dkW15oXmJvZWtnXWNjT01eSkhfV1RoX1xyZG1v YmpqWlZoV1RiVFZkVlhhU1diVFhpW2JvYWhyam1za250aGlpXV5oVlpkU1ZlU1BqV1VvXFpuW1hr W1xvXl9uYmVtYWRpWFVfT0xbSD9VQzpVRDtYRz5eTE5kUVRlVE5fTkhfRDhdQTVaQT5cREBcRkBk TkhqV1FzX1p6ZWN7Z2R0Z2JyZF9vXFxwXV1yXl5rWFhqU1FqU1FuV1NqVE9lU0loVUxtVFBrU09q UFBoTk5tVE9zWlVwWlFuV09hTkhYRkBYRj9bSEFeTkpfT0xdVVRdVVRiUU5iUU5eVFBeVFBeUFVe UFViV1ZqX15rYWltYmprYWlnXGRkXFhhWFVfU05bTkldU09kWlZoYWFtZWVpXl1oXVxnW15tYWRo Z2tqaW5tamtraWpnZWpdXGFiVVBaTUhfT0xlVVFnV1BkVU5jVExjVExbU0BcVEFiW11qY2VyaGtw Z2poX1xiWlZoWl5tXmNrYV1lW1deV1dcVVVTT0xNSUZNQTpIPTVQQThURTtYR0BbSUNVRjxWRz1U TklYU05cVFNVTUxYSk1bTU9eW1NaVk5USD9PRDtMQTxKQDtOPThTQTxORz1TTEFXTUdaT0ldU1Fa T05VSkdUSUZWUExaVE9iV1ZhVlVeVlBeVlBcWk5aV0xeW1NlYlprYmVuZGhlXF9fVlpYU0xYU0xX UEdTTENcT0pqXVhrZWFjXVhfVU9aT0laSkNnV09lY2dqaGttY2RrYmNlXmFeV1poYWVqY2hrZ2p0 b3N0b3BqZWdaVUlOST5RR0FdU01lYmpva3R3bXB0am5oYWFiW1tcWl1cWl1lXmNoYWVzaHBwZW5w YmdoWl5lXmFrZGdqZWdlYWJkW15iWFxlXF1oXl9wY2tvYmpoYWVpYmdraW1wbnJta3Bram9vaGpt ZWhlX1hiXFVlXGJrYmhqY2VlXmFcVUxWT0ZdU01oXVdoXF1pXV5pX2FrYmNwZGVwZGVoW1hlWFZn W15rX2NyYWJpWFpkUUhnVEpoXmJtY2d1aHBuYWlqV1VqV1VjVlZkV1djVVpnWF1oX2tuZXJ3b3J1 bnB5ZWNnVFFjU1FiUVBpVlRwXVtzYmNvXl9uXVpvXltoXl9oXl9lVk5WRz9UQzdUQzdTQzdaST1f TUdhTkhfTUdbSENbRjlYRDdcREBfR0RdSkhlU1ByXmF4ZGd6ZWh5ZGdwXlhyX1pyXWJ3Ymd1YmJo VVVhUUpfUElnVlVnVlVrWlBqWE9pV1BkU0xeSENiTEZoT0hzWlNvWFNrVU9jT01cSEZaSD9cSkFf T1BiUVNfUVReUFNfU05jVlFfV1ReVlNeVlVdVVRfVVFkWlZoXWhrYWtrYWlpXmdnXFtkWlhjV09h VU1fWlNiXFVlXl5pYmJuY19vZGFtXmNqXGFqY2Vyam1wcHBwcHBqaGdjYV9nV1BfUElbU09kXFht Yl5oXVplXVpiWlZfWlNfWlNlXF9wZ2p3am53am5uZV9qYlxqX15qX15rYV1qX1xiYVtcW1VWTURJ QDhJPjVIPTRMQzpRSD9fVURkWkhfVEhcUEVUUVBXVVRdVU9aUUxXSkZbTklWVlRYWFZcTUNURTtI QC5IQC5JPCxPQTFVSjxXTT5aSkReT0hbSkdYSEVVSklXTUxYU05eWFRjVlRhVFFeVlBeVlBaVlNf XFhiYmJpaWlwaHRwaHRpW11jVVdaU0lcVUxaVE1RTEVaVFxoYmpuaWhkX15kXFheVlNcVk9pY1xv c3ltcHdvZG1vZG1qZ2NiXlthXFtpZGNoZ25zcnl3a3RtYmpeXE9WVEdTUEViX1RvZ250a3N4a21z Z2hlYV9jXl1eW1deW1djXF5uZ2l0aXJyZ29yY2hnWF1lXmFqY2VqZGpnYWdnXGRnXGRkYWtoZG9u ZW9vZ3BvYm1wY25rZWtwanBtaXJuanNzaHNwZXBpY15iXFdjV1hjV1hiWFpdVFVdVEpXTkVbUUVk W05nXFttYmFzZ2p1aW11aW9vY2lkWFBiVk5pXWNvY2luZV9qYlxpYV1pYV1yaGtuZGhvXl9oV1hn VU9oVlBnXVRlXFNnWF1rXWJtZHByaXVuaGVqZGJrW1dlVVFhVlNkWlZtX19vYmJvXFxlU1NiVVNk V1VuXV5qWltoU0VbRjlXQzhWQTdaRTdjTj9jUEdlU0ljSkdfR0RaQzdbRDhbSTthT0BfTkhnVU9v Xlt1ZGF0YWFvXFxuWlp1YWF+a3J6aG53YWVqVVpfUEleT0hkU1hvXWN1ZGF0Y19qXE5dT0FcREBi SUZrVU93X1p0W1ZuVVBjVE1eT0hfTkViUEdnVlVnVlVeUU9eUU9hUUpiU0xfVU9fVU9fVVRfVVRa VFFcVlRfV15iWmFqXmJpXWFnWlpoW1tiV1ZfVVRcVVViW1tlXmFrZGdraWpraWpqYWdnXWNqY2Vu Z2lzbWp1b21yaWhvZ2VpXltkWlZnXFtpXl1pYV1rY19lYWRkX2NpYV1lXVpkW1xpX2Fza2t0bW1t Z2JtZ2JtZ2RpY2FtZGFrY19uaGNpY15fVEhRRjtQQDFOPi9KQDtUSURfXVxlY2JkYVhdWlFTUUxa WFNiVVBdUExcT09bTk5UVFFaWldeU0lbT0ZMRDJMRDJRQzlURTtaSkRdTkdeTEldSkhYTURVSUBT SENYTkhbUVNeVVZiV1ZhVlVcVFBdVVFVVFBcW1dlZGtwb3dzaHBuY2tkU01dTEZXSkZYTEdcUVBa T05bVV1pY2trZWtkXmRnW15iVlpcW1dta2h1dHlwb3RtZWprZGlpZGNfW1pfV1RoX1xkZGdtbW90 amtpX2FdVlZWT09aVlNkYV1vaGpwaWttY2doXmJfXV5eXF1iWlZkXFhqX2h1anN6bnR3anBwYmRl V1pjWl1rYmVqZG1lX2hhV11iWF5kXGNoX2dtZ21vaW9wYWppWmNjW2JnXmVuY25tYm1nZHJnZHJr YmNlXF1iW11iW11eWFZUTkxXTkVaUEdeWFRrZWFzZ2h3amt7bnl3aXRzZ21rX2VkV1dqXV11Z2tz ZGluZWRvZ2VqZWdoY2RuZGhtY2dpWFVnVlNkV1VnWldrYV1qX1xqXGFvYWVzX2pzX2pwZGVrX2Fo XFRjV09hVlNjWFVrXlxpXFpnU1BeSkhbSkdhUE1tWFtrV1pqU0hjTEFfSj1dSDtoT0htVE1vXltq WlZkTklfSUVcRzlaRTdWRjphUERiT09tWlpwX2F0Y2RyXV1rV1dvXWF5Z2p+am13Y2VvW1hnU1Bk UUxlU01qVl9yXWd0YV5uW1hoUUBdRzdkRkdvUFFrVFNzW1p0W1dtVFBhUUpfUEldVVFhWFVnWFtn WFtjV09fVExdUUheU0ldU01dU01bTk5XSkpWTkpTSkdYTkhbUEpeU1RiVldiVVVlWFhiVVVcT09Y U05eWFRkWFxwZGhqZ21pZWtoYWVpYmdqZWdqZWdvY2d1aW1vZ2VuZWRpYV1qYl5rYV9pXl1pY15q ZF9uYmNtYWJqXVtqXVtnYVpqZF1wa210b3BzbWp0bmtva2hraGRqY2NuZ2d0am5tY2dkWlRcUUxb UD9cUUBPTEZUUEpfX19kZGRpZGNeWlhWT0ZYUUhdUUldUUlaUU5XT0xYUEpcVE5fWE5cVUpVST5U SD1WSkNcUEheVFBfVVFjT01dSUdTST1ORTlVSUBbT0ZiV1RkWlZjVlRkV1VVT0pWUExXV1dfX19o Z25ta3N0bW9rZGdlU1BYRkRaR0dfTU1fVU9YTkhjXF5uZ2lqZG1eWGFnW15jV1tdWlRybmh3dHhw bnJqYmtoX2loY2RiXV5fWFhiW1tlYmhuanBuZ2diW1tYU0xUTkdbVlViXVxoaGpjY2VlX1tiXFdd W1pbWFdkV1dtX19ya3R3cHl6cHdvZWtnW1xeU1RhV11oXmRiXmdbV19aUFFaUFFfVVRjWFdnYWdr ZWtqXmJjV1tiVldjV1hlXmNpYmdnZW1kY2ptY2lqYWdlYl5eW1dWU01PTEZWTk1kXFtyaXN6cnt5 b3N3bXB5a3R3aXJ0Z29tX2hqXl9uYmN1aHB0Z290aGtwZGhqY2VnX2JtY2duZGhtXFhpWFVuXVpy YV1yX2N0YmVuYmhyZWtyY2hzZGl1Yl9zX11oVk9hT0hkTklpU05oTk5qUFBlTkReRz1bST1jUUVo VVdnVFZoUFFnT1BnT0VrVEl3XFx5Xl55ZGduWlxiUEFbSTtXRjpbST1bSUNkU0xkV1dpXFx4Y2Vz XmFtWFZwXFp0Ymh6aG57aW10YmVrV1doVFRoTk5pT09pVVptWF1yWFVoT0xkSDFeQyxlRkRuTkxp T1FqUFNnU1BiTkxcT0pYTEdfT1BkVFVeXF9fXWFfWldaVFFaTkZYTUVcUEhaTkZXSkZXSkZWTUNR SD5XRUNbSEZlT0lqVE5pV1FqWFNhVE9cT0pUTkdXUUpfWFhnX19qYmltZGtuY2ttYmptZWhuZ2lu Z2tyam9ybWtvamlqYWJrYmNuY2JwZWRvZWduZGV4Z2h4Z2hqX1pnXFZiXFdlX1tqZWlzbnJ5cnd3 b3R3bXBwZ2pqXV1yZGR9cHR5bXBrYV1lW1dhVUliVkpcVk9YU0xeXF1nZGVlX11WUE5XTUdYTkhY TEdYTEdUTkdUTkdTTExVTk5dU01bUEpXTERdUUlkV1dnWlpnYV5fWldjU09cTEhYTURTRz5VSUBi Vk1jW1dlXVppXFdhVE9WTkhaUUxaWlxjY2VpZ2hua21paWdhYV5kUVFbSEhVSkdbUE1iV1RhVlNv Y2dyZWliW11cVVdYT1BkW1xtYWd5bXN3b3Ryam9lXF9iWFxjW2JiWmFeWlhcV1ZiWFxpX2NjXVha VE9cUEheU0peVVZjWltpYmdnX2RoXl9nXV5oX1xhWFViWFplXF1vZ3BwaHJzZGdpW11fVVFbUE1c UFRnW15kX2NcV1tYTkhWTEZXT0lcVE5kW2FlXGJfWldeWFZhVVhiVlpnW15pXWFoX2dpYWh1Y2t1 Y2tuYmNnW1xiV1RcUU5hVE9zZWF5coB7dIN6cHR4bnJ9bXd4aHJzZW5pXGRqXGFuX2RwYWpzY210 aGlwZGVqYWJqYWJrXWJtXmNqXF5vYWN4ZWl+a29yY2hrXWJkXWJlXmNpW2JuX2dtW1VnVU9nUEhi TERjSkRkTEVoTkppT0xpU0pnUEhnV09nV09vV1ZwWFdoVVVjUFBnTklrU05uXF90YmV6YmFyWlhl VEdhT0NjUEdjUEdlTkRoUEZqWFNwXlh0W1dzWlZtVVR0XFt1YWV3Ymd3Yl9yXVtnVU5hT0hcSURd SkVjTUhnUExnTklnTklvUEZzVEl4VEp6Vk1zVk1pTURfTUdeTEZlTUZiSUNhTUplUU9hV11jWl9f VFpXTFFWTEZWTEZaU0lbVEpYUEpWTkhTSUBRSD9YR0FcSkVhVFFnWldrY19nXltpWFVhUE1YTU5d UVNfVV1lW2NoXmJrYmVuZGptY2lvY2RzZ2h0anB0anBzaW9vZWtrZGduZ2l3ZWd5aGl3aG10ZWpy ZWdvY2RlW1plW1pjW1pnXl1raG5wbXN0cHl4dH13b3RqY2hrYV9vZGN5bXN5bXNzYmFpWFdfWlVc VlFcW1VbWlRjXl9nYmNkW1FcU0lbTkliVVBhVlBYTkheT0hdTkdYSk1WSEpbSEhbSEhWTEZYTkho XGJuYmhrZ2pjXmJfVVRfVVRcVE5VTUdjUFBqV1dtXmNzZGlnZ2RdXVtXVFBXVFBbWl5hX2RoYl9z bWptamllY2JhWFNcVE5eUU1eUU1cUVBhVlVkX2NpZGhfU1BaTUpcTEpnVlVtX210Z3R5bXByZWlf V1FeVlBfVFdfVFdfV1FdVU9iVVBpXFdoX1piWlRqV1dlU1NnWlprXl5yX2NwXmJrX2NvY2dqY2Nk XV1fWFtiW11qZG1rZW5pYmJfWFhUT0VTTkRYUEpeVlBfWldhW1hcVE5WTkhXT0xaUU5fWFtkXV9l XVxkXFtkV1dkV1dfWFhkXV1nXWFuZGhvZG1vZG1uYmhoXGJkVlhfUVRhVlVyZ2V3cHt+eIN9cHd6 bnR+bnh5aXNyYmtnV2FlWl1uYmVuZW9waHJwaGdzamlvYl9rXlxpX2NoXmJtYmp4bXV6bnJ4a29v YWNlV1pfU1BlWFZiVVNlWFZqWExnVUhrUU1pT0pqT09uU1NzXmF1YWN3X1pzXFZvXFpyXlx1XGJz Wl9tVlBlT0lfUEllVk9yXGF0XmN3XVpvVlNqUE1pT0xlT0doUUlpUUdvV01wXVZ1YltyXV1tWFhl U1BnVFFtWF10X2RzWlVyWFRlVEdeTUBfSUFeSEBlTEdrUU1vVVVzWFh7X2J/Y2WDaGN7YVxuVkxt VUpoTkllTEdhTkheTEZcSURfTUdlU11kUVxfUVZXSU5YST9aSkBdVEphV05dVEpYT0ZRSDxPRjpX SEBXSEBdTE9qWFxwZ21qYWdkWFpdUVNaTlFeU1ZcVVdhWlxnXl1pYV9qY2NpYmJpX2NtY2duZW1u ZW1rYmVrYmVrZGduZ2lyaG50anB0amt0amt0amtuZGVrXWJrXWJoYl9lX11uaHBzbXVycHp3dX93 a3RtYmpkXlxnYV5ybW5ybW5yZF9lWFRjWFddU1FbWFdeXFtrZWtnYWdkWFBkWFBhWFVoX1xiWFpc U1RdUExdUExdU1FXTUxcSUNcSUNVTEFXTkRkWmRrYWtpZ2pfXWFeWFZfWldnXFtYTk1hT1VvXWN4 b3l6cntvbXBhXmJdUUldUUlWV1NVVlFhXl1qaGduY2JrYV9kWlRjWFNlWlFeU0pVTENcU0liWmFi WmFcT0pTRkFVSEheUVFjXGFoYWVvZG1tYmpkXlpcVlFeUU1cT0pdVEpbUUhcTEhlVVFuYmNyZWdy ZGJwY2FyY2VzZGdoX15qYmFkXV9vaGpoZWdlY2RhXF9dWFxjYWRpZ2plZWNfX11bVkxXU0hYTk1Y Tk1fTFBkUFVdUExdUExWTk1XT05cVVpiW19oXGJnW2FkWFxhVVhdVFViWFpjXF5pYmRvaG1waW5v ZWlpX2NkV1VjVlRqVl15ZGt5cHp6cnt4b3lzanR7a3h3Z3NqWGFlVFxnXWFtY2dvYm1zZXBwZ2hu ZGVoXVplW1dpXWFuYmV5a3d7bnl5bW5zZ2hoW1ZhVE9lVE5oVlBjU09pWFVvXFZvXFZwWFpvV1hy WGF3XWV6ZHB9Z3N7Z2R1YV50YWF1YmJ4X2FvV1hkTEVfR0BhUFFqWlt3YWV0XmN0V05qTkVpUE1q UU5oVU9qV1FtW1V3ZF51aGhyZGRtXFtnVlVlT0dpU0prWFhvXFxwXlduXFVtW0xpV0hiTERfSUFn TkpwV1R7X2t/Y2+CaG6EanCCbW19aGh4XVpzWFVyVUluUUZoV1hhUFFaSUZdTUliUFhjUVpiVVNa TUpYTjxbUD5eVFBfVVFdVEpeVUxWSUdYTElYTU5YTU5hUFFqWlttYmptYmpjXl1bVlVRTEdPSUVU TUNYUUdeVlBkXFZkX15nYmFpYmRqY2VpXmdtYmpwYmdtXmNrX2FrX2FtaGluaWp0bXJ1bnNybW5u aWpvZG1yZ29waW5uZ2tuaW1ybXBranJycHh4bnJrYmVkXV9jXF5qaGdtamlvaF5lXlVdWlRXVE5a U1ViW11oZG1oZG1pXl1tYmFzZGd1Z2lpYV1iWlZnXFhoXVpiV1ZcUVBbT0ZcUEdcTUVdTkZfWl9o YmhrZ2pjXmJoVlprWl1rW1pfT05hVlV3a2p4cHN4cHNzbm1kX15hT0ldTEZVT01bVVNjV1tqXmJt Y2RrYmNtY2RvZWdwY15jVlFVTEFVTEFbU1peVl1VTE1RSElNR0VXUU9aUFRkW15uX2dwYmlpZGhj XmJdWFdbVlVcT01cT01fT05tXFtwZ2p3bXB5bXB1aW15am96a3BvamtrZ2hkXV9rZGdpaWlnZ2dk YmNdW1xjYWJua21wZ2puZGhrY11kXFZcUEhbT0ddUExiVVBiVVNkV1VdV1VbVVNaU1VaU1VcU1Ze VVhjWl1fVlpiVldjV1hfWF1qY2hwaHRzandzbm9rZ2hoX1xjW1dpX2VzaW96cHd3bXNzaXltY3Nz Y29vX2tpW19nWF1nWlpuYWFpXV5vY2RyZ29rYWlnXV5kW1xoXmJrYmV0aXJ3a3R1amlwZWRpVVNn U1BiUU5iUU5pVFhwW193Y2VvXF5rWlRtW1VyXGV0Xmh7ZXJ+aHR7aXJ5Z291aW1zZ2pzWlNlTUZe RUFjSUZnVE1uW1RvW1hrV1VrVElvV01yV1x0Wl5yYWJyYWJ0YmV4ZWl5ZGl3YmdtXVZoWFFoT0hu VU5wXV15ZWV+bW57amt7aWJwXldnTUZiSEFwVVx1WmF9YWWEaG2Ea2+GbXCCbmh5ZV94X1VwWE5t VEZlTT9oWlxfUVRXUEdYUUhaUFRaUFRbUVNWTU5VSUBYTURcUVBeVFNbU1FaUVBaTU1aTU1dU1Fi V1ZjVlFpXFdrYmhrYmhjXl1bVlVUTklOSERUTEpXT05eVFNiV1ZhV11lXGJtYWJuYmNpYWpqYmtu YWFhVFReVE5eVE5nX2JvaGpwbXVybndtZ29nYWlnXGRnXGRqYWRuZGhpY2luaG5wanV3cHt6c3hv aG1lV1phU1VlX2VtZ21raGRnY19eXlFVVUhYU0xeWFFkYmVqaGtqZWRpZGN4ZGd5ZWhtZWhuZ2lw aG9vZ25pXFdiVVBeWFFhW1RiU0phUUlfWl9lX2VtYWRnW15rXWRqXGNnX1ZcVUxhV1t4bnJ6dXdz bm9zbm9nYmNiVVNcT01WUE5bVVNiVFhnWF1rYmVtY2dua29yb3N0am5iWFxWRz1WRz1aTk9dUVNb UE1TSEVKRT5RTEVbTkldUExlWFhqXV1jYWJiX2FfXV5bWFpdUE5dUE5eUFNvYWNwbm9zcHJ1bm5y amp9bXd+bnh3bXByaGtoYWNoYWNkY2plZGtoXmRkW2FnX2RwaW53Z3NyYm5rYmNlXF1eU0lfVEpi Vk1lWlBkXFtnXl1jXVhWUExTSEdUSUhaUFRdVFdiVlphVVheWFZcVlRXV1VfX11oZXNpZ3RubXJo Z2tnYmVkX2NqZG1ya3R3b3R3b3RwZ21oXmRpX2NoXmJpYV1nXltlWFhtX19oY2dvam50am5uZGhn XFZoXVdqYWJuZGVybnR1cnh1bWduZV9vWFNqVE5eU0pjV09tWlxzX2J1Y2dwXmJrXFVqW1RyX2Nz YWR4ZWt9anB6bXV5a3R1Z2lnWFtkTD5iSTxjSUNnTUZtVE9yWFRtWFZrV1VtWlpzX194Y2N6ZWV4 YmdwW19zYWd5Z217Z254Y2pwWlFvWFBqUFVyV1x3YW9/aXiAbW19aWl7ZF9qVE9pSUhpSUh1WmF+ YmmEaHKJbXeHbXV+ZG17YWF7YWF3XVhzWlVuV0ZjTTxlW1ddU09aUEdaUEdWU09WU09hVFRcT09a SkRbTEVYUE1YUE1bU09aUU5XT0lWTkhWUElbVU5jWFdpXl1oX15pYV9lXVxiWlhhT0hXRj9WSUlb Tk5eVFBeVFBcVVdhWlxuWmNyXWdpZGVnYmNlXVxcVFNcUU5eVFBpXWNyZWtwanNtZ29qYWRjWl1p WFpoV1hnXWFpX2NoX2ltZG5vZG93a3d5cHhwaG9rWFhnVFRkWmJuY2tva3JpZWtiY1pcXVRXVFBV UU5dXV9jY2VoZGppZWt1Z2tzZGlvYm1uYWtuanNva3RvYl9uYV5qYl5uZWJjV09YTUVcV1ZnYmFo Xl9lXF1oXmJpX2NkYV1bV1RjXF53b3J6cHJ3bW5ubm5hYWFkXFhdVVFdT1FdT1FWUExWUExfW1xr Z2hzc3VwcHN1bnNkXWJYSUNcTUZWUVBUT05fTUdbSENQRTxYTURVTUdXT0lYVVFeW1dlXmFjXF5j Xl9kX2FdUE5fU1BlWl9wZGpvam5pZGhoY2JrZ2V1a3J6cHd3b3JwaWtoXVpjWFViW11iW11lV15n WF9oWl5rXWJqXmJtYWRoZGFnY19eWk9eWk9eWFFhW1RqY2NuZ2djW1dbU09WSk5WSk5cUFRfVFdj VVpiVFhhWFdcVFNYVlVaV1ZhYWNjY2VqZG1qZG1oX2dpYWhrY21uZW95a3d4anVuZGpoXmRlXmFn X2JqY2VqY2VnXmVoX2dnaHBub3hzaW1qYWRoXlVrYlhzZGl4aW54cnp3cHl1Z2luX2JkV1dkV1dl W1ppXl1rYmNuZGVvZGFtYl5lW1VnXFZzYmN3ZWd4ZWt7aW97aW95Z21vYl1kV1NoT0FnTkBrU09z WlZ1XFd1XFduWldwXFpzZGl3aG10Z2RyZGJwXV1zX19yX2N4ZWl5Y213YWp0YVpyXldtWlxvXF5w ZXB1anV9aG16ZWp1XFdlTUhnTUl0WlaAaGuGbXCGaWl6Xl59YWN5XV+CYmOGZWd9ZGh+ZWlwXk5c SjtkXFthWFdfVk1cU0lWUUdXU0hpWFdoV1ZhUUpeT0hdU09dU09hUE1kVFBaU0lWT0ZUSD9aTkVi VVNiVVNhVlBkWlRpXltnXFhnVlNdTUlfTkddTEVaT0xcUU5dVVRcVFNhV11jWl9oY2RnYmNjXVtc VlRbVVNeWFZkXmduaHBva3JtaW9qYlxjW1VlVVFoV1RhU1diVFhjVlRlWFZjV1trX2N0a3NzanJw Y2FqXVtqXl90aGlybndwbXVwamhoYl9bV1RWU09aU1NfWFhrYmVwZ2pvZWduZGVuY2tuY2tybXBv am5tamlua2p0bmtvaWdlXlVcVUxiV1RrYV1nZV9fXlhnX2JrZGdpXlthVlNjXF50bW94cHN3b3Jq ampjY2NnXV5kW1xlWl1hVVhQUUhQUUhXU1RrZ2h0b3N3cnVvbnNkY2hjWltoXl9hX1xYV1RYSUFY SUFWSkNYTUVRU0xQUUpTT0lXVE5dVFVfVldjXl9jXl9bW1hWVlRaVlxraG5tZGthWF9eVlVkXFtn Z2d0dHR1c3RraWprW1pkVFNdVFVhV1hhVVZfVFVbU09eVlNhWlBdVk1hW1hjXVtkXFZlXVdlXVpq Yl5za25rZGdkWlRfVU9cSUdYRkRbTU9iVFZrV2FwXGVoXl9fVldhVlNcUU5iVlpqXmJyZG9yZG9v Y2dtYWRrYmVuZGhtaXRuanVuZ2trZGlvYWVuX2RyZWlzZ2ppZGhkX2Nram9ram9qYl5kXFhoYl1w amV4anV6bXh5bnd1anN0a2pvZ2VqXmJpXWFoXmJnXWFtY2dwZ2ptZVxnX1ZhWFVjW1duYmN1aWp9 aG14Y2h1YmJyXl5uYlhnW1FpW0ppW0pyXlx4ZGJ5ZV51Ylt3ZWJ4Z2N5bXB3am51aWFyZV1vXFxy Xl5yYWJzYmNyXWdvW2RuXFZtW1VrW1x0Y2R5bW50aGl6YmNyWltoVU5tWlNwX1x3ZWJ1ZGNyYV94 XVZ1W1R1XV56YmN+aWt/am1+ZVtvV01iTT9aRThrXl5qXV1fV1ZhWFdeUU1fU05lWFRnWlVhV01c U0hiVVNhVFFjU09kVFBbUUhYT0ZRRjtUSD1XTERfVExfVVFiV1RqWltpWFplWFRhVE9aVERTTT1Q SENWTkhbTklcT0peVlNiWlZkXV9pYmRlZGFcW1dbV1FhXVdnX2Jyam1wa29zbnJ0Z2dtX19rXlxp XFpiVVBhVE9kWFBjV09lWFRoW1Z1ZW94aHJyZ2N0aWV3amt3amtybndva3RuaWppZGViWlZYUE1X TENYTURhW1hlX11oZGFqZ2NvY2dwZGhvZWluZGhua21tamtua2ppZ2VdXldVVk9aVlNjX1xpZV9j X1ptYmFvZGNnXV5eVVZiWmFuZW1zcHR3dHhwZ2hqYWJnX19kXV1iX2FeXF1UUEpTT0lUT1BlYWJw aW54cHVyaXBkXGNqZGpuaG5uY11nXFZaTD5aTD5XTk9dVFVeV1dXUFBMSkNRUEhYU1BbVVNeV1pf WFthW1hcVlRdW1xpZ2hpYmRjXF5iVVNfU1BhY2J0d3Vzb3VtaW9nXV5iWFpkWFplWlteVFBYTkpV SkdaT0xcVFBeVlNkXFhkXFhoXVdtYlxoaWRqa2duaWhrZ2VtWlNlU0xhSkNcRj5eTEZnVE5rXmd3 aXJvamlkX15hVUlcUEVkUVxvXGdwZW5uY2tkY19iYV1tZWVuZ2dtaGttaGtwZW5uY2tzaW11a290 a2huZWJyZ2VyZ2VzZ2pvY2dvXltwX1xvaGh0bW13bXB3bXB3b3JwaWtzaW1vZWlzXl5zXl5uY2Jz aGd1Z2t4aW5uZFtlXFNlWFZqXVtzZ211aW96Z2d1YmJzYmFyYV9tYVhrX1dzYVp1Y1x6Z2d+amp6 aWVzYl5waGdzaml0bXJ1bnN4aGFuXldtXVZtXVZwXV9uW11uWmFrV15rWlRuXFZyX2N1Y2dyXlxt WldnWEpkVkhtWFZ1YV53YmJ1YWF1Y1pyX1ZpVlBtWlRwW2J3YWh+ZWR7Y2JvV0FdRjFdQy9fRTFv X1hvX1hrX2FqXl9iUVNeTk9eVE5iV1FfW1BeWk9iWlZiWlZfV1RfV1RfU1NXSkpaSDxWRTlVRURa SUheTk1kVFNtVVZvV1hnXFZfVU9fVEhaTkNYSj1WSDtbTEVdTkddUExdUExfWFhlXl5qZWRkX15f WldeWFZiW1tpYmJqaGlvbW5wbm9vbW51aWpwZGViW1teV1dnVldpWFppVlhtWlxtW2FwXmRqYl53 bmp5b3N5b3N6c3hwaW5oYWFjXFxjWlBfVk1eTkpYSEVhVlVoXVxnYV5rZWNvYWVuX2RoX15pYV90 aGt3am5wZ2hpX2FhW1ZcVlFfWldiXFplX11kXlxpYV9pYV9pXFpkV1VlV1pzZGd3dHh5d3puZGVk W1xnXV5oXl9fYmFfYmFVU1FOTEpRUE1fXltwZ2p3bXBvZWlnXWFwanNvaXJvZGFlW1dcSkFlVEpf VldeVVZfVVFeVFBaSUZeTkpcUUxcUUxaU1NaU1NiT09jUFBhV1tpX2NpYV1kXFhhV05dVEpiYWVy cHVvcHRoaW1vYl1uYVxrY19lXVpYUUdORz1USUZeVFBiXVxnYmFpZGNnYmFpYmJtZWVtaGduaWhr Y2JqYmFqV1BlU0xfTUZbSEFcUEhfVExkXGVwaHJ0anBvZWtjXFNbVEpkW2FqYWdwaWttZWhlXmFi W11kXV9oYWNqYWdrYmhwZGhyZWlza250bW9zamdvZ2NwY2FwY2FvYWNrXV9wXGFzXmNwZ2hwZ2hy aGlwZ2hwa29rZ2pwZGhrX2NwX15yYV9rXWJ4aW57b3N5bXB1ZV1qW1NqVlhwXF55ZGt4Y2p6Z2F0 YVtrX1drX1duXVpyYV14Y2F9aGV7ZWp6ZGl3ZGpyX2VvZ2VzamlzZGt0ZW15YV9yWlhrW1prW1pw XFxtWFhoV1RqWlZnV1BrXFVyXmFvXF5tWlNqV1BqVE9rVVBvW1twXFx1XVx1XVx3X1dyW1NoTklp T0puVVt1XGJ6W1NwUUlhSTddRjNjSDRoTTltX11rXlxtYWRoXF9fU05eUU1hVFFjVlRhV1hkW1xq Xl9uYmNnXV5kW1xkW1xcU1RlT0ddRz9RRDdTRThYSEViUU5lU1NqV1dlWFZlWFZlWlFjV09dTENV RDtTSUBRSD9USD9VSUBVTE9fVlplY2dkYmViXWFeWl1eVlNjW1dnYVxpY15raG5uanBwZ2pvZWlt YmFrYV9pXmdrYWltX2hrXmdkWlRfVU9eXF1ua211c3d3dHh5cnJtZWVqXV1lWFhpWFVjU09XSkZW SUVYUEpdVU9nXl1pYV9qYl5kXFheWFRlX1tuYmNzZ2huZWJlXVpdVVFaUU5bVVBcVlFjW1dkXFhl XVdnXlhtWlpuW1tqXmRtYWdzbXV6dH1pamFeX1ZiXlhhXVdbW1tdXV1VU1RQTk9eVVhuZGh3a3d0 aXRoYWNkXV9uanVva3dtaGlpZGVjV05lWlBfWlNdV1BhVUlfVEhkUVFlU1NnW1xiVldYVElVUEZW SkNcUEhdVVRnXl1oX1pjW1VnVlViUVBnYWd0bnRzanRwaHJwZ2puZGhraWpqaGleU0dVST5cT01q XVtta2Vta2VuaWhuaWhza2t1bm5vamlrZ2VqXVhjVlFlVk5jVExjUUhkU0lhV05hV05iWFpqYWJ1 ZW91ZW9uYWFlWFhkXV9lXmFrZ2hoY2RjXF5iW11hV1tiWFxkWFpkWFpqWF5vXWNtZWhrZGduX2Jr XV9uXF9uXF9pXltoXVprX2NtYWRtZGNoX15oXmJoXmJpX2VtY2lwY2NnWlpkV1VqXVtvZG11anN5 bW5yZWd0W1RpUElkUU9pVlRyXWR1YWhzX1ZuW1FrV1VqVlRuWlp0X196ZGl+aG14ZWlyX2NrXV9t XmFvY2l1aW90ZW1wYmlzX1pvXFZqX1xrYV1tWlBlU0lhT0NhT0NhUERlVUhnVE1nVE1tVlBuV1Fr U0xtVE1tVVZwWFp0XF91XWFwVlZyV1duVVFyWFVzXFR0XVV3V0xwUUZnT0NoUERuW1VwXVdnX1Zk XVRlW1ppXl1nW09cUEVdTENhT0ZfU1NlWFhtX19tX19pY2FoYl9oXl9kW1xtXVZnV1BcVkZWUEBY T0NcU0ZiU0xjVE1kV1dkV1dhV1hjWltlVVRhUE9YT0ZWTURYTURbT0ZaTk9eU1RiXWFhXF9dVlZb VFRdVVRfV1ZfXFhkYV1nY2ltaW9pX2NpX2NwY2FvYl9oYWNqY2VqY2hqY2hiW11YUVReW2FnY2l4 b3d5cHhyb3BpZ2hqXVtoW1hlWFhfU1NWTkhTSkVcUU5dU09lWltqXl9pX2NkW15hW1ZjXVhnYmVu aW1uZGVoXl9cVk9XUUpbT0ddUUlqXVtpXFpoX1poX1ptYWRrX2NrZW5uaHB1b3p1b3ptamtlY2Rh X1phX1pfW1pbVlVVT01WUE5jWl1yaGt1a29rYmVfXVFhXlNnZ2ltbW9zZ21qXmRoXVdnXFZnW1Fl WlBrXFFrXFFvYmJtX19nXFZhVlBXT0lVTUdTTUhXUU1hV1hjWltjXVZjXVZpWlFnV09nYmNqZWdq ZWdwa21vb3JtbW9ubXRubXRkW1BaUEZkWFxyZWl1bnNza3Bya2dwamV1bnB7dHdybnRqZ21tXFtn VlVnXVNpX1VuXVpuXVplXFNkW1FlXVptZGFoZWdtamtuZGVlXF1nWlpoW1toX2dqYmljXF5iW11f VlphV1tiV1ZkWlhrX2FvY2RvZWlvZWlrXlxnWldkV1dhVFRhWFViWlZnXWFoXmJpXltkWlZjVlFk V1NiWFxlXF9tW15oVlppW2JqXGNvZG10aXJ3YmtuWmNuVFhrUVZkV1dtX19zYWR0YmVuW1hpVlRp V1BoVk9uWlxzXmF0YV5yXlxqWlhpWFdiWFpnXV5wZGV1aWp3ZWRyYV91ZGF1ZGFwZWJyZ2NtYVhi Vk5iUERdTD9eSTxiTT9iUElqWFFuXlZyYlpwWlRuV1FvV1t3XmJ0YV5yXlxvXlFvXlF0Y199a2h6 Z2R0YV5zVUZvUUNrUU5zWFV0W1Z0W1ZkXFZkXFZjXFNqY1plXVpaUU5eRzteRztYTURfVEpdW15j YWRnYmNoY2RpYV9nXl1pYmJpYmJnXFZdU01hV05iWE9jWk9eVUpfVVFfVVFfV1ZiWlhpW11jVVdd VEpbUUheT0dhUUlfVFVdUVNdU09cUU5hUE1hUE1bVU5YU0xXVk5VVExdVlhpYmRlX11nYV5qY2Nr ZGRwaGRuZWJrZGdnX2JkW1xeVVZeWl1nYmV4b3l6cntycHVpaG1pXFpoW1hlXFNjWlBhV05YT0Zi UEljUUpiWlZlXVpkXlxeWFZfVVFnXFhpZGhrZ2prY2JnXl1hXFFaVUphVU1kWFByYWJ1ZGVrZWNr ZWNtXmVtXmVoY2dtaGt5bnl3a3dwaWtqY2VoaWRhYl1cXFpWVlRYT1BbUVNjW2JoX2dnYmFiXVxi WlRjW1VlY2dqaGtqYlxkXFZjXFNqY1ptYVduYlhzZWF1aGNzbmNuaV5uYlplWlFcU0hUSkBQSkRU TkdeWFRlX1tnXlhiWlRdVkxeV01fWFhqY2NoaGpra25qbWtoamlqaXBubXRkX15fW1ptZG51bXdy bndwbXVza255cnR4dH19eYJ3cnNuaWpnW1xlWltrYV9vZGN1YWV1YWVvYl9tX11qZF9uaGNpZ2hv bW5vaGhkXV1kV1NjVlFiWF5oXmRlXVxbU1FdSkpeTExdVVRlXVxuZ2l0bW91aWpyZWdvYmJqXV1i VlphVVhhVlVlW1ppXV5oXF1oXVxkWlhiWE9hV05kVlhkVlhnWFtoWlxnXFtqX15tYmpzaHB1YWVw XGFoUFFkTU5iUVBvXl1zX2JvXF5rV1dqVlZlWFZnWldpXFxpXFxuX1FtXlBrX1ZrX1ZuXF9zYWR0 aGl3amt7aGh9aWl9a2p9a2p0aGl1aWpzY1hvX1VuWEpiTT9kSTppTj5rV1d3YmJ1aGVzZWN0Ylxt W1VvW1hzXlxyXlh0YVt1ZFd5aFt4bWt+c3J/a2t1YmJyU0dvUEVtT01vUU90VlN0VlNnVlVoV1Zp V1BtW1RoV1hnVldiVEZbTT9XTz9aUUFVVVVfX19pXWFrX2NtXFtrW1pnXFhnXFhqWltoV1hpW11q XF5oX1plXVdkV1NeUU1bVEpaU0lhVlNhVlNYT0ZbUUhiVVBkV1NiVFZdT1FeT0hdTkdbTklcT0pb VElaU0hbVEdaU0ZVT0hcVk9iWlRkXFZnYmFuaWh3bm10a2puZWRoX15kXlphW1ZdVlhiW11lZWhy cnR4c3d3cnV0Z2dtX19pX2FvZWdvaGhjXFxjV0xiVkplXlViW1FlW1dhVlNfTU1nVFRlXmFtZWhu Y11nXFZoXlVfVk1iVk5qXlZ+a299am50b3Bwa21tY2dpX2NiXWFnYmV0bnd0bndvamluaWhqamhj Y2FbW1tUVFRWTk1YUE9hWlpkXV1oX1xnXltiWlhiWlhpZGVuaWpoYVZiW1BrYVt0aWN1cm5va2ht amttamtvcGtub2ptZ2JqZF9jWk9cU0hVRjxXSD5dVEpiWE9tXFtoV1ZhWFNhWFNjWlBpX1ZpY2Fo Yl9kYV1raGRqaGtvbXBqZWdiXV5pZ2pyb3Nwa29wa29ubXJ0c3h3dX10c3puaWpoY2RoXF9tYWR0 aG51aW95ZGt3YmlrYmNvZWdvamtybW5wbm9yb3BtamlqaGdpX1ZlXFNfWFheV1dkWlRfVU9kUFBl UVFkWFpuYmNvbnVta3NwaGRoX1xoXmJnXWFoVFtlUVhcU1ZfVlphWlxhWlxnXFhjWFVhVU1fVExj WFdjWFdiWlhfV1ZeVE5kWlRrYmVrYmVwXV1pVlZnTkdnTkdkUVFvXFxyXVtuWldzWlZ3XVpwXVdu W1VvXVZyX1h0aWN3a2WAa2l+aWd6Z2l6Z2l3aWl0Z2d6ZW1+aXB9anB6aG50aWVzaGR6aWV7amd4 YVhqVExlTUBvVklwXmR5Z217amd1ZGFrYVtqX1pwXVt1Yl93X1dvWFBqV1VuW1hoXmJzaW15bmpz aGRvU0lqTkVrTkhuUEp0VFN1VVRnXFhiV1RqV1VoVVNpVlhpVlhjWk9dVElcUEVdUUZdVkxkXVNu YWFtX19pXFxjVlZfVExiVk5lW1dpXltoXl9rYmNrYmNqYWJjXVtbVVNfT0NcTD9YTUVbT0dbT0df VExkWlZpXltkVlhhU1VjU1FfT05fT0xiUU5jWFNjWFNhVUxdUUhhVU1oXFRjW1VoX1prYmV1a291 cHJ3cnNoZ2NkY19kY11jYlxjW1dlXVptZWV4cHB9dHt+dX13b3J3b3J4bnJ7cnV9cHdwZGpqYl5p YV1vY1tpXVVhWFVaUU5aUEdaUEdhV1htY2RtaWFnY1tlX1teWFRlWFZvYl93bXN4bnR1cnhwbXNr aWhlY2JlYWJjXl9jZ2plaW1wa211cHJzbm9uaWpoXVdfVU9WUUZRTUFeVFBnXFhqYWJqYWJrYmNt Y2RpZ2VpZ2VnXlhpYVtwZ2p3bXB0b3Nvam5vaGh0bW13bW50amtramdramdpZV9dWlRYUUVTTD9e U0ppXVVuYV5tX11jX1diXlZlW1dnXFhkXV1iW1tkXV1pYmJpZW5qZ29iX15iX15vamtybW5taGlv amttcHdwdHp5d3p0cnVyZWllWl1iXWFpZGh0a3V3bnh3bXNwZ21oYWNrZGdvZ3NwaHRyam1waWtw a2pybWtrZ1tkX1RhV01iWE5hV05hV05hVFRiVVVoXF9tYWRyZ29wZW5vYmJpXFxoWlxiVFZiT09i T09fUVRfUVRdUVNjV1hqWFFrWlNtW1VqWFNnWlVhVE9hVFFhVFFkVU5qW1RtX11rXlxwXFpqVlRj UEdiT0ZlU1BvXFppXltuY195ZWV6Z2d4ZV9yX1p1Ylx+amR+b3SCc3iDbnN/am93ZWd1ZGVzZGdw YmR1Y2l5Z214Y2h3YmdvZGN1aml+cnN9cHJ7Ylt3XVZuW1RzX1h5aGl7amt4ZF1yXldtXFtuXVxz XFd1Xlp4WlRzVU9fVVFlW1duX2J5am17aGh6Z2dzV0drUEBtVEd0W057WFZ/XFpkV1dkV1dpWFVo V1RoV1RlVVFhV05fVk1eU0pfVExeVUxiWE9jWFdoXVxlWlthVVZeUU9eUU9kVU5nV1BoW1tqXV1l Xl5jXFxnXl1eVlVeTEZdSkVVUEZVUEZXUUpbVU5lW1ppXl1qXl9kWFpfWFhbVFReWFZjXVtlX11j XVtnWldiVVNnV1BqW1RqXV1rXl5rY2p0a3Nyb3Nyb3NtaGlqZWdpZGVoY2RlXVdnXlhlYWJzbm97 cHuAdYB6dH19d399dXp9dXqCeHt1a293b293b293c29wbWloYl1bVVBUSkBUSkBXVExnY1tkZV5t bmdqYl5nXltjWFVuY19zcnl4d351dHlvbnNza25uZ2lqY2VjXF5hYWNnZ2lwaHJ5cHpyb3BqaGlk XFZcVE5QTEBQTEBhVFFtX11zaWpwZ2hvZ2VuZWRraWppZ2hrZGdvaGpva3RybndvaGpqY2VrZWNy a2l1bnB1bnBvaGhvaGhraWplY2RiWlRaUUxfVVRoXVxpX2VwZ21tZGFoX1xnW1xlWlthWlpjXFxo XF9qXmJqYWdtY2ltYWRuYmVybXBwa29vaG1waW50bXJ4cHV5dHV1cHJuYmVhVVheXF1pZ2h7bnt7 bnt3aG1rXWJkWF5lWl9qX2hoXWVpXWFtYWRuZ2l1bnB0aWNuY11eW0lYVURbUUdbUUdfU05hVE9l VFprWl9oXmJnXWFoV1ZkVFNkVFNfT05iUEpkU01kVFVkVFVlU1NpVlZrW1pwX150Y19tXFhqXVtj VlRkUVFoVVVuWldtWFZuWlpwXFxyXlhrWFNeU0dhVUlkVU5tXVZrY19vZ2N5aGl4Z2h3ZWd4Z2h6 Z2mCbnB/c3l+cnh/a2t1YmJtW1RpV1BtXmFyY2VzZ2p3am53ZWd0Y2RtY2lzaW96bXV5a3R3Z15z Y1twX1x0Y194ZV91Y110W1ZwV1NtWFZrV1VzWFV3XFhzWFhyV1dnVU9uXFZ0X2d/anJ+am14ZGdz WkxuVUdyWFRzWlV6V1OAXVhrWl9vXWNwX1xuXVpuYWFrXl5lWFRqXVhoXFRkWFBjWlBkW1FfWFtl XmFjXGFkXWJlUU9iTkxfTkViUEdqV1BuW1RlXVdkXFZnXl1iWlhiTkxkUE5eV01eV01iWlZhWFVl W1plW1pkXV1iW1teW1dfXFhlXVppYV1nX19nX19uYV5nWldoXFRqXlZpXV5pXV5oXmJwZ2p1bnB3 b3Jvamlwa2p3amt0aGluX2RoWl5jXFxrZGR3bXN9c3l3c3t1cnp5dXt5dXt7eXh5d3V4dXl5d3p7 dX5zbXVnYmNaVVZXTkVXTkVXWE9naF5tbWpycm9nZGhnZGhkW2FwZ214dH97eIN1cnhwbXNraG5o ZGpnZGVlY2RfXGJoZGpycHpwb3lpZGViXV5hWl5kXWJXTk9eVVZrYmVzaW14bnJ1a29rZWNpY2Fj Y2NkZGRtaGt0b3N0b3Bwa21pXV5oXF1pZGVzbm91cHJ1cHJza2tvaGhtaGluaWpuY19uY19rY2Jr Y2JwZGh1aW13a2hyZ2NuY2JtYmFnX2JjXF5lXVxjW1ppW19pW19tW2NyX2htaGtzbnJzbm9wa21w a291cHR6b3h4bXVvYWNlV1piW19qY2h3a3d1anVvXl1iUVBeU0phVU1kWlZnXFhiW11qY2VoZG1t aXJ3b29waWlpZV1WU0paUEdkW1FnWldiVVNeVFNnXFtkW15eVVhjVlZlWFhoXVxnXFtrXlxrXlxn W1xlWltrV1xvW19qXF5wYmR1Z2lzZGdwYmRjVVdlU01kUUxqVlZuWlptWFtwXF5uV1FoUUxeUENf UURjUUVrWk1zXl56ZWV7aGh7aGh7Z2d7Z2d+b3eAcnl5bXByZWlyW1NpU0plT0lpU01tXmF1Z2l3 a2p1amlyX1hzYVp0aGt3am55bW53amtwZV9rYVtrW1puXVxzXFZuV1FtVUpuVkxrWFNtWlR0W1Z1 XFdzX1pwXVdrV1VvW1h0Ymh+a3J9bWV5aWJ4YVhwWlFyWlh4X15/ZF+AZWFtXWdzY210am50am50 a2pvZ2V0ZFxyYlptYVhqXlZnYVpnYVphW1hnYV5pYmJqY2NoVk1iUEdjTkBlUENoWFBpWlFuXlZt XVVqXlVlWlBqV1BtWlNrXlptX1tuXldqW1RlWFZlWFZdVlZfWFhbU1FdVVRjWltqYWJuZGhvZWlr YV1jWFVjXVtkXlxlXF9pX2NuYmVvY2d3bW53bW5yb3Byb3B6bnJ3am5vZ2VpYV9vYmJuYWFqY2V1 bnB1cHR4c3d4d3t3dXp6dXl4c3d4c3d/en6Ad310anBiXVxaVVRYT1BcU1RiYWVycHV5c3tzbXVn X2RiW19oYWN1bnB6cnB5cG9wa21uaWppa21oamtlZGliYWVfXmNlZGlzbXNwanBoYWFhWlpeV1pf WFtWT09lXl50b3N0b3N0cHdtaW9oZF5oZF5cX1pcX1ppZWtwbXNya3JtZ21iW1tnX19qaXB3dX16 eX51dHl0b3BtaGltZWhza25tZWhvaGpybW5rZ2hqYmFqYmFwaWtyam1wamhvaWdoZWRjYV9fXVFb WE1fVU9lW1VnW15qXmJvZ250a3Ntam5raW1yamp4cHB3b3J0bW9vZWdpX2FoYWNoYWNwZ21wZ21t XVVtXVVrXFVoWFFjWltlXF1jYWRoZWlpZWtuanB0b3B0b3BwZWJiV1RfWlNnYVprXlpqXVheWFFj XVZkXFtkXFtnWldpXFppXltqX1xtYWRtYWRtXmFlV1plV1poWlxoWmFvYWh3amt5bW51ZGVkVFVl U01iT0ljUFBoVVVqW1NrXFRpV1FnVU9jUUVeTUBjU0ZtXE93Ymd+aW59anB9anB7ZWp7ZWp+bnh6 anR0Y19rW1djWEdhVkVnV1BwYVp3ZWd5aGl0Z2dzZWVzXmN0X2R1Z2l1Z2l5Z2pyX2NoWFBnV09v XFZ4ZF55Ylx3X1p1XlZ1XlZ1W1t3XFxyXmF0YWN6ZWN3Yl9zW05wWExrYVtzaGJ1aV90aF54Y2Ny XV11XVx7Y2J9ZGh7Y2dqXGFyY2hvbXByb3N4bnJ0am5wY2FuYV5oZF5oZF5lZF5kY11nY11nY11t ZGNyaWhyYV9tXFtoVk9pV1BpXlhwZV94ZGR3Y2NwZFttYVduY11zaGJwZ21yaG53Y2VzX2JuXV5r W1xlWFhdUFBeTUdeTUdkVlhtXmFwZGhwZGhnX19aU1NfVVFkWlZkW1xnXV5oYWFuZ2d1bnB3b3Jz cG90cnB1bnB1bnBwa21rZ2htX1tqXVhiXV5rZ2hyam96c3h6cn54b3tzbXNvaW90cHl7eIB4dXlw bnJnXWFaUFRbVldfW1xrbXV1d391bXRoX2dcVlRdV1VjYV9qaGdwaWlyampqZWdnYmNoYmhoYmhi Y2tiY2tlZGlpaG1yam1tZWhjXVtfWldhVlNiV1RYW1xoamtycHhwb3duZGVnXV5fXlheXVdhXFte WlhrZW51b3hyam9nX2RfXV5pZ2hra3h3d4N5en5wcnVqampkZGRpX2NtY2dfXltlZGFra2tycnJt bmlpamVrY19uZWJua21ua21tamtpZ2huZWJkXFhfVVFfVVFfWFhiW1trZGdtZWhuaW1wa29zaW1w Z2puZ2tuZ2tqZWdoY2RnX2JkXV9pX2FtY2RzY1x0ZF11Y110YlxnX19lXl5kX15pZGNtZWhyam1z a3B1bnNyaGlpX2FnYmFrZ2VwYmRqXF5fW1pfW1plXF1kW1xoWFFpWlNrW1ptXFtuWmFyXWR0X2Rt WF1jVVdfUVRjVVppW190a3V1bXd5ZWVtWlpkU0RhT0BdSkRdSkRlVERnVUVoWkxlV0lkUD5nU0Bo W1t1aGh5bXB5bXB1ZGV3ZWd1Y2t3ZG19anB5Z21yYlttXVZnXFZoXVdrXV9wYmR3ZGh4ZWlyYV1w X1xvYl9zZWN1aml1amlzYl5pWFVlU0lnVEpvW1h6ZWN9ZWF7ZF97Ylt9Y1x6YmV+ZWl1aGVzZWN5 Z2F1Y110XlNyXFBrW1dtXFh1Ylt3Y1x4X150XFt1YWF9aGh9ZGV5YWJqXmJwZGh0bXJ1bnN1a3Jv ZWtrYV9pXl1lXmFpYmRrZ2htaGlvY2dvY2d3ZGp0YmhzZ2pzZ2pvY2RyZWd3amt5bW57cGp3a2Vw ZV9tYlxtaWVybmp0a3N1bXR6cnB3bm10YmhuXGJuWlppVVVfTkheTUdoV1RyYV1yZWttYWdpVlhl U1VXT0laUUxdV1VhW1hkWlhuY2JuaG51b3V0cnN3dHV1cHR4c3d4c3dwa29vZGNpXl1iXWFtaGt0 c313dX+Ac3t+cHlza3BuZ2t3bnh+dX95cnJza2tiW1tUTU1XV1pdXV9vbnNycHVwZV9nXFZXT0lX T0lcV1hiXV5nZ2doaGhuZ2loYWNdVlhfWFtfX2JeXmFlY2dqaGtua2poZWRqY1pjXFNeVlBcVE5Y W1xkZ2hwanNuaHBtZGNpYV9dXlpbXFdjXl1qZWRua29tam5uY19jWFVeWlhrZ2Vzcnl0c3p3a3Rr YWloXF9nW15hV11iWF5aVlNeW1dtY2d1a29wbnJvbXBrZV5oYltraHBuanNya3J1b3Vza25rZGdo X15iWlheVFBfVVFjXl9nYmNqZ21pZWttaGlqZWdrZGdtZWhrYmNoXl9nXFtpXl1pX2NqYWRtaGly bW57Z255ZGt3aWdzZWNqYl5oX1xnWldlWFZkYmVkYmVnYmNnYmNtZWhvaGp1ZGNrW1phWlpfWFhi WlhfV1ZkV1NnWlVvW191YWV7ZWp/aW56ZGtvWmFlU1BjUE5oVlpzYWR3bnV1bXR5ZWhwXV9vVk9r U0xeTENjUEdqWE9vXVRuXlRtXVNrWlBuXFNzYWd4ZWt5Z2pyX2NpWlNpWlNoW1ttX194Y2V3YmRu XldrXFVuXV5zYmNyZWlvY2dzX2JzX2J0YVd3Y1p6Z2l6Z2l5am9zZGl1X1RvWk5qU0hpUUdpWFVv Xlt4Y2F5ZGJ6aGJ9amR+aW59aG16Z2d7aGh7Z2d4Y2N1XlhvWFNtU0xwVk9wXFpzXlx4ZF55ZV+A aGl+ZWd5YWJ3Xl9wXmR3ZGp5bnl1anVuZGVpX2FeVlVdVVRcVVVkXV1oYWNqY2VqYWdpX2VyYmtt XWdrZGdwaWt1Z256a3N9c3d+dHh3c29ybmpwZV9uY11uZWR0a2p5bnd7cHl4dXdyb3B0aGttYWRu YWFlWFhlVE1nVU5rYV9rYV9uaWpqZWdvXl1rW1phUE1jU09jWltkW1xkW2FnXWNpZW5wbXVyc3d0 dXl3cHt4cn10c3pta3NtYmFoXVxkXV9oYWNub3N3eHt5d3p3dHhraW1pZ2pwbXV4dH1yb3Byb3Bq YmFnXl1kWFxkWFx0aGt0aGtnX1ViW1BcUU5cUU5cW1dpaGRyb25yb25raWhpZ2VjXl9hXF1hWlxj XF5qYWRzaW14cHNza25zamdqYl5rXFVoWFFdWFplYWJvaGpuZ2lyaWhuZWRnY19iXltiW11nX2Jr Z2hvamtuaGVrZWNhXFtnYmF1bXR4b3dwaWtqY2VqXVtqXVtiWlhcVFNbUE1fVVFpYmR0bW91b3h0 bnduaGNkXlpoXGJtYWdzbXV5c3twcHNtbW9rZGdkXV9fT0xjU09fW1xiXV5lYWRoY2dkYmVlY2do Yl9oYl9rXl5tX19qXVtrXlxoXF1tYWJraHB5dX59dYR5coB1a29zaW1qX15nXFtnV09kVU1pWFdl VVRnXFhrYV1wZGVyZWd0X11tWFZpXVVfVExeU0pjV09lWlFoXFRrX2NwZGh4aW56a3B7YWV4XWJu W1VpVlBrXl54amp5cHh1bXR7aGh1YmJqYVZlXFFdU09iV1RtYmF4bWt4bWd1amRyYltvX1hwYmRt XmFuW1RpVk9jVUdlV0lnVldtXF1yZGJyZGJ1XVx1XVxrYmVwZ2pyY2VzZGdyZF9yZF95Z2F6aGJ7 b3N5bXB+aWt0X2JpWlFoWFBpUUdpUUdrW1dwX1x3ZF55Z2F+ZWd+ZWd+ZG1+ZG1+ZGp9Y2l1YWV0 X2R6YV10W1dwVlNvVVFtWlp0YWF+amh/a2mCbW1/amp4XV1zWFhuX2JyY2V1anhvZHJqX15lW1pe TElaR0VcTEplVVRnU1NnU1NnVldlVVZnW1xkWFpjXVtnYV5tZWVwaWl1bnB4cHN3cnB1cG90Z2Rr XlxqYWJwZ2h4bnR7cnh4dH1zb3hwbm9qaGlrZGllXmNoXlVuZFtwYmRvYWNtbWpqamhuZV9pYVto XFNpXVRjX2VkYWdiYWVoZ2toZ25ta3Nwcnh1d317c3p4b3d0b3NuaW1nXV5kW1xpXFpvYl9ya3J4 cnhzdXducHJqZGpoYmhta3Nwb3dvdHV0eXp1dXNycm9oZ2FcW1VkXV9lXmFnZV9eXVdlWltiVldo Y2d1cHR1dXV0dHRwbnJqaGtiYWVjYmdjYl5kY19nY2lwbXN9eIh5dIR1dXhtbW9uaGNnYVxbV1Fh XVduYmNzZ2hwa2ptaGdoaWJiY1xfWFtiW11qaGlyb3BvbnNqaW5kYV1lYl50anB3bXNtamloZWRu Y11uY11iW1BcVUpbU01eVlBkYmVua29zbXV1b3h5bmhqX1piVVBnWlVwaW54cHVzcnlwb3dvZWto XmRjVlRbTkxbVlVdWFdhXF1nYmNoXmJnXWFoW1hnWldrXl5uYWFpYmJtZWVqYWRrYmVtaXR1cn15 dIN1cH91aHVwY3BuWl5lUVZkU0lkU0lpV1FnVU9kVFBtXFhwXmJzYWR3Y2F1Yl9qW1RjVE1jVE1h UUpiWlRjW1VpXWFuYmV3amt5bW50Y19vXltrX1doXFRyXmF7aGp9cHR5bXB6Z2d6Z2d1ZFdqWk1j VVpoWl50Ymh7aW9/a25+am13Z19zY1x1ZGVvXl9tW1FlVEphUUdiU0hoVFtyXWR1YWV3Ymd1YWV0 X2RzZGl0ZWp5aGd5aGdzaGRzaGR6a3B+b3R9cHJ7b3B+Z2JyW1ZpVVNtWFZrVkpqVUlqVlZvW1t1 ZGN6aWh/amh+aWd+ZG2CaHB/aW53YWVwXV91YmRzX19uW1tzW1p1XVx0ZF16amN/amqCbW2DamuC aWp7YVp3XFVnYV5oYl9uZW1rY2poWlxhU1VcSkVWRT9YRkRdSkhYTElcT01YTkpXTUlXTkVdVEpe UFViVFhkXlpoYl1rZGduZ2l0b3B4c3R1a21wZ2hqYmFoX15zZ211aW94cnh3cHd3b3Jyam1nY2lo ZGpuYmVvY2dzZGdzZGdvbWttaml0aF9uYlpjW1dkXFhlYWJpZGVnZ2dpaWlvam5wa29va3R4dH16 dXl0b3NraGJoZF5lXVxoX15qZ2Nva2h0bnR3cHdvb3Jqam1pZGVlYWJqZGptZ21ub3h1d397eoJ7 eoJ7cnNtY2RcVVVqY2Nqa2dqa2dpY15kXlpkYWl0cHl4eHp7e354c3dwa29nZWpta3BqamppaWll aXJydX56eod7e4h+fYdycHpwbm9vbW5eXVVYV09rYV94bWt1cHR3cnVvb21jY2FfXFZfXFZqZWd4 c3Rzcnlta3NkYmVkYmVvZWl0am5nZ2Rra2lyam1waWtvY2RpXV5pX2NoXmJnYWtwanVzcnd1dHl1 a29lXF9dU1FiV1ZuZGp3bXNzb3hybndwZ21pX2VoW1tkV1diXFdkXlplY2JjYV9oYWFnX19rY2Jq YmFuYmN0aGlwa290b3NybXBybXB4b3t4b3t3c350cHtyaGtoXmJkVlhiVFZlW1pqX15qX1xoXVpl VVRqWlhzXmh6ZW96a3N5anJ3Z1xyYlduXlRvX1VyZF9qXVhpXl1vZGN4ZWt4ZWtuYVxoW1ZnXFZl W1VvXl95aGl5bmp5bmp6a25zZGd0X11vW1hwW190XmN3Y3B7aHWDbXKCa3B7amd6aWV6ZWpwXGFz WlZuVVFqV1BwXVZ0Ymh5Z216ZW14Y2p0Z2RzZWN0aG51aW95ZGt4Y2pyZGR1aGh6aG6AbnR9cHJ9 cHKGaWt6XmF0VlN0VlNtV0xrVkptVVh0XF95ZGt/anJ9aWN6Z2F9Z2t7ZWp6ZWV1YWFvW11zXmF0 X2R0X2R1Yl94ZGJ5ZVx6Z116YmWDam6DamuDamuCaGN5X1tnXFtnXFtrXV9uX2JpXFxkV1ddUE5Y TElbSEZdSkhXSEBYSUFVT01RTElUTkdYU0xfVFVjV1hjXF5kXV9nXWFnXWFtam5wbnJ3bW51a21w ZWRrYV9qYl5vZ2N3cnN1cHJ5bXN3anBqYmtqYmtpYmdoYWVoX15uZWRvbW5wbm93a2pvZGNiX2Fd W1xkW15pX2NnZGNlY2Jvamt1cHJ1c3d3dHh3c3lybnRzbm9uaWpjXFxqY2Nua29wbnJ1dHl0c3h0 bnRrZWtkYmNiX2FlXl5qY2Nqa29zdHh5eIJ9e4Z/dH1zaHBnX2Jza25wbm9vbW5waGdqYmFkZGdy cnR4d359e4N7cnh1a3JoZ2tubXJta3Nta3NqbXlwc391dYZ5eYl+fYd6eYN3dHh1c3doX15hWFdr Y29+dYJ6d4JybnlpaWlkZGRYWE1VVUljX1x0cG16c4JyanloZG1hXWVqXmJvY2dpanBvcHdvbXBw bnJ0a2hvZ2NuZGVwZ2hlYWRwa29wdHptcHduaWhhXFtnXFtqX15vYm14anVzbnJuaW1uZ2drZGRq YmFqYmFrZ2VpZGNpZGVpZGVpZGVpZGVtaGdtaGd0ZWp3aG1zaHV1anh3b3R1bnN0aXJ1anN5b3N5 b3NvaWJiXFVeVFNhVlVqYWRuZGhrYmNqYWJqXV1oW1tyXmt4ZHJ5a3R9b3h7cmR1a151ZGF5aGR5 a2tyZGRuXGJ1Y2l6bnJ3am5yYWJqWltoW1tpXFxuYmV1aW15b3B4bm95am1zZGdyXWJyXWJ4Ymd4 Ymd6a3N9bnWCbm6Cbm6Ca3B9Z2t6aGt0YmV1XlpzXFdzX2J7aGp7b3B5bW54ZGR1YmJzZGdzZGd1 Z2t3aG15ZGl4Y2h3YWV4Ymd3aG96a3N9bXd6anR+X1x5W1dzVU90VlBtVlBuV1FvVVV1W1t3Y2V/ a259aWd5ZWN6ZW15ZGt3Y2F1Yl9zW1puVlVyV1NzWFRyWlh3Xl14YVt5Ylx3Y2F+amiCbnB+am19 Y1h5X1VjT1ZjT1ZlVFppV11nXlthWFVoV1RqWlZnU1BjT01cTkBYSj1USj5RSDxVTD9cU0ZeVFNh VlVeV1diW1tiWlhnXl1qZWltaGt3bXB4bnJyaGluZGVvZF5vZF5yam1za25/aXV9Z3NvYmJtX19k XV1jXFxhXFFhXFFnZ2dubm53bXBwZ2piX2FbWFpfWFtjXF5lYWJkX2Fyam9za3BycHpzcntycnRu bnB1bnBza25nYmFqZWRraW1vbXBzcndzcndyaXBqYmliXV5bVldbVVNdV1VhYWFubm5yc3d1d3p/ d4N5cH1qZWlzbnJ4dXl4dXl1bnBza25paWtycnR3dX16eYB/eH13b3Rrbm1ucG9ybndraHBuanNz b3hwcIB3d4d9eoh5d4R5dXt4dHpzZGluX2RubXd3dX90dX5qa3RpZ2ViX15YVU1XVExkZGdwcHNu bnBoaGplY2dbWFxjW1dtZGFwcH50dIJubXRubXRwaWt1bnBzanRvZ3BqYmluZW1oZ2tkY2htYl5u Y194a293am5uZGhwZ2prZGlpYmdrZGdvaGpqaWVta2hraWpraWptY2dvZWlyY2V1Z2ltamtyb3B5 bXBzZ2pvZG1zaHB0b3Bzbm93bXByaGt1a293bXBwaWlkXV1hVlNoXVpzZ2h1aWp0aGluYmNuYWFv YmJrXWRzZGt3a3d6b3p+dHV3bW53aGp3aGp7b3N6bnJ6aG59anB+cnV3am5zX19rWFhqWFxqWFxq XWV0Z291a214bm96aWh1ZGNvYWNvYWN0Y2R0Y2R+am2Db3KDb3KCbnB7amt5aGl5ZWNzX11wX1x0 Y195bXN9cHd6bm91aWp1XlpuV1NrWFtuW114XmR5X2V0YWNvXF5vWl5yXGFwY253aXR7bXJ9bnN/ ZVt+ZFp5ZV93Y11tXVNtXVNzV1p4XF55Y2h/aW57aml5aGd7ZWp5Y2h+Y2V3XF51WE9wVEppUExv VlFzV1d4XFx0Ylx0Ylx3Y2F+amh/a255ZWh6YVR3XVBtX2hiVV1oVlpnVVhlX1hoYltuZV9vZ2Fo YVdkXVRiVk1fVEpYTj9aT0BfVExjV09kV1dlWFhkWFplWltpV1tuXF9nXFttYmFybXB4c3d1cHJy bW5yaGlyaGlyam13b3J/anR7Z3ByZF9qXVhfXVBaV0peUVFeUVFlXF9zaW17b3N4a29qYWJlXF1k W15kW15eXF9lY2duaW1vam5ranJvbnVuanBqZ21vZWlwZ2puZ2duZ2dvZ251bXRyc3tvcHlwbm9q aGlkX1VhXFFbWE1aV0xdV1BjXVZqYml0a3N1dHtubXRnY2lraG53dX13dX10b3N0b3NwanB0bnR7 dYCCe4d9eX93c3lta3Bwb3RwbXVtaXJram9qaW5rbnpvcn51d310dXt5dX55dX5wZ21yaG54b3t9 dIB0c3pqaXBtaWVkYV1VVlFdXlpvbXB3dHhpZ2hjYWJpYmRfWFtkYmNraWpzeINwdYBzanJuZW10 aXR6b3p3aG1tXmNlV1pkVlhjVVpnWF1uY2t1anN4bXV4bXVyam1nX2JpX2NoXmJpX2NyaGtybW5y bW5wa21rZ2hrYmVoXmJqYWRzaW11anN3a3R0am5wZ2puZGpvZWtyb3NqaGtoZWdqaGlranJqaXBq Z21iXmRnYV5rZWNza25za25yY2VwYmRvYl9tX11tXF1tXF1yXmt6Z3R5bnd1anN5ZGt5ZGt1a295 b3N4bnJ3bXB9bXd6anR5ZWhtWlxoXVpoXVpnWFtuX2Jtamtwbm90aWVtYl5rY2JuZWR0Z2dzZWV5 Z2p9am6Aa3B9aG15ZWN3Y2F0X19zXl5zXmN5ZGl5bnt9cn99am53ZGhvXFVkUUpqUVdtVFpzVl10 V150XFtzW1ppWFpqWltqWF5yX2V4Y2p+aXCAam+EbnN+c294bWlyYVRqWk1qV1FuW1VwX15yYV9z YmNwX2F0XmN5Y2h9aWN4ZF54XlB0W01uV0ZqVENyVFF6XFp0Ylx3ZF55XmF9YmR7Z2R4Y2F0XFFu VkxqZWlhXF9rWFhqV1drXlxvYl9uaGVtZ2RtZF5oX1pkV1NnWlVlVEppV05iWlRkXFZqXmJoXF9k W15lXF9vXWFqWFxiV1FkWlRkZGRubm51bnB0bW9ybXBwa29za3B3b3R7bnd5a3RzaGRuY19jXVZe WFFcVVVcVVVkWFBuYlpyY2V0ZWhuZWJqYl5oYWFoYWFoY2RtaGlvaG1uZ2tuaWpuaWpzZ210aG5y Z29zaHByZWlyZWlrZW50bnd0cHtva3d3bXB3bXBwZ2puZGhoXVpdU09fTkdhT0hjXGFyam93b3J1 bnBvamtwa210cHt1cn1yb3Nyb3Nva3J3c3mAeoOCe4R5c3lya3JtZG5vZ3BubXJqaW5ta3BqaW5p am5wcnVucnVvc3dzd31ucnhtaW9va3Jzb3p6d4JvbW5qaGlnX19pYmJnX2Jyam1wcnhzdHppYmJc VVVhVVhhVVhoYmhya3JwdHpydXt0anBwZ210c315eIJ9aWtvXF5oW1hkV1ViVlpoXF9uZW13bnV1 bXR1bXRyaGlpX2FlXGJnXWNpYmduZ2tzbnJwa29waWtqY2VqYWRrYmVpY2lwanBuZ2tuZ2tyam1y am1raWpraWpwa29taGtrYmVtY2dtYm1vZG9pYmdpYmdoYWFqY2NyaGt1a293ZWRyYV9yYV9wX15v XFxoVVVqXmR3anB3a3d0aXR4ZWt4ZWt1anN6b3h3bm1zaml1a3J0anB5Z21vXWNoV1RkVFBqWlty YWJ0b3N0b3N6aWhzYmFzXWJ4Ymd1Z2l1Z2l5bW54a215am16a257Z2d3YmJ1XlpyW1ZrXl5wY2N4 anN6bXV4ZGdyXmFlU0lfTURiTk5kUFBnU1dtWF1wWFdwWFdrVU9qVE5lWlBlWlBwXmR4ZWt+bnqC cn6DcnN6aWp1XFdqUU1yVFB3WFVyXVt1YV5zX19vXFxyX2N1Y2d0Z2d5a2t6Z194ZF11X1BwW0xv WFN3X1p9ZV19ZV2AYl97XVt+Y157YVxuVkxqU0hlYWJjXl9pXV5pXV5oXmJpX2NpY2FlX11lW1Vl W1VjVlFlWFRlWlBqXlVtXVZtXVZtXF1qWltnW15tYWRpX2FpX2FeWk9dWE5fW1xpZGVwaWlyampw Z2p0am51a290am51bXd4b3l4bnJwZ2pqYWRpX2NkWFpjV1hjWElnXE1vX1dyYlpwZWJyZ2N0Z2R4 amhybW5wa21yZG1yZG1wZGVyZWdyaG50anByaXVyaXVzYWdvXWNpZGhybXBvaXJuaHB4aHJ6anR0 a3h3bnpwa29oY2diU0xhUUpfW1xqZWdzZ2hzZ2hwaWtyam1vbnNubXJwbXNzb3V4b3d9dHt/e4J9 eX9zc3Vqam1tZWhyam1tbnJoaW1taW9uanBpaWtpaWtqaW5ubXJzb3puanVraG5qZ21ranJ1dHtq aGdhXl1tY2RzaWptamtyb3BudXNudXNrY19cVFBfT1BqWlttZWp0bXJ1b3V3cHd3a3d0aXR4d354 d355bW5yZWdtX1trXlpjWltoXl9oZGFva2hwaW5waW5pZGVlYWJkXGVjW2RjWl9tY2lwaWtvaGpr ZGdqY2VrYmVqYWRpX2NtY2dqYWJqYWJzZGl1Z2tyaGtwZ2puZGhqYWRtYWRvY2dtX2huYWltY2dq YWRtYWRtYWR1aW93anBzZ2huYmNrY19tZGFqWlhiUVBlWGFzZW54bXh3a3d4ZWl7aW17cH57cH54 bWtwZWRyY2hzZGlzZ2puYmVvW1tpVVVkXWJrZGlwanNzbXV4Y2huWl5yWF56YWd4a210aGl4a294 a296a3N6a3N6aGtwXmJyW1ZzXFdvXFxuW1t0Ymp6aHB4X15vV1ZnTkdiSUNiSkloUE9uWF14Ymdw XV1vXFxtWlNpVk9rVU1qVExtW2F0Ymh7bXR/cHiAa256ZWh4XV93XF57XVt9Xlx1XV55YWJ0X190 X190YWN0YWN4ZWl1Y2d4ZGR4ZGR1YltzX1h1XFdzWlV6Y159ZWF6X2J4XV94Xld0W1RqUUVlTUBj V1tkWFxkWlZkWlZpV1tkU1ZdVEpdVEpdUUleU0piUU5iUU5hVE9nWlVrWFttWlxlWFhlWFhrV2F1 YWpoZWRlY2JnXlhiWlRfV1ZiWlhqY2VtZWhramdramdza2tza2twZXB0aXR5a3d0Z3JtZWhrZGdt YWJoXF1oWFFtXVZyYWJ4Z2h1Z2l6a255b3N5b3N4cHVza3BqY2VpYmRvaGp1bnBzb3hzb3h0bnlw anVoXl9pX2FpYmRuZ2lpZGVqZWduanBwbXNua290cnV0cHtraHNpVVxiTlVjW1ppYV9taGdqZWRp YWhqYmlpZW5uanNyanlyanlzand3bnp5eIJ4d4B7d3pvam5oY2RpZGVnZWpnZWpraWptamtuZGVr YmNrYmVyaGtubXRpaG9tZWpvaG1ubnBvb3Jqam1kZGdvZG93a3dzcntubXd0dX5vcHllX1hbVU5l X1hvaWJraG5raG54bXh5bnl3bnhzanRycHV0c3h5cnR4cHNybmplYl5iWlRkXFZrZ1xtaF1tZF5n XlhjX1piXlheU1RbT1BXUVdlX2VuY2tvZG1rY11lXVdjXVhkXlpoXF9pXWFnX19qY2NzZ2pyZWlq X2hoXWVoX2lrY21yZWlyZWluX2RpW19qXl9oXF1nWF1tXmNyY2p3aG9uY2tvZG1taGtpZGhoXVxd U1FdV1BoYlt0bW91bnB4bXp5bnt9c4d9c4d6a251Z2lyY2hvYWVwZGhtYWRtXmFqXF5qXmR0aG57 a3h6and4Y2hqVltuWF13YWV0amt5b3CCc3p9bnV/bXN+a3J5a2tzZWVyXlhoVU9oW1hoW1hwXGN4 Y2p1Y11uXFZuV1FpU01wVVV0WFh0Ymp9anN7bXJ0ZWpwXVtwXVtvV1hqU1RqVlhwXF5zYWd9anB/ am16ZWh1YmR6Z2mAaGd/Z2V6X2J4XV9zV1dyVlZzW1xzW1xyXV1yXV11Xlp4YVx1Y110Ylx3X1d1 XlZ5Yl14YVx5XmNwVltyVE5wU01rTUFtTkNhUFFkVFVlVVRlVVRpWFVkVFBdUUlcUEhdTkdcTUZe Tk9hUFFiUVNkVFVqWlZqWlZqXV1pXFxtXmF0ZWhwbXVraHB0Y19vXltiVk5fVExiWlZlXVpqZ15r aF9rY11pYVtuXGJyX2VwZGpwZGppZ2ppZ2prYmhrYmhzZ215bXN4bXV5bnd6cHd4bnR1bXR1bXR3 bnh0a3VrZ2huaWpwa2p1cG90cnNwbm9tZWpqY2hoYWNoYWNoXmJnXWFqY2VqY2VraWpwbm9ua21w bm9zc3VwcHNrZWFoYl1qZWdrZ2hqaGlpZ2hnX2JoYWNoY2duaW1uZW9qYmtnYmNuaWp0c3pzcnl6 c3Vza25tZ2RkXlxhX2RlZGlraWpqaGlyaWVtZGFyZ2V3a2pwcHBvb29uZGp0anBybnd0cHl1bnN0 bXJ4bXh3a3dzb3hwbXV4d4B0c31nY11kYVtzbnJ3cnV1cnpybnd4b3l9dH51b3VzbXNybnd3c3t6 dH17dX53cnBkX15tX111aGVyaWhuZWRpZFpkX1VeVlBdVU9iVFhiVFhdVFVkW1xuZ2dyampwZV9r YVtpXlhnXFZjWFdnXFtiX2NraW1rZ2plYWRhXVpiXltnX2RrZGltY2drYmVkXFhiWlZlXFNkW1Fo Wl5wYmd5Y295Y29waWlwaWlzbm1uaWhpWFViUU5kXFhrY190a3V1bXd4bXp4bXp6bn94a317Z25z XmVuXF9vXWFrYmVpX2NuY19qX1xtXF11ZGV4a293am5yXVtoVFFtWFt0X2J1aW1/c3eEdH6AcHp7 bXJ6a3B6aWh3ZWR0YWNuW11vXFxvXFxyXmF4ZGd9YmR3XF5yYV1zYl53YmR5ZGd0am56cHR9bnN4 aW54Z2NwX1xuW1hnVFFoVFZrV1puX2d6a3N/am19aGp4ZGR7aGh7aGV4ZGJ4XFxwVVVqT1ZrUFdp VVNoVFFqUFBrUVFvV1ZtVVRwWlFyW1NwWlRzXFZ3V110VVt4WFpuT1BrTUVtTkZvUEhyU0pfT05n VlVnWF9nWF9jWltfVldeU0pdUUlbUUVcU0ZhUE1kVFBnVlVlVVRnXVRtY1puYWFvYmJqYWRyaGt3 bnh3bnh1Z2ltXmFhWFVeVlNjW1dqYl5wZ2hwZ2hrYV9rYV9uXFZtW1VrXlxuYV5iX15fXVxjXF5p YmR6bXWAc3t7c3p5cHh3b3JuZ2lyZ3R0aXd1a3t3bX1zbnJzbnJzcG9zcG9zbWVqZF1kXlxkXlxn XWFpX2NpYV9jW1ptYWJwZGVybW50b3BzbnJ0b3N1bXR3bnVqampvb290b3B0b3B0c21ramRkYVtu amRvaGh0bW1lY2diX2NjYV9nZGNuaHB3cHl6cnt4b3lyam9rZGljYmliYWhoZGpuanBuaGNrZWF1 aWp3amt0cG1ybmpwZ21zaW9ya3d5c354b3d5cHh+coN6bn93cHt3cHt3dX90c31uaGVuaGV6d394 dH15dYB0cHt5bnt5bntzaHN0aXR0bW95cnR5d3p1c3dzamltZGN1anN9cnp9dIB1bXlqaGlkYmNq XVtkV1VlWFZjVlRiV1RoXVpyb3N4dXl5cG1zamdwX15tXFtnXFtrYV9pZWtuanBrX2NlWl1hWFdk XFtrZGdqY2VqZWlnYmVjXVthW1hjV1hlWlttYWJvY2RyZGR0Z2dyam15cnR5c3lvaW9rW1pkVFNo X15uZWRzanJ3bnV3bXN1a3J7cHl6b3h5Z2prWl1nVFRoVVVoW1tpXFxlXlVnX1ZkX2FpZGVzaHBu Y2ttWldpVlRoVFhzXmN3b3R7dHmAdHp+cnh3bXN0anBwZ2hyaGl5a2tvYmJrXFRuXlZ0YWF4ZGR+ Y2h7YWV1ZGF1ZGF1Y2d3ZGhyaG54bnR9anB3ZGp5ZWN1Yl9qXVhlWFRtWlprWFhvYWh4aXB6Z2R3 Y2F1YmJ3Y2N6ZWN1YV5vWFNpU01iTlNjT1RkUVFnVFRlUU9qVlRvVVVvVVVuVUptVElvUU5yVFBw UVNvUFF3VlFzU05lTUNlTUNtUERtUERhV1toXmJuZ2ttZWpnYmFjXl1iVVNcT01eU0piVk5uWmFw XGNwY2NqXV1lXVprY19vY2RvY2RqY2VuZ2lqa3JtbnRwZ2pwZ2ppYV1hWFVjV1htYWJrZGdrZGdo X1plXVdkWFBnW1NlWFRlWFRiXFpcVlRhXF1oY2Rzb3h3c3t6cHdzaW9tY2RtY2RwZ21yaG5vam50 b3N1cHJ3cnN1dXVycnJuaGNoYl1pXl1rYV9nX19oYWFoYWFnX19rZ2Vwa2pybXBybXBzaW10am5z bm93cnN7eH5+eoB7eXp5d3h4d3Bwb2lzc3V1dXh1c3RzcHJpZWtkYWdiXGJfWl9fXmNram9ycnJz c3Nza3BvaG1hW2FdV11jX2VkYWduZGVuZGVtYWR3am51b3V3cHd1c3draW1taXR1cn1wb3lzcnt4 d4Bzcntta3NjYml0a3V6cnt3b3R1bnN6dH14cnp1cnpybnd6bXV7bnd1cHRvam5ubm50dHR5c3l4 cnhyam1waWt4b3t/d4OEeYJ6b3hybnRwbXNuZ2tpYmdqZG1oYmpiXmRqZ21zdHp1d31yb3BvbW5p YV9oX15nX2JtZWhtaW9qZ21pXV5jV1hfVVRkWlhoZGFoZGFrYV1jWFVoVVVnVFRnUVhwW2JrY2Jt ZGNuZ2dtZWVwbXh3c35ubnBkZGdnYV5pY2FzaW14bnJ3bnV1bXR3bnh1bXd6cn55cH15bW5wZGVp WlNqW1RpXltrYV1zYVtwXlhrXV9wYmRzZGlwYmdrXlxnWldkV1dpXFxvamt0b3B3bXB0am5tY2dr YmVuZGhzaW1yaGlqYWJtW1FqWE9tWFhzXl54ZWt4ZWt3ZWd1ZGVwY2NwY2N4ZWt7aW99Z2t5Y2h5 YWJ1XV5rW1dqWlZyXV9wXF50Ymp1Y2t6Y154YVx1XFd1XFd3XVZyWFFkU0ZiUEReTE5jUFNqV1pr WFtqVE9nUExnTUZnTUZlSENpTEZwTkxyT01yUU91VVN+W1Z5VlFyVUxrT0ZpSUVwUExqY2VtZWhz aWpzaWp1Z2lyY2VrYV9lW1pnWlVwY15raW1qaGtyaGltY2RuYV5rXlxtWlpuW1toYl9rZWNoaGpq am1qaW5paG1tYl5nXFhoW1hvYl9oYWFvaGhtZ19pY1xrXlpuYVxwYVprXFViW1tYUVFfWFhtZWV1 cnh1cnh1a29tY2dpX2NrYmVvamtvamtwZ213bXN3cnV3cnV1d31yc3lyaGlnXV5lXF1pX2FpX2Nt Y2dyZWluYmVpaWlra2t0a3N0a3NwaWl0bW1wcHB1dXV/eX+DfYOAfoJ3dHh6eHl9ent/e4KAfYN5 dX55dX5vb3JlZWhjXl9fW1xdXV1paWlvb29qampwaW5waW5nX2JhWlxpYmdrZGlwZW5wZW5rY21v Z3B0cHt3c35zbm9oY2RnYWtwanVvbnhycHpzcnlwb3doYl9jXVtpZ2pyb3N3cnV3cnV4dHpzb3V4 c3dwa29vaXJwanNwanB0bnR5b3N9c3d5c3l1b3V0anB1a3J9cn+EeYd/en53cnV1dHtycHh1bXd0 a3VoaHRoaHRkam9la3Bvc3twdH1wa21iXV5iVVNnWldjXF5tZWhuaWprZ2hlXVxeVlVfU1NnWlpv ZGFwZWJtXVVoWFBlUU9lUU9hU1dqXGFqYWd0anB3aGp0ZWh5a3R6bXV0am5vZWluZGV0amt6cnl4 b3d0cHdybnR5a3d4anV1anh3a3l6b3hzaHByXl53Y2N0Z2dzZWV3aWRwY15yXV10X19wYmRuX2Jt Yl5pXlttXF1wX2FvZWt1a3J1aWpzZ2huYVxvYl1uZGhyaGt3aWRwY150W1RwV1BtW1VyX1p3ZGh5 Z2p9aGp6ZWh3Y2N1YmJ0X2R4Y2h7Z2l1YWNzW1pyWlhrWFZrWFZlW1dlW1dzXmN1YWV3XlRuVkxr U0huVUpwV01uVUpnT0NoUERhTU1kUFBuVVFtVFBnTkdhSEFfSj9fSj9oRz1vTkRyT011U1B0VU16 W1N7XVt5W1h0XFFvV01wVVV4XFxlY2Rtamt3b293b297a3V9bXd4a29zZ2ptZGFtZGFpaWlqampu aWptaGlpZGNeWlhdUUhbT0ZeUVFkV1doXF1pXV5qY2VvaGpoYVdlXlVpXlhtYlxyY2h4aW5ua2pt amluaWhuaWhzamltZGNrXlxlWFZiV1RqX1xvbXBzcHR4a29uYmVvYWhyY2pwaWtwaWt1anV5bnlz cnt3dX94d4BzcntwbWRpZV1kY11jYlxrZGluZ2tyY2VyY2VlZWVtbW15b3N1a29ycG10c29wcHB3 d3eCen+AeX6DeIN7cHt5d4Z+e4uIf4yEe4h7e4l4eIZ5bndtYmpkXV1fWFhhV1hjWltkY2hlZGlq YmltZGtrYV9qX15pYWhwaG90bndzbXVta3NranJ1cnh4dHpyam1hWlxcWGFoZG1tZ3JwanV1dHly cHVnYVpcVk9fYVxnaGNpaWltbW11b3V0bnRzbm1qZWRnYWlpY2tlZGtta3N7c3p3bnV6bnJ5bXBz aHN0aXR9cIKCdYeAeoB1b3VwaG9yaXBvbnhqaXNvZWtrYmhnZGhtam5ycn5vb3twaGdjW1phVlBf VU9jWl1tY2duaWhqZWRpX2FjWltnWlprXl5wa21vamttZGFjW1djU1FkVFNlWFZtX11yZWl4a290 aGlyZWd3amt5bW53bXBwZ2p0aG59cHd7d4Z4c4J7cnV4bnJ5Z296aHB4anN5a3R4bXV4bXV7aGh+ amp7bXJ6a3B0aGltYWJzW1xzW1xwXGFzXmNvXl1wX15yXWJ4Y2h1Z2t1Z2t4aWt4aWtzY1twYVh0 aWh4bWt6bWhzZWF1XlpzXFdwXFxyXV14Y2V6ZWh9YmR9YmR1YWFzXl5wXmR0Ymh5ZGd6ZWhzX110 YV5zXFdpU05nVUhpV0prW1dtXFhwWE5oUEZrT0RwVEhzW05yWk1qVUloU0doTU9pTlBqUUVpUERj TEFeRz1fR0BiSUNjSj5rU0ZtU05tU050W1B4XlR3Xl11XVx0XVdzXFZ4XGGAZGltX193aWl4a3J7 b3WAdHh/c3d9cHR1aW1zaGRtYl5pZWJraGRnYmFlYV9oYWFfWFhdVElYT0VdTUljU09lV1pqXF5w XmJwXmJvXltuXVpqXVhtX1t0ZWh4aWt5b3B3bW50b3N1cHR3bXByaGtwaGJnXlhjX1ppZV9wbnJ1 c3d7b3N4a293am5vY2dtY2RuZGVvZWl1a290c316eYN+fYJ9e4B6enpycnJvb29wcHB3a3RtYmpp X2FtY2RvaWd4cm91c3dua29tam5pZ2pwcHN3d3l/dH19cnp5bnl6b3p5dIR9eIh7eH53c3lzb3pv a3dpXmtnXGlhW1RdV1BfXltfXlteXF9dW15hWlxkXV9pYmJrZGRtZWhza253dHhvbXBtamtua21w b3dzcnltZWhjXF5dWmJkYWlkYWloZG1ybnd0cHlqY2VXUFNbWFpdW1xcWlhhXl1pYWhzanJtZWho YWNdV1BdV1BeWlhnYmFtaGt1cHR3bW5uZGVnX2JqY2V6cIJ/dYd1cHJuaWppYV9pYV9oY2JnYmFn XWFiWFxiW11nX2JoZ2tta3BybW5qZWdjW1dfV1RkW15vZWlwZWRqX15jWl1nXWFoW1ttX19wanN0 bndyampqY2NkWE9lWlBjXF5rZGd4anN7bnd0bmtzbWpzb3VybnR1bnNtZWpzZ2p7b3N+d4Z7dIN7 cHl1anN4ZWt1Y2l0ZWpzZGl6a25+b3J/cHWDdHmCc3p6a3NwZV9pXlhzW1p1XVxvW19wXGFuXVxy YV93ZGh5Z2p5Z2p3ZGhyZ2VzaGd0Y190Y190aG55bXOAbnJ9am59ZGh7Y2dtXVNrXFF3Y2N3Y2N3 Xl9yWltwVlttU1doV1huXV50ZWp3aG11Y11yX1ptVE1nTkdnVUhnVUhvW1twXFxuW1FtWlBvXFVz X1h5ZV93Y11wV1BuVU5nTUZiSEFoTEBjRzxhQzljRTtfQThkRjxoST5nSD1jSj1lTT9rU0xzWlN0 WlZ5Xlt5XWJ3W194W2SAY21rW1pvXl11Ym19aXR7c396cn53cHl1b3h5a2lzZWNtZGFwaGRrYmVr YmVrYmVnXWFkXFZiWlRjWFNkWlRkWlZpXltzX111Yl95Z211Y2luY2JuY2JuYmNzZ2h6cHd/dXt7 d3h5dHV5b3N4bnJyb3BtamtubWdvbmh3cHd+eH6AdYB+c355c351b3p0bnRya3Jya3J3cHd7dX56 dH14e4R7f4h/gId6e4J6enp6enp+c3t3a3RqZF9qZF9rZ2h5dHV1c3Rwbm9rZ2VqZWR1cnp/e4R/ dXt7cnh0a3N3bnV3bnV7c3p3cHl1b3huZGhnXWFnWFtnWFtlXl5tZWVpaWtjY2VhXVpYVVFhVVZn W1xuaG50bnR3b354cH90cHdpZWtqY2hvaG1qbXlucH1tZWhkXV9fW1xfW1xnXmhnXmhua29yb3Nu Z2deV1ddV1BbVU5YVU1fXFRlY2dvbXBzaHNuY25iW11jXF5fXFZkYVtlYmhqZ21yaG5tY2llX11o Yl9uY3BwZXNya3dwanVwZXBwZXBtZWhqY2VoY2JlYV9kXV9qY2VuZ2lrZGdrZ2VkX15lXF1lXF1l XVxoX15vZGFrYV1jWltiWFplV1xrXWJ3bX55b4B5bm10aWhoWFFpWlNoX15uZWR4bXV7cHl5cnd0 bXJybXtzbn1zbXNtZ21rZGlwaW56b3p9cn15cnRza251aWpwZGVoX1xtZGF6a3B/cHWCdH2Ac3uA bW13Y2NqXVhoW1ZyWlt0XF1uXV5tXF1qWltyYWJzZWN6bWp6Z2d4ZGR1Y2d0YmVzZ211aW94anN5 a3R7Z257Z25+am14ZGdvY1dyZVp6b2t+c29+aW50X2RwV01lTUNiT1FvXF5zZGl5am95ZWVwXV1r VU1lT0dpVEhrVkpuV1FvWFNwWlRzXFZwXGF4Y2h6Z2RyXlxyWk9tVUpnSj5jRztkTD5hSDtlST1o TD9nRzlqSjxtTT5uTj9jTTxkTj1oUU1uV1N1Wlp1Wlp1V1V3WFZyVFB3WFVlWFZlWFZpW190ZWp4 cH97dIN6dXl5dHh6b256b251b210bmtwZ2hyaGlqYmFnXl1pXltrYV1qXVhqXVhnYV5pY2F0Y2R5 aGl1cHJ5dHV3b3Jyam1tZWVvaGhyam96c3h6eHd5d3V9c3l7cnh1cHRuaW11bm51bm53cnV7d3p6 dXd4c3R3c3l7eH5+eXp6dXd1c3d1c3d9dH57c314eIZ7e4l+fYR+fYSEgIeEgIeDfYh9d4J1bWtr Y2JqZWl1cHR0dHRzc3NwaW5yam96eYB9e4N6dXd5dHVwaW50bXJzb3p7eIN6b3h1anNvZF5jWFNj VlFtX1tra2t0dHRzcHRpZ2plXF1eVVZiVVVpXFxua291c3d3c3tzb3htZWplXmNhW1hnYV5jY2Np aWlnY2llYmhfW1xdWFpkWlhoXVxnZ2dqampyaXBnXmVkXV1eV1dlX1tuaGNzb3VwbXNubXdubXdl YmhnY2lnYVxlX1tkZGRiYmJvZWtuZGpoXl9oXl9lXl5nX19tZWVtZWVtZWpyam9vZ25vZ25uZ2tu Z2tuZ2lqY2VyY2hwYmdrYmVoXmJnXltpYV1rZFtpYlhoXFBpXVFoXFNnW1FnWFtrXV95bnl5bnl5 Z2p3ZGhyX2NvXWFuYWl1aHB4dH94dH97c3p3bnV4bXV3a3RraWhpZ2VpX2FqYWJzaHB5bnd4b3dz anJyaGlrYmNlXmNpYmd0am59c3eAdHh4a293ZGhuXF9tXFtrW1p0XmNzXWJvW1htWFZjVlZpXFxq XVt1aGV9amR7aWN4ZF5vXFZwXGF1YWV1X255Y3J6aHB5Z295a2t4amp3aWd4amh6c3N7dHSAa3B6 ZWpwWlRuV1FpVVVyXV1zYWR4ZWl7aGV0YV5tVVRnT05jUEdlU0loVU5tWlN0W1Z5X1t3YWV9Z2t7 aV90YlhzXEhuV0RqTT5oSjxoVD9oVD9uVkVtVURtUERtUERtUUFvVERpUURlTkBlVEduXE91XFd0 W1ZwU09vUU5qUUdtVElqWFNvXVduYWF0Z2dycHh1dHt7c3qAeH+Ad3p9c3d+d3t5cnd7a3V5aXNv Xl1uXVxoW1ZoW1ZpXltrYV1lXmFrZGdyZWd6bm95dHh5dHh4bXV3a3Rzbm9zbm91bXR6cnmAeoB+ eH5+eH5+eH57c3p0a3N1a3J1a3J4cHV4cHV1bXR4b3d0c316eYN6d313c3l1dH51dH54d353dX10 cHt4dH94d357eoJ9eoh+e4l+c36AdYB6c3Vyam1vY2l3anB1c3R1c3Rya3d0bnl5dYB9eYR7eX10 cnVzbm91cHJuanNzb3h1cnhybnRwaWlqY2NnX2JuZ2ltcHRwdHh5d3hvbW5pXVVpXVVkXGNtZGtu bXJwb3RwcnhpanBrYmVpX2NjXl1nYmFvZWd1a21uanBqZ21uYmVvY2dqX15nXFtrX2NwZGhyZ3Jy Z3JoZWdqaGlvb293d3d9fX90dHdta3VycHpzcntvbnhwaWtqY2VjY2NkZGRuaWpuaWpuYmNpXV5k XV9oYWNwZ2hwZ2hrYmVtY2dtZGtvZ25vZWtuZGpwaW5uZ2twXmJwXmJpX2FoXl9pX2NrYmVuZWRw aGdtZF5uZV9uYlprX1doYWFrZGR1bXR3bnV4a210aGl3ZGp1Y2lyaXN5cHp5dYB9eYR/dH17cHl3 aG9yY2pvZGNyZ2VzaGdzaGd4a3J4a3Jua29pZ2ppX2FjWltkWFxpXWFvZ25zanJ5am90ZWpuXVxq WlhlXF1lXF1qYVduZFt1XlhwWlRpWFVnVlNkW1x3bW5+b3J+b3J6aGJwXlhyWl10XF91YWp4Y217 aW97aW99aGh7Z2d9aGqAa25+b3R/cHWCbXR9aG96X2J3XF5yWl10XF90YmV4ZWl7amd4Z2NyXVto VFFiU0xiU0xkUUpoVU5tVUpvV01yXlx6Z2R5ZV95ZV9wWkZuV0RzV0VwVUNwWExvV0p5WEd6Wkh0 VUp1VkxvV0RvV0RvVk94Xld/Z2WGbWuHa2SAZV51WklrUEBqTkNvU0dwX2FyYWJ0Z2d4amp1cHR4 c3d/cn2HeYSAeX5+d3uAeoB/eX97dYB0bnl4ZGdwXV9oXFRlWlFpW11tXmFlX11nYV5tY2d1a293 c3l0cHd4bXV3a3RzcHJ0cnN0cHd1cnh9d396dH15dYB5dYB7c311bXd1a291a290bndzbXV0bXJ1 bnNzb3hzb3h4cn13cHtzcH5zcH5vcn5tb3tzZ216bnR4aXB7bXR5cH17c394bXV6b3h5b3VyaG50 aGt1aW1vbnNvbnNqaXBranJ0bnl/eYR7eX16eHt6eHt0cnV1bnN3b3R1dXh3d3l5cnR0bW9wanB0 bnR0dXtzdHpzdHhub3NqZ2Nva2hvbnV1dHt4dH11cnp0a3hwaHRvZWlqYWRjX1xpZWJ1a3J+dHp4 cHN3b3J0a3NyaXBlY2RjYWJuX2d0ZW1zZW5vYmpoYmhrZWtzanJ9dHt6eHt3dHhranJoZ25qanpv b39zbXVya3RpZGVqZWdraW1raW1rZ2plYWRhYWNra259d396dH1tamtoZWdkYWdkYWdtX2pwY25q Y2hza3BzaW1qYWRrX2FtYWJqZGp1b3V9cHR7b3N9a219a21zaW1vZWlrZ2hrZ2h4anN6bXV6c3V6 c3V5b3VyaG5ubXRwb3d4c4J9eId6d31zb3VzYmNrW1xtX191aGh5b3B3bW56bm97b3B0b25taGdr W1doV1RbV1RfXFhvYm15a3d5am9yY2hwXFxvW1tvXWF4ZWlwZ2hzaWp5bmp1amdwX15pWFdrY2J1 bWt3b3J1bnB4ZGRtWlpuWlptWFhwYml4aXB7bm56bW2AaGeCaWh+a3KAbnR+cnN7b3B+aWd9aGV1 YWNuWlxtWlNtWlNuXVxyYV94YVt0XVdpVlBhTkhhSkNdRz9dRjxhST9iTEdnUEx3XF5/ZGd/ZWJ9 Y195YU94X051XU91XU90W050W059XVN7XFF1XE9vVklrVEBqUz90VFOCYV+Ha3CNcneNbmiIaWN4 W05rT0NqTEBuT0RzZGl0ZWpuZ2lpYmRvamt1cHJ7bneDdX59c3d5b3N7dHl+d3uAeH99dHt5Z29z YWloX1xlXVppW11lV1ppV05kU0lpV1tyX2N0a3h3bnpzaW9wZ21qaGttam5uanNybnd1b3VzbXNy a3J5c3l7dX54cnpwa21uaWpwanBwanByam9uZ2tvaGpuZ2lwZW5yZ29zanR4b3lvc3ltcHdwa21z bm9yZGRvYmJwZGp0aG5ya3RwanNyaXBvZ250aGt0aGtzanR0a3VwaWtvaGpzbXN6dHqDeX+Ge4KH f4SCen9/cHV+b3R9eHt+eX1+d3l9dXh4c3d5dHh4eHp5eXt7dX50bndvZG10aXJ4b3t+dYJ+d4Z9 dYR9b3h6bXV3a2h0aWVqaGltamt1cnqCfod/en56dXl3bnp3bnppbm1na2pqYmlvZ254a29uYmVl Wl1uYmVuZ2t7dHl4c3Rzbm9tZ2JqZF9pZHRwa3tva3RtaXJqZWdlYWJrZGlvaG1zZGtqXGNbX2tr cH2EgpR9eox4eHhzc3NqZWljXmJwYmlyY2ppZWtzb3V7bnd5a3RuZGhrYmVtaXR3c357eIB7eICA cnSEdXiAdHh0aGtwZGhuYmVzaHB5bnd6d319eX97dXt0bnRwa29ybXBycn94eIZ3eoBvc3ltXVZo WFFqXVt0Z2R5dHV5dHWCc3h/cHV3bm1waGduW1FoVUxjU09nVlNuY2t0aXJzZ21tYWdpVVpvW191 YWV9aG16bXp7bnuCc3h/cHV0ZWhvYWNyY2h1Z2t4bm90amt6Y15vWFRqV1FtWlRvYWN1Z2mAbW2A bW2Ea2+Ea29/anKAa3N/bm17aml4Y2F3Yl95Wl1yU1ZpVlBpVlBrWFFwXVZzXU9rVkhlTkBeRzpe QzdbPzNbQzdbQzdfQz9qTUlyWl15YWR/aGKAaWODaWSCaGOEZFqCYld/X1R/X1R/X1d+XlZ7XlFz VklvTzxtTTp0T06EXl2Oa3CUcHWIbWiDaGN7Wk5wT0RrSkNzUUl0am5tY2dnZV9jYlxrZ2Vwa2p3 a3d4bXh3a2p1aml3bW51a214bXp/dIJ5ZXV4ZHR3Y25yXmlrXFVkVU5nUD9nUD9qWFFzYVpzaGd3 a2pwYmRoWlxjXWNfWl9jW2RnXmhvZWdwZ2h1aHN6bXh5a3R5a3RwaGdtZGNrYmhyaG5waHJtZG5u ZW9yaXN0bW9za250a3N1bXR3dXp3dXpvbnVvbnV1Z2ttXmNuW11uW11uZ2dyamp0anByaG51aml0 aWh0bXJza3BoY2JlYV9qaWV0c295eH9/foaHfoaDeoJ3bXN3bXN7en+CgIaCgIZ/foN9d31/eX95 eIJ6eYODe4t1bn10ZW1yY2pybnR5dXt/eYR9d4J7c39+dYJ9eHl5dHV4dH19eYJ6eYB/foaAfYN+ eoB5cHp3bnhubXJwb3RvbXBzcHR1a29vZWlwXVdpVlBtXmN1Z2t1bnNwaW5tY2RoXl9rXWJuX2Ro XmJuZGhlY2JhXl1pYV9uZWRwX15qWlhoYm17dYCJh5aGg5KIgoiAeoB3bXBrYmVqXmJwZGhtaXJ4 dH1+cHt5a3dzZ2pwZGhyaXV/d4OEfomEfomLd4SMeIaDeIB3a3RvaWdlX11qZG11b3h5dX5+eoN+ d3d3b29zaWp0amtycHh5eH97eoR4d4B1Y2drWl1vYl94amh7cnV+dHh/cHV7bXJ1a21wZ2hvXFVk UUpnU1VpVVduXmhyYmtvXWFuXF9pWFptXF1wY2F7bmt+bn2AcH+Db3qEcHt9am56aGt0Y2RwX2F6 bW14amp1Y11vXVdwWE5tVUptWldzX119am6Cb3N+b3R9bnN4Z2h5aGl7Z2R5ZGJ1YV50X115XGNy VVxoUE9nT05pU01uV1FvWkxoU0VoST5iRDleQDVaPDFaRDNYQzJfRztlTUBtVlB4YVt7Z2l/am2I cGqJcmuHbV+AZ1p6YVZ5X1V6YVZ6YVZ5XUpvVEFtSjlwTjxvUU97XVuHbnKIb3OJdW9+amR+XlRt TkRrSj9vTkN5bXBvY2dkYVtcWFNiWlhqYmFrZW5uaHB0aGlwZGVvZWdwZ2hwZ211a3J7ZXJ+aHR4 aXB1Z25tYl5kWlZkUUhlU0lqXVttX11wZV9wZV9oXVxiV1ZjV1tfVFdjWl1jWl1qXl9tYWJqZWlw a293anB6bnRvZ2VtZGNvZWlwZ2puZ2lwaWtvZ25waG9zanR0a3Vzb3pva3d3b3R1bnNvbXBua29v Y2RtYWJrXl5yZGR0bmt0bmt1bWtyaWh3aGp0ZWhqal5paV1kXlxiXFpkXlxvaWd5eH1+fYKCeYCA eH94c3d3cnV5eoCDhIuCho59gIl+eoZ9eYR+e4l+e4mAeoBya3JwY2FyZGJvbWt5d3V4eHp3d3l0 b350b356d396d397dXt/eX97eH5/e4KAfYh7eIN4dHp1cnhta3Vta3VvaHhyanp3bXB4bnJ3Y2Fw XVtuW11uW11zZ2pyZWltY2dtY2drY2JnXl1iV1ZjWFdbWFpfXV5tZWVvaGhtY2RqYWJyY2p+b3eD gJKDgJKGe4yDeYl9bnBzZGdkWmJrYWlyaXV9dIB+dHp4bnRzZGdtXmFuZHR7coKHfY2Ifo6EeH6I e4KCe4R1b3huZ2lkXV9kX290b396dYR/eomEeoB9c3lyaGlzaWpzb3V6d32Ce4R6dH15aXVyYm5w Ymd5am96b3h9cnqEeH6AdHp6cHdyaG5yYlpuXlZtX19tX19vXWF0YmVzYl5uXVpuX2RvYWV1ZGN9 a2p/anKAa3OEb3mCbXd9Z2t1X2RyXV9zXmF0Z2R1aGV0aWNtYlxzX19vXFxuXV5uXV55Z21+a3J5 am93aG10Y2R1ZGV9aWN+amR7aGh1YmJ1XFVrU0xjSj5hSDxlTkRqU0hrVElpUUdtTTpoSDVhRDFc Py1aPStiRTJhRDVnSTtpV0dzYVB5Y2qDbXSGcHWHcneGa2F7Yld0W05yWEx+YVd+YVd+XFB1VEhy TjlvTDdwV1B7YluHbnKJcHSIbXKGam9/YlZyVUlwTUNvTEF9cHdyZWtjXVhdV1NeW1VkYVtrYmht Y2luZGhvZWluZ2tuZ2tyaGl0amt5bXB6bnJ3a3d3a3d4bmRtY1poXUpqX01uYmNyZWd0a2pyaWhp Y15eWFRhVFRhVFRjWFdpXl1rYmNvZWdqaGtqaGtzaHB4bXV0bXJ0bXJza3B0bXJ1bXRwaG9wZ21z aW9yam9vaG1uZW1vZ250anBzaW9oaGVpaWduYmNvY2RtZWhyam10bWN3b2V5b3B3bW55bW5yZWdq al5ra19vY1thVU1iWE9kW1F3c3l7eH5+d4Z9dYR4cHV6c3h4eIiCgpJ/go54eod7d4Z7d4aCf42A fox+eXpvamtyaWhyaWh3c3l9eX97en94d3tua21lY2RtZGtzanJ1bXR3bnV3cHd6dHqAfYZ6d394 eX1vcHRranRpaHJtZ3JvaXR4bXiCd4KCdXd+cnN4amhrXlxvYmJzZWVvZWdyaGlqaGdpZ2VpY15b VVBdW1xkYmNtaXJ3c3t5b3V3bXN9cn19cn19eoiCf42Gf4aCe4KDd3p0aGtjW1dqYl5vZ259dHt9 dIB5cH10bXJpYmdqZGp4cnh/eIiCeouAfYaAfYaAfoJ1c3dvZ2VnXl1nX2Rza3B6d4KAfYiCeYB5 cHh5b3NzaW1ybnd6d3+Ed39+cHl6bXh5a3d4a294a296c4N9dYaCen99dXp7bnd3aXJ1aGV1aGVz Z2pyZWlzZGlzZGlyYV9wX15uX2JwYmR5aGd7aml6Z2R/a2l6bnJ4a299aG91YWhuWl5uWl5wYmR1 Z2l0anB1a3J5am9yY2h0Y2JyYV93aG14aW55aGd0Y2J0Y2J4Z2V9bnN/cHWAc3N5a2t3XlFuVkll TT9cRDdhST9oUEZzWlZ0W1dzVUFuUD1qTDRkRi9kRzduUD9vVU50WlNrW05wX1N6YWuEanWDbXKC a3CCX1R5V0x0V051WE9/X1d/X1d/XEx/XEx5VEV4U0R4XV+CZ2mHbm+JcHKIbXKIbXKAZ1yAZ1x9 Xk91V0h3c3tuanNjYlpcW1NjVlFjVlFlWFhtX19zX191YmJwZGhvY2dtaGtvam50bW14cHB6dH17 dX6Eb3R/am90aF54a2J4cHV5cneAcnd9bnNpYmJhWlphVlVjWFdpXWFvY2dzZ2p1aW1taGtrZ2p3 cHl5c3t/cn2CdH90cHlybnd5cnR5cnRyaGtuZGhzZGdzZGdtZWhoYWNuX2RuX2RqYWJvZWd0aGl4 a210b253cnB6dXd/ent/eX9+eH56en15eXt3c21ybmh0aWNtYlxuWlpoVFR3a3l/dIKAeIR/d4N6 c3V6c3V/fY6DgJJ/eId4cH9zb3p4dH9/fYuCf42Ae39zbnJ1a215b3B4d4B9e4aHeYJ+cHlwaWlk XV1oYWNpYmRoZWlpZ2prZW5tZ291cnp0cHl3dXp0c3h3b3Ryam9oZGpoZGpwb3mDgox/gpB+gI6C f4NzcHR3am53am5vbnNvbnNzaHB3a3RvaGhkXV1rZGlza3B6cnt+dX+Ad31+dHp7dYB/eYR5eIJ/ foiIfpCHfY6Hd4Z6anlnXFtpXl1kaGtwdHh7eoR4d4Byb3BnZGVoX15uZWR1bXl/d4OCfoeCfoeG gIJ4c3R3am5wZGhnX2RvaG15eIJ/foiEe4iDeoeAe395dHh0bXJ4cHWAc4B9b314b3d0a3N4a3J4 a3J6c4N9dYaAeIR7c39+a3R4ZW5zaW1zaW17bXJ7bXJ5ZGl0X2RvW1ttWFhuYV5tX11yYV9uXVx4 X157Y2J5Z295Z293Ymd0X2RyXV1uWlp1YWh+aXB7bXR6a3N4aW50ZWpyZWdwZGV5bXB7b3N9a2h3 ZWJ5ZGR+aWmAbneDcHmEdXh7bW9+ZVdyWkxqSjpkRTRlTkRtVUpzXlx0X114W050V0pvVT5wVj9v Vk91XFV9YmKAZWV6Z2F5ZV+AZGmHam+HbWl+ZGF+YUp6XUd1X1F9Z1iGa2iHbWmEalx9Y1V7XUx9 Xk1/ZGGEaWWGameJbmqIb3CEa22HaGKHaGKHZFqAXlR5bnl4bXhwZWRlW1pjWlBkW1FpX2FrYmNz YmF1ZGNzYmNyYWJpX2NtY2d3aWd6bWp7cH6AdYOCeH5/dXt5cnJ1bm6CdXuDd32DdX5+cHlwZ2pr YmVrY2JrY2J0Ymh5Z211aW94a3Jwa21wa215c357dYCAdYCCd4J3dX13dX11cnh1cnh1bnNza3Bw bXVuanN0ZG50ZG51Y2dyX2N0aGl5bW53b3R0bXJzc3V1dXh6eHt/fYCDfYaEfoeCgIt7eoR6eHt3 dHhzbm9uaWpvYWVpW193bX2Ad4d+eoN4dH16eHt9en51fYh5gIx1cnptaXJrY2p0a3N9e4Z/foh6 eX5ycHVyb3N3dHh4d356eYCEfYx+d4Zwb3RoZ2tvXWNvXWNrX2NpXWFtW15uXF9wY2t3aXJrb3Vw dHp+eX11cHRqZ21kYWdvb3uAgI1+g5J+g5KCfol7eIN0bnd1b3h5dX55dX5zanJzanJ3am5wZGhw aG93bnV4dH97eIN7dXt9d32Cd4SCd4R/eId9dYR9eox+e42Eeot5b39uZW1oX2dlY2Ryb3B7dYB+ eINybW5taGltX1tqXVhnYmN1cHJ/eIeAeYh9eHt3cnV1a29uZGhoX2twaHR7eIOAfYiHfoiGfYeC e4d6dH93b3RwaW5zbXh0bnl3a3R0aXJ0bW9yam11c4J6eId6c4Z1boB0YmhuXGJyXGF4Ymd6bW14 ampzZWNlWFZnUUZoU0dpWlFrXFRuXldrXFVwXFp0X119ZGWCaWp5aGl3ZWdwX2FtXF1yY2h6a3B9 bnN7bXJ/am17Z2l4Y2V4Y2V7bW97bW97aGh0YWF0ZW19bnWGcHiGcHiEbnN/aW6AZ1p4XlFyU0dp Sj9uU1N3W1uDY2SDY2R9Y1h9Y1h6XVF5XFB0W1Z5X1t9ZGh/Z2p9ZGV+ZWd/Z2iDamuDb2h9aWJ5 YVN9ZFZ/a2WGcmuNcneMcHWMamiIZ2R/YlZ/YlaCY12CY12CY1+HaGSDamuGbW6EaWSEaWSGY1h+ XFF+c4B5bnt0aGtyZWlvYmJyZGRtam5vbXB3aG13aG1vZWdrYmNlXl5lXl5qX1xwZWJ6b32DeIaI eoODdX55d3h6eHl/dH+AdYB9d394cnp4aWt0ZWhwa21wa211a296cHR7a3V5aXNza3B4cHVzcnt5 eIJ5eH94d354d353dX16eX54d3t3dX9ycHp0dIRycoJ3bnp1bXlyaG5tY2lyaGt3bXB6dH15c3t5 dHh3cnV5cnJ7dHR6d3+AfYaCeomCeol9eIh5dIR0c3p0c3p0aXJtYmp5bneAdX5/eYJ9d39/d4CD eoR4foN4foN3dHVua21nYmNuaWpteH5yfYN3eXp3eXp9eHl4c3R4cnh5c3l7eYt+e416eIZ1c4Bz aW1wZ2pyaGtrYmVqX15qX15lXWRyaXBzb3p3c35+c3t4bXVwb3dkY2prb3h5fYaCgpKDg5SAe4t7 d4Z3bX14bn56eYN7eoR4cnhwanB0am5zaW1zcHR1c3d1dHl4d3t9d397dX6Ad4iAd4h1cnp3c3t+ dIaEeoyCfYx5dINwdHprb3Vra2ttbW15dYCAfYh3cnNwa21qZGJoYl9nWlpyZGRzb3p5dYB+cnV6 bnJvZWdrYmNpXmlyZ3JydX53eoN6eYB9e4N6eoh7e4l1cnhuanBzZW5zZW5rYmhtY2lyaGtvZWlv ZXV4bn55cHp0a3VzYmFoV1ZqVVptV1x1YWV5ZGl4YVhkTkZjTDlkTTprU0xwV1BzXFd0XVh1Ylx9 aWOAaGmAaGl/a2t+amp3Y2FyXlxvYWV1Z2t0amt7cnN6bWhyZF91Y2d3ZGh9aG9/anJ6ZWp3Ymd6 aG57aW+Eb3eGcHh/a2t9aWl9YWNyVlhuUE1wU09yWF56YWd/ZWuAZ216Z2d9aWmCaWh9ZGN9Xlt9 Xlt/Y2OCZWV+Y2WCZ2l/amp+aWmDa2eDa2eGa2iIbmqMc3KNdHOUc3mQb3WEameDaWV/ZF97YVx7 ZF96Y16CY19/YV16YmGAaGeDZVx/YliAX0x6WkZ6and6and1a21yaGluYWlzZW53bnh5cHp4bXV5 bndzbm1wa2pzZ2pqXmJtX110Z2R4bXh/dH+AeIKAeIJ5dHh3cnV1cnh5dXt+eXp6dXd5cG14b2tz a3B0bXJ3a3R3a3R1bXR0a3NybXBvam5uaHNya3dwb3lzcnt7c317c317eIN7eIN4c4J0b350bnly a3dtaW9ybnRzaWp3bW51a3J+dHqAdYN9cn96a250ZWhzamlwaGdyb3NvbXB0anB3bXN3a3l6b314 dHp0cHd0bnRzbXN5b3N7cnV5dX56d3+AdYCCd4J5eYZ+fot7eoJzcnlraHBuanNzcnl3dX15eH17 en96en11dXh0cnVzcHRybX16dYZ6eYN3dX96dXd+eXqDeoJ4b3dvZ25qYmlzbXh6dH95d4R5d4R0 c3p4d351bXdrY21iY2dyc3d5e4h+gI2Ae4t0b351Z2t0ZWpqaW55eH16cHRzaW1yaGtvZWlwbXN0 cHd4cnh3cHd4b3t5cH15d4R+e4l0cnNyb3B5c36AeoZ7eoR6eYN6e394eX1zcnd0c3h/eouCfY1/ d355cHhyaXBtZGtnX2JoYWNya3RzbXV1bWtvZ2VwYmRtXmFoXF9oXF9qaXB3dX13dX14d354d4B6 eYN5d4R3dIJ7b3VyZWt0X2JrV1prWFtuW11tXWlyYm54anV4anV7amlqWlhlUVRqVlh1ZXR6anmD ZGJ4Wld0V0xzVkp1XVx3Xl11ZGV5aGl9aWmAbW2GcHCEb2+CcG2CcG2AaGd7Y2J1ZV53Z195a2t/ cnKDb299aWlzZ21vY2l0Z293aXJ4Y2p5ZGt9aG99aG+Gb3eGb3eDamt+ZWdzW1pvV1ZvVlF3XVh4 XmR7Ymh/ZWJ/ZWJ/ZW6Ga3SDaml6YmF+XmKAYWSDYl2GZF9+Y2N+Y2N7aml+bWuGam2Ha26HaXOI anSIbWiGamWGaWmGaWmDaWKAZ19+ZGF9Y19/ZWF3XVh3Wk50V0x3XVh7Yl1/YV1+X1x7W0l0VEN+ b3d9bnV4b3dyaXBnYV5qZGJ0Z295a3R4bXV6b3h3bXB0am54a3JzZ213ZWd6aWp4a3KAdHqAeoN+ eIB7dHd4cHNzbXN3cHd+eIB+eIB+cHl5a3RuamdpZWJuZ2tuZ2tuZ2tuZ2twZ2pvZWlqY2VuZ2lu aG5wanB5bnl6b3p6b313a3l1bXd0a3VwaHRuZXJyam91bnN5bm17cG96cn6AeIR/eId0bXtyZWd0 aGlvZ2VtZGNqZWlpZGhvam5wa29zbnJ4c3d1cnhybnRycHVycHV1b3V7dXt+eIOCe4eCeYN+dX96 eIZ/fYt7en96eX51dH50c31wbXh1cn1+e4uCf46Afox6eIZ0dHJycm9va3Jzb3V1c4B6eIaDf4iD f4iCf4N1c3dybndzb3h4cn14cn1zcntvbnh1bXd4b3lyaXBpYWheXVpubWl1d397fYaAe4tybXtv Z2VvZ2VqZWd1cHJ5cHp3bnhwa21vamt3bXN5b3V7dHl6c3h0cHlzb3h5eoN9fod7eXpzcHJybnR5 dXt6eod9fYl7d4Z6dYR3coB6dYSAe4x/eouAe394c3dvZWdoXl9pXl1pXl1oYWVwaW51cHJzbm9z aWpvZWdkXFtiWlhoZ25zcnl3dXp6eX53c3l1cnh0dIB7e4iEe4OAeH97b3BvY2RuXF9pV1toXGJu Ymh0aGt0aGt5bW5wZGVuWF1wW19wanVzbXh9aWd4ZGJ4YVt5Ylx6Z2d7aGh9aWeCbmuEcnqHdH2I dXt/bXN6bW1/cnJ/am1+aWt9aWt9aWt7bnd/cnqCc3p7bXR3a2pvZGNyYWJwX2FyX2V4ZWt6aG59 anCDbnOCbXJ/ZWJ5X1xtWlBrWE9vWFR1Xlp5X2h6YWl9YmR/ZGd/Z2qGbXCDaWSAZ2KDaG2Gam+Q cGqMbWd+Y155Xlp6YmF7Y2KCZ2uIbXKEa22CaWqEZWJ+X1x9YWODZ2mCamR9ZV+CZ2eAZWWCZFh4 W09yVUlzVkp3XlB/Z1iDaGN9Yl1+W0p4VUV+dHp7cnh5bW5zZ2huYmNtYWJtYWRvY2dzaHB3a3R0 bnl3cHt5b391a3t5bXN7b3V6bXiCdH+CeYZ+dYJ7cHl6b3h3bXN5b3V+eH5+eH6Hc4CDb31wb2tk Y19kXV9nX2JrY21waHJuZW1vZ25rZGlqY2hpY2trZW5waHJzanRyam9uZ2t1a291a29waHRuZXJ3 aXJ5a3R5b3N6cHR5dX56d399eX9raG5nXV5oXl9pXV5rX2FtY2doXmJqY2VuZ2lwbm9yb3BvbW5u a214c3d1cHR0cn99eoh/eol/eol7d4Z3coB6eod/f4x+fYd6eYN3dX1ycHhubXJ1dHmHgpGHgpF/ foh5eIJ0c3pycHhvaXJzbXVzdHp6e4KCfoeAfYZ9fX93d3lzcnlzcnl5c351b3pzbnJvam51Z2t4 aW5qY2VoYWNrXWJ4aW51dHt7eoJ1dHlqaW5rY2JrY2JqY2NvaGh5c3t0bndrZWtqZGp7b3WDd32D eoJ/d35zcntwb3l3eYZ7fot6c3N1bm5vb21zc3B4eX95eoB7d4Z7d4Z+eIODfYiEf5CDfo6HgId5 c3lqYlxkXFZpW11uX2JpYWhwaG93a3d7cHt3cnNzbm9kX15hXFtnZGhua291cnh7eH50c3hzcnd3 cHt7dYB7eX1+e397endwb2tzaGJvZF5tZWhyam13bW50amt4a294a294ZWt0YmhzbXVzbXV5a2l6 bWp5ZWN6Z2R7aGp/a26Db2+GcnKGc3mMeX+Cc3p9bnV6aG55Z215a2t7bm5+b3J9bnB/b3l+bnh7 b3N6bnJ9aWt3Y2VzWl9yWF5vXWFyX2N0YmV5Z2p9a2h3ZWJ6XVF7XlNrWkltW0pyV1p0Wlx5XmN6 X2R4Y2F7Z2R/Z2qGbXCJbW+OcnSJc32OeIKUe3eOd3J+a2J0Ylh7Yl5+ZGF/ZWuDaW99aGV3Yl97 XlN5XFB0W1d9Y19/Z2qCaW2CZ2J+Y15/YVF3WEl0VFN6Wlh/Z2WJcG+NdHWDamuCYU94V0Z5eH14 d3uAdHp+cnh5Z21vXWNwY2FzZWNwaGd0a2p1dXV4eHh9d4KCe4d7cHt4bXh5bXB7b3N9dHt7c3p6 b3p5bnl1a293bXB5cHh7c3p/dIJ7cH5va3RpZW5pX2NqYWRrZ2prZ2puaG5pY2lpX2VtY2llYmhk YWdnaG5panBvamtrZ2hvZWtrYmhtZHBqYm5vYmpyZG1vaGp0bW93bnh5cHp4c3Rzbm9uYmNrX2Fw Y2twY2tzaWpuZGVzYmN0Y2RvamtuaWpybXB1cHR1cnh4dHp1dH55eIKAfo17eYh4dYN0cn90c31/ foh/fYt6eIZ6e4J4eX95dX59eYKEf46Ae4t7eIBybnd0cHd0cHdvbW5yb3B4dYR9eol/e4d6d4Jv a3R5dX53eIB4eYJ4cn1zbXhuaWptaGluZ2luZ2lrX2NlWl1jWl9nXWN1bXd/d4Bwb2loZ2FvZGFz aGRtZWhtZWh1cnhva3JtX2hwY2t5cH2CeYaGfo5/eIh1c4Bwbnt1dYJ5eYZ5cnR1bnB1bnBza25v a3RuanNtbnRvcHd0c3p4d357eYiCf46Eg41wb3ljY1dfX1RtY2dyaGt0a3h0a3h7bXSGd36Ad317 cnhqZGJfWldqXl9vY2R0bnR3cHdva3JybnRya3d1b3p4dXl6eHt6eXVycG13aWl4amp6a3B7bXJ1 bXR3bnV0aXJ3a3R9aG97Z257bXR+b3d/cnJ/cnJ/bm16aWh6aGt7aW1+cnh/c3mEdH6JeYODdHd5 am13YmR0X2J5Y2h7ZWp7b3B6bm+AbneCb3iCbm6EcHCDbmt4Y2F5XltyV1RtWlNpVk9pWlNwYVp5 ZV51Ylt7YVx7YVx/ZWJ6YV13XVp0W1d7X2J/Y2WAaGuAaGuAanJ/aXCEaG2NcHWJd32NeoCOfXmI d3N9bWV3Z193Y113Y115YWJ9ZGV9Y1x4Xld1WE10V0xvVlN4Xlt+ZF+CaGN/ZVt+ZFp/Y1B7X013 WFZ/YV6CbXeGcHqIa25/Y2WAXFN6Vk1+eXp9eHl9d317dXt4aWt4aWt5Z215Z213amt7b3B4c3R4 c3R5c3t/eYJ4cnp1b3h0bW91bnB4b3d+dX1+dX17c3qAbnR/bXN3b3J4cHN0cHd1cnh+bnp7a3h0 aGtwZGhvaG1za3BwZ21wZ21qY2VqY2VqXmJqXmJrX2VrX2VuX2JuX2JtX19vYmJzXmVwXGNuX2dv YWhzYWR1Y2d5Z216aG55bW53amtvZ2NwaGR0bW9za251a21wZ2hzYWR1Y2dnZ2dtbW10b3N7d3p7 dYB6dH9wb3R3dXp/eYSAeoaDeoJ1bXRybnd7eIB9eol+e4t9fYl6eod6eYN4d4CCf457eYh7dX5w anN0b3Nwa29qaXBycHh9e5B/fpJ7eH54dHpwb3d5eH99e4Z9e4ZzcndqaW53ZGp4ZWtwZ2hrYmNq XmJoXF9lXmFlXmFuY2t1anNwY2FrXlxqY2V1bnB3bXNzaW91bXRzanJuZW9waHJ1a3+CeIyEeI2I e5F9eYJ1cnp7coJ9c4N5bW54a21zaW1tY2dnZGhkYmVqZWl0b3NvbnVzcnlwd4h4fpCCgIt6eYN1 b21tZ2Ryam14cHN/dH97cHt6cHSDeX2Ef4OCfYB0a2pkXFtqXGFyY2hvbW5vbW51a3JzaW9zanRy aXNybXB0b3N4bm9wZ2h9anB9anB3cnN4c3R5bXB3am5waWtuZ2l4ZGd5ZWh4anN7bnd/a2mAbWp4 bWd0aWN9ZGN5YV9+ZG2DaXJ7bnmDdYCDdHd5am11Y2lrWl9vYWN1Z2l6bnR7b3V6bnR5bXN9aGh/ amp+amh4ZGJ4YlZyXFBrVU1qVExpVlBtWlR1W116X2J9aGWCbWqDcnB6aWh4Xlp5X1t6X2KAZWiH anKHanKCa3CAam+CaW2Dam6Cb3WLeH6JeHSCcG2Abmh7aWN5X1x5X1x6YVx7Yl16YVp5X1h0XFFv V01yWlt5YWJ+aFqAalyEbWSCamJ/Z1h5YVNyV1N6X1uAaXWDa3iEZGiAYWR9W1NyUEh9dXh7dHd9 dHt+dX17dHR9dXWAcH2AcH17cHl9cnp4cnp5c3t/dH+Cd4J7cHl6b3h0b3B1cHJycHh4d355eH11 dHl1bXd0a3VzaW1zaW1wZ2pzaW2AanR/aXN6a3N/cHh9cn15bnl3bXByaGtrYV9nXFtkVlhnWFtq WFxoVlptWlxrWFtoV1RrW1dtXFhrW1dpV1ttW15zXmFyXV9zZ2h1aWp4ZWl5Z2pwamh0bmt0bW9y am1waWluZ2dyZ2V1amlraWpvbW56bXqDdYN6cnt1bXdqbnJydXl/foZ+fYSCen11bnBqZG13cHl6 c4OAeYl/fYx+e4t6eYB5eH9/fYt9eoiAeIJzanRqaGlraWpqa3RzdH2CfYyEf45zdHhub3N0bneA eoN/fYyAfo1yb3BpZ2hvZ2V4b25vbW5lY2RlXF9qYWRuZWRuZWRvam5vam5vZGNuY2JtaW95dXt/ en5+eX1ycnJvb29oZXNraXd4coh/eZCHfY2DeYl+eoN3c3t7c3p6cnl4b253bm13a2hyZ2NlX1tl X1toX2duZW1tZ3J0bnl5dISEf5CHgIl9d3+AdHh9cHR+cniDd32AeIJ7c313dHV5d3h+e3+AfoJ3 bW5pX2FqYWdwZ21yaGl0amt0aGtyZWluX2RrXWJqXF5wYmRyZWdwZGV4aXB9bnV4c3R4c3R0a2Vq YlxvYmJvYmJ1YWh6ZW1/anSAa3WCbmd/a2R9Z1t6ZFh5Yl14YVx6YmV+ZWl9bnV/cHiEb3l+aXNz YWRvXWFwY2N4amp/cHiCc3qDbXJ6ZGl7Z2d6ZWV3ZWR0Y2J7Ylt1XFVuV1NqVE9tVlFyW1Z4XV1/ ZGSHa2uNcnKMc3eIb3OCZ194XVZ3XmJ9ZGiAa3CCbXJ/amp/amp9aGV7Z2R/a26CbnB/am1+aWt6 Z2F4ZF5/YV1/YV15XleAZV5+Y1x6X1h+ZVt+ZVuCZ2OCZ2OAaFuCaVyGbmmHb2p9Y1h1XFF0VlN6 XFiHZW6JaHCIZGJ/XFp6VUhzTkF1bXR4b3d7c39+dYJ/eYKDfYaEeYKHe4R/dYZ9c4N/eYJ/eYKD dYCGeIN+dX19dHt0b25uaWhvaG1yam90a3N3bnVza250bW90ZWhvYWN1aGV3aWd5Z299anN7b3N9 cHSGcoKEcIB3cnVybXB1ZGFwX1xtWlptWlppWFVlVVFjU09jU09oW1ZpXFdqV05qV05pVk9pVk9u Wlp0X191Z2l3aGp3aG11Z2tyaGl1a214cHV5cnd4b2twaGRza2tza2t1b3V3cHd7c397c39yam9p YmduanB3c3mCgo59fYl+eXp0b3BwZWRzaGd1bXR1bXR6c4N+d4d6eIZ4dYN/e4SAfYZ+d3l4cHNv bW5ua21rbXBzdHh7eIB5dX5zb3pzb3p1b3qDfYh+eoZ/e4d7cnN1a211c3R5d3h1dXhvb3JqaGtr aW17d3h6dXd5bm14bWt0bW91bnB3eIB+f4iEgId/e4Jyb3Bwbm9ta3Vwb3l3dIZ/fY6Gf4iGf4iD gox9e4Z9c3d3bXB6cHR9c3d3b3JvaGppX2FpX2FuY19vZGFtY2lyaG5yZ354bYR9dYR7dIN7dX55 c3uDeIaGeoiCd4R5bntva3d3c35+fYJ/foOAd3ptY2dkYmNwbm93bXB0am5wYmRvYWNtXFhuXVpu YV5uYV51Y2l1Y2l1ZXJ7a3h6cHR4bnJ3bWNvZVxvYWVwYmd3Ymt7Z3B3ZGp6aG59a2p9a2p9ZWF7 ZF9+Y2OCZ2eDZ3OAZHCAbnd/bXV9aG19aG10YmhyX2V1YWV9aG1+aW6DbnOEa2p7Y2J1XWFzW151 Ylx6Z2F6ZWh9aGp7YVpwVk9uVUhwV0p3XF6CZ2mGcHWOeX6IdXmDcHSAaWN0XVdzX196Z2d/Z2iA aGl/ZWJ7Yl55ZGJ6ZWN9aGh/amqAa2t/amp+amR+amSAZV59Ylt6X1iAZV6DaGODaGOGaWmEaGiG amV/ZF9+Y16CZ2KEa2GEa2GAYVh9XVV5Wl15Wl1+X12AYl95Wk50VUl0Tz50Tz51a3J3bXN7cHl9 cnp9cnqCd3+DeIOEeYSGd4iHeImHeoCDd317cnV/dXl+eIB+eIB9cHJ5bW54ampwY2NzZXB0Z3Jy ZWd0aGlyZGJzZWN0aWV5bmp0aGl1aWp1a211a219b299b291b3V1b3V4bnR3bXN7amt1ZGVvYl1u YVxvYl9wY2FvYl1qXVhrWlBrWlBtXFtrW1pyXmF5ZWh3amt4a210aGtzZ2pzaGd0aWh+cHmCdH2D eX99c3l5d3h5d3h4dHp0cHd1cnh1cnhyaXBnXmVrZGd0bW96eId+e4t/dIJ5bnt3aG1tXmNtaXJy bnd4dH94dH9+eIB7dX5+eIN7dYB5cnd6c3hzbm9wa21qam1vb3J6dHp9d31ybnRzb3V0c32Dgox/ f4J7e35+dHh/dXl/e4KEgId9entyb3BqZ21wbXOEgImEgImAdHWCdXd7cHl7cHl6eX6Dgod/foZ1 dHt0am53bXBvbXB0cnV4dYSAfo2Df4iEgImAf4l+fYdta2hqaWV3b3R+d3t3dHhwbnJwanBtZ21y aG5vZWtwZGhyZWlwaW51bnN7dYB7dYB3coB7d4aDeoeAeIR5cndyam9zaXl9c4N+e4mAfox/en5w a29tZG51bXd3aXJ0Z290Y2RuXV5vXl1tXFtrXV9wYmR3ZGp1Y2lwZ2p1a296bm95bW55bmhzaGJv YmJvYmJ3YmlyXWR1XVx5YV9/a2WDb2mGcHCHcnKGcHWIc3iIcH+EbXuHcnmCbXR4ZGd0YWNuXVpv XltzYWR3ZGh+a3SCb3iAaGd9ZGN4YVt1Xlh3X1t9ZWF6ZWV5ZGR7Yld1XFFzWkx1XE54Y2qAa3N+ dX2GfYSGdHWAb3B+ZF15X1h0YVtyXlh9Y1+AZ2N+ZFp7Yld6YV17Yl56Y16AaWSAZ1+AZ1+CaGGC aGF9Y1x9Y1x7YVp+Y1yCaW2GbXCIbW+EaWt/ZF97YVx6Wld/XlyDZF6AYlx5X1V3XVN3WFN1V1F3 XVp6YV14WE51VkxyTTxyTTx3aG93aG95am95am93bXN6cHd5bnd9cnqDc32GdX+Cd3+AdX53dHV4 dXd4dH95dYCAcnl7bXR3ZWRvXl1rXlxuYV5rYmVuZGhzaW90anB0b3N3cnVyam1vaGprY2JvZ2V3 aG10ZWp1aHN4anV0cnV1c3d5cnd1bnNuZGVrYmNwZWJzaGRyZGJuYV5tYWJrX2FqYWRqYWRzaGd3 a2p9b215a2l5YV93Xl11Y2d9am6Ed4SEd4SEe4h/d4N/en6Ae396eHt5d3p6cnl6cnl0anptY3Np ZGVybW55c3mAeoCDd31/c3l0am5wZ2puanVybnl0c3p1dHt7cHt+c36AdYN7cH55cnR5cnR1bnN1 bnNvamt4c3SDeIB9cnp3cHd0bnRzcnl7eoJ5fYN4e4J9en6Cf4OIg5KEf46GfYR1bXRwaHJ4b3mH g4yDf4iEd3+HeYKAd3p9c3d7eX2AfoJ+eoB1cnh4bnJ+dHh6c3h4cHV9dYSGfo2Dfo19eId6eod5 eYZwbm1oZWR3b296c3N4eHh0dHR0a3N0a3NwaW5uZ2tvZG1uY2ttaXJzb3hzb3pzb3p3b3J7dHd+ dHqCeH55dHVtaGltZG55cHp/eouCfY2AeoZ5c350ZWp0ZWp6bXV4anN6aWhuXVxuXF9rWl1yXWJ4 Y2h1ZGNwX15wZWR0aWh5am9/cHWCdXd5bW5wZWJoXVpqXVhoW1ZzW153XmJ/ZWuGa3KJdHmLdXqL e4CNfoOMfYKHeH2IcneAam9zYmNtXF1qVlhvW11wY2FzZWN+bnh/b3l9aWt9aWt4X2F3Xl95ZV96 Z2GCZ2d+Y2N7ZFx6Y1t1XFV4Xld9aG2DbnN/dXl9c3d+amN6Z19+ZFp/ZVt7Yl54Xlt9Yl2AZWF+ ZGGDaWWIbW+Lb3KEb2+CbW2HamqIa2uHbWiGa2eCamJ6Y1t7YWGDaGh/bXCDcHSLb3KDaGp5X1N1 XE91WE97XlWCZ199Ylt1XU9vV0l0TkV0TkVzWFRzWFR7WEZ5VkR7Uz51TTl3bXNvZWt1YWN3YmRw Ymd0ZWp4anN4anODcHeDcHd6dXl4c3d3bm15cG9vb3J0dHeAc4B/cn91Z25wYmlvXFpqV1VkXWJu Z2tybXB3cnV1a29uZGhjXVteWFZkWlhoXVxuXV5vXl91Y2d5Z2p0bXJ1bnNwaWttZWhqYl5tZGFr amdvbmp1bWl0a2hrZ2hrZ2hrZGdtZWh3bXN7cniDdXV7bm57ZFx1XlZ0bXJ9dXp/fYyAfo1+eoZ/ e4eAf4d9e4N6en15eXt7eIN5dYB0aXJqX2hpZGN0b251dXh3d3l4dH95dYB4cHVza3BzbXVwanNu anNwbXV1bXR1bXR1cn13c359gISChomEfoSCe4J4cnp6dH1+eoZ4dH9zcHRtam5ybnd1cnp4d357 eoJ+e3+Gg4eGhI6Af4mDeIZ7cH54anOAc3uHgIyIgo2Df4iDf4h9e4Z3dX96eYN/foh+eoN/e4R4 dH19eYJ+c359cn2EeImLfpCAeX55cndzcH9wbn1qaGtraW14cnh+eH59eHt0b3NyaGlvZWdpYmJn X19jWltjWltkX2FqZWdoYl9lX11lZ11ub2V5b3N+dHh1b21rZWNoXF1wZGV4aHR+bnp/eId+d4Z/ cHV5am93am55bXB5b2VtY1pwXFxyXV15ZXB9aXR0aWhtYmFvXl10Y2J9aG+DbnWIdXuCb3V5aGdv Xl1qWlhpWFdvVlx1XGJ7ZW2DbXSEeXiJfn2Nf4iMfoeMeYKJd3+EbnV7ZW15YWJ1XV5vXl90Y2R9 anB/bXN9bXd9bXd3anByZWtwXVdzX1p5Yl15Yl1/YV57XVt0W1Z1XFdzXFd3X1t4ZF2Cbmd/b2d/ b2d+a1x/bV2IcGuIcGuLa2iIaWWEbWeIcGqJdHmMd3uSdX2Qc3qRdHmLbnOJameQcG2LcnWGbXCE Y15/Xlp+Xl+CYmN/a2WEcGqCaWh4X150VENuTj1wUE54V1V0YVp4ZF11XE5wV0luT0VrTUNyU0h3 V011V0hzVUZ4U0F4U0F1c3Rwbm9tZWVnX19rXl5wY2NuZGpwZ213b3J5cnR5cHh3bnV0aWV4bWlz bm95dHWAeYh/eId7cHl3a3R0ZF1vX1hrYmVwZ2pvbW5wbm9zaGRrYV1jV05kWE9nXVRlXFNjW1pk XFtrYmhwZ21zZ2pyZWlvZGFuY19rXl5wY2Nvamt0b3B5cnR5cnR0bW90bW9yam1za255cHp7c313 bW5zaWpvZ2FuZV9waWt/eHqDgoyDgoyCeYOHfoiDfo1/eol+eIN5c356eIZ+e4l7cnNzaWpzaW15 b3N6dH15c3t5dIN5dIN4dHpybnR4a29zZ2prZ2pwa29wa21zbm9wbXNzb3WAhI2Hi5SGh41/gIdz c3VwcHN6b3h9cnp6dHpwanBzbXNzbXN4dHp/e4KDgISCf4N9foR9foR/dH1+c3t6bXh/cn2DfYiH gIyCg4yDhI2AeYh/eId4e4R6fod6eX57en9+fYd+fYd9dH56cnt6eIZ/fYuCd3V4bWtqYWRpX2No YWFuZ2dzdH15eoN4dHpva3JqZVtpZFptXFhoV1RlVFdkU1ZjVVplV1xkXFhiWlZlYV9uaWhtaXJy bnd4b2lzamRpYV1oX1xpXWN1aW+AeoaAeoaCc3qAcnl7b3B5bW5/b2d1ZV1wXlh3ZF54bXV3a3Rz Z2puYmVqXV1wY2N7Z2uAa3CEdXp+b3R1ZGNoV1ZkUVFjUFBoUFFuVld3YmmAa3N/dXmEen6If4mH foiHen6Dd3qHcnl/anJ/amp+aWl7aW96aG53anB1aW96a256a254Z2h1ZGV1XFd3XVh5Ylx4YVt4 XlpyWFRwW09vWk5tVFBuVVFzXFd5Yl2CamSGbmiDb3KHc3WNeH2OeX6Od3KLc26Eb22Ic3CJc3iJ c3iNcHOLbnCNa2qLaWiEaW6Lb3SMcHOJbnCIZVqAXlN4Xlt6YV17aWN5Z2F5YVRvV0puTjtvTzxt T0xwU09wWlF1XlZ7XUl1V0RzU0RvT0BvU0ZyVUh1VEh0U0dzVEl0VUp6b3p6b3p7b3BzZ2huYV5u YV5rYmVuZGh1bnN4cHV7cnh6cHd7bW95am10am54bnJ+cH6Ac4B6dHp5c3l+am17aGp0ZWp1Z2tw Z2hpX2FnXVRkW1FlW1drYV1rYVtlW1VkV1dqXV1yXml0YWtrZGdqY2VuY11rYVttX1t1aGNwbnJ7 eX1+fYR9e4OAd3p9c3d7cnV5b3N4b3l5cHp3a2p1amluaGVqZGJqY2V3b3J9e4OAf4eDeIaIfYuH gIyEfomAeoN+eICCf5F/fY6EeYKAdX54cnh7dXt6eYB1dHt6eYB7eoJ/dH93a3d4aW53aG1/anKC bXRyam1vaGpuZ2t3b3R/fYuGg5GEgIx7eIN1dXVubm50b3N6dXl1dHlzcnd4cHV0bXJzcnd6eX5/ eX9/eX+DeX+Ad31+dX16cnl7cH5+c4CDfo6Ae4yEgpGIhpWGfYmEe4h9eYKAfYaGgoiHg4mEhox9 foR5cnd5cnd3dXqCgIaAenhwamhqXlNrX1RoYltuaGF3dX9+fYeAe31zbm90aWNwZV9vZWdvZWdq XGFlV1xjVlRoW1hfW1xhXF1nXWNtY2lwaHR3bnqDcnOAb3B3ZWRuXVxkW15nXWF0c314d4B9eYR/ e4eEdXh+b3KCbmt/a2l5aGR6aWV9c3l4bnR0aGtvY2dvXl90Y2R5ZWN+amiAdHp9cHd1ZV5nV1Bj UUphT0hpT0hvVU5zYWR+a2+Cd3WNgoCQg4mMf4aJc3iDbXKEanCEanCAam9/aW5+aXB9aG99a216 aWp7aGh9aWl6Z2d4ZGR9Yl5+Y197Z2R6ZWN6Y110XVdrVkpoU0dtVElrU0huVFR5Xl5+ZGGGa2iH cHqHcHqEcHOIdHeIcGqGbmiEbnWEbnWDbnOEb3SCaGOAZ2KAZ2KGa2eIcnmJc3qJcHKIb3CCX1R0 U0d0WlV9Yl1/ZV54Xld+WkZ3Uz9uTUFyUEVrU0xwV1BwWExzW055WkR1VkByTkRvTEFyUUN0VEV0 UEZ0UEZ3UUV5VEd7c397c3+Ac354anVvaGpuZ2lwZGhvY2dzZ213anB6bnJ9cHR5b3V3bXN3am54 a293a3R3a3R5bnd+c3t/b3t/b3t3bXN3bXN3am5yZWlqWlhqWlhlWl1rX2NqYl5tZGFlXFNjWlBo WlxqXF5jXGFoYWVrYmNqYWJtYWJ3amt4b3eAeH97eoR6eYN7c315cHp4cHV1bnN0anBzaW9yaF5u ZFttX11rXlxnYmNrZ2h7d3V/enmCe4KGf4aCfol+eoZ7dXt7dXuCgo6GhpKIg5KJhJSCg4x+f4h7 eoJ0c3p4cn16dH97eIN9eYSAeISDeoeAeIR9dIBza3p0bXtvZ3N6cn6Afo2Cf456eYNta3Vya3Ry a3R5c353cHtzcHRyb3N1cHRzbnJ3dHh5d3p9c3l7cnh9dHt7c3p5dIN5dINybnl1cn2AfpCGg5WD gpaIh5uAeYh7dIN7dYCAeoZ/f42IiJaDgol6eYBycnJqampza3B4cHV9c3d3bXByZGJvYl9qYlxt ZF5zcnd9e4CAeIJ+dX9+cnh/c3l5dX5zb3hyZWtpXWNjWFVnXFhjW1VhWFNiVVVlWFhpYWp1bXd4 cn15c351a29wZ2pvZWluZGh3cHl5c3t9eIeCfYyEe4h/d4OEdXiAcnR/bm1/bm2Cc3h7bXJ5Z19y X1hwXFp1YV55ZWiGcnSGd35/cHh+ZF9uVVBhUD1cTDloTklwVlF3ZWSDcnCIeXuOf4KQgo2LfYiE cnh+a3KAbW17aGh9aWt5ZWh5am96a3B/a2l+amh9ZGV/Z2h9ZGN6YmF6ZWN+aWd+anWEcHuIc3V5 ZGdvV0ppUUVoUTxvWENvWFB0XVV+ZF+DaWSDbmuGcG6Db2l/a2WDaGSDaGR6Y299ZXKEb3KCbW97 ZF53X1p0YV5+amiIdHeNeXuIbmmCaGN9Xlh4WlR1XWGCaW2DaWR5X1t5VT9zTzpvT0p3VlFyWEp0 W016WE14Vkp1U0NvTT1zSEB0SUF4UDx7VD9zTzx0UD17Uz6AV0N9cHR7b3N4cHNza251a290am5v YmJyZGRzZWV1aGh6bnR9cHd5cHh1bXR1bnNyam91anV3a3d1bnN6c3h9dIB/d4N9dH57c317bnl1 aHNiXVNeWk9hWlxqY2VzaGRwZWJrXlxkV1ViVk5kWFBjVlRnWldrXlxtX11yZWd4a216b3h9cnp3 cnV0b3N4bWt1aml0aGt0aGt3bW5zaWpwa2pybWtyZGJtX11pYV1tZGF5bm2Cd3WEfoSIgoiDgol7 eoJzcHR1c3d6eId9eomHf5CIgJGGhI6Af4l+e31yb3B1bnN3b3R1cnp4dH16d317eH57eIN7eIN3 cHl3cHl6cIJ/dYd/eYR7dYB0b3Bwa210bnl4cn1+dHp5b3Vyam9uZ2tuZ2tyam94a297b3N7c3J+ dXR9dXp7dHl4dYN1c4Bwb3R3dXqCf42EgpCIhpWIhpV/eId7dIN7d4eCfY2DgJCJh5aEgpB5d4R0 cnNraWp4a219cHJ4cm91b21vZ2V0a2p0aWVwZWJvaHd4cH99dHt/d359c3d6cHRybnduanNwZWJo XVphVU1iVk5kWlZlW1doXFNtYVdqYmFvZ2Vya3dzbXhza3Byam90am5yaGt0anB3bXN5b39+dISA eIR9dICAdHp9cHeCcHKGdHWDdHl+b3R+aW54Y2hwXlh3ZF57bXKGd3uLeoR/b3l7Yl5tVFBnTkBd RThkTEdyWFR4ZHKJdYOOgIyQgo2NgISLfoKGb3d5Y2p3YmJyXV13Y2V1YmR0aGt1aW1/amh9aGV9 ZGh+ZWl5ZGJ5ZGJ6aWp9a22Da4KLc4mRd32LcHeCX1VzUUdrUTluVDtvV0l3XlB7aGKAbWd/amp/ amp+aWd+aWeCXlqAXVh9YmKAZWWAZ199Y1x1XVByWk1wXVRzX1aDbmuEb22CaGR9Y193YVN1X1F0 X2J/am2CaVt5YVN3XEV5Xkd9Y16AZ2J9Y1Z5X1N+VEx3TUVvTT10UUF5UEV9VEiAW0x6VUZ0TD50 TD55UDqAV0B6aWp4Z2h0bW91bnB5bXB4a29vY2drX2NzX2J1YmR5bW56bm94b255cG9zcHJyb3Bz aWp0amt0b3B0b3B3c3l7eH6CeYaAeIR+cHl1aHBiX1RhXlNqX2hwZW53bXN4bnR3YW1rVmJlUEVi TUFfT0BlVUZoWFBtXVV0ZWh4aWt5bXB5bXB4bWlzaGR3a2V0aWNwZ2p1a295cHh9dHt5dXt6d315 bW54a210ZWp3aG11bXd6cnuAfYaGgouGgJB6dYRzbXV1b3h4c3d6dXl+eXqDfn+Mg42Hfoh+e31z cHJyZWlyZWlza253b3J6d4J5dYB1b4Z1b4Z0b35wa3p4bn99c4R+dX95cHpvZWltY2dzanR6cnuD dHt9bnVyZGRtX19tYmFzaGd5am9+b3R+dX2CeYB/eX99d313dHhyb3Nybnl+eoaJhpGIhJCIg5KI g5KDeIB+c3t9eoiCf42HhpCLiZSGhI5+fYd6cHdyaG56Z3KAbXh9dXh3b3J1cHJ4c3R3bXBzaW1v ZWtyaG50bXKDe4CAeXt3b3JybW5rZ2hrXlptX1tpV1BkU0xpXFxqXV1vXWF0YmV1aGh0Z2duZGpv ZWtzaW14bnJ4bXh1anVyaGtwZ2p0Z296bXV7cH57cH54bXV4bXV+cnWMf4OGe3+CeHt5bm14bWtz Yl55aGR9b3iEd3+LeoR+bnh5X1hzWlNwWEdpUUBtW1V3ZF59cn2Lf4uNf42Nf42Gend+c29/am1z XmF0W1RzWlN1XFh4Xlt5Z2F6aGJ+amR9aWN+aWmCbW1+am1+am1+b3KDdHeLd4SLd4SSeXqMc3SD YVZ5V01vV0RwWEV3XFd9Yl1/ZWF/ZWGDaWWDaWV5YV93Xl17W1Z/XlqCZ2KAZWF+ZFp5X1V6V1N5 VlF0XFF3XlSDa2WCamR/ZGd+Y2V5Yl13X1t6YWuCaHOEZFqAYVZ7XFSCYlp/ZWKEameGZFCDYk5+ VESAVkZ3U0h5VUp9W1OAXlaCXlB7WEp3Tj50TDx6Tz5/VEN3ZGp4ZWtwaWlwaWl4ZW53ZG1vYWVv YWVvY2dwZGhua29vbXB0am53bXByb3Byb3B0am5yaGt1amd4bWl5cnR+d3mAfYh/e4eHeYJ3aXJy ZV1yZV13aG15am94b3d4b3d5bXNwZGpuWldrV1VoXlVqYVdzZWN9b215dHV3cnN6cHJ6cHJ6bW10 Z2d3Y2N1YmJvZWt4bnR4dH1+eoN/fYt9eoh9eHl1cHJ1Y2dwXmJkanJvdX2Cf46Cf46EeYR9cn14 bXV4bXV+d3t9dXp7dHl9dXqAdIaDd4h9ent0cnN3aWlyZGRqaGdraWhwZ2pzaW1vZG9wZXBtaHdv anl1bXR5cHh4bnJyaGtuYmVtYWR3Z3N+bnqAdHp+cnh3bm1yaWhvZWlwZ2ptbW90dHd9e4aEg42C fYyCfYx+d3t5cnd1bn1/eIeIh5uMi5+Eg4t+fYSDeIB+c3t6e4KEhoyGg5KJh5aHgpKCfY2AdX56 b3h4d357eoKGeoZ9cn13c3l+eoB+fn56enpyamprZGRwZGh9cHR6cHR4bnJ4a29yZWlyYV1rW1dt XE9qWk1qWlhyYV90Y2R1ZGV0ZWh0ZWhyX2h3ZG15a3d+cHt7cHt1anVyaGtwZ2puaW1ybXB0am53 bXB4a297b3N+dHqGe4KJfX6AdHVwamVtZ2JwX1x5aGSCcHKDcnODbmt7Z2R9Y2mCaG59Z2t4Ymdw XmR6aG6Ac4CDdYOHc35/a3d6amN6amN+amN7aGF/Z1x9ZFp3YWV9Z2uAam+Aam99aGh9aGh9aGV9 aGV5aGl7amt7amuAb3CCdH2Ed3+MeneCcG1/ZVh/ZVh9Xlh+X1p4YVt7ZF6EbWiHb2qNcm2Ha2d6 Y1FvWEdzWk13XVB/ZGGDaGSEal99Y1h7Vkd9V0h4XlR+ZFqDb2WAbWOCamV+Z2J6XVR9X1Z+XmSG ZWuGZ2SJamiGamqHa2uGameGameHZ1uIaFyEYU5/XEl7VkV7VkV7Wk6CX1SMYlaDWk59VEZ5UEN7 Vkl+WExlV1ppW11vXFpuW1hvW19vW19qXGFtXmNtXmNtXmNtYWdyZWtyYm51ZXJ6aHB6aHB5ZGt4 Y2p1aGV3aWd3bW57cnN/d4CCeYOCc3h5am91aGVyZGJwa2pzbm16dHp7dXt5dHV0b3ByZWl0aGt5 am19bnB7dHR/eHh7dHd9dXh9dXh9dXiHdHiCb3N6aWp0Y2RzaW94bnSEcICIdISGgJCEf45/ent4 c3R4ZV9zYVtrZXB1b3p+e4t+e4t+eH56dHp9cHd/c3mDeICDeICDdX6CdH19dH59dH57eX15d3p6 dG91b2pyampqY2NoYWNqY2VnYmVqZWlrY2pvZ25wa290b3N5bmp0aWVuY11uY111a296cHSAeXuA eXt7d3h6dXd5b3N1a29waGd3bm2DfYONh42Mho6DfYaDd32CdXt5cId+dYyHhpqLiZ6Dgox+fYd7 eIN5dYB0dXt7fYODgJCJh5aGgJF/eot5eoB3eH57en+Af4SAd3p7cnV5dHiAe3+CfoR5dXt5am1u X2JrXWJzZGltZ21vaW93amt4a214bWl1amd4ZV51Y1xyX1p4ZV93ZWJ5aGR0Z2d0Z2dyY2V0ZWh4 a3J/c3l+bnh4aHJuYmVwZGhrZ2prZ2ptZWVtZWVyaGl1a214cnp+eIB+dHp5b3VyZWdqXl9tWlN1 Ylt6aWp+bW5+a2V7aWN4a3J+cniAdHV+cnN7aW17aW2Cb3iEcnqAb3B7amt3aWR7bmmDb22EcG6G cmuEcGp6bXV7bneCa3iAaneCaG5/ZWt5ZV93Y113Yl90X115YWKCaWp/cHWCc3iLdXWLdXWRcm6R cm6Jb2iGa2SGa2SIbmeOeH+UfYSWgICMd3eHaVx+YVR1Vk57XFR+aG2Ca3CGameCZ2OCZFiCZFiA a26HcnSLd3eHc3OEamN7Ylt4Vkp5V0x3W1t+YmKEaGqJbW+Eb2+Eb2+Ha2eDaGODaVyEal2EZVR9 Xk16VkB6VkCAXFWEX1iMYViEWlF+VER6UEB7U0eAV0xrVVBvWFRpWlNqW1RuWlptWFhwWFpvV1ht WFtuWlxvWl5wW19vW2J0X2d0Ymh0Ymh7Z2t6ZWp0YmV0YmV9am6EcnV+eX19eHuDb297aGh1ZGN0 Y2JyaGl6cHKAdX6Cd39/c3d7b3N4a3J6bnR7b3N+cnV6eHl7eXqEeHuGeX2GeX+AdHqGeICHeYJ/ c3d6bnJ+a3KCb3WCbnmEcHuHeoyJfY6Cf4N3dHh1aGVyZGJqaGtzcHR7dYB6dH94dH14dH13bnV5 cHh7c32AeIKDeX+CeH5/eH1/eH19dHuCeYCIfn+DeXqAdXR3a2pvZWltY2doYWVqY2hwZGVtYWJz bm97d3iAd3p+dHh4cm11b2qDe4CCen9+e39+e3+Je4SGeICCen91bnNyYWJ0Y2R1eHmGiImHh4mC goR+dHh7cnV1a3+Ad4uLgpmLgpmDgI59eoh1dH5ycHpycHV6eX6DgI6Jh5WIg5Z/eo14dYN1c4B7 d4aAe4t7d3h4c3R4cHN9dXh/en57d3p4bWltYl5oV1htXF1lXF9oXmJzZGl5am94bnJ6cHR5bW55 bW54bWl7cG16b2l6b2l+amh6Z2R0ZWp4aW54bXV1anN5bXB3am5vYmJuYWFtYWJtYWJtY2dtY2du ZWRwaGd4anV7bnl7b3V1aW9tX1tpXFdrWE9uW1FvZGN3a2p5bm15bm1+b3SDdHmGeHOAc25+bW5+ bW59bnB+b3KAbWp/a2l3b3J6c3WEd3+GeICLeH6LeH6Ed4SGeIaJcn6Hb3uEaG+AZGt+Ymd9YWV6 YmF4X15zX2J4ZGd+aW6DbnOMd3mQen2ReoKVfoaUe3WOd3CIc3OIc3OQeISZgI2ahImSfYKIcGh6 Y1t7W1iCYV6Ga3eHbXiGam2Gam1/bm2Ab26IdX6LeICMdX2IcnmNaViEYVB9W1B7Wk91Wlp/Y2OA a2uEb2+Hb2mGbmiIamGHaV+Ial6HaV2JY1aGX1N/W1CEX1WGYl2HY16JZFp/W1B5UT11TjpwTkB5 VkhuXFZwXlhtX1twY151Y11vXVd0XFtyWlhtW15tW15tXF1uXV5vW1t3YmJ1aGh4amp7cG95bm1v Z2VwaGd0bXJ5cnd9eHl+eXqCcHJ7amt0aF90aF9zaWp6cHKAc35+cHt7bm56bW15bW59cHKAdHWD d3iCe4KCe4KGeICHeYKEeHmCdXd+eX2DfoKEd4KAc35/c3l7b3V6a257bW+Cc4SGd4h7eoJzcnlz a25waWtyaG55b3V7cH55bnt5cH13bnpwcHNwcHN1bXR5cHh7cHuAdYB/eId9dYR+d4aDe4uDfYaI gouLfoKEeHt6cHRzaW1wZ2puZGhuYmVrX2NuanCDf4aMf5WGeY57eIN1cn2CeIiEeouDeIOEeYSH gImIgouHg4l5dXtyXWJyXWJ0c3qGhIyHg4l/e4KAeHR+dXJzbXh7dYCHfZGLgJWGf4t/eYRycHht a3N1b3h7dX6AfoyGg5GIhpV/fYx0cHlwbXV+c36AdYB5d3h3dHV4cHN9dXh5d3V6eHd5b2VvZVxp XVRlWlBoW1ZpXFduZGh1a297b3V9cHd5cHh5cHh5cHh9dHuEeHmDd3iDdXN7bmt3bXB1a299bXl1 ZXJ3am50aGtwXV9uW11uXF9wXmJtYWJtYWJqYmFtZGN4Y2p5ZGt7aW10YmVuXFNoVk1qV1FvXFZ0 X2R6ZWqCbXR/anJ5a3d+cHuHc3WAbW96bm94a22Aa3CCbXJ+am2CbnB/cn+DdYOMeIaMeIaNeIKM d4CGeICGeICGd35/cHh/a259aWuDaGp/ZGd1YmJ1YmJ1Y113ZF57Y2eCaW2HcHiReoKQgIaSg4iS foCJdXiHbm+LcnORdYiafpGWgpCMeIaIcGiCamKAZ2OAZ2ODcHeCb3WHbm2GbWuJcHSJcHSJc3iL dHmMb2+GaWmLaF+EYlp5V1p5V1p0WF17X2SCZ2KIbWiLcGOGa16Eal+Eal+Eal+Ga2GJZ1uIZVqC YV6DYl+HZWGEY16OZFSIXk59UzlySC9ySThzSjlyYV13ZWJzaGd1aml1bWl0a2h6bW15a2t1amlz aGdzZWVwY2N0Y2R5aGl6bnR/c3l7dHd1bnBva2hwbWl0bnR5c3l7eIN6d4KCbXJ/am90a2p0a2p1 b215c3B5b3B3bW56bWp7bmt6bnJ+cnV6cnl6cnl9dICCeYaGeoOCd3+Gd3t/cHV7cHl+c3t/dH17 cHl7cnV3bXByampyamp/b3uDc395cnd4cHV3am53am5yZ291anN4bnR1a3J0anB1a3JtaW9taW9r ZGRvaGhza3B6c3h7eoJ6eYB+eIOAeoaEeouIfo6MgIyIfYh7eIBzb3h1anVwZXBrZGRpYmJuanWA fYiNhpWJgpF4dH93c35/eYR/eYSDdYOGeIaIgJGNhpaDhpJ5e4hua21qaGl3c35/e4eMhpGHgIyE fYJ+d3t0bW94cHN/dIKIfYuJg46Efol1dXVzc3N1bXR7c3qDfYaJg4yHhJZ+e414c3Jzbm14b3d5 cHh+eX19eHt1c3R3dHV5d3p3dHh4cGd3b2V1aV1vY1duXlZqW1NpYmRza256bnJ5bXB4bnR6cHd7 cnWCeHuGfYSEe4OIenWDdXB+c3J5bm17aHN7aHN7aW13ZGh1XVxzW1pvXF5yXmFuXV5vXl9vYl9v Yl9zX195ZWV3aWRyZF9wXVZwXVZ1Xlh7ZF5+am1/a26Aa3B/am9+b3KAcnSEc3SAb3CCcG2CcG2I dHeGcnSAbnSCb3WDbniGcHqHdHqGc3mIdXmGc3eGd3uIeX6Jd32AbnR+aWt6ZWh+aWd6ZWN1YWF0 X19wXVtyXlx1XFd/ZWGEbnOSe4CRgomSg4uSfniJdW+GZ2GLa2WNdYKSeoeSeoeMdICLcmeDal9+ aW6CbXKEc3SCcHKEa22GbW6IbnSJb3WJcG+JcG+Lal6IaFyCY2F/YV5/XFp7WFZ5W1h+X12GZGKI Z2SLaF+HZFyDZF6CY12EZV+HaGKHZFiDYVV+XViCYVyHY2GEYV6MYVCDWEh+VDx5TzhyTDNzTTR5 ZWh7aGp6anR7a3V6dHp5c3l+dHh7cnV5cnR4cHN4aWt1Z2l3aGp5am1+c3uAdX5/dXd5b3BzaW10 am53aG99bnV3c3l1cnh/cHV9bnN/cHV9bnN5b3N5b3N4bm93bW5+bWuCcG97cG97cG94bm90amt7 bW9+b3KGeoODeICJeHmEc3R6bnR5bXN9anB6aG55bm1zaGdtY2duZGh4aW59bnN9cHR+cnV7bmt6 bWpvZWlvZWl0aG53anB3am53am5zZ2pzZ2pvZWdwZ2hvaml3cnB+eX1/en6He4SCd3+AdX6DeICD fo6Ef5CAfox7eYd4cn1rZXBvZF5vZF5wb3R/foOMhJeEfZBua3pzcH96eIl5d4h+dYKCeYaHepCO gpeIhpWCf451c3Rwbm96cICDeYmLg5KMhJSHgId/eX90bXJ1bnN5cHqIf4mLhJCJg456e4R1d39y b315d4R/eYSLhJCGg5GAfox9eHd1cG9zb3V6d31/eYR+eIN5dXtzb3V4dXd4dXd6dXd6dXd7b3N1 aW1zYlVvXlFtYWRzZ2p4a215bW56aWh7aml6a26AcnSHe4mJfoyMfomMfomDeX1+dHh6aG55Z214 aWt0ZWh9ZGN6YmF0YVpuW1RpWlFqW1NvXFxwXV11XVx7Y2J1aGh1aGh6Y1t7ZFx5ZWV/a2uCc3iG d3uGc3eEcnWGcHOGcHODdHmGd3uNfoCMfX+Oe3+Jd3qAcnd+b3SCbnCEcHOAdHh+cnV6cHR7cnWG d36IeYCEdXp6a3B9ZGN7Y2J3Y1p3Y1p3XFxyV1dwXFpzXlx3Xl+AaGmEcH6Qe4mSf4aSf4aSeG2I bmN/ZGSHa2uMd3uRe4CRd32Nc3mEbWSAaWF/bm+DcnOLc26GbmmEaWmGamqHbm+Hbm+IbWiJbmmO a2GLaF2EZ12AY1qAXlR9W1B6Wld/XlyHZWOJaGWLZ2KGYl2AX1uHZWGHaV2HaV2JaFiCYVGAXFGE X1WDYViEYlqGXUiCWkWEW0SEW0SCXUmCXUl+b3SAcneCc3iAcneDeoKCeYCAd32Ad31/eHh9dXV5 b3N5b3N7b3V9cHeAdYN/dIJ+eoB6d311a29zaW11Z2t3aG1waWt3b3J+cniGeX+IeIKEdH6AdHh+ cnV7cHl5bneEcHCEcHCAc3N/cnJ7b3B4a217cnN/dXeHfYOGe4KEeoB9c3l5bmpzaGR3Ymd4Y2h1 YWFwXFxoWlxpW11vYWV5am+AcnSCc3V+cnN4a21vZ2VtZGN0amt3bW55cnJ0bW1zbm90b3B4bXV3 a3Rwbm90cnN7cnWHfYCHgoZ/en51bnN5cnd/eIiEfY2Hf5CIgJF7c3ppYWhoXVpuY191dH6DgoyJ fYOAdHpqZWRqZWRubXJwb3R0c3h4d3uCfZCHgpWRh5eGe4x9eYJ9eYKEe4iJgI2MhJSNhpWIgJGD e4x4cnp4cnp7eYeDgI6Lg5SMhJWEgIx7eIN1cn14dH+Ae4yGgJGIgJGHf5CGgIR7d3p6c4OHf5CI go2HgIyAeH96cnl7cnh+dHp+e3+AfoKGgIJ6dXd3aFpvYVNzYmN1ZGV5am16a26CaWqDamt9aG9/ anKDdYOMfoyNgJaOgpeNgISDd3p0amtvZWdyY2p3aG9/a257aGp4ZFtvXFNrWlNrWlNvXFVwXVZ5 Yl17ZF94aF96amJ/a2mAbWqAc3OGeHiEeoCJf4aHen6EeHuAcnmCc3qHdH2NeoOOg4yNgouSf4aI dXuEcHCGcnKCcnuEdH6Dd31+cnh7aW1+a2+DcHeHdHqEeHl+cnN+amF/a2KAb25/bm17Z2d0X19z YVt0Ylx9aG2DbnOJeYiNfYyQgIONfoCEcmR9al16Z2eHc3OLfoSQg4mQd3iIb3CEbWSAaWGAbW2D b2+Jb2uHbWmCZ2KDaGOIa26Ia26JbmqOc2+UdHKRcm+Jb2qGa2d+X057XUx9WleCXlyEZV+HaGKI Z2KEY16GYl+OamiMaV6Nal+LaVWHZVGDY1eHZ1uIZFaIZFaMZ1OMZ1OMZViNZ1qOalqUb16CdXmD d3qGeX2Dd3qGeoOEeYKHeYKGeICHeoCGeX+CeHt/dXl+dHV+dHV9cnp5bnd4eYJ1d391a21uZGVu YWFvYmJvaGh4cHCCdH+HeYSGe4KDeX+Cc3V/cHN+b3d/cHh/dH2Cd3+EeoCCeH59dXp6c3h+dHiA d3qAfYaAfYZ9e4B3dXp4b251bWt0ZWhzZGdvXltuXVppXVFoXFBvXWF+a2+HdHqJd317d3h4c3Ry am1yam15cnd7dHl4dXdzcHJ0bnd6dH16d4J5dYB1eHl3eXqCdXuNgIeIg4eAe395bXN3anB3bnh/ d4CCgIuCgIt9c3RtY2RpYmJza2t9eoiAfoyDdXV7bm5yampyampvam5uaW1zbnJ0b3ODe4uGfo2G gJGDfo5/fYuDgI6HhpCIh5GMhJWMhJWDgpZ9e5B5c356dH9+e42Cf5GDfpGIg5aGgot/e4R5dYB5 dYB+e4l/fYuGfo2Gfo2JgI1+dYKAdIeIe46LgoyGfYd9dH56cnuAbneGc3uDgoyIh5GOho2DeoJ+ b3R5am91bnB4cHOAcnmAcnl/dHN/dHN/cnJ7bm57c32Ee4aGf5aMhp2OhIuIfoR+bW53ZWduZGVz aWp/cnJ7bm59aWd5ZWN0YlxyX1pwYVpwYVp1ZGF7amd/b2iAcGmDbnOEb3R/d36GfYSGf4aIgoiG e4KDeX+Gc3mEcniAd3qHfYCIf4eMg4uQgIiHeH+Jc3iLdHmCeYaDeoeLd4KEcHuAa26CbW9+a3SA bneAc3ODdXWHc3CJdXOLdXWGcHB+amh4ZGJ3Y1x3Y1x9Z26Gb3eHdYiOfZCSfYSNeH+EbmJ/aV14 a2+CdXmJe4SQgouRdXWMcHCEal+CaF2AbWqEcG6JcHKIb3CIaWOIaWOQbm2Qbm2OcnKRdHSSd3OS d3OJdW+Hc21+ZVF7Y097XFGCYleDaVuDaVuEaWKAZV6CaGSHbWmLbV+Nb2KMa1+Ma1+Ia1uJbVyM aFeMaFeMaFWMaFWOamWUb2qUc2qWdW2AdHiDd3qAd32Ad32Hd4OGdYKIdH+JdYCIeIKHd4CEe4OC eYCAeXt+d3l9c3R7cnN+eIB/eYJ5cnJuZ2dtZGFvZ2N3bXB6cHR/eX+Ce4KHen6GeX2CdXl/c3d/ dXuDeX+Gf4iHgImEe4aEe4aAdYB/dH9+eX17d3qAdX6AdX6CeHt+dHh6bnJ6bnJ4a211aWpvZ2Nt ZGFwZFhzZ1tlZF5vbmh9c3l/dXt+dHh9c3d4bnJ3bXB6b3iAdX6AeX5+d3t3cHl4cnp0d4N0d4N7 en9+fYKAeoCGf4aHg4x+eoN9b3p5a3d3bXCDeX2GhIyEg4t9dXVyampoaW11d3qEg41/foh9dXV4 cHB/c3l/c3l5bXB6bnJ1c3d0cnV+foCDg4aDgJKEgpSCf46Cf46HhJSJh5aJgpGLg5KDfYh/eYR6 b3p6b3p7c4l/d42Dd4iIe42GfYeCeYN7c313bnh1dH53dX+AdYOEeYd7eIN4dH93coR/eo2He4R/ dH14bnR5b3V+cniEeH6Ng5WQhpeQiZKAeoOEd4J9b3p5bnd9cnp/eHp+d3l+eXqAe32Lfn+GeXqA dYN/dIKId42Ne5KMgI6IfYuEb3R6ZWp5Z2p7aW1+cnN+cnN+c3J5bm13aWd0Z2R1ZGF1ZGF7amuD cnOAcnR/cHODdHmEdXqDfYiHgIyJgIiIf4eEen5/dXmAbnKAbnJ/cnKDdXWIf4eIf4eQfYOEcnh/ cHiAcnmEeYeJfoyQe4eHc36GcnSCbnB/aXB/aXCCa3OLdHuMd36LdX2JeHmGdHWDb2l7aGJ5Ylp4 YVh6YmODamuGb36OeIeNeHqJdHeAbWN/a2J+b3SDdHmOeISSe4iQdHeJbnCAZ1+AZ1+HbWiMcm2J b2uJb2uMcGmLb2iLcnCJcG+NdHWReHmSeX2Qd3qIdG6GcmuDZ1aAZFSAY1aEZ1qGa16Eal2CaVyC aVyGamOJbmeNbWSObmWLbWGLbWGLal+JaV6OaFuOaFuIaVeLa1qScG6Vc3CZdG+adXCAcneCc3iD dYODdYOLdIOJc4KGd36DdHuDd32GeX+EeYSCd4KEd3+DdX6AeHeCeXh9eX9/e4J/dH15bnd4cHN5 cnR6b3p7cHt9eHmCfX6De4CDe4CAd3qCeHt/en6Ef4OHfoaIf4eEeoCDeX+DdX6Ed39+dX99dH5/ c3d+cnV+dHV9c3R/dHN/dHODcnB/bm17bXJ7bXJ/a2t+amp1aml5bm14cHV5cnd7cnh3bXN5a2l6 bWp/c3mGeX+Deod/d4N3cnN3cnN1c3d7eX1+eoB9eX95eH1+fYJ/eId+d4Z5cH10a3h1cHSDfoKD hI2HiJF5eoNvcHl3bniEe4aLhI2Ce4R9eYKAfYaGgouAfYaDe4CCen+De36AeXuDgI6AfoyDf5WA fZKGe42IfpCCgpCCgpCEfY1/eIh7cHl6b3h3aGpzZGdya3J1b3V6b3qCd4KHeYJ/cnp+a297aW11 anN5bnd9cHeAdHp3dHh4dXl9eX+CfoSGfn55cnJ4amp6bW19dHuEe4OIg5SLhpaUhIyLe4OIe4KH eoCEdXqGd3uIeXuJen2CfX6GgIKLgomJgIiNeoOIdX6HcnuLdX+Mg4uIf4eHen55bXB5am2AcnSH eHqEdXh/dHB9cm56Z2l6Z2l9aWN9aWN+cnWEeHt+dHh7cnV/cnqDdX6EfomIgo2Lf4iMgImGeXp+ cnOAa2t5ZGR6Z2eIdHSJfomLf4uNeH+Eb3eEb22Hcm+IeoaQgo2Qg4mLfoSLdXiHcnSAaGt+ZWl+ aXCEb3eMd4CNeIKNeHiIc3ODa2d7ZF91Xlh6Y119ZWGDa2eLdX2Md36Mc3SGbW6AaWR/aGN9aG2A a3CIc3qLdX2Mc3eGbXCCZ2l/ZGeDammIb26Jb2iJb2iMdG6Od3CJdHKLdXOJdXiLd3mLdXiJdHeL b2uEaWV+YVV/YlZ/ZV6IbmeJcHKEa22La2WJamSEaWKJbmeNbWSNbWSMbmSLbWONbWSMa2OOZ16N ZV2QblyRb12Ub22RbWqRaGWRaGWCc3qCc3qEdICHd4OEd4KCdH+EcnqDcHmAdHqAdHqEd3+Ed3+G eX+Ie4KDe36EfX+DfYOHgIeHeoCCdXt6b3p6b3p1bXRzanJ3bXN7cnh9dXqAeX6GeX+GeX+Ce4KE foSHe4SEeYKDeX9+dHp+cnV+cnV9cHd9cHd7b3OCdXmAd3iCeHmEdXqEdXqAdX6AdX6GeX2EeHuI dXmCb3N7amt9a217cG17cG17bm59b294aW57bXJ9dICDeoeEeYKAdX56dXl0b3Nyb3B4dXd/eH1+ d3t6c3N1bm55a3d4anV0bXJza3Bwb3R6eX56eYCAf4d5dXtzb3V3bnqCeYaIgJCCeol7d4aDfo2H hJSGg5KDfo2Dfo2Gf4iHgImDf4uAfYh7eYh4dYSAdYCAdYB9eoiAfox/d4B/d4B/cnp7bnd1Z2ty Y2h3am56bnJ4cnh7dXuGeX99cHd5bW59cHJ6bnJ7b3N4bXV6b3h5d3h7eXp/foOAf4SAe316dXd1 a216cHJ+dISIfo6LiZGIh46NgIeIe4KIfoSJf4aIe4KJfYOLeoSLeoSAfoKAfoKHfoiIf4mIeIKE dH6AcneHeH2JgoeLg4iHf395cnJ7b3B+cnOMeX2MeX2Je3uCdHR7amt6aWp/amiAa2mCdXuIe4J+ d3l9dXh7bnmAc36EeouHfY2Lf4iMgImId3WAb256aGF6aGF5a2mHeXeHfoaJgIiMeX+EcniAbW+E cHOGdYKLeoeOf4SLe4CId3WGdHN6a253aGp4aW57bXKHcnmLdX2MdG6IcGqGbWuCaWh9Y16CaGN+ a2WEcmuJdHuJdHuLcnWGbXCAZ2KAZ2KCZW2HanKLcnWMc3eIbW+DaGp+ZF2CaGGGbWuIb26Nc2+O dHCNdHONdHOJdHKLdXOIc3CNeHWUdHKOb22GZ2GDZF5/ZVuDaV6Ha2iSd3OOeH2Jc3iIbl+EalyD ZVqLbWGObmKMa1+ObWiObWiLal+HZ1yGZV2GZV2Nal+Oa2GUa2OQaF+QZVqQZVqAcHqCcnuDdYCE d4J9dH57c31+cH5+cH57cnh6cHd+dHp+dHqGdX+IeIKDeoKDeoKCe4SGf4iEe4N+dX15bnd1anNy aGtwZ2p0aWh9cnB/dXeEenuHeYSGeIODeIaDeIaCd4SCd4SAd3h+dHV7dXN4cm9+cHB/cnJ6cnuC eYODfYOEfoSIeoaIeoaHfoiIf4mIgouHgImIfYiAdYCCc3V/cHN+d3d9dXV7b3N7b3N1aW99cHd/ dYaDeYmDeoSDeoR/eX95c3l4c3R1cHJ5dHV5dHV3am5zZ2pwZGVrX2FtY2luZGpwZW55bnd4cnh7 dXt4bXV0aXJ1aXp7b4B/eYSAeoaAeIKGfYeGg5GHhJKEgIyDf4uIfo6LgJF/e4d9eYR6dHp3cHd6 cnt6cnt4dYSAfo1/dYZ9c4OAc4B/cn93b3R0bXJ6a3B4aW5zbXN5c3l+cnV6bnJ1bXR4b3d9cnp9 cnp5cHh5cHh1cnh5dXuCgIaEg4iDe356c3V5cnd+d3uCeomEfYyEf46Ig5KJhIiHgoaLhI2IgouL eoeLeoeIgIaLg4iGf4iDfYaGeIaEd4R/dH9/dH9/d4OCeYaJg4mMhoyJgoKAeXl9c3eAd3qQgIaS g4iOgoaDd3p9b210Z2R6aG6AbnSJeoKJeoJ/c3R9cHJ4a297b3ODeICHe4SQgo2LfYiGd3l+b3J4 Z2V7aml+cnWEeHuIfYuJfoyOeYOGcHp+b3KDdHeIdH+JdYCOe4SJd3+HdXeHdXd+cnh4a3J9aG2A a3CEbnqHcH2EcmuHdG6GcnKEcHCEa22Hbm+DbnWEb3eJdHmIc3iIc3OGcHCAbWN/a2KDaGiIbW2N bmuQcG6Ha2d/ZF96YVODaVuDb2WHc2mQdXuQdXuRdHeMb3KLcnWOdXmOdXmReHuSd3KMcGuEZFyA YViDY2SLamuLb2+WenqWeX6Qc3iLal6EZFiCYVyGZF+MZWSSa2qScGuScGuIal2CZFd/YlaAY1eL a2WLa2WSbWOUbmSUaF+RZV16bXp9b311bXd3bnh3b3R3b3R6b3p4bXh7cG95bm14b256cnCAcnmG d36Cd4KCd4J9eYR/e4eAeX59dXp6a25wYmRyZGR1aGh1bnOCen+HeYKJe4SHe4eGeoaGeIOHeYSC eIiGe4yHe4SDeICAeX59dXqAd32Ad31/d4OCeYaAe4uGgJCIgouIgouMg4uJgIiIg5KHgpGNg5WE eox/e4eEgIyLhomHgoZ7e3tycnJyZWd0aGl4b3l6cnuCeomAeYiAeoB+eH59eHd4c3J1c3R0cnNw YmRvYWNoYWNlXmFlXF1oXl9vXWF5Z2p3bW54bm90aGt1aW1wYmlzZGt3bXB9c3eAeIKEe4aIhpSE gpCCfYCEf4OLgJGMgpJ/fYt5d4R7c314b3l5b396cIB3c4h9eY56d4J1cn19cnqAdX57eIN1cn13 bXBtY2duZGVzaWp1aWp5bW50anB3bXN7eYd7eYd6cn51bXlycHp4d4CEe4aJgIuEfX+AeXuDfYOG f4aCfYx+eYiCfoeIhI2If4mMg42LhIuGf4aEeoCGe4KGgISIg4eLhI2Gf4iHfYB9c3d4cnp7dX5/ eomCfYyGg5GLiJaRiY6EfYKAdYOHe4mQgpCShJKViI6NgId/cm11aGN9aG+DbnWMe4aMe4aAeXt3 b3J7amt+bW6DdHuNfoaOf5GJeoyHeHp+b3J+amqEcHCCc3WEdXiJe4SHeYKGcHiDbnV9bnN/cHWG b3eIcnmJdHmDbnN/cHWAcnd9bnV9bnV/cHWEdXqIdXuHdHqGeHWHeXeIdHSHc3OHbm+GbW6AcneD dHmIdHeJdXiLdXiGcHOEb2+HcnKHcm+Ic3CMcHCJbm6Ibmp+ZGF7Yl6CaGSCb3OMeX2MdXqLdHmL b2uIbWmJa3OOcHiUdX2WeH+UeHiOc3OHbWKEal+EaGiNcHCReHuWfYCWd3OOb2uLaVqIZ1eCX1WG Y1iHZ2iQb3CUeHOUeHOLcmOCaVuCY12IaWONdHWWfX6deHeXc3KUb2GRbV54aHJ4aHJyaGtyaGt1 aWp6bm9+cnh7b3V7bW97bW95b3B6cHJ6cHd7cnh+bnqCcn5/eX+AeoCEd3+Ac3t5b3B3bW55bXN9 cHd+eoOEgImHf4SEfYKGfoOHf4SHe4SEeYKEeYeGeoiIgouGf4iGeoOHe4SEe4ODeoJ/d36DeoKH e4mJfoyJf5CMgpKMho6IgouLh52Df5WHfoiAeIJ6eIaAfoyMho6Mho6Cf4N4dXlyZGRwY2N0aGt3 am5/c3d/c3eAeISCeYaDeX2DeX19dXh7dHd3amt1aWpvY2dpXWFpV1FqWFNjYlxubWd5cnR6c3V4 ZWt1Y2ltX19tX19wZGh9cHSCeYOEe4aDf4uAfYiGeoaIfYiHfY6DeYt4c4JybXt3cHd1b3V3cnV4 c3d6cIB+dIR3c3t3c3uCeYCGfYSHfoiDeoSAd3p1a29zYVtwXlhvZGF0aWV3am59cHR+eH6DfYOD eIN5bnl0b354c4KDeYmLgJGHfoiGfYeJg4yLhI2Ee4Z+dX+HgImJg4yJgIuMg42LhIuHgIeCf4OE goaEgIeEgIeEgpCGg5GMgoaGe397c3p7c3p5d4R7eYeDe46JgpWQg5WJfY6IeYuRgpSQh5SOhpKU iZCLgIeCdWt6bmR9aG+Eb3eLeoeMe4iGe397cnWAbnJ9am5/c3mIe4KNfYmJeYaNeXmIdHSDb3KD b3KDcHSEcnWJeYOIeIKIdXl/bXB+aWt+aWuCbXSDbnWEc3R+bW5/bXWCb3iAbnSGc3mGeX+JfYOJ d3qJd3qNe32LeXqJdXiHc3WIbXKIbXKCc3WGd3mJdXWJdXWNdHWHbm+IbXKLb3SHc3WGcnSIa2uH amqDaml+ZWR7Y2J+ZWR+a3SIdX6ReHuLcnWEaW6Ha3CHanSMb3mRd32UeX+Re3uJdHSJb2iHbWWG aWuJbW+OdHqQdXuRcm+La2mHZFyDYVh9XVGDY1eIaGmVdHWWeXuUd3mScGGLaVqIZGKOamiQd3ia gIKheXWZcm6RbVyQa1twZ2pvZWl0Z2J0Z2J6aGt+a2+Acnd9bnN9bnB9bnB5b3N5b3N7cnV9c3d+ a3KAbnSDe36De36De4t/eId7eIN7eIN6dYZ+eYmHfouJgI2Jg4mLhIuLgomJgIiEgId+eoCEfoSG f4aIgouLhI2LgoyIf4mHf4KGfoCHen6JfYCGgISEf4OIfo6Ifo6Dfo2Ef46GgJR+eYyAdX59cnp5 dX6AfYaNg5SNg5SGgo16d4J5ZWVwXV1vZ2N0a2h6c3N7dHR/dH+Cd4KEfoSIgoiCgoSDg4aAd3h4 bm91aWpvY2RtWlRqV1FoY2R7d3h/dXt6cHdyY2hqXGFnW1FoXFNrX2N9cHSAdYOEeYeAf4d6eYB7 d4Z/eomCeIl9c4R0a3NyaXB4cHV4cHV5d3h6eHl7c31/d4B6cn5/d4N/foiCgIuIh46Hho2EgoZ6 eHt7amd3ZWJwY2F3aWd+cHmGeICAfYaHg4yGgIR7d3p4cn14cn2AeYiMhJSHhJKDgI6LhpWJhJSH eYJ+cHl+eoaCfomNh42Nh42Lh42IhIuHgIeHgIeIfYaGeoOGf4iIgouQgo2OgIyDd319cHd7cHt/ dH+CdYiHeo2HfY2Ge4yMeo2UgpWNiZWMiJSUjJGNhouHe3V6b2l/Z2iEa22IeoOOgImNfoOGd3t6 a254aWt6c3WAeXuJen2MfX+SfoCQe36JdHmIc3iDdHeEdXiHeYeIeoiLeXh+bWt/ZGd+Y2WAbnSE cniGdHWDcnOCbXKDbnOCb3WHdHqHeYKIeoOIe32Ie32Jen2MfX+Me3OEdGuHbm+LcnOLdXqMd3uI d3OGdHCHbnKGbXCGcHCDbm6DbmuCbWqEa2qEa2qGaWmCZWWAZGeCZWiDbXSGb3eLcnCHbm2GZWmE ZGiEanCHbXONd36QeYCHeHqCc3WHa2iIbWmHbWmGa2iLb3SOc3iMbWeDZF5/W1R9WFF+W1iGYl+E Z26UdX2Xd32Uc3mVaWSJXlp9Yl6IbWmQdHeXe36adXCSbmmOZ12OZ11uX2dvYWh3Y2V5ZWh5Z29/ bXWAdHiAdHiAc3OAc3N9dHN9dHN9dXh7dHd6cHR7cnV/d36CeYCAd4d/dYZ+d4Z/eIeEeoyHfY6J foeLf4iIf4mLgoyEg42Eg42HfoiCeYOCe4eCe4eDfpGIg5aNhpaJgpKHhIiDgISDeICEeYKEeYKD eICDfYODfYODfYOCe4J/d359dHuDdHmCc3h7eH6Df4aLgoyIf4mAf4l9e4Z+cGt6bWh7cG99cnB+ dHp+dHp/eX+Ce4KAe4uGgJCIiZKMjZaHhIiCf4N7cnNzaWpuX2JwYmR5cH1/d4ODd316bnRqY1pk XVRqXlZuYlplXF1zaWp5bnl+c35/dH1+c3t6dHp+eH6AdYN3a3lvbW5tamt0b3N7d3p9eId7d4Z5 dYB5dYB9c4N/dYZ9eoyAfpCHhJaIhpeJhpGCfol+c3J6b257b3B6bm+AeYiEfYyGfo2IgJCJgIiD eoKAcneCc3iAe4yIg5SOh5eJgpKRh5mQhpeGdYJ6and6cnl7c3qGgo2MiJSGgouCfoeEfYyHf46J foeCd3+GeX2JfYCHhouGhIl+fYJzcnd5bXN/c3l+c36DeIN/eYSCe4eAeYiGfo2If4mQh5GSjJKM hoyMf4B+cnN+amSCbmiEeHuOgoaOgoaDd3p6bW14amp4bnKDeX2LgIKRh4iNg4mQhoyOgoaIe3+G d3uCc3h/cn+GeIaNeXuEcHOCaGSIbmqMd4CVf4mUfoaRe4OIdHeDb3J+cnOCdXeIeYCQgIiUgISN en6Qe3SQe3SNfXSJeXCJdHSMd3eJd3+Jd3+Gcm+Db22EaWmDaGiIaWOHaGKDaV6Eal+EaWSHa2eH amqDZ2d/Z2qAaGuCaW2GbXCJb2uCaGSAYl6CY1+CZWiGaWuIcneLdHmJcG+Hbm2Eal+Eal+Ham+E aG2Hbm+JcHKHblyAaFZ/XE57WEp6WliGZGOJbXeVeIKaeHWVc3COZ2GMZF6DZGKMbWqSb3SRbnOQ al+MZ1yMZ1+NaGFtXF1wX2FyXl53Y2N4ZWl+a29+b3R+b3R+cnN/c3R/dXl/dXmGeICDdX57d3h5 dHWCdH2CdH1+c3t/dH1/dH+AdYCDeICIfYaJe4SOgImLf4uOg46IgouJg4yHe4mCd4SAdYCCd4KE eoyMgpSMhpGJg46LgomEe4OAeoB/eX+HeH+EdX2Ed3+DdX5+dHh/dXl/eHp/eHqHeH+Gd36CfYCD foKLfYaLfYaCf4OCf4OEenuCeHl6eHt1c3d+c3uAdX59en5+e3+EfY2MhJWSkqGQkJ6Mi5WLiZSC f4Bwbm9uZGp0anB+eIN7dYB9eHtzbnJpZV9oZF5yZF9wY15vY1twZFx6bnJ+cnV/c3d+cnV/dH2A dX6Dd313anBpZWtpZWtwaW56c3h7eIB7eIB6c3h6c3h6c3V7dHd7dYCAeoaHfY6Jf5GJg46Igo2A eXt9dXh/c3mAdHqEfYyGfo2JfY6JfY6NhIyJgIiAd3p/dXmCfY2JhJWRh5uOhJmNhpmOh5qHeoB4 a3J1bWt+dXSDfo2LhpWMgImIfYaDe4uGfo2IeoOHeYKCd3+EeYKGhImGhIl/foZ1dHt0anB4bnR4 bXV9cnp9cnp7cHl7cHmAdX6GeIaLfYuNh5COiJGOgoiDd31+bW5/bm9/eHqJgoSOe4SIdX6CcG97 aml5am2DdHeMhImUjJGUjZaSjJWSgoyJeYODdHuAcnmEcHuIdH+MeX2Gc3eAa26HcnSNe46SgJSU hIyQgIiMd3mDbnB+b3J/cHOCdXuOgoiSe4COeH2OfXuQfn2Qen2Md3mNd3uOeH2Od4aMdIOLcGuE amWGZ2OGZ2OIamGJa2KEa16GbV+IaWOJamSHaGSHaGSAaGt+ZWmDaWWGa2iMbmKHaV2JY2KMZWSI aGuIaGuNcm6Ncm6Lb2iGamOCZ1+AZV5/ZGmDaG2AbWOEcGeHbV+CaFt/XVN5V011VFaDYWOLbnOS dXqXeW+SdGqJaGOLaWSLa2mQcG6ObmWMa2OIaVeHaFaIZVqJZ1twXVt0YV5zYmN4Z2h4bWt4bWt+ a297aW14bnR6cHd+d3uAeX6IeISHd4N+dX97c31+cnV7b3N4bnJ5b3N9cHR7b3N+dHiDeX2Me4aM e4aNf4uOgIyJgpGLg5KEfYyAeYiCd4SAdYOGeYyLfpGMg42If4mNfYeDc319dHt9dHuCdH2CdH1/ dH19cnp/c3eEeHuEe4iIf4yMf4aMf4aHfYCIfoKJfYCMf4OIf4eJgIiOf4eLe4OCfYB6dXl+d3uA eX6AeoOCe4SHeo2NgJSOjJuSkJ+QkJ6Ojp2JiJJ1dH50anB6cHd7dXt4cnh1bnB0bW9taW9ybnR+ dGp6cGd1a2J0amGAcnSAcnSDeX2DeX2CeYB/d36CdXl6bnJoZGppZWtvZ254b3dzcHJ4dXd5dHV3 cnN4bnJ6cHR4dXl6eHuAeX6De4CGf4aIgoiAfoJ9en6CeH6EeoCHgImIgouJfoyLf42Qh5SMg5CA e4t/eomAeoaIgo2MhJSMhJSMiJSMiJSHeYR9b3p6bm+CdXeGfYmLgo6LgIeDeX+AeoOEfoeDeIOC d4J/dH+AdYCDeYuDeYuAeH94b3d1a290am53am53am55bW53amt4Z2N4Z2N3anB/c3mGfYmMg5CL g4iJgod9cHJ5bW59c3eHfYCLfoSHeoCDb297aGh3aG2Cc3iNhIyVjJSXjpmSiZSRgomGd35/cm16 bWh+a3KHdHqIeIKGdX+Cc3WGd3mJfoyRhpSWiJGRg4yOeXuEb3KDbXKCa3B/c3eJfYCSe4OReoKO f4KNfoCUeYKRd3+Rd32OdHqMdYKMdYKJcm2Da2eGamOIbWWLcGuLcGuLc22JcmuJbmqLb2uMaWuL aGqGZ2SEZWOEZV+GZ2GOa1+Oa1+NamKOa2OObWiMamWIbWiJbmmGa2eDaWSDZF6AYlx6YVx+ZF9+ ZF+Ga2eIal6GaFx+X1B5W0x1U06GYl2NbW6Uc3SSc22MbWeHZWGIZ2KObWqRb22Mb1yNcF2HaVyG aFuGZ1WHaFZ/amqCbW17bXJ9bnN5bW55bW56bW14ampzaW13bXB6cHR5b3OEcH6Hc4CDdIaEdYd7 b3V3anB1ZGN3ZWR5bXB+cnV/dIKDeIaHeYKHeYKLfoSMf4aGgJGEf5CCfYx+eYh/eHp7dHeAdYCI fYiIg4eEf4ODd3p9cHR9bXmAcH19d4J/eYSAeoOAeoODeoeMg5CLhpaMh5eOhpKMg5CJgIiIf4eL f4uMgIyQhJCNgo2Ug42OfoiJfYCCdXmAdHiCdXmCfYCIg4eHfoaLgomHho2Mi5KNkJ6OkZ+RkJd/ foZ+d3l9dXh1c3Jyb25wa210b3B4bXV+c3uAd3h/dXd7d3qAe3+IfYuGeoiMgImLf4iGf4aAeoCA e394c3d3dHh0cnV1b3V9d317c3p6cnl6dXR5dHN4bnJ4bnJ7dHl+d3uAeoaDfYiHfoaGfYSCe4KD fYODeX+HfYOJgI2If4yHe4mMgI6LiJeLiJeIgJF+d4d+dIR/dYaGeoaIfYiMhpGMhpGHfYB9c3d5 b3B+dHV/e4KEgIeHf4R+d3t/dH2DeICAeIJ9dH6AcHp+bnh6cIB5b393b291bm50aGl0aGl6ZWp6 ZWp6a3B6a3B5a2l3aWdvYWV3aG1/c3mLfoSJf4OJf4N+cnh5bXN9b3iDdX6GeIOEd4J/bm95aGl7 Z26Ic3qLfpGUh5qSjJeNh5KQgIOHeHp/bm95aGl3bXN7cniIeIKHd4CEen6HfYCJfoeSh5CViIyS homRe3uIc3OCZ2eAZWWCaWqJcHKNeIKUfoiWgIuWgIuUgISQfYCRd32QdXuQdYCOdH+Hbm2CaWiG amONcmqQd3WQd3WQdXuRd32OeXuNeHqUcm+Na2mJY2KIYmGGaW6LbnOWdW2Xd26VdW+RcmuRcGiQ b2eMa2ONbWSLaWSIZ2KEZFp/X1V5WlF6W1N/XFeHY16IYl6LZGGAYVZ+XlSAXFGIY1iMbWeOb2mM bmSIamGGZGKDYl+MamiScG6Ucm2Ucm2Rd2uOdGmNbVaIaFGCc3WCc3WAdHp/c3l7b3V9cHd9bnN5 am95Z214ZWt5aGl7amuCa32Jc4SEeYSAdYB5bXB1aW13aG19bnN/cnqDdX6DeICDeICGeX2Ie3+M fYKLe4CGfo2IgJCGfo6De4x/en57d3qGeIaMfoyNgo2GeoZ/eHp6c3V+c35+c36DfYaGf4iJgIuM g42JgpGNhpWJgpKLg5SMg42Mg42HgIeIgoiJgIuNhI6Ng5SNg5SOhpCNhI6Jgod/eH1/dXmAd3qH fYONg4mHgIeHgIeHho2NjJSSkJ+VkqKSjpeIhI2Je4eGeIN+eXp4c3R0b3B3cnOAe3+Ef4OAfYZ/ e4SDfYaLhI2Lg5KHf46If4mGfYeCe4SCe4SEgIeEgIeAfYiDf4uGeICIeoOAeoaDfYiEeH6Dd317 d3p4c3d7dXt+eH59dH6AeIKDfYaCe4SEfYKHf4R/e4J/e4KJgIiLgomEeYKLf4iHh5WHh5WHe4mD eIZ/dH1+c3t+eIOGf4uGhIyDgomIfoJ/dXl6cHJ/dXeDeoSEe4aCeol9dYSGd36DdHuAeoN+eICD d31+cnh5bXB1aW11aGh3aWl3ZWR4Z2V9aG2Aa3CDc3+EdICCc3iAcnd0aGtyZWl6bXWGeICAfoKA foKGb3R7ZWp1aW16bnKCcnuAcHqCbXR7Z25+a3SHdH2Jf5SNg5eOhpCLgoyQfn+Id3iAa2t6ZWV0 ZWp6a3CCdH+Ed4KEeHuGeX2LeH6VgoiZg4uVf4eUenuJcHJ+a155Z1qAZ2KHbWiLdHmSe4CXgIuX gIuXg4aSfoCOeH+Nd36JdHmHcneHamqDZ2eIbmmMcm2UeH2Xe4CWen+UeH2Uen6Qd3qOb2mHaGKJ ZWGIZF+HamqNcHCVeXuXe36VenWQdXCUdWmOcGSNaGGMZ1+JZF2JZF2CZFd9X1N5Wk96W1B6WlV9 XFeEY16IZ2KEZFyAYViEZFyHZ16JbmmLb2qIamGGaF6EY16DYl2MammScG+aeHeaeHeheXWbdHCU b16MaFd5dHV3cnN6c3V9dXh6dXl7d3p/c3l5bXN5ZWh6Z2l4aW5/cHV/a3mEcH6CdXt/c3l1Z2lz ZGd1ZXJ+bnqAdX6DeICHgoaHgoaJgIiJgIiMf4OMf4OEfoeEfoeIf4yHfouEe4OGfYSEfY2JgpKO gIyIeoaHeH2Gd3uGd3uHeH2DgI6EgpCIhJCIhJCLhIuJg4mIf4mLgoyJfomMgIyIg4eIg4eIgoiL hIuLhI2OiJGMi5WMi5WHhouAf4SIfoKIfoKGgouIhI2EgImHg4yGhI6LiZSUjqKWkaWWkpuNiZKQ h5SOhpKGhIl9e4B5dXt6d32HfouNhJGMg42Mg42Ih5GHhpCIgouDfYaAeoN+eICCd4SJfoyNg5eQ hpqNhpmIgJSMho6Mho6Ifo6EeouDeIOEeYSDe4CCen+Ae39+eX1+dHh/dXmAeoOCe4SJf4OLgISI g4eDfoKHf4SGfoN/d4CCeYN/eouGgJGEfoSCe4KDd32HeoCAfYaEgImEg4iAf4SIe3+EeHuDe3uI gICLgoyGfYeHeIuHeIt+dX2GfYSEf46Ef46GgIR9eHt7aml1ZGN1YmJ5ZWVuZWRuZWRyaGt7cnWE eImMf5GMfoeGeIBzamlpYV90aXR7cHuAeoOCe4R+b3J3aGp3Y2V4ZGd3bXB3bXCAbnR3ZGp4ZG+E cHuLe46NfpGRfoSNeoCIenqDdXWDb21/a2l6a256a25+cniCdXuDdHeEdXiLdX+OeYOSfYSRe4OS eXiLcnCAb2J+bV99bWWGdW6ReHuWfYCUgIeWg4mVgoaSf4OQfYOOe4KNdHiJcHSJamSGZ2GIb3CL cnORen+Se4CXen+Ze4CSeXqNdHWEaWV7YV2DYl2IZ2KLbm6SdXWSe4CSe4CaeHWScG6UcGiOa2OO Z2GJYlyEYVyIZF+HZFyDYVh5XU16Xk56WlV7W1aCY1+GZ2OHZWOIZ2SGZ2SLa2mLa2mOb22LbWGD ZVqDX1uEYVyMaGORbWiXdXSbeXiZd3SUcm+Sa16JY1Z6b256b259cm5+c293dHV5d3iAdHh9cHR7 aW1+a2+Dd3qHen6Jd3qLeHuHcnt+aXNzZGl1Z2t6bnKAdHiDfoKHgoaGg5GHhJKLg5KLg5KMhImN houLg4iJgoeLf4iMgImMgI6Og5GHh5WIiJaQiZKLhI2Sf4aMeX+Dd3qGeX2EgIeIhIuJiJKHhpCJ hIaOiYuOg4yOg4yOg46Og46Mg4uIf4eMfoeOgImRiJKUi5WVkJ+Ujp6Oi5SJho6LgomMg4uLhpWE f46GhI6CgIuCgpCHh5WSjqWWkqiWkaKUjp+VjZ6Si5uOjZWLiZGHfoiEe4aNgJSQg5aMhJSHf46G go2Hg46GfoOAeX59b22DdXODfYiNh5KLgpuLgpuEfYyDe4uJgpKNhpaLg5KEfYx/d4CCeYOGeoiG eoiAeoB9d31/dH2Cd39/e4eGgo2Mg42NhI6Lf4iIfYaDe4CCen+CeH6Ad319c4R+dIZ+d4aAeYh+ eoOEgImIgo2HgIyEgId/e4KGe3+IfoKHg4mJhoyOhJaMgpSNgpCHe4mGgoiHg4mJh5WIhpSJhIiC fYB9aWt1YmR5ZWiAbW9/bm+Ab3B0am54bnKGd4iVhpeOho2LgomGdW59bWV5Z21/bXN/d4B+dX9/ am17Z2l6ZWN7Z2R5a2t9b2+Ab3B6aWp9Y26Ga3eMeYKUgImRfoSLeH6Ed3eDdXWIdHeEcHOAbW97 aGp/aXB/aXB/am2Eb3KHcHqHcHqMdX2QeYCOd3KLc26Db22IdHKLeH6MeX+Re4CVf4SOg4KQhIOS fYKSfYKQfYOOe4KOdXSMc3KOb2uMbWmIc3ONeHiZfX+ZfX+VeXuUeHqReHeSeXiEamV6YVx9XliE ZV+Gb3SNd3uQen2Qen2ScmeQb2SUa2OSamKOaV+LZVyGYl2HY16HY16EYVx7XUx1V0Z3VU16WFB+ X1qCY12Lam6NbXCQdG+JbmmLa2WQcGqHbWKDaV5/XVODYVaGaFuLbV+RdHSUd3eWc2iQbWKLZFWJ Y1R5am97bXJ9cHJ/c3R+dHh+dHiAcneCc3h/c3mDd32Gf4iGf4iMeYKLeIB/dHN1aml4aWt5am16 cHSCeHuIfYiMgIyLg5KJgpGLg5SNhpaOiJGMho6NhouMhImJgIiMg4uOhpCOhpCJiZaLi5eUjZSQ iZCMhImGfoOCeH6HfYOJgoeNhouLhIuLhIuOh4yOh4yMg5COhpKLh5KLh5KMg5CMg5CNhJGOhpKR iZ2UjJ+VkqGSkJ6SjJeMhpGSiZaUi5eQh46Mg4uJhIiCfYCGhI6NjJaUkqeWlaqVkKGNiJmLhpWN iJeQjZuOjJqRg5GJe4mLgo6OhpKLg5SGfo6DfYiDfYiIeIKAcHp9anB/bXODe4uMhJSEgpB+e4l7 dHR/eHiCe4eHgIyEfoeAeoN5c3l/eX9/d359dHt7c319dH57c39/d4OCeomGfo2GgJGLhpaQh5SM g5CJhIaGgIKCdH+DdYB9d317dXt4c4J6dYR+fYeCgIuMg4uHfoaDe4CAeX6EeYKLf4iMiJSQjJeS iJmQhpaJho6Hg4yMho6Nh5CQh5GRiJKQg4eIe395bm13a2p9cHSEeHuDc3+Dc394bWt4bWuAbXqS foyUiJGRho6NfoCCc3V/am+CbXJ/cHV/cHV/amp6ZWV4ZGd9aWuAb3CCcHJ/bXB5Z2p4Y2iAa3CJ d32NeoCOf4SHeH2AcneAcneHdHqCb3WDbmt+aWd9aGV6ZWN7Z2d+aWmDbnCIc3WMdYKNd4OMc3KG bWuCbXKGcHWMeYKRfoeRgoeRgoeXgoyWgIuUfoaOeYCOeX6NeH2OeX6Md3uLd3CHc22LdXqNeH2X e36ZfX+Zf36WfXuXfn2SeXiLbWOCZFt6X1t/ZF+Ca3OLdHuUd3eUd3eOcGeLbWONa2mMamiQbWSL aF+JZ1yEYleGY1iGY1iDYUx+XEd4U0R5VEV+XUyHZVSOa26VcnSZem2UdWiLa2WMbWeHa2SEaWKG Y1iEYleLaF2RbmOSdXqUd3uXb2eQaF+LYVCJX097aW9/bXOAcneCc3iCdH2Ac3t/dH2DeICEfYyH f46JiJKHhpCOf4eDdHt1bm5vaGh4a217b3B9c3eHfYCIgIOJgoSMg42LgoyLgJGMgpKOiJSNh5KM hoyJg4mJg4yLhI2Nh5CNh5CLh5CNiZKNjJSQjpaOjZeLiZSJhIaEf4CHfouJgI2Hf46Lg5KRiJWN hJGHgIyMhpGIh5GLiZSMgpSNg5WNhpaQiJmSi56UjJ+RkJqQjpmUiZqOhJWShpeXi52RiJCOho2I goiCe4KIgJCRiZmQiKeQiKeOg5GHe4mGgoiMiI6Rjp6QjZ2Qg5aIe46EeoyJf5GJfoeEeYJ/d4B/ d4CIeIeCcoB6bnJ5bXCDeYmGe4x+eX15dHh1b216dHJ/dXuAd31/eHp6c3V4b256cnB6dHJ5c3B7 c3J6cnB4b3d7c3qCeYCHfoaGgJGLhpaUiZuSiJqQiZCJg4mGeoaCd4J/eYR+eIN6cnt1bXd7cHmD eICMe4aIeIKDd3p/c3eDdYCMfomOh5aUjJuRi5SLhI2GhI6LiZSOhpKOhpKOg46ViZWXh5GNfYeC dXuAdHqAeoaEfomCfoR7eH55b2V3bWN+aW6LdXqMgI6QhJKJgoSHf4KHdXSGdHN+cml6bmV9aWd5 ZWN5ZGd/am2Eb3KHcnSCb3N+a297aGp/a26Cc3qJeoKUfoiLdX+CbmuCbmuEcnWDcHSAdGt9cGh9 aWl3Y2N3ZFt6aF6EbnOMdXqRfoeOe4SMfn6HeXmGcG6Hcm+Jd32Sf4aUgImRfoeWgpCVgI6WgIiS fYSOeXuLdXiMd3mOeXuNeXeNeXeQeYCReoKVe3qVe3qVf4KVf4KWgoSOen2ObmOGZVt/YVuDZF6C a3CMdXqRdXiRdXiMcmeHbWKJb2uJb2uMamWJaGOIZ1eHZVaJZ1yNal+LaFOCX0p+VER6UEB+WEyI YlWManWXdYCefXSZeG+Vbl6QaVqOamWQa2eMZ1+IY1yMaGeSbm2WeG6Vd22RbmWMaWGIY1iIY1iD bnWIc3qAeX5/eH1/dH9/dH9/eYKGf4iGgJCEf46Mg4uIf4eEeXiAdXR7b3B9cHKHenuLfn+LfYaL fYaGgo2JhpGLhI2LhI2MhJWJgpKOh5aOh5aNhI6NhI6LhJCLhJCGgo2EgIyIg5KNiJeOjJqQjZuS i5qRiZmSiZSLgoyJeYaIeISCeIiDeYmHe4SIfYaDgomIh46NhJGNhJGGhI6HhpCLhI2QiZKSiZaU i5eSjpqOi5aOhpCOhpCRh5eSiJmRiZmQiJeJhJSCfYyHeoyJfY6GfYeGfYeDe36Hf4KGh42QkZeW lKaUkaOMh5aDfo2DfYODfYOEd4KAc354dHp9eX+IeoaGeIOAe399eHt/dH+EeYR9eHd4c3J9cHR/ c3d/c3mAdHqAd3h7cnN5bmp7cG1/dHCAdXKCen2AeXt7b3N6bnJ/d36CeYCIfYuMgI6QhpeWjJ6W i5SMgImHfoiCeYOEe4OAeH9+dHp3bXN6a3B/cHWGd36IeYCEc3SEc3SCdXeGeXqMhpGRi5aOhpKJ gI2Jf5GIfpCLf4uQhJCQgouRg4yVgouOe4SIeIKLeoSLhIuMhoyNh42Gf4aEdGl9bWJ5a2l/cm+D eX+IfoSLgIeJf4aLfoKHen5/c2l3amFzYVtzYVt3ZGh+a2+Ic3iLdXqLd3mEcHOAa26DbnCEcnqL eICNeoCIdXuCcG9+bWuAbnKIdXmEd3SCdHJ9cm54bWl6aGGAbmeGcm+Ld3SRe4OVf4eVf4eQeoKM eHiNeXmQeoSRe4aUfYSSe4ONg4mNg4mUgISQfYCNdHOIb26McniUeX+Re4CRe4CQd3qNdHiQdHCO c2+JdHmSfYKagIKVe32NcF+GaViAaF2AaF2Eb3KLdXiMcmqLcGmHb2mHb2mNbW6Obm+Ob12Ob12N a1eMalaMa1+NbWGQaVeLZFODWkN7Uzx6VkOCXUmHZWSVc3Kee3eee3eXb2eUa2ORaGiOZWWJaV6I aF2LaWeQbmuUcm+ScG6Ra2KQamGNYlGOY1ODe4CIgIaGfYeEe4aDeICCd3+Je4eOgIyMgpSGe42C eYN+dX9+cnOCdXeGe32Jf4CShoyOgoiQgo2Qgo2LgJGLgJGJgpKIgJGIgo2MhpGLiJaLiJaRho6R ho6Lgo6Lgo6Jf5GJf5GMh5eQi5uUjJ+VjaGWjJ6Vi52WhpCRgIuNeYSEcHt7dHd7dHeCd3+JfoeM ho6Mho6NhI6NhI6GgoiGgoiLf4iNgouUi5eVjJmQjJWJho6JgIuIf4mIgJGNhpaMhp2LhJuIfo6G e4yCeYaGfYmCfoSIhIuNh5KRi5aJjJ2RlKWdlaiZkaWMhpGHgIyCfol/e4d9dHt9dHt7d3qDfoKH fY6IfpCLgomHfoaEgImEgImDg4Z6en2CeHuGe3+De36De35/fYB9en55cnJ+d3d/en6Ig4eGgouD f4iCd3N5bmp7dHl9dXqAeH+DeoKLf42NgpCXiZKShI2HfoaDeoKGfoOAeX56eHl0cnN+bW6DcnOI eX6HeH2GeHiEd3eEeG+LfnWJgIiOho2RgomJeoKEe4iEe4iNgIeOgoiOfoiSgoyOgoiIe4KGeX+I e4KJfomQhJCVh5CShI2MgHqLf3mId3WCcG+Ad3iHfX6Qg4eRhIiRhIuJfYOCdHJ6bWpzX1hyXld4 amqAc3OIeXuIeXuMeX+HdHp/bm2Ab26Cc3iHeH2Jd3qHdHiDcnOCcHKGcHOMd3mQfYONeoCId3h/ bm9+amh/a2mIenqNf3+SgoyVhI6UhIyRgomQen+OeX6MfX+MfX+Qen+NeH2IeX6Le4CLe36Gd3mH c2uAbWWGa3KQdXuNeoCQfYOOeniIdHKHbWWLcGmNdHiXfoKdf4KZe36IdWWHdGSAbmGAbmGIb3CL cnOMcmqJb2iGbmWGbmWSb2SVcmeSc2GRcl+QcGGRcmKQdWiUeWuUc2eObmKLaFOHZE9/XEl/XEmH Z16ObmWeeXifenmZdWqUcGWRbmWOa2OIbmOHbWKMammMammQa2mSbmuOZ12MZFuOY1OOY1OMgIyL f4uJe4eIeoaHeYSHeYSMfomMfomLeomGdYSCc3iDdHmDd3iJfX6Mg4uOho2Qh5GNhI6MgIyMgIyJ fY6JfY6LfpGGeYyAeoOJg4yHiJGIiZKRiJCNhIyHfoiIf4mEf46GgJCIg5aMh5qOjJ6OjJ6OiZ2N iJuSh5KQhJCMe4aCcnuCd3WHe3qLgoyMg42ShJCOgIyIfoSLgIeJgoeHf4SIfYaMgImNi5mNi5mO hIuIfoSDe4CEfYKJfoyOg5GJh5aJh5aIhJCEgIyHfouJgI2Ef46Mh5aNhpmOh5qMi5+QjqOOi6GM iJ6JfoyJfoyLf4iJfoeCeYCEe4OCfoeCfoeIe46OgpWNgo2RhpGLiZSHhpCGh4uEhomDf4aHg4mD f4aDf4Z/gISAgoZ7eHR/e3iGgJCLhpWQiJmLg5SCf351c3J5dHV7d3h7dHR6c3OAeoaEfomUhpGR g46EgIl9eYJ7eIN6d4J6d31zb3V5bmqAdXKJeoKJeoKEen6HfYCLfn+NgIKLg4iNhouQgIaMfYKC eH6DeX+Cen2EfX+IfoSLgIeHfYOCeH6DcHeGc3mIeIeOfo2OiJGQiZKJhIOMh4aQfYCJd3qJd3qL eHuQg4eViIyUiZCMgoiDeXp6cHJ6ZFh5Y1eCb3ONen6NfoaMfYSLeICCb3h/b2d/b2eEc2+HdXKJ dHmHcneJdHKJdHKMd3uLdXqQfYaOe4SQenqHcnKGam2Lb3KIe4KNgIeUg42Ug42Wg4ySf4iNen6O e3+OfXuQfn2MeX2HdHiLcHmOdH2Md3mGcHOAbmh/bWeDam6NdHiOeYCNeH+NeXOEcGqCZ1+Ha2SQ c3iWeX6Wen+VeX6Hc2mMeG6Nem2IdWiLdXWGcHCMcmqJb2iJaV6IaF2Nb2OQcmWObmWObmWRb2qR b2qSdGqXeW+WeGqWeGqZdGGQa1iJaFaEY1GIZVuJZ1yScG6Zd3SeenKZdW2RcmKJaluCaV6GbWKM Z2mLZWiSbWORa2KLZFWGX1CJXk6LX0+IeYyGd4mCcnuCcnuDb3qDb3qHd4OLeoeJfYOHeoCHfYCH fYCLgISNg4eJho6Jho6Rg4yOgImGeX+EeH6Ed4SEd4SDeIOAdYCAd32Jf4aJiJKNjJaRho6QhI2M f4aJfYOIeoaJe4eIf4yLgo6NhpWIgJCQgpCQgpCOhpKQh5SHg4x/e4R/foOHhouQiJeOh5aUhIyL e4OIeX6QgIaIg4eJhIiMg42Qh5GNiZWNiZWLgIeJf4aIg4eMh4uLh5KIhJCHhJSIhpWEg42CgIuG gJSIg5aMhpGOiJSSiZaQh5SNh5KNh5KQfpKQfpKLgoyMg42LhomOiY2LiZGIh46Df4iDf4iJg4yN h5CLhJCMhpGNiZWMiJSLhIuMhoyLgoyLgoyDf4iIhI2IhoeIhoeCgH2Dgn6Ng5SVi5uWkJuRi5aH h4d4eHh9b3iCdH1/c3d6bnKCdIKHeYeLgJGMgpKLfYt/cn96b3p6b3p7cnV5b3OCc3iGd3uLgIeI foSGe4KLgIeNhI6NhI6Mh4uNiIyNg4eJf4N/dXl7cnV/c3eDd3qEeXiJfn2GeHWAc3B+cnV/c3eD c32LeoSSiI6Vi5GUiJGUiJGOhIuHfYOEd3SEd3SShI2XiZKXiZKQgouMfX+AcnR5a2l9b22AdX6G eoONfYyLeomIdHeCbnCAcGWGdWqMeHWNeXeOen2JdXiHdXSHdXSOeH2OeH2QeoKOeYCOdXmHbnKH a2uLb2+Ie4KOgoiQg4SOgoOSfYSOeYCQfYCOe3+Lf36MgH+LdXqDbnOEanCDaW+EameGa2iAbWN+ amF7aGqDb3KNeXuRfX+SeW6NdGmHbmOGbWKObnSXd32Ve3+Zf4OVgICXg4OXfn2UenmReHuMc3eR cHKNbW6HaGKIaWOMa22Qb3CRb2qRb2qNbmqMbWmOcGeUdWuVdW+VdW+UdG6Wd3CUcm2ScGuMbmGH aVyRcm6UdHCQdHCMcG2Nal6JZ1uGaF6LbWOLaWeGZGKRbmWRbmWOZU6HXkeEXEeHXkmCcnuAcHp9 bXd+bniEb3eHcnmHeoCJfYOIfo6MgpKMg5CIf4yMfomNf4uLf4iNgouOgICHeXmEc3KGdHOAbneD cHl+dHiAd3qCdXmMf4OOiZmNiJeQh46Qh46Le4CHeH2EeHuEeHuDeoKEe4OGeoiHe4mIfYuLf42S h5KUiJSMiJSHg46JiJKMi5WQjJWMiJGOhIaJf4CJf4aNg4mOiJSOiJSQh5SRiJWSjJeRi5aLgJGL gJGLhpmNiJuOiZqMh5eCfomAfYh/eYKAeoOJfZCNgJSLg5SOh5eRh5eQhpaOho2LgomIfYuGeoiA f4mIh5GNh5KRi5aOiZmOiZmLh5KDf4uIg5SLhpaJhpGLh5KQhI2Rho6OhIuQhoyQh5GOhpCNh5CM ho6Qh5GOhpCDgISHhIiOiJSUjZmRjZaOi5SOiJF+eIB5cHp7c32Cc3qCc3qDb32MeIaJfoyJfoyJ fYOAdHp6a3B7bXJ9cHR6bnJ/dH2EeYKIgIaHf4SEfX+JgoSSiZaQh5SJhpGJhpGLhomIg4eIe32H ent+dHV9c3R/dHOIfXuIfXmDeHSDd3iDd3iEb3eLdX2Nf4iVh5CaiZaZiJWQhoyHfYODdXOJe3mS g5WZiZubiJGUgImLd3mHc3V/dXuAd32GeX+GeX+Hen6EeHuHcm+Eb22HcneMd3uNfYmNfYmSf4OR foKJen2EdXiJc3qMdX2OenqMeHiLcnOGbW6EcGqGcmuHeH+MfYSQen+Md3uMdXqMdXqQeYCReoKQ fYOMeX+Qc3WLbnB/ZV53XVZ+ZFqCaF2DaV5/ZVt7Z2eAa2uJdHeQen2RfXWOenONdXCNdXCQc3WS dXiRe4OWgIiZgoyZgoydf4SXen+Qd3iMc3SRcHeNbXOObWuObWuMcHWQdHmUdWmSdGiSb2eQbWSO bmWUc2qUcGiRbmWObWqRb22Rb22WdHKQcmiNb2WQbm2Rb26RdXKMcG2NaF2JZFqGY1eMaV2IaVqI aVqRameVbmqSaVGHXkeGWkSGWkSAc3uCdH17bnl9b3qEb3eJdHuIe3+Mf4OMhoyNh42ShI2OgImL f4uMgIyMe4uMe4uLfX1/cnKEcHCCbm59am5/bXB7b3OAdHh/c3mJfYOLhJCNh5KNhouMhImIeXuD dHd9c3R/dXeEeoCEeoCEeYSHe4eJgIiOho2Ui5WSiZSNiZKMiJGShpmViJuUjZmRi5aNiImRjI2R i5GQiZCUi5eUi5eSi5qSi5qSi5qOh5aLhIuMhoyRjJ2RjJ2OiZmJhJSAeXt9dXh9dXqAeX6He4SJ foeHf46MhJSOhJWNg5SNhJGMg5CAfYZ9eYJ7en+HhouRh5eUiZqSi5qRiZmMhpGGf4uHhJSJh5aJ hJSLhpWVh5CUho6RjJCOiY2NhpWQiJeUjJ2RiZqJgpKIgJF/e4SDf4iIf4yVjJmRjZmOi5aMiI5/ e4J4eHh3d3d/c3SDd3iIdXmQfYCJf4aIfoSGeICAc3t7aW19am56bnJ7b3OCdH+Je4eHf4KEfX+D fYOMhoyWi5mRhpSLgoyJgIuMho6Jg4yLf4iHe4SAeXt7dHeDdX6LfYaNfoCLe36LfoKMf4OHc3WH c3WGeICNf4iWiJGZi5SShoeIe32DeHeLf36RhJaWiZueiJKVf4mGd3mGd3l/eH1/eH2HeHqGd3mE c3KDcnCCbW2Dbm6Ic3qOeYCNgpCOg5GUfoaOeYCLdX2GcHiIbneMcnqHdHiHdHiCbnCEcHN+cG5+ cG6Gd3mJen2Oen2IdHeIcGuJcm2NeoCQfYOOfoiLeoSSdXiJbW9+ZVh1XVB7YleCaF2EaWWDaGSC aGSEameIcneOeH2Sf4ORfoKUeHqRdXiOc3OOc3ONeHWQeniaf4abgIedgIaVeX6Qc3WIa26JbW+J bW+NbmuOb22Oc3OWenqVeHiSdXWWb2uUbWmQbWSSb2eRbmKQbWGQal+Ra2GMbmKOcGSMb16Mb16Q bW+UcHOVdGuScmmQa12NaVuHYVGHYVGGZ1eGZ1eOamiQa2mSaFqMYlSMX0yNYU2Ee4OAeH99dXp+ d3uDd32GeX+LfYaOgImMh5aLhpWShI2QgouNg4mMgoiHgImIgouHfYB7cnV+cnV4a297amt6aWp5 a2l7bmt7bXKEdXqHeYKOgImOh4yNhouCe3d9d3J7eXh+e3qGfoCIgIOJfoeMgImQiZKUjZaSjJeU jZmOi5SLh5CWhpWXh5aViZWXjJeVjJaXjpmSkJ+OjJuRiJWSiZaOiZmRjJuXi52Uh5mNhI6SiZSZ kaKZkaKVjJaJgIt/fXt6eHd+cniEeH6CeHuAd3p9d32DfYOHfY6OhJaRh5mQhpeEfoR7dXt9eX+J hoyOhpCQh5GRiZqOh5eLgJKIfpCIg5KLhpWJhJWQi5uUjp6VkJ+RlKKMjp2OiJSNh5KUjJuQiJeH fYOAd314bnJ+dHiAd4eOhJWMiZeNi5mLh42AfYN4eHh5eXmEfX+GfoCLgIKMgoOIfYaLf4iIeIKC cnt6b257cG95bXCCdXmIeoOOgImNfoOIeX6IeYuXiJqbkp+SiZaLfoKGeX2IgouMho6Hho2Hho2L fYaGeICIeYCMfYSShoyRhIuUh42ShoyLfX1/cnKCdXmMf4OWiJSZi5aUh4uNgISIeoOMfoeOhJWW jJ2dh46Re4OCdXd+cnN9c3eAd3qGeHiGeHiAcnR/cHN/cHWDdHmMeIaOeoiNe46LeYyLeH6Gc3mE b2+CbW2Eb22Hcm+IeX6IeX6CdHR/cnJ9cnB+c3KLdX2LdX2Je3eCdG+AbmeGc2uGd36Le4OSg4uS g4uWgH6NeHWHbV+AZ1qEZWKIaWWHa2eHa2eHbWiHbWiJcHKNdHWMeX2QfYCXfoKVe3+Sd3OMcG2J cm2NdXCXe4CZfYKZf36SeXiOc2+IbWmDZ2uDZ2uEbWeIcGqMb2+Ud3eUeXSRd3KZc2uSbWWLamKM a2ORbV6OalyMaFeJZVWGaFyJa1+LbWGLbWGNa2mQbmuUcGiWc2qXcmiOaV+NY1eIXlOHXVGIXlOL ZVyLZVySa1qRaliUalWUalWHfoaEe4ODeX+DeX+DeICEeYKHfoiJgIuLhpmMh5qNhJGJgI2HgImE foeCfoeEgImCen97dHl/c3l7b3V6bWp7bmt6bW1+cHCAa3CGcHWMeX+Wg4mUiJSNgo2Ae32CfX6A f4SDgoeJhIiJhIiLg4aOh4mQjJeUkJuUjJuSi5qNh5CNh5CRiJWSiZaVjpeZkpuWlKaUkaOSkKKR jqGVi5uVi5uVjpqVjpqWi5mQhJKJf5CUiZqXkJ+VjZ2WiJGNf4h9ent7eXp/dXmDeX1/enl9eHd5 b3N9c3eDeIOOg46QhpqQhpqEgIyAfYiAeoCHgIeIgoiNh42MhpGIgo2He4eJfomJg46MhpGGgo2N iZWUjp6WkaGSkp+MjJmGf4uHgIyNh42IgoiAd3h9c3R6a26AcnR+d3uIgIaNh5KSjJeMh4uAe39+ d3mAeXuHfYOJf4aIhoeLiImJgIiJgIiJeYOGdX99cmt7cGp6bW2Ed3eEeoCIfoSJen+Jen+LeomX h5aXkKGRiZqJgH99dHOCd4KHe4eHfY2Jf5CIeYCHeH+HcnmNeH+Igo2LhJCRiJCQh46Nf3+Ed3eG c3eQfYCXiZWajJeUhpGNf4uJe4eLfYiOho2Ui5KVh4SMfnuAc3B9b22DdHeGd3mNen6LeHuEcniD cHeDcHeHdHqJeYaIeISJeoKDdHuDb29+amp9aGp9aGqGbmmNdXCMfX+NfoCLenOCcmp+a2+Cb3OL dX2Md36NeXmHc3OAbmiCb2mEb3mIc32OeISVfouagISXfoKRd2uJb2SGa2eIbmmDb2WCbmSCamKC amKAaGeHbm2Hc3WMeHqQe3WSfniQd2mGbV+CaWiGbWuOcnmQc3qSeXiQd3WRcm+MbWp/ZGF9Yl6E Y16HZWGGaWmMb2+Ld22MeG6ScmeNbWKJaV2Lal6Na1yMaluJaFaGZFOJZVeOalyQamGOaV+NaF2R a2GRaWOVbWeQal+Qal+OZFSNY1OIXk6GXEyIZVuJZ1yNZVyOZ12OaVWUblqGeoaEeYSLd4KIdH+H eH2Jen+LgoyNhI6Qh5SQh5SQgpCJe4mEeYeGeoiAeIKGfYeEeoCCeH5/dXt6cHd5b3N9c3d+cnV+ cnWDdHmGd3uJeoKQgIiUg5CNfYmHeoCMf4aMhJWNhpaJhpGIhJCLhomQi46SiZaXjpuRiZqQiJmN h5KMhpGQh5SUi5eUkZ+VkqGXkaiRi6KOjJ6LiJqWjJ2Vi5uWjZqUi5eQhJKLf42JgpGUjJuXjZ6W jJ2Rh42Jf4aCfoeCfoeEgoaIhomDgIJ+e31+cHB9b29/d36LgomMhpGLhJCHgImDfYaIe4KNgIeM goiNg4mEfoeAeoOCe4KGf4aMgIyMgIyIf4eQh46UlKGXl6WWkpuOi5SIfoSIfoSMhISGfn59dHB/ d3OAdHiGeX2GfoCIgIOIgouSjJWSiImLgIKDe3uDe3uHfoaOho2JiJCJiJCNhJGLgo6JeYOGdX9/ dHN6b253a2V+c219enuAfn+Mf4OHen6He4eRhpGWjp6RiZmJfX6EeHmAdHqCdXuHd4aJeYiDeX+A d317cG+AdXSHe4SMgImOiJGOiJGShomEeHuLe4CSg4iWi5mViZeMf5GEeImDdX6Je4SOh4mRiYya iISQfnqMc3eIb3OGdHWNe32Rg4OOgICJdHmCbXKEa22NdHWMenmLeXiMeHqEcHOAZV57YVp5Xlt+ Y1+Da2eOd3KUgISSf4ORfXqLd3SGbXCDam6DbnWLdX2OeH2Jc3iDb2mAbWeAa3WEb3mMdYSSe4uV f4SWgIaOen2NeXuLeHKIdW+GdGd/bmF+ZVt/Z1yDYl2EY16GamqNcnKOeXeRe3mSeW6NdGmIbWiH a2eHbXOMcniSd3eRdXWScG6LaWd/YVt/YVt+ZFqAZ1yGZGKObWqLdGWLdGWRb1+Qbl6NaF2SbWKO bmKNbWGRb1uLaVWOZ2GVbWeQbWGSb2ONaF6OaV+LamKQb2eOaV6UbmOValqQZVWHYk5/W0eJXlaM YViJZFuMZ12RZ1uWa1+Gd36DdHuCcnt/b3l+dX1/d36GfYeMg42OhpKMg5COfoiHd4CAc3t+cHl9 b3iAc3uGd36EdX2AdHqAdHqAdYCEeYSGeX+HeoCIeIKIeIKMeYKLeICHeoCGeX+Cd3+IfYaJf5GN g5WLhJCGf4uEfomLhJCMg5CRiJWOh5eMhJWLgomLgomVh5KZi5aVjZ2VjZ2Ui5eQh5SRhpGQhJCO hpCMg42NgpCJfoyMgoiLgIeLiJaQjZuUiZqSiJmOgImIeoOIfYuLf42MhpGQiZWJho6AfYZ9cnCA dXR+e3+EgoaMg42LgoyIf4eEe4OGfYeIf4mMg42Mg42HfoiGfYeHfoaJgIiMgoiJf4aGgJCOiZmV laKamqeUkJuNiZWLe4OJeoKHf4KCen2CeHmEenuHfoaHfoaIgoiLhIuMg42VjJaXjpmVjJaLiIyJ h4uJiJCMi5KWiJaXiZeUiJaQhJKLe4CEdXp6bm96bm99b2qCdG+Ie3+Mf4OGfoOGfoOHfoaOho2S jJWRi5SMgoaGe3+IdXuDcHd+cnh/c3l+b3R+b3R+c2+DeHSHf4KNhoiSh5CSh5CRg4yMfoeUf4ua hpGVjpeSjJWJe4SCdH2AcneHeH2Jf4aRh42Uh4uQg4eNeH+GcHiDdHeMfX+NhouOh4yNe32CcHJ+ bmOHd2uLfnWNgHiLfnWAdGt9Y1x5X1h1XFF4XlSDb22RfXqWf4eUfYSRe4OIc3qHanKAZGt/amqG cHCLeHuJd3qEb22CbWp+aWt/am2HbnKQd3qQeYCSe4ORe4aQeoSQe3uMeHiNdGeHbmGIZVuGY1iI aF+IaF+Ha2uMcHCIdHeMeHqXfoKReHuOc26IbWiDam6Ib3OScnOUc3SWdGSLaVqAXlaAXlZ6YVZ6 YVZ+Y1+EaWWHbWKLcGWSa1yRaluRbWqWcm+Wd3CXeHKUdGSQcGGQaWWRameRbmWQbWSNZV+NZV+H ZFyNamKIZVuMaV6LaFCLaFCEXk2AW0mDWkmEW0qCWEqCWEqMX1eSZV2AbnJ+a297bXJ7bXJ7cHuA dYCHeYSNf4uOg5GJfoyHeYKEd39/dHB6b2t7b3B/c3SGd3mGd3mEeHuGeX2He4eIfYiJgIiLgomO goiHeoCHdHiCb3OCcHKAb3B4bnJ/dXmEe4iIf4yNf4iIeoOGd3uHeH2IfYuLf42IgouHgImLg4iL g4iSiI6Vi5GVjJaUi5WSjJKNh42Lh42JhoyJgoeIgIaJe4SMfoeNhIyRiJCRjJ+Qi56OhJaMgpSG d36Gd36IfoSIfoSMhpGQiZWQh5SNhJGCd3+EeYKCe4SHgImLf4uEeYSCdXmIe3+LgoyQh5GQiZWM hpGDfYOGf4aHg4yJho6NgouIfYaJg46VjpqalJ+fmaWVjpeLhI2Ef4CEf4CHgoaCfYCGfoCHf4KG goiJhoyLhI2Nh5CLg5aRiZ2UkaOSkKKOjJuMiZmNiZWOi5aUhJaVhpeUiJGWi5SXhoSJeHd5bW59 cHJ/c3SGeXqHfYCLgISHf4SIgIaGe3+Jf4OVgouWg4yWg4ySf4iLfn+GeXp+c299cm57cnN9c3R+ eX2Ef4OLhomRjJCUiJGRho6QgouOgImRg4yXiZKVkJSUjpKNf3qGeHN+bWt/bm2HeH+Sg4uRho6R ho6QgIiIeYCEeH6NgIeQhJCSh5KRfXeJdW+CcG+LeXiQhomQhomOg3+EeXV7a2F1ZVt0YlV3ZFeC c3WOf4KXgomVf4eUfoCOeXuNdHWGbW57aGV9aWd+b3SEdXqHcnSHcnSGbmmDa2eDaGSJbmqLcHmQ dX6QdX6SeICSfXqRe3mScmWLal6IZVqJZ1uJb2SHbWKMcm6OdHCIc3WOeXuWeYCUd36QdW6IbmeH Z2iHZ2iNa2mQbmuUcGiMaWGGX1CCXE15WlF1Vk57XFSEZFyGamWLb2qUb2GUb2GUcm+aeHWbe3md fXqXeGiRcmKRbV6RbV6Sb2SQbWKJaFiEY1SDX02HY1CEXVSEXVSEXEeGXUiDW0aCWkV/V0WAWEaA VkaDWEiGXEmMYk9/cnJ+cHB9aG9+aXCCa3iGb3uJc3+OeISNfpCOf5GMfYSGd35/cnJ7bm56cHR+ dHiGeXqHenuIeX6IeX6Lf4iOg4yIhI2IhI2Ng4eGe3+AdGh7b2N9bWV6amN3aWeDdXOEeoCHfYOI foKHfYCHdHiIdXmEeoCIfoSJgIiHfoaIgoiMhoyRiJCUi5KXjJeWi5aQi46NiIyJhoyJhoyJf4aL gIeIf4mMg42Rh5uWjKGVkKGRjJ2LhIuIgoiDd3qAdHiCfX6GgIKLhI2QiZKRiJKRiJKJg4mGf4aI f4mNhI6Lg4iEfYKCeH6LgIeQiZWVjpqUjZaNh5CEgImEgImHhouMi5CMgoiJf4aMhJSWjp6alaWa laWRkZSGhoiIhICMiISMhIeJgoSIg4SJhIaMh4uOiY2OiI6Nh42JgpWQiJuUjaWSjKOQi5uMh5eM iI6MiI6NgpCSh5WVjZ2XkJ+WiIiIenp7cG99cnB/d36If4eIf4eLgomLfoSHeoCDd3iEeHmRe4OV f4eai5KZiZGShoeMf4CCc3V+b3J/c3SCdXd+eoOJho6Nh5CUjZaViJqUh5mQhJCNgo2ShI2ajJWX jpmVjJaWhn6Me3R9amN/bWWLeH6Sf4aRiJKSiZSUh42Qg4mIe3+ShomSh5CSh5CUenuNdHWHc3CN eXeSiI6WjJKRh4iLgIKId3N+bWl6aF5+a2KDd32Mf4aXgomVf4eXfYOUeX+OenqMeHiAbmh7aWN+ aW6HcneJdHSIc3OJbW2GaWmCZ2eDaGiGbW6JcHKJcG+Mc3KReXOSenSUdGSLa1yHZ16IaF+Lc26N dXCOd3KNdXCNcm2UeHOVeXmWenqUeWuNc2WJaGOEY16HaGSHaGSNal+Nal+NY1eHXVF9Vk5+V0+D WFCHXFSHaGWNbmuVc26XdXCXd3qben6de3ObenKdeGSbd2ORbmKQbWGObVuObVuLaFB+XEV+VUCA V0OCVkCEWEODWD6CVz2GVz6HWD+DWkOEW0SCWEqDWkyCWkCGXUSHeH+Gd36EcHCDb2+GcHWIc3iJ d3+Oe4SNf42QgpCSg4uJeoKDdHeGd3mDeX2Ge3+Een6DeX2Jen+Jen+LfYuRg5GJhpGMiJSQhomI foJ/c2d5bWF1aml3a2p/a26Hc3WHeYKIeoOEe4aCeYOEeHuAdHiEd3+IeoOGf4iHgImMg42OhpCS i5CVjZKZjZaZjZaRiJCRiJCNh5KJg46LfoSMf4aLhI2Ri5SVjJmVjJmViZWUiJSNg4SLgIKGe3+A d3p+eYiDfo2Ig5KLhpWRiJKQh5GOh4yNhouRiJWOhpKLh5CJho6IhpSOjJqSkZuVlJ6VkZ2VkZ2J hpGJhpGLiZGHho2If4mJgIuMhJWXkKGelKaakKKSiZSOhpCOho2SiZGViZKUiJGMh4uNiIyNiIyO iY2NiZWMiJSOh5aNhpWRh5uQhpqNg5SOhJWIhIuJhoyLgo6OhpKQkJ6WlqWSkpWEhIeJeHmMenuL g4aUjI6UiZCOhIuNen6Gc3eGc3eGc3eLfYaQgouZjJ6bjqGWi5SQhI2JfYODd32EcHOJdXiNhIyU i5KWjp6XkJ+XjZ6SiJmNgo2MgIyNf4iUho6UjZmWkJuZjI2Mf4CAb25/bm2IeoiQgpCQh5GOhpCW g4mVgoiJf4OSiIyWi5SViZKUgoOJeHmHdXeNe32Uh42bjpWeiJCXgomUen6Mc3eAbW+Ld3mNf4uU hpGWgoKUf3+QfYCQfYCRg36Rg36JdXOAbWqEaGiGaWmDb3KIdHeMcm6Ga2iCZ2d/ZGSDaGOIbWiG bmmIcGuNcHCUd3eUeHCMcGmIaF+NbWSRdHuUd36Rd3ONc2+McGmOc2uUd3eWeXmXeHSVdXKHa2eA ZWGEZV+GZ2GIaF2Ma2GNaVaHY1CDW0aAWESCXk6DX0+JZ1yMaV6RcmuUdG6RdXKVeXWeeHCXcmqW cGWadGmVb2WUbmSSbl+Qa12MaVGAXkeAVUGAVUGEVUCGVkGHWD+IWkCNXUiRYUyUY06VZE+OY0qH XESIWD2JWj6MgoiMgoiMf4OHen6HdH2IdX6JeoKNfoaLfYuMfoyOgImLfYaJfYOIe4KJd32LeH6H en6GeX2Hen6Ie3+JeoKNfoaRfoeVgouOhIuLgIeDdXB+cGt7bXJ/cHWDdHeIeXuIe4KGeX+GdX+E dH6EeHt/c3eDdHuHeH+DfYiHgIyLhI2OiJGWi5SWi5SXjpuVjJmRhpGQhJCLgomLgomNgouOg4yR iJKUi5WSjpWRjZSSiZGSiZGUjZSQiZCMh4iDfn9+eoaCfomHg4yIhI2LgoyMg42Ug42Ug42Sh5WS h5WNhpWOh5aQiZWSjJeSkJ6WlKKUkJuSjpqNhJGOhpKJhpF+eoaAd32EeoCHgpKUjp+akqOWjp+X iZWXiZWRiZmRiZmVh5KWiJSSh5KRhpGMiJGQjJWSi5qSi5qQh5SOhpKNhouLg4iHfYOEeoCAe4uE f46LhJCQiZWOiZmZlKOZlZuOi5GShomRhIiUiJGZjZaUh4uQg4eQe3mEcG6CbmuGcm+Gf4aNh42Z jp+XjZ6ViZeRhpSLg4iGfoOMeX2QfYCVjJmVjJmWjJ2XjZ6SjJeNh5KHf4KEfX+Jen+QgIaOiJGV jpeVjZKNhouGeX+AdHqHfpWOhp2Qh46NhIyQgIOQgIONg4eSiIyZiJWaiZaUgIeIdXuHcneLdXqX g46diJSih5Kdgo2OeH2Jc3iEd3eLfX2Sf4iVgouXgoKVf3+Re4OSfYSRhoSRhoSRgHmHd2+Ha2eC Z2KEa2qIb26McHOGam2EaWKCZ1+Eal2IbmGIbWiIbWiLc2qReXCRd3KOdG+Sb2eXdGuXdYCaeIOV eX6RdXqLb2iIbWWQcG6ZeXeXe3uWenqMcGuGamWEY16GZF+IaFyLal6JaluLa1yGZU+DY02EZFqH Z1yIaVqJaluQbWGSb2OOb2uRcm6ab16UaViSaFeRZ1aSa16Wb2KObmORcGWRblaHZE2GXkN/WD2H WD2JWz+OX0SNXkOOXkmRYUyaZUqeaU6ZaFCRYUmHWDqLXD2Nh5CMho6Ngo2He4eHeYKHeYKEeHuH en6Ie3+Mf4OSgoyMe4aEeYSDeIOJdHeLdXiId3iJeHmEeHuHen6GeXqHenuLe4OSg4uMhpGJg46H f4KDe36HeH+IeYCGeoOHe4SJf4OEen6IeX6HeH2EeHmAdHWCdHSDdXWAd32EeoCEf4OMh4uVjJSX jpaQjJWMiJGOho2JgIiJgIiMg4uQiZKQiZKSiZaVjJmRjZaSjpeRjZaUkJmWkaKUjp+SjJWNh5CD gISIhomHhIiDgISGfoCGfoCMfoeUho6Qi5qSjZ2Li5mIiJaJiJCNjJSQjZuRjp2MiZeLiJaMgImJ foeCfYB6dXl/dHOHe3qHg46NiZWRjZmOi5aSiJqUiZuRi5aQiZWQh5GSiZSQiZCQiZCQiZKRi5SV iZWXjJeUjZaUjZaLiIyJh4uGgH+Ae3qEeYKGeoOMg5CLgo6Ri5aZkp6XlJ+SjpqRiJKSiZSXjJWa jpeUjI6Oh4mOeXeJdHKHdG6Jd3CCfoeNiZKXkqaUjqKQh5GOhpCOg4yJfoeQgouShI2VjZ6Si5uM iZeLiJaQh5SMg5CHfYCEen6Een6IfoKShJCZi5aZi5SXiZKNgouMgImLhpaNiJmSi5CQiI2SfYSQ eoKLg4aOh4mVh5WZi5mWgIOLdXiEcGqJdW+MfYSXiJCZho6Wg4ySe4OMdX2Jen+NfoOUfoaWgIib f4Sbf4SUfoaUfoaRhIiRhIiWgoKLd3eLcGWCaF2CaWqIb3CLcnOJcHKLbWOHaV+Eal+IbmOOb22O b22QdG+Ncm2Od3CSenSSeHCVenOWeYCVeH+UeHqNcnSIZ2KIZ2KHamqRdHSUe3eReXSRcGiJaWF/ X1d+XlaCYlaEZFiIa1uIa1uJaFiJaFiMbmKNb2ONcF+JbVyOaFaMZVSIamGLbWORbliNalWLZU+I Y02OY1ORZVWOaV6SbWKVcF2JZVOHYT+DXTyHXkOJYUWUZEqSY0mVZE2ZaFCaa0yfcFCXaE6RYkiI XUCHXD+SiJmOhJWOhpKNhJGMgIyLf4uIgIOEfX+Ie3+JfYCLeoeLeoeMeX+MeX+MeHiLd3eHeXeE d3SDdXWEd3eIdXmHdHiGf4iIgouJgpKJgpKNf4iOgImDf4aIhIuMhpGJg46OgoaLfoKIe3+Hen6G dHOEc3KDdXOGeHWHdHiMeX2NgIKXi4yWjJKWjJKRh42QhoyGfYSJgIiEfomHgIyJf5CLgJGOhpKM g5CLgo6NhJGWjJ2akKGUkaGQjZ2SjZ2OiZmLh42Hg4mGg4d/fYCCeHuGe3+Qf46Xh5aSiJmSiJmR iJCLgomJhoyQjJKVjJaRiJKLhIuHgIeMf4aIe4J5dHV4c3SCd3WGenmHg4mNiZCSjJWQiZKRh5eR h5eOhpCQh5GSiZGVjJSQiZCQiZCVi5GWjJKXjJqWi5mSkJ6WlKKWkJmRi5SIiY2AgoaGeX2Ie3+C en2AeXuEfomMhpGViZKWi5SSh5CViZKWjpSakpedjZWVho2SfYKRe4CSf4aSf4aQh5SVjJmXkKGR iZqJgIiLgomJfoeJfoeOg5GNgpCViZeRhpSMhoyHgIeLgIeMgoiHf4KGfoCEf4CGgIKOfouWhpKR hpSSh5WNhI6Mg42RiJWVjJmXjZSRh42QeX6Nd3uJe4mQgpCUg5KXh5aRgoSIeXuGbmmEbWiIdXmS f4OShomRhIiWf4eWf4eUhIySg4uVgoaWg4eVf4KVf4KSf4OSf4OQgIaVhouVf4SSfYKNc2uDaWKD aW+IbnSLcnCMc3KLcGWGa2GEaWWLb2uMcG2Lb2uLaWeObWqNc2+VeneWfnmagn2dgoiZfoSSdXWG aWmAYVZ5Wk93XViGa2eNbmqUdHCWc2qOa2OJY1SAW0x/Wk2CXE+LZFONZ1WNal6Nal6ObWqXdXOS eGmJb2GJZ02EYkiEY1SIZ1eOaleOaleNaFGNaFGQZVOSaFWOaFaOaFaSa1qSa1qOZESOZESRZFCU Z1OZblaUaVGVaVCUaE+Xa1OablWWak2SZ0mMYkGMYkGVi52Vi52UiZuUiZuMh5eJhJWIhJCEgIyI foSJf4aMe4iLeoeLfoSNgIeJfn2IfXuJen2Jen2LeXiMenmNeH2JdHmLe4OLe4OJgIuLgoyMg4uR iJCOiJGOiJGVjJaRiJKRgoSNfoCHf4SGfoOLeH6LeH6GeX2JfYCHfYOJf4aQgouVh5CVh5CVh5CQ gouLfYaDeoKDeoKCdYeDd4iIeISIeISHen6JfYCOgoiRhIuUi5eZkJ2Zkp6Zkp6akZmVjJSQh5SN hJGMg4uJgIiAdX6DeICLfpGQg5aRhpSMgI6MgoiLgIeLh42MiI6Oho2Mg4uHfoiGfYeIgIZ/eH2D d3p/c3eIe3+LfoKDgISJh4uOi5SOi5SSiJmRh5eSjJeRi5aSjJeWkJuUjZSSjJKZjZmZjZmWjp6U jJuRjp2WlKKWlZ2SkZmOjZeIh5GHfYCCeHt+dXJ+dXKEen6LgISRhIiViIyUiZCUiZCUjZaWkJmX jJWUiJGWiJGUho6Qh46Qh46SjJeXkZ2ZjZaUiJGNg4eMgoaMf4ONgISNhIyMg4uUho6Rg4yQg4SL fn+Ng4SNg4SLg4iLg4iJgoSGfoCJeoyQgJKOg4yQhI2OhIuQhoyQh5SRiJWUjpKRjJCRfoSLeH6I eISOfouSgo6Xh5SVgoiOe4KJdXWHc3OId3WQfn2Vg4SWhIaSg4iUhImXhI2Zho6Zg4aahIeVgIOU f4KWfYCWfYCUfoaXgombf4SVeX6Oc2uDaGGDamuIb3CLcnCMc3KDcGOAbmGAbWeGcmuLb2qJbmmM a2GIaF2IbmmQdXCZe36bfoCbf4SXe4CSd2+EaWKDYlN6Wkp0V0x7XlOHZFyRbmWQb2SJaV6IZFSA XU1/W0WAXEaNY1eQZVqIZVqIZVqNa2eUcm2ZenCQcmiNakeAXjyCXUmLZVGRbV6Ub2GWbVeWbVeX a1uWalqUaleSaVaSbVaUbleUak6Uak6Wa1aWa1abdWGVb1uVa1GWbVOeblGiclWdck+Wa0mRY0OQ YkGVi5uWjJ2WjKGWjKGQi5qMh5aOh5aMhJSOgoOOgoONgouLf4iLe42MfY6LgIeJf4aMf4OOgoaM fYSNfoaOe4KIdXuIdXuLeH6JgIuNhI6QiZKRi5SRiJWSiZaWi5mUiJaMgImHe4SGgISHgoaNgIeM f4aJf4aLgIeHe4mJfoyOgI6Nf42Of4eNfoaHen6EeHt+dHV9c3SDdHmDdHmIc3qIc3qAeX6Hf4SQ hoyUiZCZkJqdlJ6dlp+alJ2WkJmOiJGQh46NhIyLhIuHgId/d36CeYCLhomMh4uQhomJf4OMhISN hoaMjIyIiIiJf4aLgIeGfYSEe4OHf399dXV7dHeCen2JfoeNgouHgIeIgoiQiZWQiZWQjJeNiZWQ i5qRjJuUjp6SjZ2Zkp6XkZ2elKadkqWQiJeNhpWNiJeQi5qSkZuQjpmQjJeIhJCJg4mDfYOCenqA eXmDeX2HfYCMgoONg4SMhImQiI2UjJuZkaGWkJmXkZqdjJudjJuVjpeSjJWQjJWUkJmajJWWiJGR h4uNg4eJf4OJf4OEgIeJhoyViZKUiJGViImOgoORh42SiI6RhpGUiJSSgoyLeoSHeH2Jen+LfoSN gIeMgImLf4iLgoyMg42Oho2SiZGRgoeMfYKEdXiJen2Mf4aShoyWg4mWg4mUfoaQeoKNeoCOe4KS f4aXhIuXh5GXh5Gah5CXhI2ag4iag4iZhIeZhIebgoOXfn+Wf4SZgoeagISWfYCVd22GaF6CaGGJ b2iNdW2MdGuHbmGCaVyCamKJcmmMcmeHbWKLaVeIZ1WGa2eLcGuUeHSXe3iVeneQdXKSdGeJa16H Y1B+W0h0VkV1V0aAX1CGZFWHZ16DY1uCY1F+X06CX0qLaFOQamOUbmeRb1+ObV2LbliQc12Vem+R d2uWb0yEXjyEXEWNZE2RbmWWc2qZdGGadWKbdGKWb12ZbVyVaViRal2UbV+WcFyXcl2XcluXcluh dWSid2WbdWGadF+idVyjd12hclGeb0+XaEWUZEGRiJKVjJaVjpqVjpqSjpqSjpqWjZqRiJWNhouM hImJe4mIeoiHe4SIfYaMgoaMgoaNhoiNhoiOhIuLgIeJf4CCeHmCc3iDdHmHeYKQgouQh5GOhpCW iJSWiJSajJqVh5WOgoiIe4KIeoOLfYaIf4yMg5CHfoiGfYeEe4iEe4iIfoKJf4OJe3uIenqEeHt+ cnV+bWt+bWt9am6AbnKDbnWHcnmAeoaIgo2Og4yUiJGXjJebkJuZkp6SjJeLiIyLiIyShoyRhIuM hoyMhoyHgoOGgIKJhIiQi46QiIuJgoSRh4uOhIiMiYuMiYuUh42OgoiMh4uLhomLgISGe39+e3+E goaOiJGMho6OgImLfYaLhI2Nh5CQiZWOiJSQhpaUiZqXkJ+Wjp6bkaKdkqOekquXjKWOh4yGfoOG foOIgIaLiZSJiJKMhpGNh5KOiI6QiZCJhoyJhoyNhI6LgoyOgoiMf4aIgoiLhIuSh5KZjZmZkJed lJuikaGhkJ+ajpeWi5SSjJWSjJWbjZabjZaWjJKUiZCJgoeGfoODf4iMiJGXjJeXjJeUiJGUiJGX iZKZi5SajJWXiZKVgoiRfoSIenqGeHiGdX+Hd4CHeYKJe4SMg4uLgomMgImRho6VgouSf4iNeH2L dXqGeIONf4uVgoiWg4mZg4uZg4uRgoeOf4SOf4eSg4uejZqbi5eZjJCUh4uSg4aVhoiai5KdjZWa jIyUhoaXgoSXgoSXgIaWf4SWe26Jb2KHalqLbl2MdGuNdW2OcmGHalqGa2GLcGWJb2GIbl+HZ1uH Z1uJbmqNcm6MdG6NdW+SdGqMbmSLa1yIaVqQZ1SJYU6AX0x7W0d+W02AXU+DXlSAXFF+X1CAYlOH ZFiQbWGadHefeXuZem6UdWmQd2iSeWqSeW6Ve3CadF2MZ1CJY1GOaFaSc22VdW+ad2qbeGudeWGZ dV2ec2KZbl2NaVuOalyObVuScF6XcGGddWWfeGWje2mheWeheWeme2WnfWemeV2jd1ubcE6UaUeR iJKUi5WVjJmUi5eVi5uZjp+XjZ6UiZqOhpCJgIuEd3+CdH2Ee4OHfoaOfoiRgIuSg4uUhIyOh4yL g4iDe3t/eHh+c3KAdXSDdX6Nf4iNhouNhouWg4yWg4yXh5SXh5SNgo2DeIOGeX+LfoSHeYeMfoyE e4aAeIKCeYaCeYaEeoCDeX+EdXiCc3WDdHl/cHV9b217bmt6b25+c3KEb3SIc3iJe4mQgpCSh5CU iJGVjZ6Si5uOho2Mg4uOhIiNg4eWgoSZhIeShoyRhIuMgoiNg4mNh42SjJKUi5KRiJCUjI6RiYyR jZaWkpuajpqXjJeSjpeNiZKVi5GOhIuGgouMiJGQjZuOjJqNf4iLfYaEgoaGg4eJhIiIg4eNhI6R iJKSkZaSkZaVlp2Wl56elqaVjZ2RiYyHf4KGd3mJen2Nh42Nh42LhI2OiJGRi5aWkJuVjpqSjJeO iJSOiJSQhomOhIiMh4uNiIySi5CWjpSZkJ2flqOilqKflJ+fkpmajZSUiZCUiZCajJWbjZaajpeW i5SOhIiJf4OJf4aRh42XjpmakZuVjpeRi5SVjJaWjZeakJaZjpWShomQg4eGd3mGd3mHdH2Gc3uH eYKJe4SGfoCHf4KLgISMgoaRhIiQg4eUfoOMd3uGd3uLe4CRgoSRgoSZhoybiI6Uh4uQg4eQfYOU gIeXh5GdjJadkJGShoeUenuVe32ajJWhkpuikpeai5CahIyWgIiXe4CXe4CZenCUdWuJb2SMcmeM cGmOc2uQcmiMbmSJbmmJbmmJb2GIbl+Ga2GEal+MbWmQcG2SdGeUdWiScF6MaliJaFaHZVSMaFeN aViJaV2GZVqIZFZ/XE6DWE2DWE2DXU6DXU6IYlWSa16ed3eheXmdfnSXeW+SeHOUeXSUeHOXe3eX c1+NaVaQbl6WdGSdeW6deW6ZeWedfWqhe2ihe2ihdV2ec1uVb1uRa1eOaleQa1iUaVuab2GbdWuf eW+leG+meXClfW2heWmidF2jdV6edFqacFaVh5CWiJGSiJmSiJmUiZqZjp+XjpuVjJmUg5COfouG d3uEdXqJeoKLe4ORgI2Ug5CWgo2Wgo2QgIiNfoaHeHqDdHeAc3OAc3OAd32MgoiOhIiOhIiQg4eQ g4eNhIyOho2Oe4SIdX6GdHWGdHV/dXt+dHqAcnmCc3qHeYSEd4KHdH2LeICLdX2HcnmHdHqCb3WC bnCAbW9/dHOAdXSEdXiHeHqIfYiNgo2Qh5SSiZaUiJaOg5GMh4uNiIyRiY6QiI2SiI6Vi5GWjpSR iY6Mg42Mg42MhpGUjZmWkJmZkpuZkpuXkZqakqOdlaaWkaKRjJ2RkZ+SkqGUkZ+Ni5mLiJeNi5qQ i5qOiZmLgIeJf4aCf4CAfn+Ig4eLhomOiY2OiY2Oi5GUkJaUkpqSkZmZlaGVkZ2RjpCEgoOHen6N gISOhpCQh5GQh5GQh5GViZeajp2akZuXjpmSiZSSiZSSjJWQiZKQiZKRi5SVjJSSiZGRkJWZl52e l56dlp2bjpKWiY2Si42RiYybjpWdkJaZkJeWjZWWh4yRgoeJe3uQgoKajZSekZeVjpWRi5GViZKZ jZablJmXkJWUh4uMf4OGd3mCc3WHcHWCa3B+b3R/cHV/dXmGe3+JfYCNgISOgoiRhIuVf4eQeoKN eH2OeX6Rg4ORg4OXiI2XiI2SiIyNg4eQg4eRhIiSgoyXh5GejpaUhIyQen2LdXibhJSjjJunlJ2j kJmahImUfoOSeXiVe3qWenOUeHCRc2mRc2mQcGqQcGqLc22Lc22Ic3CIc3CQcmWLbWGGZ2GHaGKJ b2SOdGmXd2qWdWmVc2ORb1+RaliOaFaRbmOUcGWMbmSLbWOOalqDX0+CXUmAXEiHXUiJX0qLZVuR a2GWdW2aeXCde3ObenKbeG+ZdW2adGqZc2mUbV2QaVqRal2XcGObdWqdd2udd2uie3Clf3CmgHKj eGeec2KbcFiSaFCLYUmNY0yQaVqOaFiRaWOXb2mdd2+dd2+bdGefeGqid2OleWWfdFyXbVWRiJKQ h5GVhpmai56bjZmekJudjpqbjZmShJCJe4eLe4CNfoOJf4aLgIeOho2Mg4uRg4yQgouMgoaLgISE eHmDd3iCdG+Ed3KIe3+ShomUh42Uh42NgouJfoeLhomIg4eMeHiIdHSGc2mEcmh+cG5+cG6DbnWE b3d/c3d+cnWAcnmGd36CdXuEeH6EdH6GdX+EeHuDd3qIeXuJen2EfX2EfX2Hf4SHf4SJgIiNhIyN houOh4yOiI6SjJKUjJ2UjJ2ZjqGZjqGXkJ+UjJuRi5SLhI2Qh5SXjpuUkZ+VkqGUlKKUlKKalaiZ lKeSjaGUjqKZkKqZkKqSi56NhpmJhpGSjpqQjpaQjpaSi5CRiY6GgouGgouMgI6Lf42OiY2Qi46U i5WVjJaXjJeXjJeWjZqZkJ2NjZCHh4mGg4eIhomMhJSQiJeRiJKQh5GUiJaZjZuhkqGdjp2djpqW iJSOiZmRjJuVjZ2Wjp6XjpaVjJSVkZedmZ+el6GVjpeRiY6Si5CUjJGXkJWekp6bkJuXkZ2XkZ2V i5GRh42Jg4CMhoOVjZCXkJKVkpaSkJSVi5GXjZSdlZqakpeXi5GOgoiGeHiHeXmDdXV+cHCEcHOD b3KGdHWGdHWIdXuMeX+RfoeUgImUgIeQfYOOe4KQfYOOgoaMf4OVf4eWgIiZhoyVgoiSg4uQgIiL eoSSgoybjJSVho2SfoCMeHqQfYadiZKilJ2jlZ6hjImVgH6LdXWMd3eSdXWSdXWLd3CNeXOMeHWL d3SQeHKOd3CMeHqJdXiOdGeGa16EaWKEaWKLbWOUdWuWenWVeXSVdW+UdG6Vb2SSbWKVb2SVb2SS cmmQb2eQa1uQa1uJZUqDX0WJYUyNZE+JZFqMZ1yUa2KddGqeem+deW6XdGiSb2OSa16UbV+UbVuQ aVeSa16bdGeXdWOXdWOddWiheWuhd3Sme3muf3eoenKjdWGXalaMYk2JX0qJX02LYU6OY1ONYlGS aFqZbl+fdWKhd2OedF6hd2GhdFuablWLiZSNjJaZiZuejqGekp6dkZ2XjpuUi5eOhpCLgoyLhJCM hpGMhoyNh42OiI6MhoyMhImJgoeQgIORgoSMfn6HeXmGfXmIf3uOgoaShomNhouNhouIg4eIg4eJ hIiGgISJfX6EeHmDd26Dd26Ac3B/cm+CcHKAb3CDbnWAa3OCbnmEcHuDd32HeoCJen2NfoCNfoaQ gIiLgIeOhIuJgoSLg4aLfX2Je3uCeHuEen6Mf4OViIyVjpWWkJaai52bjJ6ZjqGZjqGXkKGSi5uQ h5SMg5CQh5SZkJ2blKOdlaWWkaKZlKWZlqaVkqKRjp2UkZ+akqaWjqKWi5aSh5KUiJGXjJWWkJuX kZ2Ujp6SjZ2NjJSJiJCLhpaQi5uQjJWNiZKQiZWVjpqUjZmUjZmSjJeUjZmRi5GMhoyIhIuLh42O hpCRiJKSiZSRiJKSjJWVjpeakKGelKWbkaKXjZ6RjJuNiJeRjZmUkJuZkJqXjpmalJ+fmaWfkZqZ i5SVh5CVh5CXjZ6bkaKZlKeXkqaXlJ+Wkp6WkJmRi5SRhIuViI6ZkJeXjpaVjpeVjpeUi5WVjJaa kZmZkJeXjJeSh5KLgIKHfX6HeHqIeXuHeHqHeHqGdHWEc3SEeHmIe32SfYKVf4SRgoSNfoCNeoOM eYKQeoKMd36Re36UfoCXgIiZgomRgoSOf4KLeICOe4SZhJCXg46WgoSRfX+Qe36Xg4ahjJejjpqh jIyXg4OSeXqQd3iSd3eRdXWQd3iUenuRfX2RfX2VeXuUeHqWe4KVeoCNd2eDbV2CZ1+DaGGJameQ cG2WeneWenebenKaeXCZdWmZdWmVcmWXdGiZdGWUb2GRa1WUbleRbVyOalqQaVqNZ1eIZFaIZFaS Z1iZbV6Zc2ibdWqWcmGUb16UaFqUaFqLa1eNblqOcGOWeGqZd2eee2ubdGSbdGShdG+nenWsg3mr gnioe2qfc2KSaVSIX0qIXkeMYkqLYUyNY06QYlWRY1aZbliab1qdclqmemKhdFubb1aJiJKOjZeW jZqbkp+dkKabjqWVi5uOhJWNh5COiJGUjJ2UjJ2SiJmRh5eQhJCNgo2Oh4eOh4eWiIaShIKRgoeM fYKLgIeQhoyUho6Uho6Oh4yMhImGgH+JhIOMgoaMgoaLgIeGe4KGenmGenmCdXd/c3R+b3KAcnSC a3B/aW6CbXSGcHiGe32LgIKMgoaMgoaQg4eViIyRiY6Si5COiY2Ig4eGd3mDdHd7c3J/d3WQfYaX hI2Wi5SZjZaZi5SZi5SWkJuZkp6UkaGMiZmOg4yMgImOi5aUkJuZlKWalaaXkqKZlKOZl6KWlZ+U kZ+SkJ6XlaOVkqGXkZqUjZaVjJSXjpaZkp6dlqKalaiWkaWUjp+OiZqRi6KZkqqSkZmJiJCMiJGR jZaVjpqUjZmOiJSQiZWQiZCNh42JhIiMh4uOho2RiJCRiY6RiY6SiZGWjZWdl6idl6ialaaWkaKU kJaLh42Ri5GVjpWXjpmZkJqalJ2dlp+akJaUiZCXi5GXi5GakZ6flqOblqaXkqKUkJuVkZ2XkZ2U jZmSiI6Vi5GakZuelZ+ZkJqWjZeZkp6VjpqakZuelZ+ZjZmWi5aUhpGRg46OgoiMf4aMfYKMfYKJ en+Gd3uEeH6HeoCUfYSWf4eQgIOMfX+LdXqNeH2OeH2MdXqOeH2Se4CSf4OVgoaUgISQfYCOfXuL eXiMf4aUh42ZhISVgICOen2RfX+dhpChiZSdh4mahIeUf4KSfoCWfXuReHeVeHqZe36WgoSVgIOX e3uWenqWfX6WfX6Od2KEbViAY1aCZFeLaWSRb2qVeXuWen2fenmfenmbeG2ad2uXdGuXdGuZdGGV cF2RbliZdV+Vc3CUcm+Wc2iWc2iSb1qJZ1GQZEyVaVCZcmKac2OZc16WcFyRZU2NYkmGZ1WMbVuO dGeWe26bem+ZeG2edWuddGqeeHClfnenhIKnhIKqfm2memmedFeQZ0qIX0GIX0GLYkiOZUyNX1CI W0ySY0maalCeclimeV+hc16ecFyNgo2UiJSViZKViZKXiZKVh5CNhIyLgomQiZCUjZSWiZ2WiZ2W i5aNgo2Qg4mRhIuSiIyVi46UiY2SiIyVhI6SgoyRho6ViZKXh5GXh5GVh5CRg4yJgoeLg4iUgImV gouNg4mMgoiLg4OJgoKGe3+Een6Ie3+Dd3p+b3J+b3KGb3eJc3qNfoOVhouRi5GQiZCUiJGUiJGV jpWSjJKOjpGGhoiDfn17d3V4bm9/dXeQeoSXgoySh5CSh5CQiZKSjJWWjZeVjJaOi5GHg4mMgIyN go2Qi5uSjZ6Wjp+Wjp+XkKGakqOblqealaaVkZ2VkZ2WlKOZlqaVkZ2VkZ2VjJSXjpaZlqWbmaeh mayelqqajqeXjKWbkKudkayWlZqMi5COiI6UjZSVkZqSjpeOiJGOiJGOiI6MhoyOhIuQhoyQh46R iJCLh42NiZCUi5KUi5Kdl6eblqafl6eakqKXjJeSh5KRiJCUi5KVjpqWkJualaWXkqKZkpuRi5SW iZCXi5GbkJuekp6hkaOejqGZjJ6ZjJ6bkaOWjJ6XkZ2Zkp6bkaObkaOZjJ6WiZuXjpmWjZeakZ6e laKalJ2UjZaVhI6VhI6Rh4uQhomSg4iUhImVgIyOeoaNfoOOf4SRhIiRhIiRhoKLf3uNeXuLd3mL dXiMd3mLd3eOenqOe4KRfoSRfX2Sfn6JeHeEc3J/cnqIeoOXgIiWf4eUgISNen6Vf4eahIybhIme h4yUhImUhImXgoSRe36SeXiWfXuXfoKXfoKWenqUeHiUenuUenuUdWmNb2ODZViCZFeLaWSRb2qU eHOXe3eZeXedfXqaeXCaeXCaeW6aeW6adWSZdGOZd2KaeGOZeHmbenudeniZd3SZcmSUbV+Va1GW bVOVa1idc1+ec2Kec2KSaU2LYkaHYk6OaVWSdGqdfnSbeGuZdWmddWWbdGSad2ubeG2ff32lhIKo gnqngHmleWGab1eNYkeLX0WLXkiLXkiJW0SJW0SJWz+ZaU2Wa1uid2WidWShdGOSfYeWgIuRgIuS goyOgIyIeoaIe4KOgoiNhIyVjJSZjZuWi5mWiZCQg4mVgouWg4ySiI6XjZSVjJaVjJaSh5KUiJSV h5KVh5KSh5KRhpGOhpCOhpCVhI6Ug42Vh5CZi5SRiZmSi5qLiZSIh5GIgIaHf4SLfoSAdHp9cHR/ c3eIc3qRe4ORg4yWiJGXjJWViZKXiZKXiZKRjZaRjZaVjpWOiI6JhIZ/ent6cHR+dHiVeoOaf4iU ho6Rg4yMhoyMhoyNhI6JgIuHf4KGfoCJgoSOh4mQiZKUjZaViZeWi5mUjp6ZlKOelqedlaaVkKGV kKGWkKeXkaiWlKKWlKKalJ2dlp+mnaqlm6ijl6ahlaObjqKbjqKhlKqhlKqalZmRjJCNhIyUi5KQ jpaRkJeRjZSOi5GMhoyOiI6UhImUhImQh5GQh5GNiZCLh42QiZCQiZCUkaGXlaWfkqWajZ+ajJqV h5WNhIyRiJCUiJSViZWXkJ+Wjp6UjZSNh42Oh4yUjJGajpeekpujjp+hjJ2aiZmZiJeZjp+XjZ6Z jp+akKGbjqKZjJ+VjJaRiJKSh5KViZWXkZ2blaGZkJqSiZSShoyRhIuVho2ZiZGZjJKZjJKZhJCU f4uShISRg4OViIyViIyUiYuOhIaOeYCJdHuHcneLdXqMd3uSfYKRfoSRfoSWf4SRen+Ld3mGcnR9 aWuHc3WNeoCVgoiVf4KQen2Qen2Re36WhIaaiImXhI2VgouXgn+SfXqReXCVfXSagn2WfnmQdW6O dG2QdHeSd3mQdG+Ncm2JaliIaVeJa2KQcmiXeW+ZenCad26beG+XeWuXeWuhfXKifnObfXCZem6d e2+bem6be3mff32ifXibd3KZcmSXcGOSa1qVblyRb12aeGWfemueeWqXcl2NaFSJZVORbVqXd26e fXSdd2uadGmWc2ead2qad2uWc2ideXujf4KmfnqogH2ne2qdcmGba0+XaEySYk+LW0iHXEGHXEGH XUaSaFCWcGWbdWqic2Web2KNeoCJd32GdX+GdX+EeH6AdHqEcniLeH6IfYaNgouUi5WVjJaXiJCU hIyQgIiUhIyUi5WakZubkJuWi5aSh5KSh5KSh5WRhpSNh5CLhI2LgoyOhpCQh46Qh46SjJWWkJmW jaWWjaWRiZqNhpaIg4eDfoKAd3p+dHiJdXWEcHCHen6NgISVho2XiJCXjJWUiJGUi5KSiZGQiZWU jZmZjZmXjJeXh5aLeomDb2+GcnKQfYOWg4mXiZKXiZKNh5CMho6Gg4d/fYCDg4OHh4eMh4uRjJCW kJmVjpeSjJeRi5aVkJ+dl6eblKWdlaadlaadlaaal6eZlqaZmaeZmaefl6eblKOimqqlnaymnaqh l6WXkqOZlKWalaiZlKeVkpaQjZGOh4mNhoiRi5aWkJuSjpqNiZWMg5COhpKQg4mOgoiOhpCQh5GR i5GNh42Nh5COiJGQiJeQiJeVjZ2Si5qUi5WSiZSQg4mViI6Uh42ShoyVjJaXjpmUiZCLgIeRhIaV iImWjJKdkpmllqKekJuSiZSSiZSUkJuRjZmZi5aekJuikZ6hkJ2UjIyQiIiOho2RiJCWjZeelZ+i lJ2djpeRhIaShoeXiZKXiZKZi5SZi5SXiJCUhIyXhI2Zho6XiZKbjZaZi5SShI2Jd3+LeICIdXuL eH6QeoKWgIiWg4ySf4iXgIiReoKQeHOJcm1+amOHc2uIeXuNfoCWf4SVfoOQenqRe3uRgoSVhoiX iIuVhoibf3+UeHiSenSWfniWg3uSf3iSeW6JcGWIbW2Jbm6OdG+Nc26NbWGMa1+QcGqVdW+WenOU eHCbeG2ZdWqUeWuXfW+ff3mjg32ffXiee3ehe3ehe3ehfnuif32he22bd2iXbluacF2Wb2KVbmGU c2iaeW6dfnCdfnCdeWOVclyObmKScmWZeG2efXKZd2SUcl+RbmOXdGmec2ehdWmfdXOieHWienem fnqleWifdGOjblOfak+ZZ1GSYUyQYUeNXkWJX0qNY06Xc2SXc2Sdc1+WbVqJd3+Abnd9a217amt7 bm57bm5/a26GcnSIeXuRgoSOi5aOi5aNhouMhImRhIiViIyUjZaWkJmdkJaViI6RhIuRhIuOgImQ gouJf4aIfoSEfoSJg4mHg4mMiI6Mi5WQjpmWjp+blKWXjpaMg4uJen2Gd3mCdHKDdXOAc3CCdHKG e3+MgoaRhIiWiY2bjJSWh46Rho6Og4yQiZKSjJWdkqWbkaOVi5+IfpKEd3SLfXqMhImVjZKfjp6e jZ2OhpKHfouHfYCEen6JiYyOjpGbkJ6flKKZlKWVkKGSh5KSh5KVjZ2akqKZlqWZlqWblKOdlaWb maeal6adma+ZlauWlqeWlqedl6ifmqufl6ifl6iXlaOSkJ6Wjp6Wjp6SjpWMiI6MhIeLg4aOi5SN iZKRjp2OjJqXjJeUiJSOgoiQg4mJg4mMhoyMhoyMhoyIf4eLgomNgouNgouOg46RhpGRiY6RiY6U hImSg4iUhImUhImQiI2UjJGWh4yOf4SOgoOViImWjJCdkpaimaGakZmRho6Og4ySiZSRiJKWi5aZ jZmmlaKfjpuVi4yRh4iJgIuOhpCRhpSbkJ6ekpudkZqViIyWiY2XjJeWi5aXiZKWiJGah42XhIuV gI6ahpSWi5aWi5afjJWXhI2QfYOMeX+HdHqHdHqOeH+Se4OWgIiWgIiSfYKMd3uLd22IdGqDb2WI dGqIdHSNeXmOeX6Qen+Oen2Qe36UfoaahIyhh4aehIObf3qUeHOVeneVeneNf3qNf3qUeXKEamOD aGGEaWKEaWKGamOIbmONc2iSeHSWe3iZfXmWeneWd3COb2mUcm2denWbgHmeg3uef3ObfXCfeHSi enejfnmlf3qlemSbclyRZ1GValWXbWGbcGSaeW6ffnOhf3SigHWffWqaeGWXeWuVd2mWe3Caf3SX d1uLak+JZVeQa12ZcmKZcmKUblqadF+ddWiheWuld1+idF2icleeblSda1aZaFOUZE2NXkeIXEaO YkyRb1+XdWWedFqXblSLdHuGb3eCamV/aGN7b2V5bWN5bmp/dHCHeHqQgIORi5aRi5aNh42Nh42Z hoyah42XjJeXjJebjpKViIyRgoeQgIaNgIKMf4CHfn2DenmHfYOIfoSMhIeRiYyQiZKVjpebjqGe kaOZjpWRh42OfXuJeHeLeXiJeHd+dXJ/d3ODeneJgH2RgoSVhoiai5CXiI2SiIyUiY2SjpeWkpub lqealaaRjJuJhJSJen+Of4SRi5aalJ+ekaObjqGVho2Le4ODfn2Mh4aSjpqZlaGel6+fmbCelqqa kqaUiJGRho6SjJeZkp6ZkaWZkaWdlKGdlKGal6abmaeem66XlaeWlqWZmaebmaifnayem66ZlqiW kpuQjJWXiZKXiZKVjpWQiZCMhImMhImRh42UiZCSjpqSjpqXkZ2UjZmQi46Mh4uOgoiJfYOGeoOH e4SJfYCNgISRgoeNfoOOf4eUhIyRg4yShI2XhIuWg4mZhoyVgoiSiI6Rh42Vho2RgomShomXi46W jpSakpelmaWekp6Sh5CNgouOg4yRho6Sh5WWi5mdkZqflJ2dkJSXi46ShoyRhIuOg5GUiJaZi5ae kJuUiJGUiJGZjZmWi5aVi46UiY2ai5CWh4yRgomVho2Vh5KVh5KbjJSZiZGVg4SLeXqIdXmHdHiN eHiOeXmSfoCVgIOUenmOdXSGcmqGcmqJcG+LcnCMd3mQen2Wen+UeH2SeX2Uen6SfYSZg4ufhICd gn6aenSSc22OdG+UeXSQf3iSgnqWe3SGa2R/YVuAYlyCY12EZV+JameRcm6WeXuZe36ZeXWXeHSW d3OQcG2Xc26deHOihoKfg3+igHibenKaeHObeXSifXilf3qfeGWVblySaVSOZVCQal+WcGWZd3Kh fnmjgHujgHuffWqaeGWWeWWXemede3OigHihemOSbVaRYVCQX0+OaFuOaFuQZ0+WbVWXc1+eeWWh dWSid2Wjd1ueclaablOXa1CUZEqVZUyRYUyOXkmSZ16bb2edc1iacFaLdHmEbnOCbWqHcm+Cd3V+ c3J+b3SEdXqJe3uRg4OOhpCOhpCJhpGNiZWXiZKXiZKZjZmZjZmXjZGUiY2QfnqUgn6Lg4OJgoKJ gH+Ee3qHfYCIfoKOhIiRh4uUi5eVjJmZkaGZkaGXkZqSjJWJgoKJgoKMhImIgIaHenuEeHmEeXOR hn+Vh4KXiYSbiJGdiZKViZWXjJeXkqKblqaZmaeWlqWUkpeNjJGOh4ySi5CWkKeblayelqaWjp6S hISMfn6Lh42VkZefmqqemaidma+fm7KhlqibkaORiJCQh46NiZWQjJeWjJ2XjZ6bkp+dlKGal6af nauhm6yblqeXkqKWkaGbmaiem6uem6qbmaeZkZaSi5CWiY2ViIyWkJmSjJWOho2Oho2RhpGUiJSW kJaZkpmXkqaXkqaWkp6Oi5aah5CSf4iGeX2Ie3+Le4COf4SOgoaNgISQg4eQg4eWhpKWhpKWh4yZ iY6Xi46Uh4uSi5CQiI2Zho6Zho6UiJSXjJeWjp6ZkaGhl5+elZ2XkJKSi42OhIiNg4eNhI6RiJKU jJudlaWdkpmXjZSXiI2Sg4iNf4uUhpGWh5mai52SiZSVjJaZkZaWjpSZjpCVi4yXjZGSiIyNgoCL f36RgomUhIyai5KbjJSViImOgoONe32Id3iIc3iMd3uWf4SWf4SVfXSSenKHc2uIdG2JdHSMd3eN eHqRe36WeH+SdHuMc3KMc3KNe3qVg4KbgoabgoaWenWOc26RdXWVeXmRfXeWgnubgHWRd2uJaFiA X1B6XEp/YU+DaGOOc26VeHibfn6Vem+Vem+adXCUb2qScmmXd26bf3ufg3+hf3SXd2uXeW2Vd2qa eW6de3Cfe2+beGuVdWGRcl2QcFyQcFybd3She3mjf3Sjf3SeeG6Zc2mZdWqdeW6he22mgHKhe2iW cl6UYliOXVSIXk6HXU2QY02SZU+Sa1qfeGWle3Kof3WnfnSieW+eclSVaUyRY0SUZUaSZVGRZFCR ZFCWaVWXbVqXbVqEd3SGeHWMfYKOf4SLhIuJg4mQgouLfYaOe4KSf4aRg46UhpGQhJCQhJCQhoyR h42ViZeUiJaSjJWRi5SShomUh4uQi4yOiYuOiYuNiImNg4eLgISRh42UiZCUi5eakZ6XlJ+alqKf lKKajp2VjpWWkJaVjJSNhIyMgoaJf4OMf4OWiY2Vi5GZjpWbjZaajJWZkJqZkJqZkJqdlJ6alaaa laaZlaGVkZ2XkJ+XkJ+WkaGblqadlp2SjJKLgn6Lgn6Vi52flaelnbCimq6fmbCfmbCdlqKXkZ2O ho2Qh46NiJmNiJmZiJeaiZmZjZmbkJuVlJ6enaeimqqdlaWWkp6Wkp6alqKdmaWdm6aenaebkp2W jZeWjZWRiJCZlJeVkJSQjJWOi5SUjZmVjpqXlJ+bl6Oblqeblqebl6GXlJ2djZCVhoiJen2HeHqJ fYOMf4aOhIiNg4eShoeShoeZho6Wg4yViIybjpKWjZWUi5KUiJGUiJGXiJCZiZGZkJqXjpmXkZ2S jJeWkJmblZ6ZjZaWi5SQhoyOhIuLgoyMg42ViZeXjJqdjJmfjpuZi5aVh5KOeYCQeoKQgouShI2Q h46RiJCWjJKdkpmfkZqdjpedjZWZiZGUhIeIeXuQen+Re4CWg4mZhoySgoyQf4mOe3+MeX2MeHiN eXmRe3mWgH6Xf3eVfXSSeHOSeHORe3mUfnuUfoCUfoCZe4COcneAbmF/bV+Lc26Ue3eXgoKXgoKU eXKSeHCVe32SeXqVeHiZe3uhf3ebenKWb1+LZFWAXEaCXUd/ZViMcmSSd3mZfX+Uem+Qd2uVcmeS b2SUcHOZdXiXfXWbgHmjf3SdeW6bd3SXc3CZem6dfnKlf3qmgHujg3+efnqVdGuVdGuac3Kienmi eW+jenCecmWWal6UbV+ZcmSab2OhdWmfeW6adGmValeLYU6DWD6CVz2HWEGQYUmRZ1SfdGGnf3uq gn6nfnSnfnSjeFaWa0qRYkiUZEqVZE+WZVCZaVGXaFCVZFGVZFGJfYCQg4eUh42Uh42Oi5GNiZCU iZCNg4mQfYCQfYCNf4iOgImQf4mOfoiOeYOQeoSOg5GRhpSQjJeMiJSUiZCZjpWXjpaVjJSVkI6Q i4mOiYuOiYuUiJGUiJGXiZWdjpqekaOfkqWjlKajlKadlaWblKOZkaGUjJuQiZKNh5CSjJeXkZ2a kZuflqGbkpqakZmXkJ+XkJ+akZudlJ6dlqKhmqahlaOekqGajJqajJqZkp6blaGZlJeOiY2Mg4KM g4KXiJufkKOll6uhlKeelaKhl6WalJ2XkZqWi5aRhpGRi5SOiJGWi5aXjJeVjJadlJ6ZlaGfm6ee maqXkqOUjJuUjJuXkqKdl6eemayhm6+hmayblKeWkaGVkJ+dmaKZlZ6SkZuSkZuWkp6VkZ2dlKGi maafmqqdl6ebmqWVlJ6akZuVjJaOhIiHfYCHeoCJfYOQg4mRhIuUhImWh4yXiI2VhouWiZCdkJaX jZGVi46XiJCWh46Xh5Sbi5eWjJ2XjZ6Wi5SSh5CUjp6WkaGZjZaWi5SVhouRgoeNfoOOf4SRgIuZ iJKeiZeeiZeSh5CNgouJd32LeH6LeH6NeoCNfoOOf4SVho2bjJSbjZmdjpqfiJCbhIyagIKReHmN eXeQe3mWgIiWgIiReoSUfYeUfoOMd3uNeHiRe3uSfneVgHmXg32ZhH6Zg4CZg4Cah4uXhIiXg4aW goSVeHiOcnKCbmR7aF6HbWmQdXKVe32Ve32We3eXfXiXfoKUen6ZfXmafnqfgHeae3KZdGGRbVqN Z1WNZ1WGaF6Nb2WRdHSXenqSeW6Mc2iOb12La1qRc2eZem6af3Seg3ilgHihfXSddXKddXKZenCh gnijfnuog4CnhIOmg4Kienmed3Wfc26jd3KleW2ne2+fcl+VaFaNZE+RaFOWaFuaa16ddWiZcmSZ b1yUaleMZEGGXjyEVzmLXT6QZFSfc2KjfXKngHWqfWuoe2qieFibclOaaVGWZU6aaU2aaU2ZZ1GZ Z1GXYUmUXUaUho6WiJGUiJSXjJeZkJqVjJaQjJKMiI6NgISIe3+HeoCIe4KIeYCHeH+IdXmLeHuR gomVho2ViZeQhJKZi5afkZ2bkJ6XjJqXkJWVjZKSiI6Vi5GVjJaUi5Wdjp2fkZ+dkqWil6qil6ij maqfl6iblKWalaaVkKGWjp6Wjp6ZlKOalaWfmaWhmqaXlqGVlJ6XkZ2UjZmZjJ6dkKKimqqhmaij l6OdkZ2diJSdiJSUi5eWjZqajJWShI2Ng4eQhomSh5KZjZmilqWbkJ6alJ2ZkpuWkJuXkZ2XjJqS h5WRi5SQiZKUi5WUi5WSiZSakZualaWfmqqdkqWZjqGWh5mXiJqXkZ2dlqKlmq+lmq+hmaydlaiX l6iZmaqjm6ufl6eblaGZkp6VkJ+Ujp6blKWfl6ibmaiWlKOUkaGUkaGXlaOVkqGViZKMgImGeX+H eoCUfoiZg42Vh5CWiJGWiY2WiY2OjJCSkJSWjJKWjJKai5KWh46XiZKdjpeZkJ2Ui5eRiY6QiI2W jJ2Zjp+bjZaWiJGWiIiRg4ONf32Nf32MfYSUhIyZiJKXh5GShoeMf4CJdXiHc3WJdHmMd3uHeHqE dXiOeYOVf4mUiY2ZjpKih42bgIeUfn6QenqNe32MenuOeH2QeX6SeX2Qd3qMeHiMeHiQe3WUf3mW hICaiISbhoueiI2ajZGajZGejZeaiZSdh46ahIyZfneOdG2CaGF9Y1yEaWSNcm2Sd3eVeXmff32h gH6WgIOVf4KVenWUeXSae3KZenCVdWWSc2OUc2qRcGiOb2mUdG6WeG6WeG6ScmWObmKNa1qMaliQ bWKZdWqifXungoCmgoSifoCdeHOdeHOZeXeefnuifoCqhoiui4mnhIOqe3Old26idWmjd2qieW+j enCedF6WbVeQZEmNYkeUZ1OZa1eecmOecmOec1+bcF2XbUyQZUWLYkSJYUOMZ1yadGmmfm6mfm6o d2OndWKec1Gab06da0+XZ0qdaE2ibVGea1adalWZY0aXYkWajJeWiJSXiZebjZuWjqKWjqKSkZmM i5KNgIeJfYOGcnKDb2+AbnKCb3OCdHSEd3eJen+MfYKMgIyJfomRiJKZkJqdjp2hkqGXjpuWjZqW iZCViI6QiZKRi5SUjJ2Wjp+akqaelqqbmaial6eelqedlaaZlqiXlaealaaalaahmqafmaWblKWV jZ6LiZGJiJCRho6UiJGWjZqakZ6al6eVkqKVkZ2Oi5aLg4aRiYyRi5aVjpqajp2UiJaIhomIhomO h4mUjI6XkZeSjJKSkJ6VkqGekJ6djp2XkZ2UjZmQiZCOiI6OhpCRiJKQiZWUjZmZjqGflaeblKOb lKOXiZKWiJGVjJmZkJ2fl6ihmaqemayalailmqynna+emayfmq6jnaialJ+Ujp6RjJuVkKGUjp+S kJ+Rjp6SjJeWkJuZlqaXlaWVjJaIf4mMd36LdX2Of4eVho2Xh5Sbi5edhpKfiJWNi46OjJCbi5Wb i5WViI6RhIuViZKXjJWWjZWUi5KOhIuOhIuSh5CSh5CViI6RhIuUh4iShoeUh4uShomMe4aMe4aN f4iNf4iVgICVgICSfnuJdXOJbnCJbnCJdHSIc3OJdXWXg4ORiYmUjIyaiImSgIKQe3uQe3uSf4OU gISRgoSQgIOQe3WMeHKHcm+NeHWNeHWUfnuWh4yejpSdjZWdjZWejZehkJqbkJuZjZmhi5Cbhoua f3iQdW6Ja1+HaV2IbWmNcm6aeHObeXShf4CjgoObgoaXfoKSd2+NcmqRdXCVeXSQdWiQdWiWd3OZ eXWafnmZfXiVe22Qd2iVc2ORb1+NaVaIZFGJZFqOaV6XeYChgomnhI2lgouaeXCScmmScG6XdXOh gH6oiIavi4KwjIOofmWfdV2fdV+jeWOlgHSlgHSde2SVdF2UakyLYkSUZ1WbblyfdWKfdWKadFuX clidc1iXblSRbVGQa1CLa1qUdGKid2WleWiodV+ndF6iclCaakmZaEybak6balOda1SdZ1OeaFSa Z0iRXkCdiJaZhJKSh5KWi5aZjqOXjaKUjZmSjJeQhoyHfYOIc3CCbWqCbmiDb2l+cnODd3iId3iJ eHmIeX6Le4COhpCSiZSXjZ6bkaKXjpuVjJmViIyOgoaLgIeQhoyNiJeRjJuZkaGblKOXlaOWlKKa lJ+dlqKelqaelqaflaehlqijmqehl6WejZeWhpCNhIOLgoCLfoKRhIiNiZKSjpeZkJeZkJeUi5KQ h46MgoiRh42Qh5GXjpmakZuWjZeUjpKNiIyMhISQiIiVjJaUi5WUjp+Ujp+WkJuXkZ2VkJ+RjJuN i46MiY2Ng4mQhoyHg4mIhIuSiZabkp+WlKKWlKKZi5SZi5SbjZubjZudkqWelKablKWakqOilaej lqialaaemaqhmqablaGZkJeXjpaXjpaVjJSRjZmMiJSSiZSUi5WZlKOXkqKZkJeOho2JfX6Ie32Q gIaOf4SWgo2bh5KVh5KVh5KRho6XjJWdkJaajZSVg4SQfn+Vh5CZi5SWjpSQiI2OgoOOgoORhIiS homWg4eUgISNg4eUiY2WjZeRiJKQgouLfYaMfYSOf4eWhIaWhIaWgn+RfXqJdW+IdG6Hcm+Ic3CI d3OUgn6XiIuai42Zh4aVg4KWgH6Xgn+Zi4uajIyZjI2RhIaQfXeHdG6Jcm2MdG+LdXiXgoSbi5Wf jpmfkJWfkJWbjZaekJmbjpWWiZCXg4aVgIOVfXSOd26Qb2SLal+NcHONcHORd3OXfXmZe4Cfgoeh g4uafYSUdWuOcGeVdXKWd3OWd3CZeXOafX2hg4Ojh4KhhH+bgnSWfW+beWmZd2eWdVyMa1OMY1CL Yk+OeH2dhoumiY6fg4idfWqSc2GQblyScF6Xe3Sihn6nh4Cjg32ie2KbdVydd2KhemWjgnmhf3eh f2WaeV+acleQaE6VaVidcF+edF6dc12acFSbclWbbleecFqdc12bclyZc1qZc1qjeWWlemeoemWn eWShcFaaalCXaEyWZ0qZaFWZaFWbZE+XYUyXZEOUYT+Wgo2Xg46WhpWdjJuakKGXjZ6akKGWjJ2U h4iOgoOHdXd/bm+CbWqGcG6Gc3eIdXmHdXKGdHCCdHSGeHiNfoaVho2XjJqajp2bjJ6XiJqVg4SS gIKLfX2Rg4OQhoySiI6SjJeXkZ2XkZqZkpudlJuelZ2hlKafkqWhmaihmaiimaaelaKfjJKVgoiR e36OeXuOgH6Rg4CQiZCRi5GXi4yZjI2QhomQhomQhI2Rho6Vh5KdjpqdkZ2ZjZmajpqSh5KQhoeS iImSjZGRjJCXi52ZjJ6VjJaWjZeSjZ2RjJuNjJSJiJCNgIKMf4CIgn+LhIKLhJCSjJeSjZ2Ujp6W i5aViZWWi5aWi5abjqGbjqGakZuakZubkJubkJudkqOhlqeblqaalaWXlJ2WkpuWjJKRh42Oho2N hIyOg4ySh5CfkZqekJmfkJKUhIeMgoaMgoaOgoOLfn+UfoiXgoyQhI2Rho6OhpCWjZeakJSWjJCU goCQfn2RhIaViImWjYyVjIuMg3+JgH2JfX6Mf4CQfn+Qfn+QhI2ZjZaZjZmUiJSRho6MgImMe4aO foiQgIiWh46Zh4iXhoeOen2MeHqJdW+Hc22IeXuSg4aZhoyah42XhoSVg4KVf4SZg4iZiY6hkZaa jpeViZKWg3uLeHCId2KGdF+Hc3OZhISfjpmhkJqdjZCejpGdjZWdjZWfiYmahISWe3CQdWqUeXKN c2uJbmeIbWWLbm6Qc3OQdW6SeHCVeX6dgIaaf4aaf4aVd2qQcmWRc2mVd22Xe3iXe3iafnqhhICl iYSjiIOjhHefgHOff22aemiXemmOcmGMa1CLak+Oc3Wbf4KliIihhISjgG6Vc2GMaVSLaFOQcG2e fnqhhH+dgHueemSXdF6bc2mle3Kjg4Cign+lf2ufemeedFyXblabcF+ec2Kid16hdV2ablCeclSf c1efc1eec12ec12id2Oid2OleWGmemKqfmWleWGnd1yeblSXaUiWaEebaVSea1afaUyaZEeZYkGU XT2UfoiRe4aWfoudhJGUiJGUiJGZkJ2Ui5eRhIuQg4mJen+DdHmEcniJd32Jd3qEcnWJdXiMeHqJ d3qJd3qOe3+Wg4eWi5SZjZadjJmaiZaVhouQgIaQgIaUhImQgIaSg4iQh46SiZGajpeekpuilqKj l6Olnayfl6efmquemaqblqablqabjpKRhIiMe3SLenOMgHqRhn+Rh4uUiY2ViIyXi46WjJKSiI6W iZCXi5GakZuelZ+flqObkp+WjZqRiJWZjpWZjpWUi5KRiJCVh5KVh5KNhJGRiJWVi5uVi5uQiZWO iJSSg4aOf4KJen2NfoCGfYSIf4eUhpSWiJaVi5uQhpaSh5WUiJaUiJaViZeajJWdjpeajpqdkZ2Z kaKZkaKZkaGblKOZlqWXlaOXkZqOiJGIf4mMg42QhoySiI6XjJeZjZmXjJWSh5CQh46Qh46QgIaQ gIaRhIuShoyOhIuQhoyOg46XjJeakJGXjY6Vg4KSgH+NgISQg4eWiYuWiYuOg3+MgH2Jen2IeXuM fn6Mfn6Ngo2Wi5abkJuXjJeWiZCOgoiMfYKQgIaRgIubi5WekZeajZSUf4KNeXuJdW+Qe3WQgIaZ iY6XiIuVhoiRgoeRgoeVgoubiJGbi5WhkJqhkJqdjJabh4SSfnuQe3uGcnKLdXOXgn+ijJaljpmd iZCZhoyahImahImhhoCfhH+deW2ad2qSenSQeHKMeXKEcmqLb2qSd3KJcGWHbmONcHCSdXWVeXub f4KWd3CSc22QdG2VeXKaeniaenibf3uhhICfi4Sfi4SihoCfg36hgH2hgH2bfXCVd2qUdV6Rc1yO b2mZeXOhhICihoKlf26VcF+OaVONaFGQbWKdeW6jfnmifXied2Sac2GadGqfeW+mgHumgHujf3Sj f3ShdWSfdGOfeGiheWmmemejeGSbb1Sbb1Seclaeclafcluhc1yfcl+hc2GjdWGqe2enf22iemim dVuhcFafbVehbliid2Oid2OjcleaaU+ZYkSSXD6Of4eMfYSUfYSZgomQhomSiIyWi5aUiJSUiJSO g46MfX+EdXiDd3qMf4OHfYCDeX2NeoCQfYOJen+QgIaOgoaViIyVjZKWjpSdjZ+djZ+bi5ebi5eR hIuRhIuUgImRfoeNgouSh5CekqGilqWhmaqimqufmq6dl6uakqKblKObl6OXlJ+UkZWMiY2MhImN houOgoOQg4SSiImWjI2ajZSajZSZjZaZjZaXjJebkJublqaemaidmaWXlJ+ZkaGZkaGXlaOal6aa kZmRiJCNhouNhouQg4mRhIuQhI2Sh5CWhpCUg42OgoiOgoiIeXuHeHqGeXqHenuNgouOg4yRho6N gouOhpKRiJWUjZmVjpqZjZmajpqhkp6ilJ+XjpuWjZqXi52ajZ+akqKdlaWblaGUjZmNhI6NhI6U h42WiZCWi5aZjZmVjJSRiJCQh46Oho2Uh4uRhIiQiI2QiI2Ng4mNg4mRg4yXiZKfkpSXi4yZhISZ hISOgoaMf4OOhIuRh42Qg4SQg4SNeXmMeHiQe3mQe3mNfoaWh46bkp+WjZqRhIuNgIeQfYCUgISa hIyeiJCilZmdkJSXhoSNe3qNcm6WeneVgoibiI6ZiY6UhImRg4ONf3+RgoSZiYydjJmfjpujlJua i5KZh4iSgIKQeX6OeH2Re4OZg4udjZWdjZWdho2Se4OVf3+WgICWgnuZhH6deniZd3SUenmVe3qR emuQeWqRd2uSeG2Jc2eHcGSGamOJbmeUdG6ZeXOWdWqUc2iQdWqVem+eeXSdeHOae3KhgnidiYOd iYOfh36ehn2ignuignuef3Kef3KZeXOZeXOaeniefnufg36dgHunf22bdGKUaViOZFSSamSbc22d eHWdeHWadWKXc1+XcmibdWumfXSle3OieXCjenKhdGWjd2ileW2ofXCsgGileWGfblufbluhcFih cFieblaeblaZa1qhc2Gjd2WnemmqfWumeWijdWGhc16jeGmqfm+og3KrhnSreWWhb1yhaUqXYUON foONfoONen6QfYCMf4COgoOVgIyXg46Vh5CQgouQfYCNen6MfoeRg4yRhIuQg4mOhIuQhoyNgIKR hIaQi46SjZGZjZmajpqXjZ6WjJ2bjJ6djZ+QhoyMgoiSg4iQgIaQhI2ViZKdkKOhlKehlquhlquh lKqdkKaajJebjZmbkpqZkJeVkJSQi46RiJWQh5SOiY2Qi46UjI6XkJKel6Oel6Obkp+akZ6XkZqZ kpuhmaiimqqblqaXkqKdlKGlm6ienqyenqyhkp6Rg46NgIeRhIuSf4OWg4eVhouVhouWh46UhIyL f4iJfoeGenmJfn2MeX2Nen6LgISJf4ONfoORgoeNgouRho6UjZaVjpeZjZmbkJuelZ+hl6Kilp+a jpefjpubi5eZjZmhlaGelKWakKGSiI6UiZCXi5GajZSZi5SZi5SbjJSZiZGai5KWh46WjJCVi46S iZGQh46ShomShomUho6XiZKfkpaajZGdiY2ei46Wg4eQfYCNgIeRhIuOgoaOgoaQgn+Qgn+Vf3+X goKUfoOZg4iekJuajJeUgIeNeoCSe4CXgIaZhomhjZGil56il56fiYmXgoKWd3CWd3CXhIuei5Ga i5CSg4iSfX2SfX2RfX+diIubiZ2di56ijpWah42af4aXfYORe4aOeYOOgoaViIyZiY6ZiY6dg4SS eXqVeXWVeXWMd3eSfX2ZfXmZfXmVgICahoaZgnWVfnKXeHSWd3OSdGiOcGSHaVyEZ1qObmOUc2iV d2qWeGuVeXKVeXKbem+bem+SeW6XfnOdgn6fhIClhH6lhH6lg3qjgnmig3mig3mjg4Cjg4ChhISe goKegn2afnmlfnSfeW+WcGWOaV6QYleVZ1yVb2iXcmqWcFyWcFyUb1yXc1+ec2Kec2KfdGWdcmOe c2SleWqlfW+ogHOrgG2ofmqjcl6ebVqec1udclqfak+aZUqSZVGhc16meHCneXKqfWunemmjdV6e cFqid2qne2+nfnWvhn2vg2+ofWmncFOdZ0mNfoaNfoaMf4OLfoKNeoCOe4KQe4eVgIyVgouWg4yQ g4eQg4eQh5SRiJWVh5CWiJGQi4yOiYuQhomQhomOho2SiZGZkp6Zkp6ajpqWi5aZjZuXjJqSjJKR i5GSg4iOf4SSiI6SiI6ViJqbjqGikKWikKWfkZ+XiZeViIyWiY2UjI6VjZCQjJKRjZSajJeajJeU jJGUjJGbkZejmZ+dmqyal6qVkZ2RjZmVjpealJ2lmqyflaeakZ6XjpuVlJufnqahnq6fnaydjpeN f4iOf4eRgomIfn+Ng4SQhoyRh42Zgo6Zgo6Rh42QhoyOg4KQhIONfoOQgIaSf4iOe4SGe3+Een6L fYaRg4yRi5aWkJuVjJmakZ6Xl6WZmaaimaOhl6KllqKjlaGbkJuflJ+ZlKWXkqOWjZeXjpmajpqd kZ2WkJmVjpebkp2dlJ6ekpuXjJWWjpSWjpSVkJSRjJCViIyRhIiQiI2WjpSblZ6fmaKhkJqbi5WV goaOe3+Ne32Vg4SOhIaMgoORhIaViImag4idhoubhoibhoidkJaekZebh4mNeXuOeX6Xgoeah4uj kJShlp2hlp2jiY2hh4uafn6Xe3uah42ei5GWiYuOgoOHcnSIc3WReoKag4uZi5SZi5Sah4uUgISX foKSeX2MdX2Nd36Qfn+Rf4CWhIaaiImZhn6VgnqUeHORdXCQc3OWeXmWfYCXfoKZg4iZg4ieg3ua f3ibeXSaeHOOd2KLc16MaFWJZVONbl6UdGSZdWmdeW2ZeXOWd3CZeGuZeGuRd2uVem+We3ebgHuf f3mignuign6ff3uihoKjh4OniYmmiIihhoCeg36dgHuafnmhenOjfXWad2uQbWKQZ0+OZU6QZFSW alqab1eZblaZb1ybcl6adF+Zc16ec1+ZblubcFufdF6bdWqlfnOnf3Knf3Kme2ifdWKhc16fcl2d a0+ZaEyUZ1Wfcl+leHOmeXSofWmid2OicFafblSdcmGne2qqgHqvhn+zg3iufnOneFeic1OOg46M gIyQgIaNfoOMeX+MeX+Me4aRgIuSg4uWh46Rh42Rh42SiZSVjJaViZeViZeSkJSQjZGRjI2OiYuQ i4yUjpCZkpualJ2ZjZaXjJWWjZeWjZeViZWSh5KNgouNgouai5CdjZKajJWbjZabjZubjZuajZSS hoyUh4uViIyXjZSWjJKSjpqVkZ2fkZqfkZqakJaakJahl5+mnaWhmaqZkaKWjpSSi5CVjpedlp+l laeikqWakJSWjJCXlZmhnqKloayemqaekZWViIyQgo2Rg46LhomNiIyQh5GQh5GVh5KWiJSWi5SZ jZaSiZGQh46WiJGXiZKQgo2LfYiIfoKGe3+LfoKNgISQh5SRiJWWjZebkp2VlaKZmaaflaaflaam mqijl6ajlaGilJ+ZkaGXkJ+ZjqGbkaOdkKaekaebkp2ZkJqakqKelqaakqKWjp6WkJaUjZSajY6Z jI2UhoaOgICRh42WjJKdlKGimaaelKWXjZ6ah42RfoSOen2WgoSOhoSOhoSWh4ybjJGfi5abh5Kd iZCbiI6bkZedkpmbiYuSgIKIfoKQhomajZSekZejlpqll5umkJCijIyXgoSSfX+Zg4udh46biYuO fX6IbmqDaWWLbniWeYOWh4yai5CZhIeRfX+WfX6WfX6ReHmReHmReHeUenmWgICeiIidiIidiIia gIKVe32UeXWSeHSWenqdgICehIafhoehh4aehIOfg3ubf3iVe2mQd2SLalSIaFGOcGOSdGebenKd e3ObfXOXeW+WeG6Vd22QbmuScG6Sc22XeHKZenCZenCXe3ibf3uihoijh4mmiYyni42niH6jhHqf hHWZfm+de3Chf3Sfe2+XdGiXbVWRZ0+OZFGQZVOXbliacFubd2Wjfm2iemiheWeic2Web2KacF2b cl6ZcGefd22jf3OlgHSnf3KlfW+meGGidF2bbleXalSWalqabl2eeG2ie3CoemOfclufblSebVOh b1yod2Osf3qwg36uf3esfnWmdVijc1aQh5GQh5GShoyQg4mRfX+Qe36RhIiXi46ajJWajJWaiZmb i5qWjZeVjJaUi5WWjZeXkZqXkZqVi46SiIyUiJGbkJmfkqWdkKKZjZmXjJeajJWbjZaSiZaSiZaU iJaajp2ekZeekZebjJGai5CSjJWUjZaQiI2MhImUiY2XjZGZjZmXjJeXkKGblKWflKKekqGakJae lJqjmqemnaqilqKZjZmUiY2Ng4eQiZKalJ2hkpudjpeWjJKZjpWel6GnoaqloaqhnaadkpmXjZSR iJCNhIyMiI6Oi5GSiZaSiZaSjZ2WkaGhlKafkqWWkaGSjZ2Wi5aWi5aUiY2QhomOgoaMf4OJen2J en2JfomMgIyRjJCVkJSdlqKel6OdlaWdlaWil6qlmqylmaeekqGdjZ+bjJ6ZjqOelKiflKKflKKd lJudlJuWkaGblqadlqKalJ+bkpqakZmei46fjJCahoaUf3+XiI2fkJWhl6KimaOZlZ6RjZaViIyR hIiOf4SRgoeVhouWh4yZiY6djZKhkZmdjZWijpediZKVjpqblaGhkpuWiJGRhIiWiY2hlJqilZui lZmll5ujl5aekpGVf4SQen+Of4eVho2Vg4SOfX6Nc2iEal+Da2eMdG+Vhoiai42ahIeSfX+Ve32W fX6Ve32UenuUen6SeX2Vf4KbhoieiI2fiY6bhouahImahIKVf32VfXiXf3qahIKdh4SfhoSfhoSj iIOfhH+bgHWWe3CRcl+La1qSdGeae26igIKigIKif36aeHeWd3OWd3OSb2eUcGiUbmOXcmeUdGSU dGSVc26beXSbf3uni4eqjI6niYyuhoKnf3uef3WdfnSjfnulf32mgnmfe3Oac2GQaVeSaVGUalOZ cmSbdGeefXSigHiqhnmohHimfXOheG6ac2OUbV2ZbmKid2qfe3CmgnengHWngHWne2ObcFibalqb alqXaliZa1qbcl6jeWWmdVufb1WXaEiXaEiVaFSdb1uqe3Syg3uufnCneGqlc1ifblSUho6Vh5CU iZCSiI6VhouUhImaiZahkJ2hlKaekaOdjpeajJWZjZmViZWUi5eXjpufkZ2fkZ2ei5GbiI6bjZah kpujkqKfjp6Wi5aUiJSUiJGViZKUi6KVjKOajKafkauekaWajaGai5CXiI2OiJSOiJSUiJSSh5KZ i5ahkp6flJ2flJ2dlKGdlKGdlJ6XjpmZjZmekp6jl6ailqWakJaUiZCMgoaLgISNg4eWjJCZjZaa jpeWjJKdkpmflqGqoaulnqWfmZ+XkZeXkZeUjZaVjpeVkZeVkZeVjpqWkJuZlqWdmqiim6eel6OS kZuRkJqVjJSVjJSZjI2Xi4yRh42QhoyMfX+Jen2Gfn6MhISQi46SjZGbkp2bkp2ZkaGdlaWfl6ii mqummqahlaGhkJ2ejZqWjZqXjpufkZ+hkqGXjpaZkJeZkJ2flqOhlZ6ekpuhlZ6dkZqbjZmbjZmb h4mVgIOWh4yhkZahlaGekp6Vi46OhIiRf4CSgIKWgIaahImWhIaWhIaZiY6bjJGhjZSfjJKbjJGa i5CUi5KZkJebkJuXjJeXi46ajZGjlp2jlp2llZ2nl5+jl5aekpGUfnuNeHWLeXiVg4KWgoSSfoCM c3KHbm2GcHCLdXWVf4eXgombhImVfoOXfoKXfoKVf4KVf4KQfn+Rf4CSe4OWf4ediZChjZShjZSh jZSljo6eiIiXfn+UenuVe3qdg4KeiYOeiYOfhH2ih3+egn6afnqdd2uWcGWWd3CefniihImjhouj h4Kbf3qWeGuUdWmUcGWUcGWVbWOSamGRbmKVcmWScGuZd3KffXqnhIKsiYiqh4aviICngHmde3Cd e3Cif3qmg36ohHmifnOfemmdeGeadWSbd2WadWSdeGeifXijfnmqh4Kui4aqgHqjenShd2GXbliS aVaZb1ybbm2idHOje2uogHCnfWSXblabZEqeZ02WaVWZa1eablWdcFeib1qea1aXaEeOXz+JZE6W cFqje26rg3WsfW2md2eialCdZUyUiJGZjZaajp2ajp2XjpaWjZWdkKKfkqWilJ+ekJuXjJWXjJWZ jJ6ViJqZjqGelKailJ+ekJuah5CdiZKajpqajpqdkpaZjpKWjJKSiI6QhomQhomRiZmVjZ2ajaGe kaWbkp+WjZqUiJGRho6Qh5GRiJKXiZKZi5SZkJqflqGlmaWhlaGdlKGbkp+ekpuajpefkZqhkpuh lZ6flJ2ZjpCVi4yQg4SNgIKQg4SbjpCXjJWajpebkJmflJ2hlaGjl6Ohl5+elZ2alJ2alJ2ZkJ2b kp+Zkp6alJ+alqKbl6Ofna+in7KfmqqalaWSjY6RjI2RjI2UjpCakpeXkJWViZWRhpGOgoOLfn+J goKNhoaSi42WjpGalZmdl5ublaGdlqKfl6efl6emmqammqailJ2djpeWjZqXjpubkJ6ajp2ViZKS h5CXh5GejZehlaOilqWhl5+dlJubkJmajpeXhIuUgIeRh42XjZSbjZaZi5SSg4aNfoCUfnuWgH6X goKfiYmVh4eVh4ediY2ijpKfjJKei5GbjI6XiIuUhIyWh46WiZCZjJKUiZCVi5GfkZqilJ2mkpms mZ+mkpmhjZSXf3mQeHKQfXeXhH6Zg4OXgoKQeX6LdHmNeH2NeH2NeIKUfoiahIyahIydh4yeiI2a hISahISUgIeVgoiUfYKVfoObhouhi5CfkJWejpSmkJKjjZCbhoiRe36Wenebf3udh4eeiIijiISj iIShhoKdgn6hfXSZdW2bfXOfgHeliZCmi5GjiICbgHmad2uUcGWScmWUc2eScF6Na1qQbl6ObV2Q al+Xcmeee2ulgnKsh4SuiIashnumf3Wfe3Cfe3ClgoCqh4arh3qjf3Oie3Cmf3SlgHWlgHWffWqh fmumf3Wmf3Wog36sh4KrgH6lenilemedc1+UcFuSb1qXa1udcF+ec2ene2+mfWKddFqaZUqZZEmZ aFebalqabViabVibalWZaFOWZ02UZEqNYkeUaE2bdGelfW+qfWunemmjblWbZ06WkJuZkp6Zjp+Z jp+ekqGekqGdkKOajaGXjpmWjZedjJaejZeXjJqbkJ6fl6efl6edjpqbjZmVho2ZiZGXjJWbkJmZ lJeVkJSSi42NhoiRg4CRg4CRh42Vi5GZjp+dkqOWkJmQiZKQgouRg4yOhpCNhI6Vh5CajJWdlJui maGmnaehl6KekJufkZ2ekJubjZmXjpaZkJeel56fmZ+dlZedlZeWiY2ViIybjpWfkpmakZmZkJee jZehkJqjlZ6ilJ2flJ2hlZ6jmqeimaabkaKdkqOWkaGZlKOdl6einayin7Kin7KbmqWRkJqVhouW h4yWkJaZkpmblZ6WkJmZjJ6ViJqUi5WQh5GSiI6UiZCWjJKakJabkZWhlpqekqGekqGflqGflqGe mqOhnaailp+ekpuZkpuZkpuXjpaSiZGVho2Sg4uZhoydiZChkqGomqijmqKdlJujlJadjZCXgoyV f4mXi46ajZGdkJGXi4yRgoSQgIOZgoedhoueiJCljpabjpKViIyjkJanlJqlkZefjJKfjJCXhIiU foCVf4KRgoSSg4aOf4KNfoCXiJCai5KijpWolZunlJqhjZSZhIKXg4CXhIiZhomVh4eUhoaVgoiU gIeShISRg4ONeHiNeHiVfoadho2liZCliZCiiImiiImZhomVgoaSfoCQe36UfYKag4ihiY6ljZKo lJaolJadiIuOen2Rd3KUeXSZf4CbgoOjhoalh4emhoKhgH2ffnWbenKigHWmhHmmi5anjJemi3+f hHmjd26bb2eZc2iWcGWQa12RbV6SamGRaV+SbWKWcGWaeW2jgnWuh3+shn6qiH2lg3ijfXWfeXKn gn+qhIKrh36rh36ognirhHqqhn2ohHulf3CmgHKngHWmf3SmgHungn2qgoCmfn2ofW6id2ibclWR aEyXZ1Sda1iab1yfdGGld2Kld2KfaUiZY0OWaVOabVabblqbblqaaFWaaFWXaFCVZU6VZU6UZE2Z aluic2OmeG+oenKreVyjclWajaGbjqKZlKWalaaelqeblKWdkqOakKGWkJmWkJmfjpufjpudjpql lqKll6qjlqihkZabjJGWiIiajIyXjpmXjpmUkZWRjpKNhoaNhoaMgH+NgoCVg4SbiYuZi5abjZmW iY2OgoaOe4KNeoCMfYSOf4eQhoyXjZSelZ+jmqWfm6WdmaKekJubjZmXjpmWjZeXjJWdkZqemqah naijmqWhl6KdlJ6dlJ6blaGblaGZjZaZjZadjZKfkJWhkp6ekJuflqOlm6imm6ymm6ydlp+blZ6d laWdlaWflqOon6ysn7KnmqyXlZmOjJCWiZCajZSSkp+Skp+akZuXjpmZjp+Zjp+SkJ+UkaGZkpuZ kpuakJaakJadkZqflJ2bkaKZjp+ekp6hlaGbl6GdmaKhlaGflJ+fkZ2ekJufkJWai5CbhIyZgomO gH6Vh4SZjZunm6qenaebmqWfkpaajZGah42ah42bjpWdkJadjZCfkJKXiYmUhoaXiZWilJ+jlaOe kJ6akZmVjJShlJqjlp2mkZ2lkJujjZWeiJCZh4iWhIaRf3uOfXmUenmSeXiJfn2QhIOZjJCekZWd kJSdkJSdiIudiIuhiZadhpKXi5GViI6diZKhjZaijpWbiI6Qe3SLd2+Rf4CaiImlkZqmkpumjpSe h4yXhoeUgoORe3mNeHWQeniVf32bh4mjjpGokpWnkZSli4yXfn+McmqLcGmRdXKWenehf4Cnhoeo iIKlhH6ifnOdeW6denWjgHulh4ymiI2qhH+og36lgHSdeW2dd2ueeG2XdWWRb1+Oa1+Sb2OXc2SZ dGWbenKffnWmgH6rhoOui4iqh4SohHueenKhe3elf3qogICrg4OnhHSqh3emh3qmh3qrgnirgnir hHqlfnSiem2je26lfnejfXWlfWqmfmufd1SddFGfblufblubblqidF+ld1+ld1+jc1OhcFCXbVee c12bb1GZbU+dZ1ObZVGWbVOZb1WebVefblihb1emdFyjdWGld2KqeF2ndVufjaGikKOelKaelKab lKOZkaGZkp6blaGekJmekJmfjpuejZqfkZ+nmaejmaqhlqedkJSajZGZjJCajZGWjpSWjpSWkZWU jpKRh4uMgoaIgIOIgIORfXqWgn+ShoyUh42QgIaLe4CLdXqLdXqNen6QfYCNg4eXjZGhlJqnmqGh l5+ZkJeXjZSWjJKWi5SXjJWajpehlZ6ina6ina6lmqyil6qhmaifl6edlqKZkp6ajZSZjJKWjJCb kZWelZ+dlJ6fkKOmlqqlnayimqqblpqVkJSdjpqhkp6blZ6mn6iroq+imaadkJaWiZCWjZWimaGa l6eVkqKVjpeWkJmZkJ2dlKGWkaGalaWel6GblZ6alZmdl5uflKKilqWdkKKZjJ6akZ6flqOflaah lqedlp+dlp+hkp6fkZ2hkZaZiY6hiY6dhouVhouOf4SUi5emnaqlnayjm6ufkZqbjZaWjJKVi5Gd kZqflJ2hlp2elJqajo2ZjYyXkZeel56jmqedlKGdkZ2dkZ2jlJmmlpuqlaGmkZ2jjZeijJabjI6X iIuZhISVgICQe3mLd3SId3ONe3iSg4iZiY6ai42djZCZhoybiI6fjJWei5Sii5Keh46RgI2djJmo kZ6jjJmZgHqMdG6IdHKUf32fkJemlp6olJahjI6Xg4ORfX2OeXeLdXOQdHCXe3iZh4ajkZCnlZam lJWnjYybgoCUdG6NbmiSc22Wd3CfgoSihIeihoChhH+fgHebfXOafnefg3ujh4eliIiliICihn6h hnieg3WignuignuffW2Vc2OWamKabmWadGqeeG6denWhfnmmgH+sh4aujYmnh4Omg36ffXieem6j f3OmgHuog36mhHinhnmoh3qnhnmohHurh36sh4KqhH+ifmOfe2GjenCieW+mfmuiemihd16fdV2h dGOhdGOdcmGfdGOdd12adFufdFyjeF+adF+Zc16dbk6dbk6faUyhak2iclqmdV2mdF6mdF6meGGn eWKleWOjeGKqeF+qeF+djZ+fkKKelqablKOVkJ+Ujp6ZkJ2dlKGdkpmdkpmdkpmdkpmikqWnl6qi laedkKKblJmakpeekZebjpWZjI2Uh4iWjJKUiZCRhIuNgIeHenuHenuOe3WVgnuUgIeUgIeQfYON eoCNd3uNd3uMenuQfn+Ngn6ViYabkZehlp2jlpqekZWVjZCSi42ZiY6ai5Cdlp+im6WjobClorKh maqelqeimaahl6WbkJmWi5SZiY6ZiY6WjpSakpeakqKZkaGXjJeekp6imaaimaaakJGUiYuXi5Ga jZSUkp2enaemn6udlqKWiZCViI6ZkJ2jmqealaaVkKGSjJeRi5aXkZ2alJ+dlaWakqKjlZ6jlZ6f lqGimaOilqWflKKZjZmWi5aajpqdkZ2hlaOjl6aelKWhlqehl6WdlKGhkpuekJmfkZqZi5Sai5KQ gIiUjZmhmqanl6qnl6qhkp6djpqXjpaZkJeel6OdlqKjmqKflp6ZkZSXkJKdlp2fmZ+hmqael6Oe lZ+flqGmkpuqlp+mmZ+ll56jkp2ejZedjZWfkJedjZKWh4yVgIONeXuLd3SMeHWXg4abh4meiYeh jImaiImejI2fjY6fjY6hi4uhi4uSgoyZiJKllJ6hkJqehoCWfnmQdW6UeXKajIyml5eqlZWjjo6f hoSXfn2UeXSOdG+MbWeSc22SfX2fiYmjkJamkpmukZaihouXd26JaWGObmWXd26df4KjhoijiICi h3+hgnWdfnKafnmdgHuiiIehh4afiHuhiX2liIOmiYSri4esjIiognedd2uZc1yadF2efXSffnWh f3eefXShfnmlgn2sjIiqiYaqhH+ifXied2med2mhfXSng3qrh3urh3usg3mrgnimgnmohHunhH+q h4KlgGibeF+hdWeid2ilfmmhemWheWeheWefeGqfeGqbdWGbdWGZc1yXcluhcmKmd2eleWOjeGKi dE2db0ifaUqjbU6od2Oue2iofWSofWSuf2qsfmmrfWioemWmdFyndV2XkZ2alJ+XkZqXkZqXjpuX jpubkp2bkp2bkpqbkpqakJaakJaakqKblKOelaKakZ6ilJ2ilJ2fkJeai5KZhomXhIiViI6WiZCR h42LgIeOgICLfX2Qgn2XiYSXhoeVg4SQeoKNeH+Md36Md36MenuOfX6SfoCZhIedjZWhkZmikpeb jJGQiI2RiY6ZiJWfjpufl6emnq6nn6+mnq6dlqKel6OhmqOfmaKakJaWjJKWh4yXiI2SjpeZlZ6d lp2XkZeSjJWZkpuflKKhlaOakJSSiIyRh4iVi4yWkJmXkZqdlp+ZkpuUiY2Vi46dlqKlnqqbkJuV iZWRh4uSiIyXjpaZkJebkp2XjpmajJWekJmbjqGilaeflqGbkp2ZjpKVi46ViZKWi5SZkJ2elaKe lKWelKWfkqailaiilqWilqWhl6Kbkp2ekZeViI6SiZSbkp2hlaOilqWflJ+ZjZmakZ6dlKGil6qe lKailKKekJ6Wjo6akpKdlJulm6OlmaKilp+bkp+bkp+jlaGml6OlmaKjl6GikZuejZedkJSdkJSf kpaXi46XhIuWg4mSfniOenSVf4Kbhoifi42hjI6hi42hi42ejIudi4mhhoyhhoyZg4iZg4iikpqm lp6ijYeXg32Nem2Sf3KZh4imlJWsl5qlkJKhiIOXf3qWenOSd2+LcmeHbmOLd3CVgHqhi5KijJSq jJSlh46Zem6LbWGIamGRc2mbenuhf4Cni4Oihn6dgnSZfnCbf3qdgHuihIejhoiliIiliIiqiYeu jYuwjYyvjIuviXiifWuWcFqZc1yig3elhnmnhnmhf3OigHWjgnenh4Cnh4CuiYCmgnmdemqZd2ef eW+jfXOohHmrh3uwg36vgn2lfnSngHeog4CrhoOlgGqhfWefdFybcFifd1yjel+lemenfWmne22j eGmec2KbcF+Zc1yVb1iicF+od2WqfWuqfWuneWSjdWGhb1ejclqnemGugGewhGusgGi2hGuygGiw fmivfWeqeVyjc1aZkJeVjJSWjJ2WjJ2ajJqbjZuekJuilJ+flJ2dkZqZjJKajZSWjZqZkJ2bkpqd lJujl6OhlaGejpaZiZGZhomah4uVhoiVhoiSiIyJf4ORgoeUhImZjJKajZSZiJKWhpCQgIaNfoOM fn6LfX2NeXmNeXmQe3WXg32ZiY6ejpSdjo6ShISOg4KRhoSWhpCfjpmhl6Wlm6ilmaWekp6dkZqe kpuflqGhl6KakJGQhoeOhIiUiY2VkJ+ZlKOdlp+XkZqUiZCXjZSajpqdkZ2ZjpKSiIyZiY6bjJGb kZebkZebkZeZjpWVi5GXjZShl6KimaOijpWXhIuUh4uWiY2akJaakJaXiJCUhIyUh42WiZCVjJaa kZualJ2blZ6fkpaViIyQhomRh4uSi5CXkJWblZ6dlp+dkKOjlqqll6ummayimaGelZ2bkZeVi5GQ h46VjJSXkZqblZ6dlaWakqKflaeelKallqWhkqGei5Sah5CZjI2ajY6fkpanmp6om5+ll5uVjJSV jJSZjZubkJ6bkJ6flKKllZ2fkJedjZKai5Cei5GbiI6XhI2UgImVgoaVgoaVhoiZiYyhi5CijJGn kZGljo6djZCai42hho6ih5CahImeiI2fkJWmlpumkZGfi4uXg4OVgICbhoumkJWqkpeokZajiYud g4SXfXWQdW6Rd2uLcGWIbmeSeHCah4ufjJCijJGijJGagG6Mc2GIal2Nb2KaeXqlg4Smh32dfnSZ fnCZfnCdfnSef3WlhIKlhIKjhoiniYyojIyqjY2ujYuwkI2vjYKjgneWdGSXdWWjfnurhoOqiH2n hnqmg3CffWqjfnmog36shn6ngHmfemebd2OZcm6feHSlf32rhoOvhn+vhn+nfWmlemehfXSmgnmh g2+fgm6jd12fc1qeclaidVqmeWinemmofWSjeF+hc16db1uZbl2XbVylc2KndWSnfWSrgGiofWun e2qldFymdV2nd1yvfmOwgmqvgGmwgm2wgm2uf2iuf2irgGGme1yZiY6ZiY6ViZeSh5Wai56bjJ+b kaKdkqOdkpmZjpWZiYyZiYyVjJaakZudlKGhl6WomqallqKXjZSSiI6VhouVhouUhImVhouRho6R ho6Ui5WUi5WXjpuelaKhkaWZiZ2OgoaMf4ONg4SOhIaUf4KNeXuNfXWUg3uShomXi46bjpCRhIaM goOOhIaSiI6XjZSflJ+hlaGhlp2ZjpWWi5aflJ+hl6WelaKhkpKajIyUiZCZjpWblKWdlaadl5uW kZWShoeViImZjJKajZSbjJGbjJGdjpqekJuZkJeWjZWbkZWZjpKViZWZjZmhlaOhlaOlkZediZCZ iY6fkJWfkJWbjJGWhIaRf4CUfoaahIybjJSdjZWelZ+flqGdlZqVjZKQiIuLg4aUhImZiY6WkZWa lZmdkZ+flKKilKKllqWjl6GekpuakJaXjZSRh42QhoySiZGXjpablKOZkaGdkqOelKWil5uelJed iIiXg4OdiIueiYyekZWmmZ2om52mmZqejIiUgn6ZhISbh4eZjJCbjpKhjZaijpeei5GZhoyag4ue h46XhIuUgIeah4udiY2ajpqbkJufjpmikZull5milZahlJeajZGeiJCeiJCei5GdiZCfkJWjlJmm kpajkJSbho2ahIybho2jjZWrkJmvlJ2skJKhhIedgn6We3iSeWqMc2SHaGKQcGqReHmiiImfjY6f jY6bgm2Qd2KOcmGRdGOdenmhfn2ffnWde3OZeGude2+eg3ieg3ilgn+nhIKliIumiYynjY6qkJGu jYuvjoyrjH+lhnmbc2mXb2Wbd3Sog4CujYmqiYaqhHCifWmfeW6lfnOngHWngHWlfWqfeGWWcGeb dWuienqshISrh3uohHmhfWSbeF+ZdWmdeW2hfXKdeW6hcFiiclqdb1ihc1yidWSleGelel+jeV6h cFiba1SXblSZb1Whc1yld1+qf2esgmmrf3Cqfm+nel6leFyqeVyygGOzgG2wfmqwgHCygnKvf3Sz g3izgmmvfmWWg4mWg4mUhpSUhpSWh5mXiJqbjqGdkKKakZmZkJeZiY6XiI2WiJGbjZadlaahmaqi lp+dkZqQhomLgISQg4mRhIuRh4uVi46ZjpWZjpWZkpublZ6fkqWjlqihjqKZh5qViImWiYuZjJKa jZSVgoiVgoiXhoebiYuWh4yXiI2ei46VgoaQg4mRhIuOhIiVi46hlJqhlJqXkJWSi5CUjZadlp+l lqWilKKakJSWjJCajpedkZqlmaelmaejmZ+elJqbh4mZhIedjZCdjZCakJaelJqekaOfkqWjlp2d kJaZjJCXi46VjJaZkJqZlKOVkJ+ajpqXjJeakJadkpmelJeZjpKUhIeUhIeUh42ZjJKXjJqekqGd kqOhlqedlZqWjpSQgIaOf4SWg4yei5SWkZWVkJSfkJehkZmbjZmfkZ2flZudkpmbkZKVi4yRgoeQ gIaMgoiSiI6VjJSXjpaZkJqdlJ6jl5aflJKijZCdiIueiJKfiZSfjpuol6WuoaWrnqKjkY2di4ed i3qaiHiaiImbiYudiZKhjZadjZWai5KfiYyZg4abhImdhouei5SfjJWajpebkJmjlJullZ2om5+q naGqmp2fkJKii5CfiI2fjpuhkJ2mlp6mlp6nlJehjZGah42ah42liZCliZCrkJuukp6ukZSni42d h4eXgoKVgHeQe3KObmKLal6McG2fg3+hjZGdiY2agHOSeWuScmWVdGibeXShfnmhfnmhfnmefnii gnujh4KliIOliIOliIOmiYyojI6nkZGokpKrjouojIiujoSmh32he22ZdGWadGqjfXOohoOvjImv h3Srg3Cjf2efe2Omfm6ogHCrfm+meWqbcGKXbV6ZcnKheXmoh36nhn2lfWqbdGKbcGKdcmOfc2Ki dWShb1Ohb1OdbVWba1SbblyidGKhdV+hdV+ebVeaaVSbbU2fcFCdc1iieF2lf2uog2+rf3CofW6n eFamd1WjeWGsgmmyh3OwhnKzhHKwgm+wgHWzg3izg3O0hHSNg4eNg4eSgo6Xh5SXiZKZi5SejZ2e jZ2djJmZiJWah42ah42ZiJKfjpmhlqeflaabkZWUiY2Rg4CQgn+RhIiViIyVjZCWjpGZjpKZjpKa lJqdlp2elaKflqOfkZ2ajJeVkJGVkJGekpudkZqXi5GViI6ZiZGXiJCZjI2ajY6ajZGShomMgoiM goiRh4uUiY2akpeZkZaUjJGVjZKUjZaalJ2dkZ+bkJ6bkp2ZkJqdlJ6dlJ6il6qhlqiel6OblaGa i5KVho2biI6ei5GXkZeel56hlKailaellp+fkZqZiYyVhoiUi5WZkJqVjZ2UjJuXjpuVjJmbkJmf lJ2ikpqbjJSVi5GVi5GXjZSakJadlKGelaKhl6Whl6WhlJeZjJCVgIOSfoCVho2bjJSakJaWjJKX hIubiI6Xi46bjpKfkJKfkJKXjouRiISUf4KUf4KMeX2Oe3+Qg4eUh4uZi5SekJmilZuekZebjZab jZaZiJWbi5ejjJuqkqKmmqajl6Ofjpmbi5WdiIiahoaahIyahIyai5CdjZKWi5SXjJWai42XiIue iI2fiY6fjpmhkJqekZefkpmmkpaqlpqomZ6snaKqm5mjlZKjjZKfiY6hjJqlkJ6qlp+qlp+rlZen kZSeiYybh4mdiIuijZCikpqhkZmrkZCjiYiihISfgoKbf3+WenqRd2mJb2KMbWqdfXqijI6jjZCd gnqVenOUc2iScmeZeXOff3mefnqff3udf3+ihISni42ojI6mjJCiiIyliIimiYmnjYyrkZCyjZCu iYyvjYSnhn2le3KddGqXcGGddWWhf3enhn2shnuuh32rgmSjel2feGWlfWqqfm+ofW6meGWfcl+a cmmheG+qg3irhHmme2ifdWKbcl6acF2dc1ihd1ydcFWbb1SablWfc1qhc16hc16hd1ybcleXaFCa alObaEmhbU6bcl6ieGSrf3OwhHiyhHOqfWund1Ojc0+ld2SrfWqshHSvh3ezhnSvgnCwg3KvgnC2 hnW6iXmShoyRhIuXhIuZhoyViImZjI2ajZSdkJaZjJKXi5GWiZCWiZCZiJWfjpujkqKejZ2Zi4uS hISNgoCOg4KQhoyZjpWbkp2dlJ6ekZefkpmblJmelpullqWnmaeflqGdlJ6elZ2dlJuhlZ6ekpuX i46ZjJCXjpmXjpmakpKZkZGajZSUh42NhouQiI2VkJGWkZKblJmXkJWZjJKWiZCWiJaXiZeWjZWW jZWakZuakZubkp+dlKGXkKOakqadlaWakqKWiJaUhpSXgI2XgI2UjZSdlp2flaaflaaflKKajp2X i46Uh4uWjZeZkJqUhpGUhpGVjJmbkp+bkpqflp6jlJudjZWXjJeXjJeXjJWajpealJ2blZ6flqOh l6WjmZqbkZKWgoKSfn6Zg4ueiJCfkJWdjZKZhIeahoiZh4ibiYudjo6djo6XjIuRhoSSfn6RfX2N eH2OeX6NfoOQgIaVho2djZWfkpmfkpmekqGbkJ6ajJWZi5SdjJaejZefkZ2ml6OejZ2djJuah5Ca h5CZhoydiZCdiY2ei46XjJWXjJWbjJSai5Kei5Sei5SfjpmikZulkZehjZSikpWnl5qlmp6onqKq nZ6nmpumjpadho2ZiJKhkJqmkpuqlp+rlJmnkJWeh4ybhImag4ieh4yfiY6ijJGni4eihoKhg4Od f3+aenidfXqWenqQdHSRcHKbenufhoejiYuhhoCaf3qRd2uOdGmVdXOZeXehfnuhfnudf4KihIen jJKrkJankZGljo6miIijhoani4eskIywkpKukJCui4ariIOrg3Wed2mbcF+ec2Kdd22ie3KqhH+w i4augnCmemmed2Sje2mqfXCoe2+idWSidWSec2ehdWmjfXKmf3SngGehemGfdV+fdV+hc16hc16e cFydb1uhc2Gld2SneWSld2KidVqhdFibak6ZaEyaaU+hb1WhdGioe2+ugHmwg3uyg26oemWnd1qo eFujd2Woe2qshnCzjHe0iHSzh3OwhHOsgG+wg3S3iXqWiZCUh42XgomXgomWh4mZiYyZjpWXjZSX jJWXjJWajp2XjJqdi56hjqKjjpqeiZWXhoeUgoONg4eWjJCflaail6ihlKehlKehlZ6flJ2hlpqh lpqekqGhlaOfkqajlqqfl6ejm6unlqGllJ6bjJGbjJGbkp2imaOfl5qfl5qdkZqViZKUjZSSjJKX kZeblZualZmVkJSdh5GZg42VhouWh4ySiIyRh4uWiJGZi5Sdi56biZ2WjZqSiZaaiJuaiJuSh5KN go2UgImSf4iSh5CViZKZjZaZjZaajJWWiJGSiIyQhomZi5SZi5SOeX6Re4CShI2XiZKbkp2dlJ6f kZqbjZaUiJSViZWakJaakJabkJuajpqbjZailJ2om52ekZKXgIaVfoOag4uhiZGikpedjZKZhomU gISXgoeahImeiI2hi5CfjY6XhoeQgoKNf3+QeYCOeH+Oe3+RfoKRf3ubiYajlp2jlp2flqOdlKGe lJeXjZGajY6Uh4iXi5GdkJaeiZqfi5uViZKViZKZiY6bjJGah42ZhoyXhIuXhIudiZKdiZKfjJWi jpejjZWijJSdh5GahI6ai5ChkZailp+lmaKqnaOrnqWqkpqfiJCWf46ag5KdiJSjjpqnl52llZqn iY6fgoebfYSfgIiehIifhomjiIOfhH+eg36dgn2bfXOZenCOenSMeHKOcnSUd3mdg4SmjI2jiH2e g3iZe2qOcmGOcGSUdWmdeniffXqefX6oh4iojI6ukZSqjZCojI6nhoeigIKnhIOriIewkIysjIis i3+si3+shHemfnChemWbdWGacmiddGqifXiog36siHuohHiofWmjeGSfdWKieGShc2Gfcl+ec2Kf dGOee2ujgHCofWuleWihem+lfnOoe3OleG+lc1+lc1+lemeofmqsfmerfWWqeFusel2lcE6ZZUSb ZE+mblimd2uufnOygoCzg4Kyf2mue2Wod16semKod2OqeGSsgG2yhnK0hnC0hnCzhnSwg3Kwg3e0 h3qWi5SSh5CXg5GVgI6Zg4uahIyUiJGViZKWjZWWjZWbkJuZjZmdjJaejZebi5WZiJKZhoyXhIuU h42fkpmjma6lmq+mmaull6qhl6KflqGhlJeilZmZkpmalJqbkaOil6qfmqqjnq6rlqKnkp6fkpme kZeflqOhl6WhlaGekp6ZjZmZjZmXkZqalJ2elaKflqOZkpuQiZKWh4yUhImRhIaQg4SNgISNgISO hIuQhoyVho2Sg4uRhIiShomXh5SXh5SRh4iLgIKOg4KViYiRh4uOhIiXi5GXi5GZiZGWh46UiZCU iZCWh4yWh4yRen+QeX6Lfn+WiYuZkZSblJabkZeakJaViI6ViI6ZiY6bjJGhjZSdiZCfjpmikZul lqKhkp6aiImWhIaWg4ediY2ikZuejZeZhIeVgIOVgoidiZChiZGii5KhkZaZiY6Uh4iUh4iWf4SS e4CQfYORfoSRfoKZhomjkp2mlZ+hmqGel56jlZ6bjZaeiYmXg4OZhomdiY2ZiZGXiJCbiJGbiJGb iJGhjZaijpWei5Gag4uag4uXhIibiIyhiZGjjJSdjZKZiY6UgISWg4eVh5CajJWilKKml6awoq6s nqqvmaGijJSbhoiWgIOUfoadh46nkJ2nkJ2miJCegIihe3meeXeaf3qhhoCdiIKdiIKegoKdgICb e3WVdW+IdWiLeGqNc26SeHOif36nhIOmh32ig3mafWuVeGeRcmKQcGGZeG2de3Cbf3ijh3+ni4eq jYmrjo6qjY2nhIKlgn+og4Kog4KnhH+ohoCvjIewjYiuiHmog3Sjfm2eeWiedWuddGqfdXOnfXqs g3quhHuvg22ne2WhdV2leWGld2SjdWOdd1+Zc1ydeGelf26mfXOnfnSmfnqogH2ugnWqfnKneWSo emWof3eof3evf3SwgHWuf2irfWWndWSfbl2hak2hak2jb2OueW2vgHuzhH+wgm2vgGuue2GwfmOu e2Ood16seme0gm63h3ezg3Owg3KyhHO2hnq0hHmZjZmZjZmXiZeUhpSRgoeSg4iNhoiUjI6XkJ+Z kaGbjZudjp2ajJWbjZaXiZKWiJGbh5Kbh5KXiZWhkp6fkqajlqqqoa6mnaqhl6KimaOelJqdkpmf kpSfkpSakZujmqWmn6uooq6qlaaolKWflqGakZuel6GhmqOhlaOdkZ+WjpSWjpSfjp6ikaGjnaah mqObkJuSh5KQhoeQhoeRg4OOgICHeHqHeHqLe4CLe4CUeX+UeX+MeHqSfoCVhouZiY6RhoSQhIOS hoebjpCVi46UiY2ZiYyXiIuVhoiZiYyXi4yajY6biIyah4uUf3mSfniOgICWiIibjpKekZWdjZKa i5CWh4yWh4yZg4uahIyZiY6bjJGfjpuikZ6ikaGikaGajZGViIyUh4ibjpCekJuekJuahoaXg4OV goaah4uZiJKejZehkpuekJmdkJafkpmhho6ZfoeQgIiRgomOf4KUhIefi5alkJunmaKllp+ijpeb iJGQgn+Rg4Cdg4SehIadi4yaiImfhI2ih5Chi5WmkJqmkpujkJmXgoSVf4KUgn6Zh4Obh4eeiYmf iYeXgn+SfXqUfnuNgIeViI6hkJ2nlqOsnqysnqywnaalkZqhh4aZf36OeniWgn+eiJChi5Kdhoub hImef3WZenCZfX2egoKliIiihoaihISfgoKee3eVc26Qc2KRdGORc2eSdGiee3elgn2jiICfhH2d gnObgHKQc12LbliZdWqhfXKegnqdgHmhg4OrjY2zkI6wjYysiYSohoCmg4Clgn+mg36nhH+siYev jImsi36qiHulgHSifnKje26heWuecmmleG+qgHewh32yhnSsgG+ne2OofWSof3WnfnSje2ufeGie eGOie2eje2unf2+mgHush4KyhnSrf26rfWqoemiuf3iyg3uyhHuwg3qshnCogm2ogmimf2Wsd1Gj bkmja16udWirf3OwhHiwhG6whG6yg26yg26zgG+semmreWGyf2eyhnSyhnSzh3qzh3qzg3OygnKV i5uUiZqXiZeUhpSOf4KSg4aVi5GelJqflaaakKGUiJGSh5CWiJGZi5SViZKWi5Sbi5edjJmbjZaf kZqjl6Gonaanoaqlnqemmqilmaeml6OjlaGljZKljZKikpqrm6Oon6yroq+nmaKjlZ6dkZ2bkJue lZ2elZ2hkpudjpedkJSdkJSakZuflqGjn6uhnaimkZ2ahpGZjJCZjJCWiY2RhIiIenqHeXmLfX2I enqIeXuIeXuIeXuSg4aai42ejpGViIyUh4uWi5SajpedlJ6ZkJqekZKajY6ekZWhlJeekZWbjpKW iY2Uh4uQe3uSfn6Vf4edh46bjpKZjJCdjZWZiZGRh42OhIuXgIadhoudkJafkpmekaOekaOmkKWl jqOjkJmfjJWai42djZCdkZqdkZqbjYuUhoObhImii5CakJabkZeilp+flJ2jlJullZ2ijpediZKU h4uRhIiWgoSWgoSai5KfkJeilZuhlJqajZSWiZCZhIeZhIedh4meiIuajouajouZhIeeiYyjkJmm kpuijJSeiJCQfn+Qfn+SfoCWgoSbh4mdiIufhH2dgnqafnqWeneOenqVgICbh5KhjJemkaKvmquq maajkp+dg4KZf36SeHOUeXSWf4SfiI2hho6hho6ign6ZeXWbfoChg4amjJCli46jiIShhoKZgnWS e2+VemuUeWqWdWqScmeaeXCjgnmmi4ejiISmi3+ih3uhgHCZeWmdfXqign+nhIKlgn+jgH+qh4av i5Cvi5Cwi4irhoOjgHulgn2if3qjgHuhhoKliYami3+jiH2ng3qlgHile3Wle3WjenSfd3Cje26s hHeyhHuyhHusgG2sgG2rfnmugHusg3qqgHiqd2iseWqmemmofWuqhHWsh3iwg3KugG+qe2mrfWqv gHizhHuuh32viH6vi3+uiX6yhnSwhHOugGSoe1+ld1+neWKsgHSvg3euhniyiXu2iH+0h360hHSv f2+qfWGvgmWohnWvjHu0jYK4kYa4i3uyhHWUiJSRhpGOg4yMgImSg4uVho2XiZWhkp6dkZ2Wi5aV houWh4yVh5KbjZmekJ6hkqGbkJuXjJeZiZGdjZWflqGjmqWjmqWjmqWnmaWnmaWmlqqikqajjZWi jJSflJ2mmqOom66rnrCmnaWelZ2ajpedkZqfkJefkJefkpmdkJaakJSakJSdkpajmZ2roq+mnaqj lJuejpaflJ+hlaGakJaVi5GUgISQfYCLe36MfX+VgIORfX+LgISUiY2bjZadjpeXiZKUho6Sh5KZ jZmekaWekaWZlZ6VkZqdlKGelaKdlZqWjpSQhomUiY2Uh42RhIuViI6ajZSbjZaekJmbjZmUhpGR ho6UiJGbho2ijJSilJ2hkpuajZ+ajZ+njqGokKKhjJqhjJqijpWfjJKajpedkZqejpGZiYybho2f iZGekZefkpmhlp2flZullJ6nlqGlkJuhjJeah42ZhoyXgoeVf4SfkJWfkJWfkJWejpSZho6Sf4iW gIadh4yeiJKeiJKai5Cai5CXjIiajoullJ6mlZ+hi5KXgomOeniNeXeRen+XgIaahImahImfh4Ke hoCXgoSWgIOUe3eVfXiVf4Kdh4mjkJmrl6GqlJ6okp2lg4eaeX2UeXWSeHSRen+VfoOdgoibgIed gIOafoCZf4OfhomljZKljZKnjIihhoKeg3ubgHmXe3SVeXKVd22SdGqaenSjg32vjY6vjY6rjoen i4OmhoKff3uigIKmhIani4eliISigHilg3qqjIysjo6wjI6qhoimhHiigHSefXKffnOee3mif32r h3urh3uohHmmgnehfXChfXChe3ebd3KidWmoe2+vgHmwgnqvg3eugnWqg3uviICyiH6vhnuvf3Kr e26leWiofWurg3Ovh3evg3KugnCqe2eoemWuf3evgHiuh3+viICwh4Cwh4Cwh32uhHqrfm+ugHKs gG2rf2urf3Cvg3SugHuyhH+3iYK2iICziHSsgm6neWKrfWWvh3m2jX+2ko23lI67kYu2jIaUgIeR foSOf4eRgomVg5aaiJuejZqejZqZiZGVho2XhI2ah5Cdh5umkKWllaejlKajlJuai5KZhombiIye kp6flJ+elaKelaKhl6Wjmqejl6ahlaOijpeijpeekp6lmaWrnrKrnrKnnaOelJqejpSbjJGjkJmi jpedkZqZjZaXjZGakJSflZmmm5+oobCmnq6hl5+flp6umaqvmqujmZ+bkZeah42ZhoyVh5KUhpGa hoiXg4aUhpGbjZmekp6dkZ2dkJSWiY2Zi5SbjZaelKWjmaqfl6ielqefmaWjnaihlZ6Wi5SVhoiX iIuViI6WiZCXjJWajpefjp6ejZ2bi5qXh5aWiZCWiZCdiJSijZmilqWhlaOijZmijZmhjJqlkJ6i kZ6ikZ6okZ6ljZqXi5GZjJKZiY6ai5CbiJGdiZKZjZabkJmbkJuflJ+olKWqlaaikqWdjZ+ijZmX g46XgIaVfoOai5KdjZWfiZGdh46SfYKQen+Wf4Sdhoueh46hiZGhiY6dhouaiIeZh4ahjJemkZ2e h46UfYSLcnOQd3iVe3+Zf4ObhoabhoaeiI2eiI2dhpCag42XfXiVenWSeXiXfn2ei5GjkJanlJen lJewiYymf4KafX2VeHiRdXqQdHmafX+df4KegIOdf4KbgImjiJGojZSmi5Glh4eniYmnh4OmhoKd gHuafnmVeXSQdG+ZfnqfhICsjo6vkZGsjo6sjo6rjpGni42niYmmiIiokIenjoajgG6ee2mjg4Cr i4isi4yriYusiH2ohHmmgHKhe22jd2+hdG2mfnCogHOohnWohnWlf26he2qddWied2mjd2ileGmr fnKwg3eyhnewhHWqh3eui3qyiH6vhnuwg3evgnWofWmqfmqogHCshHSuhniuhnireGKndF6nenOs f3ivhn2wh360i4KziYC2iXqvg3SoemWqe2eofXCjeGurfWq0hnO0h3+3iYK3i36zh3qvgGmqe2So dV+uemSwg3q7jYS2lJW6l5m/lY67kYuNe3iNe3iNfoCUhIeZiJWdjJmekJufkZ2bjJSXiJCVhouZ iY6ekJ6jlaOjlaOhkqGijJSahIyXgomahIybi5Wbi5WdjpehkpuhlKailaelmaemmqill56hlJqf lKKlmaern6uqnqqmmZqhlJWfkJeejpadkpmelJqdlJudlJuekpubkJmflp6lm6Omnq6mnq6imaam naqvoaqsnqelnZ+fl5qhlJeekZWhkp6ekJufjJCah4uUiJGViZKZlJealZmejIiZh4OXiYeekI2e lZ+hl6KilqKjl6Omnaqmnaqhl5+Ui5KUhIeUhIeShJCUhpGai52ikqWnkp6ijZmfiZGfiZGUh4uW iY2ZiZujlKanl5+hkZmeiYyahoiai5KdjZWhkJ+ikaGqlp2ijpWXhIuZhoyZg4ubho2ahIyZg4uX jJeXjJeXiZWdjpqjkqKikaGlmquhlqelkZeZhoyVfoOVfoOVgouah5Cdh4mahIeRfX2Qe3uZf4Of homhho6ih5Chh4uehIiahISfiYmhi42ijI6ahn6NeXKJcG+NdHOVeXudgIOfhoehh4ifjJKhjZSh kJqbi5WZg4CSfXqJdW6Qe3SRhIiWiY2ijJSmkJeskIyliISehoCZgHuVd22OcGeWdXeffn+fg4ad gIOeg4mhhoyljIeji4alhIKnh4SuiIOsh4Klg3iigHWaf3SXfXKWenedgH2ni5CukZavkJevkJeu kJqylJ6vlZmyl5u0mpuvlZasi36de2+deHOjfnmmhoOoiIauiIOrhoCnhHSffW2ac2WWb2KZd2ei f2+og2+og2+qfm2jeGeeb2KfcGOZc2ibdWqfd22qgHeshHSqgnKogHOrg3Wof3Wrgni0hHSwgHCu emeqd2Omem6ugnWuh3uuh3uqeFuhb1Ohc2GneWesf3qyhH+yh4S0iYe3iX2yhHiweWSrdF+hdWSf dGOneGiygnKzg3i3h3u2h3Kyg26wfmGsel2sc1+udGGvgn24i4a3jZG/lZnFlZTBkZCQhIONgoCM gH+RhoSXiZKbjZaekqGbkJ6hjZadiZKWiJGXiZKZjZahlZ6fkqWbjqGdiZCZhoyWgIuZg42Zhomb iIyZiZGbjJSfi5aolJ+lmaWonaijmZ+elJqflKKlmaemmqimmqiml6GjlZ6flZuflZudlp2blZue kqGekqGdlqKfmaWhmaimnq6on6yqoa6onq+sorOvo6+soayooqilnqWhl5+elZ2hmZ6hmZ6llZqe jpSUjI6RiYyWjpSakpeekJCZi4uXiYmbjY2akZ6elaKflKKlmaenna6jmaqfmZ+UjZSQhIOOg4KN foOSg4iWhpChkJqmlp6ejpafjJKfjJKWiY2ViIyZi5SfkZqjlJafkJKfi4uZhISZhombiIyeiZWl kJuhlJeekZWZg4CXgn+Vf4SXgoeXgoyWgIuShoyShoyah5Cei5SdjJafjpmjmqWimaOilJ2ekJmX hoeSgIKWhIabiYudh4SahIKQeniQeniZfYKihouhi42dh4mag4iZgoeahISeiIijiYiiiIeWfnWJ cmmIb3COdXeafYelh5Glh46ihIyei46jkJSikpehkZaiiImZf4CLd2+MeHCJen2QgIObhpCjjZem iYyliIuiiIyZf4OReG2Mc2iVdXKaenebh4CeiYOdh4meiIuhh4ifhoelgoCnhIOwi4iuiIanhn2j gnmefXCbem6WdHKdeninjJKwlZuzlZ2wkpqzlZ20lp6wlZGylpK3nZuwlpWwi4amgHufemuhe22h fnmjgHumg4Kqh4anhHSffW2bd2ORbVqSbl2adWSifnOlgHWqfXCmeW2fdGOZbl2Sb2OWc2edcmWh dWmne22rf3CofWerf2mqfmqugm60hHevf3KueF6lb1afc2Soe22qgnSshHeoemOhc1yeaVOmcFqn eGiwgHCwh36yiH+0iXWyh3OzgmmqeWGmdV2iclqhcmSqem2yhH+2iIOzh3Czh3Cwf2evfmWvemuq dWevgn24i4a0jIu6kZC+jo27jIuRhIuNgIeQfYOZhoyXjJWdkZqbkaKZjp+fjpuaiZaRhIuViI6e jZehkJqbjZmWiJSUh4uRhIiVgoaVgoaXhoeUgoOWf4eeh46ejpajlJunmaernauhl6KdlJ6flKKj l6almaemmqinmqymmaummqinm6qinaGfmp6hlaGflJ+bmaeem6qimaannquqoa6qoa6qo6+qo6+y pauzpqyrpa6noaqfm6eemqahmqael6OjlZ6ekJmdjZCbjI6bjpWhlJqelZ2WjZWZiY6XiI2XiZWb jZmfjpullKGqmqyomaublaGVjpqSh4OQhICQe3mUf32XhIujkJafmp6ZlJehlJqhlJqUi4mOhoSb iIyijpKikpeejpSfi4udiIiahIeZg4abhpCljpmhkZSdjZCagH+Xfn2WgH6Vf32Uf3+Sfn6Ren+R en+Vf4SbhoubiJGhjZajlaGomqail56akJaWfX6WfX6Vg3+aiISeiIudh4mWgnuSfniWf4ShiY6h jI6ahoiagISXfoKahIShi4uojpKli46WgnuOenSLcG2QdXKbfoioi5Whho6fhI2fi4umkZGlkZWm kpamkZSbh4mVenWNc26JdHKOeXeXfYOih42hi4uijIyijIyZg4OVeneNc2+Sd2+ZfXWfh4KljIei iImjiYufhoedg4SdgH2hhICsi46zkZWojIeliIOognieeG6ScG+UcnClhpCsjZeukZGukZGylZq0 l52zlZWzlZWwmpqwmpq0joyuiIaie2ebdWGZeG2ffnOjfnuqhIKnfnSmfXOfe2WSb1qRaFOUalWZ cGiedW2leGuleGufdGGab1yWa1aUaVSUaVSXbVeidF+oemWoeF2oeF2reWiwfm2zg3Wvf3Kre1qe b06hc1yqe2Sqfm2ugnCue2ilc1+jblWibVSjc1uqeWG0h36yhHuwhG60iHKwgmquf2iseWWreGSo d2WndWSvgnq3iYK0jHmvh3SwgHCufm6sf26rfm2wg366jIe2kIu0jomziYCziYCRg4yShI2WgIiZ g4ubi5edjJmajaGajaGhjJeahpGSgoyVhI6Zi5SajJWdh5GahI6XhIiSf4OLf36Lf36XgIaZgoed goujiJGfkZqhkpujlaOjlaOflJ+ekp6fkZqhkpulmaemmqimnaqroq+rn66onaumm6Khlp2ekJme kJmfl6ehmaimmaunmqyqoa6qoa6upbKqoa6vpq6vpq6rpbCooq6ml6ajlaOim6Wim6WnmqGll56h lJqdkJaekJmhkpufmaWZkp6djZWXiJCbiI6fjJKdjZWhkZmol6esm6uml6afkZ+ZjI2WiYuZhH6Q e3WShISbjY2blZuhmqGnlqGnlqGZjpCQhoeZiZGdjZWekZedkJaeiI2dh4ybho2Zg4uih5CnjJWo kZmnkJedh4ydh4yXgoeWgIaafoCUeHqVfXiUe3eSfYKVf4SXgoedh4yii5emjpull56bjpWXfoKV e3+Qgn2UhoChi5WeiJKei46ZhomXgIaii5CfiZGeiJCjhoidf4Kfg4amiYyrkJmnjJWahIyQeoKV eXmWenqbgIehhoyii5CfiI2biYiikI6lkI2ijYuhjpCbiYubf3+UeHiRdHeOcnSQdHebf4Kli4yo jpCmjI2fhoeWen2RdXiQdHCXe3ijiYurkZKojo2mjIumiIuihIejh4Kfg36ihoaukZG0lJGvjoys h4KifXiWdW2ObmWfe36rh4mrkZWulJewlJmwlJmzkZWzkZWvkpe2mZ64l5WsjImng3eeem6VdGiX d2qfe3Ojf3emfnCiem2heWeUbVuSaFCRZ0+WaVedb12idGKhc2GmeGOhc16Xa06UaEqXZ0+ebVWh c1ymeGGnd1yoeF2meGOoemWvg3SsgHKhe1ubd1aec2KmemmsgHKsgHKugG+oe2qmdF6hb1qmdF6u e2WyhnSzh3Wvg2+whHCugnCsgG+re3Cre3CreGureGune2qvg3K0iHmyhnezh3qvg3esg3mrgniy iIK4joi3jou0jIiygHmwf3iShomUh4uXgoyXgoydh46eiJCekqGajp2bjZmajJeVh5CVh5CWi5SV iZKZhJCVgIyRfoKQfYCOf4KSg4aZg4udh46biJGdiZKZi5SekJmfkZ+ekJ6ejZefjpmjkJmijpej laGrnaiqoa6on6ynna6mm6yml6GfkZqdjZWdjZWhkaOmlqillaimlqqmmaunmqyvn7Kvn7KopbCq prKupbKnnquolZ6lkZqjl6Gjl6Gilp+flJ2llZ2jlJujlZ6omqOlm6ielaKhkpuajJWZi5SajJWZ jZabkJmjl6ammqionaummqiel56VjpWXfoKVe3+Vg4SbiYuflJ2mmqOrmqWmlZ+akJSRh4uXi5GX i5Gdl5mdl5mbjJGai5CUh42ViI6ahI6jjZeikpqjlJuhkJ2fjpuhiZuii52XgoeRe4CXg4OXg4OU f4KXg4aUgoCVg4KXiJCfkJehkZmejpabhoiWgIOUfoObhoudjJmjkp+mjpaii5KfiI2ii5CdiZCd iZCjh4yfg4iahIyhi5KnkZuijJaZgI2Ue4idf4eegIidh4mhi42hiY6fiI2ijIyjjY2mjIunjYyh jIyijY2dgIaXe4CUd3mUd3mNc2+SeHSni4erjouoi42jhoiaeHWXdXOVeXmZfX2mjJCwlpqvlZas kpSriYumhIalh4eegICliIiskJCylZWukZGvi3+qhnqffW2WdGSfgHeniH6ukZSukZSylJawkpWy jZK0kJWzkZe2lJqzlpuvkpezjYiog36heWubdGeadGmdd2ulemefdWKhdV2ab1eaaFOWZE+UY0ya aVGhb1eicFimcFqoc1yicFabalCZaU+eblSmdGOreWild2KmeGOld1+qe2Swg3eugHSofmOlel+d dGqheG6sgn+rgH6wgHWufnOqe2moemiqemqwgHCyhne2iXq3iXqzhneugnOrf3Cqfm+rf3Crfm2q fWuoeWuygnSyiH+0i4K2iIO3iYS0h36wg3qyg3q8jYS4i4O6jIS0hHSsfW2Vho2ZiZGah5CXhI2a h5Cah5CeiZWeiZWejZ2ZiJeZi5SXiZKZhJCXg46VgouVgouRfoKRfoKXgoydh5GdiJSfi5aZi5SX iZKdhpKfiJWii5qii5qhjJeijZmmjZqmjZqllaerm66snqqqm6ennqiimaOjlZ6fkZqhjZahjZah kpuilJ2ikqWikqWmlKenlaiumaqwm6yupa+so66rn6uqnqqnl52ikpeilKKllqWelZ+hl6Kol6Kj kp2jlaOrnaurnrCmmaullKOfjp6hkJ2hkJ2ekJuhkp6lmaWmmqanmq6om6+qoauhl6KVhI6RgIua hIyhi5KelZ2lm6OqnqehlZ6fkJKWh4mbjI6ejpGnmqGll56ejpSai5CXhIuZhoydiZKfjJWhkJ2i kZ6nl6qmlqinlaijkaWbiIyWg4eXhIibiIybiYuaiImXgoKZg4OXiIudjZChjZGfjJChhoyhhoya g4ihiY6fjpumlaKrlJ6nkJqliZCjiI6ah4uZhomei46ah4uahIyfiZGnkZmhi5KZgomWf4eag4id houdiIieiYmeiI2hi5Cmi5GojZSnjJKmi5GijIyjjY2hhImegoeZg4ORe3uReXONdW+if36riIeq iIylg4ebenKXd26bf3qihoCoi5KwkpqvlZmwlpqsi4yqiImliYabgH2hh4amjIuwlJawlJa0kY6s iYelfW+bdGeZeXWff3uojIyskJCylJSylJSzkI6zkI6ykJa2lJq3mZ6ylJmujI2riYusf3OleGuf dGGhdWKmemSjeGKecliablWeaFSXYk6UZE2XaFCeclihdFuodFWseFindVufblSbalOhb1ereGuv e2+qem2neGqleGmnemuvgnWyhHirfWWrfWWieW+le3Kug4Csgn+yfm2uemmoemWneWSrfnKyhHi0 jYa4kYm6kIe0i4K2iXqugnOsgGisgGiqf2uqf2uqe3Owgnm2iIO7jYi3jo24kI64i4Kzhn2ugnC4 jHq3jYO3jYO2iHevgnCbh5Kbh5KZiJWWhpKVhI6VhI6WgJWdh5uXiJqVhpeXh5SWhpKbg5mdhJqa hI6Zg42XhIuWg4meiZefi5mdjpqekJuajZSXi5GXhIuah42hjZadiZKhkJ+hkJ+hkJ+ikaGmlaKo l6Wml6allqWml6ajlaOilqWdkZ+fkpmdkJaekZWekZWfkJehkZmjkp+llKGmlaKsm6isoa+vo7Ks nqeqm6WikpWbjI6jjpqlkJubkJuekp6nkp6nkp6hlKaom66om7KqnbOqmqyllaejl6ajl6aekp6e kp6fmaWlnqqqmqyomauuoaeom6KhkpuZi5SbjJSdjZWekp6lmaWsm6anlqGjjo6diIiaiImjkZKq maOqmaOmkZGbh4edhouag4ibjJSejpalkJurlqKqnbCrnrKqm6qilKKfiZShi5WhjZaijpefjJWX hI2SfXqUfnubh4efi4uhjZafjJWfiJefiJeeg4mdgoiei5GlkZeqkZ6okJ2ijI6dh4mXg4aahoia h4uZhombh4mfi42jjpGfi42ahIeXgoShi5WnkZujkJSfjJChi5CljpSmjpmqkp2rkJaih42jiI6j iI6liZKih5CeiI2Vf4SVgnuVgnuhg4irjZKrjo6hhISef3ObfXCihoani4ujjZKokpe0lp60lp6r jZKoi5CjiIShhoKih4OnjIiukpu0maKzlJuvkJerg4KddXSWdXeaeXqfgoSqjI6vkZasjpSyjZCz jpGvjY6ykJGyjZKzjpSyjImwi4isg3mmfXOieF+hd16leFyhdFibb1Sbb1SdbVCZaU2XblGZb1Of c2Kjd2Wnel6qfWGod1ymdFqmb1umb1undGWyfm+qem2oeWumem6qfnKsg3mrgnirf2usgG2rfm+u gHKrgnmrgnmyg3CsfmuoeF+oeF+qfm+whHWykY62lZK8lpG3kYy0i4Kwh36uf2qsfmmofWmofWmn fWmug2+yhH24i4O3jYe4joi0h3WugG+sfW2zg3OyhH24i4O6jHq3iXiZi5aajJeXjJqSh5WSgo6U g5CVhJSWhpWUhpGUhpGUhIyUhIyahpGeiZWZiJWWhpKZho6diZKfkZ2fkZ2bkJubkJuZiY6VhouU hImWh4ybi5WejZehlaOflKKhkp6ekJuljpmqlJ6llJ6llJ6imaGimaGilqWekqGakZmZkJefkJWe jpSejZehkJqjkp+jkp+nlqOrmqeqna+uobOwoquqm6WjlJabjI6hi5KjjZWbjpWekZemkJemkJeh kqGllqWnna+nna+qm6Wml6Glm6ijmqeimaahl6Welqafl6ejlqqqnbCwoq6un6unmaejlaOekJ6e kJ6jlaGomqawm6qrlqWnkJeii5KeiJCmkJermqqqmainlZadi4ybh4mfi42ijpWei5GjkJmsmaKr nrCsn7KrlqKnkp6liZKmi5Sqkp+qkp+jjpqahpGWf4SUfYKeiIihi4uljZeii5Wfi5afi5ahh4uZ f4Obh4mjjpGnkJeokZmijIyfiYmbf3+egoKehIObgoCeiI2dh4yfiYmijIyahISXgoKeh46slZ2r lZ2jjZWmkJKokpWnkJeulp6vkZmjho2ihImihImih5Cih5CeiJKbhpCeiIuXgoShhImrjpSskpSl i4yhhH2fg3uni5CskJWqjpWskZezlZ22l5+qjpWnjJKmiI2lh4ymiYmni4uukJezlZ2wlJmylZqs iYSffXiad26beG+igIKujI2zlJ6ykp2wjpCujI2ujYuvjoyyjIu0jo2zjI6viIuvh4Oqgn6lf3Cj fm+ieGKhd2Gicleicleiclqfb1efdF6jeGKnenKqfXSrfWirfWiufVuoeFamdV2ldFyjdWGqe2er fWqsfmuoeWuqem2ofXCqfnKrfnKugHSqe2eqe2eugHKzhnewg3Ksf26od1qlc1aleGurfnKyjIe2 kIu7ko66kY23hn+2hH62g3KvfWureWisemmnfWSqf2euhH62jIa7jYS4i4K3g3KseWimd2eoeWmq e3OzhHu3h3e3h3eVh5KZi5aWiZ2ViJuWhpWWhpWUhpGShJCSgoyUg42UhImWh4ybi5qdjJuZiZua i52ejJ+ejJ+bkJudkZ2dkpaZjpKWh4ySg4iVf4ebho2fjJWjkJmimqqfl6efkpmekZeejpSllZqo lKKrlqWmmqajl6Ojl6OhlaGhl6KelZ+ekp6ekp6fkZ+hkqGjkp+llKGlm6ijmqemlqirm66snqyo mqihkZaejpSljpamkJefkJKhkZSokZaljZKijZmlkJunnqumnaqlm6ilm6immaummauim6ehmqaf lKKhlaOnlqaunayunrKsnbCnlainlaillqKilJ+qkp+wmaarmqenlqOljpafiZGfiZSnkZuolqqr mayilp+ajpeejpaikpqnkZuhi5WhkJqrmqWqnqqonairlZ+nkZuljJmnjpuqlJ6okp2ikJGdi4yX fn2Xfn2ihoajh4eljZWnkJeijZmhjJeli4mbgoCdg4enjZGmjpaokZmljpGijI6jgoOjgoOlg4Sn hoehjIydiIiehIahh4ibg36bg36eh46rlJurl56nlJqmjpSnkJWojZaylp+wkpqoi5KihoadgICe g4mfhIubhJGeh5Shho6eg4yhg4uniZGskJKni42ih4Kih4KrjZeukJqylJuylJu0maKzl6GskpSs kpSniYylh4mlh4mmiIusjZevkJqzlZq0lpuvjoijg32heG6ddGqbfoCniYywkZu0lZ+wkI2ri4ir iIaqh4Srh4mqhoiuiIerhoSuhoKrg3+ogHCnf2+oemOneWKmdFqmdFqndGGreGSqemqufm6ugHiw g3qzhnevgnOwf2eremKrelqldFSleFyoe1+sfmmvgGuzenKvd26odWeodWeoeWmre2une2ine2io fXCugnWvg3KsgG+ye1+mcFWhc1yjdV6wg3u6jIS+kpK+kpK+iX23g3e6hHi3gnWyemiveGWoemWo emWuf3e3iH+4jHq0iHezf2muemSmdF6mdF6meGWoemiyg263iHOWgo2diJSZjJ+ajaGaiZaVhJGR g4yMfoeRfYiUf4uXh5GejZefkKKejqGdkKKdkKKmkaKijZ6hiZmii5qbjpWXi5GWh4yVhouXgoeZ g4ieiJKjjZeflKKflKKfkpadkJShjZajkJmolKKqlaOilqWlmaeimaahl6Wmlqinl6qnm6qjl6af maWblaGllqWllqWll6qll6qnl66oma+umaWqlaGfkJeikpqnkZumkJqdkpSdkpSjkJahjZShjZGi jpKilZmmmZ2mmqajl6Oomqiqm6qmnaqjmqehlaOekqGnlqGsm6ammayll6umlqqqmq6nl6qnl6qq m6eun6ummqahlaGolZ6jkJmhkpunmaKmlaWol6eilJ+hkp6llqKnmaWsl6OjjpqikZ6unaqvnqiq maOmkJeljpajkJmlkZqrlJurlJuijZCeiYyag4iag4ihiY6jjJGnlJ2mkpuikpqhkZmijImdh4Sd iIunkpWolJ+mkZ2okZunkJqjhouihImfiI2ii5CmkJeijJSfiYyfiYybhoieiIumiJWylKGwmaGu lp6qkJSojpKmjparlJuwlZunjJKdgHuZfXiSfX2WgICVgIOZhIebhImdhoufhIumi5GojpKli46j jJGjjJGojJ2wlKWzlqWzlqW3m6e2mqawm56rlpmoi42lh4mohoSmg4KmjJCojpKzkJm3lJ23kpWu iYyognqie3SfgoSlh4msjpSvkZasjImqiYeviICqg3umfn2je3qog4Cngn+rhH2uh3+whHiugnWv fWSsemKod1yndVuleWWqfmqsgHKvg3SyiH+ziYC6jH+2iHuyg26uf2qwe1urd1asel2zgGOvgnCy hHO0gHKreGmjc1iiclemdFyod16mc12reGKsemewfmqyf26yf26wfmindV+jdFOfcE+oenK3iH+/ jo3FlJLBjIK7h323g3Szf3C6g2uveWKoeF+remKvgG60hnO0jHm0jHm0hnCrfWiremKnd16wfmqz gG22hnW2hnWVgIydiJSbjJ6bjJ6bh5KXg46SfoyRfYuQf4mRgIuZi5aekJujlaOjlaOflJ+flJ+h lZ6dkZqfjJWfjJWekZeZjJKXhIuVgoiWhIaXhoeXiJCdjZWhkJqhkJqhjZabiJGhi5WljpmilJ2j lZ6jl6Gilp+ilqWlmaeil6qjmauooq6lnqqjl6aflKKmlKehjqKikaGllKOjlKaikqWqlp+nlJ2d kpmhlp2llqKllqKimaGimaGmlZ+hkJqdkJSdkJSfkJWllZqflJ2hlZ6hlaGilqKmmauom66lm6ie laKilqKlmaWrmqqrmqqvma6vma6omayrm6+un66voa+mmZ+jlp2mlZ+llJ6hlaOmmqiol6Wjkp+l kJ6mkZ+qlaaumaqwm6emkZ2ikZ6unaqznqyumaeskZqojZaljpaljpaqlJmnkZaeiI2bhouZg42a hI6hiZamjpumlZ+llJ6mkpulkZqii5Cdhouii5KokZmsl6aumaerlKGqkp+sjpalh46hi5Kljpar lZ+qlJ6mkJWijJGfiYyfiYyliZWrkJuzlZ+0lqGskpanjZGnkJWslZqslpumkJWmg4KffXuUeXSQ dXCWe3idgn6Zg4Cdh4SiiIyli46ijJGhi5CljZWljZWojJqukZ+zlqW0l6aymaawl6Wvl52ulpus jo6jhoafg4OZfX2fg4aliIuojJGwlJmwlJarjpGqg4ajfX+bfn6lh4eujI2vjY6sjIioiISnhn2j gnmie3KhenClf3qjfnmnfnSqgHergnirgnivfmOse2Goe2KnemGsfW2vf2+vf3K0hHe2jIO3jYS4 jH+2iX22h3Kuf2qzfWGye1+wfmO0gme2gni7h324iHiufm6jdFOZakmda1Ghb1WqclqyeWGvfmWz gmmwhG6yhm+zgG2vfWmveV+oc1qseW+4hHq8jYbDlIy+iX+6hnu2hnW0hHS3hG6yf2mzel+udVus f3C2iHm0jH6yiXuwgm+uf22vfWereWOwgnmwgnm3jIm8kY6Wgo2diJSbh5WahpSXg46Wgo2Rg5GR g5GUhpGZi5abjJ6ejqGhkqGjlaOllJ6mlZ+hlZ6ekpuajZSZjJKei5Sei5SXhIuVgoiWgIaZg4ia h5CfjJWbjpKZjJCbiI6Zhoyah42hjZSekZehlJqjlZ6ilJ2hlZ6ilp+llaerm66qnqylmaeikqWi kqWjlaGekJuekZefkpmfkJefkJeikpqjlJumlp6llZ2ilJ+fkZ2jlaOnmaeml6OilJ+bkZeakJae jpafkJedjpedjpedjpejlZ6jlKaqmqylmquil6iol6eol6eml6OnmaWsm6urmqqunrKvn7OuobOu obOnmqGilZullaemlqill6ummayllqWfkZ+hkJqhkJqulqiwmauymqqmjp6mlaKqmaaun66omqio kZuljZelkZemkpmmlJWhjpCjiI6fhIuXhI2diZKeiZWhjJellqKnmaWvl6Kqkp2ljZeii5Wljpmo kp2wm6eynaiym6aym6asjpmniZSikZuol6KwmaiwmaivmZ6nkZahiZGfiJCliZKrkJmylqKzl6Or kJmmi5SqjJSsjpaolZ6lkZqniYmihISXe3iUeHSVdXKaeneZfnOfhHmliIimiYmniZGmiJCmi5Sm i5SliZCnjJKskZqwlZ6ylqKvlJ+wlpewlpeuko2liYSfhICWe3ideniif32jh4yvkpewkpqsjpas hIOnf36hgH6ign+nh4SujYuujI2riYuriIajgH6mfnCed2mheWuje26mem6sgHSshHeuhnirfWin eWSreWireWiqem2ufnCygnSygnSwiHi3jn64kH+3jn67h3iyfm+zgGizgGivfWu4hnS2iIC8joe/ kH24iXesel2fblGaaFWea1imdF6wfmisfmmsfmmsfmmvgGuvgG6wgm+vf3KoeWuvfne2hH28jYy/ kI67jX62iHm0h3q2iHu2h3S0hnOygGiufWSyg3u4iYKziX+yiH6vhHCqf2uufm6zg3O2h4m6i428 kpnBlp2Wh4yVhouUgImSf4iRfoSSf4aVhJSZiJeZh5qbiZ2dkZ2dkZ2bjZullqWml6OjlaGhkp6e kJuajJWWiJGfi5meiZeah5CZho6ah4ufjJCfjY6ejI2UiY2SiIyWhIOWhIOUgoObiYubjpWekZef kZ2ilJ+ilJ+hkp6llqKnmaWnl6qfkKKdkKKekaOekJuXiZWbi5WejZefkJWhkZankp6qlaGnl5+l lZ2hlJqfkpmllJ6nlqGnlqOmlaKflZudkpmejpafkJedjpeekJmhkp6jlaGnl6urm6+mmauom66y na6vmqujlaGjlaGol6eunayunay0o7OwpbCvo6+sl6afi5mjlKarm66smq6rmaynlqOikZ6ikpqh kZmolKWvmquwmauqkqWnkp6qlaGnmaellqWolKKlkJ6nkZmokpqolZmei46fhomfhomag4udho2b iI6ei5GhkJqol6Kvl6KslZ+njY6jiYuejI2jkZKsl6iznq+0n6u0n6uvl5+qkpqllqKqm6e0n7C2 obK0naeokZujjJafiJKliZKrkJmulqGwmaOrkZWli46njY6njY6ijpWjkJaljpGdh4mXe3eWenWX c26Xc26ZeXWlhICliIini4uui5SriJGmiYymiYyfhoSbgoCjhIylho2ni5CrjpSojpCskpSqlY6l kImlg3qffnWbenKefXSif4ivjJWqjZKskJWriIOhfnmaeHWffXqmgH+rhoSmiI2oi5Crh4mmgoSi f32ee3mZdWmWc2efeGinf2+qhHWrhnesgmuofmiqem2re26sfW2vf2+vf3KsfW+ogHOyiXu6jIS/ kYm7iYK6iIC2g2+yf2uufnO2hnq2jYm6kY27jnq2iXWyf2SjcleXalafcl2ofWmugm6yg3Cwgm+u fm6ufm6ugnOwhHWygnewgHWyhHu3iYC3koa6lYi+kX23i3e3h3m0hHe2h3S3iHW0iHSzh3OzhoC3 iYSviH2viH2whG6ugmuvgnW2iHu4iYy8jZC+lJ3Cl6GZiJKXh5GZg4uVf4eSfX+UfoCZg42eiJKX i5GXi5GbjZadjpedjp2ml6ailqKilqKekpuajpebjZaXiZKaiZSZiJKeiZWfi5afkZqhkpuhi5Kd h46ZiJKXh5GXg4aVgIOUf4KXg4aah4uei46ejpafkJeikpqhkZmjlKaomauolq6ejKObi5qdjJui jZmdiJSei5Sei5ShjZSlkZeolZ6olZ6mlp6ikpqbkZWelJellKGnlqOol6eol6enm6WlmaKolKKn kqGfkZqhkpuikpejlJmhlaOonaunm6esoayunayqmaillKGjkp+llKOqmairnauzpbO3prayobCr lKGii5elkqaql6usm6iqmaaomZ6hkZaikpqejpalkJ6qlaOumaenkqGmkJWnkZajkp+mlaKrmqen lqOmkpunlJ2nlJqfjJKdh4ybhouZg4iZg4idiIuhjI6ijpWnlJqqlJmokpemjI2hh4idiIiijY2o kZ6vl6Wwn6yyoa6znaWrlZ2ll56mmZ+wn6yzoq+3n6qslZ+miJCdf4edgoinjJKnjJWwlZ6qkJGj iYuhhoCfhH+fhIuliZCijIyfiYmegn6afnqaeneZeXWZeXelhIKljZKnkJWukJKqjI6miYyni42j h4Kbf3qffXiffXihgH6jg4CfgoKniYmqjYmmiYalgoCif36dfnSbfXOffoKoh4uskJWskJWrjH+m h3qdeW2deW2ifXqmgH6jgoOmhIaqhIOmgH+hfn2ffXuefXSZeG+heG6qgHeviYSyjIe0iHmugnOu f22vgG6vgGuuf2qrf2mrf2mqeXKwf3izh3q8kIO7jIS8jYa6i3Wuf2que2qzgG+0i4S7kYu4kIKz i32whminfV+ablWdcFeoenWzhH+zhnewg3Svf3KufnCqfnKwhHi2hnq3h3uyhnmzh3qzjIC4kYbB kIS8jIC6h3OzgG2wgHC3h3e3h3e3h3ewh362jIOuhHquhHque2WqeGKsfmuzhHK4iInBkJG/lJXB lZaaiZSaiZSfg4ibf4SWe4Kaf4ahjJeijZmZjJKWiZCah42fjJKikaGqmaimlqijlKabkpqZkJeV i5GVi5GWjI2bkZKhkJ+nlqaom66jlqifiZGahIyShI2Uho6ahoaZhISXg4OZhISfi42hjI6fjJKh jZShlJqhlJqlkqamlKemkZ+hjJqfiZGfiZGijJahi5WejpabjJSjkJmolZ6ml6GjlZ6omZ6ikpej lZ6llp+ol6KmlZ+qmqyrm66onaurn66umaqqlaanlqallKOolZuolZuXjpahl5+nm6qqnqysm6ur mqqllKGikZ6ikKOjkaWqmqywobO0pberm66ilJ+ajJehjqKjkaWumaWumaWqmp+ikpehlJefkpaj kJmnlJ2rlqKnkp6mkJWljpSfjpmjkp2olKWvmqunkJ+okaGfkZGekJCdjoyWiIaWi4mViYifiYem kI2nkJerlJurlZqokpejjY2eiIifiYyijI6mjpmrlJ6vmqa0n6uym6aslqGolZumkpmsm6ayoau6 oa6slKGsi4ybenuWfYCehIiihIysjpamjIuiiIejgnmjgnmjg4ClhIKlh4mlh4mihoafg4Oeg3+d gn6df4Klh4mvlJ2wlZ6ukpmrkJaniZGoi5KsiH+lgHifeW6feW6ZdWqbeG2ie3SrhH2uiIOog36l goChfn2efXCaeW2XdXOhfnuriYuujI2ujYemhn+ie3Cdd2uXdXCZd3KfeW+mf3Wrfneoe3Soenum eHmmeXKhdG2hdG+ugHu0jIy4kJC2joeviIC0gG+zf26uf2iuf2irfWiuf2qofmqme2iqgnSyiXuz joa3kom/joO2hnqwfWmwfWm2iIO8jom+loy4kYe3jHiug2+meGOjdWGnfniwh4C4hHWyfm+uemes eWWufnOygnewgnm2h360iHmyhne0h3q/kYTFkIbBjIK4hnS0gnC2gni4hHq6hnu7h322iXW4jHi2 i3evhHCrel+nd1yreGSzf2u2hIDDkY2+lI28koybjZmXiZWahI6Zg42dhpCdhpCdjJuejZ2Uh42O goiRhIiXi46ikKOmlKenkaqjjaabkZeZjpWXi5GajZSVkJGblpeilq+mmrOrmqemlaKfhIufhIub iJGbiJGbhouahImXiYmWiIiei5GjkJajkJahjZSbkJudkZ2lkKGlkKGjjJmdhpKZiY6ejpSnkp6o lJ+ikZ6hkJ2ml6Oomqammqinm6qunrCunrComqiml6aimaOhl6KomauunrCsnbCrm6+lmaWjl6Os nqqrnaillJ6hkJqah5CfjJWilKKqm6qrnairnainlqGfjpmejZqfjpuikKOvnbCwoq6snqqmkpue i5Sii5KmjpajlJunl5+jlJmhkZafkJWikpejkJamkpmnlJ2olZ6njJKliZCeiZWijZmnkJ2vl6Wo maGjlJunkpWmkZShjZGbiIydjZKai5Cfi4ulkJCnkJWqkpeqkperlJmii5ChiY6ehIifhomhhoyl iZCulaK2naqwl6WulaKqjZKrjpSokpq0nqa2n6qul6Kni4OZfXWUeXKVenOdf3+miIiihoaihoaf hH+dgn2jgoOlg4SmhIamhIaeiIieiIihh4ahh4ang4uuiZGzl6Ozl6O0lqGwkp2qi5Kmh46rhoOn gn+hf3SaeW6acmiddGqbeG2hfXKrhoCrhoCqgoCje3qdenmbeXiXd26aeXCnf36vh4aoiIKignuh enCZc2mUcGWWc2iac2OddWWjeGmmemuleHOjd3KmdW+jc22bc22ieXOsh4S2kI26kIm0i4SshHeu hnivg3Svg3SogG6ogG6ugnCne2qoe2+vgnWwi4a3kYy8jom6jIe2hnWwgHCzhoC7jYi/lY64joi6 jH23iXqugnOrf3Cwgnm3iH+2h2+yg2uue2WvfWevgG6wgm+vf3Kzg3WuhnWshHSwg3u8joe/kYS8 joK6iXu4iHq2hnqwgHW2gnW6hnmyg260hnC3i3KwhGuvfWKsel+reGSwfWmzg3jCkYbBkYm+joed jJmaiZaXh5aWhpWXg46bh5KZi5aXiZWVgouQfYaVf4edh46ijZ6nkqOllqKjlaGbkZWZjpKai5KX iJCbkJmjl6Gnn7Onn7OrmqehkJ2ahImbhouWjJKbkZejjZehi5WhiZGhiZGdjJadjJadjpqajJee kJ6ekJ6fkZ+fkZ+iiZafh5SZiJWikZ6ol6enlqallaeikqWjl6anm6qunrCsna+zobawnrOmmaui laejl6Ommqaqm6qsnqyvoa+rnauqnaOsn6awn6qyoaurlKGeh5SRgoeVhoudiJankqGfmaWhmqau laKiiZahi5KfiZGhjJevmqaznqywm6qljpadh46dh4yijJGhkZmmlp6jlpqekZWijpWmkpmokpen kZaulp6qkpqliZKliZKjh5WmiZeokZ6okZ6rl56olZuqlJuokpqhjZGdiY2bjI6bjI6njJKmi5Gl jZKljZKqlJaslpmrkJajiI6dh4mdh4mXgoSeiIuqjJmzlaKykqKujp6vjoysjImrjZe3maO3n6qw maOwjI6mgoSafneWenOZfnmjiIOlh4emiIiiiIejiYiniYmqjIyni4eliISihoiliIuli4yiiImn iYyoi42zlZ+4mqW6mqWykp2zjJSwiZGniYylh4mhhH2dgHmhenCeeG6Wc2qeenKqgn6qgn6shn6o gnqheXWed3OZdWqWc2ibeXSffXilgHijf3eieXOXb2mXb2WVbWOWa1iZbluecmGhdGOjd2+meXKl d3KbbmmWcGmadG2ogH+3jo22lIuwjoauhHqsg3mzhHu0hn2zhn6zhn6yjH2lf3CmeWqqfW6uh3+y i4O6jIe6jIe6hne0gHK2h3+/kIi8kZG6jo64i362iHuwiHqvh3mvh3mvh3m0iXOziHKzhG+zhG+w hHOyhnSwhG6ugmuofWene2WsfW+zg3W7jIO+joa8jn+7jX62hnW0hHS0gm62g2+0gG27h3O3i3m3 i3myhm2rf2eyhne0iHmyi4O3kIi7jIu3iIediZKfjJWaiZSWhpCUho6XiZKZiZGUhIyReoeQeYaU foOZg4ibjZafkZqjkp+llKGbkZWXjZGai5CXiI2jkp+rmqeqn7Cnna6ol6KdjJaahImdh4yekZei lZuolZ6lkZqjjJSljZWdjZWbjJSbjpWbjpWejJ+hjqKhjqKfjaGfi5mhjJqbkJujl6Ovmquumaqn lqOikZ6nmaernaurnrCsn7Kyo7KworCmmaufkqWolJ+rlqKsl6Ovmqasoaqjl6Gilp+nm6Wsnqys nqyikpqVho2SfX+UfoCdg4eojpKekp6hlaGnkp6eiZWai42Vhoibho2mkJeunrKrm6+nlJqdiZCe iJChi5KhkJqmlZ+ll5ubjpKhkZamlpunlJqqlp2slqGnkZuiiZahiJWliZWliZWmkJqqlJ6vlqOs lKGslaKqkp+nkZGijIydh4yijJGskZqqjpeljpGmkJKulpuulpuukZGliIibhoiXgoSUfYKZgoei hoiukZSvkZmrjZWrjY2miIioi5eylKGzl6OwlaGzkJunhJCUfoCRe36bf3umiYaqjouliYali4yq kJGylZqylZqnjY6li4ymi5GnjJKmjJCjiY2oi5KniZGylJ64mqW0maWwlaGyi42viIumi4ejiISi hn6hhH2je3iheXWbeXeee3mjfnmngn2rg3Org3OhfWebeGKWcmGUb16Wc2iZdWqfeXKeeHCfeGqX cGOdal6ZZ1uSZVSUZ1Wfbl2mdGOldWineGqoeWuhcmSXcGOac2Wleni2i4i+lo63kIiwhHisgHSy hH2zhn6wh364joa6lISviXqqeGSmdGGoenKwgnm4iYS4iYS4iH23h3u0h367jYS+kIu7jYi7h320 gHeyhnmzh3qzh3qzh3q0iHu0iHu7i327i326jH26jH2zhG2vgGmofmWnfWSremKufWS3g3m7h327 jX68jn+4iHivf2+zgGqvfWeve2W4hG64iYC7jIO2iXqzh3i4lIu4lIu2kpG3lJK7kI24jYuXiJCW h46XiI2XiI2XiJCai5KZiZGVho2OeYCNeH+SfX+bhoidjJmdjJmdi56ejJ+ajo2ViYiXhIibiIyl kJ6qlaOlmaWflJ+ljpGeiIuWg4eah4uhkZallZqilZuilZuijJaahI6Wg4mVgoiViI6WiZCbi5Wd jJaeiZqdiJmhiZmljZ2ikZ6ol6Wsl6ilkKGjjZWhi5KqkqWulqiom66uobOwoquml6GikpqfkJel kZeqlp2umqOsmaKrm6OomaGmlaKnlqOonaulmaediY2UgISQeniRe3mZe36ihIeikpqikpqekpuZ jZaUiY2Ng4eWgo2fi5aml6aqm6qll5uajZGdiZChjZSmlZ+ol6KikpeejpSfjJWjkJmqmaOol6Kv lqOokJ2ii5WjjJaijJSijJSmi5SskZqolKWsl6iwl6WvlqOmkZSijZCfiZGljpaukpmukpmqlJal jpGslZ+wmaOukZSliIuehIaehIaagIKZf4CehoCokIuukZSqjZCojIyihoamiJWoi5eskZqskZqu kZaojJGbgIybgIyfg4ani42ujpmvkJqoi5erjZq0maWylqKqjpWukpmrkJmqjpelh4mhg4aliI2l iI2sjJu2laW2mqOylp+vjY6nhoehhISfg4Onhn2lg3qmgHuhe3eff3uign6mgHuog36qhn2ng3qn fWmdc1+WalqQZFSQZVWWa1uZbl2ab16XdF6XdF6abViXalaVaFaWaVebbWKhcmeldWimd2mleWWh dWKab2GdcmOhdWSugnC6koi2joSuhHqqgHeuhHqyiH6rh3qyjYC3kX2zjXmvfmOmdVufdGGmemeu fXW0g3u4iH23h3uzh3q4jH+7jIO6i4K0hHewgHOugmuwhG6yiXmwiHi0h3q6jH++joe/kIi8joa7 jYS3iHWsfmusfmeqe2Sue2WsemSzf3O4hHi2hni6iXu7iHe4hnS2gHKyfW6weWeze2m4h4O7iYa3 jou7ko64lJa4lJa8l5q+mZu/lpK4kIwADQEAAAMAAAABAQAAAAEBAAMAAAABAQAAAAECAAMAAAAD AAMAqgEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAACAAMAsAESAAMAAAABAAEAAAEVAAMA AAABAAMAAAEWAAMAAAABAKoAAAEXAAQAAAACAAMAuAEcAAMAAAABAAEAAAFTAAMAAAADAAMAwIdz AAcAABDoAAMAxgAAAAAACAAIAAgAAAAIAAH+CAAB/gAAAQIAAAEAAQABAAAQ6GFwcGwCAAAAbW50 clJHQiBYWVogB9YABAAeABAAAgAkYWNzcEFQUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPbW AAEAAAAA0y1hcHBs8Km3nXI3UvdB2LuwZ9wBHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAOclhZWgAAASwAAAAUZ1hZWgAAAUAAAAAUYlhZWgAAAVQAAAAUd3RwdAAAAWgAAAAUY2hhZAAA AXwAAAAsclRSQwAAAagAAAAOZ1RSQwAAAbgAAAAOYlRSQwAAAcgAAAAOdmNndAAAAdgAAAYSbmRp bgAAB+wAAAY+ZGVzYwAADiwAAABkZHNjbQAADpAAAAH+bW1vZAAAEJAAAAAoY3BydAAAELgAAAAt WFlaIAAAAAAAAF1MAAA01QAAB9tYWVogAAAAAAAAdAUAALP7AAAiflhZWiAAAAAAAAAlhQAAF0sA AKjMWFlaIAAAAAAAAPNSAAEAAAABFs9zZjMyAAAAAAABDEIAAAXe///zJgAAB5IAAP2R///7ov// /aMAAAPcAADAbGN1cnYAAAAAAAAAAQHNAABjdXJ2AAAAAAAAAAEBzQAAY3VydgAAAAAAAAABAc0A AHZjZ3QAAAAAAAAAAAADAQAAAgAAAgQC9wQFBQUGCgcFCAsJCAoNCwgMCg0QDg0PDxAOERASEBMS FBIVFBYXFxgYFhkaGhsbGhwdHR0eHyAeISIiIiMjJCUlJCYmJycpJyopKyosLC0tLi8vLzAxMjEz NDQ0NTQ2Njc3OTo6Ojs7PD09PT49Pz9BQkJCQ0NEQ0VFRkZISElJSklLTExMTk1PT1BQUVJSU1RU VVVWV1dXWFhaWltbXFxdXl5eYGBhYWJiY2RkZGVlZ2doaGlpamlra2xtbW1vcHBwcXJycnNydHR1 dHZ2eHh5eHp6e3t8fH19fn5/f4CAgYGCg4SDhYSGhoeHiIiJiYqKi4uMjI2Njo6Pj5CQkZGSkpOT lJSVlZaWl5eYmJmZmpqbm5ycnZ2enp+foKChoaKio6OkpKWlpiWmpqenqKipqaqqq6usrK2trq6v r7CwsbGysrOztLS1NLW1tra3t7i4ubm6uru7vLy9vb6+v7/AP8DAwcHCwsPDxMTFxcbGx8fIyMlI ycnKysvLzMzNzc5Nzs7Pz9DQ0dHSUdLS09PU1NXV1lXW1tfX2NjZ2dpZ2trb29zc3d3eXd7e39/g 4OHh4uLjYuPj5OTl5ebm5+foZ+jo6enq6uvr7Ozt7e5t7u7v7/Dw8fHy8vNy8/P09PX19vb39/j4 +fn6efr6+/v8/P39/v7/fv//AAACBAL3A3AEBAUJBgQHCggHCQwKBwsJDA8NDA4ODw0QDxEPEhET ERQTFRYWFxcVGBkZGhoZGxwcHB0eHh0fICAgISEiIyMiJSUmJicmKCcpKCoqKyssLS0tLzAwLzEy MjIzMjQ0NTU3ODg4OTk6Ozs7PDs9PT9AQEBBQUJBQ0NEREVFR0ZIR0lKSkpLSkxMTk5PUFBRUVFS UlRVVVVWVldXWFhZWVpbXFtdXV5eX19gYWFhY2NkZGVlZmZnZmhoaWpqamxtbW1ub29vcG9xcXJx c3N0dHV0dnZ3d3l5enp7e3x8fX1+fn9/gICBgIKCg4OEhIWFhoaHh4iIiYmKiouLjIyNjY6Oj4+Q kJGRkpKTk5SUlZWWlpeXmJiZmZqam5ucnJ2dnp6fn6CgoaGioqOjpKSlpaamp6eoqKmpqqqrKqur rKytra6ur6+wsLGxsrKzs7S0tbW2tre3uDe4uLm5urq7u7y8vb2+vr+/wMDBwcLCw8PExMXFxkXG xsfHyMjJycrKy8vMzM1Mzc3Ozs/P0NDR0dLS01LT09TU1dXW1tfX2NjZ2dpZ2trb29zc3d3e3t/f 4F/g4OHh4uLj4+Tk5eXm5udm5+fo6Onp6urr6+zs7e3u7u9u7+/w8PHx8vLz8/T09fX29vd29/f4 +Pn5+vr7+/z8/f3+/v9+//8AAAGCAmUDQAQcBPEFuwaJB1wIMQkHCdUKoQtyDEUNFA3jDrUPhBBR ER4R7hK5E4cUWBUnFfMWvReHGFEZGhngGqobdRw/HQUdyh6PH1UgHSDjIaoibyMwI/ckuSV6Jjwm /ifDKIQpRSoMKswrjSxNLQgtyS6IL0UwBDDFMYUyQzMVM+00wjWWNmw3QDgROOM5tTqHO1k8Lj0I Pdw+sj+JQF5BMEIAQtJDqER9RUlGHUbvR8RImUlrSjpLEEveTK1Nf05MTxlP6lC4UYNST1MbU+dU sVV5VkZXC1fRWJdZYFoqWvJbtlx4XTxeAl7FX4hgUGERYc5ii2NNZA1k02WoZpZnjWiLaXtqdWts bHBtYG5bb0VwOHEsciJzE3QFdPh153bTd7x4pXmUeoN7b3xOfTd+JH8Mf++A1IG8gp+DfoRohUiG KocPh/KI04m0ipaLeYxcjTuOHY79j9yQvpGhkoeTb5RPlS+WEpb4l96YvpmdmoCbZ5xRnTqeHp8D n/Sg9KH6ovuj9qT0pfCm76foqOCp46rdq9SszK3Err6vuLCzsa+yrLOqtKm1qbart664xrnMutO7 2rznvgG/FcAwwUnCbMOVxMHF78cfyFDJiMrSzCPNds7M0CnRmtMS1JHWJtfD2WjbJ90C3ujg7uMZ 5Wbn1uqP7Y/xDvVt+x7//wAAbmRpbgAAAAAAAAY2AACXOAAAVsIAAFQSAACKMAAAJ6sAABaoAABQ DQAAVDkAAiFHAAIR6wABRR4AAwEAAAIAAAABAAMACwAWACUANwBNAGUAgQCfAMEA5QELATUBYQGQ AcEB9QIrAmQCnwLcAxwDXwOjA+oENAR/BM0FHQVGBXAFxAYbBnQGzwctB4wH7ggfCFIIuAkgCYoJ 9gpkCtULRwuBC7wMMgyrDSYNog4hDmEOoQ8kD6kQLxC4EUMRzxIWEl0S7hOAFBUUqxVDFZAV3RZ5 FxcXthhYGKoY/BmhGkga8RucG/McSRz4HageWx8PH2ofxSB9ITch8iKwIw8jbyQwJPMltyZ+J0Yn qygQKNwpqSp5K0osHCzxLVwtxy6gL3kwVTEzMhIy8zPVNEc0uTWgNoc3cThcOUk6ODsoPBo9Dj4D Pn8++z/0QO5B6kLoQ+hE6UXsRvFH90j/SglLFEwhTTBOQE9SUGZRe1KSU6tUxVXhVv9YHlk/WmFb hVyrXdJe+2AlYVJif2TgZhJnR2h8abRq7WwnbWRuom/hcSJyZXOpdO92NnjJehV7Y3yyfgN/VYCp gf+DVoSvhgmIwoohi4GM445Hj6yREpJ7k+SWvJgrmZubDJx/n2qg4aJao9Wmz6hOqc6rUa5ar+Cx abLytgu3mbkpurq94b93wQ7Cp8RBx3vJGcq6zFvN/9FK0vHUm9ZF1/HZn9z/3rHgZOIZ49DnQej8 6rnsdu427/fxufVC9wj40Pqa/GX//wAAAAEAAwALACUANwBNAGUAgQCfAMEA5QELATUBYQGQAcEB 9QIrAmQCnwLcAxwDXwOjA+oENAR/BM0FHQVwBcQGGwZ0Bs8HLQdcB4wH7ghSCLgJIAmKCfYKZArV Cw4LRwu8DDIMqw0mDaIOIQ5hDqEPJA+pEC8QuBFDEc8SFhJdEu4TgBQVFKsVQxXdFisWeRcXF7YY WBj8GaEZ9BpIGvEbnBxJHPgdUB2oHlsfDx/FIH0hNyHyIlEisCNvJDAk8yW3Jn4m4idGKBAo3Cmp KnkrSiwcLPEtXC3HLqAveTBVMTMyEjLzM9U0uTWgNoc3cTfmOFw5STo4Oyg8Gj0OPgM++z/0QO5B 6kLoQ+hE6UXsRvFH90j/SglLFEwhTTBOQE9SUGZRe1KSU6tUxVXhVv9YHlk/WmFbhVyrXdJe+2Al YVJif2OvZOBmEmdHaHxptGrtbCdtZG/hcSJyZXOpdO92Nnd/eMl6FXtjfLJ+A39Vgf+DVoSvhgmH ZYjCiiGLgYzjjkePrJESknuT5Ja8mCuZm5sMnH+d9J9qolqj1aVRps+oTqnOrNSuWq/gsWmy8rR+ tgu5Kbq6vE294b93wQ7EQcXdx3vJGcq6zFvN/9FK0vHUm9ZF1/HZn9tO3P/gZOIZ49DliOdB6Pzq uex27/fxufN89UL3CPjQ+pr8Zf//AAAAAAAGABIAIwA5AFUAdQCZAMEA7gEgAVQBjgHLAgsCUQKb AucDOQOMA+QEQAShBQYFbwXdBkwGvwc4B7UINgi5CUAJygpdCu4LiAwlDMQNZQ4PDrUPZRAXENMR ixJNEw8T0hSeFVkWDxbPF40YURkaGeUasxuEHFUdIh37HtAfqyCMIXIiVyM5JCwlGCYKJvgn7ijr KeIq6SvpLPMuAC8JMB8xNjJPM2o0kTWyNuE4ETlBOnA7qDzrPi0/bkC7Qf9DUkSzRglHZ0i0SdVK 7Uv6TRxONE9RUGFRilKoU91VBlYxV1tYkVnCWvhcNl15XsNgA2FGYo9j7WU7ZoZn42lEaptr/21r bsdwOHGkcw10gnX4d25443pce959Wn7hgGWB54NmhOSGdYgDiYyLEoynjkCP1JFjku6Uf5Yfl7CZ JJqOm/qdep7xoHOh66NwpP+mdqf+qY6rH6ywrkGv07Fksva0h7YZt6q5O7rLvFu9zL9ZwOjCd8QE xXfG+8hyye/LaszVzj/PqNEP0nbT3NVB1p7X59kv2nbbvN0B3kXfeeCl4c/i+eQc5THmROdX6Gjp cepw62PsU+1A7i3vDO/r8LzxjvJW8xvz2/SV9U719vaf90L32/h0+QX5h/oK+o36+vtl+8/8OvyV /OT9NP2D/dP+I/6J/vT/X//J//8AAGRlc2MAAAAAAAAACkNvbG9yIExDRAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAABtbHVjAAAAAAAAAA8AAAAMaXRJVAAAABQAAADEZnJGUgAAAEIAAADYbmJOTwAAABIA AAEaZXNFUwAAABIAAAEsZmlGSQAAABAAAAE+cHRQVAAAABgAAAFOemhUVwAAAA4AAAFmamFKUAAA AA4AAAF0bmxOTAAAABYAAAGCZGVERQAAABAAAAGYa29LUgAAAAwAAAGoZW5VUwAAABIAAAG0c3ZT RQAAABAAAAHGZGFESwAAABwAAAHWemhDTgAAAAwAAAHyAEwAQwBEACAAYwBvAGwAbwByAGkAyQBj AHIAYQBuACAA4AAgAGMAcgBpAHMAdABhAHUAeAAgAGwAaQBxAHUAaQBkAGUAcwAgAGMAbwB1AGwA ZQB1AHIARgBhAHIAZwBlAC0ATABDAEQATABDAEQAIABjAG8AbABvAHIAVgDkAHIAaQAtAEwAQwBE AEwAQwBEACAAYwBvAGwAbwByAGkAZABvX2mCcm2yZnaYb3k6VmgwqzDpMPwAIABMAEMARABLAGwA ZQB1AHIAZQBuAC0ATABDAEQARgBhAHIAYgAtAEwAQwBEzuy37AAgAEwAQwBEAEMAbwBsAG8AcgAg AEwAQwBEAEYA5AByAGcALQBMAEMARABMAEMARAAtAGYAYQByAHYAZQBzAGsA5gByAG1faYJyACAA TABDAEQAAG1tb2QAAAAAAAAGEAAAnFYAAAAAv/h7gAAAAAAAAAAAAAAAAAAAAAB0ZXh0AAAAAENv cHlyaWdodCBBcHBsZSBDb21wdXRlciwgSW5jLiwgMjAwNQAAAAA= item7.X-ABRELATEDNAMES:assistant item7.X-ABLabel:_$!<Assistant>!$_ item8.X-ABRELATEDNAMES;type=pref:spouse item8.X-ABLabel:_$!<Spouse>!$_ item9.X-ABRELATEDNAMES:father item9.X-ABLabel:_$!<Father>!$_ item10.X-ABRELATEDNAMES:Custom relation item10.X-ABLabel:CustomRelLabel X-ABUID:3D833EB4-BFFC-41F0-9193-96695487C45E\:ABPerson END:VCARD BEGIN:VCARD VERSION:3.0 N:;The only first name;;; FN:The only first name ORG:Company; item1.ADR;type=WORK;type=pref:;;000;;;; item1.X-ABADR:us PHOTO;BASE64: TU0AKgADAAgXDgATCgAZCgAaCgAVCwAPBwACAgACAgAHAwAJBQAWBgAWBgAOAwASBwARDgARDgAe DwAeDwAWCwASBwAHAwAFAQAIBAAPCgAHCgAGCAABAgABAgAEAwAFBAATBQAaCwAEBwABAwAAAgAA AwACAwABAQAAAQAAAQAAAgABAwAABAAAAwABAwABAwABAwABBAAABAAAAwABBAAAAwAAAQABAwAA AwAABAACAwACAwADBAADBAABBAABAwACBAABBAAABAAAAwAABAAABAABAwABAwAAAAAAAAABBAAD BQABBAAABAAAAwADBgAEBwABAwACAwACAwAEBQADBAADBQADBQABBAABBAADBQABBAAEAwAGBAAF AwAGAwACAwADBAADBQADBgADAwADAwABAQADAwABBQABBAACAwACAwADAwAEBAAAAgAAAwAABAAB BQABBQAAAwAAAgAAAwACAwADBAABAwABAgAAAwAAAwAAAgABBAADAwADAwAAAgAAAwABAwAAAgAB AQABAgAAAgAAAgAAAgAAAgAAAwAAAwAAAQABAwAAAwAAAgABBQACBgABAwABBAADBgACBAAAAwAA AwABBAABAwACAwACAwAEAwADAQAAAgACBAACAgACAgABAwADBAAABAAABAABBAACBAABAwACAwAB BAAAAwAAAgABAwABAwABAwAAAQAAAgABAwABAwAAAgAAAgAAAQAAAQABAwABAwABAwAAAgABAgAC AwAEAwAFBAADBQAAAwABAwABBAABAwABAwACAwADBAADAwADBAADBQACBAADBAACAwADBAACAwAD AgAEAwAEBwADBgABBAAABAACBAABBAACBAACBAABBAACBAAEAgAEAwACAwACAwAFAwAHBAAOCAAO CAADBAADBAABBAABBAAEBAADAwAEBAAEBAAJAgAKAwAFBwAFBwABBQAABAAAAwABBAADBAAEBQAB BQABBQADBAAEBgADBAACAwAEAgAEAgAEAgAGAwAEBAAEBAAsFgAqFQApGQAiEwAeFQISCgAFBAAK CQAeDwAkFAArFAArFAAsGQA1IgEwIQAwIQAuGwAxHgArGwAjFAAZCAAXBwAjDwAqFgAXEQAVDwAF BQADAwAJBgATDwAnEwAoFAASDgEKBgAAAgAAAgABAQACAgAAAQAAAgABBAACBAABAwAAAQABAwAC AwABAwACAwADBAADBAAJBwANCgAICgAFBwABBAABAwAKBgARDAAWEQARDAAIBAALBwALCgAGBQAA AwAAAwAFAgAMCAAMBwAKBQAEBAAEBAASAwAdCwEcCwAaCgAWBgAYBwANBwAJBAAIBgAKBwARDAAP CgAPBgAPBgACAwADBAAFBQAFBQAHBQAOCwAICAAEBAABBAACBgADBwADBwAEBQAFBgAHAwAHAwAD BwABBQABBQABBAAEBgADBAABBAABBAAAAwAABAAFBwADBAACAgADAwACAwADBAAEAwAEAwADBAAD BAABAwABAwABAwAEBQABBAAABAADBAABAwAFBAAFBAABBAAAAwAAAgABAwABAwACBAABBAACBAAD BAABAwAEBQADBAABBAABBAAEBwADBQAAAwAAAwACBAABAwADBAACAwADBAADBAACAwADBAADBAAC AwACAwABAwAAAwAAAwAABAABBAACAgADAwABBAABAwABAwACBAAEAwAEAwACAwABAwACAwACAwAC AwACAwABBAAABAAAAwAABAABAwAAAgABAQABAgADBAADBAAEBAADAwABBAACBAAABAAAAwACAgAD AwABBAABBAABBAABBAADBAADBAADBAADBAACAwACAwAEBAAEBAADBAADBAAEBQADBAADBAAEBQAE BAAEBAAEAgAFAwAFAwAFAwADBAADBAAEBgAEBQACAwABAgADBAAEBgAEBwADBQAEBAAEBAAEBAAF BQAGBwAEBgAEBAAGBgADBAADBAADBwACBgADBwAECgAKCAAIBwAEBwABBAAFBAAFBAACAwACAwAD BAACAwAwHQAzIAAyHwAwHQAtFQAmDwAbDwAWCwApEgAzGwEyGQAxGAA6HgA8IAAzIwE1JQMsHAAs HAAtHgAoGQApGAAjEwAyHwAzIAAuIQAsHwAODAALCgAVCgAjFwI0JQMvIAAgHQQUEQACBAADBgAC AwACAwABBAABBAABAwABAwADBAABAwABAwABAgADAgAEAwAJAwANBgAeEAAhEwAJCwAEBwAXAwAh CgAuGwAwHQArGgQcDQAWEQAXEwAUEgEKCQAFBwAGCAAcCQAsFgMnFwAjFAAWDwAZEwA0FQA8GwE7 GgRBIAkzGgUvFgMdDwAdDwAeEwAlGQAmHAAiGAAhEwAhEwASDQAQCwAbDQAiEwIuGgArFwASFAAL DQAHCgALDwAeFAAbEQAnGAAnGAAjFQAhEwAPCgAMCAADCAAABAABAwADBAAPBwAPBwAFBAAHBQAM CAAMCAAKBwAMCAAVDQIPBwAEBAACAgACBAACBAADBAADBAADBAACAwAEBQAEBQABAwABBAAEBQAD BAAABAAABAABBAAABAAOAwAOAwANBwAKBAABBQACBgADBQACBAADBQADBgAABAAAAwABAwADBgAB BAABAwABBAABBAACAwADBAABBgAABAAAAwAAAwADBQADBQABAwABAwAEBQABAwAGAwAGAwADAwAE BAADAwAEBAADBAACAwACAwADBAAEBAAEBAADBAADBAABAwACAwADBAAEBQABBAABBAACBAACBAAE BAAEBAAGBgAEBAADBAADBAADBAACAwADBAAEBQADBQABAwACAwADBAADBAADBAADBgADBQABBAAB BAAHBwAEBAAGBAAFBAAGAwAGAwAEBAADAwADBAAEBQAFBAAHBQAFBAAGBAADAwAGBgAGBgAFBQAH BQAFBAACAwAEBQAEBQAFBwAEBgAFBwAIBQAHBAAEBQAEBQADBQAEBwAEBAAFBQACBgADBwADBQAD BgAECAADBwAEBQADBAADAwADAwAEAwADAgADAwADAwAjGQAkGgAnHQApHwAsGwAoFwAsFgAqFQAt FgAvFwA0GwA4HgAtIAAsHwAqHwEjGAAjFAAnFwAyGwA6IgE5HQA4HAA6JQA8JwA+KgA7JwAxFwAn DwAyFwBEJwVGMQE/KwA4KQcmGAAHBwAEBAABAwABAgABBAABBAABAgABAgABAwACAwAAAQABAwAE AgAGAwASBAAcDQAuFQAwFgAcFgAYEwApDwA4GwJAKABBKQA1IQAzHwAyGAAxFwAfGAEVDwAYCgAh EgA/HgBIJgE3IQAtGAAcHwAcHwA5GwBAIgBNIwFXLAc6IActFQAgEgAnGAA1JgA6KgE3IwI1IgEv HAAvHAAsGAAqFgAnFAAsGAA3HQA3HQAwIQA0JQEvFwAvFwA3GAA7HABFIwBEIgArGwAmFgAfFQAm GwALDwIDBwAIBwARDwArGQEtGwIcCwAcCwAYDwAlGgMwIgQxIwQdEwIaEAAGBwAEBQABBAABAwAC AwABAwADBAADBAACAwACAwABAwABBAADBAADBAACAQACAQADBQAFCAAsFAAnDwAjDAAeCAATCwEQ CQAEBgAGBwAOCwAJBwAIAwAOCAAmDwAmDwAUDwANCAAJCAAHBwAMBQAPCAAEBwABBAAAAwAAAwAE BAADAwAFBAAIBwAMCgEHBgAEBAADAwAEAQAEAQAHBAAIBQAFCAADBgADBAADBAADAwAFBQACBgAA BAABBAABBQAEBgADBAABBAADBQADBAACAwAEBAAEBAAEBwADBQADBAAEBQAEBQAEBgAGBwAGBwAG AgAHAwAKBwAJBwAHAwAKBwAGBgAEBAAGBAAGBAAMBwAKBQAJAwAJAwAFAwAFAwAHBAAGAwADBAAD BAAHBwAHBgAIBgAIBgAGAwAHBAAFBwAEBQADAwAEBAAEAgADAQAHBAAJBgAEAwAEAwAEAwAHBQAE CAADBwAFBwAEBwAEBQAEBQAEBwAEBwAEBQAEBQABAwABAwABBAABBAAEBAAEBAACAgABAQAFAwAH BAAqHAAiFQAiFwAmGwAtHAAqGQAyGwA1HgA0GQA1GgA0IgA0IgApIgAkHQAjDwAgDQAkDwAoEgA3 FwA8HAA+IwA9IgBAJgBFKgBDJwE8IQA9HAA+HQBAIABIJwBKLQBNLwE0JgcjFgAHCgAFCQABBAAB BAABBAAAAwADAQADAgACAwABAwABBAADBQAEAgAFAwAVBQAaCgA0GwA4HgAwHQAtGgA6IgBEKwFN LwBNLwBAKgA/KQA8IwA8IwApGAMgEAAlFgAvHwBKJwBULwJJKwFDJQArIwMhGQAqEAA/IwRQKgBT LAE6HwQvFgAvHgAzIgM5KQM3JwI3HAA9IgA7IQA1HAA4IQQwGgArGgAuHQA3HgA+JQA7JAA8JQA5 IgA1HwA+HwBJKQJHKQBFJwApHwAkGgApHgAqHwASFAEHCAAqFgA8JgNEKwE/JwAvHwIrGwAjFgAt HwI3IwIzIAAqIAIjGQAMCAAHBAACBAABBAABBAABAwAAAwABBAABAwABAwADAwADAwACAwABAgAF BgAGBwASDAAdFgA4JAE1IgAzHwMxHQEmFwAfEQAZCwAbDQAgDwAeDQAiCwAyGQM+JQA6IQAnHAAm GwAXEAAPCQAUDgAZEwAKCgAEBQABBgACBwAVDgATDAAeDgAmFQAnGggdEQEHCAAEBAAOAQAYCQEi DwMgDgEREgMGBwABBwABBwACBAADBgABBwAABAABBAADBQAEBgAEBQAEBAAEBAAEBQAEBQAEBQAE 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 GAAyGAA3HAFBJwQ+JAMhEwAiFAAoFwEuHQQdEwIVCwAhEAAyIAhQLgNRLwRKJAc4FAAjEQEnFQQV DgARCgAHBgAFBAAIBwAJCAAEBAAEBAAHBQAGBAADBAACAwABBAAABAABBAACBAAAAwAAAgABAwAA AgABAQABAQABAwAAAgAAAAAAAAABAwAAAwAAAwAAAwADBAACAwAIBAAHBAACAwADBAADBQACBAAD BgADBgACBwAECgAGCgAECAAHCQAFBwABBQAABAAABAAABAAAAwABAwAAAgAAAQAAAgAAAgAABAAA BAAAAgAAAgABAgABAQAAAwACBAAEBgAICgAUCQAWCgAPDAAMCQAKCgAHBgAPBQAQBwAOCgAOCgAO CgAPDAAPDQAYFgAPDgAKCAAKBgALBwATBwATBwAaDgAYDAAPDwASEgALDQAMDgAHCgAHCgAPDQAP DgANDQALCwADCAAECgAOBwAQCQAMCAAOCgAaDAAeDwAqFQAzHQIpEgEiDAAMCAAKBgAZDgAfEwAb EQIVCwALBQAWDwAyHAI3IAQjFgEYDQAHCQAJCwAWGQARFQAXEgAeGAAlGwApHwAvHwMwIAMlEwMd DAAGBAADAgABAwAAAgAAAgAAAwABBAABBAAAAwAAAwAAAgAAAwADBAAEBgAQBwAUCgAiFgAmGgAx IwMvIQEzIwExIQAoGQAhEwAgFgAkGgAhEwAgEgA1GwA6HwM5IQA6IgAyJQIvIgAqEAAqEAArGwAy IgE5HQBAJAQ7JQM3IQAsFQAwGAA8IAIwFgAsFAA6IAFKKQdEIwMtHQEsHAE4IQUyHAIYDAIPBAAA AwAAAgAAAgAAAgACBAACBAABBAAAAwAABAAABAACAwADBAABAgABAgABBAABBAADBAABAwABBAAC BAAJCwAQEwAoFwArGgEwHQA1IgFHKQBQMQRJMAo+JgMsFwEuGQM9IQFDJgRIKQRJKgVDIwM/IAFD IwNDIwMpHAUfEwApGwAuIAIoHgAiGAAlFQAuHQFAJQBEKAJGKwdAJgMXEwAWEQAhGAAsIwUfFQYS CQAnEQAzHARMKQVJJwRFIgNDIAFHKgk+IgMXDgERCAAOBwAMBQAIBgAIBgAEBQAHCAAFBwADBAAD AwADAwACAwACAwADBAADBAAAAwAAAwAAAQAAAQACAwABAgABAgAAAQAAAgAAAwABAwADBAABAwAA AgABAwABAwACBAABBAAAAAAAAQAABAAAAwAAAwAAAgAABAADBwABBAABBAADBAADBAAABQAAAwAA AwAAAwAAAgAAAQAAAQAAAQAAAwAAAwABAgABAQAAAgAAAgAAAgAAAgAAAQAAAwACBAADBgAFBAAG BAADBAAEBQAEBwADBgADBwAECAAGBgAEBAAECQADBwAIBwAKCgAFCgABBAAABAABBQAKBgALBwAL CQAMCgAKDQAKDQADCAAFCgAJBgAJBgAICQAICQACCAAECgAEBwADBgAGBAAHBQABBgADBwEPCgAR DAAaDwAbDwAPDgAKCgALBQALBQAKCwAJCgAFBQAEBAAIAwAPCgAkEwAlFAEPDwAKCgAGBwAICQAL CgASEQMZDgAdEQAmGwAlGgAjGQMaEQARDwAPDQAFBQADAwADAwABAgAAAwAABAADBAADBAAABQAB BgAAAwAAAwADAwAEBAAMCgATEQAbFwAdGQAlFwAqHAAsGAAqFgAPDwAODgAZDwAZDwATDQAWEAAm FQAoFgAuHgAvHwAoHAMcEQAXCQAbDAAXDwAiGAIuGAAzHQIrGgEgEAASDAAWEAAjFQAfEQAfDwAr GQMtHgQmFwAaDwAgFQEjFgYZDQAGBwAEBQABAwAAAgABBAABBAABBAABBAAABAAAAwAABAAABAAB AwABAwACAQADAgADAgACAQACAwACAwADBAAEBQAKBwAPDAAlFQArGgE1IgA6JgNDKgBFLAEuIgcg FQAgDwApFwI9HQFFJAVHJwZBIgMxGwEtFwAsGwMnFgAIDgEFCgAYFgAeHAEfFAAfFAAqGgAuHgFB KAFBKAE1JAUpGAASDgAUDwAbGwEeHgMXEQQOCAAhEwAmFwE5JAA8JwFMMAJNMQNDLA8uGQEODwEE BQAEBQAFBwAHBwAGBgAHBQAHBgAGBgAEBAADBAADBAABAgABAgAFBAAFBAAACQAACAAGDgAEDAAI EQEEDQADCgACCgADDgADDgADCwADCwACCAABBwAHBgAHBwAFBwAEBwAABgAABAABBQACBgABAwAB BAABBAADBwADAwACAgABAwABAgAAAgAAAgAAAwAAAwAAAwAAAwAAAQABAwABAwABAwAAAwAAAwAA AgAAAQAAAgAAAQAAAwAAAwABBAADBgADBAADBAADAwADAwAEBQAEBQADBAACAwADAgAEAwADBAAC AwAEBgADBAADBAACAwACAwAEBgAEBAAGBgADBAAEBQAEBwACBAACBAAEBwAGAwAHAwAGBwADBAAC AwACAwADAgADAwADBQACBAAAAwABBQAGCAAICgAHBgAEAwABAwAEBQADAwADAwAECgECBwACBQEC BQEABAADCQAPCgAPCwAECAADBwADBQAEBwAJCwAJCwAPCgASDQAVEgAWFAANDwEGCAAFBwAFBwAD BAADBAABAwAAAgADBAADBAADBAADBAABBAAAAwABAgABAgACBAADBQAKCQAODQAPEwAUGAESEgAS EgAVFQEQEAADCgAECgANCwAPDQAUDwAXEwAQFQAPEwAdEgAbEAAYDwASCQALCgAODAAPCgAVDwAb FgAaFQASEQAPDwAVDwAWDwAQEAALCwARDAAaFQMWEQEPCgALDgAOEAERDgAMCQABBgAABAABBgAA BAACBAACBAADAwADAwABBAABAwACBAADBQABAwABAwABAwABAwADAwADAwADAwADAwADAwAEBAAD BgADBQAbDwAhFQAnGQAsHgEyHwAvHAASDQARDAAWDQAfFgA4GAE4GAEtFgApEwAgFQAfFAAbEQIQ BwAHBgAKCQAbDwIbDwIUCwAaEQArGgAsGwA6HQI6HQIgEwMTBwAPDAAVEQATFAAODwAKCQAKCQAV CgAdEQMvGgA5IwFIKQNEJQEeFgUMBgACBwADCAAGBgAEBAADBAAEBQAFBQAHBwAFBwAEBQAABAAA BAAAAwAAAwACAwADBAAPJAQKHwEbJggaJQcPIwYRJQcTKgUPJQIXHAMXHAMVIQQUIAMMHQMMHQMQ HAEPGgAOGAEOGAEMHgEFFgABEQAEFQAHDQAJDwAKDAAKDQAGBgADAwABAwABAgAAAQABAgACAwAC AwABAwAAAwADBAADBAACBAABBAACBAACBAABAwABBAACBAACBAADBAACAwADBAAFBwALBgALBgAL BgAJBAAEBAAEBAAHBQAJBwAPBAAPBAAMBQALBAAKCQAFBAAEBAAEBAADBQADBQADBAACAwABBAAC BAADAwAEBAAGAwAHBAADBAADBAADAwAEBAABAwABAwADBAADBAAEBQABAgAEAwAHBgAEAwAGBAAH BAAHBAABAwACAwADBAADBAAEBAAEBAACAwAEBAABBQAABAAKBQAKBgAHBAAGAwAGBQAKCQAHBgAI BwAKCgAKCQAHCAAHBwAKCAEHBQAFBQAEBAADAwADAwABAgABAgACAwADBAAEBAADAwACAwACAwAD BAACBAACBAADBQAHBgAKCQAQCgAPCQAMBgALBQAKCAAJBwAJBwAKCAAHBwAKCQAIBgAMCgAKCgAK CQARBwASCAASCAARBwAKBgAKBgAUCQAVCgAUDwAQCwAIBwAIBwASBwATBwAODQAKCgAPBwIUCwUI BQAKBwEKCwAJCgAKCAAHBQADBAADBAADAwADAwAGAwAGAwAEAwAEAwADBAADBAAGBAAGBAAEBAAD AwAEBAAEBAAEBQAEBQAFBAAFBAAIBwAHBgAIBwAKCgAbEgAbEgAgEgAfEQAbDQAaDAASCAAUCgAZ DwAbEAAlFQAiEgAeEgEWCgAVCwAWDAAQBwASCAARCAATCgAfCQAdBwAQCgATDAAaEQAbEgAhDQAj DwASCgANBgASBwAVCQANCQAJBQALBgAPCgAXCwAZDQAhDwAjEQAqFAAoEgAcDAMTBAAHBgAIBwAF BQAFBQAFBgAHBwAHBwAHBwAKBAALBQAKBwAIBgADAwACAgAEAwAHBQAQMg8NLgscLAkcLAkPJw4Q KQ8SMA4MKQgUIwkUIwkUKgUTKQQNKgkLKAcLKAcKJgYHJAIHJAIOJAEMIgAIIAMKIgQJHwMHHQEH GQAKHAEGEwAEEAABBwAAAwAAAQABAgACAgABAQABAQABAQABAwABAwABBAADBQABAQABAgACAgAD AwABAwABBAAABAAAAwABBAADBQAEBQADBAADAwACAgACAgADAwAGAwAHBAAGBAAGBAAFBQAEBAAD BgACBAADBAACAwAABAAAAwABAQAAAQAAAQAAAQADBAADBAAEBAAHBwACCgACCgACBAADBQABBQAB BAAEBwAEBwAEBwABBAAHBAAHBAABAwADBAAFBAAFBAACAwABAwADBQADBQAFBwAEBgADAwADAwAB BwAABgADBQADBQADAwAEBAADAwAEBAAFBQAGBgAHBgAGBQALCgAHBgAGBgAFBQADBQACBAACAwAC AwACBAACBAABAwABAwADAwAEBAADBAADBAABAwABAwAEBgAEBQAHBAAIBQAHBwAGBQAGBAAHBQAD AwADAwAEBAADAwABAQADAwADAwAEBAAHBQAFBAAFAQAGAgAGAgAHAwAFBAAGBAAHBwAHBwADBAAC AwADBQAAAwAEAwAEAwABBAABBAAEBAADAwACBAAEBgEGBQEDAwAGBAAFBAAEBQADBAACAwACAwAD BAADBAADAwADAwACAwACAwABBAACBAAFBQAEBAADAwADAwAEBAAEBAADAwADAwAHBAAHBAAGBwAF BgAKCgAMDAAOCgAMCQAQBwAQBwAKBQALBgAHBwAHBwAHCAAFBgAHBgAIBwAKBwAJBQAHBAAFAwAM BwANBwAMBwAMBwAKBwAKBwANBwANBwAHBgAKCQAJBwAHBQAKCQAJCAAGBgAEBAAGBAAIBwAWDAAY DwATDQEPCQAOCAANBwAJBwAFBAAGBgAHBwAJBAAJBAAHBAAIBQAGBgAEBAALBQALBQAGBgAEBAAD AwACAgAFBAAFBAALMw8JMAwXMQ8ULQsMLQwPMQ8KKhIKKhIMKA0MKA0KKgoLLAsPMBQMLBAKKw4J Kg0IKgcKLAkMJgcMJgcPJw0PJw0MKA0OKg8JJwkJJwkIIAUDGAABCgAABgABAgABAgAAAQABAQAA AgAAAgAAAgABAwABAgABAwACAgADAwADBAABAgABAwABAwABAwABAwAAAwAABAABAwABAwABBAAC BAAAAwAAAwABAwADBAABBAABBAABBAABAwAAAwAAAQAAAwAAAwAAAQAAAQAAAAAAAAAAAwAABQAD CwAEDQAGEgAJFgAAEAABEgABEAADEwAEEQADDwADDwADDwADFAABEAAMDwAHCgACDQAEEAAECgAD BwAEDAABCAAHCgAFBwAECQAECgABBAAAAwABBgABBgACAwABAwACAwADBAABBQABBAAEBAAGBgAD BAAGBwAKCQAHBwADBAAEBQAABAABBAABBAABAwAEBQADBAABBAAABAABAwABAwADBAACAwACAwAC AwADAwAEBAAHAwAKBQAEBgADBAABBAABBAABAwAAAQABAgABAQAAAwABBAABBAABBAACAwABAgAD AwADAwABAwACAwACBAABBAACAwADBAAAAwAAAwAAAwAAAQAAAQABAwAAAwAAAwAAAwAAAQAAAgAA AwABAgABAgABBAABBAABBAABAwACBAABBAAABAAABAADBAABAwABAgABAgABAwABBAABBQABBAAC AgACAgABBAABBAABAwABAwAEAwAFBAADBAAEBQAEBgAEBgAEBQADBAAIBgAIBgAEBAAGBgAEBwAC BAAABAAAAwAABAABBAACAwABAQABAgACAwAEAwAGBAADBAAEBgADAwAEBAAEAwADAgAAAgAAAwAD AwADAwADBQADBQACBAABAwACAwAFBgELBQANBwAEBwAEBwAFBwAFBwADBAACAwAEBAAGBgAFBQAF BQAFBwAGBwADBAAEBgAKBwEHBAAEBQADBAACAwABAgAFAgAFAgAKMwoKMwoOMhMOMhMMNRsMNRsM LA8PLxIOLQoLKggKJwcNKgkKLwwMMQ4LLgwLLgwNLg0NLg0PKw8PKw8QKg8OJw0PIwsTJw8LKAoJ JQgKIQQHHQEDEwADEwABAwABAwAAAQAAAgAAAgAAAgAAAwABAwABAwAAAwACAgADAwAAAwAAAgAA AwABAwAAAgAAAQAAAwAABAABAwAAAwACAwABAwAAAgAAAwAABAAABAAABQAABAAAAwAAAwAAAwAA AgAAAgAAAQAAAgAAAgAAAQAAAQADCwAIEgAMGwAPHgENKAMLJgEEIAAEIAAEFgAKHQIGHAAHHQAM IgAKHwAOJAEJHgAKHgEHGgAGGAEIGwMKGAMKGAMLGQELGQELEwIIDwAGFwEHGAEBDAAACgAECAAE CAACBAABAwABAwABAwABBAAABAADBAAEBQADBAADBAADBwABBAAABQABBgABBAAABAACAwACAwAD BQABBAAABQABBgABBQABBAABBAACBAADAwADAwAFAwAGAwAHBgAHBwADAwADAwABAwAAAgAAAQAA AAABAwABAwABBAACBgABBgADBwAEBwADBgADCgADCgAABwADCgADBwADBwAEBQAEBQAABQAABQAA BAAABQABBQABBQACBAACBAACBAABBAAABAAABAAAAwAABAACAwACAwAAAwABBAACBAABAwAABAAA BAADBAADBAACAwACAwAABAACBgADBQACBAABBAABBAABAwABAwACAwACAwAEBQADBAAEAwAFBAAE BQADBAADBgADBQAEBgAEBwAEBgACAwAAAgAAAQAAAQAAAAAAAgABAwAAAQAAAQABAwACAwACAwAA AQAAAQACAwABAwACAwAAAwABAwAAAwAAAQADBAADBAABBAAAAwABAwABBAACBAADBQACAwAEBQAB BQAABAAEBAAGBgAEBAADAwAEBQAFBwADBQADBQADBwACBgAGBAAGBAAEBAADAwADAwADAwABBAAB AwADAwADAwAILxAJMBEJMBMGLA8KMBcKMBcNMRMKLQ8KKAgNLAsLKggKKQcKLQsJKwoJKwoJKwoL Kw8LKw8NKRAPKxIRKhIRKhIIKQ0IKQ0GKhMFKRIKJA8DGgcDFgYADwEABAAAAwAAAAAAAAAAAwAB BAABAwABBAABBAAAAwAAAwAAAwABAwABAwAAAwAABAABBAAABAABBAABBAABBAABBAAAAwAAAwAA AgAAAwAABAAABAAABAAABAAAAgAAAgAAAwAAAwAAAgAAAQAAAQAAAAAAAwABBwAEGAAIHgMHJAkJ JgoZLxMWLBAJKg0HJwoJJQUJJQUJJQgJJQgJKwoHKAcMLQwKKgoGIwMHJQQHHwQKIgcKIgcHHwQK IgcLJAgFIAQFIAQFGgIIHgQDFgMCFQIDDAACCwACBgAAAwAAAgAAAwACBAACBAACAwADBAABBAAA AwADBgABAwAAAwABBAABBQABBAABBAACBAABBAAABAADBQADBQACBgACBgAABAAABQABAwABAwAD AwAEBAADBAADBAAAAwAAAwAAAQAAAAAAAAAAAwAECAAJDQAEEQADDwAEEQAGEwAKFAAHEQAHGQEF FgAEFQADEwAFEQAFEQAFDwAHEAAHEQAHEQABDwABDwADFAABEAAEDQAHEAAGDAAIDwAEDgAACQAC BAACBAAABAAABAAABAAABAABAwABBAADBAADBAABBAABBAABBAABBAACBgADBwAEBQAEBQACBgAA BAADAwADAwABBQABBQABBQABBQACAwADBAADBQAEBwADCAACBwACBgADBwABBAAAAwAAAQAAAQAA AQAAAQABBAAEBwAFCQAEBwABCQACCgADCwAABwAABwAACAAACQAACgABCgADDAAACAAACAADDAAA CAADBwAECAADBwABBQAEBQAEBgAECAAEBwABBQADBwAEBgAFBwAFBQAEBAAEBwADBQAEBgAEBQAE CAAEBwAHBwAHBwAFBwAEBwACBAABBAADAwADAwAEBQADBAAJKxUJKxUIKw8KLhIKLBEIKQ8IKQ0I KQ0LKQ4RMBQWMREULw8KKgoIKAgKJwwKJwwJKREKKhIJKREIKBAHJRMKKRYKKw8IKQ0EJQ8IKhML Iw8EGgcDFgcADwIABwAAAwAAAAAAAQAAAQAAAQAAAwAAAwABAwACAwAAAgAAAgAABAAABAAAAwAA BAABAwABAwABAwABBAACAgACAgABAQABAgABAwABAwAABAAABAAABQAABQAAAgAAAgABAwABAwAA AQAAAAAAAAAAAQAABwAEEAEEIAgHJAsHJxQHJxQTLxgQLBYKLRQOMRcLMhcKMBYKJw4KKA8HKBUH KBUGJQ4NLhYGIwQFIgQJKAwHJQoGIgcEHwUHIw8KJhEHKQ4HKg8FJQoIKQ0GIw8DHgsBDwAADAAA BQAAAwABAwABAwAAAgAAAwACAwAEBQACBAABAwABBAAAAwAAAgAAAwAABAABBAABBAABAwAABAAB BAAABAABBAABBQABBQABBAACBAACAwABAgACAwACAwAABAAAAwAAAQAAAgAAAAAAAAAAAQABBwAJ FgQKFwYEHwQHIgYMIwUMIwUHIwcFIAQJJAoEHgYGIggIJQoFHwcEHQUIHgQIHgQHHwQKIgcHIAMH IAMDGQEGHgQDGQEEGwIEGQEFGgIKFwQFEQAHCgEBBAAABAAABAAAAgAAAwAABAAABAAAAwAAAwAA AwAABAABBAAAAwABAwACBAABBAAABAAAAwAAAwAAAgABAwAAAwAAAwAABAABBgABBAACBAAABAAB BQAEBwAEBwABBwAABQAAAgAAAAAAAAABAQAFAwALCQAFEgAIFgAJHwMEGAAEFQAHGAAHGgAJHQEE GwIEGwICFwACFwADGAAEGwIHFwMEFAAHFwMEFAALEgAPFgIDFAAEFgADDAAACAADDwACDQACCQAC CQAGBwADBAAEBgAEBgACBwABBwACBgAECAAGBwAFBwAGBwAGBwAFBwAFBwAEBAAEBAABAwABAwAF AwAEAgAHKhcKLhsKLBMJKxIHKAsIKQwHLgoIMAwZMxcmQSQ7TzcvQysgOx4ZMxcHKxYFKBQKMBcJ LxYHKA8GJw8KJxcJJhYEIg4HJhEHLRAHLRAEJQoKLhINIhEFGAkBCAAAAwAAAAAAAAAAAAAAAQAA AwAAAgABAwAAAwAAAwAABAAAAgAAAgAAAQAAAQAAAgAAAQAAAQAAAgAAAQAAAAAAAQAAAQAAAQAA AgAAAgAAAgAAAwAAAwAAAgAAAwAAAQAAAQAAAwAAAQAAAAAAAwAACwAEFgcHJxQLLRkMLR8IKBoK LRQJKxIJKxQHKRIKLyAJLR4FKBQILBcGIhMEHxAEJAkJKg4KKQ0DIAYEJAgKKw4FJA8GJQ8HJhEM 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 MSQMNB4YRCxGTk9dZWdueHJqdG5qaVZfXkxcV0xaVUk9W1c/XVo0WlwlSEoWQzkPOS8JLyEILiAI LSMHLCIPNCcEJhkFIxUHJRYJFQYABwABAwAAAQABAwAAAgACBgAABAACBAABBAABBAABBAAAAgAA AwAAAgACBAABAwACAwABAwAAAgAABAADBwABBAABBAABBAABBAACAwADBAADBQABAwAABAAAAwAA BAAABQAAEwMKIg8ILiALMiQLNSgHMCMJLSIJLSIIMSQJMiUNNR8KMhwHJxYWOic1W1Bii39zjZlj fYhTWGtUWm1PXW5JV2hPZW5PZW46YVciRj0SOxkILg8KMBEILg8NLxsIKRYFHxIHIRQHEgUACgAA AwAABAAAAwAAAwAAAwAAAwAEBQAEBQACBAACBAADAwADAwACBgABBQABBQABBQADBAABAwADBAAD BAACBAABBAADBgADBgADBQADBgACBAABAwAABAABBgAADgEGFgcEJhQNMh8KMygJMSYPLicQLygL LyALLyAKKh4KKh4KMB8MMiEKLh8MMSIHKxYMMh0nRTRHaFZteXVjb2tKWkw6SDsVQywNOSMPLicL KSIWKyAVKh8KKRUKKBQQKRQIHwsHDQEBBwAEBQAEBgAFBAAHBQAGCAAGCAAEBwAEBwADBgAEBwAD BQACBAADBQACBAABAwACAwACBAACBAAKLh8MMSIIMxkJNBoPMhoWOyIvRTJMY09jcm1vfnljdW1Y amJaZVFaZVFlaltwdWVye2pueGdWeHVAYV4bSjwOOiwILRoHKxgHKRMIKhQKJx0KJx0HIhYDHBEE Fg4AEAgAAgAAAAAAAQAAAgABAwADBAABBAACBAACAwADBAACAwACAwABAwABAwABAwABAwAAAgAB AwABBAABBAABBAABBAACAwADBAABBAABBAADBQACBAACAwABAgAAAwAAAwAAAQAAAgAABgAHFgwE JBsLLiUJNyYKOCcOMyYNMiUMNSoIMCUMLy0QNDIIMiMOOipIUUxpc215gHJ3fm9odW1caWFRZWFM X1svT1ApSEkoSTwdPTAVOCkSNCYHKyAJLSIKLyIKLyIPNygJLyEFJBoIKB4MGwwBDQEAAgAAAQAA BAABBAABBQACBgADBAACAwABBAAAAwAAAwAAAwABAwABAwABAwABBAABAgABAgADBQADBgABAwAB AwACBAABBAADBQABBAAEAwADAgAAAwAAAwAABAAABQABEwQSKBYQNysROCwKNSgKNSgKMygGLSIK LiMLMCUSNCgSNCgTKSUrRD9XcndyjZJ4g3pjbmVRUU9OTkxHTkxESkhKXWNQY2kyVVUdPT0NMh0H KhYNMh8NMh8MMSALMB8HJRoFIhcGFAQACgAABAAABAAABAAABAAAAwAABAABBAADBQAABAABBAAD BQABBAACBAADBQADBQACBAAEBQAEBgAEBQADBAAEBQACAwADBAAFBwAEBQADBAAAAwAAAwAABAAD CAEBEQcMIBQILiINNCgLNSgLNSgTMygRMSYOLSAOLSANLx0PMR8OMi0NMSwKMisJMSoHJR8RMiw6 W1xWeXptgoRhdXhJYV01TEgTOTEOMisQLCQMJx8YKBsdLSAWLiINIxcWKRgKHA0GFQIADAACAwAG BwAEBgAEBQAGBwAHCAAGCAAGCAAGCQAFCAAEBgAEBgADBAADBAACAwACAwADBAADBAALMCEKLyAK MCIKMCIRNR4fRi0vT0RFZ1treXl6iIh3i39vg3hkeHVleXdweX94gIdwiI5uhoxWcoNGYXIhUEgR PTUKMyQHMCEILRgHLBcMLyYGJx4GIxYFIhYIGw8ADwUAAwAAAQAAAgAAAgAAAwAAAwABBAABBAAD BAADBAABBAAABAAAAgAAAQAAAwAAAgAAAQACAwABBAABAwABBAABBAABAQADAwABBgABBgAABAAA BAAAAwAAAQAAAwAAAwAAAQAAAQAABwAHFgsDJBkKLSIIOCYLPCoKLyQKLyQNOCwKNCkMNDENNTIJ MyANOSVKU0d0fXB9hod6g4RceoBPbXM9Y2M3XFwyTEciOjUYMhwZMx0WNB8WNB8KLiEHKh0KMBgK MBgMMh0JLhkHKh0HKRwKGAgADAAAAwAAAAAAAwAAAwAAAwABBAABAwABAwABBQABBAAAAwAAAwAB BAABBAACBAACBAABAwADBAADBAADBAAAAwAAAwACBAACBAABBAAABAABAwABAwADBAABAgAABAAA BgABFAoLIRYKMicMNSoLNycLNycLNSwJMikKMisJMSoOKiQRLigZNzkuTlBfeHlwiYtzdWdfYlRU VEVOTj9KT0BHTD0/WlNGYVoxSkEbMioJLRoDJRMKLxcJLhYKMiAJMB4HKhsFJxgHFgcACwAAAwAA AgAAAwABBAABBAABBAABBAABBAABBAABBAABBAABBAAABAABBAACBAADBAADBAACAwADBAADBAAD BgACBAADBAADBAAEBQACAwAAAwAAAgAABAABBwAAEgoLIRcJLSsPNTMKMyQKMyQNNCYKMSMMLhwN Lx0MMRwNMh0KLikLLyoGLSYLNC0KLywRODRDXF9ifYBtf41cbns9XUwiPy8QMiYKKx8MKBoOKhwP KhQQLBYYLSQPIxoXIRQOFgoLEwoDCQEABAADBwAGCQAEBwADBQADBQAFBwAEBgAFBwAEBgADAwAE BAADBgADBQAABAAABAADBAADBAAKMyIIMSALMiQMMyUMNyUYRjMwVk9BaWJleXl3i4t0jY5rhIZf eYBYcnlkb3Vrd31ne5VjeJFNXm4/UF8jTkMPNSsHMR4HMB0IMBcIMBcNMSgKLSQHKhsDJBYIGQoA DQAAAwAAAgAAAAAAAQAAAgAAAgAAAwAAAwAAAgABAwAABAAAAwAAAgAAAgAAAwAAAgAAAQABAwAB BAABBAABAwAAAQAABAAAAwAABAAABAAAAwAABAAAAgAAAgAAAwAAAwAAAwAAAgAACAADEwUBIRYJ LCENOCwPOi4LNCkIMCUOOSsNOCoKNC0KNC0HMBkSPydNYlZrgnV5ho5yfodPdHU0V1gvVU4pTkcu PC8sOi0qPSQ5TTInSDsWNCgKMRsKMRsKNSIKNyMLNBoHLhUKLSAEJRgDEggACgIAAQAAAAAAAgAA AgAABAAABAAAAwABBAAEBQADBAABBAACBAABAwABAwAEBAADAwABAwACBAADAwADBAABAwABAwAC AwADBAABBAAAAwAABAAAAwAABAAAAwAABAAABgAAEgkHHxUKMygKMygKNSgKNSgKNCULNSYKNCsK NCsLMScONCobPjo0W1ZdcHpjd4Bdd2tac2hoa2VhZF5fbl1baVhIZVVDX08tSTMdOCMPNCsGKB8J LSIKLiMKMyIGLh0HKhsEJRYDEQYACgEAAwAAAgAAAQAAAwABBAACBAADBQACBAABAwABAwABBAAB BAAABAAABAACBAACBAADBAACAwAEBgAEBQADBQADBgADBwACBgABBAABBAAABQAABQABCgAEDwAB FwoGHhAFKCYOMzEKMygMNSoMMyULMiQKMyQJMiMKMyIKMyIIKyQJLCUHKygPNDEUNywWOi8/Wlde enhogIJNZGUiSj4RNysNMx4NMx4KLiELLyIOMCALLR0VLCAUKx8TJhYMHg8JDwECBwADBQADBQAD BAADBAABBAAAAwADBgAEBwADBAAEBQAEBQACAwACBAACBAAAAQABAgACBAACBAAKNSIHMh8KNCUO OioONCwUPDMpRkdFZGVecHdkd31ddYBWbnlJYmdGXmNOVV9OVV8+WmU5VF83SE4qO0AVOzMPNC0H LRcILxkJMiEHMB8PNSoKLiMJLRwEJhYHFgoACwEAAgAAAAAAAAAAAAAAAAABAQABAgABAwAAAgAA AgABBQABBAAABAAAAwAABAAAAwAAAwAAAwABAwACBAABAwABAwADAwADBAABBAACBAAAAwABBAAB BAAAAwAAAwAAAwAAAwAAAwAACgADEgQDIBYLLCIOOS8OOS8LNykIMiUONScPNygOOSsMNykIMiEd TTpEaGhXfX1ye4Zpc31ObWc1U000SjcuRDA3OSBJTDFWX09aY1MnVkkUPzMNOxoKOBcJMx4LNyEO Mx4OMx4LLiUFJh0BDQQACgIAAQAAAAAAAQABAwADBQABBAAABAAABQACBAABBAABAwACAwABAwAB AwABBAADBQABBAABBAADBAADBAABAwABAwABAgADAwACAwABAQABBAAAAwAAAwAAAwAABQAACAAA GAcKJhIKNCUKNSYKNSQKNSQKOSgKOCcNOCoLNSgONCMROScWPUAqVFdDZGlNb3RWcnJVcHBianBi anBidGtfcmlWdGVNalwzV0MmSDQTOigJLRwLLyQJLCEJLyEHLB4FJBYHJhgEEwQACgAAAgAAAwAA AwAAAwABAQACAwACBAACBAACAwADBAACBAACBAABBAACBAABBAABBAADBQADBQAEBgACAwAEBAAE BAADBAAEBQACBAABBAABBgADCAADCwAHEAECFw4FHBIEKyQNNy8MMyUMMyUPNSQNMyIOMyQMMSIL MCMLMCMKLh8LMCELNycLNycNMicZQTVJWGNjc35qc3lOVlwmPjUZMCgMNRwOOB4KKB0MKyAJLh0J Lh0LLR0NLx8NJRMHHg0HDQMCBwAEBQAEBQADBgADBgABBQABBQAEBQAFBwAFBQAFBQAFBQAEBAAE BAADAwADAwACAgADAwADAwAOOyUKNyELOisNPC0UOTEVOjIjOj0+V1tKXG1GV2hGV2dFVmUrVEkn T0UsSUopRkciU0QaSTsSPjIPOy8POzELNSwJLh0JLh0KNCUJMiMKMikIMCcFKRYGKhcJGQoACgAA AgAAAQAAAQAAAQADAgAEAwACBAADBQABBAABBAAABAAABAAABAAABAABBQABBAAABAAABAABBAAC BAAABAAABQACBAADBQAEBAAEBAAAAwAAAwAABQAABAAABQAABQAAAwAAAwAABwABCgMCIh0OMi0K OSsLOy0KOSgKOSgMMyUNNCYLOisOPS4XNyorTT9KZGJhe3lqfn5pfX1qb11dYlBVWzhVWzhfY0xu clpoeX1WZ2owXFgfSEUKOh4IOBwNMiMMMSINNCYJLyEGKiAILSMGEAcACQEAAwAAAgAAAQACAwAD BAADBAADBAADBAACBAABBAAAAwAAAwADBQADBQAABAAABAAABQAABQABAwABBAAAAwAAAwABAwAD BAADAwAAAQAAAgAAAQAAAwAAAQAABAAACAAAFwgMKBYKOykKOigJOSUKOycKOSoJNygKOigKOigP OigOOScPOjIZRz8xVlU1W1pRZV5RZV5UX2hYZG1jbWlncG1fcmlWaF9NZV03TkYVPSsMMiENMiUK LiEMMSQLMCMHKxoHKhkEEwMACwAABAAABAAAAgAAAwAAAgABAwAABAAAAwABBAABAwACAwACAwAC AwADBAABBQABBQACBAACBAACBAABAwADAwADBAADBQADBQABBAAABAAABQAABgAACgABDgADFwsK IRQKMSMSOywONSUPNyYROi0ONSkNNCQMMyMNNCgLMiYKNCcLNSgOPSoMOygMNy0RPTNJWF9kdHtj aW5KUFUqPTcbLScKNCEMOCQMMCEIKxwJMBoKMhwKKBgOLBwSHxEQHQ8GDwQABwADBAAFBwAFBwAE BQAEBQAEBgAGBgAGBgAECAADBwAFBwAEBQADBgADBQAEBAACAgAEAwAFBAANOSkMOCgOPS4KOSoK OS8LOzEOOzcWRUAiQ1EkRVQoRkQkQT8WPzAUPS4QPCwPOioLPSsNPy0JNygJNygLOS4JNSsILiII LiIGLSIJMSYMNy0IMSgHLBsDJRUDEQYACAAAAAAAAAAAAQAAAwABAwABAwABBAABBAABBAAABAAA AwAAAwAABAAAAwAAAwABBAAABAAABAAABAAABAAAAwAABAABAwABBAACBAACBAAAAwAAAwAABAAA BAAABAAABAAAAQAAAQAABwABDQQAIBYQNysJOioNPy8JOCwKOi4KOS0KOS0IMy8LODMYOTMfQDtD XV1deXljfYRqhIxqf3VjeG5udWd5gHJ9g4t5f4digJZGY3gmVFATPToMMS4HKicLLCIOLyUPNSsK MCYHKh8EJRoFDwYAAwAAAQAAAAAAAwABBQACBAABAwAABAABBAABAwABAwAAAwABBAABBAAAAwAA AwAABAAABQAABAAABAAABAAABAAABAACBAABBAABBAAAAwAAAwAAAQAAAgAAAwAABAAABgAADwwK HhoINCgMOi0LOicNPCkNOSkNOSkKOSoKOSoQOzEPOS8WNCIjQy8sRjc+WklYZFNealhoa2Foa2Fq bmNucmd3f3h4gHlWe3swU1MWPy4LMiIMMSQHKx4KLyIKLiEKJxkHJBYFFgQADQAABAAABQAAAwAB BQABAwABBAABBAABBAABAwABAwADAwADAwACAwACAwACBAACBAADBAADBAAEBAAEBAACAwABAgAD BQADBQAABAAABQAABwAABgAADQADEwEDGw8GIBMKMR0POCMPOCkPNygQOSwPNyoMNykLNSgNNysL NCkJMSoJMSoOOSsPOy0LODMPPTk4Yl9Pe3lldWtHVk0bPTELKh8OLyMPMSUNMyAKMB0LLR0PMSEO Jx4LJBsQIhgMHRQEDgEABwADBQACBAADBgADBgAEBAAEBAAEBgADBAADBAADBAADBgADBgADBAAD BAAEBAAEBAAFAwAHBAALOzELOzETPzEOOSsOPT0OPT0OPTgOPTgRQTwVRkAaQDkTODAUOTEWOzMO QzUMQDMMOywNPC0INScKOCkKOCsJNSkMMycLMiYJMiMKNCUPNSsLMScJLSADJRgEDgcABwIAAQAA AAAAAwAAAwAAAQAAAQABAwABAwABBAABAwABAwABBAAAAwAAAwAAAwAAAwABBAACBAABBAAAAwAA AgAAAwAABAAABAAAAwAABAABAwABAwAABAAAAwAAAwAAAQAAAgAAAwAABgABCgECGxIPLCIHNy0M PTMKOjMMPDUOPTgKOTMHNC8KOTMRPTMUQDcmV1w5bXJddYRed4ZWeoJWeoJbeX1ffoJpdZZlcpJe eIdAWGcdTEEPOjAKNywKOC0LLygMMCkKNCkKNCkHKCYEIyEFEgcABQAAAgAAAgAABAAABAABBAAA AwABBAACBgACBgABBQAAAwABBAADBAADAwAAAwAABAABAwACBAAAAwABBAAABQAABQABBAABBQAB BgAABAABBAAAAgAABAAABAAABwAACQIAEhUJHyIIMzELODUNPTMOPjQOPy8NPi4POS8SPTMQOzgP OjcbOy4gQDM9UElVaWJkd3BqfXd0eG93enJ7gH+DiIeCjJaAi5VVeYMxU1wYPDETNSsMMSIKLyAN MSQMMCMRLB8KJBcIFwUABwAAAAAAAgABBAACBAABBAACBAADBQADBgACBgABBQAEBAAGBgADBAAD BAAAAwABBAAEBAAFBQAHBAAGAwADBAADBAAEBQAEBgADBgADBgABBwABBwABCwAFEQEDGRMHHxgI MiMNOSkNOy4OPC8POi4OOS0KNyMKNyMJNyYKOCcPNDEOMzANNTENNTEOPT0VRkY7ZWRQfXtcfnc7 W1QYQDQOMygRMyUPMCIPMyQOMiMPMi0KLSgPLyAOLh8OIxkGGRAECgMBBQAEBwADBQAEBwADBgAG BwAHBwAFBQAEBAADBAADBAAGBAAFBAADAQADAQAEBAAEBAAEAQAHBAAMPC4OPjATPzMRPTELPjgM PzkQPz8MOjoOPzMOPzMPOTUONzMPNC8WPDcOQDAKPCwKOiwKOSsJNyYKOCcKNSgJMyYNOCgLNSYN MiUKLyIKLywHKicKKiQBHBYEDAcABAAAAAAAAAAAAgAAAwAAAwAAAwACBAABBAABAwABAwABBAAA BAAAAwAAAwAABAAABAACAwADBAAABAABBAAAAwAAAwAAAwABBAABAwABBAABBAAABAAAAwAAAQAA AwAAAgAAAgAAAwAABQACCgEDHBYPKyUJNzMLOjcPPT0NOzsPQDoKOTIIOC4LPDIQQTgSRDoYSVMh VF1EXWJIYmdHamVNcGtQb2tQb2tWaW9QY2lGWlcxREERQzcPPzMKNywOPDERNSwRNSwPPzMMPDAK Kx8EIxcGEQQABQAAAwAAAwAAAwAAAwADBQACBAADBAAEBgEDAwADAwABBAABBAAABAAAAwABAwAB AwAAAwABBAAAAwABBAAABAAAAwAABAABBwAABAAAAwACAwACAwAAAwAAAwAACAAACgAAEg4KIh0I NTANPDcNPDUQQDoPPzUMPDITOTEVOzMPQDQQQTUWQT4mVFBHXXRcc4tkd4dpe4xveo5we5B0e5V/ h6GAjJ13gpJRY2kxQUcTODcQNDMMNykNOCoMNSoNNysSLykGIBoDDwMABwAAAwAABAACBAABBAAD AwADAwACBAADBQACBgABBQADBQADBQAEBwADBgACBAADBQAEBQADBAADBAADBAADBAADAwAEBQAE BQAEBAAFBgEFBwEEBwEACAAEDgMDFhEIHRcGLxwPPCgNPC0KOSoMOTMOOzUKOykKOigKOSsKOSsP NDEOMzANMy0ONC4NOzIaTENIaHhXeIhVbnMzSk8TQzgPPTILNCkOOCwPNCsOMikOMi0NMSwOMiUL LyIRIRsFEw4ECQEBBQAHBwAGBgAEBgADBAAEBgAGBwAEBAAFBQAEBgAFBwAHBAAHBAADAwACAgAF AwAEAQACAwADBAAPPi8PPzAUQDcSPjQKPjELPzIMQDMLPzIMOi0NOy4PODQMNDEKNCsOOS8KOysK OioMOywKOSoKOSoLOisKNSQKNSQKNSgLNykOMzAOMzALNSwHMCcHHhQAFAoDCQEAAwAAAQAAAQAA AQAAAgAAAgABAwADBAADBAABBAABAwAABAAAAwAABAAABAAABAABBgABBAABAwABAwADBgAAAwAA AwAAAgAAAgABAwABBAACBAACBAABBAAAAgAAAQAAAQAAAwAAAwAABAABBwAAEwcPJxkKLi4SOTkQ QD0SQz8NPy8KPCwPQDQMPDAMOi0RQDMPSkEUUEcoSTwvUUQ5UEg4T0dBUENBUEM0TU0ySkoWPjIS OS0POy0PPC4JNSsKNywNMiMPNSYROTALMSkLJxcCGgwBBwAABAAAAQAAAgAAAQAAAwADBAACAwAC AQABAQABAQABAgAABAAABAABAwABAwAAAwAAAwAAAwAABAAAAwAABAABBAACBAABBAABBAAABAAA AwABAgABAgAAAgAAAgAABAAABQAADwwIHBgHNCsMPDIPPTgRPzoPQz4LPjoYOzIcPzcVQTgVQTgN Q0ogW2NJZHVRbX5TZHRVZ3dWb31UbXpdbntfcH5dbXVNXGQmRj8ZODEKNCsKNCsJNSkJNSkKNSgI MiUKJiABGRQAAwAAAwAAAwAABAABBAABBAABBAABBAADBAADBAAAAwAAAwADAwADAwABBQACBgAD BQADBQACAwABAwACBAABAwAAAwABBAACBgABBQADBQADBgABBQABBAAABgABCAEAEg0IHRcHMjAM OTcOOS0OOS0POy8OOS0KOysKOioKOSgKOSgLNykKNCcPOjAQPDIRODUiTElIX3ROZXoxSU4cMjcR OisPOCkKNCULNSYOMikKLiULLiUMLyYOLyMJKR0RIhUCDwQEBgAEBgAEBAAEBAAEBQAEBQADBgAE BwADBAAEBQAGBwAFBwAGBgAEBAAEAwACAQACAAADAQADAwACAgAPPzEPQDIUQDAUQDAPQDIMPC4O PikQQSwNPy0LPSsNOy4PPTAOOiwOOiwLOS4NOzANOzANOzALNykPPC4KOSYLOicKOCkNPC0ONzIN NTENNTEHLioEGAwADwQABwAABAAAAgAAAgAAAQAAAgACAQADAgACBAACBAACBAACBAAABAAABAAB BAABBAABAwACBAACBAACBAACAwAEBgABBgAABAAAAwAAAwABAwABAwABBAABBAABBAAAAwAAAwAA AQAABAAAAwAABAAABwAAEAQHHQ8KLywSOTUNPjINPjIPRTENQy8RQzcSRDgRQDUSQTcORDwORDwf QzwhRT4nSUQjRT8fQy4mSjUWRDoTPzULNSwPOjAOPDENOzAMNDAMNDAMOi8MOi8SOTcOMzELIRcA EQkAAgAAAgAAAwAAAwAAAgABBAABBAACBAACAwACAwABBAACBAABAwABAwACAwADBAAAAwAABAAB BAABBAACAwACAwADAwADAwACBAACBAAABAAAAwABAwAAAgAAAQAAAgAABAAABAAADwUFGQ8EKiMM NS4QPjkSQDsURDsPPjUWPzcdRz4cRj0eSD8RR0gcVVY9Wlw+W106T1RDWF1AWlc8VVNJVlhGU1U0 UUwpRT8SPzEPOy0MNTIKMi8KMyoJMSgNOCoIMSQKHxYADwcAAwAAAwAAAgAAAwADBAACAwABBAAC BAADBAADBAACBAABBAAEBAAEBAADBQACBAACAwAEBQAEBgADBAAEBAADAwABBAABBQADBAAEBQAD BQADBgABBAABBAAABwABCAEAEg0IHRcHLisLNDEKOTILOzQNPTENPTENOSsLNykMOCgNOSkMPSsK OykKOiwOPzESPzEcTD0yT04pRUQVNy4VNy4WOi8VOC0NOCgOOSkPNCcKLyIJMyAKNSIOMiULLyIM Fg8ABwMEBAAEBAAEBAAEBAAEBAAFBQADBAADBAADBgAEBwAEBwAEBwAEBwADBgADAwADAwADAQAD AgADAwADAwAOPDMOPDMYRzkWRDULQy4LQy4KPyoMQSwTRDITRDIOQCoPQSsKPCILPSMMPDAKOi4O PDEMOi8MNy0POjAKOi4KOS0KOysLPS0KOC8LOTANMCkKLSYEEwUACgAAAgAAAQAAAQAAAQAAAQAB AwADAwABAQAAAwAAAwAAAwAAAwABBAABBAADAwAEBAACBAABAwADBAADBAAGBAAGBAACBAABBAAB AwAAAwABAwABAwABAwAAAgAAAwAAAgAABAAABQAABAAABAAABAABBgABDwIEFAYDIxoPMyoMQzQL QTMNSDkNSDkMQzcNRDgTSTwSSDsMPzkQRT4WQEMXQUQbST8YRjwRQS4TRDAORjMORjMRNy8RNy8M PDISRDoOOTEOOTEMPS8KOy0OODAHLygNHRQACgMAAQAABAABAwADBAAABQAABAAABAABBQABBAAB BAABBAAABAABBAABBAAABAAABAABBAABBAAABQAABAAABAAABAACBAADBgABBAABBQABBAAAAwAE BQADBAABAwABAwAABAAABgEACgUCDwoDIiELLSwKPTkOQT0URzsSRTkZRj4aRz8iRj0mSkEWTUUW TUUnTEMlSUApQzotRz4qSUAnRj0lQ0AkQT8VPjEVPjEKOSQLOyYNOC4KNCsKMywLNS4QOC0KLyUP IA8ACgAAAQAAAQAAAwAABAAAAwAAAwACBAACBAADBAADBAADBgADBgAEBAAEBAADBQABBAADBAAE BgAHBwAHBgAEBQAEBgAEBQAEBQAEBwAEBwADAwADAwABBAACBAABCAECCgICEw8HGRYHKyAPNCkM PDUMPDUOPDEQPzQNPi4KOioMOCYPPCoPPjEPPTAJOS8OPzUWPzAZRDQeRjUYPy8PNygPNygQNTAP NC8OMikQNSwPNSgOMyYLNSQOOScWMyUPLB4KEgYABAABBAABBAADBgAEBwAEBAAGBgADBAACAwAD BwADBwAEBgAEBgACBAADBQADBQABBAABAwABAwADBAADBAALMC0SOTUWQTgZRjwPSjcNRzMKQDAO RTQRSDcNQzEMRjANRzENPjINPjILPDINPjQNPjQKOjAMOjEMOjEQOzcPOjUMPi4KPCwNODAJMisI IR0CGBUBCQAABAAAAAAAAAAAAQAAAgAAAgABAwACBAABAwAABAABBAAAAwAABAADBQACBAABBAAD BQADBQABAwABAwABBAAFBAAEAwADBAACAwAAAgAAAwABAwABAwABBAABBAAAAwAAAwABBAABBAAA BAAABAAAAgAAAwABCQADCwABGw8JJhgKPDUNQDoRSD8TSkEPRj0NQzoSRTkTRjoNRTsRSkATREAT REARQDMOPC8PQS0PQS0MRDEMRDESRTkMPTEMOzgSQz8PPzEJOCoMQDEKPS4ONCMHLBsHDwQABAAA AgAAAwACAwACAwAABAAABAABBQABBQAABAAABQABBAABBAAABAAABAAABAAABAAAAwAABAABBAAB BAAAAwAAAwABBAACBAAABQAABQAABAAAAwAEBQADBAABBQABBQABBAABBQAABwMBCwYDHRYMKiMK PjcORDwQRz4SSUAUQ0AXR0UUSUEUSUEWSEMWR0EbRT4bRT4RRDgQQzcPRT0PRj4NPDcNPDcSPTMT PjQOPysNPioKPCwLPS0OPzULPDIWLzEQKCoEDgMAAwAAAQABAQAABAABBQABBAAAAwACBAACBAAE BAAEBAADBAADBAAEBAAEBAABBQABBAADBQADBgAHBQAGBAADBAADBAACBgACBgAEBwAEBwADBQAD BQACBgADBwABCQACCgEDEAoHFQ4GJBwPMCgKPTUQRT0QQzcSRTkORDAPRjISQzMSQzMOPzMNPjIQ QDoRQTsWQzsUQDkSODIWPDcTQDISPzEPOjALNSwNMykPNSsMNycJMiMKNyoJNSkQLBYIIg0HCwAD BgADBwAECQAFCAAEBwAEBwAFCAAEBQAFBwAHCAAHCAAGBgAGBgAIBwAIBwADCAADBwAEBQADBAAD BQACBAAEIhYPMCQKOTIQQTsPRj0QRz4MRzoNSDsORzUMRTMMQzcORTkPQUANPz4POjkTPj0RQTsP PjgPPzkMPDUPQTELPS0MPC4LOy0ONCwGKiIIGw8ADgQAAwAAAQAAAAAAAgAAAwAAAwABBAABBQAA BAAABQABBgABBgAABAAABAABBAABAwABBQACBgABBgAABAABBAACBgADAwACAgABBAABBAAABAAA BQABBAAABAAABAAABQAAAwAAAwACBAACBAABBAACBAAABQAABgAABgABCgAADwQLHxEKLywQNzMP RT8RSEMQSDoQSDoQRjkRRzoPSDsQSj0USUURRkETRjwOPzUMQTAMQTAMQDMNQTQNRDUNRDUNPDkP PzwPRzkMQzQOQzgKPjMOJyAFHBYDBQAAAwAAAAAAAgACBAABAwABBAABBAABBAABBAAABAABBAAA BAAABAABBAABBAAAAwAABAAABAAABAABBQABBAACBAABAwABAwACBAAABQAABQABBAACBAACBgAB BQAABQAABAABBAAAAwAABgABCgEEIRYKKR4INy0OPjQSSjwTTD0QRUASR0MSSUEPRj4VTEARRzwT RD4TRD4QRjsQRjsQREUPQ0QNQDkKPDQOPzMOPzMNPTEKOi4MOi8OPDEPQDcLOzEZMCgKHxcBBgAA AQAAAgACBAAABAACBgABBAAABAADBAAEBQADBAADBAABBAAABAABBAABBQABBAAABAACAwADBAAE BAADAwADBAADBAACBwADCAACBgABBQABBQACBgADBwADBwABCAACCQABDAQIFgwDHhcKKCEKOC8S QTkPQDIPQDINQTQOQzUPQTgQQzkQQzcOPzMOPzUPQTgPPTINOzAUPDUUPDUPPTAPPTAPOS8POS8Q NSwPNCsOOSsHMCMKMzAMNTIOJA4BFAEBBAADBwAGCgAFCQAEBQAEBQAEBwAEBwAEBwAEBwAEBwAG CQAHBQEHBAAKBQAIBAADBAADBAACAwABAQAABAAABAABFggJIhIHNCcQQTMSQTkVRTwRST0PRzsP SDsPSDsQRT0SRz8PQzwNQDoQQTURQzcRRj4RRj4OQzgOQzgPQDILPC4PPTILOS4LKh8DHxUGFAQA CgAAAQAAAQAAAwAAAwAAAwAAAwAABAAABAABBAABBAADBAADBAABBAABBAAABAAABAABBQACBgAB BgAABQABBwACBwADBQABBAABAwADBAACBAADBQABBAAAAwAABAAABAAABAAABAABBQAABAABAwAB BAABCAABCQAABAAABQAACgAIFgkDIxoQNCsKPzoTSkUUSj8SSD0QST8RSkAPSD8PRz4TSEASRz8W ST0SRTkNQzsPRT0RRDgPQTUPRTgLPzINOjQSQDsRRzgRRzgTPjsIMC0LGQ4ACgEAAAAAAgACAwAD BAACBAACBAABBgAABQAAAwABBQABBQABBAAABQAABQAAAwABBQACBAADBQACAwABAwABBQAABAAB BQABBAACAwADBAADBAADBAABAwABAwABBAABBAAABgAABAAAAwAAAwAABgABCgEHIBQBFwwFKCYO MzEPTD4QTT8PSEgPSEgRSkAQST8TST4TST4VRD4VRD4TQzgTQzgWSDwQQTULPC4LPC4MQDULPzQM OzQLOjMOPjAMPC4WPzkUPDURJhsAEAcAAgAAAQAAAgABAwACBAADBQADBgACBAADBAADBAADBgAC BAACBAACBAABBAABBQABBAABBAAEBQAEBQADBAADBAADBQADBgADBgADBQACBAACBAABBAABBAAB BQABBAAECAAGCgACCgEHEQYBGQ0HIhUONSkONSkOPjITRTkRQzQSRDUQQTsPPzkPPjgPPjgPPjEQ PzIORTQMQzISPjcRPTUPOjIPOjIOODAPOTEQOy8POS0MOi0KNyoTMzIPLi0IEwMACQABBAAFCAAG BwAFBwAEBgAEBQAEBAAFBQAEBAAEBAAGBwAHBwAHBQAHBgAFBQAEBAADBAADBAABBAABAwABBAAD BQAACgABDQEDHA4NKRkPLCoaOTcQRjsQRjsXRT0XRT0URUEVRkMPQTgPQTgPRDcPRTgRTDwQSjsN RjcNRjcNPTENPTEOMC4DIR8HFgsADQQDAwAAAAAAAAAAAAAAAQAAAQAAAQAAAQAAAQAAAgABBAAA AwAAAwAAAwACBAACBAABAwACBAAABAABBQADBAABAwABAwABBAAAAgAAAgABAwACAwADBAADBAAC AwACAwAABAABBQAABQAABAAAAwAAAwAABAAAAwAABgABBwAAAwAABAAAAwACBwABFg0KIxgJLyES OywRTD4STT8RTUYRTUYPRkkOREcRR0UTSUcWRkYUREQNQTcQRjsSRTcPQDIRRDEPQS8RRDgNPjIU PTAUPTARMSgCHRUDCwEABAAAAAAAAgABAwADBAADBAADBAAABAAABAABBAABBAABBgAABQABBAAC BAABAwABAwACBAACBAADBQABAwADAwADAwABAwABAwACAwADBAADAwAEBAABBAAABAAEBAAEBAAA BAAABAAABAABBAAABAABBwAACAMACgUDHBYIIx0IOTgPQ0EORj0PSD8PST8PST8VRzsWSDwWRDoV QTgPRDwPRDwRRDoQQzkMQDEKPi8PPzUNPTMOPjQNPTMRPTMRPTMWNTAPLikFDAUAAQAAAAAAAgAB BAABBAADBAADBAADBAADBAADAwAEBAADBAADBAADBQADBQACBAACBAACBAADBQACBgACBgAEBgAE BQAEBQAEBgAFBQAEBAABBQACBgABBAABBQACBgACBgAGCAAHCQADDAEDCwECDQcJFg8IKSoLLS4N PS8RQzQTPjcVQDkQPz8PPj4QPjwRPz0QQTgSRDoVQzIUQTETPjcQOzMSNzUUOTgSODAPNC0OPDEO PDEQOzcOODMXLiQKHhUBBgAABAADAwADBAAFBQAFBQAEBgAFBwAIBwAIBwAHBQAHBgAKBQAKBQAE BQAFBwAEBgADBAADBQADBQABAwABAwACAwADBAAABgAABwABDAMFEgcDHBoMKCYOOzkQPjwUPzsV QDwSQ0YSQ0YVRz0URjwQSDwPRjoSR0APRD0OPjgNPTcPOTULNDEIHiABFRYEDwMABQAAAAAAAAAA AAAAAQABAwACAwABAwABBAABAwAAAgABAwAAAgABAwABAwABBAABBAADAwACAwABAwABAwADBAAD BAABBAACBAAABAAABAABBAACBAAABAAABAABBAABAwABAwABAwABBQAABAABBAABAwADBQABBAAA AwAAAwAAAwAAAwAAAwAABAAADAQEEgoBHQ4SMyIPPj4RQUEPTEkST00SSkkSSkkSSkkTTEoXRkcW REUQRjsVTEATTjwRTDoURjgTRTcSTEUPR0ATOjcPNDEIGw8ADQMAAwAAAAAAAAAAAgABBAADBQAB BAABBAACBAABAwACBAACBAABBAACBQADBAADBAABAwABAwADBAAEBQABBAABBAACBAABAwAAAwAB BAACAQADAgADBAADBAADAwADAwAEBQADBAADBQACBAABBQABBAABBAADBgAABAEACAQADwoHGxYH LS8ONzkLQzoPSD8PTUUQTkYRSEMRSEMQR0ERSEMPRUUPRUUSQzwSQzwPRTgPRTgPPzoPPzoQQzQP QTMUPDUTOzQWKSgADw4AAQAAAQAAAwABBAADBAADBAACBgADBwADBAACAwADAwAEBAAEBQADBAAD BAADBAADBQACBAAEBQAEBQADBgADBQADBQADBQABBAABBQAEBgAEBgADBAAEBgABBgABBgABBwAC BwAGCQAHCgADCQMBBwEBCAIEDQYEGhsIICEMOTcQPjwRQEATQ0MTQEEUQUMSRD4RQz0OQTsSR0AW QTgTPjQRPDkSPToSPTkUPzsUOjASOC4RPDgPOjUVPDkQNzMTIBIBCwEAAwABBAADBgAEBwAFBQAH BwAFBQAGBgAFBwAFBwAFBwAFBwAHBwAHBwAEBgAGBwAIBwAHBQADAwAEBAACAwABAwADAwAEBAAA BAAABAAACQAACwAADAcHGRMFIiQLKiwUNTEYOzcWPUQXPkUTREYSQ0USQz0PPjkSPDsTPTwOODQJ MS4LKikHJCMDDwoACgUABAEAAQAAAQAAAAAAAwAAAwADAwAEBAADAwADAwAABAAABAABBAAAAwAA BQABBgABAwABAwADBQADBQABBAABBAADBQADBQABBAADBQAABAAABAABBAABBAAABAAABAABBAAA AwABAwABBAAABAABBQABBAABBAABAwABBAAFBAAEAwAAAgAAAgABAgABAgAABgEECgUBEwoNIhgO KjIZOEAPREQQRkYQRkQRR0URVFYQU1URSkQTTUYWTkgWTkgQTEEPSkAPSkAMRjwSTEUORj8PLykD IBoDCAEAAgAAAQAAAQAAAwABBAAABAACBgAABAAABAADBAABAwAAAgABAwADBAAEBQABBAACBAAB BAADBQADBAADBAABBQABBAAAAwAAAwADBAADBAAAAwAAAwAAAgAAAwADBAADBAADBAADBAABAgAB AgADBAABAwADBQACBAAABAAABAAACgYEEQwKKCUSMS4JOzcPQz4NST8PTUMPSEUOR0QQSkkQSkkO SkMPTEQPSEEPSEERRj8SR0AWQT4WQz8WSEERQTsXOTgPLi0IDwoAAwAAAAAAAgAAAgAAAgABBAAC BgAEBQAEBQADBAACAwACBAACBAADBgACBAADBAADBAABAwACAwADBAADBAADBQACBAABAwACBAAB AwABAwADBAADBAAEBAAFBQADAwADBAAFBwAEBgAEBgAHCAADBwIDBgEACQADDgMDFhAFGBMKLjEP NDgPQ0UPQUQVP0EYREYWRz4WRz4PSEEPSEESQDwSQDwSPTwRPDsRPzoPPTgWQTgSPTMRPTURPTUX NCwGHxcDBwAABAAABAAABAADBgAEBwAEBwADBgAEBgEDBQECBgACBgAGBwAHCAAJCAAHBwAEBgAE BgAEBAAEBAABBAABBAABAwABAwADBAADBAAAAwAAAgAAAwAAAwAACQAADAAACgUDDwoDGREFHBQH JScJJykHKSwJLC8JMisJMisKLCMHKSAHIRcBGA8EEQwACwcAAwAAAQAAAAAAAAAAAQAAAQABAwAB BAACAgADAwACBAABAwABAwABAwAABAAABAABBAABBAAABAAABAACBAADBQABBAADBQADBAAEBgAD BwABBAACBAABBAABBAABBQACBAACBAAABAAABAAAAwAABAAABAABBQADBAADBAACBgACBgACAwAC AwADBAAEBQABAgACAwAAAwADBgMBDQQEEQgKGBMRIRsLNCkQOy8PRD8PQz4PSUkSTU0TSUcTSUcV RkAVRkARSUYPRkMNREALQT4NMDEGJygHFgoADAIAAgAAAQAAAQAAAgABBAADBgADBAADBAADBQAC BAADBAACAwADBAADBAADBQACBAAEBgADBAABBAADBQABBQAABAADBAADBAAABAABBAADBAAEBgAB BAAABAAAAwAAAwAAAwAAAwACBAADBQABAwABAwABBAABBAAFBQAEBAACBgABBAAACgADEAMEFxQE FxQHLCIMMigKPTUSRz8MR0MPTEcPSkYPSkYTTEoRSUgPR0YQSEcQSUMORj8QQT4SREAWPjsOMzAM HBMBDwcBAwAAAQAAAQAAAwAAAwAAAgABBAADBQAEBQADBAADBAACAwADBAACAwADBAADBAABBAAB BAABBAACBAABBAACBAABBAACBAABBAABBAAEBQADBAACBAACBAAEBAAEBAAFBAAHBQAFBwAEBQAE BgAGBwACBgABBQABCQEBCQEBEQUEFgoKHRYQJB0QLzgWNz8QPkUSQEcTRT4VR0ASTEMPSD8SR0MP RD8TPj0VQD8VQDwTPjoYRjwVQTgUNyoMLSEIEAUABAAAAQABAgABAwACBAAEAwADAwAFBQAGBgAD BQADBgABBQACBgAHBgAIBwAIBwAJCAAHBwAFBQAFBAAEAwADBAADBAAEBAADAwAEBAAEBAAAAwAA AQAAAAAAAAAAAwAABAAABAAABAAAAwAABAEBCwoACQgBDwQADAICEAUBDwQDEQQDEQQCCgIABAAA AwAAAQAAAQAAAAAAAAAAAQAAAwAAAwAAAgABAwABAwABAwAAAwAAAgABAwABAwAAAwAAAwABBAAA BAABBAADBQABAwABAwABAwABAwAEAwAFAwAABQAABAAABAAABAACAwADBAADBAADBAADBgACBAAB BAABBAAABQABBgABBAABBAADCAADCAADAwACAgAABAAAAwAAAwAABAACAwAEBQAAAwACBwAACAEF DwcEGQ8IHhMIKiEPMikKOS8LOzEOOjsRPj8PPTkPPDgMPj0KPDsHKSoJKywEFg4ADwgABgAAAwAA AAAAAAAAAAABAAAAAwAAAwABBAADBQADBQACBAACBAACBAADBgADBgADBQACBAABBgABBgABBAAD BgACBAACBAACBgABBQAABAABBAADBAADBAABBAABBAAAAwAAAwACBAACBAADBAADBAADBAADBAAD AwADAwADBAAEBgADBwABBQACCgACCgABDgkEEQwDFRENIh4IMSYOOS0MQEEMQEENQz0ORD4RRk0Q RUwSQ0YQQEQUQ0AUQ0ATQzoMOjEOIx0DFQ8BBwAAAgAAAAAAAQAAAgAAAwABBAADBQAEBwAEBwAE BQAEBQAEBQADBAADBAACAwADBQADBQACBAACBAADBAAEBQAEBgADBAADBAAEBgAEBAADAwAEBQAE BgAEBQAEBQAGBgAHBwAEBQAEBgAFBwAFBwAEBQAEBQABBwABCAADCgACCQACCgEEDQMHDwwNFhMJ GisMHi8KLjQRNz0RPDIYRTsSREAQQT4RQT4TREAZQUEZQUEVPTQVPTQaOTcVMjATHhEEDgMDBAAB AgACAgAEBAAHBQAHBQAFBAAFBAAEBgAEBQAEBAAFBQAEBAAFBQAHBAAJBgAJBQAKBwAHBwAFBQAF BQAEBAAGAwAFAwAEAgAEAgAEAwAFBAAAAgAAAQAAAgAAAgAABAAABQAAAwAAAwAAAwAAAQAABAAA BAAABwAAAwAABQAABQAABAAABAAAAwAAAQAAAQAAAAAAAAAAAAAAAAAAAQAAAQAAAgAAAQABAwAB AwABAwABAgABAQAAAwAAAwAAAgAAAgABBAACBAABAwABBAAAAgAAAgABAgABAgABAwABAwABAwAB AwABBAABAwABBAADBQABBQABBQADBQACBAACBAABBAABAwABAwAABAAABAADBAADBAABBAACBAAA AwAAAgAAAwAAAwABAgAEBAAAAwAAAQAABAAABwAACwADEwUFFQkHFgoEHRQKJRsKKR4EIRYHIh4E HRkDHBoDHBoDEAsACgYBBgAAAwAAAQAAAAAAAAAAAAAAAQABAgABAgADBAADBAADBAADBQABAwAD AgADAwACBAADBgACBgABBAAABQAABQABBAABBAABBAABBAAABAAABQAABQABBgACBgAAAwADBQAC BAABAwABAwACAwACAwADBAAEBQADBAADBAABBAAAAwAAAwAAAwACBwAABAAABQABBgAACgAACgAA CgIGFQoEHBAHHxMEJCMLLSwMOTQPPDgPO0UNOEEQNDMSNzUUOjQOMi0JHRkDFREECgQAAgAAAAAA AAAAAAAAAgAAAgAAAwABBAABBQAEBQAEBQABBAABBQADBQACBAABAwABBAABBAABBAABBAABBAAD BAAEBQAEBQADBAADBAAEBAADBgACBAADBAAEBQAEBgACBAABBAACBQABBAABBAACBwADCQACBgAC BgACBgACBgADBwEDCAEDCAQCBwMACAoDDhADFBYHGBsFIBYHIxkJLSoPNTILNT8IMTsONzUQOjkZ NDcWMDIZMCwTKSULGBQBDAgBAgAAAAADAAAGAwACAwADBAAEBgAFBwAFBQAHBwAEBwEEBgAEBAAE BAAGBgAFBQAFBQAGBgAGBAAHBgAIBwAGBAAFBwAEBgAFBAAFBAADAwADAwACAwACAwAABAAAAwAA AgABBAABBQABBAAABAAABAABBAAAAwAABAAABQAABAAAAwAAAgAAAwABAwABAwABAgAAAQAAAQAA AQAAAgAAAQAAAwAAAwABAwABBAAABAAABAAABAAAAwACAwADBAABBAACBAACBAABBAADBQADBgAB AwADBAADBQABAwACAwADBAADBQACBAACAwAEBQADBQACBAAABQAABQAABQAABAAAAwAABAAABAAB BAADBAADBAACAwACAwABBAABBQABBAABBQADBAADBAAAAwAAAwABBAACBAAABAAABAAABgAABwAA BQAABAAACgAACwACCgAFDwMBDwUBDgQBDwIACwAACQEACAABBgAAAwAAAgAAAgAAAQAAAQABBAAB AwACBAACBAABBAABBQADBQACBAADBQACBAACBAACBAADBgAEBwACBgABBAABBQABBAADBAABAwAA BQAABAABBgAABAAABQABBgABBgAABAAAAwAAAwACAQACAQACBAACBAABAwABBAADBAADBAABBgAA BQABBAABBAABBwABBwAAAgABBAAABQAABAAABAABCAEADQADEQIAEAsHGxYGGRYIHBgJHhMJHhMG Gg8HHBEEFg0BEQgCCQAABgAAAQAAAQAAAQAAAAABAQADAwABBAAAAwAABAAABAADBAACAwAAAwAB BAABBAABBAAAAwAAAwADBQABBAABAwACBAACBAACBAACBAACBAAABQAABQABBQABBAADBQACBAAC BgABBAABBAABBAABBAABBAABBgACBwABBQABBQAEBwADBgAEBgAEBQACBgABBQAABQAABwAADAAC EAEBEgcBEQcCFA0DFg8GIBoFHxkJHyELIiQKFxwEERYGDQQABQAAAwAAAgAAAQAAAQACAQADAwAC BAACBAADBAAEBAAEBAAEBAAFBwAEBQAGBgAEBAAFBQAFBQAEBgAFBwADAwAEBAAIBwAHBQAEBQAD BAAEBAAEBAACAwACAwABBAABBAACBwAABQAAAwAAAwABAwABAwAAAwAABAABBAABAwAABAAABAAA BAAAAwAAAwAAAwAAAwAAAwAAAQAAAQABAQABAQAAAwAAAwAAAwABBAADAwADBAAABAAABQABBAAA AwABBAACBgADBQABBAABBAADBwABBQACBgADBAADBAABBAACBAADBAAEBQADBAAEBQADBAADBAAC BgACBgABBwABBgABBAABBAADBAAEBQABAwACBAACBgABBQADBQACBAAABAABBAAABAACBgADBAAE BgABBwAABQAABAAABQAABgAABwABCAADCgADBwEAAwAABwAABwAEBwAEBwABBwABBwAABwAABwAA AQAAAwABBAAAAwABAwABAwABAwAAAgADBAADBAAFBQAGBgADBQADBQAEBQADBAABBQABBAAEBwAD BgADBgADBgACBAADBgADBAADBAADAwADAwACBAACBAABBQABBAABBgABBwAABQAABQABBgAABQAB BAADBQADBAADBAAABAABBgAEBAAFBQAEBQADBAADBAADBAABBQABBQAAAwAAAwAABQABBgAABgAB CAADCAECBwADCgMDCgMDCgQHDwgECgADCQABCgADDAIBBQAABAABBAABAwAAAQAAAQAAAwAAAwAC AwADBAAEBQADBAACAwADBAACBAACBAABBAABBQABBwACBwADAwADAwAEBAAEBAACAwADAwADBQAC BAABBAABBQACBAACBAABBQACBgAEBwADBgADBgADBQADAwADBAAEBAAEBAADBgACBAACBgACBgAE BgAEBgAGBwAEBQACBQACBQAABwABBwAABgADCQEACgMACAEACAABCgEEDgMCCgEBCwEBCwEDBwMA BAABAgAAAAABAQABAgAAAgABAwAEBAAEBAADBAAEBgAHBQAHBQAHBQAHBQAHBwAEBAAEBAAEBAAF BQAEBAAHBwAHBwAGBAAGBAAFBQAFBQAHBgAGBAAGAgAHAwACAgACAgADAwADAwAAAgAAAgABBAAB BAAAAQAAAQAAAwAAAwAAAgAAAQAAAwAAAwAAAgAAAQABAgABAQAAAQAAAQAAAQAAAQAAAwABBAAB AgABAQAAAQABAwABBAABBAABAwABAwABAwACBAABBAABBAABAwADBAADBAAEBQACBAACBAAABAAB BAABAwACBAADBQACBAABBAACBAADAwABAgAEBAAEBAABAwABAwACAwAEBQAFBAAFBAADBAADBAAD BAADBAABBAAAAwADAwAEAwADBQACBAACBAAEBgACBQABBAAABAABBgAABAAABQAEBAEDBAACAwAD BAEEBAEDAwACAwACAwAABAAAAwAABAAABQAAAQACAwADBQAAAwABAwACBAAEBwACBAABBAABBAAB BwACBwACBAADBAADBgAEBwAABQAABQAECAABBQABBgAABQACAwADBAACBAADBQABBAABBAAEBAAE BAAABQABBgAABAABBQAABAAABAADBgADBQACBAADBAAAAwAAAwAABAACBgADBAADBAABBAABBAAC BAACBAACBAACBAAABAAABAABBQABBgEBBgABBgAEBwEDBQADBQAEBgAEBwADBQAABAAABAABBQAA AwAAAQABAgAAAQAAAQAAAQAAAQAAAgAABAAFBQADAwAFBAAGBAACBgACBgADAwADBAAEBQADBAAD BAACAwADBgACBAAGBAAGBAAFAwAEAwAEBQAEBQADAwADBAAEAwADAgAEBAAGBgAEBgAEBgAEBAAE BAAEBQAEBQAEBQAFBgEEBgEDBAAEBAAEBAAEBAAFBQAEBwACBAADBQADBQAEBwAEBwAABAADBwED BwQABAEEBQAGBwEBBgAAAwAAAwAAAwAAAwAABAADBAADBAAAAwAAAwAEAwAEAwAFBAAGBAAHBwAH BwAHBQAHBgAIBwAHBQAHBwEFBQAFBwAGBwAGBgAGBgAKBgAJBQAGBgAFBQAEBQAGBwAIBwAHBgAK BQAJBAABAwABAwACAwACAwAABAABBAABAwABAwABAwACAwADAwADAwABBAABAwABAwAAAwAAAQAB AwADAwACAgAAAgABAwABAwABAwAAAwABBAABAgABAgACAwADBAABBQABBAACBAACBAACAwACAwAB AwABAwADBAADBAACBAABBAABAwABBAAABAAABAADBAADBAABBAABBAACAwACAwADBAADBAADBAAD BAABAgACAwACBAADBQAGBAAFBAABAwACAwADBAADBAABBQABBAAEAgAHBAAEBgACAwADBQADBgAD BAACBAAEBQAEBQADBQADBQAEBAAEAwABBAADBQABBAAAAgABBAACBAACCQAABAAABAAABAABAwAC BAABAgABAgACBAAEBgABBwAABAAEBwADBQAEBgAFBwADBQADBQADBAAFBwABBQABBAAHBwAGBgAD BgACBAAABAACBgACBAABBAADBQECBAAAAwABBAAABAAABAAAAwABBAABBAABBAAEBgADBAADBAAD AwAABAAABAAAAwACBQEDBAACAwABAwACBAAABAABBAADBAADBAABBAABBQABBAABBAAABAABBgED BgADBgABAwADBQADAwACAwABAwABAwAAAQABAgADAgAFBAAAAgABAwAAAwAAAwAAAwABBAEEBQAE BgAGBAAHBQADBwABBQAEBAAEBAACBAACBAADBAADBAAEBgADBAADAwAEBAAGBAAGBAAFBwAFBwAC AwAEBAAEBAADBAAHBQAGBAAEBQAEBgAGBgAFBQAEBgAEBQADBAAEBQAEBgEDBQEFBQAGBgAEBQAE BgADBgABBAAEBwEDBQAEBwAEBwAABAADBwEDBgEDBwIBBgEABAABAwABAwAAAwAAAwAAAwABBAAC BAEBAwACBAADBAAEBgAJCgEHBwEGBgAHCQEFBwAEBQAGBwEJBwAHBgAHBgEFBAADBAADBAAFBQAH BwAHBwAHBwAHCQAHCAAGCQAEBwAGBgAFBQAHBQAFBAABBAABAwADAwADAwABBQABBQAABQAABQAA BAABBQABBQABBQAABQABBgAAAwAAAgAAAgAAAgABAwAAAgABAwABBAAABAABBAADBAADBAADAwAD AwACAwADBAACBgABBQABBAACBAABBAACBAABBAABBAAEAwAEAwACBAABAwAAAwAAAwABBAACBAAA BAAABAACBAABAwACAwACAwABBAACBAACBgACBgABBAABBAABAwABAwADBAADBAABBAACBAABBAAB BAADBAADBAADBAADBAACAwACAwACBAADBQAAAwAAAwABBAADBQADBAADBAAABAABBAACBAADBgAD BAABAwADBQACBAABBAAAAwACAwACAwACBAADBQAABAAAAwACBAADBQABBQABBAAEBgAEBgAFBQAG BgADBwACBgADBAADBAAEBAAFBQAEBQADBAABAwACAwAEBAAGBgADBgABAwADBAADBAABBAABBAAA BQAABAABBAABBAADBAADBAACBAADBQADAwADAwABBAABBAABBAABBAABAwABAwABAwADBQABBgAB BgABBAAABAABBAABBAABBAAAAwAABQABBgADBAADBAABBAABBQABBQABBAABBAAABAAABAAABAAC AwACAwAAAwAAAwABAgADAwADBAADBAACBgACBgAEBQAEBQACBAACBAADAwADAwACAwADBAACBAAC BAACBAACBAADBAADBAAEAgAEAgADBQADBgACBAACBAADBAAEBAAEBAAEBQAEBAADAwAEBQAEBQAE BAAEBAADAwAEBAADBgACBAAEBAAFBAADBgADBgADBwACBgABBQABBQABBQACBgACBAACBAAEBAAE BAAAAgAAAgABAwABAwACBAACBAACAwACAwABBAABBAABBAABBAADBgAEBwAGBgAEBAAEBQAEBQAD BgADBQAEBQAEBgADBwABBQAAAwABBAAEBgAEBgAEBgAFBwAFBwAFBwAFBwADBAAEBAAEBAADAwAD AwABAQACAgADAwADAwADBAADBAABBAABBAABBAACBAABBgABBgABBQADBwACBgABBAACAwADBAAB AwABAwAABAABBAAABAABBQADBAADBAADBAADAwABBAACBAABBQABBQAABAAABQABBAABBAABAwAC BAAEAwAEAwACAwABAwAAAwABBAABAwABBAAAAwAAAwABAwADBAACBAACBAADAwADAwACBAACBAAC BAADBgABBAAABAADAwADAwAEBQAEBQABBAABBQADBAADBAAEBAACAwADAgADAgADBAAEBgAAAwAB BAACAwAEBQAEBQADBAAABgAABgACBAADBgADAwACAwADBAADBAADBAADBAAEAwAFBAAEBAAFBQAC BgABBAAEBQAFBwADBAAEBQAFBwAHCQAEBAAEBAADBAAEBgAEBQADBAADBAAEBQADBAACAwAEAwAF BAACAwADBAACAwADBAADBAADBAAEBAAEBAACBAABBAADBAADBAACBAACBAABBAADBgAEBAADAwAE BAAEBAADBAADBAADBQACBAABAwADBAADBQADBQACBAADBQABBAADBgACBgABBQAABAABBQABBAAC BAACAwADBAADBAADBAACBAACBAAABAAABAADBAADBAABAwACBAADBAADBAAHBQAFBAAEBAAEBAAH BQAHBQADBAADAwAEBAADAwACAgADAwADAwADAwADBAAEBQAFBQAEBAACBAABAwADAwADBAACBAAC BAACBAADBgADBAACAwACAwACAwAFBQAEBAADBAAEBQAEBAAEBAADBAADBAAEBAAFBQABBwACBwAD BgADBgADBgADBgAEBwADBgADBAADBAAEBAAEBAADAgADAgABAwABAwADAwAFBQADAwADAwADBAAE BQAEBgAEBQAGBgAEBAAHBAAHBAAEBgAGBwAEBgADBAADAwAFBQAFBwAEBQABBAABBAAFBQAEBAAE AwAHBQADBAADBAAFBQAEBAAGBAAEAwAFBAAFBAACAgACAgAEAwAEAwADBAADBAADBAACAwABAQAC AwAAAwABAwACAwADBAAEBwADBgAEBQADBAADBAADBAABBQABBQABBAABBQACBAABBAADBQACBAAC BAACBAABBAABBAABBQABBAABBAABBAADBAADBAADBAADBAAEBAADAwADBAADBAABBQABBQABBAAA AwABAgADAwADBAADBQEDBAADBAAEBQAEBQADBAACBAACBAADBgAGBAAFAwADBAAEBgAEBwADBQAE BQAEBQADBQACBAADAwADBAAFBwADBAAEBgADBAADBAAEBgACBgACBgABBwABBwABBQADBwACBAAC BAAEBgADBAAEBAAFBQAEAwAHBgAEBgAEBQACBgABBAAEBAAGBgAJBgAKBwAGBwAFBwAGBwAEBQAD BQADBAAEBgAEBQABBQACBgADBgADBgADBAADBAADAwADAgADAwAEBAADBgAEBwAEBQAEBQADBAAE BQADBAADBAABBQACBgADBgADBgADBAADBAAEBAAGBgAEBQAEBQADBAADBAAEBAAEBAAEBQAFBwAH BQAGBAAEBQADBAAEBwACBAABBAACBQABBAABBAADAwAEBQADBAADBAADBAADBAABBAACBgADBAAE BQACBAACBAAEBQAEBgAHBgEEAwAFAwAFAwAFBAAEAwADBAABAgADAwAFBgIGBgAFBQAGBgAEBAAE BQAEBQAEBgAEBgAEBQAEBQAEBAAEAwADBAADAwAEBQAEBQAEBQAEBQAEBgAEBQAHBwAHBwAHBQAG BAAGBAAHBQAHBAAHBAAEBgAEBQADCAAECgEHCgAFCAAGBgAFBQAEBgAFBwAEBwAEBwAFBQAEBAAD BAACAwABAwABAwADBAAEBgAFBQAHBwAKCQAIBwAIBwAIBwAEBQAFBwAKCQEIBwAIBwAIBwAHCAAH CAAEBgAFBwAKBwAKBwAEBwAEBwAHBwAHBwAGBwAHBwAFBwAEBQAGBAAHBgALBQAKBAAKBQAKBAAD AwACAgADAwABAQABAwABAwABBAABBAABBAABBAABAwACBAACAwADBAAAAwAAAwABBAABBAACBAAC BAABBAACBAADBQADBQAABAABBAABBAAABAABAwABAwABAgABAgABBAACBAABAwABAwABBAABBAAA BAAABAADBQADBQABBAAABAABBAABBAABAwABBAABBAABBAADBAADBAADBAADBAACAwADAwAAAwAA AwABAwADBAAEBAADAwADBAAEBQAGBgADAwADBgADBgADBQADBgACBAADBQADBgADBgADBwABBQAC BAAEBwADBwACBgADBgADBgABBAABBQABBQABBAADBAADBAADAwAFBQADAwAEBAADBgADBgADBAAE BQAFBwAEBgALBwANCQAHCAAEBgADBwAECAADBAADBAAEBwADBQABBgABBgADBQADBgADBgADBQAD BAADBAAEBAAEBAADBQACBAAEBAAEBAAEBwADBgADBQADBQADBgAEBwABBQABBQADBAADBAACBgAC BgAABQABBgADBAAEBgAFBwAGBwAEBwADBgACBAADBgADBAAEBAADBgADBQACAwADBAADBAADBAAC BAADBQABBQABBQACBgABBQADBAAFBwAEBwADBQAAAwABBAACBgADBwADBQEEBgEFAwAGBAAEBQAE BAACAwACAwADBAAEBQEFBwAEBQADBgACBAADBAAFBwACBAADBgAGBAAHBQAGBgAFBQADBAADBAAF BgEEBAAEBQAFBwAEBwADBgAHCQAGCAAFBQAGBgAHBQAHBQAHBAAHBAAEBQAEBQAEBQAGBwEGCQAG CQAGBwMEBQEDBgAEBwAGBwAEBgAEBAAEBAACBwAABAACBAACBAABBAADBwEFCAAFCAAEBwADBgAH BgAIBwAGBwAHCAEHBwEEBAAJBgAKBwEJBgAIBQAFBwAHCAEGBwEGBwEFBQAGBgAGBwAGBwAEBgAF BwAICQAHCAAGBgAJCQAKBwEIBQAHAwAIBAAGAwAFAwAEBAAEBAACBAACBAADBQABBAAABAAABAAA AwAABAADBAADBAACAwACAwABAwADBAAEBAAEBAAFBAAEAwACBAABBAACAwADBAAEAwADAQABAwAB AwABAgABAgABBAACBAADBAADBAADBAACAwABBAABBAAEBQADBAACBAABBAACAwACAwADBAACAwAA BAAABAABBAABBAABBAABBAABBAABBAADBQACBAAAAwABBAADBQACBAABBQACBgADBQABBAADBAAD BAADBAAEBQADAwABAgAEBQAEBgADBgABBAABBQACBgADBQADBgADBAADBAAEBQADBAADBwABBAAA BAABBAAGBAAGBAACBAADBQAEBAAEBAAEBQADBAAGBgAEBAAHBwAHBwAGBwAEBQAEBwAEBwABBAAD BQADBgADBQACBAACBAAABAABBQADBgADBQABBAADBQAEBAADAwADAwADAwADBAADBAABBAADBQAB BQACBgABBQACBgADBQACBAABAwABBAAABAAABAADBQACBAAFBAAGBAABBQADBwABBQABBAABAwAC BAACBAADBAABBwABBgADBQADBgADAwADAwADBAAEBQADBAADBAADBAADBAAFBQAFBQADBQADBgAD BgACBAADBQACBAACAwAEBQAEBAAEBAADBAADBAACBAADBQADAwACAwACBAACBAADBAABAwAEAwAG BAADBAADBAAEBAAEBAADBgADBQADBAADBAABBAAABAABBAABBAACBAADBgADBQADBgAEBQAHCAAI BQAHBAAFBQADAwAFBAAFBAADAwAEBQAGBwAEBQACBQACBQABBAADBwAHBwAFBQACAwAEBQADBwAB BQACBAADBgADBAAEBQAFCAAEBwAHBgAHBgAEBgAFBwAEBgAEBgAFBwAEBgAEBgAFBwADBgADBQAE BgAHCAAEBQAEBAAEBQAEBQAFBwAFBwAEBQADBAAHBgAHBQAFBQAGBgAEBgADBAADAwADAwACAwAB AwADAwAEBAABBgAABQABBAAABAABBAABBAADBAADBAABBQABBAADBQACBAAAAwABBAABBAAAAwAD BAAEBQADBgADBQACBAADBQAFBAAFBAACBgABBAABBAACBAAABAAABAABAwACBAADBAADBAADBAAD BAACBAAAAwABAwABAwADBAADBAADBAADBAABBAABBAAABAAABAABBQABBQABBAABBAABBAABBAAA AwAAAwACBgACBgABBQABBQADBAACAwACBAACBAADBAADBAABAwABAwAEBQAFBwADBgADBQAECAAE CAAEBwADBgACAwACAwABBgABBgACBgABBQAABQABBgADBgACBAADBQAEBwAEBQADBAADBAAEBQAE BAAEBAAEBwADBgAEBQAEBgAEBwAEBwAABAADBwAFBQAFBQADBQABBAABBQABBQACBgABBQACBAAC BAAEBAADAwABAwACBAADAwAEBAACBAACBAABBQABBAACBAADBQADBgACBAACBAADBgABBwAABQAD BQACBAAEBAAEBAADBAADBAAABAAABAADBQADBQABBQABBQAABAAABQABBQACBgADBQADBgADBAAE BQAFBwAEBgAFBQAEBAACBgABBQABBAAEBwAEBwADBQAEAwAEAwACAwACAwADBAADBAADBAADBAAB BQABBQABBAABBAABBAACBAABBQABBAADBAACAwAEBgAEBQADBAAEBQADBQADBQADBAADBAAAAwAB BAACBQABBAADBQADBQADBgAEBwABBAABBAADBAAEBAAEBAAEBAAFBAAFBAADAwADAwADBAABAwAA AwAAAwABBAACBgAEBQAEBQADBAADBAAABQABBgABBgABBwADCAAFCgAFBwAGBwACBgADBwAFCQAF CQAFBQAEBAAIBwAHBQAHBwAHBwAEBAAFBQAEBAAEBAAFBwAEBgAEBQADBAADBgADBgACBAABBAAE BQAEBgAFBwAFBwAEBAAFBQACAwADBAACAwABAwABAwADBAAABQAABAACBAABBAAABAAABAAABAAA BAAABAAABAABBwABBgABBAACBAABAwABAwADAwAEBAAEBQADBAABBAABBAADAwADAwACBAACBAAA BAAABQAABAABBgABAwABAwADAwADAwACAwADBAADBQABAwAAAwABBAABBAABBAACBAACBAABBAAB BAAABAABBQADBQACBAABBQABBQAAAwAABAABAwABAwACBQABBAABBQADBwADBAACAwACBAADBQAB BAAAAwAABQABBgADBQADBQADBAADBAAFCAADBgAEBwAEBwABBQACBgABBgACBwADBgADBQAEBQAE BgADBQADBQACBQADBgADBgADBgADBQAEBwADBgADBgAEBwADBQAFBQAGBgAEBQAEBQACBgACBgAG BAAGBAADBQEDBAADBQADBQACBgACBgABBgABBwACBgABBQABBAABBQADBQADBgABBAABBQABBgAA BAADBQADBQADBQADBQABBAABBAABBgABBgADBgADBQAFBAAHBgADAwAEBAACBAAEBwAEBgAEBQAB BQABBQADBwACBgADBAAEBgADBwABBQABBQACBgAEBgAFBwAFBwAEBgABBwABBgABBAACBQEEBwAD BgAFBAAEAwAEBAAEBAAEBwADBgADBAADBQACBgABBQABBAABBAAAAwABBAADBwACBgAEBQAEBgAE BgADBAADBAADBAAEBQAEBQACBgACBgAEBwADBQADBAADBAAEBQAEBQAEBgAEBgADBwADBwADBwAD BwAFBwAGBwAEBwIDBQEDAwAEBAEEBQAEBQAEBgADBAAFBQAHBwAEBAEDBAAFBQAFBQAEBgAEBgAD BgAEBwAHCAAHCAAEBwAFCAAEBwAFCAAGCAAGCAAFBwAFBwAHBwAFBQAHBwAHBwAHBwAHBwAGBgAG BgAJBwAIBwAEBwAEBwAFBwAEBgAEBQAEBQAEBwAFCAAHCgAGCgAFBwAEBgAFBwACAwABAwADBAAD AwADAwAAAwAAAwACBAACBAAABAAAAwACBAACBAAABAAAAwAABAAABAAAAwABBQACBAABAwACAQAD AgABAwABAwAABAAABAADBAACAwAABAAABAAABAAABQABBQABBAABBAAABAABBAABBAABBAABAwAB BAABAwAAAwAABAAAAwAAAwABBAACBAADBQACBAACBAACBAADBAABAwADBAAEBAADBAAEBQADAwAD AgADBAAFBwABBAABBQABBAAAAwAABAAABAAABwAABgAABQAABwABBAABBQADBQACBAAEBQAFBwAE BwADBgACBgADBwACBgADBwAEBgADBAAEBwAEBwAEBAAEBAADAwEEBQMDBQEDBAABBAADBwADBwAC BgACBgADBwAGBwAEBQAEBAAGBgACBQABBAADBAAEBgABBAABBAACBQECBQEDBQACBAABBAABBQAB BQABBQABBQABBQADBQADBQAABQABBgACBgABBAADBQADBQADBQADBgACBgABBQABBwAABQABBAAB BAADBQADBgAHBgAHBgABBAADBwADBAADAwADBAACBAAEBQAEBQADBAADBAAEBgADBAADBAADBAAD BQADBQACBgABBAABBAABBAADBAACBAADAwADAwACBAACBAACAwACAwADBAADBAADBQAEBgACBQAB BAACBgABBAAABAABBAADBQADBAADBAAFBwADBgADBQADBAADBQADBAADBAAEBQAEBQADBgADBQAD BAADBAAGBwEEBQAEBAAEBAAFBQAEBAAEBQAEBQAEBgADBQADBQECBAADBQADBQAFBwAEBgAEBQAF BgEEBgAEBgAEBQADBAAEBQAGBwAEBgAEBQADBwAECAAFBgEEBQAEBwEGCAIFBwAFBwAKBwALCAAH CAAGBwAHCAEGBwAEBQAFBwAGBwAFBwAFCAAFCAAFBwAGBwAEBwAEBwAFBwAFBwAEBgAEBQAFCAAF CAAHCQEHCQEGBwAEBQAEBQABAwACAgACAgABAwACBAAAAwAABAADBAADBAACBAACBAAEBAAEBAAB BAABBAADBAADBAABBAABBQADBAABAwADBAAEBQABBQAABAAABAAABAADBQACBAAAAwABBAADBQAD BQADBAADBAABBgABBwAAAwAAAwABBAAABAADBQABBAAAAwAAAwAAAwAAAwABBAAAAwADBAADBAAD BAADBAACBAACBAAEBAAEBAADBAADBAAEBAADAwAHBgAHBQACBAACBAADBgEAAwAAAwABBAEBBgAA BAAABAAABAABBAAABAADBgADBgAEBAAFBgECBgABBAABBAABBQABBQADBwADBQABBAABBAABBQAE BAAFBQADBQADBgAEBQADAwADBQADBgAEBwACBAADBAADBQAFBwADBAAHBQAGBAABBAABBAACBAAB AwABBAABBAABBQABBQABBAACBgAABAABBAADBQACBAABAwACBAADBQACBAABBAABBAADBQACBAAD BQADBQACAwADBAADBgACBAABBQABBAABBAABBAACBgADBwAIBQAIBQABBAACBQECBAACBAADAwAD AwAEBgAEBQABBQACBgADBQABBAADBAAEBgADBQACBAADBgADBQACBQECBQEDBQECBAACAwADBAAE BQADAwABBAABBAABBAACBQADBAAEBAEEAwAEAwADBAADBAABBAACBAACBAADBQADBQABBAACBAAC BAACBAABAwABBAADBQAEBAAEBAAEBwADBgAEBAADBAAEBAAEBAAEAwAEAwAFBAAFBAAGBAAHBQAE BQADAwADBQEDBQEEBwAEBwAEBgAFBwAFBgEEBAAEBwADBgAFBQAEBAAEBgAEBgAEBwAEBwAEBwAF CAAFBgIFBgIDBQEEBgEFBQAHBwAGBgAGBgAEBQAFBwAGBgAGBgAEBgAEBgAEBwAFCAADBwEDBgAE BwAEBwAHCAEEBgACBAAEBwEEBQAEBQAEBwAEBwAFBQAFBQAEBQADBAAEAwADAwABAQABAQACAgAD AwADBQADBQAEBQADBAADBQAEBwAEBQADBAACBAADBQAEBQAEBQAFBwAGCAADBAADBAAHBQAIBwAH CgAEBwADBAAEBQADBgADBQAEBQAEBgAEBwACBAADBAADBAACBgABBAACBAACBAABAgACAwAHBAAH BAADAwADAwAEAwAEAwAEBAAEBAAEBAAEBAAEBAAEAwABAwADBAAEBgADBAADBAAEBQAEBAADAwAE BAAEBAADBQADBQADBwAABAABBAACBQAEBQAEBQACBAABBAADBAAEBQAGBAAHBQAEBgAGBwADBgAE BwAEBwAEBwADBgACBAADBAAEBQADBAADBQAEBAAGBgADBwAEBwAHCAAEBQABBQACBgADBQAEBwAD BAADBAADBgADBgAEBAAEBAABBAADBgEDBQABBAAEBAAEBAADBAAEBQABBQABBAAABQAABQADBAAD BAAEBAAEBAADBAADBAAAAwABBAAEBAACAwADBAADBAAFBAAFBAADBgADBQADBAADBAADBQACBAAD BAAFBwAJBgAHBAABBAADBQACBAACBAADAwADAwAEBQADBAAEBQAEBgAEBAADBAADAgADAwAEBQAE BQAEBQAEBQADBwACBgADBQEDBQEEBAADBAAEBQADBAADBAAEBQADBQADBgAEBAEDAwADBAADBAAG AwAGAwACBAACBAABBQABBQABBQABBAAEBAADBAABBAAAAwACBAACBAAEBAAFBQAFBQAEBAAEBQAD BAAEBAAGBgAEBAAEBAAEBgAEBgAGBgAGBgAEBgAEBgAFBgEEBAAEBQAEBgAEBwEDBAAGBAAHBQAH BAAHBAAEAwAGBAEGBgAGBgADBQAEBwALBwAKBQAEBgAEBwEFBgEDBAAFBwAFBwAFBQAEBAACBgAE CQAGBwAEBQAFBQAEBAAFBwAHCAAGBwAFBwAEBwADBgAEBAAEBAACAwADBAAHBQAHBQAGBQEFBAAE BgADBAADBAADBAAEAgAFAwABAQABAQADAwADAwAHCAAEBQAHCQEGBwADBAAEBQAFBQAFBQAMCAAK BwAICgAGCAAKBQAQCgAHCwAHCwAKBwANCgANCgAKCAAMCAAKBwACBwADCQAFBwAJCgIKCQAJCAAI CQAGBwADBQADBQAEBAAGBgAGAwAHBAAUBQAWBwAHBwAGBgACBAADBQAFBQAGBgAEBAAFBQAFBAAE AwADBAACBAADBgADBQAEBQAEBgAEBQAEBAAFAwAJBwIHBgAKCQAKCAAKBwAGBwAKCwEMCQEJBgAD BgAEBwAEBAAEBAAGBAAHBQAEBQAFBwAHBwEFBQAHBgAKCAEHBwAHBwAFCQAKDgAFBwAHCAAEBgAE BgAEBwAFCAAJBgAOCgIICgAHCQANDQALCwAGCAAJCwAJBwAJBwANCQAMCAAJCAAKCgAJCgEGBwAJ BgAKBwAEBwADBgACBgACBgAEBQAICQMDBAADBAAFCgEHCwIHCQEDBAAABAACBwADBgADBgADBAAD BAAEBwADBgADBAAEBgACBgABBQACBQACBQAFBQAICAETCwENBgAHBAAJBgAHCAADBAAFBAAHBQAL BgMLBgMKCAEIBwAEBQADBAAEAQAHBAAQCAEQCAEOCQMHAwAHCAAHCAAHBgAHBgAFBQAGBgAHCAEH CAEQCAEQCAEOCQELBwAHBQEHBQEEBgAEBgAFAwAKBwMFCAAFCAAGCAAGCAADBQADBQAFBAADAwAD BAAEBQADBwADBwAHBwAHBwAGBQEEAwAHBwAICAANBgAPBwAPBwARCgAKDwAKDwADCAABBwAHCQAF BwAPBwAPBwAJBwAJBwADBAADBAADBwADBwALBwAKBgAFAwAHBQEHBwAEBAAFBwAFBwAWDwAUDQAK CgAKCgAHBgAKCAEKCAAJBwAKCAAJBwAOBwAUDQQMDAMICAAGBwAFBwAKCgAKCgAQBQAWCgMSCgMP BwAPCgEMCAAJCAAPDgELBwAJBAAFBAAJBwANDwAODwAHCgAHCgAEBAADAwABBAABAwABAwABAwAs HgEnGQAVFAQMCwAHBQAJBwARDAAVDwAqHAAqHAAiHQAdGAAcCwAlEwEiHAMYEwAaEQAaEQAgEQEg EQEgFgAiFwEaFQIXEgAWDAAeEwEqGAAsGgElFgEeDwAUCAARBgAPBwATCgAgEQAiEwEiGQEgFwAO EAIHCQABBAAAAwABBQABBAADBAAEBgAEBQADBAADBAACBAADBQADBQACAwADBAADBAADBAAMAgAV CQEZCwAoGAYqHwYhFgAoGAQlFgMYEgQQCgAMDQAODwEHBwEFBQAEBwAGCAAJCgAHCAAVCgAWCwAj EQMkEgMhFgAjGAEZGAAVFAAEBgAFBwAECAABBQAKBwAQDgEYEgAdFgEeGgEeGgEhFgAjGAEdEgAa DwAjDgAvGAQyGgA1HQIvGgQsFwMWEgAPDAALBAAUDAEFCgEBBQAEBQALDQMJDQYFCQMICQAODwMT EwIVFQMfFQMdEwIJCgQHCAMDBAAEBQADBAADBAADBgAEBwAEBAAFBgEDBQADBAADCAECBwADCQAM FAMdFAAnHQciFwUiFwUeDQIeDQIYDQAbDwEWEQIWEQIcEgQcEgQRDQEMCAAOCgAcGAYoFwcmFgUh FAYbDwILEQEJDwAKBwAKBwAFCAAHCgAPDgIPDgIXEAIfFwctHA4mFgghFQcYDQERCgARCgAgEAEm FgUpFgYnFAQUEgERDwADBwADBwADBQADBAADBwACBgACBgAECAAEBwADBgAEBAEDBAAJBQAVEAQi FgApHQQsHAAvHwIcGQEUEQANDwMOEAMaFgAkIAQvHgUxIAcpGw8aDgQICgEKCwMNDAATEgMhGwgf GQcKDgEECAAUCAAZDQAtHAAwHwIyIgQyIgQuHgklFgMPCwAXFAMXEAEUDQAUEQAYFgEyIQcrGgMa EgUTCwAPCAASCgAoFgcnFQcQCQAZEQQlDgEwFwk5JAc4IwYyIwgpGgIgEAEmFgUxGwE3IAQ4JgU1 JAQlGAQgFAEICQAEBQADBAADBAABAgACAwA3JwA3JwAnIwQdGQAPEQAQEwAhEAAuHAQ5JAA+KQE4 KwAwJAArIQAvJQEsJQArJAAtIwArIQAtIgEpHgArJAArJAAzHwMzHwMwIQA0JQE0JwEzJgE1IgA1 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 Kgc1IQQtGQAmFwEvIAczJQQ5Kgc7KxAwIQgsFQQsFQQvIAQ1Jgk0Iwo4Jg0rHQofEgIKCwMFBwAD BgAEBwAEBgAEBgADBwADBwAGBAAKCAAHBwALCwIWDgIdFQcqHgctIQk6JQU7JgY1HAQxGAErGgkf DwEUDwAWEQAOEQAOEQAVFAAXFgEfFAIfFAIhDwAjEQANDgEODwEaEwMdFgQkFgAhEwAoFgEuHAUo GgAsHgEkGQAiFwAhFgIfFAEcGggWFQQTBwAWCgEbDwEZDgAbFAIbFAITEQEQDwAOEQALDwAQDwAP DgAKDAIQEgcbFwcWEgMVDQITCwEXDAAcEAEiEwEpGQUqGwQiFAAeEwcWCwEHBwAJCQAMCwAIBwAE BAACAgACAwADBAASFAETFQIKDAMHCQEKCgYICQQIDQMLEAYMEQAMEQAPDgATEQEQDgEUEQMYGQkR EgMdFgMdFgMNEAATFgMVHwMVHwMZFgEZFgEWFgQTEwIUEwQREAMbGAEbGAEUEgETEQAiEwIiEwIk FAQlFQQdHgYWFgEKDAAICgADCAMDBwICBwABBgAFCAAFCAADCQABBwADBQADBQAFCAAKDQQPDgEU EwQRFAAXGgMaEwEcFQMWFAIUEgEWDwAYEQESEAAWFQMWCgAbDwQhFAMrHQorGgkyIQ8qIg0kHAgd FQoYEAYjFwMrHwkjFwMlGQQmHwYjHAQXFgcPDgEDCwMEDAQIDgAFCgATEAAWFAAdHQAiIgMpGwkr HQoqHhAkGAsVHA4GDAESDwQeGw4ZEwYWEAQMDQEREgUSEAAZFwQhGQkYEQMTCAIcEAkhEAYlFAkQ EQMZGgooHQQpHgUmHQQoHwUfFgoTCwEGBwAGBwAEBwAHCgEEBwMCBQEEBAEHBgMGBwEDBAABBAED BgMGCQEHCgEHCgALDwMUFAMREQEHDQEGDAELDAEZGgwjHAUjHAUqHwQnHAMVFAQREAIREAAZGAMs HQUsHQUoHQMgFgAWEgAWEwEhFgEhFgEkGAQhFgIWEQEXEgEUEgEWFQMeFgQgGAUgEwcbDwQeEQcb DwQiFwEnHAQmEwkkEQcLDgEICgAEBwEDBQADBQAFBwEDCQEDCQEDCAMDCAMHBAAKBwEKCgYKCwcP DAUSDwcVEwETEQAXFQYWEwQTCwITCwIQEwUMDwIKCgAMDQASDwIPDQATCgMWDQULCwIKCgEICgAK CwEJDAMMDwYODwcODwcQDwAODAAWEgAbFgIOEQARFQEREQAREQAUEAkRDgcKCwcJCgUKCgEKCgEK CwMMDgUNDgEPEAMHCwEJDgMJDgQKDwUHDAEKDwMKDwUKDwUPDgEODQARCQQQCAMNCgEPDAMODgQP DwUSDwMNCgANCwQMCgMKCAELCgMGCQAEBwADBAADBAADBAAEBQADCgABBwAEBwAFCAAGBwEHBwIH CQAHCQAFBgAHBwAFCAAICwEGAwAIBQAHBwIDBAAABAADBwADBgAEBwAGDQAFDAAECgAECgAHCAAJ CgAGCQEHCgIKCwEHCAAJCgMKCwMPDAMOCgIODwEODwEFBwAEBgADBAAGBwAECAACBgADBwACBgAF BgMEBAIDBwADBwAEBQAEBQAFCAAFCAAHCQAJCwAODgAODgAKCgAICQAKCAALCQAKCwAICQAICQAO DwMLCAANCgEPCwMPCwMQDwAWFQMJDwUECgEEBwUMDw0YCwUXCgQQEQMPEAIKDAAPEQQIBwQFBAEA BwMDCgUDCAAFCwAKDAAKDAAJDQAJDQALCwIKCgEKDgcEBwECCAIBBwEQCgITDAQHCgQICgUODAQP DQQNDQMODgQICwMNEAcHCgIICwMMDAMMDAMKBwAPCwMUEgIODAAPEQIMDwAFDwMABwADCAADCAAB BwADCQADCAMBBgEEBQEFBgIDBwIDBwIFCQMFCQMICAAHBwAEDAIFDQMKDQEICgAGDAEFCwAJCQIR EQkUEQUUEQUWEAEUDwANEAQICwEJDAEHCgALCAEKBwAFBwAKDAANCgAPDQANDgELDAAIDAAIDAAH CgAGCQAECgEFCwIIDAAHCwAHCgAJDAEHCAAKDAIUEwAYFwIMEAEJDQAIDwEGDQADCQABBwADBQAE BwEDBwIDBgEBAwAEBgMECAICBQAHCQEHCQEICAEJCQIKCgAKCgAJCAQIBwQJBgAJBgAKCgANDAAN CQAOCgAQDgEKBwAPCQEQCgIDBQAEBwEECQEHCwMKDQEJCwADBwEDBwEDBwADBwALCwAPDwAFCAAE BwAHCAAKCwEJCQEFBQADBQAEBwAGBwAHCAAJBgAKBwEJCgAJCgAHCAAHCQEEBwMFCQQMDAQKCgMG CgIKDwURDQENCQAKCgEHBwAFBgEGBwEMCQIOCgMHCgEGCQAHCQEICgEHCAAGBwAHBAAGAwACBAAB BAADAwADAwAAAwAAAQAAAgAAAQAABAAABAABBAACBAABAgABAQABAgABAgAAAQABAwABAwABAwAD AwADAwAAAwAAAwAAAQAAAQAABAAABAADBAADBAABAwACBAABAgACAwABBwABBwAEBwEHCQMFDgAD CwAEBwADBQABBwACBwADBwADBwABBwABBgACBQADBgACBQACBQACBgADBwAFDAABBwAEBwAFCQAJ CgAJCgAFCgAGCwAHCAAHCAANDgIHCAAHCAAKCwAHCgAICgAHCgAFCAAHCAAJCgABBwEBBwEABAAB BwEGBgAGBgAEBAAGBgAEBwADBgAABQAABQAAAwABBAACAgADAwADBgAEBwAGBAAHBgADBQADBQAD BwADBwACBwABBwAEBAAICAABBAACBgAECQAFCgEGCwAHDAAIDgEIDgEFCAAFCAAHCgIFCAAKCgEH BwAHCwAHCwAEDAEBBwAECwABBwABBAABBQADBAAEBgADBgABBAAHBwAHBwAEBgAHCAEDBQADBQAD CQAGDAEFCwIECgECBwAECgEGCgIHDAMHCgEFCAAGCgAGCgADAwAFBQAEBwADBgADBgACBAADBQAD BQADBgADBQAGBgAGBgACBAAEBgACBgADBwABBQABBAAAAwABBAADCAADCAAEBwAGCQAFCgEECAAH CwAKDgEECgAHDAAGDAIECgACBgABBQADBAAEBgAHCAEFBwAEBQAEBQAEBAAGBgADBgAFCAAHBwAG BwAEBwAEBwAHBgEFBAAHCAAICQAJCAAIBwAECAAGCgALCAAIBQAFBgEHBwIHCAAGBwAEBwADBgAD BQADBQAAAwABBQAABAABBAAFBgAHBwABBAAAAwACAQADAwADBAADBAACBAADBQADBwADBwACBAAD BgAEBwADBgABBQACBgADBQAEBwANBwAMBgAJBwMKCAQJCQAICAALCgEKCAAFCAAEBwAGBwAHCQAE CgAECgAGCAAHCgAGBAAHBQAGAwAGAwADBAACAwACAwACAwAAAAAAAQAAAgAAAgAAAQAAAQAAAQAA AQAEAwADAgAAAQAAAAAAAAABAQAAAAAAAAAAAAAAAAAAAQAAAQAAAAAAAAAAAAAAAQABAwAAAQAA AQAAAQAAAQAAAQAAAgAAAgADAgADAwADBAAEBgECCQAABwADBQACBAACBgADBwADBwACBgAABwAB BwABBwACBwADBgAFCAAEBwEDBwEFBQAHBwAHBwAGBgAHCQAICgACBwAECgEGCgAFCQAFBwAGBwAD BwAGCgAECAABBQABBQABBQAABAAABAAABAAAAwACBAABBAACAwADBAADBQACBAABBAABBQAAAwAA AwABAwADBAABBQABBQADBAAEBQADCAACBwAABQAABQABAwABAwACAwAEBgAABQABBwABBAACBgAD CAADCAAFCQAIDAADCQACBwAECQADBwAECgECBwAEBwADBgADBwEDBgABBgADBwECBAACBAAEBwAE BwABBQABBQAGBgAFBQADBQEEBgEDCAADCAADCAADCQADCQMCCAICBwAECgAFBwAFBwADAwADAgAA AwABBAAABQAABAABBQADBwAABQAABAAABwABCAACCAACCAADBgACBAAFBAAIBwAFCQAEBwABBwAA BQAABAABBwAACQACCwADCgABBwABBgADCAADBgABBAAEBQAEBgABBwACBwABCAABBwADBwEDBgAB BQADBwAFBwAFBwAHBgEHBgEEBwAFCAAFCAAFCAADBgAEBwAEBQEEBAEIBwAJBwAHBgAIBwAHCwAH CgAEBwAEBwAFBwAHCQEEBwACBAAAAwAAAwAAAQAAAQAAAwAAAwABAwADBAAEAwAGBAABBQAABAAC BAADBgACBAADBQACBgABBAAFBwAGCAAEBgAEBgADBgADBQADBwACBgAABAAGCwAHBwAEBAAEAwAH BwIKCQAJBwAHBwAICAEJCgIHCAAECQAGCwAHCgEGCQEHBwEHBwEHBgAHBQAEBAAEBAADBAABAwAF BwAEBwADCgAECgACCAADCQACDwABDgADCgADCgAKBwAJBQAECQAECQAHCQAKDAADCQABBwABBwAB BwADCAADBwADCgABCQABBgACBwACCgAABwABBQAABAABAwABAwAABAABBAABAwACBAADBAADBQAD CAACBwAEBwADBgACBQECBQEDBwAECQADCgABCAADBwAECAAICgEHCQEFBwEGCAIFBwAFBwAHCgAF CAAHCAAHCAADCgAECwEECAAFCgAFCAAEBwAECAAFCgECCQABCAADCQACBwACAwABAgAABQAECgAF CQAHCgABDAAGEgAIEAAFDQAKDwAMEQAIEgAIEgAIEQEDCwAEDgAGEAAFDAAECwAEDQAIEQADDwAA CgAGCgAHCgAEDQAHDwAHEQEEDgADCAADCAADCgABCAADCQAECgAECgEECgEGCgAECQAECgAECgAE CQAFCgEDBwMEBwQEBgEHCQQEBwEDBwEDBwEDBwEDBgACBQAEBwAHCgEECgAECgADCQAECgAECAIE BwEDCAQDCAQGDAEFCwAHCAAEBgAAAQAAAQAABQABBwADDwADDwAHFAAIFgAHFAAIFgAEEgMEEgMD EQAEEwEHEwEGEgAUGAEUGAEOFgELFAAHGQIKHAQCDwAEEgAGFgMEFAEHFAAHFAAEEwAEEgAHDgQD CAAGCAAHCQAECQEDBwADCQEDCgIGCQEGCQEHCgAEBwAEBwAEBwAFBwEFBwEECQIDCAEGCQEEBwAE BwEGCAIEBgEEBgEHBwEICAEKBwAKBwAHCgAHCgAGCQEFCAAEBwAFCAADBAABAwABAwACBAABBgAB BwAHEQAKFQAOEwAQFgENFQMLEwIFEwQGFAQHGAQHGAQKFgQMGQYKFgUMGQcWFwsWFwsNFQMLEwIO GwEOGwEQGgAPGAALFAASGwQIFQQGEgIKDwQLEAQJDgQFCgEHCgEGCQEHCQAHCQAHCQAHCAAJDQAH CgAHBwAICAEGBwAFBwAEBQADBAADAwADAwAEBAAEBAAJGgAMHgIMHwMPIgUKKAcKKQcLIAEPJQQX IQMXIQMLJAUJIQMUIgQOGwAPIQgPIQgEGQgDFwcKGAEHFAAKGwQIGAIEFgAIGgENGwMIFgAKFgMJ FAIKDwAIDQADCgAABwACBAABAwACBAAEBwAEBwAEBwAEBgAGBwAECQICBwADBgAFCAAEBwEEBgAC BgADBwAECAADBwAGBwEFBgEFCAAEBwAFCAAEBwAGBwAEBgAFCAAEBwAFCwAECgAFBwAGBwAECAAE CAABBwAABQABBQABBQAFBAAGBAAHEQANGAUTGQgeJRIEHwwIJBANIQcLHwYPIg8RJBEVIhESHw8N IwoHHAUPIgcKHAMNJQsQKQ8PJw0JIAcEIAgEIAgOIA4PIg8NJgoNJgoLIQoLIQoGEQMEDwIECgAD CQABBwACBwAHCAAHCAAHCgEHCgEGCQEEBwAFCAAFCAAEBwIHCQQDBgAECAIDCQADCAAECQAECAAE BwAEBwAEBwAGCQEECQAECQAFBwAFBwAFCQQDBwIDBwQDBwQHCgEFCAAEBwABAwAAAQAABAAADwAK HQcNLg8KKgsMLBAOLhIOKw0SMBEVMB8PKRgPKxAQLRIQLRIQLRIQLRQQLRQTLBITLBIPJhERKRQM JxQKJRIRLBsRLBsRJQ4RJQ4QKg0LJAgNGQoEDwIHDQEECgAFCAAEBwADBgADBwEFBwAGBwAGBwAE BgADBQADBgAEBwAEBwAEBwAEBwAEBQAFBwAECQAECgAEBAAEBAAHBAEHBAEGBQIIBwQEBQAFBgEE BgADBQAFBwEDBAABAwABAwAABAAECQAKHAQOIQcUKA0WKw8UJw8UJw8YLBgYLBgTNCEPMB0PMhgM LxYSLx0UMR8TKxcZMh4fLCAXJBgTLBIVLhQWLxUZMxggMAwcLAkZKBIcKxUaJRAMFgQPGQQMFgIE DAECCQADBwAECAAHBwAHBwAGBwAGBwAGBwAHCAAFBwAHCAAEBwACBAAGBAAFBAAEAwAEAwAEBAAE BAAHJQoOLhENMxoUPCIQOB4QOB4QNBwUOSAYNSAWMx4UNRoUNRoWOBwUNRoRNxkUOhwKLSQGKB8K JxYPLRsVMhYQLRIUJw8WKhITLQ8VLxEQKQ8LIwoKHAQHGQIEDwMACgABAwAAAQAAAQAAAwACBgAD BwAEBQAGBwADBwACBgADBAAEBQADBQADBQACBgAECAAEBQAEBQAEBwAEBwADBwEBBAAEBQAEBgAF BwAEBgADBgAEBwAGBwAFBwADBgAEBwADBwADBwABBAABBAAABAAAAwAECgAJDwEOJRATKxYZNCkb NysMLSEUNyoPNB8NMh0PNS8NMy0PNB8QNyEONRwMMxoWNyAWNyAKMyIPOigLMiQQOSoLOyYINyIM Mh0PNyEQNBwPMhoLLR0KKxsMGw0IFgkDCQEABgABAwACBAAIBwAHBQAEBwAEBwACBgACBgAHBwAF BQAFBwAFBwAEBwAEBwADBwADBwAEBwADBgAEBQAEBgAEBgAFBwAGBwAFBwAEBQAEBQAEBwAEBwAE BQEEBQEGBwABAwAAAAAAAQAABgAEDwMDIQgLLRIQOyAPOR4KOx4KOx4OOigOOigOOC4MNSwPOCkO NScOOScLNSQPNB8PNB8SMiUTMyYPNCcLLyIPNCcPNCcRNykRNykPNCURNycQNBwKLRYRHA0KFQcI DwMDCAAABAAABAAEBAAEBAAEBAAEBAAEBAAFBQAHBwAEBAAEBgAEBgAEBwADBgADBQAEBwAGBwAF BwADBAADBAAFBQAEBAAHBQAHBgAHBQAHBQADBAAEBQABAwACAwAABAAABAAACgAJFwkOLh8OLh8T OSsWPS8SOCoPNCcfOSofOSoVNSgXOSsPNSAQNyEWNCwaOjEZNCkXMicVNSgUNCcSMhwUNB4YNSEa OCMhNRsdMRcVMB8XMyIdMhYMHwYPFgkKEAQECgACBwAEBwAEBwAFCAAEBwAFBwAHCQAJCQEHBwAG BwAGBwAEBQADBAAEBQADBAACAQACAQADAwADAwASNx8ZPycdPSchQSsXPCkXPCkWPjMTOzARPiwN OScOOyUOOyUPOiAWQScWPiUROSANOCwOOS0MOCYPOykQOSgNNCQONRwONRwNNRcLMxYPNB8NMh0Q MyMHKBgLHQ4CEQQECQABBAAAAwAAAgACBAAEBwAEBwAFCAACBwADCAADBgAEBwACBgADBwADCAAD CAAFCAAEBwADCAADCQADBgAEBwAEBgAEBgAECAADBwAFCQAGCgAECAADBwABBwADCAAEBwAEBwAA BAAAAwAABQAABgABFgAMJQsNOSENOSEPPzEOPjAMQzQKQDIWPzAVPi8MPzsLPjoTQSsRPykVRC0O OyUQPS0TQDAOPjAPPzENOCwRPTEOPzEOPzEMOygOPSoPOCsLMiYPOCMPOCMSJxoFFwwHEQcBCgIC BAACBAAEBAAFBAAECAACBgAEBAAICAEEBgAFBwAFBwAEBgAEBgAEBwECBgADBwAEBQAEBgAEBwAE BwAHCAEHCAEECAACBgAEBgAFBwADCQACBwADBgAFCAADBQAAAwAAAQACAwAAEQQLIRIIOR8LPSMO RDANQy8PQywOQCoKPS4KPi8KOC0PPjMWOjgUODUVPTQTOzIQOicSPCkVOy0WPS8TPzMOOS0PPi8M OywMOi8LOS4QOSoQOSoPMSEKLBwIIRMDGg0EEwEBDwABBwABBwAEBQAEBQAEBwAFCAAGBwAFBwAG BgAGBgAGBwAGBwAFBwAGBwADBwAECAAHCgEFCAAHBwAEBAAGBwAEBgAGBgAGBgAEBAAFBgEFBgEE BQABBAAAAwAABwAFDQEFGgwOJRYPLCYaOTIWPjUWPjUOPjANPS8YQzUYQzUaPDAcPjIWPiwWPSsW PDQVOzMSOCoQNSgTOSkTOSkUNygUNygPOh8POh8bOCgZNSYZNCsXMikTLQ4NJggRFgwLEAcHDgUD CQEHCgAGCQAGCgAHCgAHCgAJCwAJCgEJCgEKCgEHBwAGBgAHBwAHBwAEBAADAQACAAADAgAEAwAR RSwPQSkXQSUbRikWRi8QPigWRDEUQS8NPysNPysNPSYPQCkUQS8SPy0ROikPOCcOOioPOysPPCYP PCYNPC0LOisOOyUNOiQNOxoNOxoUOycQNyMTNyYKLBwKKA8AGQMCDAAACQAABQAABQACBAADBgAD BwAECQADCgADCgABBwADCAADBgADBgAECgADCQAEBwEEBwEFCAAGCQADBwAECAAECAAECAAFCgAE CAAFCAAGCQAHCAEGBwAECAAECAADBwECBQABBAABBQAACgABDgACIQoPMhgLOicOPSoPQS8PQC4O PzMPQDQQPDASPjIPQDQOPzMOPysPQS0PQS0NPioSPy0UQS8PRzcORTQOQC4OQC4PQDIPQTMNOCgO OSkVPTQQOC8PPCoPPCoSNSUFJRYLHQ4ACwAABAAABAABBAADBwEFDwMEDQIGCQEKDQQECgADCQAE BwAEBwAFBwEEBwEEBwAEBwAFCAAFCAAEBgEEBwIHBwIGBwEECAADBwADBwEEBwEECgQBBgEECQAE CAABBQABBAAABAAABgABGAwMKBoKPCYOQSsNRTILQzAPPzMPPzMOPzMNPjILPDIMPTMLODIOOzUP PzMPPzMVPTISOi8VOzETOS8OPDELOS4PPi0LOikPPzUKOjAKOSgKOSgPNyYLMiIMKhwKJxkMFQcH DwIECQEECQEEBwEDBwEDBgMEBwQDCAQDBwMDBwEEBwEGCgIGCgIEBwAGCQEHCAAGBwAHCAEHCQED BwADBwAHCgEEBwAGBwEHBwIDBgAEBwEDBwMBBAEABQABBgAADAAFFQQHJhYPMB8LNCkWQTUTPzMU QDQMPS8QQzQTQDITQDIVOjIXPTUWPy0XQC4POy0NOSsMNysLNSoYMh8aNCEQMyMSNSUPNx4UPCMQ OSoSOywROScONCMQMhkIKBASHw8VIhELFwoGEQUGDwAHDwAICgAICgAICwEICwEGCgAHCgAHBwAK CgMHCgEEBwAFBwAEBgAEBQADBAADAgAEAwAURTEQQC0RRC8PQS0RRC8QQy4PQSkPQSkOPysLPCgN PSYNPSYOOS0POi4WNykWOCoXOigUNSQLNyENOSMLOicKOSYKOiMLOyQOOSkNOCgRNycPNCUONRwK MRgKLhIDIwkEEgQACgAAAwAAAwACBAADBQADBwAECQAECgAECgACBwADCAACBQABBAADBgEEBwMD CQADCQAGCQAFCAAFBwAGBwAHCAEFBwAHCgEFCAAGBwAHCAEEBwIFBwMECgADCQAECAACBgAABAAB BAAACgAEEwQEKBwPNyoLQC8PRjQUQDcQPDINPS8OPjALQTMNRDUPOysPOysMPS0PQTERPiwQPSsQ PScSPykOQDAOQDAMOywQQDERQS4QQC0UOy0ROCoWPDQWPDQPPi8PPi8ONCwLMSkOJRMBFAQABQAA BQAECgAHDgMGCwQGCwQICAEICAEEBwMEBwMDBQAEBgAGBwMFBgIFBgIFBgIEBgAEBwEGBwEHBwIG BwAGBwAEBwIEBgEDBwEECQIEDAADCgAGCQAEBwAABgAABQAABwABDQEDIRQMLR8NPC0QQDEOQzMO QzMPPzkRQTsOPjQOPjQQQTgOPjQPOjURPDgTPy0NOCYaPDIaPDIiPjIjPzMWPjIWPjIWRTATQCwP QTEMPS0PQS8KOykNOCoNOCoPLyQOLSIPFhMGDAoECwcDCgYDCAADCAADBwADBwADBwMDBwMECQED BwADBwAECQAFCAAFCAAHCQAGCAAHBwAJCQAEBgAEBgAGBwAHCAAHCgAHCgADBwADBwADBwEBBAAA BQABCAABEwcGGg4ILiANNCYPOykWRTIVQDwTPjoNPDUPPjgSOi8WPzQVQDQTPjINPioPQCwNOCYN OCYcPTUgQToxOSYwOCUnNyEnNyEbPiweQS8POykOOScONyQNNSMPNSYKLyAYMhcPJw4LEAYECQAH CgAJCwAKDAAJCwAHCgAHCgAHCAAICgAHCQEGBwACBwABBwACBgACBgADBAADBAAEAwAEAwAVQzIS Py8OPy0OPy0QPzQQPzQNPiwPQC4OPikMPCcVPSQXQCcROiAUPSMcNyIYMh4sOCEpNB4UNB4YOiMR OyYPOCMPNyYQOSgOOSkPOioTPSgTPSgKOSAJOB8NNx0DKREEFQcADgEABAAAAwABBAAAAwACBwAC BwACCAADCQADCAACBwABBgABBQACBgACBgACBwACBwADBwADBwAFBwAFBwAFBQAGBgAEBgAEBgAE BgAFBwAEBgAEBgADCQADCQAEBgADBAAABAABBQABDwAHFwMHMBkQPSUMRC8MRC8TRjUPQDAQQTMS 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 SUwWR0kXSUwWTE4WTE4WTkwWTkwTTkAYVUcjVFcvYmVVbYRyi6N1kJ90jp5ymZlymZmElaGNnqqL m7eElbBahoc7ZGUdSUwSPD4SPTkRPDgKOC0PPTISLhoEHQsQEggICgEJCgMJCgMMCgMMCgMJCgQJ CgQKCQIKCQIKCgMJCQIFBwAFBwAECQAECAAEBAADAwADBAADBAAZTEUaTUYWTk8WTk8STE8TTVAf UE4uYl9Eb3BQfX5pg451kJt9jo1vgH9wcmhub2VyeG1/hnp4jJaClqF6jKVqe5RAam8uVlsVUE8Q SkkPR0cPR0cNQzsMQToWQz8TPjsPLCIBGhECDQAABwABBAACBgAFCAAGCQADBwQECAUJCAMKCQQG CgIFCgEHCQEKCwMKDQQKDQQFDQEFDQEHDgIIDwMHDAMHCwMKCwMKCwMICwEJDAEHDQMHDQMHCgQG CgMDDAMDDAMCCgQDCwYBEw8OIx8KPjMXT0QTW1MRWFASV00VW1AWVEoWVEoWU08WU08YU08bVlMm UUokT0g3TlhAWGNVc4ZffpFdlJ1ckptQh4s8cHQeXloTUEwQUE4MSkgQSUARSkERUEEQT0AQRjsR RzwZOy8EIRYECgAAAgACBAAFBwEFBwMGCAMICQQKCwcJCgcGBwQGCQcHCggHDgQFCwIHCgQHCgQI CQQICQQFCQQIDAcHCQEKDAQKCgMKCgMFBwQGCAQKCgYKCgYLCAkKBwcBBwQCCQUADgcKHhYHNDgT REcSTlAUUFMTV1MQVE8WTFMYT1YWVV8PTVcWUVEeXFxHa3tljJ14iJ5/kKZ4lrR5l7Z3mrR/o750 pr5qm7Nkg4ZHZGcZV0MUUDwRSkERSkEPRUMMQD4POi4IMSYMGQYCDQABBAAEBwMGCgIECQEFBwQG CAQGCwUGCwUECQEECQEKCwMMDgQPDQcOCwYKCgILCwMECgEECgEHCQMGCAIECQEFCgEECAQDBwME BAEGBwMHCAcFBwYBCQMHDwgDFxMOJiEOO0EUQ0kWRUwYSE8bTU8cTlAVSUoWSkwXSEgfUVEqUVoz XGRNa4NaeZFigJZlhJpyi5lzjJp3iKN1h6JzgJpkcos4X1QmTEAPPzoQQTwSSUQMQTwSPTMPOjAW LBoFGAkFCQMFCQMIBwAKCAENCgMMCQIIBwQIBwQKCQEJBwAFBwAHCAEHCAAEBgAGCAAFBwADAwAE BAAGBAAGBAAcTUgeT0oWU0wWU0wZU0oYUUkqVk80YltJdW9Yhn9pi4xylJWClp6AlZ2MlZmOl5uO m6iUoa6MmreHlbJ6jKVoeZEzY14lU04UTEQSSUENREMQSEcRSEAORDwOPjgKOTIKJBgAFAoABwAA BQABBAADBgAICgAHCQAICgcHCgYHBwQICQYJCwYHCgQIDQMIDQMECQIECQIHDQAJDwEECwEGDgMJ DAMHCgIIDwQHDgMKCgQKCwUKDgcIDAUHCgcIDAgGEAoDDAYFCgMFCgMDDhIPHCEPODoaRkgRVVEU WFUTVU8XW1UVVlAXWlQUVVMSU1AWUVUYVFcZVlMZVlMpUVMvWFo5aHJMfYdYiZlQgJBTe4s6YW8e XFwWU1MPTVMSUVcQTUgPSkYRT0QTUUYXSkASRDoTJyAFFhAEBgABAgAEBQAHBwIGCAMHCQQHCQEJ CgMKDAcGCAMHCgYKDQkHCgQGCgMICgQICgQJCgUJCgUKCgYICQQHCQUICgcICgIJCgMHCAMICQMJ CgMJCgMQDwcKCQIBCAADCgAADQgDEQwDKisTQEETU1QTU1QRWFcQV1YWUVcYVFoYV2cUUWEXVFMb WFc4Z3VNfo1jg5BlhpJke5luhqNni51kiJpje45RaXsrYV0fU08RSkQRSkQSSkkPR0YSSEgPRUUS PTUHLicKEQgBBwAEBQAEBgAECQAGCgEECAMEBwMHCgEHCgEFCAAGCQEICAEKCgIKCgMJCQIHCAAG BwAEBgAFBwAHCAAHCAAECgAECgAFBwEEBwEEAwAFBAAGBwEFBgEACAACCwMAFhkKJCgKNUUSP08U Q1AWRlQZT1AaUFEUTUkXUU4UTUkYU08uVlsxWl45WG4/X3VGXHVHXXdUZHRXaHhNYntFWnM4UU0v SEQWRzwWRjsPRDkOQzgPRzsPRzsZPzgPMisOIAkCEQAHCAAEBgAHCAAHCQAOCgQMCQMIBwMIBwMK CQILCgMHCgIGCQEJBwAJBwAHBwAEBAADAwADAwAEBAAFBQASTU0WUVEWWEwTVUgXUU4XUU4lU1Ev Xl1AYmdNb3RWgIlbho5ihJtniaF5kqF9lqV/lax9kqp6jJ5qe41OdXQ/ZWQbVUMVTTsQSDwMQzcR R0cRR0cVSUgSRkULPDIHNy0KHxYADgYABAAAAwACAwADAwAGBgAHBwAFBwAFBwAHBwMHCAQFBgEE BQAHCgEHCgEDCgEDCgAGCQAHCgEGCgIHCwMGCgMFCQMFCgEECQEFCAAICwIIDQMHDAMICgUJCwYE EAEBDAABCQABCgAACwQGFQwHLicRPDQPUVEYXV0aWlgeXl0TV1QWW1cTVlYSVVUVVVYUVFUXW1cW WlYaWE8WVEojWFEyamM/c3s6bXUvYmUiU1YUT08WUVEQSEgRSUkPSEgTTk4RUU4SU08hSEcYPj0S IiAADAoBAgAAAAAEBQAICgIHDAMFCgEHCgAHCgAGCgAGCgAHCgMGCAIHCAEGBwAHCAEHCAEKDAQI CgIFBwEGCAIGCAMGCAMHBwIHCAMHCAQHCAQHCQMEBgAEBAAFBQADBwAECAADDwYDDwYBIB8ZPz4P Tk8WWFoRWFcUXFsYWlsWVlcXW2IVV14WV1gbXV4mX2kmX2kvZGQtYmI5Y1s+aWE/ZWU6X18uWF8k TVQPSU4OSE0QSkcPSUYRTUYPSUMRRk0USVAUODcEJCMCBwAAAwABBAACBgADBwAFCgEHCwIGCgEG CgMGCgMDBwEDCAEECgEFCwIICwIHCgEHCQEFBwAHCgEGCQAECAAECQAICAEICAEHBwIEBQAEBAAE BAAEBwEHCwQEDAUHDwgCGBMJIhwKNDkSP0QOSE0RTVEVVlcWV1gWUU4XVFATU1QPTU4XTVgeVWEh U1wjVV4oT1ArU1QxTFUxTFUdTFQZR08UT0ERTD4WSEMSQz0MQTkPRTwPQzsRRj4XOS8LKiEHFgYA CgAHCgEEBwAHBwAJCQAKCQQLCgUICgcKDAgLCwMMDAQJDAMHCgIHCgEFCAAGBwAFBwAEBQADBAAG BAAGBAAPT00TVFEXV00VVEkZVV0XU1sWUVAgXl00YV03Y187andAcH1AdIJDd4RMeHlMeHlUd3hN b3BQZ21KYWcmXFEgVUoWUUgSTEMQSUMQSUMSR0wRRkoRSEASSUEPPjMDLSMIHBMADwcABgAABAAC BAAEBwAEBgAFBwAEBwAFCAAFCAAHCgEGCAIEBwEFCgEGCgEFDQEFDQEHCwMFCgEFCAAFCAAGCQAH CgEGCgAECQAFCgAHDAEHCwMFCgEHDAMFCgEHFgEEEwADDwMACAAADAQGGA8DJR8MMiwMUVUVXWEZ X1wZX1wUXVcVXlgTW1UTW1UVV1QXW1cUXF0SWlsXWFoVVVYgWFEkXVYdYl4YXFgUWFQPUU0RU1AQ UU8SR0wUSU4QSU0QSU0RUUwQUEoiRkEWOTQIFg4ABQAAAwAAAwADBgAECAIEDAEDCgAHCgQHCQQF CgEGCgEICgUHCgQICwIICwIHCgEKDgQKEAYGDAMFCwIECgEGCAIHCQMHCAMICQMJCgMHCAEGDAMD CAABBwADCgIECwEECwEDDwMEEgUDGyMXNT4KR0gTU1QWX1sVXVgVXloSW1YUW1oQVlUTVVwZXWQb XV4XWFohWFojW1wcYVUWWE0eVk8dVU4WVFEVU1ATTk0PSEcPTkwQUE4QUE4PTkwTTEwQSEgPLyQA GQ8ABAAAAwABBwAECgAECAAGCgIFCwIFCwIHCwIGCgEECwEDCgEEBwQECAQHDAYGCwUHCQQHCgQH CgMHCgMECQAGCgELCgINCwMKCgMICAEFCQMHCgQGDwQHEAQIDwMJDwMDFgsGGg8IMCUQOy8LRkEQ TUgSWFcTWlgWUVEYVVUYW1gVVlQVVlQVVlQRT1cVVFwaTU8aTU8WR0oYSk4YSVAZSlEUTUwQSEcU RUcSQ0URRj8PRD0PPzkPQDoVLygIIBkGDwMACAAECQAHDAEJDAEKDQIHDQMHDgMHCgEHCgEKCwMM DgUKDQMHCgEECQEDBwAEBwAEBwADBQADBgAFBgAFBgAQSkkVUE8ZVlYXVFQWV1sVVVgTW1oWXl0U WFEWW1QnU2EuW2kxX2kuXGUsYVMnW003W1MyVk4rWlUpV1MWU1ERTEoTTEgSSkcPT0YOTkUQSkkQ SkkRSEAPRT0QODEEKCIDDwQACAAABAAABgACBQAEBwEECAADBwAGCQAJDAMJDAMHCgIIDQQFCgEE CgEIDwQFEAMEDwIHDQMJDwQHDwMEDAEFCgAHDAEHDgMHDgMHDgIJDwMJDgQHDAMFDwYEDwUKEgQG DgEHFQcADAECEQMFFgcBGyAHJCkJREwUU1sVX18UXl4SX2EUYmMWXV4WXl8VWlUWXFcTXVsRW1gV WFsTVlgYW1cXWlYSWlgSWlgTWE4SV00PU04QVE8VUFATTk4RTEwPSUkPT1AOTU4cRDoKLiUCDwIA BgAABAABBwEDBwIDBwIEDAEFDQIHDAQHDAQKCwMLDQQGDwQGDwQGDQQGDQQIDwMIDwMHDgQHDgQJ DwUHDgQGDAMFCwIJDgQJDgQHDgQGDAMHDgQHDQMDCgUFDggDCwMBCAEACAAACAAAGBEOKyMKQD0S SkcQVlwTWl8UXmETXV8TW1USWlQWWloXXFwWXl0QV1YUVVEXWlYRV1YQVlUXWFoUVFUSVE4RU00S T0oOSUUPSEgPSUkPSk0PSk0TSUkKPT0EEggACQEABQAABAADBgADBwEDCAEECQIHDgUECwMHDQAI DgEGDgEDCgAEBwEFCQMECgQECgQECQEECAAEBwAGCQEHCgEHCgEHCgAICwEKCgIKCgMHCAQICQQH DAMGCgEECwECCQAACgQFEQsHKB8RNSwNREASSkcPUVESVVUWV1UYW1gcV1QZVFAbWFcYVVQSTlMQ TFAaTU8cT1EVU1AWVFETVFEQUE4WTU4RR0gMRUEPSEUOTkUNTUQQRjsMQDUOJRADFgQECgACBwAH BwAJCQAGCgAHCwAHCwEFCgAJCgAKCgAICAEHBwEFCwAGDAEDCQACBwAEBQAEBgADBQADBQAEBQAE BQAOPDwYSUkbVFUdVlclWF8jVl0ZW1wXWFoQXlcTYlsmV2ghUWIkVWUjVGQaV1YbWFccVVUcVVUW WFYSVFEQU1MPUFASUEYQTkQTTkoUT0wPUE0RU08LSkENTUQPLysAGhYDDAAABAAABAAABwADBgAE CAIFCgMDCAEDCgMCCgIECgAFCwEIDQMIDQMIDwQJDwQGDwQEDQIHDAQJDwcECgQECwQJDQYIDAUG CwQFCgMIDwMHDgIMFQcLFAYFEwYGFAcHDgUFDAQEDwMCDAEBCgAABwAAFA8IHxoDMjcUSU4QWlwW YmQRYWERYWERX2QRX2QVV1UWWlcRWlUWX1sVWFgUV1cUW1wQVlcWV1gUVVYPUE0RU08NVE4PVlAS VE4PT0kVUVQXVVcRUFQOTE8TMisDHRYAAwAAAQAABgABCAAECgMDCgICCgEHEQYHDwMIEAQJDgQE CQEEDAQDCgMHCgQJCwYKDAQICgIJDAMHCgIIDwQGDAMGCAIGCAIKDQMLDwQHDAYGCwUGDwQEDgMC DgQCDgQCCgABCQABCAEBCQEAEgUHHA4FLysSQDwQUFQWWFweYmgXWl8SXmITX2MYWl0XWFwSWlgS WlgWXloQV1MWWlcWWFYUV1cRVFQQUU8PUE4SU00QUEoSU08QUE0ST00ST00YQTkGKiIBCQEABAAA BAADBwEDBwAECQEFCgEDBwAECgEECgEECgEECgEFCgAECgAECAAFCgEDBwEDBwEECgADCQADBwAE CQAHCAAHCAAECAAFCgEHCgIHCgIFCAAFCAAECQEDBwABBwACBwABBwMECgcDFhgKISMKNz8PPUYN TUkSVFAWWFUXWlYaW1MaW1MWV1sTU1YRUFEUVFUcT08cT08aTkoaTkoWT0kUTEYSSUQQR0EPTEEP TEERTUYPSUMRPDgKMy8HFgQBDgADBgAFCAAICgAKCwEHCwAIDAAGCgAHCwELCgEMCgEKCAAMCgEG DAEGDAEFDAAECgAICQAGBwACBAABBAABBQAABAAJOCoRQzQPU04WXFcgXWMeW2EbVVgeWFwWXV4U W1whVmMgVWIXU1sZVV0XWFobXV4WV1sXWFwWV1gSUVMUUVAWVVQQU0MPUUESTU0TTk4TU1QSUVMM Rj0KQzoMIhoBEwwDCQAABQAAAwABBAAEBgAHCQEECgEECgEECwEDCgAFCwEFCwEHCgEHCgEIDQIG CgAECgAHDgIIDQQHDAMECQEFCgEKCwUICQMHCQEHCAEJCwAKDQEKDQMHCgEECgEJDwQCDAABCwAE DAEEDAEECwECCQAADAMCDwYHJCscPUUYRlAjU10aXlgZXVcTYWIQXV4SW1YVXloTW1YWX1sWX1oV XVcWW1sUV1cXVlQYV1UQVE0WW1QPWlMPWlMPV08SW1MTVlgUV1oOSUgNSEcLIA8ADwEAAwAAAwAA BwACCQAGDAEGDAEEDAIHDwQHCgALDwMKCgMJCQIFDAQDCgIFBwEICgQKCAMJBwIICgIJCgMFCgYD BwMICgEJCgIKCwELDQMHDAYGCwUHCAQGBwMCBgAECQAEDgADDQAGCwAHDAAACwAGFgcGKCMPMy4P SVAUUFchV18hV18bXWgdX2oXX2EWXV4SXVcTXlgUXFcSWlUbWFcYVVQTVFcTVFcSU08SU08RUU8S U1AVVlcQUFEWT0cWTkYWKyACEwoAAwAAAgABBAAEBwEECgEGDAMGDAMECgEHCAEICgIGCQEHCgIG DQAECwAHCgAHCwAGCQAHCgEICwEGCQAHCgEHCgEKCgEJCQAHCgAHCgAHCgAHCgAGCQAHCgEECgED CQADCgACCQAGCwQHDQUDFQ8MIRsKLTIQNTsMRkgRTU8UVVMWV1UbV1AgXVYWV1gSU1QSVVUSVVUW VFEWVFEQTUgST0oWTU4USkwVTUcUTEYTTUQSTEMSSUARSD8WMysDGhMFCgADCAAFCAAHCgEODgQM DAMKDQMKDQMIDQIIDQIICgEKCwMKCQAPDgQHDQMECgEECwADCgAJCwAGCAAFBQADAwACAwADBAAH Lx4POigPT00TVVMUW1wWXl8WXloUXFcWXl0TWlgdXGMbWmETVFoXWl8SWFoWXV4WVFMWVFMTV1QT V1QYU1EZVFMUUEkWU0wUVVETVFAXUU4RSUYSQzwGMiwHGAUADgABBAABBAABBAADBwAEBwAGCQEG CQAGCQAHCgAIDAAHDAMFCgEECwAECwAHDwEHDgAGCwAKDwIHDgIGDAEHCgAHCgAHCgAHCgAFCwAF CwAHCgAHCgAKCgAJCgAGDgMGDgMHDQMFCwEHDAMDBwACCQAEDAEEDAAGDgEFGhwSKiwPPUQaSlEW X1ocZ2EYYWQWXmISXF4SXF4XXVoYXlsXYV8SWlgTW1YTW1YRWFAUXFQTW1YQV1MPW1oRXl0QW1UQ W1UQWlcSXFoSQz8GMi8BCAAABQAAAQAAAwAEBwAGCQEJCwAKDQEHDgIHDgIHCQEKDAQICgIKCwMF CgQGCwUJDAMKDgQIBwQIBwQKDgQJDAMDDQQCCwMIDQIIDQIJDQAMEAEJDQYHCwQKDQcICgUGCwQG CwQGEAEHEQIGDgEHDwMBCgADDAIBFhELIx4JNTwXSE8aT1ceVFwbWGIfXWcTYV0QXVoRXl8TYWIY Wl8XWF4ZWFcXVlUVWFgRVFQTV1AVWlMRU1AUVlQbV10YVFojSkAWOzEIEgYABAAAAgAAAwACBwAE CgMHCwMHDAMGCgIHCwMKDQMLDwQICwMKDQQLDwMLDwMMDgMLDQMHDgQHDQMHDgMGDAIIDAUHCwQL DwUKDgQKDwEJDQAKDQIKDQIJCgIKDAMKDAAKDQEHDgUECwMFCgQDCAMDEAoGFQ4HHxYQKiEKOzoS RkUSUVUVVVgZVlUZVlUVWlUTV1MRWFAQV08bVU8dV1EXVlEUUU0TTVAUTlEWT08WUFAYSkoZTEwa SDoPOy0LHQsBDwEEBgAFBwEHCgAKDQINDQQPDwYLCwQLCwQNCAMNCAMLDgELDgEMCgAPDAAKDAMH CQEGBwAJCgEJCgAEBQADAwAEBAADBQABBAAGGRIPJR0LPTwWSkkVVVsXWF4WXmIWXmITXlgSXVca XGIXWF4VW2EWXWMVXlgTXFYWV1sWVloVVlcSU1QbU1waUVsWV1gWVlcXWlYWV1QXTlMRRkoQMDIC HR8DCwEABQAAAgAAAwAABQAECgMHCgQFCQMKDAMJCgIHDQEKEAQKEgcHDwQEDwUFDwYHDAMHDAMG DAMGDAMGDgEFDQEKDAINDwQJEAIJEAIHDAMHDAMNDQQPDwcPEAUPEAUIDwgHDwcHEAUFDwQIDAUI DAUGDQcHDggGDgMGDgMBEwwHGhMKNTcRPj8ZW2McXmcXW2QYXGUVXmMVXmMWXVwWXVwVXGISWF4U XVgWX1sTW1MUXFQVXFsSWFcVVloZXF8UW1oTWlgWTU4RR0gPJhgADwQAAAAAAAABBAADBwAHCAMK CgQLDwUKDQQHDAcKDwoKDgcJDQYHCgQIDAUHDAYIDgcKDgQMDwYHCwcKDwoPEwkNEAcHEQkHEQkK EQcLEgcJDgQKDwQJDQcIDAcMDAoKCgkGDgoHDwoGEAgHEgoIDwoECwYCCAMBBwMACwcDEQwEIB0M KicKQEoUTVcPV1oTXV8TXV8VX2IYXGUbX2kZXWcXW2QXW1gXW1gUXmEPWFsUWFUYXlseW14XU1Yd T1sYSVURKyIBFg4AAwAAAgABBAACBQAGBwEKCgQJCwQJCwQJCgUMDQgKDgQJDAMICgUHCgQIDQMJ DgQKDQMKDgQICgcJCwcHDAQHDAQKCwMHCQELDwQJDAMHDQMHDQMJDQAKDwEHCwIGCgEICAEICAEF CQMECAIHCAMEBAAACAEBCgMEEAoLGBIHKi0PNDgPRUgPRkkTTVAZVVgQV1ESWlQeWlghXVwnXFgk WFUXW1EVV04UT04VUE8WUVETTk4cSEoTPT8UJhgADgMABgAABgABBwAECgEIDAAJDQAJCQIJCQIK CwMHCQEJBwAKCAEHCgAKDQIKCgALCgAICAEJCQEHBwAHBwAGBwAFBwADBAADBAACAwADBAAEDwgN GBEOJysZNDkPQEYXTFEWWl4YXWIXX14WXVwaXGIbXWMYW2EYW2EWXl8VXF0ZW2EZW2EYWl8ZW2Ec V1YcV1YWVloRUFQdVVwWTFMVPkkILjkGFxEADAcABAAABAAAAwABBAABBwEECgQFCgQHDAYLDwUL DwUEDgMHEAUIEgYHEAQEDwoEDwoKCgYLDAcJDwQJDwQKEwcKEwcJDgQKDwQJDwMIDwMKDgcKDwcN DQMPDwQHEQUGDwQJDQYLDwgIDgkHDQgKCwgODwsLFQ0LFQ0DCQUECgYBDgIBDgIHGBsNICMNQUwY UFsSWGMaY24WYWgWYWgWYWUVXmMaYmgWXGIZZF8YY14WXWEWXWESWFwVXF8aVlwdWl8cUF0XSlcW Oz8GJioDCAEAAgAAAAAAAQABBQAECAAJCAMIBwMHCwQJDQYHDAQIDgYFCgQHDAYKDAUJCwQHDAYG CwUKCgYJCgUHDQUHDAQJDgQKDwQIDQMHDAMICgUJCwYHCgMLDgcLDggICgUJCgQKCgQHDwQFDQMH DwcDCgMECgMECgMECAMEBwMABwAACgICEQ8JGhcIMjcSP0QMSU0UVFcXXF4XXF4WWmMZXWcVXGQV XGQUXF8UXF8RXlsQXVoXWF4ZW2EbUFgVSFAYPEMDIScBCAIAAwAAAQAAAwABBQAECAAICgEJCgII CwIICwIHCgMICgQHDAMHDAMGCgMFCQMECwAEDAAICwIICwIHCgEGCQEHCgAICwEJCQIKCgMHCQEG BwAHCgEJDAMIDgEGCwAGCQEHCgIJCgIFBwAHBwEHBwEJCQAHBwACBQAEBwEBCwYEEAoHGhYOIx8I LjAQOTsOSE8STlUWVFMVU1EWVFgaWF0eWlgeWlgaVVUZVFQZVlYWUVEXUFgMQUkRLCYCGBMCBwAA BQABBgAECQIHCQEJCgMKCwMKDAQJCgIJCgIKCwEJCgEICwEKDgMICwMJDAMLCwQKCgMLCQQLCQQK CQAIBwAKCAEIBwADBAACAwABBAADBgAABwEEDAUEEQ8QIB4HLS8TPT8NTE8UVVghXFYjXlgbXWEZ W14VXV4XYWIUXlwTXVsaXF0aXF0ZXF8aXWEXW1gWWFYXWF4UVFoXSU0SQ0YPKTAAFhwABAAAAwAA AQAAAQAAAwAABAADBwIECQMBCAAECwEDCwEDCwEFCwEECgEFDgUHDwcHDAkHDAkFCgEECQEIDQIH DAEHDAMHDAMJCgMICgIFCwAFCwAECwEEDAEIDgEKEAMHDwQEDAEFDAQFDAQHCgYKDQkKDQQGCQEI EAQHDwMCBgADBwADCgADCgAECQYKDwwFJiQQNDIOUEkUWFEPXFsUYmEXYmcYY2gYY2gXYmcbXmId YWQWYWUTXGEUVl8ZXWcdVGIcU2EXRU0LNT0KFxMABAEAAAAAAAAAAQABAwAABAAECQIFCQMECAIE CgUHDAcHDQUFCgMHDQMHDQMKDAMODwYHCgUFCQQICQMHCAMEDgMDCwEHDwQHDwQJDAMJDAMHCgMI CgQICwEICwENDwQKCwEICwMKDQQHDwQECwEEDQQBCQEFCgEECAAGBwQHBwQBCQADDAIDCgYDCgYB Eg8KHhoKMjQTPT8RSlAWUVcTWl8SWF4SV2ITWGMQWF8RWmERWGQRWGQbUVMXTU4ZQTUKLyQHEgsA AwAAAQAAAgAAAwADBgAGCAIHCQMECgEGDAMKCwEKDAIJDAEICwEKCwEKDAIHDQUHDQUGDgMEDAIK DAILDQMGCgIGCgIHCgIKDQQNCgUOCwYICQQICQQICQMICQMHCwIHDAMKDAQLDQQLDAYLDAYMDAQK CgMKCgIKCgIECQMDCAMFCwkGDAoCDgwEEA8EGR4JHyQGKTgKLz4ORUUPRkYLRUcSTlASUVMRUFEc TlAXSEoXSUQPPjkRLCoAFBIBCgAABgAABAAABAAEBQEJCgULDAYKCwUNDQUPDwcLDQQJCgMKCwMJ CgIKDgMOEQUMDQoKCwgLDwULDwUJCgQKCgQKCgMJCQIOCQEKBgAEBAADAwACAwACAwAABwAABwAA DAQEEwoBHhUKKiAQOzMbSEAVSkYaUU0cWF4eW2EWX2EaZGUWYmIVX18YXV0ZXl4cW10cW10WWFwX Wl0UU10QTlgRNTQEJCMGEAcABwAAAAAAAQAAAwAABQACCAECCAEDCgIHDgUFDQMFDQMDCwEEDgMK DwQHDAEECwECCQADBwEECgMJEQUHDwMJDgQGCgEECQAGCgEHCwEIDQIEDwIDDgEGDAMDCQAFDgAI EQIGDgMFDQMHDgUGDQQIDgYHDAQFCQMFCQMECQAHCwEIDQIECAADBwAJDgQMDAQMDAQEFgcHGwoI KxwRNycPPzoZTEYSWl0TW14SWlsRWFoaVFwgW2MSVVcUV1oWV1sSUVUUSkgKPTsPJh4BFA0AAwAA AAAAAAABAAAAAgACBgMEDAcGDwkEDgYEDwcKDgoKDwoIDgcHDQcGDQQJEAcMDgMNDwQIDgcHDAYH DwQHDwQHDwcFDAUHEAMFDwIKDQIKDQIJDwgHDAYIDwEJEAIKEAMMEgQKEwcKEgYJEQYHDwQCCgQE DAcPDwcSEgkHCgcHCgcIDwoFDAcCBwQABQIACgMDDwYEGQsKIBEEJyUOMzEKPUENQEUKQ0wNRk8O TE0LSEkMPj8HNzgNKBwOKR0KFggACAAAAwAAAQAAAQAABAAABAADBwIDCAQHDAcHCwYIDAcJDgQH DAMODwgKCgQKEgcIEAUEDQcEDAcHDAYHDAYKEgMKEgMHEgUFEAQHEAQHEAQLDgcICgQHCgcHCgcK CgQLDAYKDgQICwIKEQMLEwQMEAkHCgQLDgELDgEIEQIFDgAEDAIFDQMGCgYDBwMABwMABwMCCgYE DQgADw8EFBUHIxwHIxwEIyQKKywLKCgKJycKIh0HHhkEEQgACAEABgAAAwAAAwAABAADBQADBQAD BwEHCwQMDgULDQQKDQMKDgQMDwIKDQEICAEICAEKDgEKDgEKCgENDQMJDAEKDQIHCwIECAADBwAD BwAHCgAFBwAEBAAFBQAGBAAEAwACDgUACQEADAEBDgIDEgcHFgoHJRoSMicLOzEWST8WT1MbVVgY XV0aX18VXWEUXF8YWl0cXmIXW1cWWlYbU1cYT1QPODoILzEKHhAADwMAAwAAAQAAAQAAAwAABAAC BwAHDQMHDQMECgEIDwQIEgcFDwQBDwEEEwMJEQQJEQQFEAQEDwMIEAMKEgQIEgYJEwcKDwUKDwQK EQgKEQgGDwQHEAQGEQQIFAcIEgYGDwQFDwIIEgQKEgQJEQQHEgYHEgYLDwQKDgQLDAYKCwUKDgcH CwQJEwcGDwQFDAQJEAcECgQBBwEDCwAHDwEEEgcHFgoKJRsRLSMJOjkOQD8NRkMNRkMPRUYSSEkM RUQMRUQNMjAGKScNIxcADwYABgAAAwAAAAAAAAAAAQABBAAABwEDDQcIDgkKEAsJFAoIEwkMEAoL DwoKEAgKEAgMEAkLDwgPEQYLDQMHDgcHDgcHDwQKEgcMEAwJDQkIEAMMFQcNEgcKDwUPEAULDQMH DQMKEAUNEAcNEAcPEAcPEAcIEgYHEAQLFAgKEwcNDwYPEQgGDQYJEAkKEgwHDggABwEABQAABAAA BAABBwAECgABDQcEEQoCGhYEHRkFHyUEHSMFHREEGw8EFAgADQMCBwABBgAAAQAAAAAAAQAAAgAC BAAEBwADBgEHCgUICQYLDAkJDwgHDQcLDwoIDAcHCwQIDAUHDwQIEAQECwYECgUECwEGDgMEDwQG EQUGDQYECwQHCwMIDQQKDQYICgQHCwQGCgMGDgMFDQMGCgEGCgEKDgELDwIKDAMGBwAGCwAHDQAE DQAEDQAECgEECgADBwEDBwEABQAABgACBwACBwAABAABBwEDCQQBBwMABwMABgIDCwQBCQMBBwAA BgABBAAAAQAAAgAAAwAAAwABBAABBAADBwAFCwIFCwIKDwMHCwEKCwAMDQELDgEHCgAKCgIJCQEI DgAIDgAIDAAJDQAGDAEGDAEODwEKCwAHCgAFCQAECAAFCQAEBgAFBwAFCAADBgAHFAkHFQoGEAgE DgYJDwMHDQEDCgQKFAwGHRUNJh0JLSsRODURSEATSkMSR1APRE0aTE4eUFMURUUPPz8RNzoPMzcK GxAADgQBAwAAAwAAAAAAAAAAAwABBQADBwAHDAEHDQMFCwEIDAcKDwkKEAgJDwcGEQQGEQQOFAUM EgQJEQUKEwcIFAcIFAcKFAYJEwUKDwcKEAgOEw8PFREPEA4PEA4IEAQKEgYJFQcGEQQHEAMKFQcN FAkLEgcJEwcHEQYNDgkPDwoLEwsJEAkKDwUKDwUJFg0EEQgGEAcDDQQHCgcHCgcICAAFBQABBAAD BgADCQQCCAMBFQUEGQkJIhYFHREGGg8KHxQEGQ8BFQoACQAABwAAAgAAAQAAAAAAAAAAAQAAAgAB BAADBwEEDgQEDgQHEAQHEAQHEAQHEQUPEAcODwcTFQoREwkNDgkNDgkLDQQKDAQFDgUFDgUKDQML DwQIDAUHCgQIDQQJDgQMDgUKCwMKDwQKDwQHDQMIDwQLDwQLDwQQDggQDggGDwMFDwIHEAUFDwQG DwAJEgMHDwIHDwMHDwMHDwMDBwACBgAABQABCAABAwABAwABBAAAAwAABAAABAAABgEAAwAABAAA AwAAAQAAAAAAAAABAQAAAAAAAgAAAQABAwAEBQAHCAEFCgEFCgEKCgMKCgMLDQQKCwMKCwMKCwMK CwMJCgMJDQALDwIHDQEFCwAHCgEJDAMKDQMICwIHCgQHCQQJDAEKDgMKCwEJCgEHCwEHCwEGDAIH DQMLDgEKDQEICwIKDQMKDgQJDAMHDQAFCgANDQMKCgEMDAQHBwADCQAECgAICgIFBwAEBAAEBAAG BAAHBQABAwABAwAABAABBAABBAABBAAABAABBAAEBwACBAADCgAABwAABQAECgEECAIFCQMJDAMK DQQLDQQLDQQLDQMKDAILDAEKCwAKCwMKCwMJDQAHCwAKDAALDgEICgILDQQPDwMLDAEICwEGCQAI CgEFBwAFBQAFBQAFBwAFBwAKEQYLEgcKEgcECwEHCgEEBwABBwADCgADCgAEDAEDDwYFEgkGGAsG GAsHIhEEHg4HFhAEEg0HFgcJFwkFDAQCCAEAAwAAAQAAAAAAAAABBAABBAACAwAEBgAGCQAICwIH CwIFCgEJDAMLDwQNFgcMFQcMDQcKCgQKDwIKEAMKDwYGCgIFEQEFEQEGDwAHDwEKDwQKDwUHEQUF DwMHDwMJEQUKDwUKDwQHEgMGEAEKEgMMFAQLDwIPEwQODwcMDgUKDwQIDQMFCwIIDwQIEAMGDgEE EgMEEQMFDQMDCgAFDAcHDggJCgMJCgMFBwAEBQAABAAAAwAABQAABQAABAAAAwAAAwAAAwAAAwAA AwAAAwAAAwAAAgAAAwAAAwAAAwAAAwACBAAHCAEMDgUHCgEHCgEKDwMKDwMKDwQNEgYNEAYJDAMK EQEKEQEIDwMJDwMLEAYKDwQHDwcGDwYHDwQHDwQIDwQHDgQKEAUKEQYNDgcMDQcPDwcQEAcODgQP DwQLDwUKDQQNDQUMDAQKEAQLEgUOEQUPEwcIEwMGEAEHCQAJCgEGCwAHDQAKDAIHCQADCgAHDgAE BQADBAACBQABBAAABAAAAwAABAAABAAAAwAAAwABAgAAAQABAQAEBAABBQADBwAEBwAHCgEJCwQK DQYIDQQJDgQNDAcLCgUMDAQMDAQKCgIPDwYPDwMODwMKCwEHCQAKDwMKDwMKEAMNEwQNEQMLDwIH DAMKDwUNEQMLDwIMDgQKDAMJDwQJDwQKDwcKDwcNDgcNDgcKEAYNFAkPEQYLDQMLDwMKDgMQEAgQ EAgQEggMDgQKCwMKCwMFDAQFDAQKCwEKCwESCwUSCwUIDwAECgAECQADBwADBgABBAAECQEECQEE CgEGDAMHDwQHDwMECgEJDwQKDwMLEAQNDQUNDQUODwoODwoMEQUKDwQKDAIKCwEPDQMPDQMMDQcK CwUMDgQPEAcKDAMMDgQKDQENDwMKCwMICgILCgEKCQAGBAAFBAAEBQAFBwAJCgEHCQAFDgADCgAD BwACBgAGCgAECAADCgADCgACBwAABQAABAAABAAABAAAAgAABAAABAAAAwAABAAABAAAAwAAAAAA AAAAAgAAAQAABAADBwABBAACBQEGCQEGCQEHCgEKDQMHDAEHCwEFCwAJDwMNDQUMDAQIDQIKDwMJ DgQHCwIHDwMHDwIGDgMIEAQKDwQLEAQHDwMHDwIHDwMHDwMMDwYJDAMJDwcIDgYKDwULEAYMDgMO DwQPDwcUFAoKEgQKEwUIDwMHDQEMDwcKDQYIEAUECwEDDQAIEwMOFQkIDwQHCQAICgAFCgAGCgAC BQACBQAABgAABQAAAwAABQAABAAABQAAAgAAAgAAAgAABAAAAQAAAQABBwABBwAHCAEICgIJCQER EQgKDwkJDQcPEAcPEAcNEggPFQoLEQoHDQcIEAULFAgFDQIJEQUKFAQIEQIIDwgHDwcIDgcKDwkJ DQYKDwcNFQsKEgkLFAcKEgYQEgcVFgoQEAcQEAcKDgoJDQkNDwoLDggODwcREwoVEAsUDwoKEQEL EwIOEAMTFgcPDwUNDQMOEQcOEQcLEgcKEAUKDAQKCwMECgMBBwACBwABBgABBAABBAAABAAABAAC BAAEBgEEBwEFCQMDDAEEDgMLDgoLDgoNDgkLDAcIDAcJDQcMDwQMDwQPEAcQEgkMCwcPDwoODAQP DgUQDwYRDwcOEwgMEQcJDwUJDwUKDwYLEAcLEQwHDAcLDgcKDAUKDQcJCwYLDggNDwoKDwoKDwoN DwwKCwkFCgQIDgcOEwgMEQcJDwQKEQYMDwkMDwkNDgoMDQoODwsMDQoHDAQHDQUHDQMHDQMJCgQH CAMEBwAEBwAEBwAFCAAEBgAEBgAEBwEEBwEDCgMECwQGCwQGCwQHCwQIDAUKDAQKDAQLCAIPCwQK CgQMCwYKCgMKCgMKCwEKCwEKDAQJCgMLCgINCwMPDgIPDgIICAEKCgIKCwMKDAMKCgAHCAAHBwAF BQAFBAAFBAAHBQAHBgAHDQMKEAUDDQQDDQQOEwkOEwkREg0MDQgHEQEEDwABBgADBwEAAwAAAwAA AwAAAwAAAQAAAQAAAwAABAAAAwAABAAABAAABAADCwEABwAGDAMJDwUHCwEKDwMIEAQKEwcOEQcV GA0KFAgFDwQKCwUREgsQEQwKCwcHDAYHDQcLEgcIDwQKDwULEAYKEAYOFQoOEwcOEwcKEQoNFQ0P FAcNEgYMDwYNEAcOEgoKDwcQFAoPEwkPFgoKEAULDwgVGREMFQgPFwoPFAkPFAkVERASDw4SGRUS GRULEAcPFQoWGA4SFAoPFxELEw0MFg8KEw0JDQkHCgcGEAcBCgIDCgEDCgEBBgABBgAAAwABBQEA AwADBgADDAMCCwMEEAcEEQgMFA4LEw0MDwkUFhAPFgoLEgcTFA8PEAsOEQ8PEg8HEAsJEw4KFAcK FAcIEwkKFgsKEgQKEwUKEgoMFAwODgYSEgoQEgkREwoRFQgTFgoOGgoOGgoNHRQPHxYODwsQEQ4O Eg4KDwoOEwkOEwkPEA0WFhMXFAwWEgoLEwQPFgcPFQoNEggPDwcSEgoOFQkLEgcREg8KCwgKDwUM EQcHDAQGCwQGCgEDBwACBQADBgAEBAIDBAEBBAIDBQMDCgQEDAUGDwQHEAUJCwYOEAoOEAoLDggH CwQKDwcJDwUKEAYJDwQLEgcNEAcKDgQJDAMJDAMREAQTEgUMDgULDQQHDAMJDgQHCQEKCwMKCwUH CAMIDQMKDwQIDQQJDgQKDwQJDgQJCwQICgQIDAcGCgQGCgQJDQcKCgQKCgQECgEJDwQKDwMJDgMH CQEMDgQNDwQKDAIKCwMKCwMECAICBQADBgAEBwEEBwEDBwECCQADCgAECAAFCgAHCAAJCgEGCAIH CgMFCwEECgEKCgMLCwQJCgIICgELDAYNDgcICAEKCgMKCgEODgQLDgEKDAAKDQQICwMLCgIODAQM DQEKCgALCQAPDAAKCwMHCQEKCAAIBwAKCgAHBwAHBgAEAwAFBQAFBQARFgsRFgsHEAoFDggKFg8N GBEUGBQKDwoMEwcMEwcEDwMEDwMDCgAEDAEHCAEGBwADBgECBQEAAwAAAwAABAACBwAHCgcHCgcG DwMGDwMKEwcMFQkMFQkLFAgNFAcMEwYPFAkPFAkIEgYHEQUPEgcRFQoIEQgJEgkKDwkPEw0SFgsK DQQMDwkQEw0IDwcKEgkTFQkUFgoNFQ0LEwsKDwcMEgoMEQcMEQcMEwcOFQkLFAcMFQgMFgoJEwcU FgsWFw0OEwcOEwcLEwoPFg0TEwoQEAgMFA4PFxELEwsOFg4JFA0LFg8KEg8KEg8HDwsHDwoDCggD CggFCQMHCgQHCgQHCgQABAABBgEABgADCQEDCgMDCgMGCwcFCgYPEAsODwoKEQoLEwsKDwkLDwoK EAYJDwURFA4NDwoLDwgIDAUJEAkKEQoKEwcJEQUKEgoOFg4KDwcIDAUJDwQKEAUMEAENEQIPEQQN DwMQEgcREwcMFggNFwkLFwoLFwoKEwcKEwcOEQcKDQQMEQcMEQcNDAgODQkOEQcNEAYOEAMPEgQP EgoKDAULDAYNDgcNDwUNDwUIDAUHCgQMDQEMDQEFCgAFCgAECgAECgADCAADCQAEAwAGBQIGCAQH CgYGCgIGCgILDwgJDQYKCgQKCgQJDwUJDwUKDAUKDQYKDQQKDQQICgUKDAcKDgMNEAQKDwUKDwQP DwQPDwQKDQINEAQPEwMKDwAKCgIODgULDgcKDAUKCwMICgEHDAMJDgQHDAMGCgEKDwQJDgQHDQUG CwQHDAQJDwcKCwULDAYMDwcKDQYHDgUECgMHCgQIDAUKCwMKCwMKDgQJDAMFDQIEDAEJCwQGCAIH DwIHDwIFCgMGCwQICAEKCgIHCgAJDAEKCgQLDAYJDQYHCwQPDQMQDwQMDAQNDQUKCgMMDAQKCwUL DAYMDAMPDwQLDQMICgAKDAIODwQMCwYKCQQLCgENCwIOCwYSDwoQEAYMDAMODQEMCwAJCQIICAEE BAAEBAAEBwAGCAAQFgoPFQoNEggKDwYMEwcMEwcPFQoKDwYPEwsIDAUGDwYEDAQDBwEFCQMFBgME BAIECAQECAQEBwIFBwMDCwMDCgMECQQECgUJDwMKEQQMEQUMEQUKDwYMEQcNEgYKDwMKDwQKDwMK DwANEQIPEAcPEAcHEAUHEQYPEAUPEAUPFAcNEgYIDwgGDQYHEAMHEQQJEQQKEwULFAcNFgkIDwcH DgUKEgQKEwUNEQMNEQMMDwYLDwUJDwUHDgQKEgcIEAUIEgcIEgcGDwYGDwYKEQMNFQUPDwoPDgoM EQcPFAoKFQgMFgoIDAUIDAUKDwoKDgoJCwYJCwYKCgcHBwQHBwIKCgQECQIGCwQFCgEECQEECQEH DAMLCggLCggPEAUMDgMMFggKFAYKDQYLDgcIDgcKEAoNDwsMDwoKEAYIDwQKDgQLDwUKEQcJDwUI DwcKEQgMDgQKDAMHCwMKDwYMDwAPEgMOEAIPEQMPEQcSFAoOEwgPFQoNFAcMEwYNFgkKEwcPDwoP EAsOEwkKDwYODwoNDgkKEgkIDwcQEwQRFAQPEQoOEAkPEQoMDwcJEQUJEQULEgUHDgIHCwEHDAEK DgMHCgAGDwQDCwEFCgAHDAAKCgMMDAQJDQcHCwYGCgAFCgAIDwQKEAYMDwcJCwQNEAYPEgcNDwoQ Ew0QEggODwYMDwcNDwgNDwYODwcKEAMLEQMNEQMMEAMGEQMFEAMLEwMKEQELCwMODgUNDwsMDwoL DgcKDAUIDgcJDwgKDgQLDwQPEgYNEAQJDgMIDQIIDQQKDwYMDQgMDQgPDgsQDw0PEQsLDggKDwUK DwYKDwYKDwYNDwgKDQYMDwEOEAIPDwkLCgULEQMMEgQIEAUIEAUPDQoNCggMDgUNDwYMDwYLDwUQ DAcSDgkPCgYQDAcKDQcLDggJDwQIDwQKDgcLDwgODgQODgQLDwMLDwMODwcODwcLCgcMCwcMDAQM DAQREA4REA4PDgUPDQQLDwIIDAAICgAGBwAEBQAEBgAGCgAECAAKDwcKDgcKEwUKEgQIEgYHEQUL DwIJDQALCwMLCwMGCgEIDQMHDgMHDQMFCQMECAICCgQDCwYGCgEIDQMHEAUDDAIEDAIEDAIKDwIK DwIMEgMOFAQKEAMKDwINEQMOEgQMDwQNEAQJDgQKDwQMDQcNDgcMEAkJDQYPEwQOEgQIEgQHEAMJ EQYIEAUKDwIKEAMJEQYIEAUIEgQHEQQJEQYJEQYKEwcJEQYLEAYLEAYMDgMNDwQJEQUHDwQJEQUK EgYNFAcKEAQNFAcKEQQOFAUPFgcUEAkWEwsWGg8PEgcPGAcOFwcKDAMNDwUJDQYMEAkODQoKCQcH CgcHCwcGCgEHDAMKEQQJDwMFDQMGDgMHDgcECwQMEAkLDwgPDgsUExAOFAsJDwcIDwcKEgkEDQYK FAwOEAoMDwkOEwkLEAcLEgcLEgcNEw4KDwoPEQcPEQcLEgcIDwQHCgUNEQsMDwUPEwgPFgkNFAcV Fg8QEQoPEwsWGhIPGxINGA8QFwwQFwwLEQwOFA8PEAoPDwkJEAoMFA4PFBANEg8PFAoQFgsLEwsK EgoLFQsKEwoKEwcOFgoPFgoNFAgHEAcHDwcNEQ0JDQkNFgkMFQgGEQUFEAQPEAoLDAYKEAMPFQYI EgcIEgcKDwkPFQ4PEQsNDwoPDwcQEAgSDwoTEAoOEQcNEAcLEQwKEAsPEgcMDwUNEAYNEAYMDwQK DgMKEgkJEAcKEgQJEQQPCgYQDAcPDwoPEAsMDwkKDQcPEQsPEgwOEAoMDwkMDQgNDgkMDwQMDwQI DwcJEAcKDQcKDAcODQoPDwwPFg8MFAwJEQUKEgYQEQ4PDwwKDQMJDAMODwEODwENDwEQEwQKEgQJ EQQHEAQHEAQLDAYJCgQKCgQKCgQNDwQKDAIODgYODgYKDAQLDQQJCwQKDQYLDwUICwMHCgEJDAMO DgQNDQMJDAMKDQQOCgMOCgMKBwELCAIRCwEQCgEODAMNCwIPCgMRDAUKDQAHCgAHCQAFBwAEBAAE BAAFBwAFBwAMEAkPFAwQFwsNFAgMEQ4NEg8PEgcLDwQPDwQQEAYUFgsREwkHDwEMFgUKEwcJEQUK FQwIEwoHEQUKFAcLFAgIEAUIEAQJEQUKEAQLEgUPFwcPFwcNFAgLEgcKEwcKEwcPFQgMEQUMEAkP EwsLEgcKEQcPFQoTGA4LFwoHEwYJEwcJEwcIEwkLFgwOFggLFAYMEgoNEwoSFg8NEQoKEwcMFQkM FgoJEwcKEQgLEwoLDQQPEAcUFw0UFw0LFgkMFgoSFAgSFAgWGg8PEwgPFAkOEwgWEgcbFgoWGg0T FgoQGQoQGQoLEgUPFgkPFgoPFgoREgsTFA0IEwwHEgsHFAMKFgUOEwgLEAYJEwcJEwcMFhEMFhEP ExARFRIWFxgVFhcTGhcQFxULFQ8KFA4KFQoNGA4RGwoTHQsPFQgPFAcPFAoPFAoKEgoMFAwXEw4W Eg0NFgkLFAcKDwkMEAoPEQ8PEQ8LFQ8KFA4TEg4TEg4UFAoVFQsUHRAUHRARGg0MFQgKEwsMFg4N DwsQEw8NEwwOFA0ODw8SFBMPEA8MDg0KDgoMEAwUFgwUFgwQEggTFQoOFQkQFwsOFw4KFAoKEAMN EwQRFwgLEQMKEAMKEAMMDwcMDwcQEwQPEgMKEgYKEgYQEQwPDwoMCwcODQkPCwcOCgUQEQQPDwMJ CgIMDgQKDwcLEQkNDwQODwQOEQcNEAYLDQQICgELDAYLDAYNDQQODgUODAQMCgMKDAQLDQQMDgQK CwMLDggLDggNDgoLDAkKDgQHCgEFCgAHDAEIDwEKEQMMDgQKCwMPDQMQDwQNDwMLDgELDwMKDgMK CwMHCQEICgALDgELDwEMEAENDwELDgAFCwEGDAIKDgELDwIICgIKCwMMDwYKDgQKCwELDQMODwML DAEKDAIKDAIJCgMLDQQHDAEHCwEKDwEMEAMODAMODAMKDwEMEAMMCgMODAQPDAUNCgMQCgQSCwUL CwIMDAMPDAEPDQIKCwAJCgAHBwAHBwAFBQAEBAAFBQAEBAAPFQoPFAoPGAwKEwcLDwoNEQsRFQoQ FAkUFgYRFAQWEwkWEwkLFQ8LFQ8MFhUKFRMPFhENFA8HFQoIFgoTFRIVFhQPGhMLFg8PFwsOFgoM FQkLFAgNFAgOFQkIFAcKFgkPEw8SFhIVFxENDwoMFAoPFw4PFg8PFw8KFAwKEwsPFgoMEwgOFg8P FhAOFw4KFAoSFAoVFgwaFg8WEwsKEQYLEgcNEwQOFAUOFgoPFwoQFAkQFAkUFAoREQgNFgYPGAcQ EgkUFgwUGQ4OEwgOFgwRGQ8VFgwWGA4PGAYQGgcRGQcRGQcREwcUFgoRFg8NEQsTFQoQEggKFg8I EwwLEQMQFgcOEwcNEgYLEgcMEwgOEgwPEw0QFgkRFgoQEgkSFAoWFw0QEggOFgoOFgoKEQYOFQkS FAoVFgwPEgcQFAkNEgcOEwgNEAcKDgQMDQEPEAQODwQNDwQJDwcKDwcPEwgPEgcKEQgKEgkSEAYW FQoQEwUUFggRFQoRFQoMEgMOFAQODwcPEAcNDwgMDwcPEgQPEQQMDwULDwQPEQgLDQQNDwYMDgUP EQYQEgcTEgUQDwMNEQIPFAQLEgcHDgMLEgcLEgcLDwEKDwAMDwALDgAKDgELDwIPEgQUFggMFAQK EQMMDgMMDgMKDwQLEAQNDQMKCgEOEgIMEAEMDwEOEAIKDAQMDgUKDwIKEAMKDAIICgAJCwANDwMP DgYODAQLCgUNDAcPEAcMDgQMDwoJCwcKCwMMDgQNDwYODwcPDwcNDQUKDAMKDAMGDgMKEgcIDwQJ DwUKCgEMDAMOEgMMEAEMDgMNDwQPEAMODwENDwEMDwEKDwAPFAQNEAYMDwUPEAMPEAMLDwQNEAYM DgQODwYPEgMPEgMPEgwPEgwPDgYQDwcQDgMRDwMKDwQKDwQKDAMNDwUKDwULEAYKEQEKEQENCwMP DgUODwQMDgMQDgEPDQEODwEPEAMPDQIQDgMPDwEPDwETDgcRDAUNDQMKCgEHBwEFBQAGBgAFBQAE BQAEBQAMFQgKEwcOFQcLEgUKEQcKEQcPEwcNEAQRFAYRFAYPEgQSFQcOFgoKEwcMFAoKEQgOEAoP EgwMFA4KEQsRFA4PEgwKEgcIEAUOEgwPFA4KEAgKDwcJEgkKEwoLDwoOEgwPDwwQEQ4QFgcOFAUL FAcKEwcLFAYKEwUPEQoPEQoKDwYKDwUMEAkLDwgLFgkKFQgNDwMPEgQQEgcQEgcMEQUIDQIMEwEP FgMLEAYPFQoRFAYRFAYREQcPDwQKEQcMEwgKDwcMEAkKFAoJEgkPEQ0QEw8PEgcPEgcKEAgMEgoS FQcPEQQQEAcODgUMDQgPEAsMFQgOFgoPEQoOEAkPEQMTFgYSFgYPFAQMEQcPFAkLEgcOFQkKFgkL FwoWFAsWFAsSEwYREgURFgcWGgoMEwcMEwcTEQcSEAYPEQgSFAoPDwMPDwMPFAkLEAYNDQMQEAYP EQgPEAcKEAgJDwcNEggNEggLEwoLEwoTFQsUFgwOFgoPGAsUFgsSFAoRFAQSFQUREQcREQcOEQcO EQcPEwMPEwMMEgQNEwQTFgoRFQgPEQgPEQgRFAwPEQoWFw0SFAoOEAMSFQcSFgsPEggKFAoKFAoP EQgPEAcPEAUQEgcPGg0PGQwPGwsRHg4PFQoLEAcNDwgLDgcLEw0MFA4PDwkLDAYNEwQNEwQKEgMM FAQPEAoMDQcNDwUPEAcPDwcPDwcOEAIOEAINEAQPEwcPDw4PDw4VEg8WExAMEg0KDwoODQcTEgwQ EgcPEAUTFAcTFAcPDwcREQgLEAcPFAoKDgsPEg8RDwoSDwsREwcQEgcODwYREwkWFQsWFAoWFQsW FAoMEwgMEwgPEAoREgsSEAYQDwQPEAcREwkNDQQPDwYPEQ0SFRATDw0SDgwQDAkRDQoSEAcQDwYL DAYNDgcMDwUOEQcTEQcTEQcPEQMNDwEOEQcOEQcQDwMPDgIOEAENDwEKEAMKDwILCgEPDgQPCwEU DwQQDAkPCwgKDgEIDAAHBwAGBgAEBgAEBgAHBwAGBgALFQQLFQQNFQULEwQJEQQKEwUPFAoMEQcR EwcODwQPEAUQEgcPEAUPEAUKEQcKEAYMEQcLEAcLEw0IDwoJEAkHDgcPDwcPDwcLEAcMEQcQFgsP FAoMEwgPFgoMEgoKDwcKEgcHDwQPEgYVGAsQFgoMEQcMFgUMFgUOEQcNEAYIEgcKFAgOFQkKEAUM DwYOEQcOEAMSFQcSFgcPFAUMEwgKEQcPEwQSFgcSFgoQFAkKEgkLEwoREQgREQgJEgkKFAoPFg8M FAwMFQkPFwsPEwsPFAwVFg0WFw4QFg8MEgsPFAwSFg8WGg8VGA4SFQ8TFg8PFQYPFgcKEgcPFwsP FgUPFgUSFgcOEgQUGBAUGBANFwsOGAwPGRMOFxEWHBYUGhUWGgoWGgoVGQgWGgkIFgkKGAoOFA0L EQoQEgkREwoTFgsTFgsNGQsLFwoKEgYQGQwQEwsSFQ0RFhEMEAwRFQoTFgsQEggTFQoTFQsVFg0Q GQ0PGAwPEQsQEw0REwoSFAoRFgcUGAkTGA4PFQoTFwgRFgcVGg0UGQwMFw8KFg0IEwoKFg0RGg4P FwsOFAsQFg4OEg4PEw8SFQ8QEw0PEAsSEw4PFgsPFgsSGQ4WHhISIhIRIRERHBYPGhUUFw0UFw0R EwkXGQ8QGBAOFg4TFgwVGA4TGA4QFgsLFAgOFgoVGA0PEgcPEggPEggSDwcRDgcPEAoODwgODwoO DwoPDwkPEAoPDgsTEg8SFAoODwcPDwcUFAoTFgcRFAYREwkPEQcRDAYRDAYQEAgSEgoRDQoXEw8W Eg8VEA0REwkUFgsQFAcQFAcSFAoPEQgNDwUNDwUSEgkPDwYLDQMKDAILDwQKDgQNDwQMDgMNDQMP DwQPEAsQEQwRDwkPDQcMDAQQEAgNDAcKCgQLDwQPEgcKDgQKDgQOCwEUEQUMDAQNDQUHDQMECgEH DAEHDAEKDgMJDAEIDgEIDgENDAEPDgIODQAPDgENCgEQDQQKCwAICQAKCQAJCAAEBQACAwADBAAG BwASFgoTFgsOFgQMFAMRFQoTFgsRFAwTFg4NEgYMEQUSFAoUFgsOEwgQFgoUFAkVFQoRFQoPEwkP FBANEg8KFAwMFg4SEQsQDwoPEQcWFw0VFhAREg0PEg4SFRALFgoKFQkMFg4KFAwRFgwQFgsWHAwS GAkUFgoUFgoSFgoTFgsOGg0KFgoPFgoOFQkPEwsRFg4PEwkSFgsSFwwPFQoKEwcNFgoRGAwRGAwR FQoRFQoPFQwQFg4RGAoTGgwNHg8MHQ4QFQ8QFQ8QGw8PGQ0RGBQWHRgZHBQWGRERFA8XGhYTFxMT FxMVFhAWFhEXFxYXFxYUFwoWGg0PGREPGREXGQ8TFQsXFhIXFhIXHRIWHBEUGQ8YHhMVGBYSFhMS GhQTGxUVHhEUHRAUHRASGw8OHBIOHBIPFw8QGBARFQoTFgwQFhIPFBAPGQ0OGAwMFQkOFgoWFg0U FAsPGBIKFA4VGA0UFwwTEwgREQcWEgoaFg8OFhIKEw8VFxMZHBcVHBAPFgoVFg0VFg0OFgoRGg0R FA4PEgwUEw8WFRAVFhIUFREQFQ0RFg4UGgkOFAQdFA4eFQ8SFg8MEAkOFA0PFg8KFggIFAcQEwUR FAYVEgYZFgoOFgwMFAoLEg4MEw8QEggQEggVFAUXFgcREwoNDwYTEgQXFgcWGQ4NEAYKCwMREwoT EwoPDwYNEAYNEAYPEAQPDwMWFAoSEAcLDgcNDwgNDwQPEQYMDwYLDwUPEAUMDgMPDQITEAQMDgUL DQQLCwQNDQUPDQQQDwYVEAUSDgMTCgMZEAgQEAcMDAQJDwQKEAUKDgMNEAQTEwgNDQMPDAMPDAMS DgITDwMVDwcRDAQKDwQMEQUMDgMMDgMOEAIPEgMOEgQPEwQKCwMLDQQLDgEMDwIMDAQMDAQSDwMV EgQODwEKCwAWCwMYDgQVCAMYCwUPDAMRDgQWDwMTDQEODAQMCgMHDAMKDwUPDQQPDgUPDgENDAAO DQAPDwEQCQAPCAAOCgAOCgAKCAEFBAAEBAAGBgAQEw8WGBQWGg0WGQwPGQ8PGQ8SFw0SFw0RFg8S FhARFxIUGhUWFhEZGhUWGhYXHBcRGg4THA8QHhcOGxUOFhIRGhYWHxERGg0VFgwaHBEZHBcSFRAP FA8SFhITGxUPFxEQFg8TGRISGQ4UGw8SFhIQFRAWFg8VFg8PGQwPGQwSHRAMFgoPEw0QFQ8PEg8P ExAPEgoRFAwQGQ0SGw8OGAsPGQwQGAcPFwYWFgwWFgwRFhETFxMTGg4VHA8SGwwPGAoTFQsUFgwS GwwSGwwcGBcgHBsiHxoYFhEUFhIWGRUWFxIWFxIWFhUWFhUUFxYWGRgTGg8TGg8SFhASFhAXGhQR FA4RFg8UGBIeGBEcFg8ZFw4aGA8WEw0aFxEXGw8TFgsPGgsPGgsRGg4OFgoNFQsOFgwNEgcLEAYO DwQSFAgVFg8PEAoQFgsPFAoQEwsPEgoQDwsSEQ0MEwgMEwgVFgwTFQoUEgkPDgUUDwcYEwsPEgwM DwkVEwoXFgwTFgcPEQQXFgkVFAcQEgkTFQsPDwwPDwwPEAsREg0QEw0QEw0PEAcODwYWFAcUEQUV EQkTDwcOEQcQFAkQEgcREwcKEAQMEwYQEgEREwINEAQNEAQJEAcKEQgKEQYKEQYLEAQLEAQNDwQO DwQPDwkODQcNDwQLDQMLDgEMDwIODwcNDwYPDwoPEAsRDwMUEQUXDwMZEQQTEwgODgQQDQQUEAcQ DwQPDQMLDwQMDwUPEAcMDgQPEAcNDwUPDwMREgUODwcNDwYODwYPEAcSEAcQDwYaDgMeEQYKDwcM EAkMEQcPFAoOFgYLEwQWFwsUFgoPFAcMEQUUEwQTEgQcDgokFRAZFAwUDwcKEwMJEgMQEwQTFgYR EwkPEQcKDQINEAQXFAQVEQITEgQUEwQiFQQfEgIREAMPDwEYDwcYDwcmFgggEAQODgAZGQciFQQi FQQdFggcFgcWFAoSEAcQDggTEAoSDwMVEgQWFAcVEgYQDQQPDAMNDQQKCgIJBAAIBAAJBAAHAwAR Gg4PGAwOFgoMFQgTFg8VFxEPFQoPFQoQFwwPFgoSFhIRFhERFAwXGhISFhAPFA4PFQwUGhEQFRAN EQ0IEAwKEw8OFgoOFgoQEgcREwcSEgoWFg0MEwgNFAkQFQ8MEAoPEwsPFAwUFgsSFAoPEQgSFAoT FQoREwkLFAYNFgcRGg4QGQ0UFAkREQcQFAoPEwkNFAgPFgoOFggNFgcMFgUKEwMPEQQUFggWFgwT EwoQGQwQGQwVFwkUFggUFwoWGQwSFgoSFgoTFQsREwoKEwcNFgoVGg0SFwoWFgwVFQsUFgsQEggS FAoWFw0RGRELEwsQFQ0PEwsTFgYUFgcPFQoLEAYNEAYRFQoYFQoWEwkWFg0UFAsPEgcTFgsVGA4S FgsPFQoQFgoSFAoSFAoWEwsWEwsPFAkOEwgVEwgVEwgPFwoNFgkSFgsMDwYLEAYPFQoQFwsPFgoL DwgMEAkPFgoPFgsSEwwQEQoTFQoQEggPEAoXGBEWFgoUFAkTEgQREAMTFAUREgQTFA0PEAoPEAsR Eg0UFQ4UFQ4WFQwZFw8UFwwLDwQUFREPDwwRGxULFQ8RDwcXFg0WFQwbGRALEw0NFQ8XFwwVFQoZ ExAbFRIOFA8SGBMTGAsRFgoREwkUFgsREwoSFAoUFQ4VFg8WHQsQFgYPFgcQFgcQFQ0QFQ0REgsT FA0XFwUaGgcYFwoZGAoWFgQbGwggGAUbFAIfEwshFQ0oFgsrGQ4qHhEkGAwVGRUVGRUUFgwSFAoK DQcKDQcQFAoQFAoVFgwREwkZFw8ZFw8bEgwjGRMsHgsxIw8lHQogGAcpGwwuIBAvJQsqIActIA0w Iw8sJSAnIBsnGw4iFgohHhAgHQ8iHQ8iHQ8fGgsdGAobGwUdHQcqHgomGgcnIQ8tJxQvJAc5LQ5A Kg43IQc/JAdGKgxEJw1BJQs9JghBKgtBLQlELwo6LholGgkcFQsZEgkQEAcQEAcLDAEPDwMPDwMN DgIKDAMJCgIHBwAHBwAJBAAIBAAKBQALBgATFgsTFgsPFgcPFQYVFgwUFgsQEgkQEgkPFQwOFAsQ EQ4SEw8QEwsSFQ0RFQoPEwkPFg0PFQwNEwoQFg4NEwwKEAoPFg0PFg0UEw0WFQ8WFQcYFwoWGAgU FgcPFQoMEQcNEggMEQcTFgoPEgYNDwUUFgsOFgoQGQwOFAsQFg4UGQwMEQUSEgcQEAYPEQYREwcN EgYRFgoNEwoOFAsQFQYSFgcaFgQcGAYXGgoTFgYRFgoTGAsREwcUFgoTFgwUFw0TFQoWFw0SFwwT GA0VFxMTFhEQFwsTGg4WGA4ZGxAYGg4WFwsTFQkWFwsSGhQPFxEUFgwWFw4YGA0ZGQ4WGg8UFwwV FgwWFw0TGg8XHxMYGg8WFw0cIQ8WGwoVHA8WHRAVFg0aHBIZGhYbHBgfHBkfHBkWGxUUGBIWFwsX GQ0THRYRGxUTGg4WHhEPGAwRGg4TGg4RGAwWFhMXFhQVGA4SFgsZFREaFhIXGQ0WFwsXGxAWGQ8k GAstIRMiHgwnIxAdGQ8aFgwcHhEcHhEnHxcqIhotJBYrIhQiIBYfHRQeGwsnJBMqIBYrIRcqIhck HBIeGBEjHRYkHw8hHA0XGQ8WFw4fFxAqIhorJBUrJBUeHBAaGA0hHRIpJRksKBUsKBUdHA4bGgwg FhAiGBImIhYjHxQWHAoPFQQWHQgbIw0jIgojIgolIQ8hHQsiGw8iGw8pGwk0JhI4LBAvJAo6Jw48 KQ9HKhFOMBZMMxRBKgwtJxAfGQUWFQcWFQcPEAcNDwUPEQYREwcTFgcRFAYWDwcdFgwsGgc8KRNQ MhpTNBxFLgpAKgdMMxNONRVOMxNQNRVKNBFGMA4/NAw8MQpHMBVFLhM9Kw47KQw9Kw49Kw45KQQ0 JQE7KwU7KwU/KwU7JwM5Lgs9Mg9KMglPNwxaOQtcOw1YNwRcOgdiPRVeOhJVOA1bPRFcPRZRMw8/ LhYtHQgZGAkPDwEKCgELCwIQDgERDwIQDQUOCgMPDgEKCQAHBwAHCAAJBgAJBgAKCAAKCAASFgoR FQoSFwQSFwQVFwcWGQgSFw0QFgsPFw4PFg0TEQkZFw8WGxAXHRISFwwRFgsVGA0SFgoOFA0TGRIQ FxYPFhUQGBIQGBIVFxMUFhIXFgcaGQodGAobFgkWFhESEw4RGA0PFgsWFg8UFQ4RFwcXHgwRHA8S HQ8TFg4XGhIdHhgTFA8ZGQ8VFQsQFAkZHREPGAwRGg4RGA0SGQ4WGQgXGgkZGgwbHA4aHw0VGQgW Gg8WGg8TFQsWGA8XHBYVGRMUGxYWHRgSHBQPGREaFg8gHBQYFxMcGxYWHBEaIBUhJxghJxgWHBEW GxAYHhIYHhIWGxYYHRgdHBkYFxUdHA4eHQ8cFxQbFhMdGBYnIiAiHQ8iHQ8mIhYoJBgfIhAdIA8a GxQdHhYZHBcaHRgbIBsdIh0eHBMbGRAaGxQdHhYfIRYdHxUQGw4UHxEWGA4VFgwTGRISGBEaFhYX FBMTFBAREg8aFBEbFRIbFw0dGQ8gGgkpIxBAJgtJLhI9KwpBLw4uKBUpIxAzJhQ1KBZFLQ9PNxZO OhpBLhAyJREtIA00IgdFMRNMMhZNMxZNNBxAKRIzIQw3JA8yJBQsHg8nHAooHQs5IwtKMxk+MBs0 JxMrHwooHAg1IhBELxxGMhtDLxg0JxEsHwoxGgo/JxYzLBwpIhMeHwUZGgItJAkxKAw0KQg3Kwo5 Jw4yIQkwGwU7JQ1TMBNVMhVPNRBROBJcPBxePh5jPB9pQSRjQBpUMg89KhQoFgQcFgYWEAEUDwcR DAUODgYPDwcSFgcPFAUkEwE0Ig1OKw1UMBFeOR5eOR5cNxNjPRhnQyFhPRxkPx5lQB9ePQ9cOw1a OQpWNQdfOw9fOw9aNxRRLw5XNQ5TMQpMMQZQNQlWMw9YNRBWNQ5RMQpPMRdNLxZfOQtpQRJtQQxz RxBlQwtjQApvRxNrRBBiQRBiQRBdPApXNwdFMRU5JgsjIBkPDAcMDgQLDQQQDwASEAEWDwMZEQUW FAkODAMODQAODQAJBgAJBgAFBQAGBgANFg8PGBAWGBAXGhISGQsVHA4SGQ4RGA0LGREOHBQZHhYc IRgYHhIZHxMSHRAPGg4UIBYQHBMXGRYaHBkYGRwWFxoWGxYYHRgUHBQTGxMYHA8bHxEZGAkfHg4h JxoXHRESGBMSGBMiHBUhGxQlHw0lHw0iIRAgHw8lHxUlHxUiHBUiHBUqHBAnGQ4nJBQgHQ4QHQ0V IhEZHBYYGxUMGwwNHA0OHBQPHhYXHhUWHRQYHRgYHRgWHxgTGxUVGBYVGBYTGxMTGxMUHRARGg4Z HQkhJQ86JRM6JRMyLhY3MhovMRsqLBYfJhUhKBYdJQ8bIw0bHxEZHQ8gGhAgGhAgHgsgHgsrHg8t IBA+IhRJLB05KxU5KxU/JwxEKw8tJxQtJxQwKBYpIQ8tJxAwKhMnIhMhHA4oGwwrHg8yLRQ5Mxkx LRYnIw0TIBINGQwPEQoQEwsVFhQPEA4SEBEUEhMVEg4YFhETFQsVFg0gFgcgFgdGJAxiPSJoRRZq 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 OxleNRNeNRNcNA9hORJeOhlWMhNEKRo9IxU7KQ88KhBNKg9bNxljQSdnRSpkQyhaOR9NLxZJLBNF Kw1GLA5MNBZNNRdRMxtaOyJhPB5cOBpBMRI7Kw0mHQ8bEwcVEQoWEwsQDwoREAoWFQoXFgoREgQZ GgooGwkvIg85LQ43Kww+KxNDLxY7KhMyIgwxJQwuIgowJgwuJAo1Kgg+Mg9BMhA7LAs1Kg0sIQY3 JRIvHgwoIA8lHQwmHg0oIA8oIA8jGwotJQ8sJA8yJREyJRElJgwjJAonHg8dFQcgGw0lIBEsHw8p HA0vHxkqGhUnGxAkGA4QFgoNEgcNFAcNFAcSFQ8WGBIWGA8PEAcQEw0RFA4UFgwUFgwMDwUJDAMH CwAHCgAJBwALCQB+TCKDUCZ/USJ/USJzTxZ3UxlyUBppSBRvRRpyRxxzTCRqRB1vRhdySBluRRZv Rhd+ThyHViOMVyONWCSGVSSJWCeHVy2AUSh5TiB3TB51TyB+VyeATyWATyWGUzCATixwSihqRSNj QyNfPyBbQCJaPyFaQClWPSZWORhfQSBzTCV+Vi6OVy+HUCmGUDGGUDFqTjRXPCRGPTQ4LycfIyIg JCMhICQcGx8WGxccIh4WJhsWJRoVIBoQGxYXIBcbJBsWHhYZIhkbIR0XHRkoJRYvLB1PLBNhPCFn SSpeQSNRORdRORdjPhZuSB5dSClXQyRMMBRVORtUORZjRyJwTSVpRh9ySiZvSCR3USl0Tyd0TSZ6 UytzUS1vTipoSiNnSSJpRh5rSCB7UyB9VCGCUDB7SithQClVNR9hPipbOSVDOjEzKyMZGxAZGxAV GREVGREWGBQYGxYZGhYXGBUQGRYQGRYRFA4XGhQzJwxFOBpKNSJNOCQ8LQw4KQlMORtNOhxJORpP Ph9XRSFdSiZnTSdwVi9hTSVTPxlXNA9dOhRjPhZqRRtwRiN0SSZeQCpWOSNQNw9YPhZWQx5PPBg/ MhI6LQ48LxU8LxVQNBlMMBZHNBdBLxM0KA8vIwozIQ5DLxo9LxY8LhY+Lxw9Lhs0JyExJB40Jw85 KxM5LhY4LRY7LSc6LCZHKiZFKCQzIBI3IxUyJxYwJRQ+JxI/KBM9JBlBKB1HLRhILhkqJxYmIxMj HA4iGw0aFg8aFg8OFg4QGBAaGRYcGxgWFgshIRUnKBglJhYjJxEhJQ8rJBYtJhgkJhomKBwrKRwk IhYoHhYoHhYoJA4oJA4rJxQlIQ8oIA0oIA0jIxgeHhQSHQ8OGAsVGQocIRAeIBUcHhMbHRIWFw0a Fg4aFg4SFgoZHREYGA8VFQsXHQgXHQgWFwoZGgwiHxgeGxUdHRsbGxkSGQ4PFgoLFAcNFgkQFRAS FhIaHBIVFg0UFAoTEwoNFAkLEgcLDwMLDwMICwEHCgAHBwAJCABwQCJ7Sit6Tyh6Tyh3Tyd4UChw ThxyTx16Sht7TBx5TiJzSB15SB99TCJ7TSJ6TCF+UyaEWCuGVyOGVyOAYix7XSh6SCV6SCV/Tyx6 Sih1SSZ0SCVuRBdqQBVlQSNjPyFXPCZXPCZdPx9bPR1TPBlTPBlWPSZTOiNXNRFfPRdoRRZ3UyKJ VTKQWziLXjKCVitrVzFVQR5ALho3JRIdHhgbHBYWGBYZGxgbHhYVFw8THA8VHhAUGw8TGg4VIRkR HRYRFgsWHBAXGBUaGxclHxYkHhZAJgtTNxlQPyJJORxFLQ9JMRJQORFWPhZWQy1NOiU7KBQ/LBdR Oh9XPyRfPB1cORpcPSRiQylfQBtqSiRwSCpzSixcSTFMOiNWPCtUOilROB5XPSNjTy9aRidkRi9X OiRELSFELSFKNChBLCAyLx0lIhEgHhIfHREYGg8gIhYQHBEQHBEWGRUcHxoSJBQSJBQcHxodIBso JRY0MSIuLRssKxklJhYhIhM6Lhg1KhU5LRk0KRZDNBtFNx1IOBtKOh07MxYwKQ46KhxAMCJOLiBY OClGMB1DLRpBMh87LBk4Kxw5LB0yJho3Kh4pKx8cHhMgIBYqKh84KRUyJBAuJRgnHhIaHRYaHRYe HhQnJxweHhQkJBkgIBYjIxghJigWGhwkGw8uJRgnJxwnJxwlIyIsKikzIhMrGgwcGxYfHhkhHxUe HBIrIRkoHhYaGw0kJRYoIhknIRglFhkhExYcFQ0fFw8XFw8XFw8UGhMSGBEVFhAZGhUWFhEcGxYa FQ0gGhIjHRUgGhIcGQwYFgkWFxUXGRYXGhQPEgweDwocDgkPDAMPDAMOCgMNCgMVDwIVDwIICAEJ CQIKCgAKCgANCgAPDQAMCQMIBQADBAAGCAIKCwAKCwAIBwQIBwQGCAIEBwEHDAALEQMNFQQLEwMP FAQOEgMSEAgSEAgOEAINDwEKDQIKDQIKCQcLCggPDAcUEQsQDggPDQcLDQQICgEKCwELDQMIBwAH BQAEBgAHCABqPiF3SSt0TSZ1Tid1TCJ5TyV4Th54Th59UyJ7USF3TCVvRR9wRCdzRilyRiZ4TCt9 UCp3SiVySBtuRRhoSRtlRxloPhptQx5wRSJvRCFuQyVkOh1VNxFVNxFQPRpJNxVNNyNOOCRaNBxa NBxNOCBNOCBGMhY/LBBNMBJYOxtkQBxyTSd3Tix/VjN9WDF4VC1eTiVHOBI4LBgwJRIaIBUbIRYh IB0aGRYbFw0dGQ8aIRAZIA8VIBIVIBIRIBcRIBcRHA0THg8VGg8cIhYhIBsgHxo1LBxDOShEOCU4 LBpDMyI/MB8+MSJFOChDNyQ9MR80LRwyKxpIOiBBMxpJNCFJNCFBLxtINSFNOR9RPSNWOSNXOiQ8 MB46LhxENSw+MCc5KCBDMSlHPDQ9MitIMjNELi87KiU8KyY7MCgxJx8hJiEiJyIoIiIqJCQkIx4k Ix4THxYUIBYcHRcgIRsnKR4jJRotJysnISUpJyYrKSgjJSInKSYrKSwnJSg1LSUwKCAyKiItJR08 LB48LB43LiYzKyMnKBgoKRkrKyEvLyU5JSFDLiorLBwmJxcnIB0gGRYgFhQjGBYkGh8kGh8QFg4Q Fg4aFhIXEw8dFAsdFAsREQgSEgkPEwkSFgsUFAkPDwUMDQcWFg8REQcTEwgGEQMJFQYUFgsWGA4W GQobHg8dIRUiJhkoHxAgFwoYEwseGBAhGxMhGxMcGw0cGw0VGQgVGQgWGQ4SFgobDwoaDwoWEAoW EAoSFAgTFQkWFQoUEgcTEQcWFQoYFgkXFQgdFA0eFQ4hERUfDxMaFhIcFxQVFggPDwMOCgAOCgAJ BgAHBAAMBwAKBgAHBAAIBQAHBAAHBAAEBAADAwAFAwAFAwAGBAAHBQAEBwAEBwABBAACBgAFBQAE BAAHBQAEAwAFAwAGBAAGBwAHCAAKCQQPDggMDwcPEQoXFgkYFwoPEAMSEwQSEAcSEAcTEQkWFQwS EgcUFAkXFRATEAwREQkREQkPDwcODgUNCAIMBwEMCwAPDgFhOyZoQSxlRSVkRCRzRCB9TSh4TiR6 UCZ7VixzTiVlQSJbOBlXNSRePCpbPydeQypuRy9lPyhUOB5PMxpNMRhRNRxTOR9WPCJaQB9WPRxa PyNTOR1BNxxEOR5DOBtFOh1GOiNJPSZVOCRRNCFGNyhHOCk/MyE8MB49KxlDMB5TPChaQy5kSS5l Si9hRiteRClOQS44LBoxKyMxKyMhJxghJxghIxgfIRYfIBgjJBwZJBEbJhMcKBkcKBkcIx0fJiAf JxcfJxcgKRYjLBkvKyoxLSwxMiwyMy0xMScyMigzMykwMCYsLCIzMykjJh8hJB0cHh8lJygrKiMk IxwmJSIkIyAvJhcxKBkxKhI1LhYvIhA0JxUpJRkjHxQmJhwkJBofIh0aHRgqIhckHBItJBckGw8n JRssKiAvLCUuKyQXGxokKCcpJCIpJCIjJRogIhcZJRUWIREYGRQcHRciGxooISAyJR4tIBknJiEj Ih0hHRwjHx4tJCErIh8sKB4kIBYrJCMhGhkkGw8oHxMnIxcjHxQoIA8rIxEiIxElJhQiHxEiHxEP EwgKDQMREAIREAIPCgAUDgMLCQQHBAAHCgAKDQINDAALCgAJCgAHCAAGBgAGBgAEBAAGBgAEBgAE BQAEBAAEBAABBAADBQAFBwAGBwAHCgALDwEKCgMODgUYGhAXGQ8fGAohGgsbGQYfHQkhGAseFgkX FwwcHBAhHg4fHAwZFwwVEwgXFwwVFQocFg8XEgoWGQkXGgoaGg8XFwwXFQcaFwkaGA8aGA8eFg8k HBUfHBkfHBkdGxIXFg0QDwcNCwQKBgAIBAAEBwADBgAHBwAGBwAGBwAHCAAIBgAHBAACAwADAwAC AwAFBwAHCQAEBwACBAAGCQAEBAAEBAAGAwAGAwAJCgAHCAAFBQAICAANDQANDQAHCgEKDgQREwoW GA8gHAobFwcdGAocFwodFgkfGAoZGQcaGgcYGwoaHQwaGQkYFwcXEgkTDgUWEQYSDgMRCwARCwAO CAAPCQBENSJHOSVVPy9QOytaRCtaRCtdQCJbPiBaQSZTOyBONCRHLh4+Kx9DLyM9Lis9Lis9LyY5 KyIrJRwoIhklKRwpLSAvKyIqJh0vLxcwMBgmLBUdIw0iJCUnKSosKB81MSgvLSwuLCs4Mi47NTEz MyYyMiUsJyMtKCQtKR8tKR8xLBw5MyNENB88LRg3MBgyLBUgKhwVHhEWIhQbJxgaIhUeJhgWIBcX IhkWIxkYJRsTHRMXIhcbIxQbIxQcIRgcIRgdJxgWIBIaJBYbJRYiJR4bHhciKh0eJhkXJg8WJA4R FgoPFAcTEAoOCwYHDQgECgUHEAQEDgMKDgEJDQAJCgIHCAAKCgAODwENDAANDAAMBQAUDAEGCQEC BAAGBwAHCQEECAABBQAKCAAKCAALFgQJFAMNFgcMFQcRFQgXGw4aHxYdIhkaHQ4cHw8THQoSHAkg Hw8hIA8TFg8UFhAWFw4aHBIcGxUbGhQWHBYTGRQdFxkiHB4aGhEaGhEaGhAbGxEXGxAVGA4XGgka HQseIBMfIRQeHw4fIA8hIB0VFBEOEAMHCQADAwABAQAEAwAEAwADBAACAwADBQACBAABAgABAwAB AwABAwADBAADBAAEBQADBAADBAAEBQAABAAABAABBAABBAABBAABBAADBQADBgAKCAAKCAAHCgAL DwIPFgUPFgURGxETHRMYGg4aHA8aHQsbHgwTGxMVHRUZGgwaGw0aHA8bHRAWGQ8aHhMiGxgeFxUc HhQaHBITGRAVGxIWGg0XGw4YGRIYGRIdFg4fFw8ZGhUXGBMWFgoREQcKDQEGCAACBgAABAADCQAH DwAEDgAFDwENFwENFwEPEQMUFgcHFwkHFwkGGwEEGQAKHgALIAEGFwEMHwYQGgASHAEQHgkKFwQJ GwEKHQIHHAYHHAYOIggOIggIIAgIIAgLFgcKFAYUGAcWGggcFQ0XEAkWFgQYGAYVFAQSEQMWEwUZ FggRFQMVGAUSEQMQDwIKDQAKDQANDAAODQANCQAOCgAsNCcqMiUxNCsrLiU0MSIxLh8wLRQoJQ0w KREmHwkZGQ4VFQoiGhMeFg8ZFg4VEQoODgUPDwcSFQ0NDwgNDwgSFQ0WFAkQDwQVEgQWFAYVEgwQ DggSFBMVFhYPFg8PFg8VGBcWGRgWHhsaIh8tKR0tKR0qKSIhIBkiJxMgJREqJhovKx8rIxgxKR4r JxsjHxQgJBYdIRMcIBQeIhYcIhYbIRUaHhIbHxMYIhQYIhQWHRAaIhUeIBMfIRQfIhAdIA8UJxYP IREVHg8UHQ4dHxIZGw8mHhYbFA0KDwAHDAAECgABBwABAQAAAQADBAADBAACBAABBAABBgABBgAB AgABAwABAwABAgABBAABBAAAAwAAAwABBAAAAgAAAwABBQABBAABBAABBQAABAACAwAFBwAKDwEL EQIMGAoLFwoWGxUcIRohJBQdIBAZHxQZHxQhHxYkIhgbHhkcHxoiIxskJR0gHxofHhklJSMfHx0c IiIdIyMdHx4kJiUkIx4mJSAmIhklIRgoJBgmIhYoJhkoJhkgKBggKBgaIh0LEg4FCwACBwABBwAD CQAEBQADBAAECgADCQABCgABCgADBwAABAADBwAECgAHDAAFCgAGDwAGDwAKEAAKEAAGDwAGDwAC DQADDwACDAADDgEACQAFDwEJDgMKDwMDDAIJEwcMGAkKFgcTFRYYGhsWGxMUGBAUFgoZGw8SHQ4Q GwwYGg4aHA8aHRYYGxUXGxAYHBEbGxIZGRAXFw8WFg4VFgwWGA4XGQ8WFw0WFQ8aGRMVFxEVFxEW GQ8RFQoWFQUODQAEDQADCwAACgEBDQMFGwgMJA8LNSQNOCYWNyAWNyAUNB4XOSIKOSYKOCUKOSYK OSYNNRUQOhgNNSEQOiUaMhogOSAbPScZOyUPOR8UPyUPNyYMMyMUNCcUNCcPLRMQLxUNHA4NHA4S FQcUFggbFw8UEAgTEgQYFwgTEQcTEQcSFQcQEwUNDwEUFgcUDgEWEAMJDwEKEAMNDAALCgAPDQAR DwEaKyUIFhEOEQcKDQMHCwIECQAICgAHCgAHCQAEBwAEBQAEBQADBwABBQADBQACBAADAwADAwAB BAABAwAEBAAEBAACBAADBQABBQAAAQABAQADAwAAAQAAAwAAAwABBAADBAAEBgAGBwQODwsWGg0W Gg0ZIxUbJRYrKRMkIg0gIhUdHxIbJB0dJh8eIhQhJRYcJhYXIREfIBwgIR0WHxgXIBkhIxccHhMZ IRIXHxAcJhgWIBMgIBYbGxIeHRYgHxgYJB4YJB4ZIhkZIhkbJRcaJBYaGA8SEAcDCgAABQAAAwAA AwACAgACAgACBAACBAAAAgAAAQAABgAABQAAAAAAAQADAwAEBAAEBgAEBgAABAAABAAABwAABgAA BAAAAwAABAAABAACBwADCAABBQAABAACBgAGCgIMCgMXFgwYHBkeIh8cJx4aJRwUIxoaKiEbIhwd JB4hIh4jJCAeIiMhJSYiHxwgHRojJSIhIyAdJSQZISAdHx4iJCMhISMiIiQdIBshJB8ZJhwWIhgc HxggIxwkHxkeGRQPFg0DCAEDCQAABQAABQAEDAEBFAAFGQQIJQwIJQwFIwgFIwgHJQoDIAYGIQsQ LhYPKxULJhAKKBYPLhwNLRANLRAULhAXMhQLMRwJLhkHKBgLLR0RLBsPKhkLKRsRMCIPJhgLIhUW HxEPGAsZFBQcFhYVGA0RFQoRFgsRFgsRGgsOFggQFQ8MEAoQEQoWFxAXGQ8XGQ8VGA4VGA4VFhAU FQ8WEggWEggSFAoTFQsRFQoSFgoUFg8WGBAPFgsLEgcOEAIGCAAABwAABwAADwQLIRQKNCMNOCYL SDUNSjgRRzoWTT8XSjQWSDIZRjwWQTgTSToUSjsPSCsRTC4cPjQkRz0ZQzoWPjUeRzcaQzIaQTgc RDogPjUgPjUhNy8fNC0UMyESMR8NHBUKGRIOFggPFwkYHBAVGA0VGAsPEgYTEQgWFAoSFQcRFAYV FgcYGQocEAMZDgEREgUREgUNCwIJBwAKCwAMDQAMEgoHDAQGCQADBgABAQAAAAAAAAAAAAAAAgAA AwAABAAABAAAAAAAAAAAAwABAwABBAABAwAAAQAAAQAAAgAAAgAAAwAAAgAAAQAAAwAAAgAAAgAA AAABAgABBAADBQABAwABAwABBAAJDQcXFg0fHRQjJiEpLCcgKR4hKh8qMCMjKRwlLSAkLB8hKB4g Jx0cJRwjLCMhKi0eJyoTIRwZKCMlKiUmKyYkKSQbIBsgKCcgKCclJCgjIiYfJyYiKikeKSkgKysl Ki4dIiYdKB8XIhkJDwMECgAABwAABgAABQADCgAEDAEIEAQHGAQDEwADEgMDEwMAFQAGHQIHGgEB EgAGFwEHGAEKGwUKGwUHGQIMHwYIHgMEGQALIg4JHwsEHAMKIwcOIgcOIgcNGQwKFgoHEggEDgQK CgoMDAwQIBoWJiAgJx8gJx8VHxYWIBcWIRYYIxgdIR4dIR4gHh0fHRwmIR8jHhwlJxwhIxgZHA0f IhIlIhskIRofIBweHxseIhYdIRYZIxUXIRMcHxgbHhcVFA4MCwYEDAEABgAABQAABAABFQURKRYQ OSwTPC8PRzQMRDERPiwYRzQRSTEPRy8bTzcTRS0YRzkYRzkKQDQNRDgWSTQURzIPRCsTSC8PPzUO PjQKQDINRDUYRzkTQDITQDITQDISNCgIKBwMGw0KGAoPFA4RFg8SGQ0VHA8THQsOFwcSGQ4PFgoT FxEUGBIYFQodGQ8ZHxMWHBAWHRQSGA8TFBAYGRYWGQ4YHBAUGw8PFgoXFw4aGhAZGxEXGQ8UFw0M DwYJDwEECQAACgQBDAcDHRQOKyEMOi8PPjMPSkQRTUYaTU0aTU0VT0YVT0YXR0UaSkgYTEUaTkca VUQYU0EZPz0iSUciSUcdREEYUEAVTDwYTT4WSjwcST8aRz0YRzAWRS4WPCwWPS0YLykVKyURHhAT IBIYGRIWFg8aGhgXFxYdGBUdGBUVFQsWFg0XGAoZGgwYFAYWEQQPEgEPEgENDgIMDQELCgAKCQAH CwICBgADAwABAQACAQADAgAABAAAAwAAAwAABAAABAAABAAAAwABBgABBgAABAAABAAABAAABQAA BQAABgAABgABBgABBgACBwACBwADCwACCgAABQADCQAECgABBwAABwACCQAECQAHDAEYEQguJhsn KiMnKiMeLiMhMSYjLR8fKRsmKSIjJh8iKCYhJyUcICElKSogKy8dKCwiJyAjKCElKRoqLh8lLSAh KRwkKyMhKCAiJiUhJSQZJR8bJyEjKx0gKBodJh0dJh0YHhIQFgoACAAABwAADAAGFQIHJA4NLBUK MBENNBUUNRcVNxgLLhUPMxkLMSATOykUQC4HMB8SOCgSOCgTPC8QOSwQNSYWPCwTMyYUNCcVPjEP OCsNOh8SQCUXPi4TOSkaMSkVKyMPJRkDFgsKDgoIDAgIEQIZJBEkJxYjJhYjIh8gHxwaIRkYHxcc HRYjJBwkIxQgHxAbGw8fHxMmJBciIBQhIRcjIxknIhUmIRQeHxceHxciJBkgIhcZHRIdIRYcHhMg IhYVFwkOEAMDCgAABwAACgABDwADJhYPNyUPQDoXSkQQVUcPVEYTTjwUTz0NTj4PUUEbUT4ZTzwV UTsWVD0STkQOSD4XSkQaTkcTUzgRUDUWTT8SSDsVT0gSTEUZTkQWSkAWST0SRDgTOzQNMy0WIhoP GxQSGhITGxMSIhMUJBUbIRYXHRIcHhQbHRMaGxYYGRQZHRofIyAbHBYaGxYUHxsVIBwZIhcYIRYU IBYVIRcVIBoSHRcjIBskIRwjIRUhHxMWGw4KDwQJDwMJDwMAEgUFGQsHLCAPOCsQPT4YR0gTVk0U V04eV1gYUFEaUFEYTk8gT0UhUEYZU00WT0kWTUgWTEcWR0EWSEMbSUEbSUEWTkATSTwRTTkRTTkU SUEPRDwTRD0SQzwWPjAVPC4aMScbMigcJhYSGwwSFAoUFgsbFw0WEwkVFAUXFgcXFAsXFAsXFAsa Fg4YGAYVFQMJDwAJDwAPCgQPCgMMDgANDwADBwAAAgAABAADBwAFBwAFBwAADwACEgAHFwAHFwAK FgALGAAGFgAHFgAEEwACDwAEFgAGFwAHHQADFwAEEwIEEwIEFAEGFgMEFwAHGwAIHgIIHgIKGwQK GwQHHQYFGwQFFgICEQAGDQAIDwASDgEhHA0ZHhceIxwXJBwdKiIZJhMUIA4cJBcgKBsiKCQfJSEd IhsgJR4WIR8WIR8gHhQhHxUnJRsiIBYWHxIXIRQhJxsgJhokJCQkJCQcIBUhJRkgKBseJhkWIhYR HRIPEgQKDAAABQABCAADFwcTLBkKPjERRzoMSDAPTDMZSC8cTDIbTjoWSDQNQTcQRjsWU0MRTDwV RjcbTj4UTkQVT0UaT0UaT0UaSkYYSEQXUUUQSDwPSD4UTkQWST0OPzMTPzUQPDIQMSIDIBIHEgYD DgMHCwESFwoeIBUlJxspJCItKCYeHxcdHhYjISIjISIhKCAeJR0fIhsmKSIqKSQlJB8hIhoiIxsl Jh4jJBweJR0cIxskJSEjJCAlKCMlKCMeIxolKiEcKBoKFAgEDAEDCgAAEwIJHwsHMSIXRjUTVUwT VUwSWlEPVU0SU08XWlYQW1EPWlAQTEEUUEYRVEQWWkkeUVEVRkYXT08aU1MYU0QYU0QaTUkYSkcd T08ZSkoWSUQcUEoaTkoTRUEXQDoTOzQWLh4TKhoPHAwSHw8THhgQGxYZHxQdIxccGw0bGgwbHg0c Hw4dIxcbIRYaGhEWFg4RFhERFhESGQ4SGQ4RFxARFxASFw0UGQ8bGBIbGBIcGwsfHg4VGwsKDwIG DwAHDwEDFgoJHhEIMSQPOy0QREYWTE4RW08PV0wZVU4WUEkaUFEbUVMeUUocT0gWT0YWTkUXTD8X TD8eTUMYRjwYRzQbSjghUD0bSTcTR0YVSUgWUEcSTEMWST8VSD4UPzMSPTEZMiIULBwUIRMPHA8W FAoVEwoaEgUZEQQTEwgUFAkZFQ8WEg0SEAgWFQwaFAgWDwQMDwIOEAMRDgYNCgMPCgMPCgIABQAE CwAFHQMLJQkTMBUTMBUSNQ4PMgsbNxQZNBIWMxkWNBoSMxgZPCAUOSAPMhoSOBcTORgXPiASOBoM NyUNOCYMPSAKOx4RNxYTORgMOhkOPBsPOCENNR8QLyEJJhgNIhEIHAwHFgMEEgAJCwAKDQEODwgX GBEXFxYZGRcaJhcYJBYcIhYdIxchIyAhIyAfJiAcIx0ZIBYfJhwiIBYjIRYpJRwoJBsgKhwdJxkj JiEjJiEmJCMpJyYmJR4kIxwnJiMkIyAZIyAWHxwSGw8HDwQBCgALFgQHLR0ZRDISVUMQU0AOTkER U0YVV0oVV0oRV0QPVEAPVUEPVUEXVEETTjwPRj0UTEMRTEwUT08gWFEdVU4VV04QUUgXVEYUT0EW T0YWT0YPTkwKSEYPR0QKPzwQNycKLh8FGAgDFQUHCwQPFAwcJiUhKyolJiInKCQqKSIqKSIlKCMl KCMfKiEbJh0fJB0iJyAhJSIiJiMhJB8iJSAlKB8jJh0eJCAeJCAbJSAaJB8lJyYoKikeJyAZIhsS IA0DDgACBwAECQIDGQwQKxwYPTwiSEcPU08RVVEQV1YQV1YTU1gWVlwPU0wUWFEVV04XW1EaV1QW U08cUEkeU0waUU0YT0oXUUMbVkcbT0wXSkcZSEwXRkkXSUQdUEoUSUUMPzsXPz8XPz8TMygLKh8R HA8NFwoUGw0SGQsWGg0YHA8VFA4XFhAbFw0ZFgsRFwcWHQslHhEiGw8XGxAVGA4VFw8WGREUFhIV FxMdGQ8eGg8bFw0aFgwUDwcWEAkQFQYKDwEDDgMEDwQDFgoOJBYMPigWTDQTVE4UVU8SVlETV1Ma WFEVUUoTUUcTUUcXUVAXUVAbTkgfU00tTkcwUUpHW11NYWNTZ1NVaVVTaGNNYl03WlovUVEXTkYP RDwTRD0TRD0OOzUPPDcUNCkMKyATHxQZJhoZFQ8WEg0eFg8jGxMZGQ8VFQscGRUbGBQdGhYYFhEZ FQcdGAomGQcfEwMSEAEPDgAOBwASCwMBEgAHGgMNLREXOhwXPy8aQzIXRi8aSTIUUDwPSjcTRTkX Sj4PRCsMPycPRzAQSTIRSDcXUD4USj8OQzgVQToVQToWRC0YRzAaSDUZRzQPRTUOQzMKPzcNQzoP OzMKMywWLCQLIBgFFgMCEQAFCAAKDQIUDQQmHhQnJycpKSknKx8mKh4oJR4mIxwmKSQkJyImJyMn KCQhJSIlKSYoKSwmJyonKyonKyopMCgmLSUyKyoxKiklKSgmKikoKCgjIyMmLi0lLSweJiUYIB8K GAgDEAIAEgMIHgwKNywVRToXVEYcWkweVk8iW1QRVVEWW1cWXUkRV0QXXlQTWE4VXlMOVUkTU0QW VkcVVlAXWlQZV04XVUwaT0UaT0UVU0UTUEMWUUoUTkcPSE4STFEQRkYNQUEWQCsONyIPIQ8IGQgH DgUMFAobHx4iJiUhKiMhKiMhIxglJxwgJR4cIRoeJR0bIhodIRUgJBciJR4gIxweHxsgIR0iJBkh IxgiIBYmJBojJRohIxgVIRcbKB4WIBAQGQoSEwQHBwACCAEFDAQEHRQSLiQWQEUeSk8cV1caVVUW WlcYXFoXWlYVVlMWVVAcXFcWWFMWV1EPUEULSj8XUDwZUz4gVkUcUUAmUDkrVj4dUEoYSkUkSk8h R0wcSEUhTkocTUcWRT8WQz4YRUAXODAPLicPIQ8HGAcTHwsUIAwXHREWGw8gHhUgHhUcHRYYGRIV GwsZIA8aGhEeHhUeJRsXHhUgHxghIBkeHBIdGxEjHxYkIBYZHxMWHBAaGxQYGRIXGQ0ODwQDEgMH FgYHJBYPLR8MQzQYU0QSVFERU1AeUVEfU1MiVlAeUUwZU00bVU8ZWFQVU04oU088aWVaeYJujpd6 mad7mqiCmaaCmaZ1lqNvkJ1XhIZJdXcyaVggVEQeTUUcSkMPRz0NRTsXOy4PMCQXJx4YKB8YGxYW GRUYGxUZHBYZHREWGg8dFxAeGBEcFRYcFRYfEQooGRIjGwcaEwELDwALDwATCwIVDQMBGwAPLwwP QSQZTi8ZTkEeVEccTkMcTkMYU0YXUUURTUYVUUoTUz8TUz8LTDoOTz0QTEUWU0wRTUYPSUMWTUgS R0MTSToTSToVU0MRTj4OSD4LRTsRQEMSQUQQQDsKOTMSMicPLiMLFgwDDAMEDAAGDgEUFREeHxsl JisnKC0kLCcjKyYsKSYsKSYtLigqKyUgLCYjLykmLzAjLC0nJzEqKjQpLDEqLTIoLCkqLislLSoj KygjKiIgJx8nKSYmKCUqLi8fIyQVJBsOHBQGEgEDDgABHQwMLBkKRDITUD4TWEoWXE4VVEwWVk4W WFYYW1gdWk8cWE4SXVcPWlQXXVoRVVEVWEgTVkYTVk0WW1EQUUUWWEwYU0YWUEQSVEoRU0kVUE8N RkUPR0oTTVAPP0MPP0MaQzcOMygPIBADEgQIDQQKDwUWFhMeHxsZIyAVHhseIRggIxofJB8hJiEf IhkgIxocIhYcIhYZIhsaIxwkIyAfHhsmJBkkIhcmIhgmIhgdJSAeJiEVJxYQIhIXHgwQFgYKDAQH CQEECgQJDwgJJRsYOC0PTEcWVE8eV1gdVlcdXVgYV1MTVkoSVUkcVlAbVU8UVFUSUVMkUEMsWkxR aFdacF9Vd3Jae3dYfnJWe29Ocm9Hamg1aF8rXFQhUVMZSEkYT0oWTEcXRDwbSEATOzALMScJHw4E GQkPGQwYJBYbGxIeHhUgJBYbHxEeIRwaHRgZHBQdIBcWGRwaHSAeJRsdJBofIxceIhYgIBQdHREe HxsgIR0cIRgXHBQdHxQXGQ8PFQgGCgABEAAEFQEHIBgPKyMLQDsUTEYQT1USUVcZVU4bV1AaW1AZ Wk8cV1YdWFckVEwkVEw/X2hXeYJylayCpr6Zq8WXqsOSobiMmrKGm7CEmq+DjqJ4g5ZXdGo9WE8d UEoYSkUSSjoPRzcbPS8WOCoeKBobJRcaHBITFQsTFQkWFwsYFwoUEwYRDQEWEQQVEg4PDQkXDAUg FAwWEwIPDAAKCgEJCQANDQQLCwMGKhIROR8RRDoaT0UYU0QhXU4lXFEiWE4cVlAaVE4WWE8TVUwT VFEXWlcRVkgQVUcTVUwUVk0VUUoQTEUUTkUYVEoVUUoQTEUSU08PTkoPUUUNTkEMRzoPSj0RRzoR RzodODIXMSwPHxkGFA8EEgUBDQEIFAcSHxEmJyMjJCAgKCUgKCUmJyEmJyEmJSAoJyIoKh8lJxwr LSoqLCkiIiAkJCIjJikeISQeIh8jJyQeKyMfLCQrJhYsJxYjJxsaHhMbKBwTHxQTHQoLFQMDCwAC CgAFHhMWMiYNTEgYW1cRXEkPWkcsVkksVkk0T0Y7Vk03VlE0VE8jUU0pWFQcWFEWUUoaWE8gX1Ye Y1obX1YPXFEQXlQQUU4RU08TVkoSVUkXWlQWV1EmWFMoW1UVUE0QSkcbSkwWRUYVMR0EHQsHDwQF DQMSFhUXGxomKCkhIyQbJyEeKiQlLyojLSgjJh0kJx4gKSIiKyQiKiUhKSQlKSgkKCcoKSUrLCgo LiwlKyklKi4nLDAiMC8cKikmKTAiJSwNFhMHDwwEFAYDEgQGLiMWRDgSVVUVWFgeWlodWFgbXlwa XVsZXlMWWk4VV04WWlAkZF0oaWJXf3J0npCRoq6Wp7OXqsGVp76HqsGCpbt7oqx0mqVjjYhHb2oo Wl0YR0oPUE0PUE0XSkcXSkcPNDENMi8OIRgNIBcWIRYWIRYXFwwcHBAZIQsWHgkZGhYaGxcYGhcX GRYZGRkfHx8iJR4aHRYTGg8WHRElIRchHRQcGhEaGA8fHhcaGRMWHAoSGAcKEgMECwABDgAGFAQG IxoPLyYOR0YVUE8WW10XXF4XUFYYUVcaVloaVloXVFQZVlYvWk80X1Vba4B5i6GEm7yJocKQlaGH jJd+fXd+fXd1iYd1iYd+kKFvgJFed3hEW1wZUUYWTUEXTD8SRTkZQTEPNSYWJA8SIAsSGw8SGw8V GA0SFgoWFgkWFwoWFwoYGQsVFAcWFQcZEwcWEAUbFQEeFwMQDwEPDwEWDwcTDAQJOiQRRS4VT0YY VEoWVk4WVk4eTk8gUFEoUFEtVlcWWk0SVEcRTkwWVVMXVU4UUEkTWE4TWE4ZU0oWTkYUSj8XT0QS TEMQSUAQSUMTTUYWTkgRSEMOTUcRUUwYT0gSR0AYPjkTODINISQFFxoHEAQCCgAFCgMNEwofHx8f Hx8iJCUiJCUjJh8jJh8lKR0nKx8rJx4sKB8oKh8oKh8pKiYoKSUnKiUmKSQlLCQkKyMnKxwmKhsq JxYuKxkiLSQdKB8aKykXKCYXIhsPGBIJEAcHDgUGIhUWNygPT0wWWFUZY1saZFw6ZVRGc2FYeVtd fl9lgmtdeWNVcmhKZ10xXE8uWExTa2FjfXJhhoBOcm0aaGETXlcRV1sRV1sXVUwWU0kjWFUwaGRD Z3RMcH4mZ18WU0wWTUEJPDEPLSMCGxIKFBAHEQ4RGRwdJiktKyooJiUhKyogKikqMDAhJycoKicn KSYfJSMhJyUhJh8kKSIjKRwjKRwkLB8lLSAgLSMeKyElJiklJikdKCEWIRofIiUfIiUMExAFCwkA EQQEGAoIMSoYRj4SVVcYXV8cYmIXXFwUXmEVX2IVXVgVXVgWXFUaYVoyaFpRi3uGn7iWsMqmr9Sf qM2OpreLorOAnraDobh/nbR+m7Nvl6FReIAyaFoiVUcSV0oPU0YYTUAXTD8TQT8LODUPIREOHw8T HA8VHhAcGxYbGhYbIA8aHw8dHxQdHxQaHBIeIBYaGxcaGxcWHxIYIhUWHhITGg8fIRYfIRYaHhAW Gg0fHREeHBAPGBIPGBINFQQKEgIDDwMIFgkEJyEPNS8NSVYRT1wSW18YY2gbXlwZXFoeYWcZW2EU W14UW14iXFAyb2Nid5B4jaeGn6GHoaKSkIJ6eGpya1h3cF1vgnt+kYt7mZZui4hbd3JEXlobVlMS SkcWST0WST0cQzkVOjAZLB4PIBMUGQ8WHBEYGg4XGQ0bHRAZGw8fHg0dHAsaGwsjJBMlIhIiHw8j GwocFQUREQAREQAWEgURDQEJPi4TTDsXV00ZWk8WXFEWXFEjVFckVVg0TFM3TlUvVVAvVVAaVk0a Vk0eVEkhV00eVk8ZUEkcUEQZTUAgTkYiUEgcSkAeTUMUTUAWUEQxVEYuUEMXWkEZXEQYTUMXTEEW RDwRPTUMKicDHhsJEgkHDwcHCQAODwQWICAfKiosLzcwMzssMzQpMDEpLy0rMS8rKyswMDAqLisr LywsKzEwLzU3MjMxLS4zMi80MzAyLzQuKzAvLy0vLy0mMTEoMzMYMTMaMzUfKiMOFxEMDwQPEgYH KBYYPSoUUEcgX1Y4ZFVMemp0mZ6IrrORssmLq8KOp8GIobp/mqZrhpFUd3hcf4B+iZ6Un7SQorh5 i6FDeockV2MUW1wQVlcgW1UgW1U1X2FRfn9Veo1Veo05dIAhWGQSTkUKRDsSMSoDHhcHDwwJEQ8M Dw8ZHRwvLCUsKSImKSQhJB8mIygjICUnISUnISUiIiAiIiAmJR4pKCEmKSIkJyAYIh0bJSAZIhca IxgfIxYfIxYfKBYdJhQZIA4XHgwKEgYFDQIAEQQEGAoGLzMYR0wSWFoXX2EXY2MYZGQWZGEXZ2MW X2QTXGEdXVgiY15KbX5zl6qNp8GUrseao7aJkqVuiI1ngIZrg5l0jKJ7lKeAmaxzmaVYfYg3aGMs XFcTWEwRVkkWUUoSTEUTQTwNOjQPIgsQJA0RIBYPHhQfIRYkJhseHxseHxseIRofIhslIyQnJSYg ICIfHyEXHRkeJCAWIhwWIRsZIx4aJB8eJxwdJhseJRQdJBMcHRkaGxcPHg8KGQsGEgwGEgwEKyIN Ny0NSlARUFYPV1cWYWEfXF8dWl0YYWIXX2EaZF4ZY10dXlQscGVdeYt3lKaUqKibsLCmn7qZkqyQ laWXnaySo8eSo8eJoa5wh5RbdG5BWlQfV1MTSEQVTUQUTEMbPDQWNy8gLSEWIhYaFhQeGRcZGRAW Fg4eGw4fHA8bGgwdHA4ZGw8aHA8gHQ8eGw4aGgQWFgESDwIRDwETEAMRDwEPRz4WUUgcV0ofW04V WlUVWlUjXFUjXFU0XlRGcmducH13eYZvkZJrjY5hh4tcgoZRdHlJa3BHcGJGb2Fcb29ecnJYanRf cntefY1oh5dzh6Z1iahYhIZEbm8oX1AfVUYUSUEPRDwULykMJiAFDwYBCgEKCwEYGg4fJB8lKiUt KywsKispLS4oLC0nLC4oLS8qLi0qLi0mLisnLywkKCkmKissLi8qLC0sNS4jLCUlKSYrLywiLCsi LCskLCkiKicZKCUbKicaHBIREwoFDwQKFQkGKCMZQDsiVUcwZVdqh5uHpbqnt9muvuClvtiWr8mZ q8KXqsGHn7Z6kqhjl510qq+Vs8mfvtSMrNF5mbxajJs/b34xYl0uXlopVl8tW2RMeYBejZVWjJVR h5BFcnUhSU0hSEcdREMTNSkIKBwECwYIDwoKDgcTFw8mJBksKh8pKiQlJiAgHh8sKisuKCwsJioj JCkjJCkoJiknJSgmKCUlJyQlLSojKygkKCUoLCkqKycpKiYjLCUgKSIaJxsTHxQKEwcHDwQBEAMN Hw8GMTQWRkkNXl8TZ2gUZV0WaF8Vb2oUbmkWZ3AUY20kYW4oZXNQe5RvnbaLpbuLpbt9kpVid3lF aGNEZ2JAanNQe4RwkZ56m6h1m6NjiJA9a3UuW2QPVVQPVVQWTlQWTlQRQz8PPzwVLRcQKBMUIxoU IxoaIRkfJh4lIyIjISAhIyAgIh8fHiIiISUmJiYkJCQhJB8cHxoeIhYfIxccIB8ZHRwgIRseHxkj JCAbHBgkHRwkHRwQFQ0HCgQDDwoHFQ8HIR8aOTcQTE4YVlgVXloWYVwgX2IfXmEbYWEZXl4bX1wf ZGEwYVw6a2dneJCJm7Smssaqtsqmsseeqr+Qpr6Rp7+Inrh/la9/joxte3lKZWMxSkgZT04ZT04W T0kRSEMZOjIZOjIYJhcPHA8WFgwZGQ8VGA4SFgsUEw0UEw0WFgQcHAkUGAcQFQQWFQYWFQYSFgIQ FAEVDwESDQARDQMSDgMMTTsUV0UUXU4UXU4cYV4cYV4pZ1ozc2VUfXhtl5J+n6uIqraRrLyLprZ4 nqptkp5fjpJdjJBkiYJrkYl9lqWEnqx4l6t4l6uHmqyEl6p3mrJ0l69Yg4xEbXUnZ1gXVEYNRTwM RDsPMCgKKSELGgwCDwMFDQIMFQghIh4kJSEqLSYpLCUpKygoKicoKCgoKCggLCYhLSchKh8mLyQk KyMjKiIpLCcsLyoqKyMoKSEpKCEpKCEhKCIjKiQpKSciIiAZJRcYJBYYGQsPDwMHFgcIGAkNNTIW QD0vV1VOeXeDm7qWr86vttmqsNSQqrh9lqWMnrCSpbeXm7OSlq59na6Nrr+bts2euM+Ip8NzkaxH eX46am8qX2IkWFs6XFdBZF9qfY2AlKV+kp1whI5eeXdNZ2Q6amUsW1YaRD0HLCYDDQQDDQQHDQgW HBYlJyYrLSwqLCkoKicgJygkKywnKCsqKy4nLDAjKCwoLC0oLC0qLi0rLy4sLCorKyknKygnKygg MCcgMCcqKiooKCgWKCIPHxkJFgsEEAYCDwQNHRAKODsXSEwOYV0Wa2gVb2gUbmcUbmsTbWoWaGUW aGUwXmM4Z2tTeo51n7SLpbOIorBpjoxQdHI4aGMtXFclXmM6d3tfi5dwnaplnahRh5I5a3QoWGEP VVYPVFUQUEoOTUcRQTsMOzQPKh0IIRUPHA8UIRMgIhUiJBYeHRgfHhkfGxIdGRAbGxIcHBMWHxUW HhQWIBMYIhUfHxMdHREdHBYcGxUbHBUaGxQdHxUZGxEaGxcVFhINEAYICwIACgQHEw0BHxoOMSwV R0EoXlgkYlsjYVokY2IjYmEbYWMaX2ImZG0kYmopYm4xa3hUeJJyl7OSp8eUqMmao6qRmqF6iZl1 hJR1g4hzgIZye4llb31AZWs0WF4WUU4WUU4jTE8dRUgjOS8eMyoaIBUWHBEYGA8aGhEWGg8WGQ8V Fw8WGBAZGAkcGwsXGw4WGg0YGg8WFw0VFwcVFwcSDwQRDwMREAMTEgQRVUMXXUohX1EjYlQlZGEk Y18ral85em9bi4twoqKHprOLqreOpbqHnbJ7lJ14kJl5jZR+kpl7m5+IqKybqsWerMeNnbCAkKOE lZ17jJRqkZVrkpZUfoNGb3QwX1sjUEwVTUUPRT0HKiUHKiUIFw8EEgoEDwUGEAcNGhYUIh0YKh4c LiImKyYkKSQsKikrKSgjLSgiLCcgKhsjLR4mKyQmKyQrKR4tKyAnKx4oLB8kKSInLCUoLSYmKyQe NSsXLiQbLiAWKRsaJxIOGQcEGQsEGQsYOTEjRT0dYVtDjYeCo7yQssuiq76XobNzi5Fof4Z9kKGO orOQoaySo6+Im6yVqLqGrLiHrrp4naxUd4YkaWUfY188W19BYWVRbWpriIaEo66VtL+JrrqDp7N+ o6VwlZZfjZlFcHspRlcLJDMDDQQDDQQIFhMQIBwlKi4qLzMoMi8nMS4mLCwoLi4oKikoKikfLTAa KCsnKiUqLSgvLCUyLygnLiYnLiYqLygmKyQlLiMlLiMjKCMkKSQdKB0UHhQNGQsDDgIADwwHFxUI OzsRR0cPXl4WZ2cSb20RbmsSbWsVcG8UamEXb2UrbWUtb2hNfotwpbKNqLCJpaxhlpVAc3ItXGIr Wl8hXW0zc4Nni516n7JynrBVf5FFanMwVFwTVlYRVFQTWkgKTj0QPzQLOS4VJxkKGw8OGg0WIxUa IxoXIBcdJh0eJx4fHBYaFxEbHRMZGxEgHBMeGhEfGxAgHBEfHREgHhIdIBcaHRUeHRgdHBcbJRcZ IxYbHBgXGBUSHRAJEwcBFAcEGAoGISQSMDMUSkodVlYWY10XZF4XaGEZamMlXGQjWmIkX2UkX2Ux XHdEcIxbd5l3lLeSosaUo8eGoZd0joZ/h4R/h4SChoyHi5F5jaxugqFUf4A+aGkcV1QZVFAWREgR PkMUOTEQNC0SJRUPIRESGBEUGhMSFwoXHQ8eHgoeHgoZGxAdHxQhHxUfHRMeHBIdGxEYGhAWFw4b FAQXEAIZEgIYEQESRkgbUVQgWFEnYVofYmUlaW00ZWI/cm5XfXBljH9zkZ91lKJ9lKF1jJlvjpFy kZR4lJGIpaKXq7afs76dq8aVo76Al5Jwh4J0gHp3g31zg45+jppwkZpYeIAsaW8WT1USTUkPSUYP MzIGJyYHFBYACgwCCQcFDQoJHRkMIR0bLSkgMi4lLiMoMSYwNDMsMC8nMS4pMzAvMS4qLCkrMS8s MjAqMScpMCYkLygoMywsMC8oLCsmMi4nMy8iODAgNS4qMjsiKjIWJSAQHhkEFwgIHAwQLywhQz8h X1xIjYmCm6+XssaZq7yImqtngJBeeIdulJ+GrLiMo7OJobCCoauGpa97qrJ1o6twkJRVc3ciYWEl ZGQ7Y3JQeolwlbaLsNKZt9mWtNaUsNGat9iLqtV6mcNilrI7a4YiTVYILTUHDwYDCgIJFRcHEhUX IyEfKykdLyUVJhwdISAlKSgiKCYjKSciKSMiKSMhJh8mKyQoLRsrMB4hJxoiKBsnKR0kJhooKyYm KSQfKCEeJyAWGw4TGAsFEQAEDwAAEQwGGhUEOCoUTT4RX2cYaXATaXkRZ3cRaW4Wb3QXcGsWb2oo amktcG9Ue5VznbeEp7iEp7h5lZVbdXVJX2Q+VFhGWHNdcIxymad/p7aIorhuh51KdHs0XGMRVVAT V1MVVlQNTEkWQDwPOTQVJRUQIBAPHhYWJh0aKycTIx8bJSIcJiMhJh8hJh8gIxoeIRgcHSAjJCcm JSAhIBshIh4gIR0hJBsjJh0mJiYgICAfJyIcJB8TIRoVIxwYIBQSGQ4GFggEFQcCGyoSMEASTVYY VV4TY14YamUWaWkYa2sdY2McYmIkamEla2I5YXJAaXpheJV7lLKOobqZq8WWsMGWsMGZrrCZrrCX qMeSo8J9m7Rti6NGe2cwY08YWE4WVkwVSUwQREYTPjQMNSwPIhIMHg8TGAsSFwoVFwcYGwoeHgod HQoTFgoSFgkWFA8WEw8WFAoVEwoWFwcPDwENDgEODwEOEAEMDwAUTkQcWE4gYlcgYlcjam8ka3Ax aVo0bV1JdW9XhH5pi6F0lqx3laN1lKJ4kZ14kZ19maGOq7OhqryossWdr8GOobJ+kI53iId3kpaH o6eVp8GarMZ+naphfosraWgaVVQRTkkMR0MNPTEKOS0KHw8CFAUBDQYDDwcPGBgaJSUqMDooLjgj LSwkLi0qMC4mLConLCcsMSwsLi0qLCsqLCsqLCsmLScnLigpMS4lLSoqLyokKSQiLSQlMCciLycg LSUfLCQdKiIVIBAQGwwGFgYKGwoHLicaRz8mYmpKjJWHl7aWp8aNq8F7ma5nh5JbeoZolZ2CsLiO rriNrLeHq7iIrLp+qrd0n6xzjZlYcn0kYmopaHBAdJBTiKVwlbN4nbuEmrKQpr6frsmdq8Z6nqtX eYY7dG0oXlcdRTsFJx4FDAQHDwYJGBMPIBoYJR0kMSkhKygeKCUnJiMoJyQjKCEmKyQkKSAmKyIm KyQlKiMiKSMpMCoqLSYrLicrLSIsLiMqMjElLSwrLC8oKSwkKSQcIRwQGwwKFAYBFggGHg8GNCsW SkAVXWkcZ3MWa3IWa3ITa24UbW8hcnobanMrZ3I3dH9bf5p5n7uGq8GGq8GIoaeAmZ9/jod3hn51 jpCDnZ6Qss6StNF/prJnjJdBdG8wYVwVWlUWXFcYVVESTUkYQTkQOC8XIxYRHA8aHhIgJBcbJyMa JiIdJSAeJiEgJSAgJSAgJhodIxcgJBghJRkfIBghIhokIiMgHh8hHx4jISAhHhciHxgkJhseIBYd IRYaHhMSFw0NEggGEQMIFAUCGR0OKS0NREAZVFATY2UbbnAaamcba2gaZ2kZZWgoZG0saXI5Y248 Z3JMeIhnlaZ6mrCCoriEpbuHp76GpreDo7R4p7NunahihotOcHUtYmshVF0TV1QSVlMXTkkRRkEU NS0OLiYWIBIUHQ8ZGgoXGAkaGQkaGQkdGwkbGQcWFwoWFwoWFQ8TEgwfGQUhGwcTGAQPFQINDgIP EAQRFAQPEQMUTlQeW2EZZ18aaGEmaXIjZW4hZWMnbWpHd4ZVhpVni51wladwjptzkZ5/kpqGmaGN mayZpbinrsGss8aarr+RpbaSl7KQla+Np9SWsN2OrsqIp8NzlqNegI00amonW1sUVkoLSj8OPjAI NykJFwoBDQECDAEEDwQcIhYjKRwmKikpLSwnKSgnKSguLyspKiYqKyMsLSUrLCgrLCguKikvKyop LCcrLikoKyIkJx4mLiAlLR8oLB0rLyAkKSAiJx4cJxwbJhsYGwoXGgkIFwQWJxAMNy0bST8uX2hT iJGImrCXqsGQpr6EmrJuhoxfd314kZ+Rq7qOqriMp7aEp7iEp7h/pbp9ordolJ1Nd38tZXAxanU6 d3s/fYJNcnJUeXmGjZuQl6aSnaiJlJ9vhn5VamMrYlwfVE4cPzcIJx8DCQEKEQgPHBcaKSQtLzAw MjMwMDAxMTEwLCsvKyoqKigsLCoqLisrLywwMTkvMDgtLDAwLzMvLicyMSowMS0wMS0pLC8pLC80 LzQuKS4jLCEdJhsWHgkPFgMHFwQHFwQKLiwfSEYZXWkiaHQgbXQib3ccb3cbbnUpa34qbX8tbn0z dYRRepRynbeIqsaMrsqSs8qXuM+jtM6is82lu+aov+qbuuGQrtVvn6ZVg4k1bWknXFgbWFgbWFgb VVsUTFEWPS8PMyYcHhEaHA8eIBMiJBYiJBkgIhchIRcjIxkeHxkcHRccHRYgIRkeHBsfHRwgGxYi HRcmHxojHBchIRckJBodHBYdHBYkHhYhGxMcHRYVFg8TGAsRFgoHFAIHFAIDFxUMJCERPDgkU04Z YmoaY2shb28ea2seaG4faW8sZ2swa3A1Y3IwXWsua345eItPgodXi5BnjJZhhpBhg45df4tOgoBE d3VFcmI6ZVYjXGMaUVgQUE0UVVEaTUkSQz8aLisQIyAZGw8aHA8fIA8eHw8eGxUdGhQdGQ8ZFgsf IhIgIxMdHhYaGxQlHhslHhscHRcXGBMTDwMUDwMXEgMXEgMRUUwZXFYlZV4qa2QuY2owZW03aHo3 aHpDbYRQe5Rii6JpkqpvkJ1zlKGJlqGRnqidpbOmrrynt8uhsMWNp7eLpbSRobSIl6uHnbKEmq+I oaqGnqddjplJeYMra2sdW1scVk4TSkMmPjoUKiYRFAQLDgAHCwEMEQUWGw8aIBQpKR8rKyEuKi0t KSwwLCsxLSwzMCk0MSorMiooLycrKSguLCspKyorLSwsMjArMS8nLSErMSUvMCgwMSkxMTEsLCwl NC8gLyomLCAfJRkQIA8NHAsMKichQz8uWF9biZGNoa+ZrLufpb+eo76Ump2SmZurprq6tMmltMeV pbeMq7qJqLeHo76Gorxfl6xAdYkwaXIrY2spZ2cqaGg8ZWNHcm9wgpeClKp3lqxykadVg4c8aGsd VlYXT08VMCoAFhAHCgQHCwQTFxMkKSQzLy40MC8zLy4vKyosLisqLCksLyYsLyYpKSkrKyssKy8u LTEtLiorLCgoLSgqLyonJiMpKCUyLSsvKigtKSoqJicgKyAbJhsTHAcNFgIHEQQHEQQLJSMlQ0Ac VV8rZ3IZcHgacnkecHobbXckcIImc4QibYMibYNDeIxelqt0orZ6qLyCrsODr8WIr8aEq8KMq8eE o793mqZukZ1WhH5BbmgqXmIpXWEgYV8aWlgWVFMPTEoeNyoWLiIUGhUSGBMaHhsdIR4mIxUhHhAg HhQiIBYfHhkgHxolISAkIB8fIyQeIiMgHxojIh0mIRsmIRslIyQmJCUqJSMlIB4mICAmICAYIRoX IBkYISISGhsKGAoIFggBFQoLIhYYOzcgRD8hW1wjXV4dZ2oha28iaWsjam0iaWsmbnArYm8pX20o Y24rZ3I8aG9AbXRJaWtHZ2lKZGtNZ25EYm0+XGctXGIrWl8hVF8bTVgZUFUWTVEdREMQNDMdJyYW IB8VFQwbGxIcHBMcHBMcHBMbGxIdHAsiIQ8fIxYgJBckIBckIBcgHBQcGBAiGQsfFgkUDwMXEwUf FAIfFAIaTU0jV1cvZGQxZ2clamcpb2s1aHU5a3lNcoJTeIhhg51qjadpjpl0mqWGnqeOp7CassOh uMqvuM+ossmUp7aNoa99mZ5zjpR4i4d6jYmDjpWLlp1blZVNhoYmdGgWX1QjVlYbTU0bPzIMLSEI FQQBCwAFCgAQFgcaGxQtLiYnMy8oNDAvODcrMzIvMDMyMzcyMjAyMjApNS8nMy0sMC8rLy4uLi4x MTEtMjctMjctMi0sMSwvNC8vNC8rNTIpMzAqMi8oMC0uLycjJBwQIQoNHQcFLCUQOzMtU2FYgpGI m6yXq7yXr86dtNSrvNSzxdy0xeS2xuamt86On7Z+obKHqruCn8F3lLRMi545dYgqcHcjaG4YamoY amojY2ovcnlWfpdpkqxhg51WeJExeH4kaG4WW1YOUEwVKhsADwQHCwEKDwQRGxUbJh8oKicpKygo KicnKSYrKyspKSkkLB8kLB8jKiIkKyMpKCMrKiUpKiQpKiQqLCksLisqKycpKiYqKiwqKiwnKywm KismLB0hJxgQHxYKFw8EGgkCFgYDIxoUOS8RT1ogYm0Sc3kTdHoZc3oZc3ofdIYfdIYdan8fbYIv b4Y/gplagplii6JijpdijpdnjaFiiJtijJlchpJJfXtEd3UxaGssYmUWXWMVXGIWX1oUXVcXUEoS SUQhLCUXIhsWHRoWHRobJyUaJiQiKSEgJx8kJx4lKB8jKiQdJB4hJxsfJRkjJSIiJCEiJCMiJCMl JB8nJiEfIiUeISQmJyMlJiIlJSUkJCQgKicbJSIhJCcWGRwOGAsIEgYBEQUGGAsEKCITOzQUTlEh XmIhaGohaGofbW8icHMkb3MibXAdaXAYY2oWZWocbnMhbnUbZ24pYmQtZ2kpYWkkW2MeZGkWW18b U1cgWF0TVlsTVlsaU1QVTE0hPjgSLScPFg8QFg8WFwsXGQ0XGBMWFhEZFwwfHREeGgceGgcVGg0X HQ8bGw8XFwwdFAsdFAsbFgQbFgQVEwMWFQQaFgEZFQEWUVUcWFwcZGoeZ20Zc24eeXQ8aWdEcm9c e3hlhoJwjpt3laJ0l6N6nqqQn6+Xp7eoutaoutasvtWer8aRpbOLnqx+kpB5jYuAjYyNmpmRm7SX oruJnqZ1iZE7fn0kY2IWWlwOT1EPOS0FLCEEDAEBCAAHDQAOFAUYIB8oMC8qMi8qMi8sNzUoMjEq MDArMTEvMS4vMS4uOTgoMjEoLi4rMTEsLi8uMDEsKy8qKS0qLi0pLSwpKyoqLCsvMisrLicpMS4i KicqKyMeHxcPGwQNGQMEHhgTMCopT11IcoB6kquRqsOSrtSattyntuCyweunuuqZq9ttnrNfkKVt lKV9pbZwlLBcfpoqeo4fbYAcanUYZXAiaWska243ZHM8anlDeI5Jf5ZMcntBZ3AWbWkVamccW10L RUcMHwkBEQAIDQIQFgkeIRwmKSQoKisqLC0qLisqLismLCgmLCgoLSgpLiktLiorLCgwLi0uLCst LiouLystLzAwMjMsLi0rLSwrLDEtLjMnLjErMjUrMCcoLSQVLCQLIRkHGwoGGQkEGhAQKh8MPEgf VGEWc3oWc3oWc3gZeH0deH8deH8YeogXeYchb4Mkc4dBcntHeII9dXg+d3lGeoM/c3s9bnA/cHMn aWgoamklXW4jW2sbXWocXmsYZGISXFobTUESQTcXKSMNHRcWFxQaGxccIiAZHx0cIh4eJCAhIyAk JiMfJhweJRscJBUbIxQgIhciJBkhIh4kJSEeHxsgIR0PIhkNIBccIhYdIxciJhogJBgbHRMgIhco IhkdFw8RFgsJDgQBEAMEFAYDHhcSMSoNR04YVl0TYWIYaGkhanMia3Qka3Ajam8bbnAZa24fa3Uh bngZaGsYZ2oeaGkbZGUgXWEgXWEcZ14VXVUXW1gXW1gQVlwSWF4VUE0PSUYWNysNLCEQFwwUGw8d HBccGxYYGA8aGhEaHA8bHRAaHggZHQcaGhAZGQ8eGg8hHRIiGA8jGRAZGwgVFgQQEQMVFgYZGQQW FgEZTUclW1UcZWQdZ2UgZXIpcH1KdXRTfn1hjY5rmZp0mqV1m6Z6m6h9nquOpreSqruZr9SasNWO rseHpr+Em6iDmqd9l516lZqHobSUrsKMptaIotJoma5RgJUpb34aXWsaVV8QSFMOOCwGLSIFDQID CgAHCQAUFgojIigpKC4nLSkpLyssMjAnLSsnLSkoLioqListMS4uMi8qLisyLCw0Li4rLzArLzAt MTIpLS4oMC8mLi0lLjEjLC8mLCgqMCwgMCcbKyIjLicdKCEOJQgLIgYHIhUVMiQcSlcyZHJUe5dt lrOCmraIobyJob+HnryAm6t1kJ9bhotUfoNKgpFWjp5Kf4s+cn0tdIglan4fcnkWZ24cb28bbm4e aHMjbnkscnoscnova20uamsXaWcTY2EeTk8PPD0EFQEBDwAUFgsbHRIfJyQqMi8rLSwsLi0fLisi MS4lLi8oMTIvMDUuLzQqMTQpMDMsKzMqKTEsMDEsMDEuMDEvMTIuLi4vLy8mLTAqMTQsMTgoLTMh LzIhLzIQKiEMJRwKGQcHFgQEFg0IGxELMzUaRkgUX28baXkZc30Zc30WeX0Ye38ZfokWeYQbeYcY dYMmcHcmcHchd3chd3cgcnAidHMbb20dcm8Wa3IUaG4WXW8WXG4RXm0TYW8WXmIQVloVQzIJMyQP GxAKFQoUGhEXHhUWHhcYIRolIyImJCMjJRklJxsmKB0fIRYeJhYhKRkcJhgfKRskJx4iJRwdHhge HxkRIBQXJxoiKBsgJhkmJhklJRghIA8kIxEhKBUXHgwaIBQUGQ4KGAoHFQcFFxQMIBwLMzkbR00P W2EWY2kaZHAcZ3MkaHAkaHAWbXQWbXQabXQXaXAWaG8XaXAiZ2ogZGgfYm0aXGcYYmEWXl0YXWIW W18bV10XU1gdRkQUOzkXLR4PIxURFhEaHxoiIBYgHhUfIBgfIBgYHxYcIxkYHRUYHRUcGhkeHBsj Ih8lJCElHhskHRogHgscGggXGgMWGQMWFgMVFQIZRjwmVUojZVopbWEyaW0/eHtXhJBYhpFjjp5n kqJtlZ5tlZ5pkZpqkpt5mrZ6m7eAn75+nbttmaZolKFti5Vti5Vvkq9zlrN5ncWApc2Anbp3kq9Q foI9aW0daGMUXFcbV10QSU8RMCICHA8FDQMDCgAPEQcdHxQoKCgrKysqMCwtMy8uMi8wNDE0NS8y My0xNSk0OSw1Mi00MSw3Li86MTIuMjExNTQwNTotMjcuLjkzMz4uOD0oMTcuMTQxNDgeNDQZLy8e LSglNC8WMBYQKhAMKRUQLhkWQEYpVlw+cINJfZBQgJJYiZtiiZBfh41Ygo5Od4NPdIRMcIA4eIg3 d4czbn8xa30hbXkhbXkebnIXZWkYcHAac3MganMibXUdcHMWZ2kWY2kYZ20WZ2sPXmMgQToIJR4D DQECDAEPFAwZHhYlKx8pLyMrLicuMSofMCodLigpLy0oLiwtLy4qLCspLSonKygmLCgqMCwvLy0v Ly0nLSEoLiIpLCUtMCknLSsoLiwtMi0pLikpLikkKSQZLB4VJxkOGgQPGwUDEgcCEAUGKCMWPTgT WmIganMYb3kbc30Xen4afoIbfn4afX0deX4adXoedH0ccnoWbnoZc38gbXcgbXcYbnIYbnISbW4R a20UY2oUY2oUZ3ITZXAgV2MVSVUXOy4GJRkQGw4PGQwdIRMhJRYnKiMkJyAnJycmJiYmKSAnKiEp KiYlJiIiJyIkKSQZKSAaKiElKCMiJSAlIRgpJRweKBocJhgcIRoeIxwrJRorJRojIhElJBMhKiMZ IhsbIRUZHxMTHBcMFRAEFxAJHhYHKB8QNCsPUFAXXFwdZW4eZ28nZ3MnZ3MWbXkWbXkYb2sYb2sa bW0WaGgbZWcaZGUWZGgSXmIVW2UUWmQYW2EaXWMkV1oZSk0iOzIWLSUPGAoMFQcWFhQcHBoeHBse HBseHRgbGhYbHRMcHhQfHRwdGxobGhcbGhccHhEbHRAhGxMeGBAbGAoZFggVFgIWFwMbEwYaEgUS RDUiV0goYl0zb2opb3M3f4NMgpRKgJJQe4RWgotXgpRUfpBJgI1RiZZXiJ9XiJ9fh5Fcg41OhpBE eoRTeX9TeX9Wf45dh5ZeiZZch5RafX5Qc3QxaGgtY2MYZGQQWloYUUkPRT0SJRUBDwMDCwAGDwAP GhUbJyEmLS4tNDUwMTQxMjUwMzkuMTcyNzgwNDU3NTI5ODQ0NDQzMzMwMyo0OC4wOTUtNTIqNTgo MzUtLjMwMTcyNDUyNDUzNDo0NTskMzkhMDUlLicqMywfLBYWIg0NHw8PIhISKyonQ0EkYmoqaXI1 eHg+goJPf4lNfYc7fo0vcH87d4g6dYcqdX4qdX4qbnklaHMYbnQXbXMbbXIXaG0Zam8ecHUibXMi bXMfcG0WZGEdaGIXYVsWZGgMVloWLSUADwkBBwAECgEKEwsWIRgkKh4pLyMtLigpKiQlMCkiLSYs LCoqKigpLCUqLSYnLyolLSgiLCkkLissLygrLicsLyYsLyYpLCMvMiktMTIoLC0wLi8tKywqKyMp KiIhMSQZKRwTHRMWIRYJGQoEFAUCGBYRLCoKR0gXWFoRZGgacHQccnofdX4Yd34ZeH8bd3kbd3kZ dHcXcnQccn0ccn0ia3seZ3cWa28Wam4TZ2oTZ2oWZGoYZ20caG8YY2okTVMTOT4TJBoHFg0MGxYR IRseIRgiJRwoKisoKislKyckKiYnKiUmKSQrJyYqJiUqJyIpJiEiKicgKCUiKyIgKSAkJhojJRkk JyIgIx4gJBggJBglJhQlJhQlJh4iIxslJiIfIBweIhYbHxMQGAgMFAQEEA8EEA8CFQoGGg8JOjwR RUcWWGkcX3AdZHIeZXMYbXoZbnsXcGsWb2oXbW4TZ2gWZGoZaG4WZWgTYmQVWmkVWmkZV14bWmEj UE0VPzwWKg8JGwMPEAUSFAgWFg0XFw8aHQ4fIhIfHg0dHAsbGQ8gHhQjHBseFxYcHBIbGxEdGwkg HgseGwwbGAoZGgoVFgYWFgMZGQUiFA8eEAsXQTQiTkAhXV0raWkkdXsqfYMxe4A1gIYsenste308 eIk7d4g5eYc6eogmgoImgoI+fX44dXcycn0wb3pFbndGb3hFcHJGcnNFcnk+anIxbXItaG0fa2cf a2cWZ2QTY2EdTkkMOTQKFg8ACAMECwAJEQQUIBUdKh4kMjMnNTcvMCwzNDAxNDguMTQvNTUtMzM0 My4zMi0sMSotMisrMCsvNC8oOjEiMyshMykjNSssMSwtMi0nMSwjLSgwMDAvLy8sMy0tNC4oOjEe LychJiESFhIKGAgMGwoUHhcpNC0bVFQmYWEvbns0dII6go04f4sggIsdfYceg4wZfYYZd3obeX0h cngbanAbcHkWanMVdG8VdG8Za2sZa2sabWgcb2ohcGobaWMob3cdYmkcVF0PQ0wLHQ4ACQADCgAH DwQNHxwXKygoKzAqLTIrMiwnLigsLyotMCssLissLiswLSgvLCcsMy0sMy0pMTArMzIrMS0tMy8u LycvMCgsLCo0NDIsLzIsLzI0MDEvKywpLSwsMC8nMjInMjIjLicaJR4RGxEKEwoFFA8FFA8DLyYT RTsPXV8WZ2keaXgjb34heYkedYYZdXsbeH4VeHkTdXcYcHMXb3Idbnoba3gXaW4XaW4XaGoZam0e Z28aYmolXGcXTFYYMyoNJh0KFgoKFgkMHBUTJBwVJxYZLBodKCEeKSIkKBsfIxYeJg8eJg8pJBYn IhUoJRUqJxYqKB0pJxwiKSEgJx8hJh8hJh8jHBkkHRofIRYiJBklJRojIxgjIxgoKB0nIxchHRIb Iw0eJg8XHgwRFwcKEgcEDAIECgQJEAkCHA8NKx0PQUQZT1ESWF4WXWMXaW4Za3AWangXa3kba3oY aHcWZGgWZGgWZGgWZGggX2QfXmMWSlYQRE8dOioNJxgQGQQMFQEWFw0ZGxAfGxMeGhIiJRMhJBIg HxAfHg8aGhEhIRcoIhkmIBckIhYkIhYiHRAiHRAbHRIXGQ8XHQgWGwcXFgcYFwgcFg0aFQsZMSko QTkoVVsuXGIkcnsod4Awen0xe34reHsqd3oveH8tdX0pdX4nc3sneYImeIAleXsjd3kjeHggdHQ7 ZXU+aXkobnQobnQtbXUoZ28pZG8oY24cZ2EcZ2EbYWMVWFsdQDwMLCgJEQUCCQACDAEJFQcXIRQg KhwfLSwiMC8tLiowMS0xMiwyMy0xMzIvMTAsMSotMissMiUsMiUtLyQvMSYyMSoxMCktMikvNCsy NycxNSYrNC0rNC00MjM0MjMmNyslNSomOC8eLyckLisbJSIdHRsaGhgTIhkgMCcXPjkmT0knX2sx a3glfYgmfokleIQoe4gzfYctdX8een0ZdHcgcHQebnIccn0ZbnkTdG0Sc2sXbW4Ybm8cbmcdb2gf cG8fcG8ncn0XXmkUOzkFKCYEEAEBDAAHEQUUHxEYKSMeLykoLCssMC8pMikpMiksLSkvMCwwMDAx MTEvMzArLywvMTAxMzIwMDAxMTExMTExMTEwMC4vLy0pKSszMzUwLTItKi8uLysuLystLS0qKiop LDEoKzAeLSgZKCMPGBIKEw0IEAMJEQQCGQ8PKh4LPkcVSlQVX3MbaHsZc30Wb3kXc34beIMWeXoX e30Xd3cXd3cpbncjZ28fZG4jaXMiaG8fZGsrZ3QgWmcjSkwUOToOHQ8KGQsLFhAQGxYWIxkcKR8c KBceKhkkKBwlKR0iKBwdIxcjJBUlJhYrJRwpIxopJhUsKRcnJB0nJB0kJR0lJh4mJR4mJR4lIxgk IhcfJBsjKB8mJhskJBkqJxgqJxgkKh4eJBgoJhIqKBQkJA8gIAwSHQ8MFgoLEAcHDAMDEQQHFggE JRwKLSQKPDUUSUMPVFMTWlgWX2sZY28WYm8WYm8TY2UTY2UdX2UaXGIpU1YcREcVMiwLJyEMHAcK GgYPFgcRFwgWGg8bHxQhIxceIBUoJhknJRgiJBkbHRMcHBAhIRUpIRYpIRYrIxgpIRYoIhklHxYg IhYcHhMZHAoZHAoaFgoaFgoWGAwUFgoSJyMfNTElTE0tVVYgY2ctc3cvfYAreHsoeHgoeHgifnsi fnsidXggc3Usa30vb4ApdXknc3codHgpdXkdbXUdbXUacHcacHciaG8hZ24dZ20bZGocYmQeZGcu VWEbP0oaLisKGxgCCgEABgAGDgMRGg4gJBgnKx8mMSonMisyMjIyMjItNC4sMy06OTM3NTAuNDIt MzExNy8yODA0NS04OTA6OTU4NzMzOzIuNS05Oy47PTA5ODQ6OTU5OTs6Ojw1OTMzNzEuOjIlMCkm ODUeLy0iJiUgJCMXLCMWKiELMScTOzAkUVctXGItbnovcH0mc4IqeIckdXsneX8id34ecnkWdXUX d3cbdHkZcncUdW4UdW4Xb3QWbnMjbXMlb3UXcncZdHkiX2gPSFAMHQsACwABBwAIDwMUGQ4cIhYk KCksMDEnKygpLSopLiUqLyYoKyYuMSwwLi0tKyosMSwpLiksKygyMS40NDIwMC4qLyopLikxLSwx LSwvMTAtLy4nLS0nLS01Mi8zMC0vMCgqKyMnKyomKikaHh0aHh0WIRQPGQ0KEwAKEgADDwkEEQoF Iy0PLzoNRVcVT2IQXWkWZXITcnkScHgUc3oYeYAYeHgaenoub3kub3kmcHslb3oca3IWZGohU2MU Q1MbLx0PIhEPFQoPFQoVGBccIB8jKiQlLCYnKx8mKh4pKR8rKyElKSgjJyYhKSQlLSgrLCglJiIi LB4lLyEoKSMjJB4mKSIqLSYpJyYnJSQnKx8iJhonKCQpKiYlKx4kKh0nKR4oKh8nLCcjKCMmJhko KBssKh0qKBslJxwYGhAMFgwHEAcHCwIECQABCgMBCgMCEw8JHBgGKB8NMSgLP0APRUYNRUgPR0oO SEoLRUcUPUYPNz8jLykWIhwMGQUHFAEGCwAKEAMNEwwWHBUUIRMSHxEWJhQWJhQgIxMkJxYcHRcZ GhUfHREcGg8kHBUlHRYqHx0nHBoiGhIlHRUcHw0YGwohGgUiGwYaFgoWEgcWFgQaGgcYKxkeMR8d PjsnSUYaW2UiZG8hdHshdHsodXkmc3cogHglfXQkeXclenghdHshdHsjb3AlcnMkb24mcnAeb24h c3Idb3IfcnQbcnUWa28dbXMca3IfaG4bY2kbSVQKND4MFxAABwEABAADCQAUHBYmLygvMzA0OTU1 ODQ4Ojc4ODg4ODg3OTg0NzU7Ozs6Ojo0OD0yNTsxOTwwODszNDg4OTw4OTw0NTk4ODU0NDIzODkw NDU1OTwzNzo4Nz84Nz85OTs6Ojw1MDU5MzkyMDwqKDMvKSkpIyMUHhQSHBIGGhMIHRYMNDccSEof Wl8raG4ibnolcn4vd4Yvd4Yie4YZcHoceHgbd3cYeHgYeHgYd3MZeHQhbnghbngnaXkjZHQfa3MV XmUUOTEFJh8BBQACBgAHCwESFwoiHhMrJxsnKSYrLSomLCwnLS0qLSYsLygsLyYsLyYtLiovMCwn LyEpMSMxMiwvMCorMiosMysrMiotNCwxLzAvLS4uLi4uLi4xLzIxLzIzMCkxLicuMSotMCkuLysu LyspKiYpKiYmLyQeJxwLGQ4KFwwOFRAGDAgDDg0IFBMFIxsKKiIKQEQVT1MQW2URXGcaYXceZXsa a3AcbnMWa3sUaHgVY3IUYnAbVFQVTEwXOTgJJyYMGgQKGAMLEQoSGBEbHhcjJh8nKSgpKyonKSol JygoKCopKSsmKicnKygdKyocKikmKCUgIh8kJR8mJyEpJxosKh0qKSQqKSQoKCYnJyUjJSYiJCUk JicmKCklJCEjIh8iIx0jJB4eIxwfJB0jJCAiIx8hISEgICAjHRMZFAodGxEZFw4RFgQLDwAHCwAG CgACCgABCQADCgAGDgMCDwgBDQcDFgwGGxADGgsCGAoFEw4CDwoKCwAPDwIKCwAICQAMDgQPEQcW GxAWHBEdHREbGw8eIQ8cHw0cGwsaGQoZHRIYHBEdHRMgIBYnGw8rHxIgHhUeHBMkHBQjGxMeGwse GwsiHAofGQgiFgohFgkfGQUeGAQiMCAlMyMhOTIlPTcXT1ogWmQjZ3ImanUed3MgeXUjdXckd3gd eIIdeIIdc3sab3gdbm0gcnAgbm4hb28fc3MgdHQhbnUfa3MYbnQYbnQcanUaaHMrZW8cVF0ZPDgG JCAECQAABAAECQAPFAkeISgoKzIwMTcyMzkwMTczNDo0NzUxMzI0NDQyMjIyMjIzMzMyOC4yOC4x NTcxNTc0MjU3NDg6OTMyMSwyMjIyMjIsLzItMDMyODItMi0vNy4sMysvLy8wMDAtLjMtLjMtLS8r Ky0nJiMkIyAgHRYeGxUWGhIYHRUJJCIUMS8VP0EeSk0aW2UhY24hb4QgboMhdH4idX8keYAjeH8j eH8id34YdXkbeX0fcnsdb3kkbX0WXGscTlAKNzkHEwcFEAQCBgAIDQQPGxAYJRkoKyYmKSQoLCks MC0rMTEtMzMxMiwxMiwtMi0vNC8wMi8yNDErMiwrMiwwMy4tMCsuLiwxMS8wMC4xMS8xLzIyMDMw MjMwMjMxMS8wMC4zMTAwLi0sNDMrMzItLjEyMzcuMSwwMy4oKy4mKSwTJyARJR4QGBIFDAcEDgEE DgEDFAAFFwEEGw8LJRgKNDkMODwOOkwVQ1USTVYSTVYPTVoPTVoRQUUNPD8SLx8KJRYLGgoDEAII EQIKFAQVFhYdHx4cIxseJR0hJSQhJSQkJhslJxwpKCEqKSIoKB4nJx0jJyQkKCUoKB0jIxgoJSAp JiEmIhknIxonIhUqJRcmJyEhIhwgIx4hJB8nJiElJB8mJBkkIhclIxglIxgmJBknJRoqJR8rJiAf Kh8WIRYZIQsbIw0jIQ4ZFwYJFwkHFgcPCQUNBwQGCAAHCgAGBwAGBwACBwACBwAGCgAHCwAECAAE CAAHCwAHCwALDgELDgEMDQoTFBAUFw0YHBEfHhceHRYgHhQiIBYnJREjIQ4kIxEjIhAdHxQfIRYj IhMiIRIiHBInIRYnIRYoIhcgIxEgIxEgIhYdHxQfHA8fHA8iGw8hGg4fFwYfFwYiKCQmLCghMiwf MCoPPzkYSkQiVVwsYWgfcHIidHUpdXcpdXcjdYIidIAecHocbngacnAfeHccdXgZcnQjcHoib3ke cnIdcHAWcHMXcnQgaXcdZXMtVFsYPEMSIxYACgECAgAEBAAKDgsXGxgiJiUrLy4uLi4yMjIpNDQp NDQuMTkvMjo0MDM1MTQsMC0tMS4tMi0xNzEuMjExNTQzMjc0Mzg4NzMxMC0sNS4rNC0wMC4yMjAz OS8uMyozOTEtMistMTIsMDEkNDIjMzEwMS0yMy8hMSQiMiUqKCkgHh8eHhwbGxkHGhAMIBYOKR8T LyUMPkAWTE4YYWseaHMiaoAiaoAbb4IccIMidIMgcoAWc4AYdYMZbnsUZ3QdVVoNQEUTKh4CFQoE BgAEBQAECgcTGhcTISccKzEoLTEoLTEsLzQwMzkwMzczNzotNTItNTIsNDErMzAoNSsnNComMi4n My8qNTUpNDQwLjEwLjEyLi8zLzAwMy4vMi0qMi0qMi0qMCwvNTEuMC0rLSooNDImMjAnLywqMi8n LywlLSorLCgrLCgbJh8dKCEWGw8NEgcIFAAFEAADCQABBwADCAAFCwACEwcEFgkDFg8FGBEGIxoH JRwJIxcKJRkMHgoHFwQIFQMEDwALEgAKEAALEgcSGQ4cHBwhISEkJiMmKCUlKSYkKCUfJRkjKR0t KR8uKiAsKSIqJyAjKCMhJiEkJBomJhwsKSIrKCEjIxgnJxwrJRoqJBkoJR4nJB0mJiYmJiYoJSAs KSQpKR4mJhsnKCAmJx8nJiMqKSYtKx4rKRwdMCIZLB4gKhkhKxolJhUjJBMWHxwRGhcYFBwWERkR EwkUFgsPFQoJDgQGCgMGCgMKCgEKCgEKCgAODwMREwcREwcPFQoPFQoWHBEeJBgcIhYfJRkkJhsl JxwjIBsmIx4nIxkmIhgqJSMqJSMeHh4gICAjJRkiJBglHRUpIRgpHhopHhoiIxQiIxQdHREdHREf GQgfGQgcGQocGQoYFgQYFgQbIhgcIxkSJBYLHA8HMhcPPSEWRkQbTEkdX2UhZGocdHMfeHcsc4Iq cH8idH4idH4fcHofcHoadXgbd3kcbnAdb3IZc24bdXAecncbbnMcXmcPTVUbMCkGFxEABwAABAAC BgAGCgAQHBUZJh4nKywrLzAvODQvODQqOjQrOzUzODcxNTQ9Nzk9NzkzNTIyNDEwNDMxNTQ0NzUz NTQzNDw0NT03OD04OT4uOjwoMzUtODIqNC8yODIyODI3OTozNTc1NzwzNDowNTwyOD44Nz04Nz0o NDInMzEmLS4sMzQeLSgYJyIKFhIGEg4DEggDEggDHhEMKhwMOjERQDgSSU0XUFQQV1gSWlsRWGQN U14PTVoMSFUOOUMHLzkLHA0EFAYCCQAABAADBQAICwIMExAdJSIgJywkKzAnLSsrMS8rMjMqMTIo LCsrLy4kNyohMycrMCkpLicnMSwnMSwlMS0mMi4rMS8rMS8rMS8pLy0vLicsKyQlMSsmMiwjMish MCkrLikrLiktMikqLyYqMyoiKyIhLSkhLSkgLi8gLi8oLSgsMSwfLCQeKyMjJRoaHBIZGAkcGwsM FgoEDQMKEgIHDgADCgADCgABCwAACgABCwABCwAHCwAHCgAHDwAKEgIQGQQNFgITGAsWGw4WHxwb JSIjJBwnKCAlKiUmKyYtKSgrJyYiKCQkKiYvKyorJyYpKiYrLCghKyghKygkKCcmKikoJyAoJyAn KiMoKyQpJyYqKCctKicvLCkpKiIoKSEnJyUmJiQmJx8mJx8mKSApLCMmKyYjKCMpKCMoJyIjMCgf LCQhLCUeKSIlJiInKCQiJSgZHB8XHBcWGhYMGAMQHQcQFwsKEAUODwYQEggODwQODwQKDgQTFgwT FgwRFQoRFwcXHgwiIRAiIRAeIhYcIBUeIBMeIBMgGxcnIh4lIRglIRgmGxcoHRkeHhUgIBYeHBAd Gw8fFxAkHBUjGxEoIBYkIBYhHRQcFg8YEwsfGQgcFgYZFQcXEwYWEQQZFQcnKhklKBcUHxEUHxES LhoaOCMPOi4QPDAPTk8VVVYPaW0Ub3Mdbn0gcoAscIYtcocidIcjdYgce4MbeoIbc3ofeH8nc3ki bXMgaXIVW2MUODUCIB4ECgAABAAABgAEDQAHGAQOIQohMTEnODgyOjsvNzgzPDcxOjQwPDQwPDQ3 PDc0OjQ7OTw4NTkwPD4vOz0zOTsxNzk3OjQzNzEtMjcwNTo7OkM4Nz8yOUEwNz8xOTovNzgzODkx NTcyNTkyNTkwODsuNTknLzgrMzw3OD8yMzsmMC8mMC8oKCosLC4UKRoUKRoMFwcLFgYKEwcIEAUE FQMDEgEKGgoPIA8NKBwNKBwIMSQIMSQLMC4JLSsHJBkDHhQEDwMBCgACCgABCAAECAAGCgAKEAYT Gg8bHRwnKSglKiwoLS8lMSsmMiwrNTIoMi8uLiwuLiwqMCwoLionMSwlLyotLS0wMDAwMjEwMjEv MTAvMTArNTQoMjEzNTIxMzAsMC0uMi8sNC8oMCsrMS0pLyszNDAzNDAwNDUsMDEoNDAnMy8zNTQy NDMwMS0zNDAtLiorLCgrMCciJx4jIRcpJx0YIxwSHBYPIBMKGw8GFAYGFAYKEgcHDwQJDQALDwEQ EAcQEAcUGQUYHgkbHBUcHRYhJSQeIiEbJyMiLioqLSgoKyYhKygiLCkpKDAjIiooKikoKikkKCcj JyYnKSYrLSooKiklJyYlJyQkJiMqJhwsKB4qKh0pKRwmJSInJiMrJiQtKCYmJx8mJx8qJCYpIyUo IR4pIh8rJx4tKSAqKB0oJhslIRgiHhYkJhggIhUjJRomKB0nIxokIBcWIRoUHhcSIw0UJQ8WHQgS GQUUGQ8SFw0RFwcRFwcZFAwZFAwWFwsYGg4WFxQcHRkbIA4dIg8kIxQjIhMdIxUaIBIaHRUeIRgg IBQlJRgmIRsjHhgjHhokHxslIRclIRcnIBMlHhEiGhImHhYpIhMlHg8jIhEhIA8dHRMaGhAiHBIf GQ8eFAUcEgQfFAcfFAcrODMjLysuMC0rLSohLSshLSsRMSgRMSgLOjoQQEAMSVEPTlYWVGUgYXMh YnojZH0gb30hcH4ccn8Xa3kUc30VdH4gYm0TUVwXPEMGJiwGFAYACQAAAwAAAwAABwAIEwMWIhgk MScqLzEvNDczNzwvMjgsNzMrNTIxOy8xOy8vNC0zOTE0OTotMTItMzMuNDQuOTgsNzUqOzEnOC4y MSwzMi0wOTguNzUuNDQvNTUxMzIyNDMzMTA3NDMyMjAyMjAwMC4zMzEqMTInLi8xMDQuLTEvLy8u Li4qKSYrKickLh8gKhsdIw0ZHwoVGg8SFwwSFQcSFQcHFgMCDwADEQAFFAEEEgUDDwMBDwIADQEC CQABCAAFBwAHCQABCwACDQAKEAEWHAoSIx0YKiQmKisrLzAoMi8nMS4pNS8sOTIyMjIzMzM5MDM1 LTAtMDMxNDgxMTMwMDIwMTcvMDUwMjMvMTI0NDQzMzM3NzcwMDAwNDUvMzQxMzQyNDUvNTMrMS80 MjM0MjMzMzMwMDAsLzcvMjotNTQqMjEyMy8xMi4uLysvMCwsMSwsMSwnNColMigoMSoiKyQYJiUb KSgSJSYPIiMIFg4IFg4WFg8UFQ4HEQQLFgcPGhMNGBEPHA8UIRMVHBsWHh0kKCcgJCMiJCEnKSYp JyYpJyYnKiUnKiUoJy0kIykjKSUkKiYhJBslKB8oJSIrKCUsJyUsJyUrJiAqJR8rJhgrJhgjJxoi JhkfJRkeJBgsKB4tKR8nJRsmJBopIxopIxosKB4qJhwqKB0qKB0kKBsmKh0qJxgoJRYvKBowKRsp KRwpKRwmKBwlJxsmJhsmJhsXIxUeKhskJRQgIRAjGxQlHRYZHAoeIQ8jHRUgGhIhIA8oJxYiIB8l IyIoJSAkIRwrJBYsJRckJRQmJxYpJRwrJx4jIxgiIhcjJRokJhslJiIjJCAnJCEkIR4oJBsnIxop IRYqIhcqIxQoIRIkIxIhIA8hIxcfIRYlHxUjHRMjGAkhFgchFgofFAgzN0cyNUYpMkQlLj8XKygT JiMQJhwJHRQFIBYHIhgIJhsRMSYOPDMURDsXR0MbTEcaTkgaTkgZSVAQPkURQUULOj0PJiAHGxYI DQMBBAABBAABAwAABAAECQAGFgcOHw8kLSYwOjI3OC81Ny4yNDMxMzIyNDUxMzQzMzEyMjAuMyox Ny03NzQxMS8tMzMtMzMqNzIoNDAtOTEsODAtNC4vNzAvNC8xNzExNC8xNC81NDE3NTI6OTU8Ozg3 OTUzNTIxOTAyOjE1OTM0ODI7Ozs6OjomOzgjODQsOTQnMy8jNS8bLSciKyAmLyQjLSogKicXIRwY Ih0PHwoMHAcPFwMPFwMIEwMIEwMJEAEJEAENDwMNDwMHEgQPHAwTHhoWIR0iLS8nMjQeMjMkOToq MTItNDUwMi8zNTIwNzQxODUzMDU4NDo6NDo1MDUrNDgvOTwzMzMzMzMxMDcyMTgxMzQsLi8zMjc0 Mzg1MTA0MC8zNDAzNDA0NzMzNTIoMCsoMCsxMS8yMjAvMissLygtLjMsLTIqLi0rLy4uMSouMSov LCUxLicwMSkuLycmNCQmNCQrMCcrMCceKCUgKiciIiQlJScbJxkYJBYhJxgfJRYPIREOIBAZIhkc JRwjJxonKx4nKSYmKCUgKyQhLCUoKh4nKR0qKiAqKiArLiUsLyYrLCYuLykrLCYpKiQlJiAnKCIt KCYtKCYoKSUoKSUrKiMtLCUyLCEtJxwlKRwmKh0mKSAoKyIqKh8qKh8pKx0pKx0xLCYxLCYtLCcq KSQtKR0uKh4oKxooKxokKSIkKSIoJiUnJSQiJyAlKiMoKSMnKCImJR4nJh8hJh0kKSAlJh4nKCAh Ih4hIh4lKCMhJB8hIyAeIB0kJBcpKRwoJSIoJSIiIRwhIBsqJxYrKBcjKhgdJBMkIRMoJRYyIx0y Ix0iIxskJR0nIBsuJyIpJh8kIRonIh4mIR0qHhMsIBUrIhYqIRUjIxYeHhIlIhIoJRUuHQ0qGQog FQgjFwojFhEhFQ83OTg0NzUiNTsgMzkXJyUTIiAUHhQPGQ8JGRAHFg4MGAoMGAoHFQoNGw8OJRIP JhMQKQ8SKxERJA0MHggEEwkADQQEBwEAAwABAgAAAQACBQAGCgAGEQUQHQ8bJyMkMCwwNDU3Ozw9 Ojc8OTU4ODg0NDQ3NTI6OTU3Nzc3Nzc3OTU4OjczODcvMzI4ODg5OTk3NzQ3NzQwODEyOjMtODQq NDExMTE3Nzc3ODs5Oj0xNDw0OD88O0E8O0E4P0MvNzo4OTw4OTw7PD8zNDg5Oj09PkE1N0g0NUco MjopMzsiOjEiOjEnMjIrNzcyOjsrMjMkLishKygWKSYPIR4ZHx0ZHx0WHxwPGBYWGxAXHRIZFw8c GhESHRkVIBwWKykZLy0hLzAkMjMkMzkkMzkoNDAmMi4yMy8zNDAxMzIzNTQxMDQxMDQuMTQpLC8s MC8xNTQ0NS80NS81MTA3MjEzMzUyMjQtMTAvMzIzMTI0MjMyMC8xLy4yMzcxMjUvMTAxMzIxMTE1 NTUxMzIvMTAsMTMtMjQsMC8pLSwuMSovMisuLSowLywsMi4nLSkiLigjLyksNDEqMi8sMSgrMCcq KiwsLC4mLSMnLiQhNCMbLh0fKycfKyckKSQmKyYtLSAwMCMuMC0sLisnLS0rMTEoMSonMCktLSsu LiwqLC0qLC0tLCcvLikyLjEyLjEpKi0pKi0tLS0rKysnKSonKSomKikrLy4tLigrLCYnLCUnLCUn KygnKygmKCUnKSYtKyouLCsqKiorKyswKiwyLC4sLCIrKyElKRwmKh0pKiItLiYpJygmJCUlJyQk JiMmJyMjJCArJiIsJyMmJyEnKCIjKCEiJyAjJSIeIB0kJR0hIhoeIxocIRgoJhwoJhwmIR8mIR8j IhMmJRYmJRQnJhUgJBcfIxYfIBEeHxAlIBMlIBMnIxknIxknIBsmHxoqIRwlHBcnIRYkHhQrIA8r IA8oHhUqIBYhIRUfHxMlHQwoIA8rGAsnFQgbFQkaFAgfEwsgFAw7OjQ0My4oNS0oNS0iMikdLSQj KiIlLCQKGhUGFQ8VHA8SGQ0MEgIMEgIKFAAHEQALEgAJDwAGBwAEBgAEBAAEBAACBwACBwAJDQAK DgAOFggcJhYfLikkMy40NzU5Ozo6Oz46Oz47PD83ODs3OTg3OTg8PDo7Ozk3OTo3OTo6Nzw7OD03 OUU3OUU+O0A6Nzw3Oj8zNzwvMzQyNzgvNDctMjQxMTE1NTU1Ojs1OjsuNTcyOjs1OjsxNTc0OD0y NTs5NTs4NDoyNT0tMDg0NDcyMjQ3MTc7NTsyMTo1ND0qNTUoMzMrLjUtMDgwMTcyMzkgMCchMSgY KBsUIxYfJRkeJBgfIBwXGBUcIRweIx4fIBwiIx8sLSkmJyMhMiohMiooMi8sNzMmNTAmNTAsMy0s My0zMTA0MjEsMDErLzApMDEqMTItMTIrLzAvLy0xMS8wMS0yMy8yMjQxMTMxMzQ0NzgwMzcuMTQv MzIwNDMyNDMzNTQzNTQyNDM0MjM3NDU3Nzk1NTgxMjU1NzopOTErOzM0OToyNzg1ODcwMjExMjoy MzsqMTcqMTctMzEsMjAtNDorMjgxMTEpKSkqMywsNS4nLSknLSknMy8mMi4hLzIhLzIlKC0pLDEq Li0oLCsxLzAyMDEqLSgrLikpMCooLykuLC8wLjEsKS4qJywrKSowLi80LC8zKy4oKSMoKSMtLios LSktJyksJigqKigtLSsmKyYjKCMnKCAoKSEmKiclKSYiKCYkKiguJSQvJiUqKSIqKSItJiEtJiEm KB0mKB0hKRsgKBoqKBsrKRwuKCorJSckJBokJBopKBYiIRAqJRUpJBQjHg8lIBEjIhMjIhMeJRQd JBMkHxIlIBMjJxghJRYnJBYnJBYpIxgoIhcnIhUoIxYoIxYoIxYoJhsmJBkgIRkiIxsiIRAiIRAn IxcnIxclHxckHhYmHhYlHRUoIhkoIhkrIxgrIxgpIxojHRUnJB8jIBsjGxEoIBYfGRIeGBEdFgoZ EwcZEAgcEwpBPjlBPjk6PDk+QD0xOzwsNTckNDIqOzkWMy8TMCwiMysZKiIeIRoeIRoWHxwUHRoK EAMJDwELEgUQFwoLEwIPFwURHA8RHA8dISAjJyYsLyo0ODI5ODw3NTo8Oj0/PUA1PDoyOTcwPTsu Ozk0NzM4Ojc3NTo5ODwyOjszOzw4Ojs1ODkvNTUwNzc1PT40PD0tOjUoNDA0MTc1MjgxMzIyNDMx NTcvMzQxMzAyNDEzOjUwNzIqOjIpOTEyMy8zNDAyMjIwMDAtLy4uMC8vMjUpLC8sMjAwNzQzNy8x NC0xMygzNSo5MjQ6MzUzNDAxMi4nNSYpOCgyMysuLycsKSYuKygoKyYmKSQgMSseLykgKysnMjI1 NDkuLTE1NTgzMzU0Ojw1Oz0rNDgrNDg5NDU6NTc3MjM4MzQ0NTsyMzksNTksNTkzNDgyMzcyMzkz NDoyMTo3NT4wMzkxNDozNTIzNTI0NDc0NDctOjgoNDIvNTU1PDwuNzUwOTgtMTAwNDM3NzcxMTEv NTUvNTUuNzEpMSwzOjU0OzczMzExMS8uMTQtMDMtMy8uNDAyMysxMiovMCovMCowMSsrLCYrMCsp LikpKSksLCwtLCkpKCUoLyUoLyUoLSYnLCUbMCkdMistKSotKSonKiEqLSQmLCgmLCgsJyMsJyMv KyIvKyIrKBctKhkqKRksKxslKiElKiEvLCcvLCcuKygrKCUoJyIlJB8lJiIqKycrKCMrKCMnKR0o Kh4qLSQnKiEqJBwrJR0yKB4vJRspKCMsKyYoKCYoKCYlLSAhKRwlJRopKR4wJCYwJCYhIxgnKR4m LRsiKRclKhgkKRcjIxkkJBomIyAlIh8jJiEjJiEqIyAsJSIoKyQpLCUoKBsmJhkoJyApKCEnJhYn JhYmJBcpJxosJBksJBknJRsoJhwtKR8sKB4sKCcoJCMjJB4jJB4kIBUjHxQoJBgpJRkoKBsoKBso JSAoJSApJRwmIhknJRgmJBceIBMXGQ0fGgwfGgwbGQcbGQc+QD9BREM/QT4+QD09PEA8Oz8xNzkx NzkrODUqNzQpMTAmLi0hMCscKyYiIx8fIBwQIQsQIQsMIxEMIxETIhYaKh0eKCcgKikpMS4qMi8t LjU1Nz4zNTI3OTU3NzQ4ODU1NTM1NTM3OC8zNCwwNDMuMjEzMzM3NzczNzEzNzEzODQyNzMwNzQv NTMrODMsOTQ1NzI1NzI6MzU5MjQtMS40OTUvODQwOTU6Nzw7OD07PDU4OTIyOTcxODUxNzkyODo0 OTgxNTQ4NTcyMDEwNDEuMi8qNzIvPDguOzQsOTIzOTM3PDc3OzoyNzU3PzwxOjczPzUuOjA8ODk5 NDU0ODI0ODItODQvOjcnNzErOzUyN0AxNT8wOj8tNzwxNzkxNzkvOTowOjsvNzgwODk0NTs0NTs3 NTo0Mzg0NTkxMjU0MzgzMjcuNDIsMjAvNDkwNToxMjU0NTkuMTkxNDwxNTQxNTQxMzA0NzMxMi4y My8wNTgwNTgvLy8zMzMrMTEwNzcuOzcqNzIrNC0rNC0tODIqNC8xOToyOjs1MzIzMTAtNCwrMiov MCguLycxLiszMC0xMiovMCg0MSoyLyguLyswMS0vKig0Ly01MisxLicvKx8xLSEwMyosLyYgMCUi MicsLSksLSkqKycrLCguLSYrKiMvLCcwLSgyMCYvLSMtLSMvLyUsLiItLyMsLyYtMCcwLiQwLiQs MiYoLiIoLyckKyMpKiQvMCoyLi8xLS4uMCUsLiMuLiMsLCEvLCkvLCkyLi8xLS4qLiEvMyYrLSIp KyAqLiEqLiEtKhsxLh80LSoxKicpKyAsLiMsLiIrLSEsMCMnKx4iKSMkKyUnJyUkJCIlJxwkJhso JyApKCEoJyAqKSIkKh4hJxsjJxgkKBknKR4iJBkjIxYlJRgoJhkoJhklJRglJRgpJxwmJBkhIxgj JRonJB8mIx4uIhYrHxQnIRgnIRgnJRgnJRgdIRYhJRkmIBcnIRgmIRIhHA4jHQojHQokHRAiGw8a GwoYGQk4QD04QD08Pjs8Pjs3ODM3ODMtMikxNy0wMy4uMSwoMSomLygiMikhMSgqJyAqJyAnLBgn LBgYKB8WJh0VKyUYLykpLC8mKSwoLSYvNC0zLzI8ODs8PDA5OS07NzU+Ojk5Ozg3OTU+NzM+NzM/ PDc/PDczOzI1PTQ3NTI6OTU9PT06Ojo8ODk7NzgsNzUtODc4ODg7Ozs/OTs9Nzk3Ozw5PT4zPT4z PT4xPUEvOz88PDw9PT00OD8yNT0yO0QxOkM7NTs7NTs5OzwzNTc0OzkwNzQvNzoyOj0uOjouOjoz OjouNDQuOj4wPEA1Oz0xNzk4OzUxNC8/Mzs9MTk6Pj0yNzUfNzIlPTksNTctNzgrOj0sOz4qOzsp OjotMzMtMzMwNDUtMTItMTAzODcyNDUxMzQxMDQzMjcwNDUsMDExMTEzMzMtMS4rLyw1MzQ1MzQz MzMzMzMyMjIwMDAyMjIzMzMxMS83NzQ3MjE4MzI0NDc1NTgwLjE3NDgwMjMzNTcvOjQtODIuNDAu NDA3NzQzMzExNzkuMzUyNDUzNTcrNTArNTAsNS4qMywxLzAvLS4wNDEsMC0wMCYzMykvMTAxMzI1 LzE3MDI4MzQ9OTo3Myw0MSowMywuMSorMiorMiotLiouLystKy4uLC8vLy8tLS0uLi4vLy8tKSoy Li83MDI3MDIwMDAvLy8pLy0rMS8tMCkuMSooMywnMissMSooLSYqLyoqLyomKiknKyoqLSQpLCMo MiMkLh8tLywsLisrKykpKScuLCIxLyUuLCEtKyAoLCApLSEtJx4vKSAyKCAwJh4wLCAtKR0oKB0o KB0oKxojJhYZJRcaJhgiIRomJR4mJhsjIxgjIxkmJhwsIyAtJCEoJxcmJRYmIxUlIhQqIBgoHhYn JRolIxgiJBggIhYmJBcoJhkoIRIoIRIjIBIkIRMqIxYoIRQsIhgnHRQjIBIlIhQfIRYcHhMeHxce HxclHRYrIxsfHRQXFg0eFwkiGwwhGg4eFwsYFgcaFwg5ODw0Mzg6Ozc8PTk/OzA7Nyw0NDIwMC41 MC45MzE4Oi4wMictMSQyNykrOS4pNyw9Oy09Oy0/PDlAPToqPDoqPDozNTI8PjtMRTtMRTtaRD9b RUBkTEFrU0hiUUVhUERbVEpaU0ldTUldTUlrWk1tW05jVlFeUU1KSU5HRko8P0c0OD81PUA0PD8q QT0pQDw0OTo3Ozw3OUQyND8xOjk1Pj00Oj4yODw0OD8zNz46Nzw6Nzw4Nz0zMjk0NEMzM0E0Lzkz Ljg4NDoxLjMxNTIuMi8tMjQpLjAqMzQrNDUuNzMrMzAwNDMxNTQjOTMhNzEnODUnODUtMDguMTkw NzQtMzErLC8xMjUxMDcyMTgxNTQuMjExNzEyODIsNDEtNTIrODMoNDAyMjA5OTcxMzIzNTQyNDMy NDMwODktNDU1NTg3NzkvNTEtMy80NDczMzU0NzU0NzU4ODg4ODg1NTU0NDQyMTU0Mzg5Nzg0MjM0 NDI1NTMyMTU1NDkzMjk0MzooOjUnOTQuNzMtNTIzMTI4NTc0ODswMzcxNDwxNDwuNTkwODspMjMm LzAzMzMwMDAyMjAzMzErMTEpLy8xMDQwLzMzMTI0MjMxNDwvMjorLywrLywtMCkvMisuMC0tLyws LissLisrKjAtLDIrKSwrKSwrLicqLSYtLCkuLSokLi0mMC8oKzAkJywnKSgoKikqJSEwKycpLSom KicnKSYnKSYpKiYqKycnKiMoKyQpKiIoKSEoLxsmLRkqKyMsLSUvLSIqKB0tKCIsJyEoKSMpKiQm LCAmLCAqJSEpJCAqKB0qKB0uKiAtKR8kJR0jJBwtKBotKBonJxomJhkoJhkoJhknJRsiIBYjHhol IBwuJSIuJSInJB8pJiEoJhwlIxkpJB4kHxkoIx8nIh4oJCMmIiEkICMlISQqJR8tKCIlJxwjJRok JBkmJhspKiYjJCAiKBkhJxghJBQkJxYlJSMnJyUlHhsrJCEiJBYeIBMfGxMZFg4YEwodFw4cFwob FglORENRR0ZWSkFWSkFYSEVdTUldUFhTRk5WQ0xdSVNlT0lkTkhRTUFRTUFTTk1PSklWUD5dV0Vo WkxoWkxfXVBcWk1dWkhiXk1rWlBuXFNzXFR0XVV7Y1F7Y1F3X0N4YUR5aU51ZUp1Ykh6Z02DZFB+ X0x3X1drVU1WT09NRkZAPD0+OjsyPEEwOj80PD0yOjs4ODo5OTsyOjswODk3ODM6Ozc1Ojk0OTg5 OD44Nz01NTM0NDI0NDcxMTMqMDkrMTowNzItMy8uNDAsMi4xNTIuMi8zMTI0MjMxNTIxNTIuNDAy OTQtNy8uODAqNzQtOjgvNzovNzovMjUzNzo0ODAzNy8yMTgzMjk4Nzs0MzgyNT0wMzs3Nzc3Nzcv PDovPDoxPzcvPTQ5Nzg7OTovNzgxOToxODMxODM1Nzo0NTkqMzkvOT4zNzwxNDo1Nzw1NzwyMzk1 Nzw5NTs6NzwyNzUvMzIzMjs0Mzw3NTw4Nz00NzU3OTgzODkzODkyNTswMzksNzUrNTQzMzU1NTg0 NDQ0NDQyMzczNDgxMjUyMzcwOTMuNzEoNDAoNDAuMjMuMjMyMDMuLC8qMTIoLzAsNDEsNDEyMy8t LiorLSwrLSwwLyovLiksLyguMSorMCsqLyoqLSYpLCUrKykrKykuKyguKygyLyotKiUwLi0sKikn LigkKyUmKSwoKy4lKiUkKSQpKCEqKSIwMC4rKyklJiAoKSMqKSQqKSQpLCUrLicpLCUoKyQnMh4j LhokKyMmLSUtLiopKiYpKx0nKRsmKSAnKiEqLSYpLCUnKCQnKCQpKx8oKh4rKCUrKCUqKioqKiot KSwtKSwoJyQoJyQkKSIkKSIpKiIoKSEkIyApKCUnJyUoKCYmJigoKConJiMkIyAoKSUjJCAoJyIp KCMsKCksKCkqJSEqJSEuKSUwKycpKScnJyUmJiQnJyUnKSgkJiUdJhsgKR4kKBkkKBkkJSEkJSEj IBklIhsiIxIkJRQnIxcfGxAgFwogFwodGQcdGQdtWlRwXVd9ZFN9ZFN3ZFR6aFd+Z2J6Y156YVp7 YluAY1p/YlhyYUptXEZwXEVwXEVyYU10Y096Xk5/Y1N9ZVR6Y1F9ZFp9ZFp/Z1ODalZ/aUx6ZEeD aE5/ZEp7Yj+DaUaLbj+Iaz2DaEGGakSNZEaLYkSDZU93WkRXTEBIPTJFNTdGNzg0Mzo1NDs5OEA3 NT41Mzc7OTwrOjsrOjs4ODg7Ozs3Ozg4PDk9P0A7PT45ODwzMjcxNT8vMz0oOjonOTkwPjUvPTQs OTcsOTcxOToxOTo3Ozo0OTgxNDgyNTk3OTo4OjsrPTQtPzczOT01Oz85PUo4PEk6OT09PEA5PTw3 OzowOj8vOT46OT86OT81OT4zNzw6PD06PD0uPTwrOjkrOzUsPDc8Oj07OTwvPDouOzkxODMyOTQ4 NTc3NDUpMjUsNTk0Mzw4Nz83OD0zNDoqLTAwMzc0MzozMjkuOTgqNDM1NDs0Mzo1MzQ3NDUxMTM1 NTg0NDQzMzM0NzU0NzUyMzcyMzcxMi4zNDAvMTAyNDM0MjU4NTk5NDU3MjMyNS4zNy8uNS8uNS8t Ny8rNC0uMC0wMi8wNTAwNTApMy4pMy4yMSwyMSw3MS03MS03MS84MjAvLikvLikuMSotMCkoLyco LyctLS8sLC4yLjEyLjE4MjAyLSswLi8xLzAuMSorLicsLTAwMTQpMSwmLiksMi4sMi40MjEwLi0t LiotLiosLi0tLy4uLTEuLTEvLDEuKzAoMSglLiUmLyQmLyQlMCkkLygnLCMsMSgtMTAnKyoqLSYq LSYqKigpKScqKCkqKCksKi0qKCstKywyMDE1MC4yLSssKissKisiKickLCkvMCgpKiIhJxolKx4i JSApLCcoKyQnKiMtJxwrJRoqKB0nJRopKCMmJSArKCUqJyQtKBotKBorIxgqIhcrJR0vKSEsKh0p JxokJRQjJBMeIxIgJRQhIRcnJx0mJBkmJBkmIBglHxcmIRQnIhUmIAwkHgoeFwshGg4fGQghGwp5 XU2Lbl2Mck6Mck56bk94a02AaFaDaliJbVeHalWEZ1CEZ1B/ZUN0Wzl6Wzd/XzuDYkaDYkaLaE6M aU+HY0iMaE2LaU2JaEyRbkqOa0iQbkGEYziMYTSRZTmIbzyOdUGSbjqRbTmQaT2Saz+acEWacEWQ b0eGZT5cW0hQTz1IQUE/OTk7OTo9Ozw5Q0YzPUA6Oz48PUAxPT8xPT86Ojo8PDw5Pjk4PTg3Ozw3 Ozw3PTs0Ozk0PEEzO0AtRD4pPzowPDw0QEAtPTosPDkwNToxNzstPToqOjcxNDo0OD04OUA3OD8q PDosPjwyOzgxOjcvOz0wPD48Oz88Oz8wOzoqNDMsOTctOjgxNTcyNzg0MC81MTA0MjU7OTw1Nzwy MzkoMi0sNzE5ODQ5ODQ0NzUyNDMyNTA0ODIyODIuMy4uMjEwNDM1NTUyMjIyMzkyMzkuMDEwMjMx MzAvMS4rNC0rNC0zNC4wMSswMC4zMzEzMzM5OTk1Mzc3NDgzNTQyNDMzNDgyMzc0NzU3OTgxMzAz NTI1NDE0MzA5NDM1MTAzMzE3NzQsODArNy8tNy8uODAyNSwxNCsuMyouMyouNDItMzE4MzI7NzU/ NDU8MTIyNDMyNDMrLy4sMC8rNC0qMywwMDIxMTM4MTM1LzE0MDE0MDExMS8sLCotLzAyNDU1MTQz LzIwLi8wLi8pMSwpMSwsLi0rLSwxMC0wLywsLSktLiosLCouLiwvLCkuKygwLC0uKisqKigrKykt LyMqLCApKiYrLCgqLRkqLRkpLBglKBUqKh8tLSIvKyEsKB4lJxslJxspKCUpKCUqJR8tKCItKhsr KBkmJhsnJxwkJyAnKiMqKRkmJRYgIhYjJRkjJRolJxwkJhgkJhgmJBcpJxoqJxYoJRQlJhQlJhQn JRolIxgpIxssJh4lIRglIRglHg8pIhIsJRYqIxQeJA4dIw0nJQ8lIw4mHxAnIBEuIBMqHA8oIBYo IBYfIhAaHQwcHAcbGwcXFwwYGA0eGAYiHAl/XkOLaU2Mb02Mb016aVB4Z06AZUmCZ0qDY0iAYUZ+ XUmAX0yCZUSDZ0V/YTSEZTmOa0iQbUmWc0+RbkqIbUaIbUaNbUiQb0qVck2XdE+ZbkmSaESWaT+a bUOZdEGdeEWWd0iSc0WZb0WZb0WWbUOVa0GGa0qAZ0ZhXFFVUEZBREM+QD88Pj88Pj8yREYxQ0Uz O0A3PkQzOjo1PDw4PT80Ojw1Pj01Pj01OTwvMjUxOzwxOzwxOT4xOT4uOjotOTkpOjwqOz0oNzUp ODc0NDQ1NTUrODMrODMyNzUwNDMuODkvOTotOTssODozPDkvODQtOjUvPDg0OzkxODUsNzEsNzEr My4wOTMvMDMyMzcxMzAwMi85MjI9Nzc0NTk4OTwwNDMzODc3OTgzNTQ3NDM1MzIvNTEvNTEzOzIy OjE3ODE5OjM1OjkvMzIyMzk4OT4xNTQxNTQ1NzA0NS8zNTI0NzMzNTQ0NzUyNzg0OTozNzw1OT46 Mjk+Nz0yODwwNTo0NTs1Nzw4OT44OT4xODgyOTkyOTQxODMvNTEwNzIyMjI4ODgsPDQoODAzODQz ODQwOTUwOTU1NTMzMzEwNDEvMzA3Nzc0NDQ7NDc4MTMuNDQsMjImLS4pMDEkMCwiLiooLC0qLi8y LjExLTAzLzAxLS4nKyooLCsqLCkrLSouLTEtLDAxLS4wLC0sLi0qLCsnKygnKygpLSEoLCAnLiYm LSUrKiUsKyYsKSIrKCEqKycqKycpKScpKSctLiYsLSUrLCgoKSUmKhsmKhsnLBomKxkpKiIoKSEv KyIvKyIpKR4oKB0nKR4oKh8nKCAjJBwlJRgmJhknJx0mJhwpJyYrKSgkKh0iKBsjJxolKRwiKBwi KBwpJxwpJxwrJiQoIyEnJhYnJhYhJRYmKhstKSAqJh0jJCAmJyMoJyApKCEqJyArKCEwJyQuJSIn JhUlJBMsJRcsJRcsJBkuJhsrKBcqJxYoJRUoJRUdIhEYHQ0YHAcXGwYbFgkaFgggGgkiHAqCZ0+D aFCLbVSMblV/aVp4YlN6Ykx5YUp4WkZ7XUl7Wk5/XVGIZ0iLaUqGakGNckiVdE+Sck2Xb02VbUqO akqNaUmLb0mIbUeUbUSac0mZaU+ZaU+RaUqSakyUZz6fckiWbkqSakeNZ0GMZUCHZECJZ0ODaEx4 XUFXU1FPSkk9Pzw8Pjs5PTw1OjkxPEQvOkEwNTw0OkA4ODg5OTk4Nzs0Mzg0Nzg3OTo3OD00NTs5 Ozo1ODc3NTo5ODwyODwyODw3NTw4Nz0wMzsuMTk1NTU5OTkyNzMvMzA4OjsyNDUxODU1PDo8PUA4 OTw3Oj05PD8vPDovPDo3OTg1ODcxNTQyNzUrNTQuOTgyMzc3ODs0NDc4ODo6Ojw9PT87Oj47Oj41 Oz83PEAyOUExOEAxNTQyNzU1Ojk0OThAO0A/Oj89Pz45OzoxNzkyODozNzw1OT45Ozo3OTgwOTgy Ozo1PDgxODMvNTUyOTk5Nzo6ODs4Nz01NDswMzk0OD0wPTsvPDoxNzkyODo1NTg3Nzk4Ojk0NzUu NzUwOTgwNDMwNDMwNDUvMzQtODIqNC8vMzIwNDMyMzc0NTkzMzUzMzUzNDA0NTE4NTQ4NTQ6MzUz LS8vLy0vLy0wLi8vLS4sMC8pLSwrLy4qLi0vLy8tLS0yLi0wLCssLCwtLS0vKCcwKSguLysvMCwv Ly8vLy8vKy4vKy4yLyowLSg0MicvLSIsLScqKyUpKSkrKysxLCoyLSstLS8tLS8pLy0oLiwxLikx LikrKykpKScoLSYpLicqLiIrLyMpLCMmKSApKiYpKiYlLCYnLigsMCQmKh4oKSMnKCIoKicsLiss LSksLSktJykuKComJyMnKCQqKSIqKSInJyUnJyUlKiMlKiMpJyYqKCcrKCEqJyAoJyIpKCMpKiIn KCAkJyIkJyIlKiEoLSQqJyArKCEwJSMuIyEoJRUqJxYqJyIrKCMqJR8sJyEqKRkpKBgpIhIoIREi HBIeGA8WHgkWHgkcFwocFwokFwklGAqCY1SCY1R/aFSAaVV3ZFVvXU5zV0dwVUV4V0Z6Wkh0Wj5+ Y0eEaEqEaEqDZ0mEaEqIaECJaUGMY0WOZUeLY0WMZEaOaEWLZEGOZEOUaUeMbU+HaEqHY0SEYUGJ Wz+WZ0qOZESNY0OUYzOUYzOSaT6Va0CWb0yHYT5bVkxOST9HRDxEQDk8Pj07PTw4P0AxOTo4OT46 O0A4OTw4OTw4Ojs3OTo5OTk4ODg4Ojk7PTw+Pj48PDw9OkQ8OUM5OUQ4OEM6N0A8OUM8Nzw7NTs5 Oj05Oj07OD06Nzw4OkU1OEM0Ojw5PkA4PT87QEM1Ojs6Pj86Oz43ODs1Oz8zOT00OkAxNz0qOToq OToqOzknODU4OTw0NTk0OD03Oj85OTs1NTg4OT43OD0tOTstOTsxNTQxNTQyMTg4Nz05NEM3MkAy NT0uMTk3OTozNTc1NTU4ODg3NzkxMTMpMzAsNzM0NS81NzAuMjEuMjE3MjE6NTQ0NDc0NDc3NDg6 ODswPDQqNS4vMzQvMzQzNzE0ODI3ODE1NzA0MDM5NDg0NDQxMTE0Mzg0MzgxNC8xNC80OC4xNCs0 NDI0NDI4MTU3MDQ6NTQ7NzUyMjI1NTU1MzQ0MjMyMS4zMi81NTM0NDIpMzIpMzItMzEtMzEzMTA3 NDM0LjAzLS8xLzA0MjMxLy4vLSwwMSswMSsyMjIuLi4vLzEtLS8wLywvLiswMi8wMi8rLSwtLy4s LC4wMDI0LjAzLS8wLTIuKzApLS4qLi8qLCkoKiclKycoLionMSwmMCsuLSguLSgoLSglKiUlKiUm KyYkLCkhKSYjKSUnLSknKCQqKycvKyoxLSwrJygrJygoJSIpJiMhKB4fJhwkJyImKSQlJSMlJSMf JB0jKCEiJCMhIyIgIB4jIyElJRslJRspJxwmJBkfIxccIBUnKBgpKhomKRYfIhAlHxUnIRYoHxEp IBIuKB8pIxoeHxAeHxAnIhMjHg8kHQ4gGQoeGAcdFwcaGgcbGwgYFgkYFgklFwkkFghwW0N4Ykly Y0h0ZUpuXkhpWkRtVz9vWkF1XD5/ZUeDZ0WGaUeIbk+Ibk+GY0CGY0COaT6Ra0CUbUeWb0mNZ0SJ Y0CHXzuEXTmDX0OLZ0l6aEd1Y0N+XkR7XEGCWz+LY0eNY0GNY0GNZTiUaz2Vck2UcEyIbk17YkFX VUZKSDpEQztFRDxEQEZAPUM9P0A/QUM9PEA7Oj46O0A6O0A4OkU4OkU7OkA5OD4/QUBBRENDQUo/ Pkc5OUc8PEo/P0o8PEc6Pj05PTw6OT85OD43Ozo1Ojk6OT86OT81OUA0OD8yOj8zO0A7Oz08PD46 PD03OTo5NTs6NzwyMzkzNDoxMjg1Nzw1NTU1NTUxOzwwOjs0Nzg0NzgzODczODc8NzI5My85OTk3 Nzc1NzA3ODE0NDIzMzEzMTA0MjEzOTsvNDcvNTMsMjA1OTMwMy45My05My0yODIxNzEvNC80OjQ5 MjI3MDAzNDAzNDA1NTM0NDI0OzkzOjhAODlBOTo1OjkwNDM1ODk4Ojs5OTk3Nzc0OjQ0OjQ+NTk+ NTk4ODg1NTU1NTU3NzcwNDEyNzM0ODA3OjI1NTM1NTM3NzcyMjI1ODk3OTorNTIrNTIoMzMoMzMr MjMtNDU5NDg6NTkvMzIuMjEsNTcrNDUyMTgxMDctMTIrLzAwLyowLyoxMi4wMS0yMy8wMS0yMS4x MC00Lyk1MCouMSwsLyomLi0oMC8pLSwpLSwrKSwuLC8tKSguKikzKSozKSoxKSwxKSwpLCUmKSIm KCUoKicoKCgoKCgwKSQxKiUqKiApKR8jKiIkKyMlJxwoKh8kKyMhKCAlKRwlKRwoJhsoJhsnIhwn IhwkIBUnIxckKBsgJBchIBkmJR4mIxwmIxwnIxklIRcdHxQfIRYiHhYkIBccHw8fIhImIBcjHRUg IRIiIxQlIhEkIRAgIg4fIQ0dHA4fHg8iGhIjGxMkHxIkHxIeHQ8gHxAhHA4iHQ8jHA0hGgsdGQga FgYaGQkbGgoYGAYXFwUkFgshFAluX0N0ZUhyYz9rXTprXEZrXEZ0XkRzXUOCY0SNbk6LblCLblCR cEyRcEyLY0WQaEmUc06RcEyOcEmNb0h5YkV1XkF0XUx0XUxyW0V1XkhtWERlUT1uUUhwVEp9WEWH Yk6GXUaGXUaHaFSMbViEbVGGblNzY09nV0RIUEdDSkFGRkY/Pz85PT45PT4/PEZBPkg6P0E4PT83 Ozw3Ozw3N0E5OUQ5N0Q5N0Q8PUU5OkFAOkdBO0g8OUM5NT86OkU4OEM4Nzs4Nzs6Nz45NT01NTU1 NTU7Ozs6Ojo4ODo4ODozOEEyN0A3NTo3NTo8ODk5NDU4ODg6Ojo5NT00MTk0NDc3Nzk0MTc0MTc4 ODo4ODoxMS85OTcyODoyODo+NzM5MS43Nzc3NzcyODIxNzE1Ny41Ny41NTU0NDQwNDMzODcoNC4q NzA0OzczOjU9NTA+NzE5Ozo5Ozo1ODk3OTo4Nz85OEA0OTU3OzgyOzovODc0NT0yMztANz5DOUA4 OT44OT4wNToyODw1Nzw3OD03NTw3NTw8NDs9NTw8Nzw4Mjg1NDk3NToyMTU4NzsxOTIwODEyNDMv MTAsNDEqMi8mNTArOzUoODImNTApMjMsNTcoMjEnMTA1MTIzLzAvLDEwLTIsLi8wMjMtLDIpKC4r LSwsLi0sLCosLCosLScqKyUtMCksLygtLCUtLCUwLCAxLSEuKyYsKSQjKyglLSooKislJygpJSQt KSgjKR0jKR0tKiMtKiMsJyUsJyUtKiMtKiMoKh4mKBwmIRsmIRsrJx0qJhwoJhslIxgjJBwlJh4p JxosKh0nKRsjJRccJgsgKg8fIRYhIxchHRUhHRUiHRckHxkpJRkjHxQhIBElJBUbHRAcHhEjHxYl IRceHBAeHBAiHhYiHhYaHBIbHRMgHhUgHhUiHhUkIBYhHRQhHRQhHg8iHw8cGRMaFxEfGg4hHA8f Hg8eHQ8aGhAbGxEeGgkhHQshGg4fGAwaFwgaFwgbGgwaGQsbFwcaFgYkGQohFgd0XUd0XUdyXE1r VkdwWExzW057YUl+Y0yLa06QcFOMcFSJblGJZVOCXkyDYlCQblyIclaEblN9aFNuWkVeVUxeVUxf V1FeVlBeV05cVUxXTlFTSU1nTkdpUEltUEVwVEhuVkxuVkx6Xk57X091XE5vVkhWTURPRj07REM5 QUBAPUM8OT47OkA6OT87PD87PD80OzsyOTk1Pjs0PTozNzw1OT45OD47OkAzNzwxNDo6Ojw8PD49 OTo8ODk5Nzg5Nzg5MjI8NTU5Mzk7NTs9Ozo5NzU5Oj04OTw6ODk9Ozw8Nz47NT06NTc9OTo8Ojk7 OTg6Ozc1NzI6Ojw4ODo4Nzs5ODwzNzw0OD09OkE+O0M3OzoyNzU1OUA1OUBDOUBANz44OTw4OTww Oj0sNTk1OjkzODc5ODw0Mzg3Ozw3OzwtNzovOTw0NDQ6Ojo4NzM+PTo9Ojc8OTU1ODk3OTo4Nzs1 NDkwNTozOT0vNTUzOjo0OD0yNTs9NDo8Mzk0ND85OUQwOUMsND4yMzkyMzk5NDM3MjE3MjE3MjE5 MDM4LzI1MzI0MjExMTMxMTMxNTQtMTAuKzAtKi8tLy4sLi0kLygoMywrMCsqLyovLSwvLSwlLicp MisvLSwvLSwtKyouLCsvLikwLyooLCspLSwsLCwsLCwqKycoKSUoKCgnJycoKyQoKyQpKyAmKB0s KiAtKyEoJyIpKCMmLSchKCIjJh8lKCEnJiEpKCMhKRsfJxknKiEmKSAkJBolJRsoJRYrKBkpJxol IxYoJBomIhgnJRooJhspJB4mIRslIhQlIhQjHxYlIRcnIRklHxcgHhQiIBYnHxcqIhojIBseGxYc GhkgHh0nHRYlGxQdGxEeHBIXHRIZHxQhHRUlIRgfHREbGQ4eGw4hHhAaHBEZGxAaHBEZGxAcFxId GBMaFxEcGRMYGhAZGxEfGxMdGREeGBAdFw8eGg8cGA4hGRIgGBEcGQwaFwoSFQcWGQoYFgcYFgca FggcFwodFQgjGg0lGgkkGQhuVU5vVk9pV05kU0loVFRtWFhzWFR1W1Z+Yk9+Yk96XUV4W0N0XlNr Vkp1Vkx/X1V3YkpwXEVoUUlhSkNYSEVbSkdYSk1URkhPSEhKREQ/QUM8Pj9NRzhQSjtXSTpcTj5f TkVfTkVlTj1iSjpaSD9WRTxJRD1HQTs8PUA8PUA/OD5AOT9APj9BP0A3Pz4zPDs1Ojk1Ojk9Pzw7 PTo5Oj04OTw6OT08Oz87OkA7OkA7PD87PD89PT88PD47PD86Oz47Ozk6Ojg3Ozw4PD06Pj81Ojsz Oz4xOTw3Ozo5PTw8Oj0/PUA6ODk+PD04QD00PToyOj0yOj05PD83Oj01ODk1ODk5ODw7Oj48PUA5 Oj0wP0ArOjs5OEA5OEA7NT09OD81PUAxOTwsNz4sNz4wNTgvNDc1NzwyMzk0NTs1Nzw0NzM0NzMv MzI0OTgrNTItODQ5OjU4OTQyNzM0OTU0MjM3NDUyMzc3ODsxLzA0MjMxMzAxMzA4Mi44Mi4vMTAw MjEyMDEuLC00LC81LTA0MS4xLissLC4uLjA0LC00LC0yMCYxLyUtMistMisuLiQsLCIuJicyKisw LiMuLCEpLCUrLictLS0tLS0tKyotKyopLikrMCssKygrKicqKSYqKSYrLikpLCclLCIlLCIoKyYp LCclKiUmKyYoKB4pKR8mJx8mJx8rKCEpJh8iJBknKR4nKCAoKSEpKx8jJRkoJR4pJh8qJSEsJyMi JRwgIxomKh4kKBwhIxcfIRYeIBUeIBUlIxYkIhYlJhUlJhUmJBolIxkgIRkhIhokIxEkIxEcHhEd HxIhHxUhHxUgHRofHBkgHhQhHxUeGhIeGhIgGxciHRkeGRQeGRQaFxEdGhQgGhofGRkgGhIiHBQZ GxEZGxEYGRIZGhMaGA8cGhAcGQwcGQwcGw0aGQsZFRMaFhQXGhIXGhIdGQ8eGg8dGQ8YFQobFg8c Fg8fFhQgFxUeGg8bFw0WGAgXGgodGgoYFgcbEwgfFgsjFgoiFQoiFgojFwtfR0ReRkNYRj1YRj1H RURNSklaSkNdTkZoVklfTkFYSjtaTDxhSEFnTkdrTUFtTkNaTz1NQzFKPDJTRDpKRUNPSUdUREVQ QEFFRkFAQT07QDs3PDdORDVPRTdKSThMSjlKTENKTENNSDtMRzpPTERJRj5ERERDQ0M4O0A9QEZB PEQ/OkE4QD86Q0E0REMxQD88Oj0+PD9AQEM/P0E9P0A9P0A4PT84PT86PUA5PD85PD86PUA8QEE8 QEE+PEg9O0cxQEEtPD03PT05Pz85PkA4PT8zO0AyOj8vPj0sOzo5Ozw6PD01ODk6PD0wPTsuOzkv OTwuODswNzcuNDQzNTI1ODQ3NDg5NzozPDsvODcpOzclNzIsNTcuODkvNzgvNzgvNzgvNzgrNDot NzwzMjczMjczMzUyMjQzMDUzMDUxMiowMSksMystNCwjNSshMyk0OC4xNCswMDAzMzMzLig6NC4p LicrMCktKyAtKyAuKBUzLRk3LxY5MRg0Lx4sJxYvKhk3MSA8MSczKR8wKx05MyVEOzJJQDhDOSg1 LBw+MiBWSTVPTDtPTDtHPihFPCZQT0lWVU9JTEhAQz8rMiAkKxkjKiIkKyMlLCImLSMjKiIjKiIn KCQpKiYkKSQhJiElKCMnKiUkKSAgJRwiJhokKBweKBofKRspJxwqKB0oIx0mIRsiJhojJxsjJRog IhcmIyAnJCElKRwhJRgkJhsjJRoqIhkpIRgdHhYfIBgdJRgaIhYiJRMiJRMaJBYYIhUZHxQbIRYc IBQfIxYkIBYhHRQiHhUgHBMdHg4cHQ0eHQ0fHg4aHBEbHRIWGxMWGhIdHg8dHg8ZHA0ZHA0hGxMg GhIYFg4bGRAiGhMjGxQfGhYcFxQZGgwbHA4YGg8WGA4WGA4aHBEWGg0YHA8bGw8aGg8iHAcjHQge Gg8dGQ8ZGgwWFgkVFQsXFw4bGAkZFgcWFgsZGQ4dGREaFg8hGA0iGQ4aGQsWFggbGQcbGQcdFQof FgsfGQgeGAcaFQQeGAdVRUFPPzxIQz5PSUVJRD1KRT5QSENQSENbSENaR0FVRj9YSUNWTkhXT0lM RUVMRUVPSUVMRkFNR0BQSkRPSE1RSk9HRkNEQz9ESEdDR0ZFSlFBR05GQz9HREBKRz9PTERNTklK TEdFUEBATDxAREc/Q0ZAQEBFRUVEQEo+O0U6PEg8Pko6QUM6QUM7Q0g6QUc/OENDO0Y7PT4/QUM8 PUA8PUA3Oj81OT48QEE4PD0yOzo0PTw6Pj01Ojk5NEE7N0Q4PD03Ozw1OjkzODc1PDozOjg1OTw3 Oj03Oj00ODsxNzkvNDcyOTcwNzQnNTQnNTQvOjcoMi8qLisxNTIxMzIxMzI5NTI5NTIxNC81OTMn OTAnOTAzNTI0NzMuNzEuNzErMS8yOTc1NTUzMzM3MDI4MTM1MTA1MTA4LzA1LS4vLBo1MiBDPjNE PzREPTNBOzFGPzVEPTMvOS8oMSg0NypFRzpRVU1aXVVeZWVQV1ddUExnWlVyZVpqXlNaTz1WTDpb VlVnYmFqZF1nYVpnZ2RlZWNocHRiam5jZFFtbltzcm59e3h7d3V9eHdzdG15enNzfYt5g5Fed4JO ZXBHRD4zMCsqKyMqKyMpKx8oKh4jKxsjKxsnKCIoKSMiJx4hJh0jKCEkKSIlKB8hJBsgJR4hJh8k KhsiKBkeIhYjJxonJx0jIxkiIhclJRokIhclIxgmJSImJSIgJBghJRkjJRojJRomIhgkIBYfIRYd HxQaIRAgJxYiJRMbHg0jIRUjIRUjHxYhHRQfHRQhHxYeHxAdHg8dIBAbHg8aGwocHQweHQ8bGgwX GQ8VFg0VGg8YHhMWHQ0WHQ0ZHAoZHAoaGA0fHREYGhAaHBIcGg8bGQ4hGg4gGQ0eGQ0eGQ0ZHAsX GgoTGgwTGgwWGAwXGQ0aGA8ZFw4dGwkeHAoWFg0YGA8bFwcbFwcfGQ8YEwoUFQYVFgcbFAohGQ8f GAodFgkhHA8eGQ0UFAoTEwoWFgYXFgcZGAgbGgodGQUfGwcfFwQgGAVNSElKRkdIRU9MSFNKSExI RklKR09KR09PRlVORVRNR01RTFFJTE1OUFFBSU8/R01BRkdDR0hIR1BGRU5KRlRMR1VERUhDREdA P0ZFREpHQUxJRE5DQEFDQEFER0FFSENFRUNFRUNERTxDRDs8O0Q+PUY+PUFAP0Q/PUA9Oz4yPkMz P0Q6O0A8PUM5PD8zNzo+OUBAO0M6Ojw9PT81OT43Oj86OT09PEA7Pz44PDs0PToxOjcyNDE0NzM4 Ojc3OTU4Ojc1ODQzODcuMjE3ODM5OjU0Nzg1ODkxNDgyNTkyOzotNTQtMDMwMzctNDUsMzQtNTQp MTApLyssMi4zMzU0NDcyMC8zMTAwMi8xMzApMzAqNDEwMjEwMjEuMzUsMTMyNDMzNTQ0Lys3MS1A PD1HQ0RAQEBAQEBGQDpPSUNbVEloYVZvamtqZWdiYl9fX11dV1NTTUhDRzc7Py8/SEVXYV1pdIh5 hJl1gqZnc5ZnZ4RpaYd3eIx3eIx4fX56f4B9eZ16d5p3eIx4eY1wc5Btb4xkdJFpeZaAiJSGjZmC iaOHjqiEhpqLjKGDlLJ9jat7ia+AjrRrf5tbbolISU00NTkqLiEpLSAlKx8iKBwlLR0iKholKCEn KiMiKSEhKCAfKB0eJxwfIhsjJh8fJh4dJBwlKRwjJxocJBceJhkgJhkhJxojJh0iJRwnJB8nJB8j Hx4mIiEoJhsnJRomIR8mIR8eHRYiIRodJRYaIhMdIRUgJBclIRgiHhYeIBYcHhQdGRAiHhUfIxYc IBQbHxEbHxEaHw0ZHgwgHBEhHRIcGxYZGBQVGg8TGA0VGg8WHBAWHQ0WHQ0YGg4XGQ0UHQ8UHQ8Z GBQbGhYaGhAWFg0gGw0gGw0cIQ8ZHgwdHxIYGg4UGBIUGBIYGhAYGhAWFg0ZGQ8fGQ8gGhAdExEf FRMcGA4dGQ8dHAwYFwgbFgoaFgoZFAoaFQsdFgoeFwscGhAcGhAZFggbGAoeGQ0bFgoZGwgbHQoa HAkYGgcbGQYbGQZGRkZISEhNRUxNRUxOR1ZPSFdKR1FGQ01IRVxIRVxJR1VKSFZJSUlISEhHRkpD QUY7QUE9RERARko7QEVDRVBAQ047PUg5O0Y8OkY+PEhBOkBFPUQ9QUA+Q0E8REU6QUNBQURBQURB PjtDPzw9PT09PT07QEM5PkA1O0EzOT8uODsyPD85Oj85Oj8vOTovOTo9OkE8OUA6OT07Oj43NTo4 Nzs5Ozw3OTo0OTUzODQzOTMzOTM4ODU4ODU4OzU4OzU1Nzo3ODswNzcsMjI3MjM4MzQtMzMvNTUz MzM0NDQyNDMyNDMyLzc0MTkoMTQmLzIqMi8oMC0tMTAtMTAuLyswMS0uMSwxNC8pMy4pMy4rMzAq Mi8wMjExMzIuMDEvMTI3NTwwLzUtOS88SD5MT2NUV2tVYW1QXGhcXFx0dHSChJGEh5SDh556fpV6 gox/h5FjaXBXXWRXV1dMTExDVV5Ya3VrfqV3ibB9hK93fqhhcI1jc5B6f55/hKOCibR7g66De6iL g7CAhqV7gJ9ucohydYxzgpSAkKKSma6QlquMkauWm7aSlKuXmbCIn7t+lbCQkq6XmraHjJtuc4JY UVFAOjooLSgeIx4jJSIiJCEgKyIfKiEkJCQnJyckJiMiJCEcJCEcJCEdJh0cJRwaJhcaJhcjJRkk JhonKR0hIxceJBggJhogJSAgJSAkJhsjJRofIRYeIBYnJhYlJBUkHhYmIBglIB4lIB4aJBYbJRcf IR4eIB0iHRsfGhgZHxQaIBUbHhcdIBkaIRcYHxYZHREaHhIbHxEZHQ8fGxAeGg8hHRQfGxIYGg8Y Gg8ZHQ8aHhAXGgsXGgsbHg8ZHA0VHA8TGg4WFg4ZGRAeGw4cGQwiHBIgGhAaHQwaHQwdHA4bGgwc FhYdFxcXGQ8XGQ8YGg8WGA4aFgwdGQ8eEgwkFxEZGgwbHA4aHw8WGwsZGAkZGAkZFgoaFwofFhIh GBQbGgoaGQkWFgsWFgsbFg8cFg8gGQshGgwZGAoYFwogGwUhHAZDRD9GR0NJQ0NHQEBEQUNEQUNB QUQ/P0E+Pkk+Pkk9PT9BQUREREQ9PT0/PT5BP0A9PkE7PD86Pj03Ozo8Pj07PTw4O0A3Oj81Oz81 Oz86PDs6PDs6QDw3PTk7OTw8Oj09O0c8OkY5OD45OD47OD07OD04PjwzOjgwOTUzPDkwNzcwNzcw Oj0xOz4vOjcvOjc4ODo6Ojw1OUA3OkEzNTQyNDMxNTcyNzgyNDMyNDMwLi0yMC8xNTIyNzMyNS4y NS4sLisvMS4oLC0nKywuLC0uLC0vNC0vNC0uMSwuMSwwMDA1NTUxLzAwLi8mLismLisnLiYoLycu MC0sLissLi0tLy4sLyouMSwoMCsoMCsrLzAqLi8wLCs0MC8tKywzMTIuMTcqLTI6R0VUYl9ebX5f bn9zfpJteIxzeYuGjJ6ElLJ6iad5i513iJqEjJqHjp1yfotjb3tkanJUWmFEVFdfcHR4gp94gp+C f41vbXppbXB1eX2SkaaQjqOOjKt+e5qIgpubla+OkqZ7f5J1eX9/g4mRkKebmrKmocKembqalKuq o7umobSnoraboriWnbOom7KvoriSlaJ3eYZhVkdGPC4sMSgmKyImJyMlJiIgJx8iKSEjKCEiJyAj Jh8hJB0YJR0ZJh4cJRwWHxYbJRceKBoiJBgiJBgoKBslJRgdIh0iJyIaHxocIRwgIx4iJSAhJRka HhMgJBgeIhYfHRQgHhUhIB0hIB0bGxkdHRsZHRoYHBkeHxkcHRcbHBUbHBUdGhYdGhYXGxAbHxQZ HRIZHRIYHBEcIBUiHw8fHAwiHhUgHBMZHA0ZHA0cHQ0eHw8bIRUUGQ4YGg4VFgoWFw4XGQ8VGAsW GQwcGQwcGQwiGw0eFwoXGAkXGAkYFQocGA4eFg8fFw8WGg0XGw4cHBAZGQ4bGAsfHA8eGw4bGAsW FwcWFwcXHAwWGwsVGQgVGQgcGggdGwkcFwkcFwkbHA4ZGgwWGQoXGgsSEwwVFg8bFg4bFg4WFQcX FgkiHAkjHQo7QDs7QDs9PT09PT0+Pj49PT09P0A9P0A3Pj84P0A3Pzw5QT45PEE1OT47Oz0+PkA+ PUQ7OkA6PDs4Ojk7Ozk8PDo9PDk8Ozg4P0MwODszOjoyOTk4Ojc3OTU5OTc4ODUzNz4zNz4yNT0y NT04Nz01NDszOTMwNTA0Nzg3OToyOTQyOTQxNzkxNzkzNTQzNTQxMzI0NzU0NTszNDowNDMwNDMt NC4xOTIyMjAxMS8vLy8xMTEqNDEqNDEuNDAvNTEsLScsLScrLSorLSoqLTArLjExMzIxMzIoLiop LysuLjAyMjQtMzMtMzMtLS8tLS8pLikqLyorLy4oLCsrKictLCksLyouMSwtLy4pKyopKygtLywt LywsLis1Li07MzItLS0zMzNESkhiaWd1eIh+gJGRkquVlq9/kaeElqyEgJZzb4R6gpB+hpSGf5aS jKOCf5Fwbn9tcHRlaW1cXWFpam57eo59e5B+eX10b3N0cnWHhIiWla6RkKiVjJaUi5WSlKuRkqqJ h5V9eoh5eIyCgJWNja+Wlrifobqhoruiobiop7+mpsOlpcKlor+fnbqoocOnn8KNl7B5g5tiXVND PjQpLyAhJxglJiImJyMmKSAiJRwfJxkhKRsjKCMiJyIdJyQZIyAgIR0eHxsnJB0oJR4kKBkhJRYj JBMkJRQlJxwiJBkgIxoeIRgcIxkcIxkiJSAcHxodIRYcIBUcHRccHRcZHxQYHhMcHBIcHBIgGRYk HRoiHxggHRYbHxQbHxQiHhYgHBQdHRMaGhAZHREbHxMbGxEdHRMgHQ0fHAwlIRgdGREZGhMeHxca Gw0cHQ8bHRMaHBIbFw8cGA8cFxQaFhIWGgoWGgoXHxMYIBQZFggeGwwdHg8WFgkWGQ4YHBAZExAa FBEWFQobGQ4hHRQgHBMfGREhGxMZHAsYGwoaGgcbGwgdHAweHQ0YGQoXGAkeHAocGggXGAgYGQkY Gg4ZGw8XHREXHREWGAgUFgcWFgoWFgoYFwoaGQsfGQghGwo7Oz09PT89P0A9P0A7OTo/PT46QEA3 PT0zPDk1Pjs4PDs5PTw4O0A3Oj84Ojs6PD08Pj89P0A7Pz44PDs5Nzg8Ojs8PDw6Ojo5OkE1Nz43 Nzk4ODo4Ojk1ODc5Ozg1ODQwOTguNzUxMzQxMzQ6Ojo5OTk4ODU0NDIyNzUxNTQxNTIxNTIuOTMu OTMwNDMxNTQsNzUsNzU0NDc1NTg3NDU1MzQtNCwsMysxMzAvMS4wMjMyNDUxNTIsMC0wMS0uLysr KykuLiwpKScvLy0xMTMzMzUyMjQuLjAoLCsqLi0jLSgmMCsuLyszNDAwMDArKysmLygnMCkqLism KictLiYtLiYvLicwLygnLSkoLionLS0nLS0uLi4vLy84MC89NTQ3MChAOjFEUFBea2uVg5aejJ+e qMKapb6Um7aLkqyEeH53anB1eIR+gI2Cd5GSh6J/fY51c4RvcnNrbm9oZWlua29zc4B6eoh1dYNw cH5vbX6Gg5WGjqOIkaabn6iXm6WHjq5+hqWDeX+DeX90cIZ0cIaHh6ORka6enbSop7+do7qhp76a oryUm7adma+VkaeRkbOWlriJkbB5gJ9hWl5FPkMsMSghJh0nKiEoKyInJh8oJyAdJxkgKhwaJiAW IhwgICIgICIgIB4dHRsnHyArIyQhKB4gJx0jIxYkJBcnKR4iJBkeJR8dJB4dJhsZIhcfIBwdHhol JRsjIxkeJRsaIRcZHxQbIRYhIxcgIhYrHhgsHxkoIBcmHhYYHhAaIBIeIBUbHRIgHBQfGxMgGhMh GxQiHRAhHA8fGxIiHhUjIBkeGxUbHhYaHRUWHg0WHw4kHQ4iGwwgGgknIQ8qHhAlGQwUFgoYGg4d Hg4cHQ0gGgkkHgwkIwocGwQpIxAnIQ8lGgosIRAzJREwIg8wIhUoGg4aFg8cGBAbHQobHQoeGgkj Hw0kGw4gFwoiFg4jFg8kHgwgGgkaFwoaFwoaFgoaFgobGgwaGQsSGAcTGQcYFgsZFwwfGgweGQsf GwocGAc5OD47OkA5Oj85Oj84OTw1Nzo0NTk4OTw5OTc3NzQuNzEwOTM0PD0xOToxODgvNTU6PDs4 OjkxNTc3Ozw8Ojs5Nzg7OTw5NzoxMDQ3NTo5Nzo6ODs4ODU5OTczOjUwNzI3NDU5Nzg6MzM5MjIy NzgxNTc1NTU1NTUyNzgxNTc1MzI1MzIyNzgwNDUzMzUzMzUzMzM5OTkzNTcyNDUzODcuMjEyMjIz MzMwMjEvMTAtLzAwMjMyMDMvLTAsLCwvLy8sLissLispKygrLSowMDIxMTMzMzUxMTMtMTIrLzAo LykpMCosMSouMywvLy8qKioqLCkrLSopLSooLCkpLCUoKyQqKyMuLycqLygqLygmMC8hKyotLDAt LDAyLTIwKzA4LSNHPDFKV1pvfX+OkayVl7OjqMmXnbyRkqqIiaF/eH11bnOAf4l/foh5dId+eYxt b31tb311cnhzb3Vza3p3b357dYB7dYB4dYdtantjYml+fYSMl56NmZ+do7iVm7CalaiZlKeQi4x+ eXp4bnSAd32ZkJemnaWjpbqlpruZnbOeoridnauOjp2OiYuGgIKDfo6Mh5eEiJtwdIdeVl9DO0Qu KyguKygmKBwjJRkrJiQqJSMkJR8lJiAfJB8fJB8lIyIkIiEiJRwhJBsiIx8mJyMhJBscHxYeIREg IxMkKyEgJx0eIRohJB0gIxodIBckIRwnJB8oJBsnIxohIhofIBgeIRgeIRgfIRYeIBYoIBcqIhkr HhYsHxYeHw8eHw8aHhMdIRYcIRoYHRYWFRQaGBcbHxMdIRUYHhMUGQ8XGQ8bHRInIBEpIhMtIQkw JAs7Jw9ELxZDMBRFMhZINRZKOBg6MhkzLBQyLRIuKQ8tIQoxJQ46KAs9Kw5JMgxMNA5ROxlVPhxf PiJcOx9NNB5BKhUxIxM0JhYsIAo0KBA5KBE3Jg8yKhclHQwkFw8oGxMcGQocGQoaGQoaGQoaFgwZ FgsZGQ4ZGQ4WFwcXGAgZFAobFgwaFwkbGAocFwkYFAY6OUE6OUE0OD00OD0zOz4wODs1Nzw3OD01 NDk1NDksNSwwOjAuPjsoODQpPDgoOzcxPDcwOzUzODQ3Ozg3NTo3NTo6Ojw5OTsyNDE1ODQ5OTk6 Ojo4ODg1NTUoOjMpOzQzOjgzOjg+ODw7NDkzMzU0NDcvMzQvMzQxMzQ1ODk3OToyNDUyMzkyMzkz MzUzMzU1NzI0NTEyNDEyNDEzNTQxMzIwLzgwLzg0MTcyLzQzLy43MjE4MzI4MzIqMC4pLy0nMTAn MTAqLyoqLyozMzMyMjIuLiwvLy0qLTAtMDM0LjA0LjApLicsMSotLzAsLi8tLzAuMDEsMDEpLS4n LS0mLCwpKiYtLiovLSMyMCYoMC0lLSolLC0qMTIrMzArMzA4NSs5NyxQVl1zeYCJjKqRlLKjnbSX kaiIjJKGiZCDgol/foaIjKKGiZ+DgI55d4RiZW5pbXVubnpwcH15dYuJhpuGfo55coJwcnVpam5z Z2iViImSn6qSn6qboriWnbOhm6+dl6uWjKGAd4t/d4CIf4mRlKWbnq+eorqXm7ORlKWXmquMjpt9 f4xqbWtoamlvbnhwb3ltaoZdW3VRTFRFP0cxLisvLCkpKCEnJh8dJyQfKSYhKCIhKCIdJSAfJyIn JiEnJiEgJBYhJRYdJRcfJxkjJBwjJBwiJhkgJBcgKhweKBogJBghJRkkJyAiJR4YHxceJR0lIxgj IRYjIBkjIBkhIhogIRkeIRgbHhYnHRYrIRopIxonIRggIBQjIxYeIhYZHRIbIxYaIhUdIxcdIxcc JhcbJRYaIBUYHhMcHQ0hIhEtIxs1KyNFMhRJNxdROBpdQyRkRCNnRiVqTiBuUSNkSihcQyFXPyJI MRZFLQxJMQ9ILxNUOhxbPRNaPBJdQBhoSiF0Ti11Ty5oTS1UOhxFMhZALhI7KAtGMhRXOiRVOCI+ NR4vJxEoIQolHggZFwMeHAcbGwUdHQcdHA4ZGAoaHBIZGxEbGQcfHQogGQsgGQsfGAohGgwgGQ0c Fgo6Ojo4ODg5NzU5NzUxPDkuOTUzOTsxNzktNDUsMzQrNTItODQxODMxODMvPzgsPDQzOjUzOjU1 OTM4OzU1OjsyNzg0OTg1OjkvOy8wPDAxNy83PDQ6Ozc4OTQnPTgoPjkwOj0sNTkxNTczODk1NTUy MjIyNzMxNTIyMjA0NDI0NzM0NzMxMjgzNDosNTcuODkwNSwvNCszNCwyMyszMjkyMTgxMjo0NT08 NDs0LTMzMjcxMDQ0ODAyNS4pMyUoMiQnMSwoMi0sLyguMSozMzEzMzEnLywqMi8qLisnKygvLS4z MTImMiwoNC4rLy4pLSwwMTcsLTItLy4tLy4sMjApLy0mKy0nLC4vLCcwLSgqMC4pLy0hMSYmNyss NS4sNS41Ny4zNCxFUVFvfX2AjJKDjpWSkJ6SkJ6HiZeHiZeIkJqJkZuQl7eJkbCEgpR0coNPVVdY XmFla39oboJ3c4uIhJ2GgJFybX1raW1ua29zd4yAhJqOkLaOkLaVlKyUkquVkK6Qi6iLeZ+Ab5V5 dY6Cfpd6g6h/iK6Gkp96h5R6eYN7eoR0cn9oZXNTVVRQU1FOU15RVmJPVl9HTlc8PUA4OTwuLyss LSkoKCYnJyUcJR4hKiMfKSQgKiUfKSYeKCUgJCEiJiMmJxckJRYdJhQfKBYkJhglJxkhKRsfJxkc KRYYJRIdJBEcIxAdJBEeJRIaIRkeJR0fJRYbIRMfJBEdIg8aIRcbIhgaIhYZIRUhHRImIhYhIxcc HhMaJBQaJBQfIxUeIhQaIQ8aIQ8eIw8gJREgJA8eIg0hIRUdHREkHBQmHhYvJhc0KxxKOSJRPyhq SiRkRR9qSSZwTyt9UCmIWzJ6VSxyTSVvTCRoRR5kQCJiPiBXOBldPR5bQRNcQxRqRRdwShx9UCmG WDB/WjBlQRtONRVIMBBGMBBMNRVfRB9fRB9KPCA8LhQsJBAjGwkVGQcXHAoYGgYaHAcZGgobHAwZ GxEZGxEZFwwXFgoXGAoZGgwYGgYZGwcfGwgfGwg3OjQ0ODI/ODQ8NDE1PDozOjgxODMwNzItOzIt OzIyOTkxODgxNDg1OTw1PDwyOTk5OjE4OTAzODc1OjkwPDwuOjoxOjcyOzg1PTQzOzIzOjgyOTc6 PTg5PDctPz8mODgnOTcqPDoxOTwvNzo1NTg0NDc0NTE1NzI1NTU0NDQzODQzODQvNTUvNTUuNDAw NzIvODQsNDE0NzgyNDUzMDU1Mjg1NTgwMDI6MzM4MTEuMTQtMDMsNzMsNzMtMCkuMSoqLi0sMC8q Li0rLy4uLysxMi4lMS0oNDAnMCkkLSYsLisuMC0oMC0qMi8sLi0wMjEtLDQpKDAtLDAtLDApLCUt MCkuLiwrKykoLiopLyspLS4pLS4jMi0iMSwsMSwqLyo8MjE/NTRRTlhybnlteH5lcHd9fY16eot6 eJR9epaHh6iGhqeGjLB6gKVwb3dlZGtKU0xMVE1aZHRdaHheYXBtb39tantiX3BkWmdqX21fbZFi b5R6e5J5epGCe4eCe4d1dIt0c4lvZHtwZX1lYndqZ3tfbn1kc4JpcHBdZGRdW09aV0xYVklNSj5N SjNGRC00PD08REU7RD46Qz0kNywhMykmLi0nLy4jKyYgKCMkKSQjKCMlJyYmKCcmKCclJyYeIx4f JB8nJRsoJhweJBgeJBghIxcjJRkeKBoaJBYdJxYdJxYmJRYmJRYiJRIfIg8aHhMeIhYiJBkgIhcj IhMgHxAbIhgYHxYWIRQXIxYWIBAZIxMYIQ8ZIhAdJBAdJBAcJA4ZIQsTHA8WIBIeIBUdHxQbGgod HAshHxMiIBQgIg4kJhE3KRVDNB9RQyhVRitvTCNvTCNzThl9VyGQWimRWyqHWy1+UyZ9UyB4Thx0 Sh94TiJzRix1SC5nSSBnSSB0SRx7UCKDUCiMWC+NXS5zRRlYNBVRLg9MMQlWOxBoSCJkRR9VORtH LBAxJhUlGgoQFwwPFgoYGQkYGQkUFwQWGQYWFwgbHAwaGA0aGA0XGAkaGwsWHgkUGwcbFwUeGgc0 OzsyOTk9OTg4MzI0OzkxODUuOTguOTgwPTcvPDUxODMxODMxNDw1OUA1ODk1ODk5OjM5OjM3Nzc5 OTkyOzgwOTUxOjcyOzg1ODQ3OTU3PzoyOzUzPDk1PjssOkEpNz4uOjouOjo0OTgxNTQ6ODs5Nzo3 Nzc3NzczOTMyODIuPDMrOTAwOTMtNTA1ODczNTQ0NTsxMjg3NTo1NDkxMzAxMzAuNzEsNC86MzU5 MjQuLzIxMjUqNDEqNDEwMDAxMTElLyomMCssMDEsMDEwLzMwLzMsNS4sNS4tNC4nLigvLikxMCsv LicxMCkvLSwvLSwuLTEtLDAtLjMqKzAsLScvMCotLS0vLy8pKygrLSopLysnLSknLy4oMC8yLyww LSo9LitAMS5TSU1kW15fW15hXF9pZ2hpZ2hoZXNoZXN0co10co13dYlqaX1YWFtNTU9RR0ZQRkVJ UFpFTFVIT01VXFpNTlRISU9ERERQUFBPW2RVYWpkXV9kXV9rW1xvXl9fW15bVlpXTUxaT05UT1BV UFFTXE1RW0xXSkpVSEhFSTs/RDVARC87Pio9OCc5MyMtLio4OTQ1OCstLyMlMiYnNCggLSUfLCQk KSQjKCMlJRolJRojJh8jJh8kJSElJiIeJR0fJh4iJCMkJiUeKCMZIx4pJRsrJx0nKx4hJRggJhci KBknJhQmJRMeJxMdJhIcIRwdIh0gIRkhIhoiHhUfGxIgHxohIBsbJBIcJRMZJA8ZJA8eHw8eHw8h Iw0gIgwiIBYfHRMaGxQbHBUhGxEhGxEdHREdHREdIA8dIA8aHgoiJhA+LQ1NOxhbQR9hRyRtSSR0 UCqAUCCIVyaSYSqRXymMXCaIWCOHVBmIVRqAUBiAUBh7Tyh6Tid0SiFuRRxzQR59SiZ/TSWLVy6N Wy93Rh1aOA5TMQlTNQtdPxNuRiVtRSRcPh5HKw4sIQ8lGgkVFxEUFhAWFw0WFw0TFgYVFwccGQwb GAsaFggdGAocFQ4iGhMYGg8WGA4hHA8hHA84OTw3ODs5ODIzMi00OTowNDU1NDk1NDkyOzUwOTMy OTQzOjU0Mzg3NTo0ODs1OTw0Ozk3PTs9Ozo5NzUvOjcuOTUtOjMtOjM0Nzg1ODk1OzM4PTUzODc0 OTgvNzwyOj83OD0xMjg6NDI6NDI7Nzg7Nzg6OT81NDstODIuOTMvOzMqNS4uNzEvODI0NDQ1NTUz NDgwMTQyNDMzNTQqMi8sNDEoNC4pNS8xMTEzMzMzMTIxLzAsMC0tMS4qMDAqMDAmMCsjLSguLzIs LTAsKTAxLjUwMS0rLCgpLy0qMC4sLyosLyosMCQrLyMqLSYqLSYmMC0mMC0pLy8mLCwtLywtLywu KCgvKSkuMSwuMSwqLSgnKiUsLCwvLy8zMCkzMCkzMC05NTI6PThHSkVOSlNRTlZQTkNYVkpjT09l UVFhWGJlXWdhWlpUTU1MRTtAOjA9NTQ/ODc8Pzg7Pjc6PC9ERjlAOTU+NzM1Ny48PTRAQzNJTDxO STxGQTRTPDBVPjJPPjVPPjVHOCxIOS0/Ny1MQzlJPy5ANyY9My48Mi0yNDMuMC8pNCsrNy0vLCcz MCsvLy8uLi4sMSgmKyIiLyUhLiQbJhsbJhsmKSIlKCEcIxkdJBobJRYaJBYeIx4cIRwbJhsdKB0l JSMhIR8gJiQfJSMwJSEwJSEnJh8jIhsfJBsiJx4fKRocJhcaKBYXJRQbIBsdIh0nIB0rJCEeHxka GxYcHxgeIRobJRYXIRMbIA8cIRAhHRIjHxQeIhYgJBcoJR4gHRYcIBUcIBUcGQwcGQwdGgobGAka GhAZGQ8bGAsmIxU+LA9QPR1jPxluSSJzTitzTit9TSd+TiiGVSCQXiiNXCSJWCGLUyKOViV9VyF6 VR+EUyR7Sh1zSSBlPRZjOxJuRRqCTCOLVCqIVit5SB9oOxFhNAxcMAxkOBJ0RSNwQSBXPBtDKQso HA8jFwoeFBAgFhIXEw8YFBATEwgXFwwaFg4bFw8cFAkeFgoZFwwcGg8WFg0XFw4gGQ0cFgo0OTg3 Ozo8Ozg6OTU3OTg1ODc0OTgxNTQ3OTU0NzM0NDQ1NTU4NDo4NDozOTswNTgtODIxPDc6Ojo0NDQz NTQ3OTgwNzQtMzEyMjQ0NDc1NzA5OjMyNDUzNTcvOT4uOD03NToxMDQzNDA1NzI1MzQ5Nzg3NTow LzM0NzgzNTczNzEzNzEqMCwtMy81NDE0MzAwMjEvMTAsMi4tMy8qMCwqMCwtMTAuMjExMS8zMzEw LyoyMSwxMzAxMzArMzAoMC0vLy0yMjArLzAmKisrLSwsLi0sLC4pKSspKygtLywnLiYmLSUnLCUn LCUlLiUlLiUkLiklLyooMSojLCUoLCkqLissKSQrKCMhLSkhLSkqLCAoKh4oMSooMSoyLywxLisu MjEuMjEsLzIzNzo6MTc9NDo/PDk9Ojc/PDk9OjdEPjxDPTtBOzI9Ny4xMh8rLBk1Mhg3Mxk5PSU5 PSU+OSdFPy0/NCo/NCo/NSZBOChAOhpGPx9HPihDOiROOyhOOyhOPipJOiZPNCxOMytEMyhEMyg5 MyIzLh08LSozJSIwKCswKCskKiYpLysmJyMvMCwvLicsKyQqKSIqKSIhJBshJBsgJyEgJyEkJiMj JSIgJx8fJh4iJR4kJyAkJCIjIyEiKR8hKB4fJxkgKBoiIiIkJCQyIx4yIx4oJyIjIh0eJRseJRsf IxYfIxYhKRseJhgdJBweJR0nHBooHRsdHxUZGxEaHxgcIRodHhgZGhUdHBYkIxwjHxYfGxMbHxQZ HRIcHRYaGxQYGg8WFw0YGwoYGwoaHQwWGQkpGhUxIhwpIQ8uJhRELA1WPRtlQRptSCBvSCJuRyF0 RRV7TBqJVBqRWyCJWCOHViGHVCmIVSqIWCyIWCyHVyt+TyRpPxdfNxBjPA9uRheDTSKJUyeHTCWE SSN9Rx94QxtqPxpyRiB9TCxyQSNkPyRTLxYuIRgjFg8eFBIcEhAWEgoWEwoTEwoUFAoaFgwYFQod Fg8bFA0YFwgZGAkaGgcbGwgiFgogFQg5Ozo4Ojk5OTs5OTs3NDU4NTc3OTg0NzUzODczODczNDo1 Nzw4OUA1Nz4zNTQ1ODc3ODE3ODE+Ojs6NTc1NDE5ODQwNzQuNDIuNDQxODg0ODI0ODIyNTkwMzcx NDovMjgzNDw1Nz4zNzE1OTM8OTU+Ozg5NzU1MzI8OTU8OTU3OjA1OS8zNTIyNDEyMjQyMjQvMzIw NDMqNDErNTIoMSopMisrMzApMS4tLy4wMjEsLygvMis1NTMzMzEyMjAvLy0xMTEyMjIoMSonMCku Li4sLCwwLC0yLi8tLiovMCwqMCQoLiIlLSooMC0lMDAmMTEtMCsrLikpMComLScrKyksLConLCUm KyQmLCgnLSklLickLSYmLiknLyovLjIuLTEnLjMpMDUsMy0qMSswNS4wNS4yMy8xMi4rKykpKScu LB8vLSAyLR8wKx0yOCM1OyZFPCZEOyVBPi5JRjVIRTFMSDREPi1MRjRcTj5XSTpTRC1WRzBVSjlX TTtVSjxRRzlPRzhORjdTRDFQQS9OPyNJOx9HPyM/OBxBMCtALyo+MCk1KCExKikwKSgnKR4sLiMr LCQnKCAlJxkmKBolLCIgJx0cJiEgKiUmKCclJyYmKyQjKCEjIyMnJychJB8kJyIkJx4lKB8mJhsn JxwoJyIkIx4qKB4oJhwiIxslJh4lJCEjIh8mIhgnIxkhJxohJxoiJyAiJyAhIRYgIBYdIA8bHg0V IhQUIRMaIBIYHhAfHRMjIRYiIBYiIBYhHhceGxUfHRMeHBIfHRMfHRMcHBMdHRQbHRMYGhAqHQ4z JhY+KxVJNR5hPhdvTCNvTCRvTCR3TSF1TCB3ShV/UxuIWCOGViGDVCaAUSR+UyZ9USV9ViZ9ViaE WCl7UCJnPBheNBJlOg1zRhZ7USF/VSSGVCuDUSlwSBdvRxZwTR9yTiB4TjJrQyhVOyhFLBoiHhYa Fg8dFAseFQwXFgQWFAMWFAcYFgkYFgsaGA0dFw8aFQ0XGQcYGgcXGQcbHQokGAwjFws3OTg1ODc8 PDw9PT06OT04Nzs5Mzk7NTs5OzgzNTI0NzU4Ojk1Nz41Nz40NDI5OTc1NzAzNC45MzE6NDItMisv NC0yODAyODAwOTMyOzUvOjQuOTM6PTU5PDRDOzVHPzpEREFBQT9KSTVJSDRHTD4/RDdEPC1IQDFQ RjVUSTlQTUlFQT46ODs6ODsvNDcvNDc5OzwwMjMmODEmODEsMi4rMS0sNC8sNC8pOTUoODQqMjEs NDMyMTUyMTUtLy4wMjEvMi0vMi0sMi4pLyssLyoqLSgyLi0yLi0wLywzMi8uMiYvMyclLywjLSop MDEqMTItLywqLCkqMScqMScuLykpKiQqLSYoKyQpLC8rLjEpMCooLykpLikmKyYnLy4oMC8mMi4o NDAuNS8rMiwjMisnNy8kMSkmMysvLRg0Mh00MBo4Mx01Mxo7OR9IRi9GRC1WSDtVRzpKTENWV05i X0VfXUNdVUFnXkpvYVBoWklvYkhvYkhtZEdyaUxuYUlrXkdfX1NfX1NpX0dkW0NjWzxaUTNYSTJY STJOPjJQQDROQDE/MiQvMRkwMhomLRglLBcnKR0qLCAjKR0dIxchKh8fKB0eKiYeKiYiJiMgJCEh KRwhKRwnJiEoJyInKx8hJRklKCEmKSIlJRsnJx0kJhsiJBklKB8lKB8iIx0kJR8kJR8lJiAnJB8k IRwiIxsmJx8mKh0iJhkhJB8fIh0aIQ4aIQ4aIw8ZIg8gJRIgJRIdIg8fJBAkIhYlIxYgIBYgIBYi HhMiHhMlIhQkIRMiHhMlIRYjIRUgHhIvIRE6KxpOMxFhRSBrRhhyTB11USN5VSZ+USp1SSN3Rxp+ TiCAUyN+UCFzTiVvSiJpRhloRRhzTiZzTiZ7SiF0RBtdOhlaNxZfOhVrRR5vUCNyUyV6VixzTyZj QxNtTBprUCdrUCduSSplQSNJNR44JQ8ZGhYWFxQbFAocFQsZFgccGQobGRAZFw8WFgcaGQofFw4i GhAeGwsaFwgZFwYjIQ4lIQ8kIA4yNzg0OTo0PDUzOzQ1Ojk1Ojk4Mjg/Oj86PD0zNTdBOTxAODs8 PDo8PDo/PDQ+OzM8PDo3NzQ+OTRAOzc1OTMzNzFIPUBKP0NAQT05OjUxOis4QDFXRztiUUVoWklk VkZYT0VdVEltX0plWERcV0BcV0BzXEF4YUZwW09tV0xjU09TQz9BPT4/OzwyOTQxODM4OjcvMS4l NzQkNTMxMzIuMC8wMzkxNDopOzcoOjUoMCsrMy4yMzkuLzQpMS4pMS4uMjEvMzIqMTQqMTQnMjQn MjQvMTIvMTIvLCcxLikwNCguMiYpLy8qMDAvMDUsLTIoLTEoLTEnLyomLikrLSotLywuLi4sLCwh LSkiLiosLygrLicqLSYnKiMpMDMmLTAqLC0tLzArLicrLicuMSoxNC0xNCAvMh46NR9IRCxKRzdN STlPTTVTUDlnVkBnVkBuZVFuZVFoal1ucGN5d1t9el57cmSDeWuId3WId3WLf2+EeWmGeF+Je2OO e2qQfWuNf2eJe2OGd2iDdGV/d2VzalpyZEpyZEpqXExoWklWU0FMSDhNTDhOTTk7QC01OygyLx0v LBouLB8mJBcaIhUgKBocJR4eJyAZKiIYKSEcKR0gLSEoJyInJiEnJxwmJhslJiAoKSMjJiEjJiEg JiIfJSEhJh8gJR4mJyMmJyMeIxobIBclJiIjJCAhJBsiJRweJR8eJR8hISEeHh4ZIA8cIxIfJhUe JRQcIhQdIxUYIQ4ZIg8bHBUdHhYcHRcbHBYhGxQjHRYlHxUhGxEgGxYjHhgjHRUjHRUvIg81KBRO MxRcQB9lRyVtTit1TyxyTClySSptRSZzRCJ3RyVuSSRtSCNdQxhYPhVKNSBNOCJePSFnRShcOBdP LA5AKhE/KRBNMhJaPhxfQBtdPhlfQCdVNx5OMBVYOh1TPBpROxlTNR5ILBYwKBIqIg0fHBcbGBQe FhMgFxUdFgwdFgweGA8eGA8dGg0dGg0hGQ8hGQ8eHAocGggcGQodGgshHA8fGg44OTw6Oz43QDc4 QThDRzpESDtEQTVKSDxGRDVIRjhbTERaSkNXUT5XUT5bVDtWTzdVUENTTkBbTT1QQzNOPTdaSEFq TUlnSUZUSj5PRjpKRzdYVUR1Xkp7ZFB0a05rY0ZtXUNrXEFvXEBwXUF1Y0B3ZEGMbUqJakh1Y1Nw Xk5iVk1WSkFHRDw+OzM6PTU4OzMzODcyNzUpOzcmODMwMi8vMS4zMjkxMDc1Nzo3ODssMC0uMi8w NDUuMjMiMjAlNTMzMzEyMjAlLi8oMTIrMjUrMjUzMjcxMDQrLywmKicsMSwsMSwtLS8vLzEnLDAr MDQkLy8lMDAoLS8mKy0pKyorLSwoLSYpLiciMSofLicqLisoLCklLCQlLCQlLSokLCknKCApKiIs LiAsLiBAMyRAMyRFPidHQClPTjpTUT1XVEBdWkZnYkpzblZ3bVZ/dV6CeWWLgm6HgHmJg3uJg36Q iYSRg4OUhoaihoani4uWjoCUjH6hjX+hjX+lkIankoiikoCejn2ViYORhn+SgnmRgHiMg3KJgG+E em97cmd0bV9rZFdvX0lnV0FTTzxMSDVKQzNAOSo1MBUwKxAqKh8jIxggIx4dIBsbJSQbJSQlKCEo KyQmKSAkJx4mJSAjIh0lJiAlJiAeJCIiKCYmJiQlJSMlJiIlJiImKh4lKR0gJyEgJyEkJCInJyUd JSAfJyIZJBsaJRwkIB8mIiEgJBYeIhQjJh8jJh8SJBQSJBQXIBcVHRUbHxMbHxMZHQ8ZHQ8eHBMf HRQkIxIiIRAeHxkhIhweHxkbHBYhIRYoKB1DLRpKNCFWORhfQSBjRSRfQSFVOiJTOCBaNRtcOB1Y OxpXOhlNNRdFLhE1JQ06KRBGLxJJMhU0KhMxJxAwIw8tIA06KA9ALhRKLg9JLQ9JKxZDJRA4Kw84 Kw8xKhAyKxE0JhIvIQ4fHA0cGQoaFQ0YEwsYFQoZFgseFQ8gFhAkGQokGQocGA8ZFg0aGg8WFgsY GwwYGwwZFgsYFQojGxEhGQ9URT5XSEFXTkVVTENhUTpkVT1hVTppXUFkWDllWjp1Xkp1XkprWD1w XUFyXD10Xj91X0V1X0V3XTtvVjR4XD19YUF4X0l1XUdlWEFdUDphUDlvXkZ/ZE1/ZE13ZEF0Yj9w XjpnVTFvWjd/aUWDbkl/akaIbkGEaj55ZEN4Y0FqW0dbTDlKRDo+OC43Nzc6Ojo1ODcyNDMoMTIp MjMrNy8pNC0uLzQyMzktNDUsMzQ0MS40MS4vMTAvMTAqMi8qMi8xMzQxMzQqMCwqMCwyMC8zMTAu MjMsMDErLikuMSwrNC0sNS4tLzArLS4mLTAoLzIiLiojLysvMTAsLi0nLSsmLCooLSgpLiklMS8i LiwoLC0lKSokLCchKSQjJxomKh0rJxQvKxc5Lx9ANyZHOyRNQClVTjhcVT5nXEppXk11blZ4cFh0 b2GGgHKLg3mOh32VhnedjX6Zi4ajlZCalpKfm5enlI2qlpCrlZKym5mmn5qlnpmqm5mrnZqqm5uo mpqjn5uhnZmmmpSlmZKhkISjkoefl42XkIaRjYeNiYOOh32NhnuIeWiDdGNtaFxeWk5dVklVTkFQ QSdGOB4xKxcqJBEiIBYkIhcdJB4ZIBoeJBgeJBgjJyQiJiMiJCMgIiEkJyIjJiEcIx0eJR8lJCEn JiMnJSQnJSQmJSAlJB8kKyMeJR0jIxkmJhwkJhskJhseJhkdJRgjIBkmIxwkJxUdIA8eIxwgJR4S JBgRIxcWIBcSHBQaHhIaHhIdIA8bHg0aHBIaHBIaHQweIQ8cHxYdIBcaIh0WHhkWGw8XHREzJBY8 LB5HMRFIMhJBMA8+LQ07KBI9KhRHKQpHKQpMKgxIJwo7KBQyIA0lGwQtIwozKQc0KggjJRAeIAwl HQwpIQ8yIwg0JQo7KBA0IgszIw80JA8sJQouJwwsJQwoIQkkHwgjHgcgGQobFQcWFgYTEgMPEQMT FgYeFg8dFg4bGAoZFggTFQkVFgoaFwkaFwkbFw0bFw0XGQ0VFgofGg4cFwttUDt7XkhwXEluWkdz XUBzXUB/ZD6IbUaDbkOEb0SLaFCAXkd0VTGDYz6IYT6RaUaUckeScEaOaTiOaTiObj2SckCHaUR/ Yj1rVTtnUDdtUzp4XUSDZ0eCZUZzXjhwXDVyWjRzWzWMaUOWc0yObkiHZ0GOZTuWbUGMck6Eakdt Xk1aTDtKQz1AOTMtMTAxNTQyMDE0MjMmLzQoMTcyNDEwMi8xMjUxMjUvMzIvMzI3MjE3MjEyMjIy MjIuLyswMS0vMTAtLy4rMS8nLSsuLi4xMTEwMDAuLi4qMSkrMiohMCsjMi0vMTAqLCsoLCsrLy4p MS4qMi8zNDAxMi4pLy0lKykuMC8nKSglKzMlKzMmLCgjKSUcKhodKxssLBUwMBg5MyNAOypEQzFP TjxaUDtjWkRyalN3b1eDcmGRf26MhnSQiXiSi4CXkIaZlomal4umm42nnY6rmpKwn5eoop2wqqWz opqzopq7paK6o6G7paK4op+ypqKwpaG7qKW7qKWypaaypaa7pp+4o522oZ6znpuwnp+wnp+um5qn lZSmnZabkoyejYafjoeGgn6Cfnp/dWp3bWJlYVNhXE5VUT0/PCksLhUkJg4YIBQVHBAUHBYWHhcc JCEhKSYgJiQhJyUiJyAkKSIpKR8oKB4kJR8lJiAnJCEpJiMlJiAlJiAiKR8cIxkhIRYlJRolJh4k JR0jJh8kJyAgHhIfHREfHwsfHwseJhYaIhMTIxQTIxQbIRYYHhMbHxMbHxMcHw8cHw8bHxEaHhAZ Gw8cHhEYGwodIA8iIxQoKRkmKxYfJBA4Kxw5LB07NRc1MBM8MBQ8MBQ3JA07KBA/LxI8LA89LRA8 LA84Kx01KRs0LRUxKhIsJgg0Lg8wLR4vLB05MSAzLBs4LBo9MR88LxE4Kw45KxM9LxY9KhY/LBcu Lg0rKwovKRYyLBg5JBknFAocEgMcEgMVFgYTFAQcFwocFwoaFwkcGQobGgwXFgkZFQcaFgcdFxAe GBEZGw8XGQ0gHQ8eGw5+YkWGaUyDbUqDbUqNb0iSdE2UdEiXeEyVdUaOb0CGYz9/XTqEXTCbc0Sm dEOlc0GifUOadTyZd0iUckSbc0GedUSQaUaEXjxoUTVkTjJqVTtyXEF3YT13YT1wXDRwXDSAYTyG ZUCVb0aZc0mVa0CVa0Cdb0Wdb0WNbk6CY0RrYVFQRjhDQ0M3NzcwNzIxODMxMTExMTEtMDUsLzQs MTUqLzMqMTIpMDEsNC8sNC8yLi05NDM7Nzg5NDUyNTAuMSwlMSslMSssMi4mLCgqLisrLywrLCYr LCYrMCkqLyglMSskMCopMCoqMSsoMyomMSgrMCcsMSgnMSwjLSgoLyknLigrLy4oLCsnLDAnLDAq LSQkJx4lKxIwNxxAPCdDPilMQTBaTz1aUT5pYU1qZFF0bluEe2qJgG+QhnqUiX6fkIChkYKjnpKl n5Ssn5WuoZa0o5q4p56yp5uyp5u0p6izpqe6q6u8rq67rKq7rKq7rKq6q6i8rqu7rKq/rqXDsqi6 q6u8rq67rqW6rKPBqK6+pqu+qrC7p666r6O2q5+vq6ejn5uqmZGrmpKhloydkoiRjYSJhn2CenB5 cmhrbVpbXElXTzM+Nx0eJRIVGwoSHQ8UHxEiKyIeJx4lJiAmJyEnKiMkJyAeJBgfJRkjJRkkJhoh JxshJxsjJRokJhsiJx4cIRgbJB0bJB0bIx4ZIRwiIB8kIiEgIhcfIRYfIRQeIBMdHRQcHBMbIRYd IxchIh4cHRkbHhYdIBckIxQjIhMYHhAWHA8UGQ8YHhMgIxAkJxQrLxgrLxg1NyM5OiZFOypANyZH QCdDPCNEQyRBQCJIPi5GPCxMQTFHPS1IPClIPClEPTFEPTFFOi9FOi9EPi9HQTJDPTlHQT1ORTlM QzdGQDxFPztPQCpXSDFURTBRQy5XPzJbQzVWTDpQRjROREBJPzxEOzJDOjE6NCMvKhksJg8jHQgh GwgfGQciHAkhGwgYGA0XFwwUFgcWGAgXEgocFg8aGhAXFw4fGxMcGBCOa1GVcleVdE6SckyefUaj gkqif1Gee06feEyWb0SMZziOaTqhbkWve1Gwf0iufUaqe0ioekefekWmgEqsf0Wjdz2aalCJW0Fj TThfSTRkV0NlWERwVzhyWDlzWDN1WzV/YjyDZT+NaDeRazqRaTmackCXc0ORbT2IaER9XTppWExX RztERUo6O0AwOzosNzUwMjMxMzQ1ODk0NzgxMjUwMTQmNDMkMjEqNC8oMi0zMTI0MjMzMzMzMzMs NS4rNC0iMSomNS4uNzMpMS4sMSosMSosLCosLCouLjAuLjAwNDMrLy4pMikqMyokLyYhLCMqLyos MSwqLyYoLSQuLSgrKiUrLywqListKywqKCksKh0xLyI0Mxg+PSFWRz9dTkZiXEhqZFB3Z0yDc1d6 eGiGg3OQiYSXkYyekI2fkY6qlZesl5qqoZqwp6G2o5+6p6O/rK6+q6y6rqy8sK/Bqq+7paq/rqbB r6e7r6i/s6y+sLK/srPBrqzCr67BtLDCtrK+sLS+sLTDsK/DsK/Bq6jCrKq+srC8sK+7s6e4sKW2 sLK0r7C6p6O3paG3oaiym6OhmpealJGRkImHhn+HhHd6eGpwaldcVkRARi8pLhkfIw4cIAsdJRgf JxokKCUjJyQnJykkJCYiJSglKCsmKSQmKSQYKB0WJRojISAmJCMlJSchISMXHR0YHh4gIiEgIiEm IiEkIB8pJRwkIBcfIBggIRkiHhYhHRUbHRogIh8hJh8ZHhcfIR4gIh8dHRMfHxUTHBcSGxYVHhEY IhUtKyAzMSYwNydARzdPSkBMRz1bSEFeTEVeU0ldUUhTVkBUV0FjYVFcWkpdWE1bVkprWlNuXFVf WE5hWk9pWFVtXFhjXE9jXE9pV1BrWlNpV0htW0xpW0ppW0ppXEVuYUltXE9vXlF4ZVh1Y1ZvXlFy YVRvYVNzZFZqXlVlWlBYVU9NSUQ9PiovMB0uKBMlHwsiGw0kHQ8eGg8dGQ8VFwcXGgkdGAoiHQ8c FwsbFgoiGQ4iGQ6Wbkyle1ifekWbd0Grfz6ugkCsglaoflOld0iecEOWb0aac0mib0mndE6qd0Ss eUasek2vfU+sgEawhEmuhFGmfUqXalaEWEVnUUZeST5iUz9kVUF0VT14WEB/YUF9Xj9/YUGAYkOM YTSWaj2ZdDmZdDmXc0OWckGIZ0Z+XT1tXEhdTTpDQUY5ODwuODsrNDgtNDgtNDgwNTgzOTszMjcz MjcqNzIoNDAtNTArMy4vMTAuMC8uMjMuMjMrMy4uNzE0ODIwMy4uNS8mLScsLCwzMzMwMDAuLi4u Li4uLi4xMS8vLy0oLSgrMCsqMCwlKycnMSwnMSwxMiowMSk0LSgzLCcyMy0tLigpJxwsKh80MyFA PyxOTTlRUDx5Xkd9Ykp+bleDc1yQf2qbi3WXjIijl5SjnZqoop+mnpKuppq0oZq4pZ66p6O8qqbB qqrFrq6+rrC/r7K+r6/BsrLLsqzGrKfBsq/Cs7DBsrK/sLDGsLDKtLTJtrfHtLbGt7S+r6zBsrLB srLHsLPJsrTDsKzDsKy8s6y8s6y/sqjBs6q+r6++r6/CrKrBq6i/q6+7p6uzp6Gzp6GqpZeln5Kh mY6VjYOIgn16dG9iZU9WWkQ7Oh4nJg0iKBsfJRgiKCQjKSUmKCkkJicjJSQoKikqKyMmJx8YKiAW KB4jJB4lJiApKCwmJSkfIyAfIyApIyMmICAnIB8pIiEnIRklHxcgIhYeIBUdIBceIRghJiEhJiEh JSQgJCMhHx4hHx4dJRgdJRgaJRoXIhcgIhUlJxk3MSNBPC1DSTtQV0hlWFRuYVx9aWd+amh5bmh9 cmtwc2Rtb2F0clh0clh5cF14b1x6cmF6cmGAeWt5cmR/c2mAdGp6cGV7cmeLd3CEcGqEcmSLeGqQ emSNeGKIeG2IeG2HdG2HdG2IdWWMeWmMcGmOc2uLd3eQe3uEc3R7amt6b2lzaGJdXlVJSkE7QCoq LxobHgwbHgwcFg0fGQ8VGgMXHQUjHg8nIhIjHA0jHA0hGgsfGAqWbUOedEmleT+ofUOygEm3hk6z g1usfVWic0mbbUSUakOSaUGZZz6hbkWnckSqdEasfkqwgk6vgkyvgkyrfUyld0aUalV9VUBjTTxe SDhoUTVlTzN0UTh9Wj+EXEV+Vj+DXDuHXz6UZEGebkqheUObdD6eckGabj6Ga02AZ0hqW0dYSTdD Pj1APDs1OzUwNTAnNzEnNzEqNTgqNTgzMzEzMzEtNCosMykrNTIrNTIwMDAyMjIyNDMxMzIrMzAp MS41OTEvMispMSwnLyovMTItLzApMS4pMS4vLzEqKiwuLi4tLS0jLCUnMCkoMSooMSomMiwkMCou MSwvMi0zMi0xMCsxLikwLSgsJSA1LilAPy5RUD5iXkloZE+DbluIc1+Xf3qfh4KhlZGqnpqompqw oqKwpaO2qqiyrJ+vqp28qKG+qqK/qqfBq6jFrqvGr6y8s6++tLDCr7DGs7TJtK7JtK7CtrLBtLDG s7TDsLLLsK/Os7LGt7TGt7TJurfDtLLHsrTFr7LLuLrHtLbDtK/FtrC+t7S8trPDtK/DtK++r6rC s67Cr67Cr66+r6y+r6y8qqi7qKe8qJq/q52noZmlnpaqnZ6ZjI2Efm11b15PTjw/Pi0oMR4dJhQh KCIkKyUnKyolKSgmKCcmKCcoLB8lKRwbKB4bKB4hJxsfJRkoJCMnIyIjJBwjJBwnIxkmIhghIyAf IR4iHhUjHxYcIxIdJBMeIBUfIRYlJB8lJB8jIh0kIx4iIx0fIBoaIBUcIhYeIhYiJhklKBcmKRg6 OSVKSTRUVUxnaF54cGeCenCMfnuQgn+UhoaUhoaJiHSHhnKMhHeOh3mag3OWf2+Ng3WSiHqMhnKO iHSOiX6OiX6Rf3uUgn6VgoaOe3+Uf3WUf3WVgH6VgH6Uf4KVgIOagnuehn+aiHOaiHOffXihfnmX g32bh4CUgoCRf36Vf32Eb216bmVqXlZNTkc4OTIoJRQiHw8bFw8gHBQZHQcXGwYaGQkhIA8fHQoe HAodGQ8bFw2VaTydcEOqfUOugEazhk2zhk2ygFyreladb0aUZz6Raj+QaT6RZTmdcEOqd02uelCu gEivgkmqfU6nekyreE6lckiMaE15VjxfSTRfSTRiTzRiTzRyUDp3VT54VUNzUD6EVzmSZEWOZ0SS akeVb0OMZzuSaUGMYzx5ZUluWz9cUUNTSDo4Pjw3PTszODQwNDEwMi8wMi8sMjIuNDQuODArNC0v NzArMiwyNzMyNzMwMS0zNDA1ODcyNDMwMjMuMDElKysoLi4qMCwpLysnLywlLSonMS4kLisqKioq KiooKy4nKi0oLiwpLy0lMCknMisnMCkoMSosMSwpLikyNyoqLiItKx4wLiE4Lh4+NCRYRz5qWE9u aFV+eGSSgnmXh36skIuzlpGrnZ2woqK2o6K2o6K0p6i7rq+8s7K2rKvFsKjFsKjBtLC+sq7Cs7C/ sK7Cs67Cs67Cs7DHuLbFtrDHuLPGurPDt7DJs7PHsrLLtrjKtLfJtrLJtrLHuLPDtK/FsLTHs7fJ vLvGurjBs6rGuK/Dt7PDt7PGurPFuLLBtK7Dt7C+tLDBt7PCr67FsrDGr6/Frq7FrJ/Hr6K4r66y qKe8oqa0mp6ekoyLf3lkW1BWTUMxOSYjKhgiKiUjKyYjKiIkKyMuJh4rIxspJxwpJxwnKR0iJBgl JxwiJBkiIRolJB0kJhsjJRokIhgiIBYhJB8bHhkmJBclIxYeJBYcIhQWIBYWIBYlIh0jIBshIRUk JBcmIBclHxYhJRkcIBUYIBMaIhUdIg8iJxNAPC9RTT9cV01wa2F+eHOHgHuRg4CWiIafjJChjZGl lIiikYabi3+fjoOrjoujh4OjjH+nkIOXkIKXkIKei4ChjYOfi4CdiH6eh46dho2eiYOfi4SZjYmZ jYmehIajiYuiiYSji4afh36dhHuignumhn+li3+mjICih3uhhnqih3mVem2QeWqAalxjWk9JQDcv KBcoIREaFg4hHRQZGgoWFgcWFwgbHAwfGAoeFwohHA4dGAqhckere1CshE2qgkqugE+ugE+je1GZ ckiMZz6HYjqNZDuXbkSdb0WidEmfeU2adEibdEebdEeVbkqVbkqOZ0WGXj1vWEBjTTVPPzFVRTda TTlbTjpoTT1oTT1lST5pTUFtVjxzXEFyXjdyXjd6XDt4Wjl6WER3VUBlTzpaRC9HQzg1MScpLy0s MjAtMS4yNzMzMzEvLy0uNzUtNTQuOC4tNy0zMzEuLiwyNzUzODc1OTMvMi0yNzUsMC8tLDAxMDQv LS4xLzAsMC8pLSwpMCorMiwjLyklMSsuMC0rLSotMTIuMjMrMS8rMS8zNCwuLycnLSkqMCwsNDEq Mi8zNSkrLSElLBoqMR9AMxxQQypfVURzaFaCfXKQi3+dlI2mnZavopW0p5qup6Wyq6i6q6i+r6yz q6u6srK6r7C6r7DDrq7Drq7Ju7zCtLbGurjDt7bCuLLDurPDurbDurbDurjFu7rGurbKvrrNsLfR tLvNtrvHsLbGsqrGsqrKt7PLuLTHs7fHs7fFu7fGvLjLurLNu7O/trK/trLBvLO/u7K/u7K+urDB t7PBt7PBsLPDs7bDrq7KtLTGs6/BrqrCrrS8qK+6paW7pqavoqOajY5+emhhXUxBRDgxMygkJyol KCshKRwjKx4jIxkjIxknJxwoKB0nKCQkJSEoJR4oJR4mJBklIxghIhwjJB4hHRwgHBsdIh0ZHhkj IxYlJRgcJBYXHxIdIx8aIBweIBYjJRokIhYmJBcmHxwlHhsYIRgYIRgjLCMeJx4fIxUoLB1KPzhb T0dtY1h7cmeIenWShH+ejYamlY2hlJejlpqhl5SimZWil42jmY6rlZWmkJCqkYurkoynkpColJGl lIiikYanlIanlIaojpCojpCokIuqkYymjYirko2ojYmmi4edh4SeiIaejIuhjo2ijYahjISei4Sf jIaljIOji4KhiXmhiXmahHCIc191Y1RYRzk4Lh8wJxgiGhMiGhMYGA0WFgsiHBIjHRMeGQscFwop Hg4mGwufeEqje06jfUyadESZckaXcEWNZ0WHYT+CYj59XTqEYUGMaEiSakWOZ0GHZz+EZD2OZESI Xj59WEB4VDxyUDhvTjVVRjFOPys/OzBBPTJMPDBNPTFQQDJQQDJKQDJRRzlXTjdYTzheSitjTy9f RixbQShkRTRhQTFKQC9DOSg4OSUzNCEsMy0sMy0wMSs0NS8zNDAyMy8wNDUxNTcvMzQwNDUvNDct MjQuNTsuNTsyNDMxMzIuNDQrMTElNC8lNC8rMS8qMC4pLSwpLSwsLyYuMSggMCUeLiMgMSseLykq MDAtMzMoMi0mMCsqMSkpMCgrLy4rLy4jMi8hMC0qMScoLyUnLhwlLBpBMiFURDFcVUh4cGOMg3+Z kIyil5uqn6OzqJ63rKK4qqe6q6i4rKa/s6zBsrLBsrK8s6++tLDKt7jHtLbHs7fKtrrGu7zCt7jH uLPJurTCuLTDurbDurbFu7fKt7jPvL7Ps7rStrzHsLDBqqq4rqO7sKbGsLDHsrLKurzJuLvGurPJ vLbJurTKu7bJurrHuLjHu7TGurPGvLbFu7TGurbFuLTJuL7Ds7jHsrLKtLTFr6/Fr6/GrrPGrrPC rKy7pqavpaijmZ2MhnBvaVVMSjc5OCUjLicgKyQbKyAdLSIhKCAiKSEsKiApJx0kJyIjJiEkJCIk JCIgIRseHxkhIR8hIR8fHR4eHB0aIB4YHhwcIBQgJBcaIhUXHxIdIhkdIhkeHQ0lJBMkIhcfHRMg Hh8jISIeHhQfHxUpKSkjIyMiJhcqLh9KPzddUUhvZVt+dGmUf3WdiH6hjounlZGmmpajl5SnlZGr mZWml5eml5ermZemlJKmlJKrmZerlpaqlZWnlo2mlYyqlY6nkoynjYyulJKolIylkIiskY2uko6q kJSojpKhjISijYaijoiijoilkImdiIKdi4mfjYyeiIaeiIaijn6ei3qdh3OIc196YlRkTT8+NCcu JRggGBEgGBEfHAweGwseGhEcGA8gFhAgFhAlHwsiHAmMak6IZ0qLZTuIYzmJYj1+VzNzVTluUDRu US5oTCl0Vz11WD57XUB4Wj1vUTVtTzNvTzRnRy1iRyxdQyhNOilMOShDMhU8LA80MBw4Mx9DNSY/ MiM7ORg6OBc9Oig+OylJPCRFOCBHOR1KPCBIOxtFOBhJNRhHMxY3NxwyMhgxMh8uLxwrMSUpLyMs MSouMywzMTA0MjEtMTAyNzUrMjMrMjMqNzQpNTMqNDMrNTQvMisxNC0yODItMi0rMzArMzAoMSgm LyYqKigsLCosLyYsLyYoMSgkLSQgMSsjNC4nMS4qNDEoLycpMCgkMComMiwmMCslLyojMyofLyYk LSYjLCUrKR8zMSdAMyVYSjtnZFiAfnKajIyml5eoop+spqO4rKi6rqq+sKe+sKe6sKq+tK7Ct6y7 sKbCtq/BtK7Ht7rGtrjGtrjKurzHvL7Gu7zKu7jKu7jJurfKu7jFu7TFu7TGuLzLvsLOuLvKtLe8 qqaum5eonZmuop67pqPDrqvJvLvGurjLuLTKt7PJurfKu7jJv77Jv77NurbKt7PKvrfJvLbGvLvD urjLuLfJtrTLtLLKs7DDr6fKtq7Gs7LDsK/Bsq+/sK64qqWvoZuUjXt1b15eVkNAOScqLyYhJh0f LR0cKhocJhYdJxYnKR4lJxwlKBclKBckJx4lKB8hKiMbJB0kJR8kJR8aJCEWIB0TJBoTJBocIBQf IxYgJRQeIxIiJBkeIBYfHRMjIRYkIhgfHRQgHxwiIR4hHRQiHhUrHhYsHxYdIA4nKhZKQC9fVUNz ZVCEd2GZgHihiH+hlIejlomjmpajmparmZWum5esmZ+mkpmml5KilI6ilYymmZCmlJKmlJKnlouq mY2ll46hlIumkZGnkpKnlIOlkYCljIOljIOfi4SijYeji4aji4ajjoehjISZhHqWgniXfXiZfnmW g3WUgHOehnCiiXSdhnWSe2uCZVVtUUFHOCQxIxEfHA8dGg0dHAUeHQYcGwsZGAkZGAkZGAkfGgse GQpwTUZoRT5uSjVqRzJiRjFcQCxRPilKOCNVPR5UPB1WPytWPytVPylXQStYPCJWOiBQPR9JNxlI NRpFMhc8MiI5Lx89Kg8/LBA1MBM1MBM3MhM4MxQ+OBg/ORlDNRZJPBxKNx1HMxpEOCNEOCNEOBhI PBxGNBxEMho5OiY0NSIuMiUoLB8pLSEqLiIpLicrMCk0MjM3NDUwNDMuMjEsMjAuNDItOjgpNTMs NzUqNDMrMS8sMjAoMjEsNzUwMjMuMDEsMC0sMC0wMS0wMS0tLigvMColLicmLygoLycoLycnMS4q NDEuLSovLisoLS8pLjAnLSsmLCosMi4rMS0sLC4sLC4yJyc3KytDPCNWTzRqaluHh3emmpSvo52z qqO3rqe/tKq6r6XBtLDBtLC+t7S/uLa/tq+/tq/CuLTDurbDuLzDuLzKvMHJu7/JvLjNwbzPv8LN vL/LvLzNvr7Gv7zGv7zHvMHGu7/Gtru3p6yim4mRi3mbjH2jlISvmpS8p6G8sKzDt7PNwbrLv7jK u7bOv7rJvr/FurvNwb/KvrzKvrzJvLvJvLjGurbFuLTGurbGtrjDs7bFtrbHuLjDtLLBsq/CtrK/ s6/Drqu2oZ6RkId3dW1jWlBHPjUxNSgqLiEkKSAkKSAmKyQeIxwkJyAkJyAlKRwjJxoiJhciJhcb Jh8iLSYfIh0bHhkdJh0eJx4WKBwWJxshJRkfIxcgIBYiIhceJBYfJRYcJBYbIxYkIx4gHxohIB0i IR4jIh0hIBsmHw8nIBAkIR4wLSpJRzxcWk5uZVOEe2iWhHSfjX2olYuolYuonZuonZuqmp2unqGw lpeulJWjjo6bh4eajYSekYifjoOikYaqlZWqlZWjlZCjlZCeiYebh4SfhoebgoOUf3WRfXOQfnCS gHOZgHqVfXeWfniVfXeMeHWNeXeRdGGMb1yRc1qae2KagGmfhm6bgnSWfW+Ea1VqUz1FNx05KxMd HxUWGA8WFwsYGg4dHAwaGQoWGQoZHA0dHA4dHA5QPSxJNyZGOCBGOCBFLhFHMBNGORtFOBpKOBhJ NxdOOhxOOhxHOR1DNBlEMxdGNRlFOBxFOBxANCI/MyE7Nx49OSBGOCJFNyE8ORo7OBk8ORpDPyBU PixVPy1WPy1dRjNNPy9OQDBKRTVKRTVPQSlTRSxYSTdRQzBNRjBGPyoxNyEnLBcjKx4lLSAtLigu LykyMjI3NzcyOTQxODMyMjI0NDQxNTcvMzQtMjQsMTMxMjU0NTkoMzgqNTotNDgsMzcsMjIsMjIw My4wMy4uLykuLykpMCooLykpMCgpMCgoMi8nMS4pLSwsMC8uLi4xMTEwMDAuLi4wMywuMSoxMS8y MjA3LR43LR4/PRxRTyxoa1eDh3KlnZKzq6G4qqe/sK7Htq7Fs6u/trK+tLDBurfCu7i/trTCuLfG vLbGvLbGvsHFvL/Kv8PHvMHKvrzNwb/VwcXUv8PNvL/OvsHJvr/HvL7OwcXGuLy6rqemmpSejnKX iGuViXWZjXmjkouyoZm3paHFsq7Lvr/Lvr/Ov7/Nvr7NvL/NvL/NvL/NvL/Ju7zJu7zJvsLFur7K v8HFurvDu76/t7rBtre/tLbGsrbFsLTDt7bBtLPCr6u4pqKWlYx+fXRhXUxEQDAxNy0oLSQqKh0p KRwnKCQiIx8iJyAjKCEfJBshJh0mJhslJRohKSQiKiUkJyIgIx4jIxkkJBoeJhgbIxYhIhwiIx0b IRUbIRUZJxYbKRcfJhwfJhwnJRglIxYjIh0lJB8jJxshJRkeIBYiJBkqIxMxKhlGRixWVjtpZ05/ fWOWhnqjkoeslI22nZavoaGsnp6qnaGom5+zl5KvlI6jjoeZhH2ag3Sag3SdiXmei3qnkY6rlZKn koyijYeWg3OXhHSWgniQe3KLdFuHcFeMc2SQd2iQeHKNdW+MdGuReXCJdW+IdG6GblyGblyRb1+e e2udgHuliIOah3eUgHCGcldrWD9JPCQ1KRMhIxYZGw8XGQ8YGg8gGw0gGw0aGQkdHAsfHA0dGgs8 Nxw/Oh88MRY+Mxc/NBo7MBY/NBhMQCNUPyNNOR1QPSFPPCBNPiJOPyNJPiFKPyJHRSxIRi1MSDRH RDBVRypQQyZPSDBQSTFERzRJTTpYSjtcTj5uTUFvTkNqU0VtVUdaUT9bU0BbWEldW0xhUD1rW0dy X1ZrWlBeVkRWTjxHRy0zMxsjLBkhKhcqLiIuMiYvMS4zNTI0OjI0OjI4ODo0NDcwNDMtMTAxMjgw MTc0MzgzMjcpMzIoMjEwMjEwMjErMzInLy4yMDEzMTIuLiwsLCosLSktLiorLictMCkpMSwpMSwi LionMy8tMCsrLikuLi4xMTExMi4qKycqMSkpMCgvJhc8MiM7ORhWVDBzaleQh3OqnZK+sKa/sLDB srLBsqzJurTFtrbFtrbCt7jFurvCt7jBtrfJvLbLv7jJv77HvrzNwb/Nwb/OvsPPv8XPwsbSxcnL vr/OwcLLvr/Nv8HRwca7q7CsmoyfjX+XiW6fkXWbjoahlIurlZKwmpe0n5m8p6HGtrjJuLvPv8LP v8LUwb/RvrzNwsPOw8XFv8HGwcLLwcXHvMHGwcLFv8HGvr7Du7vBtry8srjGsrjGsrjGt7TCs7C+ sq62qqadlI2DenRrX1RPRDkzMC0sKSYpKiImJx8jJxshJRkpKR4qKh8hIhonKCAkKBknKxwnKiMi JR4oJhsmJBkkIBciHhYhIRYhIRYnJh8hIBkfIhIeIREgJBciJhkgJRIiJxQqJRUnIhIjIxYhIRUp Ih0oIRwoJBgnIxcvJQ83LBVOQCZfUTV0aVeDeGWVhHmikYaqlJG0npuyn6Gwnp+vm5+rl5u2nZes lI6mi4Ohhn6bfmqVeGSZf3Sdg3imjYiqkYyhjIKbh32Wf3OSe2+NemmJd2WJclSHb1GNc2WLcGON dXCMdG+McmSSeGqMcm2Ga2eEal+Jb2SQdWqZfnOfhomiiIyaiHiWhHSOdFVvVjlAOykvKhkhHhAd Gg0lHA4mHQ8gGQojHA0eGQ0bFgocFwkdGApIPi5FOytMQCdOQylTQTVVRDhRQS1bSjVbTDRYSTJi TjxiTjxfUDtlVkBtUDtuUTxlWEFpXEVtXExtXExwYU1vX0xjZVdjZVdiXVNnYld1Y1p1Y1p4a2N1 aWFwalZ0blpzcFd3dFt6cmF3bl11b155c2J6b2l3a2VtbmRjZFtYV0M+PSolMRcXIwsoKyQsLygu NDIuNDI1OTE0ODA0NDcxMTMvMTAvMTA1MzQzMTI3LjM6MTc0NzgsLi8yMC8zMTAwMSsvMCovLy0x MS8sMi4qMCwiKyQlLicoLiooLionKygsMC0kLyglMCksLScsLScwKzAwKzAzNDAtLioqMScnLiQy IiQ5KCo+OCNbVD19amGei4Cvm5XBrKbFtrbBsrLDt7bJvLvJs7bNt7rDuLrDuLrFvLzDu7vHvrzJ v77Lv77Lv77Kv8HKv8HPwsbRw8fNwsbKv8PLwcXNwsbPxcbKv8HBrLCump6ukHmrjXeokIu0m5aw oaivn6eulp6ulp60l5e+oaG7qKXDsKzHu7rOwsHUxb/PwbvJwr/Kw8HJw8LKxcPNwb/Nwb/Gvr7D u7vGu7/Fur7Ht7zGtrvHur7Hur7LvLy/sLC6r7CzqKqul5qQen1rX1ZIPTQ7LCk6KygnKiElKB8n Jx0kJBoqKB4qKB4iIR4nJiMgIxweIRodJBodJBokIxwiIRogIRsfIBolIhspJh8oKB0lJRohJRgg JBcfIxYfIxYjIw8jIw8kIRAmIxIpJhcqJxgnIxokIBceIQ8fIg8sJhEsJhFPOihoUT50YlyIdW+e iX+qlYusmpmwnp2ym6OznaW0npuznZqwmp2nkZSZhoyRfoSRd2uIbmONeHiUfn6XiYSdjomdkIaV iH6We3CSeG2UeXSNc26OdWiEa16Lc26NdXCLdXOIc3CJb2uNc2+NeXOIdG6Ib3CMc3SMenmSgH+f iYmmkJCjlISWh3iCclFoWDpQOSw7JRkpGg8lFgsiGgohGQkfGAwmHxIkHRAgGQ0gGAgjGwpeU0pe U0poVkluXE9qUU1vVlF3X054YU9wX0x3ZVGCZ2ODaGR6amN6amN3bWJ3bWJ1cl93c2F+dGp7cmiC dW2Dd259cmuCd3CEc2OLeWmJf3KHfW+Hf3SGfnOEfmqGf2uDemWDemWJemuIeWqQf3iOfneNg3mM gnh7fnplaGRaWEZDQTAxMxspKxQkJiMqLCkxLzA1MzQ3ODE1NzAzMzUxMTMvMTIwMjM7NTM4MjAz MTA3NDMrNzkoMzUuMTQqLTAqMi0pMSwtMTIrLzApMS4nLywoKyQvMisnMzEkMC4pMSwpMSwmLSco LykqLisqLisuMC8vMTAtMy8rMS0oKyInKiEuHxk/Lyk/MyFfUz6EcmSfjH6rn5u8sKzBtrfDuLrG urbHu7fHuLbLvLrFvLzFvLzGu7zKv8HJvr/Jvr/Kv8HKv8HNvL/Pv8LOwsHSxsXVx8vRw8fOwcXS xcnRycnFvLyynpCnlIami3uskYKwmqW+p7K7r7u7r7u+pq60naW2m520mpuzopnCsKfJurfSw8HO w8XNwsPKw77NxsHPxcbNwsPKv8HKv8HKwb/HvrzJvLjKvrrKur/GtrvCt77Ct77GtrjCsrS/r7e4 qLCnmZaNf31tYlNORDU1LikyKyYoKSUpKiYpKSkhISEnJRspJx0mIiMnIyQfHx0gIB4ZJBsZJBsl JCEgHxwZHBYbHhcgHxgmJR4kKBsbHxMaIxoaIxogJBciJhkpJhcpJhclJBUmJRYoKB4nJx0mJBoo JhwkJxQfIg8tIRM0KBlHNy5fTkV1Y1qHdGqaiYKjkounm5qrn56yoqW2pqi0oqGwnp2umZmmkZGV iImLfn+Lc26IcGuJeHeRf36Ri4iZkpCijoediYKRf3KNe26Mfn6Mfn6NeHWGcG6IdW+NenSUfoCN eHqMcHCRdXWJe3eGeHOHc3WHc3WLe36UhIedjoyilJGllYaXiHmDdGNuX09MOyw3JxkoHA8qHhEi Gw0fGAogGBAjGxMcGwsbGgoeGgcgHAlwaGJzamR/bl1/bl19bl+IeWqOe26MeWuOe26QfW+Sd3eS d3eHfn2LgoCIgHWNhnqDgnuCgHqGhIB+fXmGgHWIg3iagIKdg4SVgnqXhH2Mi3eQjnqRjXiSjnmV i3+Vi3+SiG6XjXOOiHOOiHOelIiXjYKViYaSh4ONiH14c2hkYUxGQy8sKw8qKQ4oKSMqKyUuLTMx MDczNTQyNDM1NzAxMiw0NzUxMzI1NzI0NTExNTQwNDMhOTQgODMpMjUmLzIpMzImMC8nMDUkLTIo MC8oMC8tLS0wMDAjMzMhMTEoMi0nMSwnLyosNC8oMi8mMC0mNTIkMzAbMCsgNTAlMioiLycuJxc3 Lx9GMRpiTDKGcF2mkHuspaW0rKy8tLTDu7vHu7fKvrrJurfLvLrKu7vLvLzHvMHLwcXNv8HNv8HR xcPRxcPWwsbYw8fOxcPOxcPUxsfSxcbNxcXNxcXLwr6+tLC0nYyokYChmY2ro5e6sre/t7zHvsbJ v8fJu7y0p6i4p5+3pp6zp6G/s6zGuLrLvr/KwsLJwcHKw8HNxsPSxcnPwsbHv8LHv8LJxcHGwr7J vLjLv7vKvrzHu7rFt7vHur7JurrGt7e/sri3qrCqlo6Vgnp3Z15TRDwvOTEqMywqKCsoJikmJyMj JCAkJhsmKB0mJiQkJCIkJyAiJR4fJh4eJR0jJBwiIxseIRoaHRYhJBskJx4fIxUcIBInKCQfIBwg IRkjJBwnJRgqKBslKRweIhYfJRkdIxcoICErIyQjJiEfIh0qJA8zLRdIPTRiVk10YlGJd2WhiIOo kIuqm5msnpuzpZ+2p6K2n6WznaKynpSolYuijoSUgHeMdWmIcmWCdG+Qgn2XkJCblJSqkYyji4aM hHmHf3SJgoSNhoiRf3uMeneNe32Ne32SeICRd3+JdW6NeXKHfn2DenmGeHOHeXSMenuUgoOfkZGh kpKljX6eh3iNc2R5X1FQQzI4KxwlIRYkIBUaGhAZGQ8eGA8dFw4WFwocHQ8gHgsgHguQg3eOgnWI hG+JhnCRhHqShnudiICeiYKfjIKZhnuMg32SiYORiIKVjIaXh36ejYSXjIaajoiWjYeVjIaZjoSZ joShjIKnkoidjX6fkICelYOhl4aWkn2XlH6ji4aljIeikoOhkYKflX2il3+blImakoidlpSXkY6S iHqDeWtwaFVORjQxMxkpKxIsKyQqKSIsMjIsMjIxNzktMjQwMi8vMS4uMy4tMi0yMy0yMy0zNzEx NC8nMSwnMSwrNTAkLikqKzAuLzQrMDIpLjAqLC0uMDEqLi0pLSwjLSwjLSwpLy0pLy0qMCwmLCgo MyopNCspNSQnMyIqNS4qNS4vMiEsLx4yLho3Mh5INRRlUSyIc12jjXeuo5e8sqa6rqrGurbHurvJ u7zJvLjKvrrPvLvPvLvLvr/Nv8HNv8HOwcLKwbzOxcHWxsnVxcfPxsXOxcPRysfRysfNycXNycXF u7e6sKy4pZq3o5murKi4t7PBwcPFxcfHw8rHw8rKwsLFvLy7sq68s6/BuLjBuLjFvrvLxcLNx8bO ycfRxcPSxsXUxsrSxcnNx8nKxcbJwr/Kw8HNwbzLv7vNvL/Lu77Kt7jKt7jHu7TFuLLFtrC+r6q2 n4ubhnJ9aFVYRTMyMysrLCQpKCUoJyQmJyEjJB4oJhsoJhsiJx4hJh0dJh8dJh8gJhogJhohJBsg IxoeHxcfIBgiJSAgIx4dIhEgJRQqJR8mIRsfIBghIhokIhgkIhglKBcjJhYjJRkgIhYhGxsnISEe IhYcIBUnJQ8pJxFFOiFfVDlvX0mCcluhg42rjZeompWun5qzpp2zpp27o6i4oaa4pZ6smZKqjoue g3+NenODcGmCd3WIfXuRhIaajY6eiIiZg4OGf26IgnCRgoSSg4aSfnuNeXeIeXuRgoSUen6SeX2J d32NeoCOfX6Qfn+QfXWUgHmRfX2WgoKekZKfkpSokIeehn2ZdV17WkNPQy03KxcZGw8bHRAXGgsa HQ4aGwsaGwscFwkiHQ4mIQomIQqiiYOehn+ZjoOakISejpGZiYyijYelkImjl4eekoKWkoyXlI2W koybl5Gekoyjl5GllIyjkoujlo2hlIuilYyjlo2flYeil4molY2nlIymlYyrmpGZlIiblouolI2o lI2mloSikoClkoKnlYSllIyikYmflpCbkoySiYiDenl1ZU9TRC89PB4qKQ4mLCooLiwsMTMwNTgx NTQuMjEqNDErNTIqMC4rMS8xMiwuLykzMzMyMjIyMy8zNDAxMiwwMSsrLy4rLy4vMS4uMC0yMDE0 MjMtMTArLy4qLi8rLzAkLygnMisvMCouLykqMiUqMiUwMiQvMSMvMCguLyctLyMrLSEzLh4vKhpD NRVlVzKEdFuaiW+yoZfHtqzBs7THurvJvr/Gu7zKvrzKvrzOwsHPw8LOwcLPwsPNv8PSxcnLx8PL x8PRx8bRx8bOxsbNxcXSys3Vzc/SysrNxcXNwb/JvLu4sKW6sqa8urjLycfJycnLy8vNy9HNy9HR ycvOxsnFwbzBvLjJvsLJvsLLwcXNwsbLxcLNxsPSw8PSw8PRw8fUxsrNxcXJwcHLwsHNw8LOwr7O wr7Lu8HJuL7NuL/Lt77GvLjCuLTJt67DsqizoZKaiHp1aFNURzM6NzMvLCkmJigoKCokJyAlKCEu LCEqKB0gJSAjKCMkJyAkJyAjJxojJxofJBsdIhkhIRclJRseJRsfJhwmJSIlJCEmIR0lIBwkJBok JBomIyAoJSIoJhsmJBkiJBgfIRYkJR8lJiAkJhgiJBYpKA8sKxE/OSRaUzxuYliCdWubhpCnkZus n5awo5qvpqKso5+8paq6oqewnaGrl5unjIiaf3uMe3SAcGmAd22Ge3KNe3qSgH+Oe26QfW+NfmuO f22VhoiUhIeVf32Vf32WhIaZh4iWg4eQfYCWf4eXgIiUeX+bgIeXe36fg4aeiIihi4ubjY2bjY2h jIyXg4Obd2OAXUpHOyY3KxcbHgweIQ8WGg8ZHREXGQcbHQolHQwlHQwqIAkoHgehjpCjkZKolJSq lZWhkZaejpSql5SrmZWinZCdl4umnZulm5qempSbl5Gjl5Snm5eql5aolpWml5Wml5Wompeompei mZKflpCnlZGolpKol5Csm5Smm5GlmpCql5alkpGml5WnmZaql5aolpWnlZSnlZSmmpmjl5aQi46E f4N4alNeUTtDQykrKxQcLScdLigeLy0lNzQmMjApNTMmNTAnNzEoMjEpMzI1MzQyMDEwNDMyNzU0 NzUxMzIwMi8yNDEkMy4fLikmLi0oMC8yNDMzNTQrMzAqMi8oLSYrMCkkMSUkMSUsLB8uLiEpLiUq LyYtMisvNC0mKikpLSwsLyoqLSgxLh0sKRg9NRpXTzGCdVidkHK3paHCr6u/tLbFurvJvr/Gu7zJ v77Jv77KwsLLw8PNv8HPwsPOwcXOwcXOxcPLwsHRx8bRx8bNxcXKwsLUy8vWzs7Wy83UycrVxcrR wca+wcLBw8XGw8XNysvRycvWztHRzdTPy9LUyc/Rxs3Nx8bJw8LNxcrSys/UxsrRw8fPw8LUx8bS x8nRxsfPxcbPxcbNycXJxcHNwb/OwsHOv7/Sw8POvsPKur/FusHHvMPNwb/Dt7bHu7rGurivpZmV i39vbVRPTTU1Mi8uKygkKCckKCcgJhofJRkpLSAnKx4nKCIkJR8lJB0mJR4cJx4cJx4cJRoYIRYj JBwhIhodJB4eJR8jISAmJCMeIRoeIRoiJBYkJhglJCElJCEjIxkjIxkjJiEkJyImKBwjJRkkJhgi JBYmKBEpKxRDOi5XTkFlXVd9dG6Ug42hkJqumpSzn5mvpaauo6W6pqyzn6avm5+qlpqhjX2Wg3OO f22Gd2SIeWqQgHKSgHOSgHOMe2KNfWOOgnWLfnKNhoaXkJCjjo6hjIyjjZKfiY6ijZCXg4aeh46i i5Kdjo6ekJCbiYiZh4ahi5Chi5CfjJKei5GbiYuXhoeRc1d5XEFFOyoyKRkWIQwVHwoWGQgbHgwZ GQ4ZGQ4gGw0fGgwiHQ8gGw2rlZqrlZqqlpqump6rlZ2slp6lm5qmnZuinpeinpehl5aimZemnZmm nZmsmpaql5SrmZWsmpalmZemmpmsl5eqlZWmnZumnZuompqnmZmnm5Wnm5WhmpejnZqsmZ2nlJem kpuqlp+mmZ2mmZ2omZ6ikpedkJSdkJSNi46Cf4N9bl9hU0VEPygtKRQgJiIhJyMiMC8jMTAfNC8f NC8lMzQlMzQtNTQtNTQ0MjMwLi8vMDUxMjgtMzMvNTUrNzckLy8oMC0pMS4mLi0kLCsvLS4vLS4l Li8hKisnMiknMikmMSgnMikwMSEwMSEqMSslLCYlLy4mMC8rKy0sLC4qLSgqLSgrJRoyLCFHOiJb TTN3blqdlH6qo6G0rqvFuLfJvLvJu7zHurvJvr/LwcLJw8LKxcPNwsPOw8XOwcLPwsPNxcXNxcXR xsfSx8nUzcrOx8XOy83Pzc7OycrSzc7Wx9TPwc3Fxs3JytHNx8nSzc7Ry8/SzdHUzdTWz9bYzdHR xsrOycrPysvNys7Nys7UxsrZy8/Yx83Zyc7Vx8vSxcnUxsfUxsfPycbOx8XKw8HJwr/Hv8LFvL/H vrzJv77Ju7/GuLzJu7/Dtrq7uLq0srOmmY6Shntta1ZRUDw1OzEoLSQgMigdLyUnKCAlJh4nJxwk JBkqKyUkJR8ZHhkdIh0WJB0XJh8YJBYWIRMgIR0dHhogIB4iIiAiIRwmJSAjJB4gIRshJRYgJBYe JRseJRsaIRsbIhwcKCIXIx0eHxcjJBwkJhsiJBkfJBAnLBc4NC9PTEZuX1GAcmOUgImijpeyl5m0 mpu0oaW6pqqwqK6ooaaumqGrl56qkoOag3SWg3WXhHeRhoSUiIeZhHqahnuWhHeQfnCXhoebiYua kZCdlJKll5mll5mjlJmjlJmrlpSqlZKmkJWokpeflJClmZWnlZahjpCikpejlJmZkpmalJqdkYuN gnuDbU9vWj1UOzQ9JiAeIBYWGA8aEgUlHA4jGxEfFw4aGA0bGQ4gGw8gGw+mmpammpalnpumn52s mZ2rl5ujmZqjmZqlmZeonZusl5eqlZWompWsnpmsnpusnpuon5umnZmimpqhmZmrmZemlJKhlpej mZqdkpaakJSnlZSolpWhkpKilJShjZGhjZGfkZGilJSomZumlpmijpWZhoyah4Cei4SNiYaEgH17 cmddVElAPCcrJxQlKiMlKiMrLSouMC0oMjEmMC8oMTQrNDgwMjE3OTgzNTQwMjEzMTQ0MjUrMjUu NTkpNTEmMi4sMC8rLy4vMCwvMCwwMDAuLi4tNDUqMTIqLisuMi8xNC0tMCktLSMzMykzLigzLigu MC8uMC80LykxLCYwMiYtLyMuJBozKR9DOx1bUzJ4bVuekn+ypaa+sLLGurjJvLvKvrzJvLvJwcHJ wcHJvr/LwcLJwcPNxcfNxcXOxsbRxsrRxsrWycrSxcbRzcnRzcnSys/VzdLOycrRy83PzdHOy8/O ytHNyc/Szc7Vz9HUy9HSys/VzdLWztTVz9HOycrVzc/Yz9LSzdHSzdHYys7Zy8/Yys7Wyc3Ryc7R yc7Sx8vRxsrUxsfUxsfNx8bJw8LGw8XFwsPLv77Lv77Ju8LJu8LKvMHGuLzCtrLBtLCyoZeWhn10 b2NQTEA9OjIuKyQnLCcnLCcsKyYmJSAjJxokKBssJyMoIx8eIB0aHBkeHh4lJSUlJRohIRYiIB8j ISAdJh8eJyAjJxofIxYhHBgkHxsuJBwrIRklJRokJBkhJB8jJiEgIR0hIh4nJRomJBkhJxgcIhQo JhksKh06OC1RT0R0XE+NdGeUh4ihlJWsmpuwnp+3paO7qKewqqWspqG2n6eul5+wmpejjYujkJaj kJaajpeWi5SfkZGilJSZkZGXkJCekpuilp+rnZ2ml5eqlp2vm6Klmpuonp+on56mnZuonZuonZus l5WumZarlZqmkJWql5molpedl5uZlJeajYSRhHuMb15wVUVUNyw+IxkqIxMiGwwfGAohGgsjHA4f GAogGhAhGxElGQ0lGQ2onZuqnp2mnZuon56rnp+om52ql5molpejlJanl5qsl5eqlZWnlo6rmpKu n5+ompqflpChl5GdlJKdlJKijpKhjZGWh4yUhImOhIiOhIibh4eeiYmXjYKUiX6WgoKeiYmbkI6i lpWlkpSXhoeOfX6MenuOg3CUiHWQiYeIgn9/b2dhUUlFPCgpIQ8mJhsrKyAqLi0qLi0sMjArMS8t MTIvMzQ0Li44MTEvMTIvMTI0MzA1NDExNTQqLi0rMS0qMCwxNC0vMisrMCksMSopMicoMSYvNTMq MC4mLCwqMDAsMystNCwtLiYtLiYwKiw0LjAsLyouMSwuMCQrLSEzMCksKSIqJRY0Lx9HMBNnTi2C bVqfiXWzpZ++r6rHurvHurvJvLvKvrzJvr/Kv8HLvr/Rw8XLw8bNxcfPxcnPxcnRxsfSx8nWy9LO w8rPx8rUy87Uyc/Wy9LPx83VzdLVz9HUzs/Uy87Sys3Ry8/Vz9TWy8/Wy8/Vz9HVz9HRy8/Ry8/Y ztbYztbUy9HVzdLWy8/Sx8vVys7Vys7Px8rSys3Vys7PxcnUw8nUw8nLycrHxcbFxcXDw8PJu7/L vsLJusbJusbLv77Hu7rFt67DtqyzpaKXiYd0bWJORz01NCQ0MyMoLiElKx4mJxchIhMkJhokJhon Ih4nIh4gIiEfISAkIx4lJB8tJhgtJhgnISEnISElJB0jIhslJh4hIhonIB8sJSQrIxopIRglJCEk IyAfIRYjJRonJiEoJyInJh8gHxgkJhgnKRsrKx4pKRw6MRxPRi9rU0iIbmOahIeqlJaompesnpu2 qJ+2qJ+0qKWzp6O4pa6ynqeym6GrlZqumaeqlaOlm6ahl6Khlp2jmZ+jmZ2nnaGunqaqmqKul5+v maGwmqWvmaOlmpunnZ6ul6KrlZ+rl5uqlpqumZmsl5eslpaokpKrlZenkZSfjJKbiI6ajouMgH2J altwU0RQNyY5IRIpIxAoIg8dGg0dGg0gGQsiGw0lGBIoGxUsHhImGA2lm5qjmpmolJGolJGql5ms mpurmZqnlZamlY2hkIifjJCjkJSikYmol5CmlpmfkJKUhoaQgoKQfn2Rf36UgHmQfXWOfnWOfnWQ g3mQg3mWh3iVhneEem+Eem+Oe3KXhHqUiY2WjJCWhIaLeXp9al2Cb2KGe26SiHqWiYuNgIJ+b2Fe UEM8NR0tJxApJhctKhsrLywsMC0xMTMxMTMqMjEtNTQ1MTA3MjEyMTUuLTEuLiwxMS8yNDMtLy4s LisrLSouMiYuMiYnMSwlLyopLyspLyswMjEtLy4jLy0gLCokLismMC0uLycqKyMrKysqKioqKS8v LjQrLy4rLy4zMCksKSIpJxoyMCNIOS1eTkGAa2mijIm2oqa+qq7GuLzKvMHHvMHKv8PHvMPHvMPL wcXOw8fKw8HKw8HPxsXPxsXOycrRy83Sys3OxsnRxsfVysvVys7Uyc3Sys3Sys3Sys/Sys/Sys/R yc7Pys7SzdHYytHZy9LSzcvW0c/Uy8vVzc3UztLVz9TVzc/Vzc/Ry83Ry83Sx8vSx8vRy83PysvR yc7OxsvOw8fPxcnPx8rKwsXHxcbFwsPKv8HLwcLOwcfNv8bLv7vFuLTGurPCtq+zp6aWi4l6bmJX TEA+OSg3MSElMiYhLiIfJxcgKBgfKBYcJRMdIhkdIhkdIBsgIx4hJRkiJhomIg8mIg8kHRosJSIr KCUlIh8kIiEhHx4mHxwrJCEoJSAkIRwmIx4lIh0iIxQgIRIlJRomJhsjJh0jJh0kJhsfIRYjHxYn Ixo4KiNIOjJdTEV5Z1+SfXqjjYuomZ6woaa0pqO0pqO3qKi0pqa4oqq0nqawoaOunqG2nqa0naWv n6Kvn6Knnqaon6esmaKvm6WvmZ6ym6G2m5q0mpmvm6KumqGmm52mm52wm56umZusl5qqlZemlJCn lZGmkZGolJSijJahi5Weh46ag4uViYiHe3p5X1hcRD1KNCg1IRYoIg8lHw0dGgsfHA0mGREmGREh Fw8iGA8nHAwlGgqlmo6hloulkIinkouomZumlpmllIyfjoehkISbi3+Vh4KbjYifjY6hjpCZi4aN f3p/c2l+cmiAdWWMgHCNhHOOhnSOh3uMhHmOfXmQfnqLeGqEcmSCb2KCb2KAdWWMgHCLgIKNg4SH dWh+bV97a1N9bVSIe2+ViHuUiY2MgoZ/c2peU0pAQCcrKxQlKR0oLCAtLy4wMjExNTItMS4rLzAs MDEsLisyNDEwNDMrLy4qLzEuMzU1NTUwMDAwMi8xMzAqNSwoMyomMSooMywtMCkuMSorLywsMC0q Mi0nLyomLyYoMSgwMSkuLyctLzApKywqMi8qMi8vMCwyMy8yMy8vMCwqLBUrLRZDNCtaSkB/a2Si jYazpqq+sLTHt7zJuL7Ju7zLvr/Fv8HGwcLLwcLNwsPNxcXNxcXNw8LOxcPOycfPysnRycvSys3V x8nUxsfUy87RycvPx8rRycvVys7RxsrUzs/Ry83Ry83Uzs/Vys7Wy8/Sys3Sys3Szc7Y0tTSys/R yc7Ryc7Ryc7Ryc7Sys/PxcnUyc3Sys3RycvPx83Ryc7NwsbPxcnPxcvLwcfJw8fHwsbLv77Lv77P wcHLvLzDurjDurjDuri/trSwqqWSjId+bmVdTkZAOC43LiUnLSsqMC4nLiQgJx0fJxkdJRcgKR4e JxwhJRkjJxsdJRccJBYmJSAkIx4oJSIpJiMiJBkfIRYfKBYdJhQiHxonJB8lJiIhIh4kHxsnIh4j JhQhJBIlJxwnKR4kKBkkKBktJRoqIhcmHxInIBMyKhc9NCFJPjVhVUx+cGuVh4KhlpqroaWsoqOr oaKyo6OwoqKzn6OynqKvoqivoqi2nqi0naewm6q0n66woqusnqesn6Oom5+ql5mvnZ6vnZ6wnp+w naGump6nmp6mmZ2qlpqnlJemlJKnlZSilYyjlo2fiZGhi5KXiZeVh5WWhpCOfoiAe3B0b2RjWEZK QC9BNB0zJxEqIxMnIBAhHRQfGxIlHA4lHA4jGxElHRMkHw8iHQ6ikYmikYmbkIyeko6WkoyUkImf i4CahnuQhniNg3WIg3iQi3+fiYmbhoaMeXJ/bWV/a2R+amODcmSSgHONhnuNhnuEg396eXV/bmp/ bmp5Z116aF56Z199aWJ6bmJ+cmWDcm6LeXWAbmF5Z1p6aVqDcmKQgn2WiIOLh42Hg4l3dW1WVU1A QykpKxQhKh8eJxwlKycuNDAuMjEuMjEzNTQvMTAsMSwuMy4sNC8nLyoqMC4vNTMyNyouMiYmMSgo MyorNCsrNCsqMi8pMS4uMC8uMC8sMC8sMC8oMCsqMi0qMywrNC0xMzIsLi0vLy8tLS0pMCorMiww My4uMSwzMCkuKyQwLRwyLx5GLR9hRjeDaWShhoCypqW+srDJu7/KvMHGvr7Hv7/GwcLGwcLKw8HL xcLJwcPNxcfOw8fPxcnSx8nPxcbPxsXPxsXVx8nSxcbSys3RycvRyc7Ryc7Uyc3Sx8vOycrOycrS x8vUyc3NysvKx8nRxsrPxcnOxsvSys/Rxs3PxcvWyc3UxsrPx8rPx8rPx8rUy87VycfUx8bOw8fR xsrNxcfLw8bNwsbNwsbHwsbJw8fHvL7Gu7zLwcXGu7/Hur7Hur7Btry4rrSmpqiQkJJ0cnNRT1Az OS8tMikoKyYoKyYjLCMgKSAiJSAgIx4iKyAeJxwfIxciJhodJxkaJBYjJCAgIR0hIh4lJiIaJBYZ IxYgJhceJBYYHxYcIxkcIiAbIR8cHhQhIxghJBQgIxMcIhYgJhoeJRQhKBYtJRoqIhcfIg8cHw0i IQcpKA08LhRbTC5vYmKGeHiVjJShl5+moaKmoaKroaWonqKsn6asn6ammqalmaWwmaawmaamlqqm lqqomauikqWbl6GdmaKekpuflJ2ilJ+llqKmkpujkJmhlJqfkpmhjZSZhoydgoieg4mXhoSXhoSH f4KEfX99en51c3d3bXBuZGhkY05XVkFUSSlBOBkyLRIrJgwiHQ8iHQ8gGhIdFw8iGA8lGxIhGgwl Hg8lHQomHguajIyajIyZjYyZjYyRjYSRjYSZhn+UgHqQe3KOenCIfXmLf3uMd3eHcnJ3al5tYVVt Ylx5bmiGeneMgH2HgHmHgHmDc2p/b2d5bWN0aF54bmN4bmN7YmiCaG56a117bV6CbmR5ZVx3X1t6 Y153a2V9cmuOeYCbho2UiJGOg4x0eXVPVFBDRDInKBgcJRwjLCMsNS4sNS4sNzMrNTIrMS8tMzE0 NDI0NDIwNS4sMSowNzItMy8nMislMCkrNC0sNS4pNTEoNDAoNDAlMS0zNTcxMzQxMTMyMjQlNC8p OTMmNyslNSotMDMuMTQtLDIvLjQtLy4wMjEmLisoMC03MCcyLCMsLBUpKRJUMh1qRzB/ZV6eg3u0 n5/Fr6+/uru/urvFvLzGvr7GvsHJwcPHwb7Jwr/HwsHJw8LNwsbPxcnUxsfVx8nSx8nRxsfKwsfN xcrPys7Oyc3Nx8vOyc3PxcbNwsPNxcXNxcXPx83NxcrNx8vLxsrSxcnUxsrPwsPSxcbOw8fNwsbR xsfPxcbKxcbKxcbRxsrRxsrPw8LUx8bUxsfUxsfVwcfUv8bPwsPLvr/DvrzFv77NvL/Lu77OwcXL vsLKu8XFtr+/t7q6srSrq6iUlJF1dXVMTEwwNSwoLSQrLiUqLSQcLiQYKiAaKSQaKSQbKCAZJh4h JB0hJB0dJBocIxknIhwpJB4hIB0iIR4gJR4cIRofJRYfJRYgIxofIhkhJBsgIxokKBwdIRYYIxwZ JB0hIxclJxsdKBUaJRInKR0nKR0oJBgjHxQaGQoUEwQjFgRAMRxVSUplWlt5cnKMhISXkpSZlJWd kJabjpWekZWekZWRi5GQiZCVjZCSi42LhJCHgIyJg46Ce4eIfYiIfYiMf4OHen6LdHuIcnmGa3KE anCCa3CAam+Ga16CaFuAYWJ/X2FqUUpwV1BlWk5fVEhWUz9VUT5fTj9XRjhUPTBOOCtBNBkzJw4u Iw8uIw8kHxAbFgkfGg4dGAwhHA4hHA4eGwwiHw8jHQgmIAqXjIuZjYyVjIiWjYmUjYiQiYSUf3+M eHiLd3SMeHWAfXl+end0cG1raGRqZF1uaGF0cG1+eneCfniHg32Gg3eHhHiOeGiHcGGGc2t/bWV5 bmh5bmh0Ymp/bXV6b2l1amR3ZVhyYVRwX2F1ZGV4bWeDeHKRe4aahI6OjpGJiYx3dX1UU1o6PzUk KSAgKSIhKiMvLy0xMS8uMTcuMTcrMzAtNTIvODcsNDMsMjAtMzEvMDMyMzctOjUpNTEyMjAxMS8o Mi8nMS4rMDIpLjAuLjA1NTgsMjIoLi4iLiokMCwiNCYkNygkMC4kMC4qLTQqLTQkLzEiLS8hMS8f Ly0wMiYtLyMoKxAsLxRMMBdhRCmEaFemiHe2o6W/rK6/tLjDuLzKvL7KvL7HurvKvL7Nwb/OwsHL wcLNwsPPwsbPwsbSx8vOw8fNxcrLw8nJvsXNwsnNxcfKwsXJwcPNxcfSxcbOwcLLw8PNxcXKw8rJ wsnLv8vNwc3Pv8XRwcbLwcLLwcLLwcXJvsLLw8bJwcPJwcbKwsfOw8XKv8HJwcPJwcPLvsLNv8PL wcXKv8POwcLNv8HJvr/HvL7Ju7/Hur7HvMPJvsXGvsHGvsHBu7y8t7iyqKWelZGId3hcTE1BOzE0 LiUkMSkhLiYeLBwfLR0mKSQkJyIcJR4bJB0gJSAfJB8dIBkhJB0iJR4fIhsgHxgjIhskJR0iIxsm IBcnIRghIhoeHxcfIRYjJRokKBwgJBgcIh4cIh4iIhcgIBYkKRciJxYkJBkmJhseJBgaIBUaHA8V FgofFAcvIxU9KiBVQDVnVkl4Z1p7cmh7cmh9b2+Ac3OCdW1/c2p3al53al5+a2J6aF51ZV1zY1tq Y1ZlXlFnWEprXU9vXU5oVkdwU0RvUUNvTTtrSThYSDlWRjdaQBxcQx5VPytVPytUOB5UOB5bPSlR NCFJMhZJMhZAMCJEMyU5KxgvIhAsIhgrIRcmIxIfHAwdIBAaHQ4cFwocFwoeHQwbGgoWGwsVGQoa GAUjIQyajIyZi4uXjIaWi4SShomRhIiQfn2GdHOAd22CeG57eG93c2pua19lY1dnXV50amuGc3eN en6Gf3qMhoCHg32EgHqQfnCJeGqAdXR7cG96bm93amt9anOAbnd4bWtyZ2VtXVNwYVZ6Z1+AbWWG dHCMeneOgoOWiYuVjJSOho2Dd31fVFo8PTkjJCAlKiMoLSYuLjAyMjQ1Mjg3MzkyNzMwNDEzODkx NTcsNDMrMzIwMjEzNTQsPDQnNy8sMSwsMSwoLykrMiwuLjAxMTMxMTEyMjIrMS0oLionLSslKykh MiojNCwtLywtLywsLi8wMjMqMjElLSwmLCopLy0yMCUwLiMkKxclLBg9Kg9bRieAalGljXOyo56/ sKvBsLbGtrvJu7zKvL7Kur/NvMLLvsLOwcXLwcXJvsLNwsbLwcXNwsnNwsnNwsnKv8bHvMHNwsbN w8LJv77LvsLOwcXPv8LOvsHKv8HJvr/LvsLNv8POvsbOvsbNv8HNv8HJvr/LwcLLvr/Nv8HNwsnK v8bKv8bLwcfJvsLFur7GvsHGvsHLvsLKvMHJvsLHvMHNv8PKvMHKvMPKvMPHtsHJt8LJu8LKvMPD uLzJvsLCurq8tLSypqKflJCEc3RaSUpIPjs9MzAkMSkeKyMdKB0cJxwiJhomKh4iJhohJRklKB8k Jx4jJCAhIh4eJhkcJBcgJBcgJBckIhgnJRsoIhooIhoiJhkgJBchJBQgIxMhIxciJBgnJB0lIhsk IhclIxggIxEcHw4fJhMfJhMfIxUgJBYfIg8ZHAobFQcjHA0pGwo3KBZKOyxURDRbSTthT0BdTkZn V09pWkZiUz9YTzhVTDRWRCpWRCpWRSdUQyVTPypTPypVQChaRSxMPiBDNRhQMBlbOiJfQCdaOyJK Nx06Jw84KANBMQlIPSJVSS1WRzBdTjdhSTRaQy5UNB4+IQ0vIBgzJBwpIhIhGgsnHxcoIBgoIBYi GhAeIREaHQ4YFgseHBAhIBEfHg8aHhAXGw4aGA8cGhGdkZCXjIuSiYiUi4mXhIiUgISHe3h7cG16 c2l5cmhubWdlZF5iXVNrZ1xya2d9d3KNeoCNeoCMgH+Lf36JhoyIhIuQfn+GdHVzcG9raWhuamd0 cG2Db32Db31+b3J/cHN0ZVR7bVuAb2KHdWiMgnSQhniRkIeWlYyUkKaMiJ54d35aWF88PTQpKiIk Kh4qMCQwMyw3OjI3ODszNDgvNTExODM0NDIyMjAuNzMuNzMyODAwNS4uOTUuOTUrNTApMy4uMjMs MDEtLSswMC4uMSwpLCcuLjAtLS8sLi0qLCspMTAoMC8pKyouMC8rLC8vMDMnMjIlMDApLy0sMjAy MCUwLiMjKBUnLBg4MSZTTD+AaWGljIOwop2/sKvCtLjHur7KvL7Lvr/KusLPv8fFvL/GvsHHwsHL xsXRxcPNwb/Gv7zJwr/NwsPHvL7Gvr7Hv7/Lv77KvrzJv77GvLvHu7rKvrzGu7zFurvKvL7Lvr/H vL7Jvr/LvsLJu7/Gu7zJvr/Jvr/Jvr/Jvr/Gu7zHuMLKu8XHvMPDuL/Du77Du77Gu8LGu8LGt8HJ usPDuLrBtrfCt77Ct77Ft77GuL/HtsHJt8LJvsLFur7FuLfDt7a+q6ynlZaAd2tUSkBBPDo3MS8s MyknLiQfLCIcKR8hJRgiJhkhJBsbHhYiJRwkJx4iIx8gIR0eJBggJhogKBsbIxYlJRslJRskJBcj IxYjKBUfJBEXIhsbJh8kIx4lJB8mJBoiIBYgIBQkJBciJBghIxcgJhkfJRgYJxEVIw4UIwkVJAoe GwsgHQ0jHA0kHQ4zKQ85LhNFMRZRPSFURTJbTDlcW0VXVkBcVERTSjtBPCtAOypEQCNEQCNJOiRO PiheT0ViU0hVUDo8OCNBMh1cTDRjXVZnYVpbVFhGP0Q/NyNORTBiXlZ4dGt4eoh6fYt+e4lzcH5o W1hFOTcyJhcmGg0jIQ4dGwkmHRElHBAeGQsgGw0eIQ8ZHAsXFwwbGw8fHg8aGQsZGgoZGgohGxEf GQ+akZCQh4aNiH2Qi3+Vg4KEc3JzbWVya2Rza2F0bWJpaGJeXVdhYVRnZ1p5d3iCf4CRh4iMgoOL goCRiIeNhoiGfoCDd2p7b2N1cGV4c2h6enp9fX2EeouJf5CLf36EeXiCem+IgHWDe3CMhHmZjoOi l4yanpeeopuVmqqGi5pubnBYWFs8Py0tMB8oLB0sMCExNzE0OjQzOjUxODMoMywrNy8yNTAyNTAx NzstMjctMTAsMC8uMSowMywnNCwlMiowNzQsMjAvMTAwMjEoMC8nLy4wLi0xLy4uMC0pKyglKSYo LCklLicnMCkuMC8uMC8qMTIqMTIqMSkqMSkrNCErNCErLRYsLhY/PDlUUE2AZGSlh4eun5+4qqrG trvJuL7Gvr7FvLzFur7Kv8PFvL/GvsHGvr7JwcHNwbzNwbzDu7vFvLzHv8LGvsHDuLrHvL7JuLvH t7rJu7/GuLzHur7Ju7/Ht7zGtrvCt7vFur7Hur7Ju7/Fur7Fur7GuL/GuL/DuLrHvL7FvL/CurzF tr/HuMLHt7zGtrvDu7vCurrFur7Ct7u/s7zCtr+/uru+uLrCs7zGt8HDtrrDtrrHt7/Ht7/FuMXF uMXGu7/Btrq/pp+njoh7dWFRTDk9PzIrLSEuLx8pKhoYKB0YKB0mJyEkJR8jJCAgIR0bJBscJRwl JiAmJyEiJBkiJBkhIxggIhcmJSAnJiEfJBMiJxYpKRwkJBcaIhUaIhUhIBkjIhsiJhoeIhYmIBgq JBwfIBwfIBwjIRUgHhIWJA4UIgwQJAwTJw8jIA8jIA8jIQomJA0nIgo1MBZAOSdcVEBhX2lranRt dIJqcn93c4hwbYJnaWpbXV5RUVFUVFRWT09kXV11a31/dYdzcnljYmlWVlRoaGV0epB+hJp1d55n aI5VU19WVGF5cJmSibOOlLCOlLCUlayGh557c3JbU1E+MBspHAohGwoiHAooHBEnGxAiGw0lHg8f Ig8cHw0eGQ0iHRAgHBEgHBEbGAogHQ4qGxQlFg+ZjpKOhIiLf3uDeHSAdWV0aVpyZ1d5bl55a2d6 bWh1aWFyZV15c2uIgnqQh5GRiJKVjoyUjYuSiYaUi4eUgoCRf36GfWiDemWDeneNhICNjY2MjIyQ jpmQjpmMi5CIh4yOkZCSlZSRjpCUkZKil5ujmZ2fnZ6in6GUmqKGjJR9c3RfVldFPy0vKhkrKRwv LSAzNy8yNS4zODQyNzMtMS4vMzAwMjExMzIyNTsuMTcuMTcuMTcxMzAvMS4wNDEwNDEkNzAiNC4v MCoxMiwqMi8nLywuLiwtLSsjLi4hLCwlKSglKSgoMCsoMCstLzAxMzQ0MjUyMDMqOC8lMiolOCEm OSIxKhI3LxZDNzRUR0V0Yliah32rnZq+r6zFtr/HuMLDu8HBuL7Jvr/Jvr/LvsLLvsLFv77Fv77F wbzCvrrFur7DuLzHusHHusHFt7jHurvHusHFt77JuMHFtLzDs7jHt7zJtLvJtLvCtsLBtMG+s7rB trzBtrq/tLjCtLjBs7fCt7vDuLzBtrrBtrrFtr/DtL7Ft7vCtLi/t7e+trbFt7vDtrq7s7i+trvD uL/Btry+rrPCsrfHtLbGs7THur7Ft7vFtsLGt8PDs7vCsrq/paamjI17cF5XTTw7PTAvMSUuLyss LSkcJyAeKSIhJxofJRggIRsjJB4fJh4cIxsoJBomIhgoJR4oJR4mJSApKCMjJBwiIxsiJBkkJhsl JRokJBkhIxYhIxYkKBsfIxYaHhIiJhkqKBsqKBsiJCMhIyIlIxkhHxYfJRkaIBUgIhUhIxYpIxgp IxgpIhIqIxMrJgw4MhZKRlVlYXBwbaV5da59grCEibiMkauJjqiSkqF9fYtyaYJ1bYZ1cICEf5CO iK6alLqWlKKEgpBqbYd6fZeQkMGWlseIkK9/h6ZvdYdkanuAgKuQkLuborSepbefmbCOiJ+Mf3Vw ZFtMNyMwHQwlHA4oHxAnGxAnGxAhHA4gGw0jIQ4mJBAjHA4hGgwiHBIgGhAcGQogHQ0sGQopFgid jJmVhJGOe3+Gc3d5a1Z/clyHf3KNhniJh4iAfn+HfnqJgH2HhIORjo2WjZeelZ+bmp+dm6GblJaZ kZSWkZWRjJCVhH2bi4OUjJGakpeimp2imp2dm6Gbmp+Ump+XnqOenp6enp6bl6Galp+fm6KemqGi n6Oin6OUlZuMjZSCeXNdVU9NPC03JxkqJyIxLik5NDU6NTczMzUzMzUuNDIuNDIzMTI3NDUxODgu NDQzNzowMzc0NzgzNTcyNDEzNTIqNDEpMzAxNSksMCQyODIxNzEyNDMqLCsnKi0tMDMsLCwrKysi My0jNC4sLC4xMTMrLzArLzAiLignMy0mOickOCUrKBcwLRxILBZdPyduZEyQhmunnZ66r7C+s7rF usHGu8LDuL/JvsXDuL+/urvCvL7BuLjDu7vFvLy+trbFurvCt7jGuLzDtrrFtLfHt7rCtr+/s7zD tL7Bsru+sLe+sLfBs7q6rLO6rre4rLa0sLywrLiuqKqwq6ywpa6zp7CwsLOvr7K7q7O6qrK/sri/ srjCtLi+sLS7trq7trq/tLu+s7q8sra+s7e/srbBs7e/r7K/r7LBsrLBsrLBs7TDtrfBtL6+sru+ s7q3rLO3nZ6ehIZ6aF5jUUhAQTswMSsjKi0lLC8kJCIoKCYrKhooJxcjJxojJxohIxciJBgnJRgn JRghJBshJBsjJBwkJR0jJRojJRomJyMkJSEmKBweIBUeHxkjJB4jJh0kJx4lJxkjJRckJBckJBcn Jx0hIRcoJhwiIBYiJBkhIxgXJRYVIhQgHhInJRgrJg4xLBMrLhs8PytWUHNya5B+erODf7iDhKeN jrKOlauQlqydl7aalbOHgpWMh5qMjK6RkbOQnb+XpcebmqKOjZV+eJ2Jg6idlcamns+em6qSkJ6M hp2IgpmQkKyenrurp76wrMOvpsGjmrSIjoZeZFw9OCkpJBYgHQ8gHQ8jHA8jHA8lIRYkIBUbGQ8j IRYgGwwfGgsmHhQjGxEeGQogGwwqHQ4sHw+ZkpuWkJmHiJGCg4yCgoKIiIiQjJWRjZaSjpqRjZmN kI6WmZeZm5qdn56dmaWbl6Ofnauhnqyhnaabl6GblqablqaZlZ6bl6GZmqGfoaeioqWdnZ+fnqOj oqejpqKipaGjoqeioaadnqWen6aoo6enoqaqnqeonaaelZ2VjJSId3NiUU5IOCk3JxkrKSoyMDE1 Mjo7OD89OTw7NzoxMzIvMTAzMTA3NDMzNTQxMzI3OjQzNzExNTcvMzQyNzUvMzIuNzMsNDEyNS4t MCktNC4qMSsyMS4rKicmKicqLispKywqLC0nNCwlMiosLyouMSwsLSkuLyspMCgpMCgoNCUoNCUv LSIzMSZTMxZbOxxwX0mSgGmjmpm3rqy/srbKvMHGu7/DuLzCuMHBt7/Ft7vGuLzFt7jCtLa/tLi7 sLTHtLbDsLK8srO8srPCtLjCtLi6sLi6sLi+rLq8q7iyp7irobKqpailn6Ovm6Kvm6Khl6WelaKS kpWWlpmUkJmWkpudnqKZmp6hnp+hnp+mo6ejoaWuorCzp7a7rrK4q6+8r7a+sLe8r7a+sLe7rrK8 r7PCsrfCsre/q6/CrrK/s7K+srC7tLu7tLu0rrenoaqmlYyUg3pzY1teT0dARDovMiksLCwuLi4l KiMhJh8fJhweJRslJxskJhofJBMjKBYnKR0nKR0gJyEgJyEfJhwiKR8iKyAfKB0eJxweJxwiKBwf JRkiJRwjJh0cJR4eJyAlJxwjJRojJhYgIxMhJRYgJBYpJxomJBciJBkfIRYfIxYgJBcgIhYfIRYo KxAkJw0uKB9FPjRhXH13cpSDe5qIgJ+Gg4SHhIaVkJ+alaWwnrOzobaRlqaSl6eXlr6dm8Oiprqm qr6snaKikpeUjaedlrCno7y0sMq0rK+hmZuXkaiXkaiXkrOmocKwp8KzqsWvqr6mobSSjoZuamJI OSUxIxEjGxEnHxUlHg8jHA4kIA4jHw0gGw8hHA8aGgcaGgceGBEfGRIkHQ4jHA0nHRYpHxedmp6b mZ2Slp+OkpuSkqGWlqWXnayVmqqbmaedmqibm6yfn7Cjnp+moaKlo6uhn6eqnqysoa+uoq6soayf naufnauen6idnqeboa6hprOipqqeoqaloqOmo6WqoqWooaOnoaemn6ajn6afm6Klnqelnqeloaef m6KijZ6VgJFub2hISUMlLRQZIQorLSowMi8zMjc6OT04OTQ1NzIzODcwNDM0NzUwMjEyMjA1NTM3 OC81Ny4rNTIpMzAtMzErMS8xNTQvMzIzNzEtMCsvNSkqMCQrKykrKykkLSYnMCkkLSYlLicmLSUr MiomMCIlLyElLSAlLSApLiUpLiUhLyAhLyAqLiEtMSQ+LhNGNRlhUUqAcGmnkZa6o6jDs7vHt7/G u7/Ct7vCur/Cur/Hs7fDr7O7s7O7s7O/trS7srDBtLPBtLO+s7e7sLS4sLa2rrOzq7uro7Oqmqyo mauhkJ2djJmRjYmLh4OUi3mUi3mDfnKGgHR6dG1+eHB7c3J7c3J+enR+enSHfnqLgn6QiI2UjJGZ laGalqKlnZ+qoqWro6i2rrOyq7K0rrS3qq66rLC7rrS6rLO+qrC+qrC8rK++rrC7tLu0rrSspqyh mqGXjX+Ad2loW1hTRkQ1P0ApMjMqLSQqLSQfKB0fKB0cLCMbKyIhIxcmKBwmKBolJxkfKxwdKRoi JhogJBgcJBcdJRgiKhwkLB4jKB8aHxYhIxYhIxYhIxYhIxYhJBsfIhkhJRgiJhkjHhEmIRQhJxod IxYiJBkiJBknIBslHhkhIxYiJBYgJBYfIxUnKRQlJxIzKhpNQzFfXnJ6eY2LhI2IgouAd2l+dGeH houRkJWrmqezoq+horqio7ulo8mencKepaqhp6yonaajl6Gdm7Clo7irrry2uMe6s7qspqyboayW m6ehl7Oso7+uo7amm66qoqefl52ViXlzaFhGOhwkGQIoGg8tHxMhGgsiGwwlHwoiHAcgGw0fGgwb GgwcGw0aGg8cHBAdGRAfGxIiGw0lHg+imaOlm6afoaqfoaqvoaqun6ijoqyhn6qjoqqjoqqsorSs orSlnqWrpaumpaqlo6ivnqizoqysoaysoaympa+lo66hoqahoqaeo7Cboa6hpa6jp7Csoayqnqqm m5+soqaqn6OjmZ2fnqaenaWenqudnaqemqGXlJqSiZR9dH5TW1EyOjEaLRIWKA4tMikwNSw1NTU7 Ozs3OjQzNzEsODArNy81OTMuMSwwNTAxNzE0NzMyNDEtNTQxOjktOTkpNDQyNzg0OToyNTAyNTAs NSwrNCsnLyonLyokLikjLSgjLiUmMSgtLiouLysnLSEmLCAjLyAlMSIqMSkoLycmMSgoMyokKhsj KRowKQ4+NxlRTEduaGOijJawmqW7r76+ssHDt8G7r7jBr7rGtL+/r7S/r7S7sLS8sra/s7K4rKu6 r7C8srO3srCvqqirp66jn6aalp2UkJaXjIuNgoCMenmAb256bm9zZ2hvaFBpYkpfXj9jYkNoVz9o Vz9oWD5pWj9bWDxfXUByXUpzXkxzYVR/bV99c2mEenCDhH2OkIiOkJaXmZ+hn6Wop6yyo6y3qLK0 qq60qq68qK+8qK+3qq64q6+0qq6uo6ejmpaSiYaHenB0aF5YUVFIQUE0PEEvNzwtLyMtLyMjKB8m KyIjMCQfLCAkKBshJRgjKRofJRYcJhcgKhsmKSIhJB0gJRwhJh0kJholJxsnJh8iIRoeJBgdIxcl IhsmIxweIhYhJRkmJhslJRokJRYnKBglJhYlJhYiIxslJh4nJB0mIxwkJhojJRkgJRIdIg8oKSUp KiY6NCZIQzNcZGp/iI6ViI6RhIt+dXJ9dHB/g4yRlZ6mmqavo6+rqMmuq8usrsejpb6ioaiZl5+h mqOooqujqMOrsMuutMuzutG2t8+vsMmlr76msL+vn7OwobSspq+ooquwn6qmlZ+dg3R+ZVdKRTUu KRsoIhcpIxgeIAodHwooIRIoIRImIxUiHxEeGw4eGw4cHQ0eHw8cHBMdHRQkGhEkGhGjnailnqqf nayhnq6vnbKvnbKonaisoaysoa+rn66rnrKnmq6qo6qup66np6enp6euoaWvoqamn6aim6Kenque nqufm6efm6eem6uem6uinrSemrChlKafkqWdkpmdkpmakJaXjZSWkJaUjZSOjZKHhouIg4d9eHtu a2pVU1E6OSgrKhopKR4tLSIvOS8vOS8zOTMxNzExNy8yODArNy8nMisxMzIwMjEyNS4yNS4xNzEs MSwsMC8sMC8nMisnMispMzArNTIuLysvMCwqMC4tMzEsMjArMS8nLigkKyUoLyklLCYrKSgpJyYm KikmKikiKyIlLiUnLSkrMS0pNC0pNC0rLBooKRc0JxM/MRxKPChuXkiUhoaompqzqrK3rra/tLi0 qq6/q7TFsLq6rLC6rLC6rLC6rLC3p6q2pqi6rLO2qK+noaefmZ+akJaUiZCGgn56d3N5bmp1amdq Yl5bU09bT0RXTEBWTjtORjNJQDRJQDRVQDNVQDNPPytMPChBOilMRDJVQS5cSDRcRjNkTjtlU0lt WlBpZFZwa11zcneDgoeMh5aZlKOnlqOqmaawo6Wypaazo6a3p6q0rLKupqumm6Khlp2RiX5/eG1y X1ZdTENIQUFDPDwtODcoMjErLBwvMCAnKx8pLSEgKyAdKB0iKSEdJBwcJyAeKSIeJx4eJx4jLCUg KSIjJxojJxokJR0mJx8oKB0kJBkgKSAbJBsjJBwjJBwjJBUkJRYqJxgnJBYhJBIiJRMhIxcgIhYh JBshJBsfIBogIRsgIx4hJB8gJBggJBgoJSItKic3LSpGPDlaXGh7fouQhpaRh5eJiI2Ih4yLjZ6V l6ilmrquo8OsqM6rp82rrr+bnq+XkqKQi5qUjq+dl7ifnsaiocmem7udmrqjo7Sfn7Cem6qsqriv orarnrKlobqqpr+rnrKilaiaeW19XVFQPzAyIxYlIB4jHhwcHw4aHQwcFwshHA8qIxYmHxIgIAoe HgkjHQsjHQshHhAgHQ8jGxMnHxahlqilmqylm6alm6anm6emmqajnaOooqivnquunaqnmaeml6am oquinqehnaahnaalmaehlaOhmqOfmaKNl6OJlJ+MjJmOjpuOjJCOjJCUjJ2Lg5SIfpCHfY6Le4OI eYCCc3WIeXuAc3N9b29wcG5hYV5iZFZbXU9QTDM/OyQ1LB0wJxgxMDQyMTUwNS4yODA3ODE3ODE0 NDQ0NDQxNDgvMjUuMjEwNDMzNy0tMCcqMi0sNC8uMTQuMTQnMS4rNTItNTApMSwxNTItMS4uMDEt LzAvMzQuMjMmLi0iKikkLB8kLB8nKx8nKx8iLSQjLiUoLB8rLyIrLiktMCstLywtLywyLCEvKR43 KRg8Lh1ONxp0Wzt/e2eUkHqsnZ+2pqi8rK+7q663qrC6rLO0qrCzqK+3qq62qKy0paqzo6iuoque kpuQi4yMh4iIenV/cm13ZF1qWFFYVklNSj5DOzg9NTJBRDQ/QTI8PTc4OTI/NT03LTQ7ODI7ODI/ MiY/MiY9KSRGMSw/MiRJPC1EMyhIOCxKPDVHOTJORDVWTD1eV01rZFpyaG5/dXuWf4yfiJWil56n naOqoqWupqinoqainaGXkY6Nh4R/clxvYk1cTTpMPSs9OjQ9OjQpOzQmODEpNCsqNSwoKSEoKSEo KB0mJhslJiIlJiIjJxsiJhoeKyMfLCQlJiAkJR8fJBsgJRwlJiAjJB4nJxomJhkgJxYeJRQgJRIj KBUgIhUgIhUoKhwgIhUgIhYiJBgcIxIdJBMbJBkbJBkjJRogIhceHxkfIBomIR8oIyEqKB4oJhwy Jio9MDRIVmhte46GhJuLiaGZlLKblrSVlbSVlbSblbuhmsGinsObl7yaoaiIjpZ7fYZ5eoOAep+I gqeUkKiNiaKGhoODg4CRh4uJf4OHe4mRhpSWjqKSi56NhqeUjK6RjI2GgIJ7ZUhrVjpPOSw6JRkn IxcgHBEeHhIdHREWFw0XGQ8kGhMlGxQgHgobGQYjGgwjGgwfGxAhHRIiGhAjGxGUkp2XlqGbmp+Z l52blJaakpWakpWakpWblZuVjpWUi5WRiJKSjJWVjpeRhpGNgo2Jh4iCf4B+e316eHl6eHd0cnB0 cnB1c3Jucl1wdF9va1praFZ0YV5zX11pY1xhW1RtVVRvV1ZkVkhjVUdXW0dQVEBHUzQ8Ryo8Mx41 LRg7MSw/NTA8ODk+Ojs6Mys+OC9HRTlJRzs8PDw1NTUxMjgxMjgxNTQwNDMwMC4yMjApNTEqNzIu LzQwMTcwODkuNTcmODMkNTEzNTcxMzQyLDAuKCwjMTAgLi0kLCsnLy4pLCUpLCUhLR4hLR4ZLyAY Lh8kMSckMScoMi0nMSwlLR8oMCIrLBooKRclKBUfIg87KBBWQSdlXUmAeGObjY2nmZmyoqe8rLK6 pqy7p66yqq+upquvp6yupqunn6WhmZ6QkYmAgnp4dWhqaFtkXk5bVUVkRUNbPDpDOzg5MS4+LzA8 LS41NTMyMjAuMC8yNDMzMzUuLjAsMy0rMiwtLiYvMCgpLSwrLy4wLSY6Ny81MisvLCUzMSY1Myg1 NCE0MyA/OilKRTNbTERoWFBzX12CbmuAfYiIhJCVjJSUi5KVkpSLiImId2d5aFhtXUVXSDFGQDE9 OCkqOC8pNy4lOzEgNSwiNDAjNTEkLygkLygpNSclMSMpKyAlJxwkJx4mKSAhLCUfKiMpKCEoJyAf KB8bJBsiJyAkKSInKR0gIhYdJBEeJRIiJRMjJhQiJBklJxwnJx0jIxkhIhwgIRsgKBodJRcYJBQd KRghJh0gJRwiIx0hIhwnKCAoKSEqKB4rKR8vKCU4MC01P05daHhzd4x6fpSQkaqQkaqRjaOMiJ6C gJWQjqOIiaF6e5J4fW5tcmNfYlVdX1NlXWdqYmt1a15uZFdiXTdXUy1pUExpUExqT1ZwVVxqXWhw Y25pYmJrZGR5ak9rXUNkTC1QORwrMh4eJRIaHwwfJBAkIBYfGxIXGgoaHQwhHRIgHBEaGQkcGwof HA8fHA8aHBEbHRIhHRIjHxSGfn6Gfn5/fYCAfoKJe3uMfn6MfnmHeXSJd3CLeHJ7cnN0amt+aW5/ am93ZGhwXmJ4ZFtzX1ZtXVVtXVVtXEhoV0RlV0dlV0dYVj5dW0NcVERcVERiSTxkTD5bT0RPRDlV QzpVQzpQQStTRC1VUENHQzU8QDM6PjE9PEBHRkpWTldWTldFUUdIVUpVVFteXWRYX11TWlc/RkQ3 PTs1NTgzMzUuNTktNDgpMzAqNDEpNTErODMwMi8wMi8yNDEwMi8mNDUmNDUyNDUwMjM0KzIzKjEj Ly0gLConKygqLisoKCgpKSkeLiUeLiUiMCEiMCEeLSghMCsqMi8lLSoeLx4gMSAjKhYfJhIoJhAj IQwvJQs9MhZUTUBrZFeDenmSiYifl5qro6asoqivpauyqLOvprCjnaOhmqGUkZCHhIN7d2puaV1r W0pfTz9JRjVFQTFDOTM7MSwwMi8wMi8vMTItLzAuLiwsLCozMTAvLSwyLywzMC0vOCosNCcwMi8t LywpLjIrMDQtMCkrLicuLysvMCwtOCgpMyQ5LyA0KxwxMCA3NSVBMy1KPDVbRjloU0VoYWFvaGh0 c3h4d3t/em91cGVvZFFdU0BHRTo3NCovNC0sMSolLyooMi0uMSovMisrMS8rMS8nLiglLCYqMSkm LSUjJxsmKh4pKyApKyAhKiEeJx4jKB8jKB8fKCEfKCEhKCIjKiQmKSAfIhkfJRghJxoqJhEqJhEk JR0hIhobIx4cJB8iJBklJxwrJiApJB4oJxcnJhYhJRkgJBgdJBwfJh4kJhsjJRokJhglJxkxLB43 MSM0NDRRUVFiW2pnX29vb3tycn5+c297cG1rYmVrYmVqaGdfXVxdU0BXTTtPQDdPQDdOPTRRQDhb RTJWQC5JOhZENBJMLSRUNCtONTJJMS5IOTVMPDlTPypUQCtNPyFHOhw/MiM3KhseIw8cIQ4eHBAf HREcHRYdHhYeHgogIAwjGxQlHRYeHQ8eHQ8dGAoeGQscGg8dGw8lHBAsIxZtXFhoV1RfXlZdXFRv WFNzXFZrWk1rWk1vVERvVERjXFFhWk9eUENaTD5XTERTRz9YRj1YRj1WTjxNRTNIPCdQRC5aSTda STdOPjtJOjdHPD9JPkFRQDtUQz1USkBQRz1OST5RTUFcT0pkV1NpWmNVRk9UTVFiW19UYXdkcohu bn5ra3tjdHVqe310eJl1eZpob4hWXXVEVVsyQ0g0NDQ3NzcpMjUmLzIsNDMsNDMsMi4sMi4wMy40 ODIzNTQtLy4xMDcxMDc1MzcuLC8tLTguLjkmMC0kLismLSMoLyUvKyorJyYjKiInLiYpLSErLyMh KyghKygqLismKicjLCMhKiEaKh0UIxYVFwcSFQUaFQMrJRBFMSVdSDtzXmGHcnSNiIyVkJSimaOm naefn66ZmaeXi46Qg4eEeG91aWFtYVdbT0ZQQzVPQTQ7OS06OCw+NC8+NC8yNS4vMiswMS0tLiol MS0jLysiKSojKispLysqMCw1MSU0MCQwLCsyLi0tLS0sLCw4Myk0MCYtMCktMCkcMicYLiMxKyAt JxwxKyAxKyAuKyQwLSZAMCJIOClKPzhcUEhlVkxvX1VpYU1fV0RWSj9KPzQ5ODwxMDQrMCssMSwp LS4qLi8tMzErMS8qMDArMTEqMC4kKigqKCcoJiUmJyEkJR8pKR4nJxwoKyQjJh8jIyEnJyUdKCEa JR4fKiMcJyAdJBwfJh4hIRYlJRorKBkqJxgfJRkgJhodIhkiJx4kJBojIxknJB8kIRwdIBkgIxwg IxohJBsgIhYgIhYmJBkmJBkqJxYrKBc0JhkyJBczKh1EOixNRUxKQ0lRSk1TTE5cTTNbTDJWQDxO OTRPRTRHPS07NCk7NCk1LyQ6Myg5JSA+KiVIMyJELx41KxQzKRI/IhlBJBs7KSc4JiQyKCAyKCA7 KhU6KRQ0KxwwJxgzKSEvJR0cHhMaHBEbGw8YGA0eHBIhHxUeIQ8dIA4fGxAhHRIdHg0ZGgodGgoi Hw8bGgweHQ8mGg8sIBVHQCtPSDJKSjJKSjJUSD1XTEBXSjdXSjdYRixUQShJRjNNSTdTRz5XTENf R01YQEZYTkpXTUlMSTtEQTNGQUBRTUxXU1FYVFNJRUZDPj9KQ0lUTFNPUV1MTlpUVFFhYV5ranR3 dX+CgJmAf5dtcn5vdICAg5SJjJ2JkriIkbeChIaLjY6DkqaElKeEibZ+g697ep5ta45UZWs7TFEx Mi45OjUyMTgtLDItLS8tLS8uLzIrLC8wMywzNy8uMjErLy40NzUzNTQxMTEuLi4uLTEsKy8sLyYt MCcnMB0pMh8uMCUuMCUqKyUsLScrLR8rLR8qLygpLicwMC4tLSsoLB8nKx4ZIxMVHg8VGAMQFAAW DAAgFgQ5IQlEKxFcTDlwX0x1cHKEf4CQhJKViZeJiJKDgox+d2tuZ1xtXERiUTpRRDRHOis6NSs5 NCo0MSozMCk5Kis6KywwMS0sLSkqKyMrLCQlLiUlLiUjLykjLyksLCosLCovKSExKyMtKicwLSor KSgrKSgvLCUtKiMxMSctLSMkMB8iLh0uLiEoKBsqLCEmKB0rKCUrKCUvKCMzLCc6LSFBNChPOiJR PCRKPitHOyg8Mi88Mi8tMTIsMDEoLSQtMikpMTAiKikoKzAsLzQmLS4jKisoLC0sMDEwLi0wLi0p KCEpKCEnKBYiIxIqKRcnJhUmJyEnKCIdIhkaHxYgKBofJxkeJBYiKBklIxYlIxYrIxgrIxghIxYj JRchJRgjJxokJhoiJBgeIBUhIxceIhYfIxcgJBgeIhYlIh0lIh0kIRMoJRYrJBUrJBUvISIsHh8f HBcxLik9Mis5LidANyZANyY+NSI4LxwwJxgwJxg3LCQwJh4rKBcsKRgsKRoqJxgwHhIzIRUvIhAy JRMiJBYlJxkxIhoyIxsvKCcoISAfIg8lKBUrJRApIw8qIxMrJBQnJBYnJBYgHhUaGA8VFgwdHxQl HwslHwsfHQodGwkbGQcdGwkdHAwdHAwaGwodHg0bGgogHw4oGg4oGg5HRjFHRjFJSDRIRzNUTU9c VVdbW1hUVFFdVDxYTzhYXFFiZVtnXmpfV2NvXGtvXGtlZ21fYWdaWlpeXl5vcHdwcnhhdHdQY2VI UUxMVU9cXWNkZWteaXlibX1rbYB4eY2Ih7CJiLKCiKx6gKV3h5+AkaqOnbSQnraMncGJmr6OkaKW maqLkrKMlLOMjq+Ii6uIg6F7d5RbX2pDR1E4MzQ4MzQ3MTcyLTIxMTMwMDItMDMrLjEuMDEvMTIm MSooMywwOTMsNC8uMDEvMTIrLzArLzAqKyUvMCosNCcsNCcvMSUtLyMtLSsqKignLCMmKyIoMC0o MC0sKyQtLCUlKBUcHw0YGwoVFwcXHAQTFwETDwAYFQIoFwE0IwlGORlXSShhVFFyZGJ4anV6bXh4 a210aGliXElbVUNUSj5MQzdEOjc+NDE3LSo6MC03MSszLigyLCQyLCQtKyosKikqKSYsKygtLSMn Jx0sKyYtLCcvKyouKikqKyctLioqLSYqLSYoKSMpKiQnLCMkKSArKyApKR4rKiMrKiMsLSksLSkp MComLSckKBwpLSEtKhkvLBsvLRgxLxo+LRY8KxQwLBg4Mx83ODEwMSsjLyklMSseLSYhMCkoMC0k LCkqKCsxLzIjLC8kLTArLjMuMTcwMS0vMCwtLSMsLCIqJyArKCEuKyYrKCMjJSQhIyIcIRoeIxwl JiIjJCAlJh4pKiIqJR8nIhwrJR0qJBwlIBomIRshIxcgIhYjJRkhIxcdJBwdJBwgIhUeIBMfIxYg JBcfIBghIhohIRUkJBcvJCAxJiIlJSUlJSUlJxklJxkwJSEwJSEvKRQ0LhgxLxotKxYrKR8oJhwp JiMrKCUqKh0rKx4qKBsmJBcwIhAuIA8jHg8oIxMmIxImIxIlIBMsJxkmJhwgIBYcIhQfJRYsJRYu JxchIA8nJhQmIBchGxMhGxMgGhIcGA8kIBYpIBEnHg8iHhMiHhMfHREdGw8fHA0gHQ4gHQ4hHg8e Gw4hHhApHRIoHBFeX1haW1RiX2Nyb3N9eZGCfpZyeH9iaG9zb3qEgIyAjZd4hI51d4t6e5CCfqKH g6eEi6F5f5V0epCAh52QlquLkaZviJdTanlaY25YYm1haHVrc4BnfY1wh5d7iaOLmbOSlr6Hi7J6 i6N4iKGHkKKQmauWp76Wp76Qp7iVrL6lqMGWmrKNjqaXmbCiobqfnreQkrBrbotWXGE/RUkuMjEt MTAxMTMtLS8sMjIoLi4yMjIwMDAsLC4vLzEoLyUtNCoxMzAvMS4kLi0kLi0hLScjLykpKygrLSor LCgvMCwwMSkwMSksLi8nKSogKiUgKiUnMCknMCkoLyUoLyUgJAsUFwIPFQQJDwANFgIKEwAOEAIP EgMWEQAkHwg8LA5DMhNFQDdNSD5UTU1bVFRdU0BbUD5YSTRMPSk+PzczNCwxMiwsLScuKyQwLSYq LCkrLSovMCorLCYpKygrLSopKCMoJyIqKSQpKCMtKyEvLSMoMCMiKh0mLikmLikiLCcjLSgnKiMn KiMhMSQeLiEjLCMgKSAnKiUoKyYoLSgoLSgkLikhKyYcMiUdMyYnLCMlKiElKxwpLyAsLiIuMCQq LiIqLiIpMS4nLywdMB4ZLBohKiElLiUvLS4xLzAqKyUuLykkLTAmLzImLTArMjUxMi4uLysrKysr KysmKSApLCMqLSYlKCEiKCYdIyEgJiIeJCAiIx8iIx8hIxgnKR4qKB0mJBkmJyEeHxkqJh0qJh0k JBkiIhciJBkfIRYeJyAZIhsfJB8eIx4fIxchJRkfIBggIRkfIRQfIRQnJRgpJxocJR4gKSIvJxwv JxwnIxkkIBYuJRYvJhYqIwwrJA0oIxMrJhYhJRkeIhYjIRUlIxYmJhsoKB0qJRcpJBYiHRApJBYq JxgnJBYYIxAdKBUYIhQXIRMYIAocJA4iIQkkIwoaHAkfIQ0iIRAcGwskGw4kGw4hHBYkHxkjIBIi HxEfHg4jIhEgIxEXGgobGQ4dGw8iGw0iGw0dGAogGwwgHgsfHQpveo15hJeJkqiUnbOOlaqEi594 iKF6i6OMnbiNnrp3lKt5lq6IkrCSnbuWnseXn8mXn7yRmbaVm8Gepcqdq8mSob6GhJtta4JhaX1n b4N1eomIjZ2Cn7eMqsKUqMmNosKVl6WChJF9gImGiZKSlKuam7Oeoa+nqrisrsOvsMarpsedl7id m7CjorehpsOQlbJ9f5BhY3NEWEozRzooNzUqOTgyMjAyMjArLy4rLy4vNTMpLy0uLi4zMzM1MTI3 MjMzMTIzMTIeMywfNC0pMSQvOCouLykvMCouMSwxNC8tNC4tNC4lLy4mMC8kLismMC0oMywoMywh KB4jKiAkJg4ZGwUPFAUKDwEJCwANDwMMEwYMEwYPGAESGwMpHwgwJg4xLhw5NSNDOi5KQTVJOzFG OC49Oig1MiEuMC8tLy4tMi0qLyoqLyorMCsrLy4rLy4cMSwaLyofKSYjLSoqKyUoKSMoKicpKygm JhstLSIlMCUhLCEcKyQhMCkgLSEgLSEmKBwoKh4oMiQlLyEfLCIeKyEjKiQlLCYlKB8oKyIjLCEj LCEiLh0jLx4qLiIoLCApKyorLSwkLCckLCcrKiMoJyArLSopKyglLCIjKiAlLSgmLikuMC8vMTAn MikrNy0nMSwpMy4oLS8qLzEvNzAtNC4pMzAlLywjKiQlLCYlLCIhKB4lKiMfJB0YJyAYJyAhIR8k JCIgKhwgKhwjJRokJhsdHhoeHxsjJh0iJRwiJBggIhYnIxomIhkhJB8iJSAdHhggIRshIxgiJBkh IhogIRkjJRohIxgmIxUqJxgjJxoeIhYuJxkwKRsmJxUjJBIsJhMrJRIsJhMsJhMpKRInJxAkJx4i JRwiJBYjJRcmJhkgIBQfHxUdHRMdHREfHxMfHxYcHBMcIBUcIBUdIxYZHxMaIxEdJhQjHxQhHRIc HAccHAcfIQ0fIQ0jHA8iGw8iHRAgGw8fHA8kIRMdIA8gIxEjJBUcHQ8fHREgHhIgHhQhHxUiHQ8k HxAjHQglHwqQkrCVl7aQoruMnreGl6qClKaJkLSMkreVmbuRlbeEkKWMl6yIl7aVpcOjrNSmr9au qsOyrseysNaurNKjq8uZocGGhpRubntqbXp3eYeAh5maobOjqs+jqs+ipsmMkLKMhImDe4CDf4uO i5aWkquemrOoqLa0tMKmrNKor9WjpsWfosGjpsKjpsKdnraSlKuIgIZ0bXJIW1g3SEYrNTIxPDky My8xMi4wMC4vLy0wMjEsLi0uLi40NDQzMzUwMDIqMCwnLSkpMisqMywqMC4oLiwvLikyMSwtMCss LyoyLywyLywyMjItLS0vLy8wMDAkLCkkLCkeIhYZHRIZGAkREAMJDAMKDQMPDQMRDwUPEgYKDgMP EgMQEwQjHA0kHQ4qIxMwKRgwJhw0KiAxLS45NDVAOTU6Mi8sLC4vLzEsLi0qLCsrLS4qLC0sLCov Ly0fMC4gMS8jKyYoMCsqKigqKigiLioiLiolLSAnLyIkLCcnLyonLyonLyoqLCAsLiIvKyEuKiAu MiYpLSEhMSgeLiUkKCUmKickIx4oJyItMSUpLSEpMBwnLhooLB8oLB8yLi0vKyohLSsfKykpKCUp KCUnLigjKiQlKykqMC4uMC0pKygoLC0qLi8rMCsxNzEtNCwrMiopMzIoMjEyNDM1ODcxMzAtLywq MSkmLSUhJh8eIxwkJhooKh4lLCQhKCAhJB8kJyIoKSUjJCAkJSEiIx8kIhcjIRYhJRYgJBYmIhgh HRQmIhknIxonIBsrJB8lIxgkIhcjIxYjIxYdIBcdIBcnKCIoKSMnLBojKBYqJBwrJR0uJxkqIxYs IQ0tIg4nJBMqJxYqJxgpJhciIxElJhQlIxklIxklIBMoIxYoJBsiHhYsIBItIRMoHxAoHxAgIBQe HhIgGxYdGBMcIBQeIhYeHhUhIRcpHBYnGhUfHQofHQojHg8iHQ4cHQ8bHA4dGAokHxAlHxYlHxYe HhQgIBYjIRYiIBYhHA4hHA4fGhUgGxYfHA8fHA8kHwgmIQqmqr6jp7ueo7CXnaqLmqyGlaeQnruU or+Wl7CVlq+NjZuVlaOZnrumq8mmt9Sis8+3tMW7uMm8wdi4vNSus86fpb+Cg4dwcnVycn6GhpKO lrOfp8WhrtGdqs2anraIjKN9gId7f4aLh5KVkZ2dmqqoprazrMW3sMmnps6op8+lpcWlpcWmq8al qsWfn66Xl6aQiJl0bX1UWm07QFMyOTcwNzQ3MzAxLisyMy0wMSswMSsvMCovMTIvMTIyMjAyMjAs LisrLSosMC0qLisuMC0pKygwLywvLisuMC8qLCsuKSUvKiYtKyouLCsyLyw1Mi8rLCQmJx8mIxIg HQ0TFAcNDgIIDAAKDgEKDwENEQMRFAQSFQUWDwYWDwYcGBAZFg4YGhAgIhcmKB0qLCErNTQqNDMs LCovLy0sLCosLCopKyooKiktLS0sLCwmKyQoLSYpLSwmKikpKScvLy0oKyQqLSYmLCgnLSkoMiQl LyEjLCUkLSYoLSYnLCUtLSIsLCEuKyQwLSYrLSomKCUkLCsoMC8qLCkpKyggKCMhKSQlKiMqLygl LR8hKRspKiYwMS0vLikpKCMnLCclKiUkKSIlKiMlKiUkKSQiLiwjLy0lLyojLSgqLCsoKiktLS8v LzEpLSorLywqNDEoMi8zNDA4OTQvNTUnLS0wMC4sLColKiMjKCEiJBkmKB0lLBokKxknKBgmJxcp JB4qJR8oJhwkIhgjIRUhHxMgIxMjJhYtJRwnHxYnIRYsJhsmIR0nIh4hIxYlJxkmIREqJRUlJxkj JRcfJB8hJiEgIRIlJhYsJSAmHxotIxsuJBwoJBgjHxQlJB8oJyIqJRclIBMqKBIpJxErIxgoIBYm IR0oIx8iJBkhIxgsIBMuIhUhHg8iHw8eIhQeIhQlHhEnIBMgIhUeIBMiHBUoIholHRMlHRMhGxQh GxQlIBMlIBMfHA8kIRMkHBQoIBckHxshHBgdHBceHRghHhAiHxEjGxMjGxMcHw4cHw4dHQodHQoh HgciHwirrMWjpbydmqiVkqGRlayZnbSaobaVm7CVma+WmrCSjqeVkaqZmq+lpruvr822ttS3usm7 vs2/xtm4v9Kwssehord/e4dzb3p5dISOiZqSlqqhpbiXp7qOnrCLlJV+h4h/h4eJkZGXlaefna+e pbuqsMesrsanqMGeobyho7+ensGiosWjo8Ghob6fn7+VlbSAgqVnaIlMVV4zPEUqOjIlNC01NzA3 ODEtMi0wNTAvMisuMSotNTIsNDEvMzAwNDEpMSwrMy4mMSomMSoxNC8sLyosKisvLS4tMzEtMzEv MistMCktLigxMiwyLyozMCstKiclIh8hIw8VFgQKEAEFCgAKDAALDQAKEQMKEQMNEAAUFwQSFAgV FgoMEQUTGAsfIRQnKRsnKxwpLR4pLjAnLC4lKSgrLy4qLyorMCstLywrLSopKyonKSggLCghLSkr LCQrLCQpLCMqLSQqKyMuLycrKyksLCooLSYrMCksLyoqLSgrLSEuMCQpKx8pKx8uLSgrKiUmKicm KicjKSkmLCwhKyohKyogLSUhLiYqMSspMCojLR8jLR8sLScqKyUnKx4kKBsmJyEoKSMjJyQiJiMn JSQpJyYkLSYkLSYiMikiMiklMS8jLy0sLi0zNTQsOTQsOTQsODAoMywqNC8uOTM0MzoxMDcrMzAo MC0mLSUhKCAkJR0mJx8nJBYnJBYpJBUpJBUmJxUlJhQgJg8eJA4cHw4hJBImIyAlIh8pJiMmIyAo Ix0sJyEkIBYkIBYnJBQlIhIjHg8qJRYsKRcsKRckJBcjIxYnIBAwKRgzJSI1JyQ1KyM0KiIqKh0n JxorIBwuIx8rKBkpJhc0KhEzKRArKRwuLB8oKh4tLyMuKxwnJBYbFw0eGg8hHwofHQkfJBEdIg8i HBIkHhQgIRIdHg8lHBclHBclGxIlGxIjHRMhGxEmHxAfGAogIAwcHAkYGwodIA4kIBUlIRYcHw8Y GwwbGhQeHRYcHRYdHhYaIAobIQseHQ8dHA4nGw8qHhGoqMuXl7qalqyUkKaSlqyUl66bmaial6eU maiQlaWRkKeXlq6lorSsqryyq8e3sM20vMm+xtLBxdm2us6orLaVmaJ7eINwbXh+d4aOh5aNkJ6R lKKRlKWMjp+IjpaGjJSGi5eOlKGbn7Obn7OaocOdo8afm7SZla6NlKiNlKiSjqiSjqiQkK+Ojq6S kbuDgqtyd5JYXXhATEo3QUAuPjcmNS4wNCg3Oy4qNDEpMzAwMy4tMCsrNTArNTAqNDEsNzMmMysn NCwmMiMoNCUyNyovMycnLCcoLSgpNC0oMywqMiUsNCcuLSYuLSYtLyQlJxwpJiMiHxwSHAkIEQEK DwAHDAAKDAANDwENDwEPEQIQEwQOEAIRGAITGgMTFgoaHhAlJxsqLCAnLCUoLSYwMSkuLycsLyYs LyYqMSknLiYrMCcqLyYkLygjLickMSkiLycrLCgsLSkiKyQiKyQoKh4uMCQuKyYrKCMpKyAyNCkr KykqKigoKh4sLiIoKh4tLyMtLiYnKCAoKSMpKiQoKicoKiclLCQiKSEiLSYgKyQlKSgkKCckJyIk JyIpLSAmKh0mKyYmKyYmJhwmJhwkJx4lKB8rKR4qKB0nKiUnKiUlMS0lMS0pMSwrMy4oLycqMSkx NSkxNSkxOiwxOiwrNTQuOTg3Nzc0NDQsMi4oLiojKiAjKiAsKRgrKBcsJRctJhgmIwssKRAmLBMl KxIrKxIrKxInJQ4oJg8uKBUuKBUwKxsyLR0qLBYqLBYxKho0LR0yLhowLBgyKCA4LSU8Lhc+MBk6 MRw4Lxo/LBZEMBk5LB0/MiNFNSg/MCM/MiM/MiM7LSc/MStBNCY/MiRGOCZENSQ9QCw6PSk4PCQ8 QCg8NBs1LhYqIxMhGgsgHggfHQciIgwhIQseHxAeHxAiIBQgHhIjHRMjHRMlGxImHBMiHhMhHRIh HRQhHRQhHwodGwcbHwoeIg0oHhYqIBgfIQ0eIAwbHAsfIA8dIhEXHAwZGgocHQwiGw0iGw0lFhEn GBOjm76dlbeXkauWkKqWl6yam7ClorCoprSen6iXmaKbnqyipbOuq7uzsMGzq86zq86zvNK3wda3 u9Kvs8qeprKHjppybnloZG93coSLhpmGhpSDg5GGg5J+e4t9gpGHjJuGjqWIkaeSn7+MmbiZlriR jrCJfox9cn9+fZF6eY15dY19eZF9fZl7e5d5fZRoa4JbW1tPT083Py4wOSgxOTAwOC8wMyozNy0p MzAoMi8xMTExMTExOzMsNS4rMS0qMCwoMywpNC0wNCgvMyc3NTA1NC8sMSorMCkqNCYqNCYwNCgu MiYyLSs4MjAuLycnKCAnIxceGg8PGAcKFAQKEAEFCgAGCgEKDwUPDAMWEwkWFAsPDgYSFgMTFgQa Fg8jHxYlJiImJyMhLCUnMisuMiUtMSQpMComLScrMiwrMiwnMCklLiclLSgnLyorLyAqLh8kLygi LSYmLColKyksLSUuLycwLCMuKiEuLSYvLicoJicpJygtKyE0MigvLCUuKyQsLiMpKyAqKiAqKiAw LSgtKiUqLCAoKh4pLSopLSorJyotKSwmKyYmKyYoKSEnKCAqJyIrKCMtKCIpJB4jJCAnKCQwKiEy LCMnLyIkLB8pMjUpMjUyMjIxMTEwMy4tMCszMC0yLywuLysvMCwtMTI3Ozw3ODM0NTEzNDAwMS0o KSEuLyc5MyUxLB45KyI/MSg3LR04Lh48LSI9LiNFMys/LiY7MSE7MSFBNCZGOSo9OSQ9OSQ/Oik/ OilFNC9IODJIQDFGPi9OPjJQQDRfPyhdPSZVRjNYSTdiTS1jTi5WUDFTTS5URTJVRjNUQzxWRT5W Q0dYRUleR0ZhSUhcUEVaTkNUUEhVUUlOUENQU0VRTjpHRDA9Oh8yLxYeIgoWGgQiHQ8nIhMYHxcb IhohHxUfHRMcGg8iIBQoIRIiGw0fGxAfGxAgHhQfHRMiIg4eHgojIA8jIA8pHg4mGwsrIxEnHw4i GQwmHQ8mHg0kHAsZHAsaHQweGwwgHQ4mHg0iGgqoo8efmr6emrCdma+jqMOjqMOjp7+ipr6Sn6yR nqujqMOmq8afpb+jqMOmorymoryrqMajob6dnbqQkKx/gJVnaHtoWl5qXGFva4NraH9oaHRpaXVq dYhfan1kbYJyepB+f5Z7fZSAeYiEfYx/eH11bnNfamJXYlpaZWJXY19dZ2VbZGNYaWpcbW5lZWNa WldcUEhPRDw+OzU8OTMoMyorNy01MzI3NDM4Mzc4MzcwMjEzNTQzOjgvNTMtNDgoLzIhMCsjMi0h MCkiMSoxNC0yNS4yMCYyMCYqMyosNSw0NS81NzA5MzExLCokLSQdJh0bHxQPEwkPFgcLEwQNDwEH CgAHCwEJDgMLDAYMDQcQEAcTEwoXEwYcFwogHxonJiEpKCMqKSQrLCYvMCooLyUmLSMsLCwpKSkp KywsLi8oMC0tNTIwOjImLygnKiEtMCcqLSgmKSQqKycqKycvLCkvLCktKx4yMCMwMSE0NSUtMycy OSw5MyU7NSc5MCU6MSYwLywuLSoqLygoLSYqKSQuLSgrLicpLCUqKycrLCgtKyosKikhLCMfKiEr KyAnJxwqKB0vLSIuKSUrJiIgJyEfJiAuLyctLiYgKyQcJyAhKi0kLTAtMjQsMTM0LzQ1MDUzMzU0 NDcxMTEyMjI0LjA4MTNAOC9HPjVMPDBNPTE+NzNEPDlORTtNRDpKRjxFQDdIPjtQRkNaRkZXRERc TkBdT0FOTTlRUDxQTkFXVUhWU0FYVURiVERoWklnVlVjU1FfWFhdVlZpW0pyY1NrYVFrYVFyYlpw YVhwX1NyYVR6ZFZ7ZVdrYV9uY2JpY15lX1tlXF1oXl9nXV5nXV50b2R1cGVubWlvbmp0dGhubmJt ZF5kXFZUU0BHRjQzMRgdGwYhGgwnIBEbGxIfHxYhHxUiIBYjHxQlIRYnIhMjHg8cHBIhIRYiIhcg IBYeIw8eIw8bHgwaHQseHAofHQohHRQiHhUoHA8pHRAjIQ4hHwwiHw8iHw8fHAwkIRAnIgomIQqf n8KWlriSl7KXnbeep8mWn8GVl7OSlbCGl6qMnrCUnb6RmruLkaeIjqWIgouJg4yXi52Mf5GHfY1+ dIRpaG9WVVxMT0VWWk9cW2JTUVhaVVhfW15aYmhVXWNfV2NlXWlra25vb3J0al9rYldtXVNdTkRX SkZUR0NNUUBPVENTVkxTVkxYTVBTR0pURkhPQURIQD1BOjczNTQxMzItNystNysvMzQuMjMvMDgx MjoyNzgzODksNDMsNDMvMjorLjUfLishMC0qMSspMCoyMS41NDEvNCssMSgoLSgrMCswNDEwNDEr LCgwMS0fMSUWKBwaFQsSDQQJCgQKCgQODQEPDgIKEgEJEAAQEAcPDwYOEQcTFgsdFAsgFg4eHRom JSIsLSksLSkwLywxMC0uLSovLiswLi8uLC0hLCwlMDApMCoqMSs1Ny4uLycuLC0sKisnKCIoKSMs KyQwLygzMzM1NTU+OjA/OzFNQTlPRDtFQzdIRjpGQTdHQzhKPS1GOSk+OjA3MikpMS4jKygoKSUr LCgrKicqKSYrKiUrKiUmKyYiJyIeKBogKhwqKh8pKR4rLSItLyQoKyIlKB8jKiAeJRssJyEsJyEk KighJyUhKyYfKSQtLS0yMjIyLTQyLTQqMC4pLy0uMSw0ODI+Ozg/PDlMQzlUSkBdSkRiT0hiUU5k VFBkV1VkV1VVWlZaXlteXlxfX11kXV9lXmFrZFpnX1VpZ1dtaltvaVhya1tzaFhzaFh/a2R5ZV53 aGp5am1vb21ubmt7d2h+eWqIdG2IdG2GeHWAc3B9dHB/d3OJc2eRem6Hfn2CeXiCeG6CeG5/d3WA eHeCen2IgIOMf4CMf4CCgHqDgnuQhICOg3+MeX2HdHhzamllXVxXVUg/PTEqJA8gGgcaGw0dHg8i IBYjIRcpGxEqHBImJBcjIRUjHhogGxcgHBEgHBEiHQ8hHA4kHQ4oIRElJBIgHw4gGRYiGxgcGQwe Gw4fIhAcHw4fHg0gHw4eHgogIAwfHwsiIg6NjayHh6aLjrCSlriLkK+Ch6aGhpSDg5F5hpJ4hJGC hox5fYN0cnNvbW5yXlx5ZWN5bW5tYWJoYl1hW1ZKUURFTD4/UUVAU0ZGSDxOUERVUU5UUE1TT0xR TkpXSFFXSFFQTEpRTUxYT0ZTSUBXRj9XRj9KRkdFQEFDRjxARDo4Pjo8Qz5KPkZGOkFHNz5GNT01 NzI0NTEqNSwpNCszNTI0NzMsMC8rLy4pMTAqMjExMzIwMjErMzItNTQ0MjU1Mzc4NTk0MjUvMi0u MSw3MjM1MTIwMDAxMTEvMTIrLS4yLzQuKzAiLiohLSkgMCMZKRwXFgcNDAALCwQODgYMDAQMDAQM DQEPEAQPEAcODwYPDwwTFBAfFhInHhklJB8oJyIyMC8sKikoKCgrKysnKC0qKzA0MDEwLC0oKikv MTAqMywoMSo0OC4vMiktMCssLyomKyQpLicwLSY3MyxEQDtFQTxEQDtNSURVUUlcWFBTVk5WWlFQ WE9PV05UUUNVU0RQTTxEQDA7PTE0NysyMCYxLyUpLCcmKSQjKiIkKyMoKyQqLSYjKB8jKB8tKyAt KyAsKB4uKiAoKyQoKyQhLCEeKR4rJx0sKB4nKCQmJyMpKygmKCUuLC0vLS4yLi81MTI1NzAyMy08 OTNFQTxKTENMTURiVVBpXFdtW151Y2d7Z2R7Z2R3bXB7cnV0c3h0c3hzeXNvdW96dYR5dIOGenmG enmCem+GfnOLgnCLgnCMe3CUg3iUhoOShIKUiISViYaRg36Qgn2NiH2JhHmUg3iXh3udiYKXhH2S i3+XkISejH6lkoSekYeViH6WjoOUjICWjYeZkImakJSZjpKbjpWdkJaSjoaSjoadkJabjpWVjZKV jZKVhouLe4B5a2dkV1NPPTc4JyEpHxgsIhslIxYlIxYrHxQoHBEfHRMfHRMjGxMmHhYmHxAnIBEl HA8pIBIlHRUjGxMiGA8pHxYlHxUkHhQcHQ8gIRIjIBIeGw4jFwonGw4jGg0iGQwdGg0iHxFzcH5q aHV3dIZ7eYtpZFpnYld0aWhpXl1oZWRnZGNiVVBhVE9hU1dhU1dbTU9YSk1cT09YTExJR0hIRkdD Tj0/Sjo/RzxBST5BQz5FRkFTSEVOREBFP0VBPEFGOkRMP0lJQ0NBOzs/QDw9Pjo+OzVFQTxAQEM6 Ojw4Pjw6QD4zOjgyOTc3OTg6PDs1NDkwLzMyMjAyMjAtOispNScxMC00MzA5NTI4NDEmNS4lNC0n MS4pMzAuMzgrMDQ1MDU1MDU3NTwzMjkuLi4wMDAzLzA0MDEuKis3MjMwMjMqLC0zKzA3LjMjLy0i LiwXIBcQGBAQEwUNDwMPCgYQCgcJCgIICgEJCwALDgANEAcPEggPEgYSFgkdIRMfIxUjLCUjLCUy MC8xLy4yMC8wLi0oKy4qLTAxLS4vKywpKCUvLispLikrMCsxOTIpMCouMSotMCkyNSw1OS85OjNA QTtQTUVXVExqW1BtXVNwZV93a2Vra19ubmJza2tza2tlZWNdXVtcVk9YU0xRVUFDRjM+PC4zMSQt MSUpLSEjKiIkKyMpKiQrLCYoLCAoLCAsKiAqKB4pJhYrKBcnKRsqLB4mLiEkLB8oKh8pKyAlKiMm KyQsKy8qKS0uKisyLi83My46NzE7OS1FQzdHST1QU0ZjXE9nX1NzbWp3cG59d3SEfnuQh4OOhoKO jI2Ni4yQjIaQjIaOkIiSlIySkJ6SkJ6WjJCZjpKmkYujjoiXjouflpKilYuilYuqlJGrlZKzlpKv ko6jlo2nmpGimpCfl42lm4mmnYuqmZCol46nm5WlmZKqlZWumZmnm5qlmZenmpunmpunlZGsmpan l5qomZusnaWrm6OlmqGnnaOqmp+llZqql5mvnZ6olpehjpCSi32AeWtqVltUQEU0KSkpHh4nJRgp JxoqJBEqJBEgJBYeIhQiGhIkHBQiGwwlHg8qHw8oHQ0fFxAkHBUkGxYqIRwhGxEiHBIcHBIeHhQf GQ8hGxEkHBQjGxMmHhQkHBIgGhAgGhAADQEAAAMAAAABAQAAAAEBAAMAAAABAQAAAAECAAMAAAAD AAMAqgEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAACAAMAsAESAAMAAAABAAEAAAEVAAMA AAABAAMAAAEWAAMAAAABAKoAAAEXAAQAAAACAAMAuAEcAAMAAAABAAEAAAFTAAMAAAADAAMAwIdz AAcAABDoAAMAxgAAAAAACAAIAAgAAAAIAAH+CAAB/gAAAQIAAAEAAQABAAAQ6GFwcGwCAAAAbW50 clJHQiBYWVogB9YABAAeABAAAgAkYWNzcEFQUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPbW AAEAAAAA0y1hcHBs8Km3nXI3UvdB2LuwZ9wBHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAOclhZWgAAASwAAAAUZ1hZWgAAAUAAAAAUYlhZWgAAAVQAAAAUd3RwdAAAAWgAAAAUY2hhZAAA AXwAAAAsclRSQwAAAagAAAAOZ1RSQwAAAbgAAAAOYlRSQwAAAcgAAAAOdmNndAAAAdgAAAYSbmRp bgAAB+wAAAY+ZGVzYwAADiwAAABkZHNjbQAADpAAAAH+bW1vZAAAEJAAAAAoY3BydAAAELgAAAAt WFlaIAAAAAAAAF1MAAA01QAAB9tYWVogAAAAAAAAdAUAALP7AAAiflhZWiAAAAAAAAAlhQAAF0sA AKjMWFlaIAAAAAAAAPNSAAEAAAABFs9zZjMyAAAAAAABDEIAAAXe///zJgAAB5IAAP2R///7ov// /aMAAAPcAADAbGN1cnYAAAAAAAAAAQHNAABjdXJ2AAAAAAAAAAEBzQAAY3VydgAAAAAAAAABAc0A AHZjZ3QAAAAAAAAAAAADAQAAAgAAAgQC9wQFBQUGCgcFCAsJCAoNCwgMCg0QDg0PDxAOERASEBMS FBIVFBYXFxgYFhkaGhsbGhwdHR0eHyAeISIiIiMjJCUlJCYmJycpJyopKyosLC0tLi8vLzAxMjEz NDQ0NTQ2Njc3OTo6Ojs7PD09PT49Pz9BQkJCQ0NEQ0VFRkZISElJSklLTExMTk1PT1BQUVJSU1RU VVVWV1dXWFhaWltbXFxdXl5eYGBhYWJiY2RkZGVlZ2doaGlpamlra2xtbW1vcHBwcXJycnNydHR1 dHZ2eHh5eHp6e3t8fH19fn5/f4CAgYGCg4SDhYSGhoeHiIiJiYqKi4uMjI2Njo6Pj5CQkZGSkpOT lJSVlZaWl5eYmJmZmpqbm5ycnZ2enp+foKChoaKio6OkpKWlpiWmpqenqKipqaqqq6usrK2trq6v r7CwsbGysrOztLS1NLW1tra3t7i4ubm6uru7vLy9vb6+v7/AP8DAwcHCwsPDxMTFxcbGx8fIyMlI ycnKysvLzMzNzc5Nzs7Pz9DQ0dHSUdLS09PU1NXV1lXW1tfX2NjZ2dpZ2trb29zc3d3eXd7e39/g 4OHh4uLjYuPj5OTl5ebm5+foZ+jo6enq6uvr7Ozt7e5t7u7v7/Dw8fHy8vNy8/P09PX19vb39/j4 +fn6efr6+/v8/P39/v7/fv//AAACBAL3A3AEBAUJBgQHCggHCQwKBwsJDA8NDA4ODw0QDxEPEhET ERQTFRYWFxcVGBkZGhoZGxwcHB0eHh0fICAgISEiIyMiJSUmJicmKCcpKCoqKyssLS0tLzAwLzEy MjIzMjQ0NTU3ODg4OTk6Ozs7PDs9PT9AQEBBQUJBQ0NEREVFR0ZIR0lKSkpLSkxMTk5PUFBRUVFS UlRVVVVWVldXWFhZWVpbXFtdXV5eX19gYWFhY2NkZGVlZmZnZmhoaWpqamxtbW1ub29vcG9xcXJx c3N0dHV0dnZ3d3l5enp7e3x8fX1+fn9/gICBgIKCg4OEhIWFhoaHh4iIiYmKiouLjIyNjY6Oj4+Q kJGRkpKTk5SUlZWWlpeXmJiZmZqam5ucnJ2dnp6fn6CgoaGioqOjpKSlpaamp6eoqKmpqqqrKqur rKytra6ur6+wsLGxsrKzs7S0tbW2tre3uDe4uLm5urq7u7y8vb2+vr+/wMDBwcLCw8PExMXFxkXG xsfHyMjJycrKy8vMzM1Mzc3Ozs/P0NDR0dLS01LT09TU1dXW1tfX2NjZ2dpZ2trb29zc3d3e3t/f 4F/g4OHh4uLj4+Tk5eXm5udm5+fo6Onp6urr6+zs7e3u7u9u7+/w8PHx8vLz8/T09fX29vd29/f4 +Pn5+vr7+/z8/f3+/v9+//8AAAGCAmUDQAQcBPEFuwaJB1wIMQkHCdUKoQtyDEUNFA3jDrUPhBBR ER4R7hK5E4cUWBUnFfMWvReHGFEZGhngGqobdRw/HQUdyh6PH1UgHSDjIaoibyMwI/ckuSV6Jjwm /ifDKIQpRSoMKswrjSxNLQgtyS6IL0UwBDDFMYUyQzMVM+00wjWWNmw3QDgROOM5tTqHO1k8Lj0I Pdw+sj+JQF5BMEIAQtJDqER9RUlGHUbvR8RImUlrSjpLEEveTK1Nf05MTxlP6lC4UYNST1MbU+dU sVV5VkZXC1fRWJdZYFoqWvJbtlx4XTxeAl7FX4hgUGERYc5ii2NNZA1k02WoZpZnjWiLaXtqdWts bHBtYG5bb0VwOHEsciJzE3QFdPh153bTd7x4pXmUeoN7b3xOfTd+JH8Mf++A1IG8gp+DfoRohUiG KocPh/KI04m0ipaLeYxcjTuOHY79j9yQvpGhkoeTb5RPlS+WEpb4l96YvpmdmoCbZ5xRnTqeHp8D n/Sg9KH6ovuj9qT0pfCm76foqOCp46rdq9SszK3Err6vuLCzsa+yrLOqtKm1qbart664xrnMutO7 2rznvgG/FcAwwUnCbMOVxMHF78cfyFDJiMrSzCPNds7M0CnRmtMS1JHWJtfD2WjbJ90C3ujg7uMZ 5Wbn1uqP7Y/xDvVt+x7//wAAbmRpbgAAAAAAAAY2AACXOAAAVsIAAFQSAACKMAAAJ6sAABaoAABQ DQAAVDkAAiFHAAIR6wABRR4AAwEAAAIAAAABAAMACwAWACUANwBNAGUAgQCfAMEA5QELATUBYQGQ AcEB9QIrAmQCnwLcAxwDXwOjA+oENAR/BM0FHQVGBXAFxAYbBnQGzwctB4wH7ggfCFIIuAkgCYoJ 9gpkCtULRwuBC7wMMgyrDSYNog4hDmEOoQ8kD6kQLxC4EUMRzxIWEl0S7hOAFBUUqxVDFZAV3RZ5 FxcXthhYGKoY/BmhGkga8RucG/McSRz4HageWx8PH2ofxSB9ITch8iKwIw8jbyQwJPMltyZ+J0Yn qygQKNwpqSp5K0osHCzxLVwtxy6gL3kwVTEzMhIy8zPVNEc0uTWgNoc3cThcOUk6ODsoPBo9Dj4D Pn8++z/0QO5B6kLoQ+hE6UXsRvFH90j/SglLFEwhTTBOQE9SUGZRe1KSU6tUxVXhVv9YHlk/WmFb hVyrXdJe+2AlYVJif2TgZhJnR2h8abRq7WwnbWRuom/hcSJyZXOpdO92NnjJehV7Y3yyfgN/VYCp gf+DVoSvhgmIwoohi4GM445Hj6yREpJ7k+SWvJgrmZubDJx/n2qg4aJao9Wmz6hOqc6rUa5ar+Cx abLytgu3mbkpurq94b93wQ7Cp8RBx3vJGcq6zFvN/9FK0vHUm9ZF1/HZn9z/3rHgZOIZ49DnQej8 6rnsdu427/fxufVC9wj40Pqa/GX//wAAAAEAAwALACUANwBNAGUAgQCfAMEA5QELATUBYQGQAcEB 9QIrAmQCnwLcAxwDXwOjA+oENAR/BM0FHQVwBcQGGwZ0Bs8HLQdcB4wH7ghSCLgJIAmKCfYKZArV Cw4LRwu8DDIMqw0mDaIOIQ5hDqEPJA+pEC8QuBFDEc8SFhJdEu4TgBQVFKsVQxXdFisWeRcXF7YY WBj8GaEZ9BpIGvEbnBxJHPgdUB2oHlsfDx/FIH0hNyHyIlEisCNvJDAk8yW3Jn4m4idGKBAo3Cmp KnkrSiwcLPEtXC3HLqAveTBVMTMyEjLzM9U0uTWgNoc3cTfmOFw5STo4Oyg8Gj0OPgM++z/0QO5B 6kLoQ+hE6UXsRvFH90j/SglLFEwhTTBOQE9SUGZRe1KSU6tUxVXhVv9YHlk/WmFbhVyrXdJe+2Al YVJif2OvZOBmEmdHaHxptGrtbCdtZG/hcSJyZXOpdO92Nnd/eMl6FXtjfLJ+A39Vgf+DVoSvhgmH ZYjCiiGLgYzjjkePrJESknuT5Ja8mCuZm5sMnH+d9J9qolqj1aVRps+oTqnOrNSuWq/gsWmy8rR+ tgu5Kbq6vE294b93wQ7EQcXdx3vJGcq6zFvN/9FK0vHUm9ZF1/HZn9tO3P/gZOIZ49DliOdB6Pzq uex27/fxufN89UL3CPjQ+pr8Zf//AAAAAAAGABIAIwA5AFUAdQCZAMEA7gEgAVQBjgHLAgsCUQKb AucDOQOMA+QEQAShBQYFbwXdBkwGvwc4B7UINgi5CUAJygpdCu4LiAwlDMQNZQ4PDrUPZRAXENMR ixJNEw8T0hSeFVkWDxbPF40YURkaGeUasxuEHFUdIh37HtAfqyCMIXIiVyM5JCwlGCYKJvgn7ijr KeIq6SvpLPMuAC8JMB8xNjJPM2o0kTWyNuE4ETlBOnA7qDzrPi0/bkC7Qf9DUkSzRglHZ0i0SdVK 7Uv6TRxONE9RUGFRilKoU91VBlYxV1tYkVnCWvhcNl15XsNgA2FGYo9j7WU7ZoZn42lEaptr/21r bsdwOHGkcw10gnX4d25443pce959Wn7hgGWB54NmhOSGdYgDiYyLEoynjkCP1JFjku6Uf5Yfl7CZ JJqOm/qdep7xoHOh66NwpP+mdqf+qY6rH6ywrkGv07Fksva0h7YZt6q5O7rLvFu9zL9ZwOjCd8QE xXfG+8hyye/LaszVzj/PqNEP0nbT3NVB1p7X59kv2nbbvN0B3kXfeeCl4c/i+eQc5THmROdX6Gjp cepw62PsU+1A7i3vDO/r8LzxjvJW8xvz2/SV9U719vaf90L32/h0+QX5h/oK+o36+vtl+8/8OvyV /OT9NP2D/dP+I/6J/vT/X//J//8AAGRlc2MAAAAAAAAACkNvbG9yIExDRAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAABtbHVjAAAAAAAAAA8AAAAMaXRJVAAAABQAAADEZnJGUgAAAEIAAADYbmJOTwAAABIA AAEaZXNFUwAAABIAAAEsZmlGSQAAABAAAAE+cHRQVAAAABgAAAFOemhUVwAAAA4AAAFmamFKUAAA AA4AAAF0bmxOTAAAABYAAAGCZGVERQAAABAAAAGYa29LUgAAAAwAAAGoZW5VUwAAABIAAAG0c3ZT RQAAABAAAAHGZGFESwAAABwAAAHWemhDTgAAAAwAAAHyAEwAQwBEACAAYwBvAGwAbwByAGkAyQBj AHIAYQBuACAA4AAgAGMAcgBpAHMAdABhAHUAeAAgAGwAaQBxAHUAaQBkAGUAcwAgAGMAbwB1AGwA ZQB1AHIARgBhAHIAZwBlAC0ATABDAEQATABDAEQAIABjAG8AbABvAHIAVgDkAHIAaQAtAEwAQwBE AEwAQwBEACAAYwBvAGwAbwByAGkAZABvX2mCcm2yZnaYb3k6VmgwqzDpMPwAIABMAEMARABLAGwA ZQB1AHIAZQBuAC0ATABDAEQARgBhAHIAYgAtAEwAQwBEzuy37AAgAEwAQwBEAEMAbwBsAG8AcgAg AEwAQwBEAEYA5AByAGcALQBMAEMARABMAEMARAAtAGYAYQByAHYAZQBzAGsA5gByAG1faYJyACAA TABDAEQAAG1tb2QAAAAAAAAGEAAAnFYAAAAAv/h7gAAAAAAAAAAAAAAAAAAAAAB0ZXh0AAAAAENv cHlyaWdodCBBcHBsZSBDb21wdXRlciwgSW5jLiwgMjAwNQAAAAA= item2.X-ABRELATEDNAMES;type=pref:9DE 'D,(1 item2.X-ABLabel:_$!<Assistant>!$_ X-ABUID:C25AB89B-020A-418C-8F48-047834773FC7\:ABPerson END:VCARD tests/auto/versit/qversit/testdata_vcf/AAB4-MultipleAscii.vcf000066400000000000000000010462611233466112000245710ustar00rootroot00000000000000BEGIN:VCARD VERSION:3.0 N:;;;; FN:Apple Computer Inc. ORG:Apple Computer Inc.; TEL;type=MAIN;type=pref:1-800-MY-APPLE item1.ADR;type=WORK;type=pref:;;1 Infinite Loop;Cupertino;CA;95014;United States item1.X-ABADR:us item2.URL;type=pref:http\://www.apple.com item2.X-ABLabel:_$!!$_ PHOTO;BASE64: TU0AKgAAAyiAP+BQOCQWDQeEQmFQuGQ2HQ+IRGJROKRWLReMRmNRuOR2PR+QSGRSOPPyTOZ1OxjM 1pLZfsVtuBxySaTWHvF5vVkNBqqlZrlTrFbqqfthvOKbUmlQN4PN6L1jMugrep1NZLtguR0uul12 RvZ8PhhstoVWhWZbr5jsywPivW+PzFxqhZUCz3ehz9st+Z3C/Rp6vd8MFks60Whis1ovd8vm/4+L ul2O5YrpgYehMBkM2mvTIZ+LOJzOm0ZVgM5qtmcvbQa1+696PZ76vXv2Eu14PJmNNsNNtN7JO7a0 yncF2u9423Wx+TPx0ZNlNFqrthslar5iddiLxispmtRsc93Pl9PuB7Xyvt3vJ575vVBlrVesOrsF ZLxgy5i2NoN1xnMxjHOWiLVmWaRrFcW5eroXDMKooS6FyV5cF8zRmmubpwG4cJyJWaT6wjB0HKIX MLHUdx4QGhbcHk7hlREvEYQeW8GQZGUbxioTtHIdB1uHFTVl4YkXxzGccSNIsjyUvDCGdALlubAx rSXJEqypK8ilyYRjq1HzXs+2seHWWBcl/LErSTNM0TWtBcGCYzjnjFR/vEY5nGnG01TPPc9KFCZf NEdM5oGaBrm27U+TZPtFUYYhmGi9NBoEX61xJRNL0WtBWFsXijKRSSB0RTNR0ZTBbvqcrJVAgb61 NV1SPkYZ1OPVaBNNV9S1JGRZl4YTxVqf9eGFXFiTVBJem8cZz2BIRlTzXNoWLGcIu+bFgTsacyTN XVuWjWDsTjUBsG6cLqGTaV0LxBhkulJ8VRY3ZsRDbt02hEl4uU1zXnAcpz21euAQgur+Vmd7ltW+ GA29ha0F0YZkTmaRsG5eeGXpi9GSlOb1nnYWFXrTZeYLUDUGzj+MRknZq0jSSnHrNxjZPi0qvrE8 U2AgVknOVZaF1mUzp8XL3ZwgramgaxtZ/btHGjfOiIKtsLaVRhbmAYtw6ehacHrqVFwZhxkSlQpt 2qtRmUtReYHOdZ26yiLAnwaOJvqVZal2+5g5VLrYnu9LauatrxXi7RWwVvF445t3FcXxnG8dx/Ic jyXJ8pyvLIIgIAANAQAAAwAAAAEAMAAAAQEAAwAAAAEAMAAAAQIAAwAAAAMAAAPKAQMAAwAAAAEA BQAAAQYAAwAAAAEAAgAAAREABAAAAAEAAAAIARUAAwAAAAEAAwAAARYABAAAAAEAAADjARcABAAA AAEAAAMgARoABQAAAAEAAAPQARsABQAAAAEAAAPYARwAAwAAAAEAAQAAASgAAwAAAAEAAgAAAAAA AAAIAAgACAAK/IAAACcQAAr8gAAAJxA= X-ABShowAs:COMPANY X-ABUID:C5C50103-C86C-40BB-8864-909A089EB390\:ABPerson END:VCARD BEGIN:VCARD VERSION:3.0 N:Lastname;Firstname;;; FN:Firstname Lastname ORG:Company; EMAIL;type=INTERNET;type=WORK;type=pref:work@email item1.EMAIL;type=INTERNET:other email item1.X-ABLabel:_$!!$_ item2.EMAIL;type=INTERNET:custom@email item2.X-ABLabel:custom email label TEL;type=WORK;type=pref:work phone TEL;type=WORK:work phone 2 TEL;type=CELL:mobile phone TEL;type=WORK;type=FAX:work fax item3.TEL:other phone item3.X-ABLabel:_$!!$_ item4.TEL:custom phone item4.X-ABLabel:custom label item5.ADR;type=WORK;type=pref:;;Work Address;Work City;Work State;Work ZIP;Work Country item5.X-ABADR:us X-AIM;type=WORK;type=pref:workaim X-JABBER;type=HOME;type=pref:Jabber X-JABBER;type=WORK:workjabber item6.X-MSN;type=pref:othermsn item6.X-ABLabel:_$!!$_ X-YAHOO;type=HOME;type=pref:homeyahoo X-ICQ;type=WORK;type=pref:workicq PHOTO;BASE64: TU0AKgADAAhIMyBIMyBIMidELiNDKxc/KBVAKRY/KBVALBZGMRpIMx5KNSBKNylOOixPOSdIMiFD LxhEMBlEKxlFLBo6LBY5KxU5JQ47Jw87KhU+LRdELh9IMiNNNSlQOSxVPi5ROytPOS1POS1PODFT OzRKQDA9MyRHKRZJKxdJMyJOOCZKOCVFMiBAMyVAMyVAMydFOCtMQDVHPDFFNyU/MSA8LyFGOSpP QC5WRzRQPzBJOSo8MiM4Lh8/LyFVRDReTEVlU0xnT0FkTT9hT0NjUUVhSTxdRjlJNSBDLxpHNy5T QTlYQzJMNydUOSVVOiZRNyNOMyBKLBpIKhhDLxpFMRxGMRxFMBtOPihQQCpMOSZHNCJMNSJWPytW QTNWQTNTPjFKNypMMiJNMyNTQTNcSjxiT0ZfTURYRS9MOSRUQC9bRzVbSjhNPStHOCY/MB9ENR5K PCRYQThXQDdROy5POSxGOiVEOCNKOy1TQzRNPS9AMSREMCRHMydELyBJNCVDLxhFMRpJNyZEMSFF MSRALSA5LB48LyFGOjhOQT9VSUBYTURTQzdNPTFJOylDNCM/Mx83Kxc6LyhEOTFRRj5XTERYRz5J OTBBMSNMOyxOOi9OOi9KMy9MNDBENSxFNy1IODJMOzVMRTxTTENUSj5GPTFGNylGNylAOSo8NCZG LytROjVQPjVTQDhNQDxOQT1OPixJOihFNC9JOTNJPz5KQD9YRUlfTFBcSUxFMzU9JBpGLCJINzdR Pz9aRkhcSEpOOzlFMjBIOTpaSUpeTElcSUdROTRNNDBDLSJDLSJBLRpELxxOOChQOipTOzRWPjhW RT9UQz1POjVNODNJMi5IMS1NPDdYR0FfSUFNODBBLy1RPjxWQ0NOOzs9JyE+KCJALCVBLSZIMiNH MSI9Lhk/MBtOOSlVPy9YQThcRTtbQzdVPTFUOS1WOy9VPylRPCZMNSk+KR1FMCtQOzVPOy5MOCtR OCVWPClcQTBUOilHOCRGNyNKNSRGMSBHMh1UPihPNCFGLBlKNSZPOipMMiREKx0/JhJAJxNEKhZH LRhIMBxNNCBQOB9NNBxHNylKOixJNSlEMCRHMR5KNCFGMB0/Khc8KQ88KQ9GKBZKLBpGMB1BLBlF LxxKNCFHOi1PQTRXRDJWQzFQQCxMPChWOzBYPTJTQTVKOi5HKhtKLR5NOidNOidJPCxFOChDNSZE NydDLyJDLyJMPC5PPzFNNSlIMSU7MSE7MSFKOylURDFWPzNTPDBFLyNFLyM3MCVEPTFaTkZhVU1i UEBkU0NfTkViUEdcUUNXTT5INSNALhxOOi1XQzVbRTRQOytYOzFcPjRYQTFTPCxQNCVNMSJKNChM NSlFMCFDLh9OPS5XRjdNOSxJNSlJOiRTQyxWRjpRQTVPPy1KOylBLiFEMCNQPzlfTkdjU09eTkpX QzRNOStMSDVVUT5bSUBQPzdIOig8Lh1GMiVVQDJdPzVaPDJROytUPS1KOitIOClIPzVMQzlIOSs9 LiFKNChMNSlKNCFKNCFBLxtHNCBNPC5EMyZFMB89KRg+LyE9LiA7NS9AOzRRRj5aTkZRRzlKQDJD PS43MSM9Kh4+Kx88LCZDMixRRUNVSEZMOjhDMS9HLSVQNS1UPTNTPDJQPDFJNSs9NCk/NytBNy9G OzNEQztJSEBORTlFPDBBMSNBMSM7My47My5BMStGNS9MQDVQRTpVRj5TRDxMPjFFOCtGMiZGMiZF MjJKODhNRUFPR0RJPjM+MylFLiVKMypNOzVXRT9cTE1XR0hFNTI8LSo9MzBPRUFWSUdUR0VNOS5H MylEMCM/LB9BLRZMNx9UPi5VPy9VQzpTQDhROjNNNS9MMSdOMylNNTJONzNTQDteTEZVQDNFMSVF Ny1TRDpTPjNHMyk9Jxs+KBxFKxhJLxxQMiFRMyJDMyVIOSpVQDNWQTRXQTpXQTpYQTFUPS1TPS1W QDBVPTFQOS1KMydDLCBNNTFTOzdJOi5GNytOOixXQzRaRDNPOipQOylTPStOOChVPi5cRDdfRzpb PCpPMSBQNyRUOidIMBo6Iw9DJg9FKBFGKhZOMRxPOSdROylNOSNOOiRHNylOPS9MNSZNNydONCRP NSVMMxtMMxtFMRhFMRhMMCFMMCFKNSJJNCFJNCFKNSJNOS5VQDVWSDtTRThNRTJHPy1MPC5OPjBN PjdGODBBLCBAKx9UPS1WPy9KQDBEOipGNyhHOClMMSlGLCRQOi5aQzdTOzRONzBFMCFELyBHMiNU Pi5WRTdTQTNHLhxEKxlDLSJJMyhUSEBYTUVhVkdfVUZfT0BjU0RfUEZeT0VQPzFDMiVJPC1QQzNW RTlWRTlVQz1aR0FdRT5YQDpUPS1TPCxMPCpNPStEMCZGMihTPDBeRztcRDdROi1HOCpWRjheTENc SUBFOjE8MSlBLiFGMiVMQDVXTEBnTU1lTExhRTpWOzBQSTNaUzxTTEFIQThEMyU+LiBNNy1WPzVe QDdYOzFcRjVbRTRUPzFPOy1NRDpKQThGNyVENCNTPDBUPTFQOyVOOSNPOCxTOy9TPTVNODBILCU/ JB0/KxxDLh9BMyxBMyxTPTVXQTpPPzFRQTNIPzM4LyRDKR9FKyFELypPOjRWRUhUQ0ZMNy9FMClB KyJPOC5VQzxUQTtNOzRBMCo/MiZAMyc9MTFFOTlMRD5ORkBOPjBHOCpALSBALSBGNCxEMipDMipE MytQPjVWRDtaRTpRPTJJOixFNShBMSNBMSNBMChJOC9UPTxTPDtOOi1MOCtQOSxVPTBQPjxWREFW QUZVQEVIOjI4KiM3JiRNOzlWTU5KQUNONy1MNCtIMx5FMBtMMiBYPitRQTVWRjpXRj1RQDhROjBO Ny1QODFTOjNPODNXPztfT0BfT0BKPS4/MiRJNDBYQz5WPytKNCE5JxY8KhhGMhdPOx9UPSlVPipT Oy9XPzNTQzdWRjpRQDRKOi5WOzBWOzBYQTVeRztUQz1MOzU+LyQ9LiNJMypMNSxMOy1OPS9URDhR QTVPPTRINy5KOylMPCpTQDpcSUNkU0NUQzNKNSRDLh1PNyBPNyA/Lg4+LQ0+KA87JQ1BLBlIMh9K NCFROydNNyVJMyJGNB5MOiNMMx9ONSFPOSdOOCZIOSNHOCJNNyNPOSVVPi5UPS1TPyxTPyxRPCpO OSdJNC1WQDldTD1YRzlURDhOPjJFOytHPS1QOSxTOy5POCtPOCtPQC5QQS9VPjFNNypJOSpMOyxN Ny1GMCdIMS5cREBYQDpTOzRNNydJMyRGMSJMNydUQzdWRTlVOi5XPDBNNyVGMB9JOTBaSD9qVlRq VlRkVEdjU0ZhUUpfUElTQTxHNzFANS5MQDlWSUdYTElUSUhVSklcSkRcSkRcRzxUPzRROytTPCxK NChNNypUQTleTENTQzdMPDBIQDFRSTpbTERaSkNRQTVENCk/MSA+MB9RPTBeSTxaT05dU1FcSURW RD5USkBaUEZWRz9KPDRAMSBDMyJKNS5UPjddQzFeRDJdRjpcRTlXQzhRPTJOPjJNPTFMOy1RQDJT PTVTPTVXQDRYQTVQPC9TPjFMQDhJPjVHMiM+KhtBLRhELxpFMB1JNCFJPC1QQzNMQzpPRj1QQTtH OTJAKh5AKh5ALzFPPT9XRERPPDxFNSRDMyJFMRxNOSNYPDlXOzhROTRONTFHMylINCpAMixDNC5J ODFQPjhTRDxHOTFAKBpFLB5GMCVFLyRELSdMNC5OPDVWRD1OQDNJPC9BOCc+NCRELyhFMClEMyhG NSpKOi5PPjJROi5YQDReSDhYQzJTQzdTQzdYPz9VPDxGNys5Kh83JyJJOTNNRUFGPjtQOjBQOjBI Mh9HMR5QOjBbRDpWREFYRkRYSDxURDhXQTxVPzpNOzlQPjxWRT9aSENcTj1YSjpNODNNODNOPDVO PDVOOSdGMSBBKRdNMyFRPS9TPjBTPjNTPjNVOi9XPDFURTtPQDdFOCg/MiNONzNXPzxbTEVdTkdW RDtJOC88LRw7LBtFLiNJMidQNS1cQDhUQzpOPTRHNzBEMy1ENDVJOjtaRkpiTlNaSTdMPCpIMyJB LRxPPi9NPC1DNRY7LhA7Kgo8Kws/LBZALRZKNSJOOSVMMx1ELBZALBlNOCRNOChKNSZKNSJMNyNJ OiRHOCJNOStPOy1QPC9RPTBOPTRNPDNPPjBNPC5KOixTQTNYRTNXRDJMPi9KPS5JOixMPC5NNSlQ OSxOOCxTPDBMQTFKQDBQPzBHNyhGMiZJNSlKNCtGMCdHLzFXPkBaR0BUQTtPOihMNyVJNCFIMyBJ OTJVRD1VQS5QPSpMNyNIMyBHMSZVPjJfTUpjUE5kT0FkT0FeU0lcUEdaPD1QMzRAMCNJOStUREBb SkdVTENVTENdTUBcTD9XSTxVRzpOPS5PPi9IOCpMOy1PQz5dUExPQy9EOCVEPCtNRTNeU0laTkVT PjBKNylBMSNDMiRMOyxXRjdYSk1cTlBaSENYR0FUSUZbUE1aRkpKODw+LiBAMCJHOCxPPzNVRDVb STtcRD9bQz5UQzpPPjVMNSxKNCtQPjVUQTlRQTVURDhVRDVUQzRPPzNQQDRKPzdDOC9HMh88KBY/ LBZBLhdJOCFNOyRGPC5JPzFFPjVNRj1ORzFJQy1JNSlDLyNBMi9MPDlTQDpNOzRHOCJHOCJMOCJP OyVVPjRVPjRTOzFROjBKOCNBLxs8LSBAMSRHNS9NOzROPzVIOjBHMiFHMiFBLCBFLyNJMyhNNytP OzBVQDVKQTVHPjJFOi9DOC1GNSpEMyhHMyZHMyZJOitRQTJbSD9iT0ZaSkRTRD1NPzBKPS5OPDNN OzJEMCY9KiA+LCpINTNKOjFIOC9KMy9MNDBENCdENCdMNzJaRD9YRkZXRUVWRERUQUFUPjlPOjRN PD9OPUBTQD5RPz1VRTdNPS9HMCRIMSVHOi1HOi1GNyNFNSJJLxhRNx9XQDNVPjFTOzdTOzdRPS9a RTdWSDtKPTA/MRw5KxZQOjteR0hcUUNWTD1TQzdOPjJGMyM9KxtEKiJPNCxYRUVjT09YR0FAMCs4 Khk3KRg5LypMQTxbST1bST1RQTVJOi5GMhc+KxFKNyxNOS5HMxo8KRE8KBA8KBA+KxZALRhJNR5N OSFMNyFGMRw9LBZFMx1HMydFMSVNOStPOy1HNyhGNSdFNSpGNytJOS1KOi5EOS5ANStKOi5JOS1M PTdMPTdKOitMOyxIOSpGNyhFNSpFNSpFNClMOy9MPC5OPjBNPzJOQDNIOyxFOClEMyhHNytNNytO OCxIMjFPOThTRkRQREFPRTdKQDJJOStJOStMOzRQPzlPQTROQDNNOzJHNS1GNB5HNR9YRkBeTEZd R0FdR0FiUUFhUEBeRDRONCZGLytKMy9MPztYTEdVSUBVSUBdTUleTkpYSUNVRj9NPTFOPjJDNSlK PTBUQURjUFNYRkBNOzVHOTFRQztbU1FRSUhOPDVJODFJMyJMNSQ+LyFJOitaT0BbUEFbSUBaSD9d TEVhT0hdRz9NODA8Lhs+MB1EOS5KPzRURTtaSkBcSEZbR0VQRTxGOzJBKydJMi5UQzdUQzdVRzpV RzpQQDFTQzNVRDtXRj1KQTVDOi5JMRlAKRJALhJINRhQOydRPChKPDJJOzFIPTJMQDVNRTJMRDFK OTBBMCg/MSpHOTFPOzBPOzBFNSBJOiRPOy1UPzFWRD5QPjlXPzlWPjhRPSVGMhs+LRc/LhhEMixJ ODFKOjNKOjNFNShDMyZBMSREMyZDMyZHOCpMPi5PQTFRQDpKOjNKOjRHNzFFNy9DNC1ENCdGNylP PjVaSD9bTk5bTk5JRTpDPjNDOC8/NCxGOSxDNSlALh5BLx9HNytHNytINzBKOTJHNS9HNS9ENS4/ MSo3LSxNQ0FbSUBXRj1WRT9UQz1QOjBOOC5OPDdRPzpRQzxTRD1TQzdMPDBMNSpHMSZAMR5FNSJI NB9TPihVPixeRzRiT0hYRj9VPTdUPDVTQzdXRztPQz4/My89KSRBLShTSEVdU09aR0FUQTxPPzxJ OjdKNSQ/Kxo9Ky1VQURbSEZVQ0BIOCwyIxgvIBg6KiI8MjFPRURjSkdbQz9TPCxAKxw0Ig88KRVN MSBTNyVPNRpKMRZFKhA/JQw/KBNGLhhJNB9NOCJHOCQ6KxgzJg83KRE+KRZGMB1HOCJGNyFGNylF NShBMic+LyRHMylGMig8LR8+LyFDMiRHNyhNODNQOzdOOi1MOCtHMSBIMiE/LyQ+LiM+NCVBOChD OypDOypMPCpKOylGMyE/LRtBMiFKOylKOylHOCZNOSxJNSlKOy9QQDRPRDlIPTJOOCtMNSlKOixN PC5OOi1POy5OPTRIOC9NNSpJMidVMSxcODJVRDheTUBYUUVVTkFXSTlNPy9BOCc8MiJIPTJXTEBN R0NIQz5TTUpTTUpcSEZPPDpTPTVXQTpQOzNMNy9QPjVbSD9YRj1NOzJIOjBYST9aUUxVTUdQQCxJ OiZGOCBGOCA4LiE+NCdJPzxMQT5aR0FeTEZkVFNdTUxURzNFOSY/MBs4KRU9MCFIOytbR0VdSUdX RERTPz9IOS1HOCxAKyJKNCtVRTlhUERaTDxVRzhTRTRWSDhbSj5bSj5URDVMPC5FMxs/LhZKNx9W QSlbRjhdSDpRRzlGPC5NOzRRPzlQRT1OQztPOjRDLik9Lhk7LBdGMSJMNydINChRPTBWQDlVPzhU QTtTQDpQPTtRPjxQOi5NNytFMBs+KhY4KRxAMSRGOC5HOS9BMiVAMSRAMSA+Lx45KhxGNyhJPC9K PTBPOjJTPTVNPDVGNS9HNytAMCU+MClJOzNUSD9YTURWRT5KOjM7LCE+LyRAMR4/MB1INCdJNShO PS9KOixENSxDNCtFNCxFNCxBMiFBMiFFNCk9LSI0Ly09ODVWRD5XRT9TQTtRQDpPOjJJNC1BLiJG MiZPOjVRPDhMNzFMNzFFMiA7KRcxIxM+Lx5PODFcRD1lT0dhSkNaSTtHOCpEOCVNQC1MRjdRTDxN RDo+NSw9LitGNzNRR0RRR0RRQztQQTpPQTRHOi1ELxY9KRFEMCZWQTdTQTtNPDVILx8/Jxc6JBk+ KB1FLjNYQEZYTUFIPTJHMh88KBY9LBZRPyhRNyFQNSBWORlTNRZGLg9AKQtFLBRHLhZNLyBTNCVH OSc9Lx40JQs5KQ89KBlELh8/MRw/MRxIOSVMPChGNSo7KyBALBtALBs6Kxg9LhtHMCRONypMNzJO OTRRPTJRPTJINB9GMh08LyE+MSNBNSNHOyhGPCtHPSxOOypKOCdGMx8/LRlAMCJHNyhNOidOOyhK OitHNyhGNylNPS9QOS9XPzVVPi5ROytVPipTPChPPCtTPy5PPzFMPC5IOClIOClILyFJMCJJPC9U RjlfTkVhT0ZYTUFVST5DPy86Nyc+MyxOQztKRUBMRkFRSkFWT0ZVSERMPztHPDNQRTxRQDpIODFQ QDRYSDxVPTdMNC5IOy5XSTxXU0hWUUdWQTdTPjNHOipIOys8MSk+Mys+Mi5MPztaR0dfTU1pUVNk TU5VRzhGOSo+MBs3KRU+MiBPQy9cSUdVQ0BPQDlKPDRFNShENCdFLiNPOCxaTUpeUU9RSkFORz5O QDNVRzpaTD5XSTxbRjtRPTJBOSNHPihRRDRXSTpWSUVXSkZQQzVOQDNNOzJQPjVRQDpPPjhPPClD MB4/KxZIMx5GNSpFNClJNC9RPDdVSUBaTkVUQzxNPDVFOTRKPjpKPDRHOTE+LRc6KRRIMyJKNSRE NS5BMyxIMyRFMCFBLRY9KRM5KhlGNyVMPC5MPC5OPDNOPDNJOi5BMidFMSVGMiZENztPQUZXSEBT RDxJOiw7LB85Jxc9KxtELiNNNytQQzVQQzVOPTdNPDVHMydEMCREMCREMCRDLyNALSE9Kxs+LBw8 LSI/MCVHOzdOQT1VQ0BPPTtFMytALydBMSZEMyhINzRHNTNENCdAMSQ9KxkyIRAyJR47LSZRPDhf SUVbT0ZWSkFMPjE3Kh49MCRKPTBTQTVUQzdJOTA+LiZAMydBNChKPzhTRz9OQzpJPjVPPi9QPzBI OSVAMR5ENSxQQThNPjdGODBEMhxBMBpAKiZAKiZMOjxXRUdRQTVENCk/MSA8Lh0/MiZMPjFUMyVa OSpiQCZdPCJWQCZUPiRPNB1KMBlPLR5cOSlTPS1FMCFAKRJAKRI8KBRELxpFMh5FMh5NPStOPixA NCI0KRc9JhE+JxJAKBZAKBZGKx5MMCNMNSpUPTFOPS5HNyg7Lxk1KhU6Jxs8KR1DMyVJOitDPCdI QSxKOyxIOSpGMCQ9KBxDLyJGMiVJOSpNPC1IPjBDOStGNSdFNCZMNzFTPThUPzJVQDNVRDRQPzBV PixVPixOPi9KOyxJOiRIOSNFOSZANCI8MiVIPjBbSEFhTkdWSkFUSD9HPjREOzE9OS5FQDVORTtQ Rz1VRUFaSUZWRT5PPjhOPjJQQDRQPzFQPzFKQzFUTDpbPjlMMCtAOCxMQzdQT0lTUUxYPzxWPTpN NzVQOjlNODNELys7LSRJOzFeRUlkSk9eSk9aRkpKRUA9ODM9MCQ4Kx9ENyhQQzNXTkRQRz1GPCxD OSlGNCxEMipFLyRTPDBYTkpcUU5UR0NQRD9QPjlXRT9cSUNYRj9URTtOPzVFOi9MQDVXR0ZdTUxb TkxXSkhMPTNHOS9KOjNKOjNOPTFIOCxALh4+LBxFMSRNOStIOCxBMSZGMSpQOzNQSkRbVU5NRDtB OTBGNTBFNC9GNS9FNC48MB44LBpGOSpGOSo7Mio3LiY8LRw8LRw8KhY9Kxc9LR9IOClQPC5RPS9O PDNKOTBENydBNCVDLSRHMShBNzpKP0NUSTtMQTM/OCc1Lh44KRs6Kx0/LylJOTJRPzlOPDVINChD LyNBKx9ELSFDMB4+LBo/KR4/KR45KhY6KxZBLR5FMCFJPjNRRjtVQT9KODVELSpAKic8Myg8MyhB Myo9LyY9MCI6LR81IxYzIRU1JiBAMCpPPDxaRkZWTDtJPy9GMCU6JRo6LCZJOzRUPTNTPDJKMydH MCRIMyROOSlTQTVTQTVRPS9QPC5GOSFGOSE9LiA3KBo+MSVNPzJNPixDNCM9KRM7JxE7JCBGLipR PDdPOjRPOS1GMCU4KBs5KRw/Li5NOztHLh5WPCthRTBlSTRjTDlbRDFPOyVBLhlKMh5ROSRWOS9W OS9MMiBFLBpEMRZFMhdFNSBJOiROPzlNPjhNNyVFLx5DKxdELBhBLBk/KhdFMCFNOChMOShNOilO PixIOSc+LBg8KhY7IxI8JBM3JRJDMBxIMyJOOSdbQC9VOypMMiBBKRdBLCBDLSFJNDBOOTRJQCxA OCRFMiA+LBpBMSRKOixQPDFXQzhUQTxVQz1dRz9cRj5WRjFTQy5OOypINSVIOSNENB89MCQ8LyNH Oj5URkpQST9TTEFURTtJOzFENClKOy9QPzlUQzxXREFbR0VURDhPPzNNOSxPOy5UPS1TPCxKQDJR RzlVPTdQOTJJODJTQDtRTkpQTUlVRD5OPThPOTpPOTpMOTdDMC43LSA6MCNNP0FcTlBdSkhXRUNJ PTk9MS07MSI1LB0+Mi5JPTlWRTlTQTVKOixGNShKNTFHMi5GMSxXQTxbSURfTkhRQzxOPzlNOzRT QDpXPzxdRUFYRkRUQT9KOjRQPzpYTEleUU9cSkVXRkBIOCk/LyFJLydOMytNNy1IMilALSFFMSVN NTJPODRKOjRAMCtFLipMNDBVSU1dUVVTQTxHNzFMMCtEKSRAKyI/KiE4MB8/OCZPQTJIOyw8LSAz JRg3Khs3Khs4Khk5Kxo7KB5INCpQOTVXPzxKOjFJOTBHOitAMyU9LShAMCtMOjhXRUNeTElNOzlA Lh49Kxs9Kh0/LB9MNC5QOTJMNzFGMSxELx5ALBtALBtIMyJGMCE+KRo7JRk7JRk7JhY+KRhMNyVP OihRQTNVRTdTQDtHNTA8LyE3Khw8Jxs8Jxs7KhM4JxA7KBI7KBI4JA07Jw83KRY+MB1HNS9TQDpU QT9INzRIKiBAIxlBMSRNPC5VOi9PNCpELyA+Khs/LCJKNyxOPDNNOzJNOStGMiVEMhpBMBg/LR0+ LBxBMiFGNyVMMx1IMBpFKRZDJxVFLyBMNSZOPS5KOitJMS1BKiY4JRk9Kh5EMCRHMydGLhZTOiFh STRnTzpnSkFeQzpNNyo/Kh5ALxdEMhpVOypcQTBWOS5ILCJKNx9JNR5NNydQOipNPDVPPjhNMidG LCFHLB1GKxxAKRZFLRlFKyBNMidMNydTPS1UOyJQOB9GMSA+Khk9JQw5IQk7IAs+Iw5FMRpNOSFe QTFdQDBRQyY/MRY4Jg06KA8+LyxBMi9FNyU/MSBELxw+KhdBKxJGLxZIOClQPzBPPjJRQDRXRT9X RT9WRz1VRjxTQTNJOStGOCZBMyJALSA6Jxo+MytMQDhQST9VTkRYQztMNy9BMSlHNy5NOjpUQEBU QTtUQTtaQzdUPTFJOC9NOzJUPzJRPTBOPy1TRDFRPi1QPSxNOzVTQDtaTU1TRkZNQD5BNTNJMS1I MCxBMic7LCE5MBs/NyFTRz9VSUFcRURTPDtGNSo+LiM7LCE9LiM/MiZFOCtMOShMOShJNCVOOSlM Oy9FNClDLilVPzpTRUxURk1JPT1DNzdENSRDNCNHNTNRPz1TQTtRQDpJOzRJOzRQPT9cSEpVSEhR RUVGMh1EMBtMMCFQNCVINSM+LBo+KRhFLx5ROy9VPjJNPTFENCk9LCdJODJVR05YSlFWQDlHMitJ LB8/IxYzHw84IxM3LiVGPTNUPzROOi8+KRg1IRE6JRk9KBw6LBc4KhY8JiBGLylTOjdWPTpQOS1O NytKNClGMCVELyhELyhUOz1iSEpaSENGNTBDLhY8KBA/KBVIMBxTOShROCdMMiBJMB5MMx1MMx1K NSZPOipKNClELiM8KRw5Jhk5KxhIOiZWRTVUQzNVPTdVPTdMOzJEMys8KhYzIg8+JhU+JhVDKA8/ JQxFKQpBJgdBKhVDKxZBLBlOOCRNODBVPzhKOTlGNDQ+Khs+KhtKMR9QNyRQOTJJMixBLCM6JRw9 KBxIMiZGNSdGNSdHMydINChDNyQ+MiA/LRs+LBpHMCVHMCVGLyNGLyNBJxhFKhtDLSRGMCdJNC9M NzFHMCVGLyQ6JBk7JRo8Kho/LR0+KhlOOSdYRzhfTj5lTUldRUFOOCxBLCE7LhY/MhlUPTBcRThW PzVOOC5TNyVVOSdXOi9UNyxQPC9RPTBJNCVALB1KLR5KLR5DKxRAKRJAKh9HMCVQNyRXPSpcQSdW PCJJOiRENB9EKxFAKA8+KxE1Iwo8JxtROy5bRDdfSDtbTDlHOSc7Kw83Jws9Lh0+Lx5FMiJGMyND LhY9KRE8JQ8/KBFFNyVKPCpMNyNOOSVPOzBUPzRTRz9TRz9UQzRQPzFKOylENCNFLiJAKh5DMyJN PStVRj9aSkRVQ0BKOTdDMiRDMiRHMydMOCtOOi9UPzRXQDdXQDdIOCxGNSpNOS5QPDFQQC5URDFR QDJRQDJOPj9OPj9USkxRSElORTlDOi5DLR5NNydENx85LBY1LB8/NShWRERVQ0NYQz5QOzc/MB87 LBs9KxlALhw8LyA9MCFIMyRMNydINSVNOilOOyZGMx9DOC1JPjNOQEVTRUlNPTFFNSo9MRtANB5H OCpPPzFPQTJNPzBMPTdJOzRKPDVTRD1RRUNRRUNHMylKNyxPOCxMNClENyg7LiA6Kxg+LxxHOCxM PDBHOS9DNCs/MStMPTdaSExbSU1YRDVNOStHLxs7JBEyIxY4KBo7MzBKQz9QPzNFNCk7IRE8IhJF KBdJLBtGLhZELBVELh1IMiFQNS1UOTBQOjBMNSxONy1NNSxHMylMOC1XRERdSUlUPzJFMSU+Kw89 Kg9FMSVUPzJYRDlWQTdUPSlUPSlPOSdQOihQOzNRPDRKMiw9JiA5JxY8KhhFPDBRSDxcSj5VRDhR Oi5POCxFNyNBMyA8Khg5JxZBKhNBKhNJLxpMMRxJNRhDLxNGLR9ILyFNOCRWQCxYQTRXQDNNOzVI NzFDLhdELxhOOSdRPCpMOShHNCRELx46JhY8JxhDLR5FNCdHNylMOCtNOSxIOiZENSJBLRw8KBc+ KRZAKxg+KRpGMCFDKxdELBg/KiFHMShVOi9XPDFQNyZKMSE/Khs6JRY4JRhDLyI6JRNHMR5RPS9e STthSkVbRT9POiZHMh8/LRNBLxVOOi1YRDdYQDNROi1PMyZRNShWOjRUODJQPSxQPSxOOyZHNCBH LxlKMhxGMRxBLRg+KxFDLxVHMR5WPytcRjVXQTFGPi1KQzFMOCBFMRpEMBY6Jw47KRdRPitfSD5n T0VlTkRXQDdFMxk4Jw88Iw9AJxNIMBxNNCBHMBVBKxA9JQ5AKBBDLhtIMyBHMSVKNChHNS9KOTJR PzlOPDVUPzJTPjFRPS9OOixPNClGLCFELyhPOjJXR0hYSElRRUVMPz9ANydANydEMyVHNyhROy9a QzdaQzlXQDdPOy5JNSlNNypQOi1RQTJWRjdbRT1YQztRQDtOPThORkBTSkVYSDlNPS5JLxhPNB1K Nx1INBs7Mh87Mh9TQTlRQDhTPChPOSVMMx9IMBxALBk+Khc9Lhk/MBtPOSdUPStPQC5TRDFWQDlP OjJIOSpGNyhQPj5VQ0NUOzdONTFBMSNBMSNKNTFUPjpTQTlPPjVOPTdIODFDOTNHPThOQDNPQTRJ OzNNPjdRPDRNODBBMiE9Lh04KRs6Kx09LiFHOCpGOSpHOitJODJRPzpWRT5VRD1XQzVTPjFKNCND LRw/Khc9KBY+NzNKQz9QQDRBMic/Iw1EJxBJLRZVOCBROB5KMRhFLRlMMx9POCtUPC9QQDRMPDBI OjJFNy9HOitJPC1QRz5RSD9OPTRAMCg7KhVINyBWRTlaSDxXRj1VRDtUPzFPOy1IOyxOQDFPPThP PThKMy1AKiQ6JxFEMBlTST9aUEZcSj5UQzdMNydJNCVNNyNKNCE/KxxELyBNNB5MMx1POSdTPCpR OClNMyVIMyJKNSRUPzJbRjlYQztUPjdMOjFKOTBMMx1UOyRbQC1aPyxQOjBIMilDMCA9Kxs+KR5B LCFHMSBNNyVTPCpROylPOiZNOCRIMBxBKhY4KRU6KxZELBhTOiVQPSpINSNHMiFQOylYPCxaPS1Q OipFLyA6JRM3IhA8IxhMMSZBKBRJLxpWQDBfSTliTT5dSDpVPipOOCRJMBdDKhJNNC5YPzlcRThX QDNXPSxQNyZVOC5VOC5NPC5PPjBRQStNPSdMNx9NOCBOOCRPOSVHMhtDLhdGMSpUPjdbQzxYQDpU PTNVPjRTPy5KOCdNNRtJMhhKMhxVPCVeTEVjUElfTUdaR0FNPSlAMR4/JQ5AJg9IMRdTOyBJNRpD LxVBKBJEKhRFLB5MMiREMCNEMCNHMSZMNSpPODFPODFTQTVVRDhYQS1WPytROytFLyBFMSdPOzBX QD9YQUBWQ0VPPD5FNCdEMyZGNyVKOylOPzVURTtXRj9YR0BWRjhIOStENSJJOydRQDRaSDxhTD5c RzpWRz1MPTNQPzdcSkFeSTxYRDdUOilUOilQOydNOCRPPCtINSVRQTNNPS9QOipTPCxXPSxUOilN MyFILx1HMh1IMx5TPyxTPyxWSkFWSkFWQDlPOjJENSRHOSdUPzRXQzhPPzFIOStBMidAMSZNNTFY QDxUQzpQPzdOPTFMOy9GNS1KOjFKQzNKQzNWQTdXQzhRQDROPTFMNSJFLxxBLBk8JxU/LCBHMydH NylKOixOOC5ROzFRRDRPQTJOPjJPPzNMOCJJNSBMMx9HLxtJOTJOPTdNPS5DMyU+LAxHNBNPPylX RzBUPipOOSVMMiJKMSFOOixUPzFXQTpRPDRMOjNHNS9IOjJNPjdOREBOREBOPjBBMiU8MyBGPSlW Rz9WRz9VRj5OPzhQQDFPPzBUPTNVPjRVPTdTOzRMLh9GKRo1LRhIPylYUUdTTEFWPzNTPDBPOSdU PStUPC9QOSxMMR5NMh9OOx9PPCBRPTBRPTBJNSlGMiZNNytOOCxUPjlcRkBXPzlTOzRKNyxMOC1O OixaRTddRTldRTlYPDdMMCtALRg8KRU8JhpGLyNNNypUPTBaQzJaQzJWQzFRPi1HMBZBKxI4KRw1 JxpGNyhXRzhXQTFQOytXQC5aQzBdQzFVOypHMCRDLCA9JBI6IQ9FKyFQNStOMBhYOiFcRDdhSDtd TDxVRDRRPi1NOilKNSJGMR5JLCpOMC5POjRTPThXQDBQOipQOihROylNPDBPPjJUQzNUQzNQPSpQ PSpTPjFTPjFNPSdFNSBMNCtTOzFWRD1WRD1UPTFVPjJPQDdOPzVRPitTPyxRPitVQS5fUUReUENj TUdeSENJPjNFOi9DLhdBLRZTPihYRC1RPitMOSZIMBhHLxdFLyNIMiY/MCI+LyE/MCJDMyVFOjFH PDNWRTleTUBdTTVURC1RPilGMx9GMiZOOi1QPzlRQDpRPzlTQDpHOipENydIOiZPQCxYQDRfRzth SUhjTEpVSERKPjpIOSpKOyxQRjhXTT5dT0FXSTxUQzpMOzJTPDJhST9hTkdaR0BUQTlTQDhUQzNR QDFPPTdJODFOPTRQPzdQQDFVRTVdTThWRjFaQzBMNSRJMCBONCRORDNORDNPSkBNSD5PQy9IPClA MSNRQTJaRTpYRDlRRzlKQDJJNStINCpPOjRbRT9URjVRRDNOQDFMPi9GNShHNylKQTVORTlbST1a SDxRQzBNPixHNyhEMyVFKRVFKRVAKxhPOSVROylPOSdQOytOOSlMPCpQQC5WQTRXQzVPPSZPPSZM PChGNyNKNCtPOS9MOCtKNypINSFWQy1aT0BYTj9WPzJROy5ONypQOSxPPzFURDVURjdPQTJIOjJF Ny9INzFINzFQPTtTPz1OPipOPipTRDFYSTdTRzxPRDlUQzpTQTlYRDdXQzVaQzlXQDdWRDtUQTlA Lxk5KBNHPDFYTUFcUEdVSUBRQS9NPStTQzdVRTlWQDlRPDRQOipPOSlQPidQPidOPS9IOCpKMydM NChONy1POC5WQDxcRkFUPzRNOS5JNzROOzlOOztUQEBWRD1WRD1TPjFBLiJBLRhALBdBLx9KOCdU PjdbRT1cSj5YRztVQDNNOSw/LBQ4JQ4yJhc5LB1NRjpVTkFRQDRNPDBYQTVYQTVTPjFDLyM9KRY/ Kxg5Jg86Jw9DLyVOOi9PMyJXOylbRjleSTxaRDxOOTFHMydGMiZHMSVELiJAKh48JhpBKiZTOjVU QC9VQTBOPixMPCpKNypNOSxOPjBPPzFOPjBPPzFNRDpKQThNPStOPixPOyVUPylVQzpVQzpRPzdQ PjVPQDpPQDpQQTpOPzhIQC9ORjRcSUNkUUpdUExYTEdORjRHPy5FPCRBOSFPPzFXRzlVRTdRQTNU PStROylNOilGMyNDMCA+LBw/MB1JOiZRPTBUPzJeTUdhT0leVEVdU0RXSDNNPipEOixEOixRPDRV PzhVQDVRPTJFOCtGOSxKPTBWSDtfSD5jTEFdSkpdSkpTRkZGOjpGNS1QPzdbTEReT0dYUUdQST9K QThJQDdOPzlRQzxhTkdbSEFbRT9hSkVYTj9RRzlWQ0NKODhDMiVQPzFNSD5UT0VaTkVWSkFUQzM+ LiBELSFONypQRjhWTD1YRj9XRT5IPytAOCRHNS1VQzpYRkBWRD5RR0RJPzxIPTJFOi9JPjdcUEhc SkFRQDhIPzNIPzNIOSpGNyhGOzJQRTxcSkVYR0FQQThMPTNHNylJOStGLBZEKhREMBdQPCJXQTFU Pi5TPy5UQC9NPC5HNylKOTBPPTRUPzRVQDVOPTdFNC5HNzBKOjNNOzJNOzJQPzlfTkdfUEhbTERW RTVQPzBTPjNXQzhYRDdeSTxUSTtUSTtKOjNFNC5IMS1IMS1JPjVQRTxTQzRbSjxiTkxjT01UQzxM OzRPRDxUSEBXTERTRz9RPzlRPzlWRkNOPjtJMidHMCVXRUdeTE5YSUNURT5QQDJPPzFURjlURjlU RT5NPjhPOjRMNzFPOzBOOi9QOi1JMydOOCRROydPOSxPOSxTPDtQOjlJODJFMy5HNDRJNzdMNTdV Pj9aQ0FWPz5TOC1NMihPMSBQMiFNOStRPS9bRENbRENQQDJJOixFMSVDLyNAKRI7JA4/LyJNPC5V Rj9TRD1ROi5POCxXPzJTOy5JKxZBJA9AJxFBKBI+JQ9ILhdTPS1UPi5KOylTQzBVRDtVRDtTPjBE MCNBLCBBLCA+Khk/Kxo+KA89Jw9ALB1IMyRJQTJMRDROPTdIODFENCFGNyNJNCVIMyRHNylNPC5J PjNHPDFOOixRPS9KPCpNPixTQzRURDVQPzBMOyxJOzNMPTVQPzlRQDpHPjJHPjJWSkNcUEhYVElP SkBKQDBGPCxFPCZFPCZNQzRTSDpRRzVMQTBXQSlTPSVMOyxFNCZHMh1MNyFIOSNRQStVRDhUQzdb TEVcTUZaUEdYT0ZUSTtRRzlMPChNPSlJOTBPPjVOPi9KOyxEOipEOipMQDhUSD9bSEZWREFXRT9W RD5UPzJKNyo/MiRTRTVeU0paTkZUSEBQRT1OPzVOPzVGPDdJPzpXR0ZWRkVbSEFeTEVaTkZUSEBX SEFFNzA4Lh9HPS1USEBbT0deSkpYRUVUPDA8JhtFKhtVOSlaQ0FaQ0FXQzhQPDFIOStJOixIODFW RT5YRUNVQT9WRkdTQ0RKQDtIPjlGPj1VTUxcTD1PPzFNPjRKPDJHOCpFNShGOzBQRTpaSUhVRURM PjFIOy5JOSpJOSpIMBxHLxtINSVVQTBWRTlXRjpUQTtUQTtMQTNFOy1JPC1MPi9QQDRRQTVOPTRJ OTBEOipEOipQPjhTQDpURENaSUhbSkdVRUFQPzNOPTFVPTFYQDRbRjleSTxaSEFXRj9NPTFDMyhF NClJOS1JPjdMQDlVRD5hT0lpT09eRUVNOzROPDVVR0laTE5VSUFQRT1PPjVVRDtWRT5NPDVJNDBM NzJYQztXQTpRPzdTQDhRPDdRPDdVQDJVQDJTQDpKOTJNNTJMNDFKNylKNylNOCZNOCZPOSdOOCZP OCxPOCxTOy5ONypKNCtNNy1HNS9HNS9EMCNPOy1PPTdNOzRTOCxTOCxbPCphQS9YRDlRPTJTQTxT QTxNOidBLx1FKxZILhlGLxRKMxdTPDBXQDRWPjpTOzdONCRPNSVPOCtKMydILRNFKhBBKRdGLRtF MCFOOSlXQzVdSDtINChTPjFXQS9YQzBRPSdEMBs5JhA6JxFBLBlDLRpELBhDKxdBLCBIMiZIPDhQ RD9TPThIMy4+LxxFNSJJMyJIMiFDLSFELiJJNStMOC1NOChPOipKPChJOydRQDhRQDhNOzJGNCxB MSZFNClRPi1RPi1NOS5KNyxQRT1YTUVbUEpPRT9GPSlDOiZGNSdHNyhNOzVTQDtTQDhTQDhWQy9R PitMPDBKOy9MOCtUPzJRPzdXRTxTQ0RRQUNVSEZaTUpaTkVWSkFVRjxWRz1QPzBJOSpEOipHPS1H PjRDOjBGNyhDMyVPPz5aSUhaRkhTP0FRPjxRPjxPOSVIMh9EMi1XRT9XTlFWTVBQQTtOPzlHPDFJ PjNGOzBFOi9TRkRRRUNaSkRXSEFWSkFXTENUSkFFPDM1MyZAPjBaRk1hTVRiSUZaQT5OOC4+KSBB LiRYRDlbRENaQ0FUQTtOPDVQOSxTOy5JPjVTRz5VQT9RPjxPPT9QPkBHPzxGPjtKQD9VSklbSEFR PzlTQTNRQDJGPTFBOS1KPjpVSERcSkRVRD1PPzFPPzFOPS9JOStGMB1IMh9NOSxbRjlXTENVSUBU RENWRkVOPjtIOTVFOTdJPTtTQTlUQzpQPzNMOy9DOC1ANStNPT5UREVTRkZVSEhYRkRUQT9QPzBO PS5UOzdWPTlWQDxdR0NbSEZWREFOPDdHNTBGNS1JOTBJODJQPjlaRk1hTVRiSklTPDtKOTdOPDpW SEpYSk1VRj9TRD1WQDlaRDxTQTNOPS9MOy9OPTFVPixUPStUOS1TOCxVOjFaPjVWPy1VPixNPC5H NylFMiJGMyNJOixKOy1OPy1QQS9QQC5OPixNNSxPOC5WPShVPCdTPC9ROy5FNCdEMyY/LB9JNShN PDBOPTFVPTFWPjJjQDdjQDdVPTNPOC5RQDFPPi9ONR1NNBxPNyBYPyhRPCxVPy9UPTBQOi1QNSpQ NSpaPS1aPS1TPCxMNSZPNyBPNyBNOidOOyhNPC5PPjBaQzlfSD5GMSBPOihaQCtcQy1YPyhTOiM+ LRQ5KA9FLRlMMx9JNB9IMx5EKx1KMSNPOjJaRDxTPjNFMSdEMR9INSNNNSpIMSY9LCQ/LiZIMSVN NSlKOCVKOCVFOSZIPClOPzVNPjRJOyM/MRo7KBI4JQ9ALB1FMCFJMyJHMSBFPztTTUhYTEdPQz5K PitANCJELx5GMSBPOy5VQDNVPy9YQzJVQDVOOi9NNy1NNy1ROjVWPjpTQD5UQT9PQ0NPQ0NRQDtU Qz1XRjpaSDxWRjhVRTdTQzdKOy9FNyVFNyVEOzI/Ny5BMyJAMiFKOzpaSUhaQT1ONzJJOi5KOy9J NyZHNCRELyhUPjdbTU9bTU9NRDtHPjVANS1EOTA/Ny5EOzJXPj5VPDxUQTtTQDpVRD5bSUReTElR Pz1BNy5EOTBYQUBiSklcRkFUPjpMMCNGKx5JOS1WRTlQRD9OQT1OPTFNPDBNNS9QOTJUPjlWQDtT PTVRPDRMNTdPOTpHPjRKQThNQDxUR0NaRkZaRkZaST1YSDxERjo7PTFJPTlPQz5eRz1YQThWQTNV QDJRPS9QPC5KNSJOOSVPQTRYSj1VUEZTTkRWRkdWRkdOPThGNTBIODJTQTxYSDxTQzdOOypJNyZE Myg9LSJGNzNQQD1XRTxWRDtbRTJYQzBXQzVTPjFNNytNNytTPDtcRURcSUxXRUdNOzJHNS1JNShJ NShMMzNcQ0NiUFZfTlRRPDhHMi5JPTlUR0NYRkZWRERYRDlaRTpdRz9fSUFXQDdQOjBROy5TPC9W PjFVPTBdQzNbQDFiQDFhPzBbQDFYPi9VQDNNOSw+LRY9LBVROydWPytYRDVWQTNUQzNPPi9ROi5Y QDRdRTlbQzdUPTNOOC5FNSRBMiFAKBpGLR9QNStTOC1WPjRaQThlRUddPT9MNzJNODNROy9POS1R PCxdRzdfTj5hTz9dRjlVPjFRPi1TPy5UQC9WQzFiSERdRD9bQzVTOy5UPixWQC5bRTRaRDNRQTVX RztdR0FWQDtDLhZPOiBbQC9eRDJcQCxYPSlJNCM/KxpBLRhPOiRNPC1GNSdGMx9GMx9VPjFcRThR QDFFNCZHNR9OPCVPOCtIMSVBLR5DLh9EMCNKNylOOyhRPitMRDRGPi9KQz1MRD5NPiZDNB0+Jg09 JQw5KBM9LBZELx5DLh1HPDRRRj5XSEBVRj5QPzNFNClDLhtIMyBOOypUQC9TPyxRPitTPDBKNClG MihKNyxOPDpRPz1VQT9QPTtMPDBOPjJHPDRFOjJPPTtPPTtaRTpbRjtURDFQQC5GOSpFOClBMyxA Mis8MiM7MSJQPj5aR0dWQTRMOCtIOSdKOylJOi5HOCw+MSNQQzNRTEdTTUhWRTxPPjVGOC5AMilB OS9EOzFROjNQOTJPPTRPPTRYQDpaQTtRR0RKQD1BOzI+OC9OPzlXSEFRRDdKPTBJNyRINSNTQDhY Rj1TRz9NQTpHPDNGOzJIMy9MNzJPPTdVQzxUSThNQzFGODBFNy9JOTBKOjFNPDdVRD5aRkZhTU1X TUdWTEZGQz0/PDdHPDRNQTpWRjpURDhXRjpWRTlPQTJHOitKNypWQTRbTERbTERWUE5RTElTRkFT RkFKQTlAOC9KOjRXRkBdTD9WRTlMPC1JOitGLyNDLCBJNyZTPy5URDhXRztiTT5fSjxeRjlXPzJI OCpKOixNPDdUQz1cTEhVRUFKOTBEMipJNSlKNypWOTpiREVlUVFdSUlHNTBEMi1NPT5YSElcSkFW RTxbST1eTUBjTUdeSENYQThQOjBUPi5XQTFbRT1dRz9jSj5jSj5dTD9XRjpdRD9dRD9YRDlQPDE9 Kxs6KBhPOihYQzBYRzhVRDRURTJRQzBTPjNeST5iT0ZdSkFOQDNDNSlGMSBALBs8JBY8JBZELSJN NSpXQTpeSEBhSERVPTlFNCdGNShKPDVPQDpXRj1fTkVlU01eTEZUPzRMOC1WQTNdSDpXTT5YTj9j UEldSkRXQDdWPzVOPi9VRTVhST9YQThHMydVQDNYQTVQOi5BKhNONR1cRi9eSDFYRTFTPyxKNyFH Mx5FMBlPOiJMPi9JPC1GOSpFOClQQDRYSDxURTBGOCRHNCBNOiVNOidINSM+LBhDMBxELx5HMiFQ OihYQS9URDRPPzBQRz5WTURNRC9BOSVAJxE/JhA8Jg4+KA8/Kxg9KRY+NC9MQTxWRz9VRj5QPC5H MyZALhpGMx9JOStPPjBOPS5MOyxQOi5KNClDLh1NOCZMOy9OPTFUQzdPPjJJOStJOStIMjFIMjFJ ODFOPDVVQzpXRTxYSDVURDFHPS9EOixJNSlHMydFNClFNClMPztQRD9VQDJOOixKOitPPi9OPixN PStENyhNPzBHRztOTkFaSkNTRDxKOjFAMChKNypOOi1JOS1NPDBPPzNNPTFTQTVVRDhOQDNNPzJH PjRBOS9HPjVRSD9NQzFJPy5MOyxQPzBTQTtaSEFVRD1OPTdMQTFHPS1GMCVMNSpEPDlQSEVbUUdP RjxGOSxBNChDNzJGOjVHPDRRRj5cUEdhVUxaTUpUR0VNPDNIOC9FPjVHQDhVSUFUSEBURT1TRDxV PzpQOzVGPjtQSEVVTkVXUEdaT05YTk1bRjtVQDVPPjVBMSlPPTdbSEFkTEdaQT1RPzdQPjVJMiZH MCROOi1WQTRXRkBbSURhUUpeT0haSkNRQztNOStOOixQPzlXRj9cT01RRUNDNC5FNzBDOC9FOjFQ PkBVQ0VaTUpNQD5FNC5JOTJMQTxQRkBXSTxWSDtWTUNdVElfTE5XREZJPjdIPTVUQzdaSDxbSj5c TD9dSkFeTENXRzlTQzRiREdoSU1aRkRKODU6Kx04KRtJPC9TRThcSjxYRzlTQzNURDRcRkBeSENf SURfSURRRDRGOSpONCZGLR87JxM5JRFBLCNMNSxXRj1bSUBbRjtQPDE9NCE5MB09OipHRDNXRUNe TElfR0RROjdJOS1JOS1WREFeTElfTUpeTEleTENaRz5RPTJRPTJNQTpWSkNYRj1PPTRHMSVNNypJ OStIOCpAKRJONR1eQTFoSjpeSDVXQS9VPylRPCZKNx9RPSVQQDROPjJGOSpGOSpKPTBVRzpVRi9I OiRFNx9GOCBMNyFGMRxBLBtAKxpELxxHMh9UOTBdQTlPRjpORTlORTlWTUBWTDtNQzJQMiNHKhs+ KxVALRZGLR1GLR0/LyRMOy9TQzdXRztVQSxKOCNMOShNOilVQDVVQDVbPzdWOzJQPC9POy5IMh9D LRpGMiVPOy1OQDFIOyxGNS0+LiY8MiU8MiVHOSNOPylVRDhcSj5jSkBiST9PQDdJOzFKOixFNCdI MilMNSxIPDpOQT9UQC9OOypKOy1PPzFQOi5POS1IOClOPS5NSDtNSDtXRUVVQ0NKPzRBNyxHNytN PDBKOixKOixRQDJRQDJXRzhYSDlVQDNVQDNNQC1GOidIOS1TQzdXRjdVRDRUQTlUQTlUQzxcSkRU RjlRRDdPRTRNQzJJOStMOy1JRD9TTUhWUElMRj9OOixKNyk3MSM4MiRHPThTSENcU0hcU0hcUEdT Rz5JPjNBNyw/Ny1GPTNMREBTSkdWTUBQRztUQzpNPDNJPENWSE9aUEZVTEFaTU1eUVFdSkFMOjFE NClDMyhPQDpeT0hqUFBcQ0NVPztbRUBQPzBGNSdJOTNPPjlPRUFYTkphUUpeT0hUTEZIQDtKOTBM OjFUQUFXRUVPSD9KRDtDMStBMCpEMytIOC9JODpKOTtTPz9QPT1OOi1KNypMOzJVRDteTUBeTUBb UUdeVUpYQUBQOjlKOjRMOzVVPzhdRz9cTkBeUENkTkhcRkBUQC1WQy9iUFRfTlFUQEVHNDk9LSJD MidJPzFRRzlWTD1USTtJQDhNRDtVSEhXSkpdRkVbRENRQDpKOjNPPCdNOiVIMxw6JhBBLCNTPDJc RzpbRjlWRTdJOStBMyBAMh9IOy5WSDtfTE5dSUxVQTBHNCQ/MiZPQTRaQ0RdRkddREZTOjxROTJO NS9KNypTPjFWRkVURENXQDNTPC9MMRxILhlHMydMOCtFLhVONxxcRDdkTD5bRjtVQDVTPjBRPS9T PjNTPjNWPzVUPTNMOCpFMSREMipMOjFXQTFPOipMNyNMNyNKNCVDLR5AKxhDLRpFLxxHMR5ROTRb QT1URT5MPTdIOCxUQzdUTUNPSD5PPCdKOCNKNxtMOBxONCRNMyNJMilONy1QPzlXRj9PPzBNPS5N PDBQPzNTQzdRQTVXPzNTOy9RPCxPOipJMidBKyBBMiFKOylOPS5MOyxIOClAMCI7Mh84LxxAMiFK PCpXQTxeSENkTEdkTEdPQDlPQDlPPThMOjRJMi9IMS5FOTROQT1RQDFRQDFKQC9JPy5RPS9OOixI PTVHPDRIPzNPRjpMRj9IQzxKPDVGODFHOS9KPDJKOixQPzFUQzNVRDRWSDtaTD5YRj1QPjVMPC5I OStTOy5YQDNYSDpYSDpWRz9VRj5WRT5eTUZaSUZUREBNRDtJQDhKOjNKOjNORUZRSElTTENMRTxM PypEOCM+NSI8MyA+NC9PRT9aTU1fU1NYTExNQEBKPDJFNy1ANStBNyxNOzlXRUNYTUFUSD1TRThO QDNVQEVaRUlYTElWSUdXTE1dUVNdTUxMPDtDMiVFNCdKRUBWUExiUEdVRDtUQz1WRT9NPixFNyVE MyZJOStPQURdT1FjU1RiUVNVSEZJPTtHOTFMPTVUQURVQ0VRRUVJPT1FLyNGMCRHNS1JOC9FNC5G NS9OPTRKOjFGMiVGMiVINzBUQTtfT0xiUU5jU1FcTEpPPTRGNCxEMyZJOStYQz1hSkVhUUpeT0he Rj9WPjhOPThYR0FfU05XSkZQPjVHNS1ILShJLilOPjJXRztWRjhRQTNKOyxOPi9WSUVWSUVYRUNV QT9QPzNOPTFNPSlMPChEMR09KxdBMiRMPC1VRTlQQDROPTRHNy5EMyhEMyhMPz9YTExiTkxUQD5I NyBBMBpGNytOPjJXQD9XQD9TOC9NMipIMSZHMCVFMClMNy9RPzlTQDpTPStJNCM/KRBBKxJJNSlT PjFFMBlTPSVYSDVfTzxRQDhIOC9IOCpMOy1NPDNPPjVTQDpTQDpJOihDMyJBLiJGMiZQNSpQNSpR NyNQNSJKNylFMSQ8KRU9KhY/Kh9FLyRMOTdYRUNVST5JPjNDLidQOzNOR0dOR0dQQzVPQTRQOylT PStVPytUPipOOixRPS9RRDdPQTRPQTFQQzJTPjNVQDVWRTdRQDJUPSlTPChQPC9NOSxKOitGNSdB MiFHOCZKOydJOiZPOSxKNChDMB5BLx0/MCNHOCpWRD1fTUZjUUVhT0NPPjVPPjVPPjlJOTNFMiBE MR9FNSdQQDFURDVVRTdTRThQQzVUQzNKOitIPTJGOzBMPTVQQTpIRDpHQzlJOzFFNy1KOixIOCpP Oy5XQzVXSEBaSkNaTkZYTUVVRzhNPzBMPC1MPC1VRDRdTDxeTURcSkFVRj9XSEFWRkNcTEhdSkhX RUNOQT1MPztOPzlMPTdRQUBURENRQUNUREVPQTRNPzJMOSRHNCA/MiRURjddUUlhVU1RSUZIQD1K PChJOydDNyA+MhxJOTNYR0FdUUlYTUVUSTtQRjhYR0FbSURWTEhUSUZVTlBYUVRXTE1KP0BENCdF NShJQzpUTURcSkVVRD5VSUBVSUBOPixGNyU+MB1FNyNVRUZlVVZlVVZYSElUOztQODhJODJMOjRO PDpRPz1UPTxNNzVINCdNOStOPy1PQC5HNR9FMx1FNyVDNCNENCE/MB1GNzVTQ0FbTk5hVFRfTExW Q0NIOjBBMyo/MShKPDJdSU5jT1RnT1NcRUhROjVROjVRR0RaT0xiTEZWQDtNOilINSVILiNILiNT PTVbRT1VQDVQPDFRPTBUPzJXR0ZRQUBQOzNTPTVKOitJOSpKNS5IMyxDLh1ELx5AMydHOi1QPzBR QDFRPz1FMzFGMiZINChPQDpYSUNYRDlINCpFMBtDLhlJOitMPC1WQDxUPjpNOS5KNyxFNSJDMyBE MyVFNCZIOStMPC5OPTRJOTBALBlIMyBeRjxpUEZJMyBbRC9bT0ZaTkVQRDA9MR8zJhI8LhlJOStQ PzFUQTlUQTlNPS9FNSg/LCA9Kh5GKxxVOSlUPylVQCpVPipOOCRFMx1BMBpFNSdFNSdIPTRUSD9U SkBORTtHMCpELSdKPzhPRDxTQTlVRDtVQDNYRDdaQzVXQDNJQDdGPTNKPzRMQDVOQDFPQTJWPzJY QTRaQzJVPi5TPCxUPS1QPSxNOilJPC1FOClFMB1HMh9HNyhNPC1QOi1POSxPNSVONCRHNCJKOCVX Rj1hT0ZeT0dbTERTQzdURDhPP0BNPT5EMyY+LiFENyhPQTJWRz9bTERYTUVPRDxOQDFDNSdGOC5G OC5OPThQPzpPRDxOQztKOjRGNTBJNStNOS5XPjhfRj9aT0lYTkhVSEhVSEhORDNMQTFOPS9UQzRY SEdiUVBdTUlXR0RKQDtNQz1XTUdfVU9fTUpWREFQPzlRQDpOPzVKPDJRPDdUPjlTQD5VQ0BYPz9V PDxUPS1ROytMOzRbSUNaTU1YTExPRjpMQzdRPCxOOSlAMyQ+MSJJRD9YU05hUE9YSEdVSUBUSD9c SkVfTkhYTkpVSkdYTU5XTE1bTk5NQEBFOCtHOi1OQztUSEBORTlPRjpWTUBVTD9PPzFGNylAMSNK OyxeSkpoVFRfT1BTQ0RRNDVPMjNHNDJJNzRNPDdTQTxNODBIMyxFNClPPjJaSDpVRDVNOR9BLhZA LB1ELyBHMCdELSREOjlPRURYTk1dU1FaR0lRP0FJOTJBMStHODlXR0hjTlNkT1RfQ0BUODVQOTJW PjhTSkdWTkpYQztNODBPODNKMy9MNSlNNypXPDFaPjNVPTBTOy5UPjdTPTVMOzJHNy5HMSJJMyRH Mh9HMh9ALSFDLyM8LRpDMyBHOSFHOSFTOy5XPzJPPi9GNSdFMSVMOCtQQDRPPzNJOyc+MB1MMiBR OCVRPS9RPS9RPzpNOzVKMixJMStJMiZHMCRINSVKOCdJOi5IOS1OOi1POy5POThdRkVoUE9qU1FM NyVcRjNdSkVeTEZURDFENCM6KBU4JhM8KhhEMR9TOzRbQzxJPy5ANyZDMRs4JxI7JhdKNCVWRjda STpaRC9XQS1QPSFKOBxMOyxKOitNPjdRQztUR0NTRkFMNC5BKyVINy5KOTBQQDJURDVaQzdeRzta SDpVRDVGPzU/OS9JOzFMPTNNPTFPPzNVPjJaQzdYRTNUQC9XQC5QOihMNyVNOCZIOSpFNSdIMyBN OCRVPjFYQTRWQTNTPjBMPChKOydOOi1XQzVdUUZdUUZWUEBPSTpQQzJTRTRPQDpNPjhENyg9MCJD MStTQDpVT0pWUExUUE1HREBGPi0+NyZDMyhHOCxKPDVPQDpTPz1PPDpKOjFFNCxBMSlJOTBUQzxd TEVXVExPTERPRURUSUhPPTRINy5FNSpVRTlaTk9eU1RUR0NNQDxBOjRHPzpXTUddU01cSkVVRD5U PDlTOzhJPjVJPjVQPjhPPTdOPzlQQTtUQT9RPz1OPDdOPDdNQEBYTExYSElXR0hPPjJOPTFQPzFM Oy0/NCo8MSdDPj9WUVNfUVRXSUxQR0hRSElYTU5eU1RVT0hOSEFPRT9QRkBWSkNMQDlFNC5GNS9M PDtMPDtKPzhMQDlVRTlVRTlRQDRHNytEMyVQPzBdSUliTk5YSEdNPTxOOC5IMilFNSpFNSpIOS1N PTFKOjFHNy4/OzFJRTtRRj5TRz9QOydJNCFMNSRQOihQOCNMMx9JODhVQ0NbSEpcSUxXPzxQOTVH OTJDNC5FOTdTRkRfSExfSExfQz9cPzxdRT5eRj9WSUdUR0VNOSxJNSlRNyxaPjNbRDNaQzJcRTRa QzJWOS5VOC1VPTNUPDJMOShJNyZJNyZMOShMOSRINSFHMCdHMCdGMCVMNSpTPS1UPi5YPi9WPC1R Oy5MNSlIMyBMNyNNPC1KOitHOCJIOSNRPitUQC1eRDRbQDFOPTFJOS1HMitGMSpIMStHMCpKNypO Oi1OPjJNPTFTPjFTPjFXPkNiSE1kT1RlUFVNOStdSDpVSUFVSUFUQC9KOCdELRQ+KA85KxU6LBZP OjRYQz1PPytJOiZENBI4KQk5KBNDMRtVPzpfSURbRjlaRThTQy5PPytRPi1QPSxPPi9RQDFUPjla RD5QPzFHNylFMSQ/LB9HOCxKOy9XQDdeRz1aUEZRSD5JPy5HPSxNNytMNSpIOCpPPjBRQDJXRjha Rz5WRDtcRjNUPixMNyNOOSVHOCZIOSdMPSlPQCxXRj1cSkFXTT5USTtORjdIQDFRQztURT1WSkNe U0pWTjxPRzVEPTNJQzlKPzhJPjc9OCk1MCJDMTFYRkZbVVBWUExQTEFIRDpIPClDNyRHMiNPOipQ PjVTQDhOPDpMOjhFOytDOSlFNCxHNy5QREFYTElTTUZMRj9HQEBNRkZHPDNEOTBENDFWRkNhVFRe UVFQQD1KOzhHOTFOPzhRUEhVVExVSEhOQUFOODtPOTxIPTRKPzdNPjdNPjdNPjhNPjhNQDxJPTlO PDpPPTtOPDpWREFaSEFUQzxQPzFUQzRURTJNPixENypAMydGPUNWTVNeUU9XSkhMRUdNRkhVSUpe U1RWT0VMRTtMPTNOPzVHQDdAOjA/LydBMSlHNTVGNDRINy5KOTBPQDdQQThTPjBOOixMNzFUPjlU RENVRURRQDtJOTNHMydHMydHOCZFNSRIOS1OPjJNPzJJPC9KPjpOQT1WREFVQ0BURDhTQzdXRjda SDlaRjJWQy9NPTpRQT5URENOPj1OPDNJOC89NSdAOSpGPjlNRT9VRUFaSUZoT0xnTkpkU0xfTkdf TUZYRj9MOyxPPi9bSUNhT0heVUheVUhiUERdTD9dRTheRjlUPDBTOy9IOClNPC1cPDJnRjxeQTNX Oy1KNyxEMCZEMy1IODFQPjVXRTxaRz5UQTlRPDdMNzFHNCJFMiBKOitMOyxRPS9QPC5URTtbTEFh Tz9bSTpTQTVIOCxEMyZDMiVJNSlINChNPDBQPzNUPjdTPTVXQTFaRDNTQENYRkhjT01kUE5FNSRP Py1URDhWRjpPPytNPSlFMx1FMx08MRk+MxtMNy9QOzNQPSpPPClEORU5Lgw7JhRHMR5XQDdfSD5W Sj9USD1URjlRRDdPQTFNPy9KPS5JPC1TPThVPzpKQThHPjRGNyhBMiRBLx1EMR9OOi9XQzhaTUpa TUpRRTFOQS5KOCVBLx1FMRxNOSNQQDRWRjpdTENcSkFeSjlYRTNTOSZGLRtBLx1INSNOQDNQQzVc SUlaR0dUREBPPzxIQDtJQTxJPzxUSUZbT0ReU0dMSjdGRTE+OCxEPTFHPDRIPTVFOi8+Myk6NDBO SERbTkxXSkhTQTtMOzRHOis/MiRAMiFOPy1RPzlMOjNJODJINzFBOis9NSdHNytKOi5UR0NbTkla RD9TPTlNPTxNPTxFNCxEMytBODdVSkldUVVbT1NNPTxFNTRAPDJIRDpRUE1UU09PQ0BGOjhGMzFK ODVOPTRQPzdKPDVHOTJFOjJIPTVMOzVOPThHPThIPjlOPThWRT9YRztTQTVUQTlaRz5VSUBUSD9O PDpDMS9JQERXTlFcUEhTRz9MQDlNQTpQRERbTk5UT0RJRTpMOy9KOi5BMiVBMiVALydBMChEMyhD MidENyhJPC1MPi9OQDFQQDFOPi9OPDdQPjlQPzlRQDpMPDBJOi5DMiVGNShJOixIOStJPjdNQTpQ QTtOPzlMPTdRQzxXRUVXRUVWSkNXTERcTEhdTUldSkVcSURbQz5aQT1RQDhPPjVNPixGOCY8MiI/ NSVKPDVTRD1QRkBYTkhnVldnVldkVU5eT0hfT0NcTD9XSD5fUEZoVFhlUVZrWFNtWlRfU1BXSkhc Sj5aSDxVPy9RPCxKNylUPzFXRT5jUElaRD5MNzFPNzBNNC5JNC9RPDdYR0BhT0hYTElPQ0BNOzRI NzBBMidGNytGNylJOixVRDtdTENiUVBhUE9XSTpURjdVPjRPOS9KNylFMSRMOShNOilPPTdTQDpU PDVUPDVXQDRUPTFKOzxTQ0RhT0llVE5ELx5GMSBJMyhNNytNOSxPOy5JNShGMiVEMR1FMh5JMiZO NypROi1TOy5JOCFFMx1BMSNGNSdQPzdbSUBWSkFWSkFVRj5TRDxMRDRHPzBFNy1FNy1GOzNKPzhM QzlMQzlHPjREOzFDNSc/MiQ/NSZPRTRdSkVeTEZURDVNPS9IOClBMSM/KxNBLRVJOi5VRTldTkRe T0VfUURbTT9POiJJNB1OMx5KMBtNPC5XRjhfTkVUQzpMOy1NPC5HPjVIPzdOQUFXSkpbTkxcT01T ST9MQzlBMyBENSJFOCtENypFNSpFNSpGNzNVRUFTRkZVSEhROzpMNTRINTNFMjBEMytOPTRMOjhF MzFGMS1GMS1AMilBMypMNSpROy9VRj9aSkRUPDVPODFMOy1MOy1ENyhBNCZFPztXUU1XUU9QSkhJ OC9EMipAOTNORkBYTkpYTkpKQTU+NSpIMStNNS9MOzRNPDVNPS9KOy1OPS9QPzFTPTlRPDhJPTlH OzdNPDVWRT5XRTxYRj1aSD9cSkFXTUlPRUFFODw9MDRIPT5USElWSUlOQUFJPTlHOzdMPDtVRURc TUNXSD5UPipKNSJHMSZIMidIMSVJMiZEMSFEMSFGNCxOPDNRQzlWRz1VRTlURDhTPjNQPDFPPzNQ QDRQQDJOPjBMNy9POjJUPjlUPjlTRUdURkhQQD1NPTpKOzhOPjtUREVWRkdXSkZVSERVSEZXSkhY TEdbTklfSDxdRjpXRztRQTVNPy9HOipGNyhDMyVMOzJYRz5XTkReVUpkWlZiV1ReUFNXSUxcSkFe TURjU1FjU1FqU1ZuVlpvVlNtVFBbTklXSkZfTURbSD9RPTBPOy5RPDRcRj5YTURbT0ZVPztMNzJR OzFMNSxJNzRRPjxVQ0NaR0dVRj5PQDlIOy5BNChBLSlJNDBINzFUQTxdSU5hTVFjTUhbRUBRPi1T Py5aQThROjBOOChKNCVMNSlQOi1QPjVRPzdRPi1QPSxROzFQOjBKOjNUQzxhVFRfU1NBKhVBKhVA LBY/KxVJMB5PNSNHMxpHMxpGMR5HMh9GMCRMNSlTNStYOzBPPCtNOilPOy5TPjFUQTlfTUReTUdc SkVYR0FWRT9ORkVGPj1DOC89MipANDBHOzdQPzlTQTtOPzlNPjhDPDA+OCxAMydMPjFXRT9cSURW RTdQPzFJPSpEOCU+LRQ9LBNANCJRRTFiUU5kVFBhVE9bTklWRjFNPSlNOCBHMhtKOjNWRT5bT0dP RDxGPi1BOilFOytEOipOPTdWRT5UR0VcT01WSkNPRDxMNydIMyREMSFEMSFDMyBGNyNNPDBTQTVQ RD9UR0NJPC9ENypFNC9FNC9KNTFMNzJJNC9IMy5FMSdFMSdAMCJEMyVKNCtNNy1URDhYSDxUQC1R PitMPyxMPyxAPjNAPjNKRkVWUVBVUUxKR0FGMSpHMitEOjdOREBaSEFXRj9OPzVGOC5GMihFMSdA OC5IPzVPQC5RQzBVRDVTQTNPPT1JODhGPTRGPTRJOjtPP0BYRj1eTENhT0lfTkhXTUlMQT5ALy06 KSdDOTVOREBRRUNMPz1GQDpAOzRIPT5PREVbSUBWRTxYRDVQPC5OOixOOixQOS1ONytHNyhKOitK ODVOOzlXRT5fTUZaSD9RQDhOOTNNODJNPTFQQDRTPTVRPDRUPTxROzpPPDxRPj5YRUdYRUdTQz9O PjtJPTtKPjxOQT1PQz5USD1QRTpQQD1RQT5UR0dYTExWSkFYTURYSEdPPz5PPz5KOzpBNy5ANS1P PzxfT0xeWEhfWklkVlhfUVRWSUVNQDxRQT5WRkNjUEpoVU9qVlZuWlpkUUxaR0FURDhdTUBjTUdc RkBUPDVVPTdaRD9dR0NbSEFaR0BaQzVYQTRRQDJNPC5HOCxRQTVVQ0NXRUVTQDpMOjNMNSxFLyZD LCFJMidNOz1eTE5jT1RfTFBYRDlTPjNRQDFWRTVcRD9ROjVRNytUOS1aQzJbRDNUQzRUQzRRPCxR PCxROy5OOCtKOTdYRkReUFNbTU9EKRhAJhY+JQ88Iw4/Jw9FLBRELxpFMBtHMhtHMhtGMyFGMyFP NCpXPDFNPTFMPDBUPzFXQzRXSD5eT0VfUEhcTUVVSUFUSEBOSERKRUBBODI6MCs/NC1BNy9HNy5K OjFHPDFNQTdKQThIPzVMOzJIOC9PPkFWRUhaQTtaQTtTPyxMOSZQNyRILx1BMSRRQDJfTExqVlZi VVVbTk5aST1QQDROOSlOOSlOPzlRQzxXTENTRz5MQTNFOy08Mys5MChDMzBQQD1RR0FWTEZUSEBT Rz9QPzlJOTJBMBpBMBpAMh9FNyNOPTFOPTFPPTtQPjxGOzJBNy4+NS09NCxANStGOzBQOjBPOS9I MyRDLh8+LSU/LiZDLyVFMSdRRDdVRzpWRjNVRTJMQzlIPzVFOzVIPjlNQUNVSUpYU05VT0pEMyhE MyhDOTNHPThUQzxTQTtNPy9FOCg/NSg8MiU6NStFQDVOSDlXUUFaUEdUSkFJQzc9NytBOitDOyw/ NTJDOTVQRz5bUUheU1RXTE1RQDtGNTA/KR4+KB0/OCdMRDJPQDpOPzlFPzlAOzRHPDRNQTpUR0dV SEhVRURWRkVRQTVTQzdTQzNOPi9MPSdGOCJPOjVWQDxbR0dlUVFaTU1MPz9IOCpJOStNPzBQQzNX QDdaQzlXQTpTPTVNOzlRPz1TQDtVQz1PPjJNPDBNPDVPPjhNQDxNQDxQPzlRQDpTQDhVQzpVR0xX SU5TT0xYVVFYUE9NRURUQUFPPT1KOy1IOStWRERnVFRjVUdcTkBUR0NOQT1DOi4/NytJOT5VREln VFRoVVVnU1NfTExRQTNJOixUSD1cUEVkTEdcRD9XPzVfRz1eRkFcRD9dRT5bQzxdRz9fSUFTRTVM Pi9KPTBWSDtYRUVXRERTOzdONzJQNSpOMyhIMSVPOCtUQTxdSkVYRz5RQDhPQTFURjVXRzhVRTVV PjJNNytXOytdQDBiRDlhQzhaQzBXQC5WPShWPShRPTBQPC9OPTdaSEFdTkdYSUNKNCNELh1DKxdA KRY/KxZBLRhFMB9HMiFINSFHNCBJNB9MNyFQOSxUPC9PQDlOPzhPPjhRQDpXTERYTUVXUEdWT0ZU TUNQST9WSj9RRjtGOSw1KR08KRxALSBFOClGOSpOPTRTQTlRQDpOPTdRPzdNOzJOOzlTPz1XQzhX QzhYQTVTPDBRPSdMOCJFMSVPOy5dUE5nWldiVlpeU1ZbSENWRD5ROy9UPTFPPzxTQz9TTENTTENV Qz1QPjlDODA5LidDLhtMNyNOPTdTQTtQRz5PRj1NQDxHOzdJMydNNypOOSlQOytQPzdNPDNPPTdO PDVJOS1GNSpDMipEMytAMSNKOyxVPy1YQzBUPS1JMyRBKx9BKx9ALSBHMyZORztRSj5YT0NYT0NR RUBHOzdKOTtKOTtJOT5VRElcSUlaR0dKPDJENSxENSxKPDJMPTdPQDpHPzxDOzhDOyxAOSpBNTNT RkRaTUpdUE5cU0lXTkVNRDpGPTNGOSxENyo7MTBBODdMR0hYVFVcU1RRSElJPC8+MSVAKBZEKxlF OCtRRDdPPjlPPjlGOjhHOzlMOzVHNzFJPj9PREVUQEdXREpORkBORkBUSEBWSkNQPzBJOSpUPTxh SUhjT09lUVFUQz1HNzFKPDJQQThYRzthT0NeTENcSUBWRT5MOzRFPDNKQTlPRDlRRjtQQzNNPzBQ PjlPPThNOzlPPTtPPTtPPTtQPjlUQTxRREZVR0lRSk1WT1FYTVBTR0pVRD5TQTxPOS9OOC5YSk1j VVdYTEdPQz5NOzRNOzRHOi1HOi1QPT1hTU1jVVpkVltcSUxRP0FPPjhQPzlUSkBbUUdhTEBdSD1k SD9pTURhSkVeSENdSUdYRUNeSU5lUFVXRj9OPTdTRD1dTkdeTElaR0VWPjRROjBNNSxKMypJNC9Q OzVQPjxRPz1QPzNVRDhdTDxfTj5VSjpKQDBNNyVPOSdXQDRcRTlbRDhcRTlVRTJWRjNcRjVbRTRU RDRTQzNRPTJXQzhcSURbSENTOy9KMyhHMx5GMh1HLxlHLxlGMSJKNSZOOSdRPCpUOilUOilUPS1Y QTFTPDJQOjBOPzVOPzVYR0FaSENbTkxaTUpXTkRWTUNeTENaRz5KPCg6LBk/KxZGMRxFOCtFOCtQ PC5TPjBRQDhPPjVOPDVMOjNKOTJOPDVQQS9URTJTRDFOPy1DRS1DRS1NPDVQPzlbTU9kVlhjVVdd T1FaSUhXR0ZXQDRUPTFOPTdRQDpORz5UTURUSD9OQzpHQDRDPDBDNyBDNyBMOyxKOitMPTNPQDdO QztKPzhQPDFRPTJROjBWPjRTQDpPPTdOPjBMPC5GOidANCJFMSRDLyI7MyJIQC5XRzheTj5VRTdM PC5JMCJILyFDMB5INSNWRz9eT0dbUE1YTkpVRUFIOTVDOTNFOzVMOjpUQUFQREFUR0VMPC5HOCpM NSxROzFMOzRRQDpPPz5MPDtIODJEMy5IOThVRURVSkVbUEpeVE5bUEpQQTpPQDlKPDJDNCs8LSpD MzBOQ0RYTU5YT1BNREVKOitFNCZBLRpGMR5JQDdUSkBPPjlNPDdEODNEODNINzBHNS9BOjdHPzxM QEROQ0ZRRUNTRkRRSElUSkxTQTtJOTJOR0dYUVFnUVZfSk9PPDxGMzNOQENYSk1dUExiVVBjU09c TEhTQTlJOTBFOzVNQz1ORTxRSD9PRzhMRDRKPjpMPztPPTtRPz1OPj1MPDtKOzxMPD1TQENVQ0VR SUZQSEVTQ0RTQ0RVRD5TQTxGPjlIQDtaSE5fTlReR0hVPj9MOTdOOzlIOS1KOy9QP0VfTlRcVVda U1VXRUVPPT1PPjVTQTlaR0BcSUNjSj5tVEd1VVB0VE9pT0hfRj9eRUBhR0NpTVZuUVtUQzxJOTJN REVbUVNcVFNWTk1XRTxQPjVKOjFBMSlGLylMNC5HOTFKPDRVRj5hUUlnVU5iUElbRT1VPzhOOSNT PSdcPTljRD9eRz1fSD5bRjtcRzxjTkBhTD5dTDxaSDlPOjJRPDRYRkRYRkRXQTpQOzNMOSRHNCBP NyBVPCVOOixPOy1MPStMPStTPCxXQDBcQDRkSDxVRDhNPDBMOzJKOjFPQDpRQzxWSUlaTU1dUUlY TUVaTkZWSkNJQCxBOSVAMRxHOCJKOy1NPS9UPzFUPzFQPzdOPTRIOjBJOzFOOypTPy5QQzNVRzhY TDhVSDRPRzVPRzVQPjxTQD5XSUxdT1FeT0hdTkdbTklVSERWPjhROjNMOy1OPS9RPzlXRT5USkBP RjxJRDJKRTNEOCU/MyFDNB9DNB9JOixOPjBNPjRMPTNNPDVOPTdOODdPOThMQDlOQztRRDNKPS1J OSpJOSpMNSlDLSE/OClJQTJaSENiUEpcSUNUQTtKMyc+KBxDLCFQOS1XSUxdT1FcTlBYSk1OPzVD NCtHOitJPC1MOzROPTdRQDpRQDpURDhRQTVXQzVWQTRTQDhRPzdWQDxQOzdOOzlFMjBHOTJNPjhV QzxbSEFXSkhTRkRIQTlJQzo/Ois9OCk6Lyg/NC1OQ0RYTU5XR0ZQQD9NOSxMOCtMNydQOytRR0ZP RURJOzFMPTNQPjVOPDNJOi5HOCxHOS9JOzFJOjdNPTpORD5VSkVYT1NQR0pMPD1MPD1TTUpcVlRo UFRdRklNPT5JOjtORk1YUFdeVVZeVVZYSk1URkhPPTdKOTJJOjlTQ0FVRj5bTERUSD9OQzpQPzlR QDpQPzlNPDVJNzdKODhKOTdOPDpQPTtUQD5TQTtNPDVTPDtUPTxTP0RQPUFFPD1IP0BWRU1YR09U R0VNQD5JOTJOPTdKOjFKOjFKP0NUSExYU05XUU1VRj9OPzlOPjBRQTNVRDtdTENjUE5qV1V1XVxv V1ZkTEFeRjxeSERqVE9yWlhqU1FNPDBMOy9NSElaVVZeVlNYUE1TRDxRQztTPCxPOSlMNSlKNChR OzFbRDpdSk1lU1ViT09XRUVaQzlVPjRMNSZVPi5dPjxjREFiSEFlTEVdSkFhTkVkT0RfSj9jSj1Y QDNNOS5POzBXRj9YR0BcPzpcPzpVPjFQOi1UPzJXQzVVRDRUQzNRQS9QQC5VQTBWQzFeRjliSTxR RDRKPS5MMiRJMCJIOCxMOy9PPzxYSEVeTEZjUEphUE1fT0xMQTFFOytIOSdJOihMOy9PPjJQPzFT QTNHPjRDOjBFOi9JPjNPOSxXQDNVRj5dTkZdVEpXTkVXRj1VRDtVRUFWRkNVSklaT05bSklYSEdW SUdRRUNQQD9JOjlDOStBOCpGNCxOPDNPRjxMQzlIPTRGOzJDNSc+MSNALxdJOB9NPSlOPipOQDNI Oy5JOi5MPDBKODhHNDRKOTdQPjxPPjBOPS9JOixHOCpNNypHMSVEMipWRDtYTElbTkxaSENVRD5N OS4/LCJIMStUPDVXSVBbTVRcSE1VQUZMOzJKOjFFOy1IPjBNOzVOPDdUPjpQOzdQPzpcSkViUEpj UUxfTz9YSDlQPjVHNS1JNC9JNC9DNC1GODBPPjVVRDtTRkFMPztEPDlEPDlBOy8/OS07MidAOCxN PTpVRUFbRT9VPzpXPzNVPTFQPzFTQTNXSkhPQ0BHNylMOy1VPTNWPjRFPDJBOS9DOShFOypJOi5O PjJUREVWRkdUR0dPQ0NPPDpUQD5eVE5fVU9kUU9RPz1IOz1IOz1PSE1YUVZaUFFRSElQQD1MPDlK OTJMOjNIPzdPRj1eTURlVEpTTD9MRTlMQDhJPjVMOyxJOSpNODBPOjJQPzdRQDhTQTlVRDtTQDpO PDVMOjNMOjNPPDpPPDpIPTVMQDlTRUlURkpQRT1OQztRQDRQPzNOPjJOPjJOPTdYR0BeU0lbT0ZX SEBNPjdMPTVPQDlTSENcUUxnW1xoXF1vVl5nTlZaQTtWPjhaSExrWl1rWFteTE5NPjdKPDRPRU1d U1tlVVRcTEpWRDtXRTxUQzdOPTFGNCxJOC9KOTtWREZbSEhfTU1dRkVWPz5aRDxQOzNHNytUQzdc Qz5aQDxdRUBcRD9WRz9YSUFlTkRoUEZlRz1TNSxGMSpKNS5XQEFYQUNcQDRfRDhaRDNWQDBWRTdc SjxYSDpVRTdTRTVRRDRWRTdXRjhhSTxlTkBWRz9MPTVKLyBILR5ALRhEMBtNPDVRQDpYRkRjUE5n VlNlVVFbRT1OOTFKNxtPOx9UQC9YRTNYQThXQDdJQTJBOitBMidJOi5QPDFbRjteTUdlVE5hVlNa T0xVR0lNP0FQQD9TQ0FPQ0NYTExbSklYSEdWRTxUQzpOPzlHOTJBMyxBMyw/MSpDNC1GOzNGOzNI OCxEMyhBMyBENSJHMh9MNyNQQDJVRTdNPy9HOipKOCVNOidFNShAMSRJOC9RPzdTPjNVQDVMPCpJ OihHMydKNypJOTJcSkRXT0xQSEVYQDpWPjhOOCtIMiZTPjNYRDlaTk9YTU5YQz5TPTlIOCxMOy9J OzFJOzFJOzNHOTFMOTdOOzlJPj9aTk9qWFxoVlpiU0haSkBOPixENCNHMSZHMSZEMyVIOClTQTJY RzhXRTxPPTRHPjJFPDBGOSpDNSdBMyJFNyVJOjdTQz9fR0BeRj9dTUBcTD9XSTpXSTpXTT5NQzRJ NC1QOzNVPjRUPTNFOi9ANStHPS1KQDBOPTRQPzdUQURYRkhURENRQUBPRT9WTEZcVFBdVVFbUUdJ QDdFOTlKPj5URkpVR0xUREVOPj9JODFGNC5KOjRKOjRJREFQSkheVFBjWFVVT0pKRUBJPjVIPTRK Oy1MPC5TQDpTQDpVRD5XRkBYRz5aSD9UQTtPPTdOPDVJODFFNzBIOjNHOS9JOzFTP0ZXREpWRT5W RT5aRDxbRT1aRThYRDdaRjRhTTtcUEhbT0daSkROPzlOPDVRPzlWTEpcUVBnV2FpWmNtU1ViSEpP PjhRQDpbSU1nVVhkVFBYSEVTQENUQURcSFFkUFpkUE5cSEZURTJVRjNTQzdOPjJFNzBHOTJOPj1R QUBaR0FbSENWRD5TQDtUQTlPPTRMOCtVQDNbQzlYQDdXQTxVPzpQPzdXRj1iTk5hTU1hQDVPMCZD LCFJMidUPjpRPDhYQTFdRjVbRjtcRzxbST1eTUBdSkFaRz5YRDVXQzRYRDldSD1hT0NjUUVdTEVU QzxOOChFLyA9LiBAMSNHNS9NOzRVQT9hTUpjVlFcT0pcSDdPPCtOOSVVPytbRDphST9bR0VWQ0BP QDdKPDJGNShIOCpRQDJaSDpkUVFnVFRjUFNbSEpQRT1FOjJFNC9HNzFGOC5RQzlaRkZcSEhYQDpW PjhTPjNJNStBLCNFLyY8LCE9LSJBLCBFLyNJMydIMiY9MR89MR9EMixHNS9VRD1bSUNQQDFIOSpF MB9HMiFFMx1HNR9JOCFVQytYQy5aRC9TPjBMOCpDNSlHOi1PPjlaSENWTUNNRDpTPDJROzFVQDNY RDdWRz1URTtcUU5YTkpUPjdJNC1JODFMOjNOPzVKPDJHOi1HOi1OOTNJNC9FOzpUSUhkVl1nWF9i VVBbTklTPypMOSRMOCJINB9FNSpMPDBcTUNfUEZaTkVTRz5JPSpFOSZHMyZHMyZNNydNNydJOzFU RTtdUE5dUE5WUExWUExaTUhdUExWTEhMQT5PNzBXPjhWQDlTPTVNPDdJOTNTQTlVRDtRPzdRPzdP RDtTRz5RRj5QRT1RRUVXSkpcVFNYUE9aST1MPDBNOzRMOjNJQEFKQUNPPjVNPDNOPS9MOy1NOjhO OzlVQ0VbSEpfT1BjU1RfVExUSEBOPDNPPTRTPThTPThRRj1QRTxUR0NTRkFTRkFUR0NWRT5PPjhI NzFHNTBBMy1IOjNJOTJMOzRWQUZXQ0ddRkVfSEddSkFdSkFfSUReSENcSUBdSkFhT0ldTEZfTElV QT9PPjhQPzlUR0VcT01oU1poU1piSEVbQT5ROTlXPj5aTlFhVVhiUElbSUNWQ0VcSEpfSk9iTVFh TkhaR0FUPzJXQzVTOzFMNCs/MSpGODBVPztYQz5cRD1aQTtUQzpUQzpXQDRROy9XPS5cQTJcQTJd QzNYQDpYQDpXRT9fTUdiUEdWRTxVOStKLyJDLCFFLiNMNTdQOjtVRTJbSjhdSThdSTheTENfTURe SEBcRj5aSDxcSj5aRD5eSENfTkdiUElhUERYSDxNPC1BMSM9LSJGNSpANStDOC1NOzRVQzxeTkpf T0xbTTxQQzJPRTdPRTdWRDtbSD9eRkFaQT1TQzdQQDRKOi5JOS1NQD5WSUdeUVFcT09cTEpXR0ZX QDRROy9HOipENydHNylRQDJYR0BdTEVURDhQQDRVPTBKMydMMCNGKx4/KxZELxpKMR9MMiBOOChO OChFNSc/MCJAMCVPPjJVSUBcUEdVSjpKQDBIMyJJNCNINB9OOiRTRC9aSjVfTkFfTkFORDVDOStD MyhDMyhKPzhYTUVYSDxURDhRPCxWQDBbSEFhTkdeU0pcUEhfVExcUEhbRzVRPi1QPC9POy5MQDhM QDhPOzBNOS5MOjNGNC5INTxWQ0leUFdfUVhhU1VeUFNdRz9XQTpPQTJGOSpEOzJKQTliVldfVFVX UU1MRkFFPSs9NSREMCZHMylJNyZNOilNPjdWRz9iUVNfT1BXTk9XTk9YSEddTUxaTkVQRTxWQTdb RjtYRkROPDpNODNPOjVUSD9TRz5UQzpVRDtORz5QSUBORkBPR0FVSEZVSEZcSUdbSEZaSENOPThN Ny1NNy1MPDlNPTpPPzNRQTVRQTNQQDJNOz1NOz1VO0FbQEdfTFNkUFdnUEpYQz1KPDJMPTNQOjlV Pj1VRUFTQz9OQzpNQTlQSkhTTUpTQDpNOzRHMitFMClHMi1MNzFMOjNMOjNWQ0lVQUhWRERbSEhd TUleTkpeTUdcSkVeTElfTUpdTU5YSEldTUxXR0ZUQD5QPTtWQ0deSk9iUFRhT1NcRkFUPjpUOzdb QT1eSlFjT1ZkUE5fTElYQz5aRD9bSEpeTE5kTEhbQz9TQTVUQzdUPjdMNy9GODBNPjdbQz9YQD1b Qz9cREBXRT5UQTtXPzlVPTdfRDhiRjpfSj1kT0FeSERhSkZkTkhlT0lnUUZdSD1MOCJHMx5JLyRO MyhPNzNUOzhRQy5VRjFcRjNhSjhcSkRcSkRYRz5VRDtTRz5WSkFbSD9eTENfUEZhUUddUUhVSUBT Qy5KOydFLyNDLSE/LydAMChFNCdPPjBQREFbTkxaTkVXTENVSUFVSUFdRT5dRT5dRjxaQzlbRT1Y QztNOStKNylMPD1OPj9TSEVTSEVXSD5VRjxTQyxOPihMPC5MPC5WOy9dQTVeSENiTEZaST1WRjpR Oy5NNypKMh5DKxdJNB1RPCRPPClRPitUPzJWQTRPPi9MOyxFMiBUQC1XTENcUEdVSUFMQDlJPSpG OidIOiZMPSlcSkFjUUhlTk1lTk1VRDtPPjVJOitENCZFQDdQTEFcSjtYRzhfSD5nT0VhUFFfT1Bf U1BcT01hVUxdUUhYTj9TSDpRRDRRRDRWPjhXPzlYQThUPTNNOS5JNStKOTlTQEBRR0RVSkdeUU9h VFFlTk1hSUhRTEdEPjo/NzhIP0BfVldaUFFTSklTSklMPi89MCJAMSBBMiFGNSpMOy9QPjxeTEli VVNdUE5YU05TTUhYUE9eVlVeVE5RR0FYRkZeTExRSElEOzxDMzBKOzhXR0hWRkdYR0FaSENPSUdP SUdTRkFUR0NXR0RXR0RMRkFNR0NOQ0RHPD1DMipDMipINzBMOjNTPTVbRT1UQzxUQzxNRDpEOzFI OTpNPT5YRUliTlNcTUZPQDpHNS1BMChINzFOPDdNPjhKPDVKPDRRQztORENPRURGPCw8MiNELSFF LiJKNCVQOipPPThNOzVOPj9PP0BOQENTRUdbR0dcSEhhTU9iTlBiUVNfT1BYRUlXREhPR0RQSEVT Qz9NPTpORUhYT1NeUFVcTlNTRz9OQztWQDtXQTxaSE5fTlReSk9fTFBcSUdXRUNTRUlaTFBfSUFY QztYQztYQztURjlKPTBNOzROPDVcRD9dRUBbRENcRURWPT1TOjpVPTdWPjhVRjxcTUNpU05rVVBu UE5rTkxoTk5qUFBoU0deST5QPCZINB9VOC1fQTddRT5TOzRQOylWQC5bRzNiTjpfTkdcSkRbRT9X QTxTRDxTRDxWRz9cTUVcT0pdUExbSEZWREFfQTdhQzhYQTFNNydBLhZDLxZPOSdROylQPjhbSEFd SkhbSEZXR0ZaSUhdSkVfTUdeTUZXRj9QPjhQPjhOOCtQOi1PPTdOPDVRPzlUQTtYSDpVRTdTQzNR QTJOPjJQQDRXQDdbRDpdSkRfTUZfVEpbT0ZKQzFJQTBKOitBMSNNPipWRzJcTDlbSjheTURaSD9V RTdURDVPPCtTPy5YRztfTkFWRT9MOzVGOCZFNyVIOCxVRDhcUU5iV1RkVFBiUU5YRz5NPDNEMR9D MB5AOC9ORTxbSUBeTURfVlpiWFxhVVhYTVBYSElYSElYUEpXT0lcT0pXSkZdRz9kTkZeUENfUURb TERXSEBbRT1RPDRQPjVRPzdORz5TTENbUUdiWE5tXF1kVFVWT09GPz9BOjlNRUReUU9bTkxWSUVX SkZMPjFDNSk+MBtAMh1GNShRQDJWRUhfTlFbU01XT0lWTkpWTkpcU1RfVldeVlVWTk1eSk9hTVFW TEhKQD1GMzhRPkNRRUNQREFRR0FRR0FPR0ZNRURMREBNRUFQRkBRR0FKRUBJRD9KPjpGOjVIOC9H Ny5OPjJURDhaR0BiT0hcTEpRQUBIOy5GOSxIPzVMQzlWQ0dcSE1KQz9DOzhFMys8KyNBMidGNytM Oy9OPTFOOzlRPjxKOTc/LixDLhs9KRZFLyBJMyRGNyhKOyxOPDdNOzVOPTdOPTdNQz1ORD5YRUNb R0VeSVBlUFdjTlVjTlVXRUVWRERTRkFWSUVVQ0BTQD5TR0hXTE1YSk9YSk9bQTtWPTdRPzlTQDpW Q0lhTVReTVViUFhYSElUREVVREdWRUheRj9aQTtcSUNcSUNYRj1OPDNNNC5QODFXRUNaR0VWRD1R PzlNOjhINTNNNTJROjdWQUZjTlNqU1ZrVFdrU1hkTFFbSEhjUFBiUERWRTlTPjBdSDprTkhvUUxh TEBPOzBQPSxXRDJaSDpiUEFkU0lfTkVcRj5YQztTQTJPPi9URTtYST9bTkleUU1aTUhTRkFYRDdX QzVUQzxQPzlNOR9QPCJQOytTPS1WQDtcRkBUR0NUR0NVRURbSklaTUpcT01bSEpXRUdQPzFIOCpM NydNOChQOTRTOzdQPzdTQTlURjdURjdWSkFUSD9RQDhQPzdURDhXRzthT0hkU0xfU05cT0pUSEBO QztFPDBFPDBMRTtQST9cTUNbTEFiUUViUUVdTz5cTj1XRjdRQDFfR0NjSkZUQzpJOTBFLyRGMCVK Oy9aST1bVFRiW1teVVZbUVNVSEZFOTdHMx5FMRw6LyhIPTVWSEpdT1FbWl5VVFhcTEpWRkVKRDpN RjxaSkNcTUVcT0pYTEdfSUVoUU1eVE5fVU9eTExXRUVRQzlNPjRVPzhTPTVKQz1UTEZfT0xpWFVl X1thW1ZWU09EQD0+OC5HQDdUTEpTSklYRkBaR0FOPixKOylFNyFFNyFENS5RQztXTlFXTlFYUE9W Tk1XUFBUTU1dUVNeU1ReU1ZcUFRfU1BbTkxRSkFFPjVIOThRQUBRSD9PRj1TRkFTRkFNRUFKQz9N Q0FRR0ZUSEBUSEBQQThPQDdOPTRMOzJKPDJJOzFUR0NbTkldTUxfT05XSEBMPTVFNy1GOC5JPjdN QTpQRT1OQztMQDVEOS5AMiE9Lx48LyBBNCVJOitJOitNOzJOPDNJNCVDLh9EMBtINB9HOSFMPSVE PypBPShKPDRMPTVTPThXQTxVRj5RQztbSEZcSUddSU5dSU5hSFBjSlNbSEFXRT5VQ0BUQT9TPz1U QD5XRUNbSEZbSEZaR0VYQztWQDlUQzpUQzpUQ0ZaSExaTFBcTlNXR0hVRUZYQUNaQ0RcRzxcRzxf TEliTkxYRj1INy5BMypKPDJUR0VbTkxbRT9TPThIOy5GOSxGNSpOPTFXRkBiUEpqVlRqVlRlTlFd RkldSk1hTlBdSkRXRT5bSENkUUxrUFNlSk1dRjVUPS1UPzJXQzVXRj1hT0ZnU1NkUFBaSENTQTxR QDJPPjBTRDpYST9bSUNhT0heTk9WRkdQQTpRQztQQD1UREBYQzJVPy9UPjdUPjdUQD5VQT9PRUFR R0RaQ0ZfSExeSk1iTlBdSkFYRj1QPzNKOi5KOitIOClNPC1TQTJQQTpRQztVRjxbTEFdTEVeTUZa SENUQz1RQzlVRjxdTEVjUUpfU05fU05bTEVTRD1HPSxMQTBRSD5USkBYTUFaTkNiUERhT0NdTkRa SkBTQzNRQTJeTUBdTD9aR0BKOTJGLyRHMCVGODBaSkNbVFZfWFtaU1NXUFBQSENAOTNHMiFBLRw3 LCVIPTVVSkVeVE5WV1BRU0xVRD1RQDpGQzBJRjNRQzxWR0BXSkhVSEZhTVFkUFVfUVRkVlheUU9V SEZNPDVOPTdRPz1PPTtIRDlOST5eTk9rW1xkX2NeWl1VT0pBPDhDNCNMPStUSD9VSUBaSD9aSD9T QzRQQDJPQTRKPTBGOjVQRD9VT01VT01VTUxTSklUTEpRSUhUTEpUTEpYTVBXTE9aUEdVTENVRTlM PDBMQDlTRz9XTENXTENRRj5RRj5QRkBORD5QQD9WRkVdSkhfTUpTRThPQTROPDxMOjpMPDtURENb TU9eUFNiUVBaSUhOPTFMOy9MOjFNOzJQOzNQOzNKPzdHPDNBNCVAMyQ/MB0/MB1DMidHNytKOy1N PS9TPDJROzFPOihPOihPOSxPOSxNPC5RQDJKQzNKQzNPQDpOPzlVPztWQDxUQz1WRT9cSkVdTEZk TkljTUhhTU9hTU9WSUlUR0dWRkNTQz9PPjJNPDBWQ0NbR0deRkNdRUFWRD1aR0BXRztTQzdPQDlT RDxYRkZbSEhfSUVaRD9UQTxRPzpWRTxhT0ZtVlFqVE9aRDxIMyxHPDRMQDlYTExdUFBhSERTOzdJ NyJMOSRKNSRQOylYRj9hTkdqVE5oUUxcSURTQDtaSENdTEZbSEhaR0deTEVjUEllTkReRz1YQTFX QDBTQTJRQDFTSEdcUVBfU1BeUU9UREBKOzhPPjBRQDJPPjJQPzNXREFjT01eU0pTRz9PQ0BMPz1Q QD9VRUReTkFdTUBbSkdVRUFTQTtNPDVPPzFURDVaQT1fR0NhTU1hTU1iUElaSEFOQzpPRDtQQDFO Pi9URDVVRTdXRj9bSUNdUUhfVEpfVExfVExbSUNTQTtPRDlXTEBaTkNiVkpeU0pfVExYSUFPQDlI PylHPihQQThURTtXTENXTENhUEFeTj9aSENYR0FRQDhWRTxbTkleUU1dTD9RQDROOi1EMCQ/OS9R SkBbVVBdV1NYTElWSUdMRTtDPDJEMSE8Kho0KxtEOilUSUZeVFBVSklRR0ZTQDtRPzpOQzhQRTpO QzpRRj1VSUFUSEBXSUxcTlBaTUpiVVNdU09USUZUQzpRQDhPPzFNPS9FPjJHQDRXTE1fVFVnXGRe VFxRTUFDPjNQPCZXQyxXRzldTT5bTEFaSkBURDhRQTVRQDRPPjJQPzlTQTtTSUBXTkVTSklRSUhR RUBUR0NRRj5PRDxPRURVSklYTUVVSUFRPzdRPzdJRD9UTkldUExcT0pPRDxOQztQP0NRQERUREVY SElYUE1YUE1XTUlMQT5OOzlPPDpKPjpaTUhiVVVhVFRbSU1KOj1JNCVJNCVPPCtQPSxVPTBUPC9J OTBHNy4+LyI/MCNBLBtGMB9JNShOOixMOyxQPzBYOzNaPDRWOS9aPDJTPDBUPTFTQDtUQTxQQThQ QThRQzxQQTtRQztPQDlQQD9VRURXSkpbTk5kTkZjTUVlTE5lTE5WTEpUSUhVRj9PQDpKOTNNOzVX RUVaR0dbQz5YQDxYRj1bSD9bSURVRD5TQTxYR0FbSklYSEddRz9YQztPPDpUQD5bTklnWlVoVU5k UUpdRjpPOS1RQDRXRjpcUUxfVU9cTD9TQzdOOSdQOylOOCRNNyNUQD5jT01kUU9cSUdXQEFTPD1Y QztXQTpRQDhVRDtYST9dTkRhTkhaR0FhST9qU0hRQDRNPDBNSD5RTUNYUEpTSkVQPjhHNS9KNChP OSxKPTBHOi1QPjxaR0VYTUVPRDxOOCxIMidINzFPPThXTkVeVUxYT1BUSkxVRzpNPzJPPjJRQDRT Qz9UREBWSkFYTURdU0BYTjxNSDtNSDtRRDRRRDRYST9dTkRiSEhhR0ddUExfU05dVlZbVFRaTk9U SElXRj9hT0hcUEViVkpdTkRXSD5WPjhTOzREOilGPCtIQTlPSD9VRD5XRkBaT0BcUUNcSUdbSEZY QUNcRUZaVFFeWFZfVUZRRzlPQTJHOitBPi5PTDtcVUxbVEpXRj1QPzdKOy1ENCdEMR87KRc4LxxE OydRRkdfVFVVSEZOQT9NPDNOPTRVPj1WPz5NRDtPRj1RRjtPRDlJRD1QSkRYUE1hWFVbU1FTSklO QT9OQT9VRDtRQDhMQTxNQz1XSU5eUFVkXWJdVltTRzxMQDVdRzdkTj1cVERfV0deTUZaSEFQRTxO QzpQSDlRSTpURT1QQTpUR0NYTEdTTENORz5NPjdQQTpPPzNPPzNQRkNUSUZWSkFVSUBRRjtPRDlH RkBUU01dU09YTkpRQDpKOjNNPT5RQUNNQz9XTUlbTkxcT01aSUZKOzhGNSpJOS1KP0NfVFdiWFxb UVVRQDpDMixINChJNSlTQzROPjBRPi1NOilFMCFALB1FLRlHLxtELhtJMyBKOylOPixQPC5TPjBX QDRYQTVUPTFVPjJWPzNVPjJTQDpUQTtOQztMQDlPPjhQPzlOQT1OQT1TQz9WRkNVSUpXTE1iUEph T0ldUE5eUU9bTk5aTU1YRkZRPz9OPzlNPjhdREZeRUdaRD5XQTxcSEZeSkhWRkNUREBaRkRcSEZW TEpUSUhYR0FTQTxONThXPkBdUE5kV1VnT05jTEpaRTpRPTJRQzxYSUNiVkpjV0xcTD9YSDxNOCRE LxxDMyJGNyVORD5dU01hT0hcSkRUPjdOOTFTOy9WPjJOPTRRQDhYRj1cSUBaR0VYRkRlUVRtWFtO PDVINzBNPDBVRDhXTENXTENaQT1PODNJMydHMSVGOidIPClGPTFUSj5USD9OQzpQNyhMMiRGMCFM NSZXRUNeTElXTE1WSkxQRjRIPi1HNy5IOC9KOTNRPzpURDRWRjdVTjhUTTdPSThNRzVURjlYSj1a TUpbTkxfTU9aR0leTkpjU09aV1ZYVlVYT1BRSElWTEheVFBdVlZdVlZXTEBJPjNNPC5JOStFOClH OitOPzVQQThUQzpaSD9XTERbT0deTEldSkhVQUhYRUxcU1RiWFpbV09NSUFQQDJMPC5IQThTTEFa UEdXTkVKRC5BOyZIOSVHOCRFNBhAMBU9LiNJOi5RRUVWSUlTRDpKPDJHPS9HPS9QQTtPQDpMQzdP RjpQQThNPjRFPThPR0FYUVFfWFhcVlRTTUpTQ0FXR0ZTRz5OQzpQRz5KQTlMRkRWUE5iWlhcVFNY SDxcTD9eTUZqWFFfVVFdU09iU0xbTEVXRj1XRj1YT0NbUUVUTUBMRTlTSENWTEZRSD9ORTxNPDdN PDdHPjJJQDRQRkNTSEVaSENeTUdbTEVRQzxOST9XU0hYTUVVSUFNQTlDOC9NPTpTQz9MREBPR0RT SkdWTkpdTENUQzpRPTJQPDFXQ05iTVhhVFRVSEhOOSdGMSBIOStOPjBVRDtRQDhOPi9IOSpELyA/ KxxBLRhELxpBMBZKOR5OPS5TQTJUPjdWQDlXRztaST1WRDtTQDhXQzVVQDNYRj1TQDhJPjNFOi9D ODBGOzNMQDlNQTpRQzxVRj9RRUBYTEdeUU9fU1BfU05eUU1bT1BbT1BcSUlVQ0NUQTxVQz1iSEVj SUZhSEVfR0ReSkpdSUlWRD5aR0FfTkhbSURWREFVQ0BRQDtOPThFMjRTP0FeSlFlUVhkUE5dSUdR QDRPPjJWQ0NcSEhlWlBjV05cSkFTQTlIMh9FLxxJMydQOi1XRT5fTUZbSj5YSDxJPy5EOilOOSlR PCxVPzhaRDxbQz5bQz5XREZcSEpiVFtfUVhGODFDNC5IOCpNPC5QRjhQRjhbQUFWPT1KOi5GNSpD NSZHOipNPjRQQThTQzdWRjpTPjFMOCtMMiJPNSVTPz1XREFWTURTSUBaRjROOypDOiZAOCRROytU PS1VPy9WQDBVSjlVSjlYST9XSD5URjlURjlWTEZbUEpeTkpcTEhcT0pbTklWVU9aWFNaUFRQR0pR RkdYTU5aVVRVUE9TQzRJOixENB9DMx5IMiZIMiZPOy1WQTNVPzhWQDlXREheSk9aUFFXTk9NQEBO QUFWTVBcU1ZWTkpKQz9POzBMOC1MRD5TSkVfUElYSUNMRS9DPCdQPSpWQy9PPylOPihIOjJNPjdX RTxYRj1TQzNJOitEPCtHPy5PRTdTSDpMRDJKQzFKQTlIPzdGPj1QSEdWVFdcWl1cTlBWSEpPR0ZR SUhGPzdJQzpMPTVJOzNJRD9TTUhkWlZdU09bT0dkWFBjWltnXV5hVVhbT1NfT05dTUxfUEheT0dc U0hbUUdbTEVYSUNUR0VXSkhVSUFMQDlMQDlJPjdHPjRJQDdPQ0BVSEZdSkhoVVNVUEVKRjtWRz1W Rz1VRj5RQztGOzJEOTBKOTdOPDpMQT5NQz9XTUdfVU9dUUZYTUFRQDpQPzlXSVBdT1ZcSkRQPzlH Mh1NOCJPPjVbSUBXTERPRDxJOC9EMio7Kx03Jxk+JxRELBhJMypXQDdYRj1WRDtUPjpXQT1eTUZf TkddTD9bST1dRT5cRD1dRT5WPjhQOylNOCZIOClHNyhHPDRKPzhQPzlRQDpRQztYSUFWSUdYTElf U05hVE9dVFVbUVNdSU5XREhVRj9QQTtYRkZeTExlTk9jTE1bSEhUQUFVQ0BbSEZdSkVYRkBTQDhU QTlRQTVMPDBHMDVYQEZnUVhqVVxhTkdcSUNUPzRRPTJVQ0NeTExiU0piU0pdTD9TQTVOOypQPSxR PTJVQDVfRkNiSEVaSENcSkVTQTVNPDBNQC1OQS5XSEBeT0dpVEZjTkBdQ0VcQURbSU9dTFFTPyxN OidPOCtTOy5RPzlVQzxVRDtWRTxPPjhKOjNGOzBGOzBOQDNKPTBTQTlVRDtQPzNNPDBMNydPOipQ PjhVQzxaRkRWQ0BTQTlKOjFGOSxFOCtOOypUQC9XRDJYRTNURjVTRTRXRkBaSENaSkBVRjxVSUBa TkVcUUxdU01dVEpbUUhcVk9eWFFcU1RPRkdRQUBWRkVXSkZbTklVQTBPPCtINB1FMRpJMyBHMR5J OS1UQzdWQTdYRDlWRT9fTkhfV1RaUU5QQTtKPDVXSUxdT1FYSEVQQD1PPjJNPDBOREBRR0RcTE1a SUpURjlTRThVRzpYSj1bSj5bSj5UQTlQPjVURDhQQDROPjJMPDBBOCpIPjBOQzhXTEBXRjhRQDJT Rz9MQDlJQERPRklWUE5cVlRfT0xdTUlWUExaVE9QRz5IPzdIOjJIOjJMRUdRSk1eVVZbUVNbU1Fk XFtqXmJnW15eVFNYTk1eUVFiVVViVVNjVlRdU01bUEpeTkpbSkdUSUZWTEhRSkBNRjxRQzxOPzlM PDlKOzhQRz1aUEZhVlBlW1VWT0ZMRTxRQTVTQzdURT5QQTtKPDRKPDRFNy1IOjBKOzpJOjlUSUhf VVRcUVBVSklNQz1MQTxQRkVWTEpYSj1RRDdOQDBRRDNVSEZbTkxTTENJQzpJNyZALh49KBdBLBtB LRVFMBdNNzVaQ0FbR0VUQD5TPTlYQz5eTE5iT1FjTkBhTD5YRz5XRj1XRj9TQTtXOjBUNy1JOStA MCNAMilJOzFQPjVVQzpXRj1XRj1VRUZcTE1jVlRnWldiVk5eU0pbUE9TSEdUQT9RPz1OQ0ZXTE9j U1RiUVNhSkZdR0NdSUxeSk1dR0FXQTxTQzdQQDRVQDVPOzBINzlXRUdoT1VoT1VWRz9PQDlPPjlO PThNPjhXSEFcTUNbTEFdRz9RPDRKOi5OPTFPPjhWRT5iSkxlTk9dSUxdSUxVPzhNODBMQzlWTUNl VkxqW1BpVk1jUEdaR0FUQTxcSEheSkpYRTFbRzNYQTVXQDRROjBQOS9QPzFTQTNQPzBQPzBNQzRJ PzFJPy5FOypKPTBOQDNPPzNMPDBPPCdPPCdPQTFTRTRYRkBWRD5QPzlJOTJNNy1OOC5MOjFWRDth T0ZcSkFRRDRTRTVUREBXR0RVTkVUTURWRz9YSUFXTUdbUEpaUUxcVE5dVU9eVlBeV05RSkFUQzpU QzpcSkFfTkVcSjtWRTVPQC5KPCpMOiFJOB9MPTNURTtWRDtVQzpRQzlcTUNeVlNcVFBRRDdHOi1Q RERXSkpbR0VaRkRTRDpQQThOQztOQztUR0NXSkZUSEBWSkNbTEFdTkRXTERbT0dXSD5MPTNRQzBW RzRIQTVDPDBDOStJPzFTRz9aTkZcTUVURT1WRkNQQD1ORD5USURXTERaTkZbTkxeUU9XUFBbVFRV SUFJPjdJOzNHOTFNPD9dTE9jV1tjV1teWl1kX2NoXmRiWF5cT0pUR0NYTU5hVVZhU1dhU1deU0lb T0ZbUE9YTk1TSklTSklPRDxPRDxMQzdGPTFIOTpHODlNSj5YVkliXFVhW1RaSD9OPTRPPTRTQDhT RDpQQThPQDdNPjRKPDJJOzFEOixDOStMPDlaSUZUTU9PSEpKQz1FPThMPDlPPzxVRD1UQzxRRjtb T0RWUExUTklWRz9QQTpQOipDLR47JRk8JhpJNSBUPylWRTxYRz5cSUBUQTlQQDRTQzddTFFkU1hf U05bTklPR0FPR0FaSkRaSkRbRDdUPTBMPCpENCNFOy1MQTNYQztcRj5YSUFVRj5RR0ZVSkliUU5l VVFnVlNjU09dU01VSkVTRkFTRkFVR0lbTU9hVFRiVVVlUEVlUEVdTkdbTEVaRz5RPzdUPzRXQzha QzlTPDJQNzdaPz9hR0NfRkFaRDxVPzhQPT1KODhFNy1KPDJYSj1YSj1bQzxYQDpQOi1VPjFUQT9a R0VfTkdiUElfTUZhTkdWQTNPOy1QRD9bTklqU1RrVFVlTExfRkZYRUVXREReTEZfTUdhT0ZhT0ZX UU9XUU9VQz1MOjRKOixKOixTPjNWQTdRRzlUSTtMRTlGPzNHOipJPCxPPzFPPzFPQTFNPy9RRDNW SDhVSERUR0NRQztKPDRKNSJKNSJMOTlWQ0NfT0xlVVFdTEVaSEFTQDtUQTxUSURUSURVSUFTRz9K SUFOTUVVTE1WTU5eTk1fT05dUUhVSUBTRDxRQztXRjpfTkFiUEBhTz9cTUNXSD5URjlNPzJPRTNY TjxfUEZVRjxURTtXSD5dU01fVU9XSTxENypJOzRVRj9fTj9jUUNeU0laTkVRRj5IPTVHPzBTSjtd UUhbT0ZiU0piU0phUUpeT0hXTzxRSTdTRzxbT0RQTTxFQTFEOS5IPTJXT0lcVE5bTkxUR0VURT1M PTVPPzxTQz9RR0RYTkpYU1BeWFZdWFdXU1FVRD1MOzRKQTVEOy9KPzdTRz5WTU5cU1RdU1tlW2Nn VVthT1VTQTtQPzlWSE9dT1ZcU1RbUVNaUEdWTURQSkhWUE5WSkNQRT1ORD5NQz1JQzpBOzI/NC1B Ny9NSUZdWlZnX19dVlZWRT5PPjhQRT1VSUFRSD5NRDpOQzpOQzpRPTBKNypBNR9GOiNKOjRUQz1R RUVRRUVKQDtANzFINzBOPDVWRkNbSkdfT0xnVlNfU1BXSkhaRThWQTRRPSdKNyFBMh1GNyFUQzdY RztYSUFURT1VRjxURTtPPjBWRTdeTVBoVlpiUU5iUU5USUZTSEVXSkhYTElbST1PPjJJOSpKOitU RT1VRj5WRDtbSD9XSEFWR0BURkhWSEpcTlBdT1FjU1RkVFVfTU1WRERWRz9aSkNbSEpeTE5dUFBh VFRjUUpeTUZdTUlYSEVYSDxVRTldRjxjTEFfSDVbRDFWQTdUPzRbRT9dR0FfRkFYPztNMipNMipM OjFQPjVWRTdYRzlXQDRaQzddQDtdQDteRkNkTEhjT09lUVFjTkBhTD5VRTBTQy5bSEZcSUdhTUpi TkxdSUdXREFWPz5cRURdSkFiT0ZhTk5eTExUVFRUVFRYRkRRPz1KOixOPS9RPi1WQzFbSUBbSUBa ST1OPjJJOylHOSdMOy1TQTNWRjhVRTdXRj1XRj1bSURaSENXRzlQQDJPOyFOOiBNODBUPjdbTkll WFRqVE9kTklXRT9UQTxVQ0BTQD5PQURPQURIQ0BJREFRRkdUSEleSERcRkFYR0BWRT5RQztTRDxU RDhXRzteTUZiUElhVUxdUUhdTkZWRz9VSjxhVkdnVklcTD9RSDxTST1hTkdkUUpdSDpKNylJNDBb RUBdUExkV1NjXF5cVVdTSEdDOThAOShPRzVkU0llVEpnVlNqWlZoVFRiTk5bTklcT0pbT1BfVFVd V0dPSTpHPzBNRTVWTU5cU1RYSUNQQTtNOzRQPjhNPjROPzVKP0BUSEldVlhfWFteVlNXT0xRQTNK Oy1FPS5FPS5GPjlIQDtPR0ZXT05bUE9jWFddU09TSEVIPzVJQDdbSU1nVVhdUVNbT1BUR0dUR0dW SUlbTk5XSD5OPzVRQTVPPzNGQTc+Oi9BNChIOy5RSkpeV1dnX2JbVFZTRThQQzVaTk9bT1BWREFT QD5VPzhWQDlVQS5JNyRJNCFPOiZQPDFUPzRRQDhQPzdNPjdJOzNPODRROjdaSExjUVVkUFVjT1Re SERaRD9WRD1VQzxQQCxNPSlJPy5NQzFbSURbSURaSkRXSEFVST5TRzxOPjJXRztkU1ZpV1trVFNl Tk1RQztRQztUSUhRR0ZTQTtNPDVNOzJRPzdaRkRXREFURDhWRjpUR0NUR0NWRUhcSk5eSk9iTlNk U1ZlVFdeT0hURT5YRkBcSURbSEheTExiUFRiUFRhVU1cUEhjUElfTUZYRj9dSkRpUEZnTkRdTTpY SDVXRj1YRz5aSENcSkVdSUdaRkROOixJNShTOzRWPjhYQTVbRDhXQDRcRTllRz9kRj5jTUVnUEhp VVdlUVRjTUdfSURYQztXQTpXRUVcSUleTUdaSENcQz5YPztVPztcRkFlTExpT09WTEZaT0leUFNd T1FcSUNXRT5VPy9RPCxNPC1VRDReTUdeTUdeTUZTQTtJPCxGOSlMPC1URDRVRjxVRjxXSD5VRjxW SkNYTUVYSj1XSTxaRC9VPytRQzBRQzBaSkNhUUltUVFuU1NkTEhiSUZXRj1VRDtQQD9NPTxJPjdN QTpTQD5WREFVSjxVSjxXTTxXTTxfSDxcRTlWRDtWRDtaR0BeTEVlVVFiUU5dVVFYUE1YT0VhV01t W1RhT0hWRz9RQztjUElkUUpYSTdHOSdJNSlbRjlcUU5jWFVkXGNaUVhQRTxBNy5IOCpaSDpjW1pq YmFpW11rXV9jVlRfU1BaTk9bT1BfTlZoVl5jWFVWTEhTQTtYR0BfUVRhU1VWRz9IOjJIOSpPPzBJ OzFMPTNNQz1ORD5aUFFbUVNbT0dUSEBNPjRNPjRHPDFEOS5AOjBGPzVVQT9dSUdhVlBjWFNYT0VN RDpDQDRIRjpYUVFeV1deTk1bSklRPkNWQ0dhSkZfSUVYRzhUQzNXRjdWRTVRRDNMPi5EMytKOjFP TUxXVVRfWlVUTklOQDNURjlcUFFaTk9bSENWRD5XQTpbRT1TQzNNPS5QQDFRQTJQPzdPPjVOPTFN PDBNPC1NPC1JNC1QOzNeSkpiTk5dRkdbREVVPzpVPzpVPztXQT1XRzlRQTNTQzRWRjhdTkddTkdd TUlaSUZcTUZcTUZXR0RYSEVoVlxpV11vVVVlTExQQzVOQDNPRj1KQTlOPDVNOzRPPjJUQzdbRT9b RT9RRj1QRTxQREFUR0VYTExaTU1fTU9lU1VkVlhhU1VhUE1YSEVfRkhjSUxdSkpfTU1dU1FfVVRh WFVdVVFbU0NYUEBcSURdSkVrUU5oTkpaSTtWRjhWRz9bTEReTEVfTUZbT0dXTERWQTdUPzRRPTJU PzRbRDhcRTlYRj1eTENkTkhkTkhiVk5hVU1iVVNfU1BcTE1aSUpVRDtUQzpQRkBPRT9XSEBWRz9T QzRPPzFQPjlVQz1iSE1kSk9bTEFhUUdfUEldTkdcTkBbTT9XSTpNPzBQQC5VRTJbSEZcSUdbTEVT RD1PPjJNPDBPQy9VSDRVST5XTEBUTUBRSj5YRj1cSUBeTUdeTUddSDtWQTRRRzVRRzVXSEBaSkNe TExfTU1iT1FiT1FbTklQRD9MQDlJPjdIPjBHPS9KPTBXSTxWUURWUURbT0RcUEVdTkdbTEVaSkBW Rz1aSEFcSkRfU05iVVBjVlRbTkxTSUBeVUxnVlVhUE9WRz9WRz9bT0daTkZWRjhIOStBOSFTSTBe UFNjVVdeW1dQTUlPQy9IPClFPDJdVElnV2FwYWprXWJoWl5iVVNcT01fT05cTEpfUVRoWlxdVlZT TExPRT9QRkBYTk1eVFNYTURMQDhHOi1OQDNOPDdNOzVBPzRIRjtTTEFXUEZdTT5XRzlTQDhOPDNE PTQ9Ny4+OytHRDNVRUZaSUpbVFZbVFZVTEFGPTNKRUBTTUhWT09WT09VSERQRD9MQUBTSEdcSkRa SEFURTtWRz1jUEpfTUdbTT1PQTJENCdJOixNSUFXVExdTkRURTtIQDFRSTpcVVVXUFBRQzlQQThR RzlWTD1QSDlQSDlVRjxXSD5YST9URTtPOy5OOi1NPS5NPS5JOzNQQTpYSUFVRj5PPzFQQDJKQThK QThVQzpaRz5YSDxYSDxdTENfTkVkU0xkU0xeU0paTkZaSUpXR0hYRkRaR0VhTVRlUVhjUVVfTlFX RzhQQDFRQDJOPS9POS9QOjBQPzNUQzdTQTNUQzROQzpQRTxWSUVXSkZXTUxXTUxeUVFiVVVjVVdi VFZdUFBaTU1iSkxlTk9hTU9lUVRbUVNjWltqWlZkVFBdTDxbSTpeSERjTUhpT1FlTE5WRjhVRTdb TEFcTUNeTENeTENcSkFdTENdRjpWPzNVPjFXQDNkTEFqUUdlU01jUEpnU1NjT09eVFNdU1FcT0pY TEdbSklXR0ZTRDxOPzhUQTlWRDtlST5oTEBlSjdbQC1OOSlUPi5bQUZiSE1eU0peU0pdU0RiV0hl WlBfVEpYUEpQSENPPzBQQDFbSEpdSk1dTkZVRj5QQD1MPDlTRTRVRzdXU0hYVElUTUBPSDxTRTVW SDlbU01cVE5cTUNURTtQRTxTRz5PRT9PRT9VRUFaSUZaTFBbTVFXT0xQSEVJQTxEPDdJOzFJOzFM PztUR0NaT0xaT0xVT0pVT0pYTEddUExcUEVXTEBcSj5cSj5YTkhbUEphT0ldTEZVSUFfVExkWlRe VE5XRj1eTURPSUdKRUNHPjVFPDNHPy5YUD5nVlVpWFdjWFNdU01ORTtHPjRHPD1fVFVuXF9zYWRy ZGRpXFxeWltTTk9VSkleVFNeVVZhV1hbTkxRRUNJQTBIQC9TSUBYT0ZYTEdQRD9OPTdUQzxVQUFK ODhBOy9NRjpbU0NcVERfTkVXRj1NPDdJOTNAOC48Myo/NTJMQT5UTU9YUVRbVldVUFFFREA9PDlK RERTTExXTUdUSURQRz1NRDpNQz9RR0RVTENQRz5PRUFVSkdiVVVhVFRaVERKRTVDOyxEPC1WRz1e T0VbTEFTRDpJQDRWTUBcW1dTUU5RRjtRRjtWTD1aT0BaUT9aUT9eUU1fU05XUEdRSkFPPzFNPS9Q PzdQPzdNQD5OQT9RRzlORDVNPihOPylNQzRRRzlYST9cTUNdTENfTkVkUU9nVFFlVVFnVlNdV1BW UElYSEdVRURXSEBWRz9eR0phSU1cSE9dSVBcSjtcSjtfSURVPzpTPjFPOy5OPDNRPzdXRTxVQzpQ RTpRRjtbSEhdSkpYTk1aT05hVFFiVVNiVFhjVVpdUE5bTkxfTUphTkxbSklcTEpjUVppV19rVFNo UE9hSD5lTUNqU1FqU1FlTlFdRklWQTdWQTdaRz5eTENeTUdaSENeTT1bSTpRRDNMPi5TPjBeSTtr VkpuWE1pUVBoUE9iT0laR0FdSkheTElfUEldTkdaSENVRD5TQTVPPjJdRD1fRj9rT0RyVUlqTkFf RDhVQDVXQzheSERhSkZiTlNlUVZhVE9oW1ZpYVtlXVdfWFhUTU1KQzNNRTVeTE5hTlBdUUhWSkFT QTtRQDpRRDNURjVTTk1XU1FbT0ZUSD9NRjBORzFRTEdbVVBeVUpVTEFQQTtURT5ORTxPRj1URjlV RzpXRkBbSURVTkVRSkFMQzdHPjJFOjFIPTRPRDxVSUFcTEhaSUZRTUNTTkRUSTtaT0BcUEVcUEVa TkVYTURaTUhaTUheTUdaSENRSElbUVNhWFVdVVFjUEljUElPRkdHPj9OPzhMPTVMQzldVEloXF1k WFphVUxeU0lPSD5DPDJKOzxdTU5lXVpqYl5nX19jXFxYWFtNTU9OR0lcVVdfVlpcU1ZVRTlMPDBD PCVIQSpTRz5VSUBUSEBQRT1RQDtQPzpQPkBNOz1DOi5RSDxYVElfW1BhVU1aTkZGPzNBOy8/MiRE NyhFOzhJPzxVUFFaVVZXU1RKRkdDPzhAPTVQRD9WSUVXSEFVRj9OSERMRkFKQz1RSURYTUVVSUFR R0FaT0lhV1hiWFpaWFBKSUFJPy9IPi5WRD1hTkdfTUpcSUdPSUVYU05eXVVRUEhQST1PSDxbUD9c UUBeU0leU0lfU1BcT01aSkNWRz9QQzNURjdUQz1VRD5RQT5TQz9TRDpURTtTQzNRQTJWR0BbTEVe TVBeTVBhUE9lVVRqWltqWltpXV5lWltfWldYU1BaR0VWREFeTEVaR0BaQ0FaQ0FdRklfSExhT0hi UElcTUVVRj5NPTFIOS1KPTBRRDdYRz5bSUBXTERYTUVbSEhdSkpbSkdbSkdbTk5cT09hT1NhT1Ni UU5bSkddSk1aR0lUR0VUR0VlVFprWl9oU1dnUVZnTUhqUExqV1doVVVfSElbREVWQDtUPjlVQ0Bb SEZfTUdbSENYSUFURT1TRDpRQzlVRDtiUEdqWk1qWk1lT0pkTklcTUVURT1cSUxdSk1kUU9hTkxf Sj9aRTpRPi1UQC9cSURlU01uVldrVFVhT0NaSDxTQTlVRDtbSENcSURjUFNoVVdoV1hrW1xnWF1l V1xfUVRVR0lNRC9RSDNdTEZhT0leTURaSD9VRDtTQTlRRDRURjdQTkNWVEhdTkRYST9QSDdNRTNR Rj5dUUljUUpcSkRKPjxMPz1MQzdPRjpRSDxRSDxYSDxbSj5WT0ZRSkFNRUFJQT5FPChGPSlNRDpR SD5YT0VWTUNPRjpORTlPRjxTST9dT0FfUURXUEdTTENbSURcSkVYSEdVRURQSEVVTUlcVE5dVU9h UE9hUE9TRkZNQEBRPDhRPDhUSUheVFNpXFxjVlZbSUNcSkRWTURNRDtMPD1XR0hdV1VqZGJlZGFf XltdXldPUElPRkdaUFFlVVRfT05QRy5IPydGOiVOQSxURjlQQzVVRD1TQTtXQTpRPDROPzhJOzNF PzlOSEFcVlRnYV5iXV5aVVZJQDhFPDNGMihINCpJOzROPzlTR0pYTVBTSUpKQUNHPThFOzVPPjhV RD1WQ0BaRkRUR0dQRERPRT9VSkVcTEpdTUxWTkpaUU5kWlZnXFhnVlNXR0ROPzVVRjxeTUZiUElk UE5jT01WTU5bUVNhW1hXUU9XSD5cTUNdVElfVkxeU0pcUEheT0hhUUpeTT1XRjdaRThdSDtbRjtW QTdQPzlVRD1USD9TRz5TRDpVRjxbSEhcSUleTk9iUVNhVFRnWlpqXGNuX2drXWRpW2JlW1pdU1FY Rz5eTURhTkdcSUNaRTpUPzRYRDdiTT9nVE1lU0xiT0haR0BPOy5NOSxOPjBWRjhfTkFjUUVdSkFa Rz5YRUNbR0VbR0VbR0VaSExYR0peSk9bR0xbSEZXRUNbSENXRT9QQTpYSUFeUVFhVFRjU1RhUFFd TEZkU01tXFhlVVFhR0daQEBQPjlWRD5WSUVdUExoUUlkTkZVRURURENXRUNWREFaR0FkUUxiV0Ze VENhTkVdSkFTQzdPPzNUQEBbR0djUUpfTkdfRz1aQThVPjFUPTBYSEliUVNoVVVkUVFdRzJWQCxP PTRTQDhcRURfSEdkUFdjT1ZlWFhpXFxqWFxqWFxnVFFYRkRTQTJTQTJbSUReTUdkU0RiUEFdTENX Rj1UPjdXQTpTTEFUTUNeT0VaSkBTRTVPQTJWRTxeTURlU01fTUdPQDlKPDRKQzNORjdRSDxVTD9Y Tj1YTj1YUUhXUEdVTEFORTtJPCxMPi5USD9XTENaTkVaTkVMRjdKRTVOQzpPRDtbSkdbSkdbTkxY TElWSUVVSERVQ0BTQD5QRkBXTUdbWlRbWlReUU9eUU9RQzxOPzlNPjdKPDRTSklfV1ZfWlNcVk9W TkpXT0xbTklXSkZQRkNVSkdhVVtpXWNlX1tjXVhcXVhTVE9ORkVUTEplTk1hSUhVSjlTSDdWSTVU RzNbSj5WRjpYRztbST1cRTtUPTNMOjhNOzlNQUVQRUhiWFxpX2NkYmVdW15PQ0BMPz1NOilINSVN PDdRQDtUSExWSk5VSEZOQT9HPjVFPDNQQTpTRDxYR0FcSkVVRj9VRj9RREZWSEpaTk9dUVNaU1Nb VFRhW1RiXFVqWFNXRkBQRkNaT0xrWFNrWFNtWFZpVVNcVFNbU1FcVk9bVU5cTD9jU0ZiW1BfWE5b UUhbUUhnU1BoVFFiUUVeTkFqT09pTk5eST5VQDVTQDhWRDtXTkVWTURUR0NWSUVXSkpUR0dWSUdd UE5iVlpoXF9rX2VqXmRnU1xpVV5kVFBjU09iU0xhUUpnVE1hTkddSD1WQTddSkRpVk9tXFtnVlVq TUpdQD5POSVMNSJaR0dnVFRqV1BkUUpcSjxXRjhWQ0BWQ0BbRUBdR0NaRkhaRkhcSE1dSU5hTU1a RkZUQzxVRD1WRD5YRkBaTU1fU1NeUVFbTk5aTk9lWltrW1xiUVNeQTxaPThWREFaR0VeUU9pXFpp W01fUURVRjxTRDpVRURTQ0FYRkRiT01hUERYSDxaSDxXRjpPPjBPPjBUQTxeTEZnVE1eTEVYQzJU Pi5QOjBUPTNXRkleTVBkUU9hTkxfQStWOSNPOjJUPjddQD1iRUFfSlFkT1ZnU1VpVVdqWFxqWFxl VE1cSkRWRjhPPzFPR0RWTkpjVExnV09hVFFWSUdXPjhWPTdQQDFVRTVbT0ZbT0ZYSj1TRThPRjxW TUNiT0hkUUpXQzhPOzBQPDFWQTdVSUFbT0dbUUdbUUdbU09aUU5cTEhaSUZTQzNPPzBRRj1XTENY TUVaTkZOQzpHPDNPPjhQPzlXRkBbSURcSURcSURYQz1YQz1TRD1QQTtWREFeTEleV1peV1piVldb T1BVRj9JOzRGOzBIPTJVRj9hUUpfXFZeW1VcUU5dU09hU1VdT1FOSEFPSUNiT1pqV2JlWl1kWFxh WlpWT09NQz9USUZdTUxdTUxcUEhdUUljWlBeVUxfTkhaSENeT0dlVk5eU0lTRz5NPjdNPjdJQERK QUVfWFtlXmFdW1pbWFdPQDdIOjBMOyxPPi9TQTVRQDRUR0dTRkZORkNKQz9JPjVFOjFRQTVWRjpW SkFXTENTSUBUSkFQRUZUSElWSE9bTVRcUU5hVlNlW1dkWlZdUUlQRT1USUZdU09pWFVrW1dpWlNj VE1bUE1eVFBdVU9dVU9WTURlXFNpYV1kXFhaT0xYTkpnWlplWFhkV1VkV1VqVltkUFVdRD1VPDVT RDxYSUFeT0hbTEVYSUNXSEFWRz9TRDxVRURaSUhlV1xpW19oXmJhV1tbT1BcUFFhUE9hUE9eUU9j VlRpXFxeUVFYR0FVRD5cT01oW1hqXF5iVFZdRz9RPDRMPjFXSTxkWFpoXF1kV1NXSkZaTD5WSDtP PTdQPjhURTtVRjxVQUFVQUFVREdYR0paR0lTQENRPzdTQDhWQDtUPjlVSEhhVFRfTUpeTElnUVZq VVpjUEpTQDtYQDpbQzxeSkhoVFFnVVttW2FpWlNeT0hYQTRXQDNQPzpQPzpWRERdSkpdTkRXSD5a SDxVRDhPPzFPPzFWRD5dSkVfUEldTkdRQTJQQDFTPjFVQDNbSEpkUVRkUUpdSkRfRTFWPClTOjNa QDpbQz5eRkFcTEpdTUxfTU9jUFNjV1hlWltjVlFbTklbSjxXRzlbRT1eSEBiUU5nVlNlVVFbSkdY QztVPzhKPS5OQDFUTURYUUhdTkRcTUNYR0BaSEFTTENYUUhVSjxPRTdOPTdTQTtYSEdfT05cUEha TkZXTE1VSUpcSUddSkhaSDxRQDRRSTpTSjtQST9TTEFOQzpHPDNOPjJVRTlXRkBbSURcSkVcSkVb TEFVRjxQRztMQzdQQ0VYSk1eV1paU1VbUE9TSEdTRz5MQDhPPjJJOS1RQDhXRj1cVVVbVFRYTkpc UU5fVFViVldQSUBNRj1aTk9kWFpkW15lXF9hV1tQR0pIQD9KQ0FcTEpjU1FiVVBjVlFiWFpbUVNa R0VdSkhdV1NfWlVeWFRaVE9PRjpGPTFBOjRHPzpcTlBrXV9jW1pVTUxRRTFNQC1RQzBRQzBVRTlO PjJNQDxOQT1NRDtKQTlHPS9FOy1QQDRaST1dTkRcTUNUSkFRSD9MRkRKRUNPRURQRkVRT05bWFdl XF1fVldaSkNTRDxbTkxnWldlVVRiUVBVUEZVUEZaT0xcUU5cVFNfV1ZhV1toXmJoYWFjXFxcT09Y TExjV1hjV1hjXFxkXV1kW15dVFdYR0BYR0BeTk9kVFVhUE1aSUZPSD9TTENRRj5OQztRQUBaSUhl VFpwXmRpXFddUExbSURcSkVaTU1cT09jUVdoVlxlXF9bUVVaR0VWREFXTE1jV1hjWlBeVUxYSDpR QTNTSEdfVVRpXV5hVVZfT05XR0ZXSD5QQThKOjFNPDNQOzNTPTVWOjhWOjhTPz9TPz9WPjpTOzdR Oy9POS1WPTdaQDpaRkReSkheTURhT0ZlTk9jTE1hRTxdQTldR0NiTEdhU1VoWlxrV1xzXmNrWlRi UEpeRjlaQTRQPjhVQzxYRUVcSEhbTT9aTD5aSjVXSDNURC9QQCxYQz5cRkFYTUVXTERXRjdVRDRX QzVbRjleUVFjVlZcUEVWSj9eRzpaQzVVPjRROzFOQT9VSEZbSEZcSUdaTkVdUUhbV1ReW1dkWlRc UUxXTENUSD9aRTpfSj9jUEpoVU9nVU9fTkhcSkVVRD5TQDhTQDhXR0RcTEhbTklcT0pdTENdTENU TUNaU0hQTD5OSTxOQzpPRDtWRUpcSlBeUU1cT0pUTURPSD9XRj9cSkRXRTxTQDhWSDlbTT1YSUFa SkNUSTtNQzRTQDhVQzpVRj5aSkNVTUlVTUlWSUVVSERRSTpNRTVQREFbTkxaVVRRTUxPR0FMRD5T RDpRQzlRQTVKOy9PPzNQQDRWT0ZXUEdUTkdaVE1cVVdiW11aT0lQRkBaTlFoXF9nX19oYWFYV1FI R0FBOjRFPThRSURbU01nVlNoV1RdVVRaUVBYRkRcSUdaVVRcV1ZdWFdYVFNURT1MPTVBOy9DPDBY TUVpXVVqWlZdTUlQRDBQRDBRRzdUSTlURjdMPi9MPTdNPjhRQzlNPjRGPSdEOyVVRj5eT0dYUEpX T0lUTklQSkZMRD5JQTxHPzpNRT9PTU5VU1RhWlpbVFRTRkFNQDxVSkldU1FdUUlYTUVVTD9YT0NU T0VXU0hYUVRcVVdiW11pYmRrXlxnWldXTkRUSkBcT01hVFFkXFtlXVxiXV5bVlddTE9fTlFjV11h VVteT0dRQztRRj1WSkFTRDxQQTpVRUZeTk9lWl1rX2NlW1pXTUxQQTpURT1aSExbSU1aU1dhWl5p W2JeUFdWREFUQT9XSkhjVlReV05XUEdORztMRTlPSEhXUFBkVFVeTk9YSEVTQz9TQTVNPDBOPS9O PS9POCtTOy5VOStWOixQOzNTPTVTPjFPOy5UPTBUPTBaQT1dRUBWSkFXTENbSUNaSEFaR0BbSEFe RT5jSUNeTkpiUU5jV1hkWFpoW1tpXFxrW1djU09iSjhYQS9OQDNMPjFTRkFVSERWT0NWT0NcTj1W SDhQPShRPilYQzteSEBbSUNbSUNdRjxeRz1aRD5dR0FaT0lcUUxXRztVRTlaRTpVQDVTQTNQPzFR RUNbTkxWRkNYSEVYTUFbT0RdVk1hWlBlWFZeUU9YT0VTST9XRjhdTD1YTUVeU0pjU09iUU5dTkdV Rj9XQTxXQTxVQ0NXRUVbR0xcSE1jTUdnUEpcVUpaU0hUTjxRTDpOSDlNRzhVRURXR0ZYTUVaTkZV SjpRRzdTRz5WSkFWRT9VRD5aRz5iT0ZcUEhXTERQSUBKRDtQQTtOPzlWREFhTkxdV1NaVE9WSUdW SUdVRzpTRThUSkFcU0laU1NOR0dPRjxORTtPRDxTRz9YTURWSkFURjdVRzhVTEFYT0VRTkpXVFBa V1heXF1YVVFQTUlaTU1qXV1iXmRhXWNYVElFQDc4Mx09OSJQSkZcVlFjWFViV1RXT0xbU09YSEVe TkphVlVfVVRdU09TSEVOQDFOQDE6OSVAPytTTUZiXFVkW1BWTUNMPi9RRDRVRzpWSDtQRC5QRC5I OStIOStMPTNKPDJFPS5JQTJVTE1dVFVbVldXU1RRSExPRklPRURMQUBGQUNMR0hQTE1aVVZbVlVV UE9TRThPQTRTSEVcUU5WTU5TSUpUSkFUSkFVTENbUUhWT1RcVVpfW1poY2JnXFZhVlBWTUBRSDxV SUBfVEpnW15kWFxkWlhdU1FbTU9jVVdnW1xhVVZcSDdWQzFTRTVXSTpRSTdORjNUREVfT1BlXF9p X2NkU01UQz1KPzhMQDlRQUNcTE1bVlVfW1poV1hhUFFURT1RQztPR0ZVTUxVTUdWTkhUSEBQRT1V SEhXSkpbSEZVQ0BPPjhMOzRMOy1MOy1QOi5ROy9TOy5WPjFYQyxYQyxPPzFRQTNUPzRTPjNaQThc RDpfRkFfRkFaST1bSj5YRz5WRTxWRDtdSkFjUEpkUUxhVFFfU1BiVVNfU1BfVVFkWlZpXFpkV1Ve TT5RQDJJOihOPixURT1XSEBXUEdbVEpjUUNbSTtWQTNWQTNeRkFhSERdSkhdSkhdSkFaRz5VRD1b SUNbSUBcSkFYRTFYRTFaRTpVQDVTQzNURDRWSUdfU1BQPjVTQDhYSUFbTERdUExiVVBjV1hcUFFX SkZTRkFURT1WRz9RTEVYU0xhVlBeVE5dSkpWRERTQTxQPzpRQzxURT5aRThbRjlfSUFhSkNbUUdd VElcU0haUEZQRz5NRDtWSkNaTkZcT0pbTklXSEBWRz9TSkVTSkVXTERXTERhT0loVlBfVkxcU0hT UEVNSj9PRzhQSDlXRjpiUERkWlRlW1VaUU5RSUZTQDpaR0BWUE5cVlReV1dUTU1RSkBPSD5YT0Zb UUhkVkheUENfSj1dSDtVTENYT0ZUT05aVVRaV1tbWFxQUE5OTkxbSU1hT1NeW2FfXGJYTVBHPD89 MyM8MiJNR0NdV1NlWFhkV1dfV1RdVVFiTlNiTlNoVVVjUFBaSD9RQDhQQzVKPTA/PitDQS5NTkdd XldfWE9QSUBNPjhVRj9eTUZdTEVaSkBRQzlJPC9HOi1JOzRJOzRIPj1QRkVTUFFXVVZcVVdRSk1T QTlRQDhNQD5NQD5KQDtORD5RR0ZbUE9aU1dRSk9QQzVQQzVTSjtWTj5YTkhWTEZQSENPR0FVSkVV SkVRTElWUE5dVlhoYWNoXF1fVFVPSDxORztUTklhW1ZqX15oXVxiUU5RQT5UREVeTk9fVVReVFNb STtdTD1fVEpfVEpVSjpQRjVWREFdSkhbVFReV1daSkROPzlMOjhOPDpRQEZeTVNeVlVhWFdoVVVh Tk5aRz5XRTxQRz1TST9aTUhXSkZUQz1UQz1aSkRaSkRdTUBYSDxNOSxMOCtFNCdGNShROjBXPzVc RURhSUhbTERaSkNQRjhQRjhYQztaRDxeRkFkTEdtUVFpTk5fTUZbSEFbQzxYQDpbREVhSUplU1Bl U1BjU1FjU1FdUExcT0piT01kUU9tW15pV1thT0NRQDRMNSJPOSVRQDpcSkRjV1hpXV5pVVNcSEZf Sj1hTD5eTEZcSURYTEdXSkZcSUBWRDtRRj1USD9bSD9dSkFaSTRXRzJcRj5aRDxRQDhWRTxaR0Vh TkxPOy5VQDNYSEVaSUZcUVBfVVRfVldaUFFYTElQREFOPjBOPjBRSD9YT0ZhT0hiUEleTUZYR0BV RjxOPzVOPi9PPzBWQTNbRjhcTUVaSkNaSkRfUEliUElfTkdPRDxOQztUTUNWT0VfT0xdTUlVRj5U RT1VTENVTENVTENcU0llVVFnVlNiWlhdVVRVTkVPSD9MRjRNRzVTSjtdVUVfXVFiX1RWVFNKSEdR PzpYRkBaTk9hVVZkWFpXTE1VST5YTUFdV1NdV1NeV01dVkxiVk1dUUhVTUdVTUdUTkxcVlRbVldY VFVTU1BJSUdQRUZcUFFiVlpeU1ZaSUhHODc/Lyc/LydJQEFXTk9tWlNtWlNnXV5iWFpcUFReU1Ze U0laTkVbSjVXRzJVRTdNPS89OCdGQC9UTEhcVFBcUEdVSUBTQDtWRD5hT0hiUElaUEZTST9MPypG OiVANylGPC5GPT5NREVcUFReU1ZYVElJRTtMPyhMPyhJPjdKPzhNPjhPQDpQSENaUUxbU09TSkdR QzlURTtUSkBYT0VbVEpYUUhaTkZXTERRSURNRT9OR0dVTk5dVFdlXF9nVVtfTlRMRD5PR0FXUFBf WFhjW1pjW1pfT1BUREVTR0haTk9iVVNhVFFkTklpU05jW1djW1dbUUhORTxUQzxaSEFbT1BbT1BU R0NQRD9UQzpXRj1dTUxkVFNlW1piV1ZiV1ReVFBcSUdaR0VYRz5aSD9dTkdaSkRVRj9XSEFdTUBj U0ZnVUZhT0BVQS5OOyhGOCZIOihRPDdbRT9eTEleTElbSENaR0FXRj9XRj9YSUNaSkRjTlNrVlty WFVuVVFkU0xcSkRaQTtaQTtjTE1lTk9kVUpkVUphUUdeT0ViUEphT0lkUU9lU1BpV1tpV1tiT0Zc SUBWPjFROi1UQEdeSlFpV11tW2FnWlpYTExcSUNeTEVeTkpaSUZaSkRXSEFaSUZXR0RaSD9WRTxc RkFdR0NdSkFdSkFkSkReRT5TQDpYRj9dSU5jT1RPPjBUQzRWREFbSEZhUE1oV1RiV1ZdU1FVTUdO RkBPQTJMPi9RPzdaRz5bSENdSkVhUERdTUBXTERVSUFRQDFPPi9QRTpYTUFaTUhXSkZbSEZdSkhf TkhcSkVVRjxURTtWTEZeVE5eVE5YTkhURTtTRDpQRztUSj5XUUpcVk9iVVVpXFxlXF1hV1hdT0FV RzpORDNPRTROST5cV0xjX1pjX1paWFNJSENTQz9QQD1WT1FfWFtiVVNaTUpdTEZjUUxeV1piW11l W1doXVpnXV5iWFpUTkdUTkdVT01YU1BhVlVdU1FVTUdPR0FMSENUUEpTTUhTTUhaSD9PPjVENCc/ MCNBPDhTTUhpWFpwX2FnXWFdVFdVSUpVSUpcUEdYTURfTz9fTz9aSjhTRDFBOCdMQTBVTENbUUhd TT5XRzlVQzpcSUBbT0ZfVEpdTUlhUE1XRzlNPS9DNyRHOyhHPTxNQ0FbSEhdSkpYT0NKQTVHPy1F PStIOjBKPDJNPjhPQDpWRkVbSklVTUdTSkVXSEFcTUZdVEpdVEpbVFRbVFReUFNXSUxQRkBMQTxQ REFTRkRWT09eV1dhU1VbTU9NRURRSUhWT09bVFRhW1ZdV1NeUU1TRkFWRUhdTE9fVExiVk5tWFhu WlpnXV5nXV5bTEVQQTtOQzpUSD9fTUdhTkhdTkdXSEFXSkhiVVNlW1dlW1diXFdiXFdeXFtaV1Ze Tk1aSUhcSURdSkVaSkRYSUNUQz1WRT9fT0xuXVpwXlhoVlBhTz9VRDRQPSxRPi1UQz1aSENdSkVe TEZdRz9aRDxXRT5XRT5WSUdcT01nU1xrV2FuVlVuVlVoWFFfUEldSUljT09qWFxtW15oXFReU0pe TENfTURkUFBjT09iUVNlVVZkVFNiUVBhTkVdSkFaRTdRPS9PREVWSkxfVFpnW2FiUVBWRkVbSENe TEZcSkRaSEFXSEFWR0BWREFWREFhRTpdQTdcRD9aQT1eSENlT0lkTEVdRT5RQztXSEBbTU9WSEpJ Oi5OPjJTQDhYRj1iUEplVE5oWFFlVk9eUU1YTEdNQzFJPy5UPzJbRjlfRDtnSkFbTEFaSkBaT0xW TEhVQzxUQTtQRkBbUEpfU1NeUVFcT09aTU1YTUVWSkNTSDpTSDpWSUleUVFeVE5YTkhTRDpTRDpN SDtPSj1YUE1dVVFqVltyXWJrXlxpXFpqVUlkT0RVRjxRQzlRSkpaU1NdW1xhXl9dWE5WUUdRTUFN SD1RT05fXVxjW1VaUUxaUU5eVlNdVlheV1piWlhnXl1lY2RjYWJWUUdUT0VfU1NeUVFfUVRdT1FQ TEBRTUFPT0BQUEFcU0lWTURVSUBTRz5PQy9EOCVFPDJWTUNfW1xoY2RoXl9aUFFXRj1XRj1hUUpe T0hjV05iVk1eT0dTRDxFOCtKPTBTRDxdTkZcTj5XSTpYRDdYRDdXTEBhVUlhUE9iUVBUT0VFQDdH OitGOSpDPDJGPzVOQzpUSD9YSUFVRj5WSDtPQTRMOyxNPC1QRz5PRj1TSkVRSURXTkRTST9UR0Nd UExiVVNiVVNbUVVaUFRbT1BVSUpTRkZPQ0NMPztQRD9USUZbUE1dUE5bTkxQSkhOSEZTTExYUVFa V1ZYVlVkUFBfTExbTEVeT0hbU01cVE5pW11tXmFiXFpXUU9YR0FUQz1TRz5WSkFhUUpjVE1hUUpf UElnVV1tW2NrXWJvYWVtX1tpXFdjWl1dVFdhTk5hTk5bSURcSkVYTUVaTkZYRkhbSEpoWmFzZGt1 ZGVvXl9oVkdYRzlYRi5aRy9VQ0NdSkpeTEZeTEZdRT5aQTtXQD9aQ0FbR0xkUFVkU1hkU1hjUFBl U1NiVVBlWFRpVlhoVVdoXF9jV1tlU01fTUdkSkplTExnUVZoU1dnVlVpWFdnV1BfUElcSUBUQTlR Pi1QPSxUQzxdTEVpVlhoVVdlTUhhSERaR0FYRkBXRTxXRTxWQ0BWQ0BYPztbQT1nRDtoRTxkSkdk SkdfTkdfTkdeTj5VRTVVPjRWPzVfRj9YPzlOPDNPPTRRPTJWQTdYTURdUUhhVlBiV1FlVUheTkFR RzdNQzJXQzRhTD1lUEFpVEVfUEZeT0VdUUlbT0ddTEZfTkhXTUlfVVFnWFtjVVdaUUxVTUdXTUdU SURPRjxMQzlRRj5cUEhbUE1XTUlWR0BURT5NRjpQST1eTVBjUVVkWlhoXVxqYlxoX1poVU9kUUxa SkRURT5VTlBYUVRiVlpoXF9jVlFaTUhUSTtWTD1VTUlfV1RjWl1dVFdeVVhfVlpjV1hhVVZdVlZl Xl5fX11hYV5cUU5YTkpYT1BcU1RbVFRXUFBKSUFNTERTTkRWUUdYUUhXUEdaSUhdTUxVTD9DOi5F OCtYSj1YXVxfZGNqX15XTUxRRDNVRzdeVE5eVE5jVVdfUVReTElTQD5FOClJPC1TRDxeT0deTEVa R0BXRjhXRjhcUUNpXk9kWlRhVlBVTkFKRDhKPTBKPTBJPC9KPTBQQzNaTDxeTUZfTkddTEVUQzxM QzdNRDhTSUBaUEdTTkNOST5USTtVSjxdTkZhUUlXVVZVU1RYTU5YTU5aTUhYTEdVSERNQDxJPTlO QT1QRD9XSkZUSURTSENKRT5MRj9PRkdQR0hXUFBdVlZfU1BaTUpWSkNTRz9QSkRVT0hkWlRlW1Vk V1NYTEdVRD1RQDpVRj5eT0deVUxfVk1fU1BfU1BoVFtvW2JyXGFwW19qVlRnU1BiVVVfU1NhT0hf TkddTUxhUE9eTk1eTk1aR0lcSUxhXF1qZWdwZGVpXV5qVUdlUENkTjtjTTpiSklhSUhjTkBjTkBe RjxbQzlaQT5cREBXSkpYTExbR0dYRUVbTkleUU1nVlVlVVRjUUxiUEpkVFNiUVBhTkVnVEpnVlNl VVFnWF1nWF1pW11nWFtlVVFbSkdUPTNQOjBUPTNbRDpYSElhUFFpVlhjUFNkTUNiSkBdRT5cRD1b RDhbRDhbQz5bQz5fQTplRz9qTUlpTEhkSkpjSUleSkhbR0VXSDNURTBVPjRWPzVVQzpUQTlTQTxR QDtRPzlTQDpUREVaSUpXT0lbU01kVkhfUURdTUBYSDxVRDtjUUhqXVhrXlplWFRhVE9dUUlfVExc T0pdUExYUE9hWFdnW15iVlpbUE1VSkdVTENRSD9RRjtOQzhNQzRXTT5cSUdcSUdaR0FUQTxRQztX SEBeTE5lU1VkWlZpXltqY2NlXl5jV09bT0dWSkNVSUFWTEpdU1FfVldkW1xcVlFaVE9VTEFPRjxP QURaTE5fVlpnXWFhWlxhWlxkVltiVFhbVlVnYmFoZ2FjYlxdVEleVUpbVVNaVFFXVE5WU01OSEFQ SkRYTExdUFBcVE5eVlBeUFNhU1VbVU5IQzxDPS5QSjtcXFxjY2NvXl1XR0ZRQzlXSD5lWl9oXGJl W1pXTUxaR0dVQ0NKOi5NPDBNPzJYSj1dSkhcSUdYTUVaTkZdVVRuZWRpYV1jW1dWT0VTTEFQRTpO QzhNPDNKOjFTRDxcTUVhVU1iVk5eVFNUSUhJRTtMRz1USUZdU09UTURQSUBNRDtPRj1dTkdjVE1U U0pRUEhQRkVPRURRSD9RSD9ORztJQzdMPTVOPzhPPzxRQT5NQTpPRDxMQDlOQztRQzxRQzxbTkxd UE5dUUlYTUVXSkZQRD9OSEFXUUpoWl5kVltkUE5YRUNPRTdRRzlaSkRiU0xeVUxeVUxdVEpeVUxl Wl9oXGJkWF5jV11jT09fTExYT0VXTkRaTkNaTkNdU09jWFVfTkhYR0FXRUVbSEhbVFZoYWNuYmVn W15pUVBqU1FpV05kU0lnT0VnT0VoUERiSj5cRDdbQzVaRD5YQz1TRD1URT5VRDhWRTlYTEleUU9o VFRlUVFeTElhTkxkUVFkUVFkUU9qV1VrW1dtXFhuW11nVFZlWFhkV1djVlFaTUhcQDhbPzdcRD9h SERdSUlhTU1kVFBiUU5nUEhlT0dlTUhiSUViRjtlST5fRz1iST9hSD5jSkBfR0NkTEdiSUZhSEVd SUldSUlcTj1XSTlTOzhWPjtXQTpdRz9OPi9QQDFNPjRKPDJMPD1PP0BVREdcSk5jUEpiT0ldTENd TENbTEVpWlNvYmJwY2NqXV1iVVVaTUpbTkxiUVNfT1BdT1RjVVplWl1hVVhfTU1bSEhVRUFUREBR Rj5MQDlMPTNURTtcTE1dTU5aSD9XRj1VRD1XRj9cTEpfT05eT0hoWFFrXWJuX2ReWFRQSkZTRDxU RT1TRz5YTURaTk9jV1hfWldaVFFTTkNKRjtFPz1QSkhbVFZnX2JjW2JjW2JjVVddT1FaU1NlXl5p YmJiW1tbT0dcUEhdU1FcUVBVTk5WT09PQ0BRRUNUQ0ZXRklTTlFXU1ZdVlZdVlZhXVVTT0dNSUFT T0dfXV5kYmNpXFdUR0NRREheUFVuX2RpW19dUUlXTERcSkVWRT9NPDNOPTRTSUBWTURWT0VUTUNX TE1cUFFkWmJqX2hvYWNtXmFcV0lVUENYST9VRjxPPTROPDNRSDxbUUVkXFhnXltfV1FRSURNRDhQ RztYUE1cVFBUTDxMRDRFPjRKRDpWTEZeVE5aVE9RTEdMRj9HQTtKQDtQRkBQRz5JQDhQQDFPPzBK OjFMOzJIOC9JOTBHPS9JPzFPPTtTQD5XR0ZbSklWSUdTRkRURENVRURRR0ZdU1FoXGJjV11eTE5Y RkhTRz5XTENaT0xfVVFdVVRcVFNbUE1cUU5hVF5iVV9eVVhbUVVdSkpYRkZUSEBWSkNaTkNaTkNf V1ZfV1ZaTUhTRkFTRD1VRj9dVFdpX2NqWFxlVFdoVVdpVlhiWlRfV1FlVE1lVE1qUU5kTEhdSDtX QzVRQDpTQTtTQTtUQzxURENaSUhaTU1dUFBkTUxjTEpeSjlcSDdhTUplUU9qVlhrV1pqVlRqVlRp UVBnT05hTk5eTExdT0FWSDtiSUNnTkdpUElnTkdcSkVeTUdhUE9lVVRtVlBtVlBpU01hSkVkQzVp RzpjRzxkSD1hSEFiSUNcQ0dfRkpkR0RnSUZiTk5lUVFdTD1VRDVWPjhXPzlhSEVrU09OPS5MOyxJ OTBGNS1HNTVKOTlQPkBVQ0VXR0RbSkdcSkVdTEZhUE1pWFVpXV5rX2FqWltlVVZXT05YUE9eTk1f T05eTk1jU1FeVVZeVVZcUEhWSkNRRj5QRT1MRD5HPzpMPTVOPzhQQ0dWSE1dTkdWR0BWRD1UQTtX SU5cTlNXT05dVVRrWl1wXmJcWlhPTUxRQzlNPjRKPS5URjdYTUVfVExeWFZXUU9WSj9PRDlGPDlN Qz9UTVFfWF1hWlxjXF5dVlhWT1FYVFVkX2FjYWJYVldaTkZaTkZaTU1bTk5QTEpOSUhKPjpHOzdN RDtNRDtQSkhWUE5eVlViWlhhWlpcVVVTTExXUFBiX2NiX2NlVVZTQ0RWSlBiVlxtXmNnWF1fT0Nc TD9fTkFeTUBRQDhOPTRUSkFaUEdbT0ZXTENTSkdaUU5jXl1nYmFtY2RuZGVfW01cV0leU0lUSD9R PzdWRDtbVVNiXFpkX15hXFtdTkZURT1RQzlWRz1dU01cUUxQSDVHPy1KPzdRRj1USURfVU9cU0lV TENJRD1BPDVJOzRQQTtRQDhUQzpYRztRQDRMOSRKOCNDNyJBNSFEPCpIQC5QQThTRDpTRD1RQzxN Q0FNQ0FQPj5WRERYTVBiVlplVFpiUFZcSkVYR0FcTUZiU0xiVVNkV1VhVFFbTkxTSEVUSUZUTU1W T09cUFRaTlFXSkhWSUdYRztcSj5YTEdbTkleV1dfWFhhTUpXREFRQUBaSUhjV1tnW15nVVtoVlxl U1VnVFZpXFplWFZfU1NfU1NlUVRhTU9XQTxQOzVQPjlUQTxWRDtUQTlVQz1aR0FbT0dbT0diT0le TEZcRTlbRDhiT0hnVE1qV1prWFtrVFVtVVZqVE9iTEddSUlcSEhcSEhfTExnVFZuW11rXFFhUUdY R0FYR0FhTU9lUVRpU05oUU1iSj5eRztjRT1kRj5iRj1hRTxdSDtdSDtcQ0dbQUZfSD5jTEFjUFNj UFNhST9UPTNUOzdcQz5iTlBqVlhMPChHOCRKOi5HNytJNC9POjRKPTBNPzJWRDtbSD9dSkRhTkdh UFFnVldqXV1pXFxiVVBeUU1bTkxXSkhdTUxhUE9iT0ZkUUhcVFNbU1FRTUFNSD1PPjVQPzdMRTxE PTRKOjNNPDVPPkFWRUhcTEhcTEhYSUFVRj5WTVBdVFddVVRcVFNjV1tpXWFkXFhWTkpOPS9JOStO PS9RQDJYTEljVlReWFRYU05fTkVVRDtNPDBUQzdUTEhdVVFiW11iW11dVlhVTlBaU1NkXV1nXl1e VlVbT0ZbT0ZhUE9bSklPR0FMRD5KPDJMPTNOQDFQQzNTRz9YTUVaUU5eVlNeV1dcVVVUTklbVVBj WltjWltjU09UREBaTUpkV1VuWlxlUVRlVE1lVE1kVUpjVElXSEFVRj9aTkVdUUhYT0NTST1TRkFa TUhhWFdrY2JvY2lwZGptYVdjV05bUUhRSD9VRD5hT0llXF9iWFxcWFVUUE1URTtTRDpQRz1YT0Vk WFBcUEhOQDFFOClRQzxVRj9QSkhcVlReUU9YTElGPjs9NTJFOjFHPDNTQTlcSkFbSTtTQTNOPydJ OyNDNRxDNRxNPS9XRzleTUBdTD9VRD1PPjhOPj1URENQQ0VURkhWTVBdVFdfU1BdUE5iTUFnUUZo VVNqV1VtWF1nU1djUE5aR0VQRD9QRD9ORkVTSklYSk1XSUxWTEpWTEpcSkVeTUdYTVBbT1NiVlpj V1tpUVBiSklUREBRQT5eSkpjT09iVVViVVVjUFBnVFRrV1dnU1NeTVBiUFRjUUxdTEZWPjhPODFR OzFWPzVaRDxYQztWRD5bSENbTkxdUE5iUU5hUE1dR0NfSUVqV1VvXFpyXlxuW1htU1NqUFBkTVBn T1NoTlBkSk1hTU9kUFNlV1poWlxpX1VcU0hUSD1TRzxeSERiTEdhST9lTkRoTTtpTjxiSEFfRj9h ST9hST9eRztdRjpeRUFfRkNeSEBdRz9fTExiTk5eSEBVPzhTPTleSERoVFFqVlRQQCpMPCZNOilH NCRJNC1OOTFIOSpNPS5TQzNdTT1dSkVfTUdWTk1YUE9iVVVnWlphV05aUEdUREBTQz9TSkdVTUlb T0ZiVk1iUU5hUE1bTERRQztXRjpXRjpVRjxTRDpRQzlPQDdRQT5WRkNWTkhaUUxdTkdeT0haU1Vf WFtnW1xqXl9rXWJrXWJhWlBWT0ZRQTNKOy1PPCtRPi1OSEReWFRoW1hhVFFQSkREPjhHPDNKPzdN SUFTT0deV1dcVVVbU01aUUxcU0llXFNpXFdhVE9bUUVYT0NdVU9XT0lUSD9TRz5MPi5RRDNURDhY SDxcTEhfT0xeVlNhWFVdVFVYT1BWSkFcUEdhVFFjVlRfTkhWRT9QTUdaVlBiW1tdVlZjVlRtX11q XVtpXFpbTklaTUhdTkZfUEhaUERPRjpMRzxPSj9iWFxtY2dvZWtyaG5uZWJoX1xnV1BRQzxYRUxp VVxkW2FhV11VT0hWUElXTTxYTj1cTEhnVlNuW1VhTkhQQCxQQCxTRkFbTkldU09dU09kUFNhTU9I QDtAOTNGNytKOy9TSUBYT0ZbUEFWTD1TSTRUSjVVRTBQQCxbSUNlVE1hU0VcTkBUQzpUQzpXRkBb SURYSElXR0hUSkxXTk9YTkpbUE1jV09vY1tuX2JoWlxfVFVaTk9aTUhRRUBUQz1RQDtRQT5TQz9T R0hWSkxXSU5YSk9YTElaTUpcUFFbT1BjVlRoW1hqWlhkVFNWSkNQRT1WRT9YR0FbT0deU0pjT1Fn U1VnU1BkUE5hTU1hTU1fTkFXRjpVPjRYQTheRT5fRj9aTkVWSkFWRz9TRDxVSERXSkZcUEhcUEhc SUdlU1BqXF5qXF5nXFZkWlRlUU9nU1BfTU1kUVFiT0hjUEllTE5tU1VwXV1yXl5yX1ZhT0ZRRzVM QTBRPzpaR0FlTUhuVVBtVUhqU0ZjSUVdRD9hSEFjSkRaSDxYRztWRD1YRj9bRDpaQzlbSURcSkVb SD9RPzdUPjpdR0NlU0xlU0xbSTpQPzBRPitOOyhKOy9JOi5IPzNJQDRURT1dTkZeT0hdTkdWSkNY TUVjUFNnVFZfVVRXTUxYQz1XQTxTRDxURT1cSUdkUU9qVlhpVVdeUU1XSkZbSUBYRz5YT0VdVElf TUZaR0BXRj1cSkFaSUZbSkdaUUxbU01dVFdjWl1kXWJoYWVvY2dtYWRoW1ZWSUVOQDNJPC9RQy5W RzJMTURTVEpYV1RbWlZNSklDQD9HPjVKQTlKQz1PR0FcU1ZcU1ZfU05aTUhXVFBaVlNjWlBeVUxe VUxdVEpcVFBbU09aSkBTRDpNRDhTST1XRj9fTkdeVlNdVVFbVFRbVFRaT0xTSEVRSD9XTkVcUFFc UFFbTEVWR0BUSkBdVElhV1hhV1heVVtpX2VpW19kVltRTElTTUpXUUFbVUVXVUhKSDxJQzlRSkBa V1tlY2doYWNqY2VvYWVtXmNiVVVRRUVbR05kUFdfWF1bVFhVTUdYUEpdUUhfVEpdU09kWlZoV1Zd TUxVST5aTkNfVU9hVlBaT0lYTkhdT1ReUFVQST9GPzVFOClHOitKQz9WTkpfVExdUUlWUURXU0Vb U0BYUD5dUE5jVlRjU0ZYSDxVRTdYSDphTkViT0ZaTkZXTERRT0RTUEVdU1FlW1pqYWRvZWlnYmFh XFtaUU5XT0xYTEdVSERTQDtQPjlRQDpPPjhPQDpWR0BbSklcTEpYTk1aT05XTk9bUVNcVFBeVlNk WFpeU1RbTEVWR0BTQTlRQDhYRj9eTEViTlBfTE5lTUllTUlbSEhaR0dbSEFbSEFaSDxeTUBnT05l Tk1cUEhWSkNYSj1TRThVRj5YSUFbTERbTERcUVBhVlVjW1pkXFtiXVNdWE5cVFBcVFBfT0xiUU5i T0ZhTkVcSE9lUVhpWmNvX2l0XVdjTUdUQC1OOyhKPDVTRD1hTkxoVVNiUUNdTT5bQzxWPjhYQztY QztYQztYQztaQTtbQzxXQTpXQTpeRkFhSEReSTxWQTRTQDtaR0FeTUZeTUZaSkBVRjxYRTNUQC9O PjBMPC5JQDhKQTlUSUZdU09jU09iUU5aSkNYSUFeTUdiUEpiUU5eTkpfTExVQUFRQztURT1aR0Fk UUxqVltrV1xiVldeU1RfT05hUE9eWFZjXVtiU0xbTEVbT0ZcUEdbT0RWSj9UUEhUUEhYT1VcU1he Xl5iYmJoYmpkXmdjWFVWTEhPQTFOQDBRRDRVRzhRTT9XU0VbVkxYVElOST9FQDdHOTJJOzRMPTdU RT5YTk1aT05bUEpXTUdVT01YU1BiV1FhVlBlVk9oWFFjWFVeVFBTRkRQREFQST9UTUNaTkVhVUxi WlRcVE5dVVRaUVBYTEdTRkFUSkBaUEZYVFNWUVBXT0lQSENVTD9eVUhlW1doXVpiXV5pZGViVVNY TElOST5TTkNcVUxeV05YU0xWUElPSUNQSkRWVFddW15iW11rZGdwYmdpW19bT1BVSUpaRkpeSk9c VVVcVVVcU0ldVEpcVUpcVUpXT05cVFNcVlFbVVBeU1RjV1hkXFteVlVTTENVTkVeU1ZdUVVUTkdQ SkRIRTRFQTFORkVaUVBfV1FeVlBfWlNeWFFeWFZbVVNdV1VcVlRcU0ZPRjpURTtdTkRkU0xfTkdX TEBRRjtRSD9USkFaT1dlW2NvZWlqYWRlWFRhVE9fUEhbTERbTERXSEBUQzpRQDhNPDBNPDBTRTha TD5eTURcSkFcTEpcTEpcT09dUFBcUU5cUU5fVFdaTlFYSEVTQz9NPzJMPjFQPkBbSEpfT0xhUE1f TUdcSURcRj5cRj5XRj9bSUNdTUliUU5qV1doVVViT09cSUlcSUNYRj9XSEFbTEVdTUleTkpfU1Nf U1NkWlhnXFtfWlNdV1BaUVBaUVBhTlBjUFNiT0lhTkhhTFBnUVZkWmdvZHJtXF1eTk9RRCtKPSVK OTJWRD1iUU5kVFBiUUNcTD1dRjxXQDdbRDhYQTVbQz5bQz5bSEFYRj9aRThaRThdRUFfR0RXSDVT RDFQPzNYRztdSkFfTURaSkNWRz9VRDhTQTVQPzdOPTRJPC1MPi9TRz5fVEprWFhnVFRaTUhTRkFX SkhaTUpaT0laT0lcT01YTElYSUNVRj9YSEVkVFBnXWNlXGJkWlhiV1ZjU09kVFBhWFdjW1pkW1Bj Wk9iWlZeVlNdVElWTUNNSj5NSj5PTEZPTEZWV1BdXlddYmNbX2FiVVVaTU1RQTNVRTdUSTtYTj9d U0FeVENYVEhWUUZRSDxEOy9IOSdKOylMPz1QREFXSkpcT09YVElVUEZOTEBWVEhfV1RfV1RoW1ht X11lXl5cVVVRSkFIQTlUSUReVE5jVlRjVlRlW1djWFVhWFdYUE9XTERVSUFaUERjWk1iWFpdVFVd TUlYSEVWTURiWE9lYWRqZWllYV9bVlVaTDxVRzhKSDxUUUVhW1RhW1RdVVRdVVRXSD5VRjxWTU5Y T1BeWltoY2RpZ2heXF1dUExTRkFTRD1bTEVXUEZbVElkVFBiUU5eU0lcUEdTSklVTUxbUE9fVVRj V1hlWltdV1BVT0hTSUBWTUReTEleTElUTEZWTkhRTEdQSkZWTU5fVldiVVNhVFFjV09kWFBkWlRe VE5dVlZbVFRaU0lKRDtUSkFcU0lhVU1aTkZWRTxTQTlRPzpVQz1UUVNeXF1pX2NnXWFkVFNiUVBa T0lbUEpcT0pXSkZVST5QRTpTQTJWRTVeUU9fU1BjUFBbSEhYRUVYRUVXRUNYRkRXTERXTERXTUdV SkVaSkNURT1NQTdNQTdPQ0BVSEZhTUpeSkhhSkZeSERbRjhbRjhUSD9YTURjV05qXlVnVFFjUE5f TUpdSkhfTElbR0VYQz1aRD5aR0BeTEVhTU1lUVFnVlVnVlViVk1fVEpdTUxaSUheSk1fTE5eTEZh TkhlT0pqVE9pV11yX2VqX15eVFNcTDRURC1VPTdbQzxeTEZhTkhlTz5kTj1jSUNlTEVoU0VeSTxc Rj5dRz9bSD9aRz5bRjtaRTpWRDtXRTxTPS1RPCxROi5XPzNdSD1hTEBaSkNaSkNXRzlTQzRTQDpP PTdPOy5POy5TRThdT0FpV1tpV1thVE9XSkZVSkVWTEZXTE1YTU5cSkRdTEVeTk1cTEpdT1FjVVdj XF5jXF5jV1hkWFplVVRnVlVoV1hpWFpjWFVlW1dkWFxkWFxfVVRaT05USkFPRj1ORTxRSD9UUEhc WFBfXV5fXV5nVldbSkxYRTNdSThhU0VlV0lhXlNfXVFaVk5WU0pTTDFHQCdUPSlaQy5QSDlRSTpY TUVcUEhaUU5cVFBUTURXUEdfXFhkYV1nZGNnZGNnXltcVFBPSTpIQzNWSUdkV1VkWFpqXl9hXFti XVxfVVRYTk1YTUVbT0dfV1ZlXVxpXl1lW1phU1dbTVFVTE1dVFVpY2ltZ21oYWFdVlZcSkFUQzpK RjtUT0RdXFhhX1xhWlxYUVRXRj1VRDtRRUVYTExdWl9lYmhoZG1dWmJXTkVORTxPRj1XTkVfVEpl WlBrWlRpV1FiVVBYTEdTRDpYST9eUU9iVVNkWFphVVZeU0lVSUBWSDtYSj1hT0lhT0laTk9YTU5W T1FWT1FeU1RkWFpfVU9hVlBkXVRlXlVoVVNiT01bT1BfVFVaUUxVTUdVUUlYVU1eVFNaT05TRkRT RkRXRTxVQzpeVFNnXFtpYmJlXl5hVFFaTUpXVE5cWFNYUUdVTkRXTkFXTkFcSUdiT01kW2FiWF5k VFBeTkpXRT5VQzxVRj9XSEFVSkVVSkVYTEdXSkZcSUNXRT5VRjxRQzlRQzlYST9oU0doU0dkU0Zd TD9TRC9URTBRSj5bVEdkXVRkXVRpV1BjUUpdTUlhUE1dSkVXRT9aQThbQzlcSEZfTElhTUpkUE5j VE1jVE1iUEpiUEpfTUdbSENcTEpcTEpeTExkUVFoVU9uW1VtW150YmV1Y2ltW2FrVkddSDpUPTBV PjFXR0RkVFBuW1R3Y1x0YV5wXVt0XE5pUURfSURbRT9bRT9cRkBWQDtXQTxYRDlUPzRPNSVTOShU OitbQDFdRz9eSEBkTkZiTERcSjxVRDVaQzlWPzVQOydOOSVTQTJdTDxnWldoW1hpWlFhUUlWSkFR Rj1VSERaTUhhT0ZhT0ZdTU5dTU5YT1BdVFVlWltlWlthVFRhVFRjU1FlVVRnVlVnVlVjWFVjWFVn VVhpV1tiVVVdUFBYRkhWREZOSkdNSUZOSkdaVlNfXV5jYWJkV1NeUU1fT0BkVEVnVlNpWFVqX15l W1pbVElbVElcTj1dTz5qU0FqU0FdU0FcUUBbUUhbUUhfVFVhVVZbT0ZcUEdeXVdpaGJpaWdnZ2Rn WlVjVlFWTUNPRjxaSExtW15qYmlpYWheYV9aXFtfVVFaT0xdTkdlVk9kW1xoXl9jXl1cV1ZdVFdY T1NUT1BbVldrZGltZWppXV5hVVZaTUhVSERJRzxUUUZfW1piXVxfXltWVVFVTD9NRDhORTtbUUdf W15lYWRkXGNcVFtVTUxIQD9JQzpQSUBeVlBqYlxtX1tqXVhhWlBXUEdTQzNXRzhdV1VfWldnXFtk WlhiVERYSjtWTUBcU0ZlVE5oVlBcUVBaT05aTE5dT1FdVFVhV1hhXVdhXVdfXlhfXlhiT0lcSURe TExiT09fT05nVlVnXVNnXVNhVFFcT01VUEZUT0VYST9XSD5lV1xrXWJtYWJlWltbU1FXT05cV1Zf W1pdVklXUERWT0NVTkFcT09kV1djXGFiW19fVVFbUE1XRztWRjpXRTxYRj1VRUFUREBVRURbSklj UElcSUNaRThVQDNQRkBdU01oW1ZrXlpiWlRbU01XRzlURDVWTEhiV1RlW1pkWlhoUUliTEReTkpi UU5fSj9VQDVaQTVeRjphTkhkUUxiT0hhTkdcT01iVVNnU1NkUFBcSURaR0FcTEhiUU5hUE9nVlVq V1FrWFNtWFtwXF5zXmNvW19qWE9dTENaPy5hRjRkUVF1YmJ1ZGV1ZGV3Y2F1Yl9zYlNnVkdeTEZW RD5WQDlVPzhXOTRUNTFWOCpTNCdFMB9QOylVPjFXQDNfSDxfSDxiUUVhUERjTD9iSj5jTj9aRTdU RC1PPylaSDpfTj9nWldlWFZoWFBoWFBcTUNVRjxaTD5cTkBjU09jU09dUVVcUFRWUExcVlFjW1dh WFViVVBeUU1iU0xiU0xjVlRlWFZlWlFhVU1lWFZlWFZiVk5cUEhVSkdQRkNHR0dFRUVERkVRVFNe WltkX2FkXFZjW1VoXVxqX15tXmFqXF5vXVdrWlRlWlBoXFNfV1ZjW1pyYV1yYV1pXltlW1djWFNe VE5fWFtdVlhUTEZbU01pXl11amlpaWtkZGdYVU9bV1FUVEhKSj9USk5jWl1rYmhpX2VeXl5YWFhV UE9VUE9eVE5kWlRkXV1nX19eW1dXVFBdWFdUT05VUE9cV1ZpYmRoYWNhV1hVTE1QSkZWUExRR0FR R0FUU01cW1VfXFZbV1FeT0dVRj5QRkBbUEpiVFhlV1xjVVddT1FcTEpOPj1JPz5USUhjXFxqY2Nk YmFcWlhhVlBWTEZVRj5YSUFcWlhiX15nXV5kW1xfVEhjV0xiV1RqX1xkY19jYl5eWFFQSkRVRUFc TEhXT05eVlVlV1xrXWJvZGNnXFtcTD1YSDpcSURhTkhdU01kWlRoYl1nYVxnWlpfU1NaVE1YU0xY T0ZcU0loWl5qXGFhXFtcV1ZaT0lbUEpeV1dhWlpeVlBWTkhYTkhaT0liUFZhT1VdVFViWFpeVFNY Tk1YTj9VSjxeSEBeSEBVQz1UQTxVSEZbTkxnT05jTEpWRjpURDhaTFBlV1xtZWhtZWhtX19kV1dY TURUSD9eUFNnWFtuW11qV1pkUUxjUEpkUVFjUFBdTD9aSDxfSj9jTkNqVE9qVE9oT0NjSj5YR0pc Sk5cT01eUU9dTEVbSUNdSUdhTUpdUExjVlFkWFBnW1NqV1drWFhqWFxqWFxkV1VeUU9pUUdyWk9v Y2R0aGlzYl5yYV1tYlxuY11uXlRdTkRdRD9YPztNOilQPSxQOihMNSRIMh9JMyBTPCxbRDNeSDhd RzdkT0BoU0RfTkViUEdjUUVhT0NlVEdfTkFbTT9XSTxeT0hhUUpiVVNlWFZlXlRoYVZnWEpfUURi Sj5kTUBiUFRpV1teWltcV1hcVUxeV05iWlRiWlRdVVRXT05YTkheVE5dVU9fV1FeV05bVEphV01i WE5nV01fUEZYTkhUSURJSENHRkBOQ0ZWSk5fVFVlWltnXV5qYWJtY2dpX2NpX2FlXF1nXVBnXVBr Xl5tX19nX2RqY2hpYmdqY2hnX2RjXGFiVlpdUVVeVFxfVV1aT05hVlVnWF1zZGlrZ2pjXmJcUU5d U09YUEpUTEZWT1FjXF5qYmlkXGNcV1hXU1RWT1FWT1FaVFFfWldhWlpiW1taVE9YU05aUFFXTk9V UU5cWFVlXl5jXFxdV1NRTEdPR0FUTEZOQztPRDxUTkdeWFFeV05dVk1OSkNOSkNQTUlTT0xjT1Rl UVZfVU9WTEZNRUFHPzxGPDtPRURaUVhkXGNlXF9fVlpcUFFXTE1TSklRSUhfW15qZWllXmFdVlhh VlNjWFVeV1ptZWhyaWVqYl5hT0ZRQDhOQT1QRD9TSEdVSkldVV5nXmhqYWJeVVZaUERUSj5WRz9b TERdUExjVlFhYWFhYWFkWF5eU1hdUFBdUFBdSkhfTUpjVlZjVlZeVE5dU01dUUldUUlcVVddVlhd UUlYTUVaT0lbUEpeUVFeUVFeUU1iVVBYUE1WTkpcTUNfUEZjUUxcSkVTRz5QRTxVR0ldT1FiUU5f T0xaSEFVRD1USk5lXF9vaGpqY2VpXltcUU5VRj5YSUFkVFBtXFhrWFZoVVNhTk5eTExkUE5iTkxi SkBhST9eSkhiTkxqU1ZqU1ZkTEhiSUZdSUdeSkheTEleTElbTEVeT0hiTEZhSkVeT0dnV09nV09l Vk5nVE5pVlBnVVhnVVhjVVdjVVdvXFxzX19zYmNwX2FuXFVuXFVuY11tYlxrXU9cTkBYQTRTPC9K NCNNNyVOOCRPOSVONypTOy5dRTtkTEFlUEVoU0doVU9kUUxiTUFkT0RkTEVlTUZjU09hUE1hVE9b TkldUE5fU1BdT1RjVVpkXFhrY19tXFhkVFBkVUpjVEllVFdtW15pXFxjVlZiVVBiVVBjWl9lXGJk VlhYSk1YTEdaTUhcU0heVUpeVlBdVU9iVk5jV09oVVVoVVViVk5eU0pUT0VPSkBTSEdVSklYVVFh XVpjXmJlYWRtYWdoXGJoXVplW1dkXFhpYV1qYWJpX2FoXWVpXmdlYmhkYWdoYWNhWlxaVVZXU1Rk VV5kVV5fVlpeVVhlWl1wZGhpZ2heXF1eT0haSkRaTkZaTkZYU05jXVhnY2lfXGJfUVRaTE5YTElb TkxbVVBaVE9hV1hhV1heVVZdVFVWT09cVVVWUVVaVVhjWl1lXF9eWk9QTEFOSTxOSTxOQzpQRTxX TUdiV1FhVlNcUU5MRkRPSUdUSUhRR0ZeTk9jU1ReUD9URjVOQzhJPjNNPTxQQD9WSk5fVFdiVVNf U1BYRkRcSUdRSkpVTk5fW1xpZGVqXF5hU1VaUVBdVVRfWFtwaWtwa2FnYldcSDRTPyxIQDFMRDRM QTxNQz1UUFhjX2hkW15aUFRYTURVSUBRRjtVST5eTExoVVVoXmJhV1teUFNbTU9YTElXSkhaR0dc SUldUUlfVExfVVFdU09bUE9hVlViWFxhV1teTExbSEhdTUxiUVBdTkdeT0hhUE1eTkpdT0FdT0Ff TkVjUUhiVVBfU05aT0xWTEhcSUxeTE5hVE9jVlFdT0FaTD5YUE9kXFtwZGhtYWRrWFZdSkhVRj5c TUVnVlVtXFtoV1hjU1RaTkZXTERfTEliTkxiSUVeRkFdRkVjTEpnUVtnUVtlUVFeSkpiTERfSUFc SUddSkhdUFBdUFBfTEliTkxfTU1iT09jUEpjUEphTkhiT0ljUE5jUE5oVVdrWFtyXV90X2JvW1hr V1VuVlVtVVRwWlRrVU9oU0VbRjlXQTpRPDRQNyZWPCtaQzJbRDNcRTtdRjxhSkZkTklrVFdzW15y V1xvVVpfSUFfSUFfTUdhTkhcT09cT09eTk9XR0hYSEdXR0ZUTEpaUVBjV1htYWJuX2JnWFtnXFhq X1xnXWNoXmRnWF1jVVpiVFZkVlhkW2FpX2VqV1VeTEleTURaSD9WSkFcUEddUUhfVEplWFRlWFRp WFpoV1hiWlheVlVXT0xUTEhYTk1XTUxUU0pXVk5jW1pjW1plWFZkV1ViV1RiV1RhVlVoXVxoYWFf WFhhVVheU1ZjV1thVVhfVlpbUVVUUVNTUFFeU1hlWl9fV2FdVV5jX2hnY2tuaW1lYWRcUEhTRz9d UE5hVFFjWlBnXVRpYmdkXWJjVVdbTU9YTk1XTUxcU1RdVFVqX15pXl1kXV9bVFZWUE5WUE5WUE5b VVNlXF1lXF1oW1hXSkhUSkFORTxPRjxaUEZiT01lU1BfVFdbT1NRQUNTQ0RQREROQUFYTUVdUUld TjlVRjFURDRPPzBJOzNKPDRVSkVYTkheUU1aTUhXRkBXRkBVSUBYTURbVFRpYmJlXFNbUUhXTUdd U01eXF9raW1waWlkXV1YRDlVQDVQPzNTQTVQPjhUQTtXU1ZhXF9jWFdbUE9RSD9ORTxVRDtcSkFi UEpnVU9kV1VfU1BXTERVSUFTRkFVSERXTERXTERaTUpeUU9iV1FhVlBiV1RkWlZpV1tpV1tjU09e TkpfU1BfU1BcTUVcTUViUEpeTUdeTENfTURlVVRpWFdjWFVjWFVeVlNXT0xaSkRbTEVdV1BdV1Bd TkdYSUNcUFRhVVhqXV1qXV1nVU5eTUZWSUVhVE9uX2JqXF5oV1ZiUVBYTURYTUReTEVhTkdkT0Rd SD1cRURjTEpiTlNjT1RhT0ZeTURiSUNhSEFaR0FfTUdkVU5iU0xhTkheTEZdTEZcSkVeTURfTkVh UE9hUE9jT09iTk5nT1BqU1RvVFRyVlZrUVFrUVFtU1NqUFBoT0VqUUdnUUZiTUFfRztdRTlfQzRi RTdfRzpiSTxjSkZiSUVeSk1lUVRtVVh7Y2d7YV1wVlNfTkhdTEZYRj1bSD9bTkxaTUpdSkpWRERU RT5WR0BVTUxWTk1iVFhpW19uYmhtYWdlXVxnXl1jW2RhWGJhVVheU1ZbU01fV1FnXV5qYWJrXFRj VExhTkVcSUBYSUNXSEFaSEFeTUZoVVduW11nW2FkWF5cVVVYUVFaTkZWSkNbTEVbTEVbVkxeWk9o XVxhVlVfU1NfU1NdU01eVE5kVltpW19qXF5cTlBeTkpeTkpfUVRhU1VbUE1TSEVTTUhTTUhXUU9d V1VcU1RbUVNiW11jXF5qYWJlXF1hV01YT0VhVE9kV1NnYldnYldpYV1rY19kXlxeWFZRT05OTEpY TVNoXGJrYmVuZGhiXVxYVFNUUEpVUUxdUExkV1NqYlxoX1poW1ZeUU1cTD9QQDRPSDxYUUVkVFBk VFBfU1BbTkxVTENORTxPPT9RP0FWSkFdUUhnU0BkUD5iUEFcSjxKQTVJQDRWTD1aT0BbUUdXTkRX TERTRz9WSDtWSDtYTVBkWFxhWFVXT0xaSkNdTkZkYWloZG1oY2JeWlhbSTtaSDpaQTRaQTRUQzRW RTdbTU9iVFZfVExXTERQQzVTRThcTUNjVElpV1FoVlBeVlBcVE5WT0VTTEFWRz9WRz9USEBWSkNf U1BoW1htX11rXlxjWltiWFppV1tqWFxqXlVjV05fVk1eVUxcSkRbSUNdTUxcTEpfTElnU1BqWltr W1xiV1RfVVFdUUlaTkZUSj5WTUBdV1BbVU5aUEZVTEFaSE5kU1hlXVplXVppVlRhTkxXT05eVlVl XF1lXF1nV09hUUlYTUVYTUVjT09lUVFnU1BiTkxiSklkTUxjUEpfTUdfTz9iUUFkTkZiTEReTEVj UElkU01jUUxiUEddTENaSEFdTEVfU05lWFRkW1FaUEdhSEVhSEVdSkVhTkhrUUprUUpnTUhnTUhr TEdtTUhoTEBtUEVuVUpuVUptUERvU0ZtVUdqU0VjTkNlUEVjSkdhSEVhTVFlUVZqVVpuWF1yVFBn SUZfU05XSkZVQzpXRTxbSURaSENWSj9USD1TQTxUQz1QSUlVTk5eV1plXmFpX2FqYWJkYmFlY2Jl XWlfV2NjVlRhVFFdVU9fV1FjXFxnX19tXFtoV1ZjWFNdU01aTkZVSUFXRT9dSkVdT1FkVlhiW1tb VFRUTkxUTkxaT0xaT0xiTkxnU1BkWlZlW1dkXV1bVFRWTk1XT05bT0deU0pjV1hpXV5lXVpYUE1Y TEdbTklYUEpXT0lUTURQSUBUSEBVSUFTTUpXUU9aUU5XT0xbVFRdVlZoW1hrXlxoXVpkWlZjW1pk XFtiWlhkXFtkXFhoX1xjZF9iY15XUEZRSkBRSExfVlptZWpwaW5kYVtcWFNWUUdTTkRlVVRqWlhu ZWJtZGFqYl5kXFhhT0hUQzxUTEZfV1FjW1dkXFhhVlNcUU5aVUlNSD1VRD1PPjhVTUdcVE5rX1Rq XlNnXVNfVkxTTT1QSjteU0ljV05fVkxaUEZTTUZQSkRVRzhXSTpUTEpcVFNdV1VbVVNbTklfU05q ZWdlYWJqXVhjVlFiUUNiUUNjUUFiUEBYSjpTRTRcSUdhTkxaTkZRRj5VRD1dTEVoXl9rYmNpX1Zo XlViWlZhWFVdWE5YVElbTERWRz9USkBWTUNlXF9vZWlvZWttY2loXF1hVVZhWF9nXmVtZGFlXVpk VU1fUEhfTURdSkFWTURbUUhdU09lW1dpXFxqXV1lWFRdUExdTkdYSUNTSUBaUEdfVVRdU1FcTUZY SUNcTE1nVldoYl9kXlxkUU9dSkhYSk9eUFVeVVheVVhfUEZeT0VdTEVeTUZlUVFjT09fTUdhTkhk SkZlTEdnUEhpU0pqW1NpWlFvV1ZrVFNkUE5iTkxfT05jU1FkVUpeT0VeTUZnVU5pV11uXGJpXl1f VVRfTkVcSkFhSEFkTEVlUEVoU0dkTTxlTj1tTkNzVEhuVkxyWk96X1iAZV5+Y197YV10YVduW1Fp VlBiT0liSUZeRkNdSUxeSk1jSU5kSk9lTT9hSDtcUVBWTEpYRDlWQTdWRkVWRkVPSD9NRj1RQzxR QzxQRkBUSURRT05dW1pjXl9oY2RjYWJkYmNiWFxcU1ZeVFBfVVFcVFBdVVFiW1tkXV1vXl1wX15p YV1iWlZdVEpWTURbTERhUUlYU0xeWFFbWlRVVE5TSkdPR0RRTUxXU1FkV1dqXV1nX2JpYmRnY19d WlZTT0lOSkVTTUpYU1BhWlxnX2JkW1xeVVZeUVFcT09WTkhRSURVSkVUSURVSEZWSUdXTUxWTEpX SkhYTElbUE9hVlVtWlxtWlxiW11nX2JkXV1iW1tiWlheVlVbVVNfWldbW11dXV9bVEpYUUhXUU9h W1hrZ2pqZWldXlpcXVhcWFNRTkhaVFFkXlxnZGhraW1uaW1iXWFnU1NkUFBYVVFkYV1oYWFnX19j W1pdVVRaU1NTTExbSklWRkVXVVhkYmVqaGlraWpraF9hXVVWU0FXVENlW1VlW1ViWlRXT0lRTkhU UEpeT0VhUUdVTUlbU09hV1tdVFdWU01YVU9dUVVkWFxjVlRkV1VoYVRoYVRvX1VpWk9XTzxTSjhd TkddTkdaT0xaT0xYTExnWlpuaG5rZWtkXlxeWFZeWFZiXFphW1ZbVVBaUUxNRT9USURdU01lY2dt am5vZ25oX2djV11iVlxjXWVlX2htY2RpX2FuVlVrVFNjUUxcSkVUSElYTU5jVVxrXWRuYmNoXF1h VU1dUUleTUdaSENdR0FlT0loVFZoVFZjTEphSUhcUFFlWltnXl1hWFdpUElkTEViUFZoVlxlWFhd UFBeTENcSUBaST1eTkFhT0lfTkhjSkZjSkZjUEdqV05uXldtXVZwXmJuXF93XmJtVVhpUE1pUE1i TkxlUU9kVU5kVU5kU0llVEptXF1rW1xlWFRhVE9WTURUSkFdTEZhT0lkU0xlVE1rU0x1XFV/YVuE ZV+EamWEamV+amR9aWN7Z2l4Y2VyXlxtWldtU05jSUVdRTlYQDRaQT1cRD9eRUBjSUVkTTxhSTlk UE5eSkheST5YRDlYTElXSkhPRUFQRkNRQztQQTpURTtVRjxTTUpfWldnXGRlW2NjWl9nXWNkV1dh VFReTk9fT1BdU1FfVVRjWltnXV5vXWFyX2NpYVtkXFZjWFNhVlBlVEpoVk1bVU5cVk9dVlZWT09W TEpVSklTTk9XU1ReVVhlXF9pXmdpXmdlZGFhX1xTVEpISUBRRklYTVBYWFtfX2JfXV5YVlddVlZb VFRXUEdORz5MRzxKRjtQRkBTSENRTElRTElTTUhRTEdWTU5bUVNlVVRqWlhiWlhoX15kXV1cVVVW T0ZVTkVXU1RXU1RcVVphWl5eU1ReU1RfV1ZtZGNvaGprZGdkXFthWFdaV1ZPTUxaVFFjXVtoaGpp aWtnZGheXF9fWlVeWFRfWlVpY15oYWFlXl5dV1NcVlFWTVBXTlFYTkhYTkhfXV5raWprZ2hpZGVi XFpdV1VfU05iVVBnXFhpXltjW1pbU1FYTkheVE5eV01hWk9cUVBbUE9dUVVdUVVbVVBaVE9YTk1h VlVfWFhkXV1zZWNwY2FuZFtjWlBhUEReTkFdVElYT0VWSkxXTE1bT1BlWltnX2RlXmNoW1tjVlZc WltbWFphVlNfVVFbT0RVST5WTURkW1FyZWlyZWltY2dkW15iVFtjVVxcWGFfXGRrYmVrYmVyYWJq WltdUE5VSEZVREdcSk5jW2dqYm5wYmRpW11iUElfTkdfTURiT0ZeTUddTEZiT01jUE5kUU9nVFFj Wl1jWl1jW1ddVVFoVU5pVk9pVFhqVVpnU1VhTU9iSj5eRztcTD9hUEReT0hiU0xkUUxpVlBrW1py YV90Z2d0Z2d1YWh1YWh5YWJuVldtVEdtVEdoUU1pU05lVVFnVlNoVkdqWElrW1poV1ZnVU9hT0lU TEpVTUxUTEpUTEphUE1pWFVyXl53Y2N+ZWl+ZWl7aGV9aWd+ZWd+ZWd+aG14YmdzXFZrVU9lTkFe RztfRTViRzhlTUNlTUNfSDteRzpdRTldRTliT0hcSUNbSUBYRz5bTEVbTEVYRkZVQ0NRQDtTQTxO Rz1TTEFVTUxcVFNhU1dkVltjWltnXV5oW1hhVFFcUFRXTE9aTlFdUVVjVVdoWlxuX2JyY2VpW11o WlxpXltkWlZvXl1zYmFdW1pYVlVcVFNaUVBVTUxQSEdQSkZVT0pUTEphWFdkYmVlY2dkYmVfXWFX VExKRz9RQzxWR0BOTEpcWlhaX1pTWFNYVU9YVU9VTkVORz5PRDlPRDlKRDpKRDpORkNORkNMRUVK RERQSkhWUE5eVE5nXFZlXVdlXVdiVk1XTENXTT5aT0BbUVNbUVNfVldeVVZfU1teUVpcUFZvY2lu aW1oY2doXF1lWltfWFtcVVdeWl1fW15oZWlua29oYWFiW1tdVVRhWFdhWlxnX2JpX2NjWl1hVlVb UE9XTE1dUVNYU0xWUElfX11oaGVkYmNfXV5hWFNeVlBdU09kWlZyZWlzZ2pjYV9VU1FYU05fWlVi X2NkYmVeVFBbUE1YT0ZbUUhaVE1VT0hUSURWTEZbW1tiYmJwZGhvY2dnYVpdV1BeU0phVU1kWkpe VEVaR0VaR0VVSklfVVRnXFhkWlZnV1BiU0xjUVVfTlFbUUVXTkFbU0NiWkljW1dpYV1yZWltYWRi W11eV1pbVFZcVVddVlthWl5oXmJrYmVrY2JjW1peU0pWSkNUR0NaTUhiWmNrY21vXl9pWFpkVFBl VVFoV1RoV1RhUFFcTE1dUUlfVExlWFZoW1heV1dfWFhlWFhiVVVnWldpXFpnWFtlV1phVFFeUU9n TkdnTkdlVEdoVkllWFZjVlRjVlRpXFpuZGhwZ2p0X2lyXWdvW19vW19uXFNnVUxrVkprVkpqV1Fo VU9qV1VnVFFrVkpwW09pWFpqWltnVU9dTEZTSUBUSkFTST9aUEZjUVVwXmJ4ZGd3Y2V3Yl93Yl90 ZF11ZV55ZGd6ZWh5ZGdvW11tVlFpU05fSDteRzplTUNrU0hpVEhkT0RbSTtYRzldRjpeRztfUEhd TkZeTURdTENhUE1dTUlfTUdbSENWREFWREFNSD5RTUNUSkBXTkRXTERbT0diWE9tY1pqYl5jW1dh WlxbVFZYT1NYT1NbUVNiWFprX2FwZGVzXmVvW2JqYWJrYmN1aGhzZWVrX2NkWFxiVFZfUVRXT05P R0ZRSD9TSUBPR0FUTEZaU1VhWlxjW2RdVV5aTUhRRUBWSTNQRC5JRzxQTkNWVlRUVFFhT1NhT1Nf UEZcTUNTSDpPRTdQRz1PRjxQRTxNQTlKQUNKQUNRR0ZdU1FbWFdjYV9nZGhdW15eVUxVTENWTEhd U09fVlpfVlpdVlZdVlZeU1hfVFpcU1ZqYWRoZGphXWNfVlphV1tiW11dVlhbVldfW1xpZ2hoZWdn XV5eVVZaT0xaT0xaUU5hWFVoXl9iWFpjVlRbTkxbR0xdSU5TTExVTk5fX19paWlhYV5aWldfV1Rc VFBhWlpnX19wZ2huZGVfXVxVU1FVTk5kXV1tZ21qZGpeUU1YTEdYT0ZbUUhXU0hTTkRPSUNTTUZa V1tnZGhrYmNoXl9dVEpeVUxeWlhnYmFrXlpbTklTQTtTQTtVSkVcUUxjWFVlW1diWlZhWFViVVNc T01WVEhcWk5nY19pZWJrZ2hvamtrY2plXWReW1VXVE5XU0dcV0xiV1FjWFNjV1hrX2FwY2NtX19j XFNYUUhWTEZiV1FuYmVuYmVyZF9rXlppXltqX1xoXVxkWlhhVU1dUUlfVVRlW1poXl9lXF1fV1Re VlNkWE9nW1FpX2NrYmVlWFhfU1NjVlRkV1VoUE9qU1FtWlNyXlduYV5nWldkWlhrYV9uZGVtY2Rq V1plU1VkU1hpV11qXVtqXVtyXl5yXl5uWlxpVVdoVVNpVlRuW1VvXFZpW2JiVFtdTkdRQzxNQTpT Rz9eTENnVEptXFt0Y2J5ZGd0X2JuXVpuXVpwX1x1ZGF5ZGRyXV1pVlhnVFZrVU1tVk5fSUFiTERi TUFkT0RiUUNcTD1bST1YRzthTEBnUUZdUUleU0pjUEliT0hhUE1hUE1fT1BfT1BaSENaSENTSENV SkVUSUhXTUxYTU5cUFFoXVxuY2JqYWJnXV5iXVxbVlVcVFNbU1FWVFVeXF1qYWRuZGhzYmNwX2Fu YmNzZ2h0amtwZ2hzX19tWlppVlRhTkxbT0dYTUVRSD9RSD9QRztTST1WSkxeU1RhVVhfVFdcUEhY TUVVSUBUSD9NRjpKRDhRTEVTTUZcTEpfT05hU0ViVEZbUUhUSkFUR0VVSEZXR0ZVRURUSUZQRkNR R0ZeVFNiXGJqZGpoZGpcWF5VVFBWVVFeV1peV1pkWFxjV1taU1VbVFZfVFVfVFViVlxnW2FlYWRi XWFdVFVdVFVeV1pYUVRVVVVaWlplYmhkYWdeXF9UUVVPTkpRUE1cT0pfU05jWltjWltjW1pcVFNj T09iTk5PSkxaVVZiX2NnZGhdWlRXVE5aVFFcVlRiXVxlYV9rY2pkXGNdVlZYUVFbUE9lW1pvYmJu YWFfVEpbT0ZhVUlhVUlaUEdWTURPSj9TTkNfW1pkX15lXVdhWFNYTkhcUUxYVlpoZWlpYVtXT0lW SDhXSTlXTkFdVEdhXFtlYV9oYWFnX19hV1hcU1RaVlNhXVpqaGlraWpybXBybXBqZGppY2ljW1VY UEpbVEpjXFNlXlViW1FhWFNlXVdyX2N3ZGhtXmFfUVRcT01lWFZtXmFuX2JvZGNvZGNtYWJrX2Fn XFtiV1ZkVUpiU0hjWl1qYWRpX2VlXGJfW1BbVkxjW1dtZGF0aGttYWRkVFNfT05jVVdjVVdnVldr W1x1ZGV4Z2hyZ2VuY2JrYmNtY2RtY1piWE9fUElhUUpjVlZpXFxvXF5rWFtvWl5vWl5nVU9lVE5j UE5oVVNpV1tuXF9pW19iVFhcT0pVSERVRj9XSEFjT01lUU9tXFtuXVxwV1BrU0xuVU5yWFFtWlRv XFZuW1FpVk1lVVFoV1RrWFZnVFFdTTpeTjtlUENlUENiUUViUUVhUERjU0ZpWFVpWFVfT0xfT0xi UEliUElkVFBkVFBiUVNeTk9cU0lbUUhiT01hTkxeTkpfT0xaTlFdUVVtYWRvY2drYmVrYmVnYV5e WFZdV1BdV1BeV1pjXF5nX2JqY2VoXl9lXF1oYl9vaWdza25vaGprW1poV1ZjVlFcT0pcUUxdU01Q TEFRTUNaTUhbTklXTE1bT1BYTU5YTU5eVE5eVE5YU0xXUUpUSTtTSDpXRj1bSUBYT0NaUERaV0pe XE9cV1ZWUVBQSkhOSEZUSEBRRj5USD1TRzxVRj5fUEhhWF9nXmVeWlhWUVBPT01UVFFcVVdcVVde U1RdUVNVSkdXTUlaUVBbU1FdVFdhV1tiWFxoXmJcVVVYUVFUU01NTEZWU09VUU5eV1xkXWJhWlxX UFNNSklQTk1fT1BiUVNoWlxqXF5lX11hW1heUU9eUU9WTEpfVVRjXVtjXVtfU05XSkZTTk9YVFVe WlhkX15jWl1iWFxeWFZbVVNbUVViWFxwZGVuYmNdV1NcVlFpYV1jW1dhTkxcSUdXSEFaSkReW1Nj X1djXE9dVklVT0hVT0haV1ZkYmFpXVRcUEddTENjUUhnVU9qWFNiW19kXWJqX2hrYWljXF5dVlhb VVNfWldtZG5uZW90a3hzandvZG1uY2trW1dkVFBoWlxrXV9nYV5jXVtfWldoYl90am5zaW1uWldi TkxbT1NhVVhuYmV0aGtwYmRuX2JwX15qWlhjWFNeVE5jVE1oWFFrYmV0am5qXmJiVlpfVU9nXFZt Y2d0am51ZGVtXF1cUEhcUEhhVVhfVFdhV11rYmh1aW14a294a21yZWdyZ2VtYmFlXFNfVk1dUUlj V09uXVpvXltrXlxqXVttWF9oVFtiUEpkU01nVE5kUUxoVlpvXWFtWlxnVFZkTkZdRz9aSDpYRzlc SkVfTkhlU1BlU1BqUUptVE1vVU5zWFFuVU5tVE1qWExpV0puXVxtXFtwVlhrUVRjTkBjTkBnUURq VUdrUU1tU05rVFNyWlh1X2RzXWJfTU1dSkpdTkZfUEhiT01kUU9nU1NjT09bU09YUE1eUU9eUU9j U1FlVVRkVFVpWFpnX2JrZGdqZGpoYmhlXVphWFVeVlNfV1RhVFRoW1tkX2NnYmVlXF1jWltiXFpo Yl9tZ21uaG5pXltiV1RjWFdfVVRYT1BaUFFaU0lYUUhYUUhYUUhdTUlYSEVVRDhaSDxcUU5nXFhl XF1kW1xhUUleT0ddTENeTURfVkxbUUdYV1FeXVdfW1xXU1RORkBHPzpRQzlQQThTRz5XTENNR0BO SEFWTVNiWF5hVlBVSkVNTEhPTkpYUVRXUFNQRkBQRkBORTlQRztMRz1OST9UTEpYUE9eWlheWlhd VVFYUE1RSkFORz5USUZVSkdaU1NbVFRfWFhcVVVVTUlQSEVaTUpeUU9jW1dlXVpqXVtoW1hcUUxc UUxaT0lWTEZiVVNiVVNhUFFdTU5UTkxWUE5aUFFeVVZqWFNpV1FqW1RiU0xcVVdfWFtvZWluZGhe XF1kYmNoaGhcXFxWTEhOREBWR0BbTEVfWFhjXFxuYVxpXFdeWk9aVUpeWlhkX15rXlpnWlVnV1Br XFVtXFtlVVRdWFpjXl9qX2hoXWVnVldiUVNaUUxeVlBnYmVtaGttZGtqYmlwZ21zaW9uYWFtX19p YmdrZGlqXmRnW2FkXWJtZWpzanJzanJkXVRTTENYTVBoXF9uZGpyaG5uZWRtZGNwX15uXVxtXVZn V1BiWlhqYmFza25waWtkWlhbUE9YU05fWlVkY2hlZGlrYV1jWFVjU1FlVVRhU1dfUVZkVV5rXGVy ZWt3anB1aWpwZGVuX2JqXF5pWFVoV1RpWFVpWFVrW1pvXl1tX11uYV5tWF1oVFhjSkZfR0NeSERf SUVjUFBuW1twWlFpU0plST5eQzhXQzVbRjlWRjpbSj5pUExrU05uV09zXFR1Ylx1Ylx1Xlh1Xlhw XVdtWlRnU1duWl5zWFtuVFZkUUpkUUprU05vVlFrUVFtU1NuVlVvV1ZvXFpwXVthTUpYRUNcSUNh TkddUExhVE9fU1BeUU9hUE1eTkpcTlBcTlBiTlNkUFVnVVhtW15jX2VqZ21yZWtuYmhpXFpiVVNd VVFYUE1hTlBqV1ppX2VtY2lkW1xkW1xnW15tYWRoZGpraG5uZV9oX1piXlteW1dfT1BbSkxfTUpj UE5jU0ZlVUhhUERbSj5XRzJXRzJbVVBnYVxoXmRpX2VkV1ViVVNkWFBoXFRqXVhfU05cVVVdVlZc U1RTSUpPQDlKPDRRQTVRQTVQRkBUSURURT1VRj5WSkxfVFVfVExWSkNVSkdXTUlbVVNXUU9RSD5R SD5NRDtJQDhKQTlRSD9PSkBTTkReVFBcUU5YV1FVVE5UT0VPSkBTSEdYTk1cUVBcUVBfUVRkVlhY UUhTTENjUUhlVEpoXVpuY19lXVxiWlheVUxbUUhcT0pUR0NdU09iV1RdV1NdV1NbUUhaUEddTUlk VFBqWlZvXltvXVdpV1FdWlZbV1RkXGNrY2pjYV9tamlubmtiYl9VTkFNRjpRSD5bUUdnXWNtY2l1 amlwZWRpXltjWFVeV1phWlxuX2JrXV9vYmJzZWVyYV9pWFdkWFxtYWRoY2RnYmNlWFZhVFFlVk9r XFVpX2NvZWlqY2VoYWNuZ2lyam1taGtuaW1uZW1tZGtlWl1kWFxoXWpuY3BvZ3BuZW9iW1FVTkVd UFBrXl5yY2hwYmdtYmFtYmFqX15tYmFtXFhrW1dkW15oXmJvZWtnXWNjT01eSkhfV1RoX1xyZG1v YmpqWlZoV1RiVFZkVlhhU1diVFhpW2JvYWhyam1za250aGlpXV5oVlpkU1ZlU1BqV1VvXFpuW1hr W1xvXl9uYmVtYWRpWFVfT0xbSD9VQzpVRDtYRz5eTE5kUVRlVE5fTkhfRDhdQTVaQT5cREBcRkBk TkhqV1FzX1p6ZWN7Z2R0Z2JyZF9vXFxwXV1yXl5rWFhqU1FqU1FuV1NqVE9lU0loVUxtVFBrU09q UFBoTk5tVE9zWlVwWlFuV09hTkhYRkBYRj9bSEFeTkpfT0xdVVRdVVRiUU5iUU5eVFBeVFBeUFVe UFViV1ZqX15rYWltYmprYWlnXGRkXFhhWFVfU05bTkldU09kWlZoYWFtZWVpXl1oXVxnW15tYWRo Z2tqaW5tamtraWpnZWpdXGFiVVBaTUhfT0xlVVFnV1BkVU5jVExjVExbU0BcVEFiW11qY2VyaGtw Z2poX1xiWlZoWl5tXmNrYV1lW1deV1dcVVVTT0xNSUZNQTpIPTVQQThURTtYR0BbSUNVRjxWRz1U TklYU05cVFNVTUxYSk1bTU9eW1NaVk5USD9PRDtMQTxKQDtOPThTQTxORz1TTEFXTUdaT0ldU1Fa T05VSkdUSUZWUExaVE9iV1ZhVlVeVlBeVlBcWk5aV0xeW1NlYlprYmVuZGhlXF9fVlpYU0xYU0xX UEdTTENcT0pqXVhrZWFjXVhfVU9aT0laSkNnV09lY2dqaGttY2RrYmNlXmFeV1poYWVqY2hrZ2p0 b3N0b3BqZWdaVUlOST5RR0FdU01lYmpva3R3bXB0am5oYWFiW1tcWl1cWl1lXmNoYWVzaHBwZW5w YmdoWl5lXmFrZGdqZWdlYWJkW15iWFxlXF1oXl9wY2tvYmpoYWVpYmdraW1wbnJta3Bram9vaGpt ZWhlX1hiXFVlXGJrYmhqY2VlXmFcVUxWT0ZdU01oXVdoXF1pXV5pX2FrYmNwZGVwZGVoW1hlWFZn W15rX2NyYWJpWFpkUUhnVEpoXmJtY2d1aHBuYWlqV1VqV1VjVlZkV1djVVpnWF1oX2tuZXJ3b3J1 bnB5ZWNnVFFjU1FiUVBpVlRwXVtzYmNvXl9uXVpvXltoXl9oXl9lVk5WRz9UQzdUQzdTQzdaST1f TUdhTkhfTUdbSENbRjlYRDdcREBfR0RdSkhlU1ByXmF4ZGd6ZWh5ZGdwXlhyX1pyXWJ3Ymd1YmJo VVVhUUpfUElnVlVnVlVrWlBqWE9pV1BkU0xeSENiTEZoT0hzWlNvWFNrVU9jT01cSEZaSD9cSkFf T1BiUVNfUVReUFNfU05jVlFfV1ReVlNeVlVdVVRfVVFkWlZoXWhrYWtrYWlpXmdnXFtkWlhjV09h VU1fWlNiXFVlXl5pYmJuY19vZGFtXmNqXGFqY2Vyam1wcHBwcHBqaGdjYV9nV1BfUElbU09kXFht Yl5oXVplXVpiWlZfWlNfWlNlXF9wZ2p3am53am5uZV9qYlxqX15qX15rYV1qX1xiYVtcW1VWTURJ QDhJPjVIPTRMQzpRSD9fVURkWkhfVEhcUEVUUVBXVVRdVU9aUUxXSkZbTklWVlRYWFZcTUNURTtI QC5IQC5JPCxPQTFVSjxXTT5aSkReT0hbSkdYSEVVSklXTUxYU05eWFRjVlRhVFFeVlBeVlBaVlNf XFhiYmJpaWlwaHRwaHRpW11jVVdaU0lcVUxaVE1RTEVaVFxoYmpuaWhkX15kXFheVlNcVk9pY1xv c3ltcHdvZG1vZG1qZ2NiXlthXFtpZGNoZ25zcnl3a3RtYmpeXE9WVEdTUEViX1RvZ250a3N4a21z Z2hlYV9jXl1eW1deW1djXF5uZ2l0aXJyZ29yY2hnWF1lXmFqY2VqZGpnYWdnXGRnXGRkYWtoZG9u ZW9vZ3BvYm1wY25rZWtwanBtaXJuanNzaHNwZXBpY15iXFdjV1hjV1hiWFpdVFVdVEpXTkVbUUVk W05nXFttYmFzZ2p1aW11aW9vY2lkWFBiVk5pXWNvY2luZV9qYlxpYV1pYV1yaGtuZGhvXl9oV1hn VU9oVlBnXVRlXFNnWF1rXWJtZHByaXVuaGVqZGJrW1dlVVFhVlNkWlZtX19vYmJvXFxlU1NiVVNk V1VuXV5qWltoU0VbRjlXQzhWQTdaRTdjTj9jUEdlU0ljSkdfR0RaQzdbRDhbSTthT0BfTkhnVU9v Xlt1ZGF0YWFvXFxuWlp1YWF+a3J6aG53YWVqVVpfUEleT0hkU1hvXWN1ZGF0Y19qXE5dT0FcREBi SUZrVU93X1p0W1ZuVVBjVE1eT0hfTkViUEdnVlVnVlVeUU9eUU9hUUpiU0xfVU9fVU9fVVRfVVRa VFFcVlRfV15iWmFqXmJpXWFnWlpoW1tiV1ZfVVRcVVViW1tlXmFrZGdraWpraWpqYWdnXWNqY2Vu Z2lzbWp1b21yaWhvZ2VpXltkWlZnXFtpXl1pYV1rY19lYWRkX2NpYV1lXVpkW1xpX2Fza2t0bW1t Z2JtZ2JtZ2RpY2FtZGFrY19uaGNpY15fVEhRRjtQQDFOPi9KQDtUSURfXVxlY2JkYVhdWlFTUUxa WFNiVVBdUExcT09bTk5UVFFaWldeU0lbT0ZMRDJMRDJRQzlURTtaSkRdTkdeTEldSkhYTURVSUBT SENYTkhbUVNeVVZiV1ZhVlVcVFBdVVFVVFBcW1dlZGtwb3dzaHBuY2tkU01dTEZXSkZYTEdcUVBa T05bVV1pY2trZWtkXmRnW15iVlpcW1dta2h1dHlwb3RtZWprZGlpZGNfW1pfV1RoX1xkZGdtbW90 amtpX2FdVlZWT09aVlNkYV1vaGpwaWttY2doXmJfXV5eXF1iWlZkXFhqX2h1anN6bnR3anBwYmRl V1pjWl1rYmVqZG1lX2hhV11iWF5kXGNoX2dtZ21vaW9wYWppWmNjW2JnXmVuY25tYm1nZHJnZHJr YmNlXF1iW11iW11eWFZUTkxXTkVaUEdeWFRrZWFzZ2h3amt7bnl3aXRzZ21rX2VkV1dqXV11Z2tz ZGluZWRvZ2VqZWdoY2RuZGhtY2dpWFVnVlNkV1VnWldrYV1qX1xqXGFvYWVzX2pzX2pwZGVrX2Fo XFRjV09hVlNjWFVrXlxpXFpnU1BeSkhbSkdhUE1tWFtrV1pqU0hjTEFfSj1dSDtoT0htVE1vXltq WlZkTklfSUVcRzlaRTdWRjphUERiT09tWlpwX2F0Y2RyXV1rV1dvXWF5Z2p+am13Y2VvW1hnU1Bk UUxlU01qVl9yXWd0YV5uW1hoUUBdRzdkRkdvUFFrVFNzW1p0W1dtVFBhUUpfUEldVVFhWFVnWFtn WFtjV09fVExdUUheU0ldU01dU01bTk5XSkpWTkpTSkdYTkhbUEpeU1RiVldiVVVlWFhiVVVcT09Y U05eWFRkWFxwZGhqZ21pZWtoYWVpYmdqZWdqZWdvY2d1aW1vZ2VuZWRpYV1qYl5rYV9pXl1pY15q ZF9uYmNtYWJqXVtqXVtnYVpqZF1wa210b3BzbWp0bmtva2hraGRqY2NuZ2d0am5tY2dkWlRcUUxb UD9cUUBPTEZUUEpfX19kZGRpZGNeWlhWT0ZYUUhdUUldUUlaUU5XT0xYUEpcVE5fWE5cVUpVST5U SD1WSkNcUEheVFBfVVFjT01dSUdTST1ORTlVSUBbT0ZiV1RkWlZjVlRkV1VVT0pWUExXV1dfX19o Z25ta3N0bW9rZGdlU1BYRkRaR0dfTU1fVU9YTkhjXF5uZ2lqZG1eWGFnW15jV1tdWlRybmh3dHhw bnJqYmtoX2loY2RiXV5fWFhiW1tlYmhuanBuZ2diW1tYU0xUTkdbVlViXVxoaGpjY2VlX1tiXFdd W1pbWFdkV1dtX19ya3R3cHl6cHdvZWtnW1xeU1RhV11oXmRiXmdbV19aUFFaUFFfVVRjWFdnYWdr ZWtqXmJjV1tiVldjV1hlXmNpYmdnZW1kY2ptY2lqYWdlYl5eW1dWU01PTEZWTk1kXFtyaXN6cnt5 b3N3bXB5a3R3aXJ0Z29tX2hqXl9uYmN1aHB0Z290aGtwZGhqY2VnX2JtY2duZGhtXFhpWFVuXVpy YV1yX2N0YmVuYmhyZWtyY2hzZGl1Yl9zX11oVk9hT0hkTklpU05oTk5qUFBlTkReRz1bST1jUUVo VVdnVFZoUFFnT1BnT0VrVEl3XFx5Xl55ZGduWlxiUEFbSTtXRjpbST1bSUNkU0xkV1dpXFx4Y2Vz XmFtWFZwXFp0Ymh6aG57aW10YmVrV1doVFRoTk5pT09pVVptWF1yWFVoT0xkSDFeQyxlRkRuTkxp T1FqUFNnU1BiTkxcT0pYTEdfT1BkVFVeXF9fXWFfWldaVFFaTkZYTUVcUEhaTkZXSkZXSkZWTUNR SD5XRUNbSEZlT0lqVE5pV1FqWFNhVE9cT0pUTkdXUUpfWFhnX19qYmltZGtuY2ttYmptZWhuZ2lu Z2tyam9ybWtvamlqYWJrYmNuY2JwZWRvZWduZGV4Z2h4Z2hqX1pnXFZiXFdlX1tqZWlzbnJ5cnd3 b3R3bXBwZ2pqXV1yZGR9cHR5bXBrYV1lW1dhVUliVkpcVk9YU0xeXF1nZGVlX11WUE5XTUdYTkhY TEdYTEdUTkdUTkdTTExVTk5dU01bUEpXTERdUUlkV1dnWlpnYV5fWldjU09cTEhYTURTRz5VSUBi Vk1jW1dlXVppXFdhVE9WTkhaUUxaWlxjY2VpZ2hua21paWdhYV5kUVFbSEhVSkdbUE1iV1RhVlNv Y2dyZWliW11cVVdYT1BkW1xtYWd5bXN3b3Ryam9lXF9iWFxjW2JiWmFeWlhcV1ZiWFxpX2NjXVha VE9cUEheU0peVVZjWltpYmdnX2RoXl9nXV5oX1xhWFViWFplXF1vZ3BwaHJzZGdpW11fVVFbUE1c UFRnW15kX2NcV1tYTkhWTEZXT0lcVE5kW2FlXGJfWldeWFZhVVhiVlpnW15pXWFoX2dpYWh1Y2t1 Y2tuYmNnW1xiV1RcUU5hVE9zZWF5coB7dIN6cHR4bnJ9bXd4aHJzZW5pXGRqXGFuX2RwYWpzY210 aGlwZGVqYWJqYWJrXWJtXmNqXF5vYWN4ZWl+a29yY2hrXWJkXWJlXmNpW2JuX2dtW1VnVU9nUEhi TERjSkRkTEVoTkppT0xpU0pnUEhnV09nV09vV1ZwWFdoVVVjUFBnTklrU05uXF90YmV6YmFyWlhl VEdhT0NjUEdjUEdlTkRoUEZqWFNwXlh0W1dzWlZtVVR0XFt1YWV3Ymd3Yl9yXVtnVU5hT0hcSURd SkVjTUhnUExnTklnTklvUEZzVEl4VEp6Vk1zVk1pTURfTUdeTEZlTUZiSUNhTUplUU9hV11jWl9f VFpXTFFWTEZWTEZaU0lbVEpYUEpWTkhTSUBRSD9YR0FcSkVhVFFnWldrY19nXltpWFVhUE1YTU5d UVNfVV1lW2NoXmJrYmVuZGptY2lvY2RzZ2h0anB0anBzaW9vZWtrZGduZ2l3ZWd5aGl3aG10ZWpy ZWdvY2RlW1plW1pjW1pnXl1raG5wbXN0cHl4dH13b3RqY2hrYV9vZGN5bXN5bXNzYmFpWFdfWlVc VlFcW1VbWlRjXl9nYmNkW1FcU0lbTkliVVBhVlBYTkheT0hdTkdYSk1WSEpbSEhbSEhWTEZYTkho XGJuYmhrZ2pjXmJfVVRfVVRcVE5VTUdjUFBqV1dtXmNzZGlnZ2RdXVtXVFBXVFBbWl5hX2RoYl9z bWptamllY2JhWFNcVE5eUU1eUU1cUVBhVlVkX2NpZGhfU1BaTUpcTEpnVlVtX210Z3R5bXByZWlf V1FeVlBfVFdfVFdfV1FdVU9iVVBpXFdoX1piWlRqV1dlU1NnWlprXl5yX2NwXmJrX2NvY2dqY2Nk XV1fWFtiW11qZG1rZW5pYmJfWFhUT0VTTkRYUEpeVlBfWldhW1hcVE5WTkhXT0xaUU5fWFtkXV9l XVxkXFtkV1dkV1dfWFhkXV1nXWFuZGhvZG1vZG1uYmhoXGJkVlhfUVRhVlVyZ2V3cHt+eIN9cHd6 bnR+bnh5aXNyYmtnV2FlWl1uYmVuZW9waHJwaGdzamlvYl9rXlxpX2NoXmJtYmp4bXV6bnJ4a29v YWNlV1pfU1BlWFZiVVNlWFZqWExnVUhrUU1pT0pqT09uU1NzXmF1YWN3X1pzXFZvXFpyXlx1XGJz Wl9tVlBlT0lfUEllVk9yXGF0XmN3XVpvVlNqUE1pT0xlT0doUUlpUUdvV01wXVZ1YltyXV1tWFhl U1BnVFFtWF10X2RzWlVyWFRlVEdeTUBfSUFeSEBlTEdrUU1vVVVzWFh7X2J/Y2WDaGN7YVxuVkxt VUpoTkllTEdhTkheTEZcSURfTUdlU11kUVxfUVZXSU5YST9aSkBdVEphV05dVEpYT0ZRSDxPRjpX SEBXSEBdTE9qWFxwZ21qYWdkWFpdUVNaTlFeU1ZcVVdhWlxnXl1pYV9qY2NpYmJpX2NtY2duZW1u ZW1rYmVrYmVrZGduZ2lyaG50anB0amt0amt0amtuZGVrXWJrXWJoYl9lX11uaHBzbXVycHp3dX93 a3RtYmpkXlxnYV5ybW5ybW5yZF9lWFRjWFddU1FbWFdeXFtrZWtnYWdkWFBkWFBhWFVoX1xiWFpc U1RdUExdUExdU1FXTUxcSUNcSUNVTEFXTkRkWmRrYWtpZ2pfXWFeWFZfWldnXFtYTk1hT1VvXWN4 b3l6cntvbXBhXmJdUUldUUlWV1NVVlFhXl1qaGduY2JrYV9kWlRjWFNlWlFeU0pVTENcU0liWmFi WmFcT0pTRkFVSEheUVFjXGFoYWVvZG1tYmpkXlpcVlFeUU1cT0pdVEpbUUhcTEhlVVFuYmNyZWdy ZGJwY2FyY2VzZGdoX15qYmFkXV9vaGpoZWdlY2RhXF9dWFxjYWRpZ2plZWNfX11bVkxXU0hYTk1Y Tk1fTFBkUFVdUExdUExWTk1XT05cVVpiW19oXGJnW2FkWFxhVVhdVFViWFpjXF5pYmRvaG1waW5v ZWlpX2NkV1VjVlRqVl15ZGt5cHp6cnt4b3lzanR7a3h3Z3NqWGFlVFxnXWFtY2dvYm1zZXBwZ2hu ZGVoXVplW1dpXWFuYmV5a3d7bnl5bW5zZ2hoW1ZhVE9lVE5oVlBjU09pWFVvXFZvXFZwWFpvV1hy WGF3XWV6ZHB9Z3N7Z2R1YV50YWF1YmJ4X2FvV1hkTEVfR0BhUFFqWlt3YWV0XmN0V05qTkVpUE1q UU5oVU9qV1FtW1V3ZF51aGhyZGRtXFtnVlVlT0dpU0prWFhvXFxwXlduXFVtW0xpV0hiTERfSUFn TkpwV1R7X2t/Y2+CaG6EanCCbW19aGh4XVpzWFVyVUluUUZoV1hhUFFaSUZdTUliUFhjUVpiVVNa TUpYTjxbUD5eVFBfVVFdVEpeVUxWSUdYTElYTU5YTU5hUFFqWlttYmptYmpjXl1bVlVRTEdPSUVU TUNYUUdeVlBkXFZkX15nYmFpYmRqY2VpXmdtYmpwYmdtXmNrX2FrX2FtaGluaWp0bXJ1bnNybW5u aWpvZG1yZ29waW5uZ2tuaW1ybXBranJycHh4bnJrYmVkXV9jXF5qaGdtamlvaF5lXlVdWlRXVE5a U1ViW11oZG1oZG1pXl1tYmFzZGd1Z2lpYV1iWlZnXFhoXVpiV1ZcUVBbT0ZcUEdcTUVdTkZfWl9o YmhrZ2pjXmJoVlprWl1rW1pfT05hVlV3a2p4cHN4cHNzbm1kX15hT0ldTEZVT01bVVNjV1tqXmJt Y2RrYmNtY2RvZWdwY15jVlFVTEFVTEFbU1peVl1VTE1RSElNR0VXUU9aUFRkW15uX2dwYmlpZGhj XmJdWFdbVlVcT01cT01fT05tXFtwZ2p3bXB5bXB1aW15am96a3BvamtrZ2hkXV9rZGdpaWlnZ2dk YmNdW1xjYWJua21wZ2puZGhrY11kXFZcUEhbT0ddUExiVVBiVVNkV1VdV1VbVVNaU1VaU1VcU1Ze VVhjWl1fVlpiVldjV1hfWF1qY2hwaHRzandzbm9rZ2hoX1xjW1dpX2VzaW96cHd3bXNzaXltY3Nz Y29vX2tpW19nWF1nWlpuYWFpXV5vY2RyZ29rYWlnXV5kW1xoXmJrYmV0aXJ3a3R1amlwZWRpVVNn U1BiUU5iUU5pVFhwW193Y2VvXF5rWlRtW1VyXGV0Xmh7ZXJ+aHR7aXJ5Z291aW1zZ2pzWlNlTUZe RUFjSUZnVE1uW1RvW1hrV1VrVElvV01yV1x0Wl5yYWJyYWJ0YmV4ZWl5ZGl3YmdtXVZoWFFoT0hu VU5wXV15ZWV+bW57amt7aWJwXldnTUZiSEFwVVx1WmF9YWWEaG2Ea2+GbXCCbmh5ZV94X1VwWE5t VEZlTT9oWlxfUVRXUEdYUUhaUFRaUFRbUVNWTU5VSUBYTURcUVBeVFNbU1FaUVBaTU1aTU1dU1Fi V1ZjVlFpXFdrYmhrYmhjXl1bVlVUTklOSERUTEpXT05eVFNiV1ZhV11lXGJtYWJuYmNpYWpqYmtu YWFhVFReVE5eVE5nX2JvaGpwbXVybndtZ29nYWlnXGRnXGRqYWRuZGhpY2luaG5wanV3cHt6c3hv aG1lV1phU1VlX2VtZ21raGRnY19eXlFVVUhYU0xeWFFkYmVqaGtqZWRpZGN4ZGd5ZWhtZWhuZ2lw aG9vZ25pXFdiVVBeWFFhW1RiU0phUUlfWl9lX2VtYWRnW15rXWRqXGNnX1ZcVUxhV1t4bnJ6dXdz bm9zbm9nYmNiVVNcT01WUE5bVVNiVFhnWF1rYmVtY2dua29yb3N0am5iWFxWRz1WRz1aTk9dUVNb UE1TSEVKRT5RTEVbTkldUExlWFhqXV1jYWJiX2FfXV5bWFpdUE5dUE5eUFNvYWNwbm9zcHJ1bm5y amp9bXd+bnh3bXByaGtoYWNoYWNkY2plZGtoXmRkW2FnX2RwaW53Z3NyYm5rYmNlXF1eU0lfVEpi Vk1lWlBkXFtnXl1jXVhWUExTSEdUSUhaUFRdVFdiVlphVVheWFZcVlRXV1VfX11oZXNpZ3RubXJo Z2tnYmVkX2NqZG1ya3R3b3R3b3RwZ21oXmRpX2NoXmJpYV1nXltlWFhtX19oY2dvam50am5uZGhn XFZoXVdqYWJuZGVybnR1cnh1bWduZV9vWFNqVE5eU0pjV09tWlxzX2J1Y2dwXmJrXFVqW1RyX2Nz YWR4ZWt9anB6bXV5a3R1Z2lnWFtkTD5iSTxjSUNnTUZtVE9yWFRtWFZrV1VtWlpzX194Y2N6ZWV4 YmdwW19zYWd5Z217Z254Y2pwWlFvWFBqUFVyV1x3YW9/aXiAbW19aWl7ZF9qVE9pSUhpSUh1WmF+ YmmEaHKJbXeHbXV+ZG17YWF7YWF3XVhzWlVuV0ZjTTxlW1ddU09aUEdaUEdWU09WU09hVFRcT09a SkRbTEVYUE1YUE1bU09aUU5XT0lWTkhWUElbVU5jWFdpXl1oX15pYV9lXVxiWlhhT0hXRj9WSUlb Tk5eVFBeVFBcVVdhWlxuWmNyXWdpZGVnYmNlXVxcVFNcUU5eVFBpXWNyZWtwanNtZ29qYWRjWl1p WFpoV1hnXWFpX2NoX2ltZG5vZG93a3d5cHhwaG9rWFhnVFRkWmJuY2tva3JpZWtiY1pcXVRXVFBV UU5dXV9jY2VoZGppZWt1Z2tzZGlvYm1uYWtuanNva3RvYl9uYV5qYl5uZWJjV09YTUVcV1ZnYmFo Xl9lXF1oXmJpX2NkYV1bV1RjXF53b3J6cHJ3bW5ubm5hYWFkXFhdVVFdT1FdT1FWUExWUExfW1xr Z2hzc3VwcHN1bnNkXWJYSUNcTUZWUVBUT05fTUdbSENQRTxYTURVTUdXT0lYVVFeW1dlXmFjXF5j Xl9kX2FdUE5fU1BlWl9wZGpvam5pZGhoY2JrZ2V1a3J6cHd3b3JwaWtoXVpjWFViW11iW11lV15n WF9oWl5rXWJqXmJtYWRoZGFnY19eWk9eWk9eWFFhW1RqY2NuZ2djW1dbU09WSk5WSk5cUFRfVFdj VVpiVFhhWFdcVFNYVlVaV1ZhYWNjY2VqZG1qZG1oX2dpYWhrY21uZW95a3d4anVuZGpoXmRlXmFn X2JqY2VqY2VnXmVoX2dnaHBub3hzaW1qYWRoXlVrYlhzZGl4aW54cnp3cHl1Z2luX2JkV1dkV1dl W1ppXl1rYmNuZGVvZGFtYl5lW1VnXFZzYmN3ZWd4ZWt7aW97aW95Z21vYl1kV1NoT0FnTkBrU09z WlZ1XFd1XFduWldwXFpzZGl3aG10Z2RyZGJwXV1zX19yX2N4ZWl5Y213YWp0YVpyXldtWlxvXF5w ZXB1anV9aG16ZWp1XFdlTUhnTUl0WlaAaGuGbXCGaWl6Xl59YWN5XV+CYmOGZWd9ZGh+ZWlwXk5c SjtkXFthWFdfVk1cU0lWUUdXU0hpWFdoV1ZhUUpeT0hdU09dU09hUE1kVFBaU0lWT0ZUSD9aTkVi VVNiVVNhVlBkWlRpXltnXFhnVlNdTUlfTkddTEVaT0xcUU5dVVRcVFNhV11jWl9oY2RnYmNjXVtc VlRbVVNeWFZkXmduaHBva3JtaW9qYlxjW1VlVVFoV1RhU1diVFhjVlRlWFZjV1trX2N0a3NzanJw Y2FqXVtqXl90aGlybndwbXVwamhoYl9bV1RWU09aU1NfWFhrYmVwZ2pvZWduZGVuY2tuY2tybXBv am5tamlua2p0bmtvaWdlXlVcVUxiV1RrYV1nZV9fXlhnX2JrZGdpXlthVlNjXF50bW94cHN3b3Jq ampjY2NnXV5kW1xlWl1hVVhQUUhQUUhXU1RrZ2h0b3N3cnVvbnNkY2hjWltoXl9hX1xYV1RYSUFY SUFWSkNYTUVRU0xQUUpTT0lXVE5dVFVfVldjXl9jXl9bW1hWVlRaVlxraG5tZGthWF9eVlVkXFtn Z2d0dHR1c3RraWprW1pkVFNdVFVhV1hhVVZfVFVbU09eVlNhWlBdVk1hW1hjXVtkXFZlXVdlXVpq Yl5za25rZGdkWlRfVU9cSUdYRkRbTU9iVFZrV2FwXGVoXl9fVldhVlNcUU5iVlpqXmJyZG9yZG9v Y2dtYWRrYmVuZGhtaXRuanVuZ2trZGlvYWVuX2RyZWlzZ2ppZGhkX2Nram9ram9qYl5kXFhoYl1w amV4anV6bXh5bnd1anN0a2pvZ2VqXmJpXWFoXmJnXWFtY2dwZ2ptZVxnX1ZhWFVjW1duYmN1aWp9 aG14Y2h1YmJyXl5uYlhnW1FpW0ppW0pyXlx4ZGJ5ZV51Ylt3ZWJ4Z2N5bXB3am51aWFyZV1vXFxy Xl5yYWJzYmNyXWdvW2RuXFZtW1VrW1x0Y2R5bW50aGl6YmNyWltoVU5tWlNwX1x3ZWJ1ZGNyYV94 XVZ1W1R1XV56YmN+aWt/am1+ZVtvV01iTT9aRThrXl5qXV1fV1ZhWFdeUU1fU05lWFRnWlVhV01c U0hiVVNhVFFjU09kVFBbUUhYT0ZRRjtUSD1XTERfVExfVVFiV1RqWltpWFplWFRhVE9aVERTTT1Q SENWTkhbTklcT0peVlNiWlZkXV9pYmRlZGFcW1dbV1FhXVdnX2Jyam1wa29zbnJ0Z2dtX19rXlxp XFpiVVBhVE9kWFBjV09lWFRoW1Z1ZW94aHJyZ2N0aWV3amt3amtybndva3RuaWppZGViWlZYUE1X TENYTURhW1hlX11oZGFqZ2NvY2dwZGhvZWluZGhua21tamtua2ppZ2VdXldVVk9aVlNjX1xpZV9j X1ptYmFvZGNnXV5eVVZiWmFuZW1zcHR3dHhwZ2hqYWJnX19kXV1iX2FeXF1UUEpTT0lUT1BlYWJw aW54cHVyaXBkXGNqZGpuaG5uY11nXFZaTD5aTD5XTk9dVFVeV1dXUFBMSkNRUEhYU1BbVVNeV1pf WFthW1hcVlRdW1xpZ2hpYmRjXF5iVVNfU1BhY2J0d3Vzb3VtaW9nXV5iWFpkWFplWlteVFBYTkpV SkdaT0xcVFBeVlNkXFhkXFhoXVdtYlxoaWRqa2duaWhrZ2VtWlNlU0xhSkNcRj5eTEZnVE5rXmd3 aXJvamlkX15hVUlcUEVkUVxvXGdwZW5uY2tkY19iYV1tZWVuZ2dtaGttaGtwZW5uY2tzaW11a290 a2huZWJyZ2VyZ2VzZ2pvY2dvXltwX1xvaGh0bW13bXB3bXB3b3JwaWtzaW1vZWlzXl5zXl5uY2Jz aGd1Z2t4aW5uZFtlXFNlWFZqXVtzZ211aW96Z2d1YmJzYmFyYV9tYVhrX1dzYVp1Y1x6Z2d+amp6 aWVzYl5waGdzaml0bXJ1bnN4aGFuXldtXVZtXVZwXV9uW11uWmFrV15rWlRuXFZyX2N1Y2dyXlxt WldnWEpkVkhtWFZ1YV53YmJ1YWF1Y1pyX1ZpVlBtWlRwW2J3YWh+ZWR7Y2JvV0FdRjFdQy9fRTFv X1hvX1hrX2FqXl9iUVNeTk9eVE5iV1FfW1BeWk9iWlZiWlZfV1RfV1RfU1NXSkpaSDxWRTlVRURa SUheTk1kVFNtVVZvV1hnXFZfVU9fVEhaTkNYSj1WSDtbTEVdTkddUExdUExfWFhlXl5qZWRkX15f WldeWFZiW1tpYmJqaGlvbW5wbm9vbW51aWpwZGViW1teV1dnVldpWFppVlhtWlxtW2FwXmRqYl53 bmp5b3N5b3N6c3hwaW5oYWFjXFxjWlBfVk1eTkpYSEVhVlVoXVxnYV5rZWNvYWVuX2RoX15pYV90 aGt3am5wZ2hpX2FhW1ZcVlFfWldiXFplX11kXlxpYV9pYV9pXFpkV1VlV1pzZGd3dHh5d3puZGVk W1xnXV5oXl9fYmFfYmFVU1FOTEpRUE1fXltwZ2p3bXBvZWlnXWFwanNvaXJvZGFlW1dcSkFlVEpf VldeVVZfVVFeVFBaSUZeTkpcUUxcUUxaU1NaU1NiT09jUFBhV1tpX2NpYV1kXFhhV05dVEpiYWVy cHVvcHRoaW1vYl1uYVxrY19lXVpYUUdORz1USUZeVFBiXVxnYmFpZGNnYmFpYmJtZWVtaGduaWhr Y2JqYmFqV1BlU0xfTUZbSEFcUEhfVExkXGVwaHJ0anBvZWtjXFNbVEpkW2FqYWdwaWttZWhlXmFi W11kXV9oYWNqYWdrYmhwZGhyZWlza250bW9zamdvZ2NwY2FwY2FvYWNrXV9wXGFzXmNwZ2hwZ2hy aGlwZ2hwa29rZ2pwZGhrX2NwX15yYV9rXWJ4aW57b3N5bXB1ZV1qW1NqVlhwXF55ZGt4Y2p6Z2F0 YVtrX1drX1duXVpyYV14Y2F9aGV7ZWp6ZGl3ZGpyX2VvZ2VzamlzZGt0ZW15YV9yWlhrW1prW1pw XFxtWFhoV1RqWlZnV1BrXFVyXmFvXF5tWlNqV1BqVE9rVVBvW1twXFx1XVx1XVx3X1dyW1NoTklp T0puVVt1XGJ6W1NwUUlhSTddRjNjSDRoTTltX11rXlxtYWRoXF9fU05eUU1hVFFjVlRhV1hkW1xq Xl9uYmNnXV5kW1xkW1xcU1RlT0ddRz9RRDdTRThYSEViUU5lU1NqV1dlWFZlWFZlWlFjV09dTENV RDtTSUBRSD9USD9VSUBVTE9fVlplY2dkYmViXWFeWl1eVlNjW1dnYVxpY15raG5uanBwZ2pvZWlt YmFrYV9pXmdrYWltX2hrXmdkWlRfVU9eXF1ua211c3d3dHh5cnJtZWVqXV1lWFhpWFVjU09XSkZW SUVYUEpdVU9nXl1pYV9qYl5kXFheWFRlX1tuYmNzZ2huZWJlXVpdVVFaUU5bVVBcVlFjW1dkXFhl XVdnXlhtWlpuW1tqXmRtYWdzbXV6dH1pamFeX1ZiXlhhXVdbW1tdXV1VU1RQTk9eVVhuZGh3a3d0 aXRoYWNkXV9uanVva3dtaGlpZGVjV05lWlBfWlNdV1BhVUlfVEhkUVFlU1NnW1xiVldYVElVUEZW SkNcUEhdVVRnXl1oX1pjW1VnVlViUVBnYWd0bnRzanRwaHJwZ2puZGhraWpqaGleU0dVST5cT01q XVtta2Vta2VuaWhuaWhza2t1bm5vamlrZ2VqXVhjVlFlVk5jVExjUUhkU0lhV05hV05iWFpqYWJ1 ZW91ZW9uYWFlWFhkXV9lXmFrZ2hoY2RjXF5iW11hV1tiWFxkWFpkWFpqWF5vXWNtZWhrZGduX2Jr XV9uXF9uXF9pXltoXVprX2NtYWRtZGNoX15oXmJoXmJpX2VtY2lwY2NnWlpkV1VqXVtvZG11anN5 bW5yZWd0W1RpUElkUU9pVlRyXWR1YWhzX1ZuW1FrV1VqVlRuWlp0X196ZGl+aG14ZWlyX2NrXV9t XmFvY2l1aW90ZW1wYmlzX1pvXFZqX1xrYV1tWlBlU0lhT0NhT0NhUERlVUhnVE1nVE1tVlBuV1Fr U0xtVE1tVVZwWFp0XF91XWFwVlZyV1duVVFyWFVzXFR0XVV3V0xwUUZnT0NoUERuW1VwXVdnX1Zk XVRlW1ppXl1nW09cUEVdTENhT0ZfU1NlWFhtX19tX19pY2FoYl9oXl9kW1xtXVZnV1BcVkZWUEBY T0NcU0ZiU0xjVE1kV1dkV1dhV1hjWltlVVRhUE9YT0ZWTURYTURbT0ZaTk9eU1RiXWFhXF9dVlZb VFRdVVRfV1ZfXFhkYV1nY2ltaW9pX2NpX2NwY2FvYl9oYWNqY2VqY2hqY2hiW11YUVReW2FnY2l4 b3d5cHhyb3BpZ2hqXVtoW1hlWFhfU1NWTkhTSkVcUU5dU09lWltqXl9pX2NkW15hW1ZjXVhnYmVu aW1uZGVoXl9cVk9XUUpbT0ddUUlqXVtpXFpoX1poX1ptYWRrX2NrZW5uaHB1b3p1b3ptamtlY2Rh X1phX1pfW1pbVlVVT01WUE5jWl1yaGt1a29rYmVfXVFhXlNnZ2ltbW9zZ21qXmRoXVdnXFZnW1Fl WlBrXFFrXFFvYmJtX19nXFZhVlBXT0lVTUdTTUhXUU1hV1hjWltjXVZjXVZpWlFnV09nYmNqZWdq ZWdwa21vb3JtbW9ubXRubXRkW1BaUEZkWFxyZWl1bnNza3Bya2dwamV1bnB7dHdybnRqZ21tXFtn VlVnXVNpX1VuXVpuXVplXFNkW1FlXVptZGFoZWdtamtuZGVlXF1nWlpoW1toX2dqYmljXF5iW11f VlphV1tiV1ZkWlhrX2FvY2RvZWlvZWlrXlxnWldkV1dhVFRhWFViWlZnXWFoXmJpXltkWlZjVlFk V1NiWFxlXF9tW15oVlppW2JqXGNvZG10aXJ3YmtuWmNuVFhrUVZkV1dtX19zYWR0YmVuW1hpVlRp V1BoVk9uWlxzXmF0YV5yXlxqWlhpWFdiWFpnXV5wZGV1aWp3ZWRyYV91ZGF1ZGFwZWJyZ2NtYVhi Vk5iUERdTD9eSTxiTT9iUElqWFFuXlZyYlpwWlRuV1FvV1t3XmJ0YV5yXlxvXlFvXlF0Y199a2h6 Z2R0YV5zVUZvUUNrUU5zWFV0W1Z0W1ZkXFZkXFZjXFNqY1plXVpaUU5eRzteRztYTURfVEpdW15j YWRnYmNoY2RpYV9nXl1pYmJpYmJnXFZdU01hV05iWE9jWk9eVUpfVVFfVVFfV1ZiWlhpW11jVVdd VEpbUUheT0dhUUlfVFVdUVNdU09cUU5hUE1hUE1bVU5YU0xXVk5VVExdVlhpYmRlX11nYV5qY2Nr ZGRwaGRuZWJrZGdnX2JkW1xeVVZeWl1nYmV4b3l6cntycHVpaG1pXFpoW1hlXFNjWlBhV05YT0Zi UEljUUpiWlZlXVpkXlxeWFZfVVFnXFhpZGhrZ2prY2JnXl1hXFFaVUphVU1kWFByYWJ1ZGVrZWNr ZWNtXmVtXmVoY2dtaGt5bnl3a3dwaWtqY2VoaWRhYl1cXFpWVlRYT1BbUVNjW2JoX2dnYmFiXVxi WlRjW1VlY2dqaGtqYlxkXFZjXFNqY1ptYVduYlhzZWF1aGNzbmNuaV5uYlplWlFcU0hUSkBQSkRU TkdeWFRlX1tnXlhiWlRdVkxeV01fWFhqY2NoaGpra25qbWtoamlqaXBubXRkX15fW1ptZG51bXdy bndwbXVza255cnR4dH19eYJ3cnNuaWpnW1xlWltrYV9vZGN1YWV1YWVvYl9tX11qZF9uaGNpZ2hv bW5vaGhkXV1kV1NjVlFiWF5oXmRlXVxbU1FdSkpeTExdVVRlXVxuZ2l0bW91aWpyZWdvYmJqXV1i VlphVVhhVlVlW1ppXV5oXF1oXVxkWlhiWE9hV05kVlhkVlhnWFtoWlxnXFtqX15tYmpzaHB1YWVw XGFoUFFkTU5iUVBvXl1zX2JvXF5rV1dqVlZlWFZnWldpXFxpXFxuX1FtXlBrX1ZrX1ZuXF9zYWR0 aGl3amt7aGh9aWl9a2p9a2p0aGl1aWpzY1hvX1VuWEpiTT9kSTppTj5rV1d3YmJ1aGVzZWN0Ylxt W1VvW1hzXlxyXlh0YVt1ZFd5aFt4bWt+c3J/a2t1YmJyU0dvUEVtT01vUU90VlN0VlNnVlVoV1Zp V1BtW1RoV1hnVldiVEZbTT9XTz9aUUFVVVVfX19pXWFrX2NtXFtrW1pnXFhnXFhqWltoV1hpW11q XF5oX1plXVdkV1NeUU1bVEpaU0lhVlNhVlNYT0ZbUUhiVVBkV1NiVFZdT1FeT0hdTkdbTklcT0pb VElaU0hbVEdaU0ZVT0hcVk9iWlRkXFZnYmFuaWh3bm10a2puZWRoX15kXlphW1ZdVlhiW11lZWhy cnR4c3d3cnV0Z2dtX19pX2FvZWdvaGhjXFxjV0xiVkplXlViW1FlW1dhVlNfTU1nVFRlXmFtZWhu Y11nXFZoXlVfVk1iVk5qXlZ+a299am50b3Bwa21tY2dpX2NiXWFnYmV0bnd0bndvamluaWhqamhj Y2FbW1tUVFRWTk1YUE9hWlpkXV1oX1xnXltiWlhiWlhpZGVuaWpoYVZiW1BrYVt0aWN1cm5va2ht amttamtvcGtub2ptZ2JqZF9jWk9cU0hVRjxXSD5dVEpiWE9tXFtoV1ZhWFNhWFNjWlBpX1ZpY2Fo Yl9kYV1raGRqaGtvbXBqZWdiXV5pZ2pyb3Nwa29wa29ubXJ0c3h3dX10c3puaWpoY2RoXF9tYWR0 aG51aW95ZGt3YmlrYmNvZWdvamtybW5wbm9yb3BtamlqaGdpX1ZlXFNfWFheV1dkWlRfVU9kUFBl UVFkWFpuYmNvbnVta3NwaGRoX1xoXmJnXWFoVFtlUVhcU1ZfVlphWlxhWlxnXFhjWFVhVU1fVExj WFdjWFdiWlhfV1ZeVE5kWlRrYmVrYmVwXV1pVlZnTkdnTkdkUVFvXFxyXVtuWldzWlZ3XVpwXVdu W1VvXVZyX1h0aWN3a2WAa2l+aWd6Z2l6Z2l3aWl0Z2d6ZW1+aXB9anB6aG50aWVzaGR6aWV7amd4 YVhqVExlTUBvVklwXmR5Z217amd1ZGFrYVtqX1pwXVt1Yl93X1dvWFBqV1VuW1hoXmJzaW15bmpz aGRvU0lqTkVrTkhuUEp0VFN1VVRnXFhiV1RqV1VoVVNpVlhpVlhjWk9dVElcUEVdUUZdVkxkXVNu YWFtX19pXFxjVlZfVExiVk5lW1dpXltoXl9rYmNrYmNqYWJjXVtbVVNfT0NcTD9YTUVbT0dbT0df VExkWlZpXltkVlhhU1VjU1FfT05fT0xiUU5jWFNjWFNhVUxdUUhhVU1oXFRjW1VoX1prYmV1a291 cHJ3cnNoZ2NkY19kY11jYlxjW1dlXVptZWV4cHB9dHt+dX13b3J3b3J4bnJ7cnV9cHdwZGpqYl5p YV1vY1tpXVVhWFVaUU5aUEdaUEdhV1htY2RtaWFnY1tlX1teWFRlWFZvYl93bXN4bnR1cnhwbXNr aWhlY2JlYWJjXl9jZ2plaW1wa211cHJzbm9uaWpoXVdfVU9WUUZRTUFeVFBnXFhqYWJqYWJrYmNt Y2RpZ2VpZ2VnXlhpYVtwZ2p3bXB0b3Nvam5vaGh0bW13bW50amtramdramdpZV9dWlRYUUVTTD9e U0ppXVVuYV5tX11jX1diXlZlW1dnXFhkXV1iW1tkXV1pYmJpZW5qZ29iX15iX15vamtybW5taGlv amttcHdwdHp5d3p0cnVyZWllWl1iXWFpZGh0a3V3bnh3bXNwZ21oYWNrZGdvZ3NwaHRyam1waWtw a2pybWtrZ1tkX1RhV01iWE5hV05hV05hVFRiVVVoXF9tYWRyZ29wZW5vYmJpXFxoWlxiVFZiT09i T09fUVRfUVRdUVNjV1hqWFFrWlNtW1VqWFNnWlVhVE9hVFFhVFFkVU5qW1RtX11rXlxwXFpqVlRj UEdiT0ZlU1BvXFppXltuY195ZWV6Z2d4ZV9yX1p1Ylx+amR+b3SCc3iDbnN/am93ZWd1ZGVzZGdw YmR1Y2l5Z214Y2h3YmdvZGN1aml+cnN9cHJ7Ylt3XVZuW1RzX1h5aGl7amt4ZF1yXldtXFtuXVxz XFd1Xlp4WlRzVU9fVVFlW1duX2J5am17aGh6Z2dzV0drUEBtVEd0W057WFZ/XFpkV1dkV1dpWFVo V1RoV1RlVVFhV05fVk1eU0pfVExeVUxiWE9jWFdoXVxlWlthVVZeUU9eUU9kVU5nV1BoW1tqXV1l Xl5jXFxnXl1eVlVeTEZdSkVVUEZVUEZXUUpbVU5lW1ppXl1qXl9kWFpfWFhbVFReWFZjXVtlX11j XVtnWldiVVNnV1BqW1RqXV1rXl5rY2p0a3Nyb3Nyb3NtaGlqZWdpZGVoY2RlXVdnXlhlYWJzbm97 cHuAdYB6dH19d399dXp9dXqCeHt1a293b293b293c29wbWloYl1bVVBUSkBUSkBXVExnY1tkZV5t bmdqYl5nXltjWFVuY19zcnl4d351dHlvbnNza25uZ2lqY2VjXF5hYWNnZ2lwaHJ5cHpyb3BqaGlk XFZcVE5QTEBQTEBhVFFtX11zaWpwZ2hvZ2VuZWRraWppZ2hrZGdvaGpva3RybndvaGpqY2VrZWNy a2l1bnB1bnBvaGhvaGhraWplY2RiWlRaUUxfVVRoXVxpX2VwZ21tZGFoX1xnW1xlWlthWlpjXFxo XF9qXmJqYWdtY2ltYWRuYmVybXBwa29vaG1waW50bXJ4cHV5dHV1cHJuYmVhVVheXF1pZ2h7bnt7 bnt3aG1rXWJkWF5lWl9qX2hoXWVpXWFtYWRuZ2l1bnB0aWNuY11eW0lYVURbUUdbUUdfU05hVE9l VFprWl9oXmJnXWFoV1ZkVFNkVFNfT05iUEpkU01kVFVkVFVlU1NpVlZrW1pwX150Y19tXFhqXVtj VlRkUVFoVVVuWldtWFZuWlpwXFxyXlhrWFNeU0dhVUlkVU5tXVZrY19vZ2N5aGl4Z2h3ZWd4Z2h6 Z2mCbnB/c3l+cnh/a2t1YmJtW1RpV1BtXmFyY2VzZ2p3am53ZWd0Y2RtY2lzaW96bXV5a3R3Z15z Y1twX1x0Y194ZV91Y110W1ZwV1NtWFZrV1VzWFV3XFhzWFhyV1dnVU9uXFZ0X2d/anJ+am14ZGdz WkxuVUdyWFRzWlV6V1OAXVhrWl9vXWNwX1xuXVpuYWFrXl5lWFRqXVhoXFRkWFBjWlBkW1FfWFtl XmFjXGFkXWJlUU9iTkxfTkViUEdqV1BuW1RlXVdkXFZnXl1iWlhiTkxkUE5eV01eV01iWlZhWFVl W1plW1pkXV1iW1teW1dfXFhlXVppYV1nX19nX19uYV5nWldoXFRqXlZpXV5pXV5oXmJwZ2p1bnB3 b3Jvamlwa2p3amt0aGluX2RoWl5jXFxrZGR3bXN9c3l3c3t1cnp5dXt5dXt7eXh5d3V4dXl5d3p7 dX5zbXVnYmNaVVZXTkVXTkVXWE9naF5tbWpycm9nZGhnZGhkW2FwZ214dH97eIN1cnhwbXNraG5o ZGpnZGVlY2RfXGJoZGpycHpwb3lpZGViXV5hWl5kXWJXTk9eVVZrYmVzaW14bnJ1a29rZWNpY2Fj Y2NkZGRtaGt0b3N0b3Bwa21pXV5oXF1pZGVzbm91cHJ1cHJza2tvaGhtaGluaWpuY19uY19rY2Jr Y2JwZGh1aW13a2hyZ2NuY2JtYmFnX2JjXF5lXVxjW1ppW19pW19tW2NyX2htaGtzbnJzbm9wa21w a291cHR6b3h4bXVvYWNlV1piW19qY2h3a3d1anVvXl1iUVBeU0phVU1kWlZnXFhiW11qY2VoZG1t aXJ3b29waWlpZV1WU0paUEdkW1FnWldiVVNeVFNnXFtkW15eVVhjVlZlWFhoXVxnXFtrXlxrXlxn W1xlWltrV1xvW19qXF5wYmR1Z2lzZGdwYmRjVVdlU01kUUxqVlZuWlptWFtwXF5uV1FoUUxeUENf UURjUUVrWk1zXl56ZWV7aGh7aGh7Z2d7Z2d+b3eAcnl5bXByZWlyW1NpU0plT0lpU01tXmF1Z2l3 a2p1amlyX1hzYVp0aGt3am55bW53amtwZV9rYVtrW1puXVxzXFZuV1FtVUpuVkxrWFNtWlR0W1Z1 XFdzX1pwXVdrV1VvW1h0Ymh+a3J9bWV5aWJ4YVhwWlFyWlh4X15/ZF+AZWFtXWdzY210am50am50 a2pvZ2V0ZFxyYlptYVhqXlZnYVpnYVphW1hnYV5pYmJqY2NoVk1iUEdjTkBlUENoWFBpWlFuXlZt XVVqXlVlWlBqV1BtWlNrXlptX1tuXldqW1RlWFZlWFZdVlZfWFhbU1FdVVRjWltqYWJuZGhvZWlr YV1jWFVjXVtkXlxlXF9pX2NuYmVvY2d3bW53bW5yb3Byb3B6bnJ3am5vZ2VpYV9vYmJuYWFqY2V1 bnB1cHR4c3d4d3t3dXp6dXl4c3d4c3d/en6Ad310anBiXVxaVVRYT1BcU1RiYWVycHV5c3tzbXVn X2RiW19oYWN1bnB6cnB5cG9wa21uaWppa21oamtlZGliYWVfXmNlZGlzbXNwanBoYWFhWlpeV1pf WFtWT09lXl50b3N0b3N0cHdtaW9oZF5oZF5cX1pcX1ppZWtwbXNya3JtZ21iW1tnX19qaXB3dX16 eX51dHl0b3BtaGltZWhza25tZWhvaGpybW5rZ2hqYmFqYmFwaWtyam1wamhvaWdoZWRjYV9fXVFb WE1fVU9lW1VnW15qXmJvZ250a3Ntam5raW1yamp4cHB3b3J0bW9vZWdpX2FoYWNoYWNwZ21wZ21t XVVtXVVrXFVoWFFjWltlXF1jYWRoZWlpZWtuanB0b3B0b3BwZWJiV1RfWlNnYVprXlpqXVheWFFj XVZkXFtkXFtnWldpXFppXltqX1xtYWRtYWRtXmFlV1plV1poWlxoWmFvYWh3amt5bW51ZGVkVFVl U01iT0ljUFBoVVVqW1NrXFRpV1FnVU9jUUVeTUBjU0ZtXE93Ymd+aW59anB9anB7ZWp7ZWp+bnh6 anR0Y19rW1djWEdhVkVnV1BwYVp3ZWd5aGl0Z2dzZWVzXmN0X2R1Z2l1Z2l5Z2pyX2NoWFBnV09v XFZ4ZF55Ylx3X1p1XlZ1XlZ1W1t3XFxyXmF0YWN6ZWN3Yl9zW05wWExrYVtzaGJ1aV90aF54Y2Ny XV11XVx7Y2J9ZGh7Y2dqXGFyY2hvbXByb3N4bnJ0am5wY2FuYV5oZF5oZF5lZF5kY11nY11nY11t ZGNyaWhyYV9tXFtoVk9pV1BpXlhwZV94ZGR3Y2NwZFttYVduY11zaGJwZ21yaG53Y2VzX2JuXV5r W1xlWFhdUFBeTUdeTUdkVlhtXmFwZGhwZGhnX19aU1NfVVFkWlZkW1xnXV5oYWFuZ2d1bnB3b3Jz cG90cnB1bnB1bnBwa21rZ2htX1tqXVhiXV5rZ2hyam96c3h6cn54b3tzbXNvaW90cHl7eIB4dXlw bnJnXWFaUFRbVldfW1xrbXV1d391bXRoX2dcVlRdV1VjYV9qaGdwaWlyampqZWdnYmNoYmhoYmhi Y2tiY2tlZGlpaG1yam1tZWhjXVtfWldhVlNiV1RYW1xoamtycHhwb3duZGVnXV5fXlheXVdhXFte WlhrZW51b3hyam9nX2RfXV5pZ2hra3h3d4N5en5wcnVqampkZGRpX2NtY2dfXltlZGFra2tycnJt bmlpamVrY19uZWJua21ua21tamtpZ2huZWJkXFhfVVFfVVFfWFhiW1trZGdtZWhuaW1wa29zaW1w Z2puZ2tuZ2tqZWdoY2RnX2JkXV9pX2FtY2RzY1x0ZF11Y110YlxnX19lXl5kX15pZGNtZWhyam1z a3B1bnNyaGlpX2FnYmFrZ2VwYmRqXF5fW1pfW1plXF1kW1xoWFFpWlNrW1ptXFtuWmFyXWR0X2Rt WF1jVVdfUVRjVVppW190a3V1bXd5ZWVtWlpkU0RhT0BdSkRdSkRlVERnVUVoWkxlV0lkUD5nU0Bo W1t1aGh5bXB5bXB1ZGV3ZWd1Y2t3ZG19anB5Z21yYlttXVZnXFZoXVdrXV9wYmR3ZGh4ZWlyYV1w X1xvYl9zZWN1aml1amlzYl5pWFVlU0lnVEpvW1h6ZWN9ZWF7ZF97Ylt9Y1x6YmV+ZWl1aGVzZWN5 Z2F1Y110XlNyXFBrW1dtXFh1Ylt3Y1x4X150XFt1YWF9aGh9ZGV5YWJqXmJwZGh0bXJ1bnN1a3Jv ZWtrYV9pXl1lXmFpYmRrZ2htaGlvY2dvY2d3ZGp0YmhzZ2pzZ2pvY2RyZWd3amt5bW57cGp3a2Vw ZV9tYlxtaWVybmp0a3N1bXR6cnB3bm10YmhuXGJuWlppVVVfTkheTUdoV1RyYV1yZWttYWdpVlhl U1VXT0laUUxdV1VhW1hkWlhuY2JuaG51b3V0cnN3dHV1cHR4c3d4c3dwa29vZGNpXl1iXWFtaGt0 c313dX+Ac3t+cHlza3BuZ2t3bnh+dX95cnJza2tiW1tUTU1XV1pdXV9vbnNycHVwZV9nXFZXT0lX T0lcV1hiXV5nZ2doaGhuZ2loYWNdVlhfWFtfX2JeXmFlY2dqaGtua2poZWRqY1pjXFNeVlBcVE5Y W1xkZ2hwanNuaHBtZGNpYV9dXlpbXFdjXl1qZWRua29tam5uY19jWFVeWlhrZ2Vzcnl0c3p3a3Rr YWloXF9nW15hV11iWF5aVlNeW1dtY2d1a29wbnJvbXBrZV5oYltraHBuanNya3J1b3Vza25rZGdo X15iWlheVFBfVVFjXl9nYmNqZ21pZWttaGlqZWdrZGdtZWhrYmNoXl9nXFtpXl1pX2NqYWRtaGly bW57Z255ZGt3aWdzZWNqYl5oX1xnWldlWFZkYmVkYmVnYmNnYmNtZWhvaGp1ZGNrW1phWlpfWFhi WlhfV1ZkV1NnWlVvW191YWV7ZWp/aW56ZGtvWmFlU1BjUE5oVlpzYWR3bnV1bXR5ZWhwXV9vVk9r U0xeTENjUEdqWE9vXVRuXlRtXVNrWlBuXFNzYWd4ZWt5Z2pyX2NpWlNpWlNoW1ttX194Y2V3YmRu XldrXFVuXV5zYmNyZWlvY2dzX2JzX2J0YVd3Y1p6Z2l6Z2l5am9zZGl1X1RvWk5qU0hpUUdpWFVv Xlt4Y2F5ZGJ6aGJ9amR+aW59aG16Z2d7aGh7Z2d4Y2N1XlhvWFNtU0xwVk9wXFpzXlx4ZF55ZV+A aGl+ZWd5YWJ3Xl9wXmR3ZGp5bnl1anVuZGVpX2FeVlVdVVRcVVVkXV1oYWNqY2VqYWdpX2VyYmtt XWdrZGdwaWt1Z256a3N9c3d+dHh3c29ybmpwZV9uY11uZWR0a2p5bnd7cHl4dXdyb3B0aGttYWRu YWFlWFhlVE1nVU5rYV9rYV9uaWpqZWdvXl1rW1phUE1jU09jWltkW1xkW2FnXWNpZW5wbXVyc3d0 dXl3cHt4cn10c3pta3NtYmFoXVxkXV9oYWNub3N3eHt5d3p3dHhraW1pZ2pwbXV4dH1yb3Byb3Bq YmFnXl1kWFxkWFx0aGt0aGtnX1ViW1BcUU5cUU5cW1dpaGRyb25yb25raWhpZ2VjXl9hXF1hWlxj XF5qYWRzaW14cHNza25zamdqYl5rXFVoWFFdWFplYWJvaGpuZ2lyaWhuZWRnY19iXltiW11nX2Jr Z2hvamtuaGVrZWNhXFtnYmF1bXR4b3dwaWtqY2VqXVtqXVtiWlhcVFNbUE1fVVFpYmR0bW91b3h0 bnduaGNkXlpoXGJtYWdzbXV5c3twcHNtbW9rZGdkXV9fT0xjU09fW1xiXV5lYWRoY2dkYmVlY2do Yl9oYl9rXl5tX19qXVtrXlxoXF1tYWJraHB5dX59dYR5coB1a29zaW1qX15nXFtnV09kVU1pWFdl VVRnXFhrYV1wZGVyZWd0X11tWFZpXVVfVExeU0pjV09lWlFoXFRrX2NwZGh4aW56a3B7YWV4XWJu W1VpVlBrXl54amp5cHh1bXR7aGh1YmJqYVZlXFFdU09iV1RtYmF4bWt4bWd1amRyYltvX1hwYmRt XmFuW1RpVk9jVUdlV0lnVldtXF1yZGJyZGJ1XVx1XVxrYmVwZ2pyY2VzZGdyZF9yZF95Z2F6aGJ7 b3N5bXB+aWt0X2JpWlFoWFBpUUdpUUdrW1dwX1x3ZF55Z2F+ZWd+ZWd+ZG1+ZG1+ZGp9Y2l1YWV0 X2R6YV10W1dwVlNvVVFtWlp0YWF+amh/a2mCbW1/amp4XV1zWFhuX2JyY2V1anhvZHJqX15lW1pe TElaR0VcTEplVVRnU1NnU1NnVldlVVZnW1xkWFpjXVtnYV5tZWVwaWl1bnB4cHN3cnB1cG90Z2Rr XlxqYWJwZ2h4bnR7cnh4dH1zb3hwbm9qaGlrZGllXmNoXlVuZFtwYmRvYWNtbWpqamhuZV9pYVto XFNpXVRjX2VkYWdiYWVoZ2toZ25ta3Nwcnh1d317c3p4b3d0b3NuaW1nXV5kW1xpXFpvYl9ya3J4 cnhzdXducHJqZGpoYmhta3Nwb3dvdHV0eXp1dXNycm9oZ2FcW1VkXV9lXmFnZV9eXVdlWltiVldo Y2d1cHR1dXV0dHRwbnJqaGtiYWVjYmdjYl5kY19nY2lwbXN9eIh5dIR1dXhtbW9uaGNnYVxbV1Fh XVduYmNzZ2hwa2ptaGdoaWJiY1xfWFtiW11qaGlyb3BvbnNqaW5kYV1lYl50anB3bXNtamloZWRu Y11uY11iW1BcVUpbU01eVlBkYmVua29zbXV1b3h5bmhqX1piVVBnWlVwaW54cHVzcnlwb3dvZWto XmRjVlRbTkxbVlVdWFdhXF1nYmNoXmJnXWFoW1hnWldrXl5uYWFpYmJtZWVqYWRrYmVtaXR1cn15 dIN1cH91aHVwY3BuWl5lUVZkU0lkU0lpV1FnVU9kVFBtXFhwXmJzYWR3Y2F1Yl9qW1RjVE1jVE1h UUpiWlRjW1VpXWFuYmV3amt5bW50Y19vXltrX1doXFRyXmF7aGp9cHR5bXB6Z2d6Z2d1ZFdqWk1j VVpoWl50Ymh7aW9/a25+am13Z19zY1x1ZGVvXl9tW1FlVEphUUdiU0hoVFtyXWR1YWV3Ymd1YWV0 X2RzZGl0ZWp5aGd5aGdzaGRzaGR6a3B+b3R9cHJ7b3B+Z2JyW1ZpVVNtWFZrVkpqVUlqVlZvW1t1 ZGN6aWh/amh+aWd+ZG2CaHB/aW53YWVwXV91YmRzX19uW1tzW1p1XVx0ZF16amN/amqCbW2DamuC aWp7YVp3XFVnYV5oYl9uZW1rY2poWlxhU1VcSkVWRT9YRkRdSkhYTElcT01YTkpXTUlXTkVdVEpe UFViVFhkXlpoYl1rZGduZ2l0b3B4c3R1a21wZ2hqYmFoX15zZ211aW94cnh3cHd3b3Jyam1nY2lo ZGpuYmVvY2dzZGdzZGdvbWttaml0aF9uYlpjW1dkXFhlYWJpZGVnZ2dpaWlvam5wa29va3R4dH16 dXl0b3NraGJoZF5lXVxoX15qZ2Nva2h0bnR3cHdvb3Jqam1pZGVlYWJqZGptZ21ub3h1d397eoJ7 eoJ7cnNtY2RcVVVqY2Nqa2dqa2dpY15kXlpkYWl0cHl4eHp7e354c3dwa29nZWpta3BqamppaWll aXJydX56eod7e4h+fYdycHpwbm9vbW5eXVVYV09rYV94bWt1cHR3cnVvb21jY2FfXFZfXFZqZWd4 c3Rzcnlta3NkYmVkYmVvZWl0am5nZ2Rra2lyam1waWtvY2RpXV5pX2NoXmJnYWtwanVzcnd1dHl1 a29lXF9dU1FiV1ZuZGp3bXNzb3hybndwZ21pX2VoW1tkV1diXFdkXlplY2JjYV9oYWFnX19rY2Jq YmFuYmN0aGlwa290b3NybXBybXB4b3t4b3t3c350cHtyaGtoXmJkVlhiVFZlW1pqX15qX1xoXVpl VVRqWlhzXmh6ZW96a3N5anJ3Z1xyYlduXlRvX1VyZF9qXVhpXl1vZGN4ZWt4ZWtuYVxoW1ZnXFZl W1VvXl95aGl5bmp5bmp6a25zZGd0X11vW1hwW190XmN3Y3B7aHWDbXKCa3B7amd6aWV6ZWpwXGFz WlZuVVFqV1BwXVZ0Ymh5Z216ZW14Y2p0Z2RzZWN0aG51aW95ZGt4Y2pyZGR1aGh6aG6AbnR9cHJ9 cHKGaWt6XmF0VlN0VlNtV0xrVkptVVh0XF95ZGt/anJ9aWN6Z2F9Z2t7ZWp6ZWV1YWFvW11zXmF0 X2R0X2R1Yl94ZGJ5ZVx6Z116YmWDam6DamuDamuCaGN5X1tnXFtnXFtrXV9uX2JpXFxkV1ddUE5Y TElbSEZdSkhXSEBYSUFVT01RTElUTkdYU0xfVFVjV1hjXF5kXV9nXWFnXWFtam5wbnJ3bW51a21w ZWRrYV9qYl5vZ2N3cnN1cHJ5bXN3anBqYmtqYmtpYmdoYWVoX15uZWRvbW5wbm93a2pvZGNiX2Fd W1xkW15pX2NnZGNlY2Jvamt1cHJ1c3d3dHh3c3lybnRzbm9uaWpjXFxqY2Nua29wbnJ1dHl0c3h0 bnRrZWtkYmNiX2FlXl5qY2Nqa29zdHh5eIJ9e4Z/dH1zaHBnX2Jza25wbm9vbW5waGdqYmFkZGdy cnR4d359e4N7cnh1a3JoZ2tubXJta3Nta3NqbXlwc391dYZ5eYl+fYd6eYN3dHh1c3doX15hWFdr Y29+dYJ6d4JybnlpaWlkZGRYWE1VVUljX1x0cG16c4JyanloZG1hXWVqXmJvY2dpanBvcHdvbXBw bnJ0a2hvZ2NuZGVwZ2hlYWRwa29wdHptcHduaWhhXFtnXFtqX15vYm14anVzbnJuaW1uZ2drZGRq YmFqYmFrZ2VpZGNpZGVpZGVpZGVpZGVtaGdtaGd0ZWp3aG1zaHV1anh3b3R1bnN0aXJ1anN5b3N5 b3NvaWJiXFVeVFNhVlVqYWRuZGhrYmNqYWJqXV1oW1tyXmt4ZHJ5a3R9b3h7cmR1a151ZGF5aGR5 a2tyZGRuXGJ1Y2l6bnJ3am5yYWJqWltoW1tpXFxuYmV1aW15b3B4bm95am1zZGdyXWJyXWJ4Ymd4 Ymd6a3N9bnWCbm6Cbm6Ca3B9Z2t6aGt0YmV1XlpzXFdzX2J7aGp7b3B5bW54ZGR1YmJzZGdzZGd1 Z2t3aG15ZGl4Y2h3YWV4Ymd3aG96a3N9bXd6anR+X1x5W1dzVU90VlBtVlBuV1FvVVV1W1t3Y2V/ a259aWd5ZWN6ZW15ZGt3Y2F1Yl9zW1puVlVyV1NzWFRyWlh3Xl14YVt5Ylx3Y2F+amiCbnB+am19 Y1h5X1VjT1ZjT1ZlVFppV11nXlthWFVoV1RqWlZnU1BjT01cTkBYSj1USj5RSDxVTD9cU0ZeVFNh VlVeV1diW1tiWlhnXl1qZWltaGt3bXB4bnJyaGluZGVvZF5vZF5yam1za25/aXV9Z3NvYmJtX19k XV1jXFxhXFFhXFFnZ2dubm53bXBwZ2piX2FbWFpfWFtjXF5lYWJkX2Fyam9za3BycHpzcntycnRu bnB1bnBza25nYmFqZWRraW1vbXBzcndzcndyaXBqYmliXV5bVldbVVNdV1VhYWFubm5yc3d1d3p/ d4N5cH1qZWlzbnJ4dXl4dXl1bnBza25paWtycnR3dX16eYB/eH13b3Rrbm1ucG9ybndraHBuanNz b3hwcIB3d4d9eoh5d4R5dXt4dHpzZGluX2RubXd3dX90dX5qa3RpZ2ViX15YVU1XVExkZGdwcHNu bnBoaGplY2dbWFxjW1dtZGFwcH50dIJubXRubXRwaWt1bnBzanRvZ3BqYmluZW1oZ2tkY2htYl5u Y194a293am5uZGhwZ2prZGlpYmdrZGdvaGpqaWVta2hraWpraWptY2dvZWlyY2V1Z2ltamtyb3B5 bXBzZ2pvZG1zaHB0b3Bzbm93bXByaGt1a293bXBwaWlkXV1hVlNoXVpzZ2h1aWp0aGluYmNuYWFv YmJrXWRzZGt3a3d6b3p+dHV3bW53aGp3aGp7b3N6bnJ6aG59anB+cnV3am5zX19rWFhqWFxqWFxq XWV0Z291a214bm96aWh1ZGNvYWNvYWN0Y2R0Y2R+am2Db3KDb3KCbnB7amt5aGl5ZWNzX11wX1x0 Y195bXN9cHd6bm91aWp1XlpuV1NrWFtuW114XmR5X2V0YWNvXF5vWl5yXGFwY253aXR7bXJ9bnN/ ZVt+ZFp5ZV93Y11tXVNtXVNzV1p4XF55Y2h/aW57aml5aGd7ZWp5Y2h+Y2V3XF51WE9wVEppUExv VlFzV1d4XFx0Ylx0Ylx3Y2F+amh/a255ZWh6YVR3XVBtX2hiVV1oVlpnVVhlX1hoYltuZV9vZ2Fo YVdkXVRiVk1fVEpYTj9aT0BfVExjV09kV1dlWFhkWFplWltpV1tuXF9nXFttYmFybXB4c3d1cHJy bW5yaGlyaGlyam13b3J/anR7Z3ByZF9qXVhfXVBaV0peUVFeUVFlXF9zaW17b3N4a29qYWJlXF1k W15kW15eXF9lY2duaW1vam5ranJvbnVuanBqZ21vZWlwZ2puZ2duZ2dvZ251bXRyc3tvcHlwbm9q aGlkX1VhXFFbWE1aV0xdV1BjXVZqYml0a3N1dHtubXRnY2lraG53dX13dX10b3N0b3NwanB0bnR7 dYCCe4d9eX93c3lta3Bwb3RwbXVtaXJram9qaW5rbnpvcn51d310dXt5dX55dX5wZ21yaG54b3t9 dIB0c3pqaXBtaWVkYV1VVlFdXlpvbXB3dHhpZ2hjYWJpYmRfWFtkYmNraWpzeINwdYBzanJuZW10 aXR6b3p3aG1tXmNlV1pkVlhjVVpnWF1uY2t1anN4bXV4bXVyam1nX2JpX2NoXmJpX2NyaGtybW5y bW5wa21rZ2hrYmVoXmJqYWRzaW11anN3a3R0am5wZ2puZGpvZWtyb3NqaGtoZWdqaGlranJqaXBq Z21iXmRnYV5rZWNza25za25yY2VwYmRvYl9tX11tXF1tXF1yXmt6Z3R5bnd1anN5ZGt5ZGt1a295 b3N4bnJ3bXB9bXd6anR5ZWhtWlxoXVpoXVpnWFtuX2Jtamtwbm90aWVtYl5rY2JuZWR0Z2dzZWV5 Z2p9am6Aa3B9aG15ZWN3Y2F0X19zXl5zXmN5ZGl5bnt9cn99am53ZGhvXFVkUUpqUVdtVFpzVl10 V150XFtzW1ppWFpqWltqWF5yX2V4Y2p+aXCAam+EbnN+c294bWlyYVRqWk1qV1FuW1VwX15yYV9z YmNwX2F0XmN5Y2h9aWN4ZF54XlB0W01uV0ZqVENyVFF6XFp0Ylx3ZF55XmF9YmR7Z2R4Y2F0XFFu VkxqZWlhXF9rWFhqV1drXlxvYl9uaGVtZ2RtZF5oX1pkV1NnWlVlVEppV05iWlRkXFZqXmJoXF9k W15lXF9vXWFqWFxiV1FkWlRkZGRubm51bnB0bW9ybXBwa29za3B3b3R7bnd5a3RzaGRuY19jXVZe WFFcVVVcVVVkWFBuYlpyY2V0ZWhuZWJqYl5oYWFoYWFoY2RtaGlvaG1uZ2tuaWpuaWpzZ210aG5y Z29zaHByZWlyZWlrZW50bnd0cHtva3d3bXB3bXBwZ2puZGhoXVpdU09fTkdhT0hjXGFyam93b3J1 bnBvamtwa210cHt1cn1yb3Nyb3Nva3J3c3mAeoOCe4R5c3lya3JtZG5vZ3BubXJqaW5ta3BqaW5p am5wcnVucnVvc3dzd31ucnhtaW9va3Jzb3p6d4JvbW5qaGlnX19pYmJnX2Jyam1wcnhzdHppYmJc VVVhVVhhVVhoYmhya3JwdHpydXt0anBwZ210c315eIJ9aWtvXF5oW1hkV1ViVlpoXF9uZW13bnV1 bXR1bXRyaGlpX2FlXGJnXWNpYmduZ2tzbnJwa29waWtqY2VqYWRrYmVpY2lwanBuZ2tuZ2tyam1y am1raWpraWpwa29taGtrYmVtY2dtYm1vZG9pYmdpYmdoYWFqY2NyaGt1a293ZWRyYV9yYV9wX15v XFxoVVVqXmR3anB3a3d0aXR4ZWt4ZWt1anN6b3h3bm1zaml1a3J0anB5Z21vXWNoV1RkVFBqWlty YWJ0b3N0b3N6aWhzYmFzXWJ4Ymd1Z2l1Z2l5bW54a215am16a257Z2d3YmJ1XlpyW1ZrXl5wY2N4 anN6bXV4ZGdyXmFlU0lfTURiTk5kUFBnU1dtWF1wWFdwWFdrVU9qVE5lWlBlWlBwXmR4ZWt+bnqC cn6DcnN6aWp1XFdqUU1yVFB3WFVyXVt1YV5zX19vXFxyX2N1Y2d0Z2d5a2t6Z194ZF11X1BwW0xv WFN3X1p9ZV19ZV2AYl97XVt+Y157YVxuVkxqU0hlYWJjXl9pXV5pXV5oXmJpX2NpY2FlX11lW1Vl W1VjVlFlWFRlWlBqXlVtXVZtXVZtXF1qWltnW15tYWRpX2FpX2FeWk9dWE5fW1xpZGVwaWlyampw Z2p0am51a290am51bXd4b3l4bnJwZ2pqYWRpX2NkWFpjV1hjWElnXE1vX1dyYlpwZWJyZ2N0Z2R4 amhybW5wa21yZG1yZG1wZGVyZWdyaG50anByaXVyaXVzYWdvXWNpZGhybXBvaXJuaHB4aHJ6anR0 a3h3bnpwa29oY2diU0xhUUpfW1xqZWdzZ2hzZ2hwaWtyam1vbnNubXJwbXNzb3V4b3d9dHt/e4J9 eX9zc3Vqam1tZWhyam1tbnJoaW1taW9uanBpaWtpaWtqaW5ubXJzb3puanVraG5qZ21ranJ1dHtq aGdhXl1tY2RzaWptamtyb3BudXNudXNrY19cVFBfT1BqWlttZWp0bXJ1b3V3cHd3a3d0aXR4d354 d355bW5yZWdtX1trXlpjWltoXl9oZGFva2hwaW5waW5pZGVlYWJkXGVjW2RjWl9tY2lwaWtvaGpr ZGdqY2VrYmVqYWRpX2NtY2dqYWJqYWJzZGl1Z2tyaGtwZ2puZGhqYWRtYWRvY2dtX2huYWltY2dq YWRtYWRtYWR1aW93anBzZ2huYmNrY19tZGFqWlhiUVBlWGFzZW54bXh3a3d4ZWl7aW17cH57cH54 bWtwZWRyY2hzZGlzZ2puYmVvW1tpVVVkXWJrZGlwanNzbXV4Y2huWl5yWF56YWd4a210aGl4a294 a296a3N6a3N6aGtwXmJyW1ZzXFdvXFxuW1t0Ymp6aHB4X15vV1ZnTkdiSUNiSkloUE9uWF14Ymdw XV1vXFxtWlNpVk9rVU1qVExtW2F0Ymh7bXR/cHiAa256ZWh4XV93XF57XVt9Xlx1XV55YWJ0X190 X190YWN0YWN4ZWl1Y2d4ZGR4ZGR1YltzX1h1XFdzWlV6Y159ZWF6X2J4XV94Xld0W1RqUUVlTUBj V1tkWFxkWlZkWlZpV1tkU1ZdVEpdVEpdUUleU0piUU5iUU5hVE9nWlVrWFttWlxlWFhlWFhrV2F1 YWpoZWRlY2JnXlhiWlRfV1ZiWlhqY2VtZWhramdramdza2tza2twZXB0aXR5a3d0Z3JtZWhrZGdt YWJoXF1oWFFtXVZyYWJ4Z2h1Z2l6a255b3N5b3N4cHVza3BqY2VpYmRvaGp1bnBzb3hzb3h0bnlw anVoXl9pX2FpYmRuZ2lpZGVqZWduanBwbXNua290cnV0cHtraHNpVVxiTlVjW1ppYV9taGdqZWRp YWhqYmlpZW5uanNyanlyanlzand3bnp5eIJ4d4B7d3pvam5oY2RpZGVnZWpnZWpraWptamtuZGVr YmNrYmVyaGtubXRpaG9tZWpvaG1ubnBvb3Jqam1kZGdvZG93a3dzcntubXd0dX5vcHllX1hbVU5l X1hvaWJraG5raG54bXh5bnl3bnhzanRycHV0c3h5cnR4cHNybmplYl5iWlRkXFZrZ1xtaF1tZF5n XlhjX1piXlheU1RbT1BXUVdlX2VuY2tvZG1rY11lXVdjXVhkXlpoXF9pXWFnX19qY2NzZ2pyZWlq X2hoXWVoX2lrY21yZWlyZWluX2RpW19qXl9oXF1nWF1tXmNyY2p3aG9uY2tvZG1taGtpZGhoXVxd U1FdV1BoYlt0bW91bnB4bXp5bnt9c4d9c4d6a251Z2lyY2hvYWVwZGhtYWRtXmFqXF5qXmR0aG57 a3h6and4Y2hqVltuWF13YWV0amt5b3CCc3p9bnV/bXN+a3J5a2tzZWVyXlhoVU9oW1hoW1hwXGN4 Y2p1Y11uXFZuV1FpU01wVVV0WFh0Ymp9anN7bXJ0ZWpwXVtwXVtvV1hqU1RqVlhwXF5zYWd9anB/ am16ZWh1YmR6Z2mAaGd/Z2V6X2J4XV9zV1dyVlZzW1xzW1xyXV1yXV11Xlp4YVx1Y110Ylx3X1d1 XlZ5Yl14YVx5XmNwVltyVE5wU01rTUFtTkNhUFFkVFVlVVRlVVRpWFVkVFBdUUlcUEhdTkdcTUZe Tk9hUFFiUVNkVFVqWlZqWlZqXV1pXFxtXmF0ZWhwbXVraHB0Y19vXltiVk5fVExiWlZlXVpqZ15r aF9rY11pYVtuXGJyX2VwZGpwZGppZ2ppZ2prYmhrYmhzZ215bXN4bXV5bnd6cHd4bnR1bXR1bXR3 bnh0a3VrZ2huaWpwa2p1cG90cnNwbm9tZWpqY2hoYWNoYWNoXmJnXWFqY2VqY2VraWpwbm9ua21w bm9zc3VwcHNrZWFoYl1qZWdrZ2hqaGlpZ2hnX2JoYWNoY2duaW1uZW9qYmtnYmNuaWp0c3pzcnl6 c3Vza25tZ2RkXlxhX2RlZGlraWpqaGlyaWVtZGFyZ2V3a2pwcHBvb29uZGp0anBybnd0cHl1bnN0 bXJ4bXh3a3dzb3hwbXV4d4B0c31nY11kYVtzbnJ3cnV1cnpybnd4b3l9dH51b3VzbXNybnd3c3t6 dH17dX53cnBkX15tX111aGVyaWhuZWRpZFpkX1VeVlBdVU9iVFhiVFhdVFVkW1xuZ2dyampwZV9r YVtpXlhnXFZjWFdnXFtiX2NraW1rZ2plYWRhXVpiXltnX2RrZGltY2drYmVkXFhiWlZlXFNkW1Fo Wl5wYmd5Y295Y29waWlwaWlzbm1uaWhpWFViUU5kXFhrY190a3V1bXd4bXp4bXp6bn94a317Z25z XmVuXF9vXWFrYmVpX2NuY19qX1xtXF11ZGV4a293am5yXVtoVFFtWFt0X2J1aW1/c3eEdH6AcHp7 bXJ6a3B6aWh3ZWR0YWNuW11vXFxvXFxyXmF4ZGd9YmR3XF5yYV1zYl53YmR5ZGd0am56cHR9bnN4 aW54Z2NwX1xuW1hnVFFoVFZrV1puX2d6a3N/am19aGp4ZGR7aGh7aGV4ZGJ4XFxwVVVqT1ZrUFdp VVNoVFFqUFBrUVFvV1ZtVVRwWlFyW1NwWlRzXFZ3V110VVt4WFpuT1BrTUVtTkZvUEhyU0pfT05n VlVnWF9nWF9jWltfVldeU0pdUUlbUUVcU0ZhUE1kVFBnVlVlVVRnXVRtY1puYWFvYmJqYWRyaGt3 bnh3bnh1Z2ltXmFhWFVeVlNjW1dqYl5wZ2hwZ2hrYV9rYV9uXFZtW1VrXlxuYV5iX15fXVxjXF5p YmR6bXWAc3t7c3p5cHh3b3JuZ2lyZ3R0aXd1a3t3bX1zbnJzbnJzcG9zcG9zbWVqZF1kXlxkXlxn XWFpX2NpYV9jW1ptYWJwZGVybW50b3BzbnJ0b3N1bXR3bnVqampvb290b3B0b3B0c21ramRkYVtu amRvaGh0bW1lY2diX2NjYV9nZGNuaHB3cHl6cnt4b3lyam9rZGljYmliYWhoZGpuanBuaGNrZWF1 aWp3amt0cG1ybmpwZ21zaW9ya3d5c354b3d5cHh+coN6bn93cHt3cHt3dX90c31uaGVuaGV6d394 dH15dYB0cHt5bnt5bntzaHN0aXR0bW95cnR5d3p1c3dzamltZGN1anN9cnp9dIB1bXlqaGlkYmNq XVtkV1VlWFZjVlRiV1RoXVpyb3N4dXl5cG1zamdwX15tXFtnXFtrYV9pZWtuanBrX2NlWl1hWFdk XFtrZGdqY2VqZWlnYmVjXVthW1hjV1hlWlttYWJvY2RyZGR0Z2dyam15cnR5c3lvaW9rW1pkVFNo X15uZWRzanJ3bnV3bXN1a3J7cHl6b3h5Z2prWl1nVFRoVVVoW1tpXFxlXlVnX1ZkX2FpZGVzaHBu Y2ttWldpVlRoVFhzXmN3b3R7dHmAdHp+cnh3bXN0anBwZ2hyaGl5a2tvYmJrXFRuXlZ0YWF4ZGR+ Y2h7YWV1ZGF1ZGF1Y2d3ZGhyaG54bnR9anB3ZGp5ZWN1Yl9qXVhlWFRtWlprWFhvYWh4aXB6Z2R3 Y2F1YmJ3Y2N6ZWN1YV5vWFNpU01iTlNjT1RkUVFnVFRlUU9qVlRvVVVvVVVuVUptVElvUU5yVFBw UVNvUFF3VlFzU05lTUNlTUNtUERtUERhV1toXmJuZ2ttZWpnYmFjXl1iVVNcT01eU0piVk5uWmFw XGNwY2NqXV1lXVprY19vY2RvY2RqY2VuZ2lqa3JtbnRwZ2pwZ2ppYV1hWFVjV1htYWJrZGdrZGdo X1plXVdkWFBnW1NlWFRlWFRiXFpcVlRhXF1oY2Rzb3h3c3t6cHdzaW9tY2RtY2RwZ21yaG5vam50 b3N1cHJ3cnN1dXVycnJuaGNoYl1pXl1rYV9nX19oYWFoYWFnX19rZ2Vwa2pybXBybXBzaW10am5z bm93cnN7eH5+eoB7eXp5d3h4d3Bwb2lzc3V1dXh1c3RzcHJpZWtkYWdiXGJfWl9fXmNram9ycnJz c3Nza3BvaG1hW2FdV11jX2VkYWduZGVuZGVtYWR3am51b3V3cHd1c3draW1taXR1cn1wb3lzcnt4 d4Bzcntta3NjYml0a3V6cnt3b3R1bnN6dH14cnp1cnpybnd6bXV7bnd1cHRvam5ubm50dHR5c3l4 cnhyam1waWt4b3t/d4OEeYJ6b3hybnRwbXNuZ2tpYmdqZG1oYmpiXmRqZ21zdHp1d31yb3BvbW5p YV9oX15nX2JtZWhtaW9qZ21pXV5jV1hfVVRkWlhoZGFoZGFrYV1jWFVoVVVnVFRnUVhwW2JrY2Jt ZGNuZ2dtZWVwbXh3c35ubnBkZGdnYV5pY2FzaW14bnJ3bnV1bXR3bnh1bXd6cn55cH15bW5wZGVp WlNqW1RpXltrYV1zYVtwXlhrXV9wYmRzZGlwYmdrXlxnWldkV1dpXFxvamt0b3B3bXB0am5tY2dr YmVuZGhzaW1yaGlqYWJtW1FqWE9tWFhzXl54ZWt4ZWt3ZWd1ZGVwY2NwY2N4ZWt7aW99Z2t5Y2h5 YWJ1XV5rW1dqWlZyXV9wXF50Ymp1Y2t6Y154YVx1XFd1XFd3XVZyWFFkU0ZiUEReTE5jUFNqV1pr WFtqVE9nUExnTUZnTUZlSENpTEZwTkxyT01yUU91VVN+W1Z5VlFyVUxrT0ZpSUVwUExqY2VtZWhz aWpzaWp1Z2lyY2VrYV9lW1pnWlVwY15raW1qaGtyaGltY2RuYV5rXlxtWlpuW1toYl9rZWNoaGpq am1qaW5paG1tYl5nXFhoW1hvYl9oYWFvaGhtZ19pY1xrXlpuYVxwYVprXFViW1tYUVFfWFhtZWV1 cnh1cnh1a29tY2dpX2NrYmVvamtvamtwZ213bXN3cnV3cnV1d31yc3lyaGlnXV5lXF1pX2FpX2Nt Y2dyZWluYmVpaWlra2t0a3N0a3NwaWl0bW1wcHB1dXV/eX+DfYOAfoJ3dHh6eHl9ent/e4KAfYN5 dX55dX5vb3JlZWhjXl9fW1xdXV1paWlvb29qampwaW5waW5nX2JhWlxpYmdrZGlwZW5wZW5rY21v Z3B0cHt3c35zbm9oY2RnYWtwanVvbnhycHpzcnlwb3doYl9jXVtpZ2pyb3N3cnV3cnV4dHpzb3V4 c3dwa29vaXJwanNwanB0bnR5b3N9c3d5c3l1b3V0anB1a3J9cn+EeYd/en53cnV1dHtycHh1bXd0 a3VoaHRoaHRkam9la3Bvc3twdH1wa21iXV5iVVNnWldjXF5tZWhuaWprZ2hlXVxeVlVfU1NnWlpv ZGFwZWJtXVVoWFBlUU9lUU9hU1dqXGFqYWd0anB3aGp0ZWh5a3R6bXV0am5vZWluZGV0amt6cnl4 b3d0cHdybnR5a3d4anV1anh3a3l6b3hzaHByXl53Y2N0Z2dzZWV3aWRwY15yXV10X19wYmRuX2Jt Yl5pXlttXF1wX2FvZWt1a3J1aWpzZ2huYVxvYl1uZGhyaGt3aWRwY150W1RwV1BtW1VyX1p3ZGh5 Z2p9aGp6ZWh3Y2N1YmJ0X2R4Y2h7Z2l1YWNzW1pyWlhrWFZrWFZlW1dlW1dzXmN1YWV3XlRuVkxr U0huVUpwV01uVUpnT0NoUERhTU1kUFBuVVFtVFBnTkdhSEFfSj9fSj9oRz1vTkRyT011U1B0VU16 W1N7XVt5W1h0XFFvV01wVVV4XFxlY2Rtamt3b293b297a3V9bXd4a29zZ2ptZGFtZGFpaWlqampu aWptaGlpZGNeWlhdUUhbT0ZeUVFkV1doXF1pXV5qY2VvaGpoYVdlXlVpXlhtYlxyY2h4aW5ua2pt amluaWhuaWhzamltZGNrXlxlWFZiV1RqX1xvbXBzcHR4a29uYmVvYWhyY2pwaWtwaWt1anV5bnlz cnt3dX94d4BzcntwbWRpZV1kY11jYlxrZGluZ2tyY2VyY2VlZWVtbW15b3N1a29ycG10c29wcHB3 d3eCen+AeX6DeIN7cHt5d4Z+e4uIf4yEe4h7e4l4eIZ5bndtYmpkXV1fWFhhV1hjWltkY2hlZGlq YmltZGtrYV9qX15pYWhwaG90bndzbXVta3NranJ1cnh4dHpyam1hWlxcWGFoZG1tZ3JwanV1dHly cHVnYVpcVk9fYVxnaGNpaWltbW11b3V0bnRzbm1qZWRnYWlpY2tlZGtta3N7c3p3bnV6bnJ5bXBz aHN0aXR9cIKCdYeAeoB1b3VwaG9yaXBvbnhqaXNvZWtrYmhnZGhtam5ycn5vb3twaGdjW1phVlBf VU9jWl1tY2duaWhqZWRpX2FjWltnWlprXl5wa21vamttZGFjW1djU1FkVFNlWFZtX11yZWl4a290 aGlyZWd3amt5bW53bXBwZ2p0aG59cHd7d4Z4c4J7cnV4bnJ5Z296aHB4anN5a3R4bXV4bXV7aGh+ amp7bXJ6a3B0aGltYWJzW1xzW1xwXGFzXmNvXl1wX15yXWJ4Y2h1Z2t1Z2t4aWt4aWtzY1twYVh0 aWh4bWt6bWhzZWF1XlpzXFdwXFxyXV14Y2V6ZWh9YmR9YmR1YWFzXl5wXmR0Ymh5ZGd6ZWhzX110 YV5zXFdpU05nVUhpV0prW1dtXFhwWE5oUEZrT0RwVEhzW05yWk1qVUloU0doTU9pTlBqUUVpUERj TEFeRz1fR0BiSUNjSj5rU0ZtU05tU050W1B4XlR3Xl11XVx0XVdzXFZ4XGGAZGltX193aWl4a3J7 b3WAdHh/c3d9cHR1aW1zaGRtYl5pZWJraGRnYmFlYV9oYWFfWFhdVElYT0VdTUljU09lV1pqXF5w XmJwXmJvXltuXVpqXVhtX1t0ZWh4aWt5b3B3bW50b3N1cHR3bXByaGtwaGJnXlhjX1ppZV9wbnJ1 c3d7b3N4a293am5vY2dtY2RuZGVvZWl1a290c316eYN+fYJ9e4B6enpycnJvb29wcHB3a3RtYmpp X2FtY2RvaWd4cm91c3dua29tam5pZ2pwcHN3d3l/dH19cnp5bnl6b3p5dIR9eIh7eH53c3lzb3pv a3dpXmtnXGlhW1RdV1BfXltfXlteXF9dW15hWlxkXV9pYmJrZGRtZWhza253dHhvbXBtamtua21w b3dzcnltZWhjXF5dWmJkYWlkYWloZG1ybnd0cHlqY2VXUFNbWFpdW1xcWlhhXl1pYWhzanJtZWho YWNdV1BdV1BeWlhnYmFtaGt1cHR3bW5uZGVnX2JqY2V6cIJ/dYd1cHJuaWppYV9pYV9oY2JnYmFn XWFiWFxiW11nX2JoZ2tta3BybW5qZWdjW1dfV1RkW15vZWlwZWRqX15jWl1nXWFoW1ttX19wanN0 bndyampqY2NkWE9lWlBjXF5rZGd4anN7bnd0bmtzbWpzb3VybnR1bnNtZWpzZ2p7b3N+d4Z7dIN7 cHl1anN4ZWt1Y2l0ZWpzZGl6a25+b3J/cHWDdHmCc3p6a3NwZV9pXlhzW1p1XVxvW19wXGFuXVxy YV93ZGh5Z2p5Z2p3ZGhyZ2VzaGd0Y190Y190aG55bXOAbnJ9am59ZGh7Y2dtXVNrXFF3Y2N3Y2N3 Xl9yWltwVlttU1doV1huXV50ZWp3aG11Y11yX1ptVE1nTkdnVUhnVUhvW1twXFxuW1FtWlBvXFVz X1h5ZV93Y11wV1BuVU5nTUZiSEFoTEBjRzxhQzljRTtfQThkRjxoST5nSD1jSj1lTT9rU0xzWlN0 WlZ5Xlt5XWJ3W194W2SAY21rW1pvXl11Ym19aXR7c396cn53cHl1b3h5a2lzZWNtZGFwaGRrYmVr YmVrYmVnXWFkXFZiWlRjWFNkWlRkWlZpXltzX111Yl95Z211Y2luY2JuY2JuYmNzZ2h6cHd/dXt7 d3h5dHV5b3N4bnJyb3BtamtubWdvbmh3cHd+eH6AdYB+c355c351b3p0bnRya3Jya3J3cHd7dX56 dH14e4R7f4h/gId6e4J6enp6enp+c3t3a3RqZF9qZF9rZ2h5dHV1c3Rwbm9rZ2VqZWR1cnp/e4R/ dXt7cnh0a3N3bnV3bnV7c3p3cHl1b3huZGhnXWFnWFtnWFtlXl5tZWVpaWtjY2VhXVpYVVFhVVZn W1xuaG50bnR3b354cH90cHdpZWtqY2hvaG1qbXlucH1tZWhkXV9fW1xfW1xnXmhnXmhua29yb3Nu Z2deV1ddV1BbVU5YVU1fXFRlY2dvbXBzaHNuY25iW11jXF5fXFZkYVtlYmhqZ21yaG5tY2llX11o Yl9uY3BwZXNya3dwanVwZXBwZXBtZWhqY2VoY2JlYV9kXV9qY2VuZ2lrZGdrZ2VkX15lXF1lXF1l XVxoX15vZGFrYV1jWltiWFplV1xrXWJ3bX55b4B5bm10aWhoWFFpWlNoX15uZWR4bXV7cHl5cnd0 bXJybXtzbn1zbXNtZ21rZGlwaW56b3p9cn15cnRza251aWpwZGVoX1xtZGF6a3B/cHWCdH2Ac3uA bW13Y2NqXVhoW1ZyWlt0XF1uXV5tXF1qWltyYWJzZWN6bWp6Z2d4ZGR1Y2d0YmVzZ211aW94anN5 a3R7Z257Z25+am14ZGdvY1dyZVp6b2t+c29+aW50X2RwV01lTUNiT1FvXF5zZGl5am95ZWVwXV1r VU1lT0dpVEhrVkpuV1FvWFNwWlRzXFZwXGF4Y2h6Z2RyXlxyWk9tVUpnSj5jRztkTD5hSDtlST1o TD9nRzlqSjxtTT5uTj9jTTxkTj1oUU1uV1N1Wlp1Wlp1V1V3WFZyVFB3WFVlWFZlWFZpW190ZWp4 cH97dIN6dXl5dHh6b256b251b210bmtwZ2hyaGlqYmFnXl1pXltrYV1qXVhqXVhnYV5pY2F0Y2R5 aGl1cHJ5dHV3b3Jyam1tZWVvaGhyam96c3h6eHd5d3V9c3l7cnh1cHRuaW11bm51bm53cnV7d3p6 dXd4c3R3c3l7eH5+eXp6dXd1c3d1c3d9dH57c314eIZ7e4l+fYR+fYSEgIeEgIeDfYh9d4J1bWtr Y2JqZWl1cHR0dHRzc3NwaW5yam96eYB9e4N6dXd5dHVwaW50bXJzb3p7eIN6b3h1anNvZF5jWFNj VlFtX1tra2t0dHRzcHRpZ2plXF1eVVZiVVVpXFxua291c3d3c3tzb3htZWplXmNhW1hnYV5jY2Np aWlnY2llYmhfW1xdWFpkWlhoXVxnZ2dqampyaXBnXmVkXV1eV1dlX1tuaGNzb3VwbXNubXdubXdl YmhnY2lnYVxlX1tkZGRiYmJvZWtuZGpoXl9oXl9lXl5nX19tZWVtZWVtZWpyam9vZ25vZ25uZ2tu Z2tuZ2lqY2VyY2hwYmdrYmVoXmJnXltpYV1rZFtpYlhoXFBpXVFoXFNnW1FnWFtrXV95bnl5bnl5 Z2p3ZGhyX2NvXWFuYWl1aHB4dH94dH97c3p3bnV4bXV3a3RraWhpZ2VpX2FqYWJzaHB5bnd4b3dz anJyaGlrYmNlXmNpYmd0am59c3eAdHh4a293ZGhuXF9tXFtrW1p0XmNzXWJvW1htWFZjVlZpXFxq XVt1aGV9amR7aWN4ZF5vXFZwXGF1YWV1X255Y3J6aHB5Z295a2t4amp3aWd4amh6c3N7dHSAa3B6 ZWpwWlRuV1FpVVVyXV1zYWR4ZWl7aGV0YV5tVVRnT05jUEdlU0loVU5tWlN0W1Z5X1t3YWV9Z2t7 aV90YlhzXEhuV0RqTT5oSjxoVD9oVD9uVkVtVURtUERtUERtUUFvVERpUURlTkBlVEduXE91XFd0 W1ZwU09vUU5qUUdtVElqWFNvXVduYWF0Z2dycHh1dHt7c3qAeH+Ad3p9c3d+d3t5cnd7a3V5aXNv Xl1uXVxoW1ZoW1ZpXltrYV1lXmFrZGdyZWd6bm95dHh5dHh4bXV3a3Rzbm9zbm91bXR6cnmAeoB+ eH5+eH5+eH57c3p0a3N1a3J1a3J4cHV4cHV1bXR4b3d0c316eYN6d313c3l1dH51dH54d353dX10 cHt4dH94d357eoJ9eoh+e4l+c36AdYB6c3Vyam1vY2l3anB1c3R1c3Rya3d0bnl5dYB9eYR7eX10 cnVzbm91cHJuanNzb3h1cnhybnRwaWlqY2NnX2JuZ2ltcHRwdHh5d3hvbW5pXVVpXVVkXGNtZGtu bXJwb3RwcnhpanBrYmVpX2NjXl1nYmFvZWd1a21uanBqZ21uYmVvY2dqX15nXFtrX2NwZGhyZ3Jy Z3JoZWdqaGlvb293d3d9fX90dHdta3VycHpzcntvbnhwaWtqY2VjY2NkZGRuaWpuaWpuYmNpXV5k XV9oYWNwZ2hwZ2hrYmVtY2dtZGtvZ25vZWtuZGpwaW5uZ2twXmJwXmJpX2FoXl9pX2NrYmVuZWRw aGdtZF5uZV9uYlprX1doYWFrZGR1bXR3bnV4a210aGl3ZGp1Y2lyaXN5cHp5dYB9eYR/dH17cHl3 aG9yY2pvZGNyZ2VzaGdzaGd4a3J4a3Jua29pZ2ppX2FjWltkWFxpXWFvZ25zanJ5am90ZWpuXVxq WlhlXF1lXF1qYVduZFt1XlhwWlRpWFVnVlNkW1x3bW5+b3J+b3J6aGJwXlhyWl10XF91YWp4Y217 aW97aW99aGh7Z2d9aGqAa25+b3R/cHWCbXR9aG96X2J3XF5yWl10XF90YmV4ZWl7amd4Z2NyXVto VFFiU0xiU0xkUUpoVU5tVUpvV01yXlx6Z2R5ZV95ZV9wWkZuV0RzV0VwVUNwWExvV0p5WEd6Wkh0 VUp1VkxvV0RvV0RvVk94Xld/Z2WGbWuHa2SAZV51WklrUEBqTkNvU0dwX2FyYWJ0Z2d4amp1cHR4 c3d/cn2HeYSAeX5+d3uAeoB/eX97dYB0bnl4ZGdwXV9oXFRlWlFpW11tXmFlX11nYV5tY2d1a293 c3l0cHd4bXV3a3RzcHJ0cnN0cHd1cnh9d396dH15dYB5dYB7c311bXd1a291a290bndzbXV0bXJ1 bnNzb3hzb3h4cn13cHtzcH5zcH5vcn5tb3tzZ216bnR4aXB7bXR5cH17c394bXV6b3h5b3VyaG50 aGt1aW1vbnNvbnNqaXBranJ0bnl/eYR7eX16eHt6eHt0cnV1bnN3b3R1dXh3d3l5cnR0bW9wanB0 bnR0dXtzdHpzdHhub3NqZ2Nva2hvbnV1dHt4dH11cnp0a3hwaHRvZWlqYWRjX1xpZWJ1a3J+dHp4 cHN3b3J0a3NyaXBlY2RjYWJuX2d0ZW1zZW5vYmpoYmhrZWtzanJ9dHt6eHt3dHhranJoZ25qanpv b39zbXVya3RpZGVqZWdraW1raW1rZ2plYWRhYWNra259d396dH1tamtoZWdkYWdkYWdtX2pwY25q Y2hza3BzaW1qYWRrX2FtYWJqZGp1b3V9cHR7b3N9a219a21zaW1vZWlrZ2hrZ2h4anN6bXV6c3V6 c3V5b3VyaG5ubXRwb3d4c4J9eId6d31zb3VzYmNrW1xtX191aGh5b3B3bW56bm97b3B0b25taGdr W1doV1RbV1RfXFhvYm15a3d5am9yY2hwXFxvW1tvXWF4ZWlwZ2hzaWp5bmp1amdwX15pWFdrY2J1 bWt3b3J1bnB4ZGRtWlpuWlptWFhwYml4aXB7bm56bW2AaGeCaWh+a3KAbnR+cnN7b3B+aWd9aGV1 YWNuWlxtWlNtWlNuXVxyYV94YVt0XVdpVlBhTkhhSkNdRz9dRjxhST9iTEdnUEx3XF5/ZGd/ZWJ9 Y195YU94X051XU91XU90W050W059XVN7XFF1XE9vVklrVEBqUz90VFOCYV+Ha3CNcneNbmiIaWN4 W05rT0NqTEBuT0RzZGl0ZWpuZ2lpYmRvamt1cHJ7bneDdX59c3d5b3N7dHl+d3uAeH99dHt5Z29z YWloX1xlXVppW11lV1ppV05kU0lpV1tyX2N0a3h3bnpzaW9wZ21qaGttam5uanNybnd1b3VzbXNy a3J5c3l7dX54cnpwa21uaWpwanBwanByam9uZ2tvaGpuZ2lwZW5yZ29zanR4b3lvc3ltcHdwa21z bm9yZGRvYmJwZGp0aG5ya3RwanNyaXBvZ250aGt0aGtzanR0a3VwaWtvaGpzbXN6dHqDeX+Ge4KH f4SCen9/cHV+b3R9eHt+eX1+d3l9dXh4c3d5dHh4eHp5eXt7dX50bndvZG10aXJ4b3t+dYJ+d4Z9 dYR9b3h6bXV3a2h0aWVqaGltamt1cnqCfod/en56dXl3bnp3bnppbm1na2pqYmlvZ254a29uYmVl Wl1uYmVuZ2t7dHl4c3Rzbm9tZ2JqZF9pZHRwa3tva3RtaXJqZWdlYWJrZGlvaG1zZGtqXGNbX2tr cH2EgpR9eox4eHhzc3NqZWljXmJwYmlyY2ppZWtzb3V7bnd5a3RuZGhrYmVtaXR3c357eIB7eICA cnSEdXiAdHh0aGtwZGhuYmVzaHB5bnd6d319eX97dXt0bnRwa29ybXBycn94eIZ3eoBvc3ltXVZo WFFqXVt0Z2R5dHV5dHWCc3h/cHV3bm1waGduW1FoVUxjU09nVlNuY2t0aXJzZ21tYWdpVVpvW191 YWV9aG16bXp7bnuCc3h/cHV0ZWhvYWNyY2h1Z2t4bm90amt6Y15vWFRqV1FtWlRvYWN1Z2mAbW2A bW2Ea2+Ea29/anKAa3N/bm17aml4Y2F3Yl95Wl1yU1ZpVlBpVlBrWFFwXVZzXU9rVkhlTkBeRzpe QzdbPzNbQzdbQzdfQz9qTUlyWl15YWR/aGKAaWODaWSCaGOEZFqCYld/X1R/X1R/X1d+XlZ7XlFz VklvTzxtTTp0T06EXl2Oa3CUcHWIbWiDaGN7Wk5wT0RrSkNzUUl0am5tY2dnZV9jYlxrZ2Vwa2p3 a3d4bXh3a2p1aml3bW51a214bXp/dIJ5ZXV4ZHR3Y25yXmlrXFVkVU5nUD9nUD9qWFFzYVpzaGd3 a2pwYmRoWlxjXWNfWl9jW2RnXmhvZWdwZ2h1aHN6bXh5a3R5a3RwaGdtZGNrYmhyaG5waHJtZG5u ZW9yaXN0bW9za250a3N1bXR3dXp3dXpvbnVvbnV1Z2ttXmNuW11uW11uZ2dyamp0anByaG51aml0 aWh0bXJza3BoY2JlYV9qaWV0c295eH9/foaHfoaDeoJ3bXN3bXN7en+CgIaCgIZ/foN9d31/eX95 eIJ6eYODe4t1bn10ZW1yY2pybnR5dXt/eYR9d4J7c39+dYJ9eHl5dHV4dH19eYJ6eYB/foaAfYN+ eoB5cHp3bnhubXJwb3RvbXBzcHR1a29vZWlwXVdpVlBtXmN1Z2t1bnNwaW5tY2RoXl9rXWJuX2Ro XmJuZGhlY2JhXl1pYV9uZWRwX15qWlhoYm17dYCJh5aGg5KIgoiAeoB3bXBrYmVqXmJwZGhtaXJ4 dH1+cHt5a3dzZ2pwZGhyaXV/d4OEfomEfomLd4SMeIaDeIB3a3RvaWdlX11qZG11b3h5dX5+eoN+ d3d3b29zaWp0amtycHh5eH97eoR4d4B1Y2drWl1vYl94amh7cnV+dHh/cHV7bXJ1a21wZ2hvXFVk UUpnU1VpVVduXmhyYmtvXWFuXF9pWFptXF1wY2F7bmt+bn2AcH+Db3qEcHt9am56aGt0Y2RwX2F6 bW14amp1Y11vXVdwWE5tVUptWldzX119am6Cb3N+b3R9bnN4Z2h5aGl7Z2R5ZGJ1YV50X115XGNy VVxoUE9nT05pU01uV1FvWkxoU0VoST5iRDleQDVaPDFaRDNYQzJfRztlTUBtVlB4YVt7Z2l/am2I cGqJcmuHbV+AZ1p6YVZ5X1V6YVZ6YVZ5XUpvVEFtSjlwTjxvUU97XVuHbnKIb3OJdW9+amR+XlRt TkRrSj9vTkN5bXBvY2dkYVtcWFNiWlhqYmFrZW5uaHB0aGlwZGVvZWdwZ2hwZ211a3J7ZXJ+aHR4 aXB1Z25tYl5kWlZkUUhlU0lqXVttX11wZV9wZV9oXVxiV1ZjV1tfVFdjWl1jWl1qXl9tYWJqZWlw a293anB6bnRvZ2VtZGNvZWlwZ2puZ2lwaWtvZ25waG9zanR0a3Vzb3pva3d3b3R1bnNvbXBua29v Y2RtYWJrXl5yZGR0bmt0bmt1bWtyaWh3aGp0ZWhqal5paV1kXlxiXFpkXlxvaWd5eH1+fYKCeYCA eH94c3d3cnV5eoCDhIuCho59gIl+eoZ9eYR+e4l+e4mAeoBya3JwY2FyZGJvbWt5d3V4eHp3d3l0 b350b356d396d397dXt/eX97eH5/e4KAfYh7eIN4dHp1cnhta3Vta3VvaHhyanp3bXB4bnJ3Y2Fw XVtuW11uW11zZ2pyZWltY2dtY2drY2JnXl1iV1ZjWFdbWFpfXV5tZWVvaGhtY2RqYWJyY2p+b3eD gJKDgJKGe4yDeYl9bnBzZGdkWmJrYWlyaXV9dIB+dHp4bnRzZGdtXmFuZHR7coKHfY2Ifo6EeH6I e4KCe4R1b3huZ2lkXV9kX290b396dYR/eomEeoB9c3lyaGlzaWpzb3V6d32Ce4R6dH15aXVyYm5w Ymd5am96b3h9cnqEeH6AdHp6cHdyaG5yYlpuXlZtX19tX19vXWF0YmVzYl5uXVpuX2RvYWV1ZGN9 a2p/anKAa3OEb3mCbXd9Z2t1X2RyXV9zXmF0Z2R1aGV0aWNtYlxzX19vXFxuXV5uXV55Z21+a3J5 am93aG10Y2R1ZGV9aWN+amR7aGh1YmJ1XFVrU0xjSj5hSDxlTkRqU0hrVElpUUdtTTpoSDVhRDFc Py1aPStiRTJhRDVnSTtpV0dzYVB5Y2qDbXSGcHWHcneGa2F7Yld0W05yWEx+YVd+YVd+XFB1VEhy TjlvTDdwV1B7YluHbnKJcHSIbXKGam9/YlZyVUlwTUNvTEF9cHdyZWtjXVhdV1NeW1VkYVtrYmht Y2luZGhvZWluZ2tuZ2tyaGl0amt5bXB6bnJ3a3d3a3d4bmRtY1poXUpqX01uYmNyZWd0a2pyaWhp Y15eWFRhVFRhVFRjWFdpXl1rYmNvZWdqaGtqaGtzaHB4bXV0bXJ0bXJza3B0bXJ1bXRwaG9wZ21z aW9yam9vaG1uZW1vZ250anBzaW9oaGVpaWduYmNvY2RtZWhyam10bWN3b2V5b3B3bW55bW5yZWdq al5ra19vY1thVU1iWE9kW1F3c3l7eH5+d4Z9dYR4cHV6c3h4eIiCgpJ/go54eod7d4Z7d4aCf42A fox+eXpvamtyaWhyaWh3c3l9eX97en94d3tua21lY2RtZGtzanJ1bXR3bnV3cHd6dHqAfYZ6d394 eX1vcHRranRpaHJtZ3JvaXR4bXiCd4KCdXd+cnN4amhrXlxvYmJzZWVvZWdyaGlqaGdpZ2VpY15b VVBdW1xkYmNtaXJ3c3t5b3V3bXN9cn19cn19eoiCf42Gf4aCe4KDd3p0aGtjW1dqYl5vZ259dHt9 dIB5cH10bXJpYmdqZGp4cnh/eIiCeouAfYaAfYaAfoJ1c3dvZ2VnXl1nX2Rza3B6d4KAfYiCeYB5 cHh5b3NzaW1ybnd6d3+Ed39+cHl6bXh5a3d4a294a296c4N9dYaCen99dXp7bnd3aXJ1aGV1aGVz Z2pyZWlzZGlzZGlyYV9wX15uX2JwYmR5aGd7aml6Z2R/a2l6bnJ4a299aG91YWhuWl5uWl5wYmR1 Z2l0anB1a3J5am9yY2h0Y2JyYV93aG14aW55aGd0Y2J0Y2J4Z2V9bnN/cHWAc3N5a2t3XlFuVkll TT9cRDdhST9oUEZzWlZ0W1dzVUFuUD1qTDRkRi9kRzduUD9vVU50WlNrW05wX1N6YWuEanWDbXKC a3CCX1R5V0x0V051WE9/X1d/X1d/XEx/XEx5VEV4U0R4XV+CZ2mHbm+JcHKIbXKIbXKAZ1yAZ1x9 Xk91V0h3c3tuanNjYlpcW1NjVlFjVlFlWFhtX19zX191YmJwZGhvY2dtaGtvam50bW14cHB6dH17 dX6Eb3R/am90aF54a2J4cHV5cneAcnd9bnNpYmJhWlphVlVjWFdpXWFvY2dzZ2p1aW1taGtrZ2p3 cHl5c3t/cn2CdH90cHlybnd5cnR5cnRyaGtuZGhzZGdzZGdtZWhoYWNuX2RuX2RqYWJvZWd0aGl4 a210b253cnB6dXd/ent/eX9+eH56en15eXt3c21ybmh0aWNtYlxuWlpoVFR3a3l/dIKAeIR/d4N6 c3V6c3V/fY6DgJJ/eId4cH9zb3p4dH9/fYuCf42Ae39zbnJ1a215b3B4d4B9e4aHeYJ+cHlwaWlk XV1oYWNpYmRoZWlpZ2prZW5tZ291cnp0cHl3dXp0c3h3b3Ryam9oZGpoZGpwb3mDgox/gpB+gI6C f4NzcHR3am53am5vbnNvbnNzaHB3a3RvaGhkXV1rZGlza3B6cnt+dX+Ad31+dHp7dYB/eYR5eIJ/ foiIfpCHfY6Hd4Z6anlnXFtpXl1kaGtwdHh7eoR4d4Byb3BnZGVoX15uZWR1bXl/d4OCfoeCfoeG gIJ4c3R3am5wZGhnX2RvaG15eIJ/foiEe4iDeoeAe395dHh0bXJ4cHWAc4B9b314b3d0a3N4a3J4 a3J6c4N9dYaAeIR7c39+a3R4ZW5zaW1zaW17bXJ7bXJ5ZGl0X2RvW1ttWFhuYV5tX11yYV9uXVx4 X157Y2J5Z295Z293Ymd0X2RyXV1uWlp1YWh+aXB7bXR6a3N4aW50ZWpyZWdwZGV5bXB7b3N9a2h3 ZWJ5ZGR+aWmAbneDcHmEdXh7bW9+ZVdyWkxqSjpkRTRlTkRtVUpzXlx0X114W050V0pvVT5wVj9v Vk91XFV9YmKAZWV6Z2F5ZV+AZGmHam+HbWl+ZGF+YUp6XUd1X1F9Z1iGa2iHbWmEalx9Y1V7XUx9 Xk1/ZGGEaWWGameJbmqIb3CEa22HaGKHaGKHZFqAXlR5bnl4bXhwZWRlW1pjWlBkW1FpX2FrYmNz YmF1ZGNzYmNyYWJpX2NtY2d3aWd6bWp7cH6AdYOCeH5/dXt5cnJ1bm6CdXuDd32DdX5+cHlwZ2pr YmVrY2JrY2J0Ymh5Z211aW94a3Jwa21wa215c357dYCAdYCCd4J3dX13dX11cnh1cnh1bnNza3Bw bXVuanN0ZG50ZG51Y2dyX2N0aGl5bW53b3R0bXJzc3V1dXh6eHt/fYCDfYaEfoeCgIt7eoR6eHt3 dHhzbm9uaWpvYWVpW193bX2Ad4d+eoN4dH16eHt9en51fYh5gIx1cnptaXJrY2p0a3N9e4Z/foh6 eX5ycHVyb3N3dHh4d356eYCEfYx+d4Zwb3RoZ2tvXWNvXWNrX2NpXWFtW15uXF9wY2t3aXJrb3Vw dHp+eX11cHRqZ21kYWdvb3uAgI1+g5J+g5KCfol7eIN0bnd1b3h5dX55dX5zanJzanJ3am5wZGhw aG93bnV4dH97eIN7dXt9d32Cd4SCd4R/eId9dYR9eox+e42Eeot5b39uZW1oX2dlY2Ryb3B7dYB+ eINybW5taGltX1tqXVhnYmN1cHJ/eIeAeYh9eHt3cnV1a29uZGhoX2twaHR7eIOAfYiHfoiGfYeC e4d6dH93b3RwaW5zbXh0bnl3a3R0aXJ0bW9yam11c4J6eId6c4Z1boB0YmhuXGJyXGF4Ymd6bW14 ampzZWNlWFZnUUZoU0dpWlFrXFRuXldrXFVwXFp0X119ZGWCaWp5aGl3ZWdwX2FtXF1yY2h6a3B9 bnN7bXJ/am17Z2l4Y2V4Y2V7bW97bW97aGh0YWF0ZW19bnWGcHiGcHiEbnN/aW6AZ1p4XlFyU0dp Sj9uU1N3W1uDY2SDY2R9Y1h9Y1h6XVF5XFB0W1Z5X1t9ZGh/Z2p9ZGV+ZWd/Z2iDamuDb2h9aWJ5 YVN9ZFZ/a2WGcmuNcneMcHWMamiIZ2R/YlZ/YlaCY12CY12CY1+HaGSDamuGbW6EaWSEaWSGY1h+ XFF+c4B5bnt0aGtyZWlvYmJyZGRtam5vbXB3aG13aG1vZWdrYmNlXl5lXl5qX1xwZWJ6b32DeIaI eoODdX55d3h6eHl/dH+AdYB9d394cnp4aWt0ZWhwa21wa211a296cHR7a3V5aXNza3B4cHVzcnt5 eIJ5eH94d354d353dX16eX54d3t3dX9ycHp0dIRycoJ3bnp1bXlyaG5tY2lyaGt3bXB6dH15c3t5 dHh3cnV5cnJ7dHR6d3+AfYaCeomCeol9eIh5dIR0c3p0c3p0aXJtYmp5bneAdX5/eYJ9d39/d4CD eoR4foN4foN3dHVua21nYmNuaWpteH5yfYN3eXp3eXp9eHl4c3R4cnh5c3l7eYt+e416eIZ1c4Bz aW1wZ2pyaGtrYmVqX15qX15lXWRyaXBzb3p3c35+c3t4bXVwb3dkY2prb3h5fYaCgpKDg5SAe4t7 d4Z3bX14bn56eYN7eoR4cnhwanB0am5zaW1zcHR1c3d1dHl4d3t9d397dX6Ad4iAd4h1cnp3c3t+ dIaEeoyCfYx5dINwdHprb3Vra2ttbW15dYCAfYh3cnNwa21qZGJoYl9nWlpyZGRzb3p5dYB+cnV6 bnJvZWdrYmNpXmlyZ3JydX53eoN6eYB9e4N6eoh7e4l1cnhuanBzZW5zZW5rYmhtY2lyaGtvZWlv ZXV4bn55cHp0a3VzYmFoV1ZqVVptV1x1YWV5ZGl4YVhkTkZjTDlkTTprU0xwV1BzXFd0XVh1Ylx9 aWOAaGmAaGl/a2t+amp3Y2FyXlxvYWV1Z2t0amt7cnN6bWhyZF91Y2d3ZGh9aG9/anJ6ZWp3Ymd6 aG57aW+Eb3eGcHh/a2t9aWl9YWNyVlhuUE1wU09yWF56YWd/ZWuAZ216Z2d9aWmCaWh9ZGN9Xlt9 Xlt/Y2OCZWV+Y2WCZ2l/amp+aWmDa2eDa2eGa2iIbmqMc3KNdHOUc3mQb3WEameDaWV/ZF97YVx7 ZF96Y16CY19/YV16YmGAaGeDZVx/YliAX0x6WkZ6and6and1a21yaGluYWlzZW53bnh5cHp4bXV5 bndzbm1wa2pzZ2pqXmJtX110Z2R4bXh/dH+AeIKAeIJ5dHh3cnV1cnh5dXt+eXp6dXd5cG14b2tz a3B0bXJ3a3R3a3R1bXR0a3NybXBvam5uaHNya3dwb3lzcnt7c317c317eIN7eIN4c4J0b350bnly a3dtaW9ybnRzaWp3bW51a3J+dHqAdYN9cn96a250ZWhzamlwaGdyb3NvbXB0anB3bXN3a3l6b314 dHp0cHd0bnRzbXN5b3N7cnV5dX56d3+AdYCCd4J5eYZ+fot7eoJzcnlraHBuanNzcnl3dX15eH17 en96en11dXh0cnVzcHRybX16dYZ6eYN3dX96dXd+eXqDeoJ4b3dvZ25qYmlzbXh6dH95d4R5d4R0 c3p4d351bXdrY21iY2dyc3d5e4h+gI2Ae4t0b351Z2t0ZWpqaW55eH16cHRzaW1yaGtvZWlwbXN0 cHd4cnh3cHd4b3t5cH15d4R+e4l0cnNyb3B5c36AeoZ7eoR6eYN6e394eX1zcnd0c3h/eouCfY1/ d355cHhyaXBtZGtnX2JoYWNya3RzbXV1bWtvZ2VwYmRtXmFoXF9oXF9qaXB3dX13dX14d354d4B6 eYN5d4R3dIJ7b3VyZWt0X2JrV1prWFtuW11tXWlyYm54anV4anV7amlqWlhlUVRqVlh1ZXR6anmD ZGJ4Wld0V0xzVkp1XVx3Xl11ZGV5aGl9aWmAbW2GcHCEb2+CcG2CcG2AaGd7Y2J1ZV53Z195a2t/ cnKDb299aWlzZ21vY2l0Z293aXJ4Y2p5ZGt9aG99aG+Gb3eGb3eDamt+ZWdzW1pvV1ZvVlF3XVh4 XmR7Ymh/ZWJ/ZWJ/ZW6Ga3SDaml6YmF+XmKAYWSDYl2GZF9+Y2N+Y2N7aml+bWuGam2Ha26HaXOI anSIbWiGamWGaWmGaWmDaWKAZ19+ZGF9Y19/ZWF3XVh3Wk50V0x3XVh7Yl1/YV1+X1x7W0l0VEN+ b3d9bnV4b3dyaXBnYV5qZGJ0Z295a3R4bXV6b3h3bXB0am54a3JzZ213ZWd6aWp4a3KAdHqAeoN+ eIB7dHd4cHNzbXN3cHd+eIB+eIB+cHl5a3RuamdpZWJuZ2tuZ2tuZ2tuZ2twZ2pvZWlqY2VuZ2lu aG5wanB5bnl6b3p6b313a3l1bXd0a3VwaHRuZXJyam91bnN5bm17cG96cn6AeIR/eId0bXtyZWd0 aGlvZ2VtZGNqZWlpZGhvam5wa29zbnJ4c3d1cnhybnRycHVycHV1b3V7dXt+eIOCe4eCeYN+dX96 eIZ/fYt7en96eX51dH50c31wbXh1cn1+e4uCf46Afox6eIZ0dHJycm9va3Jzb3V1c4B6eIaDf4iD f4iCf4N1c3dybndzb3h4cn14cn1zcntvbnh1bXd4b3lyaXBpYWheXVpubWl1d397fYaAe4tybXtv Z2VvZ2VqZWd1cHJ5cHp3bnhwa21vamt3bXN5b3V7dHl6c3h0cHlzb3h5eoN9fod7eXpzcHJybnR5 dXt6eod9fYl7d4Z6dYR3coB6dYSAe4x/eouAe394c3dvZWdoXl9pXl1pXl1oYWVwaW51cHJzbm9z aWpvZWdkXFtiWlhoZ25zcnl3dXp6eX53c3l1cnh0dIB7e4iEe4OAeH97b3BvY2RuXF9pV1toXGJu Ymh0aGt0aGt5bW5wZGVuWF1wW19wanVzbXh9aWd4ZGJ4YVt5Ylx6Z2d7aGh9aWeCbmuEcnqHdH2I dXt/bXN6bW1/cnJ/am1+aWt9aWt9aWt7bnd/cnqCc3p7bXR3a2pvZGNyYWJwX2FyX2V4ZWt6aG59 anCDbnOCbXJ/ZWJ5X1xtWlBrWE9vWFR1Xlp5X2h6YWl9YmR/ZGd/Z2qGbXCDaWSAZ2KDaG2Gam+Q cGqMbWd+Y155Xlp6YmF7Y2KCZ2uIbXKEa22CaWqEZWJ+X1x9YWODZ2mCamR9ZV+CZ2eAZWWCZFh4 W09yVUlzVkp3XlB/Z1iDaGN9Yl1+W0p4VUV+dHp7cnh5bW5zZ2huYmNtYWJtYWRvY2dzaHB3a3R0 bnl3cHt5b391a3t5bXN7b3V6bXiCdH+CeYZ+dYJ7cHl6b3h3bXN5b3V+eH5+eH6Hc4CDb31wb2tk Y19kXV9nX2JrY21waHJuZW1vZ25rZGlqY2hpY2trZW5waHJzanRyam9uZ2t1a291a29waHRuZXJ3 aXJ5a3R5b3N6cHR5dX56d399eX9raG5nXV5oXl9pXV5rX2FtY2doXmJqY2VuZ2lwbm9yb3BvbW5u a214c3d1cHR0cn99eoh/eol/eol7d4Z3coB6eod/f4x+fYd6eYN3dX1ycHhubXJ1dHmHgpGHgpF/ foh5eIJ0c3pycHhvaXJzbXVzdHp6e4KCfoeAfYZ9fX93d3lzcnlzcnl5c351b3pzbnJvam51Z2t4 aW5qY2VoYWNrXWJ4aW51dHt7eoJ1dHlqaW5rY2JrY2JqY2NvaGh5c3t0bndrZWtqZGp7b3WDd32D eoJ/d35zcntwb3l3eYZ7fot6c3N1bm5vb21zc3B4eX95eoB7d4Z7d4Z+eIODfYiEf5CDfo6HgId5 c3lqYlxkXFZpW11uX2JpYWhwaG93a3d7cHt3cnNzbm9kX15hXFtnZGhua291cnh7eH50c3hzcnd3 cHt7dYB7eX1+e397endwb2tzaGJvZF5tZWhyam13bW50amt4a294a294ZWt0YmhzbXVzbXV5a2l6 bWp5ZWN6Z2R7aGp/a26Db2+GcnKGc3mMeX+Cc3p9bnV6aG55Z215a2t7bm5+b3J9bnB/b3l+bnh7 b3N6bnJ9aWt3Y2VzWl9yWF5vXWFyX2N0YmV5Z2p9a2h3ZWJ6XVF7XlNrWkltW0pyV1p0Wlx5XmN6 X2R4Y2F7Z2R/Z2qGbXCJbW+OcnSJc32OeIKUe3eOd3J+a2J0Ylh7Yl5+ZGF/ZWuDaW99aGV3Yl97 XlN5XFB0W1d9Y19/Z2qCaW2CZ2J+Y15/YVF3WEl0VFN6Wlh/Z2WJcG+NdHWDamuCYU94V0Z5eH14 d3uAdHp+cnh5Z21vXWNwY2FzZWNwaGd0a2p1dXV4eHh9d4KCe4d7cHt4bXh5bXB7b3N9dHt7c3p6 b3p5bnl1a293bXB5cHh7c3p/dIJ7cH5va3RpZW5pX2NqYWRrZ2prZ2puaG5pY2lpX2VtY2llYmhk YWdnaG5panBvamtrZ2hvZWtrYmhtZHBqYm5vYmpyZG1vaGp0bW93bnh5cHp4c3Rzbm9uYmNrX2Fw Y2twY2tzaWpuZGVzYmN0Y2RvamtuaWpybXB1cHR1cnh4dHp1dH55eIKAfo17eYh4dYN0cn90c31/ foh/fYt6eIZ6e4J4eX95dX59eYKEf46Ae4t7eIBybnd0cHd0cHdvbW5yb3B4dYR9eol/e4d6d4Jv a3R5dX53eIB4eYJ4cn1zbXhuaWptaGluZ2luZ2lrX2NlWl1jWl9nXWN1bXd/d4Bwb2loZ2FvZGFz aGRtZWhtZWh1cnhva3JtX2hwY2t5cH2CeYaGfo5/eIh1c4Bwbnt1dYJ5eYZ5cnR1bnB1bnBza25v a3RuanNtbnRvcHd0c3p4d357eYiCf46Eg41wb3ljY1dfX1RtY2dyaGt0a3h0a3h7bXSGd36Ad317 cnhqZGJfWldqXl9vY2R0bnR3cHdva3JybnRya3d1b3p4dXl6eHt6eXVycG13aWl4amp6a3B7bXJ1 bXR3bnV0aXJ3a3R9aG97Z257bXR+b3d/cnJ/cnJ/bm16aWh6aGt7aW1+cnh/c3mEdH6JeYODdHd5 am13YmR0X2J5Y2h7ZWp7b3B6bm+AbneCb3iCbm6EcHCDbmt4Y2F5XltyV1RtWlNpVk9pWlNwYVp5 ZV51Ylt7YVx7YVx/ZWJ6YV13XVp0W1d7X2J/Y2WAaGuAaGuAanJ/aXCEaG2NcHWJd32NeoCOfXmI d3N9bWV3Z193Y113Y115YWJ9ZGV9Y1x4Xld1WE10V0xvVlN4Xlt+ZF+CaGN/ZVt+ZFp/Y1B7X013 WFZ/YV6CbXeGcHqIa25/Y2WAXFN6Vk1+eXp9eHl9d317dXt4aWt4aWt5Z215Z213amt7b3B4c3R4 c3R5c3t/eYJ4cnp1b3h0bW91bnB4b3d+dX1+dX17c3qAbnR/bXN3b3J4cHN0cHd1cnh+bnp7a3h0 aGtwZGhvaG1za3BwZ21wZ21qY2VqY2VqXmJqXmJrX2VrX2VuX2JuX2JtX19vYmJzXmVwXGNuX2dv YWhzYWR1Y2d5Z216aG55bW53amtvZ2NwaGR0bW9za251a21wZ2hzYWR1Y2dnZ2dtbW10b3N7d3p7 dYB6dH9wb3R3dXp/eYSAeoaDeoJ1bXRybnd7eIB9eol+e4t9fYl6eod6eYN4d4CCf457eYh7dX5w anN0b3Nwa29qaXBycHh9e5B/fpJ7eH54dHpwb3d5eH99e4Z9e4ZzcndqaW53ZGp4ZWtwZ2hrYmNq XmJoXF9lXmFlXmFuY2t1anNwY2FrXlxqY2V1bnB3bXNzaW91bXRzanJuZW9waHJ1a3+CeIyEeI2I e5F9eYJ1cnp7coJ9c4N5bW54a21zaW1tY2dnZGhkYmVqZWl0b3NvbnVzcnlwd4h4fpCCgIt6eYN1 b21tZ2Ryam14cHN/dH97cHt6cHSDeX2Ef4OCfYB0a2pkXFtqXGFyY2hvbW5vbW51a3JzaW9zanRy aXNybXB0b3N4bm9wZ2h9anB9anB3cnN4c3R5bXB3am5waWtuZ2l4ZGd5ZWh4anN7bnd/a2mAbWp4 bWd0aWN9ZGN5YV9+ZG2DaXJ7bnmDdYCDdHd5am11Y2lrWl9vYWN1Z2l6bnR7b3V6bnR5bXN9aGh/ amp+amh4ZGJ4YlZyXFBrVU1qVExpVlBtWlR1W116X2J9aGWCbWqDcnB6aWh4Xlp5X1t6X2KAZWiH anKHanKCa3CAam+CaW2Dam6Cb3WLeH6JeHSCcG2Abmh7aWN5X1x5X1x6YVx7Yl16YVp5X1h0XFFv V01yWlt5YWJ+aFqAalyEbWSCamJ/Z1h5YVNyV1N6X1uAaXWDa3iEZGiAYWR9W1NyUEh9dXh7dHd9 dHt+dX17dHR9dXWAcH2AcH17cHl9cnp4cnp5c3t/dH+Cd4J7cHl6b3h0b3B1cHJycHh4d355eH11 dHl1bXd0a3VzaW1zaW1wZ2pzaW2AanR/aXN6a3N/cHh9cn15bnl3bXByaGtrYV9nXFtkVlhnWFtq WFxoVlptWlxrWFtoV1RrW1dtXFhrW1dpV1ttW15zXmFyXV9zZ2h1aWp4ZWl5Z2pwamh0bmt0bW9y am1waWluZ2dyZ2V1amlraWpvbW56bXqDdYN6cnt1bXdqbnJydXl/foZ+fYSCen11bnBqZG13cHl6 c4OAeYl/fYx+e4t6eYB5eH9/fYt9eoiAeIJzanRqaGlraWpqa3RzdH2CfYyEf45zdHhub3N0bneA eoN/fYyAfo1yb3BpZ2hvZ2V4b25vbW5lY2RlXF9qYWRuZWRuZWRvam5vam5vZGNuY2JtaW95dXt/ en5+eX1ycnJvb29oZXNraXd4coh/eZCHfY2DeYl+eoN3c3t7c3p6cnl4b253bm13a2hyZ2NlX1tl X1toX2duZW1tZ3J0bnl5dISEf5CHgIl9d3+AdHh9cHR+cniDd32AeIJ7c313dHV5d3h+e3+AfoJ3 bW5pX2FqYWdwZ21yaGl0amt0aGtyZWluX2RrXWJqXF5wYmRyZWdwZGV4aXB9bnV4c3R4c3R0a2Vq YlxvYmJvYmJ1YWh6ZW1/anSAa3WCbmd/a2R9Z1t6ZFh5Yl14YVx6YmV+ZWl9bnV/cHiEb3l+aXNz YWRvXWFwY2N4amp/cHiCc3qDbXJ6ZGl7Z2d6ZWV3ZWR0Y2J7Ylt1XFVuV1NqVE9tVlFyW1Z4XV1/ ZGSHa2uNcnKMc3eIb3OCZ194XVZ3XmJ9ZGiAa3CCbXJ/amp/amp9aGV7Z2R/a26CbnB/am1+aWt6 Z2F4ZF5/YV1/YV15XleAZV5+Y1x6X1h+ZVt+ZVuCZ2OCZ2OAaFuCaVyGbmmHb2p9Y1h1XFF0VlN6 XFiHZW6JaHCIZGJ/XFp6VUhzTkF1bXR4b3d7c39+dYJ/eYKDfYaEeYKHe4R/dYZ9c4N/eYJ/eYKD dYCGeIN+dX19dHt0b25uaWhvaG1yam90a3N3bnVza250bW90ZWhvYWN1aGV3aWd5Z299anN7b3N9 cHSGcoKEcIB3cnVybXB1ZGFwX1xtWlptWlppWFVlVVFjU09jU09oW1ZpXFdqV05qV05pVk9pVk9u Wlp0X191Z2l3aGp3aG11Z2tyaGl1a214cHV5cnd4b2twaGRza2tza2t1b3V3cHd7c397c39yam9p YmduanB3c3mCgo59fYl+eXp0b3BwZWRzaGd1bXR1bXR6c4N+d4d6eIZ4dYN/e4SAfYZ+d3l4cHNv bW5ua21rbXBzdHh7eIB5dX5zb3pzb3p1b3qDfYh+eoZ/e4d7cnN1a211c3R5d3h1dXhvb3JqaGtr aW17d3h6dXd5bm14bWt0bW91bnB3eIB+f4iEgId/e4Jyb3Bwbm9ta3Vwb3l3dIZ/fY6Gf4iGf4iD gox9e4Z9c3d3bXB6cHR9c3d3b3JvaGppX2FpX2FuY19vZGFtY2lyaG5yZ354bYR9dYR7dIN7dX55 c3uDeIaGeoiCd4R5bntva3d3c35+fYJ/foOAd3ptY2dkYmNwbm93bXB0am5wYmRvYWNtXFhuXVpu YV5uYV51Y2l1Y2l1ZXJ7a3h6cHR4bnJ3bWNvZVxvYWVwYmd3Ymt7Z3B3ZGp6aG59a2p9a2p9ZWF7 ZF9+Y2OCZ2eDZ3OAZHCAbnd/bXV9aG19aG10YmhyX2V1YWV9aG1+aW6DbnOEa2p7Y2J1XWFzW151 Ylx6Z2F6ZWh9aGp7YVpwVk9uVUhwV0p3XF6CZ2mGcHWOeX6IdXmDcHSAaWN0XVdzX196Z2d/Z2iA aGl/ZWJ7Yl55ZGJ6ZWN9aGh/amqAa2t/amp+amR+amSAZV59Ylt6X1iAZV6DaGODaGOGaWmEaGiG amV/ZF9+Y16CZ2KEa2GEa2GAYVh9XVV5Wl15Wl1+X12AYl95Wk50VUl0Tz50Tz51a3J3bXN7cHl9 cnp9cnqCd3+DeIOEeYSGd4iHeImHeoCDd317cnV/dXl+eIB+eIB9cHJ5bW54ampwY2NzZXB0Z3Jy ZWd0aGlyZGJzZWN0aWV5bmp0aGl1aWp1a211a219b299b291b3V1b3V4bnR3bXN7amt1ZGVvYl1u YVxvYl9wY2FvYl1qXVhrWlBrWlBtXFtrW1pyXmF5ZWh3amt4a210aGtzZ2pzaGd0aWh+cHmCdH2D eX99c3l5d3h5d3h4dHp0cHd1cnh1cnhyaXBnXmVrZGd0bW96eId+e4t/dIJ5bnt3aG1tXmNtaXJy bnd4dH94dH9+eIB7dX5+eIN7dYB5cnd6c3hzbm9wa21qam1vb3J6dHp9d31ybnRzb3V0c32Dgox/ f4J7e35+dHh/dXl/e4KEgId9entyb3BqZ21wbXOEgImEgImAdHWCdXd7cHl7cHl6eX6Dgod/foZ1 dHt0am53bXBvbXB0cnV4dYSAfo2Df4iEgImAf4l+fYdta2hqaWV3b3R+d3t3dHhwbnJwanBtZ21y aG5vZWtwZGhyZWlwaW51bnN7dYB7dYB3coB7d4aDeoeAeIR5cndyam9zaXl9c4N+e4mAfox/en5w a29tZG51bXd3aXJ0Z290Y2RuXV5vXl1tXFtrXV9wYmR3ZGp1Y2lwZ2p1a296bm95bW55bmhzaGJv YmJvYmJ3YmlyXWR1XVx5YV9/a2WDb2mGcHCHcnKGcHWIc3iIcH+EbXuHcnmCbXR4ZGd0YWNuXVpv XltzYWR3ZGh+a3SCb3iAaGd9ZGN4YVt1Xlh3X1t9ZWF6ZWV5ZGR7Yld1XFFzWkx1XE54Y2qAa3N+ dX2GfYSGdHWAb3B+ZF15X1h0YVtyXlh9Y1+AZ2N+ZFp7Yld6YV17Yl56Y16AaWSAZ1+AZ1+CaGGC aGF9Y1x9Y1x7YVp+Y1yCaW2GbXCIbW+EaWt/ZF97YVx6Wld/XlyDZF6AYlx5X1V3XVN3WFN1V1F3 XVp6YV14WE51VkxyTTxyTTx3aG93aG95am95am93bXN6cHd5bnd9cnqDc32GdX+Cd3+AdX53dHV4 dXd4dH95dYCAcnl7bXR3ZWRvXl1rXlxuYV5rYmVuZGhzaW90anB0b3N3cnVyam1vaGprY2JvZ2V3 aG10ZWp1aHN4anV0cnV1c3d5cnd1bnNuZGVrYmNwZWJzaGRyZGJuYV5tYWJrX2FqYWRqYWRzaGd3 a2p9b215a2l5YV93Xl11Y2d9am6Ed4SEd4SEe4h/d4N/en6Ae396eHt5d3p6cnl6cnl0anptY3Np ZGVybW55c3mAeoCDd31/c3l0am5wZ2puanVybnl0c3p1dHt7cHt+c36AdYN7cH55cnR5cnR1bnN1 bnNvamt4c3SDeIB9cnp3cHd0bnRzcnl7eoJ5fYN4e4J9en6Cf4OIg5KEf46GfYR1bXRwaHJ4b3mH g4yDf4iEd3+HeYKAd3p9c3d7eX2AfoJ+eoB1cnh4bnJ+dHh6c3h4cHV9dYSGfo2Dfo19eId6eod5 eYZwbm1oZWR3b296c3N4eHh0dHR0a3N0a3NwaW5uZ2tvZG1uY2ttaXJzb3hzb3pzb3p3b3J7dHd+ dHqCeH55dHVtaGltZG55cHp/eouCfY2AeoZ5c350ZWp0ZWp6bXV4anN6aWhuXVxuXF9rWl1yXWJ4 Y2h1ZGNwX15wZWR0aWh5am9/cHWCdXd5bW5wZWJoXVpqXVhoW1ZzW153XmJ/ZWuGa3KJdHmLdXqL e4CNfoOMfYKHeH2IcneAam9zYmNtXF1qVlhvW11wY2FzZWN+bnh/b3l9aWt9aWt4X2F3Xl95ZV96 Z2GCZ2d+Y2N7ZFx6Y1t1XFV4Xld9aG2DbnN/dXl9c3d+amN6Z19+ZFp/ZVt7Yl54Xlt9Yl2AZWF+ ZGGDaWWIbW+Lb3KEb2+CbW2HamqIa2uHbWiGa2eCamJ6Y1t7YWGDaGh/bXCDcHSLb3KDaGp5X1N1 XE91WE97XlWCZ199Ylt1XU9vV0l0TkV0TkVzWFRzWFR7WEZ5VkR7Uz51TTl3bXNvZWt1YWN3YmRw Ymd0ZWp4anN4anODcHeDcHd6dXl4c3d3bm15cG9vb3J0dHeAc4B/cn91Z25wYmlvXFpqV1VkXWJu Z2tybXB3cnV1a29uZGhjXVteWFZkWlhoXVxuXV5vXl91Y2d5Z2p0bXJ1bnNwaWttZWhqYl5tZGFr amdvbmp1bWl0a2hrZ2hrZ2hrZGdtZWh3bXN7cniDdXV7bm57ZFx1XlZ0bXJ9dXp/fYyAfo1+eoZ/ e4eAf4d9e4N6en15eXt7eIN5dYB0aXJqX2hpZGN0b251dXh3d3l4dH95dYB4cHVza3BzbXVwanNu anNwbXV1bXR1bXR1cn13c359gISChomEfoSCe4J4cnp6dH1+eoZ4dH9zcHRtam5ybnd1cnp4d357 eoJ+e3+Gg4eGhI6Af4mDeIZ7cH54anOAc3uHgIyIgo2Df4iDf4h9e4Z3dX96eYN/foh+eoN/e4R4 dH19eYJ+c359cn2EeImLfpCAeX55cndzcH9wbn1qaGtraW14cnh+eH59eHt0b3NyaGlvZWdpYmJn X19jWltjWltkX2FqZWdoYl9lX11lZ11ub2V5b3N+dHh1b21rZWNoXF1wZGV4aHR+bnp/eId+d4Z/ cHV5am93am55bXB5b2VtY1pwXFxyXV15ZXB9aXR0aWhtYmFvXl10Y2J9aG+DbnWIdXuCb3V5aGdv Xl1qWlhpWFdvVlx1XGJ7ZW2DbXSEeXiJfn2Nf4iMfoeMeYKJd3+EbnV7ZW15YWJ1XV5vXl90Y2R9 anB/bXN9bXd9bXd3anByZWtwXVdzX1p5Yl15Yl1/YV57XVt0W1Z1XFdzXFd3X1t4ZF2Cbmd/b2d/ b2d+a1x/bV2IcGuIcGuLa2iIaWWEbWeIcGqJdHmMd3uSdX2Qc3qRdHmLbnOJameQcG2LcnWGbXCE Y15/Xlp+Xl+CYmN/a2WEcGqCaWh4X150VENuTj1wUE54V1V0YVp4ZF11XE5wV0luT0VrTUNyU0h3 V011V0hzVUZ4U0F4U0F1c3Rwbm9tZWVnX19rXl5wY2NuZGpwZ213b3J5cnR5cHh3bnV0aWV4bWlz bm95dHWAeYh/eId7cHl3a3R0ZF1vX1hrYmVwZ2pvbW5wbm9zaGRrYV1jV05kWE9nXVRlXFNjW1pk XFtrYmhwZ21zZ2pyZWlvZGFuY19rXl5wY2Nvamt0b3B5cnR5cnR0bW90bW9yam1za255cHp7c313 bW5zaWpvZ2FuZV9waWt/eHqDgoyDgoyCeYOHfoiDfo1/eol+eIN5c356eIZ+e4l7cnNzaWpzaW15 b3N6dH15c3t5dIN5dIN4dHpybnR4a29zZ2prZ2pwa29wa21zbm9wbXNzb3WAhI2Hi5SGh41/gIdz c3VwcHN6b3h9cnp6dHpwanBzbXNzbXN4dHp/e4KDgISCf4N9foR9foR/dH1+c3t6bXh/cn2DfYiH gIyCg4yDhI2AeYh/eId4e4R6fod6eX57en9+fYd+fYd9dH56cnt6eIZ/fYuCd3V4bWtqYWRpX2No YWFuZ2dzdH15eoN4dHpva3JqZVtpZFptXFhoV1RlVFdkU1ZjVVplV1xkXFhiWlZlYV9uaWhtaXJy bnd4b2lzamRpYV1oX1xpXWN1aW+AeoaAeoaCc3qAcnl7b3B5bW5/b2d1ZV1wXlh3ZF54bXV3a3Rz Z2puYmVqXV1wY2N7Z2uAa3CEdXp+b3R1ZGNoV1ZkUVFjUFBoUFFuVld3YmmAa3N/dXmEen6If4mH foiHen6Dd3qHcnl/anJ/amp+aWl7aW96aG53anB1aW96a256a254Z2h1ZGV1XFd3XVh5Ylx4YVt4 XlpyWFRwW09vWk5tVFBuVVFzXFd5Yl2CamSGbmiDb3KHc3WNeH2OeX6Od3KLc26Eb22Ic3CJc3iJ c3iNcHOLbnCNa2qLaWiEaW6Lb3SMcHOJbnCIZVqAXlN4Xlt6YV17aWN5Z2F5YVRvV0puTjtvTzxt T0xwU09wWlF1XlZ7XUl1V0RzU0RvT0BvU0ZyVUh1VEh0U0dzVEl0VUp6b3p6b3p7b3BzZ2huYV5u YV5rYmVuZGh1bnN4cHV7cnh6cHd7bW95am10am54bnJ+cH6Ac4B6dHp5c3l+am17aGp0ZWp1Z2tw Z2hpX2FnXVRkW1FlW1drYV1rYVtlW1VkV1dqXV1yXml0YWtrZGdqY2VuY11rYVttX1t1aGNwbnJ7 eX1+fYR9e4OAd3p9c3d7cnV5b3N4b3l5cHp3a2p1amluaGVqZGJqY2V3b3J9e4OAf4eDeIaIfYuH gIyEfomAeoN+eICCf5F/fY6EeYKAdX54cnh7dXt6eYB1dHt6eYB7eoJ/dH93a3d4aW53aG1/anKC bXRyam1vaGpuZ2t3b3R/fYuGg5GEgIx7eIN1dXVubm50b3N6dXl1dHlzcnd4cHV0bXJzcnd6eX5/ eX9/eX+DeX+Ad31+dX16cnl7cH5+c4CDfo6Ae4yEgpGIhpWGfYmEe4h9eYKAfYaGgoiHg4mEhox9 foR5cnd5cnd3dXqCgIaAenhwamhqXlNrX1RoYltuaGF3dX9+fYeAe31zbm90aWNwZV9vZWdvZWdq XGFlV1xjVlRoW1hfW1xhXF1nXWNtY2lwaHR3bnqDcnOAb3B3ZWRuXVxkW15nXWF0c314d4B9eYR/ e4eEdXh+b3KCbmt/a2l5aGR6aWV9c3l4bnR0aGtvY2dvXl90Y2R5ZWN+amiAdHp9cHd1ZV5nV1Bj UUphT0hpT0hvVU5zYWR+a2+Cd3WNgoCQg4mMf4aJc3iDbXKEanCEanCAam9/aW5+aXB9aG99a216 aWp7aGh9aWl6Z2d4ZGR9Yl5+Y197Z2R6ZWN6Y110XVdrVkpoU0dtVElrU0huVFR5Xl5+ZGGGa2iH cHqHcHqEcHOIdHeIcGqGbmiEbnWEbnWDbnOEb3SCaGOAZ2KAZ2KGa2eIcnmJc3qJcHKIb3CCX1R0 U0d0WlV9Yl1/ZV54Xld+WkZ3Uz9uTUFyUEVrU0xwV1BwWExzW055WkR1VkByTkRvTEFyUUN0VEV0 UEZ0UEZ3UUV5VEd7c397c3+Ac354anVvaGpuZ2lwZGhvY2dzZ213anB6bnJ9cHR5b3V3bXN3am54 a293a3R3a3R5bnd+c3t/b3t/b3t3bXN3bXN3am5yZWlqWlhqWlhlWl1rX2NqYl5tZGFlXFNjWlBo WlxqXF5jXGFoYWVrYmNqYWJtYWJ3amt4b3eAeH97eoR6eYN7c315cHp4cHV1bnN0anBzaW9yaF5u ZFttX11rXlxnYmNrZ2h7d3V/enmCe4KGf4aCfol+eoZ7dXt7dXuCgo6GhpKIg5KJhJSCg4x+f4h7 eoJ0c3p4cn16dH97eIN9eYSAeISDeoeAeIR9dIBza3p0bXtvZ3N6cn6Afo2Cf456eYNta3Vya3Ry a3R5c353cHtzcHRyb3N1cHRzbnJ3dHh5d3p9c3l7cnh9dHt7c3p5dIN5dINybnl1cn2AfpCGg5WD gpaIh5uAeYh7dIN7dYCAeoZ/f42IiJaDgol6eYBycnJqampza3B4cHV9c3d3bXByZGJvYl9qYlxt ZF5zcnd9e4CAeIJ+dX9+cnh/c3l5dX5zb3hyZWtpXWNjWFVnXFhjW1VhWFNiVVVlWFhpYWp1bXd4 cn15c351a29wZ2pvZWluZGh3cHl5c3t9eIeCfYyEe4h/d4OEdXiAcnR/bm1/bm2Cc3h7bXJ5Z19y X1hwXFp1YV55ZWiGcnSGd35/cHh+ZF9uVVBhUD1cTDloTklwVlF3ZWSDcnCIeXuOf4KQgo2LfYiE cnh+a3KAbW17aGh9aWt5ZWh5am96a3B/a2l+amh9ZGV/Z2h9ZGN6YmF6ZWN+aWd+anWEcHuIc3V5 ZGdvV0ppUUVoUTxvWENvWFB0XVV+ZF+DaWSDbmuGcG6Db2l/a2WDaGSDaGR6Y299ZXKEb3KCbW97 ZF53X1p0YV5+amiIdHeNeXuIbmmCaGN9Xlh4WlR1XWGCaW2DaWR5X1t5VT9zTzpvT0p3VlFyWEp0 W016WE14Vkp1U0NvTT1zSEB0SUF4UDx7VD9zTzx0UD17Uz6AV0N9cHR7b3N4cHNza251a290am5v YmJyZGRzZWV1aGh6bnR9cHd5cHh1bXR1bnNyam91anV3a3d1bnN6c3h9dIB/d4N9dH57c317bnl1 aHNiXVNeWk9hWlxqY2VzaGRwZWJrXlxkV1ViVk5kWFBjVlRnWldrXlxtX11yZWd4a216b3h9cnp3 cnV0b3N4bWt1aml0aGt0aGt3bW5zaWpwa2pybWtyZGJtX11pYV1tZGF5bm2Cd3WEfoSIgoiDgol7 eoJzcHR1c3d6eId9eomHf5CIgJGGhI6Af4l+e31yb3B1bnN3b3R1cnp4dH16d317eH57eIN7eIN3 cHl3cHl6cIJ/dYd/eYR7dYB0b3Bwa210bnl4cn1+dHp5b3Vyam9uZ2tuZ2tyam94a297b3N7c3J+ dXR9dXp7dHl4dYN1c4Bwb3R3dXqCf42EgpCIhpWIhpV/eId7dIN7d4eCfY2DgJCJh5aEgpB5d4R0 cnNraWp4a219cHJ4cm91b21vZ2V0a2p0aWVwZWJvaHd4cH99dHt/d359c3d6cHRybnduanNwZWJo XVphVU1iVk5kWlZlW1doXFNtYVdqYmFvZ2Vya3dzbXhza3Byam90am5yaGt0anB3bXN5b39+dISA eIR9dICAdHp9cHeCcHKGdHWDdHl+b3R+aW54Y2hwXlh3ZF57bXKGd3uLeoR/b3l7Yl5tVFBnTkBd RThkTEdyWFR4ZHKJdYOOgIyQgo2NgISLfoKGb3d5Y2p3YmJyXV13Y2V1YmR0aGt1aW1/amh9aGV9 ZGh+ZWl5ZGJ5ZGJ6aWp9a22Da4KLc4mRd32LcHeCX1VzUUdrUTluVDtvV0l3XlB7aGKAbWd/amp/ amp+aWd+aWeCXlqAXVh9YmKAZWWAZ199Y1x1XVByWk1wXVRzX1aDbmuEb22CaGR9Y193YVN1X1F0 X2J/am2CaVt5YVN3XEV5Xkd9Y16AZ2J9Y1Z5X1N+VEx3TUVvTT10UUF5UEV9VEiAW0x6VUZ0TD50 TD55UDqAV0B6aWp4Z2h0bW91bnB5bXB4a29vY2drX2NzX2J1YmR5bW56bm94b255cG9zcHJyb3Bz aWp0amt0b3B0b3B3c3l7eH6CeYaAeIR+cHl1aHBiX1RhXlNqX2hwZW53bXN4bnR3YW1rVmJlUEVi TUFfT0BlVUZoWFBtXVV0ZWh4aWt5bXB5bXB4bWlzaGR3a2V0aWNwZ2p1a295cHh9dHt5dXt6d315 bW54a210ZWp3aG11bXd6cnuAfYaGgouGgJB6dYRzbXV1b3h4c3d6dXl+eXqDfn+Mg42Hfoh+e31z cHJyZWlyZWlza253b3J6d4J5dYB1b4Z1b4Z0b35wa3p4bn99c4R+dX95cHpvZWltY2dzanR6cnuD dHt9bnVyZGRtX19tYmFzaGd5am9+b3R+dX2CeYB/eX99d313dHhyb3Nybnl+eoaJhpGIhJCIg5KI g5KDeIB+c3t9eoiCf42HhpCLiZSGhI5+fYd6cHdyaG56Z3KAbXh9dXh3b3J1cHJ4c3R3bXBzaW1v ZWtyaG50bXKDe4CAeXt3b3JybW5rZ2hrXlptX1tpV1BkU0xpXFxqXV1vXWF0YmV1aGh0Z2duZGpv ZWtzaW14bnJ4bXh1anVyaGtwZ2p0Z296bXV7cH57cH54bXV4bXV+cnWMf4OGe3+CeHt5bm14bWtz Yl55aGR9b3iEd3+LeoR+bnh5X1hzWlNwWEdpUUBtW1V3ZF59cn2Lf4uNf42Nf42Gend+c29/am1z XmF0W1RzWlN1XFh4Xlt5Z2F6aGJ+amR9aWN+aWmCbW1+am1+am1+b3KDdHeLd4SLd4SSeXqMc3SD YVZ5V01vV0RwWEV3XFd9Yl1/ZWF/ZWGDaWWDaWV5YV93Xl17W1Z/XlqCZ2KAZWF+ZFp5X1V6V1N5 VlF0XFF3XlSDa2WCamR/ZGd+Y2V5Yl13X1t6YWuCaHOEZFqAYVZ7XFSCYlp/ZWKEameGZFCDYk5+ VESAVkZ3U0h5VUp9W1OAXlaCXlB7WEp3Tj50TDx6Tz5/VEN3ZGp4ZWtwaWlwaWl4ZW53ZG1vYWVv YWVvY2dwZGhua29vbXB0am53bXByb3Byb3B0am5yaGt1amd4bWl5cnR+d3mAfYh/e4eHeYJ3aXJy ZV1yZV13aG15am94b3d4b3d5bXNwZGpuWldrV1VoXlVqYVdzZWN9b215dHV3cnN6cHJ6cHJ6bW10 Z2d3Y2N1YmJvZWt4bnR4dH1+eoN/fYt9eoh9eHl1cHJ1Y2dwXmJkanJvdX2Cf46Cf46EeYR9cn14 bXV4bXV+d3t9dXp7dHl9dXqAdIaDd4h9ent0cnN3aWlyZGRqaGdraWhwZ2pzaW1vZG9wZXBtaHdv anl1bXR5cHh4bnJyaGtuYmVtYWR3Z3N+bnqAdHp+cnh3bm1yaWhvZWlwZ2ptbW90dHd9e4aEg42C fYyCfYx+d3t5cnd1bn1/eIeIh5uMi5+Eg4t+fYSDeIB+c3t6e4KEhoyGg5KJh5aHgpKCfY2AdX56 b3h4d357eoKGeoZ9cn13c3l+eoB+fn56enpyamprZGRwZGh9cHR6cHR4bnJ4a29yZWlyYV1rW1dt XE9qWk1qWlhyYV90Y2R1ZGV0ZWh0ZWhyX2h3ZG15a3d+cHt7cHt1anVyaGtwZ2puaW1ybXB0am53 bXB4a297b3N+dHqGe4KJfX6AdHVwamVtZ2JwX1x5aGSCcHKDcnODbmt7Z2R9Y2mCaG59Z2t4Ymdw XmR6aG6Ac4CDdYOHc35/a3d6amN6amN+amN7aGF/Z1x9ZFp3YWV9Z2uAam+Aam99aGh9aGh9aGV9 aGV5aGl7amt7amuAb3CCdH2Ed3+MeneCcG1/ZVh/ZVh9Xlh+X1p4YVt7ZF6EbWiHb2qNcm2Ha2d6 Y1FvWEdzWk13XVB/ZGGDaGSEal99Y1h7Vkd9V0h4XlR+ZFqDb2WAbWOCamV+Z2J6XVR9X1Z+XmSG ZWuGZ2SJamiGamqHa2uGameGameHZ1uIaFyEYU5/XEl7VkV7VkV7Wk6CX1SMYlaDWk59VEZ5UEN7 Vkl+WExlV1ppW11vXFpuW1hvW19vW19qXGFtXmNtXmNtXmNtYWdyZWtyYm51ZXJ6aHB6aHB5ZGt4 Y2p1aGV3aWd3bW57cnN/d4CCeYOCc3h5am91aGVyZGJwa2pzbm16dHp7dXt5dHV0b3ByZWl0aGt5 am19bnB7dHR/eHh7dHd9dXh9dXh9dXiHdHiCb3N6aWp0Y2RzaW94bnSEcICIdISGgJCEf45/ent4 c3R4ZV9zYVtrZXB1b3p+e4t+e4t+eH56dHp9cHd/c3mDeICDeICDdX6CdH19dH59dH57eX15d3p6 dG91b2pyampqY2NoYWNqY2VnYmVqZWlrY2pvZ25wa290b3N5bmp0aWVuY11uY111a296cHSAeXuA eXt7d3h6dXd5b3N1a29waGd3bm2DfYONh42Mho6DfYaDd32CdXt5cId+dYyHhpqLiZ6Dgox+fYd7 eIN5dYB0dXt7fYODgJCJh5aGgJF/eot5eoB3eH57en+Af4SAd3p7cnV5dHiAe3+CfoR5dXt5am1u X2JrXWJzZGltZ21vaW93amt4a214bWl1amd4ZV51Y1xyX1p4ZV93ZWJ5aGR0Z2d0Z2dyY2V0ZWh4 a3J/c3l+bnh4aHJuYmVwZGhrZ2prZ2ptZWVtZWVyaGl1a214cnp+eIB+dHp5b3VyZWdqXl9tWlN1 Ylt6aWp+bW5+a2V7aWN4a3J+cniAdHV+cnN7aW17aW2Cb3iEcnqAb3B7amt3aWR7bmmDb22EcG6G cmuEcGp6bXV7bneCa3iAaneCaG5/ZWt5ZV93Y113Yl90X115YWKCaWp/cHWCc3iLdXWLdXWRcm6R cm6Jb2iGa2SGa2SIbmeOeH+UfYSWgICMd3eHaVx+YVR1Vk57XFR+aG2Ca3CGameCZ2OCZFiCZFiA a26HcnSLd3eHc3OEamN7Ylt4Vkp5V0x3W1t+YmKEaGqJbW+Eb2+Eb2+Ha2eDaGODaVyEal2EZVR9 Xk16VkB6VkCAXFWEX1iMYViEWlF+VER6UEB7U0eAV0xrVVBvWFRpWlNqW1RuWlptWFhwWFpvV1ht WFtuWlxvWl5wW19vW2J0X2d0Ymh0Ymh7Z2t6ZWp0YmV0YmV9am6EcnV+eX19eHuDb297aGh1ZGN0 Y2JyaGl6cHKAdX6Cd39/c3d7b3N4a3J6bnR7b3N+cnV6eHl7eXqEeHuGeX2GeX+AdHqGeICHeYJ/ c3d6bnJ+a3KCb3WCbnmEcHuHeoyJfY6Cf4N3dHh1aGVyZGJqaGtzcHR7dYB6dH94dH14dH13bnV5 cHh7c32AeIKDeX+CeH5/eH1/eH19dHuCeYCIfn+DeXqAdXR3a2pvZWltY2doYWVqY2hwZGVtYWJz bm97d3iAd3p+dHh4cm11b2qDe4CCen9+e39+e3+Je4SGeICCen91bnNyYWJ0Y2R1eHmGiImHh4mC goR+dHh7cnV1a3+Ad4uLgpmLgpmDgI59eoh1dH5ycHpycHV6eX6DgI6Jh5WIg5Z/eo14dYN1c4B7 d4aAe4t7d3h4c3R4cHN9dXh/en57d3p4bWltYl5oV1htXF1lXF9oXmJzZGl5am94bnJ6cHR5bW55 bW54bWl7cG16b2l6b2l+amh6Z2R0ZWp4aW54bXV1anN5bXB3am5vYmJuYWFtYWJtYWJtY2dtY2du ZWRwaGd4anV7bnl7b3V1aW9tX1tpXFdrWE9uW1FvZGN3a2p5bm15bm1+b3SDdHmGeHOAc25+bW5+ bW59bnB+b3KAbWp/a2l3b3J6c3WEd3+GeICLeH6LeH6Ed4SGeIaJcn6Hb3uEaG+AZGt+Ymd9YWV6 YmF4X15zX2J4ZGd+aW6DbnOMd3mQen2ReoKVfoaUe3WOd3CIc3OIc3OQeISZgI2ahImSfYKIcGh6 Y1t7W1iCYV6Ga3eHbXiGam2Gam1/bm2Ab26IdX6LeICMdX2IcnmNaViEYVB9W1B7Wk91Wlp/Y2OA a2uEb2+Hb2mGbmiIamGHaV+Ial6HaV2JY1aGX1N/W1CEX1WGYl2HY16JZFp/W1B5UT11TjpwTkB5 VkhuXFZwXlhtX1twY151Y11vXVd0XFtyWlhtW15tW15tXF1uXV5vW1t3YmJ1aGh4amp7cG95bm1v Z2VwaGd0bXJ5cnd9eHl+eXqCcHJ7amt0aF90aF9zaWp6cHKAc35+cHt7bm56bW15bW59cHKAdHWD d3iCe4KCe4KGeICHeYKEeHmCdXd+eX2DfoKEd4KAc35/c3l7b3V6a257bW+Cc4SGd4h7eoJzcnlz a25waWtyaG55b3V7cH55bnt5cH13bnpwcHNwcHN1bXR5cHh7cHuAdYB/eId9dYR+d4aDe4uDfYaI gouLfoKEeHt6cHRzaW1wZ2puZGhuYmVrX2NuanCDf4aMf5WGeY57eIN1cn2CeIiEeouDeIOEeYSH gImIgouHg4l5dXtyXWJyXWJ0c3qGhIyHg4l/e4KAeHR+dXJzbXh7dYCHfZGLgJWGf4t/eYRycHht a3N1b3h7dX6AfoyGg5GIhpV/fYx0cHlwbXV+c36AdYB5d3h3dHV4cHN9dXh5d3V6eHd5b2VvZVxp XVRlWlBoW1ZpXFduZGh1a297b3V9cHd5cHh5cHh5cHh9dHuEeHmDd3iDdXN7bmt3bXB1a299bXl1 ZXJ3am50aGtwXV9uW11uXF9wXmJtYWJtYWJqYmFtZGN4Y2p5ZGt7aW10YmVuXFNoVk1qV1FvXFZ0 X2R6ZWqCbXR/anJ5a3d+cHuHc3WAbW96bm94a22Aa3CCbXJ+am2CbnB/cn+DdYOMeIaMeIaNeIKM d4CGeICGeICGd35/cHh/a259aWuDaGp/ZGd1YmJ1YmJ1Y113ZF57Y2eCaW2HcHiReoKQgIaSg4iS foCJdXiHbm+LcnORdYiafpGWgpCMeIaIcGiCamKAZ2OAZ2ODcHeCb3WHbm2GbWuJcHSJcHSJc3iL dHmMb2+GaWmLaF+EYlp5V1p5V1p0WF17X2SCZ2KIbWiLcGOGa16Eal+Eal+Eal+Ga2GJZ1uIZVqC YV6DYl+HZWGEY16OZFSIXk59UzlySC9ySThzSjlyYV13ZWJzaGd1aml1bWl0a2h6bW15a2t1amlz aGdzZWVwY2N0Y2R5aGl6bnR/c3l7dHd1bnBva2hwbWl0bnR5c3l7eIN6d4KCbXJ/am90a2p0a2p1 b215c3B5b3B3bW56bWp7bmt6bnJ+cnV6cnl6cnl9dICCeYaGeoOCd3+Gd3t/cHV7cHl+c3t/dH17 cHl7cnV3bXByampyamp/b3uDc395cnd4cHV3am53am5yZ291anN4bnR1a3J0anB1a3JtaW9taW9r ZGRvaGhza3B6c3h7eoJ6eYB+eIOAeoaEeouIfo6MgIyIfYh7eIBzb3h1anVwZXBrZGRpYmJuanWA fYiNhpWJgpF4dH93c35/eYR/eYSDdYOGeIaIgJGNhpaDhpJ5e4hua21qaGl3c35/e4eMhpGHgIyE fYJ+d3t0bW94cHN/dIKIfYuJg46Efol1dXVzc3N1bXR7c3qDfYaJg4yHhJZ+e414c3Jzbm14b3d5 cHh+eX19eHt1c3R3dHV5d3p3dHh4cGd3b2V1aV1vY1duXlZqW1NpYmRza256bnJ5bXB4bnR6cHd7 cnWCeHuGfYSEe4OIenWDdXB+c3J5bm17aHN7aHN7aW13ZGh1XVxzW1pvXF5yXmFuXV5vXl9vYl9v Yl9zX195ZWV3aWRyZF9wXVZwXVZ1Xlh7ZF5+am1/a26Aa3B/am9+b3KAcnSEc3SAb3CCcG2CcG2I dHeGcnSAbnSCb3WDbniGcHqHdHqGc3mIdXmGc3eGd3uIeX6Jd32AbnR+aWt6ZWh+aWd6ZWN1YWF0 X19wXVtyXlx1XFd/ZWGEbnOSe4CRgomSg4uSfniJdW+GZ2GLa2WNdYKSeoeSeoeMdICLcmeDal9+ aW6CbXKEc3SCcHKEa22GbW6IbnSJb3WJcG+JcG+Lal6IaFyCY2F/YV5/XFp7WFZ5W1h+X12GZGKI Z2SLaF+HZFyDZF6CY12EZV+HaGKHZFiDYVV+XViCYVyHY2GEYV6MYVCDWEh+VDx5TzhyTDNzTTR5 ZWh7aGp6anR7a3V6dHp5c3l+dHh7cnV5cnR4cHN4aWt1Z2l3aGp5am1+c3uAdX5/dXd5b3BzaW10 am53aG99bnV3c3l1cnh/cHV9bnN/cHV9bnN5b3N5b3N4bm93bW5+bWuCcG97cG97cG94bm90amt7 bW9+b3KGeoODeICJeHmEc3R6bnR5bXN9anB6aG55bm1zaGdtY2duZGh4aW59bnN9cHR+cnV7bmt6 bWpvZWlvZWl0aG53anB3am53am5zZ2pzZ2pvZWdwZ2hvaml3cnB+eX1/en6He4SCd3+AdX6DeICD fo6Ef5CAfox7eYd4cn1rZXBvZF5vZF5wb3R/foOMhJeEfZBua3pzcH96eIl5d4h+dYKCeYaHepCO gpeIhpWCf451c3Rwbm96cICDeYmLg5KMhJSHgId/eX90bXJ1bnN5cHqIf4mLhJCJg456e4R1d39y b315d4R/eYSLhJCGg5GAfox9eHd1cG9zb3V6d31/eYR+eIN5dXtzb3V4dXd4dXd6dXd6dXd7b3N1 aW1zYlVvXlFtYWRzZ2p4a215bW56aWh7aml6a26AcnSHe4mJfoyMfomMfomDeX1+dHh6aG55Z214 aWt0ZWh9ZGN6YmF0YVpuW1RpWlFqW1NvXFxwXV11XVx7Y2J1aGh1aGh6Y1t7ZFx5ZWV/a2uCc3iG d3uGc3eEcnWGcHOGcHODdHmGd3uNfoCMfX+Oe3+Jd3qAcnd+b3SCbnCEcHOAdHh+cnV6cHR7cnWG d36IeYCEdXp6a3B9ZGN7Y2J3Y1p3Y1p3XFxyV1dwXFpzXlx3Xl+AaGmEcH6Qe4mSf4aSf4aSeG2I bmN/ZGSHa2uMd3uRe4CRd32Nc3mEbWSAaWF/bm+DcnOLc26GbmmEaWmGamqHbm+Hbm+IbWiJbmmO a2GLaF2EZ12AY1qAXlR9W1B6Wld/XlyHZWOJaGWLZ2KGYl2AX1uHZWGHaV2HaV2JaFiCYVGAXFGE X1WDYViEYlqGXUiCWkWEW0SEW0SCXUmCXUl+b3SAcneCc3iAcneDeoKCeYCAd32Ad31/eHh9dXV5 b3N5b3N7b3V9cHeAdYN/dIJ+eoB6d311a29zaW11Z2t3aG1waWt3b3J+cniGeX+IeIKEdH6AdHh+ cnV7cHl5bneEcHCEcHCAc3N/cnJ7b3B4a217cnN/dXeHfYOGe4KEeoB9c3l5bmpzaGR3Ymd4Y2h1 YWFwXFxoWlxpW11vYWV5am+AcnSCc3V+cnN4a21vZ2VtZGN0amt3bW55cnJ0bW1zbm90b3B4bXV3 a3Rwbm90cnN7cnWHfYCHgoZ/en51bnN5cnd/eIiEfY2Hf5CIgJF7c3ppYWhoXVpuY191dH6DgoyJ fYOAdHpqZWRqZWRubXJwb3R0c3h4d3uCfZCHgpWRh5eGe4x9eYJ9eYKEe4iJgI2MhJSNhpWIgJGD e4x4cnp4cnp7eYeDgI6Lg5SMhJWEgIx7eIN1cn14dH+Ae4yGgJGIgJGHf5CGgIR7d3p6c4OHf5CI go2HgIyAeH96cnl7cnh+dHp+e3+AfoKGgIJ6dXd3aFpvYVNzYmN1ZGV5am16a26CaWqDamt9aG9/ anKDdYOMfoyNgJaOgpeNgISDd3p0amtvZWdyY2p3aG9/a257aGp4ZFtvXFNrWlNrWlNvXFVwXVZ5 Yl17ZF94aF96amJ/a2mAbWqAc3OGeHiEeoCJf4aHen6EeHuAcnmCc3qHdH2NeoOOg4yNgouSf4aI dXuEcHCGcnKCcnuEdH6Dd31+cnh7aW1+a2+DcHeHdHqEeHl+cnN+amF/a2KAb25/bm17Z2d0X19z YVt0Ylx9aG2DbnOJeYiNfYyQgIONfoCEcmR9al16Z2eHc3OLfoSQg4mQd3iIb3CEbWSAaWGAbW2D b2+Jb2uHbWmCZ2KDaGOIa26Ia26JbmqOc2+UdHKRcm+Jb2qGa2d+X057XUx9WleCXlyEZV+HaGKI Z2KEY16GYl+OamiMaV6Nal+LaVWHZVGDY1eHZ1uIZFaIZFaMZ1OMZ1OMZViNZ1qOalqUb16CdXmD d3qGeX2Dd3qGeoOEeYKHeYKGeICHeoCGeX+CeHt/dXl+dHV+dHV9cnp5bnd4eYJ1d391a21uZGVu YWFvYmJvaGh4cHCCdH+HeYSGe4KDeX+Cc3V/cHN+b3d/cHh/dH2Cd3+EeoCCeH59dXp6c3h+dHiA d3qAfYaAfYZ9e4B3dXp4b251bWt0ZWhzZGdvXltuXVppXVFoXFBvXWF+a2+HdHqJd317d3h4c3Ry am1yam15cnd7dHl4dXdzcHJ0bnd6dH16d4J5dYB1eHl3eXqCdXuNgIeIg4eAe395bXN3anB3bnh/ d4CCgIuCgIt9c3RtY2RpYmJza2t9eoiAfoyDdXV7bm5yampyampvam5uaW1zbnJ0b3ODe4uGfo2G gJGDfo5/fYuDgI6HhpCIh5GMhJWMhJWDgpZ9e5B5c356dH9+e42Cf5GDfpGIg5aGgot/e4R5dYB5 dYB+e4l/fYuGfo2Gfo2JgI1+dYKAdIeIe46LgoyGfYd9dH56cnuAbneGc3uDgoyIh5GOho2DeoJ+ b3R5am91bnB4cHOAcnmAcnl/dHN/dHN/cnJ7bm57c32Ee4aGf5aMhp2OhIuIfoR+bW53ZWduZGVz aWp/cnJ7bm59aWd5ZWN0YlxyX1pwYVpwYVp1ZGF7amd/b2iAcGmDbnOEb3R/d36GfYSGf4aIgoiG e4KDeX+Gc3mEcniAd3qHfYCIf4eMg4uQgIiHeH+Jc3iLdHmCeYaDeoeLd4KEcHuAa26CbW9+a3SA bneAc3ODdXWHc3CJdXOLdXWGcHB+amh4ZGJ3Y1x3Y1x9Z26Gb3eHdYiOfZCSfYSNeH+EbmJ/aV14 a2+CdXmJe4SQgouRdXWMcHCEal+CaF2AbWqEcG6JcHKIb3CIaWOIaWOQbm2Qbm2OcnKRdHSSd3OS d3OJdW+Hc21+ZVF7Y097XFGCYleDaVuDaVuEaWKAZV6CaGSHbWmLbV+Nb2KMa1+Ma1+Ia1uJbVyM aFeMaFeMaFWMaFWOamWUb2qUc2qWdW2AdHiDd3qAd32Ad32Hd4OGdYKIdH+JdYCIeIKHd4CEe4OC eYCAeXt+d3l9c3R7cnN+eIB/eYJ5cnJuZ2dtZGFvZ2N3bXB6cHR/eX+Ce4KHen6GeX2CdXl/c3d/ dXuDeX+Gf4iHgImEe4aEe4aAdYB/dH9+eX17d3qAdX6AdX6CeHt+dHh6bnJ6bnJ4a211aWpvZ2Nt ZGFwZFhzZ1tlZF5vbmh9c3l/dXt+dHh9c3d4bnJ3bXB6b3iAdX6AeX5+d3t3cHl4cnp0d4N0d4N7 en9+fYKAeoCGf4aHg4x+eoN9b3p5a3d3bXCDeX2GhIyEg4t9dXVyampoaW11d3qEg41/foh9dXV4 cHB/c3l/c3l5bXB6bnJ1c3d0cnV+foCDg4aDgJKEgpSCf46Cf46HhJSJh5aJgpGLg5KDfYh/eYR6 b3p6b3p7c4l/d42Dd4iIe42GfYeCeYN7c313bnh1dH53dX+AdYOEeYd7eIN4dH93coR/eo2He4R/ dH14bnR5b3V+cniEeH6Ng5WQhpeQiZKAeoOEd4J9b3p5bnd9cnp/eHp+d3l+eXqAe32Lfn+GeXqA dYN/dIKId42Ne5KMgI6IfYuEb3R6ZWp5Z2p7aW1+cnN+cnN+c3J5bm13aWd0Z2R1ZGF1ZGF7amuD cnOAcnR/cHODdHmEdXqDfYiHgIyJgIiIf4eEen5/dXmAbnKAbnJ/cnKDdXWIf4eIf4eQfYOEcnh/ cHiAcnmEeYeJfoyQe4eHc36GcnSCbnB/aXB/aXCCa3OLdHuMd36LdX2JeHmGdHWDb2l7aGJ5Ylp4 YVh6YmODamuGb36OeIeNeHqJdHeAbWN/a2J+b3SDdHmOeISSe4iQdHeJbnCAZ1+AZ1+HbWiMcm2J b2uJb2uMcGmLb2iLcnCJcG+NdHWReHmSeX2Qd3qIdG6GcmuDZ1aAZFSAY1aEZ1qGa16Eal2CaVyC aVyGamOJbmeNbWSObmWLbWGLbWGLal+JaV6OaFuOaFuIaVeLa1qScG6Vc3CZdG+adXCAcneCc3iD dYODdYOLdIOJc4KGd36DdHuDd32GeX+EeYSCd4KEd3+DdX6AeHeCeXh9eX9/e4J/dH15bnd4cHN5 cnR6b3p7cHt9eHmCfX6De4CDe4CAd3qCeHt/en6Ef4OHfoaIf4eEeoCDeX+DdX6Ed39+dX99dH5/ c3d+cnV+dHV9c3R/dHN/dHODcnB/bm17bXJ7bXJ/a2t+amp1aml5bm14cHV5cnd7cnh3bXN5a2l6 bWp/c3mGeX+Deod/d4N3cnN3cnN1c3d7eX1+eoB9eX95eH1+fYJ/eId+d4Z5cH10a3h1cHSDfoKD hI2HiJF5eoNvcHl3bniEe4aLhI2Ce4R9eYKAfYaGgouAfYaDe4CCen+De36AeXuDgI6AfoyDf5WA fZKGe42IfpCCgpCCgpCEfY1/eIh7cHl6b3h3aGpzZGdya3J1b3V6b3qCd4KHeYJ/cnp+a297aW11 anN5bnd9cHeAdHp3dHh4dXl9eX+CfoSGfn55cnJ4amp6bW19dHuEe4OIg5SLhpaUhIyLe4OIe4KH eoCEdXqGd3uIeXuJen2CfX6GgIKLgomJgIiNeoOIdX6HcnuLdX+Mg4uIf4eHen55bXB5am2AcnSH eHqEdXh/dHB9cm56Z2l6Z2l9aWN9aWN+cnWEeHt+dHh7cnV/cnqDdX6EfomIgo2Lf4iMgImGeXp+ cnOAa2t5ZGR6Z2eIdHSJfomLf4uNeH+Eb3eEb22Hcm+IeoaQgo2Qg4mLfoSLdXiHcnSAaGt+ZWl+ aXCEb3eMd4CNeIKNeHiIc3ODa2d7ZF91Xlh6Y119ZWGDa2eLdX2Md36Mc3SGbW6AaWR/aGN9aG2A a3CIc3qLdX2Mc3eGbXCCZ2l/ZGeDammIb26Jb2iJb2iMdG6Od3CJdHKLdXOJdXiLd3mLdXiJdHeL b2uEaWV+YVV/YlZ/ZV6IbmeJcHKEa22La2WJamSEaWKJbmeNbWSNbWSMbmSLbWONbWSMa2OOZ16N ZV2QblyRb12Ub22RbWqRaGWRaGWCc3qCc3qEdICHd4OEd4KCdH+EcnqDcHmAdHqAdHqEd3+Ed3+G eX+Ie4KDe36EfX+DfYOHgIeHeoCCdXt6b3p6b3p1bXRzanJ3bXN7cnh9dXqAeX6GeX+GeX+Ce4KE foSHe4SEeYKDeX9+dHp+cnV+cnV9cHd9cHd7b3OCdXmAd3iCeHmEdXqEdXqAdX6AdX6GeX2EeHuI dXmCb3N7amt9a217cG17cG17bm59b294aW57bXJ9dICDeoeEeYKAdX56dXl0b3Nyb3B4dXd/eH1+ d3t6c3N1bm55a3d4anV0bXJza3Bwb3R6eX56eYCAf4d5dXtzb3V3bnqCeYaIgJCCeol7d4aDfo2H hJSGg5KDfo2Dfo2Gf4iHgImDf4uAfYh7eYh4dYSAdYCAdYB9eoiAfox/d4B/d4B/cnp7bnd1Z2ty Y2h3am56bnJ4cnh7dXuGeX99cHd5bW59cHJ6bnJ7b3N4bXV6b3h5d3h7eXp/foOAf4SAe316dXd1 a216cHJ+dISIfo6LiZGIh46NgIeIe4KIfoSJf4aIe4KJfYOLeoSLeoSAfoKAfoKHfoiIf4mIeIKE dH6AcneHeH2JgoeLg4iHf395cnJ7b3B+cnOMeX2MeX2Je3uCdHR7amt6aWp/amiAa2mCdXuIe4J+ d3l9dXh7bnmAc36EeouHfY2Lf4iMgImId3WAb256aGF6aGF5a2mHeXeHfoaJgIiMeX+EcniAbW+E cHOGdYKLeoeOf4SLe4CId3WGdHN6a253aGp4aW57bXKHcnmLdX2MdG6IcGqGbWuCaWh9Y16CaGN+ a2WEcmuJdHuJdHuLcnWGbXCAZ2KAZ2KCZW2HanKLcnWMc3eIbW+DaGp+ZF2CaGGGbWuIb26Nc2+O dHCNdHONdHOJdHKLdXOIc3CNeHWUdHKOb22GZ2GDZF5/ZVuDaV6Ha2iSd3OOeH2Jc3iIbl+EalyD ZVqLbWGObmKMa1+ObWiObWiLal+HZ1yGZV2GZV2Nal+Oa2GUa2OQaF+QZVqQZVqAcHqCcnuDdYCE d4J9dH57c31+cH5+cH57cnh6cHd+dHp+dHqGdX+IeIKDeoKDeoKCe4SGf4iEe4N+dX15bnd1anNy aGtwZ2p0aWh9cnB/dXeEenuHeYSGeIODeIaDeIaCd4SCd4SAd3h+dHV7dXN4cm9+cHB/cnJ6cnuC eYODfYOEfoSIeoaIeoaHfoiIf4mIgouHgImIfYiAdYCCc3V/cHN+d3d9dXV7b3N7b3N1aW99cHd/ dYaDeYmDeoSDeoR/eX95c3l4c3R1cHJ5dHV5dHV3am5zZ2pwZGVrX2FtY2luZGpwZW55bnd4cnh7 dXt4bXV0aXJ1aXp7b4B/eYSAeoaAeIKGfYeGg5GHhJKEgIyDf4uIfo6LgJF/e4d9eYR6dHp3cHd6 cnt6cnt4dYSAfo1/dYZ9c4OAc4B/cn93b3R0bXJ6a3B4aW5zbXN5c3l+cnV6bnJ1bXR4b3d9cnp9 cnp5cHh5cHh1cnh5dXuCgIaEg4iDe356c3V5cnd+d3uCeomEfYyEf46Ig5KJhIiHgoaLhI2IgouL eoeLeoeIgIaLg4iGf4iDfYaGeIaEd4R/dH9/dH9/d4OCeYaJg4mMhoyJgoKAeXl9c3eAd3qQgIaS g4iOgoaDd3p9b210Z2R6aG6AbnSJeoKJeoJ/c3R9cHJ4a297b3ODeICHe4SQgo2LfYiGd3l+b3J4 Z2V7aml+cnWEeHuIfYuJfoyOeYOGcHp+b3KDdHeIdH+JdYCOe4SJd3+HdXeHdXd+cnh4a3J9aG2A a3CEbnqHcH2EcmuHdG6GcnKEcHCEa22Hbm+DbnWEb3eJdHmIc3iIc3OGcHCAbWN/a2KDaGiIbW2N bmuQcG6Ha2d/ZF96YVODaVuDb2WHc2mQdXuQdXuRdHeMb3KLcnWOdXmOdXmReHuSd3KMcGuEZFyA YViDY2SLamuLb2+WenqWeX6Qc3iLal6EZFiCYVyGZF+MZWSSa2qScGuScGuIal2CZFd/YlaAY1eL a2WLa2WSbWOUbmSUaF+RZV16bXp9b311bXd3bnh3b3R3b3R6b3p4bXh7cG95bm14b256cnCAcnmG d36Cd4KCd4J9eYR/e4eAeX59dXp6a25wYmRyZGR1aGh1bnOCen+HeYKJe4SHe4eGeoaGeIOHeYSC eIiGe4yHe4SDeICAeX59dXqAd32Ad31/d4OCeYaAe4uGgJCIgouIgouMg4uJgIiIg5KHgpGNg5WE eox/e4eEgIyLhomHgoZ7e3tycnJyZWd0aGl4b3l6cnuCeomAeYiAeoB+eH59eHd4c3J1c3R0cnNw YmRvYWNoYWNlXmFlXF1oXl9vXWF5Z2p3bW54bm90aGt1aW1wYmlzZGt3bXB9c3eAeIKEe4aIhpSE gpCCfYCEf4OLgJGMgpJ/fYt5d4R7c314b3l5b396cIB3c4h9eY56d4J1cn19cnqAdX57eIN1cn13 bXBtY2duZGVzaWp1aWp5bW50anB3bXN7eYd7eYd6cn51bXlycHp4d4CEe4aJgIuEfX+AeXuDfYOG f4aCfYx+eYiCfoeIhI2If4mMg42LhIuGf4aEeoCGe4KGgISIg4eLhI2Gf4iHfYB9c3d4cnp7dX5/ eomCfYyGg5GLiJaRiY6EfYKAdYOHe4mQgpCShJKViI6NgId/cm11aGN9aG+DbnWMe4aMe4aAeXt3 b3J7amt+bW6DdHuNfoaOf5GJeoyHeHp+b3J+amqEcHCCc3WEdXiJe4SHeYKGcHiDbnV9bnN/cHWG b3eIcnmJdHmDbnN/cHWAcnd9bnV9bnV/cHWEdXqIdXuHdHqGeHWHeXeIdHSHc3OHbm+GbW6AcneD dHmIdHeJdXiLdXiGcHOEb2+HcnKHcm+Ic3CMcHCJbm6Ibmp+ZGF7Yl6CaGSCb3OMeX2MdXqLdHmL b2uIbWmJa3OOcHiUdX2WeH+UeHiOc3OHbWKEal+EaGiNcHCReHuWfYCWd3OOb2uLaVqIZ1eCX1WG Y1iHZ2iQb3CUeHOUeHOLcmOCaVuCY12IaWONdHWWfX6deHeXc3KUb2GRbV54aHJ4aHJyaGtyaGt1 aWp6bm9+cnh7b3V7bW97bW95b3B6cHJ6cHd7cnh+bnqCcn5/eX+AeoCEd3+Ac3t5b3B3bW55bXN9 cHd+eoOEgImHf4SEfYKGfoOHf4SHe4SEeYKEeYeGeoiIgouGf4iGeoOHe4SEe4ODeoJ/d36DeoKH e4mJfoyJf5CMgpKMho6IgouLh52Df5WHfoiAeIJ6eIaAfoyMho6Mho6Cf4N4dXlyZGRwY2N0aGt3 am5/c3d/c3eAeISCeYaDeX2DeX19dXh7dHd3amt1aWpvY2dpXWFpV1FqWFNjYlxubWd5cnR6c3V4 ZWt1Y2ltX19tX19wZGh9cHSCeYOEe4aDf4uAfYiGeoaIfYiHfY6DeYt4c4JybXt3cHd1b3V3cnV4 c3d6cIB+dIR3c3t3c3uCeYCGfYSHfoiDeoSAd3p1a29zYVtwXlhvZGF0aWV3am59cHR+eH6DfYOD eIN5bnl0b354c4KDeYmLgJGHfoiGfYeJg4yLhI2Ee4Z+dX+HgImJg4yJgIuMg42LhIuHgIeCf4OE goaEgIeEgIeEgpCGg5GMgoaGe397c3p7c3p5d4R7eYeDe46JgpWQg5WJfY6IeYuRgpSQh5SOhpKU iZCLgIeCdWt6bmR9aG+Eb3eLeoeMe4iGe397cnWAbnJ9am5/c3mIe4KNfYmJeYaNeXmIdHSDb3KD b3KDcHSEcnWJeYOIeIKIdXl/bXB+aWt+aWuCbXSDbnWEc3R+bW5/bXWCb3iAbnSGc3mGeX+JfYOJ d3qJd3qNe32LeXqJdXiHc3WIbXKIbXKCc3WGd3mJdXWJdXWNdHWHbm+IbXKLb3SHc3WGcnSIa2uH amqDaml+ZWR7Y2J+ZWR+a3SIdX6ReHuLcnWEaW6Ha3CHanSMb3mRd32UeX+Re3uJdHSJb2iHbWWG aWuJbW+OdHqQdXuRcm+La2mHZFyDYVh9XVGDY1eIaGmVdHWWeXuUd3mScGGLaVqIZGKOamiQd3ia gIKheXWZcm6RbVyQa1twZ2pvZWl0Z2J0Z2J6aGt+a2+Acnd9bnN9bnB9bnB5b3N5b3N7cnV9c3d+ a3KAbnSDe36De36De4t/eId7eIN7eIN6dYZ+eYmHfouJgI2Jg4mLhIuLgomJgIiEgId+eoCEfoSG f4aIgouLhI2LgoyIf4mHf4KGfoCHen6JfYCGgISEf4OIfo6Ifo6Dfo2Ef46GgJR+eYyAdX59cnp5 dX6AfYaNg5SNg5SGgo16d4J5ZWVwXV1vZ2N0a2h6c3N7dHR/dH+Cd4KEfoSIgoiCgoSDg4aAd3h4 bm91aWpvY2RtWlRqV1FoY2R7d3h/dXt6cHdyY2hqXGFnW1FoXFNrX2N9cHSAdYOEeYeAf4d6eYB7 d4Z/eomCeIl9c4R0a3NyaXB4cHV4cHV5d3h6eHl7c31/d4B6cn5/d4N/foiCgIuIh46Hho2EgoZ6 eHt7amd3ZWJwY2F3aWd+cHmGeICAfYaHg4yGgIR7d3p4cn14cn2AeYiMhJSHhJKDgI6LhpWJhJSH eYJ+cHl+eoaCfomNh42Nh42Lh42IhIuHgIeHgIeIfYaGeoOGf4iIgouQgo2OgIyDd319cHd7cHt/ dH+CdYiHeo2HfY2Ge4yMeo2UgpWNiZWMiJSUjJGNhouHe3V6b2l/Z2iEa22IeoOOgImNfoOGd3t6 a254aWt6c3WAeXuJen2MfX+SfoCQe36JdHmIc3iDdHeEdXiHeYeIeoiLeXh+bWt/ZGd+Y2WAbnSE cniGdHWDcnOCbXKDbnOCb3WHdHqHeYKIeoOIe32Ie32Jen2MfX+Me3OEdGuHbm+LcnOLdXqMd3uI d3OGdHCHbnKGbXCGcHCDbm6DbmuCbWqEa2qEa2qGaWmCZWWAZGeCZWiDbXSGb3eLcnCHbm2GZWmE ZGiEanCHbXONd36QeYCHeHqCc3WHa2iIbWmHbWmGa2iLb3SOc3iMbWeDZF5/W1R9WFF+W1iGYl+E Z26UdX2Xd32Uc3mVaWSJXlp9Yl6IbWmQdHeXe36adXCSbmmOZ12OZ11uX2dvYWh3Y2V5ZWh5Z29/ bXWAdHiAdHiAc3OAc3N9dHN9dHN9dXh7dHd6cHR7cnV/d36CeYCAd4d/dYZ+d4Z/eIeEeoyHfY6J foeLf4iIf4mLgoyEg42Eg42HfoiCeYOCe4eCe4eDfpGIg5aNhpaJgpKHhIiDgISDeICEeYKEeYKD eICDfYODfYODfYOCe4J/d359dHuDdHmCc3h7eH6Df4aLgoyIf4mAf4l9e4Z+cGt6bWh7cG99cnB+ dHp+dHp/eX+Ce4KAe4uGgJCIiZKMjZaHhIiCf4N7cnNzaWpuX2JwYmR5cH1/d4ODd316bnRqY1pk XVRqXlZuYlplXF1zaWp5bnl+c35/dH1+c3t6dHp+eH6AdYN3a3lvbW5tamt0b3N7d3p9eId7d4Z5 dYB5dYB9c4N/dYZ9eoyAfpCHhJaIhpeJhpGCfol+c3J6b257b3B6bm+AeYiEfYyGfo2IgJCJgIiD eoKAcneCc3iAe4yIg5SOh5eJgpKRh5mQhpeGdYJ6and6cnl7c3qGgo2MiJSGgouCfoeEfYyHf46J foeCd3+GeX2JfYCHhouGhIl+fYJzcnd5bXN/c3l+c36DeIN/eYSCe4eAeYiGfo2If4mQh5GSjJKM hoyMf4B+cnN+amSCbmiEeHuOgoaOgoaDd3p6bW14amp4bnKDeX2LgIKRh4iNg4mQhoyOgoaIe3+G d3uCc3h/cn+GeIaNeXuEcHOCaGSIbmqMd4CVf4mUfoaRe4OIdHeDb3J+cnOCdXeIeYCQgIiUgISN en6Qe3SQe3SNfXSJeXCJdHSMd3eJd3+Jd3+Gcm+Db22EaWmDaGiIaWOHaGKDaV6Eal+EaWSHa2eH amqDZ2d/Z2qAaGuCaW2GbXCJb2uCaGSAYl6CY1+CZWiGaWuIcneLdHmJcG+Hbm2Eal+Eal+Ham+E aG2Hbm+JcHKHblyAaFZ/XE57WEp6WliGZGOJbXeVeIKaeHWVc3COZ2GMZF6DZGKMbWqSb3SRbnOQ al+MZ1yMZ1+NaGFtXF1wX2FyXl53Y2N4ZWl+a29+b3R+b3R+cnN/c3R/dXl/dXmGeICDdX57d3h5 dHWCdH2CdH1+c3t/dH1/dH+AdYCDeICIfYaJe4SOgImLf4uOg46IgouJg4yHe4mCd4SAdYCCd4KE eoyMgpSMhpGJg46LgomEe4OAeoB/eX+HeH+EdX2Ed3+DdX5+dHh/dXl/eHp/eHqHeH+Gd36CfYCD foKLfYaLfYaCf4OCf4OEenuCeHl6eHt1c3d+c3uAdX59en5+e3+EfY2MhJWSkqGQkJ6Mi5WLiZSC f4Bwbm9uZGp0anB+eIN7dYB9eHtzbnJpZV9oZF5yZF9wY15vY1twZFx6bnJ+cnV/c3d+cnV/dH2A dX6Dd313anBpZWtpZWtwaW56c3h7eIB7eIB6c3h6c3h6c3V7dHd7dYCAeoaHfY6Jf5GJg46Igo2A eXt9dXh/c3mAdHqEfYyGfo2JfY6JfY6NhIyJgIiAd3p/dXmCfY2JhJWRh5uOhJmNhpmOh5qHeoB4 a3J1bWt+dXSDfo2LhpWMgImIfYaDe4uGfo2IeoOHeYKCd3+EeYKGhImGhIl/foZ1dHt0anB4bnR4 bXV9cnp9cnp7cHl7cHmAdX6GeIaLfYuNh5COiJGOgoiDd31+bW5/bm9/eHqJgoSOe4SIdX6CcG97 aml5am2DdHeMhImUjJGUjZaSjJWSgoyJeYODdHuAcnmEcHuIdH+MeX2Gc3eAa26HcnSNe46SgJSU hIyQgIiMd3mDbnB+b3J/cHOCdXuOgoiSe4COeH2OfXuQfn2Qen2Md3mNd3uOeH2Od4aMdIOLcGuE amWGZ2OGZ2OIamGJa2KEa16GbV+IaWOJamSHaGSHaGSAaGt+ZWmDaWWGa2iMbmKHaV2JY2KMZWSI aGuIaGuNcm6Ncm6Lb2iGamOCZ1+AZV5/ZGmDaG2AbWOEcGeHbV+CaFt/XVN5V011VFaDYWOLbnOS dXqXeW+SdGqJaGOLaWSLa2mQcG6ObmWMa2OIaVeHaFaIZVqJZ1twXVt0YV5zYmN4Z2h4bWt4bWt+ a297aW14bnR6cHd+d3uAeX6IeISHd4N+dX97c31+cnV7b3N4bnJ5b3N9cHR7b3N+dHiDeX2Me4aM e4aNf4uOgIyJgpGLg5KEfYyAeYiCd4SAdYOGeYyLfpGMg42If4mNfYeDc319dHt9dHuCdH2CdH1/ dH19cnp/c3eEeHuEe4iIf4yMf4aMf4aHfYCIfoKJfYCMf4OIf4eJgIiOf4eLe4OCfYB6dXl+d3uA eX6AeoOCe4SHeo2NgJSOjJuSkJ+QkJ6Ojp2JiJJ1dH50anB6cHd7dXt4cnh1bnB0bW9taW9ybnR+ dGp6cGd1a2J0amGAcnSAcnSDeX2DeX2CeYB/d36CdXl6bnJoZGppZWtvZ254b3dzcHJ4dXd5dHV3 cnN4bnJ6cHR4dXl6eHuAeX6De4CGf4aIgoiAfoJ9en6CeH6EeoCHgImIgouJfoyLf42Qh5SMg5CA e4t/eomAeoaIgo2MhJSMhJSMiJSMiJSHeYR9b3p6bm+CdXeGfYmLgo6LgIeDeX+AeoOEfoeDeIOC d4J/dH+AdYCDeYuDeYuAeH94b3d1a290am53am53am55bW53amt4Z2N4Z2N3anB/c3mGfYmMg5CL g4iJgod9cHJ5bW59c3eHfYCLfoSHeoCDb297aGh3aG2Cc3iNhIyVjJSXjpmSiZSRgomGd35/cm16 bWh+a3KHdHqIeIKGdX+Cc3WGd3mJfoyRhpSWiJGRg4yOeXuEb3KDbXKCa3B/c3eJfYCSe4OReoKO f4KNfoCUeYKRd3+Rd32OdHqMdYKMdYKJcm2Da2eGamOIbWWLcGuLcGuLc22JcmuJbmqLb2uMaWuL aGqGZ2SEZWOEZV+GZ2GOa1+Oa1+NamKOa2OObWiMamWIbWiJbmmGa2eDaWSDZF6AYlx6YVx+ZF9+ ZF+Ga2eIal6GaFx+X1B5W0x1U06GYl2NbW6Uc3SSc22MbWeHZWGIZ2KObWqRb22Mb1yNcF2HaVyG aFuGZ1WHaFZ/amqCbW17bXJ9bnN5bW55bW56bW14ampzaW13bXB6cHR5b3OEcH6Hc4CDdIaEdYd7 b3V3anB1ZGN3ZWR5bXB+cnV/dIKDeIaHeYKHeYKLfoSMf4aGgJGEf5CCfYx+eYh/eHp7dHeAdYCI fYiIg4eEf4ODd3p9cHR9bXmAcH19d4J/eYSAeoOAeoODeoeMg5CLhpaMh5eOhpKMg5CJgIiIf4eL f4uMgIyQhJCNgo2Ug42OfoiJfYCCdXmAdHiCdXmCfYCIg4eHfoaLgomHho2Mi5KNkJ6OkZ+RkJd/ foZ+d3l9dXh1c3Jyb25wa210b3B4bXV+c3uAd3h/dXd7d3qAe3+IfYuGeoiMgImLf4iGf4aAeoCA e394c3d3dHh0cnV1b3V9d317c3p6cnl6dXR5dHN4bnJ4bnJ7dHl+d3uAeoaDfYiHfoaGfYSCe4KD fYODeX+HfYOJgI2If4yHe4mMgI6LiJeLiJeIgJF+d4d+dIR/dYaGeoaIfYiMhpGMhpGHfYB9c3d5 b3B+dHV/e4KEgIeHf4R+d3t/dH2DeICAeIJ9dH6AcHp+bnh6cIB5b393b291bm50aGl0aGl6ZWp6 ZWp6a3B6a3B5a2l3aWdvYWV3aG1/c3mLfoSJf4OJf4N+cnh5bXN9b3iDdX6GeIOEd4J/bm95aGl7 Z26Ic3qLfpGUh5qSjJeNh5KQgIOHeHp/bm95aGl3bXN7cniIeIKHd4CEen6HfYCJfoeSh5CViIyS homRe3uIc3OCZ2eAZWWCaWqJcHKNeIKUfoiWgIuWgIuUgISQfYCRd32QdXuQdYCOdH+Hbm2CaWiG amONcmqQd3WQd3WQdXuRd32OeXuNeHqUcm+Na2mJY2KIYmGGaW6LbnOWdW2Xd26VdW+RcmuRcGiQ b2eMa2ONbWSLaWSIZ2KEZFp/X1V5WlF6W1N/XFeHY16IYl6LZGGAYVZ+XlSAXFGIY1iMbWeOb2mM bmSIamGGZGKDYl+MamiScG6Ucm2Ucm2Rd2uOdGmNbVaIaFGCc3WCc3WAdHp/c3l7b3V9cHd9bnN5 am95Z214ZWt5aGl7amuCa32Jc4SEeYSAdYB5bXB1aW13aG19bnN/cnqDdX6DeICDeICGeX2Ie3+M fYKLe4CGfo2IgJCGfo6De4x/en57d3qGeIaMfoyNgo2GeoZ/eHp6c3V+c35+c36DfYaGf4iJgIuM g42JgpGNhpWJgpKLg5SMg42Mg42HgIeIgoiJgIuNhI6Ng5SNg5SOhpCNhI6Jgod/eH1/dXmAd3qH fYONg4mHgIeHgIeHho2NjJSSkJ+VkqKSjpeIhI2Je4eGeIN+eXp4c3R0b3B3cnOAe3+Ef4OAfYZ/ e4SDfYaLhI2Lg5KHf46If4mGfYeCe4SCe4SEgIeEgIeAfYiDf4uGeICIeoOAeoaDfYiEeH6Dd317 d3p4c3d7dXt+eH59dH6AeIKDfYaCe4SEfYKHf4R/e4J/e4KJgIiLgomEeYKLf4iHh5WHh5WHe4mD eIZ/dH1+c3t+eIOGf4uGhIyDgomIfoJ/dXl6cHJ/dXeDeoSEe4aCeol9dYSGd36DdHuAeoN+eICD d31+cnh5bXB1aW11aGh3aWl3ZWR4Z2V9aG2Aa3CDc3+EdICCc3iAcnd0aGtyZWl6bXWGeICAfoKA foKGb3R7ZWp1aW16bnKCcnuAcHqCbXR7Z25+a3SHdH2Jf5SNg5eOhpCLgoyQfn+Id3iAa2t6ZWV0 ZWp6a3CCdH+Ed4KEeHuGeX2LeH6VgoiZg4uVf4eUenuJcHJ+a155Z1qAZ2KHbWiLdHmSe4CXgIuX gIuXg4aSfoCOeH+Nd36JdHmHcneHamqDZ2eIbmmMcm2UeH2Xe4CWen+UeH2Uen6Qd3qOb2mHaGKJ ZWGIZF+HamqNcHCVeXuXe36VenWQdXCUdWmOcGSNaGGMZ1+JZF2JZF2CZFd9X1N5Wk96W1B6WlV9 XFeEY16IZ2KEZFyAYViEZFyHZ16JbmmLb2qIamGGaF6EY16DYl2MammScG+aeHeaeHeheXWbdHCU b16MaFd5dHV3cnN6c3V9dXh6dXl7d3p/c3l5bXN5ZWh6Z2l4aW5/cHV/a3mEcH6CdXt/c3l1Z2lz ZGd1ZXJ+bnqAdX6DeICHgoaHgoaJgIiJgIiMf4OMf4OEfoeEfoeIf4yHfouEe4OGfYSEfY2JgpKO gIyIeoaHeH2Gd3uGd3uHeH2DgI6EgpCIhJCIhJCLhIuJg4mIf4mLgoyJfomMgIyIg4eIg4eIgoiL hIuLhI2OiJGMi5WMi5WHhouAf4SIfoKIfoKGgouIhI2EgImHg4yGhI6LiZSUjqKWkaWWkpuNiZKQ h5SOhpKGhIl9e4B5dXt6d32HfouNhJGMg42Mg42Ih5GHhpCIgouDfYaAeoN+eICCd4SJfoyNg5eQ hpqNhpmIgJSMho6Mho6Ifo6EeouDeIOEeYSDe4CCen+Ae39+eX1+dHh/dXmAeoOCe4SJf4OLgISI g4eDfoKHf4SGfoN/d4CCeYN/eouGgJGEfoSCe4KDd32HeoCAfYaEgImEg4iAf4SIe3+EeHuDe3uI gICLgoyGfYeHeIuHeIt+dX2GfYSEf46Ef46GgIR9eHt7aml1ZGN1YmJ5ZWVuZWRuZWRyaGt7cnWE eImMf5GMfoeGeIBzamlpYV90aXR7cHuAeoOCe4R+b3J3aGp3Y2V4ZGd3bXB3bXCAbnR3ZGp4ZG+E cHuLe46NfpGRfoSNeoCIenqDdXWDb21/a2l6a256a25+cniCdXuDdHeEdXiLdX+OeYOSfYSRe4OS eXiLcnCAb2J+bV99bWWGdW6ReHuWfYCUgIeWg4mVgoaSf4OQfYOOe4KNdHiJcHSJamSGZ2GIb3CL cnORen+Se4CXen+Ze4CSeXqNdHWEaWV7YV2DYl2IZ2KLbm6SdXWSe4CSe4CaeHWScG6UcGiOa2OO Z2GJYlyEYVyIZF+HZFyDYVh5XU16Xk56WlV7W1aCY1+GZ2OHZWOIZ2SGZ2SLa2mLa2mOb22LbWGD ZVqDX1uEYVyMaGORbWiXdXSbeXiZd3SUcm+Sa16JY1Z6b256b259cm5+c293dHV5d3iAdHh9cHR7 aW1+a2+Dd3qHen6Jd3qLeHuHcnt+aXNzZGl1Z2t6bnKAdHiDfoKHgoaGg5GHhJKLg5KLg5KMhImN houLg4iJgoeLf4iMgImMgI6Og5GHh5WIiJaQiZKLhI2Sf4aMeX+Dd3qGeX2EgIeIhIuJiJKHhpCJ hIaOiYuOg4yOg4yOg46Og46Mg4uIf4eMfoeOgImRiJKUi5WVkJ+Ujp6Oi5SJho6LgomMg4uLhpWE f46GhI6CgIuCgpCHh5WSjqWWkqiWkaKUjp+VjZ6Si5uOjZWLiZGHfoiEe4aNgJSQg5aMhJSHf46G go2Hg46GfoOAeX59b22DdXODfYiNh5KLgpuLgpuEfYyDe4uJgpKNhpaLg5KEfYx/d4CCeYOGeoiG eoiAeoB9d31/dH2Cd39/e4eGgo2Mg42NhI6Lf4iIfYaDe4CCen+CeH6Ad319c4R+dIZ+d4aAeYh+ eoOEgImIgo2HgIyEgId/e4KGe3+IfoKHg4mJhoyOhJaMgpSNgpCHe4mGgoiHg4mJh5WIhpSJhIiC fYB9aWt1YmR5ZWiAbW9/bm+Ab3B0am54bnKGd4iVhpeOho2LgomGdW59bWV5Z21/bXN/d4B+dX9/ am17Z2l6ZWN7Z2R5a2t9b2+Ab3B6aWp9Y26Ga3eMeYKUgImRfoSLeH6Ed3eDdXWIdHeEcHOAbW97 aGp/aXB/aXB/am2Eb3KHcHqHcHqMdX2QeYCOd3KLc26Db22IdHKLeH6MeX+Re4CVf4SOg4KQhIOS fYKSfYKQfYOOe4KOdXSMc3KOb2uMbWmIc3ONeHiZfX+ZfX+VeXuUeHqReHeSeXiEamV6YVx9XliE ZV+Gb3SNd3uQen2Qen2ScmeQb2SUa2OSamKOaV+LZVyGYl2HY16HY16EYVx7XUx1V0Z3VU16WFB+ X1qCY12Lam6NbXCQdG+JbmmLa2WQcGqHbWKDaV5/XVODYVaGaFuLbV+RdHSUd3eWc2iQbWKLZFWJ Y1R5am97bXJ9cHJ/c3R+dHh+dHiAcneCc3h/c3mDd32Gf4iGf4iMeYKLeIB/dHN1aml4aWt5am16 cHSCeHuIfYiMgIyLg5KJgpGLg5SNhpaOiJGMho6NhouMhImJgIiMg4uOhpCOhpCJiZaLi5eUjZSQ iZCMhImGfoOCeH6HfYOJgoeNhouLhIuLhIuOh4yOh4yMg5COhpKLh5KLh5KMg5CMg5CNhJGOhpKR iZ2UjJ+VkqGSkJ6SjJeMhpGSiZaUi5eQh46Mg4uJhIiCfYCGhI6NjJaUkqeWlaqVkKGNiJmLhpWN iJeQjZuOjJqRg5GJe4mLgo6OhpKLg5SGfo6DfYiDfYiIeIKAcHp9anB/bXODe4uMhJSEgpB+e4l7 dHR/eHiCe4eHgIyEfoeAeoN5c3l/eX9/d359dHt7c319dH57c39/d4OCeomGfo2GgJGLhpaQh5SM g5CJhIaGgIKCdH+DdYB9d317dXt4c4J6dYR+fYeCgIuMg4uHfoaDe4CAeX6EeYKLf4iMiJSQjJeS iJmQhpaJho6Hg4yMho6Nh5CQh5GRiJKQg4eIe395bm13a2p9cHSEeHuDc3+Dc394bWt4bWuAbXqS foyUiJGRho6NfoCCc3V/am+CbXJ/cHV/cHV/amp6ZWV4ZGd9aWuAb3CCcHJ/bXB5Z2p4Y2iAa3CJ d32NeoCOf4SHeH2AcneAcneHdHqCb3WDbmt+aWd9aGV6ZWN7Z2d+aWmDbnCIc3WMdYKNd4OMc3KG bWuCbXKGcHWMeYKRfoeRgoeRgoeXgoyWgIuUfoaOeYCOeX6NeH2OeX6Md3uLd3CHc22LdXqNeH2X e36ZfX+Zf36WfXuXfn2SeXiLbWOCZFt6X1t/ZF+Ca3OLdHuUd3eUd3eOcGeLbWONa2mMamiQbWSL aF+JZ1yEYleGY1iGY1iDYUx+XEd4U0R5VEV+XUyHZVSOa26VcnSZem2UdWiLa2WMbWeHa2SEaWKG Y1iEYleLaF2RbmOSdXqUd3uXb2eQaF+LYVCJX097aW9/bXOAcneCc3iCdH2Ac3t/dH2DeICEfYyH f46JiJKHhpCOf4eDdHt1bm5vaGh4a217b3B9c3eHfYCIgIOJgoSMg42LgoyLgJGMgpKOiJSNh5KM hoyJg4mJg4yLhI2Nh5CNh5CLh5CNiZKNjJSQjpaOjZeLiZSJhIaEf4CHfouJgI2Hf46Lg5KRiJWN hJGHgIyMhpGIh5GLiZSMgpSNg5WNhpaQiJmSi56UjJ+RkJqQjpmUiZqOhJWShpeXi52RiJCOho2I goiCe4KIgJCRiZmQiKeQiKeOg5GHe4mGgoiMiI6Rjp6QjZ2Qg5aIe46EeoyJf5GJfoeEeYJ/d4B/ d4CIeIeCcoB6bnJ5bXCDeYmGe4x+eX15dHh1b216dHJ/dXuAd31/eHp6c3V4b256cnB6dHJ5c3B7 c3J6cnB4b3d7c3qCeYCHfoaGgJGLhpaUiZuSiJqQiZCJg4mGeoaCd4J/eYR+eIN6cnt1bXd7cHmD eICMe4aIeIKDd3p/c3eDdYCMfomOh5aUjJuRi5SLhI2GhI6LiZSOhpKOhpKOg46ViZWXh5GNfYeC dXuAdHqAeoaEfomCfoR7eH55b2V3bWN+aW6LdXqMgI6QhJKJgoSHf4KHdXSGdHN+cml6bmV9aWd5 ZWN5ZGd/am2Eb3KHcnSCb3N+a297aGp/a26Cc3qJeoKUfoiLdX+CbmuCbmuEcnWDcHSAdGt9cGh9 aWl3Y2N3ZFt6aF6EbnOMdXqRfoeOe4SMfn6HeXmGcG6Hcm+Jd32Sf4aUgImRfoeWgpCVgI6WgIiS fYSOeXuLdXiMd3mOeXuNeXeNeXeQeYCReoKVe3qVe3qVf4KVf4KWgoSOen2ObmOGZVt/YVuDZF6C a3CMdXqRdXiRdXiMcmeHbWKJb2uJb2uMamWJaGOIZ1eHZVaJZ1yNal+LaFOCX0p+VER6UEB+WEyI YlWManWXdYCefXSZeG+Vbl6QaVqOamWQa2eMZ1+IY1yMaGeSbm2WeG6Vd22RbmWMaWGIY1iIY1iD bnWIc3qAeX5/eH1/dH9/dH9/eYKGf4iGgJCEf46Mg4uIf4eEeXiAdXR7b3B9cHKHenuLfn+LfYaL fYaGgo2JhpGLhI2LhI2MhJWJgpKOh5aOh5aNhI6NhI6LhJCLhJCGgo2EgIyIg5KNiJeOjJqQjZuS i5qRiZmSiZSLgoyJeYaIeISCeIiDeYmHe4SIfYaDgomIh46NhJGNhJGGhI6HhpCLhI2QiZKSiZaU i5eSjpqOi5aOhpCOhpCRh5eSiJmRiZmQiJeJhJSCfYyHeoyJfY6GfYeGfYeDe36Hf4KGh42QkZeW lKaUkaOMh5aDfo2DfYODfYOEd4KAc354dHp9eX+IeoaGeIOAe399eHt/dH+EeYR9eHd4c3J9cHR/ c3d/c3mAdHqAd3h7cnN5bmp7cG1/dHCAdXKCen2AeXt7b3N6bnJ/d36CeYCIfYuMgI6QhpeWjJ6W i5SMgImHfoiCeYOEe4OAeH9+dHp3bXN6a3B/cHWGd36IeYCEc3SEc3SCdXeGeXqMhpGRi5aOhpKJ gI2Jf5GIfpCLf4uQhJCQgouRg4yVgouOe4SIeIKLeoSLhIuMhoyNh42Gf4aEdGl9bWJ5a2l/cm+D eX+IfoSLgIeJf4aLfoKHen5/c2l3amFzYVtzYVt3ZGh+a2+Ic3iLdXqLd3mEcHOAa26DbnCEcnqL eICNeoCIdXuCcG9+bWuAbnKIdXmEd3SCdHJ9cm54bWl6aGGAbmeGcm+Ld3SRe4OVf4eVf4eQeoKM eHiNeXmQeoSRe4aUfYSSe4ONg4mNg4mUgISQfYCNdHOIb26McniUeX+Re4CRe4CQd3qNdHiQdHCO c2+JdHmSfYKagIKVe32NcF+GaViAaF2AaF2Eb3KLdXiMcmqLcGmHb2mHb2mNbW6Obm+Ob12Ob12N a1eMalaMa1+NbWGQaVeLZFODWkN7Uzx6VkOCXUmHZWSVc3Kee3eee3eXb2eUa2ORaGiOZWWJaV6I aF2LaWeQbmuUcm+ScG6Ra2KQamGNYlGOY1ODe4CIgIaGfYeEe4aDeICCd3+Je4eOgIyMgpSGe42C eYN+dX9+cnOCdXeGe32Jf4CShoyOgoiQgo2Qgo2LgJGLgJGJgpKIgJGIgo2MhpGLiJaLiJaRho6R ho6Lgo6Lgo6Jf5GJf5GMh5eQi5uUjJ+VjaGWjJ6Vi52WhpCRgIuNeYSEcHt7dHd7dHeCd3+JfoeM ho6Mho6NhI6NhI6GgoiGgoiLf4iNgouUi5eVjJmQjJWJho6JgIuIf4mIgJGNhpaMhp2LhJuIfo6G e4yCeYaGfYmCfoSIhIuNh5KRi5aJjJ2RlKWdlaiZkaWMhpGHgIyCfol/e4d9dHt9dHt7d3qDfoKH fY6IfpCLgomHfoaEgImEgImDg4Z6en2CeHuGe3+De36De35/fYB9en55cnJ+d3d/en6Ig4eGgouD f4iCd3N5bmp7dHl9dXqAeH+DeoKLf42NgpCXiZKShI2HfoaDeoKGfoOAeX56eHl0cnN+bW6DcnOI eX6HeH2GeHiEd3eEeG+LfnWJgIiOho2RgomJeoKEe4iEe4iNgIeOgoiOfoiSgoyOgoiIe4KGeX+I e4KJfomQhJCVh5CShI2MgHqLf3mId3WCcG+Ad3iHfX6Qg4eRhIiRhIuJfYOCdHJ6bWpzX1hyXld4 amqAc3OIeXuIeXuMeX+HdHp/bm2Ab26Cc3iHeH2Jd3qHdHiDcnOCcHKGcHOMd3mQfYONeoCId3h/ bm9+amh/a2mIenqNf3+SgoyVhI6UhIyRgomQen+OeX6MfX+MfX+Qen+NeH2IeX6Le4CLe36Gd3mH c2uAbWWGa3KQdXuNeoCQfYOOeniIdHKHbWWLcGmNdHiXfoKdf4KZe36IdWWHdGSAbmGAbmGIb3CL cnOMcmqJb2iGbmWGbmWSb2SVcmeSc2GRcl+QcGGRcmKQdWiUeWuUc2eObmKLaFOHZE9/XEl/XEmH Z16ObmWeeXifenmZdWqUcGWRbmWOa2OIbmOHbWKMammMammQa2mSbmuOZ12MZFuOY1OOY1OMgIyL f4uJe4eIeoaHeYSHeYSMfomMfomLeomGdYSCc3iDdHmDd3iJfX6Mg4uOho2Qh5GNhI6MgIyMgIyJ fY6JfY6LfpGGeYyAeoOJg4yHiJGIiZKRiJCNhIyHfoiIf4mEf46GgJCIg5aMh5qOjJ6OjJ6OiZ2N iJuSh5KQhJCMe4aCcnuCd3WHe3qLgoyMg42ShJCOgIyIfoSLgIeJgoeHf4SIfYaMgImNi5mNi5mO hIuIfoSDe4CEfYKJfoyOg5GJh5aJh5aIhJCEgIyHfouJgI2Ef46Mh5aNhpmOh5qMi5+QjqOOi6GM iJ6JfoyJfoyLf4iJfoeCeYCEe4OCfoeCfoeIe46OgpWNgo2RhpGLiZSHhpCGh4uEhomDf4aHg4mD f4aDf4Z/gISAgoZ7eHR/e3iGgJCLhpWQiJmLg5SCf351c3J5dHV7d3h7dHR6c3OAeoaEfomUhpGR g46EgIl9eYJ7eIN6d4J6d31zb3V5bmqAdXKJeoKJeoKEen6HfYCLfn+NgIKLg4iNhouQgIaMfYKC eH6DeX+Cen2EfX+IfoSLgIeHfYOCeH6DcHeGc3mIeIeOfo2OiJGQiZKJhIOMh4aQfYCJd3qJd3qL eHuQg4eViIyUiZCMgoiDeXp6cHJ6ZFh5Y1eCb3ONen6NfoaMfYSLeICCb3h/b2d/b2eEc2+HdXKJ dHmHcneJdHKJdHKMd3uLdXqQfYaOe4SQenqHcnKGam2Lb3KIe4KNgIeUg42Ug42Wg4ySf4iNen6O e3+OfXuQfn2MeX2HdHiLcHmOdH2Md3mGcHOAbmh/bWeDam6NdHiOeYCNeH+NeXOEcGqCZ1+Ha2SQ c3iWeX6Wen+VeX6Hc2mMeG6Nem2IdWiLdXWGcHCMcmqJb2iJaV6IaF2Nb2OQcmWObmWObmWRb2qR b2qSdGqXeW+WeGqWeGqZdGGQa1iJaFaEY1GIZVuJZ1yScG6Zd3SeenKZdW2RcmKJaluCaV6GbWKM Z2mLZWiSbWORa2KLZFWGX1CJXk6LX0+IeYyGd4mCcnuCcnuDb3qDb3qHd4OLeoeJfYOHeoCHfYCH fYCLgISNg4eJho6Jho6Rg4yOgImGeX+EeH6Ed4SEd4SDeIOAdYCAd32Jf4aJiJKNjJaRho6QhI2M f4aJfYOIeoaJe4eIf4yLgo6NhpWIgJCQgpCQgpCOhpKQh5SHg4x/e4R/foOHhouQiJeOh5aUhIyL e4OIeX6QgIaIg4eJhIiMg42Qh5GNiZWNiZWLgIeJf4aIg4eMh4uLh5KIhJCHhJSIhpWEg42CgIuG gJSIg5aMhpGOiJSSiZaQh5SNh5KNh5KQfpKQfpKLgoyMg42LhomOiY2LiZGIh46Df4iDf4iJg4yN h5CLhJCMhpGNiZWMiJSLhIuMhoyLgoyLgoyDf4iIhI2IhoeIhoeCgH2Dgn6Ng5SVi5uWkJuRi5aH h4d4eHh9b3iCdH1/c3d6bnKCdIKHeYeLgJGMgpKLfYt/cn96b3p6b3p7cnV5b3OCc3iGd3uLgIeI foSGe4KLgIeNhI6NhI6Mh4uNiIyNg4eJf4N/dXl7cnV/c3eDd3qEeXiJfn2GeHWAc3B+cnV/c3eD c32LeoSSiI6Vi5GUiJGUiJGOhIuHfYOEd3SEd3SShI2XiZKXiZKQgouMfX+AcnR5a2l9b22AdX6G eoONfYyLeomIdHeCbnCAcGWGdWqMeHWNeXeOen2JdXiHdXSHdXSOeH2OeH2QeoKOeYCOdXmHbnKH a2uLb2+Ie4KOgoiQg4SOgoOSfYSOeYCQfYCOe3+Lf36MgH+LdXqDbnOEanCDaW+EameGa2iAbWN+ amF7aGqDb3KNeXuRfX+SeW6NdGmHbmOGbWKObnSXd32Ve3+Zf4OVgICXg4OXfn2UenmReHuMc3eR cHKNbW6HaGKIaWOMa22Qb3CRb2qRb2qNbmqMbWmOcGeUdWuVdW+VdW+UdG6Wd3CUcm2ScGuMbmGH aVyRcm6UdHCQdHCMcG2Nal6JZ1uGaF6LbWOLaWeGZGKRbmWRbmWOZU6HXkeEXEeHXkmCcnuAcHp9 bXd+bniEb3eHcnmHeoCJfYOIfo6MgpKMg5CIf4yMfomNf4uLf4iNgouOgICHeXmEc3KGdHOAbneD cHl+dHiAd3qCdXmMf4OOiZmNiJeQh46Qh46Le4CHeH2EeHuEeHuDeoKEe4OGeoiHe4mIfYuLf42S h5KUiJSMiJSHg46JiJKMi5WQjJWMiJGOhIaJf4CJf4aNg4mOiJSOiJSQh5SRiJWSjJeRi5aLgJGL gJGLhpmNiJuOiZqMh5eCfomAfYh/eYKAeoOJfZCNgJSLg5SOh5eRh5eQhpaOho2LgomIfYuGeoiA f4mIh5GNh5KRi5aOiZmOiZmLh5KDf4uIg5SLhpaJhpGLh5KQhI2Rho6OhIuQhoyQh5GOhpCNh5CM ho6Qh5GOhpCDgISHhIiOiJSUjZmRjZaOi5SOiJF+eIB5cHp7c32Cc3qCc3qDb32MeIaJfoyJfoyJ fYOAdHp6a3B7bXJ9cHR6bnJ/dH2EeYKIgIaHf4SEfX+JgoSSiZaQh5SJhpGJhpGLhomIg4eIe32H ent+dHV9c3R/dHOIfXuIfXmDeHSDd3iDd3iEb3eLdX2Nf4iVh5CaiZaZiJWQhoyHfYODdXOJe3mS g5WZiZubiJGUgImLd3mHc3V/dXuAd32GeX+GeX+Hen6EeHuHcm+Eb22HcneMd3uNfYmNfYmSf4OR foKJen2EdXiJc3qMdX2OenqMeHiLcnOGbW6EcGqGcmuHeH+MfYSQen+Md3uMdXqMdXqQeYCReoKQ fYOMeX+Qc3WLbnB/ZV53XVZ+ZFqCaF2DaV5/ZVt7Z2eAa2uJdHeQen2RfXWOenONdXCNdXCQc3WS dXiRe4OWgIiZgoyZgoydf4SXen+Qd3iMc3SRcHeNbXOObWuObWuMcHWQdHmUdWmSdGiSb2eQbWSO bmWUc2qUcGiRbmWObWqRb22Rb22WdHKQcmiNb2WQbm2Rb26RdXKMcG2NaF2JZFqGY1eMaV2IaVqI aVqRameVbmqSaVGHXkeGWkSGWkSAc3uCdH17bnl9b3qEb3eJdHuIe3+Mf4OMhoyNh42ShI2OgImL f4uMgIyMe4uMe4uLfX1/cnKEcHCCbm59am5/bXB7b3OAdHh/c3mJfYOLhJCNh5KNhouMhImIeXuD dHd9c3R/dXeEeoCEeoCEeYSHe4eJgIiOho2Ui5WSiZSNiZKMiJGShpmViJuUjZmRi5aNiImRjI2R i5GQiZCUi5eUi5eSi5qSi5qSi5qOh5aLhIuMhoyRjJ2RjJ2OiZmJhJSAeXt9dXh9dXqAeX6He4SJ foeHf46MhJSOhJWNg5SNhJGMg5CAfYZ9eYJ7en+HhouRh5eUiZqSi5qRiZmMhpGGf4uHhJSJh5aJ hJSLhpWVh5CUho6RjJCOiY2NhpWQiJeUjJ2RiZqJgpKIgJF/e4SDf4iIf4yVjJmRjZmOi5aMiI5/ e4J4eHh3d3d/c3SDd3iIdXmQfYCJf4aIfoSGeICAc3t7aW19am56bnJ7b3OCdH+Je4eHf4KEfX+D fYOMhoyWi5mRhpSLgoyJgIuMho6Jg4yLf4iHe4SAeXt7dHeDdX6LfYaNfoCLe36LfoKMf4OHc3WH c3WGeICNf4iWiJGZi5SShoeIe32DeHeLf36RhJaWiZueiJKVf4mGd3mGd3l/eH1/eH2HeHqGd3mE c3KDcnCCbW2Dbm6Ic3qOeYCNgpCOg5GUfoaOeYCLdX2GcHiIbneMcnqHdHiHdHiCbnCEcHN+cG5+ cG6Gd3mJen2Oen2IdHeIcGuJcm2NeoCQfYOOfoiLeoSSdXiJbW9+ZVh1XVB7YleCaF2EaWWDaGSC aGSEameIcneOeH2Sf4ORfoKUeHqRdXiOc3OOc3ONeHWQeniaf4abgIedgIaVeX6Qc3WIa26JbW+J bW+NbmuOb22Oc3OWenqVeHiSdXWWb2uUbWmQbWSSb2eRbmKQbWGQal+Ra2GMbmKOcGSMb16Mb16Q bW+UcHOVdGuScmmQa12NaVuHYVGHYVGGZ1eGZ1eOamiQa2mSaFqMYlSMX0yNYU2Ee4OAeH99dXp+ d3uDd32GeX+LfYaOgImMh5aLhpWShI2QgouNg4mMgoiHgImIgouHfYB7cnV+cnV4a297amt6aWp5 a2l7bmt7bXKEdXqHeYKOgImOh4yNhouCe3d9d3J7eXh+e3qGfoCIgIOJfoeMgImQiZKUjZaSjJeU jZmOi5SLh5CWhpWXh5aViZWXjJeVjJaXjpmSkJ+OjJuRiJWSiZaOiZmRjJuXi52Uh5mNhI6SiZSZ kaKZkaKVjJaJgIt/fXt6eHd+cniEeH6CeHuAd3p9d32DfYOHfY6OhJaRh5mQhpeEfoR7dXt9eX+J hoyOhpCQh5GRiZqOh5eLgJKIfpCIg5KLhpWJhJWQi5uUjp6VkJ+RlKKMjp2OiJSNh5KUjJuQiJeH fYOAd314bnJ+dHiAd4eOhJWMiZeNi5mLh42AfYN4eHh5eXmEfX+GfoCLgIKMgoOIfYaLf4iIeIKC cnt6b257cG95bXCCdXmIeoOOgImNfoOIeX6IeYuXiJqbkp+SiZaLfoKGeX2IgouMho6Hho2Hho2L fYaGeICIeYCMfYSShoyRhIuUh42ShoyLfX1/cnKCdXmMf4OWiJSZi5aUh4uNgISIeoOMfoeOhJWW jJ2dh46Re4OCdXd+cnN9c3eAd3qGeHiGeHiAcnR/cHN/cHWDdHmMeIaOeoiNe46LeYyLeH6Gc3mE b2+CbW2Eb22Hcm+IeX6IeX6CdHR/cnJ9cnB+c3KLdX2LdX2Je3eCdG+AbmeGc2uGd36Le4OSg4uS g4uWgH6NeHWHbV+AZ1qEZWKIaWWHa2eHa2eHbWiHbWiJcHKNdHWMeX2QfYCXfoKVe3+Sd3OMcG2J cm2NdXCXe4CZfYKZf36SeXiOc2+IbWmDZ2uDZ2uEbWeIcGqMb2+Ud3eUeXSRd3KZc2uSbWWLamKM a2ORbV6OalyMaFeJZVWGaFyJa1+LbWGLbWGNa2mQbmuUcGiWc2qXcmiOaV+NY1eIXlOHXVGIXlOL ZVyLZVySa1qRaliUalWUalWHfoaEe4ODeX+DeX+DeICEeYKHfoiJgIuLhpmMh5qNhJGJgI2HgImE foeCfoeEgImCen97dHl/c3l7b3V6bWp7bmt6bW1+cHCAa3CGcHWMeX+Wg4mUiJSNgo2Ae32CfX6A f4SDgoeJhIiJhIiLg4aOh4mQjJeUkJuUjJuSi5qNh5CNh5CRiJWSiZaVjpeZkpuWlKaUkaOSkKKR jqGVi5uVi5uVjpqVjpqWi5mQhJKJf5CUiZqXkJ+VjZ2WiJGNf4h9ent7eXp/dXmDeX1/enl9eHd5 b3N9c3eDeIOOg46QhpqQhpqEgIyAfYiAeoCHgIeIgoiNh42MhpGIgo2He4eJfomJg46MhpGGgo2N iZWUjp6WkaGSkp+MjJmGf4uHgIyNh42IgoiAd3h9c3R6a26AcnR+d3uIgIaNh5KSjJeMh4uAe39+ d3mAeXuHfYOJf4aIhoeLiImJgIiJgIiJeYOGdX99cmt7cGp6bW2Ed3eEeoCIfoSJen+Jen+LeomX h5aXkKGRiZqJgH99dHOCd4KHe4eHfY2Jf5CIeYCHeH+HcnmNeH+Igo2LhJCRiJCQh46Nf3+Ed3eG c3eQfYCXiZWajJeUhpGNf4uJe4eLfYiOho2Ui5KVh4SMfnuAc3B9b22DdHeGd3mNen6LeHuEcniD cHeDcHeHdHqJeYaIeISJeoKDdHuDb29+amp9aGp9aGqGbmmNdXCMfX+NfoCLenOCcmp+a2+Cb3OL dX2Md36NeXmHc3OAbmiCb2mEb3mIc32OeISVfouagISXfoKRd2uJb2SGa2eIbmmDb2WCbmSCamKC amKAaGeHbm2Hc3WMeHqQe3WSfniQd2mGbV+CaWiGbWuOcnmQc3qSeXiQd3WRcm+MbWp/ZGF9Yl6E Y16HZWGGaWmMb2+Ld22MeG6ScmeNbWKJaV2Lal6Na1yMaluJaFaGZFOJZVeOalyQamGOaV+NaF2R a2GRaWOVbWeQal+Qal+OZFSNY1OIXk6GXEyIZVuJZ1yNZVyOZ12OaVWUblqGeoaEeYSLd4KIdH+H eH2Jen+LgoyNhI6Qh5SQh5SQgpCJe4mEeYeGeoiAeIKGfYeEeoCCeH5/dXt6cHd5b3N9c3d+cnV+ cnWDdHmGd3uJeoKQgIiUg5CNfYmHeoCMf4aMhJWNhpaJhpGIhJCLhomQi46SiZaXjpuRiZqQiJmN h5KMhpGQh5SUi5eUkZ+VkqGXkaiRi6KOjJ6LiJqWjJ2Vi5uWjZqUi5eQhJKLf42JgpGUjJuXjZ6W jJ2Rh42Jf4aCfoeCfoeEgoaIhomDgIJ+e31+cHB9b29/d36LgomMhpGLhJCHgImDfYaIe4KNgIeM goiNg4mEfoeAeoOCe4KGf4aMgIyMgIyIf4eQh46UlKGXl6WWkpuOi5SIfoSIfoSMhISGfn59dHB/ d3OAdHiGeX2GfoCIgIOIgouSjJWSiImLgIKDe3uDe3uHfoaOho2JiJCJiJCNhJGLgo6JeYOGdX9/ dHN6b253a2V+c219enuAfn+Mf4OHen6He4eRhpGWjp6RiZmJfX6EeHmAdHqCdXuHd4aJeYiDeX+A d317cG+AdXSHe4SMgImOiJGOiJGShomEeHuLe4CSg4iWi5mViZeMf5GEeImDdX6Je4SOh4mRiYya iISQfnqMc3eIb3OGdHWNe32Rg4OOgICJdHmCbXKEa22NdHWMenmLeXiMeHqEcHOAZV57YVp5Xlt+ Y1+Da2eOd3KUgISSf4ORfXqLd3SGbXCDam6DbnWLdX2OeH2Jc3iDb2mAbWeAa3WEb3mMdYSSe4uV f4SWgIaOen2NeXuLeHKIdW+GdGd/bmF+ZVt/Z1yDYl2EY16GamqNcnKOeXeRe3mSeW6NdGmIbWiH a2eHbXOMcniSd3eRdXWScG6LaWd/YVt/YVt+ZFqAZ1yGZGKObWqLdGWLdGWRb1+Qbl6NaF2SbWKO bmKNbWGRb1uLaVWOZ2GVbWeQbWGSb2ONaF6OaV+LamKQb2eOaV6UbmOValqQZVWHYk5/W0eJXlaM YViJZFuMZ12RZ1uWa1+Gd36DdHuCcnt/b3l+dX1/d36GfYeMg42OhpKMg5COfoiHd4CAc3t+cHl9 b3iAc3uGd36EdX2AdHqAdHqAdYCEeYSGeX+HeoCIeIKIeIKMeYKLeICHeoCGeX+Cd3+IfYaJf5GN g5WLhJCGf4uEfomLhJCMg5CRiJWOh5eMhJWLgomLgomVh5KZi5aVjZ2VjZ2Ui5eQh5SRhpGQhJCO hpCMg42NgpCJfoyMgoiLgIeLiJaQjZuUiZqSiJmOgImIeoOIfYuLf42MhpGQiZWJho6AfYZ9cnCA dXR+e3+EgoaMg42LgoyIf4eEe4OGfYeIf4mMg42Mg42HfoiGfYeHfoaJgIiMgoiJf4aGgJCOiZmV laKamqeUkJuNiZWLe4OJeoKHf4KCen2CeHmEenuHfoaHfoaIgoiLhIuMg42VjJaXjpmVjJaLiIyJ h4uJiJCMi5KWiJaXiZeUiJaQhJKLe4CEdXp6bm96bm99b2qCdG+Ie3+Mf4OGfoOGfoOHfoaOho2S jJWRi5SMgoaGe3+IdXuDcHd+cnh/c3l+b3R+b3R+c2+DeHSHf4KNhoiSh5CSh5CRg4yMfoeUf4ua hpGVjpeSjJWJe4SCdH2AcneHeH2Jf4aRh42Uh4uQg4eNeH+GcHiDdHeMfX+NhouOh4yNe32CcHJ+ bmOHd2uLfnWNgHiLfnWAdGt9Y1x5X1h1XFF4XlSDb22RfXqWf4eUfYSRe4OIc3qHanKAZGt/amqG cHCLeHuJd3qEb22CbWp+aWt/am2HbnKQd3qQeYCSe4ORe4aQeoSQe3uMeHiNdGeHbmGIZVuGY1iI aF+IaF+Ha2uMcHCIdHeMeHqXfoKReHuOc26IbWiDam6Ib3OScnOUc3SWdGSLaVqAXlaAXlZ6YVZ6 YVZ+Y1+EaWWHbWKLcGWSa1yRaluRbWqWcm+Wd3CXeHKUdGSQcGGQaWWRameRbmWQbWSNZV+NZV+H ZFyNamKIZVuMaV6LaFCLaFCEXk2AW0mDWkmEW0qCWEqCWEqMX1eSZV2AbnJ+a297bXJ7bXJ7cHuA dYCHeYSNf4uOg5GJfoyHeYKEd39/dHB6b2t7b3B/c3SGd3mGd3mEeHuGeX2He4eIfYiJgIiLgomO goiHeoCHdHiCb3OCcHKAb3B4bnJ/dXmEe4iIf4yNf4iIeoOGd3uHeH2IfYuLf42IgouHgImLg4iL g4iSiI6Vi5GVjJaUi5WSjJKNh42Lh42JhoyJgoeIgIaJe4SMfoeNhIyRiJCRjJ+Qi56OhJaMgpSG d36Gd36IfoSIfoSMhpGQiZWQh5SNhJGCd3+EeYKCe4SHgImLf4uEeYSCdXmIe3+LgoyQh5GQiZWM hpGDfYOGf4aHg4yJho6NgouIfYaJg46VjpqalJ+fmaWVjpeLhI2Ef4CEf4CHgoaCfYCGfoCHf4KG goiJhoyLhI2Nh5CLg5aRiZ2UkaOSkKKOjJuMiZmNiZWOi5aUhJaVhpeUiJGWi5SXhoSJeHd5bW59 cHJ/c3SGeXqHfYCLgISHf4SIgIaGe3+Jf4OVgouWg4yWg4ySf4iLfn+GeXp+c299cm57cnN9c3R+ eX2Ef4OLhomRjJCUiJGRho6QgouOgImRg4yXiZKVkJSUjpKNf3qGeHN+bWt/bm2HeH+Sg4uRho6R ho6QgIiIeYCEeH6NgIeQhJCSh5KRfXeJdW+CcG+LeXiQhomQhomOg3+EeXV7a2F1ZVt0YlV3ZFeC c3WOf4KXgomVf4eUfoCOeXuNdHWGbW57aGV9aWd+b3SEdXqHcnSHcnSGbmmDa2eDaGSJbmqLcHmQ dX6QdX6SeICSfXqRe3mScmWLal6IZVqJZ1uJb2SHbWKMcm6OdHCIc3WOeXuWeYCUd36QdW6IbmeH Z2iHZ2iNa2mQbmuUcGiMaWGGX1CCXE15WlF1Vk57XFSEZFyGamWLb2qUb2GUb2GUcm+aeHWbe3md fXqXeGiRcmKRbV6RbV6Sb2SQbWKJaFiEY1SDX02HY1CEXVSEXVSEXEeGXUiDW0aCWkV/V0WAWEaA VkaDWEiGXEmMYk9/cnJ+cHB9aG9+aXCCa3iGb3uJc3+OeISNfpCOf5GMfYSGd35/cnJ7bm56cHR+ dHiGeXqHenuIeX6IeX6Lf4iOg4yIhI2IhI2Ng4eGe3+AdGh7b2N9bWV6amN3aWeDdXOEeoCHfYOI foKHfYCHdHiIdXmEeoCIfoSJgIiHfoaIgoiMhoyRiJCUi5KXjJeWi5aQi46NiIyJhoyJhoyJf4aL gIeIf4mMg42Rh5uWjKGVkKGRjJ2LhIuIgoiDd3qAdHiCfX6GgIKLhI2QiZKRiJKRiJKJg4mGf4aI f4mNhI6Lg4iEfYKCeH6LgIeQiZWVjpqUjZaNh5CEgImEgImHhouMi5CMgoiJf4aMhJSWjp6alaWa laWRkZSGhoiIhICMiISMhIeJgoSIg4SJhIaMh4uOiY2OiI6Nh42JgpWQiJuUjaWSjKOQi5uMh5eM iI6MiI6NgpCSh5WVjZ2XkJ+WiIiIenp7cG99cnB/d36If4eIf4eLgomLfoSHeoCDd3iEeHmRe4OV f4eai5KZiZGShoeMf4CCc3V+b3J/c3SCdXd+eoOJho6Nh5CUjZaViJqUh5mQhJCNgo2ShI2ajJWX jpmVjJaWhn6Me3R9amN/bWWLeH6Sf4aRiJKSiZSUh42Qg4mIe3+ShomSh5CSh5CUenuNdHWHc3CN eXeSiI6WjJKRh4iLgIKId3N+bWl6aF5+a2KDd32Mf4aXgomVf4eXfYOUeX+OenqMeHiAbmh7aWN+ aW6HcneJdHSIc3OJbW2GaWmCZ2eDaGiGbW6JcHKJcG+Mc3KReXOSenSUdGSLa1yHZ16IaF+Lc26N dXCOd3KNdXCNcm2UeHOVeXmWenqUeWuNc2WJaGOEY16HaGSHaGSNal+Nal+NY1eHXVF9Vk5+V0+D WFCHXFSHaGWNbmuVc26XdXCXd3qben6de3ObenKdeGSbd2ORbmKQbWGObVuObVuLaFB+XEV+VUCA V0OCVkCEWEODWD6CVz2GVz6HWD+DWkOEW0SCWEqDWkyCWkCGXUSHeH+Gd36EcHCDb2+GcHWIc3iJ d3+Oe4SNf42QgpCSg4uJeoKDdHeGd3mDeX2Ge3+Een6DeX2Jen+Jen+LfYuRg5GJhpGMiJSQhomI foJ/c2d5bWF1aml3a2p/a26Hc3WHeYKIeoOEe4aCeYOEeHuAdHiEd3+IeoOGf4iHgImMg42OhpCS i5CVjZKZjZaZjZaRiJCRiJCNh5KJg46LfoSMf4aLhI2Ri5SVjJmVjJmViZWUiJSNg4SLgIKGe3+A d3p+eYiDfo2Ig5KLhpWRiJKQh5GOh4yNhouRiJWOhpKLh5CJho6IhpSOjJqSkZuVlJ6VkZ2VkZ2J hpGJhpGLiZGHho2If4mJgIuMhJWXkKGelKaakKKSiZSOhpCOho2SiZGViZKUiJGMh4uNiIyNiIyO iY2NiZWMiJSOh5aNhpWRh5uQhpqNg5SOhJWIhIuJhoyLgo6OhpKQkJ6WlqWSkpWEhIeJeHmMenuL g4aUjI6UiZCOhIuNen6Gc3eGc3eGc3eLfYaQgouZjJ6bjqGWi5SQhI2JfYODd32EcHOJdXiNhIyU i5KWjp6XkJ+XjZ6SiJmNgo2MgIyNf4iUho6UjZmWkJuZjI2Mf4CAb25/bm2IeoiQgpCQh5GOhpCW g4mVgoiJf4OSiIyWi5SViZKUgoOJeHmHdXeNe32Uh42bjpWeiJCXgomUen6Mc3eAbW+Ld3mNf4uU hpGWgoKUf3+QfYCQfYCRg36Rg36JdXOAbWqEaGiGaWmDb3KIdHeMcm6Ga2iCZ2d/ZGSDaGOIbWiG bmmIcGuNcHCUd3eUeHCMcGmIaF+NbWSRdHuUd36Rd3ONc2+McGmOc2uUd3eWeXmXeHSVdXKHa2eA ZWGEZV+GZ2GIaF2Ma2GNaVaHY1CDW0aAWESCXk6DX0+JZ1yMaV6RcmuUdG6RdXKVeXWeeHCXcmqW cGWadGmVb2WUbmSSbl+Qa12MaVGAXkeAVUGAVUGEVUCGVkGHWD+IWkCNXUiRYUyUY06VZE+OY0qH XESIWD2JWj6MgoiMgoiMf4OHen6HdH2IdX6JeoKNfoaLfYuMfoyOgImLfYaJfYOIe4KJd32LeH6H en6GeX2Hen6Ie3+JeoKNfoaRfoeVgouOhIuLgIeDdXB+cGt7bXJ/cHWDdHeIeXuIe4KGeX+GdX+E dH6EeHt/c3eDdHuHeH+DfYiHgIyLhI2OiJGWi5SWi5SXjpuVjJmRhpGQhJCLgomLgomNgouOg4yR iJKUi5WSjpWRjZSSiZGSiZGUjZSQiZCMh4iDfn9+eoaCfomHg4yIhI2LgoyMg42Ug42Ug42Sh5WS h5WNhpWOh5aQiZWSjJeSkJ6WlKKUkJuSjpqNhJGOhpKJhpF+eoaAd32EeoCHgpKUjp+akqOWjp+X iZWXiZWRiZmRiZmVh5KWiJSSh5KRhpGMiJGQjJWSi5qSi5qQh5SOhpKNhouLg4iHfYOEeoCAe4uE f46LhJCQiZWOiZmZlKOZlZuOi5GShomRhIiUiJGZjZaUh4uQg4eQe3mEcG6CbmuGcm+Gf4aNh42Z jp+XjZ6ViZeRhpSLg4iGfoOMeX2QfYCVjJmVjJmWjJ2XjZ6SjJeNh5KHf4KEfX+Jen+QgIaOiJGV jpeVjZKNhouGeX+AdHqHfpWOhp2Qh46NhIyQgIOQgIONg4eSiIyZiJWaiZaUgIeIdXuHcneLdXqX g46diJSih5Kdgo2OeH2Jc3iEd3eLfX2Sf4iVgouXgoKVf3+Re4OSfYSRhoSRhoSRgHmHd2+Ha2eC Z2KEa2qIb26McHOGam2EaWKCZ1+Eal2IbmGIbWiIbWiLc2qReXCRd3KOdG+Sb2eXdGuXdYCaeIOV eX6RdXqLb2iIbWWQcG6ZeXeXe3uWenqMcGuGamWEY16GZF+IaFyLal6JaluLa1yGZU+DY02EZFqH Z1yIaVqJaluQbWGSb2OOb2uRcm6ab16UaViSaFeRZ1aSa16Wb2KObmORcGWRblaHZE2GXkN/WD2H WD2JWz+OX0SNXkOOXkmRYUyaZUqeaU6ZaFCRYUmHWDqLXD2Nh5CMho6Ngo2He4eHeYKHeYKEeHuH en6Ie3+Mf4OSgoyMe4aEeYSDeIOJdHeLdXiId3iJeHmEeHuHen6GeXqHenuLe4OSg4uMhpGJg46H f4KDe36HeH+IeYCGeoOHe4SJf4OEen6IeX6HeH2EeHmAdHWCdHSDdXWAd32EeoCEf4OMh4uVjJSX jpaQjJWMiJGOho2JgIiJgIiMg4uQiZKQiZKSiZaVjJmRjZaSjpeRjZaUkJmWkaKUjp+SjJWNh5CD gISIhomHhIiDgISGfoCGfoCMfoeUho6Qi5qSjZ2Li5mIiJaJiJCNjJSQjZuRjp2MiZeLiJaMgImJ foeCfYB6dXl/dHOHe3qHg46NiZWRjZmOi5aSiJqUiZuRi5aQiZWQh5GSiZSQiZCQiZCQiZKRi5SV iZWXjJeUjZaUjZaLiIyJh4uGgH+Ae3qEeYKGeoOMg5CLgo6Ri5aZkp6XlJ+SjpqRiJKSiZSXjJWa jpeUjI6Oh4mOeXeJdHKHdG6Jd3CCfoeNiZKXkqaUjqKQh5GOhpCOg4yJfoeQgouShI2VjZ6Si5uM iZeLiJaQh5SMg5CHfYCEen6Een6IfoKShJCZi5aZi5SXiZKNgouMgImLhpaNiJmSi5CQiI2SfYSQ eoKLg4aOh4mVh5WZi5mWgIOLdXiEcGqJdW+MfYSXiJCZho6Wg4ySe4OMdX2Jen+NfoOUfoaWgIib f4Sbf4SUfoaUfoaRhIiRhIiWgoKLd3eLcGWCaF2CaWqIb3CLcnOJcHKLbWOHaV+Eal+IbmOOb22O b22QdG+Ncm2Od3CSenSSeHCVenOWeYCVeH+UeHqNcnSIZ2KIZ2KHamqRdHSUe3eReXSRcGiJaWF/ X1d+XlaCYlaEZFiIa1uIa1uJaFiJaFiMbmKNb2ONcF+JbVyOaFaMZVSIamGLbWORbliNalWLZU+I Y02OY1ORZVWOaV6SbWKVcF2JZVOHYT+DXTyHXkOJYUWUZEqSY0mVZE2ZaFCaa0yfcFCXaE6RYkiI XUCHXD+SiJmOhJWOhpKNhJGMgIyLf4uIgIOEfX+Ie3+JfYCLeoeLeoeMeX+MeX+MeHiLd3eHeXeE d3SDdXWEd3eIdXmHdHiGf4iIgouJgpKJgpKNf4iOgImDf4aIhIuMhpGJg46OgoaLfoKIe3+Hen6G dHOEc3KDdXOGeHWHdHiMeX2NgIKXi4yWjJKWjJKRh42QhoyGfYSJgIiEfomHgIyJf5CLgJGOhpKM g5CLgo6NhJGWjJ2akKGUkaGQjZ2SjZ2OiZmLh42Hg4mGg4d/fYCCeHuGe3+Qf46Xh5aSiJmSiJmR iJCLgomJhoyQjJKVjJaRiJKLhIuHgIeMf4aIe4J5dHV4c3SCd3WGenmHg4mNiZCSjJWQiZKRh5eR h5eOhpCQh5GSiZGVjJSQiZCQiZCVi5GWjJKXjJqWi5mSkJ6WlKKWkJmRi5SIiY2AgoaGeX2Ie3+C en2AeXuEfomMhpGViZKWi5SSh5CViZKWjpSakpedjZWVho2SfYKRe4CSf4aSf4aQh5SVjJmXkKGR iZqJgIiLgomJfoeJfoeOg5GNgpCViZeRhpSMhoyHgIeLgIeMgoiHf4KGfoCEf4CGgIKOfouWhpKR hpSSh5WNhI6Mg42RiJWVjJmXjZSRh42QeX6Nd3uJe4mQgpCUg5KXh5aRgoSIeXuGbmmEbWiIdXmS f4OShomRhIiWf4eWf4eUhIySg4uVgoaWg4eVf4KVf4KSf4OSf4OQgIaVhouVf4SSfYKNc2uDaWKD aW+IbnSLcnCMc3KLcGWGa2GEaWWLb2uMcG2Lb2uLaWeObWqNc2+VeneWfnmagn2dgoiZfoSSdXWG aWmAYVZ5Wk93XViGa2eNbmqUdHCWc2qOa2OJY1SAW0x/Wk2CXE+LZFONZ1WNal6Nal6ObWqXdXOS eGmJb2GJZ02EYkiEY1SIZ1eOaleOaleNaFGNaFGQZVOSaFWOaFaOaFaSa1qSa1qOZESOZESRZFCU Z1OZblaUaVGVaVCUaE+Xa1OablWWak2SZ0mMYkGMYkGVi52Vi52UiZuUiZuMh5eJhJWIhJCEgIyI foSJf4aMe4iLeoeLfoSNgIeJfn2IfXuJen2Jen2LeXiMenmNeH2JdHmLe4OLe4OJgIuLgoyMg4uR iJCOiJGOiJGVjJaRiJKRgoSNfoCHf4SGfoOLeH6LeH6GeX2JfYCHfYOJf4aQgouVh5CVh5CVh5CQ gouLfYaDeoKDeoKCdYeDd4iIeISIeISHen6JfYCOgoiRhIuUi5eZkJ2Zkp6Zkp6akZmVjJSQh5SN hJGMg4uJgIiAdX6DeICLfpGQg5aRhpSMgI6MgoiLgIeLh42MiI6Oho2Mg4uHfoiGfYeIgIZ/eH2D d3p/c3eIe3+LfoKDgISJh4uOi5SOi5SSiJmRh5eSjJeRi5aSjJeWkJuUjZSSjJKZjZmZjZmWjp6U jJuRjp2WlKKWlZ2SkZmOjZeIh5GHfYCCeHt+dXJ+dXKEen6LgISRhIiViIyUiZCUiZCUjZaWkJmX jJWUiJGWiJGUho6Qh46Qh46SjJeXkZ2ZjZaUiJGNg4eMgoaMf4ONgISNhIyMg4uUho6Rg4yQg4SL fn+Ng4SNg4SLg4iLg4iJgoSGfoCJeoyQgJKOg4yQhI2OhIuQhoyQh5SRiJWUjpKRjJCRfoSLeH6I eISOfouSgo6Xh5SVgoiOe4KJdXWHc3OId3WQfn2Vg4SWhIaSg4iUhImXhI2Zho6Zg4aahIeVgIOU f4KWfYCWfYCUfoaXgombf4SVeX6Oc2uDaGGDamuIb3CLcnCMc3KDcGOAbmGAbWeGcmuLb2qJbmmM a2GIaF2IbmmQdXCZe36bfoCbf4SXe4CSd2+EaWKDYlN6Wkp0V0x7XlOHZFyRbmWQb2SJaV6IZFSA XU1/W0WAXEaNY1eQZVqIZVqIZVqNa2eUcm2ZenCQcmiNakeAXjyCXUmLZVGRbV6Ub2GWbVeWbVeX a1uWalqUaleSaVaSbVaUbleUak6Uak6Wa1aWa1abdWGVb1uVa1GWbVOeblGiclWdck+Wa0mRY0OQ YkGVi5uWjJ2WjKGWjKGQi5qMh5aOh5aMhJSOgoOOgoONgouLf4iLe42MfY6LgIeJf4aMf4OOgoaM fYSNfoaOe4KIdXuIdXuLeH6JgIuNhI6QiZKRi5SRiJWSiZaWi5mUiJaMgImHe4SGgISHgoaNgIeM f4aJf4aLgIeHe4mJfoyOgI6Nf42Of4eNfoaHen6EeHt+dHV9c3SDdHmDdHmIc3qIc3qAeX6Hf4SQ hoyUiZCZkJqdlJ6dlp+alJ2WkJmOiJGQh46NhIyLhIuHgId/d36CeYCLhomMh4uQhomJf4OMhISN hoaMjIyIiIiJf4aLgIeGfYSEe4OHf399dXV7dHeCen2JfoeNgouHgIeIgoiQiZWQiZWQjJeNiZWQ i5qRjJuUjp6SjZ2Zkp6XkZ2elKadkqWQiJeNhpWNiJeQi5qSkZuQjpmQjJeIhJCJg4mDfYOCenqA eXmDeX2HfYCMgoONg4SMhImQiI2UjJuZkaGWkJmXkZqdjJudjJuVjpeSjJWQjJWUkJmajJWWiJGR h4uNg4eJf4OJf4OEgIeJhoyViZKUiJGViImOgoORh42SiI6RhpGUiJSSgoyLeoSHeH2Jen+LfoSN gIeMgImLf4iLgoyMg42Oho2SiZGRgoeMfYKEdXiJen2Mf4aShoyWg4mWg4mUfoaQeoKNeoCOe4KS f4aXhIuXh5GXh5Gah5CXhI2ag4iag4iZhIeZhIebgoOXfn+Wf4SZgoeagISWfYCVd22GaF6CaGGJ b2iNdW2MdGuHbmGCaVyCamKJcmmMcmeHbWKLaVeIZ1WGa2eLcGuUeHSXe3iVeneQdXKSdGeJa16H Y1B+W0h0VkV1V0aAX1CGZFWHZ16DY1uCY1F+X06CX0qLaFOQamOUbmeRb1+ObV2LbliQc12Vem+R d2uWb0yEXjyEXEWNZE2RbmWWc2qZdGGadWKbdGKWb12ZbVyVaViRal2UbV+WcFyXcl2XcluXcluh dWSid2WbdWGadF+idVyjd12hclGeb0+XaEWUZEGRiJKVjJaVjpqVjpqSjpqSjpqWjZqRiJWNhouM hImJe4mIeoiHe4SIfYaMgoaMgoaNhoiNhoiOhIuLgIeJf4CCeHmCc3iDdHmHeYKQgouQh5GOhpCW iJSWiJSajJqVh5WOgoiIe4KIeoOLfYaIf4yMg5CHfoiGfYeEe4iEe4iIfoKJf4OJe3uIenqEeHt+ cnV+bWt+bWt9am6AbnKDbnWHcnmAeoaIgo2Og4yUiJGXjJebkJuZkp6SjJeLiIyLiIyShoyRhIuM hoyMhoyHgoOGgIKJhIiQi46QiIuJgoSRh4uOhIiMiYuMiYuUh42OgoiMh4uLhomLgISGe39+e3+E goaOiJGMho6OgImLfYaLhI2Nh5CQiZWOiJSQhpaUiZqXkJ+Wjp6bkaKdkqOekquXjKWOh4yGfoOG foOIgIaLiZSJiJKMhpGNh5KOiI6QiZCJhoyJhoyNhI6LgoyOgoiMf4aIgoiLhIuSh5KZjZmZkJed lJuikaGhkJ+ajpeWi5SSjJWSjJWbjZabjZaWjJKUiZCJgoeGfoODf4iMiJGXjJeXjJeUiJGUiJGX iZKZi5SajJWXiZKVgoiRfoSIenqGeHiGdX+Hd4CHeYKJe4SMg4uLgomMgImRho6VgouSf4iNeH2L dXqGeIONf4uVgoiWg4mZg4uZg4uRgoeOf4SOf4eSg4uejZqbi5eZjJCUh4uSg4aVhoiai5KdjZWa jIyUhoaXgoSXgoSXgIaWf4SWe26Jb2KHalqLbl2MdGuNdW2OcmGHalqGa2GLcGWJb2GIbl+HZ1uH Z1uJbmqNcm6MdG6NdW+SdGqMbmSLa1yIaVqQZ1SJYU6AX0x7W0d+W02AXU+DXlSAXFF+X1CAYlOH ZFiQbWGadHefeXuZem6UdWmQd2iSeWqSeW6Ve3CadF2MZ1CJY1GOaFaSc22VdW+ad2qbeGudeWGZ dV2ec2KZbl2NaVuOalyObVuScF6XcGGddWWfeGWje2mheWeheWeme2WnfWemeV2jd1ubcE6UaUeR iJKUi5WVjJmUi5eVi5uZjp+XjZ6UiZqOhpCJgIuEd3+CdH2Ee4OHfoaOfoiRgIuSg4uUhIyOh4yL g4iDe3t/eHh+c3KAdXSDdX6Nf4iNhouNhouWg4yWg4yXh5SXh5SNgo2DeIOGeX+LfoSHeYeMfoyE e4aAeIKCeYaCeYaEeoCDeX+EdXiCc3WDdHl/cHV9b217bmt6b25+c3KEb3SIc3iJe4mQgpCSh5CU iJGVjZ6Si5uOho2Mg4uOhIiNg4eWgoSZhIeShoyRhIuMgoiNg4mNh42SjJKUi5KRiJCUjI6RiYyR jZaWkpuajpqXjJeSjpeNiZKVi5GOhIuGgouMiJGQjZuOjJqNf4iLfYaEgoaGg4eJhIiIg4eNhI6R iJKSkZaSkZaVlp2Wl56elqaVjZ2RiYyHf4KGd3mJen2Nh42Nh42LhI2OiJGRi5aWkJuVjpqSjJeO iJSOiJSQhomOhIiMh4uNiIySi5CWjpSZkJ2flqOilqKflJ+fkpmajZSUiZCUiZCajJWbjZaajpeW i5SOhIiJf4OJf4aRh42XjpmakZuVjpeRi5SVjJaWjZeakJaZjpWShomQg4eGd3mGd3mHdH2Gc3uH eYKJe4SGfoCHf4KLgISMgoaRhIiQg4eUfoOMd3uGd3uLe4CRgoSRgoSZhoybiI6Uh4uQg4eQfYOU gIeXh5GdjJadkJGShoeUenuVe32ajJWhkpuikpeai5CahIyWgIiXe4CXe4CZenCUdWuJb2SMcmeM cGmOc2uQcmiMbmSJbmmJbmmJb2GIbl+Ga2GEal+MbWmQcG2SdGeUdWiScF6MaliJaFaHZVSMaFeN aViJaV2GZVqIZFZ/XE6DWE2DWE2DXU6DXU6IYlWSa16ed3eheXmdfnSXeW+SeHOUeXSUeHOXe3eX c1+NaVaQbl6WdGSdeW6deW6ZeWedfWqhe2ihe2ihdV2ec1uVb1uRa1eOaleQa1iUaVuab2GbdWuf eW+leG+meXClfW2heWmidF2jdV6edFqacFaVh5CWiJGSiJmSiJmUiZqZjp+XjpuVjJmUg5COfouG d3uEdXqJeoKLe4ORgI2Ug5CWgo2Wgo2QgIiNfoaHeHqDdHeAc3OAc3OAd32MgoiOhIiOhIiQg4eQ g4eNhIyOho2Oe4SIdX6GdHWGdHV/dXt+dHqAcnmCc3qHeYSEd4KHdH2LeICLdX2HcnmHdHqCb3WC bnCAbW9/dHOAdXSEdXiHeHqIfYiNgo2Qh5SSiZaUiJaOg5GMh4uNiIyRiY6QiI2SiI6Vi5GWjpSR iY6Mg42Mg42MhpGUjZmWkJmZkpuZkpuXkZqakqOdlaaWkaKRjJ2RkZ+SkqGUkZ+Ni5mLiJeNi5qQ i5qOiZmLgIeJf4aCf4CAfn+Ig4eLhomOiY2OiY2Oi5GUkJaUkpqSkZmZlaGVkZ2RjpCEgoOHen6N gISOhpCQh5GQh5GQh5GViZeajp2akZuXjpmSiZSSiZSSjJWQiZKQiZKRi5SVjJSSiZGRkJWZl52e l56dlp2bjpKWiY2Si42RiYybjpWdkJaZkJeWjZWWh4yRgoeJe3uQgoKajZSekZeVjpWRi5GViZKZ jZablJmXkJWUh4uMf4OGd3mCc3WHcHWCa3B+b3R/cHV/dXmGe3+JfYCNgISOgoiRhIuVf4eQeoKN eH2OeX6Rg4ORg4OXiI2XiI2SiIyNg4eQg4eRhIiSgoyXh5GejpaUhIyQen2LdXibhJSjjJunlJ2j kJmahImUfoOSeXiVe3qWenOUeHCRc2mRc2mQcGqQcGqLc22Lc22Ic3CIc3CQcmWLbWGGZ2GHaGKJ b2SOdGmXd2qWdWmVc2ORb1+RaliOaFaRbmOUcGWMbmSLbWOOalqDX0+CXUmAXEiHXUiJX0qLZVuR a2GWdW2aeXCde3ObenKbeG+ZdW2adGqZc2mUbV2QaVqRal2XcGObdWqdd2udd2uie3Clf3CmgHKj eGeec2KbcFiSaFCLYUmNY0yQaVqOaFiRaWOXb2mdd2+dd2+bdGefeGqid2OleWWfdFyXbVWRiJKQ h5GVhpmai56bjZmekJudjpqbjZmShJCJe4eLe4CNfoOJf4aLgIeOho2Mg4uRg4yQgouMgoaLgISE eHmDd3iCdG+Ed3KIe3+ShomUh42Uh42NgouJfoeLhomIg4eMeHiIdHSGc2mEcmh+cG5+cG6DbnWE b3d/c3d+cnWAcnmGd36CdXuEeH6EdH6GdX+EeHuDd3qIeXuJen2EfX2EfX2Hf4SHf4SJgIiNhIyN houOh4yOiI6SjJKUjJ2UjJ2ZjqGZjqGXkJ+UjJuRi5SLhI2Qh5SXjpuUkZ+VkqGUlKKUlKKalaiZ lKeSjaGUjqKZkKqZkKqSi56NhpmJhpGSjpqQjpaQjpaSi5CRiY6GgouGgouMgI6Lf42OiY2Qi46U i5WVjJaXjJeXjJeWjZqZkJ2NjZCHh4mGg4eIhomMhJSQiJeRiJKQh5GUiJaZjZuhkqGdjp2djpqW iJSOiZmRjJuVjZ2Wjp6XjpaVjJSVkZedmZ+el6GVjpeRiY6Si5CUjJGXkJWekp6bkJuXkZ2XkZ2V i5GRh42Jg4CMhoOVjZCXkJKVkpaSkJSVi5GXjZSdlZqakpeXi5GOgoiGeHiHeXmDdXV+cHCEcHOD b3KGdHWGdHWIdXuMeX+RfoeUgImUgIeQfYOOe4KQfYOOgoaMf4OVf4eWgIiZhoyVgoiSg4uQgIiL eoSSgoybjJSVho2SfoCMeHqQfYadiZKilJ2jlZ6hjImVgH6LdXWMd3eSdXWSdXWLd3CNeXOMeHWL d3SQeHKOd3CMeHqJdXiOdGeGa16EaWKEaWKLbWOUdWuWenWVeXSVdW+UdG6Vb2SSbWKVb2SVb2SS cmmQb2eQa1uQa1uJZUqDX0WJYUyNZE+JZFqMZ1yUa2KddGqeem+deW6XdGiSb2OSa16UbV+UbVuQ aVeSa16bdGeXdWOXdWOddWiheWuhd3Sme3muf3eoenKjdWGXalaMYk2JX0qJX02LYU6OY1ONYlGS aFqZbl+fdWKhd2OedF6hd2GhdFuablWLiZSNjJaZiZuejqGekp6dkZ2XjpuUi5eOhpCLgoyLhJCM hpGMhoyNh42OiI6MhoyMhImJgoeQgIORgoSMfn6HeXmGfXmIf3uOgoaShomNhouNhouIg4eIg4eJ hIiGgISJfX6EeHmDd26Dd26Ac3B/cm+CcHKAb3CDbnWAa3OCbnmEcHuDd32HeoCJen2NfoCNfoaQ gIiLgIeOhIuJgoSLg4aLfX2Je3uCeHuEen6Mf4OViIyVjpWWkJaai52bjJ6ZjqGZjqGXkKGSi5uQ h5SMg5CQh5SZkJ2blKOdlaWWkaKZlKWZlqaVkqKRjp2UkZ+akqaWjqKWi5aSh5KUiJGXjJWWkJuX kZ2Ujp6SjZ2NjJSJiJCLhpaQi5uQjJWNiZKQiZWVjpqUjZmUjZmSjJeUjZmRi5GMhoyIhIuLh42O hpCRiJKSiZSRiJKSjJWVjpeakKGelKWbkaKXjZ6RjJuNiJeRjZmUkJuZkJqXjpmalJ+fmaWfkZqZ i5SVh5CVh5CXjZ6bkaKZlKeXkqaXlJ+Wkp6WkJmRi5SRhIuViI6ZkJeXjpaVjpeVjpeUi5WVjJaa kZmZkJeXjJeSh5KLgIKHfX6HeHqIeXuHeHqHeHqGdHWEc3SEeHmIe32SfYKVf4SRgoSNfoCNeoOM eYKQeoKMd36Re36UfoCXgIiZgomRgoSOf4KLeICOe4SZhJCXg46WgoSRfX+Qe36Xg4ahjJejjpqh jIyXg4OSeXqQd3iSd3eRdXWQd3iUenuRfX2RfX2VeXuUeHqWe4KVeoCNd2eDbV2CZ1+DaGGJameQ cG2WeneWenebenKaeXCZdWmZdWmVcmWXdGiZdGWUb2GRa1WUbleRbVyOalqQaVqNZ1eIZFaIZFaS Z1iZbV6Zc2ibdWqWcmGUb16UaFqUaFqLa1eNblqOcGOWeGqZd2eee2ubdGSbdGShdG+nenWsg3mr gnioe2qfc2KSaVSIX0qIXkeMYkqLYUyNY06QYlWRY1aZbliab1qdclqmemKhdFubb1aJiJKOjZeW jZqbkp+dkKabjqWVi5uOhJWNh5COiJGUjJ2UjJ2SiJmRh5eQhJCNgo2Oh4eOh4eWiIaShIKRgoeM fYKLgIeQhoyUho6Uho6Oh4yMhImGgH+JhIOMgoaMgoaLgIeGe4KGenmGenmCdXd/c3R+b3KAcnSC a3B/aW6CbXSGcHiGe32LgIKMgoaMgoaQg4eViIyRiY6Si5COiY2Ig4eGd3mDdHd7c3J/d3WQfYaX hI2Wi5SZjZaZi5SZi5SWkJuZkp6UkaGMiZmOg4yMgImOi5aUkJuZlKWalaaXkqKZlKOZl6KWlZ+U kZ+SkJ6XlaOVkqGXkZqUjZaVjJSXjpaZkp6dlqKalaiWkaWUjp+OiZqRi6KZkqqSkZmJiJCMiJGR jZaVjpqUjZmOiJSQiZWQiZCNh42JhIiMh4uOho2RiJCRiY6RiY6SiZGWjZWdl6idl6ialaaWkaKU kJaLh42Ri5GVjpWXjpmZkJqalJ2dlp+akJaUiZCXi5GXi5GakZ6flqOblqaXkqKUkJuVkZ2XkZ2U jZmSiI6Vi5GakZuelZ+ZkJqWjZeZkp6VjpqakZuelZ+ZjZmWi5aUhpGRg46OgoiMf4aMfYKMfYKJ en+Gd3uEeH6HeoCUfYSWf4eQgIOMfX+LdXqNeH2OeH2MdXqOeH2Se4CSf4OVgoaUgISQfYCOfXuL eXiMf4aUh42ZhISVgICOen2RfX+dhpChiZSdh4mahIeUf4KSfoCWfXuReHeVeHqZe36WgoSVgIOX e3uWenqWfX6WfX6Od2KEbViAY1aCZFeLaWSRb2qVeXuWen2fenmfenmbeG2ad2uXdGuXdGuZdGGV cF2RbliZdV+Vc3CUcm+Wc2iWc2iSb1qJZ1GQZEyVaVCZcmKac2OZc16WcFyRZU2NYkmGZ1WMbVuO dGeWe26bem+ZeG2edWuddGqeeHClfnenhIKnhIKqfm2memmedFeQZ0qIX0GIX0GLYkiOZUyNX1CI W0ySY0maalCeclimeV+hc16ecFyNgo2UiJSViZKViZKXiZKVh5CNhIyLgomQiZCUjZSWiZ2WiZ2W i5aNgo2Qg4mRhIuSiIyVi46UiY2SiIyVhI6SgoyRho6ViZKXh5GXh5GVh5CRg4yJgoeLg4iUgImV gouNg4mMgoiLg4OJgoKGe3+Een6Ie3+Dd3p+b3J+b3KGb3eJc3qNfoOVhouRi5GQiZCUiJGUiJGV jpWSjJKOjpGGhoiDfn17d3V4bm9/dXeQeoSXgoySh5CSh5CQiZKSjJWWjZeVjJaOi5GHg4mMgIyN go2Qi5uSjZ6Wjp+Wjp+XkKGakqOblqealaaVkZ2VkZ2WlKOZlqaVkZ2VkZ2VjJSXjpaZlqWbmaeh mayelqqajqeXjKWbkKudkayWlZqMi5COiI6UjZSVkZqSjpeOiJGOiJGOiI6MhoyOhIuQhoyQh46R iJCLh42NiZCUi5KUi5Kdl6eblqafl6eakqKXjJeSh5KRiJCUi5KVjpqWkJualaWXkqKZkpuRi5SW iZCXi5GbkJuekp6hkaOejqGZjJ6ZjJ6bkaOWjJ6XkZ2Zkp6bkaObkaOZjJ6WiZuXjpmWjZeakZ6e laKalJ2UjZaVhI6VhI6Rh4uQhomSg4iUhImVgIyOeoaNfoOOf4SRhIiRhIiRhoKLf3uNeXuLd3mL dXiMd3mLd3eOenqOe4KRfoSRfX2Sfn6JeHeEc3J/cnqIeoOXgIiWf4eUgISNen6Vf4eahIybhIme h4yUhImUhImXgoSRe36SeXiWfXuXfoKXfoKWenqUeHiUenuUenuUdWmNb2ODZViCZFeLaWSRb2qU eHOXe3eZeXedfXqaeXCaeXCaeW6aeW6adWSZdGOZd2KaeGOZeHmbenudeniZd3SZcmSUbV+Va1GW bVOVa1idc1+ec2Kec2KSaU2LYkaHYk6OaVWSdGqdfnSbeGuZdWmddWWbdGSad2ubeG2ff32lhIKo gnqngHmleWGab1eNYkeLX0WLXkiLXkiJW0SJW0SJWz+ZaU2Wa1uid2WidWShdGOSfYeWgIuRgIuS goyOgIyIeoaIe4KOgoiNhIyVjJSZjZuWi5mWiZCQg4mVgouWg4ySiI6XjZSVjJaVjJaSh5KUiJSV h5KVh5KSh5KRhpGOhpCOhpCVhI6Ug42Vh5CZi5SRiZmSi5qLiZSIh5GIgIaHf4SLfoSAdHp9cHR/ c3eIc3qRe4ORg4yWiJGXjJWViZKXiZKXiZKRjZaRjZaVjpWOiI6JhIZ/ent6cHR+dHiVeoOaf4iU ho6Rg4yMhoyMhoyNhI6JgIuHf4KGfoCJgoSOh4mQiZKUjZaViZeWi5mUjp6ZlKOelqedlaaVkKGV kKGWkKeXkaiWlKKWlKKalJ2dlp+mnaqlm6ijl6ahlaObjqKbjqKhlKqhlKqalZmRjJCNhIyUi5KQ jpaRkJeRjZSOi5GMhoyOiI6UhImUhImQh5GQh5GNiZCLh42QiZCQiZCUkaGXlaWfkqWajZ+ajJqV h5WNhIyRiJCUiJSViZWXkJ+Wjp6UjZSNh42Oh4yUjJGajpeekpujjp+hjJ2aiZmZiJeZjp+XjZ6Z jp+akKGbjqKZjJ+VjJaRiJKSh5KViZWXkZ2blaGZkJqSiZSShoyRhIuVho2ZiZGZjJKZjJKZhJCU f4uShISRg4OViIyViIyUiYuOhIaOeYCJdHuHcneLdXqMd3uSfYKRfoSRfoSWf4SRen+Ld3mGcnR9 aWuHc3WNeoCVgoiVf4KQen2Qen2Re36WhIaaiImXhI2VgouXgn+SfXqReXCVfXSagn2WfnmQdW6O dG2QdHeSd3mQdG+Ncm2JaliIaVeJa2KQcmiXeW+ZenCad26beG+XeWuXeWuhfXKifnObfXCZem6d e2+bem6be3mff32ifXibd3KZcmSXcGOSa1qVblyRb12aeGWfemueeWqXcl2NaFSJZVORbVqXd26e fXSdd2uadGmWc2ead2qad2uWc2ideXujf4KmfnqogH2ne2qdcmGba0+XaEySYk+LW0iHXEGHXEGH XUaSaFCWcGWbdWqic2Web2KNeoCJd32GdX+GdX+EeH6AdHqEcniLeH6IfYaNgouUi5WVjJaXiJCU hIyQgIiUhIyUi5WakZubkJuWi5aSh5KSh5KSh5WRhpSNh5CLhI2LgoyOhpCQh46Qh46SjJWWkJmW jaWWjaWRiZqNhpaIg4eDfoKAd3p+dHiJdXWEcHCHen6NgISVho2XiJCXjJWUiJGUi5KSiZGQiZWU jZmZjZmXjJeXh5aLeomDb2+GcnKQfYOWg4mXiZKXiZKNh5CMho6Gg4d/fYCDg4OHh4eMh4uRjJCW kJmVjpeSjJeRi5aVkJ+dl6eblKWdlaadlaadlaaal6eZlqaZmaeZmaefl6eblKOimqqlnaymnaqh l6WXkqOZlKWalaiZlKeVkpaQjZGOh4mNhoiRi5aWkJuSjpqNiZWMg5COhpKQg4mOgoiOhpCQh5GR i5GNh42Nh5COiJGQiJeQiJeVjZ2Si5qUi5WSiZSQg4mViI6Uh42ShoyVjJaXjpmUiZCLgIeRhIaV iImWjJKdkpmllqKekJuSiZSSiZSUkJuRjZmZi5aekJuikZ6hkJ2UjIyQiIiOho2RiJCWjZeelZ+i lJ2djpeRhIaShoeXiZKXiZKZi5SZi5SXiJCUhIyXhI2Zho6XiZKbjZaZi5SShI2Jd3+LeICIdXuL eH6QeoKWgIiWg4ySf4iXgIiReoKQeHOJcm1+amOHc2uIeXuNfoCWf4SVfoOQenqRe3uRgoSVhoiX iIuVhoibf3+UeHiSenSWfniWg3uSf3iSeW6JcGWIbW2Jbm6OdG+Nc26NbWGMa1+QcGqVdW+WenOU eHCbeG2ZdWqUeWuXfW+ff3mjg32ffXiee3ehe3ehe3ehfnuif32he22bd2iXbluacF2Wb2KVbmGU c2iaeW6dfnCdfnCdeWOVclyObmKScmWZeG2efXKZd2SUcl+RbmOXdGmec2ehdWmfdXOieHWienem fnqleWifdGOjblOfak+ZZ1GSYUyQYUeNXkWJX0qNY06Xc2SXc2Sdc1+WbVqJd3+Abnd9a217amt7 bm57bm5/a26GcnSIeXuRgoSOi5aOi5aNhouMhImRhIiViIyUjZaWkJmdkJaViI6RhIuRhIuOgImQ gouJf4aIfoSEfoSJg4mHg4mMiI6Mi5WQjpmWjp+blKWXjpaMg4uJen2Gd3mCdHKDdXOAc3CCdHKG e3+MgoaRhIiWiY2bjJSWh46Rho6Og4yQiZKSjJWdkqWbkaOVi5+IfpKEd3SLfXqMhImVjZKfjp6e jZ2OhpKHfouHfYCEen6JiYyOjpGbkJ6flKKZlKWVkKGSh5KSh5KVjZ2akqKZlqWZlqWblKOdlaWb maeal6adma+ZlauWlqeWlqedl6ifmqufl6ifl6iXlaOSkJ6Wjp6Wjp6SjpWMiI6MhIeLg4aOi5SN iZKRjp2OjJqXjJeUiJSOgoiQg4mJg4mMhoyMhoyMhoyIf4eLgomNgouNgouOg46RhpGRiY6RiY6U hImSg4iUhImUhImQiI2UjJGWh4yOf4SOgoOViImWjJCdkpaimaGakZmRho6Og4ySiZSRiJKWi5aZ jZmmlaKfjpuVi4yRh4iJgIuOhpCRhpSbkJ6ekpudkZqViIyWiY2XjJeWi5aXiZKWiJGah42XhIuV gI6ahpSWi5aWi5afjJWXhI2QfYOMeX+HdHqHdHqOeH+Se4OWgIiWgIiSfYKMd3uLd22IdGqDb2WI dGqIdHSNeXmOeX6Qen+Oen2Qe36UfoaahIyhh4aehIObf3qUeHOVeneVeneNf3qNf3qUeXKEamOD aGGEaWKEaWKGamOIbmONc2iSeHSWe3iZfXmWeneWd3COb2mUcm2denWbgHmeg3uef3ObfXCfeHSi enejfnmlf3qlemSbclyRZ1GValWXbWGbcGSaeW6ffnOhf3SigHWffWqaeGWXeWuVd2mWe3Caf3SX d1uLak+JZVeQa12ZcmKZcmKUblqadF+ddWiheWuld1+idF2icleeblSda1aZaFOUZE2NXkeIXEaO YkyRb1+XdWWedFqXblSLdHuGb3eCamV/aGN7b2V5bWN5bmp/dHCHeHqQgIORi5aRi5aNh42Nh42Z hoyah42XjJeXjJebjpKViIyRgoeQgIaNgIKMf4CHfn2DenmHfYOIfoSMhIeRiYyQiZKVjpebjqGe kaOZjpWRh42OfXuJeHeLeXiJeHd+dXJ/d3ODeneJgH2RgoSVhoiai5CXiI2SiIyUiY2SjpeWkpub lqealaaRjJuJhJSJen+Of4SRi5aalJ+ekaObjqGVho2Le4ODfn2Mh4aSjpqZlaGel6+fmbCelqqa kqaUiJGRho6SjJeZkp6ZkaWZkaWdlKGdlKGal6abmaeem66XlaeWlqWZmaebmaifnayem66ZlqiW kpuQjJWXiZKXiZKVjpWQiZCMhImMhImRh42UiZCSjpqSjpqXkZ2UjZmQi46Mh4uOgoiJfYOGeoOH e4SJfYCNgISRgoeNfoOOf4eUhIyRg4yShI2XhIuWg4mZhoyVgoiSiI6Rh42Vho2RgomShomXi46W jpSakpelmaWekp6Sh5CNgouOg4yRho6Sh5WWi5mdkZqflJ2dkJSXi46ShoyRhIuOg5GUiJaZi5ae kJuUiJGUiJGZjZmWi5aVi46UiY2ai5CWh4yRgomVho2Vh5KVh5KbjJSZiZGVg4SLeXqIdXmHdHiN eHiOeXmSfoCVgIOUenmOdXSGcmqGcmqJcG+LcnCMd3mQen2Wen+UeH2SeX2Uen6SfYSZg4ufhICd gn6aenSSc22OdG+UeXSQf3iSgnqWe3SGa2R/YVuAYlyCY12EZV+JameRcm6WeXuZe36ZeXWXeHSW d3OQcG2Xc26deHOihoKfg3+igHibenKaeHObeXSifXilf3qfeGWVblySaVSOZVCQal+WcGWZd3Kh fnmjgHujgHuffWqaeGWWeWWXemede3OigHihemOSbVaRYVCQX0+OaFuOaFuQZ0+WbVWXc1+eeWWh dWSid2Wjd1ueclaablOXa1CUZEqVZUyRYUyOXkmSZ16bb2edc1iacFaLdHmEbnOCbWqHcm+Cd3V+ c3J+b3SEdXqJe3uRg4OOhpCOhpCJhpGNiZWXiZKXiZKZjZmZjZmXjZGUiY2QfnqUgn6Lg4OJgoKJ gH+Ee3qHfYCIfoKOhIiRh4uUi5eVjJmZkaGZkaGXkZqSjJWJgoKJgoKMhImIgIaHenuEeHmEeXOR hn+Vh4KXiYSbiJGdiZKViZWXjJeXkqKblqaZmaeWlqWUkpeNjJGOh4ySi5CWkKeblayelqaWjp6S hISMfn6Lh42VkZefmqqemaidma+fm7KhlqibkaORiJCQh46NiZWQjJeWjJ2XjZ6bkp+dlKGal6af nauhm6yblqeXkqKWkaGbmaiem6uem6qbmaeZkZaSi5CWiY2ViIyWkJmSjJWOho2Oho2RhpGUiJSW kJaZkpmXkqaXkqaWkp6Oi5aah5CSf4iGeX2Ie3+Le4COf4SOgoaNgISQg4eQg4eWhpKWhpKWh4yZ iY6Xi46Uh4uSi5CQiI2Zho6Zho6UiJSXjJeWjp6ZkaGhl5+elZ2XkJKSi42OhIiNg4eNhI6RiJKU jJudlaWdkpmXjZSXiI2Sg4iNf4uUhpGWh5mai52SiZSVjJaZkZaWjpSZjpCVi4yXjZGSiIyNgoCL f36RgomUhIyai5KbjJSViImOgoONe32Id3iIc3iMd3uWf4SWf4SVfXSSenKHc2uIdG2JdHSMd3eN eHqRe36WeH+SdHuMc3KMc3KNe3qVg4KbgoabgoaWenWOc26RdXWVeXmRfXeWgnubgHWRd2uJaFiA X1B6XEp/YU+DaGOOc26VeHibfn6Vem+Vem+adXCUb2qScmmXd26bf3ufg3+hf3SXd2uXeW2Vd2qa eW6de3Cfe2+beGuVdWGRcl2QcFyQcFybd3She3mjf3Sjf3SeeG6Zc2mZdWqdeW6he22mgHKhe2iW cl6UYliOXVSIXk6HXU2QY02SZU+Sa1qfeGWle3Kof3WnfnSieW+eclSVaUyRY0SUZUaSZVGRZFCR ZFCWaVWXbVqXbVqEd3SGeHWMfYKOf4SLhIuJg4mQgouLfYaOe4KSf4aRg46UhpGQhJCQhJCQhoyR h42ViZeUiJaSjJWRi5SShomUh4uQi4yOiYuOiYuNiImNg4eLgISRh42UiZCUi5eakZ6XlJ+alqKf lKKajp2VjpWWkJaVjJSNhIyMgoaJf4OMf4OWiY2Vi5GZjpWbjZaajJWZkJqZkJqZkJqdlJ6alaaa laaZlaGVkZ2XkJ+XkJ+WkaGblqadlp2SjJKLgn6Lgn6Vi52flaelnbCimq6fmbCfmbCdlqKXkZ2O ho2Qh46NiJmNiJmZiJeaiZmZjZmbkJuVlJ6enaeimqqdlaWWkp6Wkp6alqKdmaWdm6aenaebkp2W jZeWjZWRiJCZlJeVkJSQjJWOi5SUjZmVjpqXlJ+bl6Oblqeblqebl6GXlJ2djZCVhoiJen2HeHqJ fYOMf4aOhIiNg4eShoeShoeZho6Wg4yViIybjpKWjZWUi5KUiJGUiJGXiJCZiZGZkJqXjpmXkZ2S jJeWkJmblZ6ZjZaWi5SQhoyOhIuLgoyMg42ViZeXjJqdjJmfjpuZi5aVh5KOeYCQeoKQgouShI2Q h46RiJCWjJKdkpmfkZqdjpedjZWZiZGUhIeIeXuQen+Re4CWg4mZhoySgoyQf4mOe3+MeX2MeHiN eXmRe3mWgH6Xf3eVfXSSeHOSeHORe3mUfnuUfoCUfoCZe4COcneAbmF/bV+Lc26Ue3eXgoKXgoKU eXKSeHCVe32SeXqVeHiZe3uhf3ebenKWb1+LZFWAXEaCXUd/ZViMcmSSd3mZfX+Uem+Qd2uVcmeS b2SUcHOZdXiXfXWbgHmjf3SdeW6bd3SXc3CZem6dfnKlf3qmgHujg3+efnqVdGuVdGuac3Kienmi eW+jenCecmWWal6UbV+ZcmSab2OhdWmfeW6adGmValeLYU6DWD6CVz2HWEGQYUmRZ1SfdGGnf3uq gn6nfnSnfnSjeFaWa0qRYkiUZEqVZE+WZVCZaVGXaFCVZFGVZFGJfYCQg4eUh42Uh42Oi5GNiZCU iZCNg4mQfYCQfYCNf4iOgImQf4mOfoiOeYOQeoSOg5GRhpSQjJeMiJSUiZCZjpWXjpaVjJSVkI6Q i4mOiYuOiYuUiJGUiJGXiZWdjpqekaOfkqWjlKajlKadlaWblKOZkaGUjJuQiZKNh5CSjJeXkZ2a kZuflqGbkpqakZmXkJ+XkJ+akZudlJ6dlqKhmqahlaOekqGajJqajJqZkp6blaGZlJeOiY2Mg4KM g4KXiJufkKOll6uhlKeelaKhl6WalJ2XkZqWi5aRhpGRi5SOiJGWi5aXjJeVjJadlJ6ZlaGfm6ee maqXkqOUjJuUjJuXkqKdl6eemayhm6+hmayblKeWkaGVkJ+dmaKZlZ6SkZuSkZuWkp6VkZ2dlKGi maafmqqdl6ebmqWVlJ6akZuVjJaOhIiHfYCHeoCJfYOQg4mRhIuUhImWh4yXiI2VhouWiZCdkJaX jZGVi46XiJCWh46Xh5Sbi5eWjJ2XjZ6Wi5SSh5CUjp6WkaGZjZaWi5SVhouRgoeNfoOOf4SRgIuZ iJKeiZeeiZeSh5CNgouJd32LeH6LeH6NeoCNfoOOf4SVho2bjJSbjZmdjpqfiJCbhIyagIKReHmN eXeQe3mWgIiWgIiReoSUfYeUfoOMd3uNeHiRe3uSfneVgHmXg32ZhH6Zg4CZg4Cah4uXhIiXg4aW goSVeHiOcnKCbmR7aF6HbWmQdXKVe32Ve32We3eXfXiXfoKUen6ZfXmafnqfgHeae3KZdGGRbVqN Z1WNZ1WGaF6Nb2WRdHSXenqSeW6Mc2iOb12La1qRc2eZem6af3Seg3ilgHihfXSddXKddXKZenCh gnijfnuog4CnhIOmg4Kienmed3Wfc26jd3KleW2ne2+fcl+VaFaNZE+RaFOWaFuaa16ddWiZcmSZ b1yUaleMZEGGXjyEVzmLXT6QZFSfc2KjfXKngHWqfWuoe2qieFibclOaaVGWZU6aaU2aaU2ZZ1GZ Z1GXYUmUXUaUho6WiJGUiJSXjJeZkJqVjJaQjJKMiI6NgISIe3+HeoCIe4KIeYCHeH+IdXmLeHuR gomVho2ViZeQhJKZi5afkZ2bkJ6XjJqXkJWVjZKSiI6Vi5GVjJaUi5Wdjp2fkZ+dkqWil6qil6ij maqfl6iblKWalaaVkKGWjp6Wjp6ZlKOalaWfmaWhmqaXlqGVlJ6XkZ2UjZmZjJ6dkKKimqqhmaij l6OdkZ2diJSdiJSUi5eWjZqajJWShI2Ng4eQhomSh5KZjZmilqWbkJ6alJ2ZkpuWkJuXkZ2XjJqS h5WRi5SQiZKUi5WUi5WSiZSakZualaWfmqqdkqWZjqGWh5mXiJqXkZ2dlqKlmq+lmq+hmaydlaiX l6iZmaqjm6ufl6eblaGZkp6VkJ+Ujp6blKWfl6ibmaiWlKOUkaGUkaGXlaOVkqGViZKMgImGeX+H eoCUfoiZg42Vh5CWiJGWiY2WiY2OjJCSkJSWjJKWjJKai5KWh46XiZKdjpeZkJ2Ui5eRiY6QiI2W jJ2Zjp+bjZaWiJGWiIiRg4ONf32Nf32MfYSUhIyZiJKXh5GShoeMf4CJdXiHc3WJdHmMd3uHeHqE dXiOeYOVf4mUiY2ZjpKih42bgIeUfn6QenqNe32MenuOeH2QeX6SeX2Qd3qMeHiMeHiQe3WUf3mW hICaiISbhoueiI2ajZGajZGejZeaiZSdh46ahIyZfneOdG2CaGF9Y1yEaWSNcm2Sd3eVeXmff32h gH6WgIOVf4KVenWUeXSae3KZenCVdWWSc2OUc2qRcGiOb2mUdG6WeG6WeG6ScmWObmKNa1qMaliQ bWKZdWqifXungoCmgoSifoCdeHOdeHOZeXeefnuifoCqhoiui4mnhIOqe3Old26idWmjd2qieW+j enCedF6WbVeQZEmNYkeUZ1OZa1eecmOecmOec1+bcF2XbUyQZUWLYkSJYUOMZ1yadGmmfm6mfm6o d2OndWKec1Gab06da0+XZ0qdaE2ibVGea1adalWZY0aXYkWajJeWiJSXiZebjZuWjqKWjqKSkZmM i5KNgIeJfYOGcnKDb2+AbnKCb3OCdHSEd3eJen+MfYKMgIyJfomRiJKZkJqdjp2hkqGXjpuWjZqW iZCViI6QiZKRi5SUjJ2Wjp+akqaelqqbmaial6eelqedlaaZlqiXlaealaaalaahmqafmaWblKWV jZ6LiZGJiJCRho6UiJGWjZqakZ6al6eVkqKVkZ2Oi5aLg4aRiYyRi5aVjpqajp2UiJaIhomIhomO h4mUjI6XkZeSjJKSkJ6VkqGekJ6djp2XkZ2UjZmQiZCOiI6OhpCRiJKQiZWUjZmZjqGflaeblKOb lKOXiZKWiJGVjJmZkJ2fl6ihmaqemayalailmqynna+emayfmq6jnaialJ+Ujp6RjJuVkKGUjp+S kJ+Rjp6SjJeWkJuZlqaXlaWVjJaIf4mMd36LdX2Of4eVho2Xh5Sbi5edhpKfiJWNi46OjJCbi5Wb i5WViI6RhIuViZKXjJWWjZWUi5KOhIuOhIuSh5CSh5CViI6RhIuUh4iShoeUh4uShomMe4aMe4aN f4iNf4iVgICVgICSfnuJdXOJbnCJbnCJdHSIc3OJdXWXg4ORiYmUjIyaiImSgIKQe3uQe3uSf4OU gISRgoSQgIOQe3WMeHKHcm+NeHWNeHWUfnuWh4yejpSdjZWdjZWejZehkJqbkJuZjZmhi5Cbhoua f3iQdW6Ja1+HaV2IbWmNcm6aeHObeXShf4CjgoObgoaXfoKSd2+NcmqRdXCVeXSQdWiQdWiWd3OZ eXWafnmZfXiVe22Qd2iVc2ORb1+NaVaIZFGJZFqOaV6XeYChgomnhI2lgouaeXCScmmScG6XdXOh gH6oiIavi4KwjIOofmWfdV2fdV+jeWOlgHSlgHSde2SVdF2UakyLYkSUZ1WbblyfdWKfdWKadFuX clidc1iXblSRbVGQa1CLa1qUdGKid2WleWiodV+ndF6iclCaakmZaEybak6balOda1SdZ1OeaFSa Z0iRXkCdiJaZhJKSh5KWi5aZjqOXjaKUjZmSjJeQhoyHfYOIc3CCbWqCbmiDb2l+cnODd3iId3iJ eHmIeX6Le4COhpCSiZSXjZ6bkaKXjpuVjJmViIyOgoaLgIeQhoyNiJeRjJuZkaGblKOXlaOWlKKa lJ+dlqKelqaelqaflaehlqijmqehl6WejZeWhpCNhIOLgoCLfoKRhIiNiZKSjpeZkJeZkJeUi5KQ h46MgoiRh42Qh5GXjpmakZuWjZeUjpKNiIyMhISQiIiVjJaUi5WUjp+Ujp+WkJuXkZ2VkJ+RjJuN i46MiY2Ng4mQhoyHg4mIhIuSiZabkp+WlKKWlKKZi5SZi5SbjZubjZudkqWelKablKWakqOilaej lqialaaemaqhmqablaGZkJeXjpaXjpaVjJSRjZmMiJSSiZSUi5WZlKOXkqKZkJeOho2JfX6Ie32Q gIaOf4SWgo2bh5KVh5KVh5KRho6XjJWdkJaajZSVg4SQfn+Vh5CZi5SWjpSQiI2OgoOOgoORhIiS homWg4eUgISNg4eUiY2WjZeRiJKQgouLfYaMfYSOf4eWhIaWhIaWgn+RfXqJdW+IdG6Hcm+Ic3CI d3OUgn6XiIuai42Zh4aVg4KWgH6Xgn+Zi4uajIyZjI2RhIaQfXeHdG6Jcm2MdG+LdXiXgoSbi5Wf jpmfkJWfkJWbjZaekJmbjpWWiZCXg4aVgIOVfXSOd26Qb2SLal+NcHONcHORd3OXfXmZe4Cfgoeh g4uafYSUdWuOcGeVdXKWd3OWd3CZeXOafX2hg4Ojh4KhhH+bgnSWfW+beWmZd2eWdVyMa1OMY1CL Yk+OeH2dhoumiY6fg4idfWqSc2GQblyScF6Xe3Sihn6nh4Cjg32ie2KbdVydd2KhemWjgnmhf3eh f2WaeV+acleQaE6VaVidcF+edF6dc12acFSbclWbbleecFqdc12bclyZc1qZc1qjeWWlemeoemWn eWShcFaaalCXaEyWZ0qZaFWZaFWbZE+XYUyXZEOUYT+Wgo2Xg46WhpWdjJuakKGXjZ6akKGWjJ2U h4iOgoOHdXd/bm+CbWqGcG6Gc3eIdXmHdXKGdHCCdHSGeHiNfoaVho2XjJqajp2bjJ6XiJqVg4SS gIKLfX2Rg4OQhoySiI6SjJeXkZ2XkZqZkpudlJuelZ2hlKafkqWhmaihmaiimaaelaKfjJKVgoiR e36OeXuOgH6Rg4CQiZCRi5GXi4yZjI2QhomQhomQhI2Rho6Vh5KdjpqdkZ2ZjZmajpqSh5KQhoeS iImSjZGRjJCXi52ZjJ6VjJaWjZeSjZ2RjJuNjJSJiJCNgIKMf4CIgn+LhIKLhJCSjJeSjZ2Ujp6W i5aViZWWi5aWi5abjqGbjqGakZuakZubkJubkJudkqOhlqeblqaalaWXlJ2WkpuWjJKRh42Oho2N hIyOg4ySh5CfkZqekJmfkJKUhIeMgoaMgoaOgoOLfn+UfoiXgoyQhI2Rho6OhpCWjZeakJSWjJCU goCQfn2RhIaViImWjYyVjIuMg3+JgH2JfX6Mf4CQfn+Qfn+QhI2ZjZaZjZmUiJSRho6MgImMe4aO foiQgIiWh46Zh4iXhoeOen2MeHqJdW+Hc22IeXuSg4aZhoyah42XhoSVg4KVf4SZg4iZiY6hkZaa jpeViZKWg3uLeHCId2KGdF+Hc3OZhISfjpmhkJqdjZCejpGdjZWdjZWfiYmahISWe3CQdWqUeXKN c2uJbmeIbWWLbm6Qc3OQdW6SeHCVeX6dgIaaf4aaf4aVd2qQcmWRc2mVd22Xe3iXe3iafnqhhICl iYSjiIOjhHefgHOff22aemiXemmOcmGMa1CLak+Oc3Wbf4KliIihhISjgG6Vc2GMaVSLaFOQcG2e fnqhhH+dgHueemSXdF6bc2mle3Kjg4Cign+lf2ufemeedFyXblabcF+ec2Kid16hdV2ablCeclSf c1efc1eec12ec12id2Oid2OleWGmemKqfmWleWGnd1yeblSXaUiWaEebaVSea1afaUyaZEeZYkGU XT2UfoiRe4aWfoudhJGUiJGUiJGZkJ2Ui5eRhIuQg4mJen+DdHmEcniJd32Jd3qEcnWJdXiMeHqJ d3qJd3qOe3+Wg4eWi5SZjZadjJmaiZaVhouQgIaQgIaUhImQgIaSg4iQh46SiZGajpeekpuilqKj l6Olnayfl6efmquemaqblqablqabjpKRhIiMe3SLenOMgHqRhn+Rh4uUiY2ViIyXi46WjJKSiI6W iZCXi5GakZuelZ+flqObkp+WjZqRiJWZjpWZjpWUi5KRiJCVh5KVh5KNhJGRiJWVi5uVi5uQiZWO iJSSg4aOf4KJen2NfoCGfYSIf4eUhpSWiJaVi5uQhpaSh5WUiJaUiJaViZeajJWdjpeajpqdkZ2Z kaKZkaKZkaGblKOZlqWXlaOXkZqOiJGIf4mMg42QhoySiI6XjJeZjZmXjJWSh5CQh46Qh46QgIaQ gIaRhIuShoyOhIuQhoyOg46XjJeakJGXjY6Vg4KSgH+NgISQg4eWiYuWiYuOg3+MgH2Jen2IeXuM fn6Mfn6Ngo2Wi5abkJuXjJeWiZCOgoiMfYKQgIaRgIubi5WekZeajZSUf4KNeXuJdW+Qe3WQgIaZ iY6XiIuVhoiRgoeRgoeVgoubiJGbi5WhkJqhkJqdjJabh4SSfnuQe3uGcnKLdXOXgn+ijJaljpmd iZCZhoyahImahImhhoCfhH+deW2ad2qSenSQeHKMeXKEcmqLb2qSd3KJcGWHbmONcHCSdXWVeXub f4KWd3CSc22QdG2VeXKaeniaenibf3uhhICfi4Sfi4SihoCfg36hgH2hgH2bfXCVd2qUdV6Rc1yO b2mZeXOhhICihoKlf26VcF+OaVONaFGQbWKdeW6jfnmifXied2Sac2GadGqfeW+mgHumgHujf3Sj f3ShdWSfdGOfeGiheWmmemejeGSbb1Sbb1Seclaeclafcluhc1yfcl+hc2GjdWGqe2enf22iemim dVuhcFafbVehbliid2Oid2OjcleaaU+ZYkSSXD6Of4eMfYSUfYSZgomQhomSiIyWi5aUiJSUiJSO g46MfX+EdXiDd3qMf4OHfYCDeX2NeoCQfYOJen+QgIaOgoaViIyVjZKWjpSdjZ+djZ+bi5ebi5eR hIuRhIuUgImRfoeNgouSh5CekqGilqWhmaqimqufmq6dl6uakqKblKObl6OXlJ+UkZWMiY2MhImN houOgoOQg4SSiImWjI2ajZSajZSZjZaZjZaXjJebkJublqaemaidmaWXlJ+ZkaGZkaGXlaOal6aa kZmRiJCNhouNhouQg4mRhIuQhI2Sh5CWhpCUg42OgoiOgoiIeXuHeHqGeXqHenuNgouOg4yRho6N gouOhpKRiJWUjZmVjpqZjZmajpqhkp6ilJ+XjpuWjZqXi52ajZ+akqKdlaWblaGUjZmNhI6NhI6U h42WiZCWi5aZjZmVjJSRiJCQh46Oho2Uh4uRhIiQiI2QiI2Ng4mNg4mRg4yXiZKfkpSXi4yZhISZ hISOgoaMf4OOhIuRh42Qg4SQg4SNeXmMeHiQe3mQe3mNfoaWh46bkp+WjZqRhIuNgIeQfYCUgISa hIyeiJCilZmdkJSXhoSNe3qNcm6WeneVgoibiI6ZiY6UhImRg4ONf3+RgoSZiYydjJmfjpujlJua i5KZh4iSgIKQeX6OeH2Re4OZg4udjZWdjZWdho2Se4OVf3+WgICWgnuZhH6deniZd3SUenmVe3qR emuQeWqRd2uSeG2Jc2eHcGSGamOJbmeUdG6ZeXOWdWqUc2iQdWqVem+eeXSdeHOae3KhgnidiYOd iYOfh36ehn2ignuignuef3Kef3KZeXOZeXOaeniefnufg36dgHunf22bdGKUaViOZFSSamSbc22d eHWdeHWadWKXc1+XcmibdWumfXSle3OieXCjenKhdGWjd2ileW2ofXCsgGileWGfblufbluhcFih cFieblaeblaZa1qhc2Gjd2WnemmqfWumeWijdWGhc16jeGmqfm+og3KrhnSreWWhb1yhaUqXYUON foONfoONen6QfYCMf4COgoOVgIyXg46Vh5CQgouQfYCNen6MfoeRg4yRhIuQg4mOhIuQhoyNgIKR hIaQi46SjZGZjZmajpqXjZ6WjJ2bjJ6djZ+QhoyMgoiSg4iQgIaQhI2ViZKdkKOhlKehlquhlquh lKqdkKaajJebjZmbkpqZkJeVkJSQi46RiJWQh5SOiY2Qi46UjI6XkJKel6Oel6Obkp+akZ6XkZqZ kpuhmaiimqqblqaXkqKdlKGlm6ienqyenqyhkp6Rg46NgIeRhIuSf4OWg4eVhouVhouWh46UhIyL f4iJfoeGenmJfn2MeX2Nen6LgISJf4ONfoORgoeNgouRho6UjZaVjpeZjZmbkJuelZ+hl6Kilp+a jpefjpubi5eZjZmhlaGelKWakKGSiI6UiZCXi5GajZSZi5SZi5SbjJSZiZGai5KWh46WjJCVi46S iZGQh46ShomShomUho6XiZKfkpaajZGdiY2ei46Wg4eQfYCNgIeRhIuOgoaOgoaQgn+Qgn+Vf3+X goKUfoOZg4iekJuajJeUgIeNeoCSe4CXgIaZhomhjZGil56il56fiYmXgoKWd3CWd3CXhIuei5Ga i5CSg4iSfX2SfX2RfX+diIubiZ2di56ijpWah42af4aXfYORe4aOeYOOgoaViIyZiY6ZiY6dg4SS eXqVeXWVeXWMd3eSfX2ZfXmZfXmVgICahoaZgnWVfnKXeHSWd3OSdGiOcGSHaVyEZ1qObmOUc2iV d2qWeGuVeXKVeXKbem+bem+SeW6XfnOdgn6fhIClhH6lhH6lg3qjgnmig3mig3mjg4Cjg4ChhISe goKegn2afnmlfnSfeW+WcGWOaV6QYleVZ1yVb2iXcmqWcFyWcFyUb1yXc1+ec2Kec2KfdGWdcmOe c2SleWqlfW+ogHOrgG2ofmqjcl6ebVqec1udclqfak+aZUqSZVGhc16meHCneXKqfWunemmjdV6e cFqid2qne2+nfnWvhn2vg2+ofWmncFOdZ0mNfoaNfoaMf4OLfoKNeoCOe4KQe4eVgIyVgouWg4yQ g4eQg4eQh5SRiJWVh5CWiJGQi4yOiYuQhomQhomOho2SiZGZkp6Zkp6ajpqWi5aZjZuXjJqSjJKR i5GSg4iOf4SSiI6SiI6ViJqbjqGikKWikKWfkZ+XiZeViIyWiY2UjI6VjZCQjJKRjZSajJeajJeU jJGUjJGbkZejmZ+dmqyal6qVkZ2RjZmVjpealJ2lmqyflaeakZ6XjpuVlJufnqahnq6fnaydjpeN f4iOf4eRgomIfn+Ng4SQhoyRh42Zgo6Zgo6Rh42QhoyOg4KQhIONfoOQgIaSf4iOe4SGe3+Een6L fYaRg4yRi5aWkJuVjJmakZ6Xl6WZmaaimaOhl6KllqKjlaGbkJuflJ+ZlKWXkqOWjZeXjpmajpqd kZ2WkJmVjpebkp2dlJ6ekpuXjJWWjpSWjpSVkJSRjJCViIyRhIiQiI2WjpSblZ6fmaKhkJqbi5WV goaOe3+Ne32Vg4SOhIaMgoORhIaViImag4idhoubhoibhoidkJaekZebh4mNeXuOeX6Xgoeah4uj kJShlp2hlp2jiY2hh4uafn6Xe3uah42ei5GWiYuOgoOHcnSIc3WReoKag4uZi5SZi5Sah4uUgISX foKSeX2MdX2Nd36Qfn+Rf4CWhIaaiImZhn6VgnqUeHORdXCQc3OWeXmWfYCXfoKZg4iZg4ieg3ua f3ibeXSaeHOOd2KLc16MaFWJZVONbl6UdGSZdWmdeW2ZeXOWd3CZeGuZeGuRd2uVem+We3ebgHuf f3mignuign6ff3uihoKjh4OniYmmiIihhoCeg36dgHuafnmhenOjfXWad2uQbWKQZ0+OZU6QZFSW alqab1eZblaZb1ybcl6adF+Zc16ec1+ZblubcFufdF6bdWqlfnOnf3Knf3Kme2ifdWKhc16fcl2d a0+ZaEyUZ1Wfcl+leHOmeXSofWmid2OicFafblSdcmGne2qqgHqvhn+zg3iufnOneFeic1OOg46M gIyQgIaNfoOMeX+MeX+Me4aRgIuSg4uWh46Rh42Rh42SiZSVjJaViZeViZeSkJSQjZGRjI2OiYuQ i4yUjpCZkpualJ2ZjZaXjJWWjZeWjZeViZWSh5KNgouNgouai5CdjZKajJWbjZabjZubjZuajZSS hoyUh4uViIyXjZSWjJKSjpqVkZ2fkZqfkZqakJaakJahl5+mnaWhmaqZkaKWjpSSi5CVjpedlp+l laeikqWakJSWjJCXlZmhnqKloayemqaekZWViIyQgo2Rg46LhomNiIyQh5GQh5GVh5KWiJSWi5SZ jZaSiZGQh46WiJGXiZKQgo2LfYiIfoKGe3+LfoKNgISQh5SRiJWWjZebkp2VlaKZmaaflaaflaam mqijl6ajlaGilJ+ZkaGXkJ+ZjqGbkaOdkKaekaebkp2ZkJqakqKelqaakqKWjp6WkJaUjZSajY6Z jI2UhoaOgICRh42WjJKdlKGimaaelKWXjZ6ah42RfoSOen2WgoSOhoSOhoSWh4ybjJGfi5abh5Kd iZCbiI6bkZedkpmbiYuSgIKIfoKQhomajZSekZejlpqll5umkJCijIyXgoSSfX+Zg4udh46biYuO fX6IbmqDaWWLbniWeYOWh4yai5CZhIeRfX+WfX6WfX6ReHmReHmReHeUenmWgICeiIidiIidiIia gIKVe32UeXWSeHSWenqdgICehIafhoehh4aehIOfg3ubf3iVe2mQd2SLalSIaFGOcGOSdGebenKd e3ObfXOXeW+WeG6Vd22QbmuScG6Sc22XeHKZenCZenCXe3ibf3uihoijh4mmiYyni42niH6jhHqf hHWZfm+de3Chf3Sfe2+XdGiXbVWRZ0+OZFGQZVOXbliacFubd2Wjfm2iemiheWeic2Web2KacF2b cl6ZcGefd22jf3OlgHSnf3KlfW+meGGidF2bbleXalSWalqabl2eeG2ie3CoemOfclufblSebVOh b1yod2Osf3qwg36uf3esfnWmdVijc1aQh5GQh5GShoyQg4mRfX+Qe36RhIiXi46ajJWajJWaiZmb i5qWjZeVjJaUi5WWjZeXkZqXkZqVi46SiIyUiJGbkJmfkqWdkKKZjZmXjJeajJWbjZaSiZaSiZaU iJaajp2ekZeekZebjJGai5CSjJWUjZaQiI2MhImUiY2XjZGZjZmXjJeXkKGblKWflKKekqGakJae lJqjmqemnaqilqKZjZmUiY2Ng4eQiZKalJ2hkpudjpeWjJKZjpWel6GnoaqloaqhnaadkpmXjZSR iJCNhIyMiI6Oi5GSiZaSiZaSjZ2WkaGhlKafkqWWkaGSjZ2Wi5aWi5aUiY2QhomOgoaMf4OJen2J en2JfomMgIyRjJCVkJSdlqKel6OdlaWdlaWil6qlmqylmaeekqGdjZ+bjJ6ZjqOelKiflKKflKKd lJudlJuWkaGblqadlqKalJ+bkpqakZmei46fjJCahoaUf3+XiI2fkJWhl6KimaOZlZ6RjZaViIyR hIiOf4SRgoeVhouWh4yZiY6djZKhkZmdjZWijpediZKVjpqblaGhkpuWiJGRhIiWiY2hlJqilZui lZmll5ujl5aekpGVf4SQen+Of4eVho2Vg4SOfX6Nc2iEal+Da2eMdG+Vhoiai42ahIeSfX+Ve32W fX6Ve32UenuUen6SeX2Vf4KbhoieiI2fiY6bhouahImahIKVf32VfXiXf3qahIKdh4SfhoSfhoSj iIOfhH+bgHWWe3CRcl+La1qSdGeae26igIKigIKif36aeHeWd3OWd3OSb2eUcGiUbmOXcmeUdGSU dGSVc26beXSbf3uni4eqjI6niYyuhoKnf3uef3WdfnSjfnulf32mgnmfe3Oac2GQaVeSaVGUalOZ cmSbdGeefXSigHiqhnmohHimfXOheG6ac2OUbV2ZbmKid2qfe3CmgnengHWngHWne2ObcFibalqb alqXaliZa1qbcl6jeWWmdVufb1WXaEiXaEiVaFSdb1uqe3Syg3uufnCneGqlc1ifblSUho6Vh5CU iZCSiI6VhouUhImaiZahkJ2hlKaekaOdjpeajJWZjZmViZWUi5eXjpufkZ2fkZ2ei5GbiI6bjZah kpujkqKfjp6Wi5aUiJSUiJGViZKUi6KVjKOajKafkauekaWajaGai5CXiI2OiJSOiJSUiJSSh5KZ i5ahkp6flJ2flJ2dlKGdlKGdlJ6XjpmZjZmekp6jl6ailqWakJaUiZCMgoaLgISNg4eWjJCZjZaa jpeWjJKdkpmflqGqoaulnqWfmZ+XkZeXkZeUjZaVjpeVkZeVkZeVjpqWkJuZlqWdmqiim6eel6OS kZuRkJqVjJSVjJSZjI2Xi4yRh42QhoyMfX+Jen2Gfn6MhISQi46SjZGbkp2bkp2ZkaGdlaWfl6ii mqummqahlaGhkJ2ejZqWjZqXjpufkZ+hkqGXjpaZkJeZkJ2flqOhlZ6ekpuhlZ6dkZqbjZmbjZmb h4mVgIOWh4yhkZahlaGekp6Vi46OhIiRf4CSgIKWgIaahImWhIaWhIaZiY6bjJGhjZSfjJKbjJGa i5CUi5KZkJebkJuXjJeXi46ajZGjlp2jlp2llZ2nl5+jl5aekpGUfnuNeHWLeXiVg4KWgoSSfoCM c3KHbm2GcHCLdXWVf4eXgombhImVfoOXfoKXfoKVf4KVf4KQfn+Rf4CSe4OWf4ediZChjZShjZSh jZSljo6eiIiXfn+UenuVe3qdg4KeiYOeiYOfhH2ih3+egn6afnqdd2uWcGWWd3CefniihImjhouj h4Kbf3qWeGuUdWmUcGWUcGWVbWOSamGRbmKVcmWScGuZd3KffXqnhIKsiYiqh4aviICngHmde3Cd e3Cif3qmg36ohHmifnOfemmdeGeadWSbd2WadWSdeGeifXijfnmqh4Kui4aqgHqjenShd2GXbliS aVaZb1ybbm2idHOje2uogHCnfWSXblabZEqeZ02WaVWZa1eablWdcFeib1qea1aXaEeOXz+JZE6W cFqje26rg3WsfW2md2eialCdZUyUiJGZjZaajp2ajp2XjpaWjZWdkKKfkqWilJ+ekJuXjJWXjJWZ jJ6ViJqZjqGelKailJ+ekJuah5CdiZKajpqajpqdkpaZjpKWjJKSiI6QhomQhomRiZmVjZ2ajaGe kaWbkp+WjZqUiJGRho6Qh5GRiJKXiZKZi5SZkJqflqGlmaWhlaGdlKGbkp+ekpuajpefkZqhkpuh lZ6flJ2ZjpCVi4yQg4SNgIKQg4SbjpCXjJWajpebkJmflJ2hlaGjl6Ohl5+elZ2alJ2alJ2ZkJ2b kp+Zkp6alJ+alqKbl6Ofna+in7KfmqqalaWSjY6RjI2RjI2UjpCakpeXkJWViZWRhpGOgoOLfn+J goKNhoaSi42WjpGalZmdl5ublaGdlqKfl6efl6emmqammqailJ2djpeWjZqXjpubkJ6ajp2ViZKS h5CXh5GejZehlaOilqWhl5+dlJubkJmajpeXhIuUgIeRh42XjZSbjZaZi5SSg4aNfoCUfnuWgH6X goKfiYmVh4eVh4ediY2ijpKfjJKei5GbjI6XiIuUhIyWh46WiZCZjJKUiZCVi5GfkZqilJ2mkpms mZ+mkpmhjZSXf3mQeHKQfXeXhH6Zg4OXgoKQeX6LdHmNeH2NeH2NeIKUfoiahIyahIydh4yeiI2a hISahISUgIeVgoiUfYKVfoObhouhi5CfkJWejpSmkJKjjZCbhoiRe36Wenebf3udh4eeiIijiISj iIShhoKdgn6hfXSZdW2bfXOfgHeliZCmi5GjiICbgHmad2uUcGWScmWUc2eScF6Na1qQbl6ObV2Q al+Xcmeee2ulgnKsh4SuiIashnumf3Wfe3Cfe3ClgoCqh4arh3qjf3Oie3Cmf3SlgHWlgHWffWqh fmumf3Wmf3Wog36sh4KrgH6lenilemedc1+UcFuSb1qXa1udcF+ec2ene2+mfWKddFqaZUqZZEmZ aFebalqabViabVibalWZaFOWZ02UZEqNYkeUaE2bdGelfW+qfWunemmjblWbZ06WkJuZkp6Zjp+Z jp+ekqGekqGdkKOajaGXjpmWjZedjJaejZeXjJqbkJ6fl6efl6edjpqbjZmVho2ZiZGXjJWbkJmZ lJeVkJSSi42NhoiRg4CRg4CRh42Vi5GZjp+dkqOWkJmQiZKQgouRg4yOhpCNhI6Vh5CajJWdlJui maGmnaehl6KekJufkZ2ekJubjZmXjpaZkJeel56fmZ+dlZedlZeWiY2ViIybjpWfkpmakZmZkJee jZehkJqjlZ6ilJ2flJ2hlZ6jmqeimaabkaKdkqOWkaGZlKOdl6einayin7Kin7KbmqWRkJqVhouW h4yWkJaZkpmblZ6WkJmZjJ6ViJqUi5WQh5GSiI6UiZCWjJKakJabkZWhlpqekqGekqGflqGflqGe mqOhnaailp+ekpuZkpuZkpuXjpaSiZGVho2Sg4uZhoydiZChkqGomqijmqKdlJujlJadjZCXgoyV f4mXi46ajZGdkJGXi4yRgoSQgIOZgoedhoueiJCljpabjpKViIyjkJanlJqlkZefjJKfjJCXhIiU foCVf4KRgoSSg4aOf4KNfoCXiJCai5KijpWolZunlJqhjZSZhIKXg4CXhIiZhomVh4eUhoaVgoiU gIeShISRg4ONeHiNeHiVfoadho2liZCliZCiiImiiImZhomVgoaSfoCQe36UfYKag4ihiY6ljZKo lJaolJadiIuOen2Rd3KUeXSZf4CbgoOjhoalh4emhoKhgH2ffnWbenKigHWmhHmmi5anjJemi3+f hHmjd26bb2eZc2iWcGWQa12RbV6SamGRaV+SbWKWcGWaeW2jgnWuh3+shn6qiH2lg3ijfXWfeXKn gn+qhIKrh36rh36ognirhHqqhn2ohHulf3CmgHKngHWmf3SmgHungn2qgoCmfn2ofW6id2ibclWR aEyXZ1Sda1iab1yfdGGld2Kld2KfaUiZY0OWaVOabVabblqbblqaaFWaaFWXaFCVZU6VZU6UZE2Z aluic2OmeG+oenKreVyjclWajaGbjqKZlKWalaaelqeblKWdkqOakKGWkJmWkJmfjpufjpudjpql lqKll6qjlqihkZabjJGWiIiajIyXjpmXjpmUkZWRjpKNhoaNhoaMgH+NgoCVg4SbiYuZi5abjZmW iY2OgoaOe4KNeoCMfYSOf4eQhoyXjZSelZ+jmqWfm6WdmaKekJubjZmXjpmWjZeXjJWdkZqemqah naijmqWhl6KdlJ6dlJ6blaGblaGZjZaZjZadjZKfkJWhkp6ekJuflqOlm6imm6ymm6ydlp+blZ6d laWdlaWflqOon6ysn7KnmqyXlZmOjJCWiZCajZSSkp+Skp+akZuXjpmZjp+Zjp+SkJ+UkaGZkpuZ kpuakJaakJadkZqflJ2bkaKZjp+ekp6hlaGbl6GdmaKhlaGflJ+fkZ2ekJufkJWai5CbhIyZgomO gH6Vh4SZjZunm6qenaebmqWfkpaajZGah42ah42bjpWdkJadjZCfkJKXiYmUhoaXiZWilJ+jlaOe kJ6akZmVjJShlJqjlp2mkZ2lkJujjZWeiJCZh4iWhIaRf3uOfXmUenmSeXiJfn2QhIOZjJCekZWd kJSdkJSdiIudiIuhiZadhpKXi5GViI6diZKhjZaijpWbiI6Qe3SLd2+Rf4CaiImlkZqmkpumjpSe h4yXhoeUgoORe3mNeHWQeniVf32bh4mjjpGokpWnkZSli4yXfn+McmqLcGmRdXKWenehf4Cnhoeo iIKlhH6ifnOdeW6denWjgHulh4ymiI2qhH+og36lgHSdeW2dd2ueeG2XdWWRb1+Oa1+Sb2OXc2SZ dGWbenKffnWmgH6rhoOui4iqh4SohHueenKhe3elf3qogICrg4OnhHSqh3emh3qmh3qrgnirgnir hHqlfnSiem2je26lfnejfXWlfWqmfmufd1SddFGfblufblubblqidF+ld1+ld1+jc1OhcFCXbVee c12bb1GZbU+dZ1ObZVGWbVOZb1WebVefblihb1emdFyjdWGld2KqeF2ndVufjaGikKOelKaelKab lKOZkaGZkp6blaGekJmekJmfjpuejZqfkZ+nmaejmaqhlqedkJSajZGZjJCajZGWjpSWjpSWkZWU jpKRh4uMgoaIgIOIgIORfXqWgn+ShoyUh42QgIaLe4CLdXqLdXqNen6QfYCNg4eXjZGhlJqnmqGh l5+ZkJeXjZSWjJKWi5SXjJWajpehlZ6ina6ina6lmqyil6qhmaifl6edlqKZkp6ajZSZjJKWjJCb kZWelZ+dlJ6fkKOmlqqlnayimqqblpqVkJSdjpqhkp6blZ6mn6iroq+imaadkJaWiZCWjZWimaGa l6eVkqKVjpeWkJmZkJ2dlKGWkaGalaWel6GblZ6alZmdl5uflKKilqWdkKKZjJ6akZ6flqOflaah lqedlp+dlp+hkp6fkZ2hkZaZiY6hiY6dhouVhouOf4SUi5emnaqlnayjm6ufkZqbjZaWjJKVi5Gd kZqflJ2hlp2elJqajo2ZjYyXkZeel56jmqedlKGdkZ2dkZ2jlJmmlpuqlaGmkZ2jjZeijJabjI6X iIuZhISVgICQe3mLd3SId3ONe3iSg4iZiY6ai42djZCZhoybiI6fjJWei5Sii5Keh46RgI2djJmo kZ6jjJmZgHqMdG6IdHKUf32fkJemlp6olJahjI6Xg4ORfX2OeXeLdXOQdHCXe3iZh4ajkZCnlZam lJWnjYybgoCUdG6NbmiSc22Wd3CfgoSihIeihoChhH+fgHebfXOafnefg3ujh4eliIiliICihn6h hnieg3WignuignuffW2Vc2OWamKabmWadGqeeG6denWhfnmmgH+sh4aujYmnh4Omg36ffXieem6j f3OmgHuog36mhHinhnmoh3qnhnmohHurh36sh4KqhH+ifmOfe2GjenCieW+mfmuiemihd16fdV2h dGOhdGOdcmGfdGOdd12adFufdFyjeF+adF+Zc16dbk6dbk6faUyhak2iclqmdV2mdF6mdF6meGGn eWKleWOjeGKqeF+qeF+djZ+fkKKelqablKOVkJ+Ujp6ZkJ2dlKGdkpmdkpmdkpmdkpmikqWnl6qi laedkKKblJmakpeekZebjpWZjI2Uh4iWjJKUiZCRhIuNgIeHenuHenuOe3WVgnuUgIeUgIeQfYON eoCNd3uNd3uMenuQfn+Ngn6ViYabkZehlp2jlpqekZWVjZCSi42ZiY6ai5Cdlp+im6WjobClorKh maqelqeimaahl6WbkJmWi5SZiY6ZiY6WjpSakpeakqKZkaGXjJeekp6imaaimaaakJGUiYuXi5Ga jZSUkp2enaemn6udlqKWiZCViI6ZkJ2jmqealaaVkKGSjJeRi5aXkZ2alJ+dlaWakqKjlZ6jlZ6f lqGimaOilqWflKKZjZmWi5aajpqdkZ2hlaOjl6aelKWhlqehl6WdlKGhkpuekJmfkZqZi5Sai5KQ gIiUjZmhmqanl6qnl6qhkp6djpqXjpaZkJeel6OdlqKjmqKflp6ZkZSXkJKdlp2fmZ+hmqael6Oe lZ+flqGmkpuqlp+mmZ+ll56jkp2ejZedjZWfkJedjZKWh4yVgIONeXuLd3SMeHWXg4abh4meiYeh jImaiImejI2fjY6fjY6hi4uhi4uSgoyZiJKllJ6hkJqehoCWfnmQdW6UeXKajIyml5eqlZWjjo6f hoSXfn2UeXSOdG+MbWeSc22SfX2fiYmjkJamkpmukZaihouXd26JaWGObmWXd26df4KjhoijiICi h3+hgnWdfnKafnmdgHuiiIehh4afiHuhiX2liIOmiYSri4esjIiognedd2uZc1yadF2efXSffnWh f3eefXShfnmlgn2sjIiqiYaqhH+ifXied2med2mhfXSng3qrh3urh3usg3mrgnimgnmohHunhH+q h4KlgGibeF+hdWeid2ilfmmhemWheWeheWefeGqfeGqbdWGbdWGZc1yXcluhcmKmd2eleWOjeGKi dE2db0ifaUqjbU6od2Oue2iofWSofWSuf2qsfmmrfWioemWmdFyndV2XkZ2alJ+XkZqXkZqXjpuX jpubkp2bkp2bkpqbkpqakJaakJaakqKblKOelaKakZ6ilJ2ilJ2fkJeai5KZhomXhIiViI6WiZCR h42LgIeOgICLfX2Qgn2XiYSXhoeVg4SQeoKNeH+Md36Md36MenuOfX6SfoCZhIedjZWhkZmikpeb jJGQiI2RiY6ZiJWfjpufl6emnq6nn6+mnq6dlqKel6OhmqOfmaKakJaWjJKWh4yXiI2SjpeZlZ6d lp2XkZeSjJWZkpuflKKhlaOakJSSiIyRh4iVi4yWkJmXkZqdlp+ZkpuUiY2Vi46dlqKlnqqbkJuV iZWRh4uSiIyXjpaZkJebkp2XjpmajJWekJmbjqGilaeflqGbkp2ZjpKVi46ViZKWi5SZkJ2elaKe lKWelKWfkqailaiilqWilqWhl6Kbkp2ekZeViI6SiZSbkp2hlaOilqWflJ+ZjZmakZ6dlKGil6qe lKailKKekJ6Wjo6akpKdlJulm6OlmaKilp+bkp+bkp+jlaGml6OlmaKjl6GikZuejZedkJSdkJSf kpaXi46XhIuWg4mSfniOenSVf4Kbhoifi42hjI6hi42hi42ejIudi4mhhoyhhoyZg4iZg4iikpqm lp6ijYeXg32Nem2Sf3KZh4imlJWsl5qlkJKhiIOXf3qWenOSd2+LcmeHbmOLd3CVgHqhi5KijJSq jJSlh46Zem6LbWGIamGRc2mbenuhf4Cni4Oihn6dgnSZfnCbf3qdgHuihIejhoiliIiliIiqiYeu jYuwjYyvjIuviXiifWuWcFqZc1yig3elhnmnhnmhf3OigHWjgnenh4Cnh4CuiYCmgnmdemqZd2ef eW+jfXOohHmrh3uwg36vgn2lfnSngHeog4CrhoOlgGqhfWefdFybcFifd1yjel+lemenfWmne22j eGmec2KbcF+Zc1yVb1iicF+od2WqfWuqfWuneWSjdWGhb1ejclqnemGugGewhGusgGi2hGuygGiw fmivfWeqeVyjc1aZkJeVjJSWjJ2WjJ2ajJqbjZuekJuilJ+flJ2dkZqZjJKajZSWjZqZkJ2bkpqd lJujl6OhlaGejpaZiZGZhomah4uVhoiVhoiSiIyJf4ORgoeUhImZjJKajZSZiJKWhpCQgIaNfoOM fn6LfX2NeXmNeXmQe3WXg32ZiY6ejpSdjo6ShISOg4KRhoSWhpCfjpmhl6Wlm6ilmaWekp6dkZqe kpuflqGhl6KakJGQhoeOhIiUiY2VkJ+ZlKOdlp+XkZqUiZCXjZSajpqdkZ2ZjpKSiIyZiY6bjJGb kZebkZebkZeZjpWVi5GXjZShl6KimaOijpWXhIuUh4uWiY2akJaakJaXiJCUhIyUh42WiZCVjJaa kZualJ2blZ6fkpaViIyQhomRh4uSi5CXkJWblZ6dlp+dkKOjlqqll6ummayimaGelZ2bkZeVi5GQ h46VjJSXkZqblZ6dlaWakqKflaeelKallqWhkqGei5Sah5CZjI2ajY6fkpanmp6om5+ll5uVjJSV jJSZjZubkJ6bkJ6flKKllZ2fkJedjZKai5Cei5GbiI6XhI2UgImVgoaVgoaVhoiZiYyhi5CijJGn kZGljo6djZCai42hho6ih5CahImeiI2fkJWmlpumkZGfi4uXg4OVgICbhoumkJWqkpeokZajiYud g4SXfXWQdW6Rd2uLcGWIbmeSeHCah4ufjJCijJGijJGagG6Mc2GIal2Nb2KaeXqlg4Smh32dfnSZ fnCZfnCdfnSef3WlhIKlhIKjhoiniYyojIyqjY2ujYuwkI2vjYKjgneWdGSXdWWjfnurhoOqiH2n hnqmg3CffWqjfnmog36shn6ngHmfemebd2OZcm6feHSlf32rhoOvhn+vhn+nfWmlemehfXSmgnmh g2+fgm6jd12fc1qeclaidVqmeWinemmofWSjeF+hc16db1uZbl2XbVylc2KndWSnfWSrgGiofWun e2qldFymdV2nd1yvfmOwgmqvgGmwgm2wgm2uf2iuf2irgGGme1yZiY6ZiY6ViZeSh5Wai56bjJ+b kaKdkqOdkpmZjpWZiYyZiYyVjJaakZudlKGhl6WomqallqKXjZSSiI6VhouVhouUhImVhouRho6R ho6Ui5WUi5WXjpuelaKhkaWZiZ2OgoaMf4ONg4SOhIaUf4KNeXuNfXWUg3uShomXi46bjpCRhIaM goOOhIaSiI6XjZSflJ+hlaGhlp2ZjpWWi5aflJ+hl6WelaKhkpKajIyUiZCZjpWblKWdlaadl5uW kZWShoeViImZjJKajZSbjJGbjJGdjpqekJuZkJeWjZWbkZWZjpKViZWZjZmhlaOhlaOlkZediZCZ iY6fkJWfkJWbjJGWhIaRf4CUfoaahIybjJSdjZWelZ+flqGdlZqVjZKQiIuLg4aUhImZiY6WkZWa lZmdkZ+flKKilKKllqWjl6GekpuakJaXjZSRh42QhoySiZGXjpablKOZkaGdkqOelKWil5uelJed iIiXg4OdiIueiYyekZWmmZ2om52mmZqejIiUgn6ZhISbh4eZjJCbjpKhjZaijpeei5GZhoyag4ue h46XhIuUgIeah4udiY2ajpqbkJufjpmikZull5milZahlJeajZGeiJCeiJCei5GdiZCfkJWjlJmm kpajkJSbho2ahIybho2jjZWrkJmvlJ2skJKhhIedgn6We3iSeWqMc2SHaGKQcGqReHmiiImfjY6f jY6bgm2Qd2KOcmGRdGOdenmhfn2ffnWde3OZeGude2+eg3ieg3ilgn+nhIKliIumiYynjY6qkJGu jYuvjoyrjH+lhnmbc2mXb2Wbd3Sog4CujYmqiYaqhHCifWmfeW6lfnOngHWngHWlfWqfeGWWcGeb dWuienqshISrh3uohHmhfWSbeF+ZdWmdeW2hfXKdeW6hcFiiclqdb1ihc1yidWSleGelel+jeV6h cFiba1SXblSZb1Whc1yld1+qf2esgmmrf3Cqfm+nel6leFyqeVyygGOzgG2wfmqwgHCygnKvf3Sz g3izgmmvfmWWg4mWg4mUhpSUhpSWh5mXiJqbjqGdkKKakZmZkJeZiY6XiI2WiJGbjZadlaahmaqi lp+dkZqQhomLgISQg4mRhIuRh4uVi46ZjpWZjpWZkpublZ6fkqWjlqihjqKZh5qViImWiYuZjJKa jZSVgoiVgoiXhoebiYuWh4yXiI2ei46VgoaQg4mRhIuOhIiVi46hlJqhlJqXkJWSi5CUjZadlp+l lqWilKKakJSWjJCajpedkZqlmaelmaejmZ+elJqbh4mZhIedjZCdjZCakJaelJqekaOfkqWjlp2d kJaZjJCXi46VjJaZkJqZlKOVkJ+ajpqXjJeakJadkpmelJeZjpKUhIeUhIeUh42ZjJKXjJqekqGd kqOhlqedlZqWjpSQgIaOf4SWg4yei5SWkZWVkJSfkJehkZmbjZmfkZ2flZudkpmbkZKVi4yRgoeQ gIaMgoiSiI6VjJSXjpaZkJqdlJ6jl5aflJKijZCdiIueiJKfiZSfjpuol6WuoaWrnqKjkY2di4ed i3qaiHiaiImbiYudiZKhjZadjZWai5KfiYyZg4abhImdhouei5SfjJWajpebkJmjlJullZ2om5+q naGqmp2fkJKii5CfiI2fjpuhkJ2mlp6mlp6nlJehjZGah42ah42liZCliZCrkJuukp6ukZSni42d h4eXgoKVgHeQe3KObmKLal6McG2fg3+hjZGdiY2agHOSeWuScmWVdGibeXShfnmhfnmhfnmefnii gnujh4KliIOliIOliIOmiYyojI6nkZGokpKrjouojIiujoSmh32he22ZdGWadGqjfXOohoOvjImv h3Srg3Cjf2efe2Omfm6ogHCrfm+meWqbcGKXbV6ZcnKheXmoh36nhn2lfWqbdGKbcGKdcmOfc2Ki dWShb1Ohb1OdbVWba1SbblyidGKhdV+hdV+ebVeaaVSbbU2fcFCdc1iieF2lf2uog2+rf3CofW6n eFamd1WjeWGsgmmyh3OwhnKzhHKwgm+wgHWzg3izg3O0hHSNg4eNg4eSgo6Xh5SXiZKZi5SejZ2e jZ2djJmZiJWah42ah42ZiJKfjpmhlqeflaabkZWUiY2Rg4CQgn+RhIiViIyVjZCWjpGZjpKZjpKa lJqdlp2elaKflqOfkZ2ajJeVkJGVkJGekpudkZqXi5GViI6ZiZGXiJCZjI2ajY6ajZGShomMgoiM goiRh4uUiY2akpeZkZaUjJGVjZKUjZaalJ2dkZ+bkJ6bkp2ZkJqdlJ6dlJ6il6qhlqiel6OblaGa i5KVho2biI6ei5GXkZeel56hlKailaellp+fkZqZiYyVhoiUi5WZkJqVjZ2UjJuXjpuVjJmbkJmf lJ2ikpqbjJSVi5GVi5GXjZSakJadlKGelaKhl6Whl6WhlJeZjJCVgIOSfoCVho2bjJSakJaWjJKX hIubiI6Xi46bjpKfkJKfkJKXjouRiISUf4KUf4KMeX2Oe3+Qg4eUh4uZi5SekJmilZuekZebjZab jZaZiJWbi5ejjJuqkqKmmqajl6Ofjpmbi5WdiIiahoaahIyahIyai5CdjZKWi5SXjJWai42XiIue iI2fiY6fjpmhkJqekZefkpmmkpaqlpqomZ6snaKqm5mjlZKjjZKfiY6hjJqlkJ6qlp+qlp+rlZen kZSeiYybh4mdiIuijZCikpqhkZmrkZCjiYiihISfgoKbf3+WenqRd2mJb2KMbWqdfXqijI6jjZCd gnqVenOUc2iScmeZeXOff3mefnqff3udf3+ihISni42ojI6mjJCiiIyliIimiYmnjYyrkZCyjZCu iYyvjYSnhn2le3KddGqXcGGddWWhf3enhn2shnuuh32rgmSjel2feGWlfWqqfm+ofW6meGWfcl+a cmmheG+qg3irhHmme2ifdWKbcl6acF2dc1ihd1ydcFWbb1SablWfc1qhc16hc16hd1ybcleXaFCa alObaEmhbU6bcl6ieGSrf3OwhHiyhHOqfWund1Ojc0+ld2SrfWqshHSvh3ezhnSvgnCwg3KvgnC2 hnW6iXmShoyRhIuXhIuZhoyViImZjI2ajZSdkJaZjJKXi5GWiZCWiZCZiJWfjpujkqKejZ2Zi4uS hISNgoCOg4KQhoyZjpWbkp2dlJ6ekZefkpmblJmelpullqWnmaeflqGdlJ6elZ2dlJuhlZ6ekpuX i46ZjJCXjpmXjpmakpKZkZGajZSUh42NhouQiI2VkJGWkZKblJmXkJWZjJKWiZCWiJaXiZeWjZWW jZWakZuakZubkp+dlKGXkKOakqadlaWakqKWiJaUhpSXgI2XgI2UjZSdlp2flaaflaaflKKajp2X i46Uh4uWjZeZkJqUhpGUhpGVjJmbkp+bkpqflp6jlJudjZWXjJeXjJeXjJWajpealJ2blZ6flqOh l6WjmZqbkZKWgoKSfn6Zg4ueiJCfkJWdjZKZhIeahoiZh4ibiYudjo6djo6XjIuRhoSSfn6RfX2N eH2OeX6NfoOQgIaVho2djZWfkpmfkpmekqGbkJ6ajJWZi5SdjJaejZefkZ2ml6OejZ2djJuah5Ca h5CZhoydiZCdiY2ei46XjJWXjJWbjJSai5Kei5Sei5SfjpmikZulkZehjZSikpWnl5qlmp6onqKq nZ6nmpumjpadho2ZiJKhkJqmkpuqlp+rlJmnkJWeh4ybhImag4ieh4yfiY6ijJGni4eihoKhg4Od f3+aenidfXqWenqQdHSRcHKbenufhoejiYuhhoCaf3qRd2uOdGmVdXOZeXehfnuhfnudf4KihIen jJKrkJankZGljo6miIijhoani4eskIywkpKukJCui4ariIOrg3Wed2mbcF+ec2Kdd22ie3KqhH+w i4augnCmemmed2Sje2mqfXCoe2+idWSidWSec2ehdWmjfXKmf3SngGehemGfdV+fdV+hc16hc16e cFydb1uhc2Gld2SneWSld2KidVqhdFibak6ZaEyaaU+hb1WhdGioe2+ugHmwg3uyg26oemWnd1qo eFujd2Woe2qshnCzjHe0iHSzh3OwhHOsgG+wg3S3iXqWiZCUh42XgomXgomWh4mZiYyZjpWXjZSX jJWXjJWajp2XjJqdi56hjqKjjpqeiZWXhoeUgoONg4eWjJCflaail6ihlKehlKehlZ6flJ2hlpqh lpqekqGhlaOfkqajlqqfl6ejm6unlqGllJ6bjJGbjJGbkp2imaOfl5qfl5qdkZqViZKUjZSSjJKX kZeblZualZmVkJSdh5GZg42VhouWh4ySiIyRh4uWiJGZi5Sdi56biZ2WjZqSiZaaiJuaiJuSh5KN go2UgImSf4iSh5CViZKZjZaZjZaajJWWiJGSiIyQhomZi5SZi5SOeX6Re4CShI2XiZKbkp2dlJ6f kZqbjZaUiJSViZWakJaakJabkJuajpqbjZailJ2om52ekZKXgIaVfoOag4uhiZGikpedjZKZhomU gISXgoeahImeiI2hi5CfjY6XhoeQgoKNf3+QeYCOeH+Oe3+RfoKRf3ubiYajlp2jlp2flqOdlKGe lJeXjZGajY6Uh4iXi5GdkJaeiZqfi5uViZKViZKZiY6bjJGah42ZhoyXhIuXhIudiZKdiZKfjJWi jpejjZWijJSdh5GahI6ai5ChkZailp+lmaKqnaOrnqWqkpqfiJCWf46ag5KdiJSjjpqnl52llZqn iY6fgoebfYSfgIiehIifhomjiIOfhH+eg36dgn2bfXOZenCOenSMeHKOcnSUd3mdg4SmjI2jiH2e g3iZe2qOcmGOcGSUdWmdeniffXqefX6oh4iojI6ukZSqjZCojI6nhoeigIKnhIOriIewkIysjIis i3+si3+shHemfnChemWbdWGacmiddGqifXiog36siHuohHiofWmjeGSfdWKieGShc2Gfcl+ec2Kf dGOee2ujgHCofWuleWihem+lfnOoe3OleG+lc1+lc1+lemeofmqsfmerfWWqeFusel2lcE6ZZUSb ZE+mblimd2uufnOygoCzg4Kyf2mue2Wod16semKod2OqeGSsgG2yhnK0hnC0hnCzhnSwg3Kwg3e0 h3qWi5SSh5CXg5GVgI6Zg4uahIyUiJGViZKWjZWWjZWbkJuZjZmdjJaejZebi5WZiJKZhoyXhIuU h42fkpmjma6lmq+mmaull6qhl6KflqGhlJeilZmZkpmalJqbkaOil6qfmqqjnq6rlqKnkp6fkpme kZeflqOhl6WhlaGekp6ZjZmZjZmXkZqalJ2elaKflqOZkpuQiZKWh4yUhImRhIaQg4SNgISNgISO hIuQhoyVho2Sg4uRhIiShomXh5SXh5SRh4iLgIKOg4KViYiRh4uOhIiXi5GXi5GZiZGWh46UiZCU iZCWh4yWh4yRen+QeX6Lfn+WiYuZkZSblJabkZeakJaViI6ViI6ZiY6bjJGhjZSdiZCfjpmikZul lqKhkp6aiImWhIaWg4ediY2ikZuejZeZhIeVgIOVgoidiZChiZGii5KhkZaZiY6Uh4iUh4iWf4SS e4CQfYORfoSRfoKZhomjkp2mlZ+hmqGel56jlZ6bjZaeiYmXg4OZhomdiY2ZiZGXiJCbiJGbiJGb iJGhjZaijpWei5Gag4uag4uXhIibiIyhiZGjjJSdjZKZiY6UgISWg4eVh5CajJWilKKml6awoq6s nqqvmaGijJSbhoiWgIOUfoadh46nkJ2nkJ2miJCegIihe3meeXeaf3qhhoCdiIKdiIKegoKdgICb e3WVdW+IdWiLeGqNc26SeHOif36nhIOmh32ig3mafWuVeGeRcmKQcGGZeG2de3Cbf3ijh3+ni4eq jYmrjo6qjY2nhIKlgn+og4Kog4KnhH+ohoCvjIewjYiuiHmog3Sjfm2eeWiedWuddGqfdXOnfXqs g3quhHuvg22ne2WhdV2leWGld2SjdWOdd1+Zc1ydeGelf26mfXOnfnSmfnqogH2ugnWqfnKneWSo emWof3eof3evf3SwgHWuf2irfWWndWSfbl2hak2hak2jb2OueW2vgHuzhH+wgm2vgGuue2GwfmOu e2Ood16seme0gm63h3ezg3Owg3KyhHO2hnq0hHmZjZmZjZmXiZeUhpSRgoeSg4iNhoiUjI6XkJ+Z kaGbjZudjp2ajJWbjZaXiZKWiJGbh5Kbh5KXiZWhkp6fkqajlqqqoa6mnaqhl6KimaOelJqdkpmf kpSfkpSakZujmqWmn6uooq6qlaaolKWflqGakZuel6GhmqOhlaOdkZ+WjpSWjpSfjp6ikaGjnaah mqObkJuSh5KQhoeQhoeRg4OOgICHeHqHeHqLe4CLe4CUeX+UeX+MeHqSfoCVhouZiY6RhoSQhIOS hoebjpCVi46UiY2ZiYyXiIuVhoiZiYyXi4yajY6biIyah4uUf3mSfniOgICWiIibjpKekZWdjZKa i5CWh4yWh4yZg4uahIyZiY6bjJGfjpuikZ6ikaGikaGajZGViIyUh4ibjpCekJuekJuahoaXg4OV goaah4uZiJKejZehkpuekJmdkJafkpmhho6ZfoeQgIiRgomOf4KUhIefi5alkJunmaKllp+ijpeb iJGQgn+Rg4Cdg4SehIadi4yaiImfhI2ih5Chi5WmkJqmkpujkJmXgoSVf4KUgn6Zh4Obh4eeiYmf iYeXgn+SfXqUfnuNgIeViI6hkJ2nlqOsnqysnqywnaalkZqhh4aZf36OeniWgn+eiJChi5Kdhoub hImef3WZenCZfX2egoKliIiihoaihISfgoKee3eVc26Qc2KRdGORc2eSdGiee3elgn2jiICfhH2d gnObgHKQc12LbliZdWqhfXKegnqdgHmhg4OrjY2zkI6wjYysiYSohoCmg4Clgn+mg36nhH+siYev jImsi36qiHulgHSifnKje26heWuecmmleG+qgHewh32yhnSsgG+ne2OofWSof3WnfnSje2ufeGie eGOie2eje2unf2+mgHush4KyhnSrf26rfWqoemiuf3iyg3uyhHuwg3qshnCogm2ogmimf2Wsd1Gj bkmja16udWirf3OwhHiwhG6whG6yg26yg26zgG+semmreWGyf2eyhnSyhnSzh3qzh3qzg3OygnKV i5uUiZqXiZeUhpSOf4KSg4aVi5GelJqflaaakKGUiJGSh5CWiJGZi5SViZKWi5Sbi5edjJmbjZaf kZqjl6Gonaanoaqlnqemmqilmaeml6OjlaGljZKljZKikpqrm6Oon6yroq+nmaKjlZ6dkZ2bkJue lZ2elZ2hkpudjpedkJSdkJSakZuflqGjn6uhnaimkZ2ahpGZjJCZjJCWiY2RhIiIenqHeXmLfX2I enqIeXuIeXuIeXuSg4aai42ejpGViIyUh4uWi5SajpedlJ6ZkJqekZKajY6ekZWhlJeekZWbjpKW iY2Uh4uQe3uSfn6Vf4edh46bjpKZjJCdjZWZiZGRh42OhIuXgIadhoudkJafkpmekaOekaOmkKWl jqOjkJmfjJWai42djZCdkZqdkZqbjYuUhoObhImii5CakJabkZeilp+flJ2jlJullZ2ijpediZKU h4uRhIiWgoSWgoSai5KfkJeilZuhlJqajZSWiZCZhIeZhIedh4meiIuajouajouZhIeeiYyjkJmm kpuijJSeiJCQfn+Qfn+SfoCWgoSbh4mdiIufhH2dgnqafnqWeneOenqVgICbh5KhjJemkaKvmquq maajkp+dg4KZf36SeHOUeXSWf4SfiI2hho6hho6ign6ZeXWbfoChg4amjJCli46jiIShhoKZgnWS e2+VemuUeWqWdWqScmeaeXCjgnmmi4ejiISmi3+ih3uhgHCZeWmdfXqign+nhIKlgn+jgH+qh4av i5Cvi5Cwi4irhoOjgHulgn2if3qjgHuhhoKliYami3+jiH2ng3qlgHile3Wle3WjenSfd3Cje26s hHeyhHuyhHusgG2sgG2rfnmugHusg3qqgHiqd2iseWqmemmofWuqhHWsh3iwg3KugG+qe2mrfWqv gHizhHuuh32viH6vi3+uiX6yhnSwhHOugGSoe1+ld1+neWKsgHSvg3euhniyiXu2iH+0h360hHSv f2+qfWGvgmWohnWvjHu0jYK4kYa4i3uyhHWUiJSRhpGOg4yMgImSg4uVho2XiZWhkp6dkZ2Wi5aV houWh4yVh5KbjZmekJ6hkqGbkJuXjJeZiZGdjZWflqGjmqWjmqWjmqWnmaWnmaWmlqqikqajjZWi jJSflJ2mmqOom66rnrCmnaWelZ2ajpedkZqfkJefkJefkpmdkJaakJSakJSdkpajmZ2roq+mnaqj lJuejpaflJ+hlaGakJaVi5GUgISQfYCLe36MfX+VgIORfX+LgISUiY2bjZadjpeXiZKUho6Sh5KZ jZmekaWekaWZlZ6VkZqdlKGelaKdlZqWjpSQhomUiY2Uh42RhIuViI6ajZSbjZaekJmbjZmUhpGR ho6UiJGbho2ijJSilJ2hkpuajZ+ajZ+njqGokKKhjJqhjJqijpWfjJKajpedkZqejpGZiYybho2f iZGekZefkpmhlp2flZullJ6nlqGlkJuhjJeah42ZhoyXgoeVf4SfkJWfkJWfkJWejpSZho6Sf4iW gIadh4yeiJKeiJKai5Cai5CXjIiajoullJ6mlZ+hi5KXgomOeniNeXeRen+XgIaahImahImfh4Ke hoCXgoSWgIOUe3eVfXiVf4Kdh4mjkJmrl6GqlJ6okp2lg4eaeX2UeXWSeHSRen+VfoOdgoibgIed gIOafoCZf4OfhomljZKljZKnjIihhoKeg3ubgHmXe3SVeXKVd22SdGqaenSjg32vjY6vjY6rjoen i4OmhoKff3uigIKmhIani4eliISigHilg3qqjIysjo6wjI6qhoimhHiigHSefXKffnOee3mif32r h3urh3uohHmmgnehfXChfXChe3ebd3KidWmoe2+vgHmwgnqvg3eugnWqg3uviICyiH6vhnuvf3Kr e26leWiofWurg3Ovh3evg3KugnCqe2eoemWuf3evgHiuh3+viICwh4Cwh4Cwh32uhHqrfm+ugHKs gG2rf2urf3Cvg3SugHuyhH+3iYK2iICziHSsgm6neWKrfWWvh3m2jX+2ko23lI67kYu2jIaUgIeR foSOf4eRgomVg5aaiJuejZqejZqZiZGVho2XhI2ah5Cdh5umkKWllaejlKajlJuai5KZhombiIye kp6flJ+elaKelaKhl6Wjmqejl6ahlaOijpeijpeekp6lmaWrnrKrnrKnnaOelJqejpSbjJGjkJmi jpedkZqZjZaXjZGakJSflZmmm5+oobCmnq6hl5+flp6umaqvmqujmZ+bkZeah42ZhoyVh5KUhpGa hoiXg4aUhpGbjZmekp6dkZ2dkJSWiY2Zi5SbjZaelKWjmaqfl6ielqefmaWjnaihlZ6Wi5SVhoiX iIuViI6WiZCXjJWajpefjp6ejZ2bi5qXh5aWiZCWiZCdiJSijZmilqWhlaOijZmijZmhjJqlkJ6i kZ6ikZ6okZ6ljZqXi5GZjJKZiY6ai5CbiJGdiZKZjZabkJmbkJuflJ+olKWqlaaikqWdjZ+ijZmX g46XgIaVfoOai5KdjZWfiZGdh46SfYKQen+Wf4Sdhoueh46hiZGhiY6dhouaiIeZh4ahjJemkZ2e h46UfYSLcnOQd3iVe3+Zf4ObhoabhoaeiI2eiI2dhpCag42XfXiVenWSeXiXfn2ei5GjkJanlJen lJewiYymf4KafX2VeHiRdXqQdHmafX+df4KegIOdf4KbgImjiJGojZSmi5Glh4eniYmnh4OmhoKd gHuafnmVeXSQdG+ZfnqfhICsjo6vkZGsjo6sjo6rjpGni42niYmmiIiokIenjoajgG6ee2mjg4Cr i4isi4yriYusiH2ohHmmgHKhe22jd2+hdG2mfnCogHOohnWohnWlf26he2qddWied2mjd2ileGmr fnKwg3eyhnewhHWqh3eui3qyiH6vhnuwg3evgnWofWmqfmqogHCshHSuhniuhnireGKndF6nenOs f3ivhn2wh360i4KziYC2iXqvg3SoemWqe2eofXCjeGurfWq0hnO0h3+3iYK3i36zh3qvgGmqe2So dV+uemSwg3q7jYS2lJW6l5m/lY67kYuNe3iNe3iNfoCUhIeZiJWdjJmekJufkZ2bjJSXiJCVhouZ iY6ekJ6jlaOjlaOhkqGijJSahIyXgomahIybi5Wbi5WdjpehkpuhlKailaelmaemmqill56hlJqf lKKlmaern6uqnqqmmZqhlJWfkJeejpadkpmelJqdlJudlJuekpubkJmflp6lm6Omnq6mnq6imaam naqvoaqsnqelnZ+fl5qhlJeekZWhkp6ekJufjJCah4uUiJGViZKZlJealZmejIiZh4OXiYeekI2e lZ+hl6KilqKjl6Omnaqmnaqhl5+Ui5KUhIeUhIeShJCUhpGai52ikqWnkp6ijZmfiZGfiZGUh4uW iY2ZiZujlKanl5+hkZmeiYyahoiai5KdjZWhkJ+ikaGqlp2ijpWXhIuZhoyZg4ubho2ahIyZg4uX jJeXjJeXiZWdjpqjkqKikaGlmquhlqelkZeZhoyVfoOVfoOVgouah5Cdh4mahIeRfX2Qe3uZf4Of homhho6ih5Chh4uehIiahISfiYmhi42ijI6ahn6NeXKJcG+NdHOVeXudgIOfhoehh4ifjJKhjZSh kJqbi5WZg4CSfXqJdW6Qe3SRhIiWiY2ijJSmkJeskIyliISehoCZgHuVd22OcGeWdXeffn+fg4ad gIOeg4mhhoyljIeji4alhIKnh4SuiIOsh4Klg3iigHWaf3SXfXKWenedgH2ni5CukZavkJevkJeu kJqylJ6vlZmyl5u0mpuvlZasi36de2+deHOjfnmmhoOoiIauiIOrhoCnhHSffW2ac2WWb2KZd2ei f2+og2+og2+qfm2jeGeeb2KfcGOZc2ibdWqfd22qgHeshHSqgnKogHOrg3Wof3Wrgni0hHSwgHCu emeqd2Omem6ugnWuh3uuh3uqeFuhb1Ohc2GneWesf3qyhH+yh4S0iYe3iX2yhHiweWSrdF+hdWSf dGOneGiygnKzg3i3h3u2h3Kyg26wfmGsel2sc1+udGGvgn24i4a3jZG/lZnFlZTBkZCQhIONgoCM gH+RhoSXiZKbjZaekqGbkJ6hjZadiZKWiJGXiZKZjZahlZ6fkqWbjqGdiZCZhoyWgIuZg42Zhomb iIyZiZGbjJSfi5aolJ+lmaWonaijmZ+elJqflKKlmaemmqimmqiml6GjlZ6flZuflZudlp2blZue kqGekqGdlqKfmaWhmaimnq6on6yqoa6onq+sorOvo6+soayooqilnqWhl5+elZ2hmZ6hmZ6llZqe jpSUjI6RiYyWjpSakpeekJCZi4uXiYmbjY2akZ6elaKflKKlmaenna6jmaqfmZ+UjZSQhIOOg4KN foOSg4iWhpChkJqmlp6ejpafjJKfjJKWiY2ViIyZi5SfkZqjlJafkJKfi4uZhISZhombiIyeiZWl kJuhlJeekZWZg4CXgn+Vf4SXgoeXgoyWgIuShoyShoyah5Cei5SdjJafjpmjmqWimaOilJ2ekJmX hoeSgIKWhIabiYudh4SahIKQeniQeniZfYKihouhi42dh4mag4iZgoeahISeiIijiYiiiIeWfnWJ cmmIb3COdXeafYelh5Glh46ihIyei46jkJSikpehkZaiiImZf4CLd2+MeHCJen2QgIObhpCjjZem iYyliIuiiIyZf4OReG2Mc2iVdXKaenebh4CeiYOdh4meiIuhh4ifhoelgoCnhIOwi4iuiIanhn2j gnmefXCbem6WdHKdeninjJKwlZuzlZ2wkpqzlZ20lp6wlZGylpK3nZuwlpWwi4amgHufemuhe22h fnmjgHumg4Kqh4anhHSffW2bd2ORbVqSbl2adWSifnOlgHWqfXCmeW2fdGOZbl2Sb2OWc2edcmWh dWmne22rf3CofWerf2mqfmqugm60hHevf3KueF6lb1afc2Soe22qgnSshHeoemOhc1yeaVOmcFqn eGiwgHCwh36yiH+0iXWyh3OzgmmqeWGmdV2iclqhcmSqem2yhH+2iIOzh3Czh3Cwf2evfmWvemuq dWevgn24i4a0jIu6kZC+jo27jIuRhIuNgIeQfYOZhoyXjJWdkZqbkaKZjp+fjpuaiZaRhIuViI6e jZehkJqbjZmWiJSUh4uRhIiVgoaVgoaXhoeUgoOWf4eeh46ejpajlJunmaernauhl6KdlJ6flKKj l6almaemmqinmqymmaummqinm6qinaGfmp6hlaGflJ+bmaeem6qimaannquqoa6qoa6qo6+qo6+y pauzpqyrpa6noaqfm6eemqahmqael6OjlZ6ekJmdjZCbjI6bjpWhlJqelZ2WjZWZiY6XiI2XiZWb jZmfjpullKGqmqyomaublaGVjpqSh4OQhICQe3mUf32XhIujkJafmp6ZlJehlJqhlJqUi4mOhoSb iIyijpKikpeejpSfi4udiIiahIeZg4abhpCljpmhkZSdjZCagH+Xfn2WgH6Vf32Uf3+Sfn6Ren+R en+Vf4SbhoubiJGhjZajlaGomqail56akJaWfX6WfX6Vg3+aiISeiIudh4mWgnuSfniWf4ShiY6h jI6ahoiagISXfoKahIShi4uojpKli46WgnuOenSLcG2QdXKbfoioi5Whho6fhI2fi4umkZGlkZWm kpamkZSbh4mVenWNc26JdHKOeXeXfYOih42hi4uijIyijIyZg4OVeneNc2+Sd2+ZfXWfh4KljIei iImjiYufhoedg4SdgH2hhICsi46zkZWojIeliIOognieeG6ScG+UcnClhpCsjZeukZGukZGylZq0 l52zlZWzlZWwmpqwmpq0joyuiIaie2ebdWGZeG2ffnOjfnuqhIKnfnSmfXOfe2WSb1qRaFOUalWZ cGiedW2leGuleGufdGGab1yWa1aUaVSUaVSXbVeidF+oemWoeF2oeF2reWiwfm2zg3Wvf3Kre1qe b06hc1yqe2Sqfm2ugnCue2ilc1+jblWibVSjc1uqeWG0h36yhHuwhG60iHKwgmquf2iseWWreGSo d2WndWSvgnq3iYK0jHmvh3SwgHCufm6sf26rfm2wg366jIe2kIu0jomziYCziYCRg4yShI2WgIiZ g4ubi5edjJmajaGajaGhjJeahpGSgoyVhI6Zi5SajJWdh5GahI6XhIiSf4OLf36Lf36XgIaZgoed goujiJGfkZqhkpujlaOjlaOflJ+ekp6fkZqhkpulmaemmqimnaqroq+rn66onaumm6Khlp2ekJme kJmfl6ehmaimmaunmqyqoa6qoa6upbKqoa6vpq6vpq6rpbCooq6ml6ajlaOim6Wim6WnmqGll56h lJqdkJaekJmhkpufmaWZkp6djZWXiJCbiI6fjJKdjZWhkZmol6esm6uml6afkZ+ZjI2WiYuZhH6Q e3WShISbjY2blZuhmqGnlqGnlqGZjpCQhoeZiZGdjZWekZedkJaeiI2dh4ybho2Zg4uih5CnjJWo kZmnkJedh4ydh4yXgoeWgIaafoCUeHqVfXiUe3eSfYKVf4SXgoedh4yii5emjpull56bjpWXfoKV e3+Qgn2UhoChi5WeiJKei46ZhomXgIaii5CfiZGeiJCjhoidf4Kfg4amiYyrkJmnjJWahIyQeoKV eXmWenqbgIehhoyii5CfiI2biYiikI6lkI2ijYuhjpCbiYubf3+UeHiRdHeOcnSQdHebf4Kli4yo jpCmjI2fhoeWen2RdXiQdHCXe3ijiYurkZKojo2mjIumiIuihIejh4Kfg36ihoaukZG0lJGvjoys h4KifXiWdW2ObmWfe36rh4mrkZWulJewlJmwlJmzkZWzkZWvkpe2mZ64l5WsjImng3eeem6VdGiX d2qfe3Ojf3emfnCiem2heWeUbVuSaFCRZ0+WaVedb12idGKhc2GmeGOhc16Xa06UaEqXZ0+ebVWh c1ymeGGnd1yoeF2meGOoemWvg3SsgHKhe1ubd1aec2KmemmsgHKsgHKugG+oe2qmdF6hb1qmdF6u e2WyhnSzh3Wvg2+whHCugnCsgG+re3Cre3CreGureGune2qvg3K0iHmyhnezh3qvg3esg3mrgniy iIK4joi3jou0jIiygHmwf3iShomUh4uXgoyXgoydh46eiJCekqGajp2bjZmajJeVh5CVh5CWi5SV iZKZhJCVgIyRfoKQfYCOf4KSg4aZg4udh46biJGdiZKZi5SekJmfkZ+ekJ6ejZefjpmjkJmijpej laGrnaiqoa6on6ynna6mm6yml6GfkZqdjZWdjZWhkaOmlqillaimlqqmmaunmqyvn7Kvn7KopbCq prKupbKnnquolZ6lkZqjl6Gjl6Gilp+flJ2llZ2jlJujlZ6omqOlm6ielaKhkpuajJWZi5SajJWZ jZabkJmjl6ammqionaummqiel56VjpWXfoKVe3+Vg4SbiYuflJ2mmqOrmqWmlZ+akJSRh4uXi5GX i5Gdl5mdl5mbjJGai5CUh42ViI6ahI6jjZeikpqjlJuhkJ2fjpuhiZuii52XgoeRe4CXg4OXg4OU f4KXg4aUgoCVg4KXiJCfkJehkZmejpabhoiWgIOUfoObhoudjJmjkp+mjpaii5KfiI2ii5CdiZCd iZCjh4yfg4iahIyhi5KnkZuijJaZgI2Ue4idf4eegIidh4mhi42hiY6fiI2ijIyjjY2mjIunjYyh jIyijY2dgIaXe4CUd3mUd3mNc2+SeHSni4erjouoi42jhoiaeHWXdXOVeXmZfX2mjJCwlpqvlZas kpSriYumhIalh4eegICliIiskJCylZWukZGvi3+qhnqffW2WdGSfgHeniH6ukZSukZSylJawkpWy jZK0kJWzkZe2lJqzlpuvkpezjYiog36heWubdGeadGmdd2ulemefdWKhdV2ab1eaaFOWZE+UY0ya aVGhb1eicFimcFqoc1yicFabalCZaU+eblSmdGOreWild2KmeGOld1+qe2Swg3eugHSofmOlel+d dGqheG6sgn+rgH6wgHWufnOqe2moemiqemqwgHCyhne2iXq3iXqzhneugnOrf3Cqfm+rf3Crfm2q fWuoeWuygnSyiH+0i4K2iIO3iYS0h36wg3qyg3q8jYS4i4O6jIS0hHSsfW2Vho2ZiZGah5CXhI2a h5Cah5CeiZWeiZWejZ2ZiJeZi5SXiZKZhJCXg46VgouVgouRfoKRfoKXgoydh5GdiJSfi5aZi5SX iZKdhpKfiJWii5qii5qhjJeijZmmjZqmjZqllaerm66snqqqm6ennqiimaOjlZ6fkZqhjZahjZah kpuilJ2ikqWikqWmlKenlaiumaqwm6yupa+so66rn6uqnqqnl52ikpeilKKllqWelZ+hl6Kol6Kj kp2jlaOrnaurnrCmmaullKOfjp6hkJ2hkJ2ekJuhkp6lmaWmmqanmq6om6+qoauhl6KVhI6RgIua hIyhi5KelZ2lm6OqnqehlZ6fkJKWh4mbjI6ejpGnmqGll56ejpSai5CXhIuZhoydiZKfjJWhkJ2i kZ6nl6qmlqinlaijkaWbiIyWg4eXhIibiIybiYuaiImXgoKZg4OXiIudjZChjZGfjJChhoyhhoya g4ihiY6fjpumlaKrlJ6nkJqliZCjiI6ah4uZhomei46ah4uahIyfiZGnkZmhi5KZgomWf4eag4id houdiIieiYmeiI2hi5Cmi5GojZSnjJKmi5GijIyjjY2hhImegoeZg4ORe3uReXONdW+if36riIeq iIylg4ebenKXd26bf3qihoCoi5KwkpqvlZmwlpqsi4yqiImliYabgH2hh4amjIuwlJawlJa0kY6s iYelfW+bdGeZeXWff3uojIyskJCylJSylJSzkI6zkI6ykJa2lJq3mZ6ylJmujI2riYusf3OleGuf dGGhdWKmemSjeGKecliablWeaFSXYk6UZE2XaFCeclihdFuodFWseFindVufblSbalOhb1ereGuv e2+qem2neGqleGmnemuvgnWyhHirfWWrfWWieW+le3Kug4Csgn+yfm2uemmoemWneWSrfnKyhHi0 jYa4kYm6kIe0i4K2iXqugnOsgGisgGiqf2uqf2uqe3Owgnm2iIO7jYi3jo24kI64i4Kzhn2ugnC4 jHq3jYO3jYO2iHevgnCbh5Kbh5KZiJWWhpKVhI6VhI6WgJWdh5uXiJqVhpeXh5SWhpKbg5mdhJqa hI6Zg42XhIuWg4meiZefi5mdjpqekJuajZSXi5GXhIuah42hjZadiZKhkJ+hkJ+hkJ+ikaGmlaKo l6Wml6allqWml6ajlaOilqWdkZ+fkpmdkJaekZWekZWfkJehkZmjkp+llKGmlaKsm6isoa+vo7Ks nqeqm6WikpWbjI6jjpqlkJubkJuekp6nkp6nkp6hlKaom66om7KqnbOqmqyllaejl6ajl6aekp6e kp6fmaWlnqqqmqyomauuoaeom6KhkpuZi5SbjJSdjZWekp6lmaWsm6anlqGjjo6diIiaiImjkZKq maOqmaOmkZGbh4edhouag4ibjJSejpalkJurlqKqnbCrnrKqm6qilKKfiZShi5WhjZaijpefjJWX hI2SfXqUfnubh4efi4uhjZafjJWfiJefiJeeg4mdgoiei5GlkZeqkZ6okJ2ijI6dh4mXg4aahoia h4uZhombh4mfi42jjpGfi42ahIeXgoShi5WnkZujkJSfjJChi5CljpSmjpmqkp2rkJaih42jiI6j iI6liZKih5CeiI2Vf4SVgnuVgnuhg4irjZKrjo6hhISef3ObfXCihoani4ujjZKokpe0lp60lp6r jZKoi5CjiIShhoKih4OnjIiukpu0maKzlJuvkJerg4KddXSWdXeaeXqfgoSqjI6vkZasjpSyjZCz jpGvjY6ykJGyjZKzjpSyjImwi4isg3mmfXOieF+hd16leFyhdFibb1Sbb1SdbVCZaU2XblGZb1Of c2Kjd2Wnel6qfWGod1ymdFqmb1umb1undGWyfm+qem2oeWumem6qfnKsg3mrgnirf2usgG2rfm+u gHKrgnmrgnmyg3CsfmuoeF+oeF+qfm+whHWykY62lZK8lpG3kYy0i4Kwh36uf2qsfmmofWmofWmn fWmug2+yhH24i4O3jYe4joi0h3WugG+sfW2zg3OyhH24i4O6jHq3iXiZi5aajJeXjJqSh5WSgo6U g5CVhJSWhpWUhpGUhpGUhIyUhIyahpGeiZWZiJWWhpKZho6diZKfkZ2fkZ2bkJubkJuZiY6VhouU hImWh4ybi5WejZehlaOflKKhkp6ekJuljpmqlJ6llJ6llJ6imaGimaGilqWekqGakZmZkJefkJWe jpSejZehkJqjkp+jkp+nlqOrmqeqna+uobOwoquqm6WjlJabjI6hi5KjjZWbjpWekZemkJemkJeh kqGllqWnna+nna+qm6Wml6Glm6ijmqeimaahl6Welqafl6ejlqqqnbCwoq6un6unmaejlaOekJ6e kJ6jlaGomqawm6qrlqWnkJeii5KeiJCmkJermqqqmainlZadi4ybh4mfi42ijpWei5GjkJmsmaKr nrCsn7KrlqKnkp6liZKmi5Sqkp+qkp+jjpqahpGWf4SUfYKeiIihi4uljZeii5Wfi5afi5ahh4uZ f4Obh4mjjpGnkJeokZmijIyfiYmbf3+egoKehIObgoCeiI2dh4yfiYmijIyahISXgoKeh46slZ2r lZ2jjZWmkJKokpWnkJeulp6vkZmjho2ihImihImih5Cih5CeiJKbhpCeiIuXgoShhImrjpSskpSl i4yhhH2fg3uni5CskJWqjpWskZezlZ22l5+qjpWnjJKmiI2lh4ymiYmni4uukJezlZ2wlJmylZqs iYSffXiad26beG+igIKujI2zlJ6ykp2wjpCujI2ujYuvjoyyjIu0jo2zjI6viIuvh4Oqgn6lf3Cj fm+ieGKhd2Gicleicleiclqfb1efdF6jeGKnenKqfXSrfWirfWiufVuoeFamdV2ldFyjdWGqe2er fWqsfmuoeWuqem2ofXCqfnKrfnKugHSqe2eqe2eugHKzhnewg3Ksf26od1qlc1aleGurfnKyjIe2 kIu7ko66kY23hn+2hH62g3KvfWureWisemmnfWSqf2euhH62jIa7jYS4i4K3g3KseWimd2eoeWmq e3OzhHu3h3e3h3eVh5KZi5aWiZ2ViJuWhpWWhpWUhpGShJCSgoyUg42UhImWh4ybi5qdjJuZiZua i52ejJ+ejJ+bkJudkZ2dkpaZjpKWh4ySg4iVf4ebho2fjJWjkJmimqqfl6efkpmekZeejpSllZqo lKKrlqWmmqajl6Ojl6OhlaGhl6KelZ+ekp6ekp6fkZ+hkqGjkp+llKGlm6ijmqemlqirm66snqyo mqihkZaejpSljpamkJefkJKhkZSokZaljZKijZmlkJunnqumnaqlm6ilm6immaummauim6ehmqaf lKKhlaOnlqaunayunrKsnbCnlainlaillqKilJ+qkp+wmaarmqenlqOljpafiZGfiZSnkZuolqqr mayilp+ajpeejpaikpqnkZuhi5WhkJqrmqWqnqqonairlZ+nkZuljJmnjpuqlJ6okp2ikJGdi4yX fn2Xfn2ihoajh4eljZWnkJeijZmhjJeli4mbgoCdg4enjZGmjpaokZmljpGijI6jgoOjgoOlg4Sn hoehjIydiIiehIahh4ibg36bg36eh46rlJurl56nlJqmjpSnkJWojZaylp+wkpqoi5KihoadgICe g4mfhIubhJGeh5Shho6eg4yhg4uniZGskJKni42ih4Kih4KrjZeukJqylJuylJu0maKzl6GskpSs kpSniYylh4mlh4mmiIusjZevkJqzlZq0lpuvjoijg32heG6ddGqbfoCniYywkZu0lZ+wkI2ri4ir iIaqh4Srh4mqhoiuiIerhoSuhoKrg3+ogHCnf2+oemOneWKmdFqmdFqndGGreGSqemqufm6ugHiw g3qzhnevgnOwf2eremKrelqldFSleFyoe1+sfmmvgGuzenKvd26odWeodWeoeWmre2une2ine2io fXCugnWvg3KsgG+ye1+mcFWhc1yjdV6wg3u6jIS+kpK+kpK+iX23g3e6hHi3gnWyemiveGWoemWo emWuf3e3iH+4jHq0iHezf2muemSmdF6mdF6meGWoemiyg263iHOWgo2diJSZjJ+ajaGaiZaVhJGR g4yMfoeRfYiUf4uXh5GejZefkKKejqGdkKKdkKKmkaKijZ6hiZmii5qbjpWXi5GWh4yVhouXgoeZ g4ieiJKjjZeflKKflKKfkpadkJShjZajkJmolKKqlaOilqWlmaeimaahl6Wmlqinl6qnm6qjl6af maWblaGllqWllqWll6qll6qnl66oma+umaWqlaGfkJeikpqnkZumkJqdkpSdkpSjkJahjZShjZGi jpKilZmmmZ2mmqajl6Oomqiqm6qmnaqjmqehlaOekqGnlqGsm6ammayll6umlqqqmq6nl6qnl6qq m6eun6ummqahlaGolZ6jkJmhkpunmaKmlaWol6eilJ+hkp6llqKnmaWsl6OjjpqikZ6unaqvnqiq maOmkJeljpajkJmlkZqrlJurlJuijZCeiYyag4iag4ihiY6jjJGnlJ2mkpuikpqhkZmijImdh4Sd iIunkpWolJ+mkZ2okZunkJqjhouihImfiI2ii5CmkJeijJSfiYyfiYybhoieiIumiJWylKGwmaGu lp6qkJSojpKmjparlJuwlZunjJKdgHuZfXiSfX2WgICVgIOZhIebhImdhoufhIumi5GojpKli46j jJGjjJGojJ2wlKWzlqWzlqW3m6e2mqawm56rlpmoi42lh4mohoSmg4KmjJCojpKzkJm3lJ23kpWu iYyognqie3SfgoSlh4msjpSvkZasjImqiYeviICqg3umfn2je3qog4Cngn+rhH2uh3+whHiugnWv fWSsemKod1yndVuleWWqfmqsgHKvg3SyiH+ziYC6jH+2iHuyg26uf2qwe1urd1asel2zgGOvgnCy hHO0gHKreGmjc1iiclemdFyod16mc12reGKsemewfmqyf26yf26wfmindV+jdFOfcE+oenK3iH+/ jo3FlJLBjIK7h323g3Szf3C6g2uveWKoeF+remKvgG60hnO0jHm0jHm0hnCrfWiremKnd16wfmqz gG22hnW2hnWVgIydiJSbjJ6bjJ6bh5KXg46SfoyRfYuQf4mRgIuZi5aekJujlaOjlaOflJ+flJ+h lZ6dkZqfjJWfjJWekZeZjJKXhIuVgoiWhIaXhoeXiJCdjZWhkJqhkJqhjZabiJGhi5WljpmilJ2j lZ6jl6Gilp+ilqWlmaeil6qjmauooq6lnqqjl6aflKKmlKehjqKikaGllKOjlKaikqWqlp+nlJ2d kpmhlp2llqKllqKimaGimaGmlZ+hkJqdkJSdkJSfkJWllZqflJ2hlZ6hlaGilqKmmauom66lm6ie laKilqKlmaWrmqqrmqqvma6vma6omayrm6+un66voa+mmZ+jlp2mlZ+llJ6hlaOmmqiol6Wjkp+l kJ6mkZ+qlaaumaqwm6emkZ2ikZ6unaqznqyumaeskZqojZaljpaljpaqlJmnkZaeiI2bhouZg42a hI6hiZamjpumlZ+llJ6mkpulkZqii5Cdhouii5KokZmsl6aumaerlKGqkp+sjpalh46hi5Kljpar lZ+qlJ6mkJWijJGfiYyfiYyliZWrkJuzlZ+0lqGskpanjZGnkJWslZqslpumkJWmg4KffXuUeXSQ dXCWe3idgn6Zg4Cdh4SiiIyli46ijJGhi5CljZWljZWojJqukZ+zlqW0l6aymaawl6Wvl52ulpus jo6jhoafg4OZfX2fg4aliIuojJGwlJmwlJarjpGqg4ajfX+bfn6lh4eujI2vjY6sjIioiISnhn2j gnmie3KhenClf3qjfnmnfnSqgHergnirgnivfmOse2Goe2KnemGsfW2vf2+vf3K0hHe2jIO3jYS4 jH+2iX22h3Kuf2qzfWGye1+wfmO0gme2gni7h324iHiufm6jdFOZakmda1Ghb1WqclqyeWGvfmWz gmmwhG6yhm+zgG2vfWmveV+oc1qseW+4hHq8jYbDlIy+iX+6hnu2hnW0hHS3hG6yf2mzel+udVus f3C2iHm0jH6yiXuwgm+uf22vfWereWOwgnmwgnm3jIm8kY6Wgo2diJSbh5WahpSXg46Wgo2Rg5GR g5GUhpGZi5abjJ6ejqGhkqGjlaOllJ6mlZ+hlZ6ekpuajZSZjJKei5Sei5SXhIuVgoiWgIaZg4ia h5CfjJWbjpKZjJCbiI6Zhoyah42hjZSekZehlJqjlZ6ilJ2hlZ6ilp+llaerm66qnqylmaeikqWi kqWjlaGekJuekZefkpmfkJefkJeikpqjlJumlp6llZ2ilJ+fkZ2jlaOnmaeml6OilJ+bkZeakJae jpafkJedjpedjpedjpejlZ6jlKaqmqylmquil6iol6eol6eml6OnmaWsm6urmqqunrKvn7OuobOu obOnmqGilZullaemlqill6ummayllqWfkZ+hkJqhkJqulqiwmauymqqmjp6mlaKqmaaun66omqio kZuljZelkZemkpmmlJWhjpCjiI6fhIuXhI2diZKeiZWhjJellqKnmaWvl6Kqkp2ljZeii5Wljpmo kp2wm6eynaiym6aym6asjpmniZSikZuol6KwmaiwmaivmZ6nkZahiZGfiJCliZKrkJmylqKzl6Or kJmmi5SqjJSsjpaolZ6lkZqniYmihISXe3iUeHSVdXKaeneZfnOfhHmliIimiYmniZGmiJCmi5Sm i5SliZCnjJKskZqwlZ6ylqKvlJ+wlpewlpeuko2liYSfhICWe3ideniif32jh4yvkpewkpqsjpas hIOnf36hgH6ign+nh4SujYuujI2riYuriIajgH6mfnCed2mheWuje26mem6sgHSshHeuhnirfWin eWSreWireWiqem2ufnCygnSygnSwiHi3jn64kH+3jn67h3iyfm+zgGizgGivfWu4hnS2iIC8joe/ kH24iXesel2fblGaaFWea1imdF6wfmisfmmsfmmsfmmvgGuvgG6wgm+vf3KoeWuvfne2hH28jYy/ kI67jX62iHm0h3q2iHu2h3S0hnOygGiufWSyg3u4iYKziX+yiH6vhHCqf2uufm6zg3O2h4m6i428 kpnBlp2Wh4yVhouUgImSf4iRfoSSf4aVhJSZiJeZh5qbiZ2dkZ2dkZ2bjZullqWml6OjlaGhkp6e kJuajJWWiJGfi5meiZeah5CZho6ah4ufjJCfjY6ejI2UiY2SiIyWhIOWhIOUgoObiYubjpWekZef kZ2ilJ+ilJ+hkp6llqKnmaWnl6qfkKKdkKKekaOekJuXiZWbi5WejZefkJWhkZankp6qlaGnl5+l lZ2hlJqfkpmllJ6nlqGnlqOmlaKflZudkpmejpafkJedjpeekJmhkp6jlaGnl6urm6+mmauom66y na6vmqujlaGjlaGol6eunayunay0o7OwpbCvo6+sl6afi5mjlKarm66smq6rmaynlqOikZ6ikpqh kZmolKWvmquwmauqkqWnkp6qlaGnmaellqWolKKlkJ6nkZmokpqolZmei46fhomfhomag4udho2b iI6ei5GhkJqol6Kvl6KslZ+njY6jiYuejI2jkZKsl6iznq+0n6u0n6uvl5+qkpqllqKqm6e0n7C2 obK0naeokZujjJafiJKliZKrkJmulqGwmaOrkZWli46njY6njY6ijpWjkJaljpGdh4mXe3eWenWX c26Xc26ZeXWlhICliIini4uui5SriJGmiYymiYyfhoSbgoCjhIylho2ni5CrjpSojpCskpSqlY6l kImlg3qffnWbenKefXSif4ivjJWqjZKskJWriIOhfnmaeHWffXqmgH+rhoSmiI2oi5Crh4mmgoSi f32ee3mZdWmWc2efeGinf2+qhHWrhnesgmuofmiqem2re26sfW2vf2+vf3KsfW+ogHOyiXu6jIS/ kYm7iYK6iIC2g2+yf2uufnO2hnq2jYm6kY27jnq2iXWyf2SjcleXalafcl2ofWmugm6yg3Cwgm+u fm6ufm6ugnOwhHWygnewgHWyhHu3iYC3koa6lYi+kX23i3e3h3m0hHe2h3S3iHW0iHSzh3OzhoC3 iYSviH2viH2whG6ugmuvgnW2iHu4iYy8jZC+lJ3Cl6GZiJKXh5GZg4uVf4eSfX+UfoCZg42eiJKX i5GXi5GbjZadjpedjp2ml6ailqKilqKekpuajpebjZaXiZKaiZSZiJKeiZWfi5afkZqhkpuhi5Kd h46ZiJKXh5GXg4aVgIOUf4KXg4aah4uei46ejpafkJeikpqhkZmjlKaomauolq6ejKObi5qdjJui jZmdiJSei5Sei5ShjZSlkZeolZ6olZ6mlp6ikpqbkZWelJellKGnlqOol6eol6enm6WlmaKolKKn kqGfkZqhkpuikpejlJmhlaOonaunm6esoayunayqmaillKGjkp+llKOqmairnauzpbO3prayobCr lKGii5elkqaql6usm6iqmaaomZ6hkZaikpqejpalkJ6qlaOumaenkqGmkJWnkZajkp+mlaKrmqen lqOmkpunlJ2nlJqfjJKdh4ybhouZg4iZg4idiIuhjI6ijpWnlJqqlJmokpemjI2hh4idiIiijY2o kZ6vl6Wwn6yyoa6znaWrlZ2ll56mmZ+wn6yzoq+3n6qslZ+miJCdf4edgoinjJKnjJWwlZ6qkJGj iYuhhoCfhH+fhIuliZCijIyfiYmegn6afnqaeneZeXWZeXelhIKljZKnkJWukJKqjI6miYyni42j h4Kbf3qffXiffXihgH6jg4CfgoKniYmqjYmmiYalgoCif36dfnSbfXOffoKoh4uskJWskJWrjH+m h3qdeW2deW2ifXqmgH6jgoOmhIaqhIOmgH+hfn2ffXuefXSZeG+heG6qgHeviYSyjIe0iHmugnOu f22vgG6vgGuuf2qrf2mrf2mqeXKwf3izh3q8kIO7jIS8jYa6i3Wuf2que2qzgG+0i4S7kYu4kIKz i32whminfV+ablWdcFeoenWzhH+zhnewg3Svf3KufnCqfnKwhHi2hnq3h3uyhnmzh3qzjIC4kYbB kIS8jIC6h3OzgG2wgHC3h3e3h3e3h3ewh362jIOuhHquhHque2WqeGKsfmuzhHK4iInBkJG/lJXB lZaaiZSaiZSfg4ibf4SWe4Kaf4ahjJeijZmZjJKWiZCah42fjJKikaGqmaimlqijlKabkpqZkJeV i5GVi5GWjI2bkZKhkJ+nlqaom66jlqifiZGahIyShI2Uho6ahoaZhISXg4OZhISfi42hjI6fjJKh jZShlJqhlJqlkqamlKemkZ+hjJqfiZGfiZGijJahi5WejpabjJSjkJmolZ6ml6GjlZ6omZ6ikpej lZ6llp+ol6KmlZ+qmqyrm66onaurn66umaqqlaanlqallKOolZuolZuXjpahl5+nm6qqnqysm6ur mqqllKGikZ6ikKOjkaWqmqywobO0pberm66ilJ+ajJehjqKjkaWumaWumaWqmp+ikpehlJefkpaj kJmnlJ2rlqKnkp6mkJWljpSfjpmjkp2olKWvmqunkJ+okaGfkZGekJCdjoyWiIaWi4mViYifiYem kI2nkJerlJurlZqokpejjY2eiIifiYyijI6mjpmrlJ6vmqa0n6uym6aslqGolZumkpmsm6ayoau6 oa6slKGsi4ybenuWfYCehIiihIysjpamjIuiiIejgnmjgnmjg4ClhIKlh4mlh4mihoafg4Oeg3+d gn6df4Klh4mvlJ2wlZ6ukpmrkJaniZGoi5KsiH+lgHifeW6feW6ZdWqbeG2ie3SrhH2uiIOog36l goChfn2efXCaeW2XdXOhfnuriYuujI2ujYemhn+ie3Cdd2uXdXCZd3KfeW+mf3Wrfneoe3Soenum eHmmeXKhdG2hdG+ugHu0jIy4kJC2joeviIC0gG+zf26uf2iuf2irfWiuf2qofmqme2iqgnSyiXuz joa3kom/joO2hnqwfWmwfWm2iIO8jom+loy4kYe3jHiug2+meGOjdWGnfniwh4C4hHWyfm+uemes eWWufnOygnewgnm2h360iHmyhne0h3q/kYTFkIbBjIK4hnS0gnC2gni4hHq6hnu7h322iXW4jHi2 i3evhHCrel+nd1yreGSzf2u2hIDDkY2+lI28koybjZmXiZWahI6Zg42dhpCdhpCdjJuejZ2Uh42O goiRhIiXi46ikKOmlKenkaqjjaabkZeZjpWXi5GajZSVkJGblpeilq+mmrOrmqemlaKfhIufhIub iJGbiJGbhouahImXiYmWiIiei5GjkJajkJahjZSbkJudkZ2lkKGlkKGjjJmdhpKZiY6ejpSnkp6o lJ+ikZ6hkJ2ml6Oomqammqinm6qunrCunrComqiml6aimaOhl6KomauunrCsnbCrm6+lmaWjl6Os nqqrnaillJ6hkJqah5CfjJWilKKqm6qrnairnainlqGfjpmejZqfjpuikKOvnbCwoq6snqqmkpue i5Sii5KmjpajlJunl5+jlJmhkZafkJWikpejkJamkpmnlJ2olZ6njJKliZCeiZWijZmnkJ2vl6Wo maGjlJunkpWmkZShjZGbiIydjZKai5Cfi4ulkJCnkJWqkpeqkperlJmii5ChiY6ehIifhomhhoyl iZCulaK2naqwl6WulaKqjZKrjpSokpq0nqa2n6qul6Kni4OZfXWUeXKVenOdf3+miIiihoaihoaf hH+dgn2jgoOlg4SmhIamhIaeiIieiIihh4ahh4ang4uuiZGzl6Ozl6O0lqGwkp2qi5Kmh46rhoOn gn+hf3SaeW6acmiddGqbeG2hfXKrhoCrhoCqgoCje3qdenmbeXiXd26aeXCnf36vh4aoiIKignuh enCZc2mUcGWWc2iac2OddWWjeGmmemuleHOjd3KmdW+jc22bc22ieXOsh4S2kI26kIm0i4SshHeu hnivg3Svg3SogG6ogG6ugnCne2qoe2+vgnWwi4a3kYy8jom6jIe2hnWwgHCzhoC7jYi/lY64joi6 jH23iXqugnOrf3Cwgnm3iH+2h2+yg2uue2WvfWevgG6wgm+vf3Kzg3WuhnWshHSwg3u8joe/kYS8 joK6iXu4iHq2hnqwgHW2gnW6hnmyg260hnC3i3KwhGuvfWKsel+reGSwfWmzg3jCkYbBkYm+joed jJmaiZaXh5aWhpWXg46bh5KZi5aXiZWVgouQfYaVf4edh46ijZ6nkqOllqKjlaGbkZWZjpKai5KX iJCbkJmjl6Gnn7Onn7OrmqehkJ2ahImbhouWjJKbkZejjZehi5WhiZGhiZGdjJadjJadjpqajJee kJ6ekJ6fkZ+fkZ+iiZafh5SZiJWikZ6ol6enlqallaeikqWjl6anm6qunrCsna+zobawnrOmmaui laejl6Ommqaqm6qsnqyvoa+rnauqnaOsn6awn6qyoaurlKGeh5SRgoeVhoudiJankqGfmaWhmqau laKiiZahi5KfiZGhjJevmqaznqywm6qljpadh46dh4yijJGhkZmmlp6jlpqekZWijpWmkpmokpen kZaulp6qkpqliZKliZKjh5WmiZeokZ6okZ6rl56olZuqlJuokpqhjZGdiY2bjI6bjI6njJKmi5Gl jZKljZKqlJaslpmrkJajiI6dh4mdh4mXgoSeiIuqjJmzlaKykqKujp6vjoysjImrjZe3maO3n6qw maOwjI6mgoSafneWenOZfnmjiIOlh4emiIiiiIejiYiniYmqjIyni4eliISihoiliIuli4yiiImn iYyoi42zlZ+4mqW6mqWykp2zjJSwiZGniYylh4mhhH2dgHmhenCeeG6Wc2qeenKqgn6qgn6shn6o gnqheXWed3OZdWqWc2ibeXSffXilgHijf3eieXOXb2mXb2WVbWOWa1iZbluecmGhdGOjd2+meXKl d3KbbmmWcGmadG2ogH+3jo22lIuwjoauhHqsg3mzhHu0hn2zhn6zhn6yjH2lf3CmeWqqfW6uh3+y i4O6jIe6jIe6hne0gHK2h3+/kIi8kZG6jo64i362iHuwiHqvh3mvh3mvh3m0iXOziHKzhG+zhG+w hHOyhnSwhG6ugmuofWene2WsfW+zg3W7jIO+joa8jn+7jX62hnW0hHS0gm62g2+0gG27h3O3i3m3 i3myhm2rf2eyhne0iHmyi4O3kIi7jIu3iIediZKfjJWaiZSWhpCUho6XiZKZiZGUhIyReoeQeYaU foOZg4ibjZafkZqjkp+llKGbkZWXjZGai5CXiI2jkp+rmqeqn7Cnna6ol6KdjJaahImdh4yekZei lZuolZ6lkZqjjJSljZWdjZWbjJSbjpWbjpWejJ+hjqKhjqKfjaGfi5mhjJqbkJujl6Ovmquumaqn lqOikZ6nmaernaurnrCsn7Kyo7KworCmmaufkqWolJ+rlqKsl6Ovmqasoaqjl6Gilp+nm6Wsnqys nqyikpqVho2SfX+UfoCdg4eojpKekp6hlaGnkp6eiZWai42Vhoibho2mkJeunrKrm6+nlJqdiZCe iJChi5KhkJqmlZ+ll5ubjpKhkZamlpunlJqqlp2slqGnkZuiiZahiJWliZWliZWmkJqqlJ6vlqOs lKGslaKqkp+nkZGijIydh4yijJGskZqqjpeljpGmkJKulpuulpuukZGliIibhoiXgoSUfYKZgoei hoiukZSvkZmrjZWrjY2miIioi5eylKGzl6OwlaGzkJunhJCUfoCRe36bf3umiYaqjouliYali4yq kJGylZqylZqnjY6li4ymi5GnjJKmjJCjiY2oi5KniZGylJ64mqW0maWwlaGyi42viIumi4ejiISi hn6hhH2je3iheXWbeXeee3mjfnmngn2rg3Org3OhfWebeGKWcmGUb16Wc2iZdWqfeXKeeHCfeGqX cGOdal6ZZ1uSZVSUZ1Wfbl2mdGOldWineGqoeWuhcmSXcGOac2Wleni2i4i+lo63kIiwhHisgHSy hH2zhn6wh364joa6lISviXqqeGSmdGGoenKwgnm4iYS4iYS4iH23h3u0h367jYS+kIu7jYi7h320 gHeyhnmzh3qzh3qzh3q0iHu0iHu7i327i326jH26jH2zhG2vgGmofmWnfWSremKufWS3g3m7h327 jX68jn+4iHivf2+zgGqvfWeve2W4hG64iYC7jIO2iXqzh3i4lIu4lIu2kpG3lJK7kI24jYuXiJCW h46XiI2XiI2XiJCai5KZiZGVho2OeYCNeH+SfX+bhoidjJmdjJmdi56ejJ+ajo2ViYiXhIibiIyl kJ6qlaOlmaWflJ+ljpGeiIuWg4eah4uhkZallZqilZuilZuijJaahI6Wg4mVgoiViI6WiZCbi5Wd jJaeiZqdiJmhiZmljZ2ikZ6ol6Wsl6ilkKGjjZWhi5KqkqWulqiom66uobOwoquml6GikpqfkJel kZeqlp2umqOsmaKrm6OomaGmlaKnlqOonaulmaediY2UgISQeniRe3mZe36ihIeikpqikpqekpuZ jZaUiY2Ng4eWgo2fi5aml6aqm6qll5uajZGdiZChjZSmlZ+ol6KikpeejpSfjJWjkJmqmaOol6Kv lqOokJ2ii5WjjJaijJSijJSmi5SskZqolKWsl6iwl6WvlqOmkZSijZCfiZGljpaukpmukpmqlJal jpGslZ+wmaOukZSliIuehIaehIaagIKZf4CehoCokIuukZSqjZCojIyihoamiJWoi5eskZqskZqu kZaojJGbgIybgIyfg4ani42ujpmvkJqoi5erjZq0maWylqKqjpWukpmrkJmqjpelh4mhg4aliI2l iI2sjJu2laW2mqOylp+vjY6nhoehhISfg4Onhn2lg3qmgHuhe3eff3uign6mgHuog36qhn2ng3qn fWmdc1+WalqQZFSQZVWWa1uZbl2ab16XdF6XdF6abViXalaVaFaWaVebbWKhcmeldWimd2mleWWh dWKab2GdcmOhdWSugnC6koi2joSuhHqqgHeuhHqyiH6rh3qyjYC3kX2zjXmvfmOmdVufdGGmemeu fXW0g3u4iH23h3uzh3q4jH+7jIO6i4K0hHewgHOugmuwhG6yiXmwiHi0h3q6jH++joe/kIi8joa7 jYS3iHWsfmusfmeqe2Sue2WsemSzf3O4hHi2hni6iXu7iHe4hnS2gHKyfW6weWeze2m4h4O7iYa3 jou7ko64lJa4lJa8l5q+mZu/lpK4kIwADQEAAAMAAAABAQAAAAEBAAMAAAABAQAAAAECAAMAAAAD AAMAqgEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAACAAMAsAESAAMAAAABAAEAAAEVAAMA AAABAAMAAAEWAAMAAAABAKoAAAEXAAQAAAACAAMAuAEcAAMAAAABAAEAAAFTAAMAAAADAAMAwIdz AAcAABDoAAMAxgAAAAAACAAIAAgAAAAIAAH+CAAB/gAAAQIAAAEAAQABAAAQ6GFwcGwCAAAAbW50 clJHQiBYWVogB9YABAAeABAAAgAkYWNzcEFQUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPbW AAEAAAAA0y1hcHBs8Km3nXI3UvdB2LuwZ9wBHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAOclhZWgAAASwAAAAUZ1hZWgAAAUAAAAAUYlhZWgAAAVQAAAAUd3RwdAAAAWgAAAAUY2hhZAAA AXwAAAAsclRSQwAAAagAAAAOZ1RSQwAAAbgAAAAOYlRSQwAAAcgAAAAOdmNndAAAAdgAAAYSbmRp bgAAB+wAAAY+ZGVzYwAADiwAAABkZHNjbQAADpAAAAH+bW1vZAAAEJAAAAAoY3BydAAAELgAAAAt WFlaIAAAAAAAAF1MAAA01QAAB9tYWVogAAAAAAAAdAUAALP7AAAiflhZWiAAAAAAAAAlhQAAF0sA AKjMWFlaIAAAAAAAAPNSAAEAAAABFs9zZjMyAAAAAAABDEIAAAXe///zJgAAB5IAAP2R///7ov// /aMAAAPcAADAbGN1cnYAAAAAAAAAAQHNAABjdXJ2AAAAAAAAAAEBzQAAY3VydgAAAAAAAAABAc0A AHZjZ3QAAAAAAAAAAAADAQAAAgAAAgQC9wQFBQUGCgcFCAsJCAoNCwgMCg0QDg0PDxAOERASEBMS FBIVFBYXFxgYFhkaGhsbGhwdHR0eHyAeISIiIiMjJCUlJCYmJycpJyopKyosLC0tLi8vLzAxMjEz NDQ0NTQ2Njc3OTo6Ojs7PD09PT49Pz9BQkJCQ0NEQ0VFRkZISElJSklLTExMTk1PT1BQUVJSU1RU VVVWV1dXWFhaWltbXFxdXl5eYGBhYWJiY2RkZGVlZ2doaGlpamlra2xtbW1vcHBwcXJycnNydHR1 dHZ2eHh5eHp6e3t8fH19fn5/f4CAgYGCg4SDhYSGhoeHiIiJiYqKi4uMjI2Njo6Pj5CQkZGSkpOT lJSVlZaWl5eYmJmZmpqbm5ycnZ2enp+foKChoaKio6OkpKWlpiWmpqenqKipqaqqq6usrK2trq6v r7CwsbGysrOztLS1NLW1tra3t7i4ubm6uru7vLy9vb6+v7/AP8DAwcHCwsPDxMTFxcbGx8fIyMlI ycnKysvLzMzNzc5Nzs7Pz9DQ0dHSUdLS09PU1NXV1lXW1tfX2NjZ2dpZ2trb29zc3d3eXd7e39/g 4OHh4uLjYuPj5OTl5ebm5+foZ+jo6enq6uvr7Ozt7e5t7u7v7/Dw8fHy8vNy8/P09PX19vb39/j4 +fn6efr6+/v8/P39/v7/fv//AAACBAL3A3AEBAUJBgQHCggHCQwKBwsJDA8NDA4ODw0QDxEPEhET ERQTFRYWFxcVGBkZGhoZGxwcHB0eHh0fICAgISEiIyMiJSUmJicmKCcpKCoqKyssLS0tLzAwLzEy MjIzMjQ0NTU3ODg4OTk6Ozs7PDs9PT9AQEBBQUJBQ0NEREVFR0ZIR0lKSkpLSkxMTk5PUFBRUVFS UlRVVVVWVldXWFhZWVpbXFtdXV5eX19gYWFhY2NkZGVlZmZnZmhoaWpqamxtbW1ub29vcG9xcXJx c3N0dHV0dnZ3d3l5enp7e3x8fX1+fn9/gICBgIKCg4OEhIWFhoaHh4iIiYmKiouLjIyNjY6Oj4+Q kJGRkpKTk5SUlZWWlpeXmJiZmZqam5ucnJ2dnp6fn6CgoaGioqOjpKSlpaamp6eoqKmpqqqrKqur rKytra6ur6+wsLGxsrKzs7S0tbW2tre3uDe4uLm5urq7u7y8vb2+vr+/wMDBwcLCw8PExMXFxkXG xsfHyMjJycrKy8vMzM1Mzc3Ozs/P0NDR0dLS01LT09TU1dXW1tfX2NjZ2dpZ2trb29zc3d3e3t/f 4F/g4OHh4uLj4+Tk5eXm5udm5+fo6Onp6urr6+zs7e3u7u9u7+/w8PHx8vLz8/T09fX29vd29/f4 +Pn5+vr7+/z8/f3+/v9+//8AAAGCAmUDQAQcBPEFuwaJB1wIMQkHCdUKoQtyDEUNFA3jDrUPhBBR ER4R7hK5E4cUWBUnFfMWvReHGFEZGhngGqobdRw/HQUdyh6PH1UgHSDjIaoibyMwI/ckuSV6Jjwm /ifDKIQpRSoMKswrjSxNLQgtyS6IL0UwBDDFMYUyQzMVM+00wjWWNmw3QDgROOM5tTqHO1k8Lj0I Pdw+sj+JQF5BMEIAQtJDqER9RUlGHUbvR8RImUlrSjpLEEveTK1Nf05MTxlP6lC4UYNST1MbU+dU sVV5VkZXC1fRWJdZYFoqWvJbtlx4XTxeAl7FX4hgUGERYc5ii2NNZA1k02WoZpZnjWiLaXtqdWts bHBtYG5bb0VwOHEsciJzE3QFdPh153bTd7x4pXmUeoN7b3xOfTd+JH8Mf++A1IG8gp+DfoRohUiG KocPh/KI04m0ipaLeYxcjTuOHY79j9yQvpGhkoeTb5RPlS+WEpb4l96YvpmdmoCbZ5xRnTqeHp8D n/Sg9KH6ovuj9qT0pfCm76foqOCp46rdq9SszK3Err6vuLCzsa+yrLOqtKm1qbart664xrnMutO7 2rznvgG/FcAwwUnCbMOVxMHF78cfyFDJiMrSzCPNds7M0CnRmtMS1JHWJtfD2WjbJ90C3ujg7uMZ 5Wbn1uqP7Y/xDvVt+x7//wAAbmRpbgAAAAAAAAY2AACXOAAAVsIAAFQSAACKMAAAJ6sAABaoAABQ DQAAVDkAAiFHAAIR6wABRR4AAwEAAAIAAAABAAMACwAWACUANwBNAGUAgQCfAMEA5QELATUBYQGQ AcEB9QIrAmQCnwLcAxwDXwOjA+oENAR/BM0FHQVGBXAFxAYbBnQGzwctB4wH7ggfCFIIuAkgCYoJ 9gpkCtULRwuBC7wMMgyrDSYNog4hDmEOoQ8kD6kQLxC4EUMRzxIWEl0S7hOAFBUUqxVDFZAV3RZ5 FxcXthhYGKoY/BmhGkga8RucG/McSRz4HageWx8PH2ofxSB9ITch8iKwIw8jbyQwJPMltyZ+J0Yn qygQKNwpqSp5K0osHCzxLVwtxy6gL3kwVTEzMhIy8zPVNEc0uTWgNoc3cThcOUk6ODsoPBo9Dj4D Pn8++z/0QO5B6kLoQ+hE6UXsRvFH90j/SglLFEwhTTBOQE9SUGZRe1KSU6tUxVXhVv9YHlk/WmFb hVyrXdJe+2AlYVJif2TgZhJnR2h8abRq7WwnbWRuom/hcSJyZXOpdO92NnjJehV7Y3yyfgN/VYCp gf+DVoSvhgmIwoohi4GM445Hj6yREpJ7k+SWvJgrmZubDJx/n2qg4aJao9Wmz6hOqc6rUa5ar+Cx abLytgu3mbkpurq94b93wQ7Cp8RBx3vJGcq6zFvN/9FK0vHUm9ZF1/HZn9z/3rHgZOIZ49DnQej8 6rnsdu427/fxufVC9wj40Pqa/GX//wAAAAEAAwALACUANwBNAGUAgQCfAMEA5QELATUBYQGQAcEB 9QIrAmQCnwLcAxwDXwOjA+oENAR/BM0FHQVwBcQGGwZ0Bs8HLQdcB4wH7ghSCLgJIAmKCfYKZArV Cw4LRwu8DDIMqw0mDaIOIQ5hDqEPJA+pEC8QuBFDEc8SFhJdEu4TgBQVFKsVQxXdFisWeRcXF7YY WBj8GaEZ9BpIGvEbnBxJHPgdUB2oHlsfDx/FIH0hNyHyIlEisCNvJDAk8yW3Jn4m4idGKBAo3Cmp KnkrSiwcLPEtXC3HLqAveTBVMTMyEjLzM9U0uTWgNoc3cTfmOFw5STo4Oyg8Gj0OPgM++z/0QO5B 6kLoQ+hE6UXsRvFH90j/SglLFEwhTTBOQE9SUGZRe1KSU6tUxVXhVv9YHlk/WmFbhVyrXdJe+2Al YVJif2OvZOBmEmdHaHxptGrtbCdtZG/hcSJyZXOpdO92Nnd/eMl6FXtjfLJ+A39Vgf+DVoSvhgmH ZYjCiiGLgYzjjkePrJESknuT5Ja8mCuZm5sMnH+d9J9qolqj1aVRps+oTqnOrNSuWq/gsWmy8rR+ tgu5Kbq6vE294b93wQ7EQcXdx3vJGcq6zFvN/9FK0vHUm9ZF1/HZn9tO3P/gZOIZ49DliOdB6Pzq uex27/fxufN89UL3CPjQ+pr8Zf//AAAAAAAGABIAIwA5AFUAdQCZAMEA7gEgAVQBjgHLAgsCUQKb AucDOQOMA+QEQAShBQYFbwXdBkwGvwc4B7UINgi5CUAJygpdCu4LiAwlDMQNZQ4PDrUPZRAXENMR ixJNEw8T0hSeFVkWDxbPF40YURkaGeUasxuEHFUdIh37HtAfqyCMIXIiVyM5JCwlGCYKJvgn7ijr KeIq6SvpLPMuAC8JMB8xNjJPM2o0kTWyNuE4ETlBOnA7qDzrPi0/bkC7Qf9DUkSzRglHZ0i0SdVK 7Uv6TRxONE9RUGFRilKoU91VBlYxV1tYkVnCWvhcNl15XsNgA2FGYo9j7WU7ZoZn42lEaptr/21r bsdwOHGkcw10gnX4d25443pce959Wn7hgGWB54NmhOSGdYgDiYyLEoynjkCP1JFjku6Uf5Yfl7CZ JJqOm/qdep7xoHOh66NwpP+mdqf+qY6rH6ywrkGv07Fksva0h7YZt6q5O7rLvFu9zL9ZwOjCd8QE xXfG+8hyye/LaszVzj/PqNEP0nbT3NVB1p7X59kv2nbbvN0B3kXfeeCl4c/i+eQc5THmROdX6Gjp cepw62PsU+1A7i3vDO/r8LzxjvJW8xvz2/SV9U719vaf90L32/h0+QX5h/oK+o36+vtl+8/8OvyV /OT9NP2D/dP+I/6J/vT/X//J//8AAGRlc2MAAAAAAAAACkNvbG9yIExDRAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAABtbHVjAAAAAAAAAA8AAAAMaXRJVAAAABQAAADEZnJGUgAAAEIAAADYbmJOTwAAABIA AAEaZXNFUwAAABIAAAEsZmlGSQAAABAAAAE+cHRQVAAAABgAAAFOemhUVwAAAA4AAAFmamFKUAAA AA4AAAF0bmxOTAAAABYAAAGCZGVERQAAABAAAAGYa29LUgAAAAwAAAGoZW5VUwAAABIAAAG0c3ZT RQAAABAAAAHGZGFESwAAABwAAAHWemhDTgAAAAwAAAHyAEwAQwBEACAAYwBvAGwAbwByAGkAyQBj AHIAYQBuACAA4AAgAGMAcgBpAHMAdABhAHUAeAAgAGwAaQBxAHUAaQBkAGUAcwAgAGMAbwB1AGwA ZQB1AHIARgBhAHIAZwBlAC0ATABDAEQATABDAEQAIABjAG8AbABvAHIAVgDkAHIAaQAtAEwAQwBE AEwAQwBEACAAYwBvAGwAbwByAGkAZABvX2mCcm2yZnaYb3k6VmgwqzDpMPwAIABMAEMARABLAGwA ZQB1AHIAZQBuAC0ATABDAEQARgBhAHIAYgAtAEwAQwBEzuy37AAgAEwAQwBEAEMAbwBsAG8AcgAg AEwAQwBEAEYA5AByAGcALQBMAEMARABMAEMARAAtAGYAYQByAHYAZQBzAGsA5gByAG1faYJyACAA TABDAEQAAG1tb2QAAAAAAAAGEAAAnFYAAAAAv/h7gAAAAAAAAAAAAAAAAAAAAAB0ZXh0AAAAAENv cHlyaWdodCBBcHBsZSBDb21wdXRlciwgSW5jLiwgMjAwNQAAAAA= item7.X-ABRELATEDNAMES:assistant item7.X-ABLabel:_$!!$_ item8.X-ABRELATEDNAMES;type=pref:spouse item8.X-ABLabel:_$!!$_ item9.X-ABRELATEDNAMES:father item9.X-ABLabel:_$!!$_ item10.X-ABRELATEDNAMES:Custom relation item10.X-ABLabel:CustomRelLabel X-ABUID:3D833EB4-BFFC-41F0-9193-96695487C45E\:ABPerson END:VCARD tests/auto/versit/qversit/testdata_vcf/AAB4-SingleCompany.vcf000066400000000000000000000033661233466112000245730ustar00rootroot00000000000000BEGIN:VCARD VERSION:3.0 N:;;;; FN:Apple Computer Inc. ORG:Apple Computer Inc.; TEL;type=MAIN;type=pref:1-800-MY-APPLE item1.ADR;type=WORK;type=pref:;;1 Infinite Loop;Cupertino;CA;95014;United States item1.X-ABADR:us item2.URL;type=pref:http\://www.apple.com item2.X-ABLabel:_$!!$_ PHOTO;BASE64: TU0AKgAAAyiAP+BQOCQWDQeEQmFQuGQ2HQ+IRGJROKRWLReMRmNRuOR2PR+QSGRSOPPyTOZ1OxjM 1pLZfsVtuBxySaTWHvF5vVkNBqqlZrlTrFbqqfthvOKbUmlQN4PN6L1jMugrep1NZLtguR0uul12 RvZ8PhhstoVWhWZbr5jsywPivW+PzFxqhZUCz3ehz9st+Z3C/Rp6vd8MFks60Whis1ovd8vm/4+L ul2O5YrpgYehMBkM2mvTIZ+LOJzOm0ZVgM5qtmcvbQa1+696PZ76vXv2Eu14PJmNNsNNtN7JO7a0 yncF2u9423Wx+TPx0ZNlNFqrthslar5iddiLxispmtRsc93Pl9PuB7Xyvt3vJ575vVBlrVesOrsF ZLxgy5i2NoN1xnMxjHOWiLVmWaRrFcW5eroXDMKooS6FyV5cF8zRmmubpwG4cJyJWaT6wjB0HKIX MLHUdx4QGhbcHk7hlREvEYQeW8GQZGUbxioTtHIdB1uHFTVl4YkXxzGccSNIsjyUvDCGdALlubAx rSXJEqypK8ilyYRjq1HzXs+2seHWWBcl/LErSTNM0TWtBcGCYzjnjFR/vEY5nGnG01TPPc9KFCZf NEdM5oGaBrm27U+TZPtFUYYhmGi9NBoEX61xJRNL0WtBWFsXijKRSSB0RTNR0ZTBbvqcrJVAgb61 NV1SPkYZ1OPVaBNNV9S1JGRZl4YTxVqf9eGFXFiTVBJem8cZz2BIRlTzXNoWLGcIu+bFgTsacyTN XVuWjWDsTjUBsG6cLqGTaV0LxBhkulJ8VRY3ZsRDbt02hEl4uU1zXnAcpz21euAQgur+Vmd7ltW+ GA29ha0F0YZkTmaRsG5eeGXpi9GSlOb1nnYWFXrTZeYLUDUGzj+MRknZq0jSSnHrNxjZPi0qvrE8 U2AgVknOVZaF1mUzp8XL3ZwgramgaxtZ/btHGjfOiIKtsLaVRhbmAYtw6ehacHrqVFwZhxkSlQpt 2qtRmUtReYHOdZ26yiLAnwaOJvqVZal2+5g5VLrYnu9LauatrxXi7RWwVvF445t3FcXxnG8dx/Ic jyXJ8pyvLIIgIAANAQAAAwAAAAEAMAAAAQEAAwAAAAEAMAAAAQIAAwAAAAMAAAPKAQMAAwAAAAEA BQAAAQYAAwAAAAEAAgAAAREABAAAAAEAAAAIARUAAwAAAAEAAwAAARYABAAAAAEAAADjARcABAAA AAEAAAMgARoABQAAAAEAAAPQARsABQAAAAEAAAPYARwAAwAAAAEAAQAAASgAAwAAAAEAAgAAAAAA AAAIAAgACAAK/IAAACcQAAr8gAAAJxA= X-ABShowAs:COMPANY X-ABUID:C5C50103-C86C-40BB-8864-909A089EB390\:ABPerson END:VCARD tests/auto/versit/qversit/testdata_vcf/AAB4-SingleExtensive.vcf000066400000000000000000010426731233466112000251440ustar00rootroot00000000000000BEGIN:VCARD VERSION:3.0 N:Lastname;Firstname;;; FN:Firstname Lastname ORG:Company; EMAIL;type=INTERNET;type=WORK;type=pref:work@email item1.EMAIL;type=INTERNET:other email item1.X-ABLabel:_$!!$_ item2.EMAIL;type=INTERNET:custom@email item2.X-ABLabel:custom email label TEL;type=WORK;type=pref:work phone TEL;type=WORK:work phone 2 TEL;type=CELL:mobile phone TEL;type=WORK;type=FAX:work fax item3.TEL:other phone item3.X-ABLabel:_$!!$_ item4.TEL:custom phone item4.X-ABLabel:custom label item5.ADR;type=WORK;type=pref:;;Work Address;Work City;Work State;Work ZIP;Work Country item5.X-ABADR:us X-AIM;type=WORK;type=pref:workaim X-JABBER;type=HOME;type=pref:Jabber X-JABBER;type=WORK:workjabber item6.X-MSN;type=pref:othermsn item6.X-ABLabel:_$!!$_ X-YAHOO;type=HOME;type=pref:homeyahoo X-ICQ;type=WORK;type=pref:workicq PHOTO;BASE64: TU0AKgADAAhIMyBIMyBIMidELiNDKxc/KBVAKRY/KBVALBZGMRpIMx5KNSBKNylOOixPOSdIMiFD LxhEMBlEKxlFLBo6LBY5KxU5JQ47Jw87KhU+LRdELh9IMiNNNSlQOSxVPi5ROytPOS1POS1PODFT OzRKQDA9MyRHKRZJKxdJMyJOOCZKOCVFMiBAMyVAMyVAMydFOCtMQDVHPDFFNyU/MSA8LyFGOSpP QC5WRzRQPzBJOSo8MiM4Lh8/LyFVRDReTEVlU0xnT0FkTT9hT0NjUUVhSTxdRjlJNSBDLxpHNy5T QTlYQzJMNydUOSVVOiZRNyNOMyBKLBpIKhhDLxpFMRxGMRxFMBtOPihQQCpMOSZHNCJMNSJWPytW QTNWQTNTPjFKNypMMiJNMyNTQTNcSjxiT0ZfTURYRS9MOSRUQC9bRzVbSjhNPStHOCY/MB9ENR5K PCRYQThXQDdROy5POSxGOiVEOCNKOy1TQzRNPS9AMSREMCRHMydELyBJNCVDLxhFMRpJNyZEMSFF MSRALSA5LB48LyFGOjhOQT9VSUBYTURTQzdNPTFJOylDNCM/Mx83Kxc6LyhEOTFRRj5XTERYRz5J OTBBMSNMOyxOOi9OOi9KMy9MNDBENSxFNy1IODJMOzVMRTxTTENUSj5GPTFGNylGNylAOSo8NCZG LytROjVQPjVTQDhNQDxOQT1OPixJOihFNC9JOTNJPz5KQD9YRUlfTFBcSUxFMzU9JBpGLCJINzdR Pz9aRkhcSEpOOzlFMjBIOTpaSUpeTElcSUdROTRNNDBDLSJDLSJBLRpELxxOOChQOipTOzRWPjhW RT9UQz1POjVNODNJMi5IMS1NPDdYR0FfSUFNODBBLy1RPjxWQ0NOOzs9JyE+KCJALCVBLSZIMiNH MSI9Lhk/MBtOOSlVPy9YQThcRTtbQzdVPTFUOS1WOy9VPylRPCZMNSk+KR1FMCtQOzVPOy5MOCtR OCVWPClcQTBUOilHOCRGNyNKNSRGMSBHMh1UPihPNCFGLBlKNSZPOipMMiREKx0/JhJAJxNEKhZH LRhIMBxNNCBQOB9NNBxHNylKOixJNSlEMCRHMR5KNCFGMB0/Khc8KQ88KQ9GKBZKLBpGMB1BLBlF LxxKNCFHOi1PQTRXRDJWQzFQQCxMPChWOzBYPTJTQTVKOi5HKhtKLR5NOidNOidJPCxFOChDNSZE NydDLyJDLyJMPC5PPzFNNSlIMSU7MSE7MSFKOylURDFWPzNTPDBFLyNFLyM3MCVEPTFaTkZhVU1i UEBkU0NfTkViUEdcUUNXTT5INSNALhxOOi1XQzVbRTRQOytYOzFcPjRYQTFTPCxQNCVNMSJKNChM NSlFMCFDLh9OPS5XRjdNOSxJNSlJOiRTQyxWRjpRQTVPPy1KOylBLiFEMCNQPzlfTkdjU09eTkpX QzRNOStMSDVVUT5bSUBQPzdIOig8Lh1GMiVVQDJdPzVaPDJROytUPS1KOitIOClIPzVMQzlIOSs9 LiFKNChMNSlKNCFKNCFBLxtHNCBNPC5EMyZFMB89KRg+LyE9LiA7NS9AOzRRRj5aTkZRRzlKQDJD PS43MSM9Kh4+Kx88LCZDMixRRUNVSEZMOjhDMS9HLSVQNS1UPTNTPDJQPDFJNSs9NCk/NytBNy9G OzNEQztJSEBORTlFPDBBMSNBMSM7My47My5BMStGNS9MQDVQRTpVRj5TRDxMPjFFOCtGMiZGMiZF MjJKODhNRUFPR0RJPjM+MylFLiVKMypNOzVXRT9cTE1XR0hFNTI8LSo9MzBPRUFWSUdUR0VNOS5H MylEMCM/LB9BLRZMNx9UPi5VPy9VQzpTQDhROjNNNS9MMSdOMylNNTJONzNTQDteTEZVQDNFMSVF Ny1TRDpTPjNHMyk9Jxs+KBxFKxhJLxxQMiFRMyJDMyVIOSpVQDNWQTRXQTpXQTpYQTFUPS1TPS1W QDBVPTFQOS1KMydDLCBNNTFTOzdJOi5GNytOOixXQzRaRDNPOipQOylTPStOOChVPi5cRDdfRzpb PCpPMSBQNyRUOidIMBo6Iw9DJg9FKBFGKhZOMRxPOSdROylNOSNOOiRHNylOPS9MNSZNNydONCRP NSVMMxtMMxtFMRhFMRhMMCFMMCFKNSJJNCFJNCFKNSJNOS5VQDVWSDtTRThNRTJHPy1MPC5OPjBN PjdGODBBLCBAKx9UPS1WPy9KQDBEOipGNyhHOClMMSlGLCRQOi5aQzdTOzRONzBFMCFELyBHMiNU Pi5WRTdTQTNHLhxEKxlDLSJJMyhUSEBYTUVhVkdfVUZfT0BjU0RfUEZeT0VQPzFDMiVJPC1QQzNW RTlWRTlVQz1aR0FdRT5YQDpUPS1TPCxMPCpNPStEMCZGMihTPDBeRztcRDdROi1HOCpWRjheTENc SUBFOjE8MSlBLiFGMiVMQDVXTEBnTU1lTExhRTpWOzBQSTNaUzxTTEFIQThEMyU+LiBNNy1WPzVe QDdYOzFcRjVbRTRUPzFPOy1NRDpKQThGNyVENCNTPDBUPTFQOyVOOSNPOCxTOy9TPTVNODBILCU/ JB0/KxxDLh9BMyxBMyxTPTVXQTpPPzFRQTNIPzM4LyRDKR9FKyFELypPOjRWRUhUQ0ZMNy9FMClB KyJPOC5VQzxUQTtNOzRBMCo/MiZAMyc9MTFFOTlMRD5ORkBOPjBHOCpALSBALSBGNCxEMipDMipE MytQPjVWRDtaRTpRPTJJOixFNShBMSNBMSNBMChJOC9UPTxTPDtOOi1MOCtQOSxVPTBQPjxWREFW QUZVQEVIOjI4KiM3JiRNOzlWTU5KQUNONy1MNCtIMx5FMBtMMiBYPitRQTVWRjpXRj1RQDhROjBO Ny1QODFTOjNPODNXPztfT0BfT0BKPS4/MiRJNDBYQz5WPytKNCE5JxY8KhhGMhdPOx9UPSlVPipT Oy9XPzNTQzdWRjpRQDRKOi5WOzBWOzBYQTVeRztUQz1MOzU+LyQ9LiNJMypMNSxMOy1OPS9URDhR QTVPPTRINy5KOylMPCpTQDpcSUNkU0NUQzNKNSRDLh1PNyBPNyA/Lg4+LQ0+KA87JQ1BLBlIMh9K NCFROydNNyVJMyJGNB5MOiNMMx9ONSFPOSdOOCZIOSNHOCJNNyNPOSVVPi5UPS1TPyxTPyxRPCpO OSdJNC1WQDldTD1YRzlURDhOPjJFOytHPS1QOSxTOy5POCtPOCtPQC5QQS9VPjFNNypJOSpMOyxN Ny1GMCdIMS5cREBYQDpTOzRNNydJMyRGMSJMNydUQzdWRTlVOi5XPDBNNyVGMB9JOTBaSD9qVlRq VlRkVEdjU0ZhUUpfUElTQTxHNzFANS5MQDlWSUdYTElUSUhVSklcSkRcSkRcRzxUPzRROytTPCxK NChNNypUQTleTENTQzdMPDBIQDFRSTpbTERaSkNRQTVENCk/MSA+MB9RPTBeSTxaT05dU1FcSURW RD5USkBaUEZWRz9KPDRAMSBDMyJKNS5UPjddQzFeRDJdRjpcRTlXQzhRPTJOPjJNPTFMOy1RQDJT PTVTPTVXQDRYQTVQPC9TPjFMQDhJPjVHMiM+KhtBLRhELxpFMB1JNCFJPC1QQzNMQzpPRj1QQTtH OTJAKh5AKh5ALzFPPT9XRERPPDxFNSRDMyJFMRxNOSNYPDlXOzhROTRONTFHMylINCpAMixDNC5J ODFQPjhTRDxHOTFAKBpFLB5GMCVFLyRELSdMNC5OPDVWRD1OQDNJPC9BOCc+NCRELyhFMClEMyhG NSpKOi5PPjJROi5YQDReSDhYQzJTQzdTQzdYPz9VPDxGNys5Kh83JyJJOTNNRUFGPjtQOjBQOjBI Mh9HMR5QOjBbRDpWREFYRkRYSDxURDhXQTxVPzpNOzlQPjxWRT9aSENcTj1YSjpNODNNODNOPDVO PDVOOSdGMSBBKRdNMyFRPS9TPjBTPjNTPjNVOi9XPDFURTtPQDdFOCg/MiNONzNXPzxbTEVdTkdW RDtJOC88LRw7LBtFLiNJMidQNS1cQDhUQzpOPTRHNzBEMy1ENDVJOjtaRkpiTlNaSTdMPCpIMyJB LRxPPi9NPC1DNRY7LhA7Kgo8Kws/LBZALRZKNSJOOSVMMx1ELBZALBlNOCRNOChKNSZKNSJMNyNJ OiRHOCJNOStPOy1QPC9RPTBOPTRNPDNPPjBNPC5KOixTQTNYRTNXRDJMPi9KPS5JOixMPC5NNSlQ OSxOOCxTPDBMQTFKQDBQPzBHNyhGMiZJNSlKNCtGMCdHLzFXPkBaR0BUQTtPOihMNyVJNCFIMyBJ OTJVRD1VQS5QPSpMNyNIMyBHMSZVPjJfTUpjUE5kT0FkT0FeU0lcUEdaPD1QMzRAMCNJOStUREBb SkdVTENVTENdTUBcTD9XSTxVRzpOPS5PPi9IOCpMOy1PQz5dUExPQy9EOCVEPCtNRTNeU0laTkVT PjBKNylBMSNDMiRMOyxXRjdYSk1cTlBaSENYR0FUSUZbUE1aRkpKODw+LiBAMCJHOCxPPzNVRDVb STtcRD9bQz5UQzpPPjVMNSxKNCtQPjVUQTlRQTVURDhVRDVUQzRPPzNQQDRKPzdDOC9HMh88KBY/ LBZBLhdJOCFNOyRGPC5JPzFFPjVNRj1ORzFJQy1JNSlDLyNBMi9MPDlTQDpNOzRHOCJHOCJMOCJP OyVVPjRVPjRTOzFROjBKOCNBLxs8LSBAMSRHNS9NOzROPzVIOjBHMiFHMiFBLCBFLyNJMyhNNytP OzBVQDVKQTVHPjJFOi9DOC1GNSpEMyhHMyZHMyZJOitRQTJbSD9iT0ZaSkRTRD1NPzBKPS5OPDNN OzJEMCY9KiA+LCpINTNKOjFIOC9KMy9MNDBENCdENCdMNzJaRD9YRkZXRUVWRERUQUFUPjlPOjRN PD9OPUBTQD5RPz1VRTdNPS9HMCRIMSVHOi1HOi1GNyNFNSJJLxhRNx9XQDNVPjFTOzdTOzdRPS9a RTdWSDtKPTA/MRw5KxZQOjteR0hcUUNWTD1TQzdOPjJGMyM9KxtEKiJPNCxYRUVjT09YR0FAMCs4 Khk3KRg5LypMQTxbST1bST1RQTVJOi5GMhc+KxFKNyxNOS5HMxo8KRE8KBA8KBA+KxZALRhJNR5N OSFMNyFGMRw9LBZFMx1HMydFMSVNOStPOy1HNyhGNSdFNSpGNytJOS1KOi5EOS5ANStKOi5JOS1M PTdMPTdKOitMOyxIOSpGNyhFNSpFNSpFNClMOy9MPC5OPjBNPzJOQDNIOyxFOClEMyhHNytNNytO OCxIMjFPOThTRkRQREFPRTdKQDJJOStJOStMOzRQPzlPQTROQDNNOzJHNS1GNB5HNR9YRkBeTEZd R0FdR0FiUUFhUEBeRDRONCZGLytKMy9MPztYTEdVSUBVSUBdTUleTkpYSUNVRj9NPTFOPjJDNSlK PTBUQURjUFNYRkBNOzVHOTFRQztbU1FRSUhOPDVJODFJMyJMNSQ+LyFJOitaT0BbUEFbSUBaSD9d TEVhT0hdRz9NODA8Lhs+MB1EOS5KPzRURTtaSkBcSEZbR0VQRTxGOzJBKydJMi5UQzdUQzdVRzpV RzpQQDFTQzNVRDtXRj1KQTVDOi5JMRlAKRJALhJINRhQOydRPChKPDJJOzFIPTJMQDVNRTJMRDFK OTBBMCg/MSpHOTFPOzBPOzBFNSBJOiRPOy1UPzFWRD5QPjlXPzlWPjhRPSVGMhs+LRc/LhhEMixJ ODFKOjNKOjNFNShDMyZBMSREMyZDMyZHOCpMPi5PQTFRQDpKOjNKOjRHNzFFNy9DNC1ENCdGNylP PjVaSD9bTk5bTk5JRTpDPjNDOC8/NCxGOSxDNSlALh5BLx9HNytHNytINzBKOTJHNS9HNS9ENS4/ MSo3LSxNQ0FbSUBXRj1WRT9UQz1QOjBOOC5OPDdRPzpRQzxTRD1TQzdMPDBMNSpHMSZAMR5FNSJI NB9TPihVPixeRzRiT0hYRj9VPTdUPDVTQzdXRztPQz4/My89KSRBLShTSEVdU09aR0FUQTxPPzxJ OjdKNSQ/Kxo9Ky1VQURbSEZVQ0BIOCwyIxgvIBg6KiI8MjFPRURjSkdbQz9TPCxAKxw0Ig88KRVN MSBTNyVPNRpKMRZFKhA/JQw/KBNGLhhJNB9NOCJHOCQ6KxgzJg83KRE+KRZGMB1HOCJGNyFGNylF NShBMic+LyRHMylGMig8LR8+LyFDMiRHNyhNODNQOzdOOi1MOCtHMSBIMiE/LyQ+LiM+NCVBOChD OypDOypMPCpKOylGMyE/LRtBMiFKOylKOylHOCZNOSxJNSlKOy9QQDRPRDlIPTJOOCtMNSlKOixN PC5OOi1POy5OPTRIOC9NNSpJMidVMSxcODJVRDheTUBYUUVVTkFXSTlNPy9BOCc8MiJIPTJXTEBN R0NIQz5TTUpTTUpcSEZPPDpTPTVXQTpQOzNMNy9QPjVbSD9YRj1NOzJIOjBYST9aUUxVTUdQQCxJ OiZGOCBGOCA4LiE+NCdJPzxMQT5aR0FeTEZkVFNdTUxURzNFOSY/MBs4KRU9MCFIOytbR0VdSUdX RERTPz9IOS1HOCxAKyJKNCtVRTlhUERaTDxVRzhTRTRWSDhbSj5bSj5URDVMPC5FMxs/LhZKNx9W QSlbRjhdSDpRRzlGPC5NOzRRPzlQRT1OQztPOjRDLik9Lhk7LBdGMSJMNydINChRPTBWQDlVPzhU QTtTQDpQPTtRPjxQOi5NNytFMBs+KhY4KRxAMSRGOC5HOS9BMiVAMSRAMSA+Lx45KhxGNyhJPC9K PTBPOjJTPTVNPDVGNS9HNytAMCU+MClJOzNUSD9YTURWRT5KOjM7LCE+LyRAMR4/MB1INCdJNShO PS9KOixENSxDNCtFNCxFNCxBMiFBMiFFNCk9LSI0Ly09ODVWRD5XRT9TQTtRQDpPOjJJNC1BLiJG MiZPOjVRPDhMNzFMNzFFMiA7KRcxIxM+Lx5PODFcRD1lT0dhSkNaSTtHOCpEOCVNQC1MRjdRTDxN RDo+NSw9LitGNzNRR0RRR0RRQztQQTpPQTRHOi1ELxY9KRFEMCZWQTdTQTtNPDVILx8/Jxc6JBk+ KB1FLjNYQEZYTUFIPTJHMh88KBY9LBZRPyhRNyFQNSBWORlTNRZGLg9AKQtFLBRHLhZNLyBTNCVH OSc9Lx40JQs5KQ89KBlELh8/MRw/MRxIOSVMPChGNSo7KyBALBtALBs6Kxg9LhtHMCRONypMNzJO OTRRPTJRPTJINB9GMh08LyE+MSNBNSNHOyhGPCtHPSxOOypKOCdGMx8/LRlAMCJHNyhNOidOOyhK OitHNyhGNylNPS9QOS9XPzVVPi5ROytVPipTPChPPCtTPy5PPzFMPC5IOClIOClILyFJMCJJPC9U RjlfTkVhT0ZYTUFVST5DPy86Nyc+MyxOQztKRUBMRkFRSkFWT0ZVSERMPztHPDNQRTxRQDpIODFQ QDRYSDxVPTdMNC5IOy5XSTxXU0hWUUdWQTdTPjNHOipIOys8MSk+Mys+Mi5MPztaR0dfTU1pUVNk TU5VRzhGOSo+MBs3KRU+MiBPQy9cSUdVQ0BPQDlKPDRFNShENCdFLiNPOCxaTUpeUU9RSkFORz5O QDNVRzpaTD5XSTxbRjtRPTJBOSNHPihRRDRXSTpWSUVXSkZQQzVOQDNNOzJQPjVRQDpPPjhPPClD MB4/KxZIMx5GNSpFNClJNC9RPDdVSUBaTkVUQzxNPDVFOTRKPjpKPDRHOTE+LRc6KRRIMyJKNSRE NS5BMyxIMyRFMCFBLRY9KRM5KhlGNyVMPC5MPC5OPDNOPDNJOi5BMidFMSVGMiZENztPQUZXSEBT RDxJOiw7LB85Jxc9KxtELiNNNytQQzVQQzVOPTdNPDVHMydEMCREMCREMCRDLyNALSE9Kxs+LBw8 LSI/MCVHOzdOQT1VQ0BPPTtFMytALydBMSZEMyhINzRHNTNENCdAMSQ9KxkyIRAyJR47LSZRPDhf SUVbT0ZWSkFMPjE3Kh49MCRKPTBTQTVUQzdJOTA+LiZAMydBNChKPzhTRz9OQzpJPjVPPi9QPzBI OSVAMR5ENSxQQThNPjdGODBEMhxBMBpAKiZAKiZMOjxXRUdRQTVENCk/MSA8Lh0/MiZMPjFUMyVa OSpiQCZdPCJWQCZUPiRPNB1KMBlPLR5cOSlTPS1FMCFAKRJAKRI8KBRELxpFMh5FMh5NPStOPixA NCI0KRc9JhE+JxJAKBZAKBZGKx5MMCNMNSpUPTFOPS5HNyg7Lxk1KhU6Jxs8KR1DMyVJOitDPCdI QSxKOyxIOSpGMCQ9KBxDLyJGMiVJOSpNPC1IPjBDOStGNSdFNCZMNzFTPThUPzJVQDNVRDRQPzBV PixVPixOPi9KOyxJOiRIOSNFOSZANCI8MiVIPjBbSEFhTkdWSkFUSD9HPjREOzE9OS5FQDVORTtQ Rz1VRUFaSUZWRT5PPjhOPjJQQDRQPzFQPzFKQzFUTDpbPjlMMCtAOCxMQzdQT0lTUUxYPzxWPTpN NzVQOjlNODNELys7LSRJOzFeRUlkSk9eSk9aRkpKRUA9ODM9MCQ4Kx9ENyhQQzNXTkRQRz1GPCxD OSlGNCxEMipFLyRTPDBYTkpcUU5UR0NQRD9QPjlXRT9cSUNYRj9URTtOPzVFOi9MQDVXR0ZdTUxb TkxXSkhMPTNHOS9KOjNKOjNOPTFIOCxALh4+LBxFMSRNOStIOCxBMSZGMSpQOzNQSkRbVU5NRDtB OTBGNTBFNC9GNS9FNC48MB44LBpGOSpGOSo7Mio3LiY8LRw8LRw8KhY9Kxc9LR9IOClQPC5RPS9O PDNKOTBENydBNCVDLSRHMShBNzpKP0NUSTtMQTM/OCc1Lh44KRs6Kx0/LylJOTJRPzlOPDVINChD LyNBKx9ELSFDMB4+LBo/KR4/KR45KhY6KxZBLR5FMCFJPjNRRjtVQT9KODVELSpAKic8Myg8MyhB Myo9LyY9MCI6LR81IxYzIRU1JiBAMCpPPDxaRkZWTDtJPy9GMCU6JRo6LCZJOzRUPTNTPDJKMydH MCRIMyROOSlTQTVTQTVRPS9QPC5GOSFGOSE9LiA3KBo+MSVNPzJNPixDNCM9KRM7JxE7JCBGLipR PDdPOjRPOS1GMCU4KBs5KRw/Li5NOztHLh5WPCthRTBlSTRjTDlbRDFPOyVBLhlKMh5ROSRWOS9W OS9MMiBFLBpEMRZFMhdFNSBJOiROPzlNPjhNNyVFLx5DKxdELBhBLBk/KhdFMCFNOChMOShNOilO PixIOSc+LBg8KhY7IxI8JBM3JRJDMBxIMyJOOSdbQC9VOypMMiBBKRdBLCBDLSFJNDBOOTRJQCxA OCRFMiA+LBpBMSRKOixQPDFXQzhUQTxVQz1dRz9cRj5WRjFTQy5OOypINSVIOSNENB89MCQ8LyNH Oj5URkpQST9TTEFURTtJOzFENClKOy9QPzlUQzxXREFbR0VURDhPPzNNOSxPOy5UPS1TPCxKQDJR RzlVPTdQOTJJODJTQDtRTkpQTUlVRD5OPThPOTpPOTpMOTdDMC43LSA6MCNNP0FcTlBdSkhXRUNJ PTk9MS07MSI1LB0+Mi5JPTlWRTlTQTVKOixGNShKNTFHMi5GMSxXQTxbSURfTkhRQzxOPzlNOzRT QDpXPzxdRUFYRkRUQT9KOjRQPzpYTEleUU9cSkVXRkBIOCk/LyFJLydOMytNNy1IMilALSFFMSVN NTJPODRKOjRAMCtFLipMNDBVSU1dUVVTQTxHNzFMMCtEKSRAKyI/KiE4MB8/OCZPQTJIOyw8LSAz JRg3Khs3Khs4Khk5Kxo7KB5INCpQOTVXPzxKOjFJOTBHOitAMyU9LShAMCtMOjhXRUNeTElNOzlA Lh49Kxs9Kh0/LB9MNC5QOTJMNzFGMSxELx5ALBtALBtIMyJGMCE+KRo7JRk7JRk7JhY+KRhMNyVP OihRQTNVRTdTQDtHNTA8LyE3Khw8Jxs8Jxs7KhM4JxA7KBI7KBI4JA07Jw83KRY+MB1HNS9TQDpU QT9INzRIKiBAIxlBMSRNPC5VOi9PNCpELyA+Khs/LCJKNyxOPDNNOzJNOStGMiVEMhpBMBg/LR0+ LBxBMiFGNyVMMx1IMBpFKRZDJxVFLyBMNSZOPS5KOitJMS1BKiY4JRk9Kh5EMCRHMydGLhZTOiFh STRnTzpnSkFeQzpNNyo/Kh5ALxdEMhpVOypcQTBWOS5ILCJKNx9JNR5NNydQOipNPDVPPjhNMidG LCFHLB1GKxxAKRZFLRlFKyBNMidMNydTPS1UOyJQOB9GMSA+Khk9JQw5IQk7IAs+Iw5FMRpNOSFe QTFdQDBRQyY/MRY4Jg06KA8+LyxBMi9FNyU/MSBELxw+KhdBKxJGLxZIOClQPzBPPjJRQDRXRT9X RT9WRz1VRjxTQTNJOStGOCZBMyJALSA6Jxo+MytMQDhQST9VTkRYQztMNy9BMSlHNy5NOjpUQEBU QTtUQTtaQzdUPTFJOC9NOzJUPzJRPTBOPy1TRDFRPi1QPSxNOzVTQDtaTU1TRkZNQD5BNTNJMS1I MCxBMic7LCE5MBs/NyFTRz9VSUFcRURTPDtGNSo+LiM7LCE9LiM/MiZFOCtMOShMOShJNCVOOSlM Oy9FNClDLilVPzpTRUxURk1JPT1DNzdENSRDNCNHNTNRPz1TQTtRQDpJOzRJOzRQPT9cSEpVSEhR RUVGMh1EMBtMMCFQNCVINSM+LBo+KRhFLx5ROy9VPjJNPTFENCk9LCdJODJVR05YSlFWQDlHMitJ LB8/IxYzHw84IxM3LiVGPTNUPzROOi8+KRg1IRE6JRk9KBw6LBc4KhY8JiBGLylTOjdWPTpQOS1O NytKNClGMCVELyhELyhUOz1iSEpaSENGNTBDLhY8KBA/KBVIMBxTOShROCdMMiBJMB5MMx1MMx1K NSZPOipKNClELiM8KRw5Jhk5KxhIOiZWRTVUQzNVPTdVPTdMOzJEMys8KhYzIg8+JhU+JhVDKA8/ JQxFKQpBJgdBKhVDKxZBLBlOOCRNODBVPzhKOTlGNDQ+Khs+KhtKMR9QNyRQOTJJMixBLCM6JRw9 KBxIMiZGNSdGNSdHMydINChDNyQ+MiA/LRs+LBpHMCVHMCVGLyNGLyNBJxhFKhtDLSRGMCdJNC9M NzFHMCVGLyQ6JBk7JRo8Kho/LR0+KhlOOSdYRzhfTj5lTUldRUFOOCxBLCE7LhY/MhlUPTBcRThW PzVOOC5TNyVVOSdXOi9UNyxQPC9RPTBJNCVALB1KLR5KLR5DKxRAKRJAKh9HMCVQNyRXPSpcQSdW PCJJOiRENB9EKxFAKA8+KxE1Iwo8JxtROy5bRDdfSDtbTDlHOSc7Kw83Jws9Lh0+Lx5FMiJGMyND LhY9KRE8JQ8/KBFFNyVKPCpMNyNOOSVPOzBUPzRTRz9TRz9UQzRQPzFKOylENCNFLiJAKh5DMyJN PStVRj9aSkRVQ0BKOTdDMiRDMiRHMydMOCtOOi9UPzRXQDdXQDdIOCxGNSpNOS5QPDFQQC5URDFR QDJRQDJOPj9OPj9USkxRSElORTlDOi5DLR5NNydENx85LBY1LB8/NShWRERVQ0NYQz5QOzc/MB87 LBs9KxlALhw8LyA9MCFIMyRMNydINSVNOilOOyZGMx9DOC1JPjNOQEVTRUlNPTFFNSo9MRtANB5H OCpPPzFPQTJNPzBMPTdJOzRKPDVTRD1RRUNRRUNHMylKNyxPOCxMNClENyg7LiA6Kxg+LxxHOCxM PDBHOS9DNCs/MStMPTdaSExbSU1YRDVNOStHLxs7JBEyIxY4KBo7MzBKQz9QPzNFNCk7IRE8IhJF KBdJLBtGLhZELBVELh1IMiFQNS1UOTBQOjBMNSxONy1NNSxHMylMOC1XRERdSUlUPzJFMSU+Kw89 Kg9FMSVUPzJYRDlWQTdUPSlUPSlPOSdQOihQOzNRPDRKMiw9JiA5JxY8KhhFPDBRSDxcSj5VRDhR Oi5POCxFNyNBMyA8Khg5JxZBKhNBKhNJLxpMMRxJNRhDLxNGLR9ILyFNOCRWQCxYQTRXQDNNOzVI NzFDLhdELxhOOSdRPCpMOShHNCRELx46JhY8JxhDLR5FNCdHNylMOCtNOSxIOiZENSJBLRw8KBc+ KRZAKxg+KRpGMCFDKxdELBg/KiFHMShVOi9XPDFQNyZKMSE/Khs6JRY4JRhDLyI6JRNHMR5RPS9e STthSkVbRT9POiZHMh8/LRNBLxVOOi1YRDdYQDNROi1PMyZRNShWOjRUODJQPSxQPSxOOyZHNCBH LxlKMhxGMRxBLRg+KxFDLxVHMR5WPytcRjVXQTFGPi1KQzFMOCBFMRpEMBY6Jw47KRdRPitfSD5n T0VlTkRXQDdFMxk4Jw88Iw9AJxNIMBxNNCBHMBVBKxA9JQ5AKBBDLhtIMyBHMSVKNChHNS9KOTJR PzlOPDVUPzJTPjFRPS9OOixPNClGLCFELyhPOjJXR0hYSElRRUVMPz9ANydANydEMyVHNyhROy9a QzdaQzlXQDdPOy5JNSlNNypQOi1RQTJWRjdbRT1YQztRQDtOPThORkBTSkVYSDlNPS5JLxhPNB1K Nx1INBs7Mh87Mh9TQTlRQDhTPChPOSVMMx9IMBxALBk+Khc9Lhk/MBtPOSdUPStPQC5TRDFWQDlP OjJIOSpGNyhQPj5VQ0NUOzdONTFBMSNBMSNKNTFUPjpTQTlPPjVOPTdIODFDOTNHPThOQDNPQTRJ OzNNPjdRPDRNODBBMiE9Lh04KRs6Kx09LiFHOCpGOSpHOitJODJRPzpWRT5VRD1XQzVTPjFKNCND LRw/Khc9KBY+NzNKQz9QQDRBMic/Iw1EJxBJLRZVOCBROB5KMRhFLRlMMx9POCtUPC9QQDRMPDBI OjJFNy9HOitJPC1QRz5RSD9OPTRAMCg7KhVINyBWRTlaSDxXRj1VRDtUPzFPOy1IOyxOQDFPPThP PThKMy1AKiQ6JxFEMBlTST9aUEZcSj5UQzdMNydJNCVNNyNKNCE/KxxELyBNNB5MMx1POSdTPCpR OClNMyVIMyJKNSRUPzJbRjlYQztUPjdMOjFKOTBMMx1UOyRbQC1aPyxQOjBIMilDMCA9Kxs+KR5B LCFHMSBNNyVTPCpROylPOiZNOCRIMBxBKhY4KRU6KxZELBhTOiVQPSpINSNHMiFQOylYPCxaPS1Q OipFLyA6JRM3IhA8IxhMMSZBKBRJLxpWQDBfSTliTT5dSDpVPipOOCRJMBdDKhJNNC5YPzlcRThX QDNXPSxQNyZVOC5VOC5NPC5PPjBRQStNPSdMNx9NOCBOOCRPOSVHMhtDLhdGMSpUPjdbQzxYQDpU PTNVPjRTPy5KOCdNNRtJMhhKMhxVPCVeTEVjUElfTUdaR0FNPSlAMR4/JQ5AJg9IMRdTOyBJNRpD LxVBKBJEKhRFLB5MMiREMCNEMCNHMSZMNSpPODFPODFTQTVVRDhYQS1WPytROytFLyBFMSdPOzBX QD9YQUBWQ0VPPD5FNCdEMyZGNyVKOylOPzVURTtXRj9YR0BWRjhIOStENSJJOydRQDRaSDxhTD5c RzpWRz1MPTNQPzdcSkFeSTxYRDdUOilUOilQOydNOCRPPCtINSVRQTNNPS9QOipTPCxXPSxUOilN MyFILx1HMh1IMx5TPyxTPyxWSkFWSkFWQDlPOjJENSRHOSdUPzRXQzhPPzFIOStBMidAMSZNNTFY QDxUQzpQPzdOPTFMOy9GNS1KOjFKQzNKQzNWQTdXQzhRQDROPTFMNSJFLxxBLBk8JxU/LCBHMydH NylKOixOOC5ROzFRRDRPQTJOPjJPPzNMOCJJNSBMMx9HLxtJOTJOPTdNPS5DMyU+LAxHNBNPPylX RzBUPipOOSVMMiJKMSFOOixUPzFXQTpRPDRMOjNHNS9IOjJNPjdOREBOREBOPjBBMiU8MyBGPSlW Rz9WRz9VRj5OPzhQQDFPPzBUPTNVPjRVPTdTOzRMLh9GKRo1LRhIPylYUUdTTEFWPzNTPDBPOSdU PStUPC9QOSxMMR5NMh9OOx9PPCBRPTBRPTBJNSlGMiZNNytOOCxUPjlcRkBXPzlTOzRKNyxMOC1O OixaRTddRTldRTlYPDdMMCtALRg8KRU8JhpGLyNNNypUPTBaQzJaQzJWQzFRPi1HMBZBKxI4KRw1 JxpGNyhXRzhXQTFQOytXQC5aQzBdQzFVOypHMCRDLCA9JBI6IQ9FKyFQNStOMBhYOiFcRDdhSDtd TDxVRDRRPi1NOilKNSJGMR5JLCpOMC5POjRTPThXQDBQOipQOihROylNPDBPPjJUQzNUQzNQPSpQ PSpTPjFTPjFNPSdFNSBMNCtTOzFWRD1WRD1UPTFVPjJPQDdOPzVRPitTPyxRPitVQS5fUUReUENj TUdeSENJPjNFOi9DLhdBLRZTPihYRC1RPitMOSZIMBhHLxdFLyNIMiY/MCI+LyE/MCJDMyVFOjFH PDNWRTleTUBdTTVURC1RPilGMx9GMiZOOi1QPzlRQDpRPzlTQDpHOipENydIOiZPQCxYQDRfRzth SUhjTEpVSERKPjpIOSpKOyxQRjhXTT5dT0FXSTxUQzpMOzJTPDJhST9hTkdaR0BUQTlTQDhUQzNR QDFPPTdJODFOPTRQPzdQQDFVRTVdTThWRjFaQzBMNSRJMCBONCRORDNORDNPSkBNSD5PQy9IPClA MSNRQTJaRTpYRDlRRzlKQDJJNStINCpPOjRbRT9URjVRRDNOQDFMPi9GNShHNylKQTVORTlbST1a SDxRQzBNPixHNyhEMyVFKRVFKRVAKxhPOSVROylPOSdQOytOOSlMPCpQQC5WQTRXQzVPPSZPPSZM PChGNyNKNCtPOS9MOCtKNypINSFWQy1aT0BYTj9WPzJROy5ONypQOSxPPzFURDVURjdPQTJIOjJF Ny9INzFINzFQPTtTPz1OPipOPipTRDFYSTdTRzxPRDlUQzpTQTlYRDdXQzVaQzlXQDdWRDtUQTlA Lxk5KBNHPDFYTUFcUEdVSUBRQS9NPStTQzdVRTlWQDlRPDRQOipPOSlQPidQPidOPS9IOCpKMydM NChONy1POC5WQDxcRkFUPzRNOS5JNzROOzlOOztUQEBWRD1WRD1TPjFBLiJBLRhALBdBLx9KOCdU PjdbRT1cSj5YRztVQDNNOSw/LBQ4JQ4yJhc5LB1NRjpVTkFRQDRNPDBYQTVYQTVTPjFDLyM9KRY/ Kxg5Jg86Jw9DLyVOOi9PMyJXOylbRjleSTxaRDxOOTFHMydGMiZHMSVELiJAKh48JhpBKiZTOjVU QC9VQTBOPixMPCpKNypNOSxOPjBPPzFOPjBPPzFNRDpKQThNPStOPixPOyVUPylVQzpVQzpRPzdQ PjVPQDpPQDpQQTpOPzhIQC9ORjRcSUNkUUpdUExYTEdORjRHPy5FPCRBOSFPPzFXRzlVRTdRQTNU PStROylNOilGMyNDMCA+LBw/MB1JOiZRPTBUPzJeTUdhT0leVEVdU0RXSDNNPipEOixEOixRPDRV PzhVQDVRPTJFOCtGOSxKPTBWSDtfSD5jTEFdSkpdSkpTRkZGOjpGNS1QPzdbTEReT0dYUUdQST9K QThJQDdOPzlRQzxhTkdbSEFbRT9hSkVYTj9RRzlWQ0NKODhDMiVQPzFNSD5UT0VaTkVWSkFUQzM+ LiBELSFONypQRjhWTD1YRj9XRT5IPytAOCRHNS1VQzpYRkBWRD5RR0RJPzxIPTJFOi9JPjdcUEhc SkFRQDhIPzNIPzNIOSpGNyhGOzJQRTxcSkVYR0FQQThMPTNHNylJOStGLBZEKhREMBdQPCJXQTFU Pi5TPy5UQC9NPC5HNylKOTBPPTRUPzRVQDVOPTdFNC5HNzBKOjNNOzJNOzJQPzlfTkdfUEhbTERW RTVQPzBTPjNXQzhYRDdeSTxUSTtUSTtKOjNFNC5IMS1IMS1JPjVQRTxTQzRbSjxiTkxjT01UQzxM OzRPRDxUSEBXTERTRz9RPzlRPzlWRkNOPjtJMidHMCVXRUdeTE5YSUNURT5QQDJPPzFURjlURjlU RT5NPjhPOjRMNzFPOzBOOi9QOi1JMydOOCRROydPOSxPOSxTPDtQOjlJODJFMy5HNDRJNzdMNTdV Pj9aQ0FWPz5TOC1NMihPMSBQMiFNOStRPS9bRENbRENQQDJJOixFMSVDLyNAKRI7JA4/LyJNPC5V Rj9TRD1ROi5POCxXPzJTOy5JKxZBJA9AJxFBKBI+JQ9ILhdTPS1UPi5KOylTQzBVRDtVRDtTPjBE MCNBLCBBLCA+Khk/Kxo+KA89Jw9ALB1IMyRJQTJMRDROPTdIODFENCFGNyNJNCVIMyRHNylNPC5J PjNHPDFOOixRPS9KPCpNPixTQzRURDVQPzBMOyxJOzNMPTVQPzlRQDpHPjJHPjJWSkNcUEhYVElP SkBKQDBGPCxFPCZFPCZNQzRTSDpRRzVMQTBXQSlTPSVMOyxFNCZHMh1MNyFIOSNRQStVRDhUQzdb TEVcTUZaUEdYT0ZUSTtRRzlMPChNPSlJOTBPPjVOPi9KOyxEOipEOipMQDhUSD9bSEZWREFXRT9W RD5UPzJKNyo/MiRTRTVeU0paTkZUSEBQRT1OPzVOPzVGPDdJPzpXR0ZWRkVbSEFeTEVaTkZUSEBX SEFFNzA4Lh9HPS1USEBbT0deSkpYRUVUPDA8JhtFKhtVOSlaQ0FaQ0FXQzhQPDFIOStJOixIODFW RT5YRUNVQT9WRkdTQ0RKQDtIPjlGPj1VTUxcTD1PPzFNPjRKPDJHOCpFNShGOzBQRTpaSUhVRURM PjFIOy5JOSpJOSpIMBxHLxtINSVVQTBWRTlXRjpUQTtUQTtMQTNFOy1JPC1MPi9QQDRRQTVOPTRJ OTBEOipEOipQPjhTQDpURENaSUhbSkdVRUFQPzNOPTFVPTFYQDRbRjleSTxaSEFXRj9NPTFDMyhF NClJOS1JPjdMQDlVRD5hT0lpT09eRUVNOzROPDVVR0laTE5VSUFQRT1PPjVVRDtWRT5NPDVJNDBM NzJYQztXQTpRPzdTQDhRPDdRPDdVQDJVQDJTQDpKOTJNNTJMNDFKNylKNylNOCZNOCZPOSdOOCZP OCxPOCxTOy5ONypKNCtNNy1HNS9HNS9EMCNPOy1PPTdNOzRTOCxTOCxbPCphQS9YRDlRPTJTQTxT QTxNOidBLx1FKxZILhlGLxRKMxdTPDBXQDRWPjpTOzdONCRPNSVPOCtKMydILRNFKhBBKRdGLRtF MCFOOSlXQzVdSDtINChTPjFXQS9YQzBRPSdEMBs5JhA6JxFBLBlDLRpELBhDKxdBLCBIMiZIPDhQ RD9TPThIMy4+LxxFNSJJMyJIMiFDLSFELiJJNStMOC1NOChPOipKPChJOydRQDhRQDhNOzJGNCxB MSZFNClRPi1RPi1NOS5KNyxQRT1YTUVbUEpPRT9GPSlDOiZGNSdHNyhNOzVTQDtTQDhTQDhWQy9R PitMPDBKOy9MOCtUPzJRPzdXRTxTQ0RRQUNVSEZaTUpaTkVWSkFVRjxWRz1QPzBJOSpEOipHPS1H PjRDOjBGNyhDMyVPPz5aSUhaRkhTP0FRPjxRPjxPOSVIMh9EMi1XRT9XTlFWTVBQQTtOPzlHPDFJ PjNGOzBFOi9TRkRRRUNaSkRXSEFWSkFXTENUSkFFPDM1MyZAPjBaRk1hTVRiSUZaQT5OOC4+KSBB LiRYRDlbRENaQ0FUQTtOPDVQOSxTOy5JPjVTRz5VQT9RPjxPPT9QPkBHPzxGPjtKQD9VSklbSEFR PzlTQTNRQDJGPTFBOS1KPjpVSERcSkRVRD1PPzFPPzFOPS9JOStGMB1IMh9NOSxbRjlXTENVSUBU RENWRkVOPjtIOTVFOTdJPTtTQTlUQzpQPzNMOy9DOC1ANStNPT5UREVTRkZVSEhYRkRUQT9QPzBO PS5UOzdWPTlWQDxdR0NbSEZWREFOPDdHNTBGNS1JOTBJODJQPjlaRk1hTVRiSklTPDtKOTdOPDpW SEpYSk1VRj9TRD1WQDlaRDxTQTNOPS9MOy9OPTFVPixUPStUOS1TOCxVOjFaPjVWPy1VPixNPC5H NylFMiJGMyNJOixKOy1OPy1QQS9QQC5OPixNNSxPOC5WPShVPCdTPC9ROy5FNCdEMyY/LB9JNShN PDBOPTFVPTFWPjJjQDdjQDdVPTNPOC5RQDFPPi9ONR1NNBxPNyBYPyhRPCxVPy9UPTBQOi1QNSpQ NSpaPS1aPS1TPCxMNSZPNyBPNyBNOidOOyhNPC5PPjBaQzlfSD5GMSBPOihaQCtcQy1YPyhTOiM+ LRQ5KA9FLRlMMx9JNB9IMx5EKx1KMSNPOjJaRDxTPjNFMSdEMR9INSNNNSpIMSY9LCQ/LiZIMSVN NSlKOCVKOCVFOSZIPClOPzVNPjRJOyM/MRo7KBI4JQ9ALB1FMCFJMyJHMSBFPztTTUhYTEdPQz5K PitANCJELx5GMSBPOy5VQDNVPy9YQzJVQDVOOi9NNy1NNy1ROjVWPjpTQD5UQT9PQ0NPQ0NRQDtU Qz1XRjpaSDxWRjhVRTdTQzdKOy9FNyVFNyVEOzI/Ny5BMyJAMiFKOzpaSUhaQT1ONzJJOi5KOy9J NyZHNCRELyhUPjdbTU9bTU9NRDtHPjVANS1EOTA/Ny5EOzJXPj5VPDxUQTtTQDpVRD5bSUReTElR Pz1BNy5EOTBYQUBiSklcRkFUPjpMMCNGKx5JOS1WRTlQRD9OQT1OPTFNPDBNNS9QOTJUPjlWQDtT PTVRPDRMNTdPOTpHPjRKQThNQDxUR0NaRkZaRkZaST1YSDxERjo7PTFJPTlPQz5eRz1YQThWQTNV QDJRPS9QPC5KNSJOOSVPQTRYSj1VUEZTTkRWRkdWRkdOPThGNTBIODJTQTxYSDxTQzdOOypJNyZE Myg9LSJGNzNQQD1XRTxWRDtbRTJYQzBXQzVTPjFNNytNNytTPDtcRURcSUxXRUdNOzJHNS1JNShJ NShMMzNcQ0NiUFZfTlRRPDhHMi5JPTlUR0NYRkZWRERYRDlaRTpdRz9fSUFXQDdQOjBROy5TPC9W PjFVPTBdQzNbQDFiQDFhPzBbQDFYPi9VQDNNOSw+LRY9LBVROydWPytYRDVWQTNUQzNPPi9ROi5Y QDRdRTlbQzdUPTNOOC5FNSRBMiFAKBpGLR9QNStTOC1WPjRaQThlRUddPT9MNzJNODNROy9POS1R PCxdRzdfTj5hTz9dRjlVPjFRPi1TPy5UQC9WQzFiSERdRD9bQzVTOy5UPixWQC5bRTRaRDNRQTVX RztdR0FWQDtDLhZPOiBbQC9eRDJcQCxYPSlJNCM/KxpBLRhPOiRNPC1GNSdGMx9GMx9VPjFcRThR QDFFNCZHNR9OPCVPOCtIMSVBLR5DLh9EMCNKNylOOyhRPitMRDRGPi9KQz1MRD5NPiZDNB0+Jg09 JQw5KBM9LBZELx5DLh1HPDRRRj5XSEBVRj5QPzNFNClDLhtIMyBOOypUQC9TPyxRPitTPDBKNClG MihKNyxOPDpRPz1VQT9QPTtMPDBOPjJHPDRFOjJPPTtPPTtaRTpbRjtURDFQQC5GOSpFOClBMyxA Mis8MiM7MSJQPj5aR0dWQTRMOCtIOSdKOylJOi5HOCw+MSNQQzNRTEdTTUhWRTxPPjVGOC5AMilB OS9EOzFROjNQOTJPPTRPPTRYQDpaQTtRR0RKQD1BOzI+OC9OPzlXSEFRRDdKPTBJNyRINSNTQDhY Rj1TRz9NQTpHPDNGOzJIMy9MNzJPPTdVQzxUSThNQzFGODBFNy9JOTBKOjFNPDdVRD5aRkZhTU1X TUdWTEZGQz0/PDdHPDRNQTpWRjpURDhXRjpWRTlPQTJHOitKNypWQTRbTERbTERWUE5RTElTRkFT RkFKQTlAOC9KOjRXRkBdTD9WRTlMPC1JOitGLyNDLCBJNyZTPy5URDhXRztiTT5fSjxeRjlXPzJI OCpKOixNPDdUQz1cTEhVRUFKOTBEMipJNSlKNypWOTpiREVlUVFdSUlHNTBEMi1NPT5YSElcSkFW RTxbST1eTUBjTUdeSENYQThQOjBUPi5XQTFbRT1dRz9jSj5jSj5dTD9XRjpdRD9dRD9YRDlQPDE9 Kxs6KBhPOihYQzBYRzhVRDRURTJRQzBTPjNeST5iT0ZdSkFOQDNDNSlGMSBALBs8JBY8JBZELSJN NSpXQTpeSEBhSERVPTlFNCdGNShKPDVPQDpXRj1fTkVlU01eTEZUPzRMOC1WQTNdSDpXTT5YTj9j UEldSkRXQDdWPzVOPi9VRTVhST9YQThHMydVQDNYQTVQOi5BKhNONR1cRi9eSDFYRTFTPyxKNyFH Mx5FMBlPOiJMPi9JPC1GOSpFOClQQDRYSDxURTBGOCRHNCBNOiVNOidINSM+LBhDMBxELx5HMiFQ OihYQS9URDRPPzBQRz5WTURNRC9BOSVAJxE/JhA8Jg4+KA8/Kxg9KRY+NC9MQTxWRz9VRj5QPC5H MyZALhpGMx9JOStPPjBOPS5MOyxQOi5KNClDLh1NOCZMOy9OPTFUQzdPPjJJOStJOStIMjFIMjFJ ODFOPDVVQzpXRTxYSDVURDFHPS9EOixJNSlHMydFNClFNClMPztQRD9VQDJOOixKOitPPi9OPixN PStENyhNPzBHRztOTkFaSkNTRDxKOjFAMChKNypOOi1JOS1NPDBPPzNNPTFTQTVVRDhOQDNNPzJH PjRBOS9HPjVRSD9NQzFJPy5MOyxQPzBTQTtaSEFVRD1OPTdMQTFHPS1GMCVMNSpEPDlQSEVbUUdP RjxGOSxBNChDNzJGOjVHPDRRRj5cUEdhVUxaTUpUR0VNPDNIOC9FPjVHQDhVSUFUSEBURT1TRDxV PzpQOzVGPjtQSEVVTkVXUEdaT05YTk1bRjtVQDVPPjVBMSlPPTdbSEFkTEdaQT1RPzdQPjVJMiZH MCROOi1WQTRXRkBbSURhUUpeT0haSkNRQztNOStOOixQPzlXRj9cT01RRUNDNC5FNzBDOC9FOjFQ PkBVQ0VaTUpNQD5FNC5JOTJMQTxQRkBXSTxWSDtWTUNdVElfTE5XREZJPjdIPTVUQzdaSDxbSj5c TD9dSkFeTENXRzlTQzRiREdoSU1aRkRKODU6Kx04KRtJPC9TRThcSjxYRzlTQzNURDRcRkBeSENf SURfSURRRDRGOSpONCZGLR87JxM5JRFBLCNMNSxXRj1bSUBbRjtQPDE9NCE5MB09OipHRDNXRUNe TElfR0RROjdJOS1JOS1WREFeTElfTUpeTEleTENaRz5RPTJRPTJNQTpWSkNYRj1PPTRHMSVNNypJ OStIOCpAKRJONR1eQTFoSjpeSDVXQS9VPylRPCZKNx9RPSVQQDROPjJGOSpGOSpKPTBVRzpVRi9I OiRFNx9GOCBMNyFGMRxBLBtAKxpELxxHMh9UOTBdQTlPRjpORTlORTlWTUBWTDtNQzJQMiNHKhs+ KxVALRZGLR1GLR0/LyRMOy9TQzdXRztVQSxKOCNMOShNOilVQDVVQDVbPzdWOzJQPC9POy5IMh9D LRpGMiVPOy1OQDFIOyxGNS0+LiY8MiU8MiVHOSNOPylVRDhcSj5jSkBiST9PQDdJOzFKOixFNCdI MilMNSxIPDpOQT9UQC9OOypKOy1PPzFQOi5POS1IOClOPS5NSDtNSDtXRUVVQ0NKPzRBNyxHNytN PDBKOixKOixRQDJRQDJXRzhYSDlVQDNVQDNNQC1GOidIOS1TQzdXRjdVRDRUQTlUQTlUQzxcSkRU RjlRRDdPRTRNQzJJOStMOy1JRD9TTUhWUElMRj9OOixKNyk3MSM4MiRHPThTSENcU0hcU0hcUEdT Rz5JPjNBNyw/Ny1GPTNMREBTSkdWTUBQRztUQzpNPDNJPENWSE9aUEZVTEFaTU1eUVFdSkFMOjFE NClDMyhPQDpeT0hqUFBcQ0NVPztbRUBQPzBGNSdJOTNPPjlPRUFYTkphUUpeT0hUTEZIQDtKOTBM OjFUQUFXRUVPSD9KRDtDMStBMCpEMytIOC9JODpKOTtTPz9QPT1OOi1KNypMOzJVRDteTUBeTUBb UUdeVUpYQUBQOjlKOjRMOzVVPzhdRz9cTkBeUENkTkhcRkBUQC1WQy9iUFRfTlFUQEVHNDk9LSJD MidJPzFRRzlWTD1USTtJQDhNRDtVSEhXSkpdRkVbRENRQDpKOjNPPCdNOiVIMxw6JhBBLCNTPDJc RzpbRjlWRTdJOStBMyBAMh9IOy5WSDtfTE5dSUxVQTBHNCQ/MiZPQTRaQ0RdRkddREZTOjxROTJO NS9KNypTPjFWRkVURENXQDNTPC9MMRxILhlHMydMOCtFLhVONxxcRDdkTD5bRjtVQDVTPjBRPS9T PjNTPjNWPzVUPTNMOCpFMSREMipMOjFXQTFPOipMNyNMNyNKNCVDLR5AKxhDLRpFLxxHMR5ROTRb QT1URT5MPTdIOCxUQzdUTUNPSD5PPCdKOCNKNxtMOBxONCRNMyNJMilONy1QPzlXRj9PPzBNPS5N PDBQPzNTQzdRQTVXPzNTOy9RPCxPOipJMidBKyBBMiFKOylOPS5MOyxIOClAMCI7Mh84LxxAMiFK PCpXQTxeSENkTEdkTEdPQDlPQDlPPThMOjRJMi9IMS5FOTROQT1RQDFRQDFKQC9JPy5RPS9OOixI PTVHPDRIPzNPRjpMRj9IQzxKPDVGODFHOS9KPDJKOixQPzFUQzNVRDRWSDtaTD5YRj1QPjVMPC5I OStTOy5YQDNYSDpYSDpWRz9VRj5WRT5eTUZaSUZUREBNRDtJQDhKOjNKOjNORUZRSElTTENMRTxM PypEOCM+NSI8MyA+NC9PRT9aTU1fU1NYTExNQEBKPDJFNy1ANStBNyxNOzlXRUNYTUFUSD1TRThO QDNVQEVaRUlYTElWSUdXTE1dUVNdTUxMPDtDMiVFNCdKRUBWUExiUEdVRDtUQz1WRT9NPixFNyVE MyZJOStPQURdT1FjU1RiUVNVSEZJPTtHOTFMPTVUQURVQ0VRRUVJPT1FLyNGMCRHNS1JOC9FNC5G NS9OPTRKOjFGMiVGMiVINzBUQTtfT0xiUU5jU1FcTEpPPTRGNCxEMyZJOStYQz1hSkVhUUpeT0he Rj9WPjhOPThYR0FfU05XSkZQPjVHNS1ILShJLilOPjJXRztWRjhRQTNKOyxOPi9WSUVWSUVYRUNV QT9QPzNOPTFNPSlMPChEMR09KxdBMiRMPC1VRTlQQDROPTRHNy5EMyhEMyhMPz9YTExiTkxUQD5I NyBBMBpGNytOPjJXQD9XQD9TOC9NMipIMSZHMCVFMClMNy9RPzlTQDpTPStJNCM/KRBBKxJJNSlT PjFFMBlTPSVYSDVfTzxRQDhIOC9IOCpMOy1NPDNPPjVTQDpTQDpJOihDMyJBLiJGMiZQNSpQNSpR NyNQNSJKNylFMSQ8KRU9KhY/Kh9FLyRMOTdYRUNVST5JPjNDLidQOzNOR0dOR0dQQzVPQTRQOylT PStVPytUPipOOixRPS9RRDdPQTRPQTFQQzJTPjNVQDVWRTdRQDJUPSlTPChQPC9NOSxKOitGNSdB MiFHOCZKOydJOiZPOSxKNChDMB5BLx0/MCNHOCpWRD1fTUZjUUVhT0NPPjVPPjVPPjlJOTNFMiBE MR9FNSdQQDFURDVVRTdTRThQQzVUQzNKOitIPTJGOzBMPTVQQTpIRDpHQzlJOzFFNy1KOixIOCpP Oy5XQzVXSEBaSkNaTkZYTUVVRzhNPzBMPC1MPC1VRDRdTDxeTURcSkFVRj9XSEFWRkNcTEhdSkhX RUNOQT1MPztOPzlMPTdRQUBURENRQUNUREVPQTRNPzJMOSRHNCA/MiRURjddUUlhVU1RSUZIQD1K PChJOydDNyA+MhxJOTNYR0FdUUlYTUVUSTtQRjhYR0FbSURWTEhUSUZVTlBYUVRXTE1KP0BENCdF NShJQzpUTURcSkVVRD5VSUBVSUBOPixGNyU+MB1FNyNVRUZlVVZlVVZYSElUOztQODhJODJMOjRO PDpRPz1UPTxNNzVINCdNOStOPy1PQC5HNR9FMx1FNyVDNCNENCE/MB1GNzVTQ0FbTk5hVFRfTExW Q0NIOjBBMyo/MShKPDJdSU5jT1RnT1NcRUhROjVROjVRR0RaT0xiTEZWQDtNOilINSVILiNILiNT PTVbRT1VQDVQPDFRPTBUPzJXR0ZRQUBQOzNTPTVKOitJOSpKNS5IMyxDLh1ELx5AMydHOi1QPzBR QDFRPz1FMzFGMiZINChPQDpYSUNYRDlINCpFMBtDLhlJOitMPC1WQDxUPjpNOS5KNyxFNSJDMyBE MyVFNCZIOStMPC5OPTRJOTBALBlIMyBeRjxpUEZJMyBbRC9bT0ZaTkVQRDA9MR8zJhI8LhlJOStQ PzFUQTlUQTlNPS9FNSg/LCA9Kh5GKxxVOSlUPylVQCpVPipOOCRFMx1BMBpFNSdFNSdIPTRUSD9U SkBORTtHMCpELSdKPzhPRDxTQTlVRDtVQDNYRDdaQzVXQDNJQDdGPTNKPzRMQDVOQDFPQTJWPzJY QTRaQzJVPi5TPCxUPS1QPSxNOilJPC1FOClFMB1HMh9HNyhNPC1QOi1POSxPNSVONCRHNCJKOCVX Rj1hT0ZeT0dbTERTQzdURDhPP0BNPT5EMyY+LiFENyhPQTJWRz9bTERYTUVPRDxOQDFDNSdGOC5G OC5OPThQPzpPRDxOQztKOjRGNTBJNStNOS5XPjhfRj9aT0lYTkhVSEhVSEhORDNMQTFOPS9UQzRY SEdiUVBdTUlXR0RKQDtNQz1XTUdfVU9fTUpWREFQPzlRQDpOPzVKPDJRPDdUPjlTQD5VQ0BYPz9V PDxUPS1ROytMOzRbSUNaTU1YTExPRjpMQzdRPCxOOSlAMyQ+MSJJRD9YU05hUE9YSEdVSUBUSD9c SkVfTkhYTkpVSkdYTU5XTE1bTk5NQEBFOCtHOi1OQztUSEBORTlPRjpWTUBVTD9PPzFGNylAMSNK OyxeSkpoVFRfT1BTQ0RRNDVPMjNHNDJJNzRNPDdTQTxNODBIMyxFNClPPjJaSDpVRDVNOR9BLhZA LB1ELyBHMCdELSREOjlPRURYTk1dU1FaR0lRP0FJOTJBMStHODlXR0hjTlNkT1RfQ0BUODVQOTJW PjhTSkdWTkpYQztNODBPODNKMy9MNSlNNypXPDFaPjNVPTBTOy5UPjdTPTVMOzJHNy5HMSJJMyRH Mh9HMh9ALSFDLyM8LRpDMyBHOSFHOSFTOy5XPzJPPi9GNSdFMSVMOCtQQDRPPzNJOyc+MB1MMiBR OCVRPS9RPS9RPzpNOzVKMixJMStJMiZHMCRINSVKOCdJOi5IOS1OOi1POy5POThdRkVoUE9qU1FM NyVcRjNdSkVeTEZURDFENCM6KBU4JhM8KhhEMR9TOzRbQzxJPy5ANyZDMRs4JxI7JhdKNCVWRjda STpaRC9XQS1QPSFKOBxMOyxKOitNPjdRQztUR0NTRkFMNC5BKyVINy5KOTBQQDJURDVaQzdeRzta SDpVRDVGPzU/OS9JOzFMPTNNPTFPPzNVPjJaQzdYRTNUQC9XQC5QOihMNyVNOCZIOSpFNSdIMyBN OCRVPjFYQTRWQTNTPjBMPChKOydOOi1XQzVdUUZdUUZWUEBPSTpQQzJTRTRPQDpNPjhENyg9MCJD MStTQDpVT0pWUExUUE1HREBGPi0+NyZDMyhHOCxKPDVPQDpTPz1PPDpKOjFFNCxBMSlJOTBUQzxd TEVXVExPTERPRURUSUhPPTRINy5FNSpVRTlaTk9eU1RUR0NNQDxBOjRHPzpXTUddU01cSkVVRD5U PDlTOzhJPjVJPjVQPjhPPTdOPzlQQTtUQT9RPz1OPDdOPDdNQEBYTExYSElXR0hPPjJOPTFQPzFM Oy0/NCo8MSdDPj9WUVNfUVRXSUxQR0hRSElYTU5eU1RVT0hOSEFPRT9QRkBWSkNMQDlFNC5GNS9M PDtMPDtKPzhMQDlVRTlVRTlRQDRHNytEMyVQPzBdSUliTk5YSEdNPTxOOC5IMilFNSpFNSpIOS1N PTFKOjFHNy4/OzFJRTtRRj5TRz9QOydJNCFMNSRQOihQOCNMMx9JODhVQ0NbSEpcSUxXPzxQOTVH OTJDNC5FOTdTRkRfSExfSExfQz9cPzxdRT5eRj9WSUdUR0VNOSxJNSlRNyxaPjNbRDNaQzJcRTRa QzJWOS5VOC1VPTNUPDJMOShJNyZJNyZMOShMOSRINSFHMCdHMCdGMCVMNSpTPS1UPi5YPi9WPC1R Oy5MNSlIMyBMNyNNPC1KOitHOCJIOSNRPitUQC1eRDRbQDFOPTFJOS1HMitGMSpIMStHMCpKNypO Oi1OPjJNPTFTPjFTPjFXPkNiSE1kT1RlUFVNOStdSDpVSUFVSUFUQC9KOCdELRQ+KA85KxU6LBZP OjRYQz1PPytJOiZENBI4KQk5KBNDMRtVPzpfSURbRjlaRThTQy5PPytRPi1QPSxPPi9RQDFUPjla RD5QPzFHNylFMSQ/LB9HOCxKOy9XQDdeRz1aUEZRSD5JPy5HPSxNNytMNSpIOCpPPjBRQDJXRjha Rz5WRDtcRjNUPixMNyNOOSVHOCZIOSdMPSlPQCxXRj1cSkFXTT5USTtORjdIQDFRQztURT1WSkNe U0pWTjxPRzVEPTNJQzlKPzhJPjc9OCk1MCJDMTFYRkZbVVBWUExQTEFIRDpIPClDNyRHMiNPOipQ PjVTQDhOPDpMOjhFOytDOSlFNCxHNy5QREFYTElTTUZMRj9HQEBNRkZHPDNEOTBENDFWRkNhVFRe UVFQQD1KOzhHOTFOPzhRUEhVVExVSEhOQUFOODtPOTxIPTRKPzdNPjdNPjdNPjhNPjhNQDxJPTlO PDpPPTtOPDpWREFaSEFUQzxQPzFUQzRURTJNPixENypAMydGPUNWTVNeUU9XSkhMRUdNRkhVSUpe U1RWT0VMRTtMPTNOPzVHQDdAOjA/LydBMSlHNTVGNDRINy5KOTBPQDdQQThTPjBOOixMNzFUPjlU RENVRURRQDtJOTNHMydHMydHOCZFNSRIOS1OPjJNPzJJPC9KPjpOQT1WREFVQ0BURDhTQzdXRjda SDlaRjJWQy9NPTpRQT5URENOPj1OPDNJOC89NSdAOSpGPjlNRT9VRUFaSUZoT0xnTkpkU0xfTkdf TUZYRj9MOyxPPi9bSUNhT0heVUheVUhiUERdTD9dRTheRjlUPDBTOy9IOClNPC1cPDJnRjxeQTNX Oy1KNyxEMCZEMy1IODFQPjVXRTxaRz5UQTlRPDdMNzFHNCJFMiBKOitMOyxRPS9QPC5URTtbTEFh Tz9bSTpTQTVIOCxEMyZDMiVJNSlINChNPDBQPzNUPjdTPTVXQTFaRDNTQENYRkhjT01kUE5FNSRP Py1URDhWRjpPPytNPSlFMx1FMx08MRk+MxtMNy9QOzNQPSpPPClEORU5Lgw7JhRHMR5XQDdfSD5W Sj9USD1URjlRRDdPQTFNPy9KPS5JPC1TPThVPzpKQThHPjRGNyhBMiRBLx1EMR9OOi9XQzhaTUpa TUpRRTFOQS5KOCVBLx1FMRxNOSNQQDRWRjpdTENcSkFeSjlYRTNTOSZGLRtBLx1INSNOQDNQQzVc SUlaR0dUREBPPzxIQDtJQTxJPzxUSUZbT0ReU0dMSjdGRTE+OCxEPTFHPDRIPTVFOi8+Myk6NDBO SERbTkxXSkhTQTtMOzRHOis/MiRAMiFOPy1RPzlMOjNJODJINzFBOis9NSdHNytKOi5UR0NbTkla RD9TPTlNPTxNPTxFNCxEMytBODdVSkldUVVbT1NNPTxFNTRAPDJIRDpRUE1UU09PQ0BGOjhGMzFK ODVOPTRQPzdKPDVHOTJFOjJIPTVMOzVOPThHPThIPjlOPThWRT9YRztTQTVUQTlaRz5VSUBUSD9O PDpDMS9JQERXTlFcUEhTRz9MQDlNQTpQRERbTk5UT0RJRTpMOy9KOi5BMiVBMiVALydBMChEMyhD MidENyhJPC1MPi9OQDFQQDFOPi9OPDdQPjlQPzlRQDpMPDBJOi5DMiVGNShJOixIOStJPjdNQTpQ QTtOPzlMPTdRQzxXRUVXRUVWSkNXTERcTEhdTUldSkVcSURbQz5aQT1RQDhPPjVNPixGOCY8MiI/ NSVKPDVTRD1QRkBYTkhnVldnVldkVU5eT0hfT0NcTD9XSD5fUEZoVFhlUVZrWFNtWlRfU1BXSkhc Sj5aSDxVPy9RPCxKNylUPzFXRT5jUElaRD5MNzFPNzBNNC5JNC9RPDdYR0BhT0hYTElPQ0BNOzRI NzBBMidGNytGNylJOixVRDtdTENiUVBhUE9XSTpURjdVPjRPOS9KNylFMSRMOShNOilPPTdTQDpU PDVUPDVXQDRUPTFKOzxTQ0RhT0llVE5ELx5GMSBJMyhNNytNOSxPOy5JNShGMiVEMR1FMh5JMiZO NypROi1TOy5JOCFFMx1BMSNGNSdQPzdbSUBWSkFWSkFVRj5TRDxMRDRHPzBFNy1FNy1GOzNKPzhM QzlMQzlHPjREOzFDNSc/MiQ/NSZPRTRdSkVeTEZURDVNPS9IOClBMSM/KxNBLRVJOi5VRTldTkRe T0VfUURbTT9POiJJNB1OMx5KMBtNPC5XRjhfTkVUQzpMOy1NPC5HPjVIPzdOQUFXSkpbTkxcT01T ST9MQzlBMyBENSJFOCtENypFNSpFNSpGNzNVRUFTRkZVSEhROzpMNTRINTNFMjBEMytOPTRMOjhF MzFGMS1GMS1AMilBMypMNSpROy9VRj9aSkRUPDVPODFMOy1MOy1ENyhBNCZFPztXUU1XUU9QSkhJ OC9EMipAOTNORkBYTkpYTkpKQTU+NSpIMStNNS9MOzRNPDVNPS9KOy1OPS9QPzFTPTlRPDhJPTlH OzdNPDVWRT5XRTxYRj1aSD9cSkFXTUlPRUFFODw9MDRIPT5USElWSUlOQUFJPTlHOzdMPDtVRURc TUNXSD5UPipKNSJHMSZIMidIMSVJMiZEMSFEMSFGNCxOPDNRQzlWRz1VRTlURDhTPjNQPDFPPzNQ QDRQQDJOPjBMNy9POjJUPjlUPjlTRUdURkhQQD1NPTpKOzhOPjtUREVWRkdXSkZVSERVSEZXSkhY TEdbTklfSDxdRjpXRztRQTVNPy9HOipGNyhDMyVMOzJYRz5XTkReVUpkWlZiV1ReUFNXSUxcSkFe TURjU1FjU1FqU1ZuVlpvVlNtVFBbTklXSkZfTURbSD9RPTBPOy5RPDRcRj5YTURbT0ZVPztMNzJR OzFMNSxJNzRRPjxVQ0NaR0dVRj5PQDlIOy5BNChBLSlJNDBINzFUQTxdSU5hTVFjTUhbRUBRPi1T Py5aQThROjBOOChKNCVMNSlQOi1QPjVRPzdRPi1QPSxROzFQOjBKOjNUQzxhVFRfU1NBKhVBKhVA LBY/KxVJMB5PNSNHMxpHMxpGMR5HMh9GMCRMNSlTNStYOzBPPCtNOilPOy5TPjFUQTlfTUReTUdc SkVYR0FWRT9ORkVGPj1DOC89MipANDBHOzdQPzlTQTtOPzlNPjhDPDA+OCxAMydMPjFXRT9cSURW RTdQPzFJPSpEOCU+LRQ9LBNANCJRRTFiUU5kVFBhVE9bTklWRjFNPSlNOCBHMhtKOjNWRT5bT0dP RDxGPi1BOilFOytEOipOPTdWRT5UR0VcT01WSkNPRDxMNydIMyREMSFEMSFDMyBGNyNNPDBTQTVQ RD9UR0NJPC9ENypFNC9FNC9KNTFMNzJJNC9IMy5FMSdFMSdAMCJEMyVKNCtNNy1URDhYSDxUQC1R PitMPyxMPyxAPjNAPjNKRkVWUVBVUUxKR0FGMSpHMitEOjdOREBaSEFXRj9OPzVGOC5GMihFMSdA OC5IPzVPQC5RQzBVRDVTQTNPPT1JODhGPTRGPTRJOjtPP0BYRj1eTENhT0lfTkhXTUlMQT5ALy06 KSdDOTVOREBRRUNMPz1GQDpAOzRIPT5PREVbSUBWRTxYRDVQPC5OOixOOixQOS1ONytHNyhKOitK ODVOOzlXRT5fTUZaSD9RQDhOOTNNODJNPTFQQDRTPTVRPDRUPTxROzpPPDxRPj5YRUdYRUdTQz9O PjtJPTtKPjxOQT1PQz5USD1QRTpQQD1RQT5UR0dYTExWSkFYTURYSEdPPz5PPz5KOzpBNy5ANS1P PzxfT0xeWEhfWklkVlhfUVRWSUVNQDxRQT5WRkNjUEpoVU9qVlZuWlpkUUxaR0FURDhdTUBjTUdc RkBUPDVVPTdaRD9dR0NbSEFaR0BaQzVYQTRRQDJNPC5HOCxRQTVVQ0NXRUVTQDpMOjNMNSxFLyZD LCFJMidNOz1eTE5jT1RfTFBYRDlTPjNRQDFWRTVcRD9ROjVRNytUOS1aQzJbRDNUQzRUQzRRPCxR PCxROy5OOCtKOTdYRkReUFNbTU9EKRhAJhY+JQ88Iw4/Jw9FLBRELxpFMBtHMhtHMhtGMyFGMyFP NCpXPDFNPTFMPDBUPzFXQzRXSD5eT0VfUEhcTUVVSUFUSEBOSERKRUBBODI6MCs/NC1BNy9HNy5K OjFHPDFNQTdKQThIPzVMOzJIOC9PPkFWRUhaQTtaQTtTPyxMOSZQNyRILx1BMSRRQDJfTExqVlZi VVVbTk5aST1QQDROOSlOOSlOPzlRQzxXTENTRz5MQTNFOy08Mys5MChDMzBQQD1RR0FWTEZUSEBT Rz9QPzlJOTJBMBpBMBpAMh9FNyNOPTFOPTFPPTtQPjxGOzJBNy4+NS09NCxANStGOzBQOjBPOS9I MyRDLh8+LSU/LiZDLyVFMSdRRDdVRzpWRjNVRTJMQzlIPzVFOzVIPjlNQUNVSUpYU05VT0pEMyhE MyhDOTNHPThUQzxTQTtNPy9FOCg/NSg8MiU6NStFQDVOSDlXUUFaUEdUSkFJQzc9NytBOitDOyw/ NTJDOTVQRz5bUUheU1RXTE1RQDtGNTA/KR4+KB0/OCdMRDJPQDpOPzlFPzlAOzRHPDRNQTpUR0dV SEhVRURWRkVRQTVTQzdTQzNOPi9MPSdGOCJPOjVWQDxbR0dlUVFaTU1MPz9IOCpJOStNPzBQQzNX QDdaQzlXQTpTPTVNOzlRPz1TQDtVQz1PPjJNPDBNPDVPPjhNQDxNQDxQPzlRQDpTQDhVQzpVR0xX SU5TT0xYVVFYUE9NRURUQUFPPT1KOy1IOStWRERnVFRjVUdcTkBUR0NOQT1DOi4/NytJOT5VREln VFRoVVVnU1NfTExRQTNJOixUSD1cUEVkTEdcRD9XPzVfRz1eRkFcRD9dRT5bQzxdRz9fSUFTRTVM Pi9KPTBWSDtYRUVXRERTOzdONzJQNSpOMyhIMSVPOCtUQTxdSkVYRz5RQDhPQTFURjVXRzhVRTVV PjJNNytXOytdQDBiRDlhQzhaQzBXQC5WPShWPShRPTBQPC9OPTdaSEFdTkdYSUNKNCNELh1DKxdA KRY/KxZBLRhFMB9HMiFINSFHNCBJNB9MNyFQOSxUPC9PQDlOPzhPPjhRQDpXTERYTUVXUEdWT0ZU TUNQST9WSj9RRjtGOSw1KR08KRxALSBFOClGOSpOPTRTQTlRQDpOPTdRPzdNOzJOOzlTPz1XQzhX QzhYQTVTPDBRPSdMOCJFMSVPOy5dUE5nWldiVlpeU1ZbSENWRD5ROy9UPTFPPzxTQz9TTENTTENV Qz1QPjlDODA5LidDLhtMNyNOPTdTQTtQRz5PRj1NQDxHOzdJMydNNypOOSlQOytQPzdNPDNPPTdO PDVJOS1GNSpDMipEMytAMSNKOyxVPy1YQzBUPS1JMyRBKx9BKx9ALSBHMyZORztRSj5YT0NYT0NR RUBHOzdKOTtKOTtJOT5VRElcSUlaR0dKPDJENSxENSxKPDJMPTdPQDpHPzxDOzhDOyxAOSpBNTNT RkRaTUpdUE5cU0lXTkVNRDpGPTNGOSxENyo7MTBBODdMR0hYVFVcU1RRSElJPC8+MSVAKBZEKxlF OCtRRDdPPjlPPjlGOjhHOzlMOzVHNzFJPj9PREVUQEdXREpORkBORkBUSEBWSkNQPzBJOSpUPTxh SUhjT09lUVFUQz1HNzFKPDJQQThYRzthT0NeTENcSUBWRT5MOzRFPDNKQTlPRDlRRjtQQzNNPzBQ PjlPPThNOzlPPTtPPTtPPTtQPjlUQTxRREZVR0lRSk1WT1FYTVBTR0pVRD5TQTxPOS9OOC5YSk1j VVdYTEdPQz5NOzRNOzRHOi1HOi1QPT1hTU1jVVpkVltcSUxRP0FPPjhQPzlUSkBbUUdhTEBdSD1k SD9pTURhSkVeSENdSUdYRUNeSU5lUFVXRj9OPTdTRD1dTkdeTElaR0VWPjRROjBNNSxKMypJNC9Q OzVQPjxRPz1QPzNVRDhdTDxfTj5VSjpKQDBNNyVPOSdXQDRcRTlbRDhcRTlVRTJWRjNcRjVbRTRU RDRTQzNRPTJXQzhcSURbSENTOy9KMyhHMx5GMh1HLxlHLxlGMSJKNSZOOSdRPCpUOilUOilUPS1Y QTFTPDJQOjBOPzVOPzVYR0FaSENbTkxaTUpXTkRWTUNeTENaRz5KPCg6LBk/KxZGMRxFOCtFOCtQ PC5TPjBRQDhPPjVOPDVMOjNKOTJOPDVQQS9URTJTRDFOPy1DRS1DRS1NPDVQPzlbTU9kVlhjVVdd T1FaSUhXR0ZXQDRUPTFOPTdRQDpORz5UTURUSD9OQzpHQDRDPDBDNyBDNyBMOyxKOitMPTNPQDdO QztKPzhQPDFRPTJROjBWPjRTQDpPPTdOPjBMPC5GOidANCJFMSRDLyI7MyJIQC5XRzheTj5VRTdM PC5JMCJILyFDMB5INSNWRz9eT0dbUE1YTkpVRUFIOTVDOTNFOzVMOjpUQUFQREFUR0VMPC5HOCpM NSxROzFMOzRRQDpPPz5MPDtIODJEMy5IOThVRURVSkVbUEpeVE5bUEpQQTpPQDlKPDJDNCs8LSpD MzBOQ0RYTU5YT1BNREVKOitFNCZBLRpGMR5JQDdUSkBPPjlNPDdEODNEODNINzBHNS9BOjdHPzxM QEROQ0ZRRUNTRkRRSElUSkxTQTtJOTJOR0dYUVFnUVZfSk9PPDxGMzNOQENYSk1dUExiVVBjU09c TEhTQTlJOTBFOzVNQz1ORTxRSD9PRzhMRDRKPjpMPztPPTtRPz1OPj1MPDtKOzxMPD1TQENVQ0VR SUZQSEVTQ0RTQ0RVRD5TQTxGPjlIQDtaSE5fTlReR0hVPj9MOTdOOzlIOS1KOy9QP0VfTlRcVVda U1VXRUVPPT1PPjVTQTlaR0BcSUNjSj5tVEd1VVB0VE9pT0hfRj9eRUBhR0NpTVZuUVtUQzxJOTJN REVbUVNcVFNWTk1XRTxQPjVKOjFBMSlGLylMNC5HOTFKPDRVRj5hUUlnVU5iUElbRT1VPzhOOSNT PSdcPTljRD9eRz1fSD5bRjtcRzxjTkBhTD5dTDxaSDlPOjJRPDRYRkRYRkRXQTpQOzNMOSRHNCBP NyBVPCVOOixPOy1MPStMPStTPCxXQDBcQDRkSDxVRDhNPDBMOzJKOjFPQDpRQzxWSUlaTU1dUUlY TUVaTkZWSkNJQCxBOSVAMRxHOCJKOy1NPS9UPzFUPzFQPzdOPTRIOjBJOzFOOypTPy5QQzNVRzhY TDhVSDRPRzVPRzVQPjxTQD5XSUxdT1FeT0hdTkdbTklVSERWPjhROjNMOy1OPS9RPzlXRT5USkBP RjxJRDJKRTNEOCU/MyFDNB9DNB9JOixOPjBNPjRMPTNNPDVOPTdOODdPOThMQDlOQztRRDNKPS1J OSpJOSpMNSlDLSE/OClJQTJaSENiUEpcSUNUQTtKMyc+KBxDLCFQOS1XSUxdT1FcTlBYSk1OPzVD NCtHOitJPC1MOzROPTdRQDpRQDpURDhRQTVXQzVWQTRTQDhRPzdWQDxQOzdOOzlFMjBHOTJNPjhV QzxbSEFXSkhTRkRIQTlJQzo/Ois9OCk6Lyg/NC1OQ0RYTU5XR0ZQQD9NOSxMOCtMNydQOytRR0ZP RURJOzFMPTNQPjVOPDNJOi5HOCxHOS9JOzFJOjdNPTpORD5VSkVYT1NQR0pMPD1MPD1TTUpcVlRo UFRdRklNPT5JOjtORk1YUFdeVVZeVVZYSk1URkhPPTdKOTJJOjlTQ0FVRj5bTERUSD9OQzpQPzlR QDpQPzlNPDVJNzdKODhKOTdOPDpQPTtUQD5TQTtNPDVTPDtUPTxTP0RQPUFFPD1IP0BWRU1YR09U R0VNQD5JOTJOPTdKOjFKOjFKP0NUSExYU05XUU1VRj9OPzlOPjBRQTNVRDtdTENjUE5qV1V1XVxv V1ZkTEFeRjxeSERqVE9yWlhqU1FNPDBMOy9NSElaVVZeVlNYUE1TRDxRQztTPCxPOSlMNSlKNChR OzFbRDpdSk1lU1ViT09XRUVaQzlVPjRMNSZVPi5dPjxjREFiSEFlTEVdSkFhTkVkT0RfSj9jSj1Y QDNNOS5POzBXRj9YR0BcPzpcPzpVPjFQOi1UPzJXQzVVRDRUQzNRQS9QQC5VQTBWQzFeRjliSTxR RDRKPS5MMiRJMCJIOCxMOy9PPzxYSEVeTEZjUEphUE1fT0xMQTFFOytIOSdJOihMOy9PPjJQPzFT QTNHPjRDOjBFOi9JPjNPOSxXQDNVRj5dTkZdVEpXTkVXRj1VRDtVRUFWRkNVSklaT05bSklYSEdW SUdRRUNQQD9JOjlDOStBOCpGNCxOPDNPRjxMQzlIPTRGOzJDNSc+MSNALxdJOB9NPSlOPipOQDNI Oy5JOi5MPDBKODhHNDRKOTdQPjxPPjBOPS9JOixHOCpNNypHMSVEMipWRDtYTElbTkxaSENVRD5N OS4/LCJIMStUPDVXSVBbTVRcSE1VQUZMOzJKOjFFOy1IPjBNOzVOPDdUPjpQOzdQPzpcSkViUEpj UUxfTz9YSDlQPjVHNS1JNC9JNC9DNC1GODBPPjVVRDtTRkFMPztEPDlEPDlBOy8/OS07MidAOCxN PTpVRUFbRT9VPzpXPzNVPTFQPzFTQTNXSkhPQ0BHNylMOy1VPTNWPjRFPDJBOS9DOShFOypJOi5O PjJUREVWRkdUR0dPQ0NPPDpUQD5eVE5fVU9kUU9RPz1IOz1IOz1PSE1YUVZaUFFRSElQQD1MPDlK OTJMOjNIPzdPRj1eTURlVEpTTD9MRTlMQDhJPjVMOyxJOSpNODBPOjJQPzdRQDhTQTlVRDtTQDpO PDVMOjNMOjNPPDpPPDpIPTVMQDlTRUlURkpQRT1OQztRQDRQPzNOPjJOPjJOPTdYR0BeU0lbT0ZX SEBNPjdMPTVPQDlTSENcUUxnW1xoXF1vVl5nTlZaQTtWPjhaSExrWl1rWFteTE5NPjdKPDRPRU1d U1tlVVRcTEpWRDtXRTxUQzdOPTFGNCxJOC9KOTtWREZbSEhfTU1dRkVWPz5aRDxQOzNHNytUQzdc Qz5aQDxdRUBcRD9WRz9YSUFlTkRoUEZlRz1TNSxGMSpKNS5XQEFYQUNcQDRfRDhaRDNWQDBWRTdc SjxYSDpVRTdTRTVRRDRWRTdXRjhhSTxlTkBWRz9MPTVKLyBILR5ALRhEMBtNPDVRQDpYRkRjUE5n VlNlVVFbRT1OOTFKNxtPOx9UQC9YRTNYQThXQDdJQTJBOitBMidJOi5QPDFbRjteTUdlVE5hVlNa T0xVR0lNP0FQQD9TQ0FPQ0NYTExbSklYSEdWRTxUQzpOPzlHOTJBMyxBMyw/MSpDNC1GOzNGOzNI OCxEMyhBMyBENSJHMh9MNyNQQDJVRTdNPy9HOipKOCVNOidFNShAMSRJOC9RPzdTPjNVQDVMPCpJ OihHMydKNypJOTJcSkRXT0xQSEVYQDpWPjhOOCtIMiZTPjNYRDlaTk9YTU5YQz5TPTlIOCxMOy9J OzFJOzFJOzNHOTFMOTdOOzlJPj9aTk9qWFxoVlpiU0haSkBOPixENCNHMSZHMSZEMyVIOClTQTJY RzhXRTxPPTRHPjJFPDBGOSpDNSdBMyJFNyVJOjdTQz9fR0BeRj9dTUBcTD9XSTpXSTpXTT5NQzRJ NC1QOzNVPjRUPTNFOi9ANStHPS1KQDBOPTRQPzdUQURYRkhURENRQUBPRT9WTEZcVFBdVVFbUUdJ QDdFOTlKPj5URkpVR0xUREVOPj9JODFGNC5KOjRKOjRJREFQSkheVFBjWFVVT0pKRUBJPjVIPTRK Oy1MPC5TQDpTQDpVRD5XRkBYRz5aSD9UQTtPPTdOPDVJODFFNzBIOjNHOS9JOzFTP0ZXREpWRT5W RT5aRDxbRT1aRThYRDdaRjRhTTtcUEhbT0daSkROPzlOPDVRPzlWTEpcUVBnV2FpWmNtU1ViSEpP PjhRQDpbSU1nVVhkVFBYSEVTQENUQURcSFFkUFpkUE5cSEZURTJVRjNTQzdOPjJFNzBHOTJOPj1R QUBaR0FbSENWRD5TQDtUQTlPPTRMOCtVQDNbQzlYQDdXQTxVPzpQPzdXRj1iTk5hTU1hQDVPMCZD LCFJMidUPjpRPDhYQTFdRjVbRjtcRzxbST1eTUBdSkFaRz5YRDVXQzRYRDldSD1hT0NjUUVdTEVU QzxOOChFLyA9LiBAMSNHNS9NOzRVQT9hTUpjVlFcT0pcSDdPPCtOOSVVPytbRDphST9bR0VWQ0BP QDdKPDJGNShIOCpRQDJaSDpkUVFnVFRjUFNbSEpQRT1FOjJFNC9HNzFGOC5RQzlaRkZcSEhYQDpW PjhTPjNJNStBLCNFLyY8LCE9LSJBLCBFLyNJMydIMiY9MR89MR9EMixHNS9VRD1bSUNQQDFIOSpF MB9HMiFFMx1HNR9JOCFVQytYQy5aRC9TPjBMOCpDNSlHOi1PPjlaSENWTUNNRDpTPDJROzFVQDNY RDdWRz1URTtcUU5YTkpUPjdJNC1JODFMOjNOPzVKPDJHOi1HOi1OOTNJNC9FOzpUSUhkVl1nWF9i VVBbTklTPypMOSRMOCJINB9FNSpMPDBcTUNfUEZaTkVTRz5JPSpFOSZHMyZHMyZNNydNNydJOzFU RTtdUE5dUE5WUExWUExaTUhdUExWTEhMQT5PNzBXPjhWQDlTPTVNPDdJOTNTQTlVRDtRPzdRPzdP RDtTRz5RRj5QRT1RRUVXSkpcVFNYUE9aST1MPDBNOzRMOjNJQEFKQUNPPjVNPDNOPS9MOy1NOjhO OzlVQ0VbSEpfT1BjU1RfVExUSEBOPDNPPTRTPThTPThRRj1QRTxUR0NTRkFTRkFUR0NWRT5PPjhI NzFHNTBBMy1IOjNJOTJMOzRWQUZXQ0ddRkVfSEddSkFdSkFfSUReSENcSUBdSkFhT0ldTEZfTElV QT9PPjhQPzlUR0VcT01oU1poU1piSEVbQT5ROTlXPj5aTlFhVVhiUElbSUNWQ0VcSEpfSk9iTVFh TkhaR0FUPzJXQzVTOzFMNCs/MSpGODBVPztYQz5cRD1aQTtUQzpUQzpXQDRROy9XPS5cQTJcQTJd QzNYQDpYQDpXRT9fTUdiUEdWRTxVOStKLyJDLCFFLiNMNTdQOjtVRTJbSjhdSThdSTheTENfTURe SEBcRj5aSDxcSj5aRD5eSENfTkdiUElhUERYSDxNPC1BMSM9LSJGNSpANStDOC1NOzRVQzxeTkpf T0xbTTxQQzJPRTdPRTdWRDtbSD9eRkFaQT1TQzdQQDRKOi5JOS1NQD5WSUdeUVFcT09cTEpXR0ZX QDRROy9HOipENydHNylRQDJYR0BdTEVURDhQQDRVPTBKMydMMCNGKx4/KxZELxpKMR9MMiBOOChO OChFNSc/MCJAMCVPPjJVSUBcUEdVSjpKQDBIMyJJNCNINB9OOiRTRC9aSjVfTkFfTkFORDVDOStD MyhDMyhKPzhYTUVYSDxURDhRPCxWQDBbSEFhTkdeU0pcUEhfVExcUEhbRzVRPi1QPC9POy5MQDhM QDhPOzBNOS5MOjNGNC5INTxWQ0leUFdfUVhhU1VeUFNdRz9XQTpPQTJGOSpEOzJKQTliVldfVFVX UU1MRkFFPSs9NSREMCZHMylJNyZNOilNPjdWRz9iUVNfT1BXTk9XTk9YSEddTUxaTkVQRTxWQTdb RjtYRkROPDpNODNPOjVUSD9TRz5UQzpVRDtORz5QSUBORkBPR0FVSEZVSEZcSUdbSEZaSENOPThN Ny1NNy1MPDlNPTpPPzNRQTVRQTNQQDJNOz1NOz1VO0FbQEdfTFNkUFdnUEpYQz1KPDJMPTNQOjlV Pj1VRUFTQz9OQzpNQTlQSkhTTUpTQDpNOzRHMitFMClHMi1MNzFMOjNMOjNWQ0lVQUhWRERbSEhd TUleTkpeTUdcSkVeTElfTUpdTU5YSEldTUxXR0ZUQD5QPTtWQ0deSk9iUFRhT1NcRkFUPjpUOzdb QT1eSlFjT1ZkUE5fTElYQz5aRD9bSEpeTE5kTEhbQz9TQTVUQzdUPjdMNy9GODBNPjdbQz9YQD1b Qz9cREBXRT5UQTtXPzlVPTdfRDhiRjpfSj1kT0FeSERhSkZkTkhlT0lnUUZdSD1MOCJHMx5JLyRO MyhPNzNUOzhRQy5VRjFcRjNhSjhcSkRcSkRYRz5VRDtTRz5WSkFbSD9eTENfUEZhUUddUUhVSUBT Qy5KOydFLyNDLSE/LydAMChFNCdPPjBQREFbTkxaTkVXTENVSUFVSUFdRT5dRT5dRjxaQzlbRT1Y QztNOStKNylMPD1OPj9TSEVTSEVXSD5VRjxTQyxOPihMPC5MPC5WOy9dQTVeSENiTEZaST1WRjpR Oy5NNypKMh5DKxdJNB1RPCRPPClRPitUPzJWQTRPPi9MOyxFMiBUQC1XTENcUEdVSUFMQDlJPSpG OidIOiZMPSlcSkFjUUhlTk1lTk1VRDtPPjVJOitENCZFQDdQTEFcSjtYRzhfSD5nT0VhUFFfT1Bf U1BcT01hVUxdUUhYTj9TSDpRRDRRRDRWPjhXPzlYQThUPTNNOS5JNStKOTlTQEBRR0RVSkdeUU9h VFFlTk1hSUhRTEdEPjo/NzhIP0BfVldaUFFTSklTSklMPi89MCJAMSBBMiFGNSpMOy9QPjxeTEli VVNdUE5YU05TTUhYUE9eVlVeVE5RR0FYRkZeTExRSElEOzxDMzBKOzhXR0hWRkdYR0FaSENPSUdP SUdTRkFUR0NXR0RXR0RMRkFNR0NOQ0RHPD1DMipDMipINzBMOjNTPTVbRT1UQzxUQzxNRDpEOzFI OTpNPT5YRUliTlNcTUZPQDpHNS1BMChINzFOPDdNPjhKPDVKPDRRQztORENPRURGPCw8MiNELSFF LiJKNCVQOipPPThNOzVOPj9PP0BOQENTRUdbR0dcSEhhTU9iTlBiUVNfT1BYRUlXREhPR0RQSEVT Qz9NPTpORUhYT1NeUFVcTlNTRz9OQztWQDtXQTxaSE5fTlReSk9fTFBcSUdXRUNTRUlaTFBfSUFY QztYQztYQztURjlKPTBNOzROPDVcRD9dRUBbRENcRURWPT1TOjpVPTdWPjhVRjxcTUNpU05rVVBu UE5rTkxoTk5qUFBoU0deST5QPCZINB9VOC1fQTddRT5TOzRQOylWQC5bRzNiTjpfTkdcSkRbRT9X QTxTRDxTRDxWRz9cTUVcT0pdUExbSEZWREFfQTdhQzhYQTFNNydBLhZDLxZPOSdROylQPjhbSEFd SkhbSEZXR0ZaSUhdSkVfTUdeTUZXRj9QPjhQPjhOOCtQOi1PPTdOPDVRPzlUQTtYSDpVRTdTQzNR QTJOPjJQQDRXQDdbRDpdSkRfTUZfVEpbT0ZKQzFJQTBKOitBMSNNPipWRzJcTDlbSjheTURaSD9V RTdURDVPPCtTPy5YRztfTkFWRT9MOzVGOCZFNyVIOCxVRDhcUU5iV1RkVFBiUU5YRz5NPDNEMR9D MB5AOC9ORTxbSUBeTURfVlpiWFxhVVhYTVBYSElYSElYUEpXT0lcT0pXSkZdRz9kTkZeUENfUURb TERXSEBbRT1RPDRQPjVRPzdORz5TTENbUUdiWE5tXF1kVFVWT09GPz9BOjlNRUReUU9bTkxWSUVX SkZMPjFDNSk+MBtAMh1GNShRQDJWRUhfTlFbU01XT0lWTkpWTkpcU1RfVldeVlVWTk1eSk9hTVFW TEhKQD1GMzhRPkNRRUNQREFRR0FRR0FPR0ZNRURMREBNRUFQRkBRR0FKRUBJRD9KPjpGOjVIOC9H Ny5OPjJURDhaR0BiT0hcTEpRQUBIOy5GOSxIPzVMQzlWQ0dcSE1KQz9DOzhFMys8KyNBMidGNytM Oy9OPTFOOzlRPjxKOTc/LixDLhs9KRZFLyBJMyRGNyhKOyxOPDdNOzVOPTdOPTdNQz1ORD5YRUNb R0VeSVBlUFdjTlVjTlVXRUVWRERTRkFWSUVVQ0BTQD5TR0hXTE1YSk9YSk9bQTtWPTdRPzlTQDpW Q0lhTVReTVViUFhYSElUREVVREdWRUheRj9aQTtcSUNcSUNYRj1OPDNNNC5QODFXRUNaR0VWRD1R PzlNOjhINTNNNTJROjdWQUZjTlNqU1ZrVFdrU1hkTFFbSEhjUFBiUERWRTlTPjBdSDprTkhvUUxh TEBPOzBQPSxXRDJaSDpiUEFkU0lfTkVcRj5YQztTQTJPPi9URTtYST9bTkleUU1aTUhTRkFYRDdX QzVUQzxQPzlNOR9QPCJQOytTPS1WQDtcRkBUR0NUR0NVRURbSklaTUpcT01bSEpXRUdQPzFIOCpM NydNOChQOTRTOzdQPzdTQTlURjdURjdWSkFUSD9RQDhQPzdURDhXRzthT0hkU0xfU05cT0pUSEBO QztFPDBFPDBMRTtQST9cTUNbTEFiUUViUUVdTz5cTj1XRjdRQDFfR0NjSkZUQzpJOTBFLyRGMCVK Oy9aST1bVFRiW1teVVZbUVNVSEZFOTdHMx5FMRw6LyhIPTVWSEpdT1FbWl5VVFhcTEpWRkVKRDpN RjxaSkNcTUVcT0pYTEdfSUVoUU1eVE5fVU9eTExXRUVRQzlNPjRVPzhTPTVKQz1UTEZfT0xpWFVl X1thW1ZWU09EQD0+OC5HQDdUTEpTSklYRkBaR0FOPixKOylFNyFFNyFENS5RQztXTlFXTlFYUE9W Tk1XUFBUTU1dUVNeU1ReU1ZcUFRfU1BbTkxRSkFFPjVIOThRQUBRSD9PRj1TRkFTRkFNRUFKQz9N Q0FRR0ZUSEBUSEBQQThPQDdOPTRMOzJKPDJJOzFUR0NbTkldTUxfT05XSEBMPTVFNy1GOC5JPjdN QTpQRT1OQztMQDVEOS5AMiE9Lx48LyBBNCVJOitJOitNOzJOPDNJNCVDLh9EMBtINB9HOSFMPSVE PypBPShKPDRMPTVTPThXQTxVRj5RQztbSEZcSUddSU5dSU5hSFBjSlNbSEFXRT5VQ0BUQT9TPz1U QD5XRUNbSEZbSEZaR0VYQztWQDlUQzpUQzpUQ0ZaSExaTFBcTlNXR0hVRUZYQUNaQ0RcRzxcRzxf TEliTkxYRj1INy5BMypKPDJUR0VbTkxbRT9TPThIOy5GOSxGNSpOPTFXRkBiUEpqVlRqVlRlTlFd RkldSk1hTlBdSkRXRT5bSENkUUxrUFNlSk1dRjVUPS1UPzJXQzVXRj1hT0ZnU1NkUFBaSENTQTxR QDJPPjBTRDpYST9bSUNhT0heTk9WRkdQQTpRQztQQD1UREBYQzJVPy9UPjdUPjdUQD5VQT9PRUFR R0RaQ0ZfSExeSk1iTlBdSkFYRj1QPzNKOi5KOitIOClNPC1TQTJQQTpRQztVRjxbTEFdTEVeTUZa SENUQz1RQzlVRjxdTEVjUUpfU05fU05bTEVTRD1HPSxMQTBRSD5USkBYTUFaTkNiUERhT0NdTkRa SkBTQzNRQTJeTUBdTD9aR0BKOTJGLyRHMCVGODBaSkNbVFZfWFtaU1NXUFBQSENAOTNHMiFBLRw3 LCVIPTVVSkVeVE5WV1BRU0xVRD1RQDpGQzBJRjNRQzxWR0BXSkhVSEZhTVFkUFVfUVRkVlheUU9V SEZNPDVOPTdRPz1PPTtIRDlOST5eTk9rW1xkX2NeWl1VT0pBPDhDNCNMPStUSD9VSUBaSD9aSD9T QzRQQDJPQTRKPTBGOjVQRD9VT01VT01VTUxTSklUTEpRSUhUTEpUTEpYTVBXTE9aUEdVTENVRTlM PDBMQDlTRz9XTENXTENRRj5RRj5QRkBORD5QQD9WRkVdSkhfTUpTRThPQTROPDxMOjpMPDtURENb TU9eUFNiUVBaSUhOPTFMOy9MOjFNOzJQOzNQOzNKPzdHPDNBNCVAMyQ/MB0/MB1DMidHNytKOy1N PS9TPDJROzFPOihPOihPOSxPOSxNPC5RQDJKQzNKQzNPQDpOPzlVPztWQDxUQz1WRT9cSkVdTEZk TkljTUhhTU9hTU9WSUlUR0dWRkNTQz9PPjJNPDBWQ0NbR0deRkNdRUFWRD1aR0BXRztTQzdPQDlT RDxYRkZbSEhfSUVaRD9UQTxRPzpWRTxhT0ZtVlFqVE9aRDxIMyxHPDRMQDlYTExdUFBhSERTOzdJ NyJMOSRKNSRQOylYRj9hTkdqVE5oUUxcSURTQDtaSENdTEZbSEhaR0deTEVjUEllTkReRz1YQTFX QDBTQTJRQDFTSEdcUVBfU1BeUU9UREBKOzhPPjBRQDJPPjJQPzNXREFjT01eU0pTRz9PQ0BMPz1Q QD9VRUReTkFdTUBbSkdVRUFTQTtNPDVPPzFURDVaQT1fR0NhTU1hTU1iUElaSEFOQzpPRDtQQDFO Pi9URDVVRTdXRj9bSUNdUUhfVEpfVExfVExbSUNTQTtPRDlXTEBaTkNiVkpeU0pfVExYSUFPQDlI PylHPihQQThURTtXTENXTENhUEFeTj9aSENYR0FRQDhWRTxbTkleUU1dTD9RQDROOi1EMCQ/OS9R SkBbVVBdV1NYTElWSUdMRTtDPDJEMSE8Kho0KxtEOilUSUZeVFBVSklRR0ZTQDtRPzpOQzhQRTpO QzpRRj1VSUFUSEBXSUxcTlBaTUpiVVNdU09USUZUQzpRQDhPPzFNPS9FPjJHQDRXTE1fVFVnXGRe VFxRTUFDPjNQPCZXQyxXRzldTT5bTEFaSkBURDhRQTVRQDRPPjJQPzlTQTtTSUBXTkVTSklRSUhR RUBUR0NRRj5PRDxPRURVSklYTUVVSUFRPzdRPzdJRD9UTkldUExcT0pPRDxOQztQP0NRQERUREVY SElYUE1YUE1XTUlMQT5OOzlPPDpKPjpaTUhiVVVhVFRbSU1KOj1JNCVJNCVPPCtQPSxVPTBUPC9J OTBHNy4+LyI/MCNBLBtGMB9JNShOOixMOyxQPzBYOzNaPDRWOS9aPDJTPDBUPTFTQDtUQTxQQThQ QThRQzxQQTtRQztPQDlQQD9VRURXSkpbTk5kTkZjTUVlTE5lTE5WTEpUSUhVRj9PQDpKOTNNOzVX RUVaR0dbQz5YQDxYRj1bSD9bSURVRD5TQTxYR0FbSklYSEddRz9YQztPPDpUQD5bTklnWlVoVU5k UUpdRjpPOS1RQDRXRjpcUUxfVU9cTD9TQzdOOSdQOylOOCRNNyNUQD5jT01kUU9cSUdXQEFTPD1Y QztXQTpRQDhVRDtYST9dTkRhTkhaR0FhST9qU0hRQDRNPDBNSD5RTUNYUEpTSkVQPjhHNS9KNChP OSxKPTBHOi1QPjxaR0VYTUVPRDxOOCxIMidINzFPPThXTkVeVUxYT1BUSkxVRzpNPzJPPjJRQDRT Qz9UREBWSkFYTURdU0BYTjxNSDtNSDtRRDRRRDRYST9dTkRiSEhhR0ddUExfU05dVlZbVFRaTk9U SElXRj9hT0hcUEViVkpdTkRXSD5WPjhTOzREOilGPCtIQTlPSD9VRD5XRkBaT0BcUUNcSUdbSEZY QUNcRUZaVFFeWFZfVUZRRzlPQTJHOitBPi5PTDtcVUxbVEpXRj1QPzdKOy1ENCdEMR87KRc4LxxE OydRRkdfVFVVSEZOQT9NPDNOPTRVPj1WPz5NRDtPRj1RRjtPRDlJRD1QSkRYUE1hWFVbU1FTSklO QT9OQT9VRDtRQDhMQTxNQz1XSU5eUFVkXWJdVltTRzxMQDVdRzdkTj1cVERfV0deTUZaSEFQRTxO QzpQSDlRSTpURT1QQTpUR0NYTEdTTENORz5NPjdQQTpPPzNPPzNQRkNUSUZWSkFVSUBRRjtPRDlH RkBUU01dU09YTkpRQDpKOjNNPT5RQUNNQz9XTUlbTkxcT01aSUZKOzhGNSpJOS1KP0NfVFdiWFxb UVVRQDpDMixINChJNSlTQzROPjBRPi1NOilFMCFALB1FLRlHLxtELhtJMyBKOylOPixQPC5TPjBX QDRYQTVUPTFVPjJWPzNVPjJTQDpUQTtOQztMQDlPPjhQPzlOQT1OQT1TQz9WRkNVSUpXTE1iUEph T0ldUE5eUU9bTk5aTU1YRkZRPz9OPzlNPjhdREZeRUdaRD5XQTxcSEZeSkhWRkNUREBaRkRcSEZW TEpUSUhYR0FTQTxONThXPkBdUE5kV1VnT05jTEpaRTpRPTJRQzxYSUNiVkpjV0xcTD9YSDxNOCRE LxxDMyJGNyVORD5dU01hT0hcSkRUPjdOOTFTOy9WPjJOPTRRQDhYRj1cSUBaR0VYRkRlUVRtWFtO PDVINzBNPDBVRDhXTENXTENaQT1PODNJMydHMSVGOidIPClGPTFUSj5USD9OQzpQNyhMMiRGMCFM NSZXRUNeTElXTE1WSkxQRjRIPi1HNy5IOC9KOTNRPzpURDRWRjdVTjhUTTdPSThNRzVURjlYSj1a TUpbTkxfTU9aR0leTkpjU09aV1ZYVlVYT1BRSElWTEheVFBdVlZdVlZXTEBJPjNNPC5JOStFOClH OitOPzVQQThUQzpaSD9XTERbT0deTEldSkhVQUhYRUxcU1RiWFpbV09NSUFQQDJMPC5IQThTTEFa UEdXTkVKRC5BOyZIOSVHOCRFNBhAMBU9LiNJOi5RRUVWSUlTRDpKPDJHPS9HPS9QQTtPQDpMQzdP RjpQQThNPjRFPThPR0FYUVFfWFhcVlRTTUpTQ0FXR0ZTRz5OQzpQRz5KQTlMRkRWUE5iWlhcVFNY SDxcTD9eTUZqWFFfVVFdU09iU0xbTEVXRj1XRj1YT0NbUUVUTUBMRTlTSENWTEZRSD9ORTxNPDdN PDdHPjJJQDRQRkNTSEVaSENeTUdbTEVRQzxOST9XU0hYTUVVSUFNQTlDOC9NPTpTQz9MREBPR0RT SkdWTkpdTENUQzpRPTJQPDFXQ05iTVhhVFRVSEhOOSdGMSBIOStOPjBVRDtRQDhOPi9IOSpELyA/ KxxBLRhELxpBMBZKOR5OPS5TQTJUPjdWQDlXRztaST1WRDtTQDhXQzVVQDNYRj1TQDhJPjNFOi9D ODBGOzNMQDlNQTpRQzxVRj9RRUBYTEdeUU9fU1BfU05eUU1bT1BbT1BcSUlVQ0NUQTxVQz1iSEVj SUZhSEVfR0ReSkpdSUlWRD5aR0FfTkhbSURWREFVQ0BRQDtOPThFMjRTP0FeSlFlUVhkUE5dSUdR QDRPPjJWQ0NcSEhlWlBjV05cSkFTQTlIMh9FLxxJMydQOi1XRT5fTUZbSj5YSDxJPy5EOilOOSlR PCxVPzhaRDxbQz5bQz5XREZcSEpiVFtfUVhGODFDNC5IOCpNPC5QRjhQRjhbQUFWPT1KOi5GNSpD NSZHOipNPjRQQThTQzdWRjpTPjFMOCtMMiJPNSVTPz1XREFWTURTSUBaRjROOypDOiZAOCRROytU PS1VPy9WQDBVSjlVSjlYST9XSD5URjlURjlWTEZbUEpeTkpcTEhcT0pbTklWVU9aWFNaUFRQR0pR RkdYTU5aVVRVUE9TQzRJOixENB9DMx5IMiZIMiZPOy1WQTNVPzhWQDlXREheSk9aUFFXTk9NQEBO QUFWTVBcU1ZWTkpKQz9POzBMOC1MRD5TSkVfUElYSUNMRS9DPCdQPSpWQy9PPylOPihIOjJNPjdX RTxYRj1TQzNJOitEPCtHPy5PRTdTSDpMRDJKQzFKQTlIPzdGPj1QSEdWVFdcWl1cTlBWSEpPR0ZR SUhGPzdJQzpMPTVJOzNJRD9TTUhkWlZdU09bT0dkWFBjWltnXV5hVVhbT1NfT05dTUxfUEheT0dc U0hbUUdbTEVYSUNUR0VXSkhVSUFMQDlMQDlJPjdHPjRJQDdPQ0BVSEZdSkhoVVNVUEVKRjtWRz1W Rz1VRj5RQztGOzJEOTBKOTdOPDpMQT5NQz9XTUdfVU9dUUZYTUFRQDpQPzlXSVBdT1ZcSkRQPzlH Mh1NOCJPPjVbSUBXTERPRDxJOC9EMio7Kx03Jxk+JxRELBhJMypXQDdYRj1WRDtUPjpXQT1eTUZf TkddTD9bST1dRT5cRD1dRT5WPjhQOylNOCZIOClHNyhHPDRKPzhQPzlRQDpRQztYSUFWSUdYTElf U05hVE9dVFVbUVNdSU5XREhVRj9QQTtYRkZeTExlTk9jTE1bSEhUQUFVQ0BbSEZdSkVYRkBTQDhU QTlRQTVMPDBHMDVYQEZnUVhqVVxhTkdcSUNUPzRRPTJVQ0NeTExiU0piU0pdTD9TQTVOOypQPSxR PTJVQDVfRkNiSEVaSENcSkVTQTVNPDBNQC1OQS5XSEBeT0dpVEZjTkBdQ0VcQURbSU9dTFFTPyxN OidPOCtTOy5RPzlVQzxVRDtWRTxPPjhKOjNGOzBGOzBOQDNKPTBTQTlVRDtQPzNNPDBMNydPOipQ PjhVQzxaRkRWQ0BTQTlKOjFGOSxFOCtOOypUQC9XRDJYRTNURjVTRTRXRkBaSENaSkBVRjxVSUBa TkVcUUxdU01dVEpbUUhcVk9eWFFcU1RPRkdRQUBWRkVXSkZbTklVQTBPPCtINB1FMRpJMyBHMR5J OS1UQzdWQTdYRDlWRT9fTkhfV1RaUU5QQTtKPDVXSUxdT1FYSEVQQD1PPjJNPDBOREBRR0RcTE1a SUpURjlTRThVRzpYSj1bSj5bSj5UQTlQPjVURDhQQDROPjJMPDBBOCpIPjBOQzhXTEBXRjhRQDJT Rz9MQDlJQERPRklWUE5cVlRfT0xdTUlWUExaVE9QRz5IPzdIOjJIOjJMRUdRSk1eVVZbUVNbU1Fk XFtqXmJnW15eVFNYTk1eUVFiVVViVVNjVlRdU01bUEpeTkpbSkdUSUZWTEhRSkBNRjxRQzxOPzlM PDlKOzhQRz1aUEZhVlBlW1VWT0ZMRTxRQTVTQzdURT5QQTtKPDRKPDRFNy1IOjBKOzpJOjlUSUhf VVRcUVBVSklNQz1MQTxQRkVWTEpYSj1RRDdOQDBRRDNVSEZbTkxTTENJQzpJNyZALh49KBdBLBtB LRVFMBdNNzVaQ0FbR0VUQD5TPTlYQz5eTE5iT1FjTkBhTD5YRz5XRj1XRj9TQTtXOjBUNy1JOStA MCNAMilJOzFQPjVVQzpXRj1XRj1VRUZcTE1jVlRnWldiVk5eU0pbUE9TSEdUQT9RPz1OQ0ZXTE9j U1RiUVNhSkZdR0NdSUxeSk1dR0FXQTxTQzdQQDRVQDVPOzBINzlXRUdoT1VoT1VWRz9PQDlPPjlO PThNPjhXSEFcTUNbTEFdRz9RPDRKOi5OPTFPPjhWRT5iSkxlTk9dSUxdSUxVPzhNODBMQzlWTUNl VkxqW1BpVk1jUEdaR0FUQTxcSEheSkpYRTFbRzNYQTVXQDRROjBQOS9QPzFTQTNQPzBQPzBNQzRJ PzFJPy5FOypKPTBOQDNPPzNMPDBPPCdPPCdPQTFTRTRYRkBWRD5QPzlJOTJNNy1OOC5MOjFWRDth T0ZcSkFRRDRTRTVUREBXR0RVTkVUTURWRz9YSUFXTUdbUEpaUUxcVE5dVU9eVlBeV05RSkFUQzpU QzpcSkFfTkVcSjtWRTVPQC5KPCpMOiFJOB9MPTNURTtWRDtVQzpRQzlcTUNeVlNcVFBRRDdHOi1Q RERXSkpbR0VaRkRTRDpQQThOQztOQztUR0NXSkZUSEBWSkNbTEFdTkRXTERbT0dXSD5MPTNRQzBW RzRIQTVDPDBDOStJPzFTRz9aTkZcTUVURT1WRkNQQD1ORD5USURXTERaTkZbTkxeUU9XUFBbVFRV SUFJPjdJOzNHOTFNPD9dTE9jV1tjV1teWl1kX2NoXmRiWF5cT0pUR0NYTU5hVVZhU1dhU1deU0lb T0ZbUE9YTk1TSklTSklPRDxPRDxMQzdGPTFIOTpHODlNSj5YVkliXFVhW1RaSD9OPTRPPTRTQDhT RDpQQThPQDdNPjRKPDJJOzFEOixDOStMPDlaSUZUTU9PSEpKQz1FPThMPDlPPzxVRD1UQzxRRjtb T0RWUExUTklWRz9QQTpQOipDLR47JRk8JhpJNSBUPylWRTxYRz5cSUBUQTlQQDRTQzddTFFkU1hf U05bTklPR0FPR0FaSkRaSkRbRDdUPTBMPCpENCNFOy1MQTNYQztcRj5YSUFVRj5RR0ZVSkliUU5l VVFnVlNjU09dU01VSkVTRkFTRkFVR0lbTU9hVFRiVVVlUEVlUEVdTkdbTEVaRz5RPzdUPzRXQzha QzlTPDJQNzdaPz9hR0NfRkFaRDxVPzhQPT1KODhFNy1KPDJYSj1YSj1bQzxYQDpQOi1VPjFUQT9a R0VfTkdiUElfTUZhTkdWQTNPOy1QRD9bTklqU1RrVFVlTExfRkZYRUVXREReTEZfTUdhT0ZhT0ZX UU9XUU9VQz1MOjRKOixKOixTPjNWQTdRRzlUSTtMRTlGPzNHOipJPCxPPzFPPzFPQTFNPy9RRDNW SDhVSERUR0NRQztKPDRKNSJKNSJMOTlWQ0NfT0xlVVFdTEVaSEFTQDtUQTxUSURUSURVSUFTRz9K SUFOTUVVTE1WTU5eTk1fT05dUUhVSUBTRDxRQztXRjpfTkFiUEBhTz9cTUNXSD5URjlNPzJPRTNY TjxfUEZVRjxURTtXSD5dU01fVU9XSTxENypJOzRVRj9fTj9jUUNeU0laTkVRRj5IPTVHPzBTSjtd UUhbT0ZiU0piU0phUUpeT0hXTzxRSTdTRzxbT0RQTTxFQTFEOS5IPTJXT0lcVE5bTkxUR0VURT1M PTVPPzxTQz9RR0RYTkpYU1BeWFZdWFdXU1FVRD1MOzRKQTVEOy9KPzdTRz5WTU5cU1RdU1tlW2Nn VVthT1VTQTtQPzlWSE9dT1ZcU1RbUVNaUEdWTURQSkhWUE5WSkNQRT1ORD5NQz1JQzpBOzI/NC1B Ny9NSUZdWlZnX19dVlZWRT5PPjhQRT1VSUFRSD5NRDpOQzpOQzpRPTBKNypBNR9GOiNKOjRUQz1R RUVRRUVKQDtANzFINzBOPDVWRkNbSkdfT0xnVlNfU1BXSkhaRThWQTRRPSdKNyFBMh1GNyFUQzdY RztYSUFURT1VRjxURTtPPjBWRTdeTVBoVlpiUU5iUU5USUZTSEVXSkhYTElbST1PPjJJOSpKOitU RT1VRj5WRDtbSD9XSEFWR0BURkhWSEpcTlBdT1FjU1RkVFVfTU1WRERWRz9aSkNbSEpeTE5dUFBh VFRjUUpeTUZdTUlYSEVYSDxVRTldRjxjTEFfSDVbRDFWQTdUPzRbRT9dR0FfRkFYPztNMipNMipM OjFQPjVWRTdYRzlXQDRaQzddQDtdQDteRkNkTEhjT09lUVFjTkBhTD5VRTBTQy5bSEZcSUdhTUpi TkxdSUdXREFWPz5cRURdSkFiT0ZhTk5eTExUVFRUVFRYRkRRPz1KOixOPS9RPi1WQzFbSUBbSUBa ST1OPjJJOylHOSdMOy1TQTNWRjhVRTdXRj1XRj1bSURaSENXRzlQQDJPOyFOOiBNODBUPjdbTkll WFRqVE9kTklXRT9UQTxVQ0BTQD5PQURPQURIQ0BJREFRRkdUSEleSERcRkFYR0BWRT5RQztTRDxU RDhXRzteTUZiUElhVUxdUUhdTkZWRz9VSjxhVkdnVklcTD9RSDxTST1hTkdkUUpdSDpKNylJNDBb RUBdUExkV1NjXF5cVVdTSEdDOThAOShPRzVkU0llVEpnVlNqWlZoVFRiTk5bTklcT0pbT1BfVFVd V0dPSTpHPzBNRTVWTU5cU1RYSUNQQTtNOzRQPjhNPjROPzVKP0BUSEldVlhfWFteVlNXT0xRQTNK Oy1FPS5FPS5GPjlIQDtPR0ZXT05bUE9jWFddU09TSEVIPzVJQDdbSU1nVVhdUVNbT1BUR0dUR0dW SUlbTk5XSD5OPzVRQTVPPzNGQTc+Oi9BNChIOy5RSkpeV1dnX2JbVFZTRThQQzVaTk9bT1BWREFT QD5VPzhWQDlVQS5JNyRJNCFPOiZQPDFUPzRRQDhQPzdNPjdJOzNPODRROjdaSExjUVVkUFVjT1Re SERaRD9WRD1VQzxQQCxNPSlJPy5NQzFbSURbSURaSkRXSEFVST5TRzxOPjJXRztkU1ZpV1trVFNl Tk1RQztRQztUSUhRR0ZTQTtNPDVNOzJRPzdaRkRXREFURDhWRjpUR0NUR0NWRUhcSk5eSk9iTlNk U1ZlVFdeT0hURT5YRkBcSURbSEheTExiUFRiUFRhVU1cUEhjUElfTUZYRj9dSkRpUEZnTkRdTTpY SDVXRj1YRz5aSENcSkVdSUdaRkROOixJNShTOzRWPjhYQTVbRDhXQDRcRTllRz9kRj5jTUVnUEhp VVdlUVRjTUdfSURYQztXQTpXRUVcSUleTUdaSENcQz5YPztVPztcRkFlTExpT09WTEZaT0leUFNd T1FcSUNXRT5VPy9RPCxNPC1VRDReTUdeTUdeTUZTQTtJPCxGOSlMPC1URDRVRjxVRjxXSD5VRjxW SkNYTUVYSj1XSTxaRC9VPytRQzBRQzBaSkNhUUltUVFuU1NkTEhiSUZXRj1VRDtQQD9NPTxJPjdN QTpTQD5WREFVSjxVSjxXTTxXTTxfSDxcRTlWRDtWRDtaR0BeTEVlVVFiUU5dVVFYUE1YT0VhV01t W1RhT0hWRz9RQztjUElkUUpYSTdHOSdJNSlbRjlcUU5jWFVkXGNaUVhQRTxBNy5IOCpaSDpjW1pq YmFpW11rXV9jVlRfU1BaTk9bT1BfTlZoVl5jWFVWTEhTQTtYR0BfUVRhU1VWRz9IOjJIOSpPPzBJ OzFMPTNNQz1ORD5aUFFbUVNbT0dUSEBNPjRNPjRHPDFEOS5AOjBGPzVVQT9dSUdhVlBjWFNYT0VN RDpDQDRIRjpYUVFeV1deTk1bSklRPkNWQ0dhSkZfSUVYRzhUQzNXRjdWRTVRRDNMPi5EMytKOjFP TUxXVVRfWlVUTklOQDNURjlcUFFaTk9bSENWRD5XQTpbRT1TQzNNPS5QQDFRQTJQPzdPPjVOPTFN PDBNPC1NPC1JNC1QOzNeSkpiTk5dRkdbREVVPzpVPzpVPztXQT1XRzlRQTNTQzRWRjhdTkddTkdd TUlaSUZcTUZcTUZXR0RYSEVoVlxpV11vVVVlTExQQzVOQDNPRj1KQTlOPDVNOzRPPjJUQzdbRT9b RT9RRj1QRTxQREFUR0VYTExaTU1fTU9lU1VkVlhhU1VhUE1YSEVfRkhjSUxdSkpfTU1dU1FfVVRh WFVdVVFbU0NYUEBcSURdSkVrUU5oTkpaSTtWRjhWRz9bTEReTEVfTUZbT0dXTERWQTdUPzRRPTJU PzRbRDhcRTlYRj1eTENkTkhkTkhiVk5hVU1iVVNfU1BcTE1aSUpVRDtUQzpQRkBPRT9XSEBWRz9T QzRPPzFQPjlVQz1iSE1kSk9bTEFhUUdfUEldTkdcTkBbTT9XSTpNPzBQQC5VRTJbSEZcSUdbTEVT RD1PPjJNPDBPQy9VSDRVST5XTEBUTUBRSj5YRj1cSUBeTUdeTUddSDtWQTRRRzVRRzVXSEBaSkNe TExfTU1iT1FiT1FbTklQRD9MQDlJPjdIPjBHPS9KPTBXSTxWUURWUURbT0RcUEVdTkdbTEVaSkBW Rz1aSEFcSkRfU05iVVBjVlRbTkxTSUBeVUxnVlVhUE9WRz9WRz9bT0daTkZWRjhIOStBOSFTSTBe UFNjVVdeW1dQTUlPQy9IPClFPDJdVElnV2FwYWprXWJoWl5iVVNcT01fT05cTEpfUVRoWlxdVlZT TExPRT9QRkBYTk1eVFNYTURMQDhHOi1OQDNOPDdNOzVBPzRIRjtTTEFXUEZdTT5XRzlTQDhOPDNE PTQ9Ny4+OytHRDNVRUZaSUpbVFZbVFZVTEFGPTNKRUBTTUhWT09WT09VSERQRD9MQUBTSEdcSkRa SEFURTtWRz1jUEpfTUdbTT1PQTJENCdJOixNSUFXVExdTkRURTtIQDFRSTpcVVVXUFBRQzlQQThR RzlWTD1QSDlQSDlVRjxXSD5YST9URTtPOy5OOi1NPS5NPS5JOzNQQTpYSUFVRj5PPzFQQDJKQThK QThVQzpaRz5YSDxYSDxdTENfTkVkU0xkU0xeU0paTkZaSUpXR0hYRkRaR0VhTVRlUVhjUVVfTlFX RzhQQDFRQDJOPS9POS9QOjBQPzNUQzdTQTNUQzROQzpQRTxWSUVXSkZXTUxXTUxeUVFiVVVjVVdi VFZdUFBaTU1iSkxlTk9hTU9lUVRbUVNjWltqWlZkVFBdTDxbSTpeSERjTUhpT1FlTE5WRjhVRTdb TEFcTUNeTENeTENcSkFdTENdRjpWPzNVPjFXQDNkTEFqUUdlU01jUEpnU1NjT09eVFNdU1FcT0pY TEdbSklXR0ZTRDxOPzhUQTlWRDtlST5oTEBlSjdbQC1OOSlUPi5bQUZiSE1eU0peU0pdU0RiV0hl WlBfVEpYUEpQSENPPzBQQDFbSEpdSk1dTkZVRj5QQD1MPDlTRTRVRzdXU0hYVElUTUBPSDxTRTVW SDlbU01cVE5cTUNURTtQRTxTRz5PRT9PRT9VRUFaSUZaTFBbTVFXT0xQSEVJQTxEPDdJOzFJOzFM PztUR0NaT0xaT0xVT0pVT0pYTEddUExcUEVXTEBcSj5cSj5YTkhbUEphT0ldTEZVSUFfVExkWlRe VE5XRj1eTURPSUdKRUNHPjVFPDNHPy5YUD5nVlVpWFdjWFNdU01ORTtHPjRHPD1fVFVuXF9zYWRy ZGRpXFxeWltTTk9VSkleVFNeVVZhV1hbTkxRRUNJQTBIQC9TSUBYT0ZYTEdQRD9OPTdUQzxVQUFK ODhBOy9NRjpbU0NcVERfTkVXRj1NPDdJOTNAOC48Myo/NTJMQT5UTU9YUVRbVldVUFFFREA9PDlK RERTTExXTUdUSURQRz1NRDpNQz9RR0RVTENQRz5PRUFVSkdiVVVhVFRaVERKRTVDOyxEPC1WRz1e T0VbTEFTRDpJQDRWTUBcW1dTUU5RRjtRRjtWTD1aT0BaUT9aUT9eUU1fU05XUEdRSkFPPzFNPS9Q PzdQPzdNQD5OQT9RRzlORDVNPihOPylNQzRRRzlYST9cTUNdTENfTkVkUU9nVFFlVVFnVlNdV1BW UElYSEdVRURXSEBWRz9eR0phSU1cSE9dSVBcSjtcSjtfSURVPzpTPjFPOy5OPDNRPzdXRTxVQzpQ RTpRRjtbSEhdSkpYTk1aT05hVFFiVVNiVFhjVVpdUE5bTkxfTUphTkxbSklcTEpjUVppV19rVFNo UE9hSD5lTUNqU1FqU1FlTlFdRklWQTdWQTdaRz5eTENeTUdaSENeTT1bSTpRRDNMPi5TPjBeSTtr VkpuWE1pUVBoUE9iT0laR0FdSkheTElfUEldTkdaSENVRD5TQTVPPjJdRD1fRj9rT0RyVUlqTkFf RDhVQDVXQzheSERhSkZiTlNlUVZhVE9oW1ZpYVtlXVdfWFhUTU1KQzNNRTVeTE5hTlBdUUhWSkFT QTtRQDpRRDNURjVTTk1XU1FbT0ZUSD9NRjBORzFRTEdbVVBeVUpVTEFQQTtURT5ORTxPRj1URjlV RzpXRkBbSURVTkVRSkFMQzdHPjJFOjFIPTRPRDxVSUFcTEhaSUZRTUNTTkRUSTtaT0BcUEVcUEVa TkVYTURaTUhaTUheTUdaSENRSElbUVNhWFVdVVFjUEljUElPRkdHPj9OPzhMPTVMQzldVEloXF1k WFphVUxeU0lPSD5DPDJKOzxdTU5lXVpqYl5nX19jXFxYWFtNTU9OR0lcVVdfVlpcU1ZVRTlMPDBD PCVIQSpTRz5VSUBUSEBQRT1RQDtQPzpQPkBNOz1DOi5RSDxYVElfW1BhVU1aTkZGPzNBOy8/MiRE NyhFOzhJPzxVUFFaVVZXU1RKRkdDPzhAPTVQRD9WSUVXSEFVRj9OSERMRkFKQz1RSURYTUVVSUFR R0FaT0lhV1hiWFpaWFBKSUFJPy9IPi5WRD1hTkdfTUpcSUdPSUVYU05eXVVRUEhQST1PSDxbUD9c UUBeU0leU0lfU1BcT01aSkNWRz9QQzNURjdUQz1VRD5RQT5TQz9TRDpURTtTQzNRQTJWR0BbTEVe TVBeTVBhUE9lVVRqWltqWltpXV5lWltfWldYU1BaR0VWREFeTEVaR0BaQ0FaQ0FdRklfSExhT0hi UElcTUVVRj5NPTFIOS1KPTBRRDdYRz5bSUBXTERYTUVbSEhdSkpbSkdbSkdbTk5cT09hT1NhT1Ni UU5bSkddSk1aR0lUR0VUR0VlVFprWl9oU1dnUVZnTUhqUExqV1doVVVfSElbREVWQDtUPjlVQ0Bb SEZfTUdbSENYSUFURT1TRDpRQzlVRDtiUEdqWk1qWk1lT0pkTklcTUVURT1cSUxdSk1kUU9hTkxf Sj9aRTpRPi1UQC9cSURlU01uVldrVFVhT0NaSDxTQTlVRDtbSENcSURjUFNoVVdoV1hrW1xnWF1l V1xfUVRVR0lNRC9RSDNdTEZhT0leTURaSD9VRDtTQTlRRDRURjdQTkNWVEhdTkRYST9QSDdNRTNR Rj5dUUljUUpcSkRKPjxMPz1MQzdPRjpRSDxRSDxYSDxbSj5WT0ZRSkFNRUFJQT5FPChGPSlNRDpR SD5YT0VWTUNPRjpORTlPRjxTST9dT0FfUURXUEdTTENbSURcSkVYSEdVRURQSEVVTUlcVE5dVU9h UE9hUE9TRkZNQEBRPDhRPDhUSUheVFNpXFxjVlZbSUNcSkRWTURNRDtMPD1XR0hdV1VqZGJlZGFf XltdXldPUElPRkdaUFFlVVRfT05QRy5IPydGOiVOQSxURjlQQzVVRD1TQTtXQTpRPDROPzhJOzNF PzlOSEFcVlRnYV5iXV5aVVZJQDhFPDNGMihINCpJOzROPzlTR0pYTVBTSUpKQUNHPThFOzVPPjhV RD1WQ0BaRkRUR0dQRERPRT9VSkVcTEpdTUxWTkpaUU5kWlZnXFhnVlNXR0ROPzVVRjxeTUZiUElk UE5jT01WTU5bUVNhW1hXUU9XSD5cTUNdVElfVkxeU0pcUEheT0hhUUpeTT1XRjdaRThdSDtbRjtW QTdQPzlVRD1USD9TRz5TRDpVRjxbSEhcSUleTk9iUVNhVFRnWlpqXGNuX2drXWRpW2JlW1pdU1FY Rz5eTURhTkdcSUNaRTpUPzRYRDdiTT9nVE1lU0xiT0haR0BPOy5NOSxOPjBWRjhfTkFjUUVdSkFa Rz5YRUNbR0VbR0VbR0VaSExYR0peSk9bR0xbSEZXRUNbSENXRT9QQTpYSUFeUVFhVFRjU1RhUFFd TEZkU01tXFhlVVFhR0daQEBQPjlWRD5WSUVdUExoUUlkTkZVRURURENXRUNWREFaR0FkUUxiV0Ze VENhTkVdSkFTQzdPPzNUQEBbR0djUUpfTkdfRz1aQThVPjFUPTBYSEliUVNoVVVkUVFdRzJWQCxP PTRTQDhcRURfSEdkUFdjT1ZlWFhpXFxqWFxqWFxnVFFYRkRTQTJTQTJbSUReTUdkU0RiUEFdTENX Rj1UPjdXQTpTTEFUTUNeT0VaSkBTRTVPQTJWRTxeTURlU01fTUdPQDlKPDRKQzNORjdRSDxVTD9Y Tj1YTj1YUUhXUEdVTEFORTtJPCxMPi5USD9XTENaTkVaTkVMRjdKRTVOQzpPRDtbSkdbSkdbTkxY TElWSUVVSERVQ0BTQD5QRkBXTUdbWlRbWlReUU9eUU9RQzxOPzlNPjdKPDRTSklfV1ZfWlNcVk9W TkpXT0xbTklXSkZQRkNVSkdhVVtpXWNlX1tjXVhcXVhTVE9ORkVUTEplTk1hSUhVSjlTSDdWSTVU RzNbSj5WRjpYRztbST1cRTtUPTNMOjhNOzlNQUVQRUhiWFxpX2NkYmVdW15PQ0BMPz1NOilINSVN PDdRQDtUSExWSk5VSEZOQT9HPjVFPDNQQTpTRDxYR0FcSkVVRj9VRj9RREZWSEpaTk9dUVNaU1Nb VFRhW1RiXFVqWFNXRkBQRkNaT0xrWFNrWFNtWFZpVVNcVFNbU1FcVk9bVU5cTD9jU0ZiW1BfWE5b UUhbUUhnU1BoVFFiUUVeTkFqT09pTk5eST5VQDVTQDhWRDtXTkVWTURUR0NWSUVXSkpUR0dWSUdd UE5iVlpoXF9rX2VqXmRnU1xpVV5kVFBjU09iU0xhUUpnVE1hTkddSD1WQTddSkRpVk9tXFtnVlVq TUpdQD5POSVMNSJaR0dnVFRqV1BkUUpcSjxXRjhWQ0BWQ0BbRUBdR0NaRkhaRkhcSE1dSU5hTU1a RkZUQzxVRD1WRD5YRkBaTU1fU1NeUVFbTk5aTk9lWltrW1xiUVNeQTxaPThWREFaR0VeUU9pXFpp W01fUURVRjxTRDpVRURTQ0FYRkRiT01hUERYSDxaSDxXRjpPPjBPPjBUQTxeTEZnVE1eTEVYQzJU Pi5QOjBUPTNXRkleTVBkUU9hTkxfQStWOSNPOjJUPjddQD1iRUFfSlFkT1ZnU1VpVVdqWFxqWFxl VE1cSkRWRjhPPzFPR0RWTkpjVExnV09hVFFWSUdXPjhWPTdQQDFVRTVbT0ZbT0ZYSj1TRThPRjxW TUNiT0hkUUpXQzhPOzBQPDFWQTdVSUFbT0dbUUdbUUdbU09aUU5cTEhaSUZTQzNPPzBRRj1XTENY TUVaTkZOQzpHPDNPPjhQPzlXRkBbSURcSURcSURYQz1YQz1TRD1QQTtWREFeTEleV1peV1piVldb T1BVRj9JOzRGOzBIPTJVRj9hUUpfXFZeW1VcUU5dU09hU1VdT1FOSEFPSUNiT1pqV2JlWl1kWFxh WlpWT09NQz9USUZdTUxdTUxcUEhdUUljWlBeVUxfTkhaSENeT0dlVk5eU0lTRz5NPjdNPjdJQERK QUVfWFtlXmFdW1pbWFdPQDdIOjBMOyxPPi9TQTVRQDRUR0dTRkZORkNKQz9JPjVFOjFRQTVWRjpW SkFXTENTSUBUSkFQRUZUSElWSE9bTVRcUU5hVlNlW1dkWlZdUUlQRT1USUZdU09pWFVrW1dpWlNj VE1bUE1eVFBdVU9dVU9WTURlXFNpYV1kXFhaT0xYTkpnWlplWFhkV1VkV1VqVltkUFVdRD1VPDVT RDxYSUFeT0hbTEVYSUNXSEFWRz9TRDxVRURaSUhlV1xpW19oXmJhV1tbT1BcUFFhUE9hUE9eUU9j VlRpXFxeUVFYR0FVRD5cT01oW1hqXF5iVFZdRz9RPDRMPjFXSTxkWFpoXF1kV1NXSkZaTD5WSDtP PTdQPjhURTtVRjxVQUFVQUFVREdYR0paR0lTQENRPzdTQDhWQDtUPjlVSEhhVFRfTUpeTElnUVZq VVpjUEpTQDtYQDpbQzxeSkhoVFFnVVttW2FpWlNeT0hYQTRXQDNQPzpQPzpWRERdSkpdTkRXSD5a SDxVRDhPPzFPPzFWRD5dSkVfUEldTkdRQTJQQDFTPjFVQDNbSEpkUVRkUUpdSkRfRTFWPClTOjNa QDpbQz5eRkFcTEpdTUxfTU9jUFNjV1hlWltjVlFbTklbSjxXRzlbRT1eSEBiUU5nVlNlVVFbSkdY QztVPzhKPS5OQDFUTURYUUhdTkRcTUNYR0BaSEFTTENYUUhVSjxPRTdOPTdTQTtYSEdfT05cUEha TkZXTE1VSUpcSUddSkhaSDxRQDRRSTpTSjtQST9TTEFOQzpHPDNOPjJVRTlXRkBbSURcSkVcSkVb TEFVRjxQRztMQzdQQ0VYSk1eV1paU1VbUE9TSEdTRz5MQDhPPjJJOS1RQDhXRj1cVVVbVFRYTkpc UU5fVFViVldQSUBNRj1aTk9kWFpkW15lXF9hV1tQR0pIQD9KQ0FcTEpjU1FiVVBjVlFiWFpbUVNa R0VdSkhdV1NfWlVeWFRaVE9PRjpGPTFBOjRHPzpcTlBrXV9jW1pVTUxRRTFNQC1RQzBRQzBVRTlO PjJNQDxOQT1NRDtKQTlHPS9FOy1QQDRaST1dTkRcTUNUSkFRSD9MRkRKRUNPRURQRkVRT05bWFdl XF1fVldaSkNTRDxbTkxnWldlVVRiUVBVUEZVUEZaT0xcUU5cVFNfV1ZhV1toXmJoYWFjXFxcT09Y TExjV1hjV1hjXFxkXV1kW15dVFdYR0BYR0BeTk9kVFVhUE1aSUZPSD9TTENRRj5OQztRQUBaSUhl VFpwXmRpXFddUExbSURcSkVaTU1cT09jUVdoVlxlXF9bUVVaR0VWREFXTE1jV1hjWlBeVUxYSDpR QTNTSEdfVVRpXV5hVVZfT05XR0ZXSD5QQThKOjFNPDNQOzNTPTVWOjhWOjhTPz9TPz9WPjpTOzdR Oy9POS1WPTdaQDpaRkReSkheTURhT0ZlTk9jTE1hRTxdQTldR0NiTEdhU1VoWlxrV1xzXmNrWlRi UEpeRjlaQTRQPjhVQzxYRUVcSEhbTT9aTD5aSjVXSDNURC9QQCxYQz5cRkFYTUVXTERXRjdVRDRX QzVbRjleUVFjVlZcUEVWSj9eRzpaQzVVPjRROzFOQT9VSEZbSEZcSUdaTkVdUUhbV1ReW1dkWlRc UUxXTENUSD9aRTpfSj9jUEpoVU9nVU9fTkhcSkVVRD5TQDhTQDhXR0RcTEhbTklcT0pdTENdTENU TUNaU0hQTD5OSTxOQzpPRDtWRUpcSlBeUU1cT0pUTURPSD9XRj9cSkRXRTxTQDhWSDlbTT1YSUFa SkNUSTtNQzRTQDhVQzpVRj5aSkNVTUlVTUlWSUVVSERRSTpNRTVQREFbTkxaVVRRTUxPR0FMRD5T RDpRQzlRQTVKOy9PPzNQQDRWT0ZXUEdUTkdaVE1cVVdiW11aT0lQRkBaTlFoXF9nX19oYWFYV1FI R0FBOjRFPThRSURbU01nVlNoV1RdVVRaUVBYRkRcSUdaVVRcV1ZdWFdYVFNURT1MPTVBOy9DPDBY TUVpXVVqWlZdTUlQRDBQRDBRRzdUSTlURjdMPi9MPTdNPjhRQzlNPjRGPSdEOyVVRj5eT0dYUEpX T0lUTklQSkZMRD5JQTxHPzpNRT9PTU5VU1RhWlpbVFRTRkFNQDxVSkldU1FdUUlYTUVVTD9YT0NU T0VXU0hYUVRcVVdiW11pYmRrXlxnWldXTkRUSkBcT01hVFFkXFtlXVxiXV5bVlddTE9fTlFjV11h VVteT0dRQztRRj1WSkFTRDxQQTpVRUZeTk9lWl1rX2NlW1pXTUxQQTpURT1aSExbSU1aU1dhWl5p W2JeUFdWREFUQT9XSkhjVlReV05XUEdORztMRTlPSEhXUFBkVFVeTk9YSEVTQz9TQTVNPDBOPS9O PS9POCtTOy5VOStWOixQOzNTPTVTPjFPOy5UPTBUPTBaQT1dRUBWSkFXTENbSUNaSEFaR0BbSEFe RT5jSUNeTkpiUU5jV1hkWFpoW1tpXFxrW1djU09iSjhYQS9OQDNMPjFTRkFVSERWT0NWT0NcTj1W SDhQPShRPilYQzteSEBbSUNbSUNdRjxeRz1aRD5dR0FaT0lcUUxXRztVRTlaRTpVQDVTQTNQPzFR RUNbTkxWRkNYSEVYTUFbT0RdVk1hWlBlWFZeUU9YT0VTST9XRjhdTD1YTUVeU0pjU09iUU5dTkdV Rj9XQTxXQTxVQ0NXRUVbR0xcSE1jTUdnUEpcVUpaU0hUTjxRTDpOSDlNRzhVRURXR0ZYTUVaTkZV SjpRRzdTRz5WSkFWRT9VRD5aRz5iT0ZcUEhXTERQSUBKRDtQQTtOPzlWREFhTkxdV1NaVE9WSUdW SUdVRzpTRThUSkFcU0laU1NOR0dPRjxORTtPRDxTRz9YTURWSkFURjdVRzhVTEFYT0VRTkpXVFBa V1heXF1YVVFQTUlaTU1qXV1iXmRhXWNYVElFQDc4Mx09OSJQSkZcVlFjWFViV1RXT0xbU09YSEVe TkphVlVfVVRdU09TSEVOQDFOQDE6OSVAPytTTUZiXFVkW1BWTUNMPi9RRDRVRzpWSDtQRC5QRC5I OStIOStMPTNKPDJFPS5JQTJVTE1dVFVbVldXU1RRSExPRklPRURMQUBGQUNMR0hQTE1aVVZbVlVV UE9TRThPQTRTSEVcUU5WTU5TSUpUSkFUSkFVTENbUUhWT1RcVVpfW1poY2JnXFZhVlBWTUBRSDxV SUBfVEpnW15kWFxkWlhdU1FbTU9jVVdnW1xhVVZcSDdWQzFTRTVXSTpRSTdORjNUREVfT1BlXF9p X2NkU01UQz1KPzhMQDlRQUNcTE1bVlVfW1poV1hhUFFURT1RQztPR0ZVTUxVTUdWTkhUSEBQRT1V SEhXSkpbSEZVQ0BPPjhMOzRMOy1MOy1QOi5ROy9TOy5WPjFYQyxYQyxPPzFRQTNUPzRTPjNaQThc RDpfRkFfRkFaST1bSj5YRz5WRTxWRDtdSkFjUEpkUUxhVFFfU1BiVVNfU1BfVVFkWlZpXFpkV1Ve TT5RQDJJOihOPixURT1XSEBXUEdbVEpjUUNbSTtWQTNWQTNeRkFhSERdSkhdSkhdSkFaRz5VRD1b SUNbSUBcSkFYRTFYRTFaRTpVQDVTQzNURDRWSUdfU1BQPjVTQDhYSUFbTERdUExiVVBjV1hcUFFX SkZTRkFURT1WRz9RTEVYU0xhVlBeVE5dSkpWRERTQTxQPzpRQzxURT5aRThbRjlfSUFhSkNbUUdd VElcU0haUEZQRz5NRDtWSkNaTkZcT0pbTklXSEBWRz9TSkVTSkVXTERXTERhT0loVlBfVkxcU0hT UEVNSj9PRzhQSDlXRjpiUERkWlRlW1VaUU5RSUZTQDpaR0BWUE5cVlReV1dUTU1RSkBPSD5YT0Zb UUhkVkheUENfSj1dSDtVTENYT0ZUT05aVVRaV1tbWFxQUE5OTkxbSU1hT1NeW2FfXGJYTVBHPD89 MyM8MiJNR0NdV1NlWFhkV1dfV1RdVVFiTlNiTlNoVVVjUFBaSD9RQDhQQzVKPTA/PitDQS5NTkdd XldfWE9QSUBNPjhVRj9eTUZdTEVaSkBRQzlJPC9HOi1JOzRJOzRIPj1QRkVTUFFXVVZcVVdRSk1T QTlRQDhNQD5NQD5KQDtORD5RR0ZbUE9aU1dRSk9QQzVQQzVTSjtWTj5YTkhWTEZQSENPR0FVSkVV SkVRTElWUE5dVlhoYWNoXF1fVFVPSDxORztUTklhW1ZqX15oXVxiUU5RQT5UREVeTk9fVVReVFNb STtdTD1fVEpfVEpVSjpQRjVWREFdSkhbVFReV1daSkROPzlMOjhOPDpRQEZeTVNeVlVhWFdoVVVh Tk5aRz5XRTxQRz1TST9aTUhXSkZUQz1UQz1aSkRaSkRdTUBYSDxNOSxMOCtFNCdGNShROjBXPzVc RURhSUhbTERaSkNQRjhQRjhYQztaRDxeRkFkTEdtUVFpTk5fTUZbSEFbQzxYQDpbREVhSUplU1Bl U1BjU1FjU1FdUExcT0piT01kUU9tW15pV1thT0NRQDRMNSJPOSVRQDpcSkRjV1hpXV5pVVNcSEZf Sj1hTD5eTEZcSURYTEdXSkZcSUBWRDtRRj1USD9bSD9dSkFaSTRXRzJcRj5aRDxRQDhWRTxaR0Vh TkxPOy5VQDNYSEVaSUZcUVBfVVRfVldaUFFYTElQREFOPjBOPjBRSD9YT0ZhT0hiUEleTUZYR0BV RjxOPzVOPi9PPzBWQTNbRjhcTUVaSkNaSkRfUEliUElfTkdPRDxOQztUTUNWT0VfT0xdTUlVRj5U RT1VTENVTENVTENcU0llVVFnVlNiWlhdVVRVTkVPSD9MRjRNRzVTSjtdVUVfXVFiX1RWVFNKSEdR PzpYRkBaTk9hVVZkWFpXTE1VST5YTUFdV1NdV1NeV01dVkxiVk1dUUhVTUdVTUdUTkxcVlRbVldY VFVTU1BJSUdQRUZcUFFiVlpeU1ZaSUhHODc/Lyc/LydJQEFXTk9tWlNtWlNnXV5iWFpcUFReU1Ze U0laTkVbSjVXRzJVRTdNPS89OCdGQC9UTEhcVFBcUEdVSUBTQDtWRD5hT0hiUElaUEZTST9MPypG OiVANylGPC5GPT5NREVcUFReU1ZYVElJRTtMPyhMPyhJPjdKPzhNPjhPQDpQSENaUUxbU09TSkdR QzlURTtUSkBYT0VbVEpYUUhaTkZXTERRSURNRT9OR0dVTk5dVFdlXF9nVVtfTlRMRD5PR0FXUFBf WFhjW1pjW1pfT1BUREVTR0haTk9iVVNhVFFkTklpU05jW1djW1dbUUhORTxUQzxaSEFbT1BbT1BU R0NQRD9UQzpXRj1dTUxkVFNlW1piV1ZiV1ReVFBcSUdaR0VYRz5aSD9dTkdaSkRVRj9XSEFdTUBj U0ZnVUZhT0BVQS5OOyhGOCZIOihRPDdbRT9eTEleTElbSENaR0FXRj9XRj9YSUNaSkRjTlNrVlty WFVuVVFkU0xcSkRaQTtaQTtjTE1lTk9kVUpkVUphUUdeT0ViUEphT0lkUU9lU1BpV1tpV1tiT0Zc SUBWPjFROi1UQEdeSlFpV11tW2FnWlpYTExcSUNeTEVeTkpaSUZaSkRXSEFaSUZXR0RaSD9WRTxc RkFdR0NdSkFdSkFkSkReRT5TQDpYRj9dSU5jT1RPPjBUQzRWREFbSEZhUE1oV1RiV1ZdU1FVTUdO RkBPQTJMPi9RPzdaRz5bSENdSkVhUERdTUBXTERVSUFRQDFPPi9QRTpYTUFaTUhXSkZbSEZdSkhf TkhcSkVVRjxURTtWTEZeVE5eVE5YTkhURTtTRDpQRztUSj5XUUpcVk9iVVVpXFxlXF1hV1hdT0FV RzpORDNPRTROST5cV0xjX1pjX1paWFNJSENTQz9QQD1WT1FfWFtiVVNaTUpdTEZjUUxeV1piW11l W1doXVpnXV5iWFpUTkdUTkdVT01YU1BhVlVdU1FVTUdPR0FMSENUUEpTTUhTTUhaSD9PPjVENCc/ MCNBPDhTTUhpWFpwX2FnXWFdVFdVSUpVSUpcUEdYTURfTz9fTz9aSjhTRDFBOCdMQTBVTENbUUhd TT5XRzlVQzpcSUBbT0ZfVEpdTUlhUE1XRzlNPS9DNyRHOyhHPTxNQ0FbSEhdSkpYT0NKQTVHPy1F PStIOjBKPDJNPjhPQDpWRkVbSklVTUdTSkVXSEFcTUZdVEpdVEpbVFRbVFReUFNXSUxQRkBMQTxQ REFTRkRWT09eV1dhU1VbTU9NRURRSUhWT09bVFRhW1ZdV1NeUU1TRkFWRUhdTE9fVExiVk5tWFhu WlpnXV5nXV5bTEVQQTtOQzpUSD9fTUdhTkhdTkdXSEFXSkhiVVNlW1dlW1diXFdiXFdeXFtaV1Ze Tk1aSUhcSURdSkVaSkRYSUNUQz1WRT9fT0xuXVpwXlhoVlBhTz9VRDRQPSxRPi1UQz1aSENdSkVe TEZdRz9aRDxXRT5XRT5WSUdcT01nU1xrV2FuVlVuVlVoWFFfUEldSUljT09qWFxtW15oXFReU0pe TENfTURkUFBjT09iUVNlVVZkVFNiUVBhTkVdSkFaRTdRPS9PREVWSkxfVFpnW2FiUVBWRkVbSENe TEZcSkRaSEFXSEFWR0BWREFWREFhRTpdQTdcRD9aQT1eSENlT0lkTEVdRT5RQztXSEBbTU9WSEpJ Oi5OPjJTQDhYRj1iUEplVE5oWFFlVk9eUU1YTEdNQzFJPy5UPzJbRjlfRDtnSkFbTEFaSkBaT0xW TEhVQzxUQTtQRkBbUEpfU1NeUVFcT09aTU1YTUVWSkNTSDpTSDpWSUleUVFeVE5YTkhTRDpTRDpN SDtPSj1YUE1dVVFqVltyXWJrXlxpXFpqVUlkT0RVRjxRQzlRSkpaU1NdW1xhXl9dWE5WUUdRTUFN SD1RT05fXVxjW1VaUUxaUU5eVlNdVlheV1piWlhnXl1lY2RjYWJWUUdUT0VfU1NeUVFfUVRdT1FQ TEBRTUFPT0BQUEFcU0lWTURVSUBTRz5PQy9EOCVFPDJWTUNfW1xoY2RoXl9aUFFXRj1XRj1hUUpe T0hjV05iVk1eT0dTRDxFOCtKPTBTRDxdTkZcTj5XSTpYRDdYRDdXTEBhVUlhUE9iUVBUT0VFQDdH OitGOSpDPDJGPzVOQzpUSD9YSUFVRj5WSDtPQTRMOyxNPC1QRz5PRj1TSkVRSURXTkRTST9UR0Nd UExiVVNiVVNbUVVaUFRbT1BVSUpTRkZPQ0NMPztQRD9USUZbUE1dUE5bTkxQSkhOSEZTTExYUVFa V1ZYVlVkUFBfTExbTEVeT0hbU01cVE5pW11tXmFiXFpXUU9YR0FUQz1TRz5WSkFhUUpjVE1hUUpf UElnVV1tW2NrXWJvYWVtX1tpXFdjWl1dVFdhTk5hTk5bSURcSkVYTUVaTkZYRkhbSEpoWmFzZGt1 ZGVvXl9oVkdYRzlYRi5aRy9VQ0NdSkpeTEZeTEZdRT5aQTtXQD9aQ0FbR0xkUFVkU1hkU1hjUFBl U1NiVVBlWFRpVlhoVVdoXF9jV1tlU01fTUdkSkplTExnUVZoU1dnVlVpWFdnV1BfUElcSUBUQTlR Pi1QPSxUQzxdTEVpVlhoVVdlTUhhSERaR0FYRkBXRTxXRTxWQ0BWQ0BYPztbQT1nRDtoRTxkSkdk SkdfTkdfTkdeTj5VRTVVPjRWPzVfRj9YPzlOPDNPPTRRPTJWQTdYTURdUUhhVlBiV1FlVUheTkFR RzdNQzJXQzRhTD1lUEFpVEVfUEZeT0VdUUlbT0ddTEZfTkhXTUlfVVFnWFtjVVdaUUxVTUdXTUdU SURPRjxMQzlRRj5cUEhbUE1XTUlWR0BURT5NRjpQST1eTVBjUVVkWlhoXVxqYlxoX1poVU9kUUxa SkRURT5VTlBYUVRiVlpoXF9jVlFaTUhUSTtWTD1VTUlfV1RjWl1dVFdeVVhfVlpjV1hhVVZdVlZl Xl5fX11hYV5cUU5YTkpYT1BcU1RbVFRXUFBKSUFNTERTTkRWUUdYUUhXUEdaSUhdTUxVTD9DOi5F OCtYSj1YXVxfZGNqX15XTUxRRDNVRzdeVE5eVE5jVVdfUVReTElTQD5FOClJPC1TRDxeT0deTEVa R0BXRjhXRjhcUUNpXk9kWlRhVlBVTkFKRDhKPTBKPTBJPC9KPTBQQzNaTDxeTUZfTkddTEVUQzxM QzdNRDhTSUBaUEdTTkNOST5USTtVSjxdTkZhUUlXVVZVU1RYTU5YTU5aTUhYTEdVSERNQDxJPTlO QT1QRD9XSkZUSURTSENKRT5MRj9PRkdQR0hXUFBdVlZfU1BaTUpWSkNTRz9QSkRVT0hkWlRlW1Vk V1NYTEdVRD1RQDpVRj5eT0deVUxfVk1fU1BfU1BoVFtvW2JyXGFwW19qVlRnU1BiVVVfU1NhT0hf TkddTUxhUE9eTk1eTk1aR0lcSUxhXF1qZWdwZGVpXV5qVUdlUENkTjtjTTpiSklhSUhjTkBjTkBe RjxbQzlaQT5cREBXSkpYTExbR0dYRUVbTkleUU1nVlVlVVRjUUxiUEpkVFNiUVBhTkVnVEpnVlNl VVFnWF1nWF1pW11nWFtlVVFbSkdUPTNQOjBUPTNbRDpYSElhUFFpVlhjUFNkTUNiSkBdRT5cRD1b RDhbRDhbQz5bQz5fQTplRz9qTUlpTEhkSkpjSUleSkhbR0VXSDNURTBVPjRWPzVVQzpUQTlTQTxR QDtRPzlTQDpUREVaSUpXT0lbU01kVkhfUURdTUBYSDxVRDtjUUhqXVhrXlplWFRhVE9dUUlfVExc T0pdUExYUE9hWFdnW15iVlpbUE1VSkdVTENRSD9RRjtOQzhNQzRXTT5cSUdcSUdaR0FUQTxRQztX SEBeTE5lU1VkWlZpXltqY2NlXl5jV09bT0dWSkNVSUFWTEpdU1FfVldkW1xcVlFaVE9VTEFPRjxP QURaTE5fVlpnXWFhWlxhWlxkVltiVFhbVlVnYmFoZ2FjYlxdVEleVUpbVVNaVFFXVE5WU01OSEFQ SkRYTExdUFBcVE5eVlBeUFNhU1VbVU5IQzxDPS5QSjtcXFxjY2NvXl1XR0ZRQzlXSD5lWl9oXGJl W1pXTUxaR0dVQ0NKOi5NPDBNPzJYSj1dSkhcSUdYTUVaTkZdVVRuZWRpYV1jW1dWT0VTTEFQRTpO QzhNPDNKOjFTRDxcTUVhVU1iVk5eVFNUSUhJRTtMRz1USUZdU09UTURQSUBNRDtPRj1dTkdjVE1U U0pRUEhQRkVPRURRSD9RSD9ORztJQzdMPTVOPzhPPzxRQT5NQTpPRDxMQDlOQztRQzxRQzxbTkxd UE5dUUlYTUVXSkZQRD9OSEFXUUpoWl5kVltkUE5YRUNPRTdRRzlaSkRiU0xeVUxeVUxdVEpeVUxl Wl9oXGJkWF5jV11jT09fTExYT0VXTkRaTkNaTkNdU09jWFVfTkhYR0FXRUVbSEhbVFZoYWNuYmVn W15pUVBqU1FpV05kU0lnT0VnT0VoUERiSj5cRDdbQzVaRD5YQz1TRD1URT5VRDhWRTlYTEleUU9o VFRlUVFeTElhTkxkUVFkUVFkUU9qV1VrW1dtXFhuW11nVFZlWFhkV1djVlFaTUhcQDhbPzdcRD9h SERdSUlhTU1kVFBiUU5nUEhlT0dlTUhiSUViRjtlST5fRz1iST9hSD5jSkBfR0NkTEdiSUZhSEVd SUldSUlcTj1XSTlTOzhWPjtXQTpdRz9OPi9QQDFNPjRKPDJMPD1PP0BVREdcSk5jUEpiT0ldTENd TENbTEVpWlNvYmJwY2NqXV1iVVVaTUpbTkxiUVNfT1BdT1RjVVplWl1hVVhfTU1bSEhVRUFUREBR Rj5MQDlMPTNURTtcTE1dTU5aSD9XRj1VRD1XRj9cTEpfT05eT0hoWFFrXWJuX2ReWFRQSkZTRDxU RT1TRz5YTURaTk9jV1hfWldaVFFTTkNKRjtFPz1QSkhbVFZnX2JjW2JjW2JjVVddT1FaU1NlXl5p YmJiW1tbT0dcUEhdU1FcUVBVTk5WT09PQ0BRRUNUQ0ZXRklTTlFXU1ZdVlZdVlZhXVVTT0dNSUFT T0dfXV5kYmNpXFdUR0NRREheUFVuX2RpW19dUUlXTERcSkVWRT9NPDNOPTRTSUBWTURWT0VUTUNX TE1cUFFkWmJqX2hvYWNtXmFcV0lVUENYST9VRjxPPTROPDNRSDxbUUVkXFhnXltfV1FRSURNRDhQ RztYUE1cVFBUTDxMRDRFPjRKRDpWTEZeVE5aVE9RTEdMRj9HQTtKQDtQRkBQRz5JQDhQQDFPPzBK OjFMOzJIOC9JOTBHPS9JPzFPPTtTQD5XR0ZbSklWSUdTRkRURENVRURRR0ZdU1FoXGJjV11eTE5Y RkhTRz5XTENaT0xfVVFdVVRcVFNbUE1cUU5hVF5iVV9eVVhbUVVdSkpYRkZUSEBWSkNaTkNaTkNf V1ZfV1ZaTUhTRkFTRD1VRj9dVFdpX2NqWFxlVFdoVVdpVlhiWlRfV1FlVE1lVE1qUU5kTEhdSDtX QzVRQDpTQTtTQTtUQzxURENaSUhaTU1dUFBkTUxjTEpeSjlcSDdhTUplUU9qVlhrV1pqVlRqVlRp UVBnT05hTk5eTExdT0FWSDtiSUNnTkdpUElnTkdcSkVeTUdhUE9lVVRtVlBtVlBpU01hSkVkQzVp RzpjRzxkSD1hSEFiSUNcQ0dfRkpkR0RnSUZiTk5lUVFdTD1VRDVWPjhXPzlhSEVrU09OPS5MOyxJ OTBGNS1HNTVKOTlQPkBVQ0VXR0RbSkdcSkVdTEZhUE1pWFVpXV5rX2FqWltlVVZXT05YUE9eTk1f T05eTk1jU1FeVVZeVVZcUEhWSkNRRj5QRT1MRD5HPzpMPTVOPzhQQ0dWSE1dTkdWR0BWRD1UQTtX SU5cTlNXT05dVVRrWl1wXmJcWlhPTUxRQzlNPjRKPS5URjdYTUVfVExeWFZXUU9WSj9PRDlGPDlN Qz9UTVFfWF1hWlxjXF5dVlhWT1FYVFVkX2FjYWJYVldaTkZaTkZaTU1bTk5QTEpOSUhKPjpHOzdN RDtNRDtQSkhWUE5eVlViWlhhWlpcVVVTTExXUFBiX2NiX2NlVVZTQ0RWSlBiVlxtXmNnWF1fT0Nc TD9fTkFeTUBRQDhOPTRUSkFaUEdbT0ZXTENTSkdaUU5jXl1nYmFtY2RuZGVfW01cV0leU0lUSD9R PzdWRDtbVVNiXFpkX15hXFtdTkZURT1RQzlWRz1dU01cUUxQSDVHPy1KPzdRRj1USURfVU9cU0lV TENJRD1BPDVJOzRQQTtRQDhUQzpYRztRQDRMOSRKOCNDNyJBNSFEPCpIQC5QQThTRDpTRD1RQzxN Q0FNQ0FQPj5WRERYTVBiVlplVFpiUFZcSkVYR0FcTUZiU0xiVVNkV1VhVFFbTkxTSEVUSUZUTU1W T09cUFRaTlFXSkhWSUdYRztcSj5YTEdbTkleV1dfWFhhTUpXREFRQUBaSUhjV1tnW15nVVtoVlxl U1VnVFZpXFplWFZfU1NfU1NlUVRhTU9XQTxQOzVQPjlUQTxWRDtUQTlVQz1aR0FbT0dbT0diT0le TEZcRTlbRDhiT0hnVE1qV1prWFtrVFVtVVZqVE9iTEddSUlcSEhcSEhfTExnVFZuW11rXFFhUUdY R0FYR0FhTU9lUVRpU05oUU1iSj5eRztjRT1kRj5iRj1hRTxdSDtdSDtcQ0dbQUZfSD5jTEFjUFNj UFNhST9UPTNUOzdcQz5iTlBqVlhMPChHOCRKOi5HNytJNC9POjRKPTBNPzJWRDtbSD9dSkRhTkdh UFFnVldqXV1pXFxiVVBeUU1bTkxXSkhdTUxhUE9iT0ZkUUhcVFNbU1FRTUFNSD1PPjVQPzdMRTxE PTRKOjNNPDVPPkFWRUhcTEhcTEhYSUFVRj5WTVBdVFddVVRcVFNjV1tpXWFkXFhWTkpOPS9JOStO PS9RQDJYTEljVlReWFRYU05fTkVVRDtNPDBUQzdUTEhdVVFiW11iW11dVlhVTlBaU1NkXV1nXl1e VlVbT0ZbT0ZhUE9bSklPR0FMRD5KPDJMPTNOQDFQQzNTRz9YTUVaUU5eVlNeV1dcVVVUTklbVVBj WltjWltjU09UREBaTUpkV1VuWlxlUVRlVE1lVE1kVUpjVElXSEFVRj9aTkVdUUhYT0NTST1TRkFa TUhhWFdrY2JvY2lwZGptYVdjV05bUUhRSD9VRD5hT0llXF9iWFxcWFVUUE1URTtTRDpQRz1YT0Vk WFBcUEhOQDFFOClRQzxVRj9QSkhcVlReUU9YTElGPjs9NTJFOjFHPDNTQTlcSkFbSTtTQTNOPydJ OyNDNRxDNRxNPS9XRzleTUBdTD9VRD1PPjhOPj1URENQQ0VURkhWTVBdVFdfU1BdUE5iTUFnUUZo VVNqV1VtWF1nU1djUE5aR0VQRD9QRD9ORkVTSklYSk1XSUxWTEpWTEpcSkVeTUdYTVBbT1NiVlpj V1tpUVBiSklUREBRQT5eSkpjT09iVVViVVVjUFBnVFRrV1dnU1NeTVBiUFRjUUxdTEZWPjhPODFR OzFWPzVaRDxYQztWRD5bSENbTkxdUE5iUU5hUE1dR0NfSUVqV1VvXFpyXlxuW1htU1NqUFBkTVBn T1NoTlBkSk1hTU9kUFNlV1poWlxpX1VcU0hUSD1TRzxeSERiTEdhST9lTkRoTTtpTjxiSEFfRj9h ST9hST9eRztdRjpeRUFfRkNeSEBdRz9fTExiTk5eSEBVPzhTPTleSERoVFFqVlRQQCpMPCZNOilH NCRJNC1OOTFIOSpNPS5TQzNdTT1dSkVfTUdWTk1YUE9iVVVnWlphV05aUEdUREBTQz9TSkdVTUlb T0ZiVk1iUU5hUE1bTERRQztXRjpXRjpVRjxTRDpRQzlPQDdRQT5WRkNWTkhaUUxdTkdeT0haU1Vf WFtnW1xqXl9rXWJrXWJhWlBWT0ZRQTNKOy1PPCtRPi1OSEReWFRoW1hhVFFQSkREPjhHPDNKPzdN SUFTT0deV1dcVVVbU01aUUxcU0llXFNpXFdhVE9bUUVYT0NdVU9XT0lUSD9TRz5MPi5RRDNURDhY SDxcTEhfT0xeVlNhWFVdVFVYT1BWSkFcUEdhVFFjVlRfTkhWRT9QTUdaVlBiW1tdVlZjVlRtX11q XVtpXFpbTklaTUhdTkZfUEhaUERPRjpMRzxPSj9iWFxtY2dvZWtyaG5uZWJoX1xnV1BRQzxYRUxp VVxkW2FhV11VT0hWUElXTTxYTj1cTEhnVlNuW1VhTkhQQCxQQCxTRkFbTkldU09dU09kUFNhTU9I QDtAOTNGNytKOy9TSUBYT0ZbUEFWTD1TSTRUSjVVRTBQQCxbSUNlVE1hU0VcTkBUQzpUQzpXRkBb SURYSElXR0hUSkxXTk9YTkpbUE1jV09vY1tuX2JoWlxfVFVaTk9aTUhRRUBUQz1RQDtRQT5TQz9T R0hWSkxXSU5YSk9YTElaTUpcUFFbT1BjVlRoW1hqWlhkVFNWSkNQRT1WRT9YR0FbT0deU0pjT1Fn U1VnU1BkUE5hTU1hTU1fTkFXRjpVPjRYQTheRT5fRj9aTkVWSkFWRz9TRDxVSERXSkZcUEhcUEhc SUdlU1BqXF5qXF5nXFZkWlRlUU9nU1BfTU1kUVFiT0hjUEllTE5tU1VwXV1yXl5yX1ZhT0ZRRzVM QTBRPzpaR0FlTUhuVVBtVUhqU0ZjSUVdRD9hSEFjSkRaSDxYRztWRD1YRj9bRDpaQzlbSURcSkVb SD9RPzdUPjpdR0NlU0xlU0xbSTpQPzBRPitOOyhKOy9JOi5IPzNJQDRURT1dTkZeT0hdTkdWSkNY TUVjUFNnVFZfVVRXTUxYQz1XQTxTRDxURT1cSUdkUU9qVlhpVVdeUU1XSkZbSUBYRz5YT0VdVElf TUZaR0BXRj1cSkFaSUZbSkdaUUxbU01dVFdjWl1kXWJoYWVvY2dtYWRoW1ZWSUVOQDNJPC9RQy5W RzJMTURTVEpYV1RbWlZNSklDQD9HPjVKQTlKQz1PR0FcU1ZcU1ZfU05aTUhXVFBaVlNjWlBeVUxe VUxdVEpcVFBbU09aSkBTRDpNRDhTST1XRj9fTkdeVlNdVVFbVFRbVFRaT0xTSEVRSD9XTkVcUFFc UFFbTEVWR0BUSkBdVElhV1hhV1heVVtpX2VpW19kVltRTElTTUpXUUFbVUVXVUhKSDxJQzlRSkBa V1tlY2doYWNqY2VvYWVtXmNiVVVRRUVbR05kUFdfWF1bVFhVTUdYUEpdUUhfVEpdU09kWlZoV1Zd TUxVST5aTkNfVU9hVlBaT0lYTkhdT1ReUFVQST9GPzVFOClHOitKQz9WTkpfVExdUUlWUURXU0Vb U0BYUD5dUE5jVlRjU0ZYSDxVRTdYSDphTkViT0ZaTkZXTERRT0RTUEVdU1FlW1pqYWRvZWlnYmFh XFtaUU5XT0xYTEdVSERTQDtQPjlRQDpPPjhPQDpWR0BbSklcTEpYTk1aT05XTk9bUVNcVFBeVlNk WFpeU1RbTEVWR0BTQTlRQDhYRj9eTEViTlBfTE5lTUllTUlbSEhaR0dbSEFbSEFaSDxeTUBnT05l Tk1cUEhWSkNYSj1TRThVRj5YSUFbTERbTERcUVBhVlVjW1pkXFtiXVNdWE5cVFBcVFBfT0xiUU5i T0ZhTkVcSE9lUVhpWmNvX2l0XVdjTUdUQC1OOyhKPDVTRD1hTkxoVVNiUUNdTT5bQzxWPjhYQztY QztYQztYQztaQTtbQzxXQTpXQTpeRkFhSEReSTxWQTRTQDtaR0FeTUZeTUZaSkBVRjxYRTNUQC9O PjBMPC5JQDhKQTlUSUZdU09jU09iUU5aSkNYSUFeTUdiUEpiUU5eTkpfTExVQUFRQztURT1aR0Fk UUxqVltrV1xiVldeU1RfT05hUE9eWFZjXVtiU0xbTEVbT0ZcUEdbT0RWSj9UUEhUUEhYT1VcU1he Xl5iYmJoYmpkXmdjWFVWTEhPQTFOQDBRRDRVRzhRTT9XU0VbVkxYVElOST9FQDdHOTJJOzRMPTdU RT5YTk1aT05bUEpXTUdVT01YU1BiV1FhVlBlVk9oWFFjWFVeVFBTRkRQREFQST9UTUNaTkVhVUxi WlRcVE5dVVRaUVBYTEdTRkFUSkBaUEZYVFNWUVBXT0lQSENVTD9eVUhlW1doXVpiXV5pZGViVVNY TElOST5TTkNcVUxeV05YU0xWUElPSUNQSkRWVFddW15iW11rZGdwYmdpW19bT1BVSUpaRkpeSk9c VVVcVVVcU0ldVEpcVUpcVUpXT05cVFNcVlFbVVBeU1RjV1hkXFteVlVTTENVTkVeU1ZdUVVUTkdQ SkRIRTRFQTFORkVaUVBfV1FeVlBfWlNeWFFeWFZbVVNdV1VcVlRcU0ZPRjpURTtdTkRkU0xfTkdX TEBRRjtRSD9USkFaT1dlW2NvZWlqYWRlWFRhVE9fUEhbTERbTERXSEBUQzpRQDhNPDBNPDBTRTha TD5eTURcSkFcTEpcTEpcT09dUFBcUU5cUU5fVFdaTlFYSEVTQz9NPzJMPjFQPkBbSEpfT0xhUE1f TUdcSURcRj5cRj5XRj9bSUNdTUliUU5qV1doVVViT09cSUlcSUNYRj9XSEFbTEVdTUleTkpfU1Nf U1NkWlhnXFtfWlNdV1BaUVBaUVBhTlBjUFNiT0lhTkhhTFBnUVZkWmdvZHJtXF1eTk9RRCtKPSVK OTJWRD1iUU5kVFBiUUNcTD1dRjxXQDdbRDhYQTVbQz5bQz5bSEFYRj9aRThaRThdRUFfR0RXSDVT RDFQPzNYRztdSkFfTURaSkNWRz9VRDhTQTVQPzdOPTRJPC1MPi9TRz5fVEprWFhnVFRaTUhTRkFX SkhaTUpaT0laT0lcT01YTElYSUNVRj9YSEVkVFBnXWNlXGJkWlhiV1ZjU09kVFBhWFdjW1pkW1Bj Wk9iWlZeVlNdVElWTUNNSj5NSj5PTEZPTEZWV1BdXlddYmNbX2FiVVVaTU1RQTNVRTdUSTtYTj9d U0FeVENYVEhWUUZRSDxEOy9IOSdKOylMPz1QREFXSkpcT09YVElVUEZOTEBWVEhfV1RfV1RoW1ht X11lXl5cVVVRSkFIQTlUSUReVE5jVlRjVlRlW1djWFVhWFdYUE9XTERVSUFaUERjWk1iWFpdVFVd TUlYSEVWTURiWE9lYWRqZWllYV9bVlVaTDxVRzhKSDxUUUVhW1RhW1RdVVRdVVRXSD5VRjxWTU5Y T1BeWltoY2RpZ2heXF1dUExTRkFTRD1bTEVXUEZbVElkVFBiUU5eU0lcUEdTSklVTUxbUE9fVVRj V1hlWltdV1BVT0hTSUBWTUReTEleTElUTEZWTkhRTEdQSkZWTU5fVldiVVNhVFFjV09kWFBkWlRe VE5dVlZbVFRaU0lKRDtUSkFcU0lhVU1aTkZWRTxTQTlRPzpVQz1UUVNeXF1pX2NnXWFkVFNiUVBa T0lbUEpcT0pXSkZVST5QRTpTQTJWRTVeUU9fU1BjUFBbSEhYRUVYRUVXRUNYRkRXTERXTERXTUdV SkVaSkNURT1NQTdNQTdPQ0BVSEZhTUpeSkhhSkZeSERbRjhbRjhUSD9YTURjV05qXlVnVFFjUE5f TUpdSkhfTElbR0VYQz1aRD5aR0BeTEVhTU1lUVFnVlVnVlViVk1fVEpdTUxaSUheSk1fTE5eTEZh TkhlT0pqVE9pV11yX2VqX15eVFNcTDRURC1VPTdbQzxeTEZhTkhlTz5kTj1jSUNlTEVoU0VeSTxc Rj5dRz9bSD9aRz5bRjtaRTpWRDtXRTxTPS1RPCxROi5XPzNdSD1hTEBaSkNaSkNXRzlTQzRTQDpP PTdPOy5POy5TRThdT0FpV1tpV1thVE9XSkZVSkVWTEZXTE1YTU5cSkRdTEVeTk1cTEpdT1FjVVdj XF5jXF5jV1hkWFplVVRnVlVoV1hpWFpjWFVlW1dkWFxkWFxfVVRaT05USkFPRj1ORTxRSD9UUEhc WFBfXV5fXV5nVldbSkxYRTNdSThhU0VlV0lhXlNfXVFaVk5WU0pTTDFHQCdUPSlaQy5QSDlRSTpY TUVcUEhaUU5cVFBUTURXUEdfXFhkYV1nZGNnZGNnXltcVFBPSTpIQzNWSUdkV1VkWFpqXl9hXFti XVxfVVRYTk1YTUVbT0dfV1ZlXVxpXl1lW1phU1dbTVFVTE1dVFVpY2ltZ21oYWFdVlZcSkFUQzpK RjtUT0RdXFhhX1xhWlxYUVRXRj1VRDtRRUVYTExdWl9lYmhoZG1dWmJXTkVORTxPRj1XTkVfVEpl WlBrWlRpV1FiVVBYTEdTRDpYST9eUU9iVVNkWFphVVZeU0lVSUBWSDtYSj1hT0lhT0laTk9YTU5W T1FWT1FeU1RkWFpfVU9hVlBkXVRlXlVoVVNiT01bT1BfVFVaUUxVTUdVUUlYVU1eVFNaT05TRkRT RkRXRTxVQzpeVFNnXFtpYmJlXl5hVFFaTUpXVE5cWFNYUUdVTkRXTkFXTkFcSUdiT01kW2FiWF5k VFBeTkpXRT5VQzxVRj9XSEFVSkVVSkVYTEdXSkZcSUNXRT5VRjxRQzlRQzlYST9oU0doU0dkU0Zd TD9TRC9URTBRSj5bVEdkXVRkXVRpV1BjUUpdTUlhUE1dSkVXRT9aQThbQzlcSEZfTElhTUpkUE5j VE1jVE1iUEpiUEpfTUdbSENcTEpcTEpeTExkUVFoVU9uW1VtW150YmV1Y2ltW2FrVkddSDpUPTBV PjFXR0RkVFBuW1R3Y1x0YV5wXVt0XE5pUURfSURbRT9bRT9cRkBWQDtXQTxYRDlUPzRPNSVTOShU OitbQDFdRz9eSEBkTkZiTERcSjxVRDVaQzlWPzVQOydOOSVTQTJdTDxnWldoW1hpWlFhUUlWSkFR Rj1VSERaTUhhT0ZhT0ZdTU5dTU5YT1BdVFVlWltlWlthVFRhVFRjU1FlVVRnVlVnVlVjWFVjWFVn VVhpV1tiVVVdUFBYRkhWREZOSkdNSUZOSkdaVlNfXV5jYWJkV1NeUU1fT0BkVEVnVlNpWFVqX15l W1pbVElbVElcTj1dTz5qU0FqU0FdU0FcUUBbUUhbUUhfVFVhVVZbT0ZcUEdeXVdpaGJpaWdnZ2Rn WlVjVlFWTUNPRjxaSExtW15qYmlpYWheYV9aXFtfVVFaT0xdTkdlVk9kW1xoXl9jXl1cV1ZdVFdY T1NUT1BbVldrZGltZWppXV5hVVZaTUhVSERJRzxUUUZfW1piXVxfXltWVVFVTD9NRDhORTtbUUdf W15lYWRkXGNcVFtVTUxIQD9JQzpQSUBeVlBqYlxtX1tqXVhhWlBXUEdTQzNXRzhdV1VfWldnXFtk WlhiVERYSjtWTUBcU0ZlVE5oVlBcUVBaT05aTE5dT1FdVFVhV1hhXVdhXVdfXlhfXlhiT0lcSURe TExiT09fT05nVlVnXVNnXVNhVFFcT01VUEZUT0VYST9XSD5lV1xrXWJtYWJlWltbU1FXT05cV1Zf W1pdVklXUERWT0NVTkFcT09kV1djXGFiW19fVVFbUE1XRztWRjpXRTxYRj1VRUFUREBVRURbSklj UElcSUNaRThVQDNQRkBdU01oW1ZrXlpiWlRbU01XRzlURDVWTEhiV1RlW1pkWlhoUUliTEReTkpi UU5fSj9VQDVaQTVeRjphTkhkUUxiT0hhTkdcT01iVVNnU1NkUFBcSURaR0FcTEhiUU5hUE9nVlVq V1FrWFNtWFtwXF5zXmNvW19qWE9dTENaPy5hRjRkUVF1YmJ1ZGV1ZGV3Y2F1Yl9zYlNnVkdeTEZW RD5WQDlVPzhXOTRUNTFWOCpTNCdFMB9QOylVPjFXQDNfSDxfSDxiUUVhUERjTD9iSj5jTj9aRTdU RC1PPylaSDpfTj9nWldlWFZoWFBoWFBcTUNVRjxaTD5cTkBjU09jU09dUVVcUFRWUExcVlFjW1dh WFViVVBeUU1iU0xiU0xjVlRlWFZlWlFhVU1lWFZlWFZiVk5cUEhVSkdQRkNHR0dFRUVERkVRVFNe WltkX2FkXFZjW1VoXVxqX15tXmFqXF5vXVdrWlRlWlBoXFNfV1ZjW1pyYV1yYV1pXltlW1djWFNe VE5fWFtdVlhUTEZbU01pXl11amlpaWtkZGdYVU9bV1FUVEhKSj9USk5jWl1rYmhpX2VeXl5YWFhV UE9VUE9eVE5kWlRkXV1nX19eW1dXVFBdWFdUT05VUE9cV1ZpYmRoYWNhV1hVTE1QSkZWUExRR0FR R0FUU01cW1VfXFZbV1FeT0dVRj5QRkBbUEpiVFhlV1xjVVddT1FcTEpOPj1JPz5USUhjXFxqY2Nk YmFcWlhhVlBWTEZVRj5YSUFcWlhiX15nXV5kW1xfVEhjV0xiV1RqX1xkY19jYl5eWFFQSkRVRUFc TEhXT05eVlVlV1xrXWJvZGNnXFtcTD1YSDpcSURhTkhdU01kWlRoYl1nYVxnWlpfU1NaVE1YU0xY T0ZcU0loWl5qXGFhXFtcV1ZaT0lbUEpeV1dhWlpeVlBWTkhYTkhaT0liUFZhT1VdVFViWFpeVFNY Tk1YTj9VSjxeSEBeSEBVQz1UQTxVSEZbTkxnT05jTEpWRjpURDhaTFBlV1xtZWhtZWhtX19kV1dY TURUSD9eUFNnWFtuW11qV1pkUUxjUEpkUVFjUFBdTD9aSDxfSj9jTkNqVE9qVE9oT0NjSj5YR0pc Sk5cT01eUU9dTEVbSUNdSUdhTUpdUExjVlFkWFBnW1NqV1drWFhqWFxqWFxkV1VeUU9pUUdyWk9v Y2R0aGlzYl5yYV1tYlxuY11uXlRdTkRdRD9YPztNOilQPSxQOihMNSRIMh9JMyBTPCxbRDNeSDhd RzdkT0BoU0RfTkViUEdjUUVhT0NlVEdfTkFbTT9XSTxeT0hhUUpiVVNlWFZlXlRoYVZnWEpfUURi Sj5kTUBiUFRpV1teWltcV1hcVUxeV05iWlRiWlRdVVRXT05YTkheVE5dVU9fV1FeV05bVEphV01i WE5nV01fUEZYTkhUSURJSENHRkBOQ0ZWSk5fVFVlWltnXV5qYWJtY2dpX2NpX2FlXF1nXVBnXVBr Xl5tX19nX2RqY2hpYmdqY2hnX2RjXGFiVlpdUVVeVFxfVV1aT05hVlVnWF1zZGlrZ2pjXmJcUU5d U09YUEpUTEZWT1FjXF5qYmlkXGNcV1hXU1RWT1FWT1FaVFFfWldhWlpiW1taVE9YU05aUFFXTk9V UU5cWFVlXl5jXFxdV1NRTEdPR0FUTEZOQztPRDxUTkdeWFFeV05dVk1OSkNOSkNQTUlTT0xjT1Rl UVZfVU9WTEZNRUFHPzxGPDtPRURaUVhkXGNlXF9fVlpcUFFXTE1TSklRSUhfW15qZWllXmFdVlhh VlNjWFVeV1ptZWhyaWVqYl5hT0ZRQDhOQT1QRD9TSEdVSkldVV5nXmhqYWJeVVZaUERUSj5WRz9b TERdUExjVlFhYWFhYWFkWF5eU1hdUFBdUFBdSkhfTUpjVlZjVlZeVE5dU01dUUldUUlcVVddVlhd UUlYTUVaT0lbUEpeUVFeUVFeUU1iVVBYUE1WTkpcTUNfUEZjUUxcSkVTRz5QRTxVR0ldT1FiUU5f T0xaSEFVRD1USk5lXF9vaGpqY2VpXltcUU5VRj5YSUFkVFBtXFhrWFZoVVNhTk5eTExkUE5iTkxi SkBhST9eSkhiTkxqU1ZqU1ZkTEhiSUZdSUdeSkheTEleTElbTEVeT0hiTEZhSkVeT0dnV09nV09l Vk5nVE5pVlBnVVhnVVhjVVdjVVdvXFxzX19zYmNwX2FuXFVuXFVuY11tYlxrXU9cTkBYQTRTPC9K NCNNNyVOOCRPOSVONypTOy5dRTtkTEFlUEVoU0doVU9kUUxiTUFkT0RkTEVlTUZjU09hUE1hVE9b TkldUE5fU1BdT1RjVVpkXFhrY19tXFhkVFBkVUpjVEllVFdtW15pXFxjVlZiVVBiVVBjWl9lXGJk VlhYSk1YTEdaTUhcU0heVUpeVlBdVU9iVk5jV09oVVVoVVViVk5eU0pUT0VPSkBTSEdVSklYVVFh XVpjXmJlYWRtYWdoXGJoXVplW1dkXFhpYV1qYWJpX2FoXWVpXmdlYmhkYWdoYWNhWlxaVVZXU1Rk VV5kVV5fVlpeVVhlWl1wZGhpZ2heXF1eT0haSkRaTkZaTkZYU05jXVhnY2lfXGJfUVRaTE5YTElb TkxbVVBaVE9hV1hhV1heVVZdVFVWT09cVVVWUVVaVVhjWl1lXF9eWk9QTEFOSTxOSTxOQzpQRTxX TUdiV1FhVlNcUU5MRkRPSUdUSUhRR0ZeTk9jU1ReUD9URjVOQzhJPjNNPTxQQD9WSk5fVFdiVVNf U1BYRkRcSUdRSkpVTk5fW1xpZGVqXF5hU1VaUVBdVVRfWFtwaWtwa2FnYldcSDRTPyxIQDFMRDRM QTxNQz1UUFhjX2hkW15aUFRYTURVSUBRRjtVST5eTExoVVVoXmJhV1teUFNbTU9YTElXSkhaR0dc SUldUUlfVExfVVFdU09bUE9hVlViWFxhV1teTExbSEhdTUxiUVBdTkdeT0hhUE1eTkpdT0FdT0Ff TkVjUUhiVVBfU05aT0xWTEhcSUxeTE5hVE9jVlFdT0FaTD5YUE9kXFtwZGhtYWRrWFZdSkhVRj5c TUVnVlVtXFtoV1hjU1RaTkZXTERfTEliTkxiSUVeRkFdRkVjTEpnUVtnUVtlUVFeSkpiTERfSUFc SUddSkhdUFBdUFBfTEliTkxfTU1iT09jUEpjUEphTkhiT0ljUE5jUE5oVVdrWFtyXV90X2JvW1hr V1VuVlVtVVRwWlRrVU9oU0VbRjlXQTpRPDRQNyZWPCtaQzJbRDNcRTtdRjxhSkZkTklrVFdzW15y V1xvVVpfSUFfSUFfTUdhTkhcT09cT09eTk9XR0hYSEdXR0ZUTEpaUVBjV1htYWJuX2JnWFtnXFhq X1xnXWNoXmRnWF1jVVpiVFZkVlhkW2FpX2VqV1VeTEleTURaSD9WSkFcUEddUUhfVEplWFRlWFRp WFpoV1hiWlheVlVXT0xUTEhYTk1XTUxUU0pXVk5jW1pjW1plWFZkV1ViV1RiV1RhVlVoXVxoYWFf WFhhVVheU1ZjV1thVVhfVlpbUVVUUVNTUFFeU1hlWl9fV2FdVV5jX2hnY2tuaW1lYWRcUEhTRz9d UE5hVFFjWlBnXVRpYmdkXWJjVVdbTU9YTk1XTUxcU1RdVFVqX15pXl1kXV9bVFZWUE5WUE5WUE5b VVNlXF1lXF1oW1hXSkhUSkFORTxPRjxaUEZiT01lU1BfVFdbT1NRQUNTQ0RQREROQUFYTUVdUUld TjlVRjFURDRPPzBJOzNKPDRVSkVYTkheUU1aTUhXRkBXRkBVSUBYTURbVFRpYmJlXFNbUUhXTUdd U01eXF9raW1waWlkXV1YRDlVQDVQPzNTQTVQPjhUQTtXU1ZhXF9jWFdbUE9RSD9ORTxVRDtcSkFi UEpnVU9kV1VfU1BXTERVSUFTRkFVSERXTERXTERaTUpeUU9iV1FhVlBiV1RkWlZpV1tpV1tjU09e TkpfU1BfU1BcTUVcTUViUEpeTUdeTENfTURlVVRpWFdjWFVjWFVeVlNXT0xaSkRbTEVdV1BdV1Bd TkdYSUNcUFRhVVhqXV1qXV1nVU5eTUZWSUVhVE9uX2JqXF5oV1ZiUVBYTURYTUReTEVhTkdkT0Rd SD1cRURjTEpiTlNjT1RhT0ZeTURiSUNhSEFaR0FfTUdkVU5iU0xhTkheTEZdTEZcSkVeTURfTkVh UE9hUE9jT09iTk5nT1BqU1RvVFRyVlZrUVFrUVFtU1NqUFBoT0VqUUdnUUZiTUFfRztdRTlfQzRi RTdfRzpiSTxjSkZiSUVeSk1lUVRtVVh7Y2d7YV1wVlNfTkhdTEZYRj1bSD9bTkxaTUpdSkpWRERU RT5WR0BVTUxWTk1iVFhpW19uYmhtYWdlXVxnXl1jW2RhWGJhVVheU1ZbU01fV1FnXV5qYWJrXFRj VExhTkVcSUBYSUNXSEFaSEFeTUZoVVduW11nW2FkWF5cVVVYUVFaTkZWSkNbTEVbTEVbVkxeWk9o XVxhVlVfU1NfU1NdU01eVE5kVltpW19qXF5cTlBeTkpeTkpfUVRhU1VbUE1TSEVTTUhTTUhXUU9d V1VcU1RbUVNiW11jXF5qYWJlXF1hV01YT0VhVE9kV1NnYldnYldpYV1rY19kXlxeWFZRT05OTEpY TVNoXGJrYmVuZGhiXVxYVFNUUEpVUUxdUExkV1NqYlxoX1poW1ZeUU1cTD9QQDRPSDxYUUVkVFBk VFBfU1BbTkxVTENORTxPPT9RP0FWSkFdUUhnU0BkUD5iUEFcSjxKQTVJQDRWTD1aT0BbUUdXTkRX TERTRz9WSDtWSDtYTVBkWFxhWFVXT0xaSkNdTkZkYWloZG1oY2JeWlhbSTtaSDpaQTRaQTRUQzRW RTdbTU9iVFZfVExXTERQQzVTRThcTUNjVElpV1FoVlBeVlBcVE5WT0VTTEFWRz9WRz9USEBWSkNf U1BoW1htX11rXlxjWltiWFppV1tqWFxqXlVjV05fVk1eVUxcSkRbSUNdTUxcTEpfTElnU1BqWltr W1xiV1RfVVFdUUlaTkZUSj5WTUBdV1BbVU5aUEZVTEFaSE5kU1hlXVplXVppVlRhTkxXT05eVlVl XF1lXF1nV09hUUlYTUVYTUVjT09lUVFnU1BiTkxiSklkTUxjUEpfTUdfTz9iUUFkTkZiTEReTEVj UElkU01jUUxiUEddTENaSEFdTEVfU05lWFRkW1FaUEdhSEVhSEVdSkVhTkhrUUprUUpnTUhnTUhr TEdtTUhoTEBtUEVuVUpuVUptUERvU0ZtVUdqU0VjTkNlUEVjSkdhSEVhTVFlUVZqVVpuWF1yVFBn SUZfU05XSkZVQzpXRTxbSURaSENWSj9USD1TQTxUQz1QSUlVTk5eV1plXmFpX2FqYWJkYmFlY2Jl XWlfV2NjVlRhVFFdVU9fV1FjXFxnX19tXFtoV1ZjWFNdU01aTkZVSUFXRT9dSkVdT1FkVlhiW1tb VFRUTkxUTkxaT0xaT0xiTkxnU1BkWlZlW1dkXV1bVFRWTk1XT05bT0deU0pjV1hpXV5lXVpYUE1Y TEdbTklYUEpXT0lUTURQSUBUSEBVSUFTTUpXUU9aUU5XT0xbVFRdVlZoW1hrXlxoXVpkWlZjW1pk XFtiWlhkXFtkXFhoX1xjZF9iY15XUEZRSkBRSExfVlptZWpwaW5kYVtcWFNWUUdTTkRlVVRqWlhu ZWJtZGFqYl5kXFhhT0hUQzxUTEZfV1FjW1dkXFhhVlNcUU5aVUlNSD1VRD1PPjhVTUdcVE5rX1Rq XlNnXVNfVkxTTT1QSjteU0ljV05fVkxaUEZTTUZQSkRVRzhXSTpUTEpcVFNdV1VbVVNbTklfU05q ZWdlYWJqXVhjVlFiUUNiUUNjUUFiUEBYSjpTRTRcSUdhTkxaTkZRRj5VRD1dTEVoXl9rYmNpX1Zo XlViWlZhWFVdWE5YVElbTERWRz9USkBWTUNlXF9vZWlvZWttY2loXF1hVVZhWF9nXmVtZGFlXVpk VU1fUEhfTURdSkFWTURbUUhdU09lW1dpXFxqXV1lWFRdUExdTkdYSUNTSUBaUEdfVVRdU1FcTUZY SUNcTE1nVldoYl9kXlxkUU9dSkhYSk9eUFVeVVheVVhfUEZeT0VdTEVeTUZlUVFjT09fTUdhTkhk SkZlTEdnUEhpU0pqW1NpWlFvV1ZrVFNkUE5iTkxfT05jU1FkVUpeT0VeTUZnVU5pV11uXGJpXl1f VVRfTkVcSkFhSEFkTEVlUEVoU0dkTTxlTj1tTkNzVEhuVkxyWk96X1iAZV5+Y197YV10YVduW1Fp VlBiT0liSUZeRkNdSUxeSk1jSU5kSk9lTT9hSDtcUVBWTEpYRDlWQTdWRkVWRkVPSD9NRj1RQzxR QzxQRkBUSURRT05dW1pjXl9oY2RjYWJkYmNiWFxcU1ZeVFBfVVFcVFBdVVFiW1tkXV1vXl1wX15p YV1iWlZdVEpWTURbTERhUUlYU0xeWFFbWlRVVE5TSkdPR0RRTUxXU1FkV1dqXV1nX2JpYmRnY19d WlZTT0lOSkVTTUpYU1BhWlxnX2JkW1xeVVZeUVFcT09WTkhRSURVSkVUSURVSEZWSUdXTUxWTEpX SkhYTElbUE9hVlVtWlxtWlxiW11nX2JkXV1iW1tiWlheVlVbVVNfWldbW11dXV9bVEpYUUhXUU9h W1hrZ2pqZWldXlpcXVhcWFNRTkhaVFFkXlxnZGhraW1uaW1iXWFnU1NkUFBYVVFkYV1oYWFnX19j W1pdVVRaU1NTTExbSklWRkVXVVhkYmVqaGlraWpraF9hXVVWU0FXVENlW1VlW1ViWlRXT0lRTkhU UEpeT0VhUUdVTUlbU09hV1tdVFdWU01YVU9dUVVkWFxjVlRkV1VoYVRoYVRvX1VpWk9XTzxTSjhd TkddTkdaT0xaT0xYTExnWlpuaG5rZWtkXlxeWFZeWFZiXFphW1ZbVVBaUUxNRT9USURdU01lY2dt am5vZ25oX2djV11iVlxjXWVlX2htY2RpX2FuVlVrVFNjUUxcSkVUSElYTU5jVVxrXWRuYmNoXF1h VU1dUUleTUdaSENdR0FlT0loVFZoVFZjTEphSUhcUFFlWltnXl1hWFdpUElkTEViUFZoVlxlWFhd UFBeTENcSUBaST1eTkFhT0lfTkhjSkZjSkZjUEdqV05uXldtXVZwXmJuXF93XmJtVVhpUE1pUE1i TkxlUU9kVU5kVU5kU0llVEptXF1rW1xlWFRhVE9WTURUSkFdTEZhT0lkU0xlVE1rU0x1XFV/YVuE ZV+EamWEamV+amR9aWN7Z2l4Y2VyXlxtWldtU05jSUVdRTlYQDRaQT1cRD9eRUBjSUVkTTxhSTlk UE5eSkheST5YRDlYTElXSkhPRUFQRkNRQztQQTpURTtVRjxTTUpfWldnXGRlW2NjWl9nXWNkV1dh VFReTk9fT1BdU1FfVVRjWltnXV5vXWFyX2NpYVtkXFZjWFNhVlBlVEpoVk1bVU5cVk9dVlZWT09W TEpVSklTTk9XU1ReVVhlXF9pXmdpXmdlZGFhX1xTVEpISUBRRklYTVBYWFtfX2JfXV5YVlddVlZb VFRXUEdORz5MRzxKRjtQRkBTSENRTElRTElTTUhRTEdWTU5bUVNlVVRqWlhiWlhoX15kXV1cVVVW T0ZVTkVXU1RXU1RcVVphWl5eU1ReU1RfV1ZtZGNvaGprZGdkXFthWFdaV1ZPTUxaVFFjXVtoaGpp aWtnZGheXF9fWlVeWFRfWlVpY15oYWFlXl5dV1NcVlFWTVBXTlFYTkhYTkhfXV5raWprZ2hpZGVi XFpdV1VfU05iVVBnXFhpXltjW1pbU1FYTkheVE5eV01hWk9cUVBbUE9dUVVdUVVbVVBaVE9YTk1h VlVfWFhkXV1zZWNwY2FuZFtjWlBhUEReTkFdVElYT0VWSkxXTE1bT1BlWltnX2RlXmNoW1tjVlZc WltbWFphVlNfVVFbT0RVST5WTURkW1FyZWlyZWltY2dkW15iVFtjVVxcWGFfXGRrYmVrYmVyYWJq WltdUE5VSEZVREdcSk5jW2dqYm5wYmRpW11iUElfTkdfTURiT0ZeTUddTEZiT01jUE5kUU9nVFFj Wl1jWl1jW1ddVVFoVU5pVk9pVFhqVVpnU1VhTU9iSj5eRztcTD9hUEReT0hiU0xkUUxpVlBrW1py YV90Z2d0Z2d1YWh1YWh5YWJuVldtVEdtVEdoUU1pU05lVVFnVlNoVkdqWElrW1poV1ZnVU9hT0lU TEpVTUxUTEpUTEphUE1pWFVyXl53Y2N+ZWl+ZWl7aGV9aWd+ZWd+ZWd+aG14YmdzXFZrVU9lTkFe RztfRTViRzhlTUNlTUNfSDteRzpdRTldRTliT0hcSUNbSUBYRz5bTEVbTEVYRkZVQ0NRQDtTQTxO Rz1TTEFVTUxcVFNhU1dkVltjWltnXV5oW1hhVFFcUFRXTE9aTlFdUVVjVVdoWlxuX2JyY2VpW11o WlxpXltkWlZvXl1zYmFdW1pYVlVcVFNaUVBVTUxQSEdQSkZVT0pUTEphWFdkYmVlY2dkYmVfXWFX VExKRz9RQzxWR0BOTEpcWlhaX1pTWFNYVU9YVU9VTkVORz5PRDlPRDlKRDpKRDpORkNORkNMRUVK RERQSkhWUE5eVE5nXFZlXVdlXVdiVk1XTENXTT5aT0BbUVNbUVNfVldeVVZfU1teUVpcUFZvY2lu aW1oY2doXF1lWltfWFtcVVdeWl1fW15oZWlua29oYWFiW1tdVVRhWFdhWlxnX2JpX2NjWl1hVlVb UE9XTE1dUVNYU0xWUElfX11oaGVkYmNfXV5hWFNeVlBdU09kWlZyZWlzZ2pjYV9VU1FYU05fWlVi X2NkYmVeVFBbUE1YT0ZbUUhaVE1VT0hUSURWTEZbW1tiYmJwZGhvY2dnYVpdV1BeU0phVU1kWkpe VEVaR0VaR0VVSklfVVRnXFhkWlZnV1BiU0xjUVVfTlFbUUVXTkFbU0NiWkljW1dpYV1yZWltYWRi W11eV1pbVFZcVVddVlthWl5oXmJrYmVrY2JjW1peU0pWSkNUR0NaTUhiWmNrY21vXl9pWFpkVFBl VVFoV1RoV1RhUFFcTE1dUUlfVExlWFZoW1heV1dfWFhlWFhiVVVnWldpXFpnWFtlV1phVFFeUU9n TkdnTkdlVEdoVkllWFZjVlRjVlRpXFpuZGhwZ2p0X2lyXWdvW19vW19uXFNnVUxrVkprVkpqV1Fo VU9qV1VnVFFrVkpwW09pWFpqWltnVU9dTEZTSUBUSkFTST9aUEZjUVVwXmJ4ZGd3Y2V3Yl93Yl90 ZF11ZV55ZGd6ZWh5ZGdvW11tVlFpU05fSDteRzplTUNrU0hpVEhkT0RbSTtYRzldRjpeRztfUEhd TkZeTURdTENhUE1dTUlfTUdbSENWREFWREFNSD5RTUNUSkBXTkRXTERbT0diWE9tY1pqYl5jW1dh WlxbVFZYT1NYT1NbUVNiWFprX2FwZGVzXmVvW2JqYWJrYmN1aGhzZWVrX2NkWFxiVFZfUVRXT05P R0ZRSD9TSUBPR0FUTEZaU1VhWlxjW2RdVV5aTUhRRUBWSTNQRC5JRzxQTkNWVlRUVFFhT1NhT1Nf UEZcTUNTSDpPRTdQRz1PRjxQRTxNQTlKQUNKQUNRR0ZdU1FbWFdjYV9nZGhdW15eVUxVTENWTEhd U09fVlpfVlpdVlZdVlZeU1hfVFpcU1ZqYWRoZGphXWNfVlphV1tiW11dVlhbVldfW1xpZ2hoZWdn XV5eVVZaT0xaT0xaUU5hWFVoXl9iWFpjVlRbTkxbR0xdSU5TTExVTk5fX19paWlhYV5aWldfV1Rc VFBhWlpnX19wZ2huZGVfXVxVU1FVTk5kXV1tZ21qZGpeUU1YTEdYT0ZbUUhXU0hTTkRPSUNTTUZa V1tnZGhrYmNoXl9dVEpeVUxeWlhnYmFrXlpbTklTQTtTQTtVSkVcUUxjWFVlW1diWlZhWFViVVNc T01WVEhcWk5nY19pZWJrZ2hvamtrY2plXWReW1VXVE5XU0dcV0xiV1FjWFNjV1hrX2FwY2NtX19j XFNYUUhWTEZiV1FuYmVuYmVyZF9rXlppXltqX1xoXVxkWlhhVU1dUUlfVVRlW1poXl9lXF1fV1Re VlNkWE9nW1FpX2NrYmVlWFhfU1NjVlRkV1VoUE9qU1FtWlNyXlduYV5nWldkWlhrYV9uZGVtY2Rq V1plU1VkU1hpV11qXVtqXVtyXl5yXl5uWlxpVVdoVVNpVlRuW1VvXFZpW2JiVFtdTkdRQzxNQTpT Rz9eTENnVEptXFt0Y2J5ZGd0X2JuXVpuXVpwX1x1ZGF5ZGRyXV1pVlhnVFZrVU1tVk5fSUFiTERi TUFkT0RiUUNcTD1bST1YRzthTEBnUUZdUUleU0pjUEliT0hhUE1hUE1fT1BfT1BaSENaSENTSENV SkVUSUhXTUxYTU5cUFFoXVxuY2JqYWJnXV5iXVxbVlVcVFNbU1FWVFVeXF1qYWRuZGhzYmNwX2Fu YmNzZ2h0amtwZ2hzX19tWlppVlRhTkxbT0dYTUVRSD9RSD9QRztTST1WSkxeU1RhVVhfVFdcUEhY TUVVSUBUSD9NRjpKRDhRTEVTTUZcTEpfT05hU0ViVEZbUUhUSkFUR0VVSEZXR0ZVRURUSUZQRkNR R0ZeVFNiXGJqZGpoZGpcWF5VVFBWVVFeV1peV1pkWFxjV1taU1VbVFZfVFVfVFViVlxnW2FlYWRi XWFdVFVdVFVeV1pYUVRVVVVaWlplYmhkYWdeXF9UUVVPTkpRUE1cT0pfU05jWltjWltjW1pcVFNj T09iTk5PSkxaVVZiX2NnZGhdWlRXVE5aVFFcVlRiXVxlYV9rY2pkXGNdVlZYUVFbUE9lW1pvYmJu YWFfVEpbT0ZhVUlhVUlaUEdWTURPSj9TTkNfW1pkX15lXVdhWFNYTkhcUUxYVlpoZWlpYVtXT0lW SDhXSTlXTkFdVEdhXFtlYV9oYWFnX19hV1hcU1RaVlNhXVpqaGlraWpybXBybXBqZGppY2ljW1VY UEpbVEpjXFNlXlViW1FhWFNlXVdyX2N3ZGhtXmFfUVRcT01lWFZtXmFuX2JvZGNvZGNtYWJrX2Fn XFtiV1ZkVUpiU0hjWl1qYWRpX2VlXGJfW1BbVkxjW1dtZGF0aGttYWRkVFNfT05jVVdjVVdnVldr W1x1ZGV4Z2hyZ2VuY2JrYmNtY2RtY1piWE9fUElhUUpjVlZpXFxvXF5rWFtvWl5vWl5nVU9lVE5j UE5oVVNpV1tuXF9pW19iVFhcT0pVSERVRj9XSEFjT01lUU9tXFtuXVxwV1BrU0xuVU5yWFFtWlRv XFZuW1FpVk1lVVFoV1RrWFZnVFFdTTpeTjtlUENlUENiUUViUUVhUERjU0ZpWFVpWFVfT0xfT0xi UEliUElkVFBkVFBiUVNeTk9cU0lbUUhiT01hTkxeTkpfT0xaTlFdUVVtYWRvY2drYmVrYmVnYV5e WFZdV1BdV1BeV1pjXF5nX2JqY2VoXl9lXF1oYl9vaWdza25vaGprW1poV1ZjVlFcT0pcUUxdU01Q TEFRTUNaTUhbTklXTE1bT1BYTU5YTU5eVE5eVE5YU0xXUUpUSTtTSDpXRj1bSUBYT0NaUERaV0pe XE9cV1ZWUVBQSkhOSEZUSEBRRj5USD1TRzxVRj5fUEhhWF9nXmVeWlhWUVBPT01UVFFcVVdcVVde U1RdUVNVSkdXTUlaUVBbU1FdVFdhV1tiWFxoXmJcVVVYUVFUU01NTEZWU09VUU5eV1xkXWJhWlxX UFNNSklQTk1fT1BiUVNoWlxqXF5lX11hW1heUU9eUU9WTEpfVVRjXVtjXVtfU05XSkZTTk9YVFVe WlhkX15jWl1iWFxeWFZbVVNbUVViWFxwZGVuYmNdV1NcVlFpYV1jW1dhTkxcSUdXSEFaSkReW1Nj X1djXE9dVklVT0hVT0haV1ZkYmFpXVRcUEddTENjUUhnVU9qWFNiW19kXWJqX2hrYWljXF5dVlhb VVNfWldtZG5uZW90a3hzandvZG1uY2trW1dkVFBoWlxrXV9nYV5jXVtfWldoYl90am5zaW1uWldi TkxbT1NhVVhuYmV0aGtwYmRuX2JwX15qWlhjWFNeVE5jVE1oWFFrYmV0am5qXmJiVlpfVU9nXFZt Y2d0am51ZGVtXF1cUEhcUEhhVVhfVFdhV11rYmh1aW14a294a21yZWdyZ2VtYmFlXFNfVk1dUUlj V09uXVpvXltrXlxqXVttWF9oVFtiUEpkU01nVE5kUUxoVlpvXWFtWlxnVFZkTkZdRz9aSDpYRzlc SkVfTkhlU1BlU1BqUUptVE1vVU5zWFFuVU5tVE1qWExpV0puXVxtXFtwVlhrUVRjTkBjTkBnUURq VUdrUU1tU05rVFNyWlh1X2RzXWJfTU1dSkpdTkZfUEhiT01kUU9nU1NjT09bU09YUE1eUU9eUU9j U1FlVVRkVFVpWFpnX2JrZGdqZGpoYmhlXVphWFVeVlNfV1RhVFRoW1tkX2NnYmVlXF1jWltiXFpo Yl9tZ21uaG5pXltiV1RjWFdfVVRYT1BaUFFaU0lYUUhYUUhYUUhdTUlYSEVVRDhaSDxcUU5nXFhl XF1kW1xhUUleT0ddTENeTURfVkxbUUdYV1FeXVdfW1xXU1RORkBHPzpRQzlQQThTRz5XTENNR0BO SEFWTVNiWF5hVlBVSkVNTEhPTkpYUVRXUFNQRkBQRkBORTlQRztMRz1OST9UTEpYUE9eWlheWlhd VVFYUE1RSkFORz5USUZVSkdaU1NbVFRfWFhcVVVVTUlQSEVaTUpeUU9jW1dlXVpqXVtoW1hcUUxc UUxaT0lWTEZiVVNiVVNhUFFdTU5UTkxWUE5aUFFeVVZqWFNpV1FqW1RiU0xcVVdfWFtvZWluZGhe XF1kYmNoaGhcXFxWTEhOREBWR0BbTEVfWFhjXFxuYVxpXFdeWk9aVUpeWlhkX15rXlpnWlVnV1Br XFVtXFtlVVRdWFpjXl9qX2hoXWVnVldiUVNaUUxeVlBnYmVtaGttZGtqYmlwZ21zaW9uYWFtX19p YmdrZGlqXmRnW2FkXWJtZWpzanJzanJkXVRTTENYTVBoXF9uZGpyaG5uZWRtZGNwX15uXVxtXVZn V1BiWlhqYmFza25waWtkWlhbUE9YU05fWlVkY2hlZGlrYV1jWFVjU1FlVVRhU1dfUVZkVV5rXGVy ZWt3anB1aWpwZGVuX2JqXF5pWFVoV1RpWFVpWFVrW1pvXl1tX11uYV5tWF1oVFhjSkZfR0NeSERf SUVjUFBuW1twWlFpU0plST5eQzhXQzVbRjlWRjpbSj5pUExrU05uV09zXFR1Ylx1Ylx1Xlh1Xlhw XVdtWlRnU1duWl5zWFtuVFZkUUpkUUprU05vVlFrUVFtU1NuVlVvV1ZvXFpwXVthTUpYRUNcSUNh TkddUExhVE9fU1BeUU9hUE1eTkpcTlBcTlBiTlNkUFVnVVhtW15jX2VqZ21yZWtuYmhpXFpiVVNd VVFYUE1hTlBqV1ppX2VtY2lkW1xkW1xnW15tYWRoZGpraG5uZV9oX1piXlteW1dfT1BbSkxfTUpj UE5jU0ZlVUhhUERbSj5XRzJXRzJbVVBnYVxoXmRpX2VkV1ViVVNkWFBoXFRqXVhfU05cVVVdVlZc U1RTSUpPQDlKPDRRQTVRQTVQRkBUSURURT1VRj5WSkxfVFVfVExWSkNVSkdXTUlbVVNXUU9RSD5R SD5NRDtJQDhKQTlRSD9PSkBTTkReVFBcUU5YV1FVVE5UT0VPSkBTSEdYTk1cUVBcUVBfUVRkVlhY UUhTTENjUUhlVEpoXVpuY19lXVxiWlheVUxbUUhcT0pUR0NdU09iV1RdV1NdV1NbUUhaUEddTUlk VFBqWlZvXltvXVdpV1FdWlZbV1RkXGNrY2pjYV9tamlubmtiYl9VTkFNRjpRSD5bUUdnXWNtY2l1 amlwZWRpXltjWFVeV1phWlxuX2JrXV9vYmJzZWVyYV9pWFdkWFxtYWRoY2RnYmNlWFZhVFFlVk9r XFVpX2NvZWlqY2VoYWNuZ2lyam1taGtuaW1uZW1tZGtlWl1kWFxoXWpuY3BvZ3BuZW9iW1FVTkVd UFBrXl5yY2hwYmdtYmFtYmFqX15tYmFtXFhrW1dkW15oXmJvZWtnXWNjT01eSkhfV1RoX1xyZG1v YmpqWlZoV1RiVFZkVlhhU1diVFhpW2JvYWhyam1za250aGlpXV5oVlpkU1ZlU1BqV1VvXFpuW1hr W1xvXl9uYmVtYWRpWFVfT0xbSD9VQzpVRDtYRz5eTE5kUVRlVE5fTkhfRDhdQTVaQT5cREBcRkBk TkhqV1FzX1p6ZWN7Z2R0Z2JyZF9vXFxwXV1yXl5rWFhqU1FqU1FuV1NqVE9lU0loVUxtVFBrU09q UFBoTk5tVE9zWlVwWlFuV09hTkhYRkBYRj9bSEFeTkpfT0xdVVRdVVRiUU5iUU5eVFBeVFBeUFVe UFViV1ZqX15rYWltYmprYWlnXGRkXFhhWFVfU05bTkldU09kWlZoYWFtZWVpXl1oXVxnW15tYWRo Z2tqaW5tamtraWpnZWpdXGFiVVBaTUhfT0xlVVFnV1BkVU5jVExjVExbU0BcVEFiW11qY2VyaGtw Z2poX1xiWlZoWl5tXmNrYV1lW1deV1dcVVVTT0xNSUZNQTpIPTVQQThURTtYR0BbSUNVRjxWRz1U TklYU05cVFNVTUxYSk1bTU9eW1NaVk5USD9PRDtMQTxKQDtOPThTQTxORz1TTEFXTUdaT0ldU1Fa T05VSkdUSUZWUExaVE9iV1ZhVlVeVlBeVlBcWk5aV0xeW1NlYlprYmVuZGhlXF9fVlpYU0xYU0xX UEdTTENcT0pqXVhrZWFjXVhfVU9aT0laSkNnV09lY2dqaGttY2RrYmNlXmFeV1poYWVqY2hrZ2p0 b3N0b3BqZWdaVUlOST5RR0FdU01lYmpva3R3bXB0am5oYWFiW1tcWl1cWl1lXmNoYWVzaHBwZW5w YmdoWl5lXmFrZGdqZWdlYWJkW15iWFxlXF1oXl9wY2tvYmpoYWVpYmdraW1wbnJta3Bram9vaGpt ZWhlX1hiXFVlXGJrYmhqY2VlXmFcVUxWT0ZdU01oXVdoXF1pXV5pX2FrYmNwZGVwZGVoW1hlWFZn W15rX2NyYWJpWFpkUUhnVEpoXmJtY2d1aHBuYWlqV1VqV1VjVlZkV1djVVpnWF1oX2tuZXJ3b3J1 bnB5ZWNnVFFjU1FiUVBpVlRwXVtzYmNvXl9uXVpvXltoXl9oXl9lVk5WRz9UQzdUQzdTQzdaST1f TUdhTkhfTUdbSENbRjlYRDdcREBfR0RdSkhlU1ByXmF4ZGd6ZWh5ZGdwXlhyX1pyXWJ3Ymd1YmJo VVVhUUpfUElnVlVnVlVrWlBqWE9pV1BkU0xeSENiTEZoT0hzWlNvWFNrVU9jT01cSEZaSD9cSkFf T1BiUVNfUVReUFNfU05jVlFfV1ReVlNeVlVdVVRfVVFkWlZoXWhrYWtrYWlpXmdnXFtkWlhjV09h VU1fWlNiXFVlXl5pYmJuY19vZGFtXmNqXGFqY2Vyam1wcHBwcHBqaGdjYV9nV1BfUElbU09kXFht Yl5oXVplXVpiWlZfWlNfWlNlXF9wZ2p3am53am5uZV9qYlxqX15qX15rYV1qX1xiYVtcW1VWTURJ QDhJPjVIPTRMQzpRSD9fVURkWkhfVEhcUEVUUVBXVVRdVU9aUUxXSkZbTklWVlRYWFZcTUNURTtI QC5IQC5JPCxPQTFVSjxXTT5aSkReT0hbSkdYSEVVSklXTUxYU05eWFRjVlRhVFFeVlBeVlBaVlNf XFhiYmJpaWlwaHRwaHRpW11jVVdaU0lcVUxaVE1RTEVaVFxoYmpuaWhkX15kXFheVlNcVk9pY1xv c3ltcHdvZG1vZG1qZ2NiXlthXFtpZGNoZ25zcnl3a3RtYmpeXE9WVEdTUEViX1RvZ250a3N4a21z Z2hlYV9jXl1eW1deW1djXF5uZ2l0aXJyZ29yY2hnWF1lXmFqY2VqZGpnYWdnXGRnXGRkYWtoZG9u ZW9vZ3BvYm1wY25rZWtwanBtaXJuanNzaHNwZXBpY15iXFdjV1hjV1hiWFpdVFVdVEpXTkVbUUVk W05nXFttYmFzZ2p1aW11aW9vY2lkWFBiVk5pXWNvY2luZV9qYlxpYV1pYV1yaGtuZGhvXl9oV1hn VU9oVlBnXVRlXFNnWF1rXWJtZHByaXVuaGVqZGJrW1dlVVFhVlNkWlZtX19vYmJvXFxlU1NiVVNk V1VuXV5qWltoU0VbRjlXQzhWQTdaRTdjTj9jUEdlU0ljSkdfR0RaQzdbRDhbSTthT0BfTkhnVU9v Xlt1ZGF0YWFvXFxuWlp1YWF+a3J6aG53YWVqVVpfUEleT0hkU1hvXWN1ZGF0Y19qXE5dT0FcREBi SUZrVU93X1p0W1ZuVVBjVE1eT0hfTkViUEdnVlVnVlVeUU9eUU9hUUpiU0xfVU9fVU9fVVRfVVRa VFFcVlRfV15iWmFqXmJpXWFnWlpoW1tiV1ZfVVRcVVViW1tlXmFrZGdraWpraWpqYWdnXWNqY2Vu Z2lzbWp1b21yaWhvZ2VpXltkWlZnXFtpXl1pYV1rY19lYWRkX2NpYV1lXVpkW1xpX2Fza2t0bW1t Z2JtZ2JtZ2RpY2FtZGFrY19uaGNpY15fVEhRRjtQQDFOPi9KQDtUSURfXVxlY2JkYVhdWlFTUUxa WFNiVVBdUExcT09bTk5UVFFaWldeU0lbT0ZMRDJMRDJRQzlURTtaSkRdTkdeTEldSkhYTURVSUBT SENYTkhbUVNeVVZiV1ZhVlVcVFBdVVFVVFBcW1dlZGtwb3dzaHBuY2tkU01dTEZXSkZYTEdcUVBa T05bVV1pY2trZWtkXmRnW15iVlpcW1dta2h1dHlwb3RtZWprZGlpZGNfW1pfV1RoX1xkZGdtbW90 amtpX2FdVlZWT09aVlNkYV1vaGpwaWttY2doXmJfXV5eXF1iWlZkXFhqX2h1anN6bnR3anBwYmRl V1pjWl1rYmVqZG1lX2hhV11iWF5kXGNoX2dtZ21vaW9wYWppWmNjW2JnXmVuY25tYm1nZHJnZHJr YmNlXF1iW11iW11eWFZUTkxXTkVaUEdeWFRrZWFzZ2h3amt7bnl3aXRzZ21rX2VkV1dqXV11Z2tz ZGluZWRvZ2VqZWdoY2RuZGhtY2dpWFVnVlNkV1VnWldrYV1qX1xqXGFvYWVzX2pzX2pwZGVrX2Fo XFRjV09hVlNjWFVrXlxpXFpnU1BeSkhbSkdhUE1tWFtrV1pqU0hjTEFfSj1dSDtoT0htVE1vXltq WlZkTklfSUVcRzlaRTdWRjphUERiT09tWlpwX2F0Y2RyXV1rV1dvXWF5Z2p+am13Y2VvW1hnU1Bk UUxlU01qVl9yXWd0YV5uW1hoUUBdRzdkRkdvUFFrVFNzW1p0W1dtVFBhUUpfUEldVVFhWFVnWFtn WFtjV09fVExdUUheU0ldU01dU01bTk5XSkpWTkpTSkdYTkhbUEpeU1RiVldiVVVlWFhiVVVcT09Y U05eWFRkWFxwZGhqZ21pZWtoYWVpYmdqZWdqZWdvY2d1aW1vZ2VuZWRpYV1qYl5rYV9pXl1pY15q ZF9uYmNtYWJqXVtqXVtnYVpqZF1wa210b3BzbWp0bmtva2hraGRqY2NuZ2d0am5tY2dkWlRcUUxb UD9cUUBPTEZUUEpfX19kZGRpZGNeWlhWT0ZYUUhdUUldUUlaUU5XT0xYUEpcVE5fWE5cVUpVST5U SD1WSkNcUEheVFBfVVFjT01dSUdTST1ORTlVSUBbT0ZiV1RkWlZjVlRkV1VVT0pWUExXV1dfX19o Z25ta3N0bW9rZGdlU1BYRkRaR0dfTU1fVU9YTkhjXF5uZ2lqZG1eWGFnW15jV1tdWlRybmh3dHhw bnJqYmtoX2loY2RiXV5fWFhiW1tlYmhuanBuZ2diW1tYU0xUTkdbVlViXVxoaGpjY2VlX1tiXFdd W1pbWFdkV1dtX19ya3R3cHl6cHdvZWtnW1xeU1RhV11oXmRiXmdbV19aUFFaUFFfVVRjWFdnYWdr ZWtqXmJjV1tiVldjV1hlXmNpYmdnZW1kY2ptY2lqYWdlYl5eW1dWU01PTEZWTk1kXFtyaXN6cnt5 b3N3bXB5a3R3aXJ0Z29tX2hqXl9uYmN1aHB0Z290aGtwZGhqY2VnX2JtY2duZGhtXFhpWFVuXVpy YV1yX2N0YmVuYmhyZWtyY2hzZGl1Yl9zX11oVk9hT0hkTklpU05oTk5qUFBlTkReRz1bST1jUUVo VVdnVFZoUFFnT1BnT0VrVEl3XFx5Xl55ZGduWlxiUEFbSTtXRjpbST1bSUNkU0xkV1dpXFx4Y2Vz XmFtWFZwXFp0Ymh6aG57aW10YmVrV1doVFRoTk5pT09pVVptWF1yWFVoT0xkSDFeQyxlRkRuTkxp T1FqUFNnU1BiTkxcT0pYTEdfT1BkVFVeXF9fXWFfWldaVFFaTkZYTUVcUEhaTkZXSkZXSkZWTUNR SD5XRUNbSEZlT0lqVE5pV1FqWFNhVE9cT0pUTkdXUUpfWFhnX19qYmltZGtuY2ttYmptZWhuZ2lu Z2tyam9ybWtvamlqYWJrYmNuY2JwZWRvZWduZGV4Z2h4Z2hqX1pnXFZiXFdlX1tqZWlzbnJ5cnd3 b3R3bXBwZ2pqXV1yZGR9cHR5bXBrYV1lW1dhVUliVkpcVk9YU0xeXF1nZGVlX11WUE5XTUdYTkhY TEdYTEdUTkdUTkdTTExVTk5dU01bUEpXTERdUUlkV1dnWlpnYV5fWldjU09cTEhYTURTRz5VSUBi Vk1jW1dlXVppXFdhVE9WTkhaUUxaWlxjY2VpZ2hua21paWdhYV5kUVFbSEhVSkdbUE1iV1RhVlNv Y2dyZWliW11cVVdYT1BkW1xtYWd5bXN3b3Ryam9lXF9iWFxjW2JiWmFeWlhcV1ZiWFxpX2NjXVha VE9cUEheU0peVVZjWltpYmdnX2RoXl9nXV5oX1xhWFViWFplXF1vZ3BwaHJzZGdpW11fVVFbUE1c UFRnW15kX2NcV1tYTkhWTEZXT0lcVE5kW2FlXGJfWldeWFZhVVhiVlpnW15pXWFoX2dpYWh1Y2t1 Y2tuYmNnW1xiV1RcUU5hVE9zZWF5coB7dIN6cHR4bnJ9bXd4aHJzZW5pXGRqXGFuX2RwYWpzY210 aGlwZGVqYWJqYWJrXWJtXmNqXF5vYWN4ZWl+a29yY2hrXWJkXWJlXmNpW2JuX2dtW1VnVU9nUEhi TERjSkRkTEVoTkppT0xpU0pnUEhnV09nV09vV1ZwWFdoVVVjUFBnTklrU05uXF90YmV6YmFyWlhl VEdhT0NjUEdjUEdlTkRoUEZqWFNwXlh0W1dzWlZtVVR0XFt1YWV3Ymd3Yl9yXVtnVU5hT0hcSURd SkVjTUhnUExnTklnTklvUEZzVEl4VEp6Vk1zVk1pTURfTUdeTEZlTUZiSUNhTUplUU9hV11jWl9f VFpXTFFWTEZWTEZaU0lbVEpYUEpWTkhTSUBRSD9YR0FcSkVhVFFnWldrY19nXltpWFVhUE1YTU5d UVNfVV1lW2NoXmJrYmVuZGptY2lvY2RzZ2h0anB0anBzaW9vZWtrZGduZ2l3ZWd5aGl3aG10ZWpy ZWdvY2RlW1plW1pjW1pnXl1raG5wbXN0cHl4dH13b3RqY2hrYV9vZGN5bXN5bXNzYmFpWFdfWlVc VlFcW1VbWlRjXl9nYmNkW1FcU0lbTkliVVBhVlBYTkheT0hdTkdYSk1WSEpbSEhbSEhWTEZYTkho XGJuYmhrZ2pjXmJfVVRfVVRcVE5VTUdjUFBqV1dtXmNzZGlnZ2RdXVtXVFBXVFBbWl5hX2RoYl9z bWptamllY2JhWFNcVE5eUU1eUU1cUVBhVlVkX2NpZGhfU1BaTUpcTEpnVlVtX210Z3R5bXByZWlf V1FeVlBfVFdfVFdfV1FdVU9iVVBpXFdoX1piWlRqV1dlU1NnWlprXl5yX2NwXmJrX2NvY2dqY2Nk XV1fWFtiW11qZG1rZW5pYmJfWFhUT0VTTkRYUEpeVlBfWldhW1hcVE5WTkhXT0xaUU5fWFtkXV9l XVxkXFtkV1dkV1dfWFhkXV1nXWFuZGhvZG1vZG1uYmhoXGJkVlhfUVRhVlVyZ2V3cHt+eIN9cHd6 bnR+bnh5aXNyYmtnV2FlWl1uYmVuZW9waHJwaGdzamlvYl9rXlxpX2NoXmJtYmp4bXV6bnJ4a29v YWNlV1pfU1BlWFZiVVNlWFZqWExnVUhrUU1pT0pqT09uU1NzXmF1YWN3X1pzXFZvXFpyXlx1XGJz Wl9tVlBlT0lfUEllVk9yXGF0XmN3XVpvVlNqUE1pT0xlT0doUUlpUUdvV01wXVZ1YltyXV1tWFhl U1BnVFFtWF10X2RzWlVyWFRlVEdeTUBfSUFeSEBlTEdrUU1vVVVzWFh7X2J/Y2WDaGN7YVxuVkxt VUpoTkllTEdhTkheTEZcSURfTUdlU11kUVxfUVZXSU5YST9aSkBdVEphV05dVEpYT0ZRSDxPRjpX SEBXSEBdTE9qWFxwZ21qYWdkWFpdUVNaTlFeU1ZcVVdhWlxnXl1pYV9qY2NpYmJpX2NtY2duZW1u ZW1rYmVrYmVrZGduZ2lyaG50anB0amt0amt0amtuZGVrXWJrXWJoYl9lX11uaHBzbXVycHp3dX93 a3RtYmpkXlxnYV5ybW5ybW5yZF9lWFRjWFddU1FbWFdeXFtrZWtnYWdkWFBkWFBhWFVoX1xiWFpc U1RdUExdUExdU1FXTUxcSUNcSUNVTEFXTkRkWmRrYWtpZ2pfXWFeWFZfWldnXFtYTk1hT1VvXWN4 b3l6cntvbXBhXmJdUUldUUlWV1NVVlFhXl1qaGduY2JrYV9kWlRjWFNlWlFeU0pVTENcU0liWmFi WmFcT0pTRkFVSEheUVFjXGFoYWVvZG1tYmpkXlpcVlFeUU1cT0pdVEpbUUhcTEhlVVFuYmNyZWdy ZGJwY2FyY2VzZGdoX15qYmFkXV9vaGpoZWdlY2RhXF9dWFxjYWRpZ2plZWNfX11bVkxXU0hYTk1Y Tk1fTFBkUFVdUExdUExWTk1XT05cVVpiW19oXGJnW2FkWFxhVVhdVFViWFpjXF5pYmRvaG1waW5v ZWlpX2NkV1VjVlRqVl15ZGt5cHp6cnt4b3lzanR7a3h3Z3NqWGFlVFxnXWFtY2dvYm1zZXBwZ2hu ZGVoXVplW1dpXWFuYmV5a3d7bnl5bW5zZ2hoW1ZhVE9lVE5oVlBjU09pWFVvXFZvXFZwWFpvV1hy WGF3XWV6ZHB9Z3N7Z2R1YV50YWF1YmJ4X2FvV1hkTEVfR0BhUFFqWlt3YWV0XmN0V05qTkVpUE1q UU5oVU9qV1FtW1V3ZF51aGhyZGRtXFtnVlVlT0dpU0prWFhvXFxwXlduXFVtW0xpV0hiTERfSUFn TkpwV1R7X2t/Y2+CaG6EanCCbW19aGh4XVpzWFVyVUluUUZoV1hhUFFaSUZdTUliUFhjUVpiVVNa TUpYTjxbUD5eVFBfVVFdVEpeVUxWSUdYTElYTU5YTU5hUFFqWlttYmptYmpjXl1bVlVRTEdPSUVU TUNYUUdeVlBkXFZkX15nYmFpYmRqY2VpXmdtYmpwYmdtXmNrX2FrX2FtaGluaWp0bXJ1bnNybW5u aWpvZG1yZ29waW5uZ2tuaW1ybXBranJycHh4bnJrYmVkXV9jXF5qaGdtamlvaF5lXlVdWlRXVE5a U1ViW11oZG1oZG1pXl1tYmFzZGd1Z2lpYV1iWlZnXFhoXVpiV1ZcUVBbT0ZcUEdcTUVdTkZfWl9o YmhrZ2pjXmJoVlprWl1rW1pfT05hVlV3a2p4cHN4cHNzbm1kX15hT0ldTEZVT01bVVNjV1tqXmJt Y2RrYmNtY2RvZWdwY15jVlFVTEFVTEFbU1peVl1VTE1RSElNR0VXUU9aUFRkW15uX2dwYmlpZGhj XmJdWFdbVlVcT01cT01fT05tXFtwZ2p3bXB5bXB1aW15am96a3BvamtrZ2hkXV9rZGdpaWlnZ2dk YmNdW1xjYWJua21wZ2puZGhrY11kXFZcUEhbT0ddUExiVVBiVVNkV1VdV1VbVVNaU1VaU1VcU1Ze VVhjWl1fVlpiVldjV1hfWF1qY2hwaHRzandzbm9rZ2hoX1xjW1dpX2VzaW96cHd3bXNzaXltY3Nz Y29vX2tpW19nWF1nWlpuYWFpXV5vY2RyZ29rYWlnXV5kW1xoXmJrYmV0aXJ3a3R1amlwZWRpVVNn U1BiUU5iUU5pVFhwW193Y2VvXF5rWlRtW1VyXGV0Xmh7ZXJ+aHR7aXJ5Z291aW1zZ2pzWlNlTUZe RUFjSUZnVE1uW1RvW1hrV1VrVElvV01yV1x0Wl5yYWJyYWJ0YmV4ZWl5ZGl3YmdtXVZoWFFoT0hu VU5wXV15ZWV+bW57amt7aWJwXldnTUZiSEFwVVx1WmF9YWWEaG2Ea2+GbXCCbmh5ZV94X1VwWE5t VEZlTT9oWlxfUVRXUEdYUUhaUFRaUFRbUVNWTU5VSUBYTURcUVBeVFNbU1FaUVBaTU1aTU1dU1Fi V1ZjVlFpXFdrYmhrYmhjXl1bVlVUTklOSERUTEpXT05eVFNiV1ZhV11lXGJtYWJuYmNpYWpqYmtu YWFhVFReVE5eVE5nX2JvaGpwbXVybndtZ29nYWlnXGRnXGRqYWRuZGhpY2luaG5wanV3cHt6c3hv aG1lV1phU1VlX2VtZ21raGRnY19eXlFVVUhYU0xeWFFkYmVqaGtqZWRpZGN4ZGd5ZWhtZWhuZ2lw aG9vZ25pXFdiVVBeWFFhW1RiU0phUUlfWl9lX2VtYWRnW15rXWRqXGNnX1ZcVUxhV1t4bnJ6dXdz bm9zbm9nYmNiVVNcT01WUE5bVVNiVFhnWF1rYmVtY2dua29yb3N0am5iWFxWRz1WRz1aTk9dUVNb UE1TSEVKRT5RTEVbTkldUExlWFhqXV1jYWJiX2FfXV5bWFpdUE5dUE5eUFNvYWNwbm9zcHJ1bm5y amp9bXd+bnh3bXByaGtoYWNoYWNkY2plZGtoXmRkW2FnX2RwaW53Z3NyYm5rYmNlXF1eU0lfVEpi Vk1lWlBkXFtnXl1jXVhWUExTSEdUSUhaUFRdVFdiVlphVVheWFZcVlRXV1VfX11oZXNpZ3RubXJo Z2tnYmVkX2NqZG1ya3R3b3R3b3RwZ21oXmRpX2NoXmJpYV1nXltlWFhtX19oY2dvam50am5uZGhn XFZoXVdqYWJuZGVybnR1cnh1bWduZV9vWFNqVE5eU0pjV09tWlxzX2J1Y2dwXmJrXFVqW1RyX2Nz YWR4ZWt9anB6bXV5a3R1Z2lnWFtkTD5iSTxjSUNnTUZtVE9yWFRtWFZrV1VtWlpzX194Y2N6ZWV4 YmdwW19zYWd5Z217Z254Y2pwWlFvWFBqUFVyV1x3YW9/aXiAbW19aWl7ZF9qVE9pSUhpSUh1WmF+ YmmEaHKJbXeHbXV+ZG17YWF7YWF3XVhzWlVuV0ZjTTxlW1ddU09aUEdaUEdWU09WU09hVFRcT09a SkRbTEVYUE1YUE1bU09aUU5XT0lWTkhWUElbVU5jWFdpXl1oX15pYV9lXVxiWlhhT0hXRj9WSUlb Tk5eVFBeVFBcVVdhWlxuWmNyXWdpZGVnYmNlXVxcVFNcUU5eVFBpXWNyZWtwanNtZ29qYWRjWl1p WFpoV1hnXWFpX2NoX2ltZG5vZG93a3d5cHhwaG9rWFhnVFRkWmJuY2tva3JpZWtiY1pcXVRXVFBV UU5dXV9jY2VoZGppZWt1Z2tzZGlvYm1uYWtuanNva3RvYl9uYV5qYl5uZWJjV09YTUVcV1ZnYmFo Xl9lXF1oXmJpX2NkYV1bV1RjXF53b3J6cHJ3bW5ubm5hYWFkXFhdVVFdT1FdT1FWUExWUExfW1xr Z2hzc3VwcHN1bnNkXWJYSUNcTUZWUVBUT05fTUdbSENQRTxYTURVTUdXT0lYVVFeW1dlXmFjXF5j Xl9kX2FdUE5fU1BlWl9wZGpvam5pZGhoY2JrZ2V1a3J6cHd3b3JwaWtoXVpjWFViW11iW11lV15n WF9oWl5rXWJqXmJtYWRoZGFnY19eWk9eWk9eWFFhW1RqY2NuZ2djW1dbU09WSk5WSk5cUFRfVFdj VVpiVFhhWFdcVFNYVlVaV1ZhYWNjY2VqZG1qZG1oX2dpYWhrY21uZW95a3d4anVuZGpoXmRlXmFn X2JqY2VqY2VnXmVoX2dnaHBub3hzaW1qYWRoXlVrYlhzZGl4aW54cnp3cHl1Z2luX2JkV1dkV1dl W1ppXl1rYmNuZGVvZGFtYl5lW1VnXFZzYmN3ZWd4ZWt7aW97aW95Z21vYl1kV1NoT0FnTkBrU09z WlZ1XFd1XFduWldwXFpzZGl3aG10Z2RyZGJwXV1zX19yX2N4ZWl5Y213YWp0YVpyXldtWlxvXF5w ZXB1anV9aG16ZWp1XFdlTUhnTUl0WlaAaGuGbXCGaWl6Xl59YWN5XV+CYmOGZWd9ZGh+ZWlwXk5c SjtkXFthWFdfVk1cU0lWUUdXU0hpWFdoV1ZhUUpeT0hdU09dU09hUE1kVFBaU0lWT0ZUSD9aTkVi VVNiVVNhVlBkWlRpXltnXFhnVlNdTUlfTkddTEVaT0xcUU5dVVRcVFNhV11jWl9oY2RnYmNjXVtc VlRbVVNeWFZkXmduaHBva3JtaW9qYlxjW1VlVVFoV1RhU1diVFhjVlRlWFZjV1trX2N0a3NzanJw Y2FqXVtqXl90aGlybndwbXVwamhoYl9bV1RWU09aU1NfWFhrYmVwZ2pvZWduZGVuY2tuY2tybXBv am5tamlua2p0bmtvaWdlXlVcVUxiV1RrYV1nZV9fXlhnX2JrZGdpXlthVlNjXF50bW94cHN3b3Jq ampjY2NnXV5kW1xlWl1hVVhQUUhQUUhXU1RrZ2h0b3N3cnVvbnNkY2hjWltoXl9hX1xYV1RYSUFY SUFWSkNYTUVRU0xQUUpTT0lXVE5dVFVfVldjXl9jXl9bW1hWVlRaVlxraG5tZGthWF9eVlVkXFtn Z2d0dHR1c3RraWprW1pkVFNdVFVhV1hhVVZfVFVbU09eVlNhWlBdVk1hW1hjXVtkXFZlXVdlXVpq Yl5za25rZGdkWlRfVU9cSUdYRkRbTU9iVFZrV2FwXGVoXl9fVldhVlNcUU5iVlpqXmJyZG9yZG9v Y2dtYWRrYmVuZGhtaXRuanVuZ2trZGlvYWVuX2RyZWlzZ2ppZGhkX2Nram9ram9qYl5kXFhoYl1w amV4anV6bXh5bnd1anN0a2pvZ2VqXmJpXWFoXmJnXWFtY2dwZ2ptZVxnX1ZhWFVjW1duYmN1aWp9 aG14Y2h1YmJyXl5uYlhnW1FpW0ppW0pyXlx4ZGJ5ZV51Ylt3ZWJ4Z2N5bXB3am51aWFyZV1vXFxy Xl5yYWJzYmNyXWdvW2RuXFZtW1VrW1x0Y2R5bW50aGl6YmNyWltoVU5tWlNwX1x3ZWJ1ZGNyYV94 XVZ1W1R1XV56YmN+aWt/am1+ZVtvV01iTT9aRThrXl5qXV1fV1ZhWFdeUU1fU05lWFRnWlVhV01c U0hiVVNhVFFjU09kVFBbUUhYT0ZRRjtUSD1XTERfVExfVVFiV1RqWltpWFplWFRhVE9aVERTTT1Q SENWTkhbTklcT0peVlNiWlZkXV9pYmRlZGFcW1dbV1FhXVdnX2Jyam1wa29zbnJ0Z2dtX19rXlxp XFpiVVBhVE9kWFBjV09lWFRoW1Z1ZW94aHJyZ2N0aWV3amt3amtybndva3RuaWppZGViWlZYUE1X TENYTURhW1hlX11oZGFqZ2NvY2dwZGhvZWluZGhua21tamtua2ppZ2VdXldVVk9aVlNjX1xpZV9j X1ptYmFvZGNnXV5eVVZiWmFuZW1zcHR3dHhwZ2hqYWJnX19kXV1iX2FeXF1UUEpTT0lUT1BlYWJw aW54cHVyaXBkXGNqZGpuaG5uY11nXFZaTD5aTD5XTk9dVFVeV1dXUFBMSkNRUEhYU1BbVVNeV1pf WFthW1hcVlRdW1xpZ2hpYmRjXF5iVVNfU1BhY2J0d3Vzb3VtaW9nXV5iWFpkWFplWlteVFBYTkpV SkdaT0xcVFBeVlNkXFhkXFhoXVdtYlxoaWRqa2duaWhrZ2VtWlNlU0xhSkNcRj5eTEZnVE5rXmd3 aXJvamlkX15hVUlcUEVkUVxvXGdwZW5uY2tkY19iYV1tZWVuZ2dtaGttaGtwZW5uY2tzaW11a290 a2huZWJyZ2VyZ2VzZ2pvY2dvXltwX1xvaGh0bW13bXB3bXB3b3JwaWtzaW1vZWlzXl5zXl5uY2Jz aGd1Z2t4aW5uZFtlXFNlWFZqXVtzZ211aW96Z2d1YmJzYmFyYV9tYVhrX1dzYVp1Y1x6Z2d+amp6 aWVzYl5waGdzaml0bXJ1bnN4aGFuXldtXVZtXVZwXV9uW11uWmFrV15rWlRuXFZyX2N1Y2dyXlxt WldnWEpkVkhtWFZ1YV53YmJ1YWF1Y1pyX1ZpVlBtWlRwW2J3YWh+ZWR7Y2JvV0FdRjFdQy9fRTFv X1hvX1hrX2FqXl9iUVNeTk9eVE5iV1FfW1BeWk9iWlZiWlZfV1RfV1RfU1NXSkpaSDxWRTlVRURa SUheTk1kVFNtVVZvV1hnXFZfVU9fVEhaTkNYSj1WSDtbTEVdTkddUExdUExfWFhlXl5qZWRkX15f WldeWFZiW1tpYmJqaGlvbW5wbm9vbW51aWpwZGViW1teV1dnVldpWFppVlhtWlxtW2FwXmRqYl53 bmp5b3N5b3N6c3hwaW5oYWFjXFxjWlBfVk1eTkpYSEVhVlVoXVxnYV5rZWNvYWVuX2RoX15pYV90 aGt3am5wZ2hpX2FhW1ZcVlFfWldiXFplX11kXlxpYV9pYV9pXFpkV1VlV1pzZGd3dHh5d3puZGVk W1xnXV5oXl9fYmFfYmFVU1FOTEpRUE1fXltwZ2p3bXBvZWlnXWFwanNvaXJvZGFlW1dcSkFlVEpf VldeVVZfVVFeVFBaSUZeTkpcUUxcUUxaU1NaU1NiT09jUFBhV1tpX2NpYV1kXFhhV05dVEpiYWVy cHVvcHRoaW1vYl1uYVxrY19lXVpYUUdORz1USUZeVFBiXVxnYmFpZGNnYmFpYmJtZWVtaGduaWhr Y2JqYmFqV1BlU0xfTUZbSEFcUEhfVExkXGVwaHJ0anBvZWtjXFNbVEpkW2FqYWdwaWttZWhlXmFi W11kXV9oYWNqYWdrYmhwZGhyZWlza250bW9zamdvZ2NwY2FwY2FvYWNrXV9wXGFzXmNwZ2hwZ2hy aGlwZ2hwa29rZ2pwZGhrX2NwX15yYV9rXWJ4aW57b3N5bXB1ZV1qW1NqVlhwXF55ZGt4Y2p6Z2F0 YVtrX1drX1duXVpyYV14Y2F9aGV7ZWp6ZGl3ZGpyX2VvZ2VzamlzZGt0ZW15YV9yWlhrW1prW1pw XFxtWFhoV1RqWlZnV1BrXFVyXmFvXF5tWlNqV1BqVE9rVVBvW1twXFx1XVx1XVx3X1dyW1NoTklp T0puVVt1XGJ6W1NwUUlhSTddRjNjSDRoTTltX11rXlxtYWRoXF9fU05eUU1hVFFjVlRhV1hkW1xq Xl9uYmNnXV5kW1xkW1xcU1RlT0ddRz9RRDdTRThYSEViUU5lU1NqV1dlWFZlWFZlWlFjV09dTENV RDtTSUBRSD9USD9VSUBVTE9fVlplY2dkYmViXWFeWl1eVlNjW1dnYVxpY15raG5uanBwZ2pvZWlt YmFrYV9pXmdrYWltX2hrXmdkWlRfVU9eXF1ua211c3d3dHh5cnJtZWVqXV1lWFhpWFVjU09XSkZW SUVYUEpdVU9nXl1pYV9qYl5kXFheWFRlX1tuYmNzZ2huZWJlXVpdVVFaUU5bVVBcVlFjW1dkXFhl XVdnXlhtWlpuW1tqXmRtYWdzbXV6dH1pamFeX1ZiXlhhXVdbW1tdXV1VU1RQTk9eVVhuZGh3a3d0 aXRoYWNkXV9uanVva3dtaGlpZGVjV05lWlBfWlNdV1BhVUlfVEhkUVFlU1NnW1xiVldYVElVUEZW SkNcUEhdVVRnXl1oX1pjW1VnVlViUVBnYWd0bnRzanRwaHJwZ2puZGhraWpqaGleU0dVST5cT01q XVtta2Vta2VuaWhuaWhza2t1bm5vamlrZ2VqXVhjVlFlVk5jVExjUUhkU0lhV05hV05iWFpqYWJ1 ZW91ZW9uYWFlWFhkXV9lXmFrZ2hoY2RjXF5iW11hV1tiWFxkWFpkWFpqWF5vXWNtZWhrZGduX2Jr XV9uXF9uXF9pXltoXVprX2NtYWRtZGNoX15oXmJoXmJpX2VtY2lwY2NnWlpkV1VqXVtvZG11anN5 bW5yZWd0W1RpUElkUU9pVlRyXWR1YWhzX1ZuW1FrV1VqVlRuWlp0X196ZGl+aG14ZWlyX2NrXV9t XmFvY2l1aW90ZW1wYmlzX1pvXFZqX1xrYV1tWlBlU0lhT0NhT0NhUERlVUhnVE1nVE1tVlBuV1Fr U0xtVE1tVVZwWFp0XF91XWFwVlZyV1duVVFyWFVzXFR0XVV3V0xwUUZnT0NoUERuW1VwXVdnX1Zk XVRlW1ppXl1nW09cUEVdTENhT0ZfU1NlWFhtX19tX19pY2FoYl9oXl9kW1xtXVZnV1BcVkZWUEBY T0NcU0ZiU0xjVE1kV1dkV1dhV1hjWltlVVRhUE9YT0ZWTURYTURbT0ZaTk9eU1RiXWFhXF9dVlZb VFRdVVRfV1ZfXFhkYV1nY2ltaW9pX2NpX2NwY2FvYl9oYWNqY2VqY2hqY2hiW11YUVReW2FnY2l4 b3d5cHhyb3BpZ2hqXVtoW1hlWFhfU1NWTkhTSkVcUU5dU09lWltqXl9pX2NkW15hW1ZjXVhnYmVu aW1uZGVoXl9cVk9XUUpbT0ddUUlqXVtpXFpoX1poX1ptYWRrX2NrZW5uaHB1b3p1b3ptamtlY2Rh X1phX1pfW1pbVlVVT01WUE5jWl1yaGt1a29rYmVfXVFhXlNnZ2ltbW9zZ21qXmRoXVdnXFZnW1Fl WlBrXFFrXFFvYmJtX19nXFZhVlBXT0lVTUdTTUhXUU1hV1hjWltjXVZjXVZpWlFnV09nYmNqZWdq ZWdwa21vb3JtbW9ubXRubXRkW1BaUEZkWFxyZWl1bnNza3Bya2dwamV1bnB7dHdybnRqZ21tXFtn VlVnXVNpX1VuXVpuXVplXFNkW1FlXVptZGFoZWdtamtuZGVlXF1nWlpoW1toX2dqYmljXF5iW11f VlphV1tiV1ZkWlhrX2FvY2RvZWlvZWlrXlxnWldkV1dhVFRhWFViWlZnXWFoXmJpXltkWlZjVlFk V1NiWFxlXF9tW15oVlppW2JqXGNvZG10aXJ3YmtuWmNuVFhrUVZkV1dtX19zYWR0YmVuW1hpVlRp V1BoVk9uWlxzXmF0YV5yXlxqWlhpWFdiWFpnXV5wZGV1aWp3ZWRyYV91ZGF1ZGFwZWJyZ2NtYVhi Vk5iUERdTD9eSTxiTT9iUElqWFFuXlZyYlpwWlRuV1FvV1t3XmJ0YV5yXlxvXlFvXlF0Y199a2h6 Z2R0YV5zVUZvUUNrUU5zWFV0W1Z0W1ZkXFZkXFZjXFNqY1plXVpaUU5eRzteRztYTURfVEpdW15j YWRnYmNoY2RpYV9nXl1pYmJpYmJnXFZdU01hV05iWE9jWk9eVUpfVVFfVVFfV1ZiWlhpW11jVVdd VEpbUUheT0dhUUlfVFVdUVNdU09cUU5hUE1hUE1bVU5YU0xXVk5VVExdVlhpYmRlX11nYV5qY2Nr ZGRwaGRuZWJrZGdnX2JkW1xeVVZeWl1nYmV4b3l6cntycHVpaG1pXFpoW1hlXFNjWlBhV05YT0Zi UEljUUpiWlZlXVpkXlxeWFZfVVFnXFhpZGhrZ2prY2JnXl1hXFFaVUphVU1kWFByYWJ1ZGVrZWNr ZWNtXmVtXmVoY2dtaGt5bnl3a3dwaWtqY2VoaWRhYl1cXFpWVlRYT1BbUVNjW2JoX2dnYmFiXVxi WlRjW1VlY2dqaGtqYlxkXFZjXFNqY1ptYVduYlhzZWF1aGNzbmNuaV5uYlplWlFcU0hUSkBQSkRU TkdeWFRlX1tnXlhiWlRdVkxeV01fWFhqY2NoaGpra25qbWtoamlqaXBubXRkX15fW1ptZG51bXdy bndwbXVza255cnR4dH19eYJ3cnNuaWpnW1xlWltrYV9vZGN1YWV1YWVvYl9tX11qZF9uaGNpZ2hv bW5vaGhkXV1kV1NjVlFiWF5oXmRlXVxbU1FdSkpeTExdVVRlXVxuZ2l0bW91aWpyZWdvYmJqXV1i VlphVVhhVlVlW1ppXV5oXF1oXVxkWlhiWE9hV05kVlhkVlhnWFtoWlxnXFtqX15tYmpzaHB1YWVw XGFoUFFkTU5iUVBvXl1zX2JvXF5rV1dqVlZlWFZnWldpXFxpXFxuX1FtXlBrX1ZrX1ZuXF9zYWR0 aGl3amt7aGh9aWl9a2p9a2p0aGl1aWpzY1hvX1VuWEpiTT9kSTppTj5rV1d3YmJ1aGVzZWN0Ylxt W1VvW1hzXlxyXlh0YVt1ZFd5aFt4bWt+c3J/a2t1YmJyU0dvUEVtT01vUU90VlN0VlNnVlVoV1Zp V1BtW1RoV1hnVldiVEZbTT9XTz9aUUFVVVVfX19pXWFrX2NtXFtrW1pnXFhnXFhqWltoV1hpW11q XF5oX1plXVdkV1NeUU1bVEpaU0lhVlNhVlNYT0ZbUUhiVVBkV1NiVFZdT1FeT0hdTkdbTklcT0pb VElaU0hbVEdaU0ZVT0hcVk9iWlRkXFZnYmFuaWh3bm10a2puZWRoX15kXlphW1ZdVlhiW11lZWhy cnR4c3d3cnV0Z2dtX19pX2FvZWdvaGhjXFxjV0xiVkplXlViW1FlW1dhVlNfTU1nVFRlXmFtZWhu Y11nXFZoXlVfVk1iVk5qXlZ+a299am50b3Bwa21tY2dpX2NiXWFnYmV0bnd0bndvamluaWhqamhj Y2FbW1tUVFRWTk1YUE9hWlpkXV1oX1xnXltiWlhiWlhpZGVuaWpoYVZiW1BrYVt0aWN1cm5va2ht amttamtvcGtub2ptZ2JqZF9jWk9cU0hVRjxXSD5dVEpiWE9tXFtoV1ZhWFNhWFNjWlBpX1ZpY2Fo Yl9kYV1raGRqaGtvbXBqZWdiXV5pZ2pyb3Nwa29wa29ubXJ0c3h3dX10c3puaWpoY2RoXF9tYWR0 aG51aW95ZGt3YmlrYmNvZWdvamtybW5wbm9yb3BtamlqaGdpX1ZlXFNfWFheV1dkWlRfVU9kUFBl UVFkWFpuYmNvbnVta3NwaGRoX1xoXmJnXWFoVFtlUVhcU1ZfVlphWlxhWlxnXFhjWFVhVU1fVExj WFdjWFdiWlhfV1ZeVE5kWlRrYmVrYmVwXV1pVlZnTkdnTkdkUVFvXFxyXVtuWldzWlZ3XVpwXVdu W1VvXVZyX1h0aWN3a2WAa2l+aWd6Z2l6Z2l3aWl0Z2d6ZW1+aXB9anB6aG50aWVzaGR6aWV7amd4 YVhqVExlTUBvVklwXmR5Z217amd1ZGFrYVtqX1pwXVt1Yl93X1dvWFBqV1VuW1hoXmJzaW15bmpz aGRvU0lqTkVrTkhuUEp0VFN1VVRnXFhiV1RqV1VoVVNpVlhpVlhjWk9dVElcUEVdUUZdVkxkXVNu YWFtX19pXFxjVlZfVExiVk5lW1dpXltoXl9rYmNrYmNqYWJjXVtbVVNfT0NcTD9YTUVbT0dbT0df VExkWlZpXltkVlhhU1VjU1FfT05fT0xiUU5jWFNjWFNhVUxdUUhhVU1oXFRjW1VoX1prYmV1a291 cHJ3cnNoZ2NkY19kY11jYlxjW1dlXVptZWV4cHB9dHt+dX13b3J3b3J4bnJ7cnV9cHdwZGpqYl5p YV1vY1tpXVVhWFVaUU5aUEdaUEdhV1htY2RtaWFnY1tlX1teWFRlWFZvYl93bXN4bnR1cnhwbXNr aWhlY2JlYWJjXl9jZ2plaW1wa211cHJzbm9uaWpoXVdfVU9WUUZRTUFeVFBnXFhqYWJqYWJrYmNt Y2RpZ2VpZ2VnXlhpYVtwZ2p3bXB0b3Nvam5vaGh0bW13bW50amtramdramdpZV9dWlRYUUVTTD9e U0ppXVVuYV5tX11jX1diXlZlW1dnXFhkXV1iW1tkXV1pYmJpZW5qZ29iX15iX15vamtybW5taGlv amttcHdwdHp5d3p0cnVyZWllWl1iXWFpZGh0a3V3bnh3bXNwZ21oYWNrZGdvZ3NwaHRyam1waWtw a2pybWtrZ1tkX1RhV01iWE5hV05hV05hVFRiVVVoXF9tYWRyZ29wZW5vYmJpXFxoWlxiVFZiT09i T09fUVRfUVRdUVNjV1hqWFFrWlNtW1VqWFNnWlVhVE9hVFFhVFFkVU5qW1RtX11rXlxwXFpqVlRj UEdiT0ZlU1BvXFppXltuY195ZWV6Z2d4ZV9yX1p1Ylx+amR+b3SCc3iDbnN/am93ZWd1ZGVzZGdw YmR1Y2l5Z214Y2h3YmdvZGN1aml+cnN9cHJ7Ylt3XVZuW1RzX1h5aGl7amt4ZF1yXldtXFtuXVxz XFd1Xlp4WlRzVU9fVVFlW1duX2J5am17aGh6Z2dzV0drUEBtVEd0W057WFZ/XFpkV1dkV1dpWFVo V1RoV1RlVVFhV05fVk1eU0pfVExeVUxiWE9jWFdoXVxlWlthVVZeUU9eUU9kVU5nV1BoW1tqXV1l Xl5jXFxnXl1eVlVeTEZdSkVVUEZVUEZXUUpbVU5lW1ppXl1qXl9kWFpfWFhbVFReWFZjXVtlX11j XVtnWldiVVNnV1BqW1RqXV1rXl5rY2p0a3Nyb3Nyb3NtaGlqZWdpZGVoY2RlXVdnXlhlYWJzbm97 cHuAdYB6dH19d399dXp9dXqCeHt1a293b293b293c29wbWloYl1bVVBUSkBUSkBXVExnY1tkZV5t bmdqYl5nXltjWFVuY19zcnl4d351dHlvbnNza25uZ2lqY2VjXF5hYWNnZ2lwaHJ5cHpyb3BqaGlk XFZcVE5QTEBQTEBhVFFtX11zaWpwZ2hvZ2VuZWRraWppZ2hrZGdvaGpva3RybndvaGpqY2VrZWNy a2l1bnB1bnBvaGhvaGhraWplY2RiWlRaUUxfVVRoXVxpX2VwZ21tZGFoX1xnW1xlWlthWlpjXFxo XF9qXmJqYWdtY2ltYWRuYmVybXBwa29vaG1waW50bXJ4cHV5dHV1cHJuYmVhVVheXF1pZ2h7bnt7 bnt3aG1rXWJkWF5lWl9qX2hoXWVpXWFtYWRuZ2l1bnB0aWNuY11eW0lYVURbUUdbUUdfU05hVE9l VFprWl9oXmJnXWFoV1ZkVFNkVFNfT05iUEpkU01kVFVkVFVlU1NpVlZrW1pwX150Y19tXFhqXVtj VlRkUVFoVVVuWldtWFZuWlpwXFxyXlhrWFNeU0dhVUlkVU5tXVZrY19vZ2N5aGl4Z2h3ZWd4Z2h6 Z2mCbnB/c3l+cnh/a2t1YmJtW1RpV1BtXmFyY2VzZ2p3am53ZWd0Y2RtY2lzaW96bXV5a3R3Z15z Y1twX1x0Y194ZV91Y110W1ZwV1NtWFZrV1VzWFV3XFhzWFhyV1dnVU9uXFZ0X2d/anJ+am14ZGdz WkxuVUdyWFRzWlV6V1OAXVhrWl9vXWNwX1xuXVpuYWFrXl5lWFRqXVhoXFRkWFBjWlBkW1FfWFtl XmFjXGFkXWJlUU9iTkxfTkViUEdqV1BuW1RlXVdkXFZnXl1iWlhiTkxkUE5eV01eV01iWlZhWFVl W1plW1pkXV1iW1teW1dfXFhlXVppYV1nX19nX19uYV5nWldoXFRqXlZpXV5pXV5oXmJwZ2p1bnB3 b3Jvamlwa2p3amt0aGluX2RoWl5jXFxrZGR3bXN9c3l3c3t1cnp5dXt5dXt7eXh5d3V4dXl5d3p7 dX5zbXVnYmNaVVZXTkVXTkVXWE9naF5tbWpycm9nZGhnZGhkW2FwZ214dH97eIN1cnhwbXNraG5o ZGpnZGVlY2RfXGJoZGpycHpwb3lpZGViXV5hWl5kXWJXTk9eVVZrYmVzaW14bnJ1a29rZWNpY2Fj Y2NkZGRtaGt0b3N0b3Bwa21pXV5oXF1pZGVzbm91cHJ1cHJza2tvaGhtaGluaWpuY19uY19rY2Jr Y2JwZGh1aW13a2hyZ2NuY2JtYmFnX2JjXF5lXVxjW1ppW19pW19tW2NyX2htaGtzbnJzbm9wa21w a291cHR6b3h4bXVvYWNlV1piW19qY2h3a3d1anVvXl1iUVBeU0phVU1kWlZnXFhiW11qY2VoZG1t aXJ3b29waWlpZV1WU0paUEdkW1FnWldiVVNeVFNnXFtkW15eVVhjVlZlWFhoXVxnXFtrXlxrXlxn W1xlWltrV1xvW19qXF5wYmR1Z2lzZGdwYmRjVVdlU01kUUxqVlZuWlptWFtwXF5uV1FoUUxeUENf UURjUUVrWk1zXl56ZWV7aGh7aGh7Z2d7Z2d+b3eAcnl5bXByZWlyW1NpU0plT0lpU01tXmF1Z2l3 a2p1amlyX1hzYVp0aGt3am55bW53amtwZV9rYVtrW1puXVxzXFZuV1FtVUpuVkxrWFNtWlR0W1Z1 XFdzX1pwXVdrV1VvW1h0Ymh+a3J9bWV5aWJ4YVhwWlFyWlh4X15/ZF+AZWFtXWdzY210am50am50 a2pvZ2V0ZFxyYlptYVhqXlZnYVpnYVphW1hnYV5pYmJqY2NoVk1iUEdjTkBlUENoWFBpWlFuXlZt XVVqXlVlWlBqV1BtWlNrXlptX1tuXldqW1RlWFZlWFZdVlZfWFhbU1FdVVRjWltqYWJuZGhvZWlr YV1jWFVjXVtkXlxlXF9pX2NuYmVvY2d3bW53bW5yb3Byb3B6bnJ3am5vZ2VpYV9vYmJuYWFqY2V1 bnB1cHR4c3d4d3t3dXp6dXl4c3d4c3d/en6Ad310anBiXVxaVVRYT1BcU1RiYWVycHV5c3tzbXVn X2RiW19oYWN1bnB6cnB5cG9wa21uaWppa21oamtlZGliYWVfXmNlZGlzbXNwanBoYWFhWlpeV1pf WFtWT09lXl50b3N0b3N0cHdtaW9oZF5oZF5cX1pcX1ppZWtwbXNya3JtZ21iW1tnX19qaXB3dX16 eX51dHl0b3BtaGltZWhza25tZWhvaGpybW5rZ2hqYmFqYmFwaWtyam1wamhvaWdoZWRjYV9fXVFb WE1fVU9lW1VnW15qXmJvZ250a3Ntam5raW1yamp4cHB3b3J0bW9vZWdpX2FoYWNoYWNwZ21wZ21t XVVtXVVrXFVoWFFjWltlXF1jYWRoZWlpZWtuanB0b3B0b3BwZWJiV1RfWlNnYVprXlpqXVheWFFj XVZkXFtkXFtnWldpXFppXltqX1xtYWRtYWRtXmFlV1plV1poWlxoWmFvYWh3amt5bW51ZGVkVFVl U01iT0ljUFBoVVVqW1NrXFRpV1FnVU9jUUVeTUBjU0ZtXE93Ymd+aW59anB9anB7ZWp7ZWp+bnh6 anR0Y19rW1djWEdhVkVnV1BwYVp3ZWd5aGl0Z2dzZWVzXmN0X2R1Z2l1Z2l5Z2pyX2NoWFBnV09v XFZ4ZF55Ylx3X1p1XlZ1XlZ1W1t3XFxyXmF0YWN6ZWN3Yl9zW05wWExrYVtzaGJ1aV90aF54Y2Ny XV11XVx7Y2J9ZGh7Y2dqXGFyY2hvbXByb3N4bnJ0am5wY2FuYV5oZF5oZF5lZF5kY11nY11nY11t ZGNyaWhyYV9tXFtoVk9pV1BpXlhwZV94ZGR3Y2NwZFttYVduY11zaGJwZ21yaG53Y2VzX2JuXV5r W1xlWFhdUFBeTUdeTUdkVlhtXmFwZGhwZGhnX19aU1NfVVFkWlZkW1xnXV5oYWFuZ2d1bnB3b3Jz cG90cnB1bnB1bnBwa21rZ2htX1tqXVhiXV5rZ2hyam96c3h6cn54b3tzbXNvaW90cHl7eIB4dXlw bnJnXWFaUFRbVldfW1xrbXV1d391bXRoX2dcVlRdV1VjYV9qaGdwaWlyampqZWdnYmNoYmhoYmhi Y2tiY2tlZGlpaG1yam1tZWhjXVtfWldhVlNiV1RYW1xoamtycHhwb3duZGVnXV5fXlheXVdhXFte WlhrZW51b3hyam9nX2RfXV5pZ2hra3h3d4N5en5wcnVqampkZGRpX2NtY2dfXltlZGFra2tycnJt bmlpamVrY19uZWJua21ua21tamtpZ2huZWJkXFhfVVFfVVFfWFhiW1trZGdtZWhuaW1wa29zaW1w Z2puZ2tuZ2tqZWdoY2RnX2JkXV9pX2FtY2RzY1x0ZF11Y110YlxnX19lXl5kX15pZGNtZWhyam1z a3B1bnNyaGlpX2FnYmFrZ2VwYmRqXF5fW1pfW1plXF1kW1xoWFFpWlNrW1ptXFtuWmFyXWR0X2Rt WF1jVVdfUVRjVVppW190a3V1bXd5ZWVtWlpkU0RhT0BdSkRdSkRlVERnVUVoWkxlV0lkUD5nU0Bo W1t1aGh5bXB5bXB1ZGV3ZWd1Y2t3ZG19anB5Z21yYlttXVZnXFZoXVdrXV9wYmR3ZGh4ZWlyYV1w X1xvYl9zZWN1aml1amlzYl5pWFVlU0lnVEpvW1h6ZWN9ZWF7ZF97Ylt9Y1x6YmV+ZWl1aGVzZWN5 Z2F1Y110XlNyXFBrW1dtXFh1Ylt3Y1x4X150XFt1YWF9aGh9ZGV5YWJqXmJwZGh0bXJ1bnN1a3Jv ZWtrYV9pXl1lXmFpYmRrZ2htaGlvY2dvY2d3ZGp0YmhzZ2pzZ2pvY2RyZWd3amt5bW57cGp3a2Vw ZV9tYlxtaWVybmp0a3N1bXR6cnB3bm10YmhuXGJuWlppVVVfTkheTUdoV1RyYV1yZWttYWdpVlhl U1VXT0laUUxdV1VhW1hkWlhuY2JuaG51b3V0cnN3dHV1cHR4c3d4c3dwa29vZGNpXl1iXWFtaGt0 c313dX+Ac3t+cHlza3BuZ2t3bnh+dX95cnJza2tiW1tUTU1XV1pdXV9vbnNycHVwZV9nXFZXT0lX T0lcV1hiXV5nZ2doaGhuZ2loYWNdVlhfWFtfX2JeXmFlY2dqaGtua2poZWRqY1pjXFNeVlBcVE5Y W1xkZ2hwanNuaHBtZGNpYV9dXlpbXFdjXl1qZWRua29tam5uY19jWFVeWlhrZ2Vzcnl0c3p3a3Rr YWloXF9nW15hV11iWF5aVlNeW1dtY2d1a29wbnJvbXBrZV5oYltraHBuanNya3J1b3Vza25rZGdo X15iWlheVFBfVVFjXl9nYmNqZ21pZWttaGlqZWdrZGdtZWhrYmNoXl9nXFtpXl1pX2NqYWRtaGly bW57Z255ZGt3aWdzZWNqYl5oX1xnWldlWFZkYmVkYmVnYmNnYmNtZWhvaGp1ZGNrW1phWlpfWFhi WlhfV1ZkV1NnWlVvW191YWV7ZWp/aW56ZGtvWmFlU1BjUE5oVlpzYWR3bnV1bXR5ZWhwXV9vVk9r U0xeTENjUEdqWE9vXVRuXlRtXVNrWlBuXFNzYWd4ZWt5Z2pyX2NpWlNpWlNoW1ttX194Y2V3YmRu XldrXFVuXV5zYmNyZWlvY2dzX2JzX2J0YVd3Y1p6Z2l6Z2l5am9zZGl1X1RvWk5qU0hpUUdpWFVv Xlt4Y2F5ZGJ6aGJ9amR+aW59aG16Z2d7aGh7Z2d4Y2N1XlhvWFNtU0xwVk9wXFpzXlx4ZF55ZV+A aGl+ZWd5YWJ3Xl9wXmR3ZGp5bnl1anVuZGVpX2FeVlVdVVRcVVVkXV1oYWNqY2VqYWdpX2VyYmtt XWdrZGdwaWt1Z256a3N9c3d+dHh3c29ybmpwZV9uY11uZWR0a2p5bnd7cHl4dXdyb3B0aGttYWRu YWFlWFhlVE1nVU5rYV9rYV9uaWpqZWdvXl1rW1phUE1jU09jWltkW1xkW2FnXWNpZW5wbXVyc3d0 dXl3cHt4cn10c3pta3NtYmFoXVxkXV9oYWNub3N3eHt5d3p3dHhraW1pZ2pwbXV4dH1yb3Byb3Bq YmFnXl1kWFxkWFx0aGt0aGtnX1ViW1BcUU5cUU5cW1dpaGRyb25yb25raWhpZ2VjXl9hXF1hWlxj XF5qYWRzaW14cHNza25zamdqYl5rXFVoWFFdWFplYWJvaGpuZ2lyaWhuZWRnY19iXltiW11nX2Jr Z2hvamtuaGVrZWNhXFtnYmF1bXR4b3dwaWtqY2VqXVtqXVtiWlhcVFNbUE1fVVFpYmR0bW91b3h0 bnduaGNkXlpoXGJtYWdzbXV5c3twcHNtbW9rZGdkXV9fT0xjU09fW1xiXV5lYWRoY2dkYmVlY2do Yl9oYl9rXl5tX19qXVtrXlxoXF1tYWJraHB5dX59dYR5coB1a29zaW1qX15nXFtnV09kVU1pWFdl VVRnXFhrYV1wZGVyZWd0X11tWFZpXVVfVExeU0pjV09lWlFoXFRrX2NwZGh4aW56a3B7YWV4XWJu W1VpVlBrXl54amp5cHh1bXR7aGh1YmJqYVZlXFFdU09iV1RtYmF4bWt4bWd1amRyYltvX1hwYmRt XmFuW1RpVk9jVUdlV0lnVldtXF1yZGJyZGJ1XVx1XVxrYmVwZ2pyY2VzZGdyZF9yZF95Z2F6aGJ7 b3N5bXB+aWt0X2JpWlFoWFBpUUdpUUdrW1dwX1x3ZF55Z2F+ZWd+ZWd+ZG1+ZG1+ZGp9Y2l1YWV0 X2R6YV10W1dwVlNvVVFtWlp0YWF+amh/a2mCbW1/amp4XV1zWFhuX2JyY2V1anhvZHJqX15lW1pe TElaR0VcTEplVVRnU1NnU1NnVldlVVZnW1xkWFpjXVtnYV5tZWVwaWl1bnB4cHN3cnB1cG90Z2Rr XlxqYWJwZ2h4bnR7cnh4dH1zb3hwbm9qaGlrZGllXmNoXlVuZFtwYmRvYWNtbWpqamhuZV9pYVto XFNpXVRjX2VkYWdiYWVoZ2toZ25ta3Nwcnh1d317c3p4b3d0b3NuaW1nXV5kW1xpXFpvYl9ya3J4 cnhzdXducHJqZGpoYmhta3Nwb3dvdHV0eXp1dXNycm9oZ2FcW1VkXV9lXmFnZV9eXVdlWltiVldo Y2d1cHR1dXV0dHRwbnJqaGtiYWVjYmdjYl5kY19nY2lwbXN9eIh5dIR1dXhtbW9uaGNnYVxbV1Fh XVduYmNzZ2hwa2ptaGdoaWJiY1xfWFtiW11qaGlyb3BvbnNqaW5kYV1lYl50anB3bXNtamloZWRu Y11uY11iW1BcVUpbU01eVlBkYmVua29zbXV1b3h5bmhqX1piVVBnWlVwaW54cHVzcnlwb3dvZWto XmRjVlRbTkxbVlVdWFdhXF1nYmNoXmJnXWFoW1hnWldrXl5uYWFpYmJtZWVqYWRrYmVtaXR1cn15 dIN1cH91aHVwY3BuWl5lUVZkU0lkU0lpV1FnVU9kVFBtXFhwXmJzYWR3Y2F1Yl9qW1RjVE1jVE1h UUpiWlRjW1VpXWFuYmV3amt5bW50Y19vXltrX1doXFRyXmF7aGp9cHR5bXB6Z2d6Z2d1ZFdqWk1j VVpoWl50Ymh7aW9/a25+am13Z19zY1x1ZGVvXl9tW1FlVEphUUdiU0hoVFtyXWR1YWV3Ymd1YWV0 X2RzZGl0ZWp5aGd5aGdzaGRzaGR6a3B+b3R9cHJ7b3B+Z2JyW1ZpVVNtWFZrVkpqVUlqVlZvW1t1 ZGN6aWh/amh+aWd+ZG2CaHB/aW53YWVwXV91YmRzX19uW1tzW1p1XVx0ZF16amN/amqCbW2DamuC aWp7YVp3XFVnYV5oYl9uZW1rY2poWlxhU1VcSkVWRT9YRkRdSkhYTElcT01YTkpXTUlXTkVdVEpe UFViVFhkXlpoYl1rZGduZ2l0b3B4c3R1a21wZ2hqYmFoX15zZ211aW94cnh3cHd3b3Jyam1nY2lo ZGpuYmVvY2dzZGdzZGdvbWttaml0aF9uYlpjW1dkXFhlYWJpZGVnZ2dpaWlvam5wa29va3R4dH16 dXl0b3NraGJoZF5lXVxoX15qZ2Nva2h0bnR3cHdvb3Jqam1pZGVlYWJqZGptZ21ub3h1d397eoJ7 eoJ7cnNtY2RcVVVqY2Nqa2dqa2dpY15kXlpkYWl0cHl4eHp7e354c3dwa29nZWpta3BqamppaWll aXJydX56eod7e4h+fYdycHpwbm9vbW5eXVVYV09rYV94bWt1cHR3cnVvb21jY2FfXFZfXFZqZWd4 c3Rzcnlta3NkYmVkYmVvZWl0am5nZ2Rra2lyam1waWtvY2RpXV5pX2NoXmJnYWtwanVzcnd1dHl1 a29lXF9dU1FiV1ZuZGp3bXNzb3hybndwZ21pX2VoW1tkV1diXFdkXlplY2JjYV9oYWFnX19rY2Jq YmFuYmN0aGlwa290b3NybXBybXB4b3t4b3t3c350cHtyaGtoXmJkVlhiVFZlW1pqX15qX1xoXVpl VVRqWlhzXmh6ZW96a3N5anJ3Z1xyYlduXlRvX1VyZF9qXVhpXl1vZGN4ZWt4ZWtuYVxoW1ZnXFZl W1VvXl95aGl5bmp5bmp6a25zZGd0X11vW1hwW190XmN3Y3B7aHWDbXKCa3B7amd6aWV6ZWpwXGFz WlZuVVFqV1BwXVZ0Ymh5Z216ZW14Y2p0Z2RzZWN0aG51aW95ZGt4Y2pyZGR1aGh6aG6AbnR9cHJ9 cHKGaWt6XmF0VlN0VlNtV0xrVkptVVh0XF95ZGt/anJ9aWN6Z2F9Z2t7ZWp6ZWV1YWFvW11zXmF0 X2R0X2R1Yl94ZGJ5ZVx6Z116YmWDam6DamuDamuCaGN5X1tnXFtnXFtrXV9uX2JpXFxkV1ddUE5Y TElbSEZdSkhXSEBYSUFVT01RTElUTkdYU0xfVFVjV1hjXF5kXV9nXWFnXWFtam5wbnJ3bW51a21w ZWRrYV9qYl5vZ2N3cnN1cHJ5bXN3anBqYmtqYmtpYmdoYWVoX15uZWRvbW5wbm93a2pvZGNiX2Fd W1xkW15pX2NnZGNlY2Jvamt1cHJ1c3d3dHh3c3lybnRzbm9uaWpjXFxqY2Nua29wbnJ1dHl0c3h0 bnRrZWtkYmNiX2FlXl5qY2Nqa29zdHh5eIJ9e4Z/dH1zaHBnX2Jza25wbm9vbW5waGdqYmFkZGdy cnR4d359e4N7cnh1a3JoZ2tubXJta3Nta3NqbXlwc391dYZ5eYl+fYd6eYN3dHh1c3doX15hWFdr Y29+dYJ6d4JybnlpaWlkZGRYWE1VVUljX1x0cG16c4JyanloZG1hXWVqXmJvY2dpanBvcHdvbXBw bnJ0a2hvZ2NuZGVwZ2hlYWRwa29wdHptcHduaWhhXFtnXFtqX15vYm14anVzbnJuaW1uZ2drZGRq YmFqYmFrZ2VpZGNpZGVpZGVpZGVpZGVtaGdtaGd0ZWp3aG1zaHV1anh3b3R1bnN0aXJ1anN5b3N5 b3NvaWJiXFVeVFNhVlVqYWRuZGhrYmNqYWJqXV1oW1tyXmt4ZHJ5a3R9b3h7cmR1a151ZGF5aGR5 a2tyZGRuXGJ1Y2l6bnJ3am5yYWJqWltoW1tpXFxuYmV1aW15b3B4bm95am1zZGdyXWJyXWJ4Ymd4 Ymd6a3N9bnWCbm6Cbm6Ca3B9Z2t6aGt0YmV1XlpzXFdzX2J7aGp7b3B5bW54ZGR1YmJzZGdzZGd1 Z2t3aG15ZGl4Y2h3YWV4Ymd3aG96a3N9bXd6anR+X1x5W1dzVU90VlBtVlBuV1FvVVV1W1t3Y2V/ a259aWd5ZWN6ZW15ZGt3Y2F1Yl9zW1puVlVyV1NzWFRyWlh3Xl14YVt5Ylx3Y2F+amiCbnB+am19 Y1h5X1VjT1ZjT1ZlVFppV11nXlthWFVoV1RqWlZnU1BjT01cTkBYSj1USj5RSDxVTD9cU0ZeVFNh VlVeV1diW1tiWlhnXl1qZWltaGt3bXB4bnJyaGluZGVvZF5vZF5yam1za25/aXV9Z3NvYmJtX19k XV1jXFxhXFFhXFFnZ2dubm53bXBwZ2piX2FbWFpfWFtjXF5lYWJkX2Fyam9za3BycHpzcntycnRu bnB1bnBza25nYmFqZWRraW1vbXBzcndzcndyaXBqYmliXV5bVldbVVNdV1VhYWFubm5yc3d1d3p/ d4N5cH1qZWlzbnJ4dXl4dXl1bnBza25paWtycnR3dX16eYB/eH13b3Rrbm1ucG9ybndraHBuanNz b3hwcIB3d4d9eoh5d4R5dXt4dHpzZGluX2RubXd3dX90dX5qa3RpZ2ViX15YVU1XVExkZGdwcHNu bnBoaGplY2dbWFxjW1dtZGFwcH50dIJubXRubXRwaWt1bnBzanRvZ3BqYmluZW1oZ2tkY2htYl5u Y194a293am5uZGhwZ2prZGlpYmdrZGdvaGpqaWVta2hraWpraWptY2dvZWlyY2V1Z2ltamtyb3B5 bXBzZ2pvZG1zaHB0b3Bzbm93bXByaGt1a293bXBwaWlkXV1hVlNoXVpzZ2h1aWp0aGluYmNuYWFv YmJrXWRzZGt3a3d6b3p+dHV3bW53aGp3aGp7b3N6bnJ6aG59anB+cnV3am5zX19rWFhqWFxqWFxq XWV0Z291a214bm96aWh1ZGNvYWNvYWN0Y2R0Y2R+am2Db3KDb3KCbnB7amt5aGl5ZWNzX11wX1x0 Y195bXN9cHd6bm91aWp1XlpuV1NrWFtuW114XmR5X2V0YWNvXF5vWl5yXGFwY253aXR7bXJ9bnN/ ZVt+ZFp5ZV93Y11tXVNtXVNzV1p4XF55Y2h/aW57aml5aGd7ZWp5Y2h+Y2V3XF51WE9wVEppUExv VlFzV1d4XFx0Ylx0Ylx3Y2F+amh/a255ZWh6YVR3XVBtX2hiVV1oVlpnVVhlX1hoYltuZV9vZ2Fo YVdkXVRiVk1fVEpYTj9aT0BfVExjV09kV1dlWFhkWFplWltpV1tuXF9nXFttYmFybXB4c3d1cHJy bW5yaGlyaGlyam13b3J/anR7Z3ByZF9qXVhfXVBaV0peUVFeUVFlXF9zaW17b3N4a29qYWJlXF1k W15kW15eXF9lY2duaW1vam5ranJvbnVuanBqZ21vZWlwZ2puZ2duZ2dvZ251bXRyc3tvcHlwbm9q aGlkX1VhXFFbWE1aV0xdV1BjXVZqYml0a3N1dHtubXRnY2lraG53dX13dX10b3N0b3NwanB0bnR7 dYCCe4d9eX93c3lta3Bwb3RwbXVtaXJram9qaW5rbnpvcn51d310dXt5dX55dX5wZ21yaG54b3t9 dIB0c3pqaXBtaWVkYV1VVlFdXlpvbXB3dHhpZ2hjYWJpYmRfWFtkYmNraWpzeINwdYBzanJuZW10 aXR6b3p3aG1tXmNlV1pkVlhjVVpnWF1uY2t1anN4bXV4bXVyam1nX2JpX2NoXmJpX2NyaGtybW5y bW5wa21rZ2hrYmVoXmJqYWRzaW11anN3a3R0am5wZ2puZGpvZWtyb3NqaGtoZWdqaGlranJqaXBq Z21iXmRnYV5rZWNza25za25yY2VwYmRvYl9tX11tXF1tXF1yXmt6Z3R5bnd1anN5ZGt5ZGt1a295 b3N4bnJ3bXB9bXd6anR5ZWhtWlxoXVpoXVpnWFtuX2Jtamtwbm90aWVtYl5rY2JuZWR0Z2dzZWV5 Z2p9am6Aa3B9aG15ZWN3Y2F0X19zXl5zXmN5ZGl5bnt9cn99am53ZGhvXFVkUUpqUVdtVFpzVl10 V150XFtzW1ppWFpqWltqWF5yX2V4Y2p+aXCAam+EbnN+c294bWlyYVRqWk1qV1FuW1VwX15yYV9z YmNwX2F0XmN5Y2h9aWN4ZF54XlB0W01uV0ZqVENyVFF6XFp0Ylx3ZF55XmF9YmR7Z2R4Y2F0XFFu VkxqZWlhXF9rWFhqV1drXlxvYl9uaGVtZ2RtZF5oX1pkV1NnWlVlVEppV05iWlRkXFZqXmJoXF9k W15lXF9vXWFqWFxiV1FkWlRkZGRubm51bnB0bW9ybXBwa29za3B3b3R7bnd5a3RzaGRuY19jXVZe WFFcVVVcVVVkWFBuYlpyY2V0ZWhuZWJqYl5oYWFoYWFoY2RtaGlvaG1uZ2tuaWpuaWpzZ210aG5y Z29zaHByZWlyZWlrZW50bnd0cHtva3d3bXB3bXBwZ2puZGhoXVpdU09fTkdhT0hjXGFyam93b3J1 bnBvamtwa210cHt1cn1yb3Nyb3Nva3J3c3mAeoOCe4R5c3lya3JtZG5vZ3BubXJqaW5ta3BqaW5p am5wcnVucnVvc3dzd31ucnhtaW9va3Jzb3p6d4JvbW5qaGlnX19pYmJnX2Jyam1wcnhzdHppYmJc VVVhVVhhVVhoYmhya3JwdHpydXt0anBwZ210c315eIJ9aWtvXF5oW1hkV1ViVlpoXF9uZW13bnV1 bXR1bXRyaGlpX2FlXGJnXWNpYmduZ2tzbnJwa29waWtqY2VqYWRrYmVpY2lwanBuZ2tuZ2tyam1y am1raWpraWpwa29taGtrYmVtY2dtYm1vZG9pYmdpYmdoYWFqY2NyaGt1a293ZWRyYV9yYV9wX15v XFxoVVVqXmR3anB3a3d0aXR4ZWt4ZWt1anN6b3h3bm1zaml1a3J0anB5Z21vXWNoV1RkVFBqWlty YWJ0b3N0b3N6aWhzYmFzXWJ4Ymd1Z2l1Z2l5bW54a215am16a257Z2d3YmJ1XlpyW1ZrXl5wY2N4 anN6bXV4ZGdyXmFlU0lfTURiTk5kUFBnU1dtWF1wWFdwWFdrVU9qVE5lWlBlWlBwXmR4ZWt+bnqC cn6DcnN6aWp1XFdqUU1yVFB3WFVyXVt1YV5zX19vXFxyX2N1Y2d0Z2d5a2t6Z194ZF11X1BwW0xv WFN3X1p9ZV19ZV2AYl97XVt+Y157YVxuVkxqU0hlYWJjXl9pXV5pXV5oXmJpX2NpY2FlX11lW1Vl W1VjVlFlWFRlWlBqXlVtXVZtXVZtXF1qWltnW15tYWRpX2FpX2FeWk9dWE5fW1xpZGVwaWlyampw Z2p0am51a290am51bXd4b3l4bnJwZ2pqYWRpX2NkWFpjV1hjWElnXE1vX1dyYlpwZWJyZ2N0Z2R4 amhybW5wa21yZG1yZG1wZGVyZWdyaG50anByaXVyaXVzYWdvXWNpZGhybXBvaXJuaHB4aHJ6anR0 a3h3bnpwa29oY2diU0xhUUpfW1xqZWdzZ2hzZ2hwaWtyam1vbnNubXJwbXNzb3V4b3d9dHt/e4J9 eX9zc3Vqam1tZWhyam1tbnJoaW1taW9uanBpaWtpaWtqaW5ubXJzb3puanVraG5qZ21ranJ1dHtq aGdhXl1tY2RzaWptamtyb3BudXNudXNrY19cVFBfT1BqWlttZWp0bXJ1b3V3cHd3a3d0aXR4d354 d355bW5yZWdtX1trXlpjWltoXl9oZGFva2hwaW5waW5pZGVlYWJkXGVjW2RjWl9tY2lwaWtvaGpr ZGdqY2VrYmVqYWRpX2NtY2dqYWJqYWJzZGl1Z2tyaGtwZ2puZGhqYWRtYWRvY2dtX2huYWltY2dq YWRtYWRtYWR1aW93anBzZ2huYmNrY19tZGFqWlhiUVBlWGFzZW54bXh3a3d4ZWl7aW17cH57cH54 bWtwZWRyY2hzZGlzZ2puYmVvW1tpVVVkXWJrZGlwanNzbXV4Y2huWl5yWF56YWd4a210aGl4a294 a296a3N6a3N6aGtwXmJyW1ZzXFdvXFxuW1t0Ymp6aHB4X15vV1ZnTkdiSUNiSkloUE9uWF14Ymdw XV1vXFxtWlNpVk9rVU1qVExtW2F0Ymh7bXR/cHiAa256ZWh4XV93XF57XVt9Xlx1XV55YWJ0X190 X190YWN0YWN4ZWl1Y2d4ZGR4ZGR1YltzX1h1XFdzWlV6Y159ZWF6X2J4XV94Xld0W1RqUUVlTUBj V1tkWFxkWlZkWlZpV1tkU1ZdVEpdVEpdUUleU0piUU5iUU5hVE9nWlVrWFttWlxlWFhlWFhrV2F1 YWpoZWRlY2JnXlhiWlRfV1ZiWlhqY2VtZWhramdramdza2tza2twZXB0aXR5a3d0Z3JtZWhrZGdt YWJoXF1oWFFtXVZyYWJ4Z2h1Z2l6a255b3N5b3N4cHVza3BqY2VpYmRvaGp1bnBzb3hzb3h0bnlw anVoXl9pX2FpYmRuZ2lpZGVqZWduanBwbXNua290cnV0cHtraHNpVVxiTlVjW1ppYV9taGdqZWRp YWhqYmlpZW5uanNyanlyanlzand3bnp5eIJ4d4B7d3pvam5oY2RpZGVnZWpnZWpraWptamtuZGVr YmNrYmVyaGtubXRpaG9tZWpvaG1ubnBvb3Jqam1kZGdvZG93a3dzcntubXd0dX5vcHllX1hbVU5l X1hvaWJraG5raG54bXh5bnl3bnhzanRycHV0c3h5cnR4cHNybmplYl5iWlRkXFZrZ1xtaF1tZF5n XlhjX1piXlheU1RbT1BXUVdlX2VuY2tvZG1rY11lXVdjXVhkXlpoXF9pXWFnX19qY2NzZ2pyZWlq X2hoXWVoX2lrY21yZWlyZWluX2RpW19qXl9oXF1nWF1tXmNyY2p3aG9uY2tvZG1taGtpZGhoXVxd U1FdV1BoYlt0bW91bnB4bXp5bnt9c4d9c4d6a251Z2lyY2hvYWVwZGhtYWRtXmFqXF5qXmR0aG57 a3h6and4Y2hqVltuWF13YWV0amt5b3CCc3p9bnV/bXN+a3J5a2tzZWVyXlhoVU9oW1hoW1hwXGN4 Y2p1Y11uXFZuV1FpU01wVVV0WFh0Ymp9anN7bXJ0ZWpwXVtwXVtvV1hqU1RqVlhwXF5zYWd9anB/ am16ZWh1YmR6Z2mAaGd/Z2V6X2J4XV9zV1dyVlZzW1xzW1xyXV1yXV11Xlp4YVx1Y110Ylx3X1d1 XlZ5Yl14YVx5XmNwVltyVE5wU01rTUFtTkNhUFFkVFVlVVRlVVRpWFVkVFBdUUlcUEhdTkdcTUZe Tk9hUFFiUVNkVFVqWlZqWlZqXV1pXFxtXmF0ZWhwbXVraHB0Y19vXltiVk5fVExiWlZlXVpqZ15r aF9rY11pYVtuXGJyX2VwZGpwZGppZ2ppZ2prYmhrYmhzZ215bXN4bXV5bnd6cHd4bnR1bXR1bXR3 bnh0a3VrZ2huaWpwa2p1cG90cnNwbm9tZWpqY2hoYWNoYWNoXmJnXWFqY2VqY2VraWpwbm9ua21w bm9zc3VwcHNrZWFoYl1qZWdrZ2hqaGlpZ2hnX2JoYWNoY2duaW1uZW9qYmtnYmNuaWp0c3pzcnl6 c3Vza25tZ2RkXlxhX2RlZGlraWpqaGlyaWVtZGFyZ2V3a2pwcHBvb29uZGp0anBybnd0cHl1bnN0 bXJ4bXh3a3dzb3hwbXV4d4B0c31nY11kYVtzbnJ3cnV1cnpybnd4b3l9dH51b3VzbXNybnd3c3t6 dH17dX53cnBkX15tX111aGVyaWhuZWRpZFpkX1VeVlBdVU9iVFhiVFhdVFVkW1xuZ2dyampwZV9r YVtpXlhnXFZjWFdnXFtiX2NraW1rZ2plYWRhXVpiXltnX2RrZGltY2drYmVkXFhiWlZlXFNkW1Fo Wl5wYmd5Y295Y29waWlwaWlzbm1uaWhpWFViUU5kXFhrY190a3V1bXd4bXp4bXp6bn94a317Z25z XmVuXF9vXWFrYmVpX2NuY19qX1xtXF11ZGV4a293am5yXVtoVFFtWFt0X2J1aW1/c3eEdH6AcHp7 bXJ6a3B6aWh3ZWR0YWNuW11vXFxvXFxyXmF4ZGd9YmR3XF5yYV1zYl53YmR5ZGd0am56cHR9bnN4 aW54Z2NwX1xuW1hnVFFoVFZrV1puX2d6a3N/am19aGp4ZGR7aGh7aGV4ZGJ4XFxwVVVqT1ZrUFdp VVNoVFFqUFBrUVFvV1ZtVVRwWlFyW1NwWlRzXFZ3V110VVt4WFpuT1BrTUVtTkZvUEhyU0pfT05n VlVnWF9nWF9jWltfVldeU0pdUUlbUUVcU0ZhUE1kVFBnVlVlVVRnXVRtY1puYWFvYmJqYWRyaGt3 bnh3bnh1Z2ltXmFhWFVeVlNjW1dqYl5wZ2hwZ2hrYV9rYV9uXFZtW1VrXlxuYV5iX15fXVxjXF5p YmR6bXWAc3t7c3p5cHh3b3JuZ2lyZ3R0aXd1a3t3bX1zbnJzbnJzcG9zcG9zbWVqZF1kXlxkXlxn XWFpX2NpYV9jW1ptYWJwZGVybW50b3BzbnJ0b3N1bXR3bnVqampvb290b3B0b3B0c21ramRkYVtu amRvaGh0bW1lY2diX2NjYV9nZGNuaHB3cHl6cnt4b3lyam9rZGljYmliYWhoZGpuanBuaGNrZWF1 aWp3amt0cG1ybmpwZ21zaW9ya3d5c354b3d5cHh+coN6bn93cHt3cHt3dX90c31uaGVuaGV6d394 dH15dYB0cHt5bnt5bntzaHN0aXR0bW95cnR5d3p1c3dzamltZGN1anN9cnp9dIB1bXlqaGlkYmNq XVtkV1VlWFZjVlRiV1RoXVpyb3N4dXl5cG1zamdwX15tXFtnXFtrYV9pZWtuanBrX2NlWl1hWFdk XFtrZGdqY2VqZWlnYmVjXVthW1hjV1hlWlttYWJvY2RyZGR0Z2dyam15cnR5c3lvaW9rW1pkVFNo X15uZWRzanJ3bnV3bXN1a3J7cHl6b3h5Z2prWl1nVFRoVVVoW1tpXFxlXlVnX1ZkX2FpZGVzaHBu Y2ttWldpVlRoVFhzXmN3b3R7dHmAdHp+cnh3bXN0anBwZ2hyaGl5a2tvYmJrXFRuXlZ0YWF4ZGR+ Y2h7YWV1ZGF1ZGF1Y2d3ZGhyaG54bnR9anB3ZGp5ZWN1Yl9qXVhlWFRtWlprWFhvYWh4aXB6Z2R3 Y2F1YmJ3Y2N6ZWN1YV5vWFNpU01iTlNjT1RkUVFnVFRlUU9qVlRvVVVvVVVuVUptVElvUU5yVFBw UVNvUFF3VlFzU05lTUNlTUNtUERtUERhV1toXmJuZ2ttZWpnYmFjXl1iVVNcT01eU0piVk5uWmFw XGNwY2NqXV1lXVprY19vY2RvY2RqY2VuZ2lqa3JtbnRwZ2pwZ2ppYV1hWFVjV1htYWJrZGdrZGdo X1plXVdkWFBnW1NlWFRlWFRiXFpcVlRhXF1oY2Rzb3h3c3t6cHdzaW9tY2RtY2RwZ21yaG5vam50 b3N1cHJ3cnN1dXVycnJuaGNoYl1pXl1rYV9nX19oYWFoYWFnX19rZ2Vwa2pybXBybXBzaW10am5z bm93cnN7eH5+eoB7eXp5d3h4d3Bwb2lzc3V1dXh1c3RzcHJpZWtkYWdiXGJfWl9fXmNram9ycnJz c3Nza3BvaG1hW2FdV11jX2VkYWduZGVuZGVtYWR3am51b3V3cHd1c3draW1taXR1cn1wb3lzcnt4 d4Bzcntta3NjYml0a3V6cnt3b3R1bnN6dH14cnp1cnpybnd6bXV7bnd1cHRvam5ubm50dHR5c3l4 cnhyam1waWt4b3t/d4OEeYJ6b3hybnRwbXNuZ2tpYmdqZG1oYmpiXmRqZ21zdHp1d31yb3BvbW5p YV9oX15nX2JtZWhtaW9qZ21pXV5jV1hfVVRkWlhoZGFoZGFrYV1jWFVoVVVnVFRnUVhwW2JrY2Jt ZGNuZ2dtZWVwbXh3c35ubnBkZGdnYV5pY2FzaW14bnJ3bnV1bXR3bnh1bXd6cn55cH15bW5wZGVp WlNqW1RpXltrYV1zYVtwXlhrXV9wYmRzZGlwYmdrXlxnWldkV1dpXFxvamt0b3B3bXB0am5tY2dr YmVuZGhzaW1yaGlqYWJtW1FqWE9tWFhzXl54ZWt4ZWt3ZWd1ZGVwY2NwY2N4ZWt7aW99Z2t5Y2h5 YWJ1XV5rW1dqWlZyXV9wXF50Ymp1Y2t6Y154YVx1XFd1XFd3XVZyWFFkU0ZiUEReTE5jUFNqV1pr WFtqVE9nUExnTUZnTUZlSENpTEZwTkxyT01yUU91VVN+W1Z5VlFyVUxrT0ZpSUVwUExqY2VtZWhz aWpzaWp1Z2lyY2VrYV9lW1pnWlVwY15raW1qaGtyaGltY2RuYV5rXlxtWlpuW1toYl9rZWNoaGpq am1qaW5paG1tYl5nXFhoW1hvYl9oYWFvaGhtZ19pY1xrXlpuYVxwYVprXFViW1tYUVFfWFhtZWV1 cnh1cnh1a29tY2dpX2NrYmVvamtvamtwZ213bXN3cnV3cnV1d31yc3lyaGlnXV5lXF1pX2FpX2Nt Y2dyZWluYmVpaWlra2t0a3N0a3NwaWl0bW1wcHB1dXV/eX+DfYOAfoJ3dHh6eHl9ent/e4KAfYN5 dX55dX5vb3JlZWhjXl9fW1xdXV1paWlvb29qampwaW5waW5nX2JhWlxpYmdrZGlwZW5wZW5rY21v Z3B0cHt3c35zbm9oY2RnYWtwanVvbnhycHpzcnlwb3doYl9jXVtpZ2pyb3N3cnV3cnV4dHpzb3V4 c3dwa29vaXJwanNwanB0bnR5b3N9c3d5c3l1b3V0anB1a3J9cn+EeYd/en53cnV1dHtycHh1bXd0 a3VoaHRoaHRkam9la3Bvc3twdH1wa21iXV5iVVNnWldjXF5tZWhuaWprZ2hlXVxeVlVfU1NnWlpv ZGFwZWJtXVVoWFBlUU9lUU9hU1dqXGFqYWd0anB3aGp0ZWh5a3R6bXV0am5vZWluZGV0amt6cnl4 b3d0cHdybnR5a3d4anV1anh3a3l6b3hzaHByXl53Y2N0Z2dzZWV3aWRwY15yXV10X19wYmRuX2Jt Yl5pXlttXF1wX2FvZWt1a3J1aWpzZ2huYVxvYl1uZGhyaGt3aWRwY150W1RwV1BtW1VyX1p3ZGh5 Z2p9aGp6ZWh3Y2N1YmJ0X2R4Y2h7Z2l1YWNzW1pyWlhrWFZrWFZlW1dlW1dzXmN1YWV3XlRuVkxr U0huVUpwV01uVUpnT0NoUERhTU1kUFBuVVFtVFBnTkdhSEFfSj9fSj9oRz1vTkRyT011U1B0VU16 W1N7XVt5W1h0XFFvV01wVVV4XFxlY2Rtamt3b293b297a3V9bXd4a29zZ2ptZGFtZGFpaWlqampu aWptaGlpZGNeWlhdUUhbT0ZeUVFkV1doXF1pXV5qY2VvaGpoYVdlXlVpXlhtYlxyY2h4aW5ua2pt amluaWhuaWhzamltZGNrXlxlWFZiV1RqX1xvbXBzcHR4a29uYmVvYWhyY2pwaWtwaWt1anV5bnlz cnt3dX94d4BzcntwbWRpZV1kY11jYlxrZGluZ2tyY2VyY2VlZWVtbW15b3N1a29ycG10c29wcHB3 d3eCen+AeX6DeIN7cHt5d4Z+e4uIf4yEe4h7e4l4eIZ5bndtYmpkXV1fWFhhV1hjWltkY2hlZGlq YmltZGtrYV9qX15pYWhwaG90bndzbXVta3NranJ1cnh4dHpyam1hWlxcWGFoZG1tZ3JwanV1dHly cHVnYVpcVk9fYVxnaGNpaWltbW11b3V0bnRzbm1qZWRnYWlpY2tlZGtta3N7c3p3bnV6bnJ5bXBz aHN0aXR9cIKCdYeAeoB1b3VwaG9yaXBvbnhqaXNvZWtrYmhnZGhtam5ycn5vb3twaGdjW1phVlBf VU9jWl1tY2duaWhqZWRpX2FjWltnWlprXl5wa21vamttZGFjW1djU1FkVFNlWFZtX11yZWl4a290 aGlyZWd3amt5bW53bXBwZ2p0aG59cHd7d4Z4c4J7cnV4bnJ5Z296aHB4anN5a3R4bXV4bXV7aGh+ amp7bXJ6a3B0aGltYWJzW1xzW1xwXGFzXmNvXl1wX15yXWJ4Y2h1Z2t1Z2t4aWt4aWtzY1twYVh0 aWh4bWt6bWhzZWF1XlpzXFdwXFxyXV14Y2V6ZWh9YmR9YmR1YWFzXl5wXmR0Ymh5ZGd6ZWhzX110 YV5zXFdpU05nVUhpV0prW1dtXFhwWE5oUEZrT0RwVEhzW05yWk1qVUloU0doTU9pTlBqUUVpUERj TEFeRz1fR0BiSUNjSj5rU0ZtU05tU050W1B4XlR3Xl11XVx0XVdzXFZ4XGGAZGltX193aWl4a3J7 b3WAdHh/c3d9cHR1aW1zaGRtYl5pZWJraGRnYmFlYV9oYWFfWFhdVElYT0VdTUljU09lV1pqXF5w XmJwXmJvXltuXVpqXVhtX1t0ZWh4aWt5b3B3bW50b3N1cHR3bXByaGtwaGJnXlhjX1ppZV9wbnJ1 c3d7b3N4a293am5vY2dtY2RuZGVvZWl1a290c316eYN+fYJ9e4B6enpycnJvb29wcHB3a3RtYmpp X2FtY2RvaWd4cm91c3dua29tam5pZ2pwcHN3d3l/dH19cnp5bnl6b3p5dIR9eIh7eH53c3lzb3pv a3dpXmtnXGlhW1RdV1BfXltfXlteXF9dW15hWlxkXV9pYmJrZGRtZWhza253dHhvbXBtamtua21w b3dzcnltZWhjXF5dWmJkYWlkYWloZG1ybnd0cHlqY2VXUFNbWFpdW1xcWlhhXl1pYWhzanJtZWho YWNdV1BdV1BeWlhnYmFtaGt1cHR3bW5uZGVnX2JqY2V6cIJ/dYd1cHJuaWppYV9pYV9oY2JnYmFn XWFiWFxiW11nX2JoZ2tta3BybW5qZWdjW1dfV1RkW15vZWlwZWRqX15jWl1nXWFoW1ttX19wanN0 bndyampqY2NkWE9lWlBjXF5rZGd4anN7bnd0bmtzbWpzb3VybnR1bnNtZWpzZ2p7b3N+d4Z7dIN7 cHl1anN4ZWt1Y2l0ZWpzZGl6a25+b3J/cHWDdHmCc3p6a3NwZV9pXlhzW1p1XVxvW19wXGFuXVxy YV93ZGh5Z2p5Z2p3ZGhyZ2VzaGd0Y190Y190aG55bXOAbnJ9am59ZGh7Y2dtXVNrXFF3Y2N3Y2N3 Xl9yWltwVlttU1doV1huXV50ZWp3aG11Y11yX1ptVE1nTkdnVUhnVUhvW1twXFxuW1FtWlBvXFVz X1h5ZV93Y11wV1BuVU5nTUZiSEFoTEBjRzxhQzljRTtfQThkRjxoST5nSD1jSj1lTT9rU0xzWlN0 WlZ5Xlt5XWJ3W194W2SAY21rW1pvXl11Ym19aXR7c396cn53cHl1b3h5a2lzZWNtZGFwaGRrYmVr YmVrYmVnXWFkXFZiWlRjWFNkWlRkWlZpXltzX111Yl95Z211Y2luY2JuY2JuYmNzZ2h6cHd/dXt7 d3h5dHV5b3N4bnJyb3BtamtubWdvbmh3cHd+eH6AdYB+c355c351b3p0bnRya3Jya3J3cHd7dX56 dH14e4R7f4h/gId6e4J6enp6enp+c3t3a3RqZF9qZF9rZ2h5dHV1c3Rwbm9rZ2VqZWR1cnp/e4R/ dXt7cnh0a3N3bnV3bnV7c3p3cHl1b3huZGhnXWFnWFtnWFtlXl5tZWVpaWtjY2VhXVpYVVFhVVZn W1xuaG50bnR3b354cH90cHdpZWtqY2hvaG1qbXlucH1tZWhkXV9fW1xfW1xnXmhnXmhua29yb3Nu Z2deV1ddV1BbVU5YVU1fXFRlY2dvbXBzaHNuY25iW11jXF5fXFZkYVtlYmhqZ21yaG5tY2llX11o Yl9uY3BwZXNya3dwanVwZXBwZXBtZWhqY2VoY2JlYV9kXV9qY2VuZ2lrZGdrZ2VkX15lXF1lXF1l XVxoX15vZGFrYV1jWltiWFplV1xrXWJ3bX55b4B5bm10aWhoWFFpWlNoX15uZWR4bXV7cHl5cnd0 bXJybXtzbn1zbXNtZ21rZGlwaW56b3p9cn15cnRza251aWpwZGVoX1xtZGF6a3B/cHWCdH2Ac3uA bW13Y2NqXVhoW1ZyWlt0XF1uXV5tXF1qWltyYWJzZWN6bWp6Z2d4ZGR1Y2d0YmVzZ211aW94anN5 a3R7Z257Z25+am14ZGdvY1dyZVp6b2t+c29+aW50X2RwV01lTUNiT1FvXF5zZGl5am95ZWVwXV1r VU1lT0dpVEhrVkpuV1FvWFNwWlRzXFZwXGF4Y2h6Z2RyXlxyWk9tVUpnSj5jRztkTD5hSDtlST1o TD9nRzlqSjxtTT5uTj9jTTxkTj1oUU1uV1N1Wlp1Wlp1V1V3WFZyVFB3WFVlWFZlWFZpW190ZWp4 cH97dIN6dXl5dHh6b256b251b210bmtwZ2hyaGlqYmFnXl1pXltrYV1qXVhqXVhnYV5pY2F0Y2R5 aGl1cHJ5dHV3b3Jyam1tZWVvaGhyam96c3h6eHd5d3V9c3l7cnh1cHRuaW11bm51bm53cnV7d3p6 dXd4c3R3c3l7eH5+eXp6dXd1c3d1c3d9dH57c314eIZ7e4l+fYR+fYSEgIeEgIeDfYh9d4J1bWtr Y2JqZWl1cHR0dHRzc3NwaW5yam96eYB9e4N6dXd5dHVwaW50bXJzb3p7eIN6b3h1anNvZF5jWFNj VlFtX1tra2t0dHRzcHRpZ2plXF1eVVZiVVVpXFxua291c3d3c3tzb3htZWplXmNhW1hnYV5jY2Np aWlnY2llYmhfW1xdWFpkWlhoXVxnZ2dqampyaXBnXmVkXV1eV1dlX1tuaGNzb3VwbXNubXdubXdl YmhnY2lnYVxlX1tkZGRiYmJvZWtuZGpoXl9oXl9lXl5nX19tZWVtZWVtZWpyam9vZ25vZ25uZ2tu Z2tuZ2lqY2VyY2hwYmdrYmVoXmJnXltpYV1rZFtpYlhoXFBpXVFoXFNnW1FnWFtrXV95bnl5bnl5 Z2p3ZGhyX2NvXWFuYWl1aHB4dH94dH97c3p3bnV4bXV3a3RraWhpZ2VpX2FqYWJzaHB5bnd4b3dz anJyaGlrYmNlXmNpYmd0am59c3eAdHh4a293ZGhuXF9tXFtrW1p0XmNzXWJvW1htWFZjVlZpXFxq XVt1aGV9amR7aWN4ZF5vXFZwXGF1YWV1X255Y3J6aHB5Z295a2t4amp3aWd4amh6c3N7dHSAa3B6 ZWpwWlRuV1FpVVVyXV1zYWR4ZWl7aGV0YV5tVVRnT05jUEdlU0loVU5tWlN0W1Z5X1t3YWV9Z2t7 aV90YlhzXEhuV0RqTT5oSjxoVD9oVD9uVkVtVURtUERtUERtUUFvVERpUURlTkBlVEduXE91XFd0 W1ZwU09vUU5qUUdtVElqWFNvXVduYWF0Z2dycHh1dHt7c3qAeH+Ad3p9c3d+d3t5cnd7a3V5aXNv Xl1uXVxoW1ZoW1ZpXltrYV1lXmFrZGdyZWd6bm95dHh5dHh4bXV3a3Rzbm9zbm91bXR6cnmAeoB+ eH5+eH5+eH57c3p0a3N1a3J1a3J4cHV4cHV1bXR4b3d0c316eYN6d313c3l1dH51dH54d353dX10 cHt4dH94d357eoJ9eoh+e4l+c36AdYB6c3Vyam1vY2l3anB1c3R1c3Rya3d0bnl5dYB9eYR7eX10 cnVzbm91cHJuanNzb3h1cnhybnRwaWlqY2NnX2JuZ2ltcHRwdHh5d3hvbW5pXVVpXVVkXGNtZGtu bXJwb3RwcnhpanBrYmVpX2NjXl1nYmFvZWd1a21uanBqZ21uYmVvY2dqX15nXFtrX2NwZGhyZ3Jy Z3JoZWdqaGlvb293d3d9fX90dHdta3VycHpzcntvbnhwaWtqY2VjY2NkZGRuaWpuaWpuYmNpXV5k XV9oYWNwZ2hwZ2hrYmVtY2dtZGtvZ25vZWtuZGpwaW5uZ2twXmJwXmJpX2FoXl9pX2NrYmVuZWRw aGdtZF5uZV9uYlprX1doYWFrZGR1bXR3bnV4a210aGl3ZGp1Y2lyaXN5cHp5dYB9eYR/dH17cHl3 aG9yY2pvZGNyZ2VzaGdzaGd4a3J4a3Jua29pZ2ppX2FjWltkWFxpXWFvZ25zanJ5am90ZWpuXVxq WlhlXF1lXF1qYVduZFt1XlhwWlRpWFVnVlNkW1x3bW5+b3J+b3J6aGJwXlhyWl10XF91YWp4Y217 aW97aW99aGh7Z2d9aGqAa25+b3R/cHWCbXR9aG96X2J3XF5yWl10XF90YmV4ZWl7amd4Z2NyXVto VFFiU0xiU0xkUUpoVU5tVUpvV01yXlx6Z2R5ZV95ZV9wWkZuV0RzV0VwVUNwWExvV0p5WEd6Wkh0 VUp1VkxvV0RvV0RvVk94Xld/Z2WGbWuHa2SAZV51WklrUEBqTkNvU0dwX2FyYWJ0Z2d4amp1cHR4 c3d/cn2HeYSAeX5+d3uAeoB/eX97dYB0bnl4ZGdwXV9oXFRlWlFpW11tXmFlX11nYV5tY2d1a293 c3l0cHd4bXV3a3RzcHJ0cnN0cHd1cnh9d396dH15dYB5dYB7c311bXd1a291a290bndzbXV0bXJ1 bnNzb3hzb3h4cn13cHtzcH5zcH5vcn5tb3tzZ216bnR4aXB7bXR5cH17c394bXV6b3h5b3VyaG50 aGt1aW1vbnNvbnNqaXBranJ0bnl/eYR7eX16eHt6eHt0cnV1bnN3b3R1dXh3d3l5cnR0bW9wanB0 bnR0dXtzdHpzdHhub3NqZ2Nva2hvbnV1dHt4dH11cnp0a3hwaHRvZWlqYWRjX1xpZWJ1a3J+dHp4 cHN3b3J0a3NyaXBlY2RjYWJuX2d0ZW1zZW5vYmpoYmhrZWtzanJ9dHt6eHt3dHhranJoZ25qanpv b39zbXVya3RpZGVqZWdraW1raW1rZ2plYWRhYWNra259d396dH1tamtoZWdkYWdkYWdtX2pwY25q Y2hza3BzaW1qYWRrX2FtYWJqZGp1b3V9cHR7b3N9a219a21zaW1vZWlrZ2hrZ2h4anN6bXV6c3V6 c3V5b3VyaG5ubXRwb3d4c4J9eId6d31zb3VzYmNrW1xtX191aGh5b3B3bW56bm97b3B0b25taGdr W1doV1RbV1RfXFhvYm15a3d5am9yY2hwXFxvW1tvXWF4ZWlwZ2hzaWp5bmp1amdwX15pWFdrY2J1 bWt3b3J1bnB4ZGRtWlpuWlptWFhwYml4aXB7bm56bW2AaGeCaWh+a3KAbnR+cnN7b3B+aWd9aGV1 YWNuWlxtWlNtWlNuXVxyYV94YVt0XVdpVlBhTkhhSkNdRz9dRjxhST9iTEdnUEx3XF5/ZGd/ZWJ9 Y195YU94X051XU91XU90W050W059XVN7XFF1XE9vVklrVEBqUz90VFOCYV+Ha3CNcneNbmiIaWN4 W05rT0NqTEBuT0RzZGl0ZWpuZ2lpYmRvamt1cHJ7bneDdX59c3d5b3N7dHl+d3uAeH99dHt5Z29z YWloX1xlXVppW11lV1ppV05kU0lpV1tyX2N0a3h3bnpzaW9wZ21qaGttam5uanNybnd1b3VzbXNy a3J5c3l7dX54cnpwa21uaWpwanBwanByam9uZ2tvaGpuZ2lwZW5yZ29zanR4b3lvc3ltcHdwa21z bm9yZGRvYmJwZGp0aG5ya3RwanNyaXBvZ250aGt0aGtzanR0a3VwaWtvaGpzbXN6dHqDeX+Ge4KH f4SCen9/cHV+b3R9eHt+eX1+d3l9dXh4c3d5dHh4eHp5eXt7dX50bndvZG10aXJ4b3t+dYJ+d4Z9 dYR9b3h6bXV3a2h0aWVqaGltamt1cnqCfod/en56dXl3bnp3bnppbm1na2pqYmlvZ254a29uYmVl Wl1uYmVuZ2t7dHl4c3Rzbm9tZ2JqZF9pZHRwa3tva3RtaXJqZWdlYWJrZGlvaG1zZGtqXGNbX2tr cH2EgpR9eox4eHhzc3NqZWljXmJwYmlyY2ppZWtzb3V7bnd5a3RuZGhrYmVtaXR3c357eIB7eICA cnSEdXiAdHh0aGtwZGhuYmVzaHB5bnd6d319eX97dXt0bnRwa29ybXBycn94eIZ3eoBvc3ltXVZo WFFqXVt0Z2R5dHV5dHWCc3h/cHV3bm1waGduW1FoVUxjU09nVlNuY2t0aXJzZ21tYWdpVVpvW191 YWV9aG16bXp7bnuCc3h/cHV0ZWhvYWNyY2h1Z2t4bm90amt6Y15vWFRqV1FtWlRvYWN1Z2mAbW2A bW2Ea2+Ea29/anKAa3N/bm17aml4Y2F3Yl95Wl1yU1ZpVlBpVlBrWFFwXVZzXU9rVkhlTkBeRzpe QzdbPzNbQzdbQzdfQz9qTUlyWl15YWR/aGKAaWODaWSCaGOEZFqCYld/X1R/X1R/X1d+XlZ7XlFz VklvTzxtTTp0T06EXl2Oa3CUcHWIbWiDaGN7Wk5wT0RrSkNzUUl0am5tY2dnZV9jYlxrZ2Vwa2p3 a3d4bXh3a2p1aml3bW51a214bXp/dIJ5ZXV4ZHR3Y25yXmlrXFVkVU5nUD9nUD9qWFFzYVpzaGd3 a2pwYmRoWlxjXWNfWl9jW2RnXmhvZWdwZ2h1aHN6bXh5a3R5a3RwaGdtZGNrYmhyaG5waHJtZG5u ZW9yaXN0bW9za250a3N1bXR3dXp3dXpvbnVvbnV1Z2ttXmNuW11uW11uZ2dyamp0anByaG51aml0 aWh0bXJza3BoY2JlYV9qaWV0c295eH9/foaHfoaDeoJ3bXN3bXN7en+CgIaCgIZ/foN9d31/eX95 eIJ6eYODe4t1bn10ZW1yY2pybnR5dXt/eYR9d4J7c39+dYJ9eHl5dHV4dH19eYJ6eYB/foaAfYN+ eoB5cHp3bnhubXJwb3RvbXBzcHR1a29vZWlwXVdpVlBtXmN1Z2t1bnNwaW5tY2RoXl9rXWJuX2Ro XmJuZGhlY2JhXl1pYV9uZWRwX15qWlhoYm17dYCJh5aGg5KIgoiAeoB3bXBrYmVqXmJwZGhtaXJ4 dH1+cHt5a3dzZ2pwZGhyaXV/d4OEfomEfomLd4SMeIaDeIB3a3RvaWdlX11qZG11b3h5dX5+eoN+ d3d3b29zaWp0amtycHh5eH97eoR4d4B1Y2drWl1vYl94amh7cnV+dHh/cHV7bXJ1a21wZ2hvXFVk UUpnU1VpVVduXmhyYmtvXWFuXF9pWFptXF1wY2F7bmt+bn2AcH+Db3qEcHt9am56aGt0Y2RwX2F6 bW14amp1Y11vXVdwWE5tVUptWldzX119am6Cb3N+b3R9bnN4Z2h5aGl7Z2R5ZGJ1YV50X115XGNy VVxoUE9nT05pU01uV1FvWkxoU0VoST5iRDleQDVaPDFaRDNYQzJfRztlTUBtVlB4YVt7Z2l/am2I cGqJcmuHbV+AZ1p6YVZ5X1V6YVZ6YVZ5XUpvVEFtSjlwTjxvUU97XVuHbnKIb3OJdW9+amR+XlRt TkRrSj9vTkN5bXBvY2dkYVtcWFNiWlhqYmFrZW5uaHB0aGlwZGVvZWdwZ2hwZ211a3J7ZXJ+aHR4 aXB1Z25tYl5kWlZkUUhlU0lqXVttX11wZV9wZV9oXVxiV1ZjV1tfVFdjWl1jWl1qXl9tYWJqZWlw a293anB6bnRvZ2VtZGNvZWlwZ2puZ2lwaWtvZ25waG9zanR0a3Vzb3pva3d3b3R1bnNvbXBua29v Y2RtYWJrXl5yZGR0bmt0bmt1bWtyaWh3aGp0ZWhqal5paV1kXlxiXFpkXlxvaWd5eH1+fYKCeYCA eH94c3d3cnV5eoCDhIuCho59gIl+eoZ9eYR+e4l+e4mAeoBya3JwY2FyZGJvbWt5d3V4eHp3d3l0 b350b356d396d397dXt/eX97eH5/e4KAfYh7eIN4dHp1cnhta3Vta3VvaHhyanp3bXB4bnJ3Y2Fw XVtuW11uW11zZ2pyZWltY2dtY2drY2JnXl1iV1ZjWFdbWFpfXV5tZWVvaGhtY2RqYWJyY2p+b3eD gJKDgJKGe4yDeYl9bnBzZGdkWmJrYWlyaXV9dIB+dHp4bnRzZGdtXmFuZHR7coKHfY2Ifo6EeH6I e4KCe4R1b3huZ2lkXV9kX290b396dYR/eomEeoB9c3lyaGlzaWpzb3V6d32Ce4R6dH15aXVyYm5w Ymd5am96b3h9cnqEeH6AdHp6cHdyaG5yYlpuXlZtX19tX19vXWF0YmVzYl5uXVpuX2RvYWV1ZGN9 a2p/anKAa3OEb3mCbXd9Z2t1X2RyXV9zXmF0Z2R1aGV0aWNtYlxzX19vXFxuXV5uXV55Z21+a3J5 am93aG10Y2R1ZGV9aWN+amR7aGh1YmJ1XFVrU0xjSj5hSDxlTkRqU0hrVElpUUdtTTpoSDVhRDFc Py1aPStiRTJhRDVnSTtpV0dzYVB5Y2qDbXSGcHWHcneGa2F7Yld0W05yWEx+YVd+YVd+XFB1VEhy TjlvTDdwV1B7YluHbnKJcHSIbXKGam9/YlZyVUlwTUNvTEF9cHdyZWtjXVhdV1NeW1VkYVtrYmht Y2luZGhvZWluZ2tuZ2tyaGl0amt5bXB6bnJ3a3d3a3d4bmRtY1poXUpqX01uYmNyZWd0a2pyaWhp Y15eWFRhVFRhVFRjWFdpXl1rYmNvZWdqaGtqaGtzaHB4bXV0bXJ0bXJza3B0bXJ1bXRwaG9wZ21z aW9yam9vaG1uZW1vZ250anBzaW9oaGVpaWduYmNvY2RtZWhyam10bWN3b2V5b3B3bW55bW5yZWdq al5ra19vY1thVU1iWE9kW1F3c3l7eH5+d4Z9dYR4cHV6c3h4eIiCgpJ/go54eod7d4Z7d4aCf42A fox+eXpvamtyaWhyaWh3c3l9eX97en94d3tua21lY2RtZGtzanJ1bXR3bnV3cHd6dHqAfYZ6d394 eX1vcHRranRpaHJtZ3JvaXR4bXiCd4KCdXd+cnN4amhrXlxvYmJzZWVvZWdyaGlqaGdpZ2VpY15b VVBdW1xkYmNtaXJ3c3t5b3V3bXN9cn19cn19eoiCf42Gf4aCe4KDd3p0aGtjW1dqYl5vZ259dHt9 dIB5cH10bXJpYmdqZGp4cnh/eIiCeouAfYaAfYaAfoJ1c3dvZ2VnXl1nX2Rza3B6d4KAfYiCeYB5 cHh5b3NzaW1ybnd6d3+Ed39+cHl6bXh5a3d4a294a296c4N9dYaCen99dXp7bnd3aXJ1aGV1aGVz Z2pyZWlzZGlzZGlyYV9wX15uX2JwYmR5aGd7aml6Z2R/a2l6bnJ4a299aG91YWhuWl5uWl5wYmR1 Z2l0anB1a3J5am9yY2h0Y2JyYV93aG14aW55aGd0Y2J0Y2J4Z2V9bnN/cHWAc3N5a2t3XlFuVkll TT9cRDdhST9oUEZzWlZ0W1dzVUFuUD1qTDRkRi9kRzduUD9vVU50WlNrW05wX1N6YWuEanWDbXKC a3CCX1R5V0x0V051WE9/X1d/X1d/XEx/XEx5VEV4U0R4XV+CZ2mHbm+JcHKIbXKIbXKAZ1yAZ1x9 Xk91V0h3c3tuanNjYlpcW1NjVlFjVlFlWFhtX19zX191YmJwZGhvY2dtaGtvam50bW14cHB6dH17 dX6Eb3R/am90aF54a2J4cHV5cneAcnd9bnNpYmJhWlphVlVjWFdpXWFvY2dzZ2p1aW1taGtrZ2p3 cHl5c3t/cn2CdH90cHlybnd5cnR5cnRyaGtuZGhzZGdzZGdtZWhoYWNuX2RuX2RqYWJvZWd0aGl4 a210b253cnB6dXd/ent/eX9+eH56en15eXt3c21ybmh0aWNtYlxuWlpoVFR3a3l/dIKAeIR/d4N6 c3V6c3V/fY6DgJJ/eId4cH9zb3p4dH9/fYuCf42Ae39zbnJ1a215b3B4d4B9e4aHeYJ+cHlwaWlk XV1oYWNpYmRoZWlpZ2prZW5tZ291cnp0cHl3dXp0c3h3b3Ryam9oZGpoZGpwb3mDgox/gpB+gI6C f4NzcHR3am53am5vbnNvbnNzaHB3a3RvaGhkXV1rZGlza3B6cnt+dX+Ad31+dHp7dYB/eYR5eIJ/ foiIfpCHfY6Hd4Z6anlnXFtpXl1kaGtwdHh7eoR4d4Byb3BnZGVoX15uZWR1bXl/d4OCfoeCfoeG gIJ4c3R3am5wZGhnX2RvaG15eIJ/foiEe4iDeoeAe395dHh0bXJ4cHWAc4B9b314b3d0a3N4a3J4 a3J6c4N9dYaAeIR7c39+a3R4ZW5zaW1zaW17bXJ7bXJ5ZGl0X2RvW1ttWFhuYV5tX11yYV9uXVx4 X157Y2J5Z295Z293Ymd0X2RyXV1uWlp1YWh+aXB7bXR6a3N4aW50ZWpyZWdwZGV5bXB7b3N9a2h3 ZWJ5ZGR+aWmAbneDcHmEdXh7bW9+ZVdyWkxqSjpkRTRlTkRtVUpzXlx0X114W050V0pvVT5wVj9v Vk91XFV9YmKAZWV6Z2F5ZV+AZGmHam+HbWl+ZGF+YUp6XUd1X1F9Z1iGa2iHbWmEalx9Y1V7XUx9 Xk1/ZGGEaWWGameJbmqIb3CEa22HaGKHaGKHZFqAXlR5bnl4bXhwZWRlW1pjWlBkW1FpX2FrYmNz YmF1ZGNzYmNyYWJpX2NtY2d3aWd6bWp7cH6AdYOCeH5/dXt5cnJ1bm6CdXuDd32DdX5+cHlwZ2pr YmVrY2JrY2J0Ymh5Z211aW94a3Jwa21wa215c357dYCAdYCCd4J3dX13dX11cnh1cnh1bnNza3Bw bXVuanN0ZG50ZG51Y2dyX2N0aGl5bW53b3R0bXJzc3V1dXh6eHt/fYCDfYaEfoeCgIt7eoR6eHt3 dHhzbm9uaWpvYWVpW193bX2Ad4d+eoN4dH16eHt9en51fYh5gIx1cnptaXJrY2p0a3N9e4Z/foh6 eX5ycHVyb3N3dHh4d356eYCEfYx+d4Zwb3RoZ2tvXWNvXWNrX2NpXWFtW15uXF9wY2t3aXJrb3Vw dHp+eX11cHRqZ21kYWdvb3uAgI1+g5J+g5KCfol7eIN0bnd1b3h5dX55dX5zanJzanJ3am5wZGhw aG93bnV4dH97eIN7dXt9d32Cd4SCd4R/eId9dYR9eox+e42Eeot5b39uZW1oX2dlY2Ryb3B7dYB+ eINybW5taGltX1tqXVhnYmN1cHJ/eIeAeYh9eHt3cnV1a29uZGhoX2twaHR7eIOAfYiHfoiGfYeC e4d6dH93b3RwaW5zbXh0bnl3a3R0aXJ0bW9yam11c4J6eId6c4Z1boB0YmhuXGJyXGF4Ymd6bW14 ampzZWNlWFZnUUZoU0dpWlFrXFRuXldrXFVwXFp0X119ZGWCaWp5aGl3ZWdwX2FtXF1yY2h6a3B9 bnN7bXJ/am17Z2l4Y2V4Y2V7bW97bW97aGh0YWF0ZW19bnWGcHiGcHiEbnN/aW6AZ1p4XlFyU0dp Sj9uU1N3W1uDY2SDY2R9Y1h9Y1h6XVF5XFB0W1Z5X1t9ZGh/Z2p9ZGV+ZWd/Z2iDamuDb2h9aWJ5 YVN9ZFZ/a2WGcmuNcneMcHWMamiIZ2R/YlZ/YlaCY12CY12CY1+HaGSDamuGbW6EaWSEaWSGY1h+ XFF+c4B5bnt0aGtyZWlvYmJyZGRtam5vbXB3aG13aG1vZWdrYmNlXl5lXl5qX1xwZWJ6b32DeIaI eoODdX55d3h6eHl/dH+AdYB9d394cnp4aWt0ZWhwa21wa211a296cHR7a3V5aXNza3B4cHVzcnt5 eIJ5eH94d354d353dX16eX54d3t3dX9ycHp0dIRycoJ3bnp1bXlyaG5tY2lyaGt3bXB6dH15c3t5 dHh3cnV5cnJ7dHR6d3+AfYaCeomCeol9eIh5dIR0c3p0c3p0aXJtYmp5bneAdX5/eYJ9d39/d4CD eoR4foN4foN3dHVua21nYmNuaWpteH5yfYN3eXp3eXp9eHl4c3R4cnh5c3l7eYt+e416eIZ1c4Bz aW1wZ2pyaGtrYmVqX15qX15lXWRyaXBzb3p3c35+c3t4bXVwb3dkY2prb3h5fYaCgpKDg5SAe4t7 d4Z3bX14bn56eYN7eoR4cnhwanB0am5zaW1zcHR1c3d1dHl4d3t9d397dX6Ad4iAd4h1cnp3c3t+ dIaEeoyCfYx5dINwdHprb3Vra2ttbW15dYCAfYh3cnNwa21qZGJoYl9nWlpyZGRzb3p5dYB+cnV6 bnJvZWdrYmNpXmlyZ3JydX53eoN6eYB9e4N6eoh7e4l1cnhuanBzZW5zZW5rYmhtY2lyaGtvZWlv ZXV4bn55cHp0a3VzYmFoV1ZqVVptV1x1YWV5ZGl4YVhkTkZjTDlkTTprU0xwV1BzXFd0XVh1Ylx9 aWOAaGmAaGl/a2t+amp3Y2FyXlxvYWV1Z2t0amt7cnN6bWhyZF91Y2d3ZGh9aG9/anJ6ZWp3Ymd6 aG57aW+Eb3eGcHh/a2t9aWl9YWNyVlhuUE1wU09yWF56YWd/ZWuAZ216Z2d9aWmCaWh9ZGN9Xlt9 Xlt/Y2OCZWV+Y2WCZ2l/amp+aWmDa2eDa2eGa2iIbmqMc3KNdHOUc3mQb3WEameDaWV/ZF97YVx7 ZF96Y16CY19/YV16YmGAaGeDZVx/YliAX0x6WkZ6and6and1a21yaGluYWlzZW53bnh5cHp4bXV5 bndzbm1wa2pzZ2pqXmJtX110Z2R4bXh/dH+AeIKAeIJ5dHh3cnV1cnh5dXt+eXp6dXd5cG14b2tz a3B0bXJ3a3R3a3R1bXR0a3NybXBvam5uaHNya3dwb3lzcnt7c317c317eIN7eIN4c4J0b350bnly a3dtaW9ybnRzaWp3bW51a3J+dHqAdYN9cn96a250ZWhzamlwaGdyb3NvbXB0anB3bXN3a3l6b314 dHp0cHd0bnRzbXN5b3N7cnV5dX56d3+AdYCCd4J5eYZ+fot7eoJzcnlraHBuanNzcnl3dX15eH17 en96en11dXh0cnVzcHRybX16dYZ6eYN3dX96dXd+eXqDeoJ4b3dvZ25qYmlzbXh6dH95d4R5d4R0 c3p4d351bXdrY21iY2dyc3d5e4h+gI2Ae4t0b351Z2t0ZWpqaW55eH16cHRzaW1yaGtvZWlwbXN0 cHd4cnh3cHd4b3t5cH15d4R+e4l0cnNyb3B5c36AeoZ7eoR6eYN6e394eX1zcnd0c3h/eouCfY1/ d355cHhyaXBtZGtnX2JoYWNya3RzbXV1bWtvZ2VwYmRtXmFoXF9oXF9qaXB3dX13dX14d354d4B6 eYN5d4R3dIJ7b3VyZWt0X2JrV1prWFtuW11tXWlyYm54anV4anV7amlqWlhlUVRqVlh1ZXR6anmD ZGJ4Wld0V0xzVkp1XVx3Xl11ZGV5aGl9aWmAbW2GcHCEb2+CcG2CcG2AaGd7Y2J1ZV53Z195a2t/ cnKDb299aWlzZ21vY2l0Z293aXJ4Y2p5ZGt9aG99aG+Gb3eGb3eDamt+ZWdzW1pvV1ZvVlF3XVh4 XmR7Ymh/ZWJ/ZWJ/ZW6Ga3SDaml6YmF+XmKAYWSDYl2GZF9+Y2N+Y2N7aml+bWuGam2Ha26HaXOI anSIbWiGamWGaWmGaWmDaWKAZ19+ZGF9Y19/ZWF3XVh3Wk50V0x3XVh7Yl1/YV1+X1x7W0l0VEN+ b3d9bnV4b3dyaXBnYV5qZGJ0Z295a3R4bXV6b3h3bXB0am54a3JzZ213ZWd6aWp4a3KAdHqAeoN+ eIB7dHd4cHNzbXN3cHd+eIB+eIB+cHl5a3RuamdpZWJuZ2tuZ2tuZ2tuZ2twZ2pvZWlqY2VuZ2lu aG5wanB5bnl6b3p6b313a3l1bXd0a3VwaHRuZXJyam91bnN5bm17cG96cn6AeIR/eId0bXtyZWd0 aGlvZ2VtZGNqZWlpZGhvam5wa29zbnJ4c3d1cnhybnRycHVycHV1b3V7dXt+eIOCe4eCeYN+dX96 eIZ/fYt7en96eX51dH50c31wbXh1cn1+e4uCf46Afox6eIZ0dHJycm9va3Jzb3V1c4B6eIaDf4iD f4iCf4N1c3dybndzb3h4cn14cn1zcntvbnh1bXd4b3lyaXBpYWheXVpubWl1d397fYaAe4tybXtv Z2VvZ2VqZWd1cHJ5cHp3bnhwa21vamt3bXN5b3V7dHl6c3h0cHlzb3h5eoN9fod7eXpzcHJybnR5 dXt6eod9fYl7d4Z6dYR3coB6dYSAe4x/eouAe394c3dvZWdoXl9pXl1pXl1oYWVwaW51cHJzbm9z aWpvZWdkXFtiWlhoZ25zcnl3dXp6eX53c3l1cnh0dIB7e4iEe4OAeH97b3BvY2RuXF9pV1toXGJu Ymh0aGt0aGt5bW5wZGVuWF1wW19wanVzbXh9aWd4ZGJ4YVt5Ylx6Z2d7aGh9aWeCbmuEcnqHdH2I dXt/bXN6bW1/cnJ/am1+aWt9aWt9aWt7bnd/cnqCc3p7bXR3a2pvZGNyYWJwX2FyX2V4ZWt6aG59 anCDbnOCbXJ/ZWJ5X1xtWlBrWE9vWFR1Xlp5X2h6YWl9YmR/ZGd/Z2qGbXCDaWSAZ2KDaG2Gam+Q cGqMbWd+Y155Xlp6YmF7Y2KCZ2uIbXKEa22CaWqEZWJ+X1x9YWODZ2mCamR9ZV+CZ2eAZWWCZFh4 W09yVUlzVkp3XlB/Z1iDaGN9Yl1+W0p4VUV+dHp7cnh5bW5zZ2huYmNtYWJtYWRvY2dzaHB3a3R0 bnl3cHt5b391a3t5bXN7b3V6bXiCdH+CeYZ+dYJ7cHl6b3h3bXN5b3V+eH5+eH6Hc4CDb31wb2tk Y19kXV9nX2JrY21waHJuZW1vZ25rZGlqY2hpY2trZW5waHJzanRyam9uZ2t1a291a29waHRuZXJ3 aXJ5a3R5b3N6cHR5dX56d399eX9raG5nXV5oXl9pXV5rX2FtY2doXmJqY2VuZ2lwbm9yb3BvbW5u a214c3d1cHR0cn99eoh/eol/eol7d4Z3coB6eod/f4x+fYd6eYN3dX1ycHhubXJ1dHmHgpGHgpF/ foh5eIJ0c3pycHhvaXJzbXVzdHp6e4KCfoeAfYZ9fX93d3lzcnlzcnl5c351b3pzbnJvam51Z2t4 aW5qY2VoYWNrXWJ4aW51dHt7eoJ1dHlqaW5rY2JrY2JqY2NvaGh5c3t0bndrZWtqZGp7b3WDd32D eoJ/d35zcntwb3l3eYZ7fot6c3N1bm5vb21zc3B4eX95eoB7d4Z7d4Z+eIODfYiEf5CDfo6HgId5 c3lqYlxkXFZpW11uX2JpYWhwaG93a3d7cHt3cnNzbm9kX15hXFtnZGhua291cnh7eH50c3hzcnd3 cHt7dYB7eX1+e397endwb2tzaGJvZF5tZWhyam13bW50amt4a294a294ZWt0YmhzbXVzbXV5a2l6 bWp5ZWN6Z2R7aGp/a26Db2+GcnKGc3mMeX+Cc3p9bnV6aG55Z215a2t7bm5+b3J9bnB/b3l+bnh7 b3N6bnJ9aWt3Y2VzWl9yWF5vXWFyX2N0YmV5Z2p9a2h3ZWJ6XVF7XlNrWkltW0pyV1p0Wlx5XmN6 X2R4Y2F7Z2R/Z2qGbXCJbW+OcnSJc32OeIKUe3eOd3J+a2J0Ylh7Yl5+ZGF/ZWuDaW99aGV3Yl97 XlN5XFB0W1d9Y19/Z2qCaW2CZ2J+Y15/YVF3WEl0VFN6Wlh/Z2WJcG+NdHWDamuCYU94V0Z5eH14 d3uAdHp+cnh5Z21vXWNwY2FzZWNwaGd0a2p1dXV4eHh9d4KCe4d7cHt4bXh5bXB7b3N9dHt7c3p6 b3p5bnl1a293bXB5cHh7c3p/dIJ7cH5va3RpZW5pX2NqYWRrZ2prZ2puaG5pY2lpX2VtY2llYmhk YWdnaG5panBvamtrZ2hvZWtrYmhtZHBqYm5vYmpyZG1vaGp0bW93bnh5cHp4c3Rzbm9uYmNrX2Fw Y2twY2tzaWpuZGVzYmN0Y2RvamtuaWpybXB1cHR1cnh4dHp1dH55eIKAfo17eYh4dYN0cn90c31/ foh/fYt6eIZ6e4J4eX95dX59eYKEf46Ae4t7eIBybnd0cHd0cHdvbW5yb3B4dYR9eol/e4d6d4Jv a3R5dX53eIB4eYJ4cn1zbXhuaWptaGluZ2luZ2lrX2NlWl1jWl9nXWN1bXd/d4Bwb2loZ2FvZGFz aGRtZWhtZWh1cnhva3JtX2hwY2t5cH2CeYaGfo5/eIh1c4Bwbnt1dYJ5eYZ5cnR1bnB1bnBza25v a3RuanNtbnRvcHd0c3p4d357eYiCf46Eg41wb3ljY1dfX1RtY2dyaGt0a3h0a3h7bXSGd36Ad317 cnhqZGJfWldqXl9vY2R0bnR3cHdva3JybnRya3d1b3p4dXl6eHt6eXVycG13aWl4amp6a3B7bXJ1 bXR3bnV0aXJ3a3R9aG97Z257bXR+b3d/cnJ/cnJ/bm16aWh6aGt7aW1+cnh/c3mEdH6JeYODdHd5 am13YmR0X2J5Y2h7ZWp7b3B6bm+AbneCb3iCbm6EcHCDbmt4Y2F5XltyV1RtWlNpVk9pWlNwYVp5 ZV51Ylt7YVx7YVx/ZWJ6YV13XVp0W1d7X2J/Y2WAaGuAaGuAanJ/aXCEaG2NcHWJd32NeoCOfXmI d3N9bWV3Z193Y113Y115YWJ9ZGV9Y1x4Xld1WE10V0xvVlN4Xlt+ZF+CaGN/ZVt+ZFp/Y1B7X013 WFZ/YV6CbXeGcHqIa25/Y2WAXFN6Vk1+eXp9eHl9d317dXt4aWt4aWt5Z215Z213amt7b3B4c3R4 c3R5c3t/eYJ4cnp1b3h0bW91bnB4b3d+dX1+dX17c3qAbnR/bXN3b3J4cHN0cHd1cnh+bnp7a3h0 aGtwZGhvaG1za3BwZ21wZ21qY2VqY2VqXmJqXmJrX2VrX2VuX2JuX2JtX19vYmJzXmVwXGNuX2dv YWhzYWR1Y2d5Z216aG55bW53amtvZ2NwaGR0bW9za251a21wZ2hzYWR1Y2dnZ2dtbW10b3N7d3p7 dYB6dH9wb3R3dXp/eYSAeoaDeoJ1bXRybnd7eIB9eol+e4t9fYl6eod6eYN4d4CCf457eYh7dX5w anN0b3Nwa29qaXBycHh9e5B/fpJ7eH54dHpwb3d5eH99e4Z9e4ZzcndqaW53ZGp4ZWtwZ2hrYmNq XmJoXF9lXmFlXmFuY2t1anNwY2FrXlxqY2V1bnB3bXNzaW91bXRzanJuZW9waHJ1a3+CeIyEeI2I e5F9eYJ1cnp7coJ9c4N5bW54a21zaW1tY2dnZGhkYmVqZWl0b3NvbnVzcnlwd4h4fpCCgIt6eYN1 b21tZ2Ryam14cHN/dH97cHt6cHSDeX2Ef4OCfYB0a2pkXFtqXGFyY2hvbW5vbW51a3JzaW9zanRy aXNybXB0b3N4bm9wZ2h9anB9anB3cnN4c3R5bXB3am5waWtuZ2l4ZGd5ZWh4anN7bnd/a2mAbWp4 bWd0aWN9ZGN5YV9+ZG2DaXJ7bnmDdYCDdHd5am11Y2lrWl9vYWN1Z2l6bnR7b3V6bnR5bXN9aGh/ amp+amh4ZGJ4YlZyXFBrVU1qVExpVlBtWlR1W116X2J9aGWCbWqDcnB6aWh4Xlp5X1t6X2KAZWiH anKHanKCa3CAam+CaW2Dam6Cb3WLeH6JeHSCcG2Abmh7aWN5X1x5X1x6YVx7Yl16YVp5X1h0XFFv V01yWlt5YWJ+aFqAalyEbWSCamJ/Z1h5YVNyV1N6X1uAaXWDa3iEZGiAYWR9W1NyUEh9dXh7dHd9 dHt+dX17dHR9dXWAcH2AcH17cHl9cnp4cnp5c3t/dH+Cd4J7cHl6b3h0b3B1cHJycHh4d355eH11 dHl1bXd0a3VzaW1zaW1wZ2pzaW2AanR/aXN6a3N/cHh9cn15bnl3bXByaGtrYV9nXFtkVlhnWFtq WFxoVlptWlxrWFtoV1RrW1dtXFhrW1dpV1ttW15zXmFyXV9zZ2h1aWp4ZWl5Z2pwamh0bmt0bW9y am1waWluZ2dyZ2V1amlraWpvbW56bXqDdYN6cnt1bXdqbnJydXl/foZ+fYSCen11bnBqZG13cHl6 c4OAeYl/fYx+e4t6eYB5eH9/fYt9eoiAeIJzanRqaGlraWpqa3RzdH2CfYyEf45zdHhub3N0bneA eoN/fYyAfo1yb3BpZ2hvZ2V4b25vbW5lY2RlXF9qYWRuZWRuZWRvam5vam5vZGNuY2JtaW95dXt/ en5+eX1ycnJvb29oZXNraXd4coh/eZCHfY2DeYl+eoN3c3t7c3p6cnl4b253bm13a2hyZ2NlX1tl X1toX2duZW1tZ3J0bnl5dISEf5CHgIl9d3+AdHh9cHR+cniDd32AeIJ7c313dHV5d3h+e3+AfoJ3 bW5pX2FqYWdwZ21yaGl0amt0aGtyZWluX2RrXWJqXF5wYmRyZWdwZGV4aXB9bnV4c3R4c3R0a2Vq YlxvYmJvYmJ1YWh6ZW1/anSAa3WCbmd/a2R9Z1t6ZFh5Yl14YVx6YmV+ZWl9bnV/cHiEb3l+aXNz YWRvXWFwY2N4amp/cHiCc3qDbXJ6ZGl7Z2d6ZWV3ZWR0Y2J7Ylt1XFVuV1NqVE9tVlFyW1Z4XV1/ ZGSHa2uNcnKMc3eIb3OCZ194XVZ3XmJ9ZGiAa3CCbXJ/amp/amp9aGV7Z2R/a26CbnB/am1+aWt6 Z2F4ZF5/YV1/YV15XleAZV5+Y1x6X1h+ZVt+ZVuCZ2OCZ2OAaFuCaVyGbmmHb2p9Y1h1XFF0VlN6 XFiHZW6JaHCIZGJ/XFp6VUhzTkF1bXR4b3d7c39+dYJ/eYKDfYaEeYKHe4R/dYZ9c4N/eYJ/eYKD dYCGeIN+dX19dHt0b25uaWhvaG1yam90a3N3bnVza250bW90ZWhvYWN1aGV3aWd5Z299anN7b3N9 cHSGcoKEcIB3cnVybXB1ZGFwX1xtWlptWlppWFVlVVFjU09jU09oW1ZpXFdqV05qV05pVk9pVk9u Wlp0X191Z2l3aGp3aG11Z2tyaGl1a214cHV5cnd4b2twaGRza2tza2t1b3V3cHd7c397c39yam9p YmduanB3c3mCgo59fYl+eXp0b3BwZWRzaGd1bXR1bXR6c4N+d4d6eIZ4dYN/e4SAfYZ+d3l4cHNv bW5ua21rbXBzdHh7eIB5dX5zb3pzb3p1b3qDfYh+eoZ/e4d7cnN1a211c3R5d3h1dXhvb3JqaGtr aW17d3h6dXd5bm14bWt0bW91bnB3eIB+f4iEgId/e4Jyb3Bwbm9ta3Vwb3l3dIZ/fY6Gf4iGf4iD gox9e4Z9c3d3bXB6cHR9c3d3b3JvaGppX2FpX2FuY19vZGFtY2lyaG5yZ354bYR9dYR7dIN7dX55 c3uDeIaGeoiCd4R5bntva3d3c35+fYJ/foOAd3ptY2dkYmNwbm93bXB0am5wYmRvYWNtXFhuXVpu YV5uYV51Y2l1Y2l1ZXJ7a3h6cHR4bnJ3bWNvZVxvYWVwYmd3Ymt7Z3B3ZGp6aG59a2p9a2p9ZWF7 ZF9+Y2OCZ2eDZ3OAZHCAbnd/bXV9aG19aG10YmhyX2V1YWV9aG1+aW6DbnOEa2p7Y2J1XWFzW151 Ylx6Z2F6ZWh9aGp7YVpwVk9uVUhwV0p3XF6CZ2mGcHWOeX6IdXmDcHSAaWN0XVdzX196Z2d/Z2iA aGl/ZWJ7Yl55ZGJ6ZWN9aGh/amqAa2t/amp+amR+amSAZV59Ylt6X1iAZV6DaGODaGOGaWmEaGiG amV/ZF9+Y16CZ2KEa2GEa2GAYVh9XVV5Wl15Wl1+X12AYl95Wk50VUl0Tz50Tz51a3J3bXN7cHl9 cnp9cnqCd3+DeIOEeYSGd4iHeImHeoCDd317cnV/dXl+eIB+eIB9cHJ5bW54ampwY2NzZXB0Z3Jy ZWd0aGlyZGJzZWN0aWV5bmp0aGl1aWp1a211a219b299b291b3V1b3V4bnR3bXN7amt1ZGVvYl1u YVxvYl9wY2FvYl1qXVhrWlBrWlBtXFtrW1pyXmF5ZWh3amt4a210aGtzZ2pzaGd0aWh+cHmCdH2D eX99c3l5d3h5d3h4dHp0cHd1cnh1cnhyaXBnXmVrZGd0bW96eId+e4t/dIJ5bnt3aG1tXmNtaXJy bnd4dH94dH9+eIB7dX5+eIN7dYB5cnd6c3hzbm9wa21qam1vb3J6dHp9d31ybnRzb3V0c32Dgox/ f4J7e35+dHh/dXl/e4KEgId9entyb3BqZ21wbXOEgImEgImAdHWCdXd7cHl7cHl6eX6Dgod/foZ1 dHt0am53bXBvbXB0cnV4dYSAfo2Df4iEgImAf4l+fYdta2hqaWV3b3R+d3t3dHhwbnJwanBtZ21y aG5vZWtwZGhyZWlwaW51bnN7dYB7dYB3coB7d4aDeoeAeIR5cndyam9zaXl9c4N+e4mAfox/en5w a29tZG51bXd3aXJ0Z290Y2RuXV5vXl1tXFtrXV9wYmR3ZGp1Y2lwZ2p1a296bm95bW55bmhzaGJv YmJvYmJ3YmlyXWR1XVx5YV9/a2WDb2mGcHCHcnKGcHWIc3iIcH+EbXuHcnmCbXR4ZGd0YWNuXVpv XltzYWR3ZGh+a3SCb3iAaGd9ZGN4YVt1Xlh3X1t9ZWF6ZWV5ZGR7Yld1XFFzWkx1XE54Y2qAa3N+ dX2GfYSGdHWAb3B+ZF15X1h0YVtyXlh9Y1+AZ2N+ZFp7Yld6YV17Yl56Y16AaWSAZ1+AZ1+CaGGC aGF9Y1x9Y1x7YVp+Y1yCaW2GbXCIbW+EaWt/ZF97YVx6Wld/XlyDZF6AYlx5X1V3XVN3WFN1V1F3 XVp6YV14WE51VkxyTTxyTTx3aG93aG95am95am93bXN6cHd5bnd9cnqDc32GdX+Cd3+AdX53dHV4 dXd4dH95dYCAcnl7bXR3ZWRvXl1rXlxuYV5rYmVuZGhzaW90anB0b3N3cnVyam1vaGprY2JvZ2V3 aG10ZWp1aHN4anV0cnV1c3d5cnd1bnNuZGVrYmNwZWJzaGRyZGJuYV5tYWJrX2FqYWRqYWRzaGd3 a2p9b215a2l5YV93Xl11Y2d9am6Ed4SEd4SEe4h/d4N/en6Ae396eHt5d3p6cnl6cnl0anptY3Np ZGVybW55c3mAeoCDd31/c3l0am5wZ2puanVybnl0c3p1dHt7cHt+c36AdYN7cH55cnR5cnR1bnN1 bnNvamt4c3SDeIB9cnp3cHd0bnRzcnl7eoJ5fYN4e4J9en6Cf4OIg5KEf46GfYR1bXRwaHJ4b3mH g4yDf4iEd3+HeYKAd3p9c3d7eX2AfoJ+eoB1cnh4bnJ+dHh6c3h4cHV9dYSGfo2Dfo19eId6eod5 eYZwbm1oZWR3b296c3N4eHh0dHR0a3N0a3NwaW5uZ2tvZG1uY2ttaXJzb3hzb3pzb3p3b3J7dHd+ dHqCeH55dHVtaGltZG55cHp/eouCfY2AeoZ5c350ZWp0ZWp6bXV4anN6aWhuXVxuXF9rWl1yXWJ4 Y2h1ZGNwX15wZWR0aWh5am9/cHWCdXd5bW5wZWJoXVpqXVhoW1ZzW153XmJ/ZWuGa3KJdHmLdXqL e4CNfoOMfYKHeH2IcneAam9zYmNtXF1qVlhvW11wY2FzZWN+bnh/b3l9aWt9aWt4X2F3Xl95ZV96 Z2GCZ2d+Y2N7ZFx6Y1t1XFV4Xld9aG2DbnN/dXl9c3d+amN6Z19+ZFp/ZVt7Yl54Xlt9Yl2AZWF+ ZGGDaWWIbW+Lb3KEb2+CbW2HamqIa2uHbWiGa2eCamJ6Y1t7YWGDaGh/bXCDcHSLb3KDaGp5X1N1 XE91WE97XlWCZ199Ylt1XU9vV0l0TkV0TkVzWFRzWFR7WEZ5VkR7Uz51TTl3bXNvZWt1YWN3YmRw Ymd0ZWp4anN4anODcHeDcHd6dXl4c3d3bm15cG9vb3J0dHeAc4B/cn91Z25wYmlvXFpqV1VkXWJu Z2tybXB3cnV1a29uZGhjXVteWFZkWlhoXVxuXV5vXl91Y2d5Z2p0bXJ1bnNwaWttZWhqYl5tZGFr amdvbmp1bWl0a2hrZ2hrZ2hrZGdtZWh3bXN7cniDdXV7bm57ZFx1XlZ0bXJ9dXp/fYyAfo1+eoZ/ e4eAf4d9e4N6en15eXt7eIN5dYB0aXJqX2hpZGN0b251dXh3d3l4dH95dYB4cHVza3BzbXVwanNu anNwbXV1bXR1bXR1cn13c359gISChomEfoSCe4J4cnp6dH1+eoZ4dH9zcHRtam5ybnd1cnp4d357 eoJ+e3+Gg4eGhI6Af4mDeIZ7cH54anOAc3uHgIyIgo2Df4iDf4h9e4Z3dX96eYN/foh+eoN/e4R4 dH19eYJ+c359cn2EeImLfpCAeX55cndzcH9wbn1qaGtraW14cnh+eH59eHt0b3NyaGlvZWdpYmJn X19jWltjWltkX2FqZWdoYl9lX11lZ11ub2V5b3N+dHh1b21rZWNoXF1wZGV4aHR+bnp/eId+d4Z/ cHV5am93am55bXB5b2VtY1pwXFxyXV15ZXB9aXR0aWhtYmFvXl10Y2J9aG+DbnWIdXuCb3V5aGdv Xl1qWlhpWFdvVlx1XGJ7ZW2DbXSEeXiJfn2Nf4iMfoeMeYKJd3+EbnV7ZW15YWJ1XV5vXl90Y2R9 anB/bXN9bXd9bXd3anByZWtwXVdzX1p5Yl15Yl1/YV57XVt0W1Z1XFdzXFd3X1t4ZF2Cbmd/b2d/ b2d+a1x/bV2IcGuIcGuLa2iIaWWEbWeIcGqJdHmMd3uSdX2Qc3qRdHmLbnOJameQcG2LcnWGbXCE Y15/Xlp+Xl+CYmN/a2WEcGqCaWh4X150VENuTj1wUE54V1V0YVp4ZF11XE5wV0luT0VrTUNyU0h3 V011V0hzVUZ4U0F4U0F1c3Rwbm9tZWVnX19rXl5wY2NuZGpwZ213b3J5cnR5cHh3bnV0aWV4bWlz bm95dHWAeYh/eId7cHl3a3R0ZF1vX1hrYmVwZ2pvbW5wbm9zaGRrYV1jV05kWE9nXVRlXFNjW1pk XFtrYmhwZ21zZ2pyZWlvZGFuY19rXl5wY2Nvamt0b3B5cnR5cnR0bW90bW9yam1za255cHp7c313 bW5zaWpvZ2FuZV9waWt/eHqDgoyDgoyCeYOHfoiDfo1/eol+eIN5c356eIZ+e4l7cnNzaWpzaW15 b3N6dH15c3t5dIN5dIN4dHpybnR4a29zZ2prZ2pwa29wa21zbm9wbXNzb3WAhI2Hi5SGh41/gIdz c3VwcHN6b3h9cnp6dHpwanBzbXNzbXN4dHp/e4KDgISCf4N9foR9foR/dH1+c3t6bXh/cn2DfYiH gIyCg4yDhI2AeYh/eId4e4R6fod6eX57en9+fYd+fYd9dH56cnt6eIZ/fYuCd3V4bWtqYWRpX2No YWFuZ2dzdH15eoN4dHpva3JqZVtpZFptXFhoV1RlVFdkU1ZjVVplV1xkXFhiWlZlYV9uaWhtaXJy bnd4b2lzamRpYV1oX1xpXWN1aW+AeoaAeoaCc3qAcnl7b3B5bW5/b2d1ZV1wXlh3ZF54bXV3a3Rz Z2puYmVqXV1wY2N7Z2uAa3CEdXp+b3R1ZGNoV1ZkUVFjUFBoUFFuVld3YmmAa3N/dXmEen6If4mH foiHen6Dd3qHcnl/anJ/amp+aWl7aW96aG53anB1aW96a256a254Z2h1ZGV1XFd3XVh5Ylx4YVt4 XlpyWFRwW09vWk5tVFBuVVFzXFd5Yl2CamSGbmiDb3KHc3WNeH2OeX6Od3KLc26Eb22Ic3CJc3iJ c3iNcHOLbnCNa2qLaWiEaW6Lb3SMcHOJbnCIZVqAXlN4Xlt6YV17aWN5Z2F5YVRvV0puTjtvTzxt T0xwU09wWlF1XlZ7XUl1V0RzU0RvT0BvU0ZyVUh1VEh0U0dzVEl0VUp6b3p6b3p7b3BzZ2huYV5u YV5rYmVuZGh1bnN4cHV7cnh6cHd7bW95am10am54bnJ+cH6Ac4B6dHp5c3l+am17aGp0ZWp1Z2tw Z2hpX2FnXVRkW1FlW1drYV1rYVtlW1VkV1dqXV1yXml0YWtrZGdqY2VuY11rYVttX1t1aGNwbnJ7 eX1+fYR9e4OAd3p9c3d7cnV5b3N4b3l5cHp3a2p1amluaGVqZGJqY2V3b3J9e4OAf4eDeIaIfYuH gIyEfomAeoN+eICCf5F/fY6EeYKAdX54cnh7dXt6eYB1dHt6eYB7eoJ/dH93a3d4aW53aG1/anKC bXRyam1vaGpuZ2t3b3R/fYuGg5GEgIx7eIN1dXVubm50b3N6dXl1dHlzcnd4cHV0bXJzcnd6eX5/ eX9/eX+DeX+Ad31+dX16cnl7cH5+c4CDfo6Ae4yEgpGIhpWGfYmEe4h9eYKAfYaGgoiHg4mEhox9 foR5cnd5cnd3dXqCgIaAenhwamhqXlNrX1RoYltuaGF3dX9+fYeAe31zbm90aWNwZV9vZWdvZWdq XGFlV1xjVlRoW1hfW1xhXF1nXWNtY2lwaHR3bnqDcnOAb3B3ZWRuXVxkW15nXWF0c314d4B9eYR/ e4eEdXh+b3KCbmt/a2l5aGR6aWV9c3l4bnR0aGtvY2dvXl90Y2R5ZWN+amiAdHp9cHd1ZV5nV1Bj UUphT0hpT0hvVU5zYWR+a2+Cd3WNgoCQg4mMf4aJc3iDbXKEanCEanCAam9/aW5+aXB9aG99a216 aWp7aGh9aWl6Z2d4ZGR9Yl5+Y197Z2R6ZWN6Y110XVdrVkpoU0dtVElrU0huVFR5Xl5+ZGGGa2iH cHqHcHqEcHOIdHeIcGqGbmiEbnWEbnWDbnOEb3SCaGOAZ2KAZ2KGa2eIcnmJc3qJcHKIb3CCX1R0 U0d0WlV9Yl1/ZV54Xld+WkZ3Uz9uTUFyUEVrU0xwV1BwWExzW055WkR1VkByTkRvTEFyUUN0VEV0 UEZ0UEZ3UUV5VEd7c397c3+Ac354anVvaGpuZ2lwZGhvY2dzZ213anB6bnJ9cHR5b3V3bXN3am54 a293a3R3a3R5bnd+c3t/b3t/b3t3bXN3bXN3am5yZWlqWlhqWlhlWl1rX2NqYl5tZGFlXFNjWlBo WlxqXF5jXGFoYWVrYmNqYWJtYWJ3amt4b3eAeH97eoR6eYN7c315cHp4cHV1bnN0anBzaW9yaF5u ZFttX11rXlxnYmNrZ2h7d3V/enmCe4KGf4aCfol+eoZ7dXt7dXuCgo6GhpKIg5KJhJSCg4x+f4h7 eoJ0c3p4cn16dH97eIN9eYSAeISDeoeAeIR9dIBza3p0bXtvZ3N6cn6Afo2Cf456eYNta3Vya3Ry a3R5c353cHtzcHRyb3N1cHRzbnJ3dHh5d3p9c3l7cnh9dHt7c3p5dIN5dINybnl1cn2AfpCGg5WD gpaIh5uAeYh7dIN7dYCAeoZ/f42IiJaDgol6eYBycnJqampza3B4cHV9c3d3bXByZGJvYl9qYlxt ZF5zcnd9e4CAeIJ+dX9+cnh/c3l5dX5zb3hyZWtpXWNjWFVnXFhjW1VhWFNiVVVlWFhpYWp1bXd4 cn15c351a29wZ2pvZWluZGh3cHl5c3t9eIeCfYyEe4h/d4OEdXiAcnR/bm1/bm2Cc3h7bXJ5Z19y X1hwXFp1YV55ZWiGcnSGd35/cHh+ZF9uVVBhUD1cTDloTklwVlF3ZWSDcnCIeXuOf4KQgo2LfYiE cnh+a3KAbW17aGh9aWt5ZWh5am96a3B/a2l+amh9ZGV/Z2h9ZGN6YmF6ZWN+aWd+anWEcHuIc3V5 ZGdvV0ppUUVoUTxvWENvWFB0XVV+ZF+DaWSDbmuGcG6Db2l/a2WDaGSDaGR6Y299ZXKEb3KCbW97 ZF53X1p0YV5+amiIdHeNeXuIbmmCaGN9Xlh4WlR1XWGCaW2DaWR5X1t5VT9zTzpvT0p3VlFyWEp0 W016WE14Vkp1U0NvTT1zSEB0SUF4UDx7VD9zTzx0UD17Uz6AV0N9cHR7b3N4cHNza251a290am5v YmJyZGRzZWV1aGh6bnR9cHd5cHh1bXR1bnNyam91anV3a3d1bnN6c3h9dIB/d4N9dH57c317bnl1 aHNiXVNeWk9hWlxqY2VzaGRwZWJrXlxkV1ViVk5kWFBjVlRnWldrXlxtX11yZWd4a216b3h9cnp3 cnV0b3N4bWt1aml0aGt0aGt3bW5zaWpwa2pybWtyZGJtX11pYV1tZGF5bm2Cd3WEfoSIgoiDgol7 eoJzcHR1c3d6eId9eomHf5CIgJGGhI6Af4l+e31yb3B1bnN3b3R1cnp4dH16d317eH57eIN7eIN3 cHl3cHl6cIJ/dYd/eYR7dYB0b3Bwa210bnl4cn1+dHp5b3Vyam9uZ2tuZ2tyam94a297b3N7c3J+ dXR9dXp7dHl4dYN1c4Bwb3R3dXqCf42EgpCIhpWIhpV/eId7dIN7d4eCfY2DgJCJh5aEgpB5d4R0 cnNraWp4a219cHJ4cm91b21vZ2V0a2p0aWVwZWJvaHd4cH99dHt/d359c3d6cHRybnduanNwZWJo XVphVU1iVk5kWlZlW1doXFNtYVdqYmFvZ2Vya3dzbXhza3Byam90am5yaGt0anB3bXN5b39+dISA eIR9dICAdHp9cHeCcHKGdHWDdHl+b3R+aW54Y2hwXlh3ZF57bXKGd3uLeoR/b3l7Yl5tVFBnTkBd RThkTEdyWFR4ZHKJdYOOgIyQgo2NgISLfoKGb3d5Y2p3YmJyXV13Y2V1YmR0aGt1aW1/amh9aGV9 ZGh+ZWl5ZGJ5ZGJ6aWp9a22Da4KLc4mRd32LcHeCX1VzUUdrUTluVDtvV0l3XlB7aGKAbWd/amp/ amp+aWd+aWeCXlqAXVh9YmKAZWWAZ199Y1x1XVByWk1wXVRzX1aDbmuEb22CaGR9Y193YVN1X1F0 X2J/am2CaVt5YVN3XEV5Xkd9Y16AZ2J9Y1Z5X1N+VEx3TUVvTT10UUF5UEV9VEiAW0x6VUZ0TD50 TD55UDqAV0B6aWp4Z2h0bW91bnB5bXB4a29vY2drX2NzX2J1YmR5bW56bm94b255cG9zcHJyb3Bz aWp0amt0b3B0b3B3c3l7eH6CeYaAeIR+cHl1aHBiX1RhXlNqX2hwZW53bXN4bnR3YW1rVmJlUEVi TUFfT0BlVUZoWFBtXVV0ZWh4aWt5bXB5bXB4bWlzaGR3a2V0aWNwZ2p1a295cHh9dHt5dXt6d315 bW54a210ZWp3aG11bXd6cnuAfYaGgouGgJB6dYRzbXV1b3h4c3d6dXl+eXqDfn+Mg42Hfoh+e31z cHJyZWlyZWlza253b3J6d4J5dYB1b4Z1b4Z0b35wa3p4bn99c4R+dX95cHpvZWltY2dzanR6cnuD dHt9bnVyZGRtX19tYmFzaGd5am9+b3R+dX2CeYB/eX99d313dHhyb3Nybnl+eoaJhpGIhJCIg5KI g5KDeIB+c3t9eoiCf42HhpCLiZSGhI5+fYd6cHdyaG56Z3KAbXh9dXh3b3J1cHJ4c3R3bXBzaW1v ZWtyaG50bXKDe4CAeXt3b3JybW5rZ2hrXlptX1tpV1BkU0xpXFxqXV1vXWF0YmV1aGh0Z2duZGpv ZWtzaW14bnJ4bXh1anVyaGtwZ2p0Z296bXV7cH57cH54bXV4bXV+cnWMf4OGe3+CeHt5bm14bWtz Yl55aGR9b3iEd3+LeoR+bnh5X1hzWlNwWEdpUUBtW1V3ZF59cn2Lf4uNf42Nf42Gend+c29/am1z XmF0W1RzWlN1XFh4Xlt5Z2F6aGJ+amR9aWN+aWmCbW1+am1+am1+b3KDdHeLd4SLd4SSeXqMc3SD YVZ5V01vV0RwWEV3XFd9Yl1/ZWF/ZWGDaWWDaWV5YV93Xl17W1Z/XlqCZ2KAZWF+ZFp5X1V6V1N5 VlF0XFF3XlSDa2WCamR/ZGd+Y2V5Yl13X1t6YWuCaHOEZFqAYVZ7XFSCYlp/ZWKEameGZFCDYk5+ VESAVkZ3U0h5VUp9W1OAXlaCXlB7WEp3Tj50TDx6Tz5/VEN3ZGp4ZWtwaWlwaWl4ZW53ZG1vYWVv YWVvY2dwZGhua29vbXB0am53bXByb3Byb3B0am5yaGt1amd4bWl5cnR+d3mAfYh/e4eHeYJ3aXJy ZV1yZV13aG15am94b3d4b3d5bXNwZGpuWldrV1VoXlVqYVdzZWN9b215dHV3cnN6cHJ6cHJ6bW10 Z2d3Y2N1YmJvZWt4bnR4dH1+eoN/fYt9eoh9eHl1cHJ1Y2dwXmJkanJvdX2Cf46Cf46EeYR9cn14 bXV4bXV+d3t9dXp7dHl9dXqAdIaDd4h9ent0cnN3aWlyZGRqaGdraWhwZ2pzaW1vZG9wZXBtaHdv anl1bXR5cHh4bnJyaGtuYmVtYWR3Z3N+bnqAdHp+cnh3bm1yaWhvZWlwZ2ptbW90dHd9e4aEg42C fYyCfYx+d3t5cnd1bn1/eIeIh5uMi5+Eg4t+fYSDeIB+c3t6e4KEhoyGg5KJh5aHgpKCfY2AdX56 b3h4d357eoKGeoZ9cn13c3l+eoB+fn56enpyamprZGRwZGh9cHR6cHR4bnJ4a29yZWlyYV1rW1dt XE9qWk1qWlhyYV90Y2R1ZGV0ZWh0ZWhyX2h3ZG15a3d+cHt7cHt1anVyaGtwZ2puaW1ybXB0am53 bXB4a297b3N+dHqGe4KJfX6AdHVwamVtZ2JwX1x5aGSCcHKDcnODbmt7Z2R9Y2mCaG59Z2t4Ymdw XmR6aG6Ac4CDdYOHc35/a3d6amN6amN+amN7aGF/Z1x9ZFp3YWV9Z2uAam+Aam99aGh9aGh9aGV9 aGV5aGl7amt7amuAb3CCdH2Ed3+MeneCcG1/ZVh/ZVh9Xlh+X1p4YVt7ZF6EbWiHb2qNcm2Ha2d6 Y1FvWEdzWk13XVB/ZGGDaGSEal99Y1h7Vkd9V0h4XlR+ZFqDb2WAbWOCamV+Z2J6XVR9X1Z+XmSG ZWuGZ2SJamiGamqHa2uGameGameHZ1uIaFyEYU5/XEl7VkV7VkV7Wk6CX1SMYlaDWk59VEZ5UEN7 Vkl+WExlV1ppW11vXFpuW1hvW19vW19qXGFtXmNtXmNtXmNtYWdyZWtyYm51ZXJ6aHB6aHB5ZGt4 Y2p1aGV3aWd3bW57cnN/d4CCeYOCc3h5am91aGVyZGJwa2pzbm16dHp7dXt5dHV0b3ByZWl0aGt5 am19bnB7dHR/eHh7dHd9dXh9dXh9dXiHdHiCb3N6aWp0Y2RzaW94bnSEcICIdISGgJCEf45/ent4 c3R4ZV9zYVtrZXB1b3p+e4t+e4t+eH56dHp9cHd/c3mDeICDeICDdX6CdH19dH59dH57eX15d3p6 dG91b2pyampqY2NoYWNqY2VnYmVqZWlrY2pvZ25wa290b3N5bmp0aWVuY11uY111a296cHSAeXuA eXt7d3h6dXd5b3N1a29waGd3bm2DfYONh42Mho6DfYaDd32CdXt5cId+dYyHhpqLiZ6Dgox+fYd7 eIN5dYB0dXt7fYODgJCJh5aGgJF/eot5eoB3eH57en+Af4SAd3p7cnV5dHiAe3+CfoR5dXt5am1u X2JrXWJzZGltZ21vaW93amt4a214bWl1amd4ZV51Y1xyX1p4ZV93ZWJ5aGR0Z2d0Z2dyY2V0ZWh4 a3J/c3l+bnh4aHJuYmVwZGhrZ2prZ2ptZWVtZWVyaGl1a214cnp+eIB+dHp5b3VyZWdqXl9tWlN1 Ylt6aWp+bW5+a2V7aWN4a3J+cniAdHV+cnN7aW17aW2Cb3iEcnqAb3B7amt3aWR7bmmDb22EcG6G cmuEcGp6bXV7bneCa3iAaneCaG5/ZWt5ZV93Y113Yl90X115YWKCaWp/cHWCc3iLdXWLdXWRcm6R cm6Jb2iGa2SGa2SIbmeOeH+UfYSWgICMd3eHaVx+YVR1Vk57XFR+aG2Ca3CGameCZ2OCZFiCZFiA a26HcnSLd3eHc3OEamN7Ylt4Vkp5V0x3W1t+YmKEaGqJbW+Eb2+Eb2+Ha2eDaGODaVyEal2EZVR9 Xk16VkB6VkCAXFWEX1iMYViEWlF+VER6UEB7U0eAV0xrVVBvWFRpWlNqW1RuWlptWFhwWFpvV1ht WFtuWlxvWl5wW19vW2J0X2d0Ymh0Ymh7Z2t6ZWp0YmV0YmV9am6EcnV+eX19eHuDb297aGh1ZGN0 Y2JyaGl6cHKAdX6Cd39/c3d7b3N4a3J6bnR7b3N+cnV6eHl7eXqEeHuGeX2GeX+AdHqGeICHeYJ/ c3d6bnJ+a3KCb3WCbnmEcHuHeoyJfY6Cf4N3dHh1aGVyZGJqaGtzcHR7dYB6dH94dH14dH13bnV5 cHh7c32AeIKDeX+CeH5/eH1/eH19dHuCeYCIfn+DeXqAdXR3a2pvZWltY2doYWVqY2hwZGVtYWJz bm97d3iAd3p+dHh4cm11b2qDe4CCen9+e39+e3+Je4SGeICCen91bnNyYWJ0Y2R1eHmGiImHh4mC goR+dHh7cnV1a3+Ad4uLgpmLgpmDgI59eoh1dH5ycHpycHV6eX6DgI6Jh5WIg5Z/eo14dYN1c4B7 d4aAe4t7d3h4c3R4cHN9dXh/en57d3p4bWltYl5oV1htXF1lXF9oXmJzZGl5am94bnJ6cHR5bW55 bW54bWl7cG16b2l6b2l+amh6Z2R0ZWp4aW54bXV1anN5bXB3am5vYmJuYWFtYWJtYWJtY2dtY2du ZWRwaGd4anV7bnl7b3V1aW9tX1tpXFdrWE9uW1FvZGN3a2p5bm15bm1+b3SDdHmGeHOAc25+bW5+ bW59bnB+b3KAbWp/a2l3b3J6c3WEd3+GeICLeH6LeH6Ed4SGeIaJcn6Hb3uEaG+AZGt+Ymd9YWV6 YmF4X15zX2J4ZGd+aW6DbnOMd3mQen2ReoKVfoaUe3WOd3CIc3OIc3OQeISZgI2ahImSfYKIcGh6 Y1t7W1iCYV6Ga3eHbXiGam2Gam1/bm2Ab26IdX6LeICMdX2IcnmNaViEYVB9W1B7Wk91Wlp/Y2OA a2uEb2+Hb2mGbmiIamGHaV+Ial6HaV2JY1aGX1N/W1CEX1WGYl2HY16JZFp/W1B5UT11TjpwTkB5 VkhuXFZwXlhtX1twY151Y11vXVd0XFtyWlhtW15tW15tXF1uXV5vW1t3YmJ1aGh4amp7cG95bm1v Z2VwaGd0bXJ5cnd9eHl+eXqCcHJ7amt0aF90aF9zaWp6cHKAc35+cHt7bm56bW15bW59cHKAdHWD d3iCe4KCe4KGeICHeYKEeHmCdXd+eX2DfoKEd4KAc35/c3l7b3V6a257bW+Cc4SGd4h7eoJzcnlz a25waWtyaG55b3V7cH55bnt5cH13bnpwcHNwcHN1bXR5cHh7cHuAdYB/eId9dYR+d4aDe4uDfYaI gouLfoKEeHt6cHRzaW1wZ2puZGhuYmVrX2NuanCDf4aMf5WGeY57eIN1cn2CeIiEeouDeIOEeYSH gImIgouHg4l5dXtyXWJyXWJ0c3qGhIyHg4l/e4KAeHR+dXJzbXh7dYCHfZGLgJWGf4t/eYRycHht a3N1b3h7dX6AfoyGg5GIhpV/fYx0cHlwbXV+c36AdYB5d3h3dHV4cHN9dXh5d3V6eHd5b2VvZVxp XVRlWlBoW1ZpXFduZGh1a297b3V9cHd5cHh5cHh5cHh9dHuEeHmDd3iDdXN7bmt3bXB1a299bXl1 ZXJ3am50aGtwXV9uW11uXF9wXmJtYWJtYWJqYmFtZGN4Y2p5ZGt7aW10YmVuXFNoVk1qV1FvXFZ0 X2R6ZWqCbXR/anJ5a3d+cHuHc3WAbW96bm94a22Aa3CCbXJ+am2CbnB/cn+DdYOMeIaMeIaNeIKM d4CGeICGeICGd35/cHh/a259aWuDaGp/ZGd1YmJ1YmJ1Y113ZF57Y2eCaW2HcHiReoKQgIaSg4iS foCJdXiHbm+LcnORdYiafpGWgpCMeIaIcGiCamKAZ2OAZ2ODcHeCb3WHbm2GbWuJcHSJcHSJc3iL dHmMb2+GaWmLaF+EYlp5V1p5V1p0WF17X2SCZ2KIbWiLcGOGa16Eal+Eal+Eal+Ga2GJZ1uIZVqC YV6DYl+HZWGEY16OZFSIXk59UzlySC9ySThzSjlyYV13ZWJzaGd1aml1bWl0a2h6bW15a2t1amlz aGdzZWVwY2N0Y2R5aGl6bnR/c3l7dHd1bnBva2hwbWl0bnR5c3l7eIN6d4KCbXJ/am90a2p0a2p1 b215c3B5b3B3bW56bWp7bmt6bnJ+cnV6cnl6cnl9dICCeYaGeoOCd3+Gd3t/cHV7cHl+c3t/dH17 cHl7cnV3bXByampyamp/b3uDc395cnd4cHV3am53am5yZ291anN4bnR1a3J0anB1a3JtaW9taW9r ZGRvaGhza3B6c3h7eoJ6eYB+eIOAeoaEeouIfo6MgIyIfYh7eIBzb3h1anVwZXBrZGRpYmJuanWA fYiNhpWJgpF4dH93c35/eYR/eYSDdYOGeIaIgJGNhpaDhpJ5e4hua21qaGl3c35/e4eMhpGHgIyE fYJ+d3t0bW94cHN/dIKIfYuJg46Efol1dXVzc3N1bXR7c3qDfYaJg4yHhJZ+e414c3Jzbm14b3d5 cHh+eX19eHt1c3R3dHV5d3p3dHh4cGd3b2V1aV1vY1duXlZqW1NpYmRza256bnJ5bXB4bnR6cHd7 cnWCeHuGfYSEe4OIenWDdXB+c3J5bm17aHN7aHN7aW13ZGh1XVxzW1pvXF5yXmFuXV5vXl9vYl9v Yl9zX195ZWV3aWRyZF9wXVZwXVZ1Xlh7ZF5+am1/a26Aa3B/am9+b3KAcnSEc3SAb3CCcG2CcG2I dHeGcnSAbnSCb3WDbniGcHqHdHqGc3mIdXmGc3eGd3uIeX6Jd32AbnR+aWt6ZWh+aWd6ZWN1YWF0 X19wXVtyXlx1XFd/ZWGEbnOSe4CRgomSg4uSfniJdW+GZ2GLa2WNdYKSeoeSeoeMdICLcmeDal9+ aW6CbXKEc3SCcHKEa22GbW6IbnSJb3WJcG+JcG+Lal6IaFyCY2F/YV5/XFp7WFZ5W1h+X12GZGKI Z2SLaF+HZFyDZF6CY12EZV+HaGKHZFiDYVV+XViCYVyHY2GEYV6MYVCDWEh+VDx5TzhyTDNzTTR5 ZWh7aGp6anR7a3V6dHp5c3l+dHh7cnV5cnR4cHN4aWt1Z2l3aGp5am1+c3uAdX5/dXd5b3BzaW10 am53aG99bnV3c3l1cnh/cHV9bnN/cHV9bnN5b3N5b3N4bm93bW5+bWuCcG97cG97cG94bm90amt7 bW9+b3KGeoODeICJeHmEc3R6bnR5bXN9anB6aG55bm1zaGdtY2duZGh4aW59bnN9cHR+cnV7bmt6 bWpvZWlvZWl0aG53anB3am53am5zZ2pzZ2pvZWdwZ2hvaml3cnB+eX1/en6He4SCd3+AdX6DeICD fo6Ef5CAfox7eYd4cn1rZXBvZF5vZF5wb3R/foOMhJeEfZBua3pzcH96eIl5d4h+dYKCeYaHepCO gpeIhpWCf451c3Rwbm96cICDeYmLg5KMhJSHgId/eX90bXJ1bnN5cHqIf4mLhJCJg456e4R1d39y b315d4R/eYSLhJCGg5GAfox9eHd1cG9zb3V6d31/eYR+eIN5dXtzb3V4dXd4dXd6dXd6dXd7b3N1 aW1zYlVvXlFtYWRzZ2p4a215bW56aWh7aml6a26AcnSHe4mJfoyMfomMfomDeX1+dHh6aG55Z214 aWt0ZWh9ZGN6YmF0YVpuW1RpWlFqW1NvXFxwXV11XVx7Y2J1aGh1aGh6Y1t7ZFx5ZWV/a2uCc3iG d3uGc3eEcnWGcHOGcHODdHmGd3uNfoCMfX+Oe3+Jd3qAcnd+b3SCbnCEcHOAdHh+cnV6cHR7cnWG d36IeYCEdXp6a3B9ZGN7Y2J3Y1p3Y1p3XFxyV1dwXFpzXlx3Xl+AaGmEcH6Qe4mSf4aSf4aSeG2I bmN/ZGSHa2uMd3uRe4CRd32Nc3mEbWSAaWF/bm+DcnOLc26GbmmEaWmGamqHbm+Hbm+IbWiJbmmO a2GLaF2EZ12AY1qAXlR9W1B6Wld/XlyHZWOJaGWLZ2KGYl2AX1uHZWGHaV2HaV2JaFiCYVGAXFGE X1WDYViEYlqGXUiCWkWEW0SEW0SCXUmCXUl+b3SAcneCc3iAcneDeoKCeYCAd32Ad31/eHh9dXV5 b3N5b3N7b3V9cHeAdYN/dIJ+eoB6d311a29zaW11Z2t3aG1waWt3b3J+cniGeX+IeIKEdH6AdHh+ cnV7cHl5bneEcHCEcHCAc3N/cnJ7b3B4a217cnN/dXeHfYOGe4KEeoB9c3l5bmpzaGR3Ymd4Y2h1 YWFwXFxoWlxpW11vYWV5am+AcnSCc3V+cnN4a21vZ2VtZGN0amt3bW55cnJ0bW1zbm90b3B4bXV3 a3Rwbm90cnN7cnWHfYCHgoZ/en51bnN5cnd/eIiEfY2Hf5CIgJF7c3ppYWhoXVpuY191dH6DgoyJ fYOAdHpqZWRqZWRubXJwb3R0c3h4d3uCfZCHgpWRh5eGe4x9eYJ9eYKEe4iJgI2MhJSNhpWIgJGD e4x4cnp4cnp7eYeDgI6Lg5SMhJWEgIx7eIN1cn14dH+Ae4yGgJGIgJGHf5CGgIR7d3p6c4OHf5CI go2HgIyAeH96cnl7cnh+dHp+e3+AfoKGgIJ6dXd3aFpvYVNzYmN1ZGV5am16a26CaWqDamt9aG9/ anKDdYOMfoyNgJaOgpeNgISDd3p0amtvZWdyY2p3aG9/a257aGp4ZFtvXFNrWlNrWlNvXFVwXVZ5 Yl17ZF94aF96amJ/a2mAbWqAc3OGeHiEeoCJf4aHen6EeHuAcnmCc3qHdH2NeoOOg4yNgouSf4aI dXuEcHCGcnKCcnuEdH6Dd31+cnh7aW1+a2+DcHeHdHqEeHl+cnN+amF/a2KAb25/bm17Z2d0X19z YVt0Ylx9aG2DbnOJeYiNfYyQgIONfoCEcmR9al16Z2eHc3OLfoSQg4mQd3iIb3CEbWSAaWGAbW2D b2+Jb2uHbWmCZ2KDaGOIa26Ia26JbmqOc2+UdHKRcm+Jb2qGa2d+X057XUx9WleCXlyEZV+HaGKI Z2KEY16GYl+OamiMaV6Nal+LaVWHZVGDY1eHZ1uIZFaIZFaMZ1OMZ1OMZViNZ1qOalqUb16CdXmD d3qGeX2Dd3qGeoOEeYKHeYKGeICHeoCGeX+CeHt/dXl+dHV+dHV9cnp5bnd4eYJ1d391a21uZGVu YWFvYmJvaGh4cHCCdH+HeYSGe4KDeX+Cc3V/cHN+b3d/cHh/dH2Cd3+EeoCCeH59dXp6c3h+dHiA d3qAfYaAfYZ9e4B3dXp4b251bWt0ZWhzZGdvXltuXVppXVFoXFBvXWF+a2+HdHqJd317d3h4c3Ry am1yam15cnd7dHl4dXdzcHJ0bnd6dH16d4J5dYB1eHl3eXqCdXuNgIeIg4eAe395bXN3anB3bnh/ d4CCgIuCgIt9c3RtY2RpYmJza2t9eoiAfoyDdXV7bm5yampyampvam5uaW1zbnJ0b3ODe4uGfo2G gJGDfo5/fYuDgI6HhpCIh5GMhJWMhJWDgpZ9e5B5c356dH9+e42Cf5GDfpGIg5aGgot/e4R5dYB5 dYB+e4l/fYuGfo2Gfo2JgI1+dYKAdIeIe46LgoyGfYd9dH56cnuAbneGc3uDgoyIh5GOho2DeoJ+ b3R5am91bnB4cHOAcnmAcnl/dHN/dHN/cnJ7bm57c32Ee4aGf5aMhp2OhIuIfoR+bW53ZWduZGVz aWp/cnJ7bm59aWd5ZWN0YlxyX1pwYVpwYVp1ZGF7amd/b2iAcGmDbnOEb3R/d36GfYSGf4aIgoiG e4KDeX+Gc3mEcniAd3qHfYCIf4eMg4uQgIiHeH+Jc3iLdHmCeYaDeoeLd4KEcHuAa26CbW9+a3SA bneAc3ODdXWHc3CJdXOLdXWGcHB+amh4ZGJ3Y1x3Y1x9Z26Gb3eHdYiOfZCSfYSNeH+EbmJ/aV14 a2+CdXmJe4SQgouRdXWMcHCEal+CaF2AbWqEcG6JcHKIb3CIaWOIaWOQbm2Qbm2OcnKRdHSSd3OS d3OJdW+Hc21+ZVF7Y097XFGCYleDaVuDaVuEaWKAZV6CaGSHbWmLbV+Nb2KMa1+Ma1+Ia1uJbVyM aFeMaFeMaFWMaFWOamWUb2qUc2qWdW2AdHiDd3qAd32Ad32Hd4OGdYKIdH+JdYCIeIKHd4CEe4OC eYCAeXt+d3l9c3R7cnN+eIB/eYJ5cnJuZ2dtZGFvZ2N3bXB6cHR/eX+Ce4KHen6GeX2CdXl/c3d/ dXuDeX+Gf4iHgImEe4aEe4aAdYB/dH9+eX17d3qAdX6AdX6CeHt+dHh6bnJ6bnJ4a211aWpvZ2Nt ZGFwZFhzZ1tlZF5vbmh9c3l/dXt+dHh9c3d4bnJ3bXB6b3iAdX6AeX5+d3t3cHl4cnp0d4N0d4N7 en9+fYKAeoCGf4aHg4x+eoN9b3p5a3d3bXCDeX2GhIyEg4t9dXVyampoaW11d3qEg41/foh9dXV4 cHB/c3l/c3l5bXB6bnJ1c3d0cnV+foCDg4aDgJKEgpSCf46Cf46HhJSJh5aJgpGLg5KDfYh/eYR6 b3p6b3p7c4l/d42Dd4iIe42GfYeCeYN7c313bnh1dH53dX+AdYOEeYd7eIN4dH93coR/eo2He4R/ dH14bnR5b3V+cniEeH6Ng5WQhpeQiZKAeoOEd4J9b3p5bnd9cnp/eHp+d3l+eXqAe32Lfn+GeXqA dYN/dIKId42Ne5KMgI6IfYuEb3R6ZWp5Z2p7aW1+cnN+cnN+c3J5bm13aWd0Z2R1ZGF1ZGF7amuD cnOAcnR/cHODdHmEdXqDfYiHgIyJgIiIf4eEen5/dXmAbnKAbnJ/cnKDdXWIf4eIf4eQfYOEcnh/ cHiAcnmEeYeJfoyQe4eHc36GcnSCbnB/aXB/aXCCa3OLdHuMd36LdX2JeHmGdHWDb2l7aGJ5Ylp4 YVh6YmODamuGb36OeIeNeHqJdHeAbWN/a2J+b3SDdHmOeISSe4iQdHeJbnCAZ1+AZ1+HbWiMcm2J b2uJb2uMcGmLb2iLcnCJcG+NdHWReHmSeX2Qd3qIdG6GcmuDZ1aAZFSAY1aEZ1qGa16Eal2CaVyC aVyGamOJbmeNbWSObmWLbWGLbWGLal+JaV6OaFuOaFuIaVeLa1qScG6Vc3CZdG+adXCAcneCc3iD dYODdYOLdIOJc4KGd36DdHuDd32GeX+EeYSCd4KEd3+DdX6AeHeCeXh9eX9/e4J/dH15bnd4cHN5 cnR6b3p7cHt9eHmCfX6De4CDe4CAd3qCeHt/en6Ef4OHfoaIf4eEeoCDeX+DdX6Ed39+dX99dH5/ c3d+cnV+dHV9c3R/dHN/dHODcnB/bm17bXJ7bXJ/a2t+amp1aml5bm14cHV5cnd7cnh3bXN5a2l6 bWp/c3mGeX+Deod/d4N3cnN3cnN1c3d7eX1+eoB9eX95eH1+fYJ/eId+d4Z5cH10a3h1cHSDfoKD hI2HiJF5eoNvcHl3bniEe4aLhI2Ce4R9eYKAfYaGgouAfYaDe4CCen+De36AeXuDgI6AfoyDf5WA fZKGe42IfpCCgpCCgpCEfY1/eIh7cHl6b3h3aGpzZGdya3J1b3V6b3qCd4KHeYJ/cnp+a297aW11 anN5bnd9cHeAdHp3dHh4dXl9eX+CfoSGfn55cnJ4amp6bW19dHuEe4OIg5SLhpaUhIyLe4OIe4KH eoCEdXqGd3uIeXuJen2CfX6GgIKLgomJgIiNeoOIdX6HcnuLdX+Mg4uIf4eHen55bXB5am2AcnSH eHqEdXh/dHB9cm56Z2l6Z2l9aWN9aWN+cnWEeHt+dHh7cnV/cnqDdX6EfomIgo2Lf4iMgImGeXp+ cnOAa2t5ZGR6Z2eIdHSJfomLf4uNeH+Eb3eEb22Hcm+IeoaQgo2Qg4mLfoSLdXiHcnSAaGt+ZWl+ aXCEb3eMd4CNeIKNeHiIc3ODa2d7ZF91Xlh6Y119ZWGDa2eLdX2Md36Mc3SGbW6AaWR/aGN9aG2A a3CIc3qLdX2Mc3eGbXCCZ2l/ZGeDammIb26Jb2iJb2iMdG6Od3CJdHKLdXOJdXiLd3mLdXiJdHeL b2uEaWV+YVV/YlZ/ZV6IbmeJcHKEa22La2WJamSEaWKJbmeNbWSNbWSMbmSLbWONbWSMa2OOZ16N ZV2QblyRb12Ub22RbWqRaGWRaGWCc3qCc3qEdICHd4OEd4KCdH+EcnqDcHmAdHqAdHqEd3+Ed3+G eX+Ie4KDe36EfX+DfYOHgIeHeoCCdXt6b3p6b3p1bXRzanJ3bXN7cnh9dXqAeX6GeX+GeX+Ce4KE foSHe4SEeYKDeX9+dHp+cnV+cnV9cHd9cHd7b3OCdXmAd3iCeHmEdXqEdXqAdX6AdX6GeX2EeHuI dXmCb3N7amt9a217cG17cG17bm59b294aW57bXJ9dICDeoeEeYKAdX56dXl0b3Nyb3B4dXd/eH1+ d3t6c3N1bm55a3d4anV0bXJza3Bwb3R6eX56eYCAf4d5dXtzb3V3bnqCeYaIgJCCeol7d4aDfo2H hJSGg5KDfo2Dfo2Gf4iHgImDf4uAfYh7eYh4dYSAdYCAdYB9eoiAfox/d4B/d4B/cnp7bnd1Z2ty Y2h3am56bnJ4cnh7dXuGeX99cHd5bW59cHJ6bnJ7b3N4bXV6b3h5d3h7eXp/foOAf4SAe316dXd1 a216cHJ+dISIfo6LiZGIh46NgIeIe4KIfoSJf4aIe4KJfYOLeoSLeoSAfoKAfoKHfoiIf4mIeIKE dH6AcneHeH2JgoeLg4iHf395cnJ7b3B+cnOMeX2MeX2Je3uCdHR7amt6aWp/amiAa2mCdXuIe4J+ d3l9dXh7bnmAc36EeouHfY2Lf4iMgImId3WAb256aGF6aGF5a2mHeXeHfoaJgIiMeX+EcniAbW+E cHOGdYKLeoeOf4SLe4CId3WGdHN6a253aGp4aW57bXKHcnmLdX2MdG6IcGqGbWuCaWh9Y16CaGN+ a2WEcmuJdHuJdHuLcnWGbXCAZ2KAZ2KCZW2HanKLcnWMc3eIbW+DaGp+ZF2CaGGGbWuIb26Nc2+O dHCNdHONdHOJdHKLdXOIc3CNeHWUdHKOb22GZ2GDZF5/ZVuDaV6Ha2iSd3OOeH2Jc3iIbl+EalyD ZVqLbWGObmKMa1+ObWiObWiLal+HZ1yGZV2GZV2Nal+Oa2GUa2OQaF+QZVqQZVqAcHqCcnuDdYCE d4J9dH57c31+cH5+cH57cnh6cHd+dHp+dHqGdX+IeIKDeoKDeoKCe4SGf4iEe4N+dX15bnd1anNy aGtwZ2p0aWh9cnB/dXeEenuHeYSGeIODeIaDeIaCd4SCd4SAd3h+dHV7dXN4cm9+cHB/cnJ6cnuC eYODfYOEfoSIeoaIeoaHfoiIf4mIgouHgImIfYiAdYCCc3V/cHN+d3d9dXV7b3N7b3N1aW99cHd/ dYaDeYmDeoSDeoR/eX95c3l4c3R1cHJ5dHV5dHV3am5zZ2pwZGVrX2FtY2luZGpwZW55bnd4cnh7 dXt4bXV0aXJ1aXp7b4B/eYSAeoaAeIKGfYeGg5GHhJKEgIyDf4uIfo6LgJF/e4d9eYR6dHp3cHd6 cnt6cnt4dYSAfo1/dYZ9c4OAc4B/cn93b3R0bXJ6a3B4aW5zbXN5c3l+cnV6bnJ1bXR4b3d9cnp9 cnp5cHh5cHh1cnh5dXuCgIaEg4iDe356c3V5cnd+d3uCeomEfYyEf46Ig5KJhIiHgoaLhI2IgouL eoeLeoeIgIaLg4iGf4iDfYaGeIaEd4R/dH9/dH9/d4OCeYaJg4mMhoyJgoKAeXl9c3eAd3qQgIaS g4iOgoaDd3p9b210Z2R6aG6AbnSJeoKJeoJ/c3R9cHJ4a297b3ODeICHe4SQgo2LfYiGd3l+b3J4 Z2V7aml+cnWEeHuIfYuJfoyOeYOGcHp+b3KDdHeIdH+JdYCOe4SJd3+HdXeHdXd+cnh4a3J9aG2A a3CEbnqHcH2EcmuHdG6GcnKEcHCEa22Hbm+DbnWEb3eJdHmIc3iIc3OGcHCAbWN/a2KDaGiIbW2N bmuQcG6Ha2d/ZF96YVODaVuDb2WHc2mQdXuQdXuRdHeMb3KLcnWOdXmOdXmReHuSd3KMcGuEZFyA YViDY2SLamuLb2+WenqWeX6Qc3iLal6EZFiCYVyGZF+MZWSSa2qScGuScGuIal2CZFd/YlaAY1eL a2WLa2WSbWOUbmSUaF+RZV16bXp9b311bXd3bnh3b3R3b3R6b3p4bXh7cG95bm14b256cnCAcnmG d36Cd4KCd4J9eYR/e4eAeX59dXp6a25wYmRyZGR1aGh1bnOCen+HeYKJe4SHe4eGeoaGeIOHeYSC eIiGe4yHe4SDeICAeX59dXqAd32Ad31/d4OCeYaAe4uGgJCIgouIgouMg4uJgIiIg5KHgpGNg5WE eox/e4eEgIyLhomHgoZ7e3tycnJyZWd0aGl4b3l6cnuCeomAeYiAeoB+eH59eHd4c3J1c3R0cnNw YmRvYWNoYWNlXmFlXF1oXl9vXWF5Z2p3bW54bm90aGt1aW1wYmlzZGt3bXB9c3eAeIKEe4aIhpSE gpCCfYCEf4OLgJGMgpJ/fYt5d4R7c314b3l5b396cIB3c4h9eY56d4J1cn19cnqAdX57eIN1cn13 bXBtY2duZGVzaWp1aWp5bW50anB3bXN7eYd7eYd6cn51bXlycHp4d4CEe4aJgIuEfX+AeXuDfYOG f4aCfYx+eYiCfoeIhI2If4mMg42LhIuGf4aEeoCGe4KGgISIg4eLhI2Gf4iHfYB9c3d4cnp7dX5/ eomCfYyGg5GLiJaRiY6EfYKAdYOHe4mQgpCShJKViI6NgId/cm11aGN9aG+DbnWMe4aMe4aAeXt3 b3J7amt+bW6DdHuNfoaOf5GJeoyHeHp+b3J+amqEcHCCc3WEdXiJe4SHeYKGcHiDbnV9bnN/cHWG b3eIcnmJdHmDbnN/cHWAcnd9bnV9bnV/cHWEdXqIdXuHdHqGeHWHeXeIdHSHc3OHbm+GbW6AcneD dHmIdHeJdXiLdXiGcHOEb2+HcnKHcm+Ic3CMcHCJbm6Ibmp+ZGF7Yl6CaGSCb3OMeX2MdXqLdHmL b2uIbWmJa3OOcHiUdX2WeH+UeHiOc3OHbWKEal+EaGiNcHCReHuWfYCWd3OOb2uLaVqIZ1eCX1WG Y1iHZ2iQb3CUeHOUeHOLcmOCaVuCY12IaWONdHWWfX6deHeXc3KUb2GRbV54aHJ4aHJyaGtyaGt1 aWp6bm9+cnh7b3V7bW97bW95b3B6cHJ6cHd7cnh+bnqCcn5/eX+AeoCEd3+Ac3t5b3B3bW55bXN9 cHd+eoOEgImHf4SEfYKGfoOHf4SHe4SEeYKEeYeGeoiIgouGf4iGeoOHe4SEe4ODeoJ/d36DeoKH e4mJfoyJf5CMgpKMho6IgouLh52Df5WHfoiAeIJ6eIaAfoyMho6Mho6Cf4N4dXlyZGRwY2N0aGt3 am5/c3d/c3eAeISCeYaDeX2DeX19dXh7dHd3amt1aWpvY2dpXWFpV1FqWFNjYlxubWd5cnR6c3V4 ZWt1Y2ltX19tX19wZGh9cHSCeYOEe4aDf4uAfYiGeoaIfYiHfY6DeYt4c4JybXt3cHd1b3V3cnV4 c3d6cIB+dIR3c3t3c3uCeYCGfYSHfoiDeoSAd3p1a29zYVtwXlhvZGF0aWV3am59cHR+eH6DfYOD eIN5bnl0b354c4KDeYmLgJGHfoiGfYeJg4yLhI2Ee4Z+dX+HgImJg4yJgIuMg42LhIuHgIeCf4OE goaEgIeEgIeEgpCGg5GMgoaGe397c3p7c3p5d4R7eYeDe46JgpWQg5WJfY6IeYuRgpSQh5SOhpKU iZCLgIeCdWt6bmR9aG+Eb3eLeoeMe4iGe397cnWAbnJ9am5/c3mIe4KNfYmJeYaNeXmIdHSDb3KD b3KDcHSEcnWJeYOIeIKIdXl/bXB+aWt+aWuCbXSDbnWEc3R+bW5/bXWCb3iAbnSGc3mGeX+JfYOJ d3qJd3qNe32LeXqJdXiHc3WIbXKIbXKCc3WGd3mJdXWJdXWNdHWHbm+IbXKLb3SHc3WGcnSIa2uH amqDaml+ZWR7Y2J+ZWR+a3SIdX6ReHuLcnWEaW6Ha3CHanSMb3mRd32UeX+Re3uJdHSJb2iHbWWG aWuJbW+OdHqQdXuRcm+La2mHZFyDYVh9XVGDY1eIaGmVdHWWeXuUd3mScGGLaVqIZGKOamiQd3ia gIKheXWZcm6RbVyQa1twZ2pvZWl0Z2J0Z2J6aGt+a2+Acnd9bnN9bnB9bnB5b3N5b3N7cnV9c3d+ a3KAbnSDe36De36De4t/eId7eIN7eIN6dYZ+eYmHfouJgI2Jg4mLhIuLgomJgIiEgId+eoCEfoSG f4aIgouLhI2LgoyIf4mHf4KGfoCHen6JfYCGgISEf4OIfo6Ifo6Dfo2Ef46GgJR+eYyAdX59cnp5 dX6AfYaNg5SNg5SGgo16d4J5ZWVwXV1vZ2N0a2h6c3N7dHR/dH+Cd4KEfoSIgoiCgoSDg4aAd3h4 bm91aWpvY2RtWlRqV1FoY2R7d3h/dXt6cHdyY2hqXGFnW1FoXFNrX2N9cHSAdYOEeYeAf4d6eYB7 d4Z/eomCeIl9c4R0a3NyaXB4cHV4cHV5d3h6eHl7c31/d4B6cn5/d4N/foiCgIuIh46Hho2EgoZ6 eHt7amd3ZWJwY2F3aWd+cHmGeICAfYaHg4yGgIR7d3p4cn14cn2AeYiMhJSHhJKDgI6LhpWJhJSH eYJ+cHl+eoaCfomNh42Nh42Lh42IhIuHgIeHgIeIfYaGeoOGf4iIgouQgo2OgIyDd319cHd7cHt/ dH+CdYiHeo2HfY2Ge4yMeo2UgpWNiZWMiJSUjJGNhouHe3V6b2l/Z2iEa22IeoOOgImNfoOGd3t6 a254aWt6c3WAeXuJen2MfX+SfoCQe36JdHmIc3iDdHeEdXiHeYeIeoiLeXh+bWt/ZGd+Y2WAbnSE cniGdHWDcnOCbXKDbnOCb3WHdHqHeYKIeoOIe32Ie32Jen2MfX+Me3OEdGuHbm+LcnOLdXqMd3uI d3OGdHCHbnKGbXCGcHCDbm6DbmuCbWqEa2qEa2qGaWmCZWWAZGeCZWiDbXSGb3eLcnCHbm2GZWmE ZGiEanCHbXONd36QeYCHeHqCc3WHa2iIbWmHbWmGa2iLb3SOc3iMbWeDZF5/W1R9WFF+W1iGYl+E Z26UdX2Xd32Uc3mVaWSJXlp9Yl6IbWmQdHeXe36adXCSbmmOZ12OZ11uX2dvYWh3Y2V5ZWh5Z29/ bXWAdHiAdHiAc3OAc3N9dHN9dHN9dXh7dHd6cHR7cnV/d36CeYCAd4d/dYZ+d4Z/eIeEeoyHfY6J foeLf4iIf4mLgoyEg42Eg42HfoiCeYOCe4eCe4eDfpGIg5aNhpaJgpKHhIiDgISDeICEeYKEeYKD eICDfYODfYODfYOCe4J/d359dHuDdHmCc3h7eH6Df4aLgoyIf4mAf4l9e4Z+cGt6bWh7cG99cnB+ dHp+dHp/eX+Ce4KAe4uGgJCIiZKMjZaHhIiCf4N7cnNzaWpuX2JwYmR5cH1/d4ODd316bnRqY1pk XVRqXlZuYlplXF1zaWp5bnl+c35/dH1+c3t6dHp+eH6AdYN3a3lvbW5tamt0b3N7d3p9eId7d4Z5 dYB5dYB9c4N/dYZ9eoyAfpCHhJaIhpeJhpGCfol+c3J6b257b3B6bm+AeYiEfYyGfo2IgJCJgIiD eoKAcneCc3iAe4yIg5SOh5eJgpKRh5mQhpeGdYJ6and6cnl7c3qGgo2MiJSGgouCfoeEfYyHf46J foeCd3+GeX2JfYCHhouGhIl+fYJzcnd5bXN/c3l+c36DeIN/eYSCe4eAeYiGfo2If4mQh5GSjJKM hoyMf4B+cnN+amSCbmiEeHuOgoaOgoaDd3p6bW14amp4bnKDeX2LgIKRh4iNg4mQhoyOgoaIe3+G d3uCc3h/cn+GeIaNeXuEcHOCaGSIbmqMd4CVf4mUfoaRe4OIdHeDb3J+cnOCdXeIeYCQgIiUgISN en6Qe3SQe3SNfXSJeXCJdHSMd3eJd3+Jd3+Gcm+Db22EaWmDaGiIaWOHaGKDaV6Eal+EaWSHa2eH amqDZ2d/Z2qAaGuCaW2GbXCJb2uCaGSAYl6CY1+CZWiGaWuIcneLdHmJcG+Hbm2Eal+Eal+Ham+E aG2Hbm+JcHKHblyAaFZ/XE57WEp6WliGZGOJbXeVeIKaeHWVc3COZ2GMZF6DZGKMbWqSb3SRbnOQ al+MZ1yMZ1+NaGFtXF1wX2FyXl53Y2N4ZWl+a29+b3R+b3R+cnN/c3R/dXl/dXmGeICDdX57d3h5 dHWCdH2CdH1+c3t/dH1/dH+AdYCDeICIfYaJe4SOgImLf4uOg46IgouJg4yHe4mCd4SAdYCCd4KE eoyMgpSMhpGJg46LgomEe4OAeoB/eX+HeH+EdX2Ed3+DdX5+dHh/dXl/eHp/eHqHeH+Gd36CfYCD foKLfYaLfYaCf4OCf4OEenuCeHl6eHt1c3d+c3uAdX59en5+e3+EfY2MhJWSkqGQkJ6Mi5WLiZSC f4Bwbm9uZGp0anB+eIN7dYB9eHtzbnJpZV9oZF5yZF9wY15vY1twZFx6bnJ+cnV/c3d+cnV/dH2A dX6Dd313anBpZWtpZWtwaW56c3h7eIB7eIB6c3h6c3h6c3V7dHd7dYCAeoaHfY6Jf5GJg46Igo2A eXt9dXh/c3mAdHqEfYyGfo2JfY6JfY6NhIyJgIiAd3p/dXmCfY2JhJWRh5uOhJmNhpmOh5qHeoB4 a3J1bWt+dXSDfo2LhpWMgImIfYaDe4uGfo2IeoOHeYKCd3+EeYKGhImGhIl/foZ1dHt0anB4bnR4 bXV9cnp9cnp7cHl7cHmAdX6GeIaLfYuNh5COiJGOgoiDd31+bW5/bm9/eHqJgoSOe4SIdX6CcG97 aml5am2DdHeMhImUjJGUjZaSjJWSgoyJeYODdHuAcnmEcHuIdH+MeX2Gc3eAa26HcnSNe46SgJSU hIyQgIiMd3mDbnB+b3J/cHOCdXuOgoiSe4COeH2OfXuQfn2Qen2Md3mNd3uOeH2Od4aMdIOLcGuE amWGZ2OGZ2OIamGJa2KEa16GbV+IaWOJamSHaGSHaGSAaGt+ZWmDaWWGa2iMbmKHaV2JY2KMZWSI aGuIaGuNcm6Ncm6Lb2iGamOCZ1+AZV5/ZGmDaG2AbWOEcGeHbV+CaFt/XVN5V011VFaDYWOLbnOS dXqXeW+SdGqJaGOLaWSLa2mQcG6ObmWMa2OIaVeHaFaIZVqJZ1twXVt0YV5zYmN4Z2h4bWt4bWt+ a297aW14bnR6cHd+d3uAeX6IeISHd4N+dX97c31+cnV7b3N4bnJ5b3N9cHR7b3N+dHiDeX2Me4aM e4aNf4uOgIyJgpGLg5KEfYyAeYiCd4SAdYOGeYyLfpGMg42If4mNfYeDc319dHt9dHuCdH2CdH1/ dH19cnp/c3eEeHuEe4iIf4yMf4aMf4aHfYCIfoKJfYCMf4OIf4eJgIiOf4eLe4OCfYB6dXl+d3uA eX6AeoOCe4SHeo2NgJSOjJuSkJ+QkJ6Ojp2JiJJ1dH50anB6cHd7dXt4cnh1bnB0bW9taW9ybnR+ dGp6cGd1a2J0amGAcnSAcnSDeX2DeX2CeYB/d36CdXl6bnJoZGppZWtvZ254b3dzcHJ4dXd5dHV3 cnN4bnJ6cHR4dXl6eHuAeX6De4CGf4aIgoiAfoJ9en6CeH6EeoCHgImIgouJfoyLf42Qh5SMg5CA e4t/eomAeoaIgo2MhJSMhJSMiJSMiJSHeYR9b3p6bm+CdXeGfYmLgo6LgIeDeX+AeoOEfoeDeIOC d4J/dH+AdYCDeYuDeYuAeH94b3d1a290am53am53am55bW53amt4Z2N4Z2N3anB/c3mGfYmMg5CL g4iJgod9cHJ5bW59c3eHfYCLfoSHeoCDb297aGh3aG2Cc3iNhIyVjJSXjpmSiZSRgomGd35/cm16 bWh+a3KHdHqIeIKGdX+Cc3WGd3mJfoyRhpSWiJGRg4yOeXuEb3KDbXKCa3B/c3eJfYCSe4OReoKO f4KNfoCUeYKRd3+Rd32OdHqMdYKMdYKJcm2Da2eGamOIbWWLcGuLcGuLc22JcmuJbmqLb2uMaWuL aGqGZ2SEZWOEZV+GZ2GOa1+Oa1+NamKOa2OObWiMamWIbWiJbmmGa2eDaWSDZF6AYlx6YVx+ZF9+ ZF+Ga2eIal6GaFx+X1B5W0x1U06GYl2NbW6Uc3SSc22MbWeHZWGIZ2KObWqRb22Mb1yNcF2HaVyG aFuGZ1WHaFZ/amqCbW17bXJ9bnN5bW55bW56bW14ampzaW13bXB6cHR5b3OEcH6Hc4CDdIaEdYd7 b3V3anB1ZGN3ZWR5bXB+cnV/dIKDeIaHeYKHeYKLfoSMf4aGgJGEf5CCfYx+eYh/eHp7dHeAdYCI fYiIg4eEf4ODd3p9cHR9bXmAcH19d4J/eYSAeoOAeoODeoeMg5CLhpaMh5eOhpKMg5CJgIiIf4eL f4uMgIyQhJCNgo2Ug42OfoiJfYCCdXmAdHiCdXmCfYCIg4eHfoaLgomHho2Mi5KNkJ6OkZ+RkJd/ foZ+d3l9dXh1c3Jyb25wa210b3B4bXV+c3uAd3h/dXd7d3qAe3+IfYuGeoiMgImLf4iGf4aAeoCA e394c3d3dHh0cnV1b3V9d317c3p6cnl6dXR5dHN4bnJ4bnJ7dHl+d3uAeoaDfYiHfoaGfYSCe4KD fYODeX+HfYOJgI2If4yHe4mMgI6LiJeLiJeIgJF+d4d+dIR/dYaGeoaIfYiMhpGMhpGHfYB9c3d5 b3B+dHV/e4KEgIeHf4R+d3t/dH2DeICAeIJ9dH6AcHp+bnh6cIB5b393b291bm50aGl0aGl6ZWp6 ZWp6a3B6a3B5a2l3aWdvYWV3aG1/c3mLfoSJf4OJf4N+cnh5bXN9b3iDdX6GeIOEd4J/bm95aGl7 Z26Ic3qLfpGUh5qSjJeNh5KQgIOHeHp/bm95aGl3bXN7cniIeIKHd4CEen6HfYCJfoeSh5CViIyS homRe3uIc3OCZ2eAZWWCaWqJcHKNeIKUfoiWgIuWgIuUgISQfYCRd32QdXuQdYCOdH+Hbm2CaWiG amONcmqQd3WQd3WQdXuRd32OeXuNeHqUcm+Na2mJY2KIYmGGaW6LbnOWdW2Xd26VdW+RcmuRcGiQ b2eMa2ONbWSLaWSIZ2KEZFp/X1V5WlF6W1N/XFeHY16IYl6LZGGAYVZ+XlSAXFGIY1iMbWeOb2mM bmSIamGGZGKDYl+MamiScG6Ucm2Ucm2Rd2uOdGmNbVaIaFGCc3WCc3WAdHp/c3l7b3V9cHd9bnN5 am95Z214ZWt5aGl7amuCa32Jc4SEeYSAdYB5bXB1aW13aG19bnN/cnqDdX6DeICDeICGeX2Ie3+M fYKLe4CGfo2IgJCGfo6De4x/en57d3qGeIaMfoyNgo2GeoZ/eHp6c3V+c35+c36DfYaGf4iJgIuM g42JgpGNhpWJgpKLg5SMg42Mg42HgIeIgoiJgIuNhI6Ng5SNg5SOhpCNhI6Jgod/eH1/dXmAd3qH fYONg4mHgIeHgIeHho2NjJSSkJ+VkqKSjpeIhI2Je4eGeIN+eXp4c3R0b3B3cnOAe3+Ef4OAfYZ/ e4SDfYaLhI2Lg5KHf46If4mGfYeCe4SCe4SEgIeEgIeAfYiDf4uGeICIeoOAeoaDfYiEeH6Dd317 d3p4c3d7dXt+eH59dH6AeIKDfYaCe4SEfYKHf4R/e4J/e4KJgIiLgomEeYKLf4iHh5WHh5WHe4mD eIZ/dH1+c3t+eIOGf4uGhIyDgomIfoJ/dXl6cHJ/dXeDeoSEe4aCeol9dYSGd36DdHuAeoN+eICD d31+cnh5bXB1aW11aGh3aWl3ZWR4Z2V9aG2Aa3CDc3+EdICCc3iAcnd0aGtyZWl6bXWGeICAfoKA foKGb3R7ZWp1aW16bnKCcnuAcHqCbXR7Z25+a3SHdH2Jf5SNg5eOhpCLgoyQfn+Id3iAa2t6ZWV0 ZWp6a3CCdH+Ed4KEeHuGeX2LeH6VgoiZg4uVf4eUenuJcHJ+a155Z1qAZ2KHbWiLdHmSe4CXgIuX gIuXg4aSfoCOeH+Nd36JdHmHcneHamqDZ2eIbmmMcm2UeH2Xe4CWen+UeH2Uen6Qd3qOb2mHaGKJ ZWGIZF+HamqNcHCVeXuXe36VenWQdXCUdWmOcGSNaGGMZ1+JZF2JZF2CZFd9X1N5Wk96W1B6WlV9 XFeEY16IZ2KEZFyAYViEZFyHZ16JbmmLb2qIamGGaF6EY16DYl2MammScG+aeHeaeHeheXWbdHCU b16MaFd5dHV3cnN6c3V9dXh6dXl7d3p/c3l5bXN5ZWh6Z2l4aW5/cHV/a3mEcH6CdXt/c3l1Z2lz ZGd1ZXJ+bnqAdX6DeICHgoaHgoaJgIiJgIiMf4OMf4OEfoeEfoeIf4yHfouEe4OGfYSEfY2JgpKO gIyIeoaHeH2Gd3uGd3uHeH2DgI6EgpCIhJCIhJCLhIuJg4mIf4mLgoyJfomMgIyIg4eIg4eIgoiL hIuLhI2OiJGMi5WMi5WHhouAf4SIfoKIfoKGgouIhI2EgImHg4yGhI6LiZSUjqKWkaWWkpuNiZKQ h5SOhpKGhIl9e4B5dXt6d32HfouNhJGMg42Mg42Ih5GHhpCIgouDfYaAeoN+eICCd4SJfoyNg5eQ hpqNhpmIgJSMho6Mho6Ifo6EeouDeIOEeYSDe4CCen+Ae39+eX1+dHh/dXmAeoOCe4SJf4OLgISI g4eDfoKHf4SGfoN/d4CCeYN/eouGgJGEfoSCe4KDd32HeoCAfYaEgImEg4iAf4SIe3+EeHuDe3uI gICLgoyGfYeHeIuHeIt+dX2GfYSEf46Ef46GgIR9eHt7aml1ZGN1YmJ5ZWVuZWRuZWRyaGt7cnWE eImMf5GMfoeGeIBzamlpYV90aXR7cHuAeoOCe4R+b3J3aGp3Y2V4ZGd3bXB3bXCAbnR3ZGp4ZG+E cHuLe46NfpGRfoSNeoCIenqDdXWDb21/a2l6a256a25+cniCdXuDdHeEdXiLdX+OeYOSfYSRe4OS eXiLcnCAb2J+bV99bWWGdW6ReHuWfYCUgIeWg4mVgoaSf4OQfYOOe4KNdHiJcHSJamSGZ2GIb3CL cnORen+Se4CXen+Ze4CSeXqNdHWEaWV7YV2DYl2IZ2KLbm6SdXWSe4CSe4CaeHWScG6UcGiOa2OO Z2GJYlyEYVyIZF+HZFyDYVh5XU16Xk56WlV7W1aCY1+GZ2OHZWOIZ2SGZ2SLa2mLa2mOb22LbWGD ZVqDX1uEYVyMaGORbWiXdXSbeXiZd3SUcm+Sa16JY1Z6b256b259cm5+c293dHV5d3iAdHh9cHR7 aW1+a2+Dd3qHen6Jd3qLeHuHcnt+aXNzZGl1Z2t6bnKAdHiDfoKHgoaGg5GHhJKLg5KLg5KMhImN houLg4iJgoeLf4iMgImMgI6Og5GHh5WIiJaQiZKLhI2Sf4aMeX+Dd3qGeX2EgIeIhIuJiJKHhpCJ hIaOiYuOg4yOg4yOg46Og46Mg4uIf4eMfoeOgImRiJKUi5WVkJ+Ujp6Oi5SJho6LgomMg4uLhpWE f46GhI6CgIuCgpCHh5WSjqWWkqiWkaKUjp+VjZ6Si5uOjZWLiZGHfoiEe4aNgJSQg5aMhJSHf46G go2Hg46GfoOAeX59b22DdXODfYiNh5KLgpuLgpuEfYyDe4uJgpKNhpaLg5KEfYx/d4CCeYOGeoiG eoiAeoB9d31/dH2Cd39/e4eGgo2Mg42NhI6Lf4iIfYaDe4CCen+CeH6Ad319c4R+dIZ+d4aAeYh+ eoOEgImIgo2HgIyEgId/e4KGe3+IfoKHg4mJhoyOhJaMgpSNgpCHe4mGgoiHg4mJh5WIhpSJhIiC fYB9aWt1YmR5ZWiAbW9/bm+Ab3B0am54bnKGd4iVhpeOho2LgomGdW59bWV5Z21/bXN/d4B+dX9/ am17Z2l6ZWN7Z2R5a2t9b2+Ab3B6aWp9Y26Ga3eMeYKUgImRfoSLeH6Ed3eDdXWIdHeEcHOAbW97 aGp/aXB/aXB/am2Eb3KHcHqHcHqMdX2QeYCOd3KLc26Db22IdHKLeH6MeX+Re4CVf4SOg4KQhIOS fYKSfYKQfYOOe4KOdXSMc3KOb2uMbWmIc3ONeHiZfX+ZfX+VeXuUeHqReHeSeXiEamV6YVx9XliE ZV+Gb3SNd3uQen2Qen2ScmeQb2SUa2OSamKOaV+LZVyGYl2HY16HY16EYVx7XUx1V0Z3VU16WFB+ X1qCY12Lam6NbXCQdG+JbmmLa2WQcGqHbWKDaV5/XVODYVaGaFuLbV+RdHSUd3eWc2iQbWKLZFWJ Y1R5am97bXJ9cHJ/c3R+dHh+dHiAcneCc3h/c3mDd32Gf4iGf4iMeYKLeIB/dHN1aml4aWt5am16 cHSCeHuIfYiMgIyLg5KJgpGLg5SNhpaOiJGMho6NhouMhImJgIiMg4uOhpCOhpCJiZaLi5eUjZSQ iZCMhImGfoOCeH6HfYOJgoeNhouLhIuLhIuOh4yOh4yMg5COhpKLh5KLh5KMg5CMg5CNhJGOhpKR iZ2UjJ+VkqGSkJ6SjJeMhpGSiZaUi5eQh46Mg4uJhIiCfYCGhI6NjJaUkqeWlaqVkKGNiJmLhpWN iJeQjZuOjJqRg5GJe4mLgo6OhpKLg5SGfo6DfYiDfYiIeIKAcHp9anB/bXODe4uMhJSEgpB+e4l7 dHR/eHiCe4eHgIyEfoeAeoN5c3l/eX9/d359dHt7c319dH57c39/d4OCeomGfo2GgJGLhpaQh5SM g5CJhIaGgIKCdH+DdYB9d317dXt4c4J6dYR+fYeCgIuMg4uHfoaDe4CAeX6EeYKLf4iMiJSQjJeS iJmQhpaJho6Hg4yMho6Nh5CQh5GRiJKQg4eIe395bm13a2p9cHSEeHuDc3+Dc394bWt4bWuAbXqS foyUiJGRho6NfoCCc3V/am+CbXJ/cHV/cHV/amp6ZWV4ZGd9aWuAb3CCcHJ/bXB5Z2p4Y2iAa3CJ d32NeoCOf4SHeH2AcneAcneHdHqCb3WDbmt+aWd9aGV6ZWN7Z2d+aWmDbnCIc3WMdYKNd4OMc3KG bWuCbXKGcHWMeYKRfoeRgoeRgoeXgoyWgIuUfoaOeYCOeX6NeH2OeX6Md3uLd3CHc22LdXqNeH2X e36ZfX+Zf36WfXuXfn2SeXiLbWOCZFt6X1t/ZF+Ca3OLdHuUd3eUd3eOcGeLbWONa2mMamiQbWSL aF+JZ1yEYleGY1iGY1iDYUx+XEd4U0R5VEV+XUyHZVSOa26VcnSZem2UdWiLa2WMbWeHa2SEaWKG Y1iEYleLaF2RbmOSdXqUd3uXb2eQaF+LYVCJX097aW9/bXOAcneCc3iCdH2Ac3t/dH2DeICEfYyH f46JiJKHhpCOf4eDdHt1bm5vaGh4a217b3B9c3eHfYCIgIOJgoSMg42LgoyLgJGMgpKOiJSNh5KM hoyJg4mJg4yLhI2Nh5CNh5CLh5CNiZKNjJSQjpaOjZeLiZSJhIaEf4CHfouJgI2Hf46Lg5KRiJWN hJGHgIyMhpGIh5GLiZSMgpSNg5WNhpaQiJmSi56UjJ+RkJqQjpmUiZqOhJWShpeXi52RiJCOho2I goiCe4KIgJCRiZmQiKeQiKeOg5GHe4mGgoiMiI6Rjp6QjZ2Qg5aIe46EeoyJf5GJfoeEeYJ/d4B/ d4CIeIeCcoB6bnJ5bXCDeYmGe4x+eX15dHh1b216dHJ/dXuAd31/eHp6c3V4b256cnB6dHJ5c3B7 c3J6cnB4b3d7c3qCeYCHfoaGgJGLhpaUiZuSiJqQiZCJg4mGeoaCd4J/eYR+eIN6cnt1bXd7cHmD eICMe4aIeIKDd3p/c3eDdYCMfomOh5aUjJuRi5SLhI2GhI6LiZSOhpKOhpKOg46ViZWXh5GNfYeC dXuAdHqAeoaEfomCfoR7eH55b2V3bWN+aW6LdXqMgI6QhJKJgoSHf4KHdXSGdHN+cml6bmV9aWd5 ZWN5ZGd/am2Eb3KHcnSCb3N+a297aGp/a26Cc3qJeoKUfoiLdX+CbmuCbmuEcnWDcHSAdGt9cGh9 aWl3Y2N3ZFt6aF6EbnOMdXqRfoeOe4SMfn6HeXmGcG6Hcm+Jd32Sf4aUgImRfoeWgpCVgI6WgIiS fYSOeXuLdXiMd3mOeXuNeXeNeXeQeYCReoKVe3qVe3qVf4KVf4KWgoSOen2ObmOGZVt/YVuDZF6C a3CMdXqRdXiRdXiMcmeHbWKJb2uJb2uMamWJaGOIZ1eHZVaJZ1yNal+LaFOCX0p+VER6UEB+WEyI YlWManWXdYCefXSZeG+Vbl6QaVqOamWQa2eMZ1+IY1yMaGeSbm2WeG6Vd22RbmWMaWGIY1iIY1iD bnWIc3qAeX5/eH1/dH9/dH9/eYKGf4iGgJCEf46Mg4uIf4eEeXiAdXR7b3B9cHKHenuLfn+LfYaL fYaGgo2JhpGLhI2LhI2MhJWJgpKOh5aOh5aNhI6NhI6LhJCLhJCGgo2EgIyIg5KNiJeOjJqQjZuS i5qRiZmSiZSLgoyJeYaIeISCeIiDeYmHe4SIfYaDgomIh46NhJGNhJGGhI6HhpCLhI2QiZKSiZaU i5eSjpqOi5aOhpCOhpCRh5eSiJmRiZmQiJeJhJSCfYyHeoyJfY6GfYeGfYeDe36Hf4KGh42QkZeW lKaUkaOMh5aDfo2DfYODfYOEd4KAc354dHp9eX+IeoaGeIOAe399eHt/dH+EeYR9eHd4c3J9cHR/ c3d/c3mAdHqAd3h7cnN5bmp7cG1/dHCAdXKCen2AeXt7b3N6bnJ/d36CeYCIfYuMgI6QhpeWjJ6W i5SMgImHfoiCeYOEe4OAeH9+dHp3bXN6a3B/cHWGd36IeYCEc3SEc3SCdXeGeXqMhpGRi5aOhpKJ gI2Jf5GIfpCLf4uQhJCQgouRg4yVgouOe4SIeIKLeoSLhIuMhoyNh42Gf4aEdGl9bWJ5a2l/cm+D eX+IfoSLgIeJf4aLfoKHen5/c2l3amFzYVtzYVt3ZGh+a2+Ic3iLdXqLd3mEcHOAa26DbnCEcnqL eICNeoCIdXuCcG9+bWuAbnKIdXmEd3SCdHJ9cm54bWl6aGGAbmeGcm+Ld3SRe4OVf4eVf4eQeoKM eHiNeXmQeoSRe4aUfYSSe4ONg4mNg4mUgISQfYCNdHOIb26McniUeX+Re4CRe4CQd3qNdHiQdHCO c2+JdHmSfYKagIKVe32NcF+GaViAaF2AaF2Eb3KLdXiMcmqLcGmHb2mHb2mNbW6Obm+Ob12Ob12N a1eMalaMa1+NbWGQaVeLZFODWkN7Uzx6VkOCXUmHZWSVc3Kee3eee3eXb2eUa2ORaGiOZWWJaV6I aF2LaWeQbmuUcm+ScG6Ra2KQamGNYlGOY1ODe4CIgIaGfYeEe4aDeICCd3+Je4eOgIyMgpSGe42C eYN+dX9+cnOCdXeGe32Jf4CShoyOgoiQgo2Qgo2LgJGLgJGJgpKIgJGIgo2MhpGLiJaLiJaRho6R ho6Lgo6Lgo6Jf5GJf5GMh5eQi5uUjJ+VjaGWjJ6Vi52WhpCRgIuNeYSEcHt7dHd7dHeCd3+JfoeM ho6Mho6NhI6NhI6GgoiGgoiLf4iNgouUi5eVjJmQjJWJho6JgIuIf4mIgJGNhpaMhp2LhJuIfo6G e4yCeYaGfYmCfoSIhIuNh5KRi5aJjJ2RlKWdlaiZkaWMhpGHgIyCfol/e4d9dHt9dHt7d3qDfoKH fY6IfpCLgomHfoaEgImEgImDg4Z6en2CeHuGe3+De36De35/fYB9en55cnJ+d3d/en6Ig4eGgouD f4iCd3N5bmp7dHl9dXqAeH+DeoKLf42NgpCXiZKShI2HfoaDeoKGfoOAeX56eHl0cnN+bW6DcnOI eX6HeH2GeHiEd3eEeG+LfnWJgIiOho2RgomJeoKEe4iEe4iNgIeOgoiOfoiSgoyOgoiIe4KGeX+I e4KJfomQhJCVh5CShI2MgHqLf3mId3WCcG+Ad3iHfX6Qg4eRhIiRhIuJfYOCdHJ6bWpzX1hyXld4 amqAc3OIeXuIeXuMeX+HdHp/bm2Ab26Cc3iHeH2Jd3qHdHiDcnOCcHKGcHOMd3mQfYONeoCId3h/ bm9+amh/a2mIenqNf3+SgoyVhI6UhIyRgomQen+OeX6MfX+MfX+Qen+NeH2IeX6Le4CLe36Gd3mH c2uAbWWGa3KQdXuNeoCQfYOOeniIdHKHbWWLcGmNdHiXfoKdf4KZe36IdWWHdGSAbmGAbmGIb3CL cnOMcmqJb2iGbmWGbmWSb2SVcmeSc2GRcl+QcGGRcmKQdWiUeWuUc2eObmKLaFOHZE9/XEl/XEmH Z16ObmWeeXifenmZdWqUcGWRbmWOa2OIbmOHbWKMammMammQa2mSbmuOZ12MZFuOY1OOY1OMgIyL f4uJe4eIeoaHeYSHeYSMfomMfomLeomGdYSCc3iDdHmDd3iJfX6Mg4uOho2Qh5GNhI6MgIyMgIyJ fY6JfY6LfpGGeYyAeoOJg4yHiJGIiZKRiJCNhIyHfoiIf4mEf46GgJCIg5aMh5qOjJ6OjJ6OiZ2N iJuSh5KQhJCMe4aCcnuCd3WHe3qLgoyMg42ShJCOgIyIfoSLgIeJgoeHf4SIfYaMgImNi5mNi5mO hIuIfoSDe4CEfYKJfoyOg5GJh5aJh5aIhJCEgIyHfouJgI2Ef46Mh5aNhpmOh5qMi5+QjqOOi6GM iJ6JfoyJfoyLf4iJfoeCeYCEe4OCfoeCfoeIe46OgpWNgo2RhpGLiZSHhpCGh4uEhomDf4aHg4mD f4aDf4Z/gISAgoZ7eHR/e3iGgJCLhpWQiJmLg5SCf351c3J5dHV7d3h7dHR6c3OAeoaEfomUhpGR g46EgIl9eYJ7eIN6d4J6d31zb3V5bmqAdXKJeoKJeoKEen6HfYCLfn+NgIKLg4iNhouQgIaMfYKC eH6DeX+Cen2EfX+IfoSLgIeHfYOCeH6DcHeGc3mIeIeOfo2OiJGQiZKJhIOMh4aQfYCJd3qJd3qL eHuQg4eViIyUiZCMgoiDeXp6cHJ6ZFh5Y1eCb3ONen6NfoaMfYSLeICCb3h/b2d/b2eEc2+HdXKJ dHmHcneJdHKJdHKMd3uLdXqQfYaOe4SQenqHcnKGam2Lb3KIe4KNgIeUg42Ug42Wg4ySf4iNen6O e3+OfXuQfn2MeX2HdHiLcHmOdH2Md3mGcHOAbmh/bWeDam6NdHiOeYCNeH+NeXOEcGqCZ1+Ha2SQ c3iWeX6Wen+VeX6Hc2mMeG6Nem2IdWiLdXWGcHCMcmqJb2iJaV6IaF2Nb2OQcmWObmWObmWRb2qR b2qSdGqXeW+WeGqWeGqZdGGQa1iJaFaEY1GIZVuJZ1yScG6Zd3SeenKZdW2RcmKJaluCaV6GbWKM Z2mLZWiSbWORa2KLZFWGX1CJXk6LX0+IeYyGd4mCcnuCcnuDb3qDb3qHd4OLeoeJfYOHeoCHfYCH fYCLgISNg4eJho6Jho6Rg4yOgImGeX+EeH6Ed4SEd4SDeIOAdYCAd32Jf4aJiJKNjJaRho6QhI2M f4aJfYOIeoaJe4eIf4yLgo6NhpWIgJCQgpCQgpCOhpKQh5SHg4x/e4R/foOHhouQiJeOh5aUhIyL e4OIeX6QgIaIg4eJhIiMg42Qh5GNiZWNiZWLgIeJf4aIg4eMh4uLh5KIhJCHhJSIhpWEg42CgIuG gJSIg5aMhpGOiJSSiZaQh5SNh5KNh5KQfpKQfpKLgoyMg42LhomOiY2LiZGIh46Df4iDf4iJg4yN h5CLhJCMhpGNiZWMiJSLhIuMhoyLgoyLgoyDf4iIhI2IhoeIhoeCgH2Dgn6Ng5SVi5uWkJuRi5aH h4d4eHh9b3iCdH1/c3d6bnKCdIKHeYeLgJGMgpKLfYt/cn96b3p6b3p7cnV5b3OCc3iGd3uLgIeI foSGe4KLgIeNhI6NhI6Mh4uNiIyNg4eJf4N/dXl7cnV/c3eDd3qEeXiJfn2GeHWAc3B+cnV/c3eD c32LeoSSiI6Vi5GUiJGUiJGOhIuHfYOEd3SEd3SShI2XiZKXiZKQgouMfX+AcnR5a2l9b22AdX6G eoONfYyLeomIdHeCbnCAcGWGdWqMeHWNeXeOen2JdXiHdXSHdXSOeH2OeH2QeoKOeYCOdXmHbnKH a2uLb2+Ie4KOgoiQg4SOgoOSfYSOeYCQfYCOe3+Lf36MgH+LdXqDbnOEanCDaW+EameGa2iAbWN+ amF7aGqDb3KNeXuRfX+SeW6NdGmHbmOGbWKObnSXd32Ve3+Zf4OVgICXg4OXfn2UenmReHuMc3eR cHKNbW6HaGKIaWOMa22Qb3CRb2qRb2qNbmqMbWmOcGeUdWuVdW+VdW+UdG6Wd3CUcm2ScGuMbmGH aVyRcm6UdHCQdHCMcG2Nal6JZ1uGaF6LbWOLaWeGZGKRbmWRbmWOZU6HXkeEXEeHXkmCcnuAcHp9 bXd+bniEb3eHcnmHeoCJfYOIfo6MgpKMg5CIf4yMfomNf4uLf4iNgouOgICHeXmEc3KGdHOAbneD cHl+dHiAd3qCdXmMf4OOiZmNiJeQh46Qh46Le4CHeH2EeHuEeHuDeoKEe4OGeoiHe4mIfYuLf42S h5KUiJSMiJSHg46JiJKMi5WQjJWMiJGOhIaJf4CJf4aNg4mOiJSOiJSQh5SRiJWSjJeRi5aLgJGL gJGLhpmNiJuOiZqMh5eCfomAfYh/eYKAeoOJfZCNgJSLg5SOh5eRh5eQhpaOho2LgomIfYuGeoiA f4mIh5GNh5KRi5aOiZmOiZmLh5KDf4uIg5SLhpaJhpGLh5KQhI2Rho6OhIuQhoyQh5GOhpCNh5CM ho6Qh5GOhpCDgISHhIiOiJSUjZmRjZaOi5SOiJF+eIB5cHp7c32Cc3qCc3qDb32MeIaJfoyJfoyJ fYOAdHp6a3B7bXJ9cHR6bnJ/dH2EeYKIgIaHf4SEfX+JgoSSiZaQh5SJhpGJhpGLhomIg4eIe32H ent+dHV9c3R/dHOIfXuIfXmDeHSDd3iDd3iEb3eLdX2Nf4iVh5CaiZaZiJWQhoyHfYODdXOJe3mS g5WZiZubiJGUgImLd3mHc3V/dXuAd32GeX+GeX+Hen6EeHuHcm+Eb22HcneMd3uNfYmNfYmSf4OR foKJen2EdXiJc3qMdX2OenqMeHiLcnOGbW6EcGqGcmuHeH+MfYSQen+Md3uMdXqMdXqQeYCReoKQ fYOMeX+Qc3WLbnB/ZV53XVZ+ZFqCaF2DaV5/ZVt7Z2eAa2uJdHeQen2RfXWOenONdXCNdXCQc3WS dXiRe4OWgIiZgoyZgoydf4SXen+Qd3iMc3SRcHeNbXOObWuObWuMcHWQdHmUdWmSdGiSb2eQbWSO bmWUc2qUcGiRbmWObWqRb22Rb22WdHKQcmiNb2WQbm2Rb26RdXKMcG2NaF2JZFqGY1eMaV2IaVqI aVqRameVbmqSaVGHXkeGWkSGWkSAc3uCdH17bnl9b3qEb3eJdHuIe3+Mf4OMhoyNh42ShI2OgImL f4uMgIyMe4uMe4uLfX1/cnKEcHCCbm59am5/bXB7b3OAdHh/c3mJfYOLhJCNh5KNhouMhImIeXuD dHd9c3R/dXeEeoCEeoCEeYSHe4eJgIiOho2Ui5WSiZSNiZKMiJGShpmViJuUjZmRi5aNiImRjI2R i5GQiZCUi5eUi5eSi5qSi5qSi5qOh5aLhIuMhoyRjJ2RjJ2OiZmJhJSAeXt9dXh9dXqAeX6He4SJ foeHf46MhJSOhJWNg5SNhJGMg5CAfYZ9eYJ7en+HhouRh5eUiZqSi5qRiZmMhpGGf4uHhJSJh5aJ hJSLhpWVh5CUho6RjJCOiY2NhpWQiJeUjJ2RiZqJgpKIgJF/e4SDf4iIf4yVjJmRjZmOi5aMiI5/ e4J4eHh3d3d/c3SDd3iIdXmQfYCJf4aIfoSGeICAc3t7aW19am56bnJ7b3OCdH+Je4eHf4KEfX+D fYOMhoyWi5mRhpSLgoyJgIuMho6Jg4yLf4iHe4SAeXt7dHeDdX6LfYaNfoCLe36LfoKMf4OHc3WH c3WGeICNf4iWiJGZi5SShoeIe32DeHeLf36RhJaWiZueiJKVf4mGd3mGd3l/eH1/eH2HeHqGd3mE c3KDcnCCbW2Dbm6Ic3qOeYCNgpCOg5GUfoaOeYCLdX2GcHiIbneMcnqHdHiHdHiCbnCEcHN+cG5+ cG6Gd3mJen2Oen2IdHeIcGuJcm2NeoCQfYOOfoiLeoSSdXiJbW9+ZVh1XVB7YleCaF2EaWWDaGSC aGSEameIcneOeH2Sf4ORfoKUeHqRdXiOc3OOc3ONeHWQeniaf4abgIedgIaVeX6Qc3WIa26JbW+J bW+NbmuOb22Oc3OWenqVeHiSdXWWb2uUbWmQbWSSb2eRbmKQbWGQal+Ra2GMbmKOcGSMb16Mb16Q bW+UcHOVdGuScmmQa12NaVuHYVGHYVGGZ1eGZ1eOamiQa2mSaFqMYlSMX0yNYU2Ee4OAeH99dXp+ d3uDd32GeX+LfYaOgImMh5aLhpWShI2QgouNg4mMgoiHgImIgouHfYB7cnV+cnV4a297amt6aWp5 a2l7bmt7bXKEdXqHeYKOgImOh4yNhouCe3d9d3J7eXh+e3qGfoCIgIOJfoeMgImQiZKUjZaSjJeU jZmOi5SLh5CWhpWXh5aViZWXjJeVjJaXjpmSkJ+OjJuRiJWSiZaOiZmRjJuXi52Uh5mNhI6SiZSZ kaKZkaKVjJaJgIt/fXt6eHd+cniEeH6CeHuAd3p9d32DfYOHfY6OhJaRh5mQhpeEfoR7dXt9eX+J hoyOhpCQh5GRiZqOh5eLgJKIfpCIg5KLhpWJhJWQi5uUjp6VkJ+RlKKMjp2OiJSNh5KUjJuQiJeH fYOAd314bnJ+dHiAd4eOhJWMiZeNi5mLh42AfYN4eHh5eXmEfX+GfoCLgIKMgoOIfYaLf4iIeIKC cnt6b257cG95bXCCdXmIeoOOgImNfoOIeX6IeYuXiJqbkp+SiZaLfoKGeX2IgouMho6Hho2Hho2L fYaGeICIeYCMfYSShoyRhIuUh42ShoyLfX1/cnKCdXmMf4OWiJSZi5aUh4uNgISIeoOMfoeOhJWW jJ2dh46Re4OCdXd+cnN9c3eAd3qGeHiGeHiAcnR/cHN/cHWDdHmMeIaOeoiNe46LeYyLeH6Gc3mE b2+CbW2Eb22Hcm+IeX6IeX6CdHR/cnJ9cnB+c3KLdX2LdX2Je3eCdG+AbmeGc2uGd36Le4OSg4uS g4uWgH6NeHWHbV+AZ1qEZWKIaWWHa2eHa2eHbWiHbWiJcHKNdHWMeX2QfYCXfoKVe3+Sd3OMcG2J cm2NdXCXe4CZfYKZf36SeXiOc2+IbWmDZ2uDZ2uEbWeIcGqMb2+Ud3eUeXSRd3KZc2uSbWWLamKM a2ORbV6OalyMaFeJZVWGaFyJa1+LbWGLbWGNa2mQbmuUcGiWc2qXcmiOaV+NY1eIXlOHXVGIXlOL ZVyLZVySa1qRaliUalWUalWHfoaEe4ODeX+DeX+DeICEeYKHfoiJgIuLhpmMh5qNhJGJgI2HgImE foeCfoeEgImCen97dHl/c3l7b3V6bWp7bmt6bW1+cHCAa3CGcHWMeX+Wg4mUiJSNgo2Ae32CfX6A f4SDgoeJhIiJhIiLg4aOh4mQjJeUkJuUjJuSi5qNh5CNh5CRiJWSiZaVjpeZkpuWlKaUkaOSkKKR jqGVi5uVi5uVjpqVjpqWi5mQhJKJf5CUiZqXkJ+VjZ2WiJGNf4h9ent7eXp/dXmDeX1/enl9eHd5 b3N9c3eDeIOOg46QhpqQhpqEgIyAfYiAeoCHgIeIgoiNh42MhpGIgo2He4eJfomJg46MhpGGgo2N iZWUjp6WkaGSkp+MjJmGf4uHgIyNh42IgoiAd3h9c3R6a26AcnR+d3uIgIaNh5KSjJeMh4uAe39+ d3mAeXuHfYOJf4aIhoeLiImJgIiJgIiJeYOGdX99cmt7cGp6bW2Ed3eEeoCIfoSJen+Jen+LeomX h5aXkKGRiZqJgH99dHOCd4KHe4eHfY2Jf5CIeYCHeH+HcnmNeH+Igo2LhJCRiJCQh46Nf3+Ed3eG c3eQfYCXiZWajJeUhpGNf4uJe4eLfYiOho2Ui5KVh4SMfnuAc3B9b22DdHeGd3mNen6LeHuEcniD cHeDcHeHdHqJeYaIeISJeoKDdHuDb29+amp9aGp9aGqGbmmNdXCMfX+NfoCLenOCcmp+a2+Cb3OL dX2Md36NeXmHc3OAbmiCb2mEb3mIc32OeISVfouagISXfoKRd2uJb2SGa2eIbmmDb2WCbmSCamKC amKAaGeHbm2Hc3WMeHqQe3WSfniQd2mGbV+CaWiGbWuOcnmQc3qSeXiQd3WRcm+MbWp/ZGF9Yl6E Y16HZWGGaWmMb2+Ld22MeG6ScmeNbWKJaV2Lal6Na1yMaluJaFaGZFOJZVeOalyQamGOaV+NaF2R a2GRaWOVbWeQal+Qal+OZFSNY1OIXk6GXEyIZVuJZ1yNZVyOZ12OaVWUblqGeoaEeYSLd4KIdH+H eH2Jen+LgoyNhI6Qh5SQh5SQgpCJe4mEeYeGeoiAeIKGfYeEeoCCeH5/dXt6cHd5b3N9c3d+cnV+ cnWDdHmGd3uJeoKQgIiUg5CNfYmHeoCMf4aMhJWNhpaJhpGIhJCLhomQi46SiZaXjpuRiZqQiJmN h5KMhpGQh5SUi5eUkZ+VkqGXkaiRi6KOjJ6LiJqWjJ2Vi5uWjZqUi5eQhJKLf42JgpGUjJuXjZ6W jJ2Rh42Jf4aCfoeCfoeEgoaIhomDgIJ+e31+cHB9b29/d36LgomMhpGLhJCHgImDfYaIe4KNgIeM goiNg4mEfoeAeoOCe4KGf4aMgIyMgIyIf4eQh46UlKGXl6WWkpuOi5SIfoSIfoSMhISGfn59dHB/ d3OAdHiGeX2GfoCIgIOIgouSjJWSiImLgIKDe3uDe3uHfoaOho2JiJCJiJCNhJGLgo6JeYOGdX9/ dHN6b253a2V+c219enuAfn+Mf4OHen6He4eRhpGWjp6RiZmJfX6EeHmAdHqCdXuHd4aJeYiDeX+A d317cG+AdXSHe4SMgImOiJGOiJGShomEeHuLe4CSg4iWi5mViZeMf5GEeImDdX6Je4SOh4mRiYya iISQfnqMc3eIb3OGdHWNe32Rg4OOgICJdHmCbXKEa22NdHWMenmLeXiMeHqEcHOAZV57YVp5Xlt+ Y1+Da2eOd3KUgISSf4ORfXqLd3SGbXCDam6DbnWLdX2OeH2Jc3iDb2mAbWeAa3WEb3mMdYSSe4uV f4SWgIaOen2NeXuLeHKIdW+GdGd/bmF+ZVt/Z1yDYl2EY16GamqNcnKOeXeRe3mSeW6NdGmIbWiH a2eHbXOMcniSd3eRdXWScG6LaWd/YVt/YVt+ZFqAZ1yGZGKObWqLdGWLdGWRb1+Qbl6NaF2SbWKO bmKNbWGRb1uLaVWOZ2GVbWeQbWGSb2ONaF6OaV+LamKQb2eOaV6UbmOValqQZVWHYk5/W0eJXlaM YViJZFuMZ12RZ1uWa1+Gd36DdHuCcnt/b3l+dX1/d36GfYeMg42OhpKMg5COfoiHd4CAc3t+cHl9 b3iAc3uGd36EdX2AdHqAdHqAdYCEeYSGeX+HeoCIeIKIeIKMeYKLeICHeoCGeX+Cd3+IfYaJf5GN g5WLhJCGf4uEfomLhJCMg5CRiJWOh5eMhJWLgomLgomVh5KZi5aVjZ2VjZ2Ui5eQh5SRhpGQhJCO hpCMg42NgpCJfoyMgoiLgIeLiJaQjZuUiZqSiJmOgImIeoOIfYuLf42MhpGQiZWJho6AfYZ9cnCA dXR+e3+EgoaMg42LgoyIf4eEe4OGfYeIf4mMg42Mg42HfoiGfYeHfoaJgIiMgoiJf4aGgJCOiZmV laKamqeUkJuNiZWLe4OJeoKHf4KCen2CeHmEenuHfoaHfoaIgoiLhIuMg42VjJaXjpmVjJaLiIyJ h4uJiJCMi5KWiJaXiZeUiJaQhJKLe4CEdXp6bm96bm99b2qCdG+Ie3+Mf4OGfoOGfoOHfoaOho2S jJWRi5SMgoaGe3+IdXuDcHd+cnh/c3l+b3R+b3R+c2+DeHSHf4KNhoiSh5CSh5CRg4yMfoeUf4ua hpGVjpeSjJWJe4SCdH2AcneHeH2Jf4aRh42Uh4uQg4eNeH+GcHiDdHeMfX+NhouOh4yNe32CcHJ+ bmOHd2uLfnWNgHiLfnWAdGt9Y1x5X1h1XFF4XlSDb22RfXqWf4eUfYSRe4OIc3qHanKAZGt/amqG cHCLeHuJd3qEb22CbWp+aWt/am2HbnKQd3qQeYCSe4ORe4aQeoSQe3uMeHiNdGeHbmGIZVuGY1iI aF+IaF+Ha2uMcHCIdHeMeHqXfoKReHuOc26IbWiDam6Ib3OScnOUc3SWdGSLaVqAXlaAXlZ6YVZ6 YVZ+Y1+EaWWHbWKLcGWSa1yRaluRbWqWcm+Wd3CXeHKUdGSQcGGQaWWRameRbmWQbWSNZV+NZV+H ZFyNamKIZVuMaV6LaFCLaFCEXk2AW0mDWkmEW0qCWEqCWEqMX1eSZV2AbnJ+a297bXJ7bXJ7cHuA dYCHeYSNf4uOg5GJfoyHeYKEd39/dHB6b2t7b3B/c3SGd3mGd3mEeHuGeX2He4eIfYiJgIiLgomO goiHeoCHdHiCb3OCcHKAb3B4bnJ/dXmEe4iIf4yNf4iIeoOGd3uHeH2IfYuLf42IgouHgImLg4iL g4iSiI6Vi5GVjJaUi5WSjJKNh42Lh42JhoyJgoeIgIaJe4SMfoeNhIyRiJCRjJ+Qi56OhJaMgpSG d36Gd36IfoSIfoSMhpGQiZWQh5SNhJGCd3+EeYKCe4SHgImLf4uEeYSCdXmIe3+LgoyQh5GQiZWM hpGDfYOGf4aHg4yJho6NgouIfYaJg46VjpqalJ+fmaWVjpeLhI2Ef4CEf4CHgoaCfYCGfoCHf4KG goiJhoyLhI2Nh5CLg5aRiZ2UkaOSkKKOjJuMiZmNiZWOi5aUhJaVhpeUiJGWi5SXhoSJeHd5bW59 cHJ/c3SGeXqHfYCLgISHf4SIgIaGe3+Jf4OVgouWg4yWg4ySf4iLfn+GeXp+c299cm57cnN9c3R+ eX2Ef4OLhomRjJCUiJGRho6QgouOgImRg4yXiZKVkJSUjpKNf3qGeHN+bWt/bm2HeH+Sg4uRho6R ho6QgIiIeYCEeH6NgIeQhJCSh5KRfXeJdW+CcG+LeXiQhomQhomOg3+EeXV7a2F1ZVt0YlV3ZFeC c3WOf4KXgomVf4eUfoCOeXuNdHWGbW57aGV9aWd+b3SEdXqHcnSHcnSGbmmDa2eDaGSJbmqLcHmQ dX6QdX6SeICSfXqRe3mScmWLal6IZVqJZ1uJb2SHbWKMcm6OdHCIc3WOeXuWeYCUd36QdW6IbmeH Z2iHZ2iNa2mQbmuUcGiMaWGGX1CCXE15WlF1Vk57XFSEZFyGamWLb2qUb2GUb2GUcm+aeHWbe3md fXqXeGiRcmKRbV6RbV6Sb2SQbWKJaFiEY1SDX02HY1CEXVSEXVSEXEeGXUiDW0aCWkV/V0WAWEaA VkaDWEiGXEmMYk9/cnJ+cHB9aG9+aXCCa3iGb3uJc3+OeISNfpCOf5GMfYSGd35/cnJ7bm56cHR+ dHiGeXqHenuIeX6IeX6Lf4iOg4yIhI2IhI2Ng4eGe3+AdGh7b2N9bWV6amN3aWeDdXOEeoCHfYOI foKHfYCHdHiIdXmEeoCIfoSJgIiHfoaIgoiMhoyRiJCUi5KXjJeWi5aQi46NiIyJhoyJhoyJf4aL gIeIf4mMg42Rh5uWjKGVkKGRjJ2LhIuIgoiDd3qAdHiCfX6GgIKLhI2QiZKRiJKRiJKJg4mGf4aI f4mNhI6Lg4iEfYKCeH6LgIeQiZWVjpqUjZaNh5CEgImEgImHhouMi5CMgoiJf4aMhJSWjp6alaWa laWRkZSGhoiIhICMiISMhIeJgoSIg4SJhIaMh4uOiY2OiI6Nh42JgpWQiJuUjaWSjKOQi5uMh5eM iI6MiI6NgpCSh5WVjZ2XkJ+WiIiIenp7cG99cnB/d36If4eIf4eLgomLfoSHeoCDd3iEeHmRe4OV f4eai5KZiZGShoeMf4CCc3V+b3J/c3SCdXd+eoOJho6Nh5CUjZaViJqUh5mQhJCNgo2ShI2ajJWX jpmVjJaWhn6Me3R9amN/bWWLeH6Sf4aRiJKSiZSUh42Qg4mIe3+ShomSh5CSh5CUenuNdHWHc3CN eXeSiI6WjJKRh4iLgIKId3N+bWl6aF5+a2KDd32Mf4aXgomVf4eXfYOUeX+OenqMeHiAbmh7aWN+ aW6HcneJdHSIc3OJbW2GaWmCZ2eDaGiGbW6JcHKJcG+Mc3KReXOSenSUdGSLa1yHZ16IaF+Lc26N dXCOd3KNdXCNcm2UeHOVeXmWenqUeWuNc2WJaGOEY16HaGSHaGSNal+Nal+NY1eHXVF9Vk5+V0+D WFCHXFSHaGWNbmuVc26XdXCXd3qben6de3ObenKdeGSbd2ORbmKQbWGObVuObVuLaFB+XEV+VUCA V0OCVkCEWEODWD6CVz2GVz6HWD+DWkOEW0SCWEqDWkyCWkCGXUSHeH+Gd36EcHCDb2+GcHWIc3iJ d3+Oe4SNf42QgpCSg4uJeoKDdHeGd3mDeX2Ge3+Een6DeX2Jen+Jen+LfYuRg5GJhpGMiJSQhomI foJ/c2d5bWF1aml3a2p/a26Hc3WHeYKIeoOEe4aCeYOEeHuAdHiEd3+IeoOGf4iHgImMg42OhpCS i5CVjZKZjZaZjZaRiJCRiJCNh5KJg46LfoSMf4aLhI2Ri5SVjJmVjJmViZWUiJSNg4SLgIKGe3+A d3p+eYiDfo2Ig5KLhpWRiJKQh5GOh4yNhouRiJWOhpKLh5CJho6IhpSOjJqSkZuVlJ6VkZ2VkZ2J hpGJhpGLiZGHho2If4mJgIuMhJWXkKGelKaakKKSiZSOhpCOho2SiZGViZKUiJGMh4uNiIyNiIyO iY2NiZWMiJSOh5aNhpWRh5uQhpqNg5SOhJWIhIuJhoyLgo6OhpKQkJ6WlqWSkpWEhIeJeHmMenuL g4aUjI6UiZCOhIuNen6Gc3eGc3eGc3eLfYaQgouZjJ6bjqGWi5SQhI2JfYODd32EcHOJdXiNhIyU i5KWjp6XkJ+XjZ6SiJmNgo2MgIyNf4iUho6UjZmWkJuZjI2Mf4CAb25/bm2IeoiQgpCQh5GOhpCW g4mVgoiJf4OSiIyWi5SViZKUgoOJeHmHdXeNe32Uh42bjpWeiJCXgomUen6Mc3eAbW+Ld3mNf4uU hpGWgoKUf3+QfYCQfYCRg36Rg36JdXOAbWqEaGiGaWmDb3KIdHeMcm6Ga2iCZ2d/ZGSDaGOIbWiG bmmIcGuNcHCUd3eUeHCMcGmIaF+NbWSRdHuUd36Rd3ONc2+McGmOc2uUd3eWeXmXeHSVdXKHa2eA ZWGEZV+GZ2GIaF2Ma2GNaVaHY1CDW0aAWESCXk6DX0+JZ1yMaV6RcmuUdG6RdXKVeXWeeHCXcmqW cGWadGmVb2WUbmSSbl+Qa12MaVGAXkeAVUGAVUGEVUCGVkGHWD+IWkCNXUiRYUyUY06VZE+OY0qH XESIWD2JWj6MgoiMgoiMf4OHen6HdH2IdX6JeoKNfoaLfYuMfoyOgImLfYaJfYOIe4KJd32LeH6H en6GeX2Hen6Ie3+JeoKNfoaRfoeVgouOhIuLgIeDdXB+cGt7bXJ/cHWDdHeIeXuIe4KGeX+GdX+E dH6EeHt/c3eDdHuHeH+DfYiHgIyLhI2OiJGWi5SWi5SXjpuVjJmRhpGQhJCLgomLgomNgouOg4yR iJKUi5WSjpWRjZSSiZGSiZGUjZSQiZCMh4iDfn9+eoaCfomHg4yIhI2LgoyMg42Ug42Ug42Sh5WS h5WNhpWOh5aQiZWSjJeSkJ6WlKKUkJuSjpqNhJGOhpKJhpF+eoaAd32EeoCHgpKUjp+akqOWjp+X iZWXiZWRiZmRiZmVh5KWiJSSh5KRhpGMiJGQjJWSi5qSi5qQh5SOhpKNhouLg4iHfYOEeoCAe4uE f46LhJCQiZWOiZmZlKOZlZuOi5GShomRhIiUiJGZjZaUh4uQg4eQe3mEcG6CbmuGcm+Gf4aNh42Z jp+XjZ6ViZeRhpSLg4iGfoOMeX2QfYCVjJmVjJmWjJ2XjZ6SjJeNh5KHf4KEfX+Jen+QgIaOiJGV jpeVjZKNhouGeX+AdHqHfpWOhp2Qh46NhIyQgIOQgIONg4eSiIyZiJWaiZaUgIeIdXuHcneLdXqX g46diJSih5Kdgo2OeH2Jc3iEd3eLfX2Sf4iVgouXgoKVf3+Re4OSfYSRhoSRhoSRgHmHd2+Ha2eC Z2KEa2qIb26McHOGam2EaWKCZ1+Eal2IbmGIbWiIbWiLc2qReXCRd3KOdG+Sb2eXdGuXdYCaeIOV eX6RdXqLb2iIbWWQcG6ZeXeXe3uWenqMcGuGamWEY16GZF+IaFyLal6JaluLa1yGZU+DY02EZFqH Z1yIaVqJaluQbWGSb2OOb2uRcm6ab16UaViSaFeRZ1aSa16Wb2KObmORcGWRblaHZE2GXkN/WD2H WD2JWz+OX0SNXkOOXkmRYUyaZUqeaU6ZaFCRYUmHWDqLXD2Nh5CMho6Ngo2He4eHeYKHeYKEeHuH en6Ie3+Mf4OSgoyMe4aEeYSDeIOJdHeLdXiId3iJeHmEeHuHen6GeXqHenuLe4OSg4uMhpGJg46H f4KDe36HeH+IeYCGeoOHe4SJf4OEen6IeX6HeH2EeHmAdHWCdHSDdXWAd32EeoCEf4OMh4uVjJSX jpaQjJWMiJGOho2JgIiJgIiMg4uQiZKQiZKSiZaVjJmRjZaSjpeRjZaUkJmWkaKUjp+SjJWNh5CD gISIhomHhIiDgISGfoCGfoCMfoeUho6Qi5qSjZ2Li5mIiJaJiJCNjJSQjZuRjp2MiZeLiJaMgImJ foeCfYB6dXl/dHOHe3qHg46NiZWRjZmOi5aSiJqUiZuRi5aQiZWQh5GSiZSQiZCQiZCQiZKRi5SV iZWXjJeUjZaUjZaLiIyJh4uGgH+Ae3qEeYKGeoOMg5CLgo6Ri5aZkp6XlJ+SjpqRiJKSiZSXjJWa jpeUjI6Oh4mOeXeJdHKHdG6Jd3CCfoeNiZKXkqaUjqKQh5GOhpCOg4yJfoeQgouShI2VjZ6Si5uM iZeLiJaQh5SMg5CHfYCEen6Een6IfoKShJCZi5aZi5SXiZKNgouMgImLhpaNiJmSi5CQiI2SfYSQ eoKLg4aOh4mVh5WZi5mWgIOLdXiEcGqJdW+MfYSXiJCZho6Wg4ySe4OMdX2Jen+NfoOUfoaWgIib f4Sbf4SUfoaUfoaRhIiRhIiWgoKLd3eLcGWCaF2CaWqIb3CLcnOJcHKLbWOHaV+Eal+IbmOOb22O b22QdG+Ncm2Od3CSenSSeHCVenOWeYCVeH+UeHqNcnSIZ2KIZ2KHamqRdHSUe3eReXSRcGiJaWF/ X1d+XlaCYlaEZFiIa1uIa1uJaFiJaFiMbmKNb2ONcF+JbVyOaFaMZVSIamGLbWORbliNalWLZU+I Y02OY1ORZVWOaV6SbWKVcF2JZVOHYT+DXTyHXkOJYUWUZEqSY0mVZE2ZaFCaa0yfcFCXaE6RYkiI XUCHXD+SiJmOhJWOhpKNhJGMgIyLf4uIgIOEfX+Ie3+JfYCLeoeLeoeMeX+MeX+MeHiLd3eHeXeE d3SDdXWEd3eIdXmHdHiGf4iIgouJgpKJgpKNf4iOgImDf4aIhIuMhpGJg46OgoaLfoKIe3+Hen6G dHOEc3KDdXOGeHWHdHiMeX2NgIKXi4yWjJKWjJKRh42QhoyGfYSJgIiEfomHgIyJf5CLgJGOhpKM g5CLgo6NhJGWjJ2akKGUkaGQjZ2SjZ2OiZmLh42Hg4mGg4d/fYCCeHuGe3+Qf46Xh5aSiJmSiJmR iJCLgomJhoyQjJKVjJaRiJKLhIuHgIeMf4aIe4J5dHV4c3SCd3WGenmHg4mNiZCSjJWQiZKRh5eR h5eOhpCQh5GSiZGVjJSQiZCQiZCVi5GWjJKXjJqWi5mSkJ6WlKKWkJmRi5SIiY2AgoaGeX2Ie3+C en2AeXuEfomMhpGViZKWi5SSh5CViZKWjpSakpedjZWVho2SfYKRe4CSf4aSf4aQh5SVjJmXkKGR iZqJgIiLgomJfoeJfoeOg5GNgpCViZeRhpSMhoyHgIeLgIeMgoiHf4KGfoCEf4CGgIKOfouWhpKR hpSSh5WNhI6Mg42RiJWVjJmXjZSRh42QeX6Nd3uJe4mQgpCUg5KXh5aRgoSIeXuGbmmEbWiIdXmS f4OShomRhIiWf4eWf4eUhIySg4uVgoaWg4eVf4KVf4KSf4OSf4OQgIaVhouVf4SSfYKNc2uDaWKD aW+IbnSLcnCMc3KLcGWGa2GEaWWLb2uMcG2Lb2uLaWeObWqNc2+VeneWfnmagn2dgoiZfoSSdXWG aWmAYVZ5Wk93XViGa2eNbmqUdHCWc2qOa2OJY1SAW0x/Wk2CXE+LZFONZ1WNal6Nal6ObWqXdXOS eGmJb2GJZ02EYkiEY1SIZ1eOaleOaleNaFGNaFGQZVOSaFWOaFaOaFaSa1qSa1qOZESOZESRZFCU Z1OZblaUaVGVaVCUaE+Xa1OablWWak2SZ0mMYkGMYkGVi52Vi52UiZuUiZuMh5eJhJWIhJCEgIyI foSJf4aMe4iLeoeLfoSNgIeJfn2IfXuJen2Jen2LeXiMenmNeH2JdHmLe4OLe4OJgIuLgoyMg4uR iJCOiJGOiJGVjJaRiJKRgoSNfoCHf4SGfoOLeH6LeH6GeX2JfYCHfYOJf4aQgouVh5CVh5CVh5CQ gouLfYaDeoKDeoKCdYeDd4iIeISIeISHen6JfYCOgoiRhIuUi5eZkJ2Zkp6Zkp6akZmVjJSQh5SN hJGMg4uJgIiAdX6DeICLfpGQg5aRhpSMgI6MgoiLgIeLh42MiI6Oho2Mg4uHfoiGfYeIgIZ/eH2D d3p/c3eIe3+LfoKDgISJh4uOi5SOi5SSiJmRh5eSjJeRi5aSjJeWkJuUjZSSjJKZjZmZjZmWjp6U jJuRjp2WlKKWlZ2SkZmOjZeIh5GHfYCCeHt+dXJ+dXKEen6LgISRhIiViIyUiZCUiZCUjZaWkJmX jJWUiJGWiJGUho6Qh46Qh46SjJeXkZ2ZjZaUiJGNg4eMgoaMf4ONgISNhIyMg4uUho6Rg4yQg4SL fn+Ng4SNg4SLg4iLg4iJgoSGfoCJeoyQgJKOg4yQhI2OhIuQhoyQh5SRiJWUjpKRjJCRfoSLeH6I eISOfouSgo6Xh5SVgoiOe4KJdXWHc3OId3WQfn2Vg4SWhIaSg4iUhImXhI2Zho6Zg4aahIeVgIOU f4KWfYCWfYCUfoaXgombf4SVeX6Oc2uDaGGDamuIb3CLcnCMc3KDcGOAbmGAbWeGcmuLb2qJbmmM a2GIaF2IbmmQdXCZe36bfoCbf4SXe4CSd2+EaWKDYlN6Wkp0V0x7XlOHZFyRbmWQb2SJaV6IZFSA XU1/W0WAXEaNY1eQZVqIZVqIZVqNa2eUcm2ZenCQcmiNakeAXjyCXUmLZVGRbV6Ub2GWbVeWbVeX a1uWalqUaleSaVaSbVaUbleUak6Uak6Wa1aWa1abdWGVb1uVa1GWbVOeblGiclWdck+Wa0mRY0OQ YkGVi5uWjJ2WjKGWjKGQi5qMh5aOh5aMhJSOgoOOgoONgouLf4iLe42MfY6LgIeJf4aMf4OOgoaM fYSNfoaOe4KIdXuIdXuLeH6JgIuNhI6QiZKRi5SRiJWSiZaWi5mUiJaMgImHe4SGgISHgoaNgIeM f4aJf4aLgIeHe4mJfoyOgI6Nf42Of4eNfoaHen6EeHt+dHV9c3SDdHmDdHmIc3qIc3qAeX6Hf4SQ hoyUiZCZkJqdlJ6dlp+alJ2WkJmOiJGQh46NhIyLhIuHgId/d36CeYCLhomMh4uQhomJf4OMhISN hoaMjIyIiIiJf4aLgIeGfYSEe4OHf399dXV7dHeCen2JfoeNgouHgIeIgoiQiZWQiZWQjJeNiZWQ i5qRjJuUjp6SjZ2Zkp6XkZ2elKadkqWQiJeNhpWNiJeQi5qSkZuQjpmQjJeIhJCJg4mDfYOCenqA eXmDeX2HfYCMgoONg4SMhImQiI2UjJuZkaGWkJmXkZqdjJudjJuVjpeSjJWQjJWUkJmajJWWiJGR h4uNg4eJf4OJf4OEgIeJhoyViZKUiJGViImOgoORh42SiI6RhpGUiJSSgoyLeoSHeH2Jen+LfoSN gIeMgImLf4iLgoyMg42Oho2SiZGRgoeMfYKEdXiJen2Mf4aShoyWg4mWg4mUfoaQeoKNeoCOe4KS f4aXhIuXh5GXh5Gah5CXhI2ag4iag4iZhIeZhIebgoOXfn+Wf4SZgoeagISWfYCVd22GaF6CaGGJ b2iNdW2MdGuHbmGCaVyCamKJcmmMcmeHbWKLaVeIZ1WGa2eLcGuUeHSXe3iVeneQdXKSdGeJa16H Y1B+W0h0VkV1V0aAX1CGZFWHZ16DY1uCY1F+X06CX0qLaFOQamOUbmeRb1+ObV2LbliQc12Vem+R d2uWb0yEXjyEXEWNZE2RbmWWc2qZdGGadWKbdGKWb12ZbVyVaViRal2UbV+WcFyXcl2XcluXcluh dWSid2WbdWGadF+idVyjd12hclGeb0+XaEWUZEGRiJKVjJaVjpqVjpqSjpqSjpqWjZqRiJWNhouM hImJe4mIeoiHe4SIfYaMgoaMgoaNhoiNhoiOhIuLgIeJf4CCeHmCc3iDdHmHeYKQgouQh5GOhpCW iJSWiJSajJqVh5WOgoiIe4KIeoOLfYaIf4yMg5CHfoiGfYeEe4iEe4iIfoKJf4OJe3uIenqEeHt+ cnV+bWt+bWt9am6AbnKDbnWHcnmAeoaIgo2Og4yUiJGXjJebkJuZkp6SjJeLiIyLiIyShoyRhIuM hoyMhoyHgoOGgIKJhIiQi46QiIuJgoSRh4uOhIiMiYuMiYuUh42OgoiMh4uLhomLgISGe39+e3+E goaOiJGMho6OgImLfYaLhI2Nh5CQiZWOiJSQhpaUiZqXkJ+Wjp6bkaKdkqOekquXjKWOh4yGfoOG foOIgIaLiZSJiJKMhpGNh5KOiI6QiZCJhoyJhoyNhI6LgoyOgoiMf4aIgoiLhIuSh5KZjZmZkJed lJuikaGhkJ+ajpeWi5SSjJWSjJWbjZabjZaWjJKUiZCJgoeGfoODf4iMiJGXjJeXjJeUiJGUiJGX iZKZi5SajJWXiZKVgoiRfoSIenqGeHiGdX+Hd4CHeYKJe4SMg4uLgomMgImRho6VgouSf4iNeH2L dXqGeIONf4uVgoiWg4mZg4uZg4uRgoeOf4SOf4eSg4uejZqbi5eZjJCUh4uSg4aVhoiai5KdjZWa jIyUhoaXgoSXgoSXgIaWf4SWe26Jb2KHalqLbl2MdGuNdW2OcmGHalqGa2GLcGWJb2GIbl+HZ1uH Z1uJbmqNcm6MdG6NdW+SdGqMbmSLa1yIaVqQZ1SJYU6AX0x7W0d+W02AXU+DXlSAXFF+X1CAYlOH ZFiQbWGadHefeXuZem6UdWmQd2iSeWqSeW6Ve3CadF2MZ1CJY1GOaFaSc22VdW+ad2qbeGudeWGZ dV2ec2KZbl2NaVuOalyObVuScF6XcGGddWWfeGWje2mheWeheWeme2WnfWemeV2jd1ubcE6UaUeR iJKUi5WVjJmUi5eVi5uZjp+XjZ6UiZqOhpCJgIuEd3+CdH2Ee4OHfoaOfoiRgIuSg4uUhIyOh4yL g4iDe3t/eHh+c3KAdXSDdX6Nf4iNhouNhouWg4yWg4yXh5SXh5SNgo2DeIOGeX+LfoSHeYeMfoyE e4aAeIKCeYaCeYaEeoCDeX+EdXiCc3WDdHl/cHV9b217bmt6b25+c3KEb3SIc3iJe4mQgpCSh5CU iJGVjZ6Si5uOho2Mg4uOhIiNg4eWgoSZhIeShoyRhIuMgoiNg4mNh42SjJKUi5KRiJCUjI6RiYyR jZaWkpuajpqXjJeSjpeNiZKVi5GOhIuGgouMiJGQjZuOjJqNf4iLfYaEgoaGg4eJhIiIg4eNhI6R iJKSkZaSkZaVlp2Wl56elqaVjZ2RiYyHf4KGd3mJen2Nh42Nh42LhI2OiJGRi5aWkJuVjpqSjJeO iJSOiJSQhomOhIiMh4uNiIySi5CWjpSZkJ2flqOilqKflJ+fkpmajZSUiZCUiZCajJWbjZaajpeW i5SOhIiJf4OJf4aRh42XjpmakZuVjpeRi5SVjJaWjZeakJaZjpWShomQg4eGd3mGd3mHdH2Gc3uH eYKJe4SGfoCHf4KLgISMgoaRhIiQg4eUfoOMd3uGd3uLe4CRgoSRgoSZhoybiI6Uh4uQg4eQfYOU gIeXh5GdjJadkJGShoeUenuVe32ajJWhkpuikpeai5CahIyWgIiXe4CXe4CZenCUdWuJb2SMcmeM cGmOc2uQcmiMbmSJbmmJbmmJb2GIbl+Ga2GEal+MbWmQcG2SdGeUdWiScF6MaliJaFaHZVSMaFeN aViJaV2GZVqIZFZ/XE6DWE2DWE2DXU6DXU6IYlWSa16ed3eheXmdfnSXeW+SeHOUeXSUeHOXe3eX c1+NaVaQbl6WdGSdeW6deW6ZeWedfWqhe2ihe2ihdV2ec1uVb1uRa1eOaleQa1iUaVuab2GbdWuf eW+leG+meXClfW2heWmidF2jdV6edFqacFaVh5CWiJGSiJmSiJmUiZqZjp+XjpuVjJmUg5COfouG d3uEdXqJeoKLe4ORgI2Ug5CWgo2Wgo2QgIiNfoaHeHqDdHeAc3OAc3OAd32MgoiOhIiOhIiQg4eQ g4eNhIyOho2Oe4SIdX6GdHWGdHV/dXt+dHqAcnmCc3qHeYSEd4KHdH2LeICLdX2HcnmHdHqCb3WC bnCAbW9/dHOAdXSEdXiHeHqIfYiNgo2Qh5SSiZaUiJaOg5GMh4uNiIyRiY6QiI2SiI6Vi5GWjpSR iY6Mg42Mg42MhpGUjZmWkJmZkpuZkpuXkZqakqOdlaaWkaKRjJ2RkZ+SkqGUkZ+Ni5mLiJeNi5qQ i5qOiZmLgIeJf4aCf4CAfn+Ig4eLhomOiY2OiY2Oi5GUkJaUkpqSkZmZlaGVkZ2RjpCEgoOHen6N gISOhpCQh5GQh5GQh5GViZeajp2akZuXjpmSiZSSiZSSjJWQiZKQiZKRi5SVjJSSiZGRkJWZl52e l56dlp2bjpKWiY2Si42RiYybjpWdkJaZkJeWjZWWh4yRgoeJe3uQgoKajZSekZeVjpWRi5GViZKZ jZablJmXkJWUh4uMf4OGd3mCc3WHcHWCa3B+b3R/cHV/dXmGe3+JfYCNgISOgoiRhIuVf4eQeoKN eH2OeX6Rg4ORg4OXiI2XiI2SiIyNg4eQg4eRhIiSgoyXh5GejpaUhIyQen2LdXibhJSjjJunlJ2j kJmahImUfoOSeXiVe3qWenOUeHCRc2mRc2mQcGqQcGqLc22Lc22Ic3CIc3CQcmWLbWGGZ2GHaGKJ b2SOdGmXd2qWdWmVc2ORb1+RaliOaFaRbmOUcGWMbmSLbWOOalqDX0+CXUmAXEiHXUiJX0qLZVuR a2GWdW2aeXCde3ObenKbeG+ZdW2adGqZc2mUbV2QaVqRal2XcGObdWqdd2udd2uie3Clf3CmgHKj eGeec2KbcFiSaFCLYUmNY0yQaVqOaFiRaWOXb2mdd2+dd2+bdGefeGqid2OleWWfdFyXbVWRiJKQ h5GVhpmai56bjZmekJudjpqbjZmShJCJe4eLe4CNfoOJf4aLgIeOho2Mg4uRg4yQgouMgoaLgISE eHmDd3iCdG+Ed3KIe3+ShomUh42Uh42NgouJfoeLhomIg4eMeHiIdHSGc2mEcmh+cG5+cG6DbnWE b3d/c3d+cnWAcnmGd36CdXuEeH6EdH6GdX+EeHuDd3qIeXuJen2EfX2EfX2Hf4SHf4SJgIiNhIyN houOh4yOiI6SjJKUjJ2UjJ2ZjqGZjqGXkJ+UjJuRi5SLhI2Qh5SXjpuUkZ+VkqGUlKKUlKKalaiZ lKeSjaGUjqKZkKqZkKqSi56NhpmJhpGSjpqQjpaQjpaSi5CRiY6GgouGgouMgI6Lf42OiY2Qi46U i5WVjJaXjJeXjJeWjZqZkJ2NjZCHh4mGg4eIhomMhJSQiJeRiJKQh5GUiJaZjZuhkqGdjp2djpqW iJSOiZmRjJuVjZ2Wjp6XjpaVjJSVkZedmZ+el6GVjpeRiY6Si5CUjJGXkJWekp6bkJuXkZ2XkZ2V i5GRh42Jg4CMhoOVjZCXkJKVkpaSkJSVi5GXjZSdlZqakpeXi5GOgoiGeHiHeXmDdXV+cHCEcHOD b3KGdHWGdHWIdXuMeX+RfoeUgImUgIeQfYOOe4KQfYOOgoaMf4OVf4eWgIiZhoyVgoiSg4uQgIiL eoSSgoybjJSVho2SfoCMeHqQfYadiZKilJ2jlZ6hjImVgH6LdXWMd3eSdXWSdXWLd3CNeXOMeHWL d3SQeHKOd3CMeHqJdXiOdGeGa16EaWKEaWKLbWOUdWuWenWVeXSVdW+UdG6Vb2SSbWKVb2SVb2SS cmmQb2eQa1uQa1uJZUqDX0WJYUyNZE+JZFqMZ1yUa2KddGqeem+deW6XdGiSb2OSa16UbV+UbVuQ aVeSa16bdGeXdWOXdWOddWiheWuhd3Sme3muf3eoenKjdWGXalaMYk2JX0qJX02LYU6OY1ONYlGS aFqZbl+fdWKhd2OedF6hd2GhdFuablWLiZSNjJaZiZuejqGekp6dkZ2XjpuUi5eOhpCLgoyLhJCM hpGMhoyNh42OiI6MhoyMhImJgoeQgIORgoSMfn6HeXmGfXmIf3uOgoaShomNhouNhouIg4eIg4eJ hIiGgISJfX6EeHmDd26Dd26Ac3B/cm+CcHKAb3CDbnWAa3OCbnmEcHuDd32HeoCJen2NfoCNfoaQ gIiLgIeOhIuJgoSLg4aLfX2Je3uCeHuEen6Mf4OViIyVjpWWkJaai52bjJ6ZjqGZjqGXkKGSi5uQ h5SMg5CQh5SZkJ2blKOdlaWWkaKZlKWZlqaVkqKRjp2UkZ+akqaWjqKWi5aSh5KUiJGXjJWWkJuX kZ2Ujp6SjZ2NjJSJiJCLhpaQi5uQjJWNiZKQiZWVjpqUjZmUjZmSjJeUjZmRi5GMhoyIhIuLh42O hpCRiJKSiZSRiJKSjJWVjpeakKGelKWbkaKXjZ6RjJuNiJeRjZmUkJuZkJqXjpmalJ+fmaWfkZqZ i5SVh5CVh5CXjZ6bkaKZlKeXkqaXlJ+Wkp6WkJmRi5SRhIuViI6ZkJeXjpaVjpeVjpeUi5WVjJaa kZmZkJeXjJeSh5KLgIKHfX6HeHqIeXuHeHqHeHqGdHWEc3SEeHmIe32SfYKVf4SRgoSNfoCNeoOM eYKQeoKMd36Re36UfoCXgIiZgomRgoSOf4KLeICOe4SZhJCXg46WgoSRfX+Qe36Xg4ahjJejjpqh jIyXg4OSeXqQd3iSd3eRdXWQd3iUenuRfX2RfX2VeXuUeHqWe4KVeoCNd2eDbV2CZ1+DaGGJameQ cG2WeneWenebenKaeXCZdWmZdWmVcmWXdGiZdGWUb2GRa1WUbleRbVyOalqQaVqNZ1eIZFaIZFaS Z1iZbV6Zc2ibdWqWcmGUb16UaFqUaFqLa1eNblqOcGOWeGqZd2eee2ubdGSbdGShdG+nenWsg3mr gnioe2qfc2KSaVSIX0qIXkeMYkqLYUyNY06QYlWRY1aZbliab1qdclqmemKhdFubb1aJiJKOjZeW jZqbkp+dkKabjqWVi5uOhJWNh5COiJGUjJ2UjJ2SiJmRh5eQhJCNgo2Oh4eOh4eWiIaShIKRgoeM fYKLgIeQhoyUho6Uho6Oh4yMhImGgH+JhIOMgoaMgoaLgIeGe4KGenmGenmCdXd/c3R+b3KAcnSC a3B/aW6CbXSGcHiGe32LgIKMgoaMgoaQg4eViIyRiY6Si5COiY2Ig4eGd3mDdHd7c3J/d3WQfYaX hI2Wi5SZjZaZi5SZi5SWkJuZkp6UkaGMiZmOg4yMgImOi5aUkJuZlKWalaaXkqKZlKOZl6KWlZ+U kZ+SkJ6XlaOVkqGXkZqUjZaVjJSXjpaZkp6dlqKalaiWkaWUjp+OiZqRi6KZkqqSkZmJiJCMiJGR jZaVjpqUjZmOiJSQiZWQiZCNh42JhIiMh4uOho2RiJCRiY6RiY6SiZGWjZWdl6idl6ialaaWkaKU kJaLh42Ri5GVjpWXjpmZkJqalJ2dlp+akJaUiZCXi5GXi5GakZ6flqOblqaXkqKUkJuVkZ2XkZ2U jZmSiI6Vi5GakZuelZ+ZkJqWjZeZkp6VjpqakZuelZ+ZjZmWi5aUhpGRg46OgoiMf4aMfYKMfYKJ en+Gd3uEeH6HeoCUfYSWf4eQgIOMfX+LdXqNeH2OeH2MdXqOeH2Se4CSf4OVgoaUgISQfYCOfXuL eXiMf4aUh42ZhISVgICOen2RfX+dhpChiZSdh4mahIeUf4KSfoCWfXuReHeVeHqZe36WgoSVgIOX e3uWenqWfX6WfX6Od2KEbViAY1aCZFeLaWSRb2qVeXuWen2fenmfenmbeG2ad2uXdGuXdGuZdGGV cF2RbliZdV+Vc3CUcm+Wc2iWc2iSb1qJZ1GQZEyVaVCZcmKac2OZc16WcFyRZU2NYkmGZ1WMbVuO dGeWe26bem+ZeG2edWuddGqeeHClfnenhIKnhIKqfm2memmedFeQZ0qIX0GIX0GLYkiOZUyNX1CI W0ySY0maalCeclimeV+hc16ecFyNgo2UiJSViZKViZKXiZKVh5CNhIyLgomQiZCUjZSWiZ2WiZ2W i5aNgo2Qg4mRhIuSiIyVi46UiY2SiIyVhI6SgoyRho6ViZKXh5GXh5GVh5CRg4yJgoeLg4iUgImV gouNg4mMgoiLg4OJgoKGe3+Een6Ie3+Dd3p+b3J+b3KGb3eJc3qNfoOVhouRi5GQiZCUiJGUiJGV jpWSjJKOjpGGhoiDfn17d3V4bm9/dXeQeoSXgoySh5CSh5CQiZKSjJWWjZeVjJaOi5GHg4mMgIyN go2Qi5uSjZ6Wjp+Wjp+XkKGakqOblqealaaVkZ2VkZ2WlKOZlqaVkZ2VkZ2VjJSXjpaZlqWbmaeh mayelqqajqeXjKWbkKudkayWlZqMi5COiI6UjZSVkZqSjpeOiJGOiJGOiI6MhoyOhIuQhoyQh46R iJCLh42NiZCUi5KUi5Kdl6eblqafl6eakqKXjJeSh5KRiJCUi5KVjpqWkJualaWXkqKZkpuRi5SW iZCXi5GbkJuekp6hkaOejqGZjJ6ZjJ6bkaOWjJ6XkZ2Zkp6bkaObkaOZjJ6WiZuXjpmWjZeakZ6e laKalJ2UjZaVhI6VhI6Rh4uQhomSg4iUhImVgIyOeoaNfoOOf4SRhIiRhIiRhoKLf3uNeXuLd3mL dXiMd3mLd3eOenqOe4KRfoSRfX2Sfn6JeHeEc3J/cnqIeoOXgIiWf4eUgISNen6Vf4eahIybhIme h4yUhImUhImXgoSRe36SeXiWfXuXfoKXfoKWenqUeHiUenuUenuUdWmNb2ODZViCZFeLaWSRb2qU eHOXe3eZeXedfXqaeXCaeXCaeW6aeW6adWSZdGOZd2KaeGOZeHmbenudeniZd3SZcmSUbV+Va1GW bVOVa1idc1+ec2Kec2KSaU2LYkaHYk6OaVWSdGqdfnSbeGuZdWmddWWbdGSad2ubeG2ff32lhIKo gnqngHmleWGab1eNYkeLX0WLXkiLXkiJW0SJW0SJWz+ZaU2Wa1uid2WidWShdGOSfYeWgIuRgIuS goyOgIyIeoaIe4KOgoiNhIyVjJSZjZuWi5mWiZCQg4mVgouWg4ySiI6XjZSVjJaVjJaSh5KUiJSV h5KVh5KSh5KRhpGOhpCOhpCVhI6Ug42Vh5CZi5SRiZmSi5qLiZSIh5GIgIaHf4SLfoSAdHp9cHR/ c3eIc3qRe4ORg4yWiJGXjJWViZKXiZKXiZKRjZaRjZaVjpWOiI6JhIZ/ent6cHR+dHiVeoOaf4iU ho6Rg4yMhoyMhoyNhI6JgIuHf4KGfoCJgoSOh4mQiZKUjZaViZeWi5mUjp6ZlKOelqedlaaVkKGV kKGWkKeXkaiWlKKWlKKalJ2dlp+mnaqlm6ijl6ahlaObjqKbjqKhlKqhlKqalZmRjJCNhIyUi5KQ jpaRkJeRjZSOi5GMhoyOiI6UhImUhImQh5GQh5GNiZCLh42QiZCQiZCUkaGXlaWfkqWajZ+ajJqV h5WNhIyRiJCUiJSViZWXkJ+Wjp6UjZSNh42Oh4yUjJGajpeekpujjp+hjJ2aiZmZiJeZjp+XjZ6Z jp+akKGbjqKZjJ+VjJaRiJKSh5KViZWXkZ2blaGZkJqSiZSShoyRhIuVho2ZiZGZjJKZjJKZhJCU f4uShISRg4OViIyViIyUiYuOhIaOeYCJdHuHcneLdXqMd3uSfYKRfoSRfoSWf4SRen+Ld3mGcnR9 aWuHc3WNeoCVgoiVf4KQen2Qen2Re36WhIaaiImXhI2VgouXgn+SfXqReXCVfXSagn2WfnmQdW6O dG2QdHeSd3mQdG+Ncm2JaliIaVeJa2KQcmiXeW+ZenCad26beG+XeWuXeWuhfXKifnObfXCZem6d e2+bem6be3mff32ifXibd3KZcmSXcGOSa1qVblyRb12aeGWfemueeWqXcl2NaFSJZVORbVqXd26e fXSdd2uadGmWc2ead2qad2uWc2ideXujf4KmfnqogH2ne2qdcmGba0+XaEySYk+LW0iHXEGHXEGH XUaSaFCWcGWbdWqic2Web2KNeoCJd32GdX+GdX+EeH6AdHqEcniLeH6IfYaNgouUi5WVjJaXiJCU hIyQgIiUhIyUi5WakZubkJuWi5aSh5KSh5KSh5WRhpSNh5CLhI2LgoyOhpCQh46Qh46SjJWWkJmW jaWWjaWRiZqNhpaIg4eDfoKAd3p+dHiJdXWEcHCHen6NgISVho2XiJCXjJWUiJGUi5KSiZGQiZWU jZmZjZmXjJeXh5aLeomDb2+GcnKQfYOWg4mXiZKXiZKNh5CMho6Gg4d/fYCDg4OHh4eMh4uRjJCW kJmVjpeSjJeRi5aVkJ+dl6eblKWdlaadlaadlaaal6eZlqaZmaeZmaefl6eblKOimqqlnaymnaqh l6WXkqOZlKWalaiZlKeVkpaQjZGOh4mNhoiRi5aWkJuSjpqNiZWMg5COhpKQg4mOgoiOhpCQh5GR i5GNh42Nh5COiJGQiJeQiJeVjZ2Si5qUi5WSiZSQg4mViI6Uh42ShoyVjJaXjpmUiZCLgIeRhIaV iImWjJKdkpmllqKekJuSiZSSiZSUkJuRjZmZi5aekJuikZ6hkJ2UjIyQiIiOho2RiJCWjZeelZ+i lJ2djpeRhIaShoeXiZKXiZKZi5SZi5SXiJCUhIyXhI2Zho6XiZKbjZaZi5SShI2Jd3+LeICIdXuL eH6QeoKWgIiWg4ySf4iXgIiReoKQeHOJcm1+amOHc2uIeXuNfoCWf4SVfoOQenqRe3uRgoSVhoiX iIuVhoibf3+UeHiSenSWfniWg3uSf3iSeW6JcGWIbW2Jbm6OdG+Nc26NbWGMa1+QcGqVdW+WenOU eHCbeG2ZdWqUeWuXfW+ff3mjg32ffXiee3ehe3ehe3ehfnuif32he22bd2iXbluacF2Wb2KVbmGU c2iaeW6dfnCdfnCdeWOVclyObmKScmWZeG2efXKZd2SUcl+RbmOXdGmec2ehdWmfdXOieHWienem fnqleWifdGOjblOfak+ZZ1GSYUyQYUeNXkWJX0qNY06Xc2SXc2Sdc1+WbVqJd3+Abnd9a217amt7 bm57bm5/a26GcnSIeXuRgoSOi5aOi5aNhouMhImRhIiViIyUjZaWkJmdkJaViI6RhIuRhIuOgImQ gouJf4aIfoSEfoSJg4mHg4mMiI6Mi5WQjpmWjp+blKWXjpaMg4uJen2Gd3mCdHKDdXOAc3CCdHKG e3+MgoaRhIiWiY2bjJSWh46Rho6Og4yQiZKSjJWdkqWbkaOVi5+IfpKEd3SLfXqMhImVjZKfjp6e jZ2OhpKHfouHfYCEen6JiYyOjpGbkJ6flKKZlKWVkKGSh5KSh5KVjZ2akqKZlqWZlqWblKOdlaWb maeal6adma+ZlauWlqeWlqedl6ifmqufl6ifl6iXlaOSkJ6Wjp6Wjp6SjpWMiI6MhIeLg4aOi5SN iZKRjp2OjJqXjJeUiJSOgoiQg4mJg4mMhoyMhoyMhoyIf4eLgomNgouNgouOg46RhpGRiY6RiY6U hImSg4iUhImUhImQiI2UjJGWh4yOf4SOgoOViImWjJCdkpaimaGakZmRho6Og4ySiZSRiJKWi5aZ jZmmlaKfjpuVi4yRh4iJgIuOhpCRhpSbkJ6ekpudkZqViIyWiY2XjJeWi5aXiZKWiJGah42XhIuV gI6ahpSWi5aWi5afjJWXhI2QfYOMeX+HdHqHdHqOeH+Se4OWgIiWgIiSfYKMd3uLd22IdGqDb2WI dGqIdHSNeXmOeX6Qen+Oen2Qe36UfoaahIyhh4aehIObf3qUeHOVeneVeneNf3qNf3qUeXKEamOD aGGEaWKEaWKGamOIbmONc2iSeHSWe3iZfXmWeneWd3COb2mUcm2denWbgHmeg3uef3ObfXCfeHSi enejfnmlf3qlemSbclyRZ1GValWXbWGbcGSaeW6ffnOhf3SigHWffWqaeGWXeWuVd2mWe3Caf3SX d1uLak+JZVeQa12ZcmKZcmKUblqadF+ddWiheWuld1+idF2icleeblSda1aZaFOUZE2NXkeIXEaO YkyRb1+XdWWedFqXblSLdHuGb3eCamV/aGN7b2V5bWN5bmp/dHCHeHqQgIORi5aRi5aNh42Nh42Z hoyah42XjJeXjJebjpKViIyRgoeQgIaNgIKMf4CHfn2DenmHfYOIfoSMhIeRiYyQiZKVjpebjqGe kaOZjpWRh42OfXuJeHeLeXiJeHd+dXJ/d3ODeneJgH2RgoSVhoiai5CXiI2SiIyUiY2SjpeWkpub lqealaaRjJuJhJSJen+Of4SRi5aalJ+ekaObjqGVho2Le4ODfn2Mh4aSjpqZlaGel6+fmbCelqqa kqaUiJGRho6SjJeZkp6ZkaWZkaWdlKGdlKGal6abmaeem66XlaeWlqWZmaebmaifnayem66ZlqiW kpuQjJWXiZKXiZKVjpWQiZCMhImMhImRh42UiZCSjpqSjpqXkZ2UjZmQi46Mh4uOgoiJfYOGeoOH e4SJfYCNgISRgoeNfoOOf4eUhIyRg4yShI2XhIuWg4mZhoyVgoiSiI6Rh42Vho2RgomShomXi46W jpSakpelmaWekp6Sh5CNgouOg4yRho6Sh5WWi5mdkZqflJ2dkJSXi46ShoyRhIuOg5GUiJaZi5ae kJuUiJGUiJGZjZmWi5aVi46UiY2ai5CWh4yRgomVho2Vh5KVh5KbjJSZiZGVg4SLeXqIdXmHdHiN eHiOeXmSfoCVgIOUenmOdXSGcmqGcmqJcG+LcnCMd3mQen2Wen+UeH2SeX2Uen6SfYSZg4ufhICd gn6aenSSc22OdG+UeXSQf3iSgnqWe3SGa2R/YVuAYlyCY12EZV+JameRcm6WeXuZe36ZeXWXeHSW d3OQcG2Xc26deHOihoKfg3+igHibenKaeHObeXSifXilf3qfeGWVblySaVSOZVCQal+WcGWZd3Kh fnmjgHujgHuffWqaeGWWeWWXemede3OigHihemOSbVaRYVCQX0+OaFuOaFuQZ0+WbVWXc1+eeWWh dWSid2Wjd1ueclaablOXa1CUZEqVZUyRYUyOXkmSZ16bb2edc1iacFaLdHmEbnOCbWqHcm+Cd3V+ c3J+b3SEdXqJe3uRg4OOhpCOhpCJhpGNiZWXiZKXiZKZjZmZjZmXjZGUiY2QfnqUgn6Lg4OJgoKJ gH+Ee3qHfYCIfoKOhIiRh4uUi5eVjJmZkaGZkaGXkZqSjJWJgoKJgoKMhImIgIaHenuEeHmEeXOR hn+Vh4KXiYSbiJGdiZKViZWXjJeXkqKblqaZmaeWlqWUkpeNjJGOh4ySi5CWkKeblayelqaWjp6S hISMfn6Lh42VkZefmqqemaidma+fm7KhlqibkaORiJCQh46NiZWQjJeWjJ2XjZ6bkp+dlKGal6af nauhm6yblqeXkqKWkaGbmaiem6uem6qbmaeZkZaSi5CWiY2ViIyWkJmSjJWOho2Oho2RhpGUiJSW kJaZkpmXkqaXkqaWkp6Oi5aah5CSf4iGeX2Ie3+Le4COf4SOgoaNgISQg4eQg4eWhpKWhpKWh4yZ iY6Xi46Uh4uSi5CQiI2Zho6Zho6UiJSXjJeWjp6ZkaGhl5+elZ2XkJKSi42OhIiNg4eNhI6RiJKU jJudlaWdkpmXjZSXiI2Sg4iNf4uUhpGWh5mai52SiZSVjJaZkZaWjpSZjpCVi4yXjZGSiIyNgoCL f36RgomUhIyai5KbjJSViImOgoONe32Id3iIc3iMd3uWf4SWf4SVfXSSenKHc2uIdG2JdHSMd3eN eHqRe36WeH+SdHuMc3KMc3KNe3qVg4KbgoabgoaWenWOc26RdXWVeXmRfXeWgnubgHWRd2uJaFiA X1B6XEp/YU+DaGOOc26VeHibfn6Vem+Vem+adXCUb2qScmmXd26bf3ufg3+hf3SXd2uXeW2Vd2qa eW6de3Cfe2+beGuVdWGRcl2QcFyQcFybd3She3mjf3Sjf3SeeG6Zc2mZdWqdeW6he22mgHKhe2iW cl6UYliOXVSIXk6HXU2QY02SZU+Sa1qfeGWle3Kof3WnfnSieW+eclSVaUyRY0SUZUaSZVGRZFCR ZFCWaVWXbVqXbVqEd3SGeHWMfYKOf4SLhIuJg4mQgouLfYaOe4KSf4aRg46UhpGQhJCQhJCQhoyR h42ViZeUiJaSjJWRi5SShomUh4uQi4yOiYuOiYuNiImNg4eLgISRh42UiZCUi5eakZ6XlJ+alqKf lKKajp2VjpWWkJaVjJSNhIyMgoaJf4OMf4OWiY2Vi5GZjpWbjZaajJWZkJqZkJqZkJqdlJ6alaaa laaZlaGVkZ2XkJ+XkJ+WkaGblqadlp2SjJKLgn6Lgn6Vi52flaelnbCimq6fmbCfmbCdlqKXkZ2O ho2Qh46NiJmNiJmZiJeaiZmZjZmbkJuVlJ6enaeimqqdlaWWkp6Wkp6alqKdmaWdm6aenaebkp2W jZeWjZWRiJCZlJeVkJSQjJWOi5SUjZmVjpqXlJ+bl6Oblqeblqebl6GXlJ2djZCVhoiJen2HeHqJ fYOMf4aOhIiNg4eShoeShoeZho6Wg4yViIybjpKWjZWUi5KUiJGUiJGXiJCZiZGZkJqXjpmXkZ2S jJeWkJmblZ6ZjZaWi5SQhoyOhIuLgoyMg42ViZeXjJqdjJmfjpuZi5aVh5KOeYCQeoKQgouShI2Q h46RiJCWjJKdkpmfkZqdjpedjZWZiZGUhIeIeXuQen+Re4CWg4mZhoySgoyQf4mOe3+MeX2MeHiN eXmRe3mWgH6Xf3eVfXSSeHOSeHORe3mUfnuUfoCUfoCZe4COcneAbmF/bV+Lc26Ue3eXgoKXgoKU eXKSeHCVe32SeXqVeHiZe3uhf3ebenKWb1+LZFWAXEaCXUd/ZViMcmSSd3mZfX+Uem+Qd2uVcmeS b2SUcHOZdXiXfXWbgHmjf3SdeW6bd3SXc3CZem6dfnKlf3qmgHujg3+efnqVdGuVdGuac3Kienmi eW+jenCecmWWal6UbV+ZcmSab2OhdWmfeW6adGmValeLYU6DWD6CVz2HWEGQYUmRZ1SfdGGnf3uq gn6nfnSnfnSjeFaWa0qRYkiUZEqVZE+WZVCZaVGXaFCVZFGVZFGJfYCQg4eUh42Uh42Oi5GNiZCU iZCNg4mQfYCQfYCNf4iOgImQf4mOfoiOeYOQeoSOg5GRhpSQjJeMiJSUiZCZjpWXjpaVjJSVkI6Q i4mOiYuOiYuUiJGUiJGXiZWdjpqekaOfkqWjlKajlKadlaWblKOZkaGUjJuQiZKNh5CSjJeXkZ2a kZuflqGbkpqakZmXkJ+XkJ+akZudlJ6dlqKhmqahlaOekqGajJqajJqZkp6blaGZlJeOiY2Mg4KM g4KXiJufkKOll6uhlKeelaKhl6WalJ2XkZqWi5aRhpGRi5SOiJGWi5aXjJeVjJadlJ6ZlaGfm6ee maqXkqOUjJuUjJuXkqKdl6eemayhm6+hmayblKeWkaGVkJ+dmaKZlZ6SkZuSkZuWkp6VkZ2dlKGi maafmqqdl6ebmqWVlJ6akZuVjJaOhIiHfYCHeoCJfYOQg4mRhIuUhImWh4yXiI2VhouWiZCdkJaX jZGVi46XiJCWh46Xh5Sbi5eWjJ2XjZ6Wi5SSh5CUjp6WkaGZjZaWi5SVhouRgoeNfoOOf4SRgIuZ iJKeiZeeiZeSh5CNgouJd32LeH6LeH6NeoCNfoOOf4SVho2bjJSbjZmdjpqfiJCbhIyagIKReHmN eXeQe3mWgIiWgIiReoSUfYeUfoOMd3uNeHiRe3uSfneVgHmXg32ZhH6Zg4CZg4Cah4uXhIiXg4aW goSVeHiOcnKCbmR7aF6HbWmQdXKVe32Ve32We3eXfXiXfoKUen6ZfXmafnqfgHeae3KZdGGRbVqN Z1WNZ1WGaF6Nb2WRdHSXenqSeW6Mc2iOb12La1qRc2eZem6af3Seg3ilgHihfXSddXKddXKZenCh gnijfnuog4CnhIOmg4Kienmed3Wfc26jd3KleW2ne2+fcl+VaFaNZE+RaFOWaFuaa16ddWiZcmSZ b1yUaleMZEGGXjyEVzmLXT6QZFSfc2KjfXKngHWqfWuoe2qieFibclOaaVGWZU6aaU2aaU2ZZ1GZ Z1GXYUmUXUaUho6WiJGUiJSXjJeZkJqVjJaQjJKMiI6NgISIe3+HeoCIe4KIeYCHeH+IdXmLeHuR gomVho2ViZeQhJKZi5afkZ2bkJ6XjJqXkJWVjZKSiI6Vi5GVjJaUi5Wdjp2fkZ+dkqWil6qil6ij maqfl6iblKWalaaVkKGWjp6Wjp6ZlKOalaWfmaWhmqaXlqGVlJ6XkZ2UjZmZjJ6dkKKimqqhmaij l6OdkZ2diJSdiJSUi5eWjZqajJWShI2Ng4eQhomSh5KZjZmilqWbkJ6alJ2ZkpuWkJuXkZ2XjJqS h5WRi5SQiZKUi5WUi5WSiZSakZualaWfmqqdkqWZjqGWh5mXiJqXkZ2dlqKlmq+lmq+hmaydlaiX l6iZmaqjm6ufl6eblaGZkp6VkJ+Ujp6blKWfl6ibmaiWlKOUkaGUkaGXlaOVkqGViZKMgImGeX+H eoCUfoiZg42Vh5CWiJGWiY2WiY2OjJCSkJSWjJKWjJKai5KWh46XiZKdjpeZkJ2Ui5eRiY6QiI2W jJ2Zjp+bjZaWiJGWiIiRg4ONf32Nf32MfYSUhIyZiJKXh5GShoeMf4CJdXiHc3WJdHmMd3uHeHqE dXiOeYOVf4mUiY2ZjpKih42bgIeUfn6QenqNe32MenuOeH2QeX6SeX2Qd3qMeHiMeHiQe3WUf3mW hICaiISbhoueiI2ajZGajZGejZeaiZSdh46ahIyZfneOdG2CaGF9Y1yEaWSNcm2Sd3eVeXmff32h gH6WgIOVf4KVenWUeXSae3KZenCVdWWSc2OUc2qRcGiOb2mUdG6WeG6WeG6ScmWObmKNa1qMaliQ bWKZdWqifXungoCmgoSifoCdeHOdeHOZeXeefnuifoCqhoiui4mnhIOqe3Old26idWmjd2qieW+j enCedF6WbVeQZEmNYkeUZ1OZa1eecmOecmOec1+bcF2XbUyQZUWLYkSJYUOMZ1yadGmmfm6mfm6o d2OndWKec1Gab06da0+XZ0qdaE2ibVGea1adalWZY0aXYkWajJeWiJSXiZebjZuWjqKWjqKSkZmM i5KNgIeJfYOGcnKDb2+AbnKCb3OCdHSEd3eJen+MfYKMgIyJfomRiJKZkJqdjp2hkqGXjpuWjZqW iZCViI6QiZKRi5SUjJ2Wjp+akqaelqqbmaial6eelqedlaaZlqiXlaealaaalaahmqafmaWblKWV jZ6LiZGJiJCRho6UiJGWjZqakZ6al6eVkqKVkZ2Oi5aLg4aRiYyRi5aVjpqajp2UiJaIhomIhomO h4mUjI6XkZeSjJKSkJ6VkqGekJ6djp2XkZ2UjZmQiZCOiI6OhpCRiJKQiZWUjZmZjqGflaeblKOb lKOXiZKWiJGVjJmZkJ2fl6ihmaqemayalailmqynna+emayfmq6jnaialJ+Ujp6RjJuVkKGUjp+S kJ+Rjp6SjJeWkJuZlqaXlaWVjJaIf4mMd36LdX2Of4eVho2Xh5Sbi5edhpKfiJWNi46OjJCbi5Wb i5WViI6RhIuViZKXjJWWjZWUi5KOhIuOhIuSh5CSh5CViI6RhIuUh4iShoeUh4uShomMe4aMe4aN f4iNf4iVgICVgICSfnuJdXOJbnCJbnCJdHSIc3OJdXWXg4ORiYmUjIyaiImSgIKQe3uQe3uSf4OU gISRgoSQgIOQe3WMeHKHcm+NeHWNeHWUfnuWh4yejpSdjZWdjZWejZehkJqbkJuZjZmhi5Cbhoua f3iQdW6Ja1+HaV2IbWmNcm6aeHObeXShf4CjgoObgoaXfoKSd2+NcmqRdXCVeXSQdWiQdWiWd3OZ eXWafnmZfXiVe22Qd2iVc2ORb1+NaVaIZFGJZFqOaV6XeYChgomnhI2lgouaeXCScmmScG6XdXOh gH6oiIavi4KwjIOofmWfdV2fdV+jeWOlgHSlgHSde2SVdF2UakyLYkSUZ1WbblyfdWKfdWKadFuX clidc1iXblSRbVGQa1CLa1qUdGKid2WleWiodV+ndF6iclCaakmZaEybak6balOda1SdZ1OeaFSa Z0iRXkCdiJaZhJKSh5KWi5aZjqOXjaKUjZmSjJeQhoyHfYOIc3CCbWqCbmiDb2l+cnODd3iId3iJ eHmIeX6Le4COhpCSiZSXjZ6bkaKXjpuVjJmViIyOgoaLgIeQhoyNiJeRjJuZkaGblKOXlaOWlKKa lJ+dlqKelqaelqaflaehlqijmqehl6WejZeWhpCNhIOLgoCLfoKRhIiNiZKSjpeZkJeZkJeUi5KQ h46MgoiRh42Qh5GXjpmakZuWjZeUjpKNiIyMhISQiIiVjJaUi5WUjp+Ujp+WkJuXkZ2VkJ+RjJuN i46MiY2Ng4mQhoyHg4mIhIuSiZabkp+WlKKWlKKZi5SZi5SbjZubjZudkqWelKablKWakqOilaej lqialaaemaqhmqablaGZkJeXjpaXjpaVjJSRjZmMiJSSiZSUi5WZlKOXkqKZkJeOho2JfX6Ie32Q gIaOf4SWgo2bh5KVh5KVh5KRho6XjJWdkJaajZSVg4SQfn+Vh5CZi5SWjpSQiI2OgoOOgoORhIiS homWg4eUgISNg4eUiY2WjZeRiJKQgouLfYaMfYSOf4eWhIaWhIaWgn+RfXqJdW+IdG6Hcm+Ic3CI d3OUgn6XiIuai42Zh4aVg4KWgH6Xgn+Zi4uajIyZjI2RhIaQfXeHdG6Jcm2MdG+LdXiXgoSbi5Wf jpmfkJWfkJWbjZaekJmbjpWWiZCXg4aVgIOVfXSOd26Qb2SLal+NcHONcHORd3OXfXmZe4Cfgoeh g4uafYSUdWuOcGeVdXKWd3OWd3CZeXOafX2hg4Ojh4KhhH+bgnSWfW+beWmZd2eWdVyMa1OMY1CL Yk+OeH2dhoumiY6fg4idfWqSc2GQblyScF6Xe3Sihn6nh4Cjg32ie2KbdVydd2KhemWjgnmhf3eh f2WaeV+acleQaE6VaVidcF+edF6dc12acFSbclWbbleecFqdc12bclyZc1qZc1qjeWWlemeoemWn eWShcFaaalCXaEyWZ0qZaFWZaFWbZE+XYUyXZEOUYT+Wgo2Xg46WhpWdjJuakKGXjZ6akKGWjJ2U h4iOgoOHdXd/bm+CbWqGcG6Gc3eIdXmHdXKGdHCCdHSGeHiNfoaVho2XjJqajp2bjJ6XiJqVg4SS gIKLfX2Rg4OQhoySiI6SjJeXkZ2XkZqZkpudlJuelZ2hlKafkqWhmaihmaiimaaelaKfjJKVgoiR e36OeXuOgH6Rg4CQiZCRi5GXi4yZjI2QhomQhomQhI2Rho6Vh5KdjpqdkZ2ZjZmajpqSh5KQhoeS iImSjZGRjJCXi52ZjJ6VjJaWjZeSjZ2RjJuNjJSJiJCNgIKMf4CIgn+LhIKLhJCSjJeSjZ2Ujp6W i5aViZWWi5aWi5abjqGbjqGakZuakZubkJubkJudkqOhlqeblqaalaWXlJ2WkpuWjJKRh42Oho2N hIyOg4ySh5CfkZqekJmfkJKUhIeMgoaMgoaOgoOLfn+UfoiXgoyQhI2Rho6OhpCWjZeakJSWjJCU goCQfn2RhIaViImWjYyVjIuMg3+JgH2JfX6Mf4CQfn+Qfn+QhI2ZjZaZjZmUiJSRho6MgImMe4aO foiQgIiWh46Zh4iXhoeOen2MeHqJdW+Hc22IeXuSg4aZhoyah42XhoSVg4KVf4SZg4iZiY6hkZaa jpeViZKWg3uLeHCId2KGdF+Hc3OZhISfjpmhkJqdjZCejpGdjZWdjZWfiYmahISWe3CQdWqUeXKN c2uJbmeIbWWLbm6Qc3OQdW6SeHCVeX6dgIaaf4aaf4aVd2qQcmWRc2mVd22Xe3iXe3iafnqhhICl iYSjiIOjhHefgHOff22aemiXemmOcmGMa1CLak+Oc3Wbf4KliIihhISjgG6Vc2GMaVSLaFOQcG2e fnqhhH+dgHueemSXdF6bc2mle3Kjg4Cign+lf2ufemeedFyXblabcF+ec2Kid16hdV2ablCeclSf c1efc1eec12ec12id2Oid2OleWGmemKqfmWleWGnd1yeblSXaUiWaEebaVSea1afaUyaZEeZYkGU XT2UfoiRe4aWfoudhJGUiJGUiJGZkJ2Ui5eRhIuQg4mJen+DdHmEcniJd32Jd3qEcnWJdXiMeHqJ d3qJd3qOe3+Wg4eWi5SZjZadjJmaiZaVhouQgIaQgIaUhImQgIaSg4iQh46SiZGajpeekpuilqKj l6Olnayfl6efmquemaqblqablqabjpKRhIiMe3SLenOMgHqRhn+Rh4uUiY2ViIyXi46WjJKSiI6W iZCXi5GakZuelZ+flqObkp+WjZqRiJWZjpWZjpWUi5KRiJCVh5KVh5KNhJGRiJWVi5uVi5uQiZWO iJSSg4aOf4KJen2NfoCGfYSIf4eUhpSWiJaVi5uQhpaSh5WUiJaUiJaViZeajJWdjpeajpqdkZ2Z kaKZkaKZkaGblKOZlqWXlaOXkZqOiJGIf4mMg42QhoySiI6XjJeZjZmXjJWSh5CQh46Qh46QgIaQ gIaRhIuShoyOhIuQhoyOg46XjJeakJGXjY6Vg4KSgH+NgISQg4eWiYuWiYuOg3+MgH2Jen2IeXuM fn6Mfn6Ngo2Wi5abkJuXjJeWiZCOgoiMfYKQgIaRgIubi5WekZeajZSUf4KNeXuJdW+Qe3WQgIaZ iY6XiIuVhoiRgoeRgoeVgoubiJGbi5WhkJqhkJqdjJabh4SSfnuQe3uGcnKLdXOXgn+ijJaljpmd iZCZhoyahImahImhhoCfhH+deW2ad2qSenSQeHKMeXKEcmqLb2qSd3KJcGWHbmONcHCSdXWVeXub f4KWd3CSc22QdG2VeXKaeniaenibf3uhhICfi4Sfi4SihoCfg36hgH2hgH2bfXCVd2qUdV6Rc1yO b2mZeXOhhICihoKlf26VcF+OaVONaFGQbWKdeW6jfnmifXied2Sac2GadGqfeW+mgHumgHujf3Sj f3ShdWSfdGOfeGiheWmmemejeGSbb1Sbb1Seclaeclafcluhc1yfcl+hc2GjdWGqe2enf22iemim dVuhcFafbVehbliid2Oid2OjcleaaU+ZYkSSXD6Of4eMfYSUfYSZgomQhomSiIyWi5aUiJSUiJSO g46MfX+EdXiDd3qMf4OHfYCDeX2NeoCQfYOJen+QgIaOgoaViIyVjZKWjpSdjZ+djZ+bi5ebi5eR hIuRhIuUgImRfoeNgouSh5CekqGilqWhmaqimqufmq6dl6uakqKblKObl6OXlJ+UkZWMiY2MhImN houOgoOQg4SSiImWjI2ajZSajZSZjZaZjZaXjJebkJublqaemaidmaWXlJ+ZkaGZkaGXlaOal6aa kZmRiJCNhouNhouQg4mRhIuQhI2Sh5CWhpCUg42OgoiOgoiIeXuHeHqGeXqHenuNgouOg4yRho6N gouOhpKRiJWUjZmVjpqZjZmajpqhkp6ilJ+XjpuWjZqXi52ajZ+akqKdlaWblaGUjZmNhI6NhI6U h42WiZCWi5aZjZmVjJSRiJCQh46Oho2Uh4uRhIiQiI2QiI2Ng4mNg4mRg4yXiZKfkpSXi4yZhISZ hISOgoaMf4OOhIuRh42Qg4SQg4SNeXmMeHiQe3mQe3mNfoaWh46bkp+WjZqRhIuNgIeQfYCUgISa hIyeiJCilZmdkJSXhoSNe3qNcm6WeneVgoibiI6ZiY6UhImRg4ONf3+RgoSZiYydjJmfjpujlJua i5KZh4iSgIKQeX6OeH2Re4OZg4udjZWdjZWdho2Se4OVf3+WgICWgnuZhH6deniZd3SUenmVe3qR emuQeWqRd2uSeG2Jc2eHcGSGamOJbmeUdG6ZeXOWdWqUc2iQdWqVem+eeXSdeHOae3KhgnidiYOd iYOfh36ehn2ignuignuef3Kef3KZeXOZeXOaeniefnufg36dgHunf22bdGKUaViOZFSSamSbc22d eHWdeHWadWKXc1+XcmibdWumfXSle3OieXCjenKhdGWjd2ileW2ofXCsgGileWGfblufbluhcFih cFieblaeblaZa1qhc2Gjd2WnemmqfWumeWijdWGhc16jeGmqfm+og3KrhnSreWWhb1yhaUqXYUON foONfoONen6QfYCMf4COgoOVgIyXg46Vh5CQgouQfYCNen6MfoeRg4yRhIuQg4mOhIuQhoyNgIKR hIaQi46SjZGZjZmajpqXjZ6WjJ2bjJ6djZ+QhoyMgoiSg4iQgIaQhI2ViZKdkKOhlKehlquhlquh lKqdkKaajJebjZmbkpqZkJeVkJSQi46RiJWQh5SOiY2Qi46UjI6XkJKel6Oel6Obkp+akZ6XkZqZ kpuhmaiimqqblqaXkqKdlKGlm6ienqyenqyhkp6Rg46NgIeRhIuSf4OWg4eVhouVhouWh46UhIyL f4iJfoeGenmJfn2MeX2Nen6LgISJf4ONfoORgoeNgouRho6UjZaVjpeZjZmbkJuelZ+hl6Kilp+a jpefjpubi5eZjZmhlaGelKWakKGSiI6UiZCXi5GajZSZi5SZi5SbjJSZiZGai5KWh46WjJCVi46S iZGQh46ShomShomUho6XiZKfkpaajZGdiY2ei46Wg4eQfYCNgIeRhIuOgoaOgoaQgn+Qgn+Vf3+X goKUfoOZg4iekJuajJeUgIeNeoCSe4CXgIaZhomhjZGil56il56fiYmXgoKWd3CWd3CXhIuei5Ga i5CSg4iSfX2SfX2RfX+diIubiZ2di56ijpWah42af4aXfYORe4aOeYOOgoaViIyZiY6ZiY6dg4SS eXqVeXWVeXWMd3eSfX2ZfXmZfXmVgICahoaZgnWVfnKXeHSWd3OSdGiOcGSHaVyEZ1qObmOUc2iV d2qWeGuVeXKVeXKbem+bem+SeW6XfnOdgn6fhIClhH6lhH6lg3qjgnmig3mig3mjg4Cjg4ChhISe goKegn2afnmlfnSfeW+WcGWOaV6QYleVZ1yVb2iXcmqWcFyWcFyUb1yXc1+ec2Kec2KfdGWdcmOe c2SleWqlfW+ogHOrgG2ofmqjcl6ebVqec1udclqfak+aZUqSZVGhc16meHCneXKqfWunemmjdV6e cFqid2qne2+nfnWvhn2vg2+ofWmncFOdZ0mNfoaNfoaMf4OLfoKNeoCOe4KQe4eVgIyVgouWg4yQ g4eQg4eQh5SRiJWVh5CWiJGQi4yOiYuQhomQhomOho2SiZGZkp6Zkp6ajpqWi5aZjZuXjJqSjJKR i5GSg4iOf4SSiI6SiI6ViJqbjqGikKWikKWfkZ+XiZeViIyWiY2UjI6VjZCQjJKRjZSajJeajJeU jJGUjJGbkZejmZ+dmqyal6qVkZ2RjZmVjpealJ2lmqyflaeakZ6XjpuVlJufnqahnq6fnaydjpeN f4iOf4eRgomIfn+Ng4SQhoyRh42Zgo6Zgo6Rh42QhoyOg4KQhIONfoOQgIaSf4iOe4SGe3+Een6L fYaRg4yRi5aWkJuVjJmakZ6Xl6WZmaaimaOhl6KllqKjlaGbkJuflJ+ZlKWXkqOWjZeXjpmajpqd kZ2WkJmVjpebkp2dlJ6ekpuXjJWWjpSWjpSVkJSRjJCViIyRhIiQiI2WjpSblZ6fmaKhkJqbi5WV goaOe3+Ne32Vg4SOhIaMgoORhIaViImag4idhoubhoibhoidkJaekZebh4mNeXuOeX6Xgoeah4uj kJShlp2hlp2jiY2hh4uafn6Xe3uah42ei5GWiYuOgoOHcnSIc3WReoKag4uZi5SZi5Sah4uUgISX foKSeX2MdX2Nd36Qfn+Rf4CWhIaaiImZhn6VgnqUeHORdXCQc3OWeXmWfYCXfoKZg4iZg4ieg3ua f3ibeXSaeHOOd2KLc16MaFWJZVONbl6UdGSZdWmdeW2ZeXOWd3CZeGuZeGuRd2uVem+We3ebgHuf f3mignuign6ff3uihoKjh4OniYmmiIihhoCeg36dgHuafnmhenOjfXWad2uQbWKQZ0+OZU6QZFSW alqab1eZblaZb1ybcl6adF+Zc16ec1+ZblubcFufdF6bdWqlfnOnf3Knf3Kme2ifdWKhc16fcl2d a0+ZaEyUZ1Wfcl+leHOmeXSofWmid2OicFafblSdcmGne2qqgHqvhn+zg3iufnOneFeic1OOg46M gIyQgIaNfoOMeX+MeX+Me4aRgIuSg4uWh46Rh42Rh42SiZSVjJaViZeViZeSkJSQjZGRjI2OiYuQ i4yUjpCZkpualJ2ZjZaXjJWWjZeWjZeViZWSh5KNgouNgouai5CdjZKajJWbjZabjZubjZuajZSS hoyUh4uViIyXjZSWjJKSjpqVkZ2fkZqfkZqakJaakJahl5+mnaWhmaqZkaKWjpSSi5CVjpedlp+l laeikqWakJSWjJCXlZmhnqKloayemqaekZWViIyQgo2Rg46LhomNiIyQh5GQh5GVh5KWiJSWi5SZ jZaSiZGQh46WiJGXiZKQgo2LfYiIfoKGe3+LfoKNgISQh5SRiJWWjZebkp2VlaKZmaaflaaflaam mqijl6ajlaGilJ+ZkaGXkJ+ZjqGbkaOdkKaekaebkp2ZkJqakqKelqaakqKWjp6WkJaUjZSajY6Z jI2UhoaOgICRh42WjJKdlKGimaaelKWXjZ6ah42RfoSOen2WgoSOhoSOhoSWh4ybjJGfi5abh5Kd iZCbiI6bkZedkpmbiYuSgIKIfoKQhomajZSekZejlpqll5umkJCijIyXgoSSfX+Zg4udh46biYuO fX6IbmqDaWWLbniWeYOWh4yai5CZhIeRfX+WfX6WfX6ReHmReHmReHeUenmWgICeiIidiIidiIia gIKVe32UeXWSeHSWenqdgICehIafhoehh4aehIOfg3ubf3iVe2mQd2SLalSIaFGOcGOSdGebenKd e3ObfXOXeW+WeG6Vd22QbmuScG6Sc22XeHKZenCZenCXe3ibf3uihoijh4mmiYyni42niH6jhHqf hHWZfm+de3Chf3Sfe2+XdGiXbVWRZ0+OZFGQZVOXbliacFubd2Wjfm2iemiheWeic2Web2KacF2b cl6ZcGefd22jf3OlgHSnf3KlfW+meGGidF2bbleXalSWalqabl2eeG2ie3CoemOfclufblSebVOh b1yod2Osf3qwg36uf3esfnWmdVijc1aQh5GQh5GShoyQg4mRfX+Qe36RhIiXi46ajJWajJWaiZmb i5qWjZeVjJaUi5WWjZeXkZqXkZqVi46SiIyUiJGbkJmfkqWdkKKZjZmXjJeajJWbjZaSiZaSiZaU iJaajp2ekZeekZebjJGai5CSjJWUjZaQiI2MhImUiY2XjZGZjZmXjJeXkKGblKWflKKekqGakJae lJqjmqemnaqilqKZjZmUiY2Ng4eQiZKalJ2hkpudjpeWjJKZjpWel6GnoaqloaqhnaadkpmXjZSR iJCNhIyMiI6Oi5GSiZaSiZaSjZ2WkaGhlKafkqWWkaGSjZ2Wi5aWi5aUiY2QhomOgoaMf4OJen2J en2JfomMgIyRjJCVkJSdlqKel6OdlaWdlaWil6qlmqylmaeekqGdjZ+bjJ6ZjqOelKiflKKflKKd lJudlJuWkaGblqadlqKalJ+bkpqakZmei46fjJCahoaUf3+XiI2fkJWhl6KimaOZlZ6RjZaViIyR hIiOf4SRgoeVhouWh4yZiY6djZKhkZmdjZWijpediZKVjpqblaGhkpuWiJGRhIiWiY2hlJqilZui lZmll5ujl5aekpGVf4SQen+Of4eVho2Vg4SOfX6Nc2iEal+Da2eMdG+Vhoiai42ahIeSfX+Ve32W fX6Ve32UenuUen6SeX2Vf4KbhoieiI2fiY6bhouahImahIKVf32VfXiXf3qahIKdh4SfhoSfhoSj iIOfhH+bgHWWe3CRcl+La1qSdGeae26igIKigIKif36aeHeWd3OWd3OSb2eUcGiUbmOXcmeUdGSU dGSVc26beXSbf3uni4eqjI6niYyuhoKnf3uef3WdfnSjfnulf32mgnmfe3Oac2GQaVeSaVGUalOZ cmSbdGeefXSigHiqhnmohHimfXOheG6ac2OUbV2ZbmKid2qfe3CmgnengHWngHWne2ObcFibalqb alqXaliZa1qbcl6jeWWmdVufb1WXaEiXaEiVaFSdb1uqe3Syg3uufnCneGqlc1ifblSUho6Vh5CU iZCSiI6VhouUhImaiZahkJ2hlKaekaOdjpeajJWZjZmViZWUi5eXjpufkZ2fkZ2ei5GbiI6bjZah kpujkqKfjp6Wi5aUiJSUiJGViZKUi6KVjKOajKafkauekaWajaGai5CXiI2OiJSOiJSUiJSSh5KZ i5ahkp6flJ2flJ2dlKGdlKGdlJ6XjpmZjZmekp6jl6ailqWakJaUiZCMgoaLgISNg4eWjJCZjZaa jpeWjJKdkpmflqGqoaulnqWfmZ+XkZeXkZeUjZaVjpeVkZeVkZeVjpqWkJuZlqWdmqiim6eel6OS kZuRkJqVjJSVjJSZjI2Xi4yRh42QhoyMfX+Jen2Gfn6MhISQi46SjZGbkp2bkp2ZkaGdlaWfl6ii mqummqahlaGhkJ2ejZqWjZqXjpufkZ+hkqGXjpaZkJeZkJ2flqOhlZ6ekpuhlZ6dkZqbjZmbjZmb h4mVgIOWh4yhkZahlaGekp6Vi46OhIiRf4CSgIKWgIaahImWhIaWhIaZiY6bjJGhjZSfjJKbjJGa i5CUi5KZkJebkJuXjJeXi46ajZGjlp2jlp2llZ2nl5+jl5aekpGUfnuNeHWLeXiVg4KWgoSSfoCM c3KHbm2GcHCLdXWVf4eXgombhImVfoOXfoKXfoKVf4KVf4KQfn+Rf4CSe4OWf4ediZChjZShjZSh jZSljo6eiIiXfn+UenuVe3qdg4KeiYOeiYOfhH2ih3+egn6afnqdd2uWcGWWd3CefniihImjhouj h4Kbf3qWeGuUdWmUcGWUcGWVbWOSamGRbmKVcmWScGuZd3KffXqnhIKsiYiqh4aviICngHmde3Cd e3Cif3qmg36ohHmifnOfemmdeGeadWSbd2WadWSdeGeifXijfnmqh4Kui4aqgHqjenShd2GXbliS aVaZb1ybbm2idHOje2uogHCnfWSXblabZEqeZ02WaVWZa1eablWdcFeib1qea1aXaEeOXz+JZE6W cFqje26rg3WsfW2md2eialCdZUyUiJGZjZaajp2ajp2XjpaWjZWdkKKfkqWilJ+ekJuXjJWXjJWZ jJ6ViJqZjqGelKailJ+ekJuah5CdiZKajpqajpqdkpaZjpKWjJKSiI6QhomQhomRiZmVjZ2ajaGe kaWbkp+WjZqUiJGRho6Qh5GRiJKXiZKZi5SZkJqflqGlmaWhlaGdlKGbkp+ekpuajpefkZqhkpuh lZ6flJ2ZjpCVi4yQg4SNgIKQg4SbjpCXjJWajpebkJmflJ2hlaGjl6Ohl5+elZ2alJ2alJ2ZkJ2b kp+Zkp6alJ+alqKbl6Ofna+in7KfmqqalaWSjY6RjI2RjI2UjpCakpeXkJWViZWRhpGOgoOLfn+J goKNhoaSi42WjpGalZmdl5ublaGdlqKfl6efl6emmqammqailJ2djpeWjZqXjpubkJ6ajp2ViZKS h5CXh5GejZehlaOilqWhl5+dlJubkJmajpeXhIuUgIeRh42XjZSbjZaZi5SSg4aNfoCUfnuWgH6X goKfiYmVh4eVh4ediY2ijpKfjJKei5GbjI6XiIuUhIyWh46WiZCZjJKUiZCVi5GfkZqilJ2mkpms mZ+mkpmhjZSXf3mQeHKQfXeXhH6Zg4OXgoKQeX6LdHmNeH2NeH2NeIKUfoiahIyahIydh4yeiI2a hISahISUgIeVgoiUfYKVfoObhouhi5CfkJWejpSmkJKjjZCbhoiRe36Wenebf3udh4eeiIijiISj iIShhoKdgn6hfXSZdW2bfXOfgHeliZCmi5GjiICbgHmad2uUcGWScmWUc2eScF6Na1qQbl6ObV2Q al+Xcmeee2ulgnKsh4SuiIashnumf3Wfe3Cfe3ClgoCqh4arh3qjf3Oie3Cmf3SlgHWlgHWffWqh fmumf3Wmf3Wog36sh4KrgH6lenilemedc1+UcFuSb1qXa1udcF+ec2ene2+mfWKddFqaZUqZZEmZ aFebalqabViabVibalWZaFOWZ02UZEqNYkeUaE2bdGelfW+qfWunemmjblWbZ06WkJuZkp6Zjp+Z jp+ekqGekqGdkKOajaGXjpmWjZedjJaejZeXjJqbkJ6fl6efl6edjpqbjZmVho2ZiZGXjJWbkJmZ lJeVkJSSi42NhoiRg4CRg4CRh42Vi5GZjp+dkqOWkJmQiZKQgouRg4yOhpCNhI6Vh5CajJWdlJui maGmnaehl6KekJufkZ2ekJubjZmXjpaZkJeel56fmZ+dlZedlZeWiY2ViIybjpWfkpmakZmZkJee jZehkJqjlZ6ilJ2flJ2hlZ6jmqeimaabkaKdkqOWkaGZlKOdl6einayin7Kin7KbmqWRkJqVhouW h4yWkJaZkpmblZ6WkJmZjJ6ViJqUi5WQh5GSiI6UiZCWjJKakJabkZWhlpqekqGekqGflqGflqGe mqOhnaailp+ekpuZkpuZkpuXjpaSiZGVho2Sg4uZhoydiZChkqGomqijmqKdlJujlJadjZCXgoyV f4mXi46ajZGdkJGXi4yRgoSQgIOZgoedhoueiJCljpabjpKViIyjkJanlJqlkZefjJKfjJCXhIiU foCVf4KRgoSSg4aOf4KNfoCXiJCai5KijpWolZunlJqhjZSZhIKXg4CXhIiZhomVh4eUhoaVgoiU gIeShISRg4ONeHiNeHiVfoadho2liZCliZCiiImiiImZhomVgoaSfoCQe36UfYKag4ihiY6ljZKo lJaolJadiIuOen2Rd3KUeXSZf4CbgoOjhoalh4emhoKhgH2ffnWbenKigHWmhHmmi5anjJemi3+f hHmjd26bb2eZc2iWcGWQa12RbV6SamGRaV+SbWKWcGWaeW2jgnWuh3+shn6qiH2lg3ijfXWfeXKn gn+qhIKrh36rh36ognirhHqqhn2ohHulf3CmgHKngHWmf3SmgHungn2qgoCmfn2ofW6id2ibclWR aEyXZ1Sda1iab1yfdGGld2Kld2KfaUiZY0OWaVOabVabblqbblqaaFWaaFWXaFCVZU6VZU6UZE2Z aluic2OmeG+oenKreVyjclWajaGbjqKZlKWalaaelqeblKWdkqOakKGWkJmWkJmfjpufjpudjpql lqKll6qjlqihkZabjJGWiIiajIyXjpmXjpmUkZWRjpKNhoaNhoaMgH+NgoCVg4SbiYuZi5abjZmW iY2OgoaOe4KNeoCMfYSOf4eQhoyXjZSelZ+jmqWfm6WdmaKekJubjZmXjpmWjZeXjJWdkZqemqah naijmqWhl6KdlJ6dlJ6blaGblaGZjZaZjZadjZKfkJWhkp6ekJuflqOlm6imm6ymm6ydlp+blZ6d laWdlaWflqOon6ysn7KnmqyXlZmOjJCWiZCajZSSkp+Skp+akZuXjpmZjp+Zjp+SkJ+UkaGZkpuZ kpuakJaakJadkZqflJ2bkaKZjp+ekp6hlaGbl6GdmaKhlaGflJ+fkZ2ekJufkJWai5CbhIyZgomO gH6Vh4SZjZunm6qenaebmqWfkpaajZGah42ah42bjpWdkJadjZCfkJKXiYmUhoaXiZWilJ+jlaOe kJ6akZmVjJShlJqjlp2mkZ2lkJujjZWeiJCZh4iWhIaRf3uOfXmUenmSeXiJfn2QhIOZjJCekZWd kJSdkJSdiIudiIuhiZadhpKXi5GViI6diZKhjZaijpWbiI6Qe3SLd2+Rf4CaiImlkZqmkpumjpSe h4yXhoeUgoORe3mNeHWQeniVf32bh4mjjpGokpWnkZSli4yXfn+McmqLcGmRdXKWenehf4Cnhoeo iIKlhH6ifnOdeW6denWjgHulh4ymiI2qhH+og36lgHSdeW2dd2ueeG2XdWWRb1+Oa1+Sb2OXc2SZ dGWbenKffnWmgH6rhoOui4iqh4SohHueenKhe3elf3qogICrg4OnhHSqh3emh3qmh3qrgnirgnir hHqlfnSiem2je26lfnejfXWlfWqmfmufd1SddFGfblufblubblqidF+ld1+ld1+jc1OhcFCXbVee c12bb1GZbU+dZ1ObZVGWbVOZb1WebVefblihb1emdFyjdWGld2KqeF2ndVufjaGikKOelKaelKab lKOZkaGZkp6blaGekJmekJmfjpuejZqfkZ+nmaejmaqhlqedkJSajZGZjJCajZGWjpSWjpSWkZWU jpKRh4uMgoaIgIOIgIORfXqWgn+ShoyUh42QgIaLe4CLdXqLdXqNen6QfYCNg4eXjZGhlJqnmqGh l5+ZkJeXjZSWjJKWi5SXjJWajpehlZ6ina6ina6lmqyil6qhmaifl6edlqKZkp6ajZSZjJKWjJCb kZWelZ+dlJ6fkKOmlqqlnayimqqblpqVkJSdjpqhkp6blZ6mn6iroq+imaadkJaWiZCWjZWimaGa l6eVkqKVjpeWkJmZkJ2dlKGWkaGalaWel6GblZ6alZmdl5uflKKilqWdkKKZjJ6akZ6flqOflaah lqedlp+dlp+hkp6fkZ2hkZaZiY6hiY6dhouVhouOf4SUi5emnaqlnayjm6ufkZqbjZaWjJKVi5Gd kZqflJ2hlp2elJqajo2ZjYyXkZeel56jmqedlKGdkZ2dkZ2jlJmmlpuqlaGmkZ2jjZeijJabjI6X iIuZhISVgICQe3mLd3SId3ONe3iSg4iZiY6ai42djZCZhoybiI6fjJWei5Sii5Keh46RgI2djJmo kZ6jjJmZgHqMdG6IdHKUf32fkJemlp6olJahjI6Xg4ORfX2OeXeLdXOQdHCXe3iZh4ajkZCnlZam lJWnjYybgoCUdG6NbmiSc22Wd3CfgoSihIeihoChhH+fgHebfXOafnefg3ujh4eliIiliICihn6h hnieg3WignuignuffW2Vc2OWamKabmWadGqeeG6denWhfnmmgH+sh4aujYmnh4Omg36ffXieem6j f3OmgHuog36mhHinhnmoh3qnhnmohHurh36sh4KqhH+ifmOfe2GjenCieW+mfmuiemihd16fdV2h dGOhdGOdcmGfdGOdd12adFufdFyjeF+adF+Zc16dbk6dbk6faUyhak2iclqmdV2mdF6mdF6meGGn eWKleWOjeGKqeF+qeF+djZ+fkKKelqablKOVkJ+Ujp6ZkJ2dlKGdkpmdkpmdkpmdkpmikqWnl6qi laedkKKblJmakpeekZebjpWZjI2Uh4iWjJKUiZCRhIuNgIeHenuHenuOe3WVgnuUgIeUgIeQfYON eoCNd3uNd3uMenuQfn+Ngn6ViYabkZehlp2jlpqekZWVjZCSi42ZiY6ai5Cdlp+im6WjobClorKh maqelqeimaahl6WbkJmWi5SZiY6ZiY6WjpSakpeakqKZkaGXjJeekp6imaaimaaakJGUiYuXi5Ga jZSUkp2enaemn6udlqKWiZCViI6ZkJ2jmqealaaVkKGSjJeRi5aXkZ2alJ+dlaWakqKjlZ6jlZ6f lqGimaOilqWflKKZjZmWi5aajpqdkZ2hlaOjl6aelKWhlqehl6WdlKGhkpuekJmfkZqZi5Sai5KQ gIiUjZmhmqanl6qnl6qhkp6djpqXjpaZkJeel6OdlqKjmqKflp6ZkZSXkJKdlp2fmZ+hmqael6Oe lZ+flqGmkpuqlp+mmZ+ll56jkp2ejZedjZWfkJedjZKWh4yVgIONeXuLd3SMeHWXg4abh4meiYeh jImaiImejI2fjY6fjY6hi4uhi4uSgoyZiJKllJ6hkJqehoCWfnmQdW6UeXKajIyml5eqlZWjjo6f hoSXfn2UeXSOdG+MbWeSc22SfX2fiYmjkJamkpmukZaihouXd26JaWGObmWXd26df4KjhoijiICi h3+hgnWdfnKafnmdgHuiiIehh4afiHuhiX2liIOmiYSri4esjIiognedd2uZc1yadF2efXSffnWh f3eefXShfnmlgn2sjIiqiYaqhH+ifXied2med2mhfXSng3qrh3urh3usg3mrgnimgnmohHunhH+q h4KlgGibeF+hdWeid2ilfmmhemWheWeheWefeGqfeGqbdWGbdWGZc1yXcluhcmKmd2eleWOjeGKi dE2db0ifaUqjbU6od2Oue2iofWSofWSuf2qsfmmrfWioemWmdFyndV2XkZ2alJ+XkZqXkZqXjpuX jpubkp2bkp2bkpqbkpqakJaakJaakqKblKOelaKakZ6ilJ2ilJ2fkJeai5KZhomXhIiViI6WiZCR h42LgIeOgICLfX2Qgn2XiYSXhoeVg4SQeoKNeH+Md36Md36MenuOfX6SfoCZhIedjZWhkZmikpeb jJGQiI2RiY6ZiJWfjpufl6emnq6nn6+mnq6dlqKel6OhmqOfmaKakJaWjJKWh4yXiI2SjpeZlZ6d lp2XkZeSjJWZkpuflKKhlaOakJSSiIyRh4iVi4yWkJmXkZqdlp+ZkpuUiY2Vi46dlqKlnqqbkJuV iZWRh4uSiIyXjpaZkJebkp2XjpmajJWekJmbjqGilaeflqGbkp2ZjpKVi46ViZKWi5SZkJ2elaKe lKWelKWfkqailaiilqWilqWhl6Kbkp2ekZeViI6SiZSbkp2hlaOilqWflJ+ZjZmakZ6dlKGil6qe lKailKKekJ6Wjo6akpKdlJulm6OlmaKilp+bkp+bkp+jlaGml6OlmaKjl6GikZuejZedkJSdkJSf kpaXi46XhIuWg4mSfniOenSVf4Kbhoifi42hjI6hi42hi42ejIudi4mhhoyhhoyZg4iZg4iikpqm lp6ijYeXg32Nem2Sf3KZh4imlJWsl5qlkJKhiIOXf3qWenOSd2+LcmeHbmOLd3CVgHqhi5KijJSq jJSlh46Zem6LbWGIamGRc2mbenuhf4Cni4Oihn6dgnSZfnCbf3qdgHuihIejhoiliIiliIiqiYeu jYuwjYyvjIuviXiifWuWcFqZc1yig3elhnmnhnmhf3OigHWjgnenh4Cnh4CuiYCmgnmdemqZd2ef eW+jfXOohHmrh3uwg36vgn2lfnSngHeog4CrhoOlgGqhfWefdFybcFifd1yjel+lemenfWmne22j eGmec2KbcF+Zc1yVb1iicF+od2WqfWuqfWuneWSjdWGhb1ejclqnemGugGewhGusgGi2hGuygGiw fmivfWeqeVyjc1aZkJeVjJSWjJ2WjJ2ajJqbjZuekJuilJ+flJ2dkZqZjJKajZSWjZqZkJ2bkpqd lJujl6OhlaGejpaZiZGZhomah4uVhoiVhoiSiIyJf4ORgoeUhImZjJKajZSZiJKWhpCQgIaNfoOM fn6LfX2NeXmNeXmQe3WXg32ZiY6ejpSdjo6ShISOg4KRhoSWhpCfjpmhl6Wlm6ilmaWekp6dkZqe kpuflqGhl6KakJGQhoeOhIiUiY2VkJ+ZlKOdlp+XkZqUiZCXjZSajpqdkZ2ZjpKSiIyZiY6bjJGb kZebkZebkZeZjpWVi5GXjZShl6KimaOijpWXhIuUh4uWiY2akJaakJaXiJCUhIyUh42WiZCVjJaa kZualJ2blZ6fkpaViIyQhomRh4uSi5CXkJWblZ6dlp+dkKOjlqqll6ummayimaGelZ2bkZeVi5GQ h46VjJSXkZqblZ6dlaWakqKflaeelKallqWhkqGei5Sah5CZjI2ajY6fkpanmp6om5+ll5uVjJSV jJSZjZubkJ6bkJ6flKKllZ2fkJedjZKai5Cei5GbiI6XhI2UgImVgoaVgoaVhoiZiYyhi5CijJGn kZGljo6djZCai42hho6ih5CahImeiI2fkJWmlpumkZGfi4uXg4OVgICbhoumkJWqkpeokZajiYud g4SXfXWQdW6Rd2uLcGWIbmeSeHCah4ufjJCijJGijJGagG6Mc2GIal2Nb2KaeXqlg4Smh32dfnSZ fnCZfnCdfnSef3WlhIKlhIKjhoiniYyojIyqjY2ujYuwkI2vjYKjgneWdGSXdWWjfnurhoOqiH2n hnqmg3CffWqjfnmog36shn6ngHmfemebd2OZcm6feHSlf32rhoOvhn+vhn+nfWmlemehfXSmgnmh g2+fgm6jd12fc1qeclaidVqmeWinemmofWSjeF+hc16db1uZbl2XbVylc2KndWSnfWSrgGiofWun e2qldFymdV2nd1yvfmOwgmqvgGmwgm2wgm2uf2iuf2irgGGme1yZiY6ZiY6ViZeSh5Wai56bjJ+b kaKdkqOdkpmZjpWZiYyZiYyVjJaakZudlKGhl6WomqallqKXjZSSiI6VhouVhouUhImVhouRho6R ho6Ui5WUi5WXjpuelaKhkaWZiZ2OgoaMf4ONg4SOhIaUf4KNeXuNfXWUg3uShomXi46bjpCRhIaM goOOhIaSiI6XjZSflJ+hlaGhlp2ZjpWWi5aflJ+hl6WelaKhkpKajIyUiZCZjpWblKWdlaadl5uW kZWShoeViImZjJKajZSbjJGbjJGdjpqekJuZkJeWjZWbkZWZjpKViZWZjZmhlaOhlaOlkZediZCZ iY6fkJWfkJWbjJGWhIaRf4CUfoaahIybjJSdjZWelZ+flqGdlZqVjZKQiIuLg4aUhImZiY6WkZWa lZmdkZ+flKKilKKllqWjl6GekpuakJaXjZSRh42QhoySiZGXjpablKOZkaGdkqOelKWil5uelJed iIiXg4OdiIueiYyekZWmmZ2om52mmZqejIiUgn6ZhISbh4eZjJCbjpKhjZaijpeei5GZhoyag4ue h46XhIuUgIeah4udiY2ajpqbkJufjpmikZull5milZahlJeajZGeiJCeiJCei5GdiZCfkJWjlJmm kpajkJSbho2ahIybho2jjZWrkJmvlJ2skJKhhIedgn6We3iSeWqMc2SHaGKQcGqReHmiiImfjY6f jY6bgm2Qd2KOcmGRdGOdenmhfn2ffnWde3OZeGude2+eg3ieg3ilgn+nhIKliIumiYynjY6qkJGu jYuvjoyrjH+lhnmbc2mXb2Wbd3Sog4CujYmqiYaqhHCifWmfeW6lfnOngHWngHWlfWqfeGWWcGeb dWuienqshISrh3uohHmhfWSbeF+ZdWmdeW2hfXKdeW6hcFiiclqdb1ihc1yidWSleGelel+jeV6h cFiba1SXblSZb1Whc1yld1+qf2esgmmrf3Cqfm+nel6leFyqeVyygGOzgG2wfmqwgHCygnKvf3Sz g3izgmmvfmWWg4mWg4mUhpSUhpSWh5mXiJqbjqGdkKKakZmZkJeZiY6XiI2WiJGbjZadlaahmaqi lp+dkZqQhomLgISQg4mRhIuRh4uVi46ZjpWZjpWZkpublZ6fkqWjlqihjqKZh5qViImWiYuZjJKa jZSVgoiVgoiXhoebiYuWh4yXiI2ei46VgoaQg4mRhIuOhIiVi46hlJqhlJqXkJWSi5CUjZadlp+l lqWilKKakJSWjJCajpedkZqlmaelmaejmZ+elJqbh4mZhIedjZCdjZCakJaelJqekaOfkqWjlp2d kJaZjJCXi46VjJaZkJqZlKOVkJ+ajpqXjJeakJadkpmelJeZjpKUhIeUhIeUh42ZjJKXjJqekqGd kqOhlqedlZqWjpSQgIaOf4SWg4yei5SWkZWVkJSfkJehkZmbjZmfkZ2flZudkpmbkZKVi4yRgoeQ gIaMgoiSiI6VjJSXjpaZkJqdlJ6jl5aflJKijZCdiIueiJKfiZSfjpuol6WuoaWrnqKjkY2di4ed i3qaiHiaiImbiYudiZKhjZadjZWai5KfiYyZg4abhImdhouei5SfjJWajpebkJmjlJullZ2om5+q naGqmp2fkJKii5CfiI2fjpuhkJ2mlp6mlp6nlJehjZGah42ah42liZCliZCrkJuukp6ukZSni42d h4eXgoKVgHeQe3KObmKLal6McG2fg3+hjZGdiY2agHOSeWuScmWVdGibeXShfnmhfnmhfnmefnii gnujh4KliIOliIOliIOmiYyojI6nkZGokpKrjouojIiujoSmh32he22ZdGWadGqjfXOohoOvjImv h3Srg3Cjf2efe2Omfm6ogHCrfm+meWqbcGKXbV6ZcnKheXmoh36nhn2lfWqbdGKbcGKdcmOfc2Ki dWShb1Ohb1OdbVWba1SbblyidGKhdV+hdV+ebVeaaVSbbU2fcFCdc1iieF2lf2uog2+rf3CofW6n eFamd1WjeWGsgmmyh3OwhnKzhHKwgm+wgHWzg3izg3O0hHSNg4eNg4eSgo6Xh5SXiZKZi5SejZ2e jZ2djJmZiJWah42ah42ZiJKfjpmhlqeflaabkZWUiY2Rg4CQgn+RhIiViIyVjZCWjpGZjpKZjpKa lJqdlp2elaKflqOfkZ2ajJeVkJGVkJGekpudkZqXi5GViI6ZiZGXiJCZjI2ajY6ajZGShomMgoiM goiRh4uUiY2akpeZkZaUjJGVjZKUjZaalJ2dkZ+bkJ6bkp2ZkJqdlJ6dlJ6il6qhlqiel6OblaGa i5KVho2biI6ei5GXkZeel56hlKailaellp+fkZqZiYyVhoiUi5WZkJqVjZ2UjJuXjpuVjJmbkJmf lJ2ikpqbjJSVi5GVi5GXjZSakJadlKGelaKhl6Whl6WhlJeZjJCVgIOSfoCVho2bjJSakJaWjJKX hIubiI6Xi46bjpKfkJKfkJKXjouRiISUf4KUf4KMeX2Oe3+Qg4eUh4uZi5SekJmilZuekZebjZab jZaZiJWbi5ejjJuqkqKmmqajl6Ofjpmbi5WdiIiahoaahIyahIyai5CdjZKWi5SXjJWai42XiIue iI2fiY6fjpmhkJqekZefkpmmkpaqlpqomZ6snaKqm5mjlZKjjZKfiY6hjJqlkJ6qlp+qlp+rlZen kZSeiYybh4mdiIuijZCikpqhkZmrkZCjiYiihISfgoKbf3+WenqRd2mJb2KMbWqdfXqijI6jjZCd gnqVenOUc2iScmeZeXOff3mefnqff3udf3+ihISni42ojI6mjJCiiIyliIimiYmnjYyrkZCyjZCu iYyvjYSnhn2le3KddGqXcGGddWWhf3enhn2shnuuh32rgmSjel2feGWlfWqqfm+ofW6meGWfcl+a cmmheG+qg3irhHmme2ifdWKbcl6acF2dc1ihd1ydcFWbb1SablWfc1qhc16hc16hd1ybcleXaFCa alObaEmhbU6bcl6ieGSrf3OwhHiyhHOqfWund1Ojc0+ld2SrfWqshHSvh3ezhnSvgnCwg3KvgnC2 hnW6iXmShoyRhIuXhIuZhoyViImZjI2ajZSdkJaZjJKXi5GWiZCWiZCZiJWfjpujkqKejZ2Zi4uS hISNgoCOg4KQhoyZjpWbkp2dlJ6ekZefkpmblJmelpullqWnmaeflqGdlJ6elZ2dlJuhlZ6ekpuX i46ZjJCXjpmXjpmakpKZkZGajZSUh42NhouQiI2VkJGWkZKblJmXkJWZjJKWiZCWiJaXiZeWjZWW jZWakZuakZubkp+dlKGXkKOakqadlaWakqKWiJaUhpSXgI2XgI2UjZSdlp2flaaflaaflKKajp2X i46Uh4uWjZeZkJqUhpGUhpGVjJmbkp+bkpqflp6jlJudjZWXjJeXjJeXjJWajpealJ2blZ6flqOh l6WjmZqbkZKWgoKSfn6Zg4ueiJCfkJWdjZKZhIeahoiZh4ibiYudjo6djo6XjIuRhoSSfn6RfX2N eH2OeX6NfoOQgIaVho2djZWfkpmfkpmekqGbkJ6ajJWZi5SdjJaejZefkZ2ml6OejZ2djJuah5Ca h5CZhoydiZCdiY2ei46XjJWXjJWbjJSai5Kei5Sei5SfjpmikZulkZehjZSikpWnl5qlmp6onqKq nZ6nmpumjpadho2ZiJKhkJqmkpuqlp+rlJmnkJWeh4ybhImag4ieh4yfiY6ijJGni4eihoKhg4Od f3+aenidfXqWenqQdHSRcHKbenufhoejiYuhhoCaf3qRd2uOdGmVdXOZeXehfnuhfnudf4KihIen jJKrkJankZGljo6miIijhoani4eskIywkpKukJCui4ariIOrg3Wed2mbcF+ec2Kdd22ie3KqhH+w i4augnCmemmed2Sje2mqfXCoe2+idWSidWSec2ehdWmjfXKmf3SngGehemGfdV+fdV+hc16hc16e cFydb1uhc2Gld2SneWSld2KidVqhdFibak6ZaEyaaU+hb1WhdGioe2+ugHmwg3uyg26oemWnd1qo eFujd2Woe2qshnCzjHe0iHSzh3OwhHOsgG+wg3S3iXqWiZCUh42XgomXgomWh4mZiYyZjpWXjZSX jJWXjJWajp2XjJqdi56hjqKjjpqeiZWXhoeUgoONg4eWjJCflaail6ihlKehlKehlZ6flJ2hlpqh lpqekqGhlaOfkqajlqqfl6ejm6unlqGllJ6bjJGbjJGbkp2imaOfl5qfl5qdkZqViZKUjZSSjJKX kZeblZualZmVkJSdh5GZg42VhouWh4ySiIyRh4uWiJGZi5Sdi56biZ2WjZqSiZaaiJuaiJuSh5KN go2UgImSf4iSh5CViZKZjZaZjZaajJWWiJGSiIyQhomZi5SZi5SOeX6Re4CShI2XiZKbkp2dlJ6f kZqbjZaUiJSViZWakJaakJabkJuajpqbjZailJ2om52ekZKXgIaVfoOag4uhiZGikpedjZKZhomU gISXgoeahImeiI2hi5CfjY6XhoeQgoKNf3+QeYCOeH+Oe3+RfoKRf3ubiYajlp2jlp2flqOdlKGe lJeXjZGajY6Uh4iXi5GdkJaeiZqfi5uViZKViZKZiY6bjJGah42ZhoyXhIuXhIudiZKdiZKfjJWi jpejjZWijJSdh5GahI6ai5ChkZailp+lmaKqnaOrnqWqkpqfiJCWf46ag5KdiJSjjpqnl52llZqn iY6fgoebfYSfgIiehIifhomjiIOfhH+eg36dgn2bfXOZenCOenSMeHKOcnSUd3mdg4SmjI2jiH2e g3iZe2qOcmGOcGSUdWmdeniffXqefX6oh4iojI6ukZSqjZCojI6nhoeigIKnhIOriIewkIysjIis i3+si3+shHemfnChemWbdWGacmiddGqifXiog36siHuohHiofWmjeGSfdWKieGShc2Gfcl+ec2Kf dGOee2ujgHCofWuleWihem+lfnOoe3OleG+lc1+lc1+lemeofmqsfmerfWWqeFusel2lcE6ZZUSb ZE+mblimd2uufnOygoCzg4Kyf2mue2Wod16semKod2OqeGSsgG2yhnK0hnC0hnCzhnSwg3Kwg3e0 h3qWi5SSh5CXg5GVgI6Zg4uahIyUiJGViZKWjZWWjZWbkJuZjZmdjJaejZebi5WZiJKZhoyXhIuU h42fkpmjma6lmq+mmaull6qhl6KflqGhlJeilZmZkpmalJqbkaOil6qfmqqjnq6rlqKnkp6fkpme kZeflqOhl6WhlaGekp6ZjZmZjZmXkZqalJ2elaKflqOZkpuQiZKWh4yUhImRhIaQg4SNgISNgISO hIuQhoyVho2Sg4uRhIiShomXh5SXh5SRh4iLgIKOg4KViYiRh4uOhIiXi5GXi5GZiZGWh46UiZCU iZCWh4yWh4yRen+QeX6Lfn+WiYuZkZSblJabkZeakJaViI6ViI6ZiY6bjJGhjZSdiZCfjpmikZul lqKhkp6aiImWhIaWg4ediY2ikZuejZeZhIeVgIOVgoidiZChiZGii5KhkZaZiY6Uh4iUh4iWf4SS e4CQfYORfoSRfoKZhomjkp2mlZ+hmqGel56jlZ6bjZaeiYmXg4OZhomdiY2ZiZGXiJCbiJGbiJGb iJGhjZaijpWei5Gag4uag4uXhIibiIyhiZGjjJSdjZKZiY6UgISWg4eVh5CajJWilKKml6awoq6s nqqvmaGijJSbhoiWgIOUfoadh46nkJ2nkJ2miJCegIihe3meeXeaf3qhhoCdiIKdiIKegoKdgICb e3WVdW+IdWiLeGqNc26SeHOif36nhIOmh32ig3mafWuVeGeRcmKQcGGZeG2de3Cbf3ijh3+ni4eq jYmrjo6qjY2nhIKlgn+og4Kog4KnhH+ohoCvjIewjYiuiHmog3Sjfm2eeWiedWuddGqfdXOnfXqs g3quhHuvg22ne2WhdV2leWGld2SjdWOdd1+Zc1ydeGelf26mfXOnfnSmfnqogH2ugnWqfnKneWSo emWof3eof3evf3SwgHWuf2irfWWndWSfbl2hak2hak2jb2OueW2vgHuzhH+wgm2vgGuue2GwfmOu e2Ood16seme0gm63h3ezg3Owg3KyhHO2hnq0hHmZjZmZjZmXiZeUhpSRgoeSg4iNhoiUjI6XkJ+Z kaGbjZudjp2ajJWbjZaXiZKWiJGbh5Kbh5KXiZWhkp6fkqajlqqqoa6mnaqhl6KimaOelJqdkpmf kpSfkpSakZujmqWmn6uooq6qlaaolKWflqGakZuel6GhmqOhlaOdkZ+WjpSWjpSfjp6ikaGjnaah mqObkJuSh5KQhoeQhoeRg4OOgICHeHqHeHqLe4CLe4CUeX+UeX+MeHqSfoCVhouZiY6RhoSQhIOS hoebjpCVi46UiY2ZiYyXiIuVhoiZiYyXi4yajY6biIyah4uUf3mSfniOgICWiIibjpKekZWdjZKa i5CWh4yWh4yZg4uahIyZiY6bjJGfjpuikZ6ikaGikaGajZGViIyUh4ibjpCekJuekJuahoaXg4OV goaah4uZiJKejZehkpuekJmdkJafkpmhho6ZfoeQgIiRgomOf4KUhIefi5alkJunmaKllp+ijpeb iJGQgn+Rg4Cdg4SehIadi4yaiImfhI2ih5Chi5WmkJqmkpujkJmXgoSVf4KUgn6Zh4Obh4eeiYmf iYeXgn+SfXqUfnuNgIeViI6hkJ2nlqOsnqysnqywnaalkZqhh4aZf36OeniWgn+eiJChi5Kdhoub hImef3WZenCZfX2egoKliIiihoaihISfgoKee3eVc26Qc2KRdGORc2eSdGiee3elgn2jiICfhH2d gnObgHKQc12LbliZdWqhfXKegnqdgHmhg4OrjY2zkI6wjYysiYSohoCmg4Clgn+mg36nhH+siYev jImsi36qiHulgHSifnKje26heWuecmmleG+qgHewh32yhnSsgG+ne2OofWSof3WnfnSje2ufeGie eGOie2eje2unf2+mgHush4KyhnSrf26rfWqoemiuf3iyg3uyhHuwg3qshnCogm2ogmimf2Wsd1Gj bkmja16udWirf3OwhHiwhG6whG6yg26yg26zgG+semmreWGyf2eyhnSyhnSzh3qzh3qzg3OygnKV i5uUiZqXiZeUhpSOf4KSg4aVi5GelJqflaaakKGUiJGSh5CWiJGZi5SViZKWi5Sbi5edjJmbjZaf kZqjl6Gonaanoaqlnqemmqilmaeml6OjlaGljZKljZKikpqrm6Oon6yroq+nmaKjlZ6dkZ2bkJue lZ2elZ2hkpudjpedkJSdkJSakZuflqGjn6uhnaimkZ2ahpGZjJCZjJCWiY2RhIiIenqHeXmLfX2I enqIeXuIeXuIeXuSg4aai42ejpGViIyUh4uWi5SajpedlJ6ZkJqekZKajY6ekZWhlJeekZWbjpKW iY2Uh4uQe3uSfn6Vf4edh46bjpKZjJCdjZWZiZGRh42OhIuXgIadhoudkJafkpmekaOekaOmkKWl jqOjkJmfjJWai42djZCdkZqdkZqbjYuUhoObhImii5CakJabkZeilp+flJ2jlJullZ2ijpediZKU h4uRhIiWgoSWgoSai5KfkJeilZuhlJqajZSWiZCZhIeZhIedh4meiIuajouajouZhIeeiYyjkJmm kpuijJSeiJCQfn+Qfn+SfoCWgoSbh4mdiIufhH2dgnqafnqWeneOenqVgICbh5KhjJemkaKvmquq maajkp+dg4KZf36SeHOUeXSWf4SfiI2hho6hho6ign6ZeXWbfoChg4amjJCli46jiIShhoKZgnWS e2+VemuUeWqWdWqScmeaeXCjgnmmi4ejiISmi3+ih3uhgHCZeWmdfXqign+nhIKlgn+jgH+qh4av i5Cvi5Cwi4irhoOjgHulgn2if3qjgHuhhoKliYami3+jiH2ng3qlgHile3Wle3WjenSfd3Cje26s hHeyhHuyhHusgG2sgG2rfnmugHusg3qqgHiqd2iseWqmemmofWuqhHWsh3iwg3KugG+qe2mrfWqv gHizhHuuh32viH6vi3+uiX6yhnSwhHOugGSoe1+ld1+neWKsgHSvg3euhniyiXu2iH+0h360hHSv f2+qfWGvgmWohnWvjHu0jYK4kYa4i3uyhHWUiJSRhpGOg4yMgImSg4uVho2XiZWhkp6dkZ2Wi5aV houWh4yVh5KbjZmekJ6hkqGbkJuXjJeZiZGdjZWflqGjmqWjmqWjmqWnmaWnmaWmlqqikqajjZWi jJSflJ2mmqOom66rnrCmnaWelZ2ajpedkZqfkJefkJefkpmdkJaakJSakJSdkpajmZ2roq+mnaqj lJuejpaflJ+hlaGakJaVi5GUgISQfYCLe36MfX+VgIORfX+LgISUiY2bjZadjpeXiZKUho6Sh5KZ jZmekaWekaWZlZ6VkZqdlKGelaKdlZqWjpSQhomUiY2Uh42RhIuViI6ajZSbjZaekJmbjZmUhpGR ho6UiJGbho2ijJSilJ2hkpuajZ+ajZ+njqGokKKhjJqhjJqijpWfjJKajpedkZqejpGZiYybho2f iZGekZefkpmhlp2flZullJ6nlqGlkJuhjJeah42ZhoyXgoeVf4SfkJWfkJWfkJWejpSZho6Sf4iW gIadh4yeiJKeiJKai5Cai5CXjIiajoullJ6mlZ+hi5KXgomOeniNeXeRen+XgIaahImahImfh4Ke hoCXgoSWgIOUe3eVfXiVf4Kdh4mjkJmrl6GqlJ6okp2lg4eaeX2UeXWSeHSRen+VfoOdgoibgIed gIOafoCZf4OfhomljZKljZKnjIihhoKeg3ubgHmXe3SVeXKVd22SdGqaenSjg32vjY6vjY6rjoen i4OmhoKff3uigIKmhIani4eliISigHilg3qqjIysjo6wjI6qhoimhHiigHSefXKffnOee3mif32r h3urh3uohHmmgnehfXChfXChe3ebd3KidWmoe2+vgHmwgnqvg3eugnWqg3uviICyiH6vhnuvf3Kr e26leWiofWurg3Ovh3evg3KugnCqe2eoemWuf3evgHiuh3+viICwh4Cwh4Cwh32uhHqrfm+ugHKs gG2rf2urf3Cvg3SugHuyhH+3iYK2iICziHSsgm6neWKrfWWvh3m2jX+2ko23lI67kYu2jIaUgIeR foSOf4eRgomVg5aaiJuejZqejZqZiZGVho2XhI2ah5Cdh5umkKWllaejlKajlJuai5KZhombiIye kp6flJ+elaKelaKhl6Wjmqejl6ahlaOijpeijpeekp6lmaWrnrKrnrKnnaOelJqejpSbjJGjkJmi jpedkZqZjZaXjZGakJSflZmmm5+oobCmnq6hl5+flp6umaqvmqujmZ+bkZeah42ZhoyVh5KUhpGa hoiXg4aUhpGbjZmekp6dkZ2dkJSWiY2Zi5SbjZaelKWjmaqfl6ielqefmaWjnaihlZ6Wi5SVhoiX iIuViI6WiZCXjJWajpefjp6ejZ2bi5qXh5aWiZCWiZCdiJSijZmilqWhlaOijZmijZmhjJqlkJ6i kZ6ikZ6okZ6ljZqXi5GZjJKZiY6ai5CbiJGdiZKZjZabkJmbkJuflJ+olKWqlaaikqWdjZ+ijZmX g46XgIaVfoOai5KdjZWfiZGdh46SfYKQen+Wf4Sdhoueh46hiZGhiY6dhouaiIeZh4ahjJemkZ2e h46UfYSLcnOQd3iVe3+Zf4ObhoabhoaeiI2eiI2dhpCag42XfXiVenWSeXiXfn2ei5GjkJanlJen lJewiYymf4KafX2VeHiRdXqQdHmafX+df4KegIOdf4KbgImjiJGojZSmi5Glh4eniYmnh4OmhoKd gHuafnmVeXSQdG+ZfnqfhICsjo6vkZGsjo6sjo6rjpGni42niYmmiIiokIenjoajgG6ee2mjg4Cr i4isi4yriYusiH2ohHmmgHKhe22jd2+hdG2mfnCogHOohnWohnWlf26he2qddWied2mjd2ileGmr fnKwg3eyhnewhHWqh3eui3qyiH6vhnuwg3evgnWofWmqfmqogHCshHSuhniuhnireGKndF6nenOs f3ivhn2wh360i4KziYC2iXqvg3SoemWqe2eofXCjeGurfWq0hnO0h3+3iYK3i36zh3qvgGmqe2So dV+uemSwg3q7jYS2lJW6l5m/lY67kYuNe3iNe3iNfoCUhIeZiJWdjJmekJufkZ2bjJSXiJCVhouZ iY6ekJ6jlaOjlaOhkqGijJSahIyXgomahIybi5Wbi5WdjpehkpuhlKailaelmaemmqill56hlJqf lKKlmaern6uqnqqmmZqhlJWfkJeejpadkpmelJqdlJudlJuekpubkJmflp6lm6Omnq6mnq6imaam naqvoaqsnqelnZ+fl5qhlJeekZWhkp6ekJufjJCah4uUiJGViZKZlJealZmejIiZh4OXiYeekI2e lZ+hl6KilqKjl6Omnaqmnaqhl5+Ui5KUhIeUhIeShJCUhpGai52ikqWnkp6ijZmfiZGfiZGUh4uW iY2ZiZujlKanl5+hkZmeiYyahoiai5KdjZWhkJ+ikaGqlp2ijpWXhIuZhoyZg4ubho2ahIyZg4uX jJeXjJeXiZWdjpqjkqKikaGlmquhlqelkZeZhoyVfoOVfoOVgouah5Cdh4mahIeRfX2Qe3uZf4Of homhho6ih5Chh4uehIiahISfiYmhi42ijI6ahn6NeXKJcG+NdHOVeXudgIOfhoehh4ifjJKhjZSh kJqbi5WZg4CSfXqJdW6Qe3SRhIiWiY2ijJSmkJeskIyliISehoCZgHuVd22OcGeWdXeffn+fg4ad gIOeg4mhhoyljIeji4alhIKnh4SuiIOsh4Klg3iigHWaf3SXfXKWenedgH2ni5CukZavkJevkJeu kJqylJ6vlZmyl5u0mpuvlZasi36de2+deHOjfnmmhoOoiIauiIOrhoCnhHSffW2ac2WWb2KZd2ei f2+og2+og2+qfm2jeGeeb2KfcGOZc2ibdWqfd22qgHeshHSqgnKogHOrg3Wof3Wrgni0hHSwgHCu emeqd2Omem6ugnWuh3uuh3uqeFuhb1Ohc2GneWesf3qyhH+yh4S0iYe3iX2yhHiweWSrdF+hdWSf dGOneGiygnKzg3i3h3u2h3Kyg26wfmGsel2sc1+udGGvgn24i4a3jZG/lZnFlZTBkZCQhIONgoCM gH+RhoSXiZKbjZaekqGbkJ6hjZadiZKWiJGXiZKZjZahlZ6fkqWbjqGdiZCZhoyWgIuZg42Zhomb iIyZiZGbjJSfi5aolJ+lmaWonaijmZ+elJqflKKlmaemmqimmqiml6GjlZ6flZuflZudlp2blZue kqGekqGdlqKfmaWhmaimnq6on6yqoa6onq+sorOvo6+soayooqilnqWhl5+elZ2hmZ6hmZ6llZqe jpSUjI6RiYyWjpSakpeekJCZi4uXiYmbjY2akZ6elaKflKKlmaenna6jmaqfmZ+UjZSQhIOOg4KN foOSg4iWhpChkJqmlp6ejpafjJKfjJKWiY2ViIyZi5SfkZqjlJafkJKfi4uZhISZhombiIyeiZWl kJuhlJeekZWZg4CXgn+Vf4SXgoeXgoyWgIuShoyShoyah5Cei5SdjJafjpmjmqWimaOilJ2ekJmX hoeSgIKWhIabiYudh4SahIKQeniQeniZfYKihouhi42dh4mag4iZgoeahISeiIijiYiiiIeWfnWJ cmmIb3COdXeafYelh5Glh46ihIyei46jkJSikpehkZaiiImZf4CLd2+MeHCJen2QgIObhpCjjZem iYyliIuiiIyZf4OReG2Mc2iVdXKaenebh4CeiYOdh4meiIuhh4ifhoelgoCnhIOwi4iuiIanhn2j gnmefXCbem6WdHKdeninjJKwlZuzlZ2wkpqzlZ20lp6wlZGylpK3nZuwlpWwi4amgHufemuhe22h fnmjgHumg4Kqh4anhHSffW2bd2ORbVqSbl2adWSifnOlgHWqfXCmeW2fdGOZbl2Sb2OWc2edcmWh dWmne22rf3CofWerf2mqfmqugm60hHevf3KueF6lb1afc2Soe22qgnSshHeoemOhc1yeaVOmcFqn eGiwgHCwh36yiH+0iXWyh3OzgmmqeWGmdV2iclqhcmSqem2yhH+2iIOzh3Czh3Cwf2evfmWvemuq dWevgn24i4a0jIu6kZC+jo27jIuRhIuNgIeQfYOZhoyXjJWdkZqbkaKZjp+fjpuaiZaRhIuViI6e jZehkJqbjZmWiJSUh4uRhIiVgoaVgoaXhoeUgoOWf4eeh46ejpajlJunmaernauhl6KdlJ6flKKj l6almaemmqinmqymmaummqinm6qinaGfmp6hlaGflJ+bmaeem6qimaannquqoa6qoa6qo6+qo6+y pauzpqyrpa6noaqfm6eemqahmqael6OjlZ6ekJmdjZCbjI6bjpWhlJqelZ2WjZWZiY6XiI2XiZWb jZmfjpullKGqmqyomaublaGVjpqSh4OQhICQe3mUf32XhIujkJafmp6ZlJehlJqhlJqUi4mOhoSb iIyijpKikpeejpSfi4udiIiahIeZg4abhpCljpmhkZSdjZCagH+Xfn2WgH6Vf32Uf3+Sfn6Ren+R en+Vf4SbhoubiJGhjZajlaGomqail56akJaWfX6WfX6Vg3+aiISeiIudh4mWgnuSfniWf4ShiY6h jI6ahoiagISXfoKahIShi4uojpKli46WgnuOenSLcG2QdXKbfoioi5Whho6fhI2fi4umkZGlkZWm kpamkZSbh4mVenWNc26JdHKOeXeXfYOih42hi4uijIyijIyZg4OVeneNc2+Sd2+ZfXWfh4KljIei iImjiYufhoedg4SdgH2hhICsi46zkZWojIeliIOognieeG6ScG+UcnClhpCsjZeukZGukZGylZq0 l52zlZWzlZWwmpqwmpq0joyuiIaie2ebdWGZeG2ffnOjfnuqhIKnfnSmfXOfe2WSb1qRaFOUalWZ cGiedW2leGuleGufdGGab1yWa1aUaVSUaVSXbVeidF+oemWoeF2oeF2reWiwfm2zg3Wvf3Kre1qe b06hc1yqe2Sqfm2ugnCue2ilc1+jblWibVSjc1uqeWG0h36yhHuwhG60iHKwgmquf2iseWWreGSo d2WndWSvgnq3iYK0jHmvh3SwgHCufm6sf26rfm2wg366jIe2kIu0jomziYCziYCRg4yShI2WgIiZ g4ubi5edjJmajaGajaGhjJeahpGSgoyVhI6Zi5SajJWdh5GahI6XhIiSf4OLf36Lf36XgIaZgoed goujiJGfkZqhkpujlaOjlaOflJ+ekp6fkZqhkpulmaemmqimnaqroq+rn66onaumm6Khlp2ekJme kJmfl6ehmaimmaunmqyqoa6qoa6upbKqoa6vpq6vpq6rpbCooq6ml6ajlaOim6Wim6WnmqGll56h lJqdkJaekJmhkpufmaWZkp6djZWXiJCbiI6fjJKdjZWhkZmol6esm6uml6afkZ+ZjI2WiYuZhH6Q e3WShISbjY2blZuhmqGnlqGnlqGZjpCQhoeZiZGdjZWekZedkJaeiI2dh4ybho2Zg4uih5CnjJWo kZmnkJedh4ydh4yXgoeWgIaafoCUeHqVfXiUe3eSfYKVf4SXgoedh4yii5emjpull56bjpWXfoKV e3+Qgn2UhoChi5WeiJKei46ZhomXgIaii5CfiZGeiJCjhoidf4Kfg4amiYyrkJmnjJWahIyQeoKV eXmWenqbgIehhoyii5CfiI2biYiikI6lkI2ijYuhjpCbiYubf3+UeHiRdHeOcnSQdHebf4Kli4yo jpCmjI2fhoeWen2RdXiQdHCXe3ijiYurkZKojo2mjIumiIuihIejh4Kfg36ihoaukZG0lJGvjoys h4KifXiWdW2ObmWfe36rh4mrkZWulJewlJmwlJmzkZWzkZWvkpe2mZ64l5WsjImng3eeem6VdGiX d2qfe3Ojf3emfnCiem2heWeUbVuSaFCRZ0+WaVedb12idGKhc2GmeGOhc16Xa06UaEqXZ0+ebVWh c1ymeGGnd1yoeF2meGOoemWvg3SsgHKhe1ubd1aec2KmemmsgHKsgHKugG+oe2qmdF6hb1qmdF6u e2WyhnSzh3Wvg2+whHCugnCsgG+re3Cre3CreGureGune2qvg3K0iHmyhnezh3qvg3esg3mrgniy iIK4joi3jou0jIiygHmwf3iShomUh4uXgoyXgoydh46eiJCekqGajp2bjZmajJeVh5CVh5CWi5SV iZKZhJCVgIyRfoKQfYCOf4KSg4aZg4udh46biJGdiZKZi5SekJmfkZ+ekJ6ejZefjpmjkJmijpej laGrnaiqoa6on6ynna6mm6yml6GfkZqdjZWdjZWhkaOmlqillaimlqqmmaunmqyvn7Kvn7KopbCq prKupbKnnquolZ6lkZqjl6Gjl6Gilp+flJ2llZ2jlJujlZ6omqOlm6ielaKhkpuajJWZi5SajJWZ jZabkJmjl6ammqionaummqiel56VjpWXfoKVe3+Vg4SbiYuflJ2mmqOrmqWmlZ+akJSRh4uXi5GX i5Gdl5mdl5mbjJGai5CUh42ViI6ahI6jjZeikpqjlJuhkJ2fjpuhiZuii52XgoeRe4CXg4OXg4OU f4KXg4aUgoCVg4KXiJCfkJehkZmejpabhoiWgIOUfoObhoudjJmjkp+mjpaii5KfiI2ii5CdiZCd iZCjh4yfg4iahIyhi5KnkZuijJaZgI2Ue4idf4eegIidh4mhi42hiY6fiI2ijIyjjY2mjIunjYyh jIyijY2dgIaXe4CUd3mUd3mNc2+SeHSni4erjouoi42jhoiaeHWXdXOVeXmZfX2mjJCwlpqvlZas kpSriYumhIalh4eegICliIiskJCylZWukZGvi3+qhnqffW2WdGSfgHeniH6ukZSukZSylJawkpWy jZK0kJWzkZe2lJqzlpuvkpezjYiog36heWubdGeadGmdd2ulemefdWKhdV2ab1eaaFOWZE+UY0ya aVGhb1eicFimcFqoc1yicFabalCZaU+eblSmdGOreWild2KmeGOld1+qe2Swg3eugHSofmOlel+d dGqheG6sgn+rgH6wgHWufnOqe2moemiqemqwgHCyhne2iXq3iXqzhneugnOrf3Cqfm+rf3Crfm2q fWuoeWuygnSyiH+0i4K2iIO3iYS0h36wg3qyg3q8jYS4i4O6jIS0hHSsfW2Vho2ZiZGah5CXhI2a h5Cah5CeiZWeiZWejZ2ZiJeZi5SXiZKZhJCXg46VgouVgouRfoKRfoKXgoydh5GdiJSfi5aZi5SX iZKdhpKfiJWii5qii5qhjJeijZmmjZqmjZqllaerm66snqqqm6ennqiimaOjlZ6fkZqhjZahjZah kpuilJ2ikqWikqWmlKenlaiumaqwm6yupa+so66rn6uqnqqnl52ikpeilKKllqWelZ+hl6Kol6Kj kp2jlaOrnaurnrCmmaullKOfjp6hkJ2hkJ2ekJuhkp6lmaWmmqanmq6om6+qoauhl6KVhI6RgIua hIyhi5KelZ2lm6OqnqehlZ6fkJKWh4mbjI6ejpGnmqGll56ejpSai5CXhIuZhoydiZKfjJWhkJ2i kZ6nl6qmlqinlaijkaWbiIyWg4eXhIibiIybiYuaiImXgoKZg4OXiIudjZChjZGfjJChhoyhhoya g4ihiY6fjpumlaKrlJ6nkJqliZCjiI6ah4uZhomei46ah4uahIyfiZGnkZmhi5KZgomWf4eag4id houdiIieiYmeiI2hi5Cmi5GojZSnjJKmi5GijIyjjY2hhImegoeZg4ORe3uReXONdW+if36riIeq iIylg4ebenKXd26bf3qihoCoi5KwkpqvlZmwlpqsi4yqiImliYabgH2hh4amjIuwlJawlJa0kY6s iYelfW+bdGeZeXWff3uojIyskJCylJSylJSzkI6zkI6ykJa2lJq3mZ6ylJmujI2riYusf3OleGuf dGGhdWKmemSjeGKecliablWeaFSXYk6UZE2XaFCeclihdFuodFWseFindVufblSbalOhb1ereGuv e2+qem2neGqleGmnemuvgnWyhHirfWWrfWWieW+le3Kug4Csgn+yfm2uemmoemWneWSrfnKyhHi0 jYa4kYm6kIe0i4K2iXqugnOsgGisgGiqf2uqf2uqe3Owgnm2iIO7jYi3jo24kI64i4Kzhn2ugnC4 jHq3jYO3jYO2iHevgnCbh5Kbh5KZiJWWhpKVhI6VhI6WgJWdh5uXiJqVhpeXh5SWhpKbg5mdhJqa hI6Zg42XhIuWg4meiZefi5mdjpqekJuajZSXi5GXhIuah42hjZadiZKhkJ+hkJ+hkJ+ikaGmlaKo l6Wml6allqWml6ajlaOilqWdkZ+fkpmdkJaekZWekZWfkJehkZmjkp+llKGmlaKsm6isoa+vo7Ks nqeqm6WikpWbjI6jjpqlkJubkJuekp6nkp6nkp6hlKaom66om7KqnbOqmqyllaejl6ajl6aekp6e kp6fmaWlnqqqmqyomauuoaeom6KhkpuZi5SbjJSdjZWekp6lmaWsm6anlqGjjo6diIiaiImjkZKq maOqmaOmkZGbh4edhouag4ibjJSejpalkJurlqKqnbCrnrKqm6qilKKfiZShi5WhjZaijpefjJWX hI2SfXqUfnubh4efi4uhjZafjJWfiJefiJeeg4mdgoiei5GlkZeqkZ6okJ2ijI6dh4mXg4aahoia h4uZhombh4mfi42jjpGfi42ahIeXgoShi5WnkZujkJSfjJChi5CljpSmjpmqkp2rkJaih42jiI6j iI6liZKih5CeiI2Vf4SVgnuVgnuhg4irjZKrjo6hhISef3ObfXCihoani4ujjZKokpe0lp60lp6r jZKoi5CjiIShhoKih4OnjIiukpu0maKzlJuvkJerg4KddXSWdXeaeXqfgoSqjI6vkZasjpSyjZCz jpGvjY6ykJGyjZKzjpSyjImwi4isg3mmfXOieF+hd16leFyhdFibb1Sbb1SdbVCZaU2XblGZb1Of c2Kjd2Wnel6qfWGod1ymdFqmb1umb1undGWyfm+qem2oeWumem6qfnKsg3mrgnirf2usgG2rfm+u gHKrgnmrgnmyg3CsfmuoeF+oeF+qfm+whHWykY62lZK8lpG3kYy0i4Kwh36uf2qsfmmofWmofWmn fWmug2+yhH24i4O3jYe4joi0h3WugG+sfW2zg3OyhH24i4O6jHq3iXiZi5aajJeXjJqSh5WSgo6U g5CVhJSWhpWUhpGUhpGUhIyUhIyahpGeiZWZiJWWhpKZho6diZKfkZ2fkZ2bkJubkJuZiY6VhouU hImWh4ybi5WejZehlaOflKKhkp6ekJuljpmqlJ6llJ6llJ6imaGimaGilqWekqGakZmZkJefkJWe jpSejZehkJqjkp+jkp+nlqOrmqeqna+uobOwoquqm6WjlJabjI6hi5KjjZWbjpWekZemkJemkJeh kqGllqWnna+nna+qm6Wml6Glm6ijmqeimaahl6Welqafl6ejlqqqnbCwoq6un6unmaejlaOekJ6e kJ6jlaGomqawm6qrlqWnkJeii5KeiJCmkJermqqqmainlZadi4ybh4mfi42ijpWei5GjkJmsmaKr nrCsn7KrlqKnkp6liZKmi5Sqkp+qkp+jjpqahpGWf4SUfYKeiIihi4uljZeii5Wfi5afi5ahh4uZ f4Obh4mjjpGnkJeokZmijIyfiYmbf3+egoKehIObgoCeiI2dh4yfiYmijIyahISXgoKeh46slZ2r lZ2jjZWmkJKokpWnkJeulp6vkZmjho2ihImihImih5Cih5CeiJKbhpCeiIuXgoShhImrjpSskpSl i4yhhH2fg3uni5CskJWqjpWskZezlZ22l5+qjpWnjJKmiI2lh4ymiYmni4uukJezlZ2wlJmylZqs iYSffXiad26beG+igIKujI2zlJ6ykp2wjpCujI2ujYuvjoyyjIu0jo2zjI6viIuvh4Oqgn6lf3Cj fm+ieGKhd2Gicleicleiclqfb1efdF6jeGKnenKqfXSrfWirfWiufVuoeFamdV2ldFyjdWGqe2er fWqsfmuoeWuqem2ofXCqfnKrfnKugHSqe2eqe2eugHKzhnewg3Ksf26od1qlc1aleGurfnKyjIe2 kIu7ko66kY23hn+2hH62g3KvfWureWisemmnfWSqf2euhH62jIa7jYS4i4K3g3KseWimd2eoeWmq e3OzhHu3h3e3h3eVh5KZi5aWiZ2ViJuWhpWWhpWUhpGShJCSgoyUg42UhImWh4ybi5qdjJuZiZua i52ejJ+ejJ+bkJudkZ2dkpaZjpKWh4ySg4iVf4ebho2fjJWjkJmimqqfl6efkpmekZeejpSllZqo lKKrlqWmmqajl6Ojl6OhlaGhl6KelZ+ekp6ekp6fkZ+hkqGjkp+llKGlm6ijmqemlqirm66snqyo mqihkZaejpSljpamkJefkJKhkZSokZaljZKijZmlkJunnqumnaqlm6ilm6immaummauim6ehmqaf lKKhlaOnlqaunayunrKsnbCnlainlaillqKilJ+qkp+wmaarmqenlqOljpafiZGfiZSnkZuolqqr mayilp+ajpeejpaikpqnkZuhi5WhkJqrmqWqnqqonairlZ+nkZuljJmnjpuqlJ6okp2ikJGdi4yX fn2Xfn2ihoajh4eljZWnkJeijZmhjJeli4mbgoCdg4enjZGmjpaokZmljpGijI6jgoOjgoOlg4Sn hoehjIydiIiehIahh4ibg36bg36eh46rlJurl56nlJqmjpSnkJWojZaylp+wkpqoi5KihoadgICe g4mfhIubhJGeh5Shho6eg4yhg4uniZGskJKni42ih4Kih4KrjZeukJqylJuylJu0maKzl6GskpSs kpSniYylh4mlh4mmiIusjZevkJqzlZq0lpuvjoijg32heG6ddGqbfoCniYywkZu0lZ+wkI2ri4ir iIaqh4Srh4mqhoiuiIerhoSuhoKrg3+ogHCnf2+oemOneWKmdFqmdFqndGGreGSqemqufm6ugHiw g3qzhnevgnOwf2eremKrelqldFSleFyoe1+sfmmvgGuzenKvd26odWeodWeoeWmre2une2ine2io fXCugnWvg3KsgG+ye1+mcFWhc1yjdV6wg3u6jIS+kpK+kpK+iX23g3e6hHi3gnWyemiveGWoemWo emWuf3e3iH+4jHq0iHezf2muemSmdF6mdF6meGWoemiyg263iHOWgo2diJSZjJ+ajaGaiZaVhJGR g4yMfoeRfYiUf4uXh5GejZefkKKejqGdkKKdkKKmkaKijZ6hiZmii5qbjpWXi5GWh4yVhouXgoeZ g4ieiJKjjZeflKKflKKfkpadkJShjZajkJmolKKqlaOilqWlmaeimaahl6Wmlqinl6qnm6qjl6af maWblaGllqWllqWll6qll6qnl66oma+umaWqlaGfkJeikpqnkZumkJqdkpSdkpSjkJahjZShjZGi jpKilZmmmZ2mmqajl6Oomqiqm6qmnaqjmqehlaOekqGnlqGsm6ammayll6umlqqqmq6nl6qnl6qq m6eun6ummqahlaGolZ6jkJmhkpunmaKmlaWol6eilJ+hkp6llqKnmaWsl6OjjpqikZ6unaqvnqiq maOmkJeljpajkJmlkZqrlJurlJuijZCeiYyag4iag4ihiY6jjJGnlJ2mkpuikpqhkZmijImdh4Sd iIunkpWolJ+mkZ2okZunkJqjhouihImfiI2ii5CmkJeijJSfiYyfiYybhoieiIumiJWylKGwmaGu lp6qkJSojpKmjparlJuwlZunjJKdgHuZfXiSfX2WgICVgIOZhIebhImdhoufhIumi5GojpKli46j jJGjjJGojJ2wlKWzlqWzlqW3m6e2mqawm56rlpmoi42lh4mohoSmg4KmjJCojpKzkJm3lJ23kpWu iYyognqie3SfgoSlh4msjpSvkZasjImqiYeviICqg3umfn2je3qog4Cngn+rhH2uh3+whHiugnWv fWSsemKod1yndVuleWWqfmqsgHKvg3SyiH+ziYC6jH+2iHuyg26uf2qwe1urd1asel2zgGOvgnCy hHO0gHKreGmjc1iiclemdFyod16mc12reGKsemewfmqyf26yf26wfmindV+jdFOfcE+oenK3iH+/ jo3FlJLBjIK7h323g3Szf3C6g2uveWKoeF+remKvgG60hnO0jHm0jHm0hnCrfWiremKnd16wfmqz gG22hnW2hnWVgIydiJSbjJ6bjJ6bh5KXg46SfoyRfYuQf4mRgIuZi5aekJujlaOjlaOflJ+flJ+h lZ6dkZqfjJWfjJWekZeZjJKXhIuVgoiWhIaXhoeXiJCdjZWhkJqhkJqhjZabiJGhi5WljpmilJ2j lZ6jl6Gilp+ilqWlmaeil6qjmauooq6lnqqjl6aflKKmlKehjqKikaGllKOjlKaikqWqlp+nlJ2d kpmhlp2llqKllqKimaGimaGmlZ+hkJqdkJSdkJSfkJWllZqflJ2hlZ6hlaGilqKmmauom66lm6ie laKilqKlmaWrmqqrmqqvma6vma6omayrm6+un66voa+mmZ+jlp2mlZ+llJ6hlaOmmqiol6Wjkp+l kJ6mkZ+qlaaumaqwm6emkZ2ikZ6unaqznqyumaeskZqojZaljpaljpaqlJmnkZaeiI2bhouZg42a hI6hiZamjpumlZ+llJ6mkpulkZqii5Cdhouii5KokZmsl6aumaerlKGqkp+sjpalh46hi5Kljpar lZ+qlJ6mkJWijJGfiYyfiYyliZWrkJuzlZ+0lqGskpanjZGnkJWslZqslpumkJWmg4KffXuUeXSQ dXCWe3idgn6Zg4Cdh4SiiIyli46ijJGhi5CljZWljZWojJqukZ+zlqW0l6aymaawl6Wvl52ulpus jo6jhoafg4OZfX2fg4aliIuojJGwlJmwlJarjpGqg4ajfX+bfn6lh4eujI2vjY6sjIioiISnhn2j gnmie3KhenClf3qjfnmnfnSqgHergnirgnivfmOse2Goe2KnemGsfW2vf2+vf3K0hHe2jIO3jYS4 jH+2iX22h3Kuf2qzfWGye1+wfmO0gme2gni7h324iHiufm6jdFOZakmda1Ghb1WqclqyeWGvfmWz gmmwhG6yhm+zgG2vfWmveV+oc1qseW+4hHq8jYbDlIy+iX+6hnu2hnW0hHS3hG6yf2mzel+udVus f3C2iHm0jH6yiXuwgm+uf22vfWereWOwgnmwgnm3jIm8kY6Wgo2diJSbh5WahpSXg46Wgo2Rg5GR g5GUhpGZi5abjJ6ejqGhkqGjlaOllJ6mlZ+hlZ6ekpuajZSZjJKei5Sei5SXhIuVgoiWgIaZg4ia h5CfjJWbjpKZjJCbiI6Zhoyah42hjZSekZehlJqjlZ6ilJ2hlZ6ilp+llaerm66qnqylmaeikqWi kqWjlaGekJuekZefkpmfkJefkJeikpqjlJumlp6llZ2ilJ+fkZ2jlaOnmaeml6OilJ+bkZeakJae jpafkJedjpedjpedjpejlZ6jlKaqmqylmquil6iol6eol6eml6OnmaWsm6urmqqunrKvn7OuobOu obOnmqGilZullaemlqill6ummayllqWfkZ+hkJqhkJqulqiwmauymqqmjp6mlaKqmaaun66omqio kZuljZelkZemkpmmlJWhjpCjiI6fhIuXhI2diZKeiZWhjJellqKnmaWvl6Kqkp2ljZeii5Wljpmo kp2wm6eynaiym6aym6asjpmniZSikZuol6KwmaiwmaivmZ6nkZahiZGfiJCliZKrkJmylqKzl6Or kJmmi5SqjJSsjpaolZ6lkZqniYmihISXe3iUeHSVdXKaeneZfnOfhHmliIimiYmniZGmiJCmi5Sm i5SliZCnjJKskZqwlZ6ylqKvlJ+wlpewlpeuko2liYSfhICWe3ideniif32jh4yvkpewkpqsjpas hIOnf36hgH6ign+nh4SujYuujI2riYuriIajgH6mfnCed2mheWuje26mem6sgHSshHeuhnirfWin eWSreWireWiqem2ufnCygnSygnSwiHi3jn64kH+3jn67h3iyfm+zgGizgGivfWu4hnS2iIC8joe/ kH24iXesel2fblGaaFWea1imdF6wfmisfmmsfmmsfmmvgGuvgG6wgm+vf3KoeWuvfne2hH28jYy/ kI67jX62iHm0h3q2iHu2h3S0hnOygGiufWSyg3u4iYKziX+yiH6vhHCqf2uufm6zg3O2h4m6i428 kpnBlp2Wh4yVhouUgImSf4iRfoSSf4aVhJSZiJeZh5qbiZ2dkZ2dkZ2bjZullqWml6OjlaGhkp6e kJuajJWWiJGfi5meiZeah5CZho6ah4ufjJCfjY6ejI2UiY2SiIyWhIOWhIOUgoObiYubjpWekZef kZ2ilJ+ilJ+hkp6llqKnmaWnl6qfkKKdkKKekaOekJuXiZWbi5WejZefkJWhkZankp6qlaGnl5+l lZ2hlJqfkpmllJ6nlqGnlqOmlaKflZudkpmejpafkJedjpeekJmhkp6jlaGnl6urm6+mmauom66y na6vmqujlaGjlaGol6eunayunay0o7OwpbCvo6+sl6afi5mjlKarm66smq6rmaynlqOikZ6ikpqh kZmolKWvmquwmauqkqWnkp6qlaGnmaellqWolKKlkJ6nkZmokpqolZmei46fhomfhomag4udho2b iI6ei5GhkJqol6Kvl6KslZ+njY6jiYuejI2jkZKsl6iznq+0n6u0n6uvl5+qkpqllqKqm6e0n7C2 obK0naeokZujjJafiJKliZKrkJmulqGwmaOrkZWli46njY6njY6ijpWjkJaljpGdh4mXe3eWenWX c26Xc26ZeXWlhICliIini4uui5SriJGmiYymiYyfhoSbgoCjhIylho2ni5CrjpSojpCskpSqlY6l kImlg3qffnWbenKefXSif4ivjJWqjZKskJWriIOhfnmaeHWffXqmgH+rhoSmiI2oi5Crh4mmgoSi f32ee3mZdWmWc2efeGinf2+qhHWrhnesgmuofmiqem2re26sfW2vf2+vf3KsfW+ogHOyiXu6jIS/ kYm7iYK6iIC2g2+yf2uufnO2hnq2jYm6kY27jnq2iXWyf2SjcleXalafcl2ofWmugm6yg3Cwgm+u fm6ufm6ugnOwhHWygnewgHWyhHu3iYC3koa6lYi+kX23i3e3h3m0hHe2h3S3iHW0iHSzh3OzhoC3 iYSviH2viH2whG6ugmuvgnW2iHu4iYy8jZC+lJ3Cl6GZiJKXh5GZg4uVf4eSfX+UfoCZg42eiJKX i5GXi5GbjZadjpedjp2ml6ailqKilqKekpuajpebjZaXiZKaiZSZiJKeiZWfi5afkZqhkpuhi5Kd h46ZiJKXh5GXg4aVgIOUf4KXg4aah4uei46ejpafkJeikpqhkZmjlKaomauolq6ejKObi5qdjJui jZmdiJSei5Sei5ShjZSlkZeolZ6olZ6mlp6ikpqbkZWelJellKGnlqOol6eol6enm6WlmaKolKKn kqGfkZqhkpuikpejlJmhlaOonaunm6esoayunayqmaillKGjkp+llKOqmairnauzpbO3prayobCr lKGii5elkqaql6usm6iqmaaomZ6hkZaikpqejpalkJ6qlaOumaenkqGmkJWnkZajkp+mlaKrmqen lqOmkpunlJ2nlJqfjJKdh4ybhouZg4iZg4idiIuhjI6ijpWnlJqqlJmokpemjI2hh4idiIiijY2o kZ6vl6Wwn6yyoa6znaWrlZ2ll56mmZ+wn6yzoq+3n6qslZ+miJCdf4edgoinjJKnjJWwlZ6qkJGj iYuhhoCfhH+fhIuliZCijIyfiYmegn6afnqaeneZeXWZeXelhIKljZKnkJWukJKqjI6miYyni42j h4Kbf3qffXiffXihgH6jg4CfgoKniYmqjYmmiYalgoCif36dfnSbfXOffoKoh4uskJWskJWrjH+m h3qdeW2deW2ifXqmgH6jgoOmhIaqhIOmgH+hfn2ffXuefXSZeG+heG6qgHeviYSyjIe0iHmugnOu f22vgG6vgGuuf2qrf2mrf2mqeXKwf3izh3q8kIO7jIS8jYa6i3Wuf2que2qzgG+0i4S7kYu4kIKz i32whminfV+ablWdcFeoenWzhH+zhnewg3Svf3KufnCqfnKwhHi2hnq3h3uyhnmzh3qzjIC4kYbB kIS8jIC6h3OzgG2wgHC3h3e3h3e3h3ewh362jIOuhHquhHque2WqeGKsfmuzhHK4iInBkJG/lJXB lZaaiZSaiZSfg4ibf4SWe4Kaf4ahjJeijZmZjJKWiZCah42fjJKikaGqmaimlqijlKabkpqZkJeV i5GVi5GWjI2bkZKhkJ+nlqaom66jlqifiZGahIyShI2Uho6ahoaZhISXg4OZhISfi42hjI6fjJKh jZShlJqhlJqlkqamlKemkZ+hjJqfiZGfiZGijJahi5WejpabjJSjkJmolZ6ml6GjlZ6omZ6ikpej lZ6llp+ol6KmlZ+qmqyrm66onaurn66umaqqlaanlqallKOolZuolZuXjpahl5+nm6qqnqysm6ur mqqllKGikZ6ikKOjkaWqmqywobO0pberm66ilJ+ajJehjqKjkaWumaWumaWqmp+ikpehlJefkpaj kJmnlJ2rlqKnkp6mkJWljpSfjpmjkp2olKWvmqunkJ+okaGfkZGekJCdjoyWiIaWi4mViYifiYem kI2nkJerlJurlZqokpejjY2eiIifiYyijI6mjpmrlJ6vmqa0n6uym6aslqGolZumkpmsm6ayoau6 oa6slKGsi4ybenuWfYCehIiihIysjpamjIuiiIejgnmjgnmjg4ClhIKlh4mlh4mihoafg4Oeg3+d gn6df4Klh4mvlJ2wlZ6ukpmrkJaniZGoi5KsiH+lgHifeW6feW6ZdWqbeG2ie3SrhH2uiIOog36l goChfn2efXCaeW2XdXOhfnuriYuujI2ujYemhn+ie3Cdd2uXdXCZd3KfeW+mf3Wrfneoe3Soenum eHmmeXKhdG2hdG+ugHu0jIy4kJC2joeviIC0gG+zf26uf2iuf2irfWiuf2qofmqme2iqgnSyiXuz joa3kom/joO2hnqwfWmwfWm2iIO8jom+loy4kYe3jHiug2+meGOjdWGnfniwh4C4hHWyfm+uemes eWWufnOygnewgnm2h360iHmyhne0h3q/kYTFkIbBjIK4hnS0gnC2gni4hHq6hnu7h322iXW4jHi2 i3evhHCrel+nd1yreGSzf2u2hIDDkY2+lI28koybjZmXiZWahI6Zg42dhpCdhpCdjJuejZ2Uh42O goiRhIiXi46ikKOmlKenkaqjjaabkZeZjpWXi5GajZSVkJGblpeilq+mmrOrmqemlaKfhIufhIub iJGbiJGbhouahImXiYmWiIiei5GjkJajkJahjZSbkJudkZ2lkKGlkKGjjJmdhpKZiY6ejpSnkp6o lJ+ikZ6hkJ2ml6Oomqammqinm6qunrCunrComqiml6aimaOhl6KomauunrCsnbCrm6+lmaWjl6Os nqqrnaillJ6hkJqah5CfjJWilKKqm6qrnairnainlqGfjpmejZqfjpuikKOvnbCwoq6snqqmkpue i5Sii5KmjpajlJunl5+jlJmhkZafkJWikpejkJamkpmnlJ2olZ6njJKliZCeiZWijZmnkJ2vl6Wo maGjlJunkpWmkZShjZGbiIydjZKai5Cfi4ulkJCnkJWqkpeqkperlJmii5ChiY6ehIifhomhhoyl iZCulaK2naqwl6WulaKqjZKrjpSokpq0nqa2n6qul6Kni4OZfXWUeXKVenOdf3+miIiihoaihoaf hH+dgn2jgoOlg4SmhIamhIaeiIieiIihh4ahh4ang4uuiZGzl6Ozl6O0lqGwkp2qi5Kmh46rhoOn gn+hf3SaeW6acmiddGqbeG2hfXKrhoCrhoCqgoCje3qdenmbeXiXd26aeXCnf36vh4aoiIKignuh enCZc2mUcGWWc2iac2OddWWjeGmmemuleHOjd3KmdW+jc22bc22ieXOsh4S2kI26kIm0i4SshHeu hnivg3Svg3SogG6ogG6ugnCne2qoe2+vgnWwi4a3kYy8jom6jIe2hnWwgHCzhoC7jYi/lY64joi6 jH23iXqugnOrf3Cwgnm3iH+2h2+yg2uue2WvfWevgG6wgm+vf3Kzg3WuhnWshHSwg3u8joe/kYS8 joK6iXu4iHq2hnqwgHW2gnW6hnmyg260hnC3i3KwhGuvfWKsel+reGSwfWmzg3jCkYbBkYm+joed jJmaiZaXh5aWhpWXg46bh5KZi5aXiZWVgouQfYaVf4edh46ijZ6nkqOllqKjlaGbkZWZjpKai5KX iJCbkJmjl6Gnn7Onn7OrmqehkJ2ahImbhouWjJKbkZejjZehi5WhiZGhiZGdjJadjJadjpqajJee kJ6ekJ6fkZ+fkZ+iiZafh5SZiJWikZ6ol6enlqallaeikqWjl6anm6qunrCsna+zobawnrOmmaui laejl6Ommqaqm6qsnqyvoa+rnauqnaOsn6awn6qyoaurlKGeh5SRgoeVhoudiJankqGfmaWhmqau laKiiZahi5KfiZGhjJevmqaznqywm6qljpadh46dh4yijJGhkZmmlp6jlpqekZWijpWmkpmokpen kZaulp6qkpqliZKliZKjh5WmiZeokZ6okZ6rl56olZuqlJuokpqhjZGdiY2bjI6bjI6njJKmi5Gl jZKljZKqlJaslpmrkJajiI6dh4mdh4mXgoSeiIuqjJmzlaKykqKujp6vjoysjImrjZe3maO3n6qw maOwjI6mgoSafneWenOZfnmjiIOlh4emiIiiiIejiYiniYmqjIyni4eliISihoiliIuli4yiiImn iYyoi42zlZ+4mqW6mqWykp2zjJSwiZGniYylh4mhhH2dgHmhenCeeG6Wc2qeenKqgn6qgn6shn6o gnqheXWed3OZdWqWc2ibeXSffXilgHijf3eieXOXb2mXb2WVbWOWa1iZbluecmGhdGOjd2+meXKl d3KbbmmWcGmadG2ogH+3jo22lIuwjoauhHqsg3mzhHu0hn2zhn6zhn6yjH2lf3CmeWqqfW6uh3+y i4O6jIe6jIe6hne0gHK2h3+/kIi8kZG6jo64i362iHuwiHqvh3mvh3mvh3m0iXOziHKzhG+zhG+w hHOyhnSwhG6ugmuofWene2WsfW+zg3W7jIO+joa8jn+7jX62hnW0hHS0gm62g2+0gG27h3O3i3m3 i3myhm2rf2eyhne0iHmyi4O3kIi7jIu3iIediZKfjJWaiZSWhpCUho6XiZKZiZGUhIyReoeQeYaU foOZg4ibjZafkZqjkp+llKGbkZWXjZGai5CXiI2jkp+rmqeqn7Cnna6ol6KdjJaahImdh4yekZei lZuolZ6lkZqjjJSljZWdjZWbjJSbjpWbjpWejJ+hjqKhjqKfjaGfi5mhjJqbkJujl6Ovmquumaqn lqOikZ6nmaernaurnrCsn7Kyo7KworCmmaufkqWolJ+rlqKsl6Ovmqasoaqjl6Gilp+nm6Wsnqys nqyikpqVho2SfX+UfoCdg4eojpKekp6hlaGnkp6eiZWai42Vhoibho2mkJeunrKrm6+nlJqdiZCe iJChi5KhkJqmlZ+ll5ubjpKhkZamlpunlJqqlp2slqGnkZuiiZahiJWliZWliZWmkJqqlJ6vlqOs lKGslaKqkp+nkZGijIydh4yijJGskZqqjpeljpGmkJKulpuulpuukZGliIibhoiXgoSUfYKZgoei hoiukZSvkZmrjZWrjY2miIioi5eylKGzl6OwlaGzkJunhJCUfoCRe36bf3umiYaqjouliYali4yq kJGylZqylZqnjY6li4ymi5GnjJKmjJCjiY2oi5KniZGylJ64mqW0maWwlaGyi42viIumi4ejiISi hn6hhH2je3iheXWbeXeee3mjfnmngn2rg3Org3OhfWebeGKWcmGUb16Wc2iZdWqfeXKeeHCfeGqX cGOdal6ZZ1uSZVSUZ1Wfbl2mdGOldWineGqoeWuhcmSXcGOac2Wleni2i4i+lo63kIiwhHisgHSy hH2zhn6wh364joa6lISviXqqeGSmdGGoenKwgnm4iYS4iYS4iH23h3u0h367jYS+kIu7jYi7h320 gHeyhnmzh3qzh3qzh3q0iHu0iHu7i327i326jH26jH2zhG2vgGmofmWnfWSremKufWS3g3m7h327 jX68jn+4iHivf2+zgGqvfWeve2W4hG64iYC7jIO2iXqzh3i4lIu4lIu2kpG3lJK7kI24jYuXiJCW h46XiI2XiI2XiJCai5KZiZGVho2OeYCNeH+SfX+bhoidjJmdjJmdi56ejJ+ajo2ViYiXhIibiIyl kJ6qlaOlmaWflJ+ljpGeiIuWg4eah4uhkZallZqilZuilZuijJaahI6Wg4mVgoiViI6WiZCbi5Wd jJaeiZqdiJmhiZmljZ2ikZ6ol6Wsl6ilkKGjjZWhi5KqkqWulqiom66uobOwoquml6GikpqfkJel kZeqlp2umqOsmaKrm6OomaGmlaKnlqOonaulmaediY2UgISQeniRe3mZe36ihIeikpqikpqekpuZ jZaUiY2Ng4eWgo2fi5aml6aqm6qll5uajZGdiZChjZSmlZ+ol6KikpeejpSfjJWjkJmqmaOol6Kv lqOokJ2ii5WjjJaijJSijJSmi5SskZqolKWsl6iwl6WvlqOmkZSijZCfiZGljpaukpmukpmqlJal jpGslZ+wmaOukZSliIuehIaehIaagIKZf4CehoCokIuukZSqjZCojIyihoamiJWoi5eskZqskZqu kZaojJGbgIybgIyfg4ani42ujpmvkJqoi5erjZq0maWylqKqjpWukpmrkJmqjpelh4mhg4aliI2l iI2sjJu2laW2mqOylp+vjY6nhoehhISfg4Onhn2lg3qmgHuhe3eff3uign6mgHuog36qhn2ng3qn fWmdc1+WalqQZFSQZVWWa1uZbl2ab16XdF6XdF6abViXalaVaFaWaVebbWKhcmeldWimd2mleWWh dWKab2GdcmOhdWSugnC6koi2joSuhHqqgHeuhHqyiH6rh3qyjYC3kX2zjXmvfmOmdVufdGGmemeu fXW0g3u4iH23h3uzh3q4jH+7jIO6i4K0hHewgHOugmuwhG6yiXmwiHi0h3q6jH++joe/kIi8joa7 jYS3iHWsfmusfmeqe2Sue2WsemSzf3O4hHi2hni6iXu7iHe4hnS2gHKyfW6weWeze2m4h4O7iYa3 jou7ko64lJa4lJa8l5q+mZu/lpK4kIwADQEAAAMAAAABAQAAAAEBAAMAAAABAQAAAAECAAMAAAAD AAMAqgEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAACAAMAsAESAAMAAAABAAEAAAEVAAMA AAABAAMAAAEWAAMAAAABAKoAAAEXAAQAAAACAAMAuAEcAAMAAAABAAEAAAFTAAMAAAADAAMAwIdz AAcAABDoAAMAxgAAAAAACAAIAAgAAAAIAAH+CAAB/gAAAQIAAAEAAQABAAAQ6GFwcGwCAAAAbW50 clJHQiBYWVogB9YABAAeABAAAgAkYWNzcEFQUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPbW AAEAAAAA0y1hcHBs8Km3nXI3UvdB2LuwZ9wBHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAOclhZWgAAASwAAAAUZ1hZWgAAAUAAAAAUYlhZWgAAAVQAAAAUd3RwdAAAAWgAAAAUY2hhZAAA AXwAAAAsclRSQwAAAagAAAAOZ1RSQwAAAbgAAAAOYlRSQwAAAcgAAAAOdmNndAAAAdgAAAYSbmRp bgAAB+wAAAY+ZGVzYwAADiwAAABkZHNjbQAADpAAAAH+bW1vZAAAEJAAAAAoY3BydAAAELgAAAAt WFlaIAAAAAAAAF1MAAA01QAAB9tYWVogAAAAAAAAdAUAALP7AAAiflhZWiAAAAAAAAAlhQAAF0sA AKjMWFlaIAAAAAAAAPNSAAEAAAABFs9zZjMyAAAAAAABDEIAAAXe///zJgAAB5IAAP2R///7ov// /aMAAAPcAADAbGN1cnYAAAAAAAAAAQHNAABjdXJ2AAAAAAAAAAEBzQAAY3VydgAAAAAAAAABAc0A AHZjZ3QAAAAAAAAAAAADAQAAAgAAAgQC9wQFBQUGCgcFCAsJCAoNCwgMCg0QDg0PDxAOERASEBMS FBIVFBYXFxgYFhkaGhsbGhwdHR0eHyAeISIiIiMjJCUlJCYmJycpJyopKyosLC0tLi8vLzAxMjEz NDQ0NTQ2Njc3OTo6Ojs7PD09PT49Pz9BQkJCQ0NEQ0VFRkZISElJSklLTExMTk1PT1BQUVJSU1RU VVVWV1dXWFhaWltbXFxdXl5eYGBhYWJiY2RkZGVlZ2doaGlpamlra2xtbW1vcHBwcXJycnNydHR1 dHZ2eHh5eHp6e3t8fH19fn5/f4CAgYGCg4SDhYSGhoeHiIiJiYqKi4uMjI2Njo6Pj5CQkZGSkpOT lJSVlZaWl5eYmJmZmpqbm5ycnZ2enp+foKChoaKio6OkpKWlpiWmpqenqKipqaqqq6usrK2trq6v r7CwsbGysrOztLS1NLW1tra3t7i4ubm6uru7vLy9vb6+v7/AP8DAwcHCwsPDxMTFxcbGx8fIyMlI ycnKysvLzMzNzc5Nzs7Pz9DQ0dHSUdLS09PU1NXV1lXW1tfX2NjZ2dpZ2trb29zc3d3eXd7e39/g 4OHh4uLjYuPj5OTl5ebm5+foZ+jo6enq6uvr7Ozt7e5t7u7v7/Dw8fHy8vNy8/P09PX19vb39/j4 +fn6efr6+/v8/P39/v7/fv//AAACBAL3A3AEBAUJBgQHCggHCQwKBwsJDA8NDA4ODw0QDxEPEhET ERQTFRYWFxcVGBkZGhoZGxwcHB0eHh0fICAgISEiIyMiJSUmJicmKCcpKCoqKyssLS0tLzAwLzEy MjIzMjQ0NTU3ODg4OTk6Ozs7PDs9PT9AQEBBQUJBQ0NEREVFR0ZIR0lKSkpLSkxMTk5PUFBRUVFS UlRVVVVWVldXWFhZWVpbXFtdXV5eX19gYWFhY2NkZGVlZmZnZmhoaWpqamxtbW1ub29vcG9xcXJx c3N0dHV0dnZ3d3l5enp7e3x8fX1+fn9/gICBgIKCg4OEhIWFhoaHh4iIiYmKiouLjIyNjY6Oj4+Q kJGRkpKTk5SUlZWWlpeXmJiZmZqam5ucnJ2dnp6fn6CgoaGioqOjpKSlpaamp6eoqKmpqqqrKqur rKytra6ur6+wsLGxsrKzs7S0tbW2tre3uDe4uLm5urq7u7y8vb2+vr+/wMDBwcLCw8PExMXFxkXG xsfHyMjJycrKy8vMzM1Mzc3Ozs/P0NDR0dLS01LT09TU1dXW1tfX2NjZ2dpZ2trb29zc3d3e3t/f 4F/g4OHh4uLj4+Tk5eXm5udm5+fo6Onp6urr6+zs7e3u7u9u7+/w8PHx8vLz8/T09fX29vd29/f4 +Pn5+vr7+/z8/f3+/v9+//8AAAGCAmUDQAQcBPEFuwaJB1wIMQkHCdUKoQtyDEUNFA3jDrUPhBBR ER4R7hK5E4cUWBUnFfMWvReHGFEZGhngGqobdRw/HQUdyh6PH1UgHSDjIaoibyMwI/ckuSV6Jjwm /ifDKIQpRSoMKswrjSxNLQgtyS6IL0UwBDDFMYUyQzMVM+00wjWWNmw3QDgROOM5tTqHO1k8Lj0I Pdw+sj+JQF5BMEIAQtJDqER9RUlGHUbvR8RImUlrSjpLEEveTK1Nf05MTxlP6lC4UYNST1MbU+dU sVV5VkZXC1fRWJdZYFoqWvJbtlx4XTxeAl7FX4hgUGERYc5ii2NNZA1k02WoZpZnjWiLaXtqdWts bHBtYG5bb0VwOHEsciJzE3QFdPh153bTd7x4pXmUeoN7b3xOfTd+JH8Mf++A1IG8gp+DfoRohUiG KocPh/KI04m0ipaLeYxcjTuOHY79j9yQvpGhkoeTb5RPlS+WEpb4l96YvpmdmoCbZ5xRnTqeHp8D n/Sg9KH6ovuj9qT0pfCm76foqOCp46rdq9SszK3Err6vuLCzsa+yrLOqtKm1qbart664xrnMutO7 2rznvgG/FcAwwUnCbMOVxMHF78cfyFDJiMrSzCPNds7M0CnRmtMS1JHWJtfD2WjbJ90C3ujg7uMZ 5Wbn1uqP7Y/xDvVt+x7//wAAbmRpbgAAAAAAAAY2AACXOAAAVsIAAFQSAACKMAAAJ6sAABaoAABQ DQAAVDkAAiFHAAIR6wABRR4AAwEAAAIAAAABAAMACwAWACUANwBNAGUAgQCfAMEA5QELATUBYQGQ AcEB9QIrAmQCnwLcAxwDXwOjA+oENAR/BM0FHQVGBXAFxAYbBnQGzwctB4wH7ggfCFIIuAkgCYoJ 9gpkCtULRwuBC7wMMgyrDSYNog4hDmEOoQ8kD6kQLxC4EUMRzxIWEl0S7hOAFBUUqxVDFZAV3RZ5 FxcXthhYGKoY/BmhGkga8RucG/McSRz4HageWx8PH2ofxSB9ITch8iKwIw8jbyQwJPMltyZ+J0Yn qygQKNwpqSp5K0osHCzxLVwtxy6gL3kwVTEzMhIy8zPVNEc0uTWgNoc3cThcOUk6ODsoPBo9Dj4D Pn8++z/0QO5B6kLoQ+hE6UXsRvFH90j/SglLFEwhTTBOQE9SUGZRe1KSU6tUxVXhVv9YHlk/WmFb hVyrXdJe+2AlYVJif2TgZhJnR2h8abRq7WwnbWRuom/hcSJyZXOpdO92NnjJehV7Y3yyfgN/VYCp gf+DVoSvhgmIwoohi4GM445Hj6yREpJ7k+SWvJgrmZubDJx/n2qg4aJao9Wmz6hOqc6rUa5ar+Cx abLytgu3mbkpurq94b93wQ7Cp8RBx3vJGcq6zFvN/9FK0vHUm9ZF1/HZn9z/3rHgZOIZ49DnQej8 6rnsdu427/fxufVC9wj40Pqa/GX//wAAAAEAAwALACUANwBNAGUAgQCfAMEA5QELATUBYQGQAcEB 9QIrAmQCnwLcAxwDXwOjA+oENAR/BM0FHQVwBcQGGwZ0Bs8HLQdcB4wH7ghSCLgJIAmKCfYKZArV Cw4LRwu8DDIMqw0mDaIOIQ5hDqEPJA+pEC8QuBFDEc8SFhJdEu4TgBQVFKsVQxXdFisWeRcXF7YY WBj8GaEZ9BpIGvEbnBxJHPgdUB2oHlsfDx/FIH0hNyHyIlEisCNvJDAk8yW3Jn4m4idGKBAo3Cmp KnkrSiwcLPEtXC3HLqAveTBVMTMyEjLzM9U0uTWgNoc3cTfmOFw5STo4Oyg8Gj0OPgM++z/0QO5B 6kLoQ+hE6UXsRvFH90j/SglLFEwhTTBOQE9SUGZRe1KSU6tUxVXhVv9YHlk/WmFbhVyrXdJe+2Al YVJif2OvZOBmEmdHaHxptGrtbCdtZG/hcSJyZXOpdO92Nnd/eMl6FXtjfLJ+A39Vgf+DVoSvhgmH ZYjCiiGLgYzjjkePrJESknuT5Ja8mCuZm5sMnH+d9J9qolqj1aVRps+oTqnOrNSuWq/gsWmy8rR+ tgu5Kbq6vE294b93wQ7EQcXdx3vJGcq6zFvN/9FK0vHUm9ZF1/HZn9tO3P/gZOIZ49DliOdB6Pzq uex27/fxufN89UL3CPjQ+pr8Zf//AAAAAAAGABIAIwA5AFUAdQCZAMEA7gEgAVQBjgHLAgsCUQKb AucDOQOMA+QEQAShBQYFbwXdBkwGvwc4B7UINgi5CUAJygpdCu4LiAwlDMQNZQ4PDrUPZRAXENMR ixJNEw8T0hSeFVkWDxbPF40YURkaGeUasxuEHFUdIh37HtAfqyCMIXIiVyM5JCwlGCYKJvgn7ijr KeIq6SvpLPMuAC8JMB8xNjJPM2o0kTWyNuE4ETlBOnA7qDzrPi0/bkC7Qf9DUkSzRglHZ0i0SdVK 7Uv6TRxONE9RUGFRilKoU91VBlYxV1tYkVnCWvhcNl15XsNgA2FGYo9j7WU7ZoZn42lEaptr/21r bsdwOHGkcw10gnX4d25443pce959Wn7hgGWB54NmhOSGdYgDiYyLEoynjkCP1JFjku6Uf5Yfl7CZ JJqOm/qdep7xoHOh66NwpP+mdqf+qY6rH6ywrkGv07Fksva0h7YZt6q5O7rLvFu9zL9ZwOjCd8QE xXfG+8hyye/LaszVzj/PqNEP0nbT3NVB1p7X59kv2nbbvN0B3kXfeeCl4c/i+eQc5THmROdX6Gjp cepw62PsU+1A7i3vDO/r8LzxjvJW8xvz2/SV9U719vaf90L32/h0+QX5h/oK+o36+vtl+8/8OvyV /OT9NP2D/dP+I/6J/vT/X//J//8AAGRlc2MAAAAAAAAACkNvbG9yIExDRAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAABtbHVjAAAAAAAAAA8AAAAMaXRJVAAAABQAAADEZnJGUgAAAEIAAADYbmJOTwAAABIA AAEaZXNFUwAAABIAAAEsZmlGSQAAABAAAAE+cHRQVAAAABgAAAFOemhUVwAAAA4AAAFmamFKUAAA AA4AAAF0bmxOTAAAABYAAAGCZGVERQAAABAAAAGYa29LUgAAAAwAAAGoZW5VUwAAABIAAAG0c3ZT RQAAABAAAAHGZGFESwAAABwAAAHWemhDTgAAAAwAAAHyAEwAQwBEACAAYwBvAGwAbwByAGkAyQBj AHIAYQBuACAA4AAgAGMAcgBpAHMAdABhAHUAeAAgAGwAaQBxAHUAaQBkAGUAcwAgAGMAbwB1AGwA ZQB1AHIARgBhAHIAZwBlAC0ATABDAEQATABDAEQAIABjAG8AbABvAHIAVgDkAHIAaQAtAEwAQwBE AEwAQwBEACAAYwBvAGwAbwByAGkAZABvX2mCcm2yZnaYb3k6VmgwqzDpMPwAIABMAEMARABLAGwA ZQB1AHIAZQBuAC0ATABDAEQARgBhAHIAYgAtAEwAQwBEzuy37AAgAEwAQwBEAEMAbwBsAG8AcgAg AEwAQwBEAEYA5AByAGcALQBMAEMARABMAEMARAAtAGYAYQByAHYAZQBzAGsA5gByAG1faYJyACAA TABDAEQAAG1tb2QAAAAAAAAGEAAAnFYAAAAAv/h7gAAAAAAAAAAAAAAAAAAAAAB0ZXh0AAAAAENv cHlyaWdodCBBcHBsZSBDb21wdXRlciwgSW5jLiwgMjAwNQAAAAA= item7.X-ABRELATEDNAMES:assistant item7.X-ABLabel:_$!!$_ item8.X-ABRELATEDNAMES;type=pref:spouse item8.X-ABLabel:_$!!$_ item9.X-ABRELATEDNAMES:father item9.X-ABLabel:_$!!$_ item10.X-ABRELATEDNAMES:Custom relation item10.X-ABLabel:CustomRelLabel X-ABUID:3D833EB4-BFFC-41F0-9193-96695487C45E\:ABPerson END:VCARD tests/auto/versit/qversit/testdata_vcf/AAB4-SingleNonAscii.vcf000066400000000000000000000011201233466112000246520ustar00rootroot00000000000000BEGIN:VCARD VERSION:3.0 N:;The only first name;;; FN:The only first name ORG:Company; item1.ADR;type=WORK;type=pref:;;000;;;; item1.X-ABADR:us item2.X-ABRELATEDNAMES;type=pref:9DE 'D,(1 item2.X-ABLabel:_$!<Assistant>!$_ X-ABUID:C25AB89B-020A-418C-8F48-047834773FC7\:ABPerson END:VCARD tests/auto/versit/qversit/testdata_vcf/AAB4-SingleNonAsciiWithPhoto.vcf000066400000000000000000021021161233466112000265310ustar00rootroot00000000000000BEGIN:VCARD VERSION:3.0 N:;The only first name;;; FN:The only first name ORG:Company; item1.ADR;type=WORK;type=pref:;;000;;;; item1.X-ABADR:us PHOTO;BASE64: TU0AKgADAAgXDgATCgAZCgAaCgAVCwAPBwACAgACAgAHAwAJBQAWBgAWBgAOAwASBwARDgARDgAe DwAeDwAWCwASBwAHAwAFAQAIBAAPCgAHCgAGCAABAgABAgAEAwAFBAATBQAaCwAEBwABAwAAAgAA AwACAwABAQAAAQAAAQAAAgABAwAABAAAAwABAwABAwABAwABBAAABAAAAwABBAAAAwAAAQABAwAA AwAABAACAwACAwADBAADBAABBAABAwACBAABBAAABAAAAwAABAAABAABAwABAwAAAAAAAAABBAAD BQABBAAABAAAAwADBgAEBwABAwACAwACAwAEBQADBAADBQADBQABBAABBAADBQABBAAEAwAGBAAF AwAGAwACAwADBAADBQADBgADAwADAwABAQADAwABBQABBAACAwACAwADAwAEBAAAAgAAAwAABAAB BQABBQAAAwAAAgAAAwACAwADBAABAwABAgAAAwAAAwAAAgABBAADAwADAwAAAgAAAwABAwAAAgAB AQABAgAAAgAAAgAAAgAAAgAAAwAAAwAAAQABAwAAAwAAAgABBQACBgABAwABBAADBgACBAAAAwAA AwABBAABAwACAwACAwAEAwADAQAAAgACBAACAgACAgABAwADBAAABAAABAABBAACBAABAwACAwAB BAAAAwAAAgABAwABAwABAwAAAQAAAgABAwABAwAAAgAAAgAAAQAAAQABAwABAwABAwAAAgABAgAC AwAEAwAFBAADBQAAAwABAwABBAABAwABAwACAwADBAADAwADBAADBQACBAADBAACAwADBAACAwAD AgAEAwAEBwADBgABBAAABAACBAABBAACBAACBAABBAACBAAEAgAEAwACAwACAwAFAwAHBAAOCAAO CAADBAADBAABBAABBAAEBAADAwAEBAAEBAAJAgAKAwAFBwAFBwABBQAABAAAAwABBAADBAAEBQAB BQABBQADBAAEBgADBAACAwAEAgAEAgAEAgAGAwAEBAAEBAAsFgAqFQApGQAiEwAeFQISCgAFBAAK CQAeDwAkFAArFAArFAAsGQA1IgEwIQAwIQAuGwAxHgArGwAjFAAZCAAXBwAjDwAqFgAXEQAVDwAF BQADAwAJBgATDwAnEwAoFAASDgEKBgAAAgAAAgABAQACAgAAAQAAAgABBAACBAABAwAAAQABAwAC AwABAwACAwADBAADBAAJBwANCgAICgAFBwABBAABAwAKBgARDAAWEQARDAAIBAALBwALCgAGBQAA AwAAAwAFAgAMCAAMBwAKBQAEBAAEBAASAwAdCwEcCwAaCgAWBgAYBwANBwAJBAAIBgAKBwARDAAP CgAPBgAPBgACAwADBAAFBQAFBQAHBQAOCwAICAAEBAABBAACBgADBwADBwAEBQAFBgAHAwAHAwAD BwABBQABBQABBAAEBgADBAABBAABBAAAAwAABAAFBwADBAACAgADAwACAwADBAAEAwAEAwADBAAD BAABAwABAwABAwAEBQABBAAABAADBAABAwAFBAAFBAABBAAAAwAAAgABAwABAwACBAABBAACBAAD BAABAwAEBQADBAABBAABBAAEBwADBQAAAwAAAwACBAABAwADBAACAwADBAADBAACAwADBAADBAAC AwACAwABAwAAAwAAAwAABAABBAACAgADAwABBAABAwABAwACBAAEAwAEAwACAwABAwACAwACAwAC AwACAwABBAAABAAAAwAABAABAwAAAgABAQABAgADBAADBAAEBAADAwABBAACBAAABAAAAwACAgAD AwABBAABBAABBAABBAADBAADBAADBAADBAACAwACAwAEBAAEBAADBAADBAAEBQADBAADBAAEBQAE BAAEBAAEAgAFAwAFAwAFAwADBAADBAAEBgAEBQACAwABAgADBAAEBgAEBwADBQAEBAAEBAAEBAAF BQAGBwAEBgAEBAAGBgADBAADBAADBwACBgADBwAECgAKCAAIBwAEBwABBAAFBAAFBAACAwACAwAD BAACAwAwHQAzIAAyHwAwHQAtFQAmDwAbDwAWCwApEgAzGwEyGQAxGAA6HgA8IAAzIwE1JQMsHAAs HAAtHgAoGQApGAAjEwAyHwAzIAAuIQAsHwAODAALCgAVCgAjFwI0JQMvIAAgHQQUEQACBAADBgAC AwACAwABBAABBAABAwABAwADBAABAwABAwABAgADAgAEAwAJAwANBgAeEAAhEwAJCwAEBwAXAwAh CgAuGwAwHQArGgQcDQAWEQAXEwAUEgEKCQAFBwAGCAAcCQAsFgMnFwAjFAAWDwAZEwA0FQA8GwE7 GgRBIAkzGgUvFgMdDwAdDwAeEwAlGQAmHAAiGAAhEwAhEwASDQAQCwAbDQAiEwIuGgArFwASFAAL DQAHCgALDwAeFAAbEQAnGAAnGAAjFQAhEwAPCgAMCAADCAAABAABAwADBAAPBwAPBwAFBAAHBQAM CAAMCAAKBwAMCAAVDQIPBwAEBAACAgACBAACBAADBAADBAADBAACAwAEBQAEBQABAwABBAAEBQAD BAAABAAABAABBAAABAAOAwAOAwANBwAKBAABBQACBgADBQACBAADBQADBgAABAAAAwABAwADBgAB BAABAwABBAABBAACAwADBAABBgAABAAAAwAAAwADBQADBQABAwABAwAEBQABAwAGAwAGAwADAwAE BAADAwAEBAADBAACAwACAwADBAAEBAAEBAADBAADBAABAwACAwADBAAEBQABBAABBAACBAACBAAE BAAEBAAGBgAEBAADBAADBAADBAACAwADBAAEBQADBQABAwACAwADBAADBAADBAADBgADBQABBAAB BAAHBwAEBAAGBAAFBAAGAwAGAwAEBAADAwADBAAEBQAFBAAHBQAFBAAGBAADAwAGBgAGBgAFBQAH BQAFBAACAwAEBQAEBQAFBwAEBgAFBwAIBQAHBAAEBQAEBQADBQAEBwAEBAAFBQACBgADBwADBQAD BgAECAADBwAEBQADBAADAwADAwAEAwADAgADAwADAwAjGQAkGgAnHQApHwAsGwAoFwAsFgAqFQAt FgAvFwA0GwA4HgAtIAAsHwAqHwEjGAAjFAAnFwAyGwA6IgE5HQA4HAA6JQA8JwA+KgA7JwAxFwAn DwAyFwBEJwVGMQE/KwA4KQcmGAAHBwAEBAABAwABAgABBAABBAABAgABAgABAwACAwAAAQABAwAE AgAGAwASBAAcDQAuFQAwFgAcFgAYEwApDwA4GwJAKABBKQA1IQAzHwAyGAAxFwAfGAEVDwAYCgAh EgA/HgBIJgE3IQAtGAAcHwAcHwA5GwBAIgBNIwFXLAc6IActFQAgEgAnGAA1JgA6KgE3IwI1IgEv HAAvHAAsGAAqFgAnFAAsGAA3HQA3HQAwIQA0JQEvFwAvFwA3GAA7HABFIwBEIgArGwAmFgAfFQAm GwALDwIDBwAIBwARDwArGQEtGwIcCwAcCwAYDwAlGgMwIgQxIwQdEwIaEAAGBwAEBQABBAABAwAC AwABAwADBAADBAACAwACAwABAwABBAADBAADBAACAQACAQADBQAFCAAsFAAnDwAjDAAeCAATCwEQ CQAEBgAGBwAOCwAJBwAIAwAOCAAmDwAmDwAUDwANCAAJCAAHBwAMBQAPCAAEBwABBAAAAwAAAwAE BAADAwAFBAAIBwAMCgEHBgAEBAADAwAEAQAEAQAHBAAIBQAFCAADBgADBAADBAADAwAFBQACBgAA BAABBAABBQAEBgADBAABBAADBQADBAACAwAEBAAEBAAEBwADBQADBAAEBQAEBQAEBgAGBwAGBwAG AgAHAwAKBwAJBwAHAwAKBwAGBgAEBAAGBAAGBAAMBwAKBQAJAwAJAwAFAwAFAwAHBAAGAwADBAAD BAAHBwAHBgAIBgAIBgAGAwAHBAAFBwAEBQADAwAEBAAEAgADAQAHBAAJBgAEAwAEAwAEAwAHBQAE CAADBwAFBwAEBwAEBQAEBQAEBwAEBwAEBQAEBQABAwABAwABBAABBAAEBAAEBAACAgABAQAFAwAH BAAqHAAiFQAiFwAmGwAtHAAqGQAyGwA1HgA0GQA1GgA0IgA0IgApIgAkHQAjDwAgDQAkDwAoEgA3 FwA8HAA+IwA9IgBAJgBFKgBDJwE8IQA9HAA+HQBAIABIJwBKLQBNLwE0JgcjFgAHCgAFCQABBAAB BAABBAAAAwADAQADAgACAwABAwABBAADBQAEAgAFAwAVBQAaCgA0GwA4HgAwHQAtGgA6IgBEKwFN LwBNLwBAKgA/KQA8IwA8IwApGAMgEAAlFgAvHwBKJwBULwJJKwFDJQArIwMhGQAqEAA/IwRQKgBT LAE6HwQvFgAvHgAzIgM5KQM3JwI3HAA9IgA7IQA1HAA4IQQwGgArGgAuHQA3HgA+JQA7JAA8JQA5 IgA1HwA+HwBJKQJHKQBFJwApHwAkGgApHgAqHwASFAEHCAAqFgA8JgNEKwE/JwAvHwIrGwAjFgAt HwI3IwIzIAAqIAIjGQAMCAAHBAACBAABBAABBAABAwAAAwABBAABAwABAwADAwADAwACAwABAgAF BgAGBwASDAAdFgA4JAE1IgAzHwMxHQEmFwAfEQAZCwAbDQAgDwAeDQAiCwAyGQM+JQA6IQAnHAAm GwAXEAAPCQAUDgAZEwAKCgAEBQABBgACBwAVDgATDAAeDgAmFQAnGggdEQEHCAAEBAAOAQAYCQEi DwMgDgEREgMGBwABBwABBwACBAADBgABBwAABAABBAADBQAEBgAEBQAEBAAEBAAEBQAEBQAEBQAE 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 GAAyGAA3HAFBJwQ+JAMhEwAiFAAoFwEuHQQdEwIVCwAhEAAyIAhQLgNRLwRKJAc4FAAjEQEnFQQV DgARCgAHBgAFBAAIBwAJCAAEBAAEBAAHBQAGBAADBAACAwABBAAABAABBAACBAAAAwAAAgABAwAA AgABAQABAQABAwAAAgAAAAAAAAABAwAAAwAAAwAAAwADBAACAwAIBAAHBAACAwADBAADBQACBAAD BgADBgACBwAECgAGCgAECAAHCQAFBwABBQAABAAABAAABAAAAwABAwAAAgAAAQAAAgAAAgAABAAA BAAAAgAAAgABAgABAQAAAwACBAAEBgAICgAUCQAWCgAPDAAMCQAKCgAHBgAPBQAQBwAOCgAOCgAO CgAPDAAPDQAYFgAPDgAKCAAKBgALBwATBwATBwAaDgAYDAAPDwASEgALDQAMDgAHCgAHCgAPDQAP DgANDQALCwADCAAECgAOBwAQCQAMCAAOCgAaDAAeDwAqFQAzHQIpEgEiDAAMCAAKBgAZDgAfEwAb EQIVCwALBQAWDwAyHAI3IAQjFgEYDQAHCQAJCwAWGQARFQAXEgAeGAAlGwApHwAvHwMwIAMlEwMd DAAGBAADAgABAwAAAgAAAgAAAwABBAABBAAAAwAAAwAAAgAAAwADBAAEBgAQBwAUCgAiFgAmGgAx IwMvIQEzIwExIQAoGQAhEwAgFgAkGgAhEwAgEgA1GwA6HwM5IQA6IgAyJQIvIgAqEAAqEAArGwAy IgE5HQBAJAQ7JQM3IQAsFQAwGAA8IAIwFgAsFAA6IAFKKQdEIwMtHQEsHAE4IQUyHAIYDAIPBAAA AwAAAgAAAgAAAgACBAACBAABBAAAAwAABAAABAACAwADBAABAgABAgABBAABBAADBAABAwABBAAC BAAJCwAQEwAoFwArGgEwHQA1IgFHKQBQMQRJMAo+JgMsFwEuGQM9IQFDJgRIKQRJKgVDIwM/IAFD IwNDIwMpHAUfEwApGwAuIAIoHgAiGAAlFQAuHQFAJQBEKAJGKwdAJgMXEwAWEQAhGAAsIwUfFQYS CQAnEQAzHARMKQVJJwRFIgNDIAFHKgk+IgMXDgERCAAOBwAMBQAIBgAIBgAEBQAHCAAFBwADBAAD AwADAwACAwACAwADBAADBAAAAwAAAwAAAQAAAQACAwABAgABAgAAAQAAAgAAAwABAwADBAABAwAA AgABAwABAwACBAABBAAAAAAAAQAABAAAAwAAAwAAAgAABAADBwABBAABBAADBAADBAAABQAAAwAA AwAAAwAAAgAAAQAAAQAAAQAAAwAAAwABAgABAQAAAgAAAgAAAgAAAgAAAQAAAwACBAADBgAFBAAG BAADBAAEBQAEBwADBgADBwAECAAGBgAEBAAECQADBwAIBwAKCgAFCgABBAAABAABBQAKBgALBwAL CQAMCgAKDQAKDQADCAAFCgAJBgAJBgAICQAICQACCAAECgAEBwADBgAGBAAHBQABBgADBwEPCgAR DAAaDwAbDwAPDgAKCgALBQALBQAKCwAJCgAFBQAEBAAIAwAPCgAkEwAlFAEPDwAKCgAGBwAICQAL CgASEQMZDgAdEQAmGwAlGgAjGQMaEQARDwAPDQAFBQADAwADAwABAgAAAwAABAADBAADBAAABQAB BgAAAwAAAwADAwAEBAAMCgATEQAbFwAdGQAlFwAqHAAsGAAqFgAPDwAODgAZDwAZDwATDQAWEAAm FQAoFgAuHgAvHwAoHAMcEQAXCQAbDAAXDwAiGAIuGAAzHQIrGgEgEAASDAAWEAAjFQAfEQAfDwAr GQMtHgQmFwAaDwAgFQEjFgYZDQAGBwAEBQABAwAAAgABBAABBAABBAABBAAABAAAAwAABAAABAAB AwABAwACAQADAgADAgACAQACAwACAwADBAAEBQAKBwAPDAAlFQArGgE1IgA6JgNDKgBFLAEuIgcg FQAgDwApFwI9HQFFJAVHJwZBIgMxGwEtFwAsGwMnFgAIDgEFCgAYFgAeHAEfFAAfFAAqGgAuHgFB KAFBKAE1JAUpGAASDgAUDwAbGwEeHgMXEQQOCAAhEwAmFwE5JAA8JwFMMAJNMQNDLA8uGQEODwEE BQAEBQAFBwAHBwAGBgAHBQAHBgAGBgAEBAADBAADBAABAgABAgAFBAAFBAAACQAACAAGDgAEDAAI EQEEDQADCgACCgADDgADDgADCwADCwACCAABBwAHBgAHBwAFBwAEBwAABgAABAABBQACBgABAwAB BAABBAADBwADAwACAgABAwABAgAAAgAAAgAAAwAAAwAAAwAAAwAAAQABAwABAwABAwAAAwAAAwAA AgAAAQAAAgAAAQAAAwAAAwABBAADBgADBAADBAADAwADAwAEBQAEBQADBAACAwADAgAEAwADBAAC AwAEBgADBAADBAACAwACAwAEBgAEBAAGBgADBAAEBQAEBwACBAACBAAEBwAGAwAHAwAGBwADBAAC AwACAwADAgADAwADBQACBAAAAwABBQAGCAAICgAHBgAEAwABAwAEBQADAwADAwAECgECBwACBQEC BQEABAADCQAPCgAPCwAECAADBwADBQAEBwAJCwAJCwAPCgASDQAVEgAWFAANDwEGCAAFBwAFBwAD BAADBAABAwAAAgADBAADBAADBAADBAABBAAAAwABAgABAgACBAADBQAKCQAODQAPEwAUGAESEgAS EgAVFQEQEAADCgAECgANCwAPDQAUDwAXEwAQFQAPEwAdEgAbEAAYDwASCQALCgAODAAPCgAVDwAb FgAaFQASEQAPDwAVDwAWDwAQEAALCwARDAAaFQMWEQEPCgALDgAOEAERDgAMCQABBgAABAABBgAA BAACBAACBAADAwADAwABBAABAwACBAADBQABAwABAwABAwABAwADAwADAwADAwADAwADAwAEBAAD BgADBQAbDwAhFQAnGQAsHgEyHwAvHAASDQARDAAWDQAfFgA4GAE4GAEtFgApEwAgFQAfFAAbEQIQ BwAHBgAKCQAbDwIbDwIUCwAaEQArGgAsGwA6HQI6HQIgEwMTBwAPDAAVEQATFAAODwAKCQAKCQAV CgAdEQMvGgA5IwFIKQNEJQEeFgUMBgACBwADCAAGBgAEBAADBAAEBQAFBQAHBwAFBwAEBQAABAAA BAAAAwAAAwACAwADBAAPJAQKHwEbJggaJQcPIwYRJQcTKgUPJQIXHAMXHAMVIQQUIAMMHQMMHQMQ HAEPGgAOGAEOGAEMHgEFFgABEQAEFQAHDQAJDwAKDAAKDQAGBgADAwABAwABAgAAAQABAgACAwAC AwABAwAAAwADBAADBAACBAABBAACBAACBAABAwABBAACBAACBAADBAACAwADBAAFBwALBgALBgAL BgAJBAAEBAAEBAAHBQAJBwAPBAAPBAAMBQALBAAKCQAFBAAEBAAEBAADBQADBQADBAACAwABBAAC BAADAwAEBAAGAwAHBAADBAADBAADAwAEBAABAwABAwADBAADBAAEBQABAgAEAwAHBgAEAwAGBAAH BAAHBAABAwACAwADBAADBAAEBAAEBAACAwAEBAABBQAABAAKBQAKBgAHBAAGAwAGBQAKCQAHBgAI BwAKCgAKCQAHCAAHBwAKCAEHBQAFBQAEBAADAwADAwABAgABAgACAwADBAAEBAADAwACAwACAwAD BAACBAACBAADBQAHBgAKCQAQCgAPCQAMBgALBQAKCAAJBwAJBwAKCAAHBwAKCQAIBgAMCgAKCgAK CQARBwASCAASCAARBwAKBgAKBgAUCQAVCgAUDwAQCwAIBwAIBwASBwATBwAODQAKCgAPBwIUCwUI BQAKBwEKCwAJCgAKCAAHBQADBAADBAADAwADAwAGAwAGAwAEAwAEAwADBAADBAAGBAAGBAAEBAAD AwAEBAAEBAAEBQAEBQAFBAAFBAAIBwAHBgAIBwAKCgAbEgAbEgAgEgAfEQAbDQAaDAASCAAUCgAZ DwAbEAAlFQAiEgAeEgEWCgAVCwAWDAAQBwASCAARCAATCgAfCQAdBwAQCgATDAAaEQAbEgAhDQAj DwASCgANBgASBwAVCQANCQAJBQALBgAPCgAXCwAZDQAhDwAjEQAqFAAoEgAcDAMTBAAHBgAIBwAF BQAFBQAFBgAHBwAHBwAHBwAKBAALBQAKBwAIBgADAwACAgAEAwAHBQAQMg8NLgscLAkcLAkPJw4Q KQ8SMA4MKQgUIwkUIwkUKgUTKQQNKgkLKAcLKAcKJgYHJAIHJAIOJAEMIgAIIAMKIgQJHwMHHQEH GQAKHAEGEwAEEAABBwAAAwAAAQABAgACAgABAQABAQABAQABAwABAwABBAADBQABAQABAgACAgAD AwABAwABBAAABAAAAwABBAADBQAEBQADBAADAwACAgACAgADAwAGAwAHBAAGBAAGBAAFBQAEBAAD BgACBAADBAACAwAABAAAAwABAQAAAQAAAQAAAQADBAADBAAEBAAHBwACCgACCgACBAADBQABBQAB BAAEBwAEBwAEBwABBAAHBAAHBAABAwADBAAFBAAFBAACAwABAwADBQADBQAFBwAEBgADAwADAwAB BwAABgADBQADBQADAwAEBAADAwAEBAAFBQAGBgAHBgAGBQALCgAHBgAGBgAFBQADBQACBAACAwAC AwACBAACBAABAwABAwADAwAEBAADBAADBAABAwABAwAEBgAEBQAHBAAIBQAHBwAGBQAGBAAHBQAD AwADAwAEBAADAwABAQADAwADAwAEBAAHBQAFBAAFAQAGAgAGAgAHAwAFBAAGBAAHBwAHBwADBAAC AwADBQAAAwAEAwAEAwABBAABBAAEBAADAwACBAAEBgEGBQEDAwAGBAAFBAAEBQADBAACAwACAwAD BAADBAADAwADAwACAwACAwABBAACBAAFBQAEBAADAwADAwAEBAAEBAADAwADAwAHBAAHBAAGBwAF BgAKCgAMDAAOCgAMCQAQBwAQBwAKBQALBgAHBwAHBwAHCAAFBgAHBgAIBwAKBwAJBQAHBAAFAwAM BwANBwAMBwAMBwAKBwAKBwANBwANBwAHBgAKCQAJBwAHBQAKCQAJCAAGBgAEBAAGBAAIBwAWDAAY DwATDQEPCQAOCAANBwAJBwAFBAAGBgAHBwAJBAAJBAAHBAAIBQAGBgAEBAALBQALBQAGBgAEBAAD AwACAgAFBAAFBAALMw8JMAwXMQ8ULQsMLQwPMQ8KKhIKKhIMKA0MKA0KKgoLLAsPMBQMLBAKKw4J Kg0IKgcKLAkMJgcMJgcPJw0PJw0MKA0OKg8JJwkJJwkIIAUDGAABCgAABgABAgABAgAAAQABAQAA AgAAAgAAAgABAwABAgABAwACAgADAwADBAABAgABAwABAwABAwABAwAAAwAABAABAwABAwABBAAC BAAAAwAAAwABAwADBAABBAABBAABBAABAwAAAwAAAQAAAwAAAwAAAQAAAQAAAAAAAAAAAwAABQAD CwAEDQAGEgAJFgAAEAABEgABEAADEwAEEQADDwADDwADDwADFAABEAAMDwAHCgACDQAEEAAECgAD BwAEDAABCAAHCgAFBwAECQAECgABBAAAAwABBgABBgACAwABAwACAwADBAABBQABBAAEBAAGBgAD BAAGBwAKCQAHBwADBAAEBQAABAABBAABBAABAwAEBQADBAABBAAABAABAwABAwADBAACAwACAwAC AwADAwAEBAAHAwAKBQAEBgADBAABBAABBAABAwAAAQABAgABAQAAAwABBAABBAABBAACAwABAgAD AwADAwABAwACAwACBAABBAACAwADBAAAAwAAAwAAAwAAAQAAAQABAwAAAwAAAwAAAwAAAQAAAgAA AwABAgABAgABBAABBAABBAABAwACBAABBAAABAAABAADBAABAwABAgABAgABAwABBAABBQABBAAC AgACAgABBAABBAABAwABAwAEAwAFBAADBAAEBQAEBgAEBgAEBQADBAAIBgAIBgAEBAAGBgAEBwAC BAAABAAAAwAABAABBAACAwABAQABAgACAwAEAwAGBAADBAAEBgADAwAEBAAEAwADAgAAAgAAAwAD AwADAwADBQADBQACBAABAwACAwAFBgELBQANBwAEBwAEBwAFBwAFBwADBAACAwAEBAAGBgAFBQAF BQAFBwAGBwADBAAEBgAKBwEHBAAEBQADBAACAwABAgAFAgAFAgAKMwoKMwoOMhMOMhMMNRsMNRsM LA8PLxIOLQoLKggKJwcNKgkKLwwMMQ4LLgwLLgwNLg0NLg0PKw8PKw8QKg8OJw0PIwsTJw8LKAoJ JQgKIQQHHQEDEwADEwABAwABAwAAAQAAAgAAAgAAAgAAAwABAwABAwAAAwACAgADAwAAAwAAAgAA AwABAwAAAgAAAQAAAwAABAABAwAAAwACAwABAwAAAgAAAwAABAAABAAABQAABAAAAwAAAwAAAwAA AgAAAgAAAQAAAgAAAgAAAQAAAQADCwAIEgAMGwAPHgENKAMLJgEEIAAEIAAEFgAKHQIGHAAHHQAM IgAKHwAOJAEJHgAKHgEHGgAGGAEIGwMKGAMKGAMLGQELGQELEwIIDwAGFwEHGAEBDAAACgAECAAE CAACBAABAwABAwABAwABBAAABAADBAAEBQADBAADBAADBwABBAAABQABBgABBAAABAACAwACAwAD BQABBAAABQABBgABBQABBAABBAACBAADAwADAwAFAwAGAwAHBgAHBwADAwADAwABAwAAAgAAAQAA AAABAwABAwABBAACBgABBgADBwAEBwADBgADCgADCgAABwADCgADBwADBwAEBQAEBQAABQAABQAA BAAABQABBQABBQACBAACBAACBAABBAAABAAABAAAAwAABAACAwACAwAAAwABBAACBAABAwAABAAA BAADBAADBAACAwACAwAABAACBgADBQACBAABBAABBAABAwABAwACAwACAwAEBQADBAAEAwAFBAAE BQADBAADBgADBQAEBgAEBwAEBgACAwAAAgAAAQAAAQAAAAAAAgABAwAAAQAAAQABAwACAwACAwAA AQAAAQACAwABAwACAwAAAwABAwAAAwAAAQADBAADBAABBAAAAwABAwABBAACBAADBQACAwAEBQAB BQAABAAEBAAGBgAEBAADAwAEBQAFBwADBQADBQADBwACBgAGBAAGBAAEBAADAwADAwADAwABBAAB AwADAwADAwAILxAJMBEJMBMGLA8KMBcKMBcNMRMKLQ8KKAgNLAsLKggKKQcKLQsJKwoJKwoJKwoL Kw8LKw8NKRAPKxIRKhIRKhIIKQ0IKQ0GKhMFKRIKJA8DGgcDFgYADwEABAAAAwAAAAAAAAAAAwAB BAABAwABBAABBAAAAwAAAwAAAwABAwABAwAAAwAABAABBAAABAABBAABBAABBAABBAAAAwAAAwAA AgAAAwAABAAABAAABAAABAAAAgAAAgAAAwAAAwAAAgAAAQAAAQAAAAAAAwABBwAEGAAIHgMHJAkJ JgoZLxMWLBAJKg0HJwoJJQUJJQUJJQgJJQgJKwoHKAcMLQwKKgoGIwMHJQQHHwQKIgcKIgcHHwQK IgcLJAgFIAQFIAQFGgIIHgQDFgMCFQIDDAACCwACBgAAAwAAAgAAAwACBAACBAACAwADBAABBAAA AwADBgABAwAAAwABBAABBQABBAABBAACBAABBAAABAADBQADBQACBgACBgAABAAABQABAwABAwAD AwAEBAADBAADBAAAAwAAAwAAAQAAAAAAAAAAAwAECAAJDQAEEQADDwAEEQAGEwAKFAAHEQAHGQEF FgAEFQADEwAFEQAFEQAFDwAHEAAHEQAHEQABDwABDwADFAABEAAEDQAHEAAGDAAIDwAEDgAACQAC BAACBAAABAAABAAABAAABAABAwABBAADBAADBAABBAABBAABBAABBAACBgADBwAEBQAEBQACBgAA BAADAwADAwABBQABBQABBQABBQACAwADBAADBQAEBwADCAACBwACBgADBwABBAAAAwAAAQAAAQAA AQAAAQABBAAEBwAFCQAEBwABCQACCgADCwAABwAABwAACAAACQAACgABCgADDAAACAAACAADDAAA CAADBwAECAADBwABBQAEBQAEBgAECAAEBwABBQADBwAEBgAFBwAFBQAEBAAEBwADBQAEBgAEBQAE CAAEBwAHBwAHBwAFBwAEBwACBAABBAADAwADAwAEBQADBAAJKxUJKxUIKw8KLhIKLBEIKQ8IKQ0I KQ0LKQ4RMBQWMREULw8KKgoIKAgKJwwKJwwJKREKKhIJKREIKBAHJRMKKRYKKw8IKQ0EJQ8IKhML Iw8EGgcDFgcADwIABwAAAwAAAAAAAQAAAQAAAQAAAwAAAwABAwACAwAAAgAAAgAABAAABAAAAwAA BAABAwABAwABAwABBAACAgACAgABAQABAgABAwABAwAABAAABAAABQAABQAAAgAAAgABAwABAwAA AQAAAAAAAAAAAQAABwAEEAEEIAgHJAsHJxQHJxQTLxgQLBYKLRQOMRcLMhcKMBYKJw4KKA8HKBUH KBUGJQ4NLhYGIwQFIgQJKAwHJQoGIgcEHwUHIw8KJhEHKQ4HKg8FJQoIKQ0GIw8DHgsBDwAADAAA BQAAAwABAwABAwAAAgAAAwACAwAEBQACBAABAwABBAAAAwAAAgAAAwAABAABBAABBAABAwAABAAB BAAABAABBAABBQABBQABBAACBAACAwABAgACAwACAwAABAAAAwAAAQAAAgAAAAAAAAAAAQABBwAJ FgQKFwYEHwQHIgYMIwUMIwUHIwcFIAQJJAoEHgYGIggIJQoFHwcEHQUIHgQIHgQHHwQKIgcHIAMH IAMDGQEGHgQDGQEEGwIEGQEFGgIKFwQFEQAHCgEBBAAABAAABAAAAgAAAwAABAAABAAAAwAAAwAA AwAABAABBAAAAwABAwACBAABBAAABAAAAwAAAwAAAgABAwAAAwAAAwAABAABBgABBAACBAAABAAB BQAEBwAEBwABBwAABQAAAgAAAAAAAAABAQAFAwALCQAFEgAIFgAJHwMEGAAEFQAHGAAHGgAJHQEE GwIEGwICFwACFwADGAAEGwIHFwMEFAAHFwMEFAALEgAPFgIDFAAEFgADDAAACAADDwACDQACCQAC CQAGBwADBAAEBgAEBgACBwABBwACBgAECAAGBwAFBwAGBwAGBwAFBwAFBwAEBAAEBAABAwABAwAF AwAEAgAHKhcKLhsKLBMJKxIHKAsIKQwHLgoIMAwZMxcmQSQ7TzcvQysgOx4ZMxcHKxYFKBQKMBcJ LxYHKA8GJw8KJxcJJhYEIg4HJhEHLRAHLRAEJQoKLhINIhEFGAkBCAAAAwAAAAAAAAAAAAAAAQAA AwAAAgABAwAAAwAAAwAABAAAAgAAAgAAAQAAAQAAAgAAAQAAAQAAAgAAAQAAAAAAAQAAAQAAAQAA AgAAAgAAAgAAAwAAAwAAAgAAAwAAAQAAAQAAAwAAAQAAAAAAAwAACwAEFgcHJxQLLRkMLR8IKBoK LRQJKxIJKxQHKRIKLyAJLR4FKBQILBcGIhMEHxAEJAkJKg4KKQ0DIAYEJAgKKw4FJA8GJQ8HJhEM LRcHLBUGKhMFIg4FIg4KLBgKLBgBFAAAEgABCQAABAAAAgAAAwAAAgAAAwAABAABBAAAAQABAwAB AgABAgAAAgAAAwAAAwAABAAAAwABBAAAAwAABAADBAADBAAAAwAABAAAAwAAAwABBAACBAAABAAA BAAAAwAAAQAAAQAAAAAAAAAAAgAABwAEEQcIJRYKJxcKMBsLMRwPMhYPMhYKLgsLMA0PLxMLKw8M LhoIKRYKJg8KJxAIKBMHJhEPJhEPJhEHKAkKLAwLKQ4HJAoHHwcPKQ8HJAsFIQkJIAgBFgEDEQMB DwEEBgABAwAAAgAAAwAAAwAAAwAABAAABAAAAgAAAwABBAAAAwAAAQAAAgABBAABAwAAAwAAAwAA AwAAAwAAAQAAAQABBAACBAABAgABAgACBAACBAADBgADBQABBAAAAwAAAAAAAAAABAAEDgADFwAJ HwUIIwoLJw0JJwkJJwkLJAUPKQkLJw0MKA4HJhEFJA8EIgoHJg0KJA8IIg0KIwcHIAULIhAMIxEK IgoJIQkKIAwKIAwHGAkEFQYNGwIMGgEJFAQDDQABBAAAAwADBAADBAACBgACBgADBAAEBQAEBgAE BgAEBwAFCAADBgADBgAFBQAEBAADAwADAwADAwADAwAHJRYHJhcJLBAIKw8JLQoKLgsKMQ8SPBcx STs+V0hTYVpNW1RBTz40QTEROR0HKxEJKxQKLBUHJA4HJQ8NKhYMKRUHJw0IKQ8IKw8HKQ4GJgoH JwoKIQ0GHAkDDQAACAAAAQAAAAAAAQAAAQAAAwAAAgABAwABAwAAAwAAAwAAAgAAAgAAAgAAAgAA AgAAAgABAgABAgABAgABAgAAAwAAAwABAwABAwAAAwAAAwAAAwAABAABAwABBAAAAgAAAQAAAwAA AgAAAwACCQAAEgEGGwgHKhkLMB8PLx4OLh0KKxULLBYJLRgKLxoKKx0IKBoHJxYJKhgKKxMKKxMV Nw0PLwcXMw4XMw4VNBYVNBYOMBUOMBUJJxIKKRQLLxMJLBAHLQ4HLQ4KLRQKLRQEGgQAFQAACAAA BAAABAAABAAAAwAABAABBAACBAABBAABBQABBAABBAAAAwAAAgAAAgABAwACBAACBAABBAADBQAB BQAABAABBAABBAAAAwAAAwADBAADBAACBAACBAABBAAAAwAAAQAAAAAAAQAABAAADgMKHQ8IKRkJ KhoJLxUILhQIMBUKMhYKLxcILRYPLx4MLBsKKhUGJRAIKhQKLRYMKx4KKBsSKBYUKhgKKRIKKRIP LQ0OKwsMKQoMKQoGJAsGJAsLIhAIHg0FGgcAEQEBBwAABAABAwABBAACBAACBAACBAABBAAAAwAA AwABBAABBAACAgADAwABAwACBAACBAACBAABBAABBAACAwACAwAEBAAEBAADAwADAwACAgADAwAE BgAEBgAAAgAAAQAAAAAAAgAACwAHGQQEJQgIKw0SLhkUMBsaLRkWKRYPKw8PKw8OLRYMKxQMKxYK KRUGKhMJLhYKLRYJKxQKKQ0KKQ0OKhwOKhwNKhMNKhMPKQ8KIwoGIQUGIQUTJwwLHgUHEAQDDAEF BwAEBQADBAADBAADBAADBAADBAADBAADBgADBgADBAADBAADBAADBAADBAADBAAEBAADAwACAwAC AwAIKhQIKhQILhYJLxYILhQMMxgWOBUqTihBXVFPa19qeHphbnBVY15OXFcyTDgeNSMNLg0JKQkG Iw8MKxYLKxgKKRYHJw8HJg8HKxgFKBYIKQ8FJQsMIRAEFwgCEAMADAAAAgAAAQAAAwAAAwAAAwAA AwABBAABBAAAAwAAAwAAAgAAAgAAAgAAAgABAQACAwABAQACAwAABAABBAAABAAAAwABAwADBAAD BAADBAAABAAABAABAwAAAgAAAgAAAQAAAgAAAgAABAABCgADHAYJJAwMMSAMMSAOLhsPMB0LMBsK LhkJMBoKMRsKLh8JLB0HJxIPMBorOh8yQSY8Vik3UCQ5TBs9UB81UysxTicrQCYkOR8MLRUPMRgI KhQKLRYHLhEHLRAGKBsKLSAFHQ8AFQgABwAABAAABAABBAABAwABBAABBAABBAABAwACBAAAAQAB AgABBAACBAADBAAEBQABBAABBQADBAADBAADBgADBQADAwAEBAADBAADBAABAwABAwABBAABAwAB AwAAAQAAAQAAAAAABQAFDQIEHAoPKhYKMBcILRUILRUILRUKLxoMMh0KMB0KMB0LMB0KLhsHKhkH KxoKKBoHJBYPKBYULRoZLCAfMiYKKRUKKRUMKg4PLRAOKxQOKxQHKRMKLRYLJhAJIw4HHggAFAEB CAAABQACAwACAwACAwACAwADBAACAwABBAABBAADBAAEBQAEBwADBgADBQADBQAEBAADAwADAwAD BAADAwADAwAEAwAEAwADBAADBAAEBAAEBAACBgABBAAAAQAAAAAABAADCQADGAAKIwYHLQ8JMBEQ MSIPLyAWLh4WLh4PMhsNMBkMMR4LMB0PLyAOLh8JLhsJLhsILRYLMRkKKxUMLRYSLhoOKRYOLhIN LREQLg4KJwgIJQoLKQ4PJxsGGxAGEgIDDgAEBwACBAAGCAAFBwAFBwAEBgAEBgADBAAEBwAGCgAH BwAEBAAEBgAEBQAEBwAEBwADBAABAgACBAADBQAGLBgHLhoILB0ILB0GKxMKMRgcOiUzVD1KY1tW b2dofX1bb29PYVtMXVdPXENIVTwtRyoXLxUKKBEOLRYKKhcIJxUHJxYJKhgKKh4GJRkEJBMHJxYJ JREEHgsEEAYACQAABAAAAQAAAQAAAwAABAABBAABBAACBAAAAwAAAwABBAABAwAAAgAAAgAAAwAB BAABAwABAwADBAADBAAAAgAAAgACAwADBAACBAABBAABBAABBAAAAwAAAwABAQABAQAAAgAAAwAA BwABCwADHAoKJhIMMSILMCELMBsMMRwKLx4ILBsHKx4KLiEKNB8GLxoUMhEkRSFTX0Vdak9ab15U aVhKaFdJZ1ZGYk9HY1BAWkg6U0EaRTMJLx8GKRgILBsJLB0JLB0HJxkHJhgIHg0DFwcDEAMABgAA AAAAAQABAwADBAABBQABBQAABAABBAACAwADBAACBAABBAAABAABBQABBAABBAACBAADBgABBAAB AwAFBAAGBAACAwACAwAAAgAAAQAABAABBAAAAgAAAQAAAAAAAQAACQAHEgUHJA8OLRcLMRkILRYM Mh8KLxwLMB0NMh8PMh4OMBwKLxoKMBsGJxEJKxUdLRMnOBwtOyQ3RS0zQR41RCAoPiAgNRgVOiEP MxsLKxoJKBcHLBUJLxcHKAwFJQoGFgcEFQYECgEBBgADBAABAwABAgABAgACAwACAwACBgABBQAB BAABBQABBAAABAADBAAEBQADBAADBAADBAADBAABBAABBAACAwADBAAEBQADBAAEBQAEBgACBAAB AwAAAQAAAQABBgAHDQEFHwYNKQ4KMBcKMBcLMB8MMSAPMCQOLyMLLyAKLh8NLRwKKhkNJhsOJxwO Lh8PLyAKKhsKKhsJKg8KLBEVMxQPLA4NLxQKLBEKKRQKKRQLJRYPKRoKJBYGHhAKGwoDEgMDDAAA BwAEBwADBgAEBQAEBgAFBQAGBgAJCAAJCAAKBQAKBQAGBwAEBgAEBQAEBQADBAABAgABAwADBAAH LR0HLR0KLycKLycHLBwLMiIaOCssTD5MX2JWam1UcnhPbXNNX2VFV11MW01KWkxEWkYwRTITNxgJ Kg4HJRMIJxUKJRkKJRkKLh8GKBkHJhEJKRQEIQ0DHgoGFgcACgAABAAAAAAAAQAAAgAAAwAAAwAB AwABBAAAAQABAgADAwACAgABAwABAwAAAwABBQACBAACBAABBAABBAABAQABAgABBAABBAABBAAA BAAABAAABAABBAACBAAAAwAAAQAAAgAAAwAABgACDAEDHA4LJxcKLiEKLyILMCEKLyAKLx4KLx4K LR4MMCEKMBYILRMuQy9KYUxdfXtbenlQcHNFZGdNZWlKY2dPYV1UZWJBY2Q8XV4kUFMMMzUKNCMI MSAHKx4LMCMOMCAKLBwLJxkEHRAFFQcACgAAAwAAAgABAwACAwAEBAAFBQADBAADBAABBAACBAAC BAABBAAABAAABAABAwABAwADAwAEBAAEBAADAwAEBQAEBQABBAABBAABBAAAAwABBAABBAAAAgAA AQAABAAABAAADQEHFggHKxoNMiEMMh8KLxwOMiUMMCMLMCMKLiEPMiAOMB4ILRwHLBsIJAYXNxVG STRYXEZbZFFaY1BQYkZMXUFGUDlETjcxSjcgOCULMR4HKxgHJhEMLRcKKxMEIwwGIAcGIAcFEAQA CAAEBwAAAwABAwABBAABBAABBAADBAADBAAABAABBAABBAABAwABAwABBAABBAACBAADBAADBAAB BAABBAACBAACBAADBAAEBQADBAAEBQADBAABAgAAAQAAAgAABwAHEAcIIxIPLBoLLhYMLxcPLiEP LyIMLSMKKiAJKhoMLh4KLh0ILBsNKxsLKRkKLBoLLRsHIxIJJhUaLxMoPiAgQB0VMxIPMxkKLBMM KhgLKRcOLSINLCELKhMHJQ8IHAoDFgQJDAEGCQAFCAADBgAEBgADBAAHBgAJCAAIBwAHBQAHBQAF BAAEBAAEBAAFBQADAwACAwABAQADBAADBAAILh4KMCAKMSUHLCAGJRkPMCQaOCMsTDVOXl9WZ2hV YW1PW2dMVFdKU1ZMW09GVUlGWlBFWE8dTTUNOSMFIxMJKBcHKBgJKhoFJxgGKBkFJwwHKQ4DGQwD GQwEEwkACgEAAwAAAAAAAQAAAQAAAgAAAgAAAwABBAABAQABAQABAwAAAgABAwAAAgAAAwAAAwAB AwABBAABBAABBAABAwABAwABBAABAwAABAAAAwAAAQAAAwABBAAAAwAAAgAAAQAABAAAAwAABQAC CwQKIQ8ULRoILx0KMR8JMiMHMCEKLR4LLyAQMigQMigQMRYcPyNBTkZaZ15YfYRRdX1WYWJJVFVV UVdWU1hOXl9UZGU9Ymo0WGEhUFgJMjoFKRgFKRgJLB8KLiENMSIGKBkGIRcEHxYEDgMABQAAAQAB AQAAAQAAAQADBAADBAABAwABAwAABAAABAAAAgAAAwAABAAABAAAAgAAAgABAwABBAADAwAEBAAD BAAEBQABAwADBAADBAADBAAABAAABAAAAwAAAQAABAAABwACFwMHHwkHLhgNNR8NMiUKLyIKLyAJ LR4JLyMKMCQKLyAILB0JLhYJLhYdMxZNZ0Vrfnhrfnhibmpjb2tabmlUaGNPYVtMXVdDV04sPzcP OBcKMBEHKxQHKxQKLRQIKhEJIw4HIQwIDgYBBQABBAAAAwAAAwAAAwABBAABBAADBAADBAACAwAD BAADBAACAwABBAABBAAABAAABQADBQACBAACAwADBAADBQACBAADBAAEBQADBAADBAACAwABAwAA AwAABAAACwAEFAUIKRkOMCALLyALLyAOLyEMLR8NLCELKh8KKRwMKx4NMh8LMB0KLBoLLRsKLh0J LRwNIAspPydPUUFXWkk+TjEpOB0TNxgKLA8NKRcOKhgSLSAPKh0KKBoFIRQEGwYEGwYHCgEDBQAE BgAEBgADBAADBAAGBwAGBwADBgADBgADBAAEBQAEBQACAwADBAADBAABAwAAAgACAwABAwAJLx8K MCALMiQILiAHLBkLMR4eOSQ1UzxQYl5db2tVaGVEVlRESkhHTkxTXExXYVBbXlRbXlRDZ1gwU0UP OCcJLx8ILRYILRYHKBgHKBgOKhwOKhwGIRYEHhMDFQwAEAgAAAAAAAAAAQAAAQABBAABBAAAAwAB BQABAQABAgACBAACBAABAwABBAABBAABAwABAwACBAACBAACBAABAwABBAAAAwABAwABBAABBAAA AgAAAwABAwAAAgAAAQAAAAAABAAABQAABAADDgIFIAwRLxkJNyQHNCIHMCEMNycSNCYLLB4MMSQM MSQMNB4YRCxGTk9dZWdueHJqdG5qaVZfXkxcV0xaVUk9W1c/XVo0WlwlSEoWQzkPOS8JLyEILiAI LSMHLCIPNCcEJhkFIxUHJRYJFQYABwABAwAAAQABAwAAAgACBgAABAACBAABBAABBAABBAAAAgAA AwAAAgACBAABAwACAwABAwAAAgAABAADBwABBAABBAABBAABBAACAwADBAADBQABAwAABAAAAwAA BAAABQAAEwMKIg8ILiALMiQLNSgHMCMJLSIJLSIIMSQJMiUNNR8KMhwHJxYWOic1W1Bii39zjZlj fYhTWGtUWm1PXW5JV2hPZW5PZW46YVciRj0SOxkILg8KMBEILg8NLxsIKRYFHxIHIRQHEgUACgAA AwAABAAAAwAAAwAAAwAAAwAEBQAEBQACBAACBAADAwADAwACBgABBQABBQABBQADBAABAwADBAAD BAACBAABBAADBgADBgADBQADBgACBAABAwAABAABBgAADgEGFgcEJhQNMh8KMygJMSYPLicQLygL LyALLyAKKh4KKh4KMB8MMiEKLh8MMSIHKxYMMh0nRTRHaFZteXVjb2tKWkw6SDsVQywNOSMPLicL KSIWKyAVKh8KKRUKKBQQKRQIHwsHDQEBBwAEBQAEBgAFBAAHBQAGCAAGCAAEBwAEBwADBgAEBwAD BQACBAADBQACBAABAwACAwACBAACBAAKLh8MMSIIMxkJNBoPMhoWOyIvRTJMY09jcm1vfnljdW1Y amJaZVFaZVFlaltwdWVye2pueGdWeHVAYV4bSjwOOiwILRoHKxgHKRMIKhQKJx0KJx0HIhYDHBEE Fg4AEAgAAgAAAAAAAQAAAgABAwADBAABBAACBAACAwADBAACAwACAwABAwABAwABAwABAwAAAgAB AwABBAABBAABBAABBAACAwADBAABBAABBAADBQACBAACAwABAgAAAwAAAwAAAQAAAgAABgAHFgwE JBsLLiUJNyYKOCcOMyYNMiUMNSoIMCUMLy0QNDIIMiMOOipIUUxpc215gHJ3fm9odW1caWFRZWFM X1svT1ApSEkoSTwdPTAVOCkSNCYHKyAJLSIKLyIKLyIPNygJLyEFJBoIKB4MGwwBDQEAAgAAAQAA BAABBAABBQACBgADBAACAwABBAAAAwAAAwAAAwABAwABAwABAwABBAABAgABAgADBQADBgABAwAB AwACBAABBAADBQABBAAEAwADAgAAAwAAAwAABAAABQABEwQSKBYQNysROCwKNSgKNSgKMygGLSIK LiMLMCUSNCgSNCgTKSUrRD9XcndyjZJ4g3pjbmVRUU9OTkxHTkxESkhKXWNQY2kyVVUdPT0NMh0H KhYNMh8NMh8MMSALMB8HJRoFIhcGFAQACgAABAAABAAABAAABAAAAwAABAABBAADBQAABAABBAAD BQABBAACBAADBQADBQACBAAEBQAEBgAEBQADBAAEBQACAwADBAAFBwAEBQADBAAAAwAAAwAABAAD CAEBEQcMIBQILiINNCgLNSgLNSgTMygRMSYOLSAOLSANLx0PMR8OMi0NMSwKMisJMSoHJR8RMiw6 W1xWeXptgoRhdXhJYV01TEgTOTEOMisQLCQMJx8YKBsdLSAWLiINIxcWKRgKHA0GFQIADAACAwAG BwAEBgAEBQAGBwAHCAAGCAAGCAAGCQAFCAAEBgAEBgADBAADBAACAwACAwADBAADBAALMCEKLyAK MCIKMCIRNR4fRi0vT0RFZ1treXl6iIh3i39vg3hkeHVleXdweX94gIdwiI5uhoxWcoNGYXIhUEgR PTUKMyQHMCEILRgHLBcMLyYGJx4GIxYFIhYIGw8ADwUAAwAAAQAAAgAAAgAAAwAAAwABBAABBAAD BAADBAABBAAABAAAAgAAAQAAAwAAAgAAAQACAwABBAABAwABBAABBAABAQADAwABBgABBgAABAAA BAAAAwAAAQAAAwAAAwAAAQAAAQAABwAHFgsDJBkKLSIIOCYLPCoKLyQKLyQNOCwKNCkMNDENNTIJ MyANOSVKU0d0fXB9hod6g4RceoBPbXM9Y2M3XFwyTEciOjUYMhwZMx0WNB8WNB8KLiEHKh0KMBgK MBgMMh0JLhkHKh0HKRwKGAgADAAAAwAAAAAAAwAAAwAAAwABBAABAwABAwABBQABBAAAAwAAAwAB BAABBAACBAACBAABAwADBAADBAADBAAAAwAAAwACBAACBAABBAAABAABAwABAwADBAABAgAABAAA BgABFAoLIRYKMicMNSoLNycLNycLNSwJMikKMisJMSoOKiQRLigZNzkuTlBfeHlwiYtzdWdfYlRU VEVOTj9KT0BHTD0/WlNGYVoxSkEbMioJLRoDJRMKLxcJLhYKMiAJMB4HKhsFJxgHFgcACwAAAwAA AgAAAwABBAABBAABBAABBAABBAABBAABBAABBAABBAAABAABBAACBAADBAADBAACAwADBAADBAAD BgACBAADBAADBAAEBQACAwAAAwAAAgAABAABBwAAEgoLIRcJLSsPNTMKMyQKMyQNNCYKMSMMLhwN Lx0MMRwNMh0KLikLLyoGLSYLNC0KLywRODRDXF9ifYBtf41cbns9XUwiPy8QMiYKKx8MKBoOKhwP KhQQLBYYLSQPIxoXIRQOFgoLEwoDCQEABAADBwAGCQAEBwADBQADBQAFBwAEBgAFBwAEBgADAwAE BAADBgADBQAABAAABAADBAADBAAKMyIIMSALMiQMMyUMNyUYRjMwVk9BaWJleXl3i4t0jY5rhIZf eYBYcnlkb3Vrd31ne5VjeJFNXm4/UF8jTkMPNSsHMR4HMB0IMBcIMBcNMSgKLSQHKhsDJBYIGQoA DQAAAwAAAgAAAAAAAQAAAgAAAgAAAwAAAwAAAgABAwAABAAAAwAAAgAAAgAAAwAAAgAAAQABAwAB BAABBAABAwAAAQAABAAAAwAABAAABAAAAwAABAAAAgAAAgAAAwAAAwAAAwAAAgAACAADEwUBIRYJ LCENOCwPOi4LNCkIMCUOOSsNOCoKNC0KNC0HMBkSPydNYlZrgnV5ho5yfodPdHU0V1gvVU4pTkcu PC8sOi0qPSQ5TTInSDsWNCgKMRsKMRsKNSIKNyMLNBoHLhUKLSAEJRgDEggACgIAAQAAAAAAAgAA AgAABAAABAAAAwABBAAEBQADBAABBAACBAABAwABAwAEBAADAwABAwACBAADAwADBAABAwABAwAC AwADBAABBAAAAwAABAAAAwAABAAAAwAABAAABgAAEgkHHxUKMygKMygKNSgKNSgKNCULNSYKNCsK NCsLMScONCobPjo0W1ZdcHpjd4Bdd2tac2hoa2VhZF5fbl1baVhIZVVDX08tSTMdOCMPNCsGKB8J LSIKLiMKMyIGLh0HKhsEJRYDEQYACgEAAwAAAgAAAQAAAwABBAACBAADBQACBAABAwABAwABBAAB BAAABAAABAACBAACBAADBAACAwAEBgAEBQADBQADBgADBwACBgABBAABBAAABQAABQABCgAEDwAB FwoGHhAFKCYOMzEKMygMNSoMMyULMiQKMyQJMiMKMyIKMyIIKyQJLCUHKygPNDEUNywWOi8/Wlde enhogIJNZGUiSj4RNysNMx4NMx4KLiELLyIOMCALLR0VLCAUKx8TJhYMHg8JDwECBwADBQADBQAD BAADBAABBAAAAwADBgAEBwADBAAEBQAEBQACAwACBAACBAAAAQABAgACBAACBAAKNSIHMh8KNCUO OioONCwUPDMpRkdFZGVecHdkd31ddYBWbnlJYmdGXmNOVV9OVV8+WmU5VF83SE4qO0AVOzMPNC0H LRcILxkJMiEHMB8PNSoKLiMJLRwEJhYHFgoACwEAAgAAAAAAAAAAAAAAAAABAQABAgABAwAAAgAA AgABBQABBAAABAAAAwAABAAAAwAAAwAAAwABAwACBAABAwABAwADAwADBAABBAACBAAAAwABBAAB BAAAAwAAAwAAAwAAAwAAAwAACgADEgQDIBYLLCIOOS8OOS8LNykIMiUONScPNygOOSsMNykIMiEd TTpEaGhXfX1ye4Zpc31ObWc1U000SjcuRDA3OSBJTDFWX09aY1MnVkkUPzMNOxoKOBcJMx4LNyEO Mx4OMx4LLiUFJh0BDQQACgIAAQAAAAAAAQABAwADBQABBAAABAAABQACBAABBAABAwACAwABAwAB AwABBAADBQABBAABBAADBAADBAABAwABAwABAgADAwACAwABAQABBAAAAwAAAwAAAwAABQAACAAA GAcKJhIKNCUKNSYKNSQKNSQKOSgKOCcNOCoLNSgONCMROScWPUAqVFdDZGlNb3RWcnJVcHBianBi anBidGtfcmlWdGVNalwzV0MmSDQTOigJLRwLLyQJLCEJLyEHLB4FJBYHJhgEEwQACgAAAgAAAwAA AwAAAwABAQACAwACBAACBAACAwADBAACBAACBAABBAACBAABBAABBAADBQADBQAEBgACAwAEBAAE BAADBAAEBQACBAABBAABBgADCAADCwAHEAECFw4FHBIEKyQNNy8MMyUMMyUPNSQNMyIOMyQMMSIL MCMLMCMKLh8LMCELNycLNycNMicZQTVJWGNjc35qc3lOVlwmPjUZMCgMNRwOOB4KKB0MKyAJLh0J Lh0LLR0NLx8NJRMHHg0HDQMCBwAEBQAEBQADBgADBgABBQABBQAEBQAFBwAFBQAFBQAFBQAEBAAE BAADAwADAwACAgADAwADAwAOOyUKNyELOisNPC0UOTEVOjIjOj0+V1tKXG1GV2hGV2dFVmUrVEkn T0UsSUopRkciU0QaSTsSPjIPOy8POzELNSwJLh0JLh0KNCUJMiMKMikIMCcFKRYGKhcJGQoACgAA AgAAAQAAAQAAAQADAgAEAwACBAADBQABBAABBAAABAAABAAABAAABAABBQABBAAABAAABAABBAAC BAAABAAABQACBAADBQAEBAAEBAAAAwAAAwAABQAABAAABQAABQAAAwAAAwAABwABCgMCIh0OMi0K OSsLOy0KOSgKOSgMMyUNNCYLOisOPS4XNyorTT9KZGJhe3lqfn5pfX1qb11dYlBVWzhVWzhfY0xu clpoeX1WZ2owXFgfSEUKOh4IOBwNMiMMMSINNCYJLyEGKiAILSMGEAcACQEAAwAAAgAAAQACAwAD BAADBAADBAADBAACBAABBAAAAwAAAwADBQADBQAABAAABAAABQAABQABAwABBAAAAwAAAwABAwAD BAADAwAAAQAAAgAAAQAAAwAAAQAABAAACAAAFwgMKBYKOykKOigJOSUKOycKOSoJNygKOigKOigP OigOOScPOjIZRz8xVlU1W1pRZV5RZV5UX2hYZG1jbWlncG1fcmlWaF9NZV03TkYVPSsMMiENMiUK LiEMMSQLMCMHKxoHKhkEEwMACwAABAAABAAAAgAAAwAAAgABAwAABAAAAwABBAABAwACAwACAwAC AwADBAABBQABBQACBAACBAACBAABAwADAwADBAADBQADBQABBAAABAAABQAABgAACgABDgADFwsK IRQKMSMSOywONSUPNyYROi0ONSkNNCQMMyMNNCgLMiYKNCcLNSgOPSoMOygMNy0RPTNJWF9kdHtj aW5KUFUqPTcbLScKNCEMOCQMMCEIKxwJMBoKMhwKKBgOLBwSHxEQHQ8GDwQABwADBAAFBwAFBwAE BQAEBQAEBgAGBgAGBgAECAADBwAFBwAEBQADBgADBQAEBAACAgAEAwAFBAANOSkMOCgOPS4KOSoK OS8LOzEOOzcWRUAiQ1EkRVQoRkQkQT8WPzAUPS4QPCwPOioLPSsNPy0JNygJNygLOS4JNSsILiII LiIGLSIJMSYMNy0IMSgHLBsDJRUDEQYACAAAAAAAAAAAAQAAAwABAwABAwABBAABBAABBAAABAAA AwAAAwAABAAAAwAAAwABBAAABAAABAAABAAABAAAAwAABAABAwABBAACBAACBAAAAwAAAwAABAAA BAAABAAABAAAAQAAAQAABwABDQQAIBYQNysJOioNPy8JOCwKOi4KOS0KOS0IMy8LODMYOTMfQDtD XV1deXljfYRqhIxqf3VjeG5udWd5gHJ9g4t5f4digJZGY3gmVFATPToMMS4HKicLLCIOLyUPNSsK MCYHKh8EJRoFDwYAAwAAAQAAAAAAAwABBQACBAABAwAABAABBAABAwABAwAAAwABBAABBAAAAwAA AwAABAAABQAABAAABAAABAAABAAABAACBAABBAABBAAAAwAAAwAAAQAAAgAAAwAABAAABgAADwwK HhoINCgMOi0LOicNPCkNOSkNOSkKOSoKOSoQOzEPOS8WNCIjQy8sRjc+WklYZFNealhoa2Foa2Fq bmNucmd3f3h4gHlWe3swU1MWPy4LMiIMMSQHKx4KLyIKLiEKJxkHJBYFFgQADQAABAAABQAAAwAB BQABAwABBAABBAABBAABAwABAwADAwADAwACAwACAwACBAACBAADBAADBAAEBAAEBAACAwABAgAD BQADBQAABAAABQAABwAABgAADQADEwEDGw8GIBMKMR0POCMPOCkPNygQOSwPNyoMNykLNSgNNysL NCkJMSoJMSoOOSsPOy0LODMPPTk4Yl9Pe3lldWtHVk0bPTELKh8OLyMPMSUNMyAKMB0LLR0PMSEO Jx4LJBsQIhgMHRQEDgEABwADBQACBAADBgADBgAEBAAEBAAEBgADBAADBAADBAADBgADBgADBAAD BAAEBAAEBAAFAwAHBAALOzELOzETPzEOOSsOPT0OPT0OPTgOPTgRQTwVRkAaQDkTODAUOTEWOzMO QzUMQDMMOywNPC0INScKOCkKOCsJNSkMMycLMiYJMiMKNCUPNSsLMScJLSADJRgEDgcABwIAAQAA AAAAAwAAAwAAAQAAAQABAwABAwABBAABAwABAwABBAAAAwAAAwAAAwAAAwABBAACBAABBAAAAwAA AgAAAwAABAAABAAAAwAABAABAwABAwAABAAAAwAAAwAAAQAAAgAAAwAABgABCgECGxIPLCIHNy0M PTMKOjMMPDUOPTgKOTMHNC8KOTMRPTMUQDcmV1w5bXJddYRed4ZWeoJWeoJbeX1ffoJpdZZlcpJe eIdAWGcdTEEPOjAKNywKOC0LLygMMCkKNCkKNCkHKCYEIyEFEgcABQAAAgAAAgAABAAABAABBAAA AwABBAACBgACBgABBQAAAwABBAADBAADAwAAAwAABAABAwACBAAAAwABBAAABQAABQABBAABBQAB BgAABAABBAAAAgAABAAABAAABwAACQIAEhUJHyIIMzELODUNPTMOPjQOPy8NPi4POS8SPTMQOzgP OjcbOy4gQDM9UElVaWJkd3BqfXd0eG93enJ7gH+DiIeCjJaAi5VVeYMxU1wYPDETNSsMMSIKLyAN MSQMMCMRLB8KJBcIFwUABwAAAAAAAgABBAACBAABBAACBAADBQADBgACBgABBQAEBAAGBgADBAAD BAAAAwABBAAEBAAFBQAHBAAGAwADBAADBAAEBQAEBgADBgADBgABBwABBwABCwAFEQEDGRMHHxgI MiMNOSkNOy4OPC8POi4OOS0KNyMKNyMJNyYKOCcPNDEOMzANNTENNTEOPT0VRkY7ZWRQfXtcfnc7 W1QYQDQOMygRMyUPMCIPMyQOMiMPMi0KLSgPLyAOLh8OIxkGGRAECgMBBQAEBwADBQAEBwADBgAG BwAHBwAFBQAEBAADBAADBAAGBAAFBAADAQADAQAEBAAEBAAEAQAHBAAMPC4OPjATPzMRPTELPjgM PzkQPz8MOjoOPzMOPzMPOTUONzMPNC8WPDcOQDAKPCwKOiwKOSsJNyYKOCcKNSgJMyYNOCgLNSYN MiUKLyIKLywHKicKKiQBHBYEDAcABAAAAAAAAAAAAgAAAwAAAwAAAwACBAABBAABAwABAwABBAAA BAAAAwAAAwAABAAABAACAwADBAAABAABBAAAAwAAAwAAAwABBAABAwABBAABBAAABAAAAwAAAQAA AwAAAgAAAgAAAwAABQACCgEDHBYPKyUJNzMLOjcPPT0NOzsPQDoKOTIIOC4LPDIQQTgSRDoYSVMh VF1EXWJIYmdHamVNcGtQb2tQb2tWaW9QY2lGWlcxREERQzcPPzMKNywOPDERNSwRNSwPPzMMPDAK Kx8EIxcGEQQABQAAAwAAAwAAAwAAAwADBQACBAADBAAEBgEDAwADAwABBAABBAAABAAAAwABAwAB AwAAAwABBAAAAwABBAAABAAAAwAABAABBwAABAAAAwACAwACAwAAAwAAAwAACAAACgAAEg4KIh0I NTANPDcNPDUQQDoPPzUMPDITOTEVOzMPQDQQQTUWQT4mVFBHXXRcc4tkd4dpe4xveo5we5B0e5V/ h6GAjJ13gpJRY2kxQUcTODcQNDMMNykNOCoMNSoNNysSLykGIBoDDwMABwAAAwAABAACBAABBAAD AwADAwACBAADBQACBgABBQADBQADBQAEBwADBgACBAADBQAEBQADBAADBAADBAADBAADAwAEBQAE BQAEBAAFBgEFBwEEBwEACAAEDgMDFhEIHRcGLxwPPCgNPC0KOSoMOTMOOzUKOykKOigKOSsKOSsP NDEOMzANMy0ONC4NOzIaTENIaHhXeIhVbnMzSk8TQzgPPTILNCkOOCwPNCsOMikOMi0NMSwOMiUL LyIRIRsFEw4ECQEBBQAHBwAGBgAEBgADBAAEBgAGBwAEBAAFBQAEBgAFBwAHBAAHBAADAwACAgAF AwAEAQACAwADBAAPPi8PPzAUQDcSPjQKPjELPzIMQDMLPzIMOi0NOy4PODQMNDEKNCsOOS8KOysK OioMOywKOSoKOSoLOisKNSQKNSQKNSgLNykOMzAOMzALNSwHMCcHHhQAFAoDCQEAAwAAAQAAAQAA AQAAAgAAAgABAwADBAADBAABBAABAwAABAAAAwAABAAABAAABAABBgABBAABAwABAwADBgAAAwAA AwAAAgAAAgABAwABBAACBAACBAABBAAAAgAAAQAAAQAAAwAAAwAABAABBwAAEwcPJxkKLi4SOTkQ QD0SQz8NPy8KPCwPQDQMPDAMOi0RQDMPSkEUUEcoSTwvUUQ5UEg4T0dBUENBUEM0TU0ySkoWPjIS OS0POy0PPC4JNSsKNywNMiMPNSYROTALMSkLJxcCGgwBBwAABAAAAQAAAgAAAQAAAwADBAACAwAC AQABAQABAQABAgAABAAABAABAwABAwAAAwAAAwAAAwAABAAAAwAABAABBAACBAABBAABBAAABAAA AwABAgABAgAAAgAAAgAABAAABQAADwwIHBgHNCsMPDIPPTgRPzoPQz4LPjoYOzIcPzcVQTgVQTgN Q0ogW2NJZHVRbX5TZHRVZ3dWb31UbXpdbntfcH5dbXVNXGQmRj8ZODEKNCsKNCsJNSkJNSkKNSgI MiUKJiABGRQAAwAAAwAAAwAABAABBAABBAABBAABBAADBAADBAAAAwAAAwADAwADAwABBQACBgAD BQADBQACAwABAwACBAABAwAAAwABBAACBgABBQADBQADBgABBQABBAAABgABCAEAEg0IHRcHMjAM OTcOOS0OOS0POy8OOS0KOysKOioKOSgKOSgLNykKNCcPOjAQPDIRODUiTElIX3ROZXoxSU4cMjcR OisPOCkKNCULNSYOMikKLiULLiUMLyYOLyMJKR0RIhUCDwQEBgAEBgAEBAAEBAAEBQAEBQADBgAE BwADBAAEBQAGBwAFBwAGBgAEBAAEAwACAQACAAADAQADAwACAgAPPzEPQDIUQDAUQDAPQDIMPC4O PikQQSwNPy0LPSsNOy4PPTAOOiwOOiwLOS4NOzANOzANOzALNykPPC4KOSYLOicKOCkNPC0ONzIN NTENNTEHLioEGAwADwQABwAABAAAAgAAAgAAAQAAAgACAQADAgACBAACBAACBAACBAAABAAABAAB BAABBAABAwACBAACBAACBAACAwAEBgABBgAABAAAAwAAAwABAwABAwABBAABBAABBAAAAwAAAwAA AQAABAAAAwAABAAABwAAEAQHHQ8KLywSOTUNPjINPjIPRTENQy8RQzcSRDgRQDUSQTcORDwORDwf QzwhRT4nSUQjRT8fQy4mSjUWRDoTPzULNSwPOjAOPDENOzAMNDAMNDAMOi8MOi8SOTcOMzELIRcA EQkAAgAAAgAAAwAAAwAAAgABBAABBAACBAACAwACAwABBAACBAABAwABAwACAwADBAAAAwAABAAB BAABBAACAwACAwADAwADAwACBAACBAAABAAAAwABAwAAAgAAAQAAAgAABAAABAAADwUFGQ8EKiMM NS4QPjkSQDsURDsPPjUWPzcdRz4cRj0eSD8RR0gcVVY9Wlw+W106T1RDWF1AWlc8VVNJVlhGU1U0 UUwpRT8SPzEPOy0MNTIKMi8KMyoJMSgNOCoIMSQKHxYADwcAAwAAAwAAAgAAAwADBAACAwABBAAC BAADBAADBAACBAABBAAEBAAEBAADBQACBAACAwAEBQAEBgADBAAEBAADAwABBAABBQADBAAEBQAD BQADBgABBAABBAAABwABCAEAEg0IHRcHLisLNDEKOTILOzQNPTENPTENOSsLNykMOCgNOSkMPSsK OykKOiwOPzESPzEcTD0yT04pRUQVNy4VNy4WOi8VOC0NOCgOOSkPNCcKLyIJMyAKNSIOMiULLyIM Fg8ABwMEBAAEBAAEBAAEBAAEBAAFBQADBAADBAADBgAEBwAEBwAEBwAEBwADBgADAwADAwADAQAD AgADAwADAwAOPDMOPDMYRzkWRDULQy4LQy4KPyoMQSwTRDITRDIOQCoPQSsKPCILPSMMPDAKOi4O PDEMOi8MNy0POjAKOi4KOS0KOysLPS0KOC8LOTANMCkKLSYEEwUACgAAAgAAAQAAAQAAAQAAAQAB AwADAwABAQAAAwAAAwAAAwAAAwABBAABBAADAwAEBAACBAABAwADBAADBAAGBAAGBAACBAABBAAB AwAAAwABAwABAwABAwAAAgAAAwAAAgAABAAABQAABAAABAAABAABBgABDwIEFAYDIxoPMyoMQzQL QTMNSDkNSDkMQzcNRDgTSTwSSDsMPzkQRT4WQEMXQUQbST8YRjwRQS4TRDAORjMORjMRNy8RNy8M PDISRDoOOTEOOTEMPS8KOy0OODAHLygNHRQACgMAAQAABAABAwADBAAABQAABAAABAABBQABBAAB BAABBAAABAABBAABBAAABAAABAABBAABBAAABQAABAAABAAABAACBAADBgABBAABBQABBAAAAwAE BQADBAABAwABAwAABAAABgEACgUCDwoDIiELLSwKPTkOQT0URzsSRTkZRj4aRz8iRj0mSkEWTUUW TUUnTEMlSUApQzotRz4qSUAnRj0lQ0AkQT8VPjEVPjEKOSQLOyYNOC4KNCsKMywLNS4QOC0KLyUP IA8ACgAAAQAAAQAAAwAABAAAAwAAAwACBAACBAADBAADBAADBgADBgAEBAAEBAADBQABBAADBAAE BgAHBwAHBgAEBQAEBgAEBQAEBQAEBwAEBwADAwADAwABBAACBAABCAECCgICEw8HGRYHKyAPNCkM PDUMPDUOPDEQPzQNPi4KOioMOCYPPCoPPjEPPTAJOS8OPzUWPzAZRDQeRjUYPy8PNygPNygQNTAP NC8OMikQNSwPNSgOMyYLNSQOOScWMyUPLB4KEgYABAABBAABBAADBgAEBwAEBAAGBgADBAACAwAD BwADBwAEBgAEBgACBAADBQADBQABBAABAwABAwADBAADBAALMC0SOTUWQTgZRjwPSjcNRzMKQDAO RTQRSDcNQzEMRjANRzENPjINPjILPDINPjQNPjQKOjAMOjEMOjEQOzcPOjUMPi4KPCwNODAJMisI IR0CGBUBCQAABAAAAAAAAAAAAQAAAgAAAgABAwACBAABAwAABAABBAAAAwAABAADBQACBAABBAAD BQADBQABAwABAwABBAAFBAAEAwADBAACAwAAAgAAAwABAwABAwABBAABBAAAAwAAAwABBAABBAAA BAAABAAAAgAAAwABCQADCwABGw8JJhgKPDUNQDoRSD8TSkEPRj0NQzoSRTkTRjoNRTsRSkATREAT REARQDMOPC8PQS0PQS0MRDEMRDESRTkMPTEMOzgSQz8PPzEJOCoMQDEKPS4ONCMHLBsHDwQABAAA AgAAAwACAwACAwAABAAABAABBQABBQAABAAABQABBAABBAAABAAABAAABAAABAAAAwAABAABBAAB BAAAAwAAAwABBAACBAAABQAABQAABAAAAwAEBQADBAABBQABBQABBAABBQAABwMBCwYDHRYMKiMK PjcORDwQRz4SSUAUQ0AXR0UUSUEUSUEWSEMWR0EbRT4bRT4RRDgQQzcPRT0PRj4NPDcNPDcSPTMT PjQOPysNPioKPCwLPS0OPzULPDIWLzEQKCoEDgMAAwAAAQABAQAABAABBQABBAAAAwACBAACBAAE BAAEBAADBAADBAAEBAAEBAABBQABBAADBQADBgAHBQAGBAADBAADBAACBgACBgAEBwAEBwADBQAD BQACBgADBwABCQACCgEDEAoHFQ4GJBwPMCgKPTUQRT0QQzcSRTkORDAPRjISQzMSQzMOPzMNPjIQ QDoRQTsWQzsUQDkSODIWPDcTQDISPzEPOjALNSwNMykPNSsMNycJMiMKNyoJNSkQLBYIIg0HCwAD BgADBwAECQAFCAAEBwAEBwAFCAAEBQAFBwAHCAAHCAAGBgAGBgAIBwAIBwADCAADBwAEBQADBAAD BQACBAAEIhYPMCQKOTIQQTsPRj0QRz4MRzoNSDsORzUMRTMMQzcORTkPQUANPz4POjkTPj0RQTsP PjgPPzkMPDUPQTELPS0MPC4LOy0ONCwGKiIIGw8ADgQAAwAAAQAAAAAAAgAAAwAAAwABBAABBQAA BAAABQABBgABBgAABAAABAABBAABAwABBQACBgABBgAABAABBAACBgADAwACAgABBAABBAAABAAA BQABBAAABAAABAAABQAAAwAAAwACBAACBAABBAACBAAABQAABgAABgABCgAADwQLHxEKLywQNzMP RT8RSEMQSDoQSDoQRjkRRzoPSDsQSj0USUURRkETRjwOPzUMQTAMQTAMQDMNQTQNRDUNRDUNPDkP PzwPRzkMQzQOQzgKPjMOJyAFHBYDBQAAAwAAAAAAAgACBAABAwABBAABBAABBAABBAAABAABBAAA BAAABAABBAABBAAAAwAABAAABAAABAABBQABBAACBAABAwABAwACBAAABQAABQABBAACBAACBgAB BQAABQAABAABBAAAAwAABgABCgEEIRYKKR4INy0OPjQSSjwTTD0QRUASR0MSSUEPRj4VTEARRzwT RD4TRD4QRjsQRjsQREUPQ0QNQDkKPDQOPzMOPzMNPTEKOi4MOi8OPDEPQDcLOzEZMCgKHxcBBgAA AQAAAgACBAAABAACBgABBAAABAADBAAEBQADBAADBAABBAAABAABBAABBQABBAAABAACAwADBAAE BAADAwADBAADBAACBwADCAACBgABBQABBQACBgADBwADBwABCAACCQABDAQIFgwDHhcKKCEKOC8S QTkPQDIPQDINQTQOQzUPQTgQQzkQQzcOPzMOPzUPQTgPPTINOzAUPDUUPDUPPTAPPTAPOS8POS8Q NSwPNCsOOSsHMCMKMzAMNTIOJA4BFAEBBAADBwAGCgAFCQAEBQAEBQAEBwAEBwAEBwAEBwAEBwAG CQAHBQEHBAAKBQAIBAADBAADBAACAwABAQAABAAABAABFggJIhIHNCcQQTMSQTkVRTwRST0PRzsP SDsPSDsQRT0SRz8PQzwNQDoQQTURQzcRRj4RRj4OQzgOQzgPQDILPC4PPTILOS4LKh8DHxUGFAQA CgAAAQAAAQAAAwAAAwAAAwAAAwAABAAABAABBAABBAADBAADBAABBAABBAAABAAABAABBQACBgAB BgAABQABBwACBwADBQABBAABAwADBAACBAADBQABBAAAAwAABAAABAAABAAABAABBQAABAABAwAB BAABCAABCQAABAAABQAACgAIFgkDIxoQNCsKPzoTSkUUSj8SSD0QST8RSkAPSD8PRz4TSEASRz8W ST0SRTkNQzsPRT0RRDgPQTUPRTgLPzINOjQSQDsRRzgRRzgTPjsIMC0LGQ4ACgEAAAAAAgACAwAD BAACBAACBAABBgAABQAAAwABBQABBQABBAAABQAABQAAAwABBQACBAADBQACAwABAwABBQAABAAB BQABBAACAwADBAADBAADBAABAwABAwABBAABBAAABgAABAAAAwAAAwAABgABCgEHIBQBFwwFKCYO MzEPTD4QTT8PSEgPSEgRSkAQST8TST4TST4VRD4VRD4TQzgTQzgWSDwQQTULPC4LPC4MQDULPzQM OzQLOjMOPjAMPC4WPzkUPDURJhsAEAcAAgAAAQAAAgABAwACBAADBQADBgACBAADBAADBAADBgAC BAACBAACBAABBAABBQABBAABBAAEBQAEBQADBAADBAADBQADBgADBgADBQACBAACBAABBAABBAAB BQABBAAECAAGCgACCgEHEQYBGQ0HIhUONSkONSkOPjITRTkRQzQSRDUQQTsPPzkPPjgPPjgPPjEQ PzIORTQMQzISPjcRPTUPOjIPOjIOODAPOTEQOy8POS0MOi0KNyoTMzIPLi0IEwMACQABBAAFCAAG BwAFBwAEBgAEBQAEBAAFBQAEBAAEBAAGBwAHBwAHBQAHBgAFBQAEBAADBAADBAABBAABAwABBAAD BQAACgABDQEDHA4NKRkPLCoaOTcQRjsQRjsXRT0XRT0URUEVRkMPQTgPQTgPRDcPRTgRTDwQSjsN RjcNRjcNPTENPTEOMC4DIR8HFgsADQQDAwAAAAAAAAAAAAAAAQAAAQAAAQAAAQAAAQAAAgABBAAA AwAAAwAAAwACBAACBAABAwACBAAABAABBQADBAABAwABAwABBAAAAgAAAgABAwACAwADBAADBAAC AwACAwAABAABBQAABQAABAAAAwAAAwAABAAAAwAABgABBwAAAwAABAAAAwACBwABFg0KIxgJLyES OywRTD4STT8RTUYRTUYPRkkOREcRR0UTSUcWRkYUREQNQTcQRjsSRTcPQDIRRDEPQS8RRDgNPjIU PTAUPTARMSgCHRUDCwEABAAAAAAAAgABAwADBAADBAADBAAABAAABAABBAABBAABBgAABQABBAAC BAABAwABAwACBAACBAADBQABAwADAwADAwABAwABAwACAwADBAADAwAEBAABBAAABAAEBAAEBAAA BAAABAAABAABBAAABAABBwAACAMACgUDHBYIIx0IOTgPQ0EORj0PSD8PST8PST8VRzsWSDwWRDoV QTgPRDwPRDwRRDoQQzkMQDEKPi8PPzUNPTMOPjQNPTMRPTMRPTMWNTAPLikFDAUAAQAAAAAAAgAB BAABBAADBAADBAADBAADBAADAwAEBAADBAADBAADBQADBQACBAACBAACBAADBQACBgACBgAEBgAE BQAEBQAEBgAFBQAEBAABBQACBgABBAABBQACBgACBgAGCAAHCQADDAEDCwECDQcJFg8IKSoLLS4N PS8RQzQTPjcVQDkQPz8PPj4QPjwRPz0QQTgSRDoVQzIUQTETPjcQOzMSNzUUOTgSODAPNC0OPDEO PDEQOzcOODMXLiQKHhUBBgAABAADAwADBAAFBQAFBQAEBgAFBwAIBwAIBwAHBQAHBgAKBQAKBQAE BQAFBwAEBgADBAADBQADBQABAwABAwACAwADBAAABgAABwABDAMFEgcDHBoMKCYOOzkQPjwUPzsV QDwSQ0YSQ0YVRz0URjwQSDwPRjoSR0APRD0OPjgNPTcPOTULNDEIHiABFRYEDwMABQAAAAAAAAAA AAAAAQABAwACAwABAwABBAABAwAAAgABAwAAAgABAwABAwABBAABBAADAwACAwABAwABAwADBAAD BAABBAACBAAABAAABAABBAACBAAABAAABAABBAABAwABAwABAwABBQAABAABBAABAwADBQABBAAA AwAAAwAAAwAAAwAAAwAABAAADAQEEgoBHQ4SMyIPPj4RQUEPTEkST00SSkkSSkkSSkkTTEoXRkcW REUQRjsVTEATTjwRTDoURjgTRTcSTEUPR0ATOjcPNDEIGw8ADQMAAwAAAAAAAAAAAgABBAADBQAB BAABBAACBAABAwACBAACBAABBAACBQADBAADBAABAwABAwADBAAEBQABBAABBAACBAABAwAAAwAB BAACAQADAgADBAADBAADAwADAwAEBQADBAADBQACBAABBQABBAABBAADBgAABAEACAQADwoHGxYH LS8ONzkLQzoPSD8PTUUQTkYRSEMRSEMQR0ERSEMPRUUPRUUSQzwSQzwPRTgPRTgPPzoPPzoQQzQP QTMUPDUTOzQWKSgADw4AAQAAAQAAAwABBAADBAADBAACBgADBwADBAACAwADAwAEBAAEBQADBAAD BAADBAADBQACBAAEBQAEBQADBgADBQADBQADBQABBAABBQAEBgAEBgADBAAEBgABBgABBgABBwAC BwAGCQAHCgADCQMBBwEBCAIEDQYEGhsIICEMOTcQPjwRQEATQ0MTQEEUQUMSRD4RQz0OQTsSR0AW QTgTPjQRPDkSPToSPTkUPzsUOjASOC4RPDgPOjUVPDkQNzMTIBIBCwEAAwABBAADBgAEBwAFBQAH BwAFBQAGBgAFBwAFBwAFBwAFBwAHBwAHBwAEBgAGBwAIBwAHBQADAwAEBAACAwABAwADAwAEBAAA BAAABAAACQAACwAADAcHGRMFIiQLKiwUNTEYOzcWPUQXPkUTREYSQ0USQz0PPjkSPDsTPTwOODQJ MS4LKikHJCMDDwoACgUABAEAAQAAAQAAAAAAAwAAAwADAwAEBAADAwADAwAABAAABAABBAAAAwAA BQABBgABAwABAwADBQADBQABBAABBAADBQADBQABBAADBQAABAAABAABBAABBAAABAAABAABBAAA AwABAwABBAAABAABBQABBAABBAABAwABBAAFBAAEAwAAAgAAAgABAgABAgAABgEECgUBEwoNIhgO KjIZOEAPREQQRkYQRkQRR0URVFYQU1URSkQTTUYWTkgWTkgQTEEPSkAPSkAMRjwSTEUORj8PLykD IBoDCAEAAgAAAQAAAQAAAwABBAAABAACBgAABAAABAADBAABAwAAAgABAwADBAAEBQABBAACBAAB BAADBQADBAADBAABBQABBAAAAwAAAwADBAADBAAAAwAAAwAAAgAAAwADBAADBAADBAADBAABAgAB AgADBAABAwADBQACBAAABAAABAAACgYEEQwKKCUSMS4JOzcPQz4NST8PTUMPSEUOR0QQSkkQSkkO SkMPTEQPSEEPSEERRj8SR0AWQT4WQz8WSEERQTsXOTgPLi0IDwoAAwAAAAAAAgAAAgAAAgABBAAC BgAEBQAEBQADBAACAwACBAACBAADBgACBAADBAADBAABAwACAwADBAADBAADBQACBAABAwACBAAB AwABAwADBAADBAAEBAAFBQADAwADBAAFBwAEBgAEBgAHCAADBwIDBgEACQADDgMDFhAFGBMKLjEP NDgPQ0UPQUQVP0EYREYWRz4WRz4PSEEPSEESQDwSQDwSPTwRPDsRPzoPPTgWQTgSPTMRPTURPTUX NCwGHxcDBwAABAAABAAABAADBgAEBwAEBwADBgAEBgEDBQECBgACBgAGBwAHCAAJCAAHBwAEBgAE BgAEBAAEBAABBAABBAABAwABAwADBAADBAAAAwAAAgAAAwAAAwAACQAADAAACgUDDwoDGREFHBQH JScJJykHKSwJLC8JMisJMisKLCMHKSAHIRcBGA8EEQwACwcAAwAAAQAAAAAAAAAAAQAAAQABAwAB BAACAgADAwACBAABAwABAwABAwAABAAABAABBAABBAAABAAABAACBAADBQABBAADBQADBAAEBgAD BwABBAACBAABBAABBAABBQACBAACBAAABAAABAAAAwAABAAABAABBQADBAADBAACBgACBgACAwAC AwADBAAEBQABAgACAwAAAwADBgMBDQQEEQgKGBMRIRsLNCkQOy8PRD8PQz4PSUkSTU0TSUcTSUcV RkAVRkARSUYPRkMNREALQT4NMDEGJygHFgoADAIAAgAAAQAAAQAAAgABBAADBgADBAADBAADBQAC BAADBAACAwADBAADBAADBQACBAAEBgADBAABBAADBQABBQAABAADBAADBAAABAABBAADBAAEBgAB BAAABAAAAwAAAwAAAwAAAwACBAADBQABAwABAwABBAABBAAFBQAEBAACBgABBAAACgADEAMEFxQE FxQHLCIMMigKPTUSRz8MR0MPTEcPSkYPSkYTTEoRSUgPR0YQSEcQSUMORj8QQT4SREAWPjsOMzAM HBMBDwcBAwAAAQAAAQAAAwAAAwAAAgABBAADBQAEBQADBAADBAACAwADBAACAwADBAADBAABBAAB BAABBAACBAABBAACBAABBAACBAABBAABBAAEBQADBAACBAACBAAEBAAEBAAFBAAHBQAFBwAEBQAE BgAGBwACBgABBQABCQEBCQEBEQUEFgoKHRYQJB0QLzgWNz8QPkUSQEcTRT4VR0ASTEMPSD8SR0MP RD8TPj0VQD8VQDwTPjoYRjwVQTgUNyoMLSEIEAUABAAAAQABAgABAwACBAAEAwADAwAFBQAGBgAD BQADBgABBQACBgAHBgAIBwAIBwAJCAAHBwAFBQAFBAAEAwADBAADBAAEBAADAwAEBAAEBAAAAwAA AQAAAAAAAAAAAwAABAAABAAABAAAAwAABAEBCwoACQgBDwQADAICEAUBDwQDEQQDEQQCCgIABAAA AwAAAQAAAQAAAAAAAAAAAQAAAwAAAwAAAgABAwABAwABAwAAAwAAAgABAwABAwAAAwAAAwABBAAA BAABBAADBQABAwABAwABAwABAwAEAwAFAwAABQAABAAABAAABAACAwADBAADBAADBAADBgACBAAB BAABBAAABQABBgABBAABBAADCAADCAADAwACAgAABAAAAwAAAwAABAACAwAEBQAAAwACBwAACAEF DwcEGQ8IHhMIKiEPMikKOS8LOzEOOjsRPj8PPTkPPDgMPj0KPDsHKSoJKywEFg4ADwgABgAAAwAA AAAAAAAAAAABAAAAAwAAAwABBAADBQADBQACBAACBAACBAADBgADBgADBQACBAABBgABBgABBAAD BgACBAACBAACBgABBQAABAABBAADBAADBAABBAABBAAAAwAAAwACBAACBAADBAADBAADBAADBAAD AwADAwADBAAEBgADBwABBQACCgACCgABDgkEEQwDFRENIh4IMSYOOS0MQEEMQEENQz0ORD4RRk0Q RUwSQ0YQQEQUQ0AUQ0ATQzoMOjEOIx0DFQ8BBwAAAgAAAAAAAQAAAgAAAwABBAADBQAEBwAEBwAE BQAEBQAEBQADBAADBAACAwADBQADBQACBAACBAADBAAEBQAEBgADBAADBAAEBgAEBAADAwAEBQAE BgAEBQAEBQAGBgAHBwAEBQAEBgAFBwAFBwAEBQAEBQABBwABCAADCgACCQACCgEEDQMHDwwNFhMJ GisMHi8KLjQRNz0RPDIYRTsSREAQQT4RQT4TREAZQUEZQUEVPTQVPTQaOTcVMjATHhEEDgMDBAAB AgACAgAEBAAHBQAHBQAFBAAFBAAEBgAEBQAEBAAFBQAEBAAFBQAHBAAJBgAJBQAKBwAHBwAFBQAF BQAEBAAGAwAFAwAEAgAEAgAEAwAFBAAAAgAAAQAAAgAAAgAABAAABQAAAwAAAwAAAwAAAQAABAAA BAAABwAAAwAABQAABQAABAAABAAAAwAAAQAAAQAAAAAAAAAAAAAAAAAAAQAAAQAAAgAAAQABAwAB AwABAwABAgABAQAAAwAAAwAAAgAAAgABBAACBAABAwABBAAAAgAAAgABAgABAgABAwABAwABAwAB AwABBAABAwABBAADBQABBQABBQADBQACBAACBAABBAABAwABAwAABAAABAADBAADBAABBAACBAAA AwAAAgAAAwAAAwABAgAEBAAAAwAAAQAABAAABwAACwADEwUFFQkHFgoEHRQKJRsKKR4EIRYHIh4E HRkDHBoDHBoDEAsACgYBBgAAAwAAAQAAAAAAAAAAAAAAAQABAgABAgADBAADBAADBAADBQABAwAD AgADAwACBAADBgACBgABBAAABQAABQABBAABBAABBAABBAAABAAABQAABQABBgACBgAAAwADBQAC BAABAwABAwACAwACAwADBAAEBQADBAADBAABBAAAAwAAAwAAAwACBwAABAAABQABBgAACgAACgAA CgIGFQoEHBAHHxMEJCMLLSwMOTQPPDgPO0UNOEEQNDMSNzUUOjQOMi0JHRkDFREECgQAAgAAAAAA AAAAAAAAAgAAAgAAAwABBAABBQAEBQAEBQABBAABBQADBQACBAABAwABBAABBAABBAABBAABBAAD BAAEBQAEBQADBAADBAAEBAADBgACBAADBAAEBQAEBgACBAABBAACBQABBAABBAACBwADCQACBgAC BgACBgACBgADBwEDCAEDCAQCBwMACAoDDhADFBYHGBsFIBYHIxkJLSoPNTILNT8IMTsONzUQOjkZ NDcWMDIZMCwTKSULGBQBDAgBAgAAAAADAAAGAwACAwADBAAEBgAFBwAFBQAHBwAEBwEEBgAEBAAE BAAGBgAFBQAFBQAGBgAGBAAHBgAIBwAGBAAFBwAEBgAFBAAFBAADAwADAwACAwACAwAABAAAAwAA AgABBAABBQABBAAABAAABAABBAAAAwAABAAABQAABAAAAwAAAgAAAwABAwABAwABAgAAAQAAAQAA AQAAAgAAAQAAAwAAAwABAwABBAAABAAABAAABAAAAwACAwADBAABBAACBAACBAABBAADBQADBgAB AwADBAADBQABAwACAwADBAADBQACBAACAwAEBQADBQACBAAABQAABQAABQAABAAAAwAABAAABAAB BAADBAADBAACAwACAwABBAABBQABBAABBQADBAADBAAAAwAAAwABBAACBAAABAAABAAABgAABwAA BQAABAAACgAACwACCgAFDwMBDwUBDgQBDwIACwAACQEACAABBgAAAwAAAgAAAgAAAQAAAQABBAAB AwACBAACBAABBAABBQADBQACBAADBQACBAACBAACBAADBgAEBwACBgABBAABBQABBAADBAABAwAA BQAABAABBgAABAAABQABBgABBgAABAAAAwAAAwACAQACAQACBAACBAABAwABBAADBAADBAABBgAA BQABBAABBAABBwABBwAAAgABBAAABQAABAAABAABCAEADQADEQIAEAsHGxYGGRYIHBgJHhMJHhMG Gg8HHBEEFg0BEQgCCQAABgAAAQAAAQAAAQAAAAABAQADAwABBAAAAwAABAAABAADBAACAwAAAwAB BAABBAABBAAAAwAAAwADBQABBAABAwACBAACBAACBAACBAACBAAABQAABQABBQABBAADBQACBAAC BgABBAABBAABBAABBAABBAABBgACBwABBQABBQAEBwADBgAEBgAEBQACBgABBQAABQAABwAADAAC EAEBEgcBEQcCFA0DFg8GIBoFHxkJHyELIiQKFxwEERYGDQQABQAAAwAAAgAAAQAAAQACAQADAwAC BAACBAADBAAEBAAEBAAEBAAFBwAEBQAGBgAEBAAFBQAFBQAEBgAFBwADAwAEBAAIBwAHBQAEBQAD BAAEBAAEBAACAwACAwABBAABBAACBwAABQAAAwAAAwABAwABAwAAAwAABAABBAABAwAABAAABAAA BAAAAwAAAwAAAwAAAwAAAwAAAQAAAQABAQABAQAAAwAAAwAAAwABBAADAwADBAAABAAABQABBAAA AwABBAACBgADBQABBAABBAADBwABBQACBgADBAADBAABBAACBAADBAAEBQADBAAEBQADBAADBAAC BgACBgABBwABBgABBAABBAADBAAEBQABAwACBAACBgABBQADBQACBAAABAABBAAABAACBgADBAAE BgABBwAABQAABAAABQAABgAABwABCAADCgADBwEAAwAABwAABwAEBwAEBwABBwABBwAABwAABwAA AQAAAwABBAAAAwABAwABAwABAwAAAgADBAADBAAFBQAGBgADBQADBQAEBQADBAABBQABBAAEBwAD BgADBgADBgACBAADBgADBAADBAADAwADAwACBAACBAABBQABBAABBgABBwAABQAABQABBgAABQAB BAADBQADBAADBAAABAABBgAEBAAFBQAEBQADBAADBAADBAABBQABBQAAAwAAAwAABQABBgAABgAB CAADCAECBwADCgMDCgMDCgQHDwgECgADCQABCgADDAIBBQAABAABBAABAwAAAQAAAQAAAwAAAwAC AwADBAAEBQADBAACAwADBAACBAACBAABBAABBQABBwACBwADAwADAwAEBAAEBAACAwADAwADBQAC BAABBAABBQACBAACBAABBQACBgAEBwADBgADBgADBQADAwADBAAEBAAEBAADBgACBAACBgACBgAE BgAEBgAGBwAEBQACBQACBQAABwABBwAABgADCQEACgMACAEACAABCgEEDgMCCgEBCwEBCwEDBwMA BAABAgAAAAABAQABAgAAAgABAwAEBAAEBAADBAAEBgAHBQAHBQAHBQAHBQAHBwAEBAAEBAAEBAAF BQAEBAAHBwAHBwAGBAAGBAAFBQAFBQAHBgAGBAAGAgAHAwACAgACAgADAwADAwAAAgAAAgABBAAB BAAAAQAAAQAAAwAAAwAAAgAAAQAAAwAAAwAAAgAAAQABAgABAQAAAQAAAQAAAQAAAQAAAwABBAAB AgABAQAAAQABAwABBAABBAABAwABAwABAwACBAABBAABBAABAwADBAADBAAEBQACBAACBAAABAAB BAABAwACBAADBQACBAABBAACBAADAwABAgAEBAAEBAABAwABAwACAwAEBQAFBAAFBAADBAADBAAD BAADBAABBAAAAwADAwAEAwADBQACBAACBAAEBgACBQABBAAABAABBgAABAAABQAEBAEDBAACAwAD BAEEBAEDAwACAwACAwAABAAAAwAABAAABQAAAQACAwADBQAAAwABAwACBAAEBwACBAABBAABBAAB BwACBwACBAADBAADBgAEBwAABQAABQAECAABBQABBgAABQACAwADBAACBAADBQABBAABBAAEBAAE BAAABQABBgAABAABBQAABAAABAADBgADBQACBAADBAAAAwAAAwAABAACBgADBAADBAABBAABBAAC BAACBAACBAACBAAABAAABAABBQABBgEBBgABBgAEBwEDBQADBQAEBgAEBwADBQAABAAABAABBQAA AwAAAQABAgAAAQAAAQAAAQAAAQAAAgAABAAFBQADAwAFBAAGBAACBgACBgADAwADBAAEBQADBAAD BAACAwADBgACBAAGBAAGBAAFAwAEAwAEBQAEBQADAwADBAAEAwADAgAEBAAGBgAEBgAEBgAEBAAE BAAEBQAEBQAEBQAFBgEEBgEDBAAEBAAEBAAEBAAFBQAEBwACBAADBQADBQAEBwAEBwAABAADBwED BwQABAEEBQAGBwEBBgAAAwAAAwAAAwAAAwAABAADBAADBAAAAwAAAwAEAwAEAwAFBAAGBAAHBwAH BwAHBQAHBgAIBwAHBQAHBwEFBQAFBwAGBwAGBgAGBgAKBgAJBQAGBgAFBQAEBQAGBwAIBwAHBgAK BQAJBAABAwABAwACAwACAwAABAABBAABAwABAwABAwACAwADAwADAwABBAABAwABAwAAAwAAAQAB AwADAwACAgAAAgABAwABAwABAwAAAwABBAABAgABAgACAwADBAABBQABBAACBAACBAACAwACAwAB AwABAwADBAADBAACBAABBAABAwABBAAABAAABAADBAADBAABBAABBAACAwACAwADBAADBAADBAAD BAABAgACAwACBAADBQAGBAAFBAABAwACAwADBAADBAABBQABBAAEAgAHBAAEBgACAwADBQADBgAD BAACBAAEBQAEBQADBQADBQAEBAAEAwABBAADBQABBAAAAgABBAACBAACCQAABAAABAAABAABAwAC BAABAgABAgACBAAEBgABBwAABAAEBwADBQAEBgAFBwADBQADBQADBAAFBwABBQABBAAHBwAGBgAD BgACBAAABAACBgACBAABBAADBQECBAAAAwABBAAABAAABAAAAwABBAABBAABBAAEBgADBAADBAAD AwAABAAABAAAAwACBQEDBAACAwABAwACBAAABAABBAADBAADBAABBAABBQABBAABBAAABAABBgED BgADBgABAwADBQADAwACAwABAwABAwAAAQABAgADAgAFBAAAAgABAwAAAwAAAwAAAwABBAEEBQAE BgAGBAAHBQADBwABBQAEBAAEBAACBAACBAADBAADBAAEBgADBAADAwAEBAAGBAAGBAAFBwAFBwAC AwAEBAAEBAADBAAHBQAGBAAEBQAEBgAGBgAFBQAEBgAEBQADBAAEBQAEBgEDBQEFBQAGBgAEBQAE BgADBgABBAAEBwEDBQAEBwAEBwAABAADBwEDBgEDBwIBBgEABAABAwABAwAAAwAAAwAAAwABBAAC BAEBAwACBAADBAAEBgAJCgEHBwEGBgAHCQEFBwAEBQAGBwEJBwAHBgAHBgEFBAADBAADBAAFBQAH BwAHBwAHBwAHCQAHCAAGCQAEBwAGBgAFBQAHBQAFBAABBAABAwADAwADAwABBQABBQAABQAABQAA BAABBQABBQABBQAABQABBgAAAwAAAgAAAgAAAgABAwAAAgABAwABBAAABAABBAADBAADBAADAwAD AwACAwADBAACBgABBQABBAACBAABBAACBAABBAABBAAEAwAEAwACBAABAwAAAwAAAwABBAACBAAA BAAABAACBAABAwACAwACAwABBAACBAACBgACBgABBAABBAABAwABAwADBAADBAABBAACBAABBAAB BAADBAADBAADBAADBAACAwACAwACBAADBQAAAwAAAwABBAADBQADBAADBAAABAABBAACBAADBgAD BAABAwADBQACBAABBAAAAwACAwACAwACBAADBQAABAAAAwACBAADBQABBQABBAAEBgAEBgAFBQAG BgADBwACBgADBAADBAAEBAAFBQAEBQADBAABAwACAwAEBAAGBgADBgABAwADBAADBAABBAABBAAA BQAABAABBAABBAADBAADBAACBAADBQADAwADAwABBAABBAABBAABBAABAwABAwABAwADBQABBgAB BgABBAAABAABBAABBAABBAAAAwAABQABBgADBAADBAABBAABBQABBQABBAABBAAABAAABAAABAAC AwACAwAAAwAAAwABAgADAwADBAADBAACBgACBgAEBQAEBQACBAACBAADAwADAwACAwADBAACBAAC BAACBAACBAADBAADBAAEAgAEAgADBQADBgACBAACBAADBAAEBAAEBAAEBQAEBAADAwAEBQAEBQAE BAAEBAADAwAEBAADBgACBAAEBAAFBAADBgADBgADBwACBgABBQABBQABBQACBgACBAACBAAEBAAE BAAAAgAAAgABAwABAwACBAACBAACAwACAwABBAABBAABBAABBAADBgAEBwAGBgAEBAAEBQAEBQAD BgADBQAEBQAEBgADBwABBQAAAwABBAAEBgAEBgAEBgAFBwAFBwAFBwAFBwADBAAEBAAEBAADAwAD AwABAQACAgADAwADAwADBAADBAABBAABBAABBAACBAABBgABBgABBQADBwACBgABBAACAwADBAAB AwABAwAABAABBAAABAABBQADBAADBAADBAADAwABBAACBAABBQABBQAABAAABQABBAABBAABAwAC BAAEAwAEAwACAwABAwAAAwABBAABAwABBAAAAwAAAwABAwADBAACBAACBAADAwADAwACBAACBAAC BAADBgABBAAABAADAwADAwAEBQAEBQABBAABBQADBAADBAAEBAACAwADAgADAgADBAAEBgAAAwAB BAACAwAEBQAEBQADBAAABgAABgACBAADBgADAwACAwADBAADBAADBAADBAAEAwAFBAAEBAAFBQAC BgABBAAEBQAFBwADBAAEBQAFBwAHCQAEBAAEBAADBAAEBgAEBQADBAADBAAEBQADBAACAwAEAwAF BAACAwADBAACAwADBAADBAADBAAEBAAEBAACBAABBAADBAADBAACBAACBAABBAADBgAEBAADAwAE BAAEBAADBAADBAADBQACBAABAwADBAADBQADBQACBAADBQABBAADBgACBgABBQAABAABBQABBAAC BAACAwADBAADBAADBAACBAACBAAABAAABAADBAADBAABAwACBAADBAADBAAHBQAFBAAEBAAEBAAH BQAHBQADBAADAwAEBAADAwACAgADAwADAwADAwADBAAEBQAFBQAEBAACBAABAwADAwADBAACBAAC BAACBAADBgADBAACAwACAwACAwAFBQAEBAADBAAEBQAEBAAEBAADBAADBAAEBAAFBQABBwACBwAD BgADBgADBgADBgAEBwADBgADBAADBAAEBAAEBAADAgADAgABAwABAwADAwAFBQADAwADAwADBAAE BQAEBgAEBQAGBgAEBAAHBAAHBAAEBgAGBwAEBgADBAADAwAFBQAFBwAEBQABBAABBAAFBQAEBAAE AwAHBQADBAADBAAFBQAEBAAGBAAEAwAFBAAFBAACAgACAgAEAwAEAwADBAADBAADBAACAwABAQAC AwAAAwABAwACAwADBAAEBwADBgAEBQADBAADBAADBAABBQABBQABBAABBQACBAABBAADBQACBAAC BAACBAABBAABBAABBQABBAABBAABBAADBAADBAADBAADBAAEBAADAwADBAADBAABBQABBQABBAAA AwABAgADAwADBAADBQEDBAADBAAEBQAEBQADBAACBAACBAADBgAGBAAFAwADBAAEBgAEBwADBQAE BQAEBQADBQACBAADAwADBAAFBwADBAAEBgADBAADBAAEBgACBgACBgABBwABBwABBQADBwACBAAC BAAEBgADBAAEBAAFBQAEAwAHBgAEBgAEBQACBgABBAAEBAAGBgAJBgAKBwAGBwAFBwAGBwAEBQAD BQADBAAEBgAEBQABBQACBgADBgADBgADBAADBAADAwADAgADAwAEBAADBgAEBwAEBQAEBQADBAAE BQADBAADBAABBQACBgADBgADBgADBAADBAAEBAAGBgAEBQAEBQADBAADBAAEBAAEBAAEBQAFBwAH BQAGBAAEBQADBAAEBwACBAABBAACBQABBAABBAADAwAEBQADBAADBAADBAADBAABBAACBgADBAAE BQACBAACBAAEBQAEBgAHBgEEAwAFAwAFAwAFBAAEAwADBAABAgADAwAFBgIGBgAFBQAGBgAEBAAE BQAEBQAEBgAEBgAEBQAEBQAEBAAEAwADBAADAwAEBQAEBQAEBQAEBQAEBgAEBQAHBwAHBwAHBQAG BAAGBAAHBQAHBAAHBAAEBgAEBQADCAAECgEHCgAFCAAGBgAFBQAEBgAFBwAEBwAEBwAFBQAEBAAD BAACAwABAwABAwADBAAEBgAFBQAHBwAKCQAIBwAIBwAIBwAEBQAFBwAKCQEIBwAIBwAIBwAHCAAH CAAEBgAFBwAKBwAKBwAEBwAEBwAHBwAHBwAGBwAHBwAFBwAEBQAGBAAHBgALBQAKBAAKBQAKBAAD AwACAgADAwABAQABAwABAwABBAABBAABBAABBAABAwACBAACAwADBAAAAwAAAwABBAABBAACBAAC BAABBAACBAADBQADBQAABAABBAABBAAABAABAwABAwABAgABAgABBAACBAABAwABAwABBAABBAAA BAAABAADBQADBQABBAAABAABBAABBAABAwABBAABBAABBAADBAADBAADBAADBAACAwADAwAAAwAA AwABAwADBAAEBAADAwADBAAEBQAGBgADAwADBgADBgADBQADBgACBAADBQADBgADBgADBwABBQAC BAAEBwADBwACBgADBgADBgABBAABBQABBQABBAADBAADBAADAwAFBQADAwAEBAADBgADBgADBAAE BQAFBwAEBgALBwANCQAHCAAEBgADBwAECAADBAADBAAEBwADBQABBgABBgADBQADBgADBgADBQAD BAADBAAEBAAEBAADBQACBAAEBAAEBAAEBwADBgADBQADBQADBgAEBwABBQABBQADBAADBAACBgAC BgAABQABBgADBAAEBgAFBwAGBwAEBwADBgACBAADBgADBAAEBAADBgADBQACAwADBAADBAADBAAC BAADBQABBQABBQACBgABBQADBAAFBwAEBwADBQAAAwABBAACBgADBwADBQEEBgEFAwAGBAAEBQAE BAACAwACAwADBAAEBQEFBwAEBQADBgACBAADBAAFBwACBAADBgAGBAAHBQAGBgAFBQADBAADBAAF BgEEBAAEBQAFBwAEBwADBgAHCQAGCAAFBQAGBgAHBQAHBQAHBAAHBAAEBQAEBQAEBQAGBwEGCQAG CQAGBwMEBQEDBgAEBwAGBwAEBgAEBAAEBAACBwAABAACBAACBAABBAADBwEFCAAFCAAEBwADBgAH BgAIBwAGBwAHCAEHBwEEBAAJBgAKBwEJBgAIBQAFBwAHCAEGBwEGBwEFBQAGBgAGBwAGBwAEBgAF BwAICQAHCAAGBgAJCQAKBwEIBQAHAwAIBAAGAwAFAwAEBAAEBAACBAACBAADBQABBAAABAAABAAA AwAABAADBAADBAACAwACAwABAwADBAAEBAAEBAAFBAAEAwACBAABBAACAwADBAAEAwADAQABAwAB AwABAgABAgABBAACBAADBAADBAADBAACAwABBAABBAAEBQADBAACBAABBAACAwACAwADBAACAwAA BAAABAABBAABBAABBAABBAABBAABBAADBQACBAAAAwABBAADBQACBAABBQACBgADBQABBAADBAAD BAADBAAEBQADAwABAgAEBQAEBgADBgABBAABBQACBgADBQADBgADBAADBAAEBQADBAADBwABBAAA BAABBAAGBAAGBAACBAADBQAEBAAEBAAEBQADBAAGBgAEBAAHBwAHBwAGBwAEBQAEBwAEBwABBAAD BQADBgADBQACBAACBAAABAABBQADBgADBQABBAADBQAEBAADAwADAwADAwADBAADBAABBAADBQAB BQACBgABBQACBgADBQACBAABAwABBAAABAAABAADBQACBAAFBAAGBAABBQADBwABBQABBAABAwAC BAACBAADBAABBwABBgADBQADBgADAwADAwADBAAEBQADBAADBAADBAADBAAFBQAFBQADBQADBgAD BgACBAADBQACBAACAwAEBQAEBAAEBAADBAADBAACBAADBQADAwACAwACBAACBAADBAABAwAEAwAG BAADBAADBAAEBAAEBAADBgADBQADBAADBAABBAAABAABBAABBAACBAADBgADBQADBgAEBQAHCAAI BQAHBAAFBQADAwAFBAAFBAADAwAEBQAGBwAEBQACBQACBQABBAADBwAHBwAFBQACAwAEBQADBwAB BQACBAADBgADBAAEBQAFCAAEBwAHBgAHBgAEBgAFBwAEBgAEBgAFBwAEBgAEBgAFBwADBgADBQAE BgAHCAAEBQAEBAAEBQAEBQAFBwAFBwAEBQADBAAHBgAHBQAFBQAGBgAEBgADBAADAwADAwACAwAB AwADAwAEBAABBgAABQABBAAABAABBAABBAADBAADBAABBQABBAADBQACBAAAAwABBAABBAAAAwAD BAAEBQADBgADBQACBAADBQAFBAAFBAACBgABBAABBAACBAAABAAABAABAwACBAADBAADBAADBAAD BAACBAAAAwABAwABAwADBAADBAADBAADBAABBAABBAAABAAABAABBQABBQABBAABBAABBAABBAAA AwAAAwACBgACBgABBQABBQADBAACAwACBAACBAADBAADBAABAwABAwAEBQAFBwADBgADBQAECAAE CAAEBwADBgACAwACAwABBgABBgACBgABBQAABQABBgADBgACBAADBQAEBwAEBQADBAADBAAEBQAE BAAEBAAEBwADBgAEBQAEBgAEBwAEBwAABAADBwAFBQAFBQADBQABBAABBQABBQACBgABBQACBAAC BAAEBAADAwABAwACBAADAwAEBAACBAACBAABBQABBAACBAADBQADBgACBAACBAADBgABBwAABQAD BQACBAAEBAAEBAADBAADBAAABAAABAADBQADBQABBQABBQAABAAABQABBQACBgADBQADBgADBAAE BQAFBwAEBgAFBQAEBAACBgABBQABBAAEBwAEBwADBQAEAwAEAwACAwACAwADBAADBAADBAADBAAB BQABBQABBAABBAABBAACBAABBQABBAADBAACAwAEBgAEBQADBAAEBQADBQADBQADBAADBAAAAwAB BAACBQABBAADBQADBQADBgAEBwABBAABBAADBAAEBAAEBAAEBAAFBAAFBAADAwADAwADBAABAwAA AwAAAwABBAACBgAEBQAEBQADBAADBAAABQABBgABBgABBwADCAAFCgAFBwAGBwACBgADBwAFCQAF CQAFBQAEBAAIBwAHBQAHBwAHBwAEBAAFBQAEBAAEBAAFBwAEBgAEBQADBAADBgADBgACBAABBAAE BQAEBgAFBwAFBwAEBAAFBQACAwADBAACAwABAwABAwADBAAABQAABAACBAABBAAABAAABAAABAAA BAAABAAABAABBwABBgABBAACBAABAwABAwADAwAEBAAEBQADBAABBAABBAADAwADAwACBAACBAAA BAAABQAABAABBgABAwABAwADAwADAwACAwADBAADBQABAwAAAwABBAABBAABBAACBAACBAABBAAB BAAABAABBQADBQACBAABBQABBQAAAwAABAABAwABAwACBQABBAABBQADBwADBAACAwACBAADBQAB BAAAAwAABQABBgADBQADBQADBAADBAAFCAADBgAEBwAEBwABBQACBgABBgACBwADBgADBQAEBQAE BgADBQADBQACBQADBgADBgADBgADBQAEBwADBgADBgAEBwADBQAFBQAGBgAEBQAEBQACBgACBgAG BAAGBAADBQEDBAADBQADBQACBgACBgABBgABBwACBgABBQABBAABBQADBQADBgABBAABBQABBgAA BAADBQADBQADBQADBQABBAABBAABBgABBgADBgADBQAFBAAHBgADAwAEBAACBAAEBwAEBgAEBQAB BQABBQADBwACBgADBAAEBgADBwABBQABBQACBgAEBgAFBwAFBwAEBgABBwABBgABBAACBQEEBwAD BgAFBAAEAwAEBAAEBAAEBwADBgADBAADBQACBgABBQABBAABBAAAAwABBAADBwACBgAEBQAEBgAE BgADBAADBAADBAAEBQAEBQACBgACBgAEBwADBQADBAADBAAEBQAEBQAEBgAEBgADBwADBwADBwAD BwAFBwAGBwAEBwIDBQEDAwAEBAEEBQAEBQAEBgADBAAFBQAHBwAEBAEDBAAFBQAFBQAEBgAEBgAD BgAEBwAHCAAHCAAEBwAFCAAEBwAFCAAGCAAGCAAFBwAFBwAHBwAFBQAHBwAHBwAHBwAHBwAGBgAG BgAJBwAIBwAEBwAEBwAFBwAEBgAEBQAEBQAEBwAFCAAHCgAGCgAFBwAEBgAFBwACAwABAwADBAAD AwADAwAAAwAAAwACBAACBAAABAAAAwACBAACBAAABAAAAwAABAAABAAAAwABBQACBAABAwACAQAD AgABAwABAwAABAAABAADBAACAwAABAAABAAABAAABQABBQABBAABBAAABAABBAABBAABBAABAwAB BAABAwAAAwAABAAAAwAAAwABBAACBAADBQACBAACBAACBAADBAABAwADBAAEBAADBAAEBQADAwAD AgADBAAFBwABBAABBQABBAAAAwAABAAABAAABwAABgAABQAABwABBAABBQADBQACBAAEBQAFBwAE BwADBgACBgADBwACBgADBwAEBgADBAAEBwAEBwAEBAAEBAADAwEEBQMDBQEDBAABBAADBwADBwAC BgACBgADBwAGBwAEBQAEBAAGBgACBQABBAADBAAEBgABBAABBAACBQECBQEDBQACBAABBAABBQAB BQABBQABBQABBQADBQADBQAABQABBgACBgABBAADBQADBQADBQADBgACBgABBQABBwAABQABBAAB BAADBQADBgAHBgAHBgABBAADBwADBAADAwADBAACBAAEBQAEBQADBAADBAAEBgADBAADBAADBAAD BQADBQACBgABBAABBAABBAADBAACBAADAwADAwACBAACBAACAwACAwADBAADBAADBQAEBgACBQAB BAACBgABBAAABAABBAADBQADBAADBAAFBwADBgADBQADBAADBQADBAADBAAEBQAEBQADBgADBQAD BAADBAAGBwEEBQAEBAAEBAAFBQAEBAAEBQAEBQAEBgADBQADBQECBAADBQADBQAFBwAEBgAEBQAF BgEEBgAEBgAEBQADBAAEBQAGBwAEBgAEBQADBwAECAAFBgEEBQAEBwEGCAIFBwAFBwAKBwALCAAH CAAGBwAHCAEGBwAEBQAFBwAGBwAFBwAFCAAFCAAFBwAGBwAEBwAEBwAFBwAFBwAEBgAEBQAFCAAF CAAHCQEHCQEGBwAEBQAEBQABAwACAgACAgABAwACBAAAAwAABAADBAADBAACBAACBAAEBAAEBAAB BAABBAADBAADBAABBAABBQADBAABAwADBAAEBQABBQAABAAABAAABAADBQACBAAAAwABBAADBQAD BQADBAADBAABBgABBwAAAwAAAwABBAAABAADBQABBAAAAwAAAwAAAwAAAwABBAAAAwADBAADBAAD BAADBAACBAACBAAEBAAEBAADBAADBAAEBAADAwAHBgAHBQACBAACBAADBgEAAwAAAwABBAEBBgAA BAAABAAABAABBAAABAADBgADBgAEBAAFBgECBgABBAABBAABBQABBQADBwADBQABBAABBAABBQAE BAAFBQADBQADBgAEBQADAwADBQADBgAEBwACBAADBAADBQAFBwADBAAHBQAGBAABBAABBAACBAAB AwABBAABBAABBQABBQABBAACBgAABAABBAADBQACBAABAwACBAADBQACBAABBAABBAADBQACBAAD BQADBQACAwADBAADBgACBAABBQABBAABBAABBAACBgADBwAIBQAIBQABBAACBQECBAACBAADAwAD AwAEBgAEBQABBQACBgADBQABBAADBAAEBgADBQACBAADBgADBQACBQECBQEDBQECBAACAwADBAAE BQADAwABBAABBAABBAACBQADBAAEBAEEAwAEAwADBAADBAABBAACBAACBAADBQADBQABBAACBAAC BAACBAABAwABBAADBQAEBAAEBAAEBwADBgAEBAADBAAEBAAEBAAEAwAEAwAFBAAFBAAGBAAHBQAE BQADAwADBQEDBQEEBwAEBwAEBgAFBwAFBgEEBAAEBwADBgAFBQAEBAAEBgAEBgAEBwAEBwAEBwAF CAAFBgIFBgIDBQEEBgEFBQAHBwAGBgAGBgAEBQAFBwAGBgAGBgAEBgAEBgAEBwAFCAADBwEDBgAE BwAEBwAHCAEEBgACBAAEBwEEBQAEBQAEBwAEBwAFBQAFBQAEBQADBAAEAwADAwABAQABAQACAgAD AwADBQADBQAEBQADBAADBQAEBwAEBQADBAACBAADBQAEBQAEBQAFBwAGCAADBAADBAAHBQAIBwAH CgAEBwADBAAEBQADBgADBQAEBQAEBgAEBwACBAADBAADBAACBgABBAACBAACBAABAgACAwAHBAAH BAADAwADAwAEAwAEAwAEBAAEBAAEBAAEBAAEBAAEAwABAwADBAAEBgADBAADBAAEBQAEBAADAwAE BAAEBAADBQADBQADBwAABAABBAACBQAEBQAEBQACBAABBAADBAAEBQAGBAAHBQAEBgAGBwADBgAE BwAEBwAEBwADBgACBAADBAAEBQADBAADBQAEBAAGBgADBwAEBwAHCAAEBQABBQACBgADBQAEBwAD BAADBAADBgADBgAEBAAEBAABBAADBgEDBQABBAAEBAAEBAADBAAEBQABBQABBAAABQAABQADBAAD BAAEBAAEBAADBAADBAAAAwABBAAEBAACAwADBAADBAAFBAAFBAADBgADBQADBAADBAADBQACBAAD BAAFBwAJBgAHBAABBAADBQACBAACBAADAwADAwAEBQADBAAEBQAEBgAEBAADBAADAgADAwAEBQAE BQAEBQAEBQADBwACBgADBQEDBQEEBAADBAAEBQADBAADBAAEBQADBQADBgAEBAEDAwADBAADBAAG AwAGAwACBAACBAABBQABBQABBQABBAAEBAADBAABBAAAAwACBAACBAAEBAAFBQAFBQAEBAAEBQAD BAAEBAAGBgAEBAAEBAAEBgAEBgAGBgAGBgAEBgAEBgAFBgEEBAAEBQAEBgAEBwEDBAAGBAAHBQAH BAAHBAAEAwAGBAEGBgAGBgADBQAEBwALBwAKBQAEBgAEBwEFBgEDBAAFBwAFBwAFBQAEBAACBgAE CQAGBwAEBQAFBQAEBAAFBwAHCAAGBwAFBwAEBwADBgAEBAAEBAACAwADBAAHBQAHBQAGBQEFBAAE BgADBAADBAADBAAEAgAFAwABAQABAQADAwADAwAHCAAEBQAHCQEGBwADBAAEBQAFBQAFBQAMCAAK BwAICgAGCAAKBQAQCgAHCwAHCwAKBwANCgANCgAKCAAMCAAKBwACBwADCQAFBwAJCgIKCQAJCAAI CQAGBwADBQADBQAEBAAGBgAGAwAHBAAUBQAWBwAHBwAGBgACBAADBQAFBQAGBgAEBAAFBQAFBAAE AwADBAACBAADBgADBQAEBQAEBgAEBQAEBAAFAwAJBwIHBgAKCQAKCAAKBwAGBwAKCwEMCQEJBgAD BgAEBwAEBAAEBAAGBAAHBQAEBQAFBwAHBwEFBQAHBgAKCAEHBwAHBwAFCQAKDgAFBwAHCAAEBgAE BgAEBwAFCAAJBgAOCgIICgAHCQANDQALCwAGCAAJCwAJBwAJBwANCQAMCAAJCAAKCgAJCgEGBwAJ BgAKBwAEBwADBgACBgACBgAEBQAICQMDBAADBAAFCgEHCwIHCQEDBAAABAACBwADBgADBgADBAAD BAAEBwADBgADBAAEBgACBgABBQACBQACBQAFBQAICAETCwENBgAHBAAJBgAHCAADBAAFBAAHBQAL BgMLBgMKCAEIBwAEBQADBAAEAQAHBAAQCAEQCAEOCQMHAwAHCAAHCAAHBgAHBgAFBQAGBgAHCAEH CAEQCAEQCAEOCQELBwAHBQEHBQEEBgAEBgAFAwAKBwMFCAAFCAAGCAAGCAADBQADBQAFBAADAwAD BAAEBQADBwADBwAHBwAHBwAGBQEEAwAHBwAICAANBgAPBwAPBwARCgAKDwAKDwADCAABBwAHCQAF BwAPBwAPBwAJBwAJBwADBAADBAADBwADBwALBwAKBgAFAwAHBQEHBwAEBAAFBwAFBwAWDwAUDQAK CgAKCgAHBgAKCAEKCAAJBwAKCAAJBwAOBwAUDQQMDAMICAAGBwAFBwAKCgAKCgAQBQAWCgMSCgMP BwAPCgEMCAAJCAAPDgELBwAJBAAFBAAJBwANDwAODwAHCgAHCgAEBAADAwABBAABAwABAwABAwAs HgEnGQAVFAQMCwAHBQAJBwARDAAVDwAqHAAqHAAiHQAdGAAcCwAlEwEiHAMYEwAaEQAaEQAgEQEg EQEgFgAiFwEaFQIXEgAWDAAeEwEqGAAsGgElFgEeDwAUCAARBgAPBwATCgAgEQAiEwEiGQEgFwAO EAIHCQABBAAAAwABBQABBAADBAAEBgAEBQADBAADBAACBAADBQADBQACAwADBAADBAADBAAMAgAV CQEZCwAoGAYqHwYhFgAoGAQlFgMYEgQQCgAMDQAODwEHBwEFBQAEBwAGCAAJCgAHCAAVCgAWCwAj EQMkEgMhFgAjGAEZGAAVFAAEBgAFBwAECAABBQAKBwAQDgEYEgAdFgEeGgEeGgEhFgAjGAEdEgAa DwAjDgAvGAQyGgA1HQIvGgQsFwMWEgAPDAALBAAUDAEFCgEBBQAEBQALDQMJDQYFCQMICQAODwMT EwIVFQMfFQMdEwIJCgQHCAMDBAAEBQADBAADBAADBgAEBwAEBAAFBgEDBQADBAADCAECBwADCQAM FAMdFAAnHQciFwUiFwUeDQIeDQIYDQAbDwEWEQIWEQIcEgQcEgQRDQEMCAAOCgAcGAYoFwcmFgUh FAYbDwILEQEJDwAKBwAKBwAFCAAHCgAPDgIPDgIXEAIfFwctHA4mFgghFQcYDQERCgARCgAgEAEm FgUpFgYnFAQUEgERDwADBwADBwADBQADBAADBwACBgACBgAECAAEBwADBgAEBAEDBAAJBQAVEAQi FgApHQQsHAAvHwIcGQEUEQANDwMOEAMaFgAkIAQvHgUxIAcpGw8aDgQICgEKCwMNDAATEgMhGwgf GQcKDgEECAAUCAAZDQAtHAAwHwIyIgQyIgQuHgklFgMPCwAXFAMXEAEUDQAUEQAYFgEyIQcrGgMa EgUTCwAPCAASCgAoFgcnFQcQCQAZEQQlDgEwFwk5JAc4IwYyIwgpGgIgEAEmFgUxGwE3IAQ4JgU1 JAQlGAQgFAEICQAEBQADBAADBAABAgACAwA3JwA3JwAnIwQdGQAPEQAQEwAhEAAuHAQ5JAA+KQE4 KwAwJAArIQAvJQEsJQArJAAtIwArIQAtIgEpHgArJAArJAAzHwMzHwMwIQA0JQE0JwEzJgE1IgA1 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 Kgc1IQQtGQAmFwEvIAczJQQ5Kgc7KxAwIQgsFQQsFQQvIAQ1Jgk0Iwo4Jg0rHQofEgIKCwMFBwAD BgAEBwAEBgAEBgADBwADBwAGBAAKCAAHBwALCwIWDgIdFQcqHgctIQk6JQU7JgY1HAQxGAErGgkf DwEUDwAWEQAOEQAOEQAVFAAXFgEfFAIfFAIhDwAjEQANDgEODwEaEwMdFgQkFgAhEwAoFgEuHAUo GgAsHgEkGQAiFwAhFgIfFAEcGggWFQQTBwAWCgEbDwEZDgAbFAIbFAITEQEQDwAOEQALDwAQDwAP DgAKDAIQEgcbFwcWEgMVDQITCwEXDAAcEAEiEwEpGQUqGwQiFAAeEwcWCwEHBwAJCQAMCwAIBwAE BAACAgACAwADBAASFAETFQIKDAMHCQEKCgYICQQIDQMLEAYMEQAMEQAPDgATEQEQDgEUEQMYGQkR EgMdFgMdFgMNEAATFgMVHwMVHwMZFgEZFgEWFgQTEwIUEwQREAMbGAEbGAEUEgETEQAiEwIiEwIk FAQlFQQdHgYWFgEKDAAICgADCAMDBwICBwABBgAFCAAFCAADCQABBwADBQADBQAFCAAKDQQPDgEU EwQRFAAXGgMaEwEcFQMWFAIUEgEWDwAYEQESEAAWFQMWCgAbDwQhFAMrHQorGgkyIQ8qIg0kHAgd FQoYEAYjFwMrHwkjFwMlGQQmHwYjHAQXFgcPDgEDCwMEDAQIDgAFCgATEAAWFAAdHQAiIgMpGwkr HQoqHhAkGAsVHA4GDAESDwQeGw4ZEwYWEAQMDQEREgUSEAAZFwQhGQkYEQMTCAIcEAkhEAYlFAkQ EQMZGgooHQQpHgUmHQQoHwUfFgoTCwEGBwAGBwAEBwAHCgEEBwMCBQEEBAEHBgMGBwEDBAABBAED BgMGCQEHCgEHCgALDwMUFAMREQEHDQEGDAELDAEZGgwjHAUjHAUqHwQnHAMVFAQREAIREAAZGAMs HQUsHQUoHQMgFgAWEgAWEwEhFgEhFgEkGAQhFgIWEQEXEgEUEgEWFQMeFgQgGAUgEwcbDwQeEQcb DwQiFwEnHAQmEwkkEQcLDgEICgAEBwEDBQADBQAFBwEDCQEDCQEDCAMDCAMHBAAKBwEKCgYKCwcP DAUSDwcVEwETEQAXFQYWEwQTCwITCwIQEwUMDwIKCgAMDQASDwIPDQATCgMWDQULCwIKCgEICgAK CwEJDAMMDwYODwcODwcQDwAODAAWEgAbFgIOEQARFQEREQAREQAUEAkRDgcKCwcJCgUKCgEKCgEK CwMMDgUNDgEPEAMHCwEJDgMJDgQKDwUHDAEKDwMKDwUKDwUPDgEODQARCQQQCAMNCgEPDAMODgQP DwUSDwMNCgANCwQMCgMKCAELCgMGCQAEBwADBAADBAADBAAEBQADCgABBwAEBwAFCAAGBwEHBwIH CQAHCQAFBgAHBwAFCAAICwEGAwAIBQAHBwIDBAAABAADBwADBgAEBwAGDQAFDAAECgAECgAHCAAJ CgAGCQEHCgIKCwEHCAAJCgMKCwMPDAMOCgIODwEODwEFBwAEBgADBAAGBwAECAACBgADBwACBgAF BgMEBAIDBwADBwAEBQAEBQAFCAAFCAAHCQAJCwAODgAODgAKCgAICQAKCAALCQAKCwAICQAICQAO DwMLCAANCgEPCwMPCwMQDwAWFQMJDwUECgEEBwUMDw0YCwUXCgQQEQMPEAIKDAAPEQQIBwQFBAEA BwMDCgUDCAAFCwAKDAAKDAAJDQAJDQALCwIKCgEKDgcEBwECCAIBBwEQCgITDAQHCgQICgUODAQP DQQNDQMODgQICwMNEAcHCgIICwMMDAMMDAMKBwAPCwMUEgIODAAPEQIMDwAFDwMABwADCAADCAAB BwADCQADCAMBBgEEBQEFBgIDBwIDBwIFCQMFCQMICAAHBwAEDAIFDQMKDQEICgAGDAEFCwAJCQIR EQkUEQUUEQUWEAEUDwANEAQICwEJDAEHCgALCAEKBwAFBwAKDAANCgAPDQANDgELDAAIDAAIDAAH CgAGCQAECgEFCwIIDAAHCwAHCgAJDAEHCAAKDAIUEwAYFwIMEAEJDQAIDwEGDQADCQABBwADBQAE BwEDBwIDBgEBAwAEBgMECAICBQAHCQEHCQEICAEJCQIKCgAKCgAJCAQIBwQJBgAJBgAKCgANDAAN CQAOCgAQDgEKBwAPCQEQCgIDBQAEBwEECQEHCwMKDQEJCwADBwEDBwEDBwADBwALCwAPDwAFCAAE BwAHCAAKCwEJCQEFBQADBQAEBwAGBwAHCAAJBgAKBwEJCgAJCgAHCAAHCQEEBwMFCQQMDAQKCgMG CgIKDwURDQENCQAKCgEHBwAFBgEGBwEMCQIOCgMHCgEGCQAHCQEICgEHCAAGBwAHBAAGAwACBAAB BAADAwADAwAAAwAAAQAAAgAAAQAABAAABAABBAACBAABAgABAQABAgABAgAAAQABAwABAwABAwAD AwADAwAAAwAAAwAAAQAAAQAABAAABAADBAADBAABAwACBAABAgACAwABBwABBwAEBwEHCQMFDgAD CwAEBwADBQABBwACBwADBwADBwABBwABBgACBQADBgACBQACBQACBgADBwAFDAABBwAEBwAFCQAJ CgAJCgAFCgAGCwAHCAAHCAANDgIHCAAHCAAKCwAHCgAICgAHCgAFCAAHCAAJCgABBwEBBwEABAAB BwEGBgAGBgAEBAAGBgAEBwADBgAABQAABQAAAwABBAACAgADAwADBgAEBwAGBAAHBgADBQADBQAD BwADBwACBwABBwAEBAAICAABBAACBgAECQAFCgEGCwAHDAAIDgEIDgEFCAAFCAAHCgIFCAAKCgEH BwAHCwAHCwAEDAEBBwAECwABBwABBAABBQADBAAEBgADBgABBAAHBwAHBwAEBgAHCAEDBQADBQAD CQAGDAEFCwIECgECBwAECgEGCgIHDAMHCgEFCAAGCgAGCgADAwAFBQAEBwADBgADBgACBAADBQAD BQADBgADBQAGBgAGBgACBAAEBgACBgADBwABBQABBAAAAwABBAADCAADCAAEBwAGCQAFCgEECAAH CwAKDgEECgAHDAAGDAIECgACBgABBQADBAAEBgAHCAEFBwAEBQAEBQAEBAAGBgADBgAFCAAHBwAG BwAEBwAEBwAHBgEFBAAHCAAICQAJCAAIBwAECAAGCgALCAAIBQAFBgEHBwIHCAAGBwAEBwADBgAD BQADBQAAAwABBQAABAABBAAFBgAHBwABBAAAAwACAQADAwADBAADBAACBAADBQADBwADBwACBAAD BgAEBwADBgABBQACBgADBQAEBwANBwAMBgAJBwMKCAQJCQAICAALCgEKCAAFCAAEBwAGBwAHCQAE CgAECgAGCAAHCgAGBAAHBQAGAwAGAwADBAACAwACAwACAwAAAAAAAQAAAgAAAgAAAQAAAQAAAQAA AQAEAwADAgAAAQAAAAAAAAABAQAAAAAAAAAAAAAAAAAAAQAAAQAAAAAAAAAAAAAAAQABAwAAAQAA AQAAAQAAAQAAAQAAAgAAAgADAgADAwADBAAEBgECCQAABwADBQACBAACBgADBwADBwACBgAABwAB BwABBwACBwADBgAFCAAEBwEDBwEFBQAHBwAHBwAGBgAHCQAICgACBwAECgEGCgAFCQAFBwAGBwAD BwAGCgAECAABBQABBQABBQAABAAABAAABAAAAwACBAABBAACAwADBAADBQACBAABBAABBQAAAwAA AwABAwADBAABBQABBQADBAAEBQADCAACBwAABQAABQABAwABAwACAwAEBgAABQABBwABBAACBgAD CAADCAAFCQAIDAADCQACBwAECQADBwAECgECBwAEBwADBgADBwEDBgABBgADBwECBAACBAAEBwAE BwABBQABBQAGBgAFBQADBQEEBgEDCAADCAADCAADCQADCQMCCAICBwAECgAFBwAFBwADAwADAgAA AwABBAAABQAABAABBQADBwAABQAABAAABwABCAACCAACCAADBgACBAAFBAAIBwAFCQAEBwABBwAA BQAABAABBwAACQACCwADCgABBwABBgADCAADBgABBAAEBQAEBgABBwACBwABCAABBwADBwEDBgAB BQADBwAFBwAFBwAHBgEHBgEEBwAFCAAFCAAFCAADBgAEBwAEBQEEBAEIBwAJBwAHBgAIBwAHCwAH CgAEBwAEBwAFBwAHCQEEBwACBAAAAwAAAwAAAQAAAQAAAwAAAwABAwADBAAEAwAGBAABBQAABAAC BAADBgACBAADBQACBgABBAAFBwAGCAAEBgAEBgADBgADBQADBwACBgAABAAGCwAHBwAEBAAEAwAH BwIKCQAJBwAHBwAICAEJCgIHCAAECQAGCwAHCgEGCQEHBwEHBwEHBgAHBQAEBAAEBAADBAABAwAF BwAEBwADCgAECgACCAADCQACDwABDgADCgADCgAKBwAJBQAECQAECQAHCQAKDAADCQABBwABBwAB BwADCAADBwADCgABCQABBgACBwACCgAABwABBQAABAABAwABAwAABAABBAABAwACBAADBAADBQAD CAACBwAEBwADBgACBQECBQEDBwAECQADCgABCAADBwAECAAICgEHCQEFBwEGCAIFBwAFBwAHCgAF CAAHCAAHCAADCgAECwEECAAFCgAFCAAEBwAECAAFCgECCQABCAADCQACBwACAwABAgAABQAECgAF CQAHCgABDAAGEgAIEAAFDQAKDwAMEQAIEgAIEgAIEQEDCwAEDgAGEAAFDAAECwAEDQAIEQADDwAA CgAGCgAHCgAEDQAHDwAHEQEEDgADCAADCAADCgABCAADCQAECgAECgEECgEGCgAECQAECgAECgAE CQAFCgEDBwMEBwQEBgEHCQQEBwEDBwEDBwEDBwEDBgACBQAEBwAHCgEECgAECgADCQAECgAECAIE BwEDCAQDCAQGDAEFCwAHCAAEBgAAAQAAAQAABQABBwADDwADDwAHFAAIFgAHFAAIFgAEEgMEEgMD EQAEEwEHEwEGEgAUGAEUGAEOFgELFAAHGQIKHAQCDwAEEgAGFgMEFAEHFAAHFAAEEwAEEgAHDgQD CAAGCAAHCQAECQEDBwADCQEDCgIGCQEGCQEHCgAEBwAEBwAEBwAFBwEFBwEECQIDCAEGCQEEBwAE BwEGCAIEBgEEBgEHBwEICAEKBwAKBwAHCgAHCgAGCQEFCAAEBwAFCAADBAABAwABAwACBAABBgAB BwAHEQAKFQAOEwAQFgENFQMLEwIFEwQGFAQHGAQHGAQKFgQMGQYKFgUMGQcWFwsWFwsNFQMLEwIO GwEOGwEQGgAPGAALFAASGwQIFQQGEgIKDwQLEAQJDgQFCgEHCgEGCQEHCQAHCQAHCQAHCAAJDQAH CgAHBwAICAEGBwAFBwAEBQADBAADAwADAwAEBAAEBAAJGgAMHgIMHwMPIgUKKAcKKQcLIAEPJQQX IQMXIQMLJAUJIQMUIgQOGwAPIQgPIQgEGQgDFwcKGAEHFAAKGwQIGAIEFgAIGgENGwMIFgAKFgMJ FAIKDwAIDQADCgAABwACBAABAwACBAAEBwAEBwAEBwAEBgAGBwAECQICBwADBgAFCAAEBwEEBgAC BgADBwAECAADBwAGBwEFBgEFCAAEBwAFCAAEBwAGBwAEBgAFCAAEBwAFCwAECgAFBwAGBwAECAAE CAABBwAABQABBQABBQAFBAAGBAAHEQANGAUTGQgeJRIEHwwIJBANIQcLHwYPIg8RJBEVIhESHw8N IwoHHAUPIgcKHAMNJQsQKQ8PJw0JIAcEIAgEIAgOIA4PIg8NJgoNJgoLIQoLIQoGEQMEDwIECgAD CQABBwACBwAHCAAHCAAHCgEHCgEGCQEEBwAFCAAFCAAEBwIHCQQDBgAECAIDCQADCAAECQAECAAE BwAEBwAEBwAGCQEECQAECQAFBwAFBwAFCQQDBwIDBwQDBwQHCgEFCAAEBwABAwAAAQAABAAADwAK HQcNLg8KKgsMLBAOLhIOKw0SMBEVMB8PKRgPKxAQLRIQLRIQLRIQLRQQLRQTLBITLBIPJhERKRQM JxQKJRIRLBsRLBsRJQ4RJQ4QKg0LJAgNGQoEDwIHDQEECgAFCAAEBwADBgADBwEFBwAGBwAGBwAE BgADBQADBgAEBwAEBwAEBwAEBwAEBQAFBwAECQAECgAEBAAEBAAHBAEHBAEGBQIIBwQEBQAFBgEE BgADBQAFBwEDBAABAwABAwAABAAECQAKHAQOIQcUKA0WKw8UJw8UJw8YLBgYLBgTNCEPMB0PMhgM LxYSLx0UMR8TKxcZMh4fLCAXJBgTLBIVLhQWLxUZMxggMAwcLAkZKBIcKxUaJRAMFgQPGQQMFgIE DAECCQADBwAECAAHBwAHBwAGBwAGBwAGBwAHCAAFBwAHCAAEBwACBAAGBAAFBAAEAwAEAwAEBAAE BAAHJQoOLhENMxoUPCIQOB4QOB4QNBwUOSAYNSAWMx4UNRoUNRoWOBwUNRoRNxkUOhwKLSQGKB8K JxYPLRsVMhYQLRIUJw8WKhITLQ8VLxEQKQ8LIwoKHAQHGQIEDwMACgABAwAAAQAAAQAAAwACBgAD BwAEBQAGBwADBwACBgADBAAEBQADBQADBQACBgAECAAEBQAEBQAEBwAEBwADBwEBBAAEBQAEBgAF BwAEBgADBgAEBwAGBwAFBwADBgAEBwADBwADBwABBAABBAAABAAAAwAECgAJDwEOJRATKxYZNCkb NysMLSEUNyoPNB8NMh0PNS8NMy0PNB8QNyEONRwMMxoWNyAWNyAKMyIPOigLMiQQOSoLOyYINyIM Mh0PNyEQNBwPMhoLLR0KKxsMGw0IFgkDCQEABgABAwACBAAIBwAHBQAEBwAEBwACBgACBgAHBwAF BQAFBwAFBwAEBwAEBwADBwADBwAEBwADBgAEBQAEBgAEBgAFBwAGBwAFBwAEBQAEBQAEBwAEBwAE BQEEBQEGBwABAwAAAAAAAQAABgAEDwMDIQgLLRIQOyAPOR4KOx4KOx4OOigOOigOOC4MNSwPOCkO NScOOScLNSQPNB8PNB8SMiUTMyYPNCcLLyIPNCcPNCcRNykRNykPNCURNycQNBwKLRYRHA0KFQcI DwMDCAAABAAABAAEBAAEBAAEBAAEBAAEBAAFBQAHBwAEBAAEBgAEBgAEBwADBgADBQAEBwAGBwAF BwADBAADBAAFBQAEBAAHBQAHBgAHBQAHBQADBAAEBQABAwACAwAABAAABAAACgAJFwkOLh8OLh8T OSsWPS8SOCoPNCcfOSofOSoVNSgXOSsPNSAQNyEWNCwaOjEZNCkXMicVNSgUNCcSMhwUNB4YNSEa OCMhNRsdMRcVMB8XMyIdMhYMHwYPFgkKEAQECgACBwAEBwAEBwAFCAAEBwAFBwAHCQAJCQEHBwAG BwAGBwAEBQADBAAEBQADBAACAQACAQADAwADAwASNx8ZPycdPSchQSsXPCkXPCkWPjMTOzARPiwN OScOOyUOOyUPOiAWQScWPiUROSANOCwOOS0MOCYPOykQOSgNNCQONRwONRwNNRcLMxYPNB8NMh0Q MyMHKBgLHQ4CEQQECQABBAAAAwAAAgACBAAEBwAEBwAFCAACBwADCAADBgAEBwACBgADBwADCAAD CAAFCAAEBwADCAADCQADBgAEBwAEBgAEBgAECAADBwAFCQAGCgAECAADBwABBwADCAAEBwAEBwAA BAAAAwAABQAABgABFgAMJQsNOSENOSEPPzEOPjAMQzQKQDIWPzAVPi8MPzsLPjoTQSsRPykVRC0O OyUQPS0TQDAOPjAPPzENOCwRPTEOPzEOPzEMOygOPSoPOCsLMiYPOCMPOCMSJxoFFwwHEQcBCgIC BAACBAAEBAAFBAAECAACBgAEBAAICAEEBgAFBwAFBwAEBgAEBgAEBwECBgADBwAEBQAEBgAEBwAE BwAHCAEHCAEECAACBgAEBgAFBwADCQACBwADBgAFCAADBQAAAwAAAQACAwAAEQQLIRIIOR8LPSMO RDANQy8PQywOQCoKPS4KPi8KOC0PPjMWOjgUODUVPTQTOzIQOicSPCkVOy0WPS8TPzMOOS0PPi8M OywMOi8LOS4QOSoQOSoPMSEKLBwIIRMDGg0EEwEBDwABBwABBwAEBQAEBQAEBwAFCAAGBwAFBwAG BgAGBgAGBwAGBwAFBwAGBwADBwAECAAHCgEFCAAHBwAEBAAGBwAEBgAGBgAGBgAEBAAFBgEFBgEE BQABBAAAAwAABwAFDQEFGgwOJRYPLCYaOTIWPjUWPjUOPjANPS8YQzUYQzUaPDAcPjIWPiwWPSsW PDQVOzMSOCoQNSgTOSkTOSkUNygUNygPOh8POh8bOCgZNSYZNCsXMikTLQ4NJggRFgwLEAcHDgUD CQEHCgAGCQAGCgAHCgAHCgAJCwAJCgEJCgEKCgEHBwAGBgAHBwAHBwAEBAADAQACAAADAgAEAwAR RSwPQSkXQSUbRikWRi8QPigWRDEUQS8NPysNPysNPSYPQCkUQS8SPy0ROikPOCcOOioPOysPPCYP PCYNPC0LOisOOyUNOiQNOxoNOxoUOycQNyMTNyYKLBwKKA8AGQMCDAAACQAABQAABQACBAADBgAD BwAECQADCgADCgABBwADCAADBgADBgAECgADCQAEBwEEBwEFCAAGCQADBwAECAAECAAECAAFCgAE CAAFCAAGCQAHCAEGBwAECAAECAADBwECBQABBAABBQAACgABDgACIQoPMhgLOicOPSoPQS8PQC4O PzMPQDQQPDASPjIPQDQOPzMOPysPQS0PQS0NPioSPy0UQS8PRzcORTQOQC4OQC4PQDIPQTMNOCgO OSkVPTQQOC8PPCoPPCoSNSUFJRYLHQ4ACwAABAAABAABBAADBwEFDwMEDQIGCQEKDQQECgADCQAE BwAEBwAFBwEEBwEEBwAEBwAFCAAFCAAEBgEEBwIHBwIGBwEECAADBwADBwEEBwEECgQBBgEECQAE CAABBQABBAAABAAABgABGAwMKBoKPCYOQSsNRTILQzAPPzMPPzMOPzMNPjILPDIMPTMLODIOOzUP PzMPPzMVPTISOi8VOzETOS8OPDELOS4PPi0LOikPPzUKOjAKOSgKOSgPNyYLMiIMKhwKJxkMFQcH DwIECQEECQEEBwEDBwEDBgMEBwQDCAQDBwMDBwEEBwEGCgIGCgIEBwAGCQEHCAAGBwAHCAEHCQED BwADBwAHCgEEBwAGBwEHBwIDBgAEBwEDBwMBBAEABQABBgAADAAFFQQHJhYPMB8LNCkWQTUTPzMU QDQMPS8QQzQTQDITQDIVOjIXPTUWPy0XQC4POy0NOSsMNysLNSoYMh8aNCEQMyMSNSUPNx4UPCMQ OSoSOywROScONCMQMhkIKBASHw8VIhELFwoGEQUGDwAHDwAICgAICgAICwEICwEGCgAHCgAHBwAK CgMHCgEEBwAFBwAEBgAEBQADBAADAgAEAwAURTEQQC0RRC8PQS0RRC8QQy4PQSkPQSkOPysLPCgN PSYNPSYOOS0POi4WNykWOCoXOigUNSQLNyENOSMLOicKOSYKOiMLOyQOOSkNOCgRNycPNCUONRwK MRgKLhIDIwkEEgQACgAAAwAAAwACBAADBQADBwAECQAECgAECgACBwADCAACBQABBAADBgEEBwMD CQADCQAGCQAFCAAFBwAGBwAHCAEFBwAHCgEFCAAGBwAHCAEEBwIFBwMECgADCQAECAACBgAABAAB BAAACgAEEwQEKBwPNyoLQC8PRjQUQDcQPDINPS8OPjALQTMNRDUPOysPOysMPS0PQTERPiwQPSsQ PScSPykOQDAOQDAMOywQQDERQS4QQC0UOy0ROCoWPDQWPDQPPi8PPi8ONCwLMSkOJRMBFAQABQAA BQAECgAHDgMGCwQGCwQICAEICAEEBwMEBwMDBQAEBgAGBwMFBgIFBgIFBgIEBgAEBwEGBwEHBwIG BwAGBwAEBwIEBgEDBwEECQIEDAADCgAGCQAEBwAABgAABQAABwABDQEDIRQMLR8NPC0QQDEOQzMO QzMPPzkRQTsOPjQOPjQQQTgOPjQPOjURPDgTPy0NOCYaPDIaPDIiPjIjPzMWPjIWPjIWRTATQCwP QTEMPS0PQS8KOykNOCoNOCoPLyQOLSIPFhMGDAoECwcDCgYDCAADCAADBwADBwADBwMDBwMECQED BwADBwAECQAFCAAFCAAHCQAGCAAHBwAJCQAEBgAEBgAGBwAHCAAHCgAHCgADBwADBwADBwEBBAAA BQABCAABEwcGGg4ILiANNCYPOykWRTIVQDwTPjoNPDUPPjgSOi8WPzQVQDQTPjINPioPQCwNOCYN OCYcPTUgQToxOSYwOCUnNyEnNyEbPiweQS8POykOOScONyQNNSMPNSYKLyAYMhcPJw4LEAYECQAH CgAJCwAKDAAJCwAHCgAHCgAHCAAICgAHCQEGBwACBwABBwACBgACBgADBAADBAAEAwAEAwAVQzIS Py8OPy0OPy0QPzQQPzQNPiwPQC4OPikMPCcVPSQXQCcROiAUPSMcNyIYMh4sOCEpNB4UNB4YOiMR OyYPOCMPNyYQOSgOOSkPOioTPSgTPSgKOSAJOB8NNx0DKREEFQcADgEABAAAAwABBAAAAwACBwAC BwACCAADCQADCAACBwABBgABBQACBgACBgACBwACBwADBwADBwAFBwAFBwAFBQAGBgAEBgAEBgAE BgAFBwAEBgAEBgADCQADCQAEBgADBAAABAABBQABDwAHFwMHMBkQPSUMRC8MRC8TRjUPQDAQQTMS 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 SUwWR0kXSUwWTE4WTE4WTkwWTkwTTkAYVUcjVFcvYmVVbYRyi6N1kJ90jp5ymZlymZmElaGNnqqL m7eElbBahoc7ZGUdSUwSPD4SPTkRPDgKOC0PPTISLhoEHQsQEggICgEJCgMJCgMMCgMMCgMJCgQJ CgQKCQIKCQIKCgMJCQIFBwAFBwAECQAECAAEBAADAwADBAADBAAZTEUaTUYWTk8WTk8STE8TTVAf UE4uYl9Eb3BQfX5pg451kJt9jo1vgH9wcmhub2VyeG1/hnp4jJaClqF6jKVqe5RAam8uVlsVUE8Q SkkPR0cPR0cNQzsMQToWQz8TPjsPLCIBGhECDQAABwABBAACBgAFCAAGCQADBwQECAUJCAMKCQQG CgIFCgEHCQEKCwMKDQQKDQQFDQEFDQEHDgIIDwMHDAMHCwMKCwMKCwMICwEJDAEHDQMHDQMHCgQG CgMDDAMDDAMCCgQDCwYBEw8OIx8KPjMXT0QTW1MRWFASV00VW1AWVEoWVEoWU08WU08YU08bVlMm UUokT0g3TlhAWGNVc4ZffpFdlJ1ckptQh4s8cHQeXloTUEwQUE4MSkgQSUARSkERUEEQT0AQRjsR RzwZOy8EIRYECgAAAgACBAAFBwEFBwMGCAMICQQKCwcJCgcGBwQGCQcHCggHDgQFCwIHCgQHCgQI CQQICQQFCQQIDAcHCQEKDAQKCgMKCgMFBwQGCAQKCgYKCgYLCAkKBwcBBwQCCQUADgcKHhYHNDgT REcSTlAUUFMTV1MQVE8WTFMYT1YWVV8PTVcWUVEeXFxHa3tljJ14iJ5/kKZ4lrR5l7Z3mrR/o750 pr5qm7Nkg4ZHZGcZV0MUUDwRSkERSkEPRUMMQD4POi4IMSYMGQYCDQABBAAEBwMGCgIECQEFBwQG CAQGCwUGCwUECQEECQEKCwMMDgQPDQcOCwYKCgILCwMECgEECgEHCQMGCAIECQEFCgEECAQDBwME BAEGBwMHCAcFBwYBCQMHDwgDFxMOJiEOO0EUQ0kWRUwYSE8bTU8cTlAVSUoWSkwXSEgfUVEqUVoz XGRNa4NaeZFigJZlhJpyi5lzjJp3iKN1h6JzgJpkcos4X1QmTEAPPzoQQTwSSUQMQTwSPTMPOjAW LBoFGAkFCQMFCQMIBwAKCAENCgMMCQIIBwQIBwQKCQEJBwAFBwAHCAEHCAAEBgAGCAAFBwADAwAE BAAGBAAGBAAcTUgeT0oWU0wWU0wZU0oYUUkqVk80YltJdW9Yhn9pi4xylJWClp6AlZ2MlZmOl5uO m6iUoa6MmreHlbJ6jKVoeZEzY14lU04UTEQSSUENREMQSEcRSEAORDwOPjgKOTIKJBgAFAoABwAA BQABBAADBgAICgAHCQAICgcHCgYHBwQICQYJCwYHCgQIDQMIDQMECQIECQIHDQAJDwEECwEGDgMJ DAMHCgIIDwQHDgMKCgQKCwUKDgcIDAUHCgcIDAgGEAoDDAYFCgMFCgMDDhIPHCEPODoaRkgRVVEU WFUTVU8XW1UVVlAXWlQUVVMSU1AWUVUYVFcZVlMZVlMpUVMvWFo5aHJMfYdYiZlQgJBTe4s6YW8e XFwWU1MPTVMSUVcQTUgPSkYRT0QTUUYXSkASRDoTJyAFFhAEBgABAgAEBQAHBwIGCAMHCQQHCQEJ CgMKDAcGCAMHCgYKDQkHCgQGCgMICgQICgQJCgUJCgUKCgYICQQHCQUICgcICgIJCgMHCAMICQMJ CgMJCgMQDwcKCQIBCAADCgAADQgDEQwDKisTQEETU1QTU1QRWFcQV1YWUVcYVFoYV2cUUWEXVFMb WFc4Z3VNfo1jg5BlhpJke5luhqNni51kiJpje45RaXsrYV0fU08RSkQRSkQSSkkPR0YSSEgPRUUS PTUHLicKEQgBBwAEBQAEBgAECQAGCgEECAMEBwMHCgEHCgEFCAAGCQEICAEKCgIKCgMJCQIHCAAG BwAEBgAFBwAHCAAHCAAECgAECgAFBwEEBwEEAwAFBAAGBwEFBgEACAACCwMAFhkKJCgKNUUSP08U Q1AWRlQZT1AaUFEUTUkXUU4UTUkYU08uVlsxWl45WG4/X3VGXHVHXXdUZHRXaHhNYntFWnM4UU0v SEQWRzwWRjsPRDkOQzgPRzsPRzsZPzgPMisOIAkCEQAHCAAEBgAHCAAHCQAOCgQMCQMIBwMIBwMK CQILCgMHCgIGCQEJBwAJBwAHBwAEBAADAwADAwAEBAAFBQASTU0WUVEWWEwTVUgXUU4XUU4lU1Ev Xl1AYmdNb3RWgIlbho5ihJtniaF5kqF9lqV/lax9kqp6jJ5qe41OdXQ/ZWQbVUMVTTsQSDwMQzcR R0cRR0cVSUgSRkULPDIHNy0KHxYADgYABAAAAwACAwADAwAGBgAHBwAFBwAFBwAHBwMHCAQFBgEE BQAHCgEHCgEDCgEDCgAGCQAHCgEGCgIHCwMGCgMFCQMFCgEECQEFCAAICwIIDQMHDAMICgUJCwYE EAEBDAABCQABCgAACwQGFQwHLicRPDQPUVEYXV0aWlgeXl0TV1QWW1cTVlYSVVUVVVYUVFUXW1cW WlYaWE8WVEojWFEyamM/c3s6bXUvYmUiU1YUT08WUVEQSEgRSUkPSEgTTk4RUU4SU08hSEcYPj0S IiAADAoBAgAAAAAEBQAICgIHDAMFCgEHCgAHCgAGCgAGCgAHCgMGCAIHCAEGBwAHCAEHCAEKDAQI CgIFBwEGCAIGCAMGCAMHBwIHCAMHCAQHCAQHCQMEBgAEBAAFBQADBwAECAADDwYDDwYBIB8ZPz4P Tk8WWFoRWFcUXFsYWlsWVlcXW2IVV14WV1gbXV4mX2kmX2kvZGQtYmI5Y1s+aWE/ZWU6X18uWF8k TVQPSU4OSE0QSkcPSUYRTUYPSUMRRk0USVAUODcEJCMCBwAAAwABBAACBgADBwAFCgEHCwIGCgEG CgMGCgMDBwEDCAEECgEFCwIICwIHCgEHCQEFBwAHCgEGCQAECAAECQAICAEICAEHBwIEBQAEBAAE BAAEBwEHCwQEDAUHDwgCGBMJIhwKNDkSP0QOSE0RTVEVVlcWV1gWUU4XVFATU1QPTU4XTVgeVWEh U1wjVV4oT1ArU1QxTFUxTFUdTFQZR08UT0ERTD4WSEMSQz0MQTkPRTwPQzsRRj4XOS8LKiEHFgYA CgAHCgEEBwAHBwAJCQAKCQQLCgUICgcKDAgLCwMMDAQJDAMHCgIHCgEFCAAGBwAFBwAEBQADBAAG BAAGBAAPT00TVFEXV00VVEkZVV0XU1sWUVAgXl00YV03Y187andAcH1AdIJDd4RMeHlMeHlUd3hN b3BQZ21KYWcmXFEgVUoWUUgSTEMQSUMQSUMSR0wRRkoRSEASSUEPPjMDLSMIHBMADwcABgAABAAC BAAEBwAEBgAFBwAEBwAFCAAFCAAHCgEGCAIEBwEFCgEGCgEFDQEFDQEHCwMFCgEFCAAFCAAGCQAH CgEGCgAECQAFCgAHDAEHCwMFCgEHDAMFCgEHFgEEEwADDwMACAAADAQGGA8DJR8MMiwMUVUVXWEZ X1wZX1wUXVcVXlgTW1UTW1UVV1QXW1cUXF0SWlsXWFoVVVYgWFEkXVYdYl4YXFgUWFQPUU0RU1AQ UU8SR0wUSU4QSU0QSU0RUUwQUEoiRkEWOTQIFg4ABQAAAwAAAwADBgAECAIEDAEDCgAHCgQHCQQF CgEGCgEICgUHCgQICwIICwIHCgEKDgQKEAYGDAMFCwIECgEGCAIHCQMHCAMICQMJCgMHCAEGDAMD CAABBwADCgIECwEECwEDDwMEEgUDGyMXNT4KR0gTU1QWX1sVXVgVXloSW1YUW1oQVlUTVVwZXWQb XV4XWFohWFojW1wcYVUWWE0eVk8dVU4WVFEVU1ATTk0PSEcPTkwQUE4QUE4PTkwTTEwQSEgPLyQA GQ8ABAAAAwABBwAECgAECAAGCgIFCwIFCwIHCwIGCgEECwEDCgEEBwQECAQHDAYGCwUHCQQHCgQH CgMHCgMECQAGCgELCgINCwMKCgMICAEFCQMHCgQGDwQHEAQIDwMJDwMDFgsGGg8IMCUQOy8LRkEQ TUgSWFcTWlgWUVEYVVUYW1gVVlQVVlQVVlQRT1cVVFwaTU8aTU8WR0oYSk4YSVAZSlEUTUwQSEcU RUcSQ0URRj8PRD0PPzkPQDoVLygIIBkGDwMACAAECQAHDAEJDAEKDQIHDQMHDgMHCgEHCgEKCwMM DgUKDQMHCgEECQEDBwAEBwAEBwADBQADBgAFBgAFBgAQSkkVUE8ZVlYXVFQWV1sVVVgTW1oWXl0U WFEWW1QnU2EuW2kxX2kuXGUsYVMnW003W1MyVk4rWlUpV1MWU1ERTEoTTEgSSkcPT0YOTkUQSkkQ SkkRSEAPRT0QODEEKCIDDwQACAAABAAABgACBQAEBwEECAADBwAGCQAJDAMJDAMHCgIIDQQFCgEE CgEIDwQFEAMEDwIHDQMJDwQHDwMEDAEFCgAHDAEHDgMHDgMHDgIJDwMJDgQHDAMFDwYEDwUKEgQG DgEHFQcADAECEQMFFgcBGyAHJCkJREwUU1sVX18UXl4SX2EUYmMWXV4WXl8VWlUWXFcTXVsRW1gV WFsTVlgYW1cXWlYSWlgSWlgTWE4SV00PU04QVE8VUFATTk4RTEwPSUkPT1AOTU4cRDoKLiUCDwIA BgAABAABBwEDBwIDBwIEDAEFDQIHDAQHDAQKCwMLDQQGDwQGDwQGDQQGDQQIDwMIDwMHDgQHDgQJ DwUHDgQGDAMFCwIJDgQJDgQHDgQGDAMHDgQHDQMDCgUFDggDCwMBCAEACAAACAAAGBEOKyMKQD0S SkcQVlwTWl8UXmETXV8TW1USWlQWWloXXFwWXl0QV1YUVVEXWlYRV1YQVlUXWFoUVFUSVE4RU00S T0oOSUUPSEgPSUkPSk0PSk0TSUkKPT0EEggACQEABQAABAADBgADBwEDCAEECQIHDgUECwMHDQAI DgEGDgEDCgAEBwEFCQMECgQECgQECQEECAAEBwAGCQEHCgEHCgEHCgAICwEKCgIKCgMHCAQICQQH DAMGCgEECwECCQAACgQFEQsHKB8RNSwNREASSkcPUVESVVUWV1UYW1gcV1QZVFAbWFcYVVQSTlMQ TFAaTU8cT1EVU1AWVFETVFEQUE4WTU4RR0gMRUEPSEUOTkUNTUQQRjsMQDUOJRADFgQECgACBwAH BwAJCQAGCgAHCwAHCwEFCgAJCgAKCgAICAEHBwEFCwAGDAEDCQACBwAEBQAEBgADBQADBQAEBQAE BQAOPDwYSUkbVFUdVlclWF8jVl0ZW1wXWFoQXlcTYlsmV2ghUWIkVWUjVGQaV1YbWFccVVUcVVUW WFYSVFEQU1MPUFASUEYQTkQTTkoUT0wPUE0RU08LSkENTUQPLysAGhYDDAAABAAABAAABwADBgAE CAIFCgMDCAEDCgMCCgIECgAFCwEIDQMIDQMIDwQJDwQGDwQEDQIHDAQJDwcECgQECwQJDQYIDAUG CwQFCgMIDwMHDgIMFQcLFAYFEwYGFAcHDgUFDAQEDwMCDAEBCgAABwAAFA8IHxoDMjcUSU4QWlwW YmQRYWERYWERX2QRX2QVV1UWWlcRWlUWX1sVWFgUV1cUW1wQVlcWV1gUVVYPUE0RU08NVE4PVlAS VE4PT0kVUVQXVVcRUFQOTE8TMisDHRYAAwAAAQAABgABCAAECgMDCgICCgEHEQYHDwMIEAQJDgQE CQEEDAQDCgMHCgQJCwYKDAQICgIJDAMHCgIIDwQGDAMGCAIGCAIKDQMLDwQHDAYGCwUGDwQEDgMC DgQCDgQCCgABCQABCAEBCQEAEgUHHA4FLysSQDwQUFQWWFweYmgXWl8SXmITX2MYWl0XWFwSWlgS WlgWXloQV1MWWlcWWFYUV1cRVFQQUU8PUE4SU00QUEoSU08QUE0ST00ST00YQTkGKiIBCQEABAAA BAADBwEDBwAECQEFCgEDBwAECgEECgEECgEECgEFCgAECgAECAAFCgEDBwEDBwEECgADCQADBwAE CQAHCAAHCAAECAAFCgEHCgIHCgIFCAAFCAAECQEDBwABBwACBwABBwMECgcDFhgKISMKNz8PPUYN TUkSVFAWWFUXWlYaW1MaW1MWV1sTU1YRUFEUVFUcT08cT08aTkoaTkoWT0kUTEYSSUQQR0EPTEEP TEERTUYPSUMRPDgKMy8HFgQBDgADBgAFCAAICgAKCwEHCwAIDAAGCgAHCwELCgEMCgEKCAAMCgEG DAEGDAEFDAAECgAICQAGBwACBAABBAABBQAABAAJOCoRQzQPU04WXFcgXWMeW2EbVVgeWFwWXV4U W1whVmMgVWIXU1sZVV0XWFobXV4WV1sXWFwWV1gSUVMUUVAWVVQQU0MPUUESTU0TTk4TU1QSUVMM Rj0KQzoMIhoBEwwDCQAABQAAAwABBAAEBgAHCQEECgEECgEECwEDCgAFCwEFCwEHCgEHCgEIDQIG CgAECgAHDgIIDQQHDAMECQEFCgEKCwUICQMHCQEHCAEJCwAKDQEKDQMHCgEECgEJDwQCDAABCwAE DAEEDAEECwECCQAADAMCDwYHJCscPUUYRlAjU10aXlgZXVcTYWIQXV4SW1YVXloTW1YWX1sWX1oV XVcWW1sUV1cXVlQYV1UQVE0WW1QPWlMPWlMPV08SW1MTVlgUV1oOSUgNSEcLIA8ADwEAAwAAAwAA BwACCQAGDAEGDAEEDAIHDwQHCgALDwMKCgMJCQIFDAQDCgIFBwEICgQKCAMJBwIICgIJCgMFCgYD BwMICgEJCgIKCwELDQMHDAYGCwUHCAQGBwMCBgAECQAEDgADDQAGCwAHDAAACwAGFgcGKCMPMy4P SVAUUFchV18hV18bXWgdX2oXX2EWXV4SXVcTXlgUXFcSWlUbWFcYVVQTVFcTVFcSU08SU08RUU8S U1AVVlcQUFEWT0cWTkYWKyACEwoAAwAAAgABBAAEBwEECgEGDAMGDAMECgEHCAEICgIGCQEHCgIG DQAECwAHCgAHCwAGCQAHCgEICwEGCQAHCgEHCgEKCgEJCQAHCgAHCgAHCgAHCgAGCQAHCgEECgED CQADCgACCQAGCwQHDQUDFQ8MIRsKLTIQNTsMRkgRTU8UVVMWV1UbV1AgXVYWV1gSU1QSVVUSVVUW VFEWVFEQTUgST0oWTU4USkwVTUcUTEYTTUQSTEMSSUARSD8WMysDGhMFCgADCAAFCAAHCgEODgQM DAMKDQMKDQMIDQIIDQIICgEKCwMKCQAPDgQHDQMECgEECwADCgAJCwAGCAAFBQADAwACAwADBAAH Lx4POigPT00TVVMUW1wWXl8WXloUXFcWXl0TWlgdXGMbWmETVFoXWl8SWFoWXV4WVFMWVFMTV1QT V1QYU1EZVFMUUEkWU0wUVVETVFAXUU4RSUYSQzwGMiwHGAUADgABBAABBAABBAADBwAEBwAGCQEG CQAGCQAHCgAIDAAHDAMFCgEECwAECwAHDwEHDgAGCwAKDwIHDgIGDAEHCgAHCgAHCgAHCgAFCwAF CwAHCgAHCgAKCgAJCgAGDgMGDgMHDQMFCwEHDAMDBwACCQAEDAEEDAAGDgEFGhwSKiwPPUQaSlEW X1ocZ2EYYWQWXmISXF4SXF4XXVoYXlsXYV8SWlgTW1YTW1YRWFAUXFQTW1YQV1MPW1oRXl0QW1UQ W1UQWlcSXFoSQz8GMi8BCAAABQAAAQAAAwAEBwAGCQEJCwAKDQEHDgIHDgIHCQEKDAQICgIKCwMF CgQGCwUJDAMKDgQIBwQIBwQKDgQJDAMDDQQCCwMIDQIIDQIJDQAMEAEJDQYHCwQKDQcICgUGCwQG CwQGEAEHEQIGDgEHDwMBCgADDAIBFhELIx4JNTwXSE8aT1ceVFwbWGIfXWcTYV0QXVoRXl8TYWIY Wl8XWF4ZWFcXVlUVWFgRVFQTV1AVWlMRU1AUVlQbV10YVFojSkAWOzEIEgYABAAAAgAAAwACBwAE CgMHCwMHDAMGCgIHCwMKDQMLDwQICwMKDQQLDwMLDwMMDgMLDQMHDgQHDQMHDgMGDAIIDAUHCwQL DwUKDgQKDwEJDQAKDQIKDQIJCgIKDAMKDAAKDQEHDgUECwMFCgQDCAMDEAoGFQ4HHxYQKiEKOzoS RkUSUVUVVVgZVlUZVlUVWlUTV1MRWFAQV08bVU8dV1EXVlEUUU0TTVAUTlEWT08WUFAYSkoZTEwa SDoPOy0LHQsBDwEEBgAFBwEHCgAKDQINDQQPDwYLCwQLCwQNCAMNCAMLDgELDgEMCgAPDAAKDAMH CQEGBwAJCgEJCgAEBQADAwAEBAADBQABBAAGGRIPJR0LPTwWSkkVVVsXWF4WXmIWXmITXlgSXVca XGIXWF4VW2EWXWMVXlgTXFYWV1sWVloVVlcSU1QbU1waUVsWV1gWVlcXWlYWV1QXTlMRRkoQMDIC HR8DCwEABQAAAgAAAwAABQAECgMHCgQFCQMKDAMJCgIHDQEKEAQKEgcHDwQEDwUFDwYHDAMHDAMG DAMGDAMGDgEFDQEKDAINDwQJEAIJEAIHDAMHDAMNDQQPDwcPEAUPEAUIDwgHDwcHEAUFDwQIDAUI DAUGDQcHDggGDgMGDgMBEwwHGhMKNTcRPj8ZW2McXmcXW2QYXGUVXmMVXmMWXVwWXVwVXGISWF4U XVgWX1sTW1MUXFQVXFsSWFcVVloZXF8UW1oTWlgWTU4RR0gPJhgADwQAAAAAAAABBAADBwAHCAMK CgQLDwUKDQQHDAcKDwoKDgcJDQYHCgQIDAUHDAYIDgcKDgQMDwYHCwcKDwoPEwkNEAcHEQkHEQkK EQcLEgcJDgQKDwQJDQcIDAcMDAoKCgkGDgoHDwoGEAgHEgoIDwoECwYCCAMBBwMACwcDEQwEIB0M KicKQEoUTVcPV1oTXV8TXV8VX2IYXGUbX2kZXWcXW2QXW1gXW1gUXmEPWFsUWFUYXlseW14XU1Yd T1sYSVURKyIBFg4AAwAAAgABBAACBQAGBwEKCgQJCwQJCwQJCgUMDQgKDgQJDAMICgUHCgQIDQMJ DgQKDQMKDgQICgcJCwcHDAQHDAQKCwMHCQELDwQJDAMHDQMHDQMJDQAKDwEHCwIGCgEICAEICAEF CQMECAIHCAMEBAAACAEBCgMEEAoLGBIHKi0PNDgPRUgPRkkTTVAZVVgQV1ESWlQeWlghXVwnXFgk WFUXW1EVV04UT04VUE8WUVETTk4cSEoTPT8UJhgADgMABgAABgABBwAECgEIDAAJDQAJCQIJCQIK CwMHCQEJBwAKCAEHCgAKDQIKCgALCgAICAEJCQEHBwAHBwAGBwAFBwADBAADBAACAwADBAAEDwgN GBEOJysZNDkPQEYXTFEWWl4YXWIXX14WXVwaXGIbXWMYW2EYW2EWXl8VXF0ZW2EZW2EYWl8ZW2Ec V1YcV1YWVloRUFQdVVwWTFMVPkkILjkGFxEADAcABAAABAAAAwABBAABBwEECgQFCgQHDAYLDwUL DwUEDgMHEAUIEgYHEAQEDwoEDwoKCgYLDAcJDwQJDwQKEwcKEwcJDgQKDwQJDwMIDwMKDgcKDwcN DQMPDwQHEQUGDwQJDQYLDwgIDgkHDQgKCwgODwsLFQ0LFQ0DCQUECgYBDgIBDgIHGBsNICMNQUwY UFsSWGMaY24WYWgWYWgWYWUVXmMaYmgWXGIZZF8YY14WXWEWXWESWFwVXF8aVlwdWl8cUF0XSlcW Oz8GJioDCAEAAgAAAAAAAQABBQAECAAJCAMIBwMHCwQJDQYHDAQIDgYFCgQHDAYKDAUJCwQHDAYG CwUKCgYJCgUHDQUHDAQJDgQKDwQIDQMHDAMICgUJCwYHCgMLDgcLDggICgUJCgQKCgQHDwQFDQMH DwcDCgMECgMECgMECAMEBwMABwAACgICEQ8JGhcIMjcSP0QMSU0UVFcXXF4XXF4WWmMZXWcVXGQV XGQUXF8UXF8RXlsQXVoXWF4ZW2EbUFgVSFAYPEMDIScBCAIAAwAAAQAAAwABBQAECAAICgEJCgII CwIICwIHCgMICgQHDAMHDAMGCgMFCQMECwAEDAAICwIICwIHCgEGCQEHCgAICwEJCQIKCgMHCQEG BwAHCgEJDAMIDgEGCwAGCQEHCgIJCgIFBwAHBwEHBwEJCQAHBwACBQAEBwEBCwYEEAoHGhYOIx8I LjAQOTsOSE8STlUWVFMVU1EWVFgaWF0eWlgeWlgaVVUZVFQZVlYWUVEXUFgMQUkRLCYCGBMCBwAA BQABBgAECQIHCQEJCgMKCwMKDAQJCgIJCgIKCwEJCgEICwEKDgMICwMJDAMLCwQKCgMLCQQLCQQK CQAIBwAKCAEIBwADBAACAwABBAADBgAABwEEDAUEEQ8QIB4HLS8TPT8NTE8UVVghXFYjXlgbXWEZ W14VXV4XYWIUXlwTXVsaXF0aXF0ZXF8aXWEXW1gWWFYXWF4UVFoXSU0SQ0YPKTAAFhwABAAAAwAA AQAAAQAAAwAABAADBwIECQMBCAAECwEDCwEDCwEFCwEECgEFDgUHDwcHDAkHDAkFCgEECQEIDQIH DAEHDAMHDAMJCgMICgIFCwAFCwAECwEEDAEIDgEKEAMHDwQEDAEFDAQFDAQHCgYKDQkKDQQGCQEI EAQHDwMCBgADBwADCgADCgAECQYKDwwFJiQQNDIOUEkUWFEPXFsUYmEXYmcYY2gYY2gXYmcbXmId YWQWYWUTXGEUVl8ZXWcdVGIcU2EXRU0LNT0KFxMABAEAAAAAAAAAAQABAwAABAAECQIFCQMECAIE CgUHDAcHDQUFCgMHDQMHDQMKDAMODwYHCgUFCQQICQMHCAMEDgMDCwEHDwQHDwQJDAMJDAMHCgMI CgQICwEICwENDwQKCwEICwMKDQQHDwQECwEEDQQBCQEFCgEECAAGBwQHBwQBCQADDAIDCgYDCgYB Eg8KHhoKMjQTPT8RSlAWUVcTWl8SWF4SV2ITWGMQWF8RWmERWGQRWGQbUVMXTU4ZQTUKLyQHEgsA AwAAAQAAAgAAAwADBgAGCAIHCQMECgEGDAMKCwEKDAIJDAEICwEKCwEKDAIHDQUHDQUGDgMEDAIK DAILDQMGCgIGCgIHCgIKDQQNCgUOCwYICQQICQQICQMICQMHCwIHDAMKDAQLDQQLDAYLDAYMDAQK CgMKCgIKCgIECQMDCAMFCwkGDAoCDgwEEA8EGR4JHyQGKTgKLz4ORUUPRkYLRUcSTlASUVMRUFEc TlAXSEoXSUQPPjkRLCoAFBIBCgAABgAABAAABAAEBQEJCgULDAYKCwUNDQUPDwcLDQQJCgMKCwMJ CgIKDgMOEQUMDQoKCwgLDwULDwUJCgQKCgQKCgMJCQIOCQEKBgAEBAADAwACAwACAwAABwAABwAA DAQEEwoBHhUKKiAQOzMbSEAVSkYaUU0cWF4eW2EWX2EaZGUWYmIVX18YXV0ZXl4cW10cW10WWFwX Wl0UU10QTlgRNTQEJCMGEAcABwAAAAAAAQAAAwAABQACCAECCAEDCgIHDgUFDQMFDQMDCwEEDgMK DwQHDAEECwECCQADBwEECgMJEQUHDwMJDgQGCgEECQAGCgEHCwEIDQIEDwIDDgEGDAMDCQAFDgAI EQIGDgMFDQMHDgUGDQQIDgYHDAQFCQMFCQMECQAHCwEIDQIECAADBwAJDgQMDAQMDAQEFgcHGwoI KxwRNycPPzoZTEYSWl0TW14SWlsRWFoaVFwgW2MSVVcUV1oWV1sSUVUUSkgKPTsPJh4BFA0AAwAA AAAAAAABAAAAAgACBgMEDAcGDwkEDgYEDwcKDgoKDwoIDgcHDQcGDQQJEAcMDgMNDwQIDgcHDAYH DwQHDwQHDwcFDAUHEAMFDwIKDQIKDQIJDwgHDAYIDwEJEAIKEAMMEgQKEwcKEgYJEQYHDwQCCgQE DAcPDwcSEgkHCgcHCgcIDwoFDAcCBwQABQIACgMDDwYEGQsKIBEEJyUOMzEKPUENQEUKQ0wNRk8O TE0LSEkMPj8HNzgNKBwOKR0KFggACAAAAwAAAQAAAQAABAAABAADBwIDCAQHDAcHCwYIDAcJDgQH DAMODwgKCgQKEgcIEAUEDQcEDAcHDAYHDAYKEgMKEgMHEgUFEAQHEAQHEAQLDgcICgQHCgcHCgcK CgQLDAYKDgQICwIKEQMLEwQMEAkHCgQLDgELDgEIEQIFDgAEDAIFDQMGCgYDBwMABwMABwMCCgYE DQgADw8EFBUHIxwHIxwEIyQKKywLKCgKJycKIh0HHhkEEQgACAEABgAAAwAAAwAABAADBQADBQAD BwEHCwQMDgULDQQKDQMKDgQMDwIKDQEICAEICAEKDgEKDgEKCgENDQMJDAEKDQIHCwIECAADBwAD BwAHCgAFBwAEBAAFBQAGBAAEAwACDgUACQEADAEBDgIDEgcHFgoHJRoSMicLOzEWST8WT1MbVVgY XV0aX18VXWEUXF8YWl0cXmIXW1cWWlYbU1cYT1QPODoILzEKHhAADwMAAwAAAQAAAQAAAwAABAAC BwAHDQMHDQMECgEIDwQIEgcFDwQBDwEEEwMJEQQJEQQFEAQEDwMIEAMKEgQIEgYJEwcKDwUKDwQK EQgKEQgGDwQHEAQGEQQIFAcIEgYGDwQFDwIIEgQKEgQJEQQHEgYHEgYLDwQKDgQLDAYKCwUKDgcH CwQJEwcGDwQFDAQJEAcECgQBBwEDCwAHDwEEEgcHFgoKJRsRLSMJOjkOQD8NRkMNRkMPRUYSSEkM RUQMRUQNMjAGKScNIxcADwYABgAAAwAAAAAAAAAAAQABBAAABwEDDQcIDgkKEAsJFAoIEwkMEAoL DwoKEAgKEAgMEAkLDwgPEQYLDQMHDgcHDgcHDwQKEgcMEAwJDQkIEAMMFQcNEgcKDwUPEAULDQMH DQMKEAUNEAcNEAcPEAcPEAcIEgYHEAQLFAgKEwcNDwYPEQgGDQYJEAkKEgwHDggABwEABQAABAAA BAABBwAECgABDQcEEQoCGhYEHRkFHyUEHSMFHREEGw8EFAgADQMCBwABBgAAAQAAAAAAAQAAAgAC BAAEBwADBgEHCgUICQYLDAkJDwgHDQcLDwoIDAcHCwQIDAUHDwQIEAQECwYECgUECwEGDgMEDwQG EQUGDQYECwQHCwMIDQQKDQYICgQHCwQGCgMGDgMFDQMGCgEGCgEKDgELDwIKDAMGBwAGCwAHDQAE DQAEDQAECgEECgADBwEDBwEABQAABgACBwACBwAABAABBwEDCQQBBwMABwMABgIDCwQBCQMBBwAA BgABBAAAAQAAAgAAAwAAAwABBAABBAADBwAFCwIFCwIKDwMHCwEKCwAMDQELDgEHCgAKCgIJCQEI DgAIDgAIDAAJDQAGDAEGDAEODwEKCwAHCgAFCQAECAAFCQAEBgAFBwAFCAADBgAHFAkHFQoGEAgE DgYJDwMHDQEDCgQKFAwGHRUNJh0JLSsRODURSEATSkMSR1APRE0aTE4eUFMURUUPPz8RNzoPMzcK GxAADgQBAwAAAwAAAAAAAAAAAwABBQADBwAHDAEHDQMFCwEIDAcKDwkKEAgJDwcGEQQGEQQOFAUM EgQJEQUKEwcIFAcIFAcKFAYJEwUKDwcKEAgOEw8PFREPEA4PEA4IEAQKEgYJFQcGEQQHEAMKFQcN FAkLEgcJEwcHEQYNDgkPDwoLEwsJEAkKDwUKDwUJFg0EEQgGEAcDDQQHCgcHCgcICAAFBQABBAAD BgADCQQCCAMBFQUEGQkJIhYFHREGGg8KHxQEGQ8BFQoACQAABwAAAgAAAQAAAAAAAAAAAQAAAgAB BAADBwEEDgQEDgQHEAQHEAQHEAQHEQUPEAcODwcTFQoREwkNDgkNDgkLDQQKDAQFDgUFDgUKDQML DwQIDAUHCgQIDQQJDgQMDgUKCwMKDwQKDwQHDQMIDwQLDwQLDwQQDggQDggGDwMFDwIHEAUFDwQG DwAJEgMHDwIHDwMHDwMHDwMDBwACBgAABQABCAABAwABAwABBAAAAwAABAAABAAABgEAAwAABAAA AwAAAQAAAAAAAAABAQAAAAAAAgAAAQABAwAEBQAHCAEFCgEFCgEKCgMKCgMLDQQKCwMKCwMKCwMK CwMJCgMJDQALDwIHDQEFCwAHCgEJDAMKDQMICwIHCgQHCQQJDAEKDgMKCwEJCgEHCwEHCwEGDAIH DQMLDgEKDQEICwIKDQMKDgQJDAMHDQAFCgANDQMKCgEMDAQHBwADCQAECgAICgIFBwAEBAAEBAAG BAAHBQABAwABAwAABAABBAABBAABBAAABAABBAAEBwACBAADCgAABwAABQAECgEECAIFCQMJDAMK DQQLDQQLDQQLDQMKDAILDAEKCwAKCwMKCwMJDQAHCwAKDAALDgEICgILDQQPDwMLDAEICwEGCQAI CgEFBwAFBQAFBQAFBwAFBwAKEQYLEgcKEgcECwEHCgEEBwABBwADCgADCgAEDAEDDwYFEgkGGAsG GAsHIhEEHg4HFhAEEg0HFgcJFwkFDAQCCAEAAwAAAQAAAAAAAAABBAABBAACAwAEBgAGCQAICwIH CwIFCgEJDAMLDwQNFgcMFQcMDQcKCgQKDwIKEAMKDwYGCgIFEQEFEQEGDwAHDwEKDwQKDwUHEQUF DwMHDwMJEQUKDwUKDwQHEgMGEAEKEgMMFAQLDwIPEwQODwcMDgUKDwQIDQMFCwIIDwQIEAMGDgEE EgMEEQMFDQMDCgAFDAcHDggJCgMJCgMFBwAEBQAABAAAAwAABQAABQAABAAAAwAAAwAAAwAAAwAA AwAAAwAAAwAAAgAAAwAAAwAAAwAAAwACBAAHCAEMDgUHCgEHCgEKDwMKDwMKDwQNEgYNEAYJDAMK EQEKEQEIDwMJDwMLEAYKDwQHDwcGDwYHDwQHDwQIDwQHDgQKEAUKEQYNDgcMDQcPDwcQEAcODgQP DwQLDwUKDQQNDQUMDAQKEAQLEgUOEQUPEwcIEwMGEAEHCQAJCgEGCwAHDQAKDAIHCQADCgAHDgAE BQADBAACBQABBAAABAAAAwAABAAABAAAAwAAAwABAgAAAQABAQAEBAABBQADBwAEBwAHCgEJCwQK DQYIDQQJDgQNDAcLCgUMDAQMDAQKCgIPDwYPDwMODwMKCwEHCQAKDwMKDwMKEAMNEwQNEQMLDwIH DAMKDwUNEQMLDwIMDgQKDAMJDwQJDwQKDwcKDwcNDgcNDgcKEAYNFAkPEQYLDQMLDwMKDgMQEAgQ EAgQEggMDgQKCwMKCwMFDAQFDAQKCwEKCwESCwUSCwUIDwAECgAECQADBwADBgABBAAECQEECQEE CgEGDAMHDwQHDwMECgEJDwQKDwMLEAQNDQUNDQUODwoODwoMEQUKDwQKDAIKCwEPDQMPDQMMDQcK CwUMDgQPEAcKDAMMDgQKDQENDwMKCwMICgILCgEKCQAGBAAFBAAEBQAFBwAJCgEHCQAFDgADCgAD BwACBgAGCgAECAADCgADCgACBwAABQAABAAABAAABAAAAgAABAAABAAAAwAABAAABAAAAwAAAAAA AAAAAgAAAQAABAADBwABBAACBQEGCQEGCQEHCgEKDQMHDAEHCwEFCwAJDwMNDQUMDAQIDQIKDwMJ DgQHCwIHDwMHDwIGDgMIEAQKDwQLEAQHDwMHDwIHDwMHDwMMDwYJDAMJDwcIDgYKDwULEAYMDgMO DwQPDwcUFAoKEgQKEwUIDwMHDQEMDwcKDQYIEAUECwEDDQAIEwMOFQkIDwQHCQAICgAFCgAGCgAC BQACBQAABgAABQAAAwAABQAABAAABQAAAgAAAgAAAgAABAAAAQAAAQABBwABBwAHCAEICgIJCQER EQgKDwkJDQcPEAcPEAcNEggPFQoLEQoHDQcIEAULFAgFDQIJEQUKFAQIEQIIDwgHDwcIDgcKDwkJ DQYKDwcNFQsKEgkLFAcKEgYQEgcVFgoQEAcQEAcKDgoJDQkNDwoLDggODwcREwoVEAsUDwoKEQEL EwIOEAMTFgcPDwUNDQMOEQcOEQcLEgcKEAUKDAQKCwMECgMBBwACBwABBgABBAABBAAABAAABAAC BAAEBgEEBwEFCQMDDAEEDgMLDgoLDgoNDgkLDAcIDAcJDQcMDwQMDwQPEAcQEgkMCwcPDwoODAQP DgUQDwYRDwcOEwgMEQcJDwUJDwUKDwYLEAcLEQwHDAcLDgcKDAUKDQcJCwYLDggNDwoKDwoKDwoN DwwKCwkFCgQIDgcOEwgMEQcJDwQKEQYMDwkMDwkNDgoMDQoODwsMDQoHDAQHDQUHDQMHDQMJCgQH CAMEBwAEBwAEBwAFCAAEBgAEBgAEBwEEBwEDCgMECwQGCwQGCwQHCwQIDAUKDAQKDAQLCAIPCwQK CgQMCwYKCgMKCgMKCwEKCwEKDAQJCgMLCgINCwMPDgIPDgIICAEKCgIKCwMKDAMKCgAHCAAHBwAF BQAFBAAFBAAHBQAHBgAHDQMKEAUDDQQDDQQOEwkOEwkREg0MDQgHEQEEDwABBgADBwEAAwAAAwAA AwAAAwAAAQAAAQAAAwAABAAAAwAABAAABAAABAADCwEABwAGDAMJDwUHCwEKDwMIEAQKEwcOEQcV GA0KFAgFDwQKCwUREgsQEQwKCwcHDAYHDQcLEgcIDwQKDwULEAYKEAYOFQoOEwcOEwcKEQoNFQ0P FAcNEgYMDwYNEAcOEgoKDwcQFAoPEwkPFgoKEAULDwgVGREMFQgPFwoPFAkPFAkVERASDw4SGRUS GRULEAcPFQoWGA4SFAoPFxELEw0MFg8KEw0JDQkHCgcGEAcBCgIDCgEDCgEBBgABBgAAAwABBQEA AwADBgADDAMCCwMEEAcEEQgMFA4LEw0MDwkUFhAPFgoLEgcTFA8PEAsOEQ8PEg8HEAsJEw4KFAcK FAcIEwkKFgsKEgQKEwUKEgoMFAwODgYSEgoQEgkREwoRFQgTFgoOGgoOGgoNHRQPHxYODwsQEQ4O Eg4KDwoOEwkOEwkPEA0WFhMXFAwWEgoLEwQPFgcPFQoNEggPDwcSEgoOFQkLEgcREg8KCwgKDwUM EQcHDAQGCwQGCgEDBwACBQADBgAEBAIDBAEBBAIDBQMDCgQEDAUGDwQHEAUJCwYOEAoOEAoLDggH CwQKDwcJDwUKEAYJDwQLEgcNEAcKDgQJDAMJDAMREAQTEgUMDgULDQQHDAMJDgQHCQEKCwMKCwUH CAMIDQMKDwQIDQQJDgQKDwQJDgQJCwQICgQIDAcGCgQGCgQJDQcKCgQKCgQECgEJDwQKDwMJDgMH CQEMDgQNDwQKDAIKCwMKCwMECAICBQADBgAEBwEEBwEDBwECCQADCgAECAAFCgAHCAAJCgEGCAIH CgMFCwEECgEKCgMLCwQJCgIICgELDAYNDgcICAEKCgMKCgEODgQLDgEKDAAKDQQICwMLCgIODAQM DQEKCgALCQAPDAAKCwMHCQEKCAAIBwAKCgAHBwAHBgAEAwAFBQAFBQARFgsRFgsHEAoFDggKFg8N GBEUGBQKDwoMEwcMEwcEDwMEDwMDCgAEDAEHCAEGBwADBgECBQEAAwAAAwAABAACBwAHCgcHCgcG DwMGDwMKEwcMFQkMFQkLFAgNFAcMEwYPFAkPFAkIEgYHEQUPEgcRFQoIEQgJEgkKDwkPEw0SFgsK DQQMDwkQEw0IDwcKEgkTFQkUFgoNFQ0LEwsKDwcMEgoMEQcMEQcMEwcOFQkLFAcMFQgMFgoJEwcU FgsWFw0OEwcOEwcLEwoPFg0TEwoQEAgMFA4PFxELEwsOFg4JFA0LFg8KEg8KEg8HDwsHDwoDCggD CggFCQMHCgQHCgQHCgQABAABBgEABgADCQEDCgMDCgMGCwcFCgYPEAsODwoKEQoLEwsKDwkLDwoK EAYJDwURFA4NDwoLDwgIDAUJEAkKEQoKEwcJEQUKEgoOFg4KDwcIDAUJDwQKEAUMEAENEQIPEQQN DwMQEgcREwcMFggNFwkLFwoLFwoKEwcKEwcOEQcKDQQMEQcMEQcNDAgODQkOEQcNEAYOEAMPEgQP EgoKDAULDAYNDgcNDwUNDwUIDAUHCgQMDQEMDQEFCgAFCgAECgAECgADCAADCQAEAwAGBQIGCAQH CgYGCgIGCgILDwgJDQYKCgQKCgQJDwUJDwUKDAUKDQYKDQQKDQQICgUKDAcKDgMNEAQKDwUKDwQP DwQPDwQKDQINEAQPEwMKDwAKCgIODgULDgcKDAUKCwMICgEHDAMJDgQHDAMGCgEKDwQJDgQHDQUG CwQHDAQJDwcKCwULDAYMDwcKDQYHDgUECgMHCgQIDAUKCwMKCwMKDgQJDAMFDQIEDAEJCwQGCAIH DwIHDwIFCgMGCwQICAEKCgIHCgAJDAEKCgQLDAYJDQYHCwQPDQMQDwQMDAQNDQUKCgMMDAQKCwUL DAYMDAMPDwQLDQMICgAKDAIODwQMCwYKCQQLCgENCwIOCwYSDwoQEAYMDAMODQEMCwAJCQIICAEE BAAEBAAEBwAGCAAQFgoPFQoNEggKDwYMEwcMEwcPFQoKDwYPEwsIDAUGDwYEDAQDBwEFCQMFBgME BAIECAQECAQEBwIFBwMDCwMDCgMECQQECgUJDwMKEQQMEQUMEQUKDwYMEQcNEgYKDwMKDwQKDwMK DwANEQIPEAcPEAcHEAUHEQYPEAUPEAUPFAcNEgYIDwgGDQYHEAMHEQQJEQQKEwULFAcNFgkIDwcH DgUKEgQKEwUNEQMNEQMMDwYLDwUJDwUHDgQKEgcIEAUIEgcIEgcGDwYGDwYKEQMNFQUPDwoPDgoM EQcPFAoKFQgMFgoIDAUIDAUKDwoKDgoJCwYJCwYKCgcHBwQHBwIKCgQECQIGCwQFCgEECQEECQEH DAMLCggLCggPEAUMDgMMFggKFAYKDQYLDgcIDgcKEAoNDwsMDwoKEAYIDwQKDgQLDwUKEQcJDwUI DwcKEQgMDgQKDAMHCwMKDwYMDwAPEgMOEAIPEQMPEQcSFAoOEwgPFQoNFAcMEwYNFgkKEwcPDwoP EAsOEwkKDwYODwoNDgkKEgkIDwcQEwQRFAQPEQoOEAkPEQoMDwcJEQUJEQULEgUHDgIHCwEHDAEK DgMHCgAGDwQDCwEFCgAHDAAKCgMMDAQJDQcHCwYGCgAFCgAIDwQKEAYMDwcJCwQNEAYPEgcNDwoQ Ew0QEggODwYMDwcNDwgNDwYODwcKEAMLEQMNEQMMEAMGEQMFEAMLEwMKEQELCwMODgUNDwsMDwoL DgcKDAUIDgcJDwgKDgQLDwQPEgYNEAQJDgMIDQIIDQQKDwYMDQgMDQgPDgsQDw0PEQsLDggKDwUK DwYKDwYKDwYNDwgKDQYMDwEOEAIPDwkLCgULEQMMEgQIEAUIEAUPDQoNCggMDgUNDwYMDwYLDwUQ DAcSDgkPCgYQDAcKDQcLDggJDwQIDwQKDgcLDwgODgQODgQLDwMLDwMODwcODwcLCgcMCwcMDAQM DAQREA4REA4PDgUPDQQLDwIIDAAICgAGBwAEBQAEBgAGCgAECAAKDwcKDgcKEwUKEgQIEgYHEQUL DwIJDQALCwMLCwMGCgEIDQMHDgMHDQMFCQMECAICCgQDCwYGCgEIDQMHEAUDDAIEDAIEDAIKDwIK DwIMEgMOFAQKEAMKDwINEQMOEgQMDwQNEAQJDgQKDwQMDQcNDgcMEAkJDQYPEwQOEgQIEgQHEAMJ EQYIEAUKDwIKEAMJEQYIEAUIEgQHEQQJEQYJEQYKEwcJEQYLEAYLEAYMDgMNDwQJEQUHDwQJEQUK EgYNFAcKEAQNFAcKEQQOFAUPFgcUEAkWEwsWGg8PEgcPGAcOFwcKDAMNDwUJDQYMEAkODQoKCQcH CgcHCwcGCgEHDAMKEQQJDwMFDQMGDgMHDgcECwQMEAkLDwgPDgsUExAOFAsJDwcIDwcKEgkEDQYK FAwOEAoMDwkOEwkLEAcLEgcLEgcNEw4KDwoPEQcPEQcLEgcIDwQHCgUNEQsMDwUPEwgPFgkNFAcV Fg8QEQoPEwsWGhIPGxINGA8QFwwQFwwLEQwOFA8PEAoPDwkJEAoMFA4PFBANEg8PFAoQFgsLEwsK EgoLFQsKEwoKEwcOFgoPFgoNFAgHEAcHDwcNEQ0JDQkNFgkMFQgGEQUFEAQPEAoLDAYKEAMPFQYI EgcIEgcKDwkPFQ4PEQsNDwoPDwcQEAgSDwoTEAoOEQcNEAcLEQwKEAsPEgcMDwUNEAYNEAYMDwQK DgMKEgkJEAcKEgQJEQQPCgYQDAcPDwoPEAsMDwkKDQcPEQsPEgwOEAoMDwkMDQgNDgkMDwQMDwQI DwcJEAcKDQcKDAcODQoPDwwPFg8MFAwJEQUKEgYQEQ4PDwwKDQMJDAMODwEODwENDwEQEwQKEgQJ EQQHEAQHEAQLDAYJCgQKCgQKCgQNDwQKDAIODgYODgYKDAQLDQQJCwQKDQYLDwUICwMHCgEJDAMO DgQNDQMJDAMKDQQOCgMOCgMKBwELCAIRCwEQCgEODAMNCwIPCgMRDAUKDQAHCgAHCQAFBwAEBAAE BAAFBwAFBwAMEAkPFAwQFwsNFAgMEQ4NEg8PEgcLDwQPDwQQEAYUFgsREwkHDwEMFgUKEwcJEQUK FQwIEwoHEQUKFAcLFAgIEAUIEAQJEQUKEAQLEgUPFwcPFwcNFAgLEgcKEwcKEwcPFQgMEQUMEAkP EwsLEgcKEQcPFQoTGA4LFwoHEwYJEwcJEwcIEwkLFgwOFggLFAYMEgoNEwoSFg8NEQoKEwcMFQkM FgoJEwcKEQgLEwoLDQQPEAcUFw0UFw0LFgkMFgoSFAgSFAgWGg8PEwgPFAkOEwgWEgcbFgoWGg0T FgoQGQoQGQoLEgUPFgkPFgoPFgoREgsTFA0IEwwHEgsHFAMKFgUOEwgLEAYJEwcJEwcMFhEMFhEP ExARFRIWFxgVFhcTGhcQFxULFQ8KFA4KFQoNGA4RGwoTHQsPFQgPFAcPFAoPFAoKEgoMFAwXEw4W Eg0NFgkLFAcKDwkMEAoPEQ8PEQ8LFQ8KFA4TEg4TEg4UFAoVFQsUHRAUHRARGg0MFQgKEwsMFg4N DwsQEw8NEwwOFA0ODw8SFBMPEA8MDg0KDgoMEAwUFgwUFgwQEggTFQoOFQkQFwsOFw4KFAoKEAMN EwQRFwgLEQMKEAMKEAMMDwcMDwcQEwQPEgMKEgYKEgYQEQwPDwoMCwcODQkPCwcOCgUQEQQPDwMJ CgIMDgQKDwcLEQkNDwQODwQOEQcNEAYLDQQICgELDAYLDAYNDQQODgUODAQMCgMKDAQLDQQMDgQK CwMLDggLDggNDgoLDAkKDgQHCgEFCgAHDAEIDwEKEQMMDgQKCwMPDQMQDwQNDwMLDgELDwMKDgMK CwMHCQEICgALDgELDwEMEAENDwELDgAFCwEGDAIKDgELDwIICgIKCwMMDwYKDgQKCwELDQMODwML DAEKDAIKDAIJCgMLDQQHDAEHCwEKDwEMEAMODAMODAMKDwEMEAMMCgMODAQPDAUNCgMQCgQSCwUL CwIMDAMPDAEPDQIKCwAJCgAHBwAHBwAFBQAEBAAFBQAEBAAPFQoPFAoPGAwKEwcLDwoNEQsRFQoQ FAkUFgYRFAQWEwkWEwkLFQ8LFQ8MFhUKFRMPFhENFA8HFQoIFgoTFRIVFhQPGhMLFg8PFwsOFgoM FQkLFAgNFAgOFQkIFAcKFgkPEw8SFhIVFxENDwoMFAoPFw4PFg8PFw8KFAwKEwsPFgoMEwgOFg8P FhAOFw4KFAoSFAoVFgwaFg8WEwsKEQYLEgcNEwQOFAUOFgoPFwoQFAkQFAkUFAoREQgNFgYPGAcQ EgkUFgwUGQ4OEwgOFgwRGQ8VFgwWGA4PGAYQGgcRGQcRGQcREwcUFgoRFg8NEQsTFQoQEggKFg8I EwwLEQMQFgcOEwcNEgYLEgcMEwgOEgwPEw0QFgkRFgoQEgkSFAoWFw0QEggOFgoOFgoKEQYOFQkS FAoVFgwPEgcQFAkNEgcOEwgNEAcKDgQMDQEPEAQODwQNDwQJDwcKDwcPEwgPEgcKEQgKEgkSEAYW FQoQEwUUFggRFQoRFQoMEgMOFAQODwcPEAcNDwgMDwcPEgQPEQQMDwULDwQPEQgLDQQNDwYMDgUP EQYQEgcTEgUQDwMNEQIPFAQLEgcHDgMLEgcLEgcLDwEKDwAMDwALDgAKDgELDwIPEgQUFggMFAQK EQMMDgMMDgMKDwQLEAQNDQMKCgEOEgIMEAEMDwEOEAIKDAQMDgUKDwIKEAMKDAIICgAJCwANDwMP DgYODAQLCgUNDAcPEAcMDgQMDwoJCwcKCwMMDgQNDwYODwcPDwcNDQUKDAMKDAMGDgMKEgcIDwQJ DwUKCgEMDAMOEgMMEAEMDgMNDwQPEAMODwENDwEMDwEKDwAPFAQNEAYMDwUPEAMPEAMLDwQNEAYM DgQODwYPEgMPEgMPEgwPEgwPDgYQDwcQDgMRDwMKDwQKDwQKDAMNDwUKDwULEAYKEQEKEQENCwMP DgUODwQMDgMQDgEPDQEODwEPEAMPDQIQDgMPDwEPDwETDgcRDAUNDQMKCgEHBwEFBQAGBgAFBQAE BQAEBQAMFQgKEwcOFQcLEgUKEQcKEQcPEwcNEAQRFAYRFAYPEgQSFQcOFgoKEwcMFAoKEQgOEAoP EgwMFA4KEQsRFA4PEgwKEgcIEAUOEgwPFA4KEAgKDwcJEgkKEwoLDwoOEgwPDwwQEQ4QFgcOFAUL FAcKEwcLFAYKEwUPEQoPEQoKDwYKDwUMEAkLDwgLFgkKFQgNDwMPEgQQEgcQEgcMEQUIDQIMEwEP FgMLEAYPFQoRFAYRFAYREQcPDwQKEQcMEwgKDwcMEAkKFAoJEgkPEQ0QEw8PEgcPEgcKEAgMEgoS FQcPEQQQEAcODgUMDQgPEAsMFQgOFgoPEQoOEAkPEQMTFgYSFgYPFAQMEQcPFAkLEgcOFQkKFgkL FwoWFAsWFAsSEwYREgURFgcWGgoMEwcMEwcTEQcSEAYPEQgSFAoPDwMPDwMPFAkLEAYNDQMQEAYP EQgPEAcKEAgJDwcNEggNEggLEwoLEwoTFQsUFgwOFgoPGAsUFgsSFAoRFAQSFQUREQcREQcOEQcO EQcPEwMPEwMMEgQNEwQTFgoRFQgPEQgPEQgRFAwPEQoWFw0SFAoOEAMSFQcSFgsPEggKFAoKFAoP EQgPEAcPEAUQEgcPGg0PGQwPGwsRHg4PFQoLEAcNDwgLDgcLEw0MFA4PDwkLDAYNEwQNEwQKEgMM FAQPEAoMDQcNDwUPEAcPDwcPDwcOEAIOEAINEAQPEwcPDw4PDw4VEg8WExAMEg0KDwoODQcTEgwQ EgcPEAUTFAcTFAcPDwcREQgLEAcPFAoKDgsPEg8RDwoSDwsREwcQEgcODwYREwkWFQsWFAoWFQsW FAoMEwgMEwgPEAoREgsSEAYQDwQPEAcREwkNDQQPDwYPEQ0SFRATDw0SDgwQDAkRDQoSEAcQDwYL DAYNDgcMDwUOEQcTEQcTEQcPEQMNDwEOEQcOEQcQDwMPDgIOEAENDwEKEAMKDwILCgEPDgQPCwEU DwQQDAkPCwgKDgEIDAAHBwAGBgAEBgAEBgAHBwAGBgALFQQLFQQNFQULEwQJEQQKEwUPFAoMEQcR EwcODwQPEAUQEgcPEAUPEAUKEQcKEAYMEQcLEAcLEw0IDwoJEAkHDgcPDwcPDwcLEAcMEQcQFgsP FAoMEwgPFgoMEgoKDwcKEgcHDwQPEgYVGAsQFgoMEQcMFgUMFgUOEQcNEAYIEgcKFAgOFQkKEAUM DwYOEQcOEAMSFQcSFgcPFAUMEwgKEQcPEwQSFgcSFgoQFAkKEgkLEwoREQgREQgJEgkKFAoPFg8M FAwMFQkPFwsPEwsPFAwVFg0WFw4QFg8MEgsPFAwSFg8WGg8VGA4SFQ8TFg8PFQYPFgcKEgcPFwsP FgUPFgUSFgcOEgQUGBAUGBANFwsOGAwPGRMOFxEWHBYUGhUWGgoWGgoVGQgWGgkIFgkKGAoOFA0L EQoQEgkREwoTFgsTFgsNGQsLFwoKEgYQGQwQEwsSFQ0RFhEMEAwRFQoTFgsQEggTFQoTFQsVFg0Q GQ0PGAwPEQsQEw0REwoSFAoRFgcUGAkTGA4PFQoTFwgRFgcVGg0UGQwMFw8KFg0IEwoKFg0RGg4P FwsOFAsQFg4OEg4PEw8SFQ8QEw0PEAsSEw4PFgsPFgsSGQ4WHhISIhIRIRERHBYPGhUUFw0UFw0R EwkXGQ8QGBAOFg4TFgwVGA4TGA4QFgsLFAgOFgoVGA0PEgcPEggPEggSDwcRDgcPEAoODwgODwoO DwoPDwkPEAoPDgsTEg8SFAoODwcPDwcUFAoTFgcRFAYREwkPEQcRDAYRDAYQEAgSEgoRDQoXEw8W Eg8VEA0REwkUFgsQFAcQFAcSFAoPEQgNDwUNDwUSEgkPDwYLDQMKDAILDwQKDgQNDwQMDgMNDQMP DwQPEAsQEQwRDwkPDQcMDAQQEAgNDAcKCgQLDwQPEgcKDgQKDgQOCwEUEQUMDAQNDQUHDQMECgEH DAEHDAEKDgMJDAEIDgEIDgENDAEPDgIODQAPDgENCgEQDQQKCwAICQAKCQAJCAAEBQACAwADBAAG BwASFgoTFgsOFgQMFAMRFQoTFgsRFAwTFg4NEgYMEQUSFAoUFgsOEwgQFgoUFAkVFQoRFQoPEwkP FBANEg8KFAwMFg4SEQsQDwoPEQcWFw0VFhAREg0PEg4SFRALFgoKFQkMFg4KFAwRFgwQFgsWHAwS GAkUFgoUFgoSFgoTFgsOGg0KFgoPFgoOFQkPEwsRFg4PEwkSFgsSFwwPFQoKEwcNFgoRGAwRGAwR FQoRFQoPFQwQFg4RGAoTGgwNHg8MHQ4QFQ8QFQ8QGw8PGQ0RGBQWHRgZHBQWGRERFA8XGhYTFxMT FxMVFhAWFhEXFxYXFxYUFwoWGg0PGREPGREXGQ8TFQsXFhIXFhIXHRIWHBEUGQ8YHhMVGBYSFhMS GhQTGxUVHhEUHRAUHRASGw8OHBIOHBIPFw8QGBARFQoTFgwQFhIPFBAPGQ0OGAwMFQkOFgoWFg0U FAsPGBIKFA4VGA0UFwwTEwgREQcWEgoaFg8OFhIKEw8VFxMZHBcVHBAPFgoVFg0VFg0OFgoRGg0R FA4PEgwUEw8WFRAVFhIUFREQFQ0RFg4UGgkOFAQdFA4eFQ8SFg8MEAkOFA0PFg8KFggIFAcQEwUR FAYVEgYZFgoOFgwMFAoLEg4MEw8QEggQEggVFAUXFgcREwoNDwYTEgQXFgcWGQ4NEAYKCwMREwoT EwoPDwYNEAYNEAYPEAQPDwMWFAoSEAcLDgcNDwgNDwQPEQYMDwYLDwUPEAUMDgMPDQITEAQMDgUL DQQLCwQNDQUPDQQQDwYVEAUSDgMTCgMZEAgQEAcMDAQJDwQKEAUKDgMNEAQTEwgNDQMPDAMPDAMS DgITDwMVDwcRDAQKDwQMEQUMDgMMDgMOEAIPEgMOEgQPEwQKCwMLDQQLDgEMDwIMDAQMDAQSDwMV EgQODwEKCwAWCwMYDgQVCAMYCwUPDAMRDgQWDwMTDQEODAQMCgMHDAMKDwUPDQQPDgUPDgENDAAO DQAPDwEQCQAPCAAOCgAOCgAKCAEFBAAEBAAGBgAQEw8WGBQWGg0WGQwPGQ8PGQ8SFw0SFw0RFg8S FhARFxIUGhUWFhEZGhUWGhYXHBcRGg4THA8QHhcOGxUOFhIRGhYWHxERGg0VFgwaHBEZHBcSFRAP FA8SFhITGxUPFxEQFg8TGRISGQ4UGw8SFhIQFRAWFg8VFg8PGQwPGQwSHRAMFgoPEw0QFQ8PEg8P ExAPEgoRFAwQGQ0SGw8OGAsPGQwQGAcPFwYWFgwWFgwRFhETFxMTGg4VHA8SGwwPGAoTFQsUFgwS GwwSGwwcGBcgHBsiHxoYFhEUFhIWGRUWFxIWFxIWFhUWFhUUFxYWGRgTGg8TGg8SFhASFhAXGhQR FA4RFg8UGBIeGBEcFg8ZFw4aGA8WEw0aFxEXGw8TFgsPGgsPGgsRGg4OFgoNFQsOFgwNEgcLEAYO DwQSFAgVFg8PEAoQFgsPFAoQEwsPEgoQDwsSEQ0MEwgMEwgVFgwTFQoUEgkPDgUUDwcYEwsPEgwM DwkVEwoXFgwTFgcPEQQXFgkVFAcQEgkTFQsPDwwPDwwPEAsREg0QEw0QEw0PEAcODwYWFAcUEQUV EQkTDwcOEQcQFAkQEgcREwcKEAQMEwYQEgEREwINEAQNEAQJEAcKEQgKEQYKEQYLEAQLEAQNDwQO DwQPDwkODQcNDwQLDQMLDgEMDwIODwcNDwYPDwoPEAsRDwMUEQUXDwMZEQQTEwgODgQQDQQUEAcQ DwQPDQMLDwQMDwUPEAcMDgQPEAcNDwUPDwMREgUODwcNDwYODwYPEAcSEAcQDwYaDgMeEQYKDwcM EAkMEQcPFAoOFgYLEwQWFwsUFgoPFAcMEQUUEwQTEgQcDgokFRAZFAwUDwcKEwMJEgMQEwQTFgYR EwkPEQcKDQINEAQXFAQVEQITEgQUEwQiFQQfEgIREAMPDwEYDwcYDwcmFgggEAQODgAZGQciFQQi FQQdFggcFgcWFAoSEAcQDggTEAoSDwMVEgQWFAcVEgYQDQQPDAMNDQQKCgIJBAAIBAAJBAAHAwAR Gg4PGAwOFgoMFQgTFg8VFxEPFQoPFQoQFwwPFgoSFhIRFhERFAwXGhISFhAPFA4PFQwUGhEQFRAN EQ0IEAwKEw8OFgoOFgoQEgcREwcSEgoWFg0MEwgNFAkQFQ8MEAoPEwsPFAwUFgsSFAoPEQgSFAoT FQoREwkLFAYNFgcRGg4QGQ0UFAkREQcQFAoPEwkNFAgPFgoOFggNFgcMFgUKEwMPEQQUFggWFgwT EwoQGQwQGQwVFwkUFggUFwoWGQwSFgoSFgoTFQsREwoKEwcNFgoVGg0SFwoWFgwVFQsUFgsQEggS FAoWFw0RGRELEwsQFQ0PEwsTFgYUFgcPFQoLEAYNEAYRFQoYFQoWEwkWFg0UFAsPEgcTFgsVGA4S FgsPFQoQFgoSFAoSFAoWEwsWEwsPFAkOEwgVEwgVEwgPFwoNFgkSFgsMDwYLEAYPFQoQFwsPFgoL DwgMEAkPFgoPFgsSEwwQEQoTFQoQEggPEAoXGBEWFgoUFAkTEgQREAMTFAUREgQTFA0PEAoPEAsR Eg0UFQ4UFQ4WFQwZFw8UFwwLDwQUFREPDwwRGxULFQ8RDwcXFg0WFQwbGRALEw0NFQ8XFwwVFQoZ ExAbFRIOFA8SGBMTGAsRFgoREwkUFgsREwoSFAoUFQ4VFg8WHQsQFgYPFgcQFgcQFQ0QFQ0REgsT FA0XFwUaGgcYFwoZGAoWFgQbGwggGAUbFAIfEwshFQ0oFgsrGQ4qHhEkGAwVGRUVGRUUFgwSFAoK DQcKDQcQFAoQFAoVFgwREwkZFw8ZFw8bEgwjGRMsHgsxIw8lHQogGAcpGwwuIBAvJQsqIActIA0w Iw8sJSAnIBsnGw4iFgohHhAgHQ8iHQ8iHQ8fGgsdGAobGwUdHQcqHgomGgcnIQ8tJxQvJAc5LQ5A Kg43IQc/JAdGKgxEJw1BJQs9JghBKgtBLQlELwo6LholGgkcFQsZEgkQEAcQEAcLDAEPDwMPDwMN DgIKDAMJCgIHBwAHBwAJBAAIBAAKBQALBgATFgsTFgsPFgcPFQYVFgwUFgsQEgkQEgkPFQwOFAsQ EQ4SEw8QEwsSFQ0RFQoPEwkPFg0PFQwNEwoQFg4NEwwKEAoPFg0PFg0UEw0WFQ8WFQcYFwoWGAgU FgcPFQoMEQcNEggMEQcTFgoPEgYNDwUUFgsOFgoQGQwOFAsQFg4UGQwMEQUSEgcQEAYPEQYREwcN EgYRFgoNEwoOFAsQFQYSFgcaFgQcGAYXGgoTFgYRFgoTGAsREwcUFgoTFgwUFw0TFQoWFw0SFwwT GA0VFxMTFhEQFwsTGg4WGA4ZGxAYGg4WFwsTFQkWFwsSGhQPFxEUFgwWFw4YGA0ZGQ4WGg8UFwwV FgwWFw0TGg8XHxMYGg8WFw0cIQ8WGwoVHA8WHRAVFg0aHBIZGhYbHBgfHBkfHBkWGxUUGBIWFwsX GQ0THRYRGxUTGg4WHhEPGAwRGg4TGg4RGAwWFhMXFhQVGA4SFgsZFREaFhIXGQ0WFwsXGxAWGQ8k GAstIRMiHgwnIxAdGQ8aFgwcHhEcHhEnHxcqIhotJBYrIhQiIBYfHRQeGwsnJBMqIBYrIRcqIhck HBIeGBEjHRYkHw8hHA0XGQ8WFw4fFxAqIhorJBUrJBUeHBAaGA0hHRIpJRksKBUsKBUdHA4bGgwg FhAiGBImIhYjHxQWHAoPFQQWHQgbIw0jIgojIgolIQ8hHQsiGw8iGw8pGwk0JhI4LBAvJAo6Jw48 KQ9HKhFOMBZMMxRBKgwtJxAfGQUWFQcWFQcPEAcNDwUPEQYREwcTFgcRFAYWDwcdFgwsGgc8KRNQ MhpTNBxFLgpAKgdMMxNONRVOMxNQNRVKNBFGMA4/NAw8MQpHMBVFLhM9Kw47KQw9Kw49Kw45KQQ0 JQE7KwU7KwU/KwU7JwM5Lgs9Mg9KMglPNwxaOQtcOw1YNwRcOgdiPRVeOhJVOA1bPRFcPRZRMw8/ LhYtHQgZGAkPDwEKCgELCwIQDgERDwIQDQUOCgMPDgEKCQAHBwAHCAAJBgAJBgAKCAAKCAASFgoR FQoSFwQSFwQVFwcWGQgSFw0QFgsPFw4PFg0TEQkZFw8WGxAXHRISFwwRFgsVGA0SFgoOFA0TGRIQ FxYPFhUQGBIQGBIVFxMUFhIXFgcaGQodGAobFgkWFhESEw4RGA0PFgsWFg8UFQ4RFwcXHgwRHA8S HQ8TFg4XGhIdHhgTFA8ZGQ8VFQsQFAkZHREPGAwRGg4RGA0SGQ4WGQgXGgkZGgwbHA4aHw0VGQgW Gg8WGg8TFQsWGA8XHBYVGRMUGxYWHRgSHBQPGREaFg8gHBQYFxMcGxYWHBEaIBUhJxghJxgWHBEW GxAYHhIYHhIWGxYYHRgdHBkYFxUdHA4eHQ8cFxQbFhMdGBYnIiAiHQ8iHQ8mIhYoJBgfIhAdIA8a GxQdHhYZHBcaHRgbIBsdIh0eHBMbGRAaGxQdHhYfIRYdHxUQGw4UHxEWGA4VFgwTGRISGBEaFhYX FBMTFBAREg8aFBEbFRIbFw0dGQ8gGgkpIxBAJgtJLhI9KwpBLw4uKBUpIxAzJhQ1KBZFLQ9PNxZO OhpBLhAyJREtIA00IgdFMRNMMhZNMxZNNBxAKRIzIQw3JA8yJBQsHg8nHAooHQs5IwtKMxk+MBs0 JxMrHwooHAg1IhBELxxGMhtDLxg0JxEsHwoxGgo/JxYzLBwpIhMeHwUZGgItJAkxKAw0KQg3Kwo5 Jw4yIQkwGwU7JQ1TMBNVMhVPNRBROBJcPBxePh5jPB9pQSRjQBpUMg89KhQoFgQcFgYWEAEUDwcR DAUODgYPDwcSFgcPFAUkEwE0Ig1OKw1UMBFeOR5eOR5cNxNjPRhnQyFhPRxkPx5lQB9ePQ9cOw1a OQpWNQdfOw9fOw9aNxRRLw5XNQ5TMQpMMQZQNQlWMw9YNRBWNQ5RMQpPMRdNLxZfOQtpQRJtQQxz RxBlQwtjQApvRxNrRBBiQRBiQRBdPApXNwdFMRU5JgsjIBkPDAcMDgQLDQQQDwASEAEWDwMZEQUW FAkODAMODQAODQAJBgAJBgAFBQAGBgANFg8PGBAWGBAXGhISGQsVHA4SGQ4RGA0LGREOHBQZHhYc IRgYHhIZHxMSHRAPGg4UIBYQHBMXGRYaHBkYGRwWFxoWGxYYHRgUHBQTGxMYHA8bHxEZGAkfHg4h JxoXHRESGBMSGBMiHBUhGxQlHw0lHw0iIRAgHw8lHxUlHxUiHBUiHBUqHBAnGQ4nJBQgHQ4QHQ0V IhEZHBYYGxUMGwwNHA0OHBQPHhYXHhUWHRQYHRgYHRgWHxgTGxUVGBYVGBYTGxMTGxMUHRARGg4Z HQkhJQ86JRM6JRMyLhY3MhovMRsqLBYfJhUhKBYdJQ8bIw0bHxEZHQ8gGhAgGhAgHgsgHgsrHg8t IBA+IhRJLB05KxU5KxU/JwxEKw8tJxQtJxQwKBYpIQ8tJxAwKhMnIhMhHA4oGwwrHg8yLRQ5Mxkx LRYnIw0TIBINGQwPEQoQEwsVFhQPEA4SEBEUEhMVEg4YFhETFQsVFg0gFgcgFgdGJAxiPSJoRRZq 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 OxleNRNeNRNcNA9hORJeOhlWMhNEKRo9IxU7KQ88KhBNKg9bNxljQSdnRSpkQyhaOR9NLxZJLBNF Kw1GLA5MNBZNNRdRMxtaOyJhPB5cOBpBMRI7Kw0mHQ8bEwcVEQoWEwsQDwoREAoWFQoXFgoREgQZ GgooGwkvIg85LQ43Kww+KxNDLxY7KhMyIgwxJQwuIgowJgwuJAo1Kgg+Mg9BMhA7LAs1Kg0sIQY3 JRIvHgwoIA8lHQwmHg0oIA8oIA8jGwotJQ8sJA8yJREyJRElJgwjJAonHg8dFQcgGw0lIBEsHw8p HA0vHxkqGhUnGxAkGA4QFgoNEgcNFAcNFAcSFQ8WGBIWGA8PEAcQEw0RFA4UFgwUFgwMDwUJDAMH CwAHCgAJBwALCQB+TCKDUCZ/USJ/USJzTxZ3UxlyUBppSBRvRRpyRxxzTCRqRB1vRhdySBluRRZv Rhd+ThyHViOMVyONWCSGVSSJWCeHVy2AUSh5TiB3TB51TyB+VyeATyWATyWGUzCATixwSihqRSNj QyNfPyBbQCJaPyFaQClWPSZWORhfQSBzTCV+Vi6OVy+HUCmGUDGGUDFqTjRXPCRGPTQ4LycfIyIg JCMhICQcGx8WGxccIh4WJhsWJRoVIBoQGxYXIBcbJBsWHhYZIhkbIR0XHRkoJRYvLB1PLBNhPCFn SSpeQSNRORdRORdjPhZuSB5dSClXQyRMMBRVORtUORZjRyJwTSVpRh9ySiZvSCR3USl0Tyd0TSZ6 UytzUS1vTipoSiNnSSJpRh5rSCB7UyB9VCGCUDB7SithQClVNR9hPipbOSVDOjEzKyMZGxAZGxAV GREVGREWGBQYGxYZGhYXGBUQGRYQGRYRFA4XGhQzJwxFOBpKNSJNOCQ8LQw4KQlMORtNOhxJORpP Ph9XRSFdSiZnTSdwVi9hTSVTPxlXNA9dOhRjPhZqRRtwRiN0SSZeQCpWOSNQNw9YPhZWQx5PPBg/ MhI6LQ48LxU8LxVQNBlMMBZHNBdBLxM0KA8vIwozIQ5DLxo9LxY8LhY+Lxw9Lhs0JyExJB40Jw85 KxM5LhY4LRY7LSc6LCZHKiZFKCQzIBI3IxUyJxYwJRQ+JxI/KBM9JBlBKB1HLRhILhkqJxYmIxMj HA4iGw0aFg8aFg8OFg4QGBAaGRYcGxgWFgshIRUnKBglJhYjJxEhJQ8rJBYtJhgkJhomKBwrKRwk IhYoHhYoHhYoJA4oJA4rJxQlIQ8oIA0oIA0jIxgeHhQSHQ8OGAsVGQocIRAeIBUcHhMbHRIWFw0a Fg4aFg4SFgoZHREYGA8VFQsXHQgXHQgWFwoZGgwiHxgeGxUdHRsbGxkSGQ4PFgoLFAcNFgkQFRAS FhIaHBIVFg0UFAoTEwoNFAkLEgcLDwMLDwMICwEHCgAHBwAJCABwQCJ7Sit6Tyh6Tyh3Tyd4UChw ThxyTx16Sht7TBx5TiJzSB15SB99TCJ7TSJ6TCF+UyaEWCuGVyOGVyOAYix7XSh6SCV6SCV/Tyx6 Sih1SSZ0SCVuRBdqQBVlQSNjPyFXPCZXPCZdPx9bPR1TPBlTPBlWPSZTOiNXNRFfPRdoRRZ3UyKJ VTKQWziLXjKCVitrVzFVQR5ALho3JRIdHhgbHBYWGBYZGxgbHhYVFw8THA8VHhAUGw8TGg4VIRkR HRYRFgsWHBAXGBUaGxclHxYkHhZAJgtTNxlQPyJJORxFLQ9JMRJQORFWPhZWQy1NOiU7KBQ/LBdR Oh9XPyRfPB1cORpcPSRiQylfQBtqSiRwSCpzSixcSTFMOiNWPCtUOilROB5XPSNjTy9aRidkRi9X OiRELSFELSFKNChBLCAyLx0lIhEgHhIfHREYGg8gIhYQHBEQHBEWGRUcHxoSJBQSJBQcHxodIBso JRY0MSIuLRssKxklJhYhIhM6Lhg1KhU5LRk0KRZDNBtFNx1IOBtKOh07MxYwKQ46KhxAMCJOLiBY OClGMB1DLRpBMh87LBk4Kxw5LB0yJho3Kh4pKx8cHhMgIBYqKh84KRUyJBAuJRgnHhIaHRYaHRYe HhQnJxweHhQkJBkgIBYjIxghJigWGhwkGw8uJRgnJxwnJxwlIyIsKikzIhMrGgwcGxYfHhkhHxUe HBIrIRkoHhYaGw0kJRYoIhknIRglFhkhExYcFQ0fFw8XFw8XFw8UGhMSGBEVFhAZGhUWFhEcGxYa FQ0gGhIjHRUgGhIcGQwYFgkWFxUXGRYXGhQPEgweDwocDgkPDAMPDAMOCgMNCgMVDwIVDwIICAEJ CQIKCgAKCgANCgAPDQAMCQMIBQADBAAGCAIKCwAKCwAIBwQIBwQGCAIEBwEHDAALEQMNFQQLEwMP FAQOEgMSEAgSEAgOEAINDwEKDQIKDQIKCQcLCggPDAcUEQsQDggPDQcLDQQICgEKCwELDQMIBwAH BQAEBgAHCABqPiF3SSt0TSZ1Tid1TCJ5TyV4Th54Th59UyJ7USF3TCVvRR9wRCdzRilyRiZ4TCt9 UCp3SiVySBtuRRhoSRtlRxloPhptQx5wRSJvRCFuQyVkOh1VNxFVNxFQPRpJNxVNNyNOOCRaNBxa NBxNOCBNOCBGMhY/LBBNMBJYOxtkQBxyTSd3Tix/VjN9WDF4VC1eTiVHOBI4LBgwJRIaIBUbIRYh IB0aGRYbFw0dGQ8aIRAZIA8VIBIVIBIRIBcRIBcRHA0THg8VGg8cIhYhIBsgHxo1LBxDOShEOCU4 LBpDMyI/MB8+MSJFOChDNyQ9MR80LRwyKxpIOiBBMxpJNCFJNCFBLxtINSFNOR9RPSNWOSNXOiQ8 MB46LhxENSw+MCc5KCBDMSlHPDQ9MitIMjNELi87KiU8KyY7MCgxJx8hJiEiJyIoIiIqJCQkIx4k Ix4THxYUIBYcHRcgIRsnKR4jJRotJysnISUpJyYrKSgjJSInKSYrKSwnJSg1LSUwKCAyKiItJR08 LB48LB43LiYzKyMnKBgoKRkrKyEvLyU5JSFDLiorLBwmJxcnIB0gGRYgFhQjGBYkGh8kGh8QFg4Q Fg4aFhIXEw8dFAsdFAsREQgSEgkPEwkSFgsUFAkPDwUMDQcWFg8REQcTEwgGEQMJFQYUFgsWGA4W GQobHg8dIRUiJhkoHxAgFwoYEwseGBAhGxMhGxMcGw0cGw0VGQgVGQgWGQ4SFgobDwoaDwoWEAoW EAoSFAgTFQkWFQoUEgcTEQcWFQoYFgkXFQgdFA0eFQ4hERUfDxMaFhIcFxQVFggPDwMOCgAOCgAJ BgAHBAAMBwAKBgAHBAAIBQAHBAAHBAAEBAADAwAFAwAFAwAGBAAHBQAEBwAEBwABBAACBgAFBQAE BAAHBQAEAwAFAwAGBAAGBwAHCAAKCQQPDggMDwcPEQoXFgkYFwoPEAMSEwQSEAcSEAcTEQkWFQwS EgcUFAkXFRATEAwREQkREQkPDwcODgUNCAIMBwEMCwAPDgFhOyZoQSxlRSVkRCRzRCB9TSh4TiR6 UCZ7VixzTiVlQSJbOBlXNSRePCpbPydeQypuRy9lPyhUOB5PMxpNMRhRNRxTOR9WPCJaQB9WPRxa PyNTOR1BNxxEOR5DOBtFOh1GOiNJPSZVOCRRNCFGNyhHOCk/MyE8MB49KxlDMB5TPChaQy5kSS5l Si9hRiteRClOQS44LBoxKyMxKyMhJxghJxghIxgfIRYfIBgjJBwZJBEbJhMcKBkcKBkcIx0fJiAf JxcfJxcgKRYjLBkvKyoxLSwxMiwyMy0xMScyMigzMykwMCYsLCIzMykjJh8hJB0cHh8lJygrKiMk IxwmJSIkIyAvJhcxKBkxKhI1LhYvIhA0JxUpJRkjHxQmJhwkJBofIh0aHRgqIhckHBItJBckGw8n JRssKiAvLCUuKyQXGxokKCcpJCIpJCIjJRogIhcZJRUWIREYGRQcHRciGxooISAyJR4tIBknJiEj Ih0hHRwjHx4tJCErIh8sKB4kIBYrJCMhGhkkGw8oHxMnIxcjHxQoIA8rIxEiIxElJhQiHxEiHxEP EwgKDQMREAIREAIPCgAUDgMLCQQHBAAHCgAKDQINDAALCgAJCgAHCAAGBgAGBgAEBAAGBgAEBgAE BQAEBAAEBAABBAADBQAFBwAGBwAHCgALDwEKCgMODgUYGhAXGQ8fGAohGgsbGQYfHQkhGAseFgkX FwwcHBAhHg4fHAwZFwwVEwgXFwwVFQocFg8XEgoWGQkXGgoaGg8XFwwXFQcaFwkaGA8aGA8eFg8k HBUfHBkfHBkdGxIXFg0QDwcNCwQKBgAIBAAEBwADBgAHBwAGBwAGBwAHCAAIBgAHBAACAwADAwAC AwAFBwAHCQAEBwACBAAGCQAEBAAEBAAGAwAGAwAJCgAHCAAFBQAICAANDQANDQAHCgEKDgQREwoW GA8gHAobFwcdGAocFwodFgkfGAoZGQcaGgcYGwoaHQwaGQkYFwcXEgkTDgUWEQYSDgMRCwARCwAO CAAPCQBENSJHOSVVPy9QOytaRCtaRCtdQCJbPiBaQSZTOyBONCRHLh4+Kx9DLyM9Lis9Lis9LyY5 KyIrJRwoIhklKRwpLSAvKyIqJh0vLxcwMBgmLBUdIw0iJCUnKSosKB81MSgvLSwuLCs4Mi47NTEz MyYyMiUsJyMtKCQtKR8tKR8xLBw5MyNENB88LRg3MBgyLBUgKhwVHhEWIhQbJxgaIhUeJhgWIBcX IhkWIxkYJRsTHRMXIhcbIxQbIxQcIRgcIRgdJxgWIBIaJBYbJRYiJR4bHhciKh0eJhkXJg8WJA4R FgoPFAcTEAoOCwYHDQgECgUHEAQEDgMKDgEJDQAJCgIHCAAKCgAODwENDAANDAAMBQAUDAEGCQEC BAAGBwAHCQEECAABBQAKCAAKCAALFgQJFAMNFgcMFQcRFQgXGw4aHxYdIhkaHQ4cHw8THQoSHAkg Hw8hIA8TFg8UFhAWFw4aHBIcGxUbGhQWHBYTGRQdFxkiHB4aGhEaGhEaGhAbGxEXGxAVGA4XGgka HQseIBMfIRQeHw4fIA8hIB0VFBEOEAMHCQADAwABAQAEAwAEAwADBAACAwADBQACBAABAgABAwAB AwABAwADBAADBAAEBQADBAADBAAEBQAABAAABAABBAABBAABBAABBAADBQADBgAKCAAKCAAHCgAL DwIPFgUPFgURGxETHRMYGg4aHA8aHQsbHgwTGxMVHRUZGgwaGw0aHA8bHRAWGQ8aHhMiGxgeFxUc HhQaHBITGRAVGxIWGg0XGw4YGRIYGRIdFg4fFw8ZGhUXGBMWFgoREQcKDQEGCAACBgAABAADCQAH DwAEDgAFDwENFwENFwEPEQMUFgcHFwkHFwkGGwEEGQAKHgALIAEGFwEMHwYQGgASHAEQHgkKFwQJ GwEKHQIHHAYHHAYOIggOIggIIAgIIAgLFgcKFAYUGAcWGggcFQ0XEAkWFgQYGAYVFAQSEQMWEwUZ FggRFQMVGAUSEQMQDwIKDQAKDQANDAAODQANCQAOCgAsNCcqMiUxNCsrLiU0MSIxLh8wLRQoJQ0w KREmHwkZGQ4VFQoiGhMeFg8ZFg4VEQoODgUPDwcSFQ0NDwgNDwgSFQ0WFAkQDwQVEgQWFAYVEgwQ DggSFBMVFhYPFg8PFg8VGBcWGRgWHhsaIh8tKR0tKR0qKSIhIBkiJxMgJREqJhovKx8rIxgxKR4r JxsjHxQgJBYdIRMcIBQeIhYcIhYbIRUaHhIbHxMYIhQYIhQWHRAaIhUeIBMfIRQfIhAdIA8UJxYP IREVHg8UHQ4dHxIZGw8mHhYbFA0KDwAHDAAECgABBwABAQAAAQADBAADBAACBAABBAABBgABBgAB AgABAwABAwABAgABBAABBAAAAwAAAwABBAAAAgAAAwABBQABBAABBAABBQAABAACAwAFBwAKDwEL EQIMGAoLFwoWGxUcIRohJBQdIBAZHxQZHxQhHxYkIhgbHhkcHxoiIxskJR0gHxofHhklJSMfHx0c IiIdIyMdHx4kJiUkIx4mJSAmIhklIRgoJBgmIhYoJhkoJhkgKBggKBgaIh0LEg4FCwACBwABBwAD CQAEBQADBAAECgADCQABCgABCgADBwAABAADBwAECgAHDAAFCgAGDwAGDwAKEAAKEAAGDwAGDwAC DQADDwACDAADDgEACQAFDwEJDgMKDwMDDAIJEwcMGAkKFgcTFRYYGhsWGxMUGBAUFgoZGw8SHQ4Q GwwYGg4aHA8aHRYYGxUXGxAYHBEbGxIZGRAXFw8WFg4VFgwWGA4XGQ8WFw0WFQ8aGRMVFxEVFxEW GQ8RFQoWFQUODQAEDQADCwAACgEBDQMFGwgMJA8LNSQNOCYWNyAWNyAUNB4XOSIKOSYKOCUKOSYK OSYNNRUQOhgNNSEQOiUaMhogOSAbPScZOyUPOR8UPyUPNyYMMyMUNCcUNCcPLRMQLxUNHA4NHA4S FQcUFggbFw8UEAgTEgQYFwgTEQcTEQcSFQcQEwUNDwEUFgcUDgEWEAMJDwEKEAMNDAALCgAPDQAR DwEaKyUIFhEOEQcKDQMHCwIECQAICgAHCgAHCQAEBwAEBQAEBQADBwABBQADBQACBAADAwADAwAB BAABAwAEBAAEBAACBAADBQABBQAAAQABAQADAwAAAQAAAwAAAwABBAADBAAEBgAGBwQODwsWGg0W Gg0ZIxUbJRYrKRMkIg0gIhUdHxIbJB0dJh8eIhQhJRYcJhYXIREfIBwgIR0WHxgXIBkhIxccHhMZ IRIXHxAcJhgWIBMgIBYbGxIeHRYgHxgYJB4YJB4ZIhkZIhkbJRcaJBYaGA8SEAcDCgAABQAAAwAA AwACAgACAgACBAACBAAAAgAAAQAABgAABQAAAAAAAQADAwAEBAAEBgAEBgAABAAABAAABwAABgAA BAAAAwAABAAABAACBwADCAABBQAABAACBgAGCgIMCgMXFgwYHBkeIh8cJx4aJRwUIxoaKiEbIhwd JB4hIh4jJCAeIiMhJSYiHxwgHRojJSIhIyAdJSQZISAdHx4iJCMhISMiIiQdIBshJB8ZJhwWIhgc HxggIxwkHxkeGRQPFg0DCAEDCQAABQAABQAEDAEBFAAFGQQIJQwIJQwFIwgFIwgHJQoDIAYGIQsQ LhYPKxULJhAKKBYPLhwNLRANLRAULhAXMhQLMRwJLhkHKBgLLR0RLBsPKhkLKRsRMCIPJhgLIhUW HxEPGAsZFBQcFhYVGA0RFQoRFgsRFgsRGgsOFggQFQ8MEAoQEQoWFxAXGQ8XGQ8VGA4VGA4VFhAU FQ8WEggWEggSFAoTFQsRFQoSFgoUFg8WGBAPFgsLEgcOEAIGCAAABwAABwAADwQLIRQKNCMNOCYL SDUNSjgRRzoWTT8XSjQWSDIZRjwWQTgTSToUSjsPSCsRTC4cPjQkRz0ZQzoWPjUeRzcaQzIaQTgc RDogPjUgPjUhNy8fNC0UMyESMR8NHBUKGRIOFggPFwkYHBAVGA0VGAsPEgYTEQgWFAoSFQcRFAYV FgcYGQocEAMZDgEREgUREgUNCwIJBwAKCwAMDQAMEgoHDAQGCQADBgABAQAAAAAAAAAAAAAAAgAA AwAABAAABAAAAAAAAAAAAwABAwABBAABAwAAAQAAAQAAAgAAAgAAAwAAAgAAAQAAAwAAAgAAAgAA AAABAgABBAADBQABAwABAwABBAAJDQcXFg0fHRQjJiEpLCcgKR4hKh8qMCMjKRwlLSAkLB8hKB4g Jx0cJRwjLCMhKi0eJyoTIRwZKCMlKiUmKyYkKSQbIBsgKCcgKCclJCgjIiYfJyYiKikeKSkgKysl Ki4dIiYdKB8XIhkJDwMECgAABwAABgAABQADCgAEDAEIEAQHGAQDEwADEgMDEwMAFQAGHQIHGgEB EgAGFwEHGAEKGwUKGwUHGQIMHwYIHgMEGQALIg4JHwsEHAMKIwcOIgcOIgcNGQwKFgoHEggEDgQK CgoMDAwQIBoWJiAgJx8gJx8VHxYWIBcWIRYYIxgdIR4dIR4gHh0fHRwmIR8jHhwlJxwhIxgZHA0f IhIlIhskIRofIBweHxseIhYdIRYZIxUXIRMcHxgbHhcVFA4MCwYEDAEABgAABQAABAABFQURKRYQ OSwTPC8PRzQMRDERPiwYRzQRSTEPRy8bTzcTRS0YRzkYRzkKQDQNRDgWSTQURzIPRCsTSC8PPzUO PjQKQDINRDUYRzkTQDITQDITQDISNCgIKBwMGw0KGAoPFA4RFg8SGQ0VHA8THQsOFwcSGQ4PFgoT FxEUGBIYFQodGQ8ZHxMWHBAWHRQSGA8TFBAYGRYWGQ4YHBAUGw8PFgoXFw4aGhAZGxEXGQ8UFw0M DwYJDwEECQAACgQBDAcDHRQOKyEMOi8PPjMPSkQRTUYaTU0aTU0VT0YVT0YXR0UaSkgYTEUaTkca VUQYU0EZPz0iSUciSUcdREEYUEAVTDwYTT4WSjwcST8aRz0YRzAWRS4WPCwWPS0YLykVKyURHhAT IBIYGRIWFg8aGhgXFxYdGBUdGBUVFQsWFg0XGAoZGgwYFAYWEQQPEgEPEgENDgIMDQELCgAKCQAH CwICBgADAwABAQACAQADAgAABAAAAwAAAwAABAAABAAABAAAAwABBgABBgAABAAABAAABAAABQAA BQAABgAABgABBgABBgACBwACBwADCwACCgAABQADCQAECgABBwAABwACCQAECQAHDAEYEQguJhsn KiMnKiMeLiMhMSYjLR8fKRsmKSIjJh8iKCYhJyUcICElKSogKy8dKCwiJyAjKCElKRoqLh8lLSAh KRwkKyMhKCAiJiUhJSQZJR8bJyEjKx0gKBodJh0dJh0YHhIQFgoACAAABwAADAAGFQIHJA4NLBUK MBENNBUUNRcVNxgLLhUPMxkLMSATOykUQC4HMB8SOCgSOCgTPC8QOSwQNSYWPCwTMyYUNCcVPjEP OCsNOh8SQCUXPi4TOSkaMSkVKyMPJRkDFgsKDgoIDAgIEQIZJBEkJxYjJhYjIh8gHxwaIRkYHxcc HRYjJBwkIxQgHxAbGw8fHxMmJBciIBQhIRcjIxknIhUmIRQeHxceHxciJBkgIhcZHRIdIRYcHhMg IhYVFwkOEAMDCgAABwAACgABDwADJhYPNyUPQDoXSkQQVUcPVEYTTjwUTz0NTj4PUUEbUT4ZTzwV UTsWVD0STkQOSD4XSkQaTkcTUzgRUDUWTT8SSDsVT0gSTEUZTkQWSkAWST0SRDgTOzQNMy0WIhoP GxQSGhITGxMSIhMUJBUbIRYXHRIcHhQbHRMaGxYYGRQZHRofIyAbHBYaGxYUHxsVIBwZIhcYIRYU IBYVIRcVIBoSHRcjIBskIRwjIRUhHxMWGw4KDwQJDwMJDwMAEgUFGQsHLCAPOCsQPT4YR0gTVk0U V04eV1gYUFEaUFEYTk8gT0UhUEYZU00WT0kWTUgWTEcWR0EWSEMbSUEbSUEWTkATSTwRTTkRTTkU SUEPRDwTRD0SQzwWPjAVPC4aMScbMigcJhYSGwwSFAoUFgsbFw0WEwkVFAUXFgcXFAsXFAsXFAsa Fg4YGAYVFQMJDwAJDwAPCgQPCgMMDgANDwADBwAAAgAABAADBwAFBwAFBwAADwACEgAHFwAHFwAK FgALGAAGFgAHFgAEEwACDwAEFgAGFwAHHQADFwAEEwIEEwIEFAEGFgMEFwAHGwAIHgIIHgIKGwQK GwQHHQYFGwQFFgICEQAGDQAIDwASDgEhHA0ZHhceIxwXJBwdKiIZJhMUIA4cJBcgKBsiKCQfJSEd IhsgJR4WIR8WIR8gHhQhHxUnJRsiIBYWHxIXIRQhJxsgJhokJCQkJCQcIBUhJRkgKBseJhkWIhYR HRIPEgQKDAAABQABCAADFwcTLBkKPjERRzoMSDAPTDMZSC8cTDIbTjoWSDQNQTcQRjsWU0MRTDwV RjcbTj4UTkQVT0UaT0UaT0UaSkYYSEQXUUUQSDwPSD4UTkQWST0OPzMTPzUQPDIQMSIDIBIHEgYD DgMHCwESFwoeIBUlJxspJCItKCYeHxcdHhYjISIjISIhKCAeJR0fIhsmKSIqKSQlJB8hIhoiIxsl Jh4jJBweJR0cIxskJSEjJCAlKCMlKCMeIxolKiEcKBoKFAgEDAEDCgAAEwIJHwsHMSIXRjUTVUwT VUwSWlEPVU0SU08XWlYQW1EPWlAQTEEUUEYRVEQWWkkeUVEVRkYXT08aU1MYU0QYU0QaTUkYSkcd T08ZSkoWSUQcUEoaTkoTRUEXQDoTOzQWLh4TKhoPHAwSHw8THhgQGxYZHxQdIxccGw0bGgwbHg0c Hw4dIxcbIRYaGhEWFg4RFhERFhESGQ4SGQ4RFxARFxASFw0UGQ8bGBIbGBIcGwsfHg4VGwsKDwIG DwAHDwEDFgoJHhEIMSQPOy0QREYWTE4RW08PV0wZVU4WUEkaUFEbUVMeUUocT0gWT0YWTkUXTD8X TD8eTUMYRjwYRzQbSjghUD0bSTcTR0YVSUgWUEcSTEMWST8VSD4UPzMSPTEZMiIULBwUIRMPHA8W FAoVEwoaEgUZEQQTEwgUFAkZFQ8WEg0SEAgWFQwaFAgWDwQMDwIOEAMRDgYNCgMPCgMPCgIABQAE CwAFHQMLJQkTMBUTMBUSNQ4PMgsbNxQZNBIWMxkWNBoSMxgZPCAUOSAPMhoSOBcTORgXPiASOBoM NyUNOCYMPSAKOx4RNxYTORgMOhkOPBsPOCENNR8QLyEJJhgNIhEIHAwHFgMEEgAJCwAKDQEODwgX GBEXFxYZGRcaJhcYJBYcIhYdIxchIyAhIyAfJiAcIx0ZIBYfJhwiIBYjIRYpJRwoJBsgKhwdJxkj JiEjJiEmJCMpJyYmJR4kIxwnJiMkIyAZIyAWHxwSGw8HDwQBCgALFgQHLR0ZRDISVUMQU0AOTkER U0YVV0oVV0oRV0QPVEAPVUEPVUEXVEETTjwPRj0UTEMRTEwUT08gWFEdVU4VV04QUUgXVEYUT0EW T0YWT0YPTkwKSEYPR0QKPzwQNycKLh8FGAgDFQUHCwQPFAwcJiUhKyolJiInKCQqKSIqKSIlKCMl KCMfKiEbJh0fJB0iJyAhJSIiJiMhJB8iJSAlKB8jJh0eJCAeJCAbJSAaJB8lJyYoKikeJyAZIhsS IA0DDgACBwAECQIDGQwQKxwYPTwiSEcPU08RVVEQV1YQV1YTU1gWVlwPU0wUWFEVV04XW1EaV1QW U08cUEkeU0waUU0YT0oXUUMbVkcbT0wXSkcZSEwXRkkXSUQdUEoUSUUMPzsXPz8XPz8TMygLKh8R HA8NFwoUGw0SGQsWGg0YHA8VFA4XFhAbFw0ZFgsRFwcWHQslHhEiGw8XGxAVGA4VFw8WGREUFhIV FxMdGQ8eGg8bFw0aFgwUDwcWEAkQFQYKDwEDDgMEDwQDFgoOJBYMPigWTDQTVE4UVU8SVlETV1Ma WFEVUUoTUUcTUUcXUVAXUVAbTkgfU00tTkcwUUpHW11NYWNTZ1NVaVVTaGNNYl03WlovUVEXTkYP RDwTRD0TRD0OOzUPPDcUNCkMKyATHxQZJhoZFQ8WEg0eFg8jGxMZGQ8VFQscGRUbGBQdGhYYFhEZ FQcdGAomGQcfEwMSEAEPDgAOBwASCwMBEgAHGgMNLREXOhwXPy8aQzIXRi8aSTIUUDwPSjcTRTkX Sj4PRCsMPycPRzAQSTIRSDcXUD4USj8OQzgVQToVQToWRC0YRzAaSDUZRzQPRTUOQzMKPzcNQzoP OzMKMywWLCQLIBgFFgMCEQAFCAAKDQIUDQQmHhQnJycpKSknKx8mKh4oJR4mIxwmKSQkJyImJyMn KCQhJSIlKSYoKSwmJyonKyonKyopMCgmLSUyKyoxKiklKSgmKikoKCgjIyMmLi0lLSweJiUYIB8K GAgDEAIAEgMIHgwKNywVRToXVEYcWkweVk8iW1QRVVEWW1cWXUkRV0QXXlQTWE4VXlMOVUkTU0QW VkcVVlAXWlQZV04XVUwaT0UaT0UVU0UTUEMWUUoUTkcPSE4STFEQRkYNQUEWQCsONyIPIQ8IGQgH DgUMFAobHx4iJiUhKiMhKiMhIxglJxwgJR4cIRoeJR0bIhodIRUgJBciJR4gIxweHxsgIR0iJBkh IxgiIBYmJBojJRohIxgVIRcbKB4WIBAQGQoSEwQHBwACCAEFDAQEHRQSLiQWQEUeSk8cV1caVVUW WlcYXFoXWlYVVlMWVVAcXFcWWFMWV1EPUEULSj8XUDwZUz4gVkUcUUAmUDkrVj4dUEoYSkUkSk8h R0wcSEUhTkocTUcWRT8WQz4YRUAXODAPLicPIQ8HGAcTHwsUIAwXHREWGw8gHhUgHhUcHRYYGRIV GwsZIA8aGhEeHhUeJRsXHhUgHxghIBkeHBIdGxEjHxYkIBYZHxMWHBAaGxQYGRIXGQ0ODwQDEgMH FgYHJBYPLR8MQzQYU0QSVFERU1AeUVEfU1MiVlAeUUwZU00bVU8ZWFQVU04oU088aWVaeYJujpd6 mad7mqiCmaaCmaZ1lqNvkJ1XhIZJdXcyaVggVEQeTUUcSkMPRz0NRTsXOy4PMCQXJx4YKB8YGxYW GRUYGxUZHBYZHREWGg8dFxAeGBEcFRYcFRYfEQooGRIjGwcaEwELDwALDwATCwIVDQMBGwAPLwwP QSQZTi8ZTkEeVEccTkMcTkMYU0YXUUURTUYVUUoTUz8TUz8LTDoOTz0QTEUWU0wRTUYPSUMWTUgS R0MTSToTSToVU0MRTj4OSD4LRTsRQEMSQUQQQDsKOTMSMicPLiMLFgwDDAMEDAAGDgEUFREeHxsl JisnKC0kLCcjKyYsKSYsKSYtLigqKyUgLCYjLykmLzAjLC0nJzEqKjQpLDEqLTIoLCkqLislLSoj KygjKiIgJx8nKSYmKCUqLi8fIyQVJBsOHBQGEgEDDgABHQwMLBkKRDITUD4TWEoWXE4VVEwWVk4W WFYYW1gdWk8cWE4SXVcPWlQXXVoRVVEVWEgTVkYTVk0WW1EQUUUWWEwYU0YWUEQSVEoRU0kVUE8N RkUPR0oTTVAPP0MPP0MaQzcOMygPIBADEgQIDQQKDwUWFhMeHxsZIyAVHhseIRggIxofJB8hJiEf IhkgIxocIhYcIhYZIhsaIxwkIyAfHhsmJBkkIhcmIhgmIhgdJSAeJiEVJxYQIhIXHgwQFgYKDAQH CQEECgQJDwgJJRsYOC0PTEcWVE8eV1gdVlcdXVgYV1MTVkoSVUkcVlAbVU8UVFUSUVMkUEMsWkxR aFdacF9Vd3Jae3dYfnJWe29Ocm9Hamg1aF8rXFQhUVMZSEkYT0oWTEcXRDwbSEATOzALMScJHw4E GQkPGQwYJBYbGxIeHhUgJBYbHxEeIRwaHRgZHBQdIBcWGRwaHSAeJRsdJBofIxceIhYgIBQdHREe HxsgIR0cIRgXHBQdHxQXGQ8PFQgGCgABEAAEFQEHIBgPKyMLQDsUTEYQT1USUVcZVU4bV1AaW1AZ Wk8cV1YdWFckVEwkVEw/X2hXeYJylayCpr6Zq8WXqsOSobiMmrKGm7CEmq+DjqJ4g5ZXdGo9WE8d UEoYSkUSSjoPRzcbPS8WOCoeKBobJRcaHBITFQsTFQkWFwsYFwoUEwYRDQEWEQQVEg4PDQkXDAUg FAwWEwIPDAAKCgEJCQANDQQLCwMGKhIROR8RRDoaT0UYU0QhXU4lXFEiWE4cVlAaVE4WWE8TVUwT VFEXWlcRVkgQVUcTVUwUVk0VUUoQTEUUTkUYVEoVUUoQTEUSU08PTkoPUUUNTkEMRzoPSj0RRzoR RzodODIXMSwPHxkGFA8EEgUBDQEIFAcSHxEmJyMjJCAgKCUgKCUmJyEmJyEmJSAoJyIoKh8lJxwr LSoqLCkiIiAkJCIjJikeISQeIh8jJyQeKyMfLCQrJhYsJxYjJxsaHhMbKBwTHxQTHQoLFQMDCwAC CgAFHhMWMiYNTEgYW1cRXEkPWkcsVkksVkk0T0Y7Vk03VlE0VE8jUU0pWFQcWFEWUUoaWE8gX1Ye Y1obX1YPXFEQXlQQUU4RU08TVkoSVUkXWlQWV1EmWFMoW1UVUE0QSkcbSkwWRUYVMR0EHQsHDwQF DQMSFhUXGxomKCkhIyQbJyEeKiQlLyojLSgjJh0kJx4gKSIiKyQiKiUhKSQlKSgkKCcoKSUrLCgo LiwlKyklKi4nLDAiMC8cKikmKTAiJSwNFhMHDwwEFAYDEgQGLiMWRDgSVVUVWFgeWlodWFgbXlwa XVsZXlMWWk4VV04WWlAkZF0oaWJXf3J0npCRoq6Wp7OXqsGVp76HqsGCpbt7oqx0mqVjjYhHb2oo Wl0YR0oPUE0PUE0XSkcXSkcPNDENMi8OIRgNIBcWIRYWIRYXFwwcHBAZIQsWHgkZGhYaGxcYGhcX GRYZGRkfHx8iJR4aHRYTGg8WHRElIRchHRQcGhEaGA8fHhcaGRMWHAoSGAcKEgMECwABDgAGFAQG IxoPLyYOR0YVUE8WW10XXF4XUFYYUVcaVloaVloXVFQZVlYvWk80X1Vba4B5i6GEm7yJocKQlaGH jJd+fXd+fXd1iYd1iYd+kKFvgJFed3hEW1wZUUYWTUEXTD8SRTkZQTEPNSYWJA8SIAsSGw8SGw8V GA0SFgoWFgkWFwoWFwoYGQsVFAcWFQcZEwcWEAUbFQEeFwMQDwEPDwEWDwcTDAQJOiQRRS4VT0YY VEoWVk4WVk4eTk8gUFEoUFEtVlcWWk0SVEcRTkwWVVMXVU4UUEkTWE4TWE4ZU0oWTkYUSj8XT0QS TEMQSUAQSUMTTUYWTkgRSEMOTUcRUUwYT0gSR0AYPjkTODINISQFFxoHEAQCCgAFCgMNEwofHx8f Hx8iJCUiJCUjJh8jJh8lKR0nKx8rJx4sKB8oKh8oKh8pKiYoKSUnKiUmKSQlLCQkKyMnKxwmKhsq JxYuKxkiLSQdKB8aKykXKCYXIhsPGBIJEAcHDgUGIhUWNygPT0wWWFUZY1saZFw6ZVRGc2FYeVtd fl9lgmtdeWNVcmhKZ10xXE8uWExTa2FjfXJhhoBOcm0aaGETXlcRV1sRV1sXVUwWU0kjWFUwaGRD Z3RMcH4mZ18WU0wWTUEJPDEPLSMCGxIKFBAHEQ4RGRwdJiktKyooJiUhKyogKikqMDAhJycoKicn KSYfJSMhJyUhJh8kKSIjKRwjKRwkLB8lLSAgLSMeKyElJiklJikdKCEWIRofIiUfIiUMExAFCwkA EQQEGAoIMSoYRj4SVVcYXV8cYmIXXFwUXmEVX2IVXVgVXVgWXFUaYVoyaFpRi3uGn7iWsMqmr9Sf qM2OpreLorOAnraDobh/nbR+m7Nvl6FReIAyaFoiVUcSV0oPU0YYTUAXTD8TQT8LODUPIREOHw8T HA8VHhAcGxYbGhYbIA8aHw8dHxQdHxQaHBIeIBYaGxcaGxcWHxIYIhUWHhITGg8fIRYfIRYaHhAW Gg0fHREeHBAPGBIPGBINFQQKEgIDDwMIFgkEJyEPNS8NSVYRT1wSW18YY2gbXlwZXFoeYWcZW2EU W14UW14iXFAyb2Nid5B4jaeGn6GHoaKSkIJ6eGpya1h3cF1vgnt+kYt7mZZui4hbd3JEXlobVlMS SkcWST0WST0cQzkVOjAZLB4PIBMUGQ8WHBEYGg4XGQ0bHRAZGw8fHg0dHAsaGwsjJBMlIhIiHw8j GwocFQUREQAREQAWEgURDQEJPi4TTDsXV00ZWk8WXFEWXFEjVFckVVg0TFM3TlUvVVAvVVAaVk0a Vk0eVEkhV00eVk8ZUEkcUEQZTUAgTkYiUEgcSkAeTUMUTUAWUEQxVEYuUEMXWkEZXEQYTUMXTEEW RDwRPTUMKicDHhsJEgkHDwcHCQAODwQWICAfKiosLzcwMzssMzQpMDEpLy0rMS8rKyswMDAqLisr LywsKzEwLzU3MjMxLS4zMi80MzAyLzQuKzAvLy0vLy0mMTEoMzMYMTMaMzUfKiMOFxEMDwQPEgYH KBYYPSoUUEcgX1Y4ZFVMemp0mZ6IrrORssmLq8KOp8GIobp/mqZrhpFUd3hcf4B+iZ6Un7SQorh5 i6FDeockV2MUW1wQVlcgW1UgW1U1X2FRfn9Veo1Veo05dIAhWGQSTkUKRDsSMSoDHhcHDwwJEQ8M Dw8ZHRwvLCUsKSImKSQhJB8mIygjICUnISUnISUiIiAiIiAmJR4pKCEmKSIkJyAYIh0bJSAZIhca IxgfIxYfIxYfKBYdJhQZIA4XHgwKEgYFDQIAEQQEGAoGLzMYR0wSWFoXX2EXY2MYZGQWZGEXZ2MW X2QTXGEdXVgiY15KbX5zl6qNp8GUrseao7aJkqVuiI1ngIZrg5l0jKJ7lKeAmaxzmaVYfYg3aGMs XFcTWEwRVkkWUUoSTEUTQTwNOjQPIgsQJA0RIBYPHhQfIRYkJhseHxseHxseIRofIhslIyQnJSYg ICIfHyEXHRkeJCAWIhwWIRsZIx4aJB8eJxwdJhseJRQdJBMcHRkaGxcPHg8KGQsGEgwGEgwEKyIN Ny0NSlARUFYPV1cWYWEfXF8dWl0YYWIXX2EaZF4ZY10dXlQscGVdeYt3lKaUqKibsLCmn7qZkqyQ laWXnaySo8eSo8eJoa5wh5RbdG5BWlQfV1MTSEQVTUQUTEMbPDQWNy8gLSEWIhYaFhQeGRcZGRAW Fg4eGw4fHA8bGgwdHA4ZGw8aHA8gHQ8eGw4aGgQWFgESDwIRDwETEAMRDwEPRz4WUUgcV0ofW04V WlUVWlUjXFUjXFU0XlRGcmducH13eYZvkZJrjY5hh4tcgoZRdHlJa3BHcGJGb2Fcb29ecnJYanRf cntefY1oh5dzh6Z1iahYhIZEbm8oX1AfVUYUSUEPRDwULykMJiAFDwYBCgEKCwEYGg4fJB8lKiUt KywsKispLS4oLC0nLC4oLS8qLi0qLi0mLisnLywkKCkmKissLi8qLC0sNS4jLCUlKSYrLywiLCsi LCskLCkiKicZKCUbKicaHBIREwoFDwQKFQkGKCMZQDsiVUcwZVdqh5uHpbqnt9muvuClvtiWr8mZ q8KXqsGHn7Z6kqhjl510qq+Vs8mfvtSMrNF5mbxajJs/b34xYl0uXlopVl8tW2RMeYBejZVWjJVR h5BFcnUhSU0hSEcdREMTNSkIKBwECwYIDwoKDgcTFw8mJBksKh8pKiQlJiAgHh8sKisuKCwsJioj JCkjJCkoJiknJSgmKCUlJyQlLSojKygkKCUoLCkqKycpKiYjLCUgKSIaJxsTHxQKEwcHDwQBEAMN Hw8GMTQWRkkNXl8TZ2gUZV0WaF8Vb2oUbmkWZ3AUY20kYW4oZXNQe5RvnbaLpbuLpbt9kpVid3lF aGNEZ2JAanNQe4RwkZ56m6h1m6NjiJA9a3UuW2QPVVQPVVQWTlQWTlQRQz8PPzwVLRcQKBMUIxoU IxoaIRkfJh4lIyIjISAhIyAgIh8fHiIiISUmJiYkJCQhJB8cHxoeIhYfIxccIB8ZHRwgIRseHxkj JCAbHBgkHRwkHRwQFQ0HCgQDDwoHFQ8HIR8aOTcQTE4YVlgVXloWYVwgX2IfXmEbYWEZXl4bX1wf ZGEwYVw6a2dneJCJm7Smssaqtsqmsseeqr+Qpr6Rp7+Inrh/la9/joxte3lKZWMxSkgZT04ZT04W T0kRSEMZOjIZOjIYJhcPHA8WFgwZGQ8VGA4SFgsUEw0UEw0WFgQcHAkUGAcQFQQWFQYWFQYSFgIQ FAEVDwESDQARDQMSDgMMTTsUV0UUXU4UXU4cYV4cYV4pZ1ozc2VUfXhtl5J+n6uIqraRrLyLprZ4 nqptkp5fjpJdjJBkiYJrkYl9lqWEnqx4l6t4l6uHmqyEl6p3mrJ0l69Yg4xEbXUnZ1gXVEYNRTwM RDsPMCgKKSELGgwCDwMFDQIMFQghIh4kJSEqLSYpLCUpKygoKicoKCgoKCggLCYhLSchKh8mLyQk KyMjKiIpLCcsLyoqKyMoKSEpKCEpKCEhKCIjKiQpKSciIiAZJRcYJBYYGQsPDwMHFgcIGAkNNTIW QD0vV1VOeXeDm7qWr86vttmqsNSQqrh9lqWMnrCSpbeXm7OSlq59na6Nrr+bts2euM+Ip8NzkaxH eX46am8qX2IkWFs6XFdBZF9qfY2AlKV+kp1whI5eeXdNZ2Q6amUsW1YaRD0HLCYDDQQDDQQHDQgW HBYlJyYrLSwqLCkoKicgJygkKywnKCsqKy4nLDAjKCwoLC0oLC0qLi0rLy4sLCorKyknKygnKygg MCcgMCcqKiooKCgWKCIPHxkJFgsEEAYCDwQNHRAKODsXSEwOYV0Wa2gVb2gUbmcUbmsTbWoWaGUW aGUwXmM4Z2tTeo51n7SLpbOIorBpjoxQdHI4aGMtXFclXmM6d3tfi5dwnaplnahRh5I5a3QoWGEP VVYPVFUQUEoOTUcRQTsMOzQPKh0IIRUPHA8UIRMgIhUiJBYeHRgfHhkfGxIdGRAbGxIcHBMWHxUW HhQWIBMYIhUfHxMdHREdHBYcGxUbHBUaGxQdHxUZGxEaGxcVFhINEAYICwIACgQHEw0BHxoOMSwV R0EoXlgkYlsjYVokY2IjYmEbYWMaX2ImZG0kYmopYm4xa3hUeJJyl7OSp8eUqMmao6qRmqF6iZl1 hJR1g4hzgIZye4llb31AZWs0WF4WUU4WUU4jTE8dRUgjOS8eMyoaIBUWHBEYGA8aGhEWGg8WGQ8V Fw8WGBAZGAkcGwsXGw4WGg0YGg8WFw0VFwcVFwcSDwQRDwMREAMTEgQRVUMXXUohX1EjYlQlZGEk Y18ral85em9bi4twoqKHprOLqreOpbqHnbJ7lJ14kJl5jZR+kpl7m5+IqKybqsWerMeNnbCAkKOE lZ17jJRqkZVrkpZUfoNGb3QwX1sjUEwVTUUPRT0HKiUHKiUIFw8EEgoEDwUGEAcNGhYUIh0YKh4c LiImKyYkKSQsKikrKSgjLSgiLCcgKhsjLR4mKyQmKyQrKR4tKyAnKx4oLB8kKSInLCUoLSYmKyQe NSsXLiQbLiAWKRsaJxIOGQcEGQsEGQsYOTEjRT0dYVtDjYeCo7yQssuiq76XobNzi5Fof4Z9kKGO orOQoaySo6+Im6yVqLqGrLiHrrp4naxUd4YkaWUfY188W19BYWVRbWpriIaEo66VtL+JrrqDp7N+ o6VwlZZfjZlFcHspRlcLJDMDDQQDDQQIFhMQIBwlKi4qLzMoMi8nMS4mLCwoLi4oKikoKikfLTAa KCsnKiUqLSgvLCUyLygnLiYnLiYqLygmKyQlLiMlLiMjKCMkKSQdKB0UHhQNGQsDDgIADwwHFxUI OzsRR0cPXl4WZ2cSb20RbmsSbWsVcG8UamEXb2UrbWUtb2hNfotwpbKNqLCJpaxhlpVAc3ItXGIr Wl8hXW0zc4Nni516n7JynrBVf5FFanMwVFwTVlYRVFQTWkgKTj0QPzQLOS4VJxkKGw8OGg0WIxUa IxoXIBcdJh0eJx4fHBYaFxEbHRMZGxEgHBMeGhEfGxAgHBEfHREgHhIdIBcaHRUeHRgdHBcbJRcZ IxYbHBgXGBUSHRAJEwcBFAcEGAoGISQSMDMUSkodVlYWY10XZF4XaGEZamMlXGQjWmIkX2UkX2Ux XHdEcIxbd5l3lLeSosaUo8eGoZd0joZ/h4R/h4SChoyHi5F5jaxugqFUf4A+aGkcV1QZVFAWREgR PkMUOTEQNC0SJRUPIRESGBEUGhMSFwoXHQ8eHgoeHgoZGxAdHxQhHxUfHRMeHBIdGxEYGhAWFw4b FAQXEAIZEgIYEQESRkgbUVQgWFEnYVofYmUlaW00ZWI/cm5XfXBljH9zkZ91lKJ9lKF1jJlvjpFy kZR4lJGIpaKXq7afs76dq8aVo76Al5Jwh4J0gHp3g31zg45+jppwkZpYeIAsaW8WT1USTUkPSUYP MzIGJyYHFBYACgwCCQcFDQoJHRkMIR0bLSkgMi4lLiMoMSYwNDMsMC8nMS4pMzAvMS4qLCkrMS8s MjAqMScpMCYkLygoMywsMC8oLCsmMi4nMy8iODAgNS4qMjsiKjIWJSAQHhkEFwgIHAwQLywhQz8h X1xIjYmCm6+XssaZq7yImqtngJBeeIdulJ+GrLiMo7OJobCCoauGpa97qrJ1o6twkJRVc3ciYWEl ZGQ7Y3JQeolwlbaLsNKZt9mWtNaUsNGat9iLqtV6mcNilrI7a4YiTVYILTUHDwYDCgIJFRcHEhUX IyEfKykdLyUVJhwdISAlKSgiKCYjKSciKSMiKSMhJh8mKyQoLRsrMB4hJxoiKBsnKR0kJhooKyYm KSQfKCEeJyAWGw4TGAsFEQAEDwAAEQwGGhUEOCoUTT4RX2cYaXATaXkRZ3cRaW4Wb3QXcGsWb2oo amktcG9Ue5VznbeEp7iEp7h5lZVbdXVJX2Q+VFhGWHNdcIxymad/p7aIorhuh51KdHs0XGMRVVAT V1MVVlQNTEkWQDwPOTQVJRUQIBAPHhYWJh0aKycTIx8bJSIcJiMhJh8hJh8gIxoeIRgcHSAjJCcm JSAhIBshIh4gIR0hJBsjJh0mJiYgICAfJyIcJB8TIRoVIxwYIBQSGQ4GFggEFQcCGyoSMEASTVYY VV4TY14YamUWaWkYa2sdY2McYmIkamEla2I5YXJAaXpheJV7lLKOobqZq8WWsMGWsMGZrrCZrrCX qMeSo8J9m7Rti6NGe2cwY08YWE4WVkwVSUwQREYTPjQMNSwPIhIMHg8TGAsSFwoVFwcYGwoeHgod HQoTFgoSFgkWFA8WEw8WFAoVEwoWFwcPDwENDgEODwEOEAEMDwAUTkQcWE4gYlcgYlcjam8ka3Ax aVo0bV1JdW9XhH5pi6F0lqx3laN1lKJ4kZ14kZ19maGOq7OhqryossWdr8GOobJ+kI53iId3kpaH o6eVp8GarMZ+naphfosraWgaVVQRTkkMR0MNPTEKOS0KHw8CFAUBDQYDDwcPGBgaJSUqMDooLjgj LSwkLi0qMC4mLConLCcsMSwsLi0qLCsqLCsqLCsmLScnLigpMS4lLSoqLyokKSQiLSQlMCciLycg LSUfLCQdKiIVIBAQGwwGFgYKGwoHLicaRz8mYmpKjJWHl7aWp8aNq8F7ma5nh5JbeoZolZ2CsLiO rriNrLeHq7iIrLp+qrd0n6xzjZlYcn0kYmopaHBAdJBTiKVwlbN4nbuEmrKQpr6frsmdq8Z6nqtX eYY7dG0oXlcdRTsFJx4FDAQHDwYJGBMPIBoYJR0kMSkhKygeKCUnJiMoJyQjKCEmKyQkKSAmKyIm KyQlKiMiKSMpMCoqLSYrLicrLSIsLiMqMjElLSwrLC8oKSwkKSQcIRwQGwwKFAYBFggGHg8GNCsW SkAVXWkcZ3MWa3IWa3ITa24UbW8hcnobanMrZ3I3dH9bf5p5n7uGq8GGq8GIoaeAmZ9/jod3hn51 jpCDnZ6Qss6StNF/prJnjJdBdG8wYVwVWlUWXFcYVVESTUkYQTkQOC8XIxYRHA8aHhIgJBcbJyMa JiIdJSAeJiEgJSAgJSAgJhodIxcgJBghJRkfIBghIhokIiMgHh8hHx4jISAhHhciHxgkJhseIBYd IRYaHhMSFw0NEggGEQMIFAUCGR0OKS0NREAZVFATY2UbbnAaamcba2gaZ2kZZWgoZG0saXI5Y248 Z3JMeIhnlaZ6mrCCoriEpbuHp76GpreDo7R4p7NunahihotOcHUtYmshVF0TV1QSVlMXTkkRRkEU NS0OLiYWIBIUHQ8ZGgoXGAkaGQkaGQkdGwkbGQcWFwoWFwoWFQ8TEgwfGQUhGwcTGAQPFQINDgIP EAQRFAQPEQMUTlQeW2EZZ18aaGEmaXIjZW4hZWMnbWpHd4ZVhpVni51wladwjptzkZ5/kpqGmaGN mayZpbinrsGss8aarr+RpbaSl7KQla+Np9SWsN2OrsqIp8NzlqNegI00amonW1sUVkoLSj8OPjAI NykJFwoBDQECDAEEDwQcIhYjKRwmKikpLSwnKSgnKSguLyspKiYqKyMsLSUrLCgrLCguKikvKyop LCcrLikoKyIkJx4mLiAlLR8oLB0rLyAkKSAiJx4cJxwbJhsYGwoXGgkIFwQWJxAMNy0bST8uX2hT iJGImrCXqsGQpr6EmrJuhoxfd314kZ+Rq7qOqriMp7aEp7iEp7h/pbp9ordolJ1Nd38tZXAxanU6 d3s/fYJNcnJUeXmGjZuQl6aSnaiJlJ9vhn5VamMrYlwfVE4cPzcIJx8DCQEKEQgPHBcaKSQtLzAw MjMwMDAxMTEwLCsvKyoqKigsLCoqLisrLywwMTkvMDgtLDAwLzMvLicyMSowMS0wMS0pLC8pLC80 LzQuKS4jLCEdJhsWHgkPFgMHFwQHFwQKLiwfSEYZXWkiaHQgbXQib3ccb3cbbnUpa34qbX8tbn0z dYRRepRynbeIqsaMrsqSs8qXuM+jtM6is82lu+aov+qbuuGQrtVvn6ZVg4k1bWknXFgbWFgbWFgb VVsUTFEWPS8PMyYcHhEaHA8eIBMiJBYiJBkgIhchIRcjIxkeHxkcHRccHRYgIRkeHBsfHRwgGxYi HRcmHxojHBchIRckJBodHBYdHBYkHhYhGxMcHRYVFg8TGAsRFgoHFAIHFAIDFxUMJCERPDgkU04Z YmoaY2shb28ea2seaG4faW8sZ2swa3A1Y3IwXWsua345eItPgodXi5BnjJZhhpBhg45df4tOgoBE d3VFcmI6ZVYjXGMaUVgQUE0UVVEaTUkSQz8aLisQIyAZGw8aHA8fIA8eHw8eGxUdGhQdGQ8ZFgsf IhIgIxMdHhYaGxQlHhslHhscHRcXGBMTDwMUDwMXEgMXEgMRUUwZXFYlZV4qa2QuY2owZW03aHo3 aHpDbYRQe5Rii6JpkqpvkJ1zlKGJlqGRnqidpbOmrrynt8uhsMWNp7eLpbSRobSIl6uHnbKEmq+I oaqGnqddjplJeYMra2sdW1scVk4TSkMmPjoUKiYRFAQLDgAHCwEMEQUWGw8aIBQpKR8rKyEuKi0t KSwwLCsxLSwzMCk0MSorMiooLycrKSguLCspKyorLSwsMjArMS8nLSErMSUvMCgwMSkxMTEsLCwl NC8gLyomLCAfJRkQIA8NHAsMKichQz8uWF9biZGNoa+ZrLufpb+eo76Ump2SmZurprq6tMmltMeV pbeMq7qJqLeHo76Gorxfl6xAdYkwaXIrY2spZ2cqaGg8ZWNHcm9wgpeClKp3lqxykadVg4c8aGsd VlYXT08VMCoAFhAHCgQHCwQTFxMkKSQzLy40MC8zLy4vKyosLisqLCksLyYsLyYpKSkrKyssKy8u LTEtLiorLCgoLSgqLyonJiMpKCUyLSsvKigtKSoqJicgKyAbJhsTHAcNFgIHEQQHEQQLJSMlQ0Ac VV8rZ3IZcHgacnkecHobbXckcIImc4QibYMibYNDeIxelqt0orZ6qLyCrsODr8WIr8aEq8KMq8eE o793mqZukZ1WhH5BbmgqXmIpXWEgYV8aWlgWVFMPTEoeNyoWLiIUGhUSGBMaHhsdIR4mIxUhHhAg HhQiIBYfHhkgHxolISAkIB8fIyQeIiMgHxojIh0mIRsmIRslIyQmJCUqJSMlIB4mICAmICAYIRoX IBkYISISGhsKGAoIFggBFQoLIhYYOzcgRD8hW1wjXV4dZ2oha28iaWsjam0iaWsmbnArYm8pX20o Y24rZ3I8aG9AbXRJaWtHZ2lKZGtNZ25EYm0+XGctXGIrWl8hVF8bTVgZUFUWTVEdREMQNDMdJyYW IB8VFQwbGxIcHBMcHBMcHBMbGxIdHAsiIQ8fIxYgJBckIBckIBcgHBQcGBAiGQsfFgkUDwMXEwUf FAIfFAIaTU0jV1cvZGQxZ2clamcpb2s1aHU5a3lNcoJTeIhhg51qjadpjpl0mqWGnqeOp7CassOh uMqvuM+ossmUp7aNoa99mZ5zjpR4i4d6jYmDjpWLlp1blZVNhoYmdGgWX1QjVlYbTU0bPzIMLSEI FQQBCwAFCgAQFgcaGxQtLiYnMy8oNDAvODcrMzIvMDMyMzcyMjAyMjApNS8nMy0sMC8rLy4uLi4x MTEtMjctMjctMi0sMSwvNC8vNC8rNTIpMzAqMi8oMC0uLycjJBwQIQoNHQcFLCUQOzMtU2FYgpGI m6yXq7yXr86dtNSrvNSzxdy0xeS2xuamt86On7Z+obKHqruCn8F3lLRMi545dYgqcHcjaG4YamoY amojY2ovcnlWfpdpkqxhg51WeJExeH4kaG4WW1YOUEwVKhsADwQHCwEKDwQRGxUbJh8oKicpKygo KicnKSYrKyspKSkkLB8kLB8jKiIkKyMpKCMrKiUpKiQpKiQqLCksLisqKycpKiYqKiwqKiwnKywm KismLB0hJxgQHxYKFw8EGgkCFgYDIxoUOS8RT1ogYm0Sc3kTdHoZc3oZc3ofdIYfdIYdan8fbYIv b4Y/gplagplii6JijpdijpdnjaFiiJtijJlchpJJfXtEd3UxaGssYmUWXWMVXGIWX1oUXVcXUEoS SUQhLCUXIhsWHRoWHRobJyUaJiQiKSEgJx8kJx4lKB8jKiQdJB4hJxsfJRkjJSIiJCEiJCMiJCMl JB8nJiEfIiUeISQmJyMlJiIlJSUkJCQgKicbJSIhJCcWGRwOGAsIEgYBEQUGGAsEKCITOzQUTlEh XmIhaGohaGofbW8icHMkb3MibXAdaXAYY2oWZWocbnMhbnUbZ24pYmQtZ2kpYWkkW2MeZGkWW18b U1cgWF0TVlsTVlsaU1QVTE0hPjgSLScPFg8QFg8WFwsXGQ0XGBMWFhEZFwwfHREeGgceGgcVGg0X HQ8bGw8XFwwdFAsdFAsbFgQbFgQVEwMWFQQaFgEZFQEWUVUcWFwcZGoeZ20Zc24eeXQ8aWdEcm9c e3hlhoJwjpt3laJ0l6N6nqqQn6+Xp7eoutaoutasvtWer8aRpbOLnqx+kpB5jYuAjYyNmpmRm7SX oruJnqZ1iZE7fn0kY2IWWlwOT1EPOS0FLCEEDAEBCAAHDQAOFAUYIB8oMC8qMi8qMi8sNzUoMjEq MDArMTEvMS4vMS4uOTgoMjEoLi4rMTEsLi8uMDEsKy8qKS0qLi0pLSwpKyoqLCsvMisrLicpMS4i KicqKyMeHxcPGwQNGQMEHhgTMCopT11IcoB6kquRqsOSrtSattyntuCyweunuuqZq9ttnrNfkKVt lKV9pbZwlLBcfpoqeo4fbYAcanUYZXAiaWska243ZHM8anlDeI5Jf5ZMcntBZ3AWbWkVamccW10L RUcMHwkBEQAIDQIQFgkeIRwmKSQoKisqLC0qLisqLismLCgmLCgoLSgpLiktLiorLCgwLi0uLCst LiouLystLzAwMjMsLi0rLSwrLDEtLjMnLjErMjUrMCcoLSQVLCQLIRkHGwoGGQkEGhAQKh8MPEgf VGEWc3oWc3oWc3gZeH0deH8deH8YeogXeYchb4Mkc4dBcntHeII9dXg+d3lGeoM/c3s9bnA/cHMn aWgoamklXW4jW2sbXWocXmsYZGISXFobTUESQTcXKSMNHRcWFxQaGxccIiAZHx0cIh4eJCAhIyAk JiMfJhweJRscJBUbIxQgIhciJBkhIh4kJSEeHxsgIR0PIhkNIBccIhYdIxciJhogJBgbHRMgIhco IhkdFw8RFgsJDgQBEAMEFAYDHhcSMSoNR04YVl0TYWIYaGkhanMia3Qka3Ajam8bbnAZa24fa3Uh bngZaGsYZ2oeaGkbZGUgXWEgXWEcZ14VXVUXW1gXW1gQVlwSWF4VUE0PSUYWNysNLCEQFwwUGw8d HBccGxYYGA8aGhEaHA8bHRAaHggZHQcaGhAZGQ8eGg8hHRIiGA8jGRAZGwgVFgQQEQMVFgYZGQQW FgEZTUclW1UcZWQdZ2UgZXIpcH1KdXRTfn1hjY5rmZp0mqV1m6Z6m6h9nquOpreSqruZr9SasNWO rseHpr+Em6iDmqd9l516lZqHobSUrsKMptaIotJoma5RgJUpb34aXWsaVV8QSFMOOCwGLSIFDQID CgAHCQAUFgojIigpKC4nLSkpLyssMjAnLSsnLSkoLioqListMS4uMi8qLisyLCw0Li4rLzArLzAt MTIpLS4oMC8mLi0lLjEjLC8mLCgqMCwgMCcbKyIjLicdKCEOJQgLIgYHIhUVMiQcSlcyZHJUe5dt lrOCmraIobyJob+HnryAm6t1kJ9bhotUfoNKgpFWjp5Kf4s+cn0tdIglan4fcnkWZ24cb28bbm4e aHMjbnkscnoscnova20uamsXaWcTY2EeTk8PPD0EFQEBDwAUFgsbHRIfJyQqMi8rLSwsLi0fLisi MS4lLi8oMTIvMDUuLzQqMTQpMDMsKzMqKTEsMDEsMDEuMDEvMTIuLi4vLy8mLTAqMTQsMTgoLTMh LzIhLzIQKiEMJRwKGQcHFgQEFg0IGxELMzUaRkgUX28baXkZc30Zc30WeX0Ye38ZfokWeYQbeYcY dYMmcHcmcHchd3chd3cgcnAidHMbb20dcm8Wa3IUaG4WXW8WXG4RXm0TYW8WXmIQVloVQzIJMyQP GxAKFQoUGhEXHhUWHhcYIRolIyImJCMjJRklJxsmKB0fIRYeJhYhKRkcJhgfKRskJx4iJRwdHhge HxkRIBQXJxoiKBsgJhkmJhklJRghIA8kIxEhKBUXHgwaIBQUGQ4KGAoHFQcFFxQMIBwLMzkbR00P W2EWY2kaZHAcZ3MkaHAkaHAWbXQWbXQabXQXaXAWaG8XaXAiZ2ogZGgfYm0aXGcYYmEWXl0YXWIW W18bV10XU1gdRkQUOzkXLR4PIxURFhEaHxoiIBYgHhUfIBgfIBgYHxYcIxkYHRUYHRUcGhkeHBsj Ih8lJCElHhskHRogHgscGggXGgMWGQMWFgMVFQIZRjwmVUojZVopbWEyaW0/eHtXhJBYhpFjjp5n kqJtlZ5tlZ5pkZpqkpt5mrZ6m7eAn75+nbttmaZolKFti5Vti5Vvkq9zlrN5ncWApc2Anbp3kq9Q foI9aW0daGMUXFcbV10QSU8RMCICHA8FDQMDCgAPEQcdHxQoKCgrKysqMCwtMy8uMi8wNDE0NS8y My0xNSk0OSw1Mi00MSw3Li86MTIuMjExNTQwNTotMjcuLjkzMz4uOD0oMTcuMTQxNDgeNDQZLy8e LSglNC8WMBYQKhAMKRUQLhkWQEYpVlw+cINJfZBQgJJYiZtiiZBfh41Ygo5Od4NPdIRMcIA4eIg3 d4czbn8xa30hbXkhbXkebnIXZWkYcHAac3MganMibXUdcHMWZ2kWY2kYZ20WZ2sPXmMgQToIJR4D DQECDAEPFAwZHhYlKx8pLyMrLicuMSofMCodLigpLy0oLiwtLy4qLCspLSonKygmLCgqMCwvLy0v Ly0nLSEoLiIpLCUtMCknLSsoLiwtMi0pLikpLikkKSQZLB4VJxkOGgQPGwUDEgcCEAUGKCMWPTgT WmIganMYb3kbc30Xen4afoIbfn4afX0deX4adXoedH0ccnoWbnoZc38gbXcgbXcYbnIYbnISbW4R a20UY2oUY2oUZ3ITZXAgV2MVSVUXOy4GJRkQGw4PGQwdIRMhJRYnKiMkJyAnJycmJiYmKSAnKiEp KiYlJiIiJyIkKSQZKSAaKiElKCMiJSAlIRgpJRweKBocJhgcIRoeIxwrJRorJRojIhElJBMhKiMZ IhsbIRUZHxMTHBcMFRAEFxAJHhYHKB8QNCsPUFAXXFwdZW4eZ28nZ3MnZ3MWbXkWbXkYb2sYb2sa bW0WaGgbZWcaZGUWZGgSXmIVW2UUWmQYW2EaXWMkV1oZSk0iOzIWLSUPGAoMFQcWFhQcHBoeHBse HBseHRgbGhYbHRMcHhQfHRwdGxobGhcbGhccHhEbHRAhGxMeGBAbGAoZFggVFgIWFwMbEwYaEgUS RDUiV0goYl0zb2opb3M3f4NMgpRKgJJQe4RWgotXgpRUfpBJgI1RiZZXiJ9XiJ9fh5Fcg41OhpBE eoRTeX9TeX9Wf45dh5ZeiZZch5RafX5Qc3QxaGgtY2MYZGQQWloYUUkPRT0SJRUBDwMDCwAGDwAP GhUbJyEmLS4tNDUwMTQxMjUwMzkuMTcyNzgwNDU3NTI5ODQ0NDQzMzMwMyo0OC4wOTUtNTIqNTgo MzUtLjMwMTcyNDUyNDUzNDo0NTskMzkhMDUlLicqMywfLBYWIg0NHw8PIhISKyonQ0EkYmoqaXI1 eHg+goJPf4lNfYc7fo0vcH87d4g6dYcqdX4qdX4qbnklaHMYbnQXbXMbbXIXaG0Zam8ecHUibXMi bXMfcG0WZGEdaGIXYVsWZGgMVloWLSUADwkBBwAECgEKEwsWIRgkKh4pLyMtLigpKiQlMCkiLSYs LCoqKigpLCUqLSYnLyolLSgiLCkkLissLygrLicsLyYsLyYpLCMvMiktMTIoLC0wLi8tKywqKyMp KiIhMSQZKRwTHRMWIRYJGQoEFAUCGBYRLCoKR0gXWFoRZGgacHQccnofdX4Yd34ZeH8bd3kbd3kZ dHcXcnQccn0ccn0ia3seZ3cWa28Wam4TZ2oTZ2oWZGoYZ20caG8YY2okTVMTOT4TJBoHFg0MGxYR IRseIRgiJRwoKisoKislKyckKiYnKiUmKSQrJyYqJiUqJyIpJiEiKicgKCUiKyIgKSAkJhojJRkk JyIgIx4gJBggJBglJhQlJhQlJh4iIxslJiIfIBweIhYbHxMQGAgMFAQEEA8EEA8CFQoGGg8JOjwR RUcWWGkcX3AdZHIeZXMYbXoZbnsXcGsWb2oXbW4TZ2gWZGoZaG4WZWgTYmQVWmkVWmkZV14bWmEj UE0VPzwWKg8JGwMPEAUSFAgWFg0XFw8aHQ4fIhIfHg0dHAsbGQ8gHhQjHBseFxYcHBIbGxEdGwkg HgseGwwbGAoZGgoVFgYWFgMZGQUiFA8eEAsXQTQiTkAhXV0raWkkdXsqfYMxe4A1gIYsenste308 eIk7d4g5eYc6eogmgoImgoI+fX44dXcycn0wb3pFbndGb3hFcHJGcnNFcnk+anIxbXItaG0fa2cf a2cWZ2QTY2EdTkkMOTQKFg8ACAMECwAJEQQUIBUdKh4kMjMnNTcvMCwzNDAxNDguMTQvNTUtMzM0 My4zMi0sMSotMisrMCsvNC8oOjEiMyshMykjNSssMSwtMi0nMSwjLSgwMDAvLy8sMy0tNC4oOjEe LychJiESFhIKGAgMGwoUHhcpNC0bVFQmYWEvbns0dII6go04f4sggIsdfYceg4wZfYYZd3obeX0h cngbanAbcHkWanMVdG8VdG8Za2sZa2sabWgcb2ohcGobaWMob3cdYmkcVF0PQ0wLHQ4ACQADCgAH DwQNHxwXKygoKzAqLTIrMiwnLigsLyotMCssLissLiswLSgvLCcsMy0sMy0pMTArMzIrMS0tMy8u LycvMCgsLCo0NDIsLzIsLzI0MDEvKywpLSwsMC8nMjInMjIjLicaJR4RGxEKEwoFFA8FFA8DLyYT RTsPXV8WZ2keaXgjb34heYkedYYZdXsbeH4VeHkTdXcYcHMXb3Idbnoba3gXaW4XaW4XaGoZam0e Z28aYmolXGcXTFYYMyoNJh0KFgoKFgkMHBUTJBwVJxYZLBodKCEeKSIkKBsfIxYeJg8eJg8pJBYn IhUoJRUqJxYqKB0pJxwiKSEgJx8hJh8hJh8jHBkkHRofIRYiJBklJRojIxgjIxgoKB0nIxchHRIb Iw0eJg8XHgwRFwcKEgcEDAIECgQJEAkCHA8NKx0PQUQZT1ESWF4WXWMXaW4Za3AWangXa3kba3oY aHcWZGgWZGgWZGgWZGggX2QfXmMWSlYQRE8dOioNJxgQGQQMFQEWFw0ZGxAfGxMeGhIiJRMhJBIg HxAfHg8aGhEhIRcoIhkmIBckIhYkIhYiHRAiHRAbHRIXGQ8XHQgWGwcXFgcYFwgcFg0aFQsZMSko QTkoVVsuXGIkcnsod4Awen0xe34reHsqd3oveH8tdX0pdX4nc3sneYImeIAleXsjd3kjeHggdHQ7 ZXU+aXkobnQobnQtbXUoZ28pZG8oY24cZ2EcZ2EbYWMVWFsdQDwMLCgJEQUCCQACDAEJFQcXIRQg KhwfLSwiMC8tLiowMS0xMiwyMy0xMzIvMTAsMSotMissMiUsMiUtLyQvMSYyMSoxMCktMikvNCsy NycxNSYrNC0rNC00MjM0MjMmNyslNSomOC8eLyckLisbJSIdHRsaGhgTIhkgMCcXPjkmT0knX2sx a3glfYgmfokleIQoe4gzfYctdX8een0ZdHcgcHQebnIccn0ZbnkTdG0Sc2sXbW4Ybm8cbmcdb2gf cG8fcG8ncn0XXmkUOzkFKCYEEAEBDAAHEQUUHxEYKSMeLykoLCssMC8pMikpMiksLSkvMCwwMDAx MTEvMzArLywvMTAxMzIwMDAxMTExMTExMTEwMC4vLy0pKSszMzUwLTItKi8uLysuLystLS0qKiop LDEoKzAeLSgZKCMPGBIKEw0IEAMJEQQCGQ8PKh4LPkcVSlQVX3MbaHsZc30Wb3kXc34beIMWeXoX e30Xd3cXd3cpbncjZ28fZG4jaXMiaG8fZGsrZ3QgWmcjSkwUOToOHQ8KGQsLFhAQGxYWIxkcKR8c KBceKhkkKBwlKR0iKBwdIxcjJBUlJhYrJRwpIxopJhUsKRcnJB0nJB0kJR0lJh4mJR4mJR4lIxgk IhcfJBsjKB8mJhskJBkqJxgqJxgkKh4eJBgoJhIqKBQkJA8gIAwSHQ8MFgoLEAcHDAMDEQQHFggE JRwKLSQKPDUUSUMPVFMTWlgWX2sZY28WYm8WYm8TY2UTY2UdX2UaXGIpU1YcREcVMiwLJyEMHAcK GgYPFgcRFwgWGg8bHxQhIxceIBUoJhknJRgiJBkbHRMcHBAhIRUpIRYpIRYrIxgpIRYoIhklHxYg IhYcHhMZHAoZHAoaFgoaFgoWGAwUFgoSJyMfNTElTE0tVVYgY2ctc3cvfYAreHsoeHgoeHgifnsi fnsidXggc3Usa30vb4ApdXknc3codHgpdXkdbXUdbXUacHcacHciaG8hZ24dZ20bZGocYmQeZGcu VWEbP0oaLisKGxgCCgEABgAGDgMRGg4gJBgnKx8mMSonMisyMjIyMjItNC4sMy06OTM3NTAuNDIt MzExNy8yODA0NS04OTA6OTU4NzMzOzIuNS05Oy47PTA5ODQ6OTU5OTs6Ojw1OTMzNzEuOjIlMCkm ODUeLy0iJiUgJCMXLCMWKiELMScTOzAkUVctXGItbnovcH0mc4IqeIckdXsneX8id34ecnkWdXUX d3cbdHkZcncUdW4UdW4Xb3QWbnMjbXMlb3UXcncZdHkiX2gPSFAMHQsACwABBwAIDwMUGQ4cIhYk KCksMDEnKygpLSopLiUqLyYoKyYuMSwwLi0tKyosMSwpLiksKygyMS40NDIwMC4qLyopLikxLSwx LSwvMTAtLy4nLS0nLS01Mi8zMC0vMCgqKyMnKyomKikaHh0aHh0WIRQPGQ0KEwAKEgADDwkEEQoF Iy0PLzoNRVcVT2IQXWkWZXITcnkScHgUc3oYeYAYeHgaenoub3kub3kmcHslb3oca3IWZGohU2MU Q1MbLx0PIhEPFQoPFQoVGBccIB8jKiQlLCYnKx8mKh4pKR8rKyElKSgjJyYhKSQlLSgrLCglJiIi LB4lLyEoKSMjJB4mKSIqLSYpJyYnJSQnKx8iJhonKCQpKiYlKx4kKh0nKR4oKh8nLCcjKCMmJhko KBssKh0qKBslJxwYGhAMFgwHEAcHCwIECQABCgMBCgMCEw8JHBgGKB8NMSgLP0APRUYNRUgPR0oO SEoLRUcUPUYPNz8jLykWIhwMGQUHFAEGCwAKEAMNEwwWHBUUIRMSHxEWJhQWJhQgIxMkJxYcHRcZ GhUfHREcGg8kHBUlHRYqHx0nHBoiGhIlHRUcHw0YGwohGgUiGwYaFgoWEgcWFgQaGgcYKxkeMR8d PjsnSUYaW2UiZG8hdHshdHsodXkmc3cogHglfXQkeXclenghdHshdHsjb3AlcnMkb24mcnAeb24h c3Idb3IfcnQbcnUWa28dbXMca3IfaG4bY2kbSVQKND4MFxAABwEABAADCQAUHBYmLygvMzA0OTU1 ODQ4Ojc4ODg4ODg3OTg0NzU7Ozs6Ojo0OD0yNTsxOTwwODszNDg4OTw4OTw0NTk4ODU0NDIzODkw NDU1OTwzNzo4Nz84Nz85OTs6Ojw1MDU5MzkyMDwqKDMvKSkpIyMUHhQSHBIGGhMIHRYMNDccSEof Wl8raG4ibnolcn4vd4Yvd4Yie4YZcHoceHgbd3cYeHgYeHgYd3MZeHQhbnghbngnaXkjZHQfa3MV XmUUOTEFJh8BBQACBgAHCwESFwoiHhMrJxsnKSYrLSomLCwnLS0qLSYsLygsLyYsLyYtLiovMCwn LyEpMSMxMiwvMCorMiosMysrMiotNCwxLzAvLS4uLi4uLi4xLzIxLzIzMCkxLicuMSotMCkuLysu LyspKiYpKiYmLyQeJxwLGQ4KFwwOFRAGDAgDDg0IFBMFIxsKKiIKQEQVT1MQW2URXGcaYXceZXsa a3AcbnMWa3sUaHgVY3IUYnAbVFQVTEwXOTgJJyYMGgQKGAMLEQoSGBEbHhcjJh8nKSgpKyonKSol JygoKCopKSsmKicnKygdKyocKikmKCUgIh8kJR8mJyEpJxosKh0qKSQqKSQoKCYnJyUjJSYiJCUk JicmKCklJCEjIh8iIx0jJB4eIxwfJB0jJCAiIx8hISEgICAjHRMZFAodGxEZFw4RFgQLDwAHCwAG CgACCgABCQADCgAGDgMCDwgBDQcDFgwGGxADGgsCGAoFEw4CDwoKCwAPDwIKCwAICQAMDgQPEQcW GxAWHBEdHREbGw8eIQ8cHw0cGwsaGQoZHRIYHBEdHRMgIBYnGw8rHxIgHhUeHBMkHBQjGxMeGwse GwsiHAofGQgiFgohFgkfGQUeGAQiMCAlMyMhOTIlPTcXT1ogWmQjZ3ImanUed3MgeXUjdXckd3gd eIIdeIIdc3sab3gdbm0gcnAgbm4hb28fc3MgdHQhbnUfa3MYbnQYbnQcanUaaHMrZW8cVF0ZPDgG JCAECQAABAAECQAPFAkeISgoKzIwMTcyMzkwMTczNDo0NzUxMzI0NDQyMjIyMjIzMzMyOC4yOC4x NTcxNTc0MjU3NDg6OTMyMSwyMjIyMjIsLzItMDMyODItMi0vNy4sMysvLy8wMDAtLjMtLjMtLS8r Ky0nJiMkIyAgHRYeGxUWGhIYHRUJJCIUMS8VP0EeSk0aW2UhY24hb4QgboMhdH4idX8keYAjeH8j eH8id34YdXkbeX0fcnsdb3kkbX0WXGscTlAKNzkHEwcFEAQCBgAIDQQPGxAYJRkoKyYmKSQoLCks MC0rMTEtMzMxMiwxMiwtMi0vNC8wMi8yNDErMiwrMiwwMy4tMCsuLiwxMS8wMC4xMS8xLzIyMDMw MjMwMjMxMS8wMC4zMTAwLi0sNDMrMzItLjEyMzcuMSwwMy4oKy4mKSwTJyARJR4QGBIFDAcEDgEE DgEDFAAFFwEEGw8LJRgKNDkMODwOOkwVQ1USTVYSTVYPTVoPTVoRQUUNPD8SLx8KJRYLGgoDEAII EQIKFAQVFhYdHx4cIxseJR0hJSQhJSQkJhslJxwpKCEqKSIoKB4nJx0jJyQkKCUoKB0jIxgoJSAp JiEmIhknIxonIhUqJRcmJyEhIhwgIx4hJB8nJiElJB8mJBkkIhclIxglIxgmJBknJRoqJR8rJiAf Kh8WIRYZIQsbIw0jIQ4ZFwYJFwkHFgcPCQUNBwQGCAAHCgAGBwAGBwACBwACBwAGCgAHCwAECAAE CAAHCwAHCwALDgELDgEMDQoTFBAUFw0YHBEfHhceHRYgHhQiIBYnJREjIQ4kIxEjIhAdHxQfIRYj IhMiIRIiHBInIRYnIRYoIhcgIxEgIxEgIhYdHxQfHA8fHA8iGw8hGg4fFwYfFwYiKCQmLCghMiwf MCoPPzkYSkQiVVwsYWgfcHIidHUpdXcpdXcjdYIidIAecHocbngacnAfeHccdXgZcnQjcHoib3ke cnIdcHAWcHMXcnQgaXcdZXMtVFsYPEMSIxYACgECAgAEBAAKDgsXGxgiJiUrLy4uLi4yMjIpNDQp NDQuMTkvMjo0MDM1MTQsMC0tMS4tMi0xNzEuMjExNTQzMjc0Mzg4NzMxMC0sNS4rNC0wMC4yMjAz OS8uMyozOTEtMistMTIsMDEkNDIjMzEwMS0yMy8hMSQiMiUqKCkgHh8eHhwbGxkHGhAMIBYOKR8T LyUMPkAWTE4YYWseaHMiaoAiaoAbb4IccIMidIMgcoAWc4AYdYMZbnsUZ3QdVVoNQEUTKh4CFQoE BgAEBQAECgcTGhcTISccKzEoLTEoLTEsLzQwMzkwMzczNzotNTItNTIsNDErMzAoNSsnNComMi4n My8qNTUpNDQwLjEwLjEyLi8zLzAwMy4vMi0qMi0qMi0qMCwvNTEuMC0rLSooNDImMjAnLywqMi8n LywlLSorLCgrLCgbJh8dKCEWGw8NEgcIFAAFEAADCQABBwADCAAFCwACEwcEFgkDFg8FGBEGIxoH JRwJIxcKJRkMHgoHFwQIFQMEDwALEgAKEAALEgcSGQ4cHBwhISEkJiMmKCUlKSYkKCUfJRkjKR0t KR8uKiAsKSIqJyAjKCMhJiEkJBomJhwsKSIrKCEjIxgnJxwrJRoqJBkoJR4nJB0mJiYmJiYoJSAs KSQpKR4mJhsnKCAmJx8nJiMqKSYtKx4rKRwdMCIZLB4gKhkhKxolJhUjJBMWHxwRGhcYFBwWERkR EwkUFgsPFQoJDgQGCgMGCgMKCgEKCgEKCgAODwMREwcREwcPFQoPFQoWHBEeJBgcIhYfJRkkJhsl JxwjIBsmIx4nIxkmIhgqJSMqJSMeHh4gICAjJRkiJBglHRUpIRgpHhopHhoiIxQiIxQdHREdHREf GQgfGQgcGQocGQoYFgQYFgQbIhgcIxkSJBYLHA8HMhcPPSEWRkQbTEkdX2UhZGocdHMfeHcsc4Iq cH8idH4idH4fcHofcHoadXgbd3kcbnAdb3IZc24bdXAecncbbnMcXmcPTVUbMCkGFxEABwAABAAC BgAGCgAQHBUZJh4nKywrLzAvODQvODQqOjQrOzUzODcxNTQ9Nzk9NzkzNTIyNDEwNDMxNTQ0NzUz NTQzNDw0NT03OD04OT4uOjwoMzUtODIqNC8yODIyODI3OTozNTc1NzwzNDowNTwyOD44Nz04Nz0o NDInMzEmLS4sMzQeLSgYJyIKFhIGEg4DEggDEggDHhEMKhwMOjERQDgSSU0XUFQQV1gSWlsRWGQN U14PTVoMSFUOOUMHLzkLHA0EFAYCCQAABAADBQAICwIMExAdJSIgJywkKzAnLSsrMS8rMjMqMTIo LCsrLy4kNyohMycrMCkpLicnMSwnMSwlMS0mMi4rMS8rMS8rMS8pLy0vLicsKyQlMSsmMiwjMish MCkrLikrLiktMikqLyYqMyoiKyIhLSkhLSkgLi8gLi8oLSgsMSwfLCQeKyMjJRoaHBIZGAkcGwsM FgoEDQMKEgIHDgADCgADCgABCwAACgABCwABCwAHCwAHCgAHDwAKEgIQGQQNFgITGAsWGw4WHxwb JSIjJBwnKCAlKiUmKyYtKSgrJyYiKCQkKiYvKyorJyYpKiYrLCghKyghKygkKCcmKikoJyAoJyAn KiMoKyQpJyYqKCctKicvLCkpKiIoKSEnJyUmJiQmJx8mJx8mKSApLCMmKyYjKCMpKCMoJyIjMCgf LCQhLCUeKSIlJiInKCQiJSgZHB8XHBcWGhYMGAMQHQcQFwsKEAUODwYQEggODwQODwQKDgQTFgwT FgwRFQoRFwcXHgwiIRAiIRAeIhYcIBUeIBMeIBMgGxcnIh4lIRglIRgmGxcoHRkeHhUgIBYeHBAd Gw8fFxAkHBUjGxEoIBYkIBYhHRQcFg8YEwsfGQgcFgYZFQcXEwYWEQQZFQcnKhklKBcUHxEUHxES LhoaOCMPOi4QPDAPTk8VVVYPaW0Ub3Mdbn0gcoAscIYtcocidIcjdYgce4MbeoIbc3ofeH8nc3ki bXMgaXIVW2MUODUCIB4ECgAABAAABgAEDQAHGAQOIQohMTEnODgyOjsvNzgzPDcxOjQwPDQwPDQ3 PDc0OjQ7OTw4NTkwPD4vOz0zOTsxNzk3OjQzNzEtMjcwNTo7OkM4Nz8yOUEwNz8xOTovNzgzODkx NTcyNTkyNTkwODsuNTknLzgrMzw3OD8yMzsmMC8mMC8oKCosLC4UKRoUKRoMFwcLFgYKEwcIEAUE FQMDEgEKGgoPIA8NKBwNKBwIMSQIMSQLMC4JLSsHJBkDHhQEDwMBCgACCgABCAAECAAGCgAKEAYT Gg8bHRwnKSglKiwoLS8lMSsmMiwrNTIoMi8uLiwuLiwqMCwoLionMSwlLyotLS0wMDAwMjEwMjEv MTAvMTArNTQoMjEzNTIxMzAsMC0uMi8sNC8oMCsrMS0pLyszNDAzNDAwNDUsMDEoNDAnMy8zNTQy NDMwMS0zNDAtLiorLCgrMCciJx4jIRcpJx0YIxwSHBYPIBMKGw8GFAYGFAYKEgcHDwQJDQALDwEQ EAcQEAcUGQUYHgkbHBUcHRYhJSQeIiEbJyMiLioqLSgoKyYhKygiLCkpKDAjIiooKikoKikkKCcj JyYnKSYrLSooKiklJyYlJyQkJiMqJhwsKB4qKh0pKRwmJSInJiMrJiQtKCYmJx8mJx8qJCYpIyUo IR4pIh8rJx4tKSAqKB0oJhslIRgiHhYkJhggIhUjJRomKB0nIxokIBcWIRoUHhcSIw0UJQ8WHQgS GQUUGQ8SFw0RFwcRFwcZFAwZFAwWFwsYGg4WFxQcHRkbIA4dIg8kIxQjIhMdIxUaIBIaHRUeIRgg IBQlJRgmIRsjHhgjHhokHxslIRclIRcnIBMlHhEiGhImHhYpIhMlHg8jIhEhIA8dHRMaGhAiHBIf GQ8eFAUcEgQfFAcfFAcrODMjLysuMC0rLSohLSshLSsRMSgRMSgLOjoQQEAMSVEPTlYWVGUgYXMh YnojZH0gb30hcH4ccn8Xa3kUc30VdH4gYm0TUVwXPEMGJiwGFAYACQAAAwAAAwAABwAIEwMWIhgk MScqLzEvNDczNzwvMjgsNzMrNTIxOy8xOy8vNC0zOTE0OTotMTItMzMuNDQuOTgsNzUqOzEnOC4y MSwzMi0wOTguNzUuNDQvNTUxMzIyNDMzMTA3NDMyMjAyMjAwMC4zMzEqMTInLi8xMDQuLTEvLy8u Li4qKSYrKickLh8gKhsdIw0ZHwoVGg8SFwwSFQcSFQcHFgMCDwADEQAFFAEEEgUDDwMBDwIADQEC CQABCAAFBwAHCQABCwACDQAKEAEWHAoSIx0YKiQmKisrLzAoMi8nMS4pNS8sOTIyMjIzMzM5MDM1 LTAtMDMxNDgxMTMwMDIwMTcvMDUwMjMvMTI0NDQzMzM3NzcwMDAwNDUvMzQxMzQyNDUvNTMrMS80 MjM0MjMzMzMwMDAsLzcvMjotNTQqMjEyMy8xMi4uLysvMCwsMSwsMSwnNColMigoMSoiKyQYJiUb KSgSJSYPIiMIFg4IFg4WFg8UFQ4HEQQLFgcPGhMNGBEPHA8UIRMVHBsWHh0kKCcgJCMiJCEnKSYp JyYpJyYnKiUnKiUoJy0kIykjKSUkKiYhJBslKB8oJSIrKCUsJyUsJyUrJiAqJR8rJhgrJhgjJxoi JhkfJRkeJBgsKB4tKR8nJRsmJBopIxopIxosKB4qJhwqKB0qKB0kKBsmKh0qJxgoJRYvKBowKRsp KRwpKRwmKBwlJxsmJhsmJhsXIxUeKhskJRQgIRAjGxQlHRYZHAoeIQ8jHRUgGhIhIA8oJxYiIB8l IyIoJSAkIRwrJBYsJRckJRQmJxYpJRwrJx4jIxgiIhcjJRokJhslJiIjJCAnJCEkIR4oJBsnIxop IRYqIhcqIxQoIRIkIxIhIA8hIxcfIRYlHxUjHRMjGAkhFgchFgofFAgzN0cyNUYpMkQlLj8XKygT JiMQJhwJHRQFIBYHIhgIJhsRMSYOPDMURDsXR0MbTEcaTkgaTkgZSVAQPkURQUULOj0PJiAHGxYI DQMBBAABBAABAwAABAAECQAGFgcOHw8kLSYwOjI3OC81Ny4yNDMxMzIyNDUxMzQzMzEyMjAuMyox Ny03NzQxMS8tMzMtMzMqNzIoNDAtOTEsODAtNC4vNzAvNC8xNzExNC8xNC81NDE3NTI6OTU8Ozg3 OTUzNTIxOTAyOjE1OTM0ODI7Ozs6OjomOzgjODQsOTQnMy8jNS8bLSciKyAmLyQjLSogKicXIRwY Ih0PHwoMHAcPFwMPFwMIEwMIEwMJEAEJEAENDwMNDwMHEgQPHAwTHhoWIR0iLS8nMjQeMjMkOToq MTItNDUwMi8zNTIwNzQxODUzMDU4NDo6NDo1MDUrNDgvOTwzMzMzMzMxMDcyMTgxMzQsLi8zMjc0 Mzg1MTA0MC8zNDAzNDA0NzMzNTIoMCsoMCsxMS8yMjAvMissLygtLjMsLTIqLi0rLy4uMSouMSov LCUxLicwMSkuLycmNCQmNCQrMCcrMCceKCUgKiciIiQlJScbJxkYJBYhJxgfJRYPIREOIBAZIhkc JRwjJxonKx4nKSYmKCUgKyQhLCUoKh4nKR0qKiAqKiArLiUsLyYrLCYuLykrLCYpKiQlJiAnKCIt KCYtKCYoKSUoKSUrKiMtLCUyLCEtJxwlKRwmKh0mKSAoKyIqKh8qKh8pKx0pKx0xLCYxLCYtLCcq KSQtKR0uKh4oKxooKxokKSIkKSIoJiUnJSQiJyAlKiMoKSMnKCImJR4nJh8hJh0kKSAlJh4nKCAh Ih4hIh4lKCMhJB8hIyAeIB0kJBcpKRwoJSIoJSIiIRwhIBsqJxYrKBcjKhgdJBMkIRMoJRYyIx0y Ix0iIxskJR0nIBsuJyIpJh8kIRonIh4mIR0qHhMsIBUrIhYqIRUjIxYeHhIlIhIoJRUuHQ0qGQog FQgjFwojFhEhFQ83OTg0NzUiNTsgMzkXJyUTIiAUHhQPGQ8JGRAHFg4MGAoMGAoHFQoNGw8OJRIP JhMQKQ8SKxERJA0MHggEEwkADQQEBwEAAwABAgAAAQACBQAGCgAGEQUQHQ8bJyMkMCwwNDU3Ozw9 Ojc8OTU4ODg0NDQ3NTI6OTU3Nzc3Nzc3OTU4OjczODcvMzI4ODg5OTk3NzQ3NzQwODEyOjMtODQq NDExMTE3Nzc3ODs5Oj0xNDw0OD88O0E8O0E4P0MvNzo4OTw4OTw7PD8zNDg5Oj09PkE1N0g0NUco MjopMzsiOjEiOjEnMjIrNzcyOjsrMjMkLishKygWKSYPIR4ZHx0ZHx0WHxwPGBYWGxAXHRIZFw8c GhESHRkVIBwWKykZLy0hLzAkMjMkMzkkMzkoNDAmMi4yMy8zNDAxMzIzNTQxMDQxMDQuMTQpLC8s MC8xNTQ0NS80NS81MTA3MjEzMzUyMjQtMTAvMzIzMTI0MjMyMC8xLy4yMzcxMjUvMTAxMzIxMTE1 NTUxMzIvMTAsMTMtMjQsMC8pLSwuMSovMisuLSowLywsMi4nLSkiLigjLyksNDEqMi8sMSgrMCcq KiwsLC4mLSMnLiQhNCMbLh0fKycfKyckKSQmKyYtLSAwMCMuMC0sLisnLS0rMTEoMSonMCktLSsu LiwqLC0qLC0tLCcvLikyLjEyLjEpKi0pKi0tLS0rKysnKSonKSomKikrLy4tLigrLCYnLCUnLCUn KygnKygmKCUnKSYtKyouLCsqKiorKyswKiwyLC4sLCIrKyElKRwmKh0pKiItLiYpJygmJCUlJyQk JiMmJyMjJCArJiIsJyMmJyEnKCIjKCEiJyAjJSIeIB0kJR0hIhoeIxocIRgoJhwoJhwmIR8mIR8j IhMmJRYmJRQnJhUgJBcfIxYfIBEeHxAlIBMlIBMnIxknIxknIBsmHxoqIRwlHBcnIRYkHhQrIA8r IA8oHhUqIBYhIRUfHxMlHQwoIA8rGAsnFQgbFQkaFAgfEwsgFAw7OjQ0My4oNS0oNS0iMikdLSQj KiIlLCQKGhUGFQ8VHA8SGQ0MEgIMEgIKFAAHEQALEgAJDwAGBwAEBgAEBAAEBAACBwACBwAJDQAK DgAOFggcJhYfLikkMy40NzU5Ozo6Oz46Oz47PD83ODs3OTg3OTg8PDo7Ozk3OTo3OTo6Nzw7OD03 OUU3OUU+O0A6Nzw3Oj8zNzwvMzQyNzgvNDctMjQxMTE1NTU1Ojs1OjsuNTcyOjs1OjsxNTc0OD0y NTs5NTs4NDoyNT0tMDg0NDcyMjQ3MTc7NTsyMTo1ND0qNTUoMzMrLjUtMDgwMTcyMzkgMCchMSgY KBsUIxYfJRkeJBgfIBwXGBUcIRweIx4fIBwiIx8sLSkmJyMhMiohMiooMi8sNzMmNTAmNTAsMy0s My0zMTA0MjEsMDErLzApMDEqMTItMTIrLzAvLy0xMS8wMS0yMy8yMjQxMTMxMzQ0NzgwMzcuMTQv MzIwNDMyNDMzNTQzNTQyNDM0MjM3NDU3Nzk1NTgxMjU1NzopOTErOzM0OToyNzg1ODcwMjExMjoy MzsqMTcqMTctMzEsMjAtNDorMjgxMTEpKSkqMywsNS4nLSknLSknMy8mMi4hLzIhLzIlKC0pLDEq Li0oLCsxLzAyMDEqLSgrLikpMCooLykuLC8wLjEsKS4qJywrKSowLi80LC8zKy4oKSMoKSMtLios LSktJyksJigqKigtLSsmKyYjKCMnKCAoKSEmKiclKSYiKCYkKiguJSQvJiUqKSIqKSItJiEtJiEm KB0mKB0hKRsgKBoqKBsrKRwuKCorJSckJBokJBopKBYiIRAqJRUpJBQjHg8lIBEjIhMjIhMeJRQd JBMkHxIlIBMjJxghJRYnJBYnJBYpIxgoIhcnIhUoIxYoIxYoIxYoJhsmJBkgIRkiIxsiIRAiIRAn IxcnIxclHxckHhYmHhYlHRUoIhkoIhkrIxgrIxgpIxojHRUnJB8jIBsjGxEoIBYfGRIeGBEdFgoZ EwcZEAgcEwpBPjlBPjk6PDk+QD0xOzwsNTckNDIqOzkWMy8TMCwiMysZKiIeIRoeIRoWHxwUHRoK EAMJDwELEgUQFwoLEwIPFwURHA8RHA8dISAjJyYsLyo0ODI5ODw3NTo8Oj0/PUA1PDoyOTcwPTsu Ozk0NzM4Ojc3NTo5ODwyOjszOzw4Ojs1ODkvNTUwNzc1PT40PD0tOjUoNDA0MTc1MjgxMzIyNDMx NTcvMzQxMzAyNDEzOjUwNzIqOjIpOTEyMy8zNDAyMjIwMDAtLy4uMC8vMjUpLC8sMjAwNzQzNy8x NC0xMygzNSo5MjQ6MzUzNDAxMi4nNSYpOCgyMysuLycsKSYuKygoKyYmKSQgMSseLykgKysnMjI1 NDkuLTE1NTgzMzU0Ojw1Oz0rNDgrNDg5NDU6NTc3MjM4MzQ0NTsyMzksNTksNTkzNDgyMzcyMzkz NDoyMTo3NT4wMzkxNDozNTIzNTI0NDc0NDctOjgoNDIvNTU1PDwuNzUwOTgtMTAwNDM3NzcxMTEv NTUvNTUuNzEpMSwzOjU0OzczMzExMS8uMTQtMDMtMy8uNDAyMysxMiovMCovMCowMSsrLCYrMCsp LikpKSksLCwtLCkpKCUoLyUoLyUoLSYnLCUbMCkdMistKSotKSonKiEqLSQmLCgmLCgsJyMsJyMv KyIvKyIrKBctKhkqKRksKxslKiElKiEvLCcvLCcuKygrKCUoJyIlJB8lJiIqKycrKCMrKCMnKR0o Kh4qLSQnKiEqJBwrJR0yKB4vJRspKCMsKyYoKCYoKCYlLSAhKRwlJRopKR4wJCYwJCYhIxgnKR4m LRsiKRclKhgkKRcjIxkkJBomIyAlIh8jJiEjJiEqIyAsJSIoKyQpLCUoKBsmJhkoJyApKCEnJhYn JhYmJBcpJxosJBksJBknJRsoJhwtKR8sKB4sKCcoJCMjJB4jJB4kIBUjHxQoJBgpJRkoKBsoKBso JSAoJSApJRwmIhknJRgmJBceIBMXGQ0fGgwfGgwbGQcbGQc+QD9BREM/QT4+QD09PEA8Oz8xNzkx NzkrODUqNzQpMTAmLi0hMCscKyYiIx8fIBwQIQsQIQsMIxEMIxETIhYaKh0eKCcgKikpMS4qMi8t LjU1Nz4zNTI3OTU3NzQ4ODU1NTM1NTM3OC8zNCwwNDMuMjEzMzM3NzczNzEzNzEzODQyNzMwNzQv NTMrODMsOTQ1NzI1NzI6MzU5MjQtMS40OTUvODQwOTU6Nzw7OD07PDU4OTIyOTcxODUxNzkyODo0 OTgxNTQ4NTcyMDEwNDEuMi8qNzIvPDguOzQsOTIzOTM3PDc3OzoyNzU3PzwxOjczPzUuOjA8ODk5 NDU0ODI0ODItODQvOjcnNzErOzUyN0AxNT8wOj8tNzwxNzkxNzkvOTowOjsvNzgwODk0NTs0NTs3 NTo0Mzg0NTkxMjU0MzgzMjcuNDIsMjAvNDkwNToxMjU0NTkuMTkxNDwxNTQxNTQxMzA0NzMxMi4y My8wNTgwNTgvLy8zMzMrMTEwNzcuOzcqNzIrNC0rNC0tODIqNC8xOToyOjs1MzIzMTAtNCwrMiov MCguLycxLiszMC0xMiovMCg0MSoyLyguLyswMS0vKig0Ly01MisxLicvKx8xLSEwMyosLyYgMCUi MicsLSksLSkqKycrLCguLSYrKiMvLCcwLSgyMCYvLSMtLSMvLyUsLiItLyMsLyYtMCcwLiQwLiQs MiYoLiIoLyckKyMpKiQvMCoyLi8xLS4uMCUsLiMuLiMsLCEvLCkvLCkyLi8xLS4qLiEvMyYrLSIp KyAqLiEqLiEtKhsxLh80LSoxKicpKyAsLiMsLiIrLSEsMCMnKx4iKSMkKyUnJyUkJCIlJxwkJhso JyApKCEoJyAqKSIkKh4hJxsjJxgkKBknKR4iJBkjIxYlJRgoJhkoJhklJRglJRgpJxwmJBkhIxgj JRonJB8mIx4uIhYrHxQnIRgnIRgnJRgnJRgdIRYhJRkmIBcnIRgmIRIhHA4jHQojHQokHRAiGw8a GwoYGQk4QD04QD08Pjs8Pjs3ODM3ODMtMikxNy0wMy4uMSwoMSomLygiMikhMSgqJyAqJyAnLBgn LBgYKB8WJh0VKyUYLykpLC8mKSwoLSYvNC0zLzI8ODs8PDA5OS07NzU+Ojk5Ozg3OTU+NzM+NzM/ PDc/PDczOzI1PTQ3NTI6OTU9PT06Ojo8ODk7NzgsNzUtODc4ODg7Ozs/OTs9Nzk3Ozw5PT4zPT4z PT4xPUEvOz88PDw9PT00OD8yNT0yO0QxOkM7NTs7NTs5OzwzNTc0OzkwNzQvNzoyOj0uOjouOjoz OjouNDQuOj4wPEA1Oz0xNzk4OzUxNC8/Mzs9MTk6Pj0yNzUfNzIlPTksNTctNzgrOj0sOz4qOzsp OjotMzMtMzMwNDUtMTItMTAzODcyNDUxMzQxMDQzMjcwNDUsMDExMTEzMzMtMS4rLyw1MzQ1MzQz MzMzMzMyMjIwMDAyMjIzMzMxMS83NzQ3MjE4MzI0NDc1NTgwLjE3NDgwMjMzNTcvOjQtODIuNDAu NDA3NzQzMzExNzkuMzUyNDUzNTcrNTArNTAsNS4qMywxLzAvLS4wNDEsMC0wMCYzMykvMTAxMzI1 LzE3MDI4MzQ9OTo3Myw0MSowMywuMSorMiorMiotLiouLystKy4uLC8vLy8tLS0uLi4vLy8tKSoy Li83MDI3MDIwMDAvLy8pLy0rMS8tMCkuMSooMywnMissMSooLSYqLyoqLyomKiknKyoqLSQpLCMo MiMkLh8tLywsLisrKykpKScuLCIxLyUuLCEtKyAoLCApLSEtJx4vKSAyKCAwJh4wLCAtKR0oKB0o KB0oKxojJhYZJRcaJhgiIRomJR4mJhsjIxgjIxkmJhwsIyAtJCEoJxcmJRYmIxUlIhQqIBgoHhYn JRolIxgiJBggIhYmJBcoJhkoIRIoIRIjIBIkIRMqIxYoIRQsIhgnHRQjIBIlIhQfIRYcHhMeHxce HxclHRYrIxsfHRQXFg0eFwkiGwwhGg4eFwsYFgcaFwg5ODw0Mzg6Ozc8PTk/OzA7Nyw0NDIwMC41 MC45MzE4Oi4wMictMSQyNykrOS4pNyw9Oy09Oy0/PDlAPToqPDoqPDozNTI8PjtMRTtMRTtaRD9b RUBkTEFrU0hiUUVhUERbVEpaU0ldTUldTUlrWk1tW05jVlFeUU1KSU5HRko8P0c0OD81PUA0PD8q QT0pQDw0OTo3Ozw3OUQyND8xOjk1Pj00Oj4yODw0OD8zNz46Nzw6Nzw4Nz0zMjk0NEMzM0E0Lzkz Ljg4NDoxLjMxNTIuMi8tMjQpLjAqMzQrNDUuNzMrMzAwNDMxNTQjOTMhNzEnODUnODUtMDguMTkw NzQtMzErLC8xMjUxMDcyMTgxNTQuMjExNzEyODIsNDEtNTIrODMoNDAyMjA5OTcxMzIzNTQyNDMy NDMwODktNDU1NTg3NzkvNTEtMy80NDczMzU0NzU0NzU4ODg4ODg1NTU0NDQyMTU0Mzg5Nzg0MjM0 NDI1NTMyMTU1NDkzMjk0MzooOjUnOTQuNzMtNTIzMTI4NTc0ODswMzcxNDwxNDwuNTkwODspMjMm LzAzMzMwMDAyMjAzMzErMTEpLy8xMDQwLzMzMTI0MjMxNDwvMjorLywrLywtMCkvMisuMC0tLyws LissLisrKjAtLDIrKSwrKSwrLicqLSYtLCkuLSokLi0mMC8oKzAkJywnKSgoKikqJSEwKycpLSom KicnKSYnKSYpKiYqKycnKiMoKyQpKiIoKSEoLxsmLRkqKyMsLSUvLSIqKB0tKCIsJyEoKSMpKiQm LCAmLCAqJSEpJCAqKB0qKB0uKiAtKR8kJR0jJBwtKBotKBonJxomJhkoJhkoJhknJRsiIBYjHhol IBwuJSIuJSInJB8pJiEoJhwlIxkpJB4kHxkoIx8nIh4oJCMmIiEkICMlISQqJR8tKCIlJxwjJRok JBkmJhspKiYjJCAiKBkhJxghJBQkJxYlJSMnJyUlHhsrJCEiJBYeIBMfGxMZFg4YEwodFw4cFwob FglORENRR0ZWSkFWSkFYSEVdTUldUFhTRk5WQ0xdSVNlT0lkTkhRTUFRTUFTTk1PSklWUD5dV0Vo WkxoWkxfXVBcWk1dWkhiXk1rWlBuXFNzXFR0XVV7Y1F7Y1F3X0N4YUR5aU51ZUp1Ykh6Z02DZFB+ X0x3X1drVU1WT09NRkZAPD0+OjsyPEEwOj80PD0yOjs4ODo5OTsyOjswODk3ODM6Ozc1Ojk0OTg5 OD44Nz01NTM0NDI0NDcxMTMqMDkrMTowNzItMy8uNDAsMi4xNTIuMi8zMTI0MjMxNTIxNTIuNDAy OTQtNy8uODAqNzQtOjgvNzovNzovMjUzNzo0ODAzNy8yMTgzMjk4Nzs0MzgyNT0wMzs3Nzc3Nzcv PDovPDoxPzcvPTQ5Nzg7OTovNzgxOToxODMxODM1Nzo0NTkqMzkvOT4zNzwxNDo1Nzw1NzwyMzk1 Nzw5NTs6NzwyNzUvMzIzMjs0Mzw3NTw4Nz00NzU3OTgzODkzODkyNTswMzksNzUrNTQzMzU1NTg0 NDQ0NDQyMzczNDgxMjUyMzcwOTMuNzEoNDAoNDAuMjMuMjMyMDMuLC8qMTIoLzAsNDEsNDEyMy8t LiorLSwrLSwwLyovLiksLyguMSorMCsqLyoqLSYpLCUrKykrKykuKyguKygyLyotKiUwLi0sKikn LigkKyUmKSwoKy4lKiUkKSQpKCEqKSIwMC4rKyklJiAoKSMqKSQqKSQpLCUrLicpLCUoKyQnMh4j LhokKyMmLSUtLiopKiYpKx0nKRsmKSAnKiEqLSYpLCUnKCQnKCQpKx8oKh4rKCUrKCUqKioqKiot KSwtKSwoJyQoJyQkKSIkKSIpKiIoKSEkIyApKCUnJyUoKCYmJigoKConJiMkIyAoKSUjJCAoJyIp KCMsKCksKCkqJSEqJSEuKSUwKycpKScnJyUmJiQnJyUnKSgkJiUdJhsgKR4kKBkkKBkkJSEkJSEj IBklIhsiIxIkJRQnIxcfGxAgFwogFwodGQcdGQdtWlRwXVd9ZFN9ZFN3ZFR6aFd+Z2J6Y156YVp7 YluAY1p/YlhyYUptXEZwXEVwXEVyYU10Y096Xk5/Y1N9ZVR6Y1F9ZFp9ZFp/Z1ODalZ/aUx6ZEeD aE5/ZEp7Yj+DaUaLbj+Iaz2DaEGGakSNZEaLYkSDZU93WkRXTEBIPTJFNTdGNzg0Mzo1NDs5OEA3 NT41Mzc7OTwrOjsrOjs4ODg7Ozs3Ozg4PDk9P0A7PT45ODwzMjcxNT8vMz0oOjonOTkwPjUvPTQs OTcsOTcxOToxOTo3Ozo0OTgxNDgyNTk3OTo4OjsrPTQtPzczOT01Oz85PUo4PEk6OT09PEA5PTw3 OzowOj8vOT46OT86OT81OT4zNzw6PD06PD0uPTwrOjkrOzUsPDc8Oj07OTwvPDouOzkxODMyOTQ4 NTc3NDUpMjUsNTk0Mzw4Nz83OD0zNDoqLTAwMzc0MzozMjkuOTgqNDM1NDs0Mzo1MzQ3NDUxMTM1 NTg0NDQzMzM0NzU0NzUyMzcyMzcxMi4zNDAvMTAyNDM0MjU4NTk5NDU3MjMyNS4zNy8uNS8uNS8t Ny8rNC0uMC0wMi8wNTAwNTApMy4pMy4yMSwyMSw3MS03MS03MS84MjAvLikvLikuMSotMCkoLyco LyctLS8sLC4yLjEyLjE4MjAyLSswLi8xLzAuMSorLicsLTAwMTQpMSwmLiksMi4sMi40MjEwLi0t LiotLiosLi0tLy4uLTEuLTEvLDEuKzAoMSglLiUmLyQmLyQlMCkkLygnLCMsMSgtMTAnKyoqLSYq LSYqKigpKScqKCkqKCksKi0qKCstKywyMDE1MC4yLSssKissKisiKickLCkvMCgpKiIhJxolKx4i JSApLCcoKyQnKiMtJxwrJRoqKB0nJRopKCMmJSArKCUqJyQtKBotKBorIxgqIhcrJR0vKSEsKh0p JxokJRQjJBMeIxIgJRQhIRcnJx0mJBkmJBkmIBglHxcmIRQnIhUmIAwkHgoeFwshGg4fGQghGwp5 XU2Lbl2Mck6Mck56bk94a02AaFaDaliJbVeHalWEZ1CEZ1B/ZUN0Wzl6Wzd/XzuDYkaDYkaLaE6M aU+HY0iMaE2LaU2JaEyRbkqOa0iQbkGEYziMYTSRZTmIbzyOdUGSbjqRbTmQaT2Saz+acEWacEWQ b0eGZT5cW0hQTz1IQUE/OTk7OTo9Ozw5Q0YzPUA6Oz48PUAxPT8xPT86Ojo8PDw5Pjk4PTg3Ozw3 Ozw3PTs0Ozk0PEEzO0AtRD4pPzowPDw0QEAtPTosPDkwNToxNzstPToqOjcxNDo0OD04OUA3OD8q PDosPjwyOzgxOjcvOz0wPD48Oz88Oz8wOzoqNDMsOTctOjgxNTcyNzg0MC81MTA0MjU7OTw1Nzwy MzkoMi0sNzE5ODQ5ODQ0NzUyNDMyNTA0ODIyODIuMy4uMjEwNDM1NTUyMjIyMzkyMzkuMDEwMjMx MzAvMS4rNC0rNC0zNC4wMSswMC4zMzEzMzM5OTk1Mzc3NDgzNTQyNDMzNDgyMzc0NzU3OTgxMzAz NTI1NDE0MzA5NDM1MTAzMzE3NzQsODArNy8tNy8uODAyNSwxNCsuMyouMyouNDItMzE4MzI7NzU/ NDU8MTIyNDMyNDMrLy4sMC8rNC0qMywwMDIxMTM4MTM1LzE0MDE0MDExMS8sLCotLzAyNDU1MTQz LzIwLi8wLi8pMSwpMSwsLi0rLSwxMC0wLywsLSktLiosLCouLiwvLCkuKygwLC0uKisqKigrKykt LyMqLCApKiYrLCgqLRkqLRkpLBglKBUqKh8tLSIvKyEsKB4lJxslJxspKCUpKCUqJR8tKCItKhsr KBkmJhsnJxwkJyAnKiMqKRkmJRYgIhYjJRkjJRolJxwkJhgkJhgmJBcpJxoqJxYoJRQlJhQlJhQn JRolIxgpIxssJh4lIRglIRglHg8pIhIsJRYqIxQeJA4dIw0nJQ8lIw4mHxAnIBEuIBMqHA8oIBYo IBYfIhAaHQwcHAcbGwcXFwwYGA0eGAYiHAl/XkOLaU2Mb02Mb016aVB4Z06AZUmCZ0qDY0iAYUZ+ XUmAX0yCZUSDZ0V/YTSEZTmOa0iQbUmWc0+RbkqIbUaIbUaNbUiQb0qVck2XdE+ZbkmSaESWaT+a bUOZdEGdeEWWd0iSc0WZb0WZb0WWbUOVa0GGa0qAZ0ZhXFFVUEZBREM+QD88Pj88Pj8yREYxQ0Uz O0A3PkQzOjo1PDw4PT80Ojw1Pj01Pj01OTwvMjUxOzwxOzwxOT4xOT4uOjotOTkpOjwqOz0oNzUp ODc0NDQ1NTUrODMrODMyNzUwNDMuODkvOTotOTssODozPDkvODQtOjUvPDg0OzkxODUsNzEsNzEr My4wOTMvMDMyMzcxMzAwMi85MjI9Nzc0NTk4OTwwNDMzODc3OTgzNTQ3NDM1MzIvNTEvNTEzOzIy OjE3ODE5OjM1OjkvMzIyMzk4OT4xNTQxNTQ1NzA0NS8zNTI0NzMzNTQ0NzUyNzg0OTozNzw1OT46 Mjk+Nz0yODwwNTo0NTs1Nzw4OT44OT4xODgyOTkyOTQxODMvNTEwNzIyMjI4ODgsPDQoODAzODQz ODQwOTUwOTU1NTMzMzEwNDEvMzA3Nzc0NDQ7NDc4MTMuNDQsMjImLS4pMDEkMCwiLiooLC0qLi8y LjExLTAzLzAxLS4nKyooLCsqLCkrLSouLTEtLDAxLS4wLC0sLi0qLCsnKygnKygpLSEoLCAnLiYm LSUrKiUsKyYsKSIrKCEqKycqKycpKScpKSctLiYsLSUrLCgoKSUmKhsmKhsnLBomKxkpKiIoKSEv KyIvKyIpKR4oKB0nKR4oKh8nKCAjJBwlJRgmJhknJx0mJhwpJyYrKSgkKh0iKBsjJxolKRwiKBwi KBwpJxwpJxwrJiQoIyEnJhYnJhYhJRYmKhstKSAqJh0jJCAmJyMoJyApKCEqJyArKCEwJyQuJSIn JhUlJBMsJRcsJRcsJBkuJhsrKBcqJxYoJRUoJRUdIhEYHQ0YHAcXGwYbFgkaFgggGgkiHAqCZ0+D aFCLbVSMblV/aVp4YlN6Ykx5YUp4WkZ7XUl7Wk5/XVGIZ0iLaUqGakGNckiVdE+Sck2Xb02VbUqO akqNaUmLb0mIbUeUbUSac0mZaU+ZaU+RaUqSakyUZz6fckiWbkqSakeNZ0GMZUCHZECJZ0ODaEx4 XUFXU1FPSkk9Pzw8Pjs5PTw1OjkxPEQvOkEwNTw0OkA4ODg5OTk4Nzs0Mzg0Nzg3OTo3OD00NTs5 Ozo1ODc3NTo5ODwyODwyODw3NTw4Nz0wMzsuMTk1NTU5OTkyNzMvMzA4OjsyNDUxODU1PDo8PUA4 OTw3Oj05PD8vPDovPDo3OTg1ODcxNTQyNzUrNTQuOTgyMzc3ODs0NDc4ODo6Ojw9PT87Oj47Oj41 Oz83PEAyOUExOEAxNTQyNzU1Ojk0OThAO0A/Oj89Pz45OzoxNzkyODozNzw1OT45Ozo3OTgwOTgy Ozo1PDgxODMvNTUyOTk5Nzo6ODs4Nz01NDswMzk0OD0wPTsvPDoxNzkyODo1NTg3Nzk4Ojk0NzUu NzUwOTgwNDMwNDMwNDUvMzQtODIqNC8vMzIwNDMyMzc0NTkzMzUzMzUzNDA0NTE4NTQ4NTQ6MzUz LS8vLy0vLy0wLi8vLS4sMC8pLSwrLy4qLi0vLy8tLS0yLi0wLCssLCwtLS0vKCcwKSguLysvMCwv Ly8vLy8vKy4vKy4yLyowLSg0MicvLSIsLScqKyUpKSkrKysxLCoyLSstLS8tLS8pLy0oLiwxLikx LikrKykpKScoLSYpLicqLiIrLyMpLCMmKSApKiYpKiYlLCYnLigsMCQmKh4oKSMnKCIoKicsLiss LSksLSktJykuKComJyMnKCQqKSIqKSInJyUnJyUlKiMlKiMpJyYqKCcrKCEqJyAoJyIpKCMpKiIn KCAkJyIkJyIlKiEoLSQqJyArKCEwJSMuIyEoJRUqJxYqJyIrKCMqJR8sJyEqKRkpKBgpIhIoIREi HBIeGA8WHgkWHgkcFwocFwokFwklGAqCY1SCY1R/aFSAaVV3ZFVvXU5zV0dwVUV4V0Z6Wkh0Wj5+ Y0eEaEqEaEqDZ0mEaEqIaECJaUGMY0WOZUeLY0WMZEaOaEWLZEGOZEOUaUeMbU+HaEqHY0SEYUGJ Wz+WZ0qOZESNY0OUYzOUYzOSaT6Va0CWb0yHYT5bVkxOST9HRDxEQDk8Pj07PTw4P0AxOTo4OT46 O0A4OTw4OTw4Ojs3OTo5OTk4ODg4Ojk7PTw+Pj48PDw9OkQ8OUM5OUQ4OEM6N0A8OUM8Nzw7NTs5 Oj05Oj07OD06Nzw4OkU1OEM0Ojw5PkA4PT87QEM1Ojs6Pj86Oz43ODs1Oz8zOT00OkAxNz0qOToq OToqOzknODU4OTw0NTk0OD03Oj85OTs1NTg4OT43OD0tOTstOTsxNTQxNTQyMTg4Nz05NEM3MkAy NT0uMTk3OTozNTc1NTU4ODg3NzkxMTMpMzAsNzM0NS81NzAuMjEuMjE3MjE6NTQ0NDc0NDc3NDg6 ODswPDQqNS4vMzQvMzQzNzE0ODI3ODE1NzA0MDM5NDg0NDQxMTE0Mzg0MzgxNC8xNC80OC4xNCs0 NDI0NDI4MTU3MDQ6NTQ7NzUyMjI1NTU1MzQ0MjMyMS4zMi81NTM0NDIpMzIpMzItMzEtMzEzMTA3 NDM0LjAzLS8xLzA0MjMxLy4vLSwwMSswMSsyMjIuLi4vLzEtLS8wLywvLiswMi8wMi8rLSwtLy4s LC4wMDI0LjAzLS8wLTIuKzApLS4qLi8qLCkoKiclKycoLionMSwmMCsuLSguLSgoLSglKiUlKiUm KyYkLCkhKSYjKSUnLSknKCQqKycvKyoxLSwrJygrJygoJSIpJiMhKB4fJhwkJyImKSQlJSMlJSMf JB0jKCEiJCMhIyIgIB4jIyElJRslJRspJxwmJBkfIxccIBUnKBgpKhomKRYfIhAlHxUnIRYoHxEp IBIuKB8pIxoeHxAeHxAnIhMjHg8kHQ4gGQoeGAcdFwcaGgcbGwgYFgkYFgklFwkkFghwW0N4Ykly Y0h0ZUpuXkhpWkRtVz9vWkF1XD5/ZUeDZ0WGaUeIbk+Ibk+GY0CGY0COaT6Ra0CUbUeWb0mNZ0SJ Y0CHXzuEXTmDX0OLZ0l6aEd1Y0N+XkR7XEGCWz+LY0eNY0GNY0GNZTiUaz2Vck2UcEyIbk17YkFX VUZKSDpEQztFRDxEQEZAPUM9P0A/QUM9PEA7Oj46O0A6O0A4OkU4OkU7OkA5OD4/QUBBRENDQUo/ Pkc5OUc8PEo/P0o8PEc6Pj05PTw6OT85OD43Ozo1Ojk6OT86OT81OUA0OD8yOj8zO0A7Oz08PD46 PD03OTo5NTs6NzwyMzkzNDoxMjg1Nzw1NTU1NTUxOzwwOjs0Nzg0NzgzODczODc8NzI5My85OTk3 Nzc1NzA3ODE0NDIzMzEzMTA0MjEzOTsvNDcvNTMsMjA1OTMwMy45My05My0yODIxNzEvNC80OjQ5 MjI3MDAzNDAzNDA1NTM0NDI0OzkzOjhAODlBOTo1OjkwNDM1ODk4Ojs5OTk3Nzc0OjQ0OjQ+NTk+ NTk4ODg1NTU1NTU3NzcwNDEyNzM0ODA3OjI1NTM1NTM3NzcyMjI1ODk3OTorNTIrNTIoMzMoMzMr MjMtNDU5NDg6NTkvMzIuMjEsNTcrNDUyMTgxMDctMTIrLzAwLyowLyoxMi4wMS0yMy8wMS0yMS4x MC00Lyk1MCouMSwsLyomLi0oMC8pLSwpLSwrKSwuLC8tKSguKikzKSozKSoxKSwxKSwpLCUmKSIm KCUoKicoKCgoKCgwKSQxKiUqKiApKR8jKiIkKyMlJxwoKh8kKyMhKCAlKRwlKRwoJhsoJhsnIhwn IhwkIBUnIxckKBsgJBchIBkmJR4mIxwmIxwnIxklIRcdHxQfIRYiHhYkIBccHw8fIhImIBcjHRUg IRIiIxQlIhEkIRAgIg4fIQ0dHA4fHg8iGhIjGxMkHxIkHxIeHQ8gHxAhHA4iHQ8jHA0hGgsdGQga FgYaGQkbGgoYGAYXFwUkFgshFAluX0N0ZUhyYz9rXTprXEZrXEZ0XkRzXUOCY0SNbk6LblCLblCR cEyRcEyLY0WQaEmUc06RcEyOcEmNb0h5YkV1XkF0XUx0XUxyW0V1XkhtWERlUT1uUUhwVEp9WEWH Yk6GXUaGXUaHaFSMbViEbVGGblNzY09nV0RIUEdDSkFGRkY/Pz85PT45PT4/PEZBPkg6P0E4PT83 Ozw3Ozw3N0E5OUQ5N0Q5N0Q8PUU5OkFAOkdBO0g8OUM5NT86OkU4OEM4Nzs4Nzs6Nz45NT01NTU1 NTU7Ozs6Ojo4ODo4ODozOEEyN0A3NTo3NTo8ODk5NDU4ODg6Ojo5NT00MTk0NDc3Nzk0MTc0MTc4 ODo4ODoxMS85OTcyODoyODo+NzM5MS43Nzc3NzcyODIxNzE1Ny41Ny41NTU0NDQwNDMzODcoNC4q NzA0OzczOjU9NTA+NzE5Ozo5Ozo1ODk3OTo4Nz85OEA0OTU3OzgyOzovODc0NT0yMztANz5DOUA4 OT44OT4wNToyODw1Nzw3OD03NTw3NTw8NDs9NTw8Nzw4Mjg1NDk3NToyMTU4NzsxOTIwODEyNDMv MTAsNDEqMi8mNTArOzUoODImNTApMjMsNTcoMjEnMTA1MTIzLzAvLDEwLTIsLi8wMjMtLDIpKC4r LSwsLi0sLCosLCosLScqKyUtMCksLygtLCUtLCUwLCAxLSEuKyYsKSQjKyglLSooKislJygpJSQt KSgjKR0jKR0tKiMtKiMsJyUsJyUtKiMtKiMoKh4mKBwmIRsmIRsrJx0qJhwoJhslIxgjJBwlJh4p JxosKh0nKRsjJRccJgsgKg8fIRYhIxchHRUhHRUiHRckHxkpJRkjHxQhIBElJBUbHRAcHhEjHxYl IRceHBAeHBAiHhYiHhYaHBIbHRMgHhUgHhUiHhUkIBYhHRQhHRQhHg8iHw8cGRMaFxEfGg4hHA8f Hg8eHQ8aGhAbGxEeGgkhHQshGg4fGAwaFwgaFwgbGgwaGQsbFwcaFgYkGQohFgd0XUd0XUdyXE1r VkdwWExzW057YUl+Y0yLa06QcFOMcFSJblGJZVOCXkyDYlCQblyIclaEblN9aFNuWkVeVUxeVUxf V1FeVlBeV05cVUxXTlFTSU1nTkdpUEltUEVwVEhuVkxuVkx6Xk57X091XE5vVkhWTURPRj07REM5 QUBAPUM8OT47OkA6OT87PD87PD80OzsyOTk1Pjs0PTozNzw1OT45OD47OkAzNzwxNDo6Ojw8PD49 OTo8ODk5Nzg5Nzg5MjI8NTU5Mzk7NTs9Ozo5NzU5Oj04OTw6ODk9Ozw8Nz47NT06NTc9OTo8Ojk7 OTg6Ozc1NzI6Ojw4ODo4Nzs5ODwzNzw0OD09OkE+O0M3OzoyNzU1OUA1OUBDOUBANz44OTw4OTww Oj0sNTk1OjkzODc5ODw0Mzg3Ozw3OzwtNzovOTw0NDQ6Ojo4NzM+PTo9Ojc8OTU1ODk3OTo4Nzs1 NDkwNTozOT0vNTUzOjo0OD0yNTs9NDo8Mzk0ND85OUQwOUMsND4yMzkyMzk5NDM3MjE3MjE3MjE5 MDM4LzI1MzI0MjExMTMxMTMxNTQtMTAuKzAtKi8tLy4sLi0kLygoMywrMCsqLyovLSwvLSwlLicp MisvLSwvLSwtKyouLCsvLikwLyooLCspLSwsLCwsLCwqKycoKSUoKCgnJycoKyQoKyQpKyAmKB0s KiAtKyEoJyIpKCMmLSchKCIjJh8lKCEnJiEpKCMhKRsfJxknKiEmKSAkJBolJRsoJRYrKBkpJxol IxYoJBomIhgnJRooJhspJB4mIRslIhQlIhQjHxYlIRcnIRklHxcgHhQiIBYnHxcqIhojIBseGxYc GhkgHh0nHRYlGxQdGxEeHBIXHRIZHxQhHRUlIRgfHREbGQ4eGw4hHhAaHBEZGxAaHBEZGxAcFxId GBMaFxEcGRMYGhAZGxEfGxMdGREeGBAdFw8eGg8cGA4hGRIgGBEcGQwaFwoSFQcWGQoYFgcYFgca FggcFwodFQgjGg0lGgkkGQhuVU5vVk9pV05kU0loVFRtWFhzWFR1W1Z+Yk9+Yk96XUV4W0N0XlNr Vkp1Vkx/X1V3YkpwXEVoUUlhSkNYSEVbSkdYSk1URkhPSEhKREQ/QUM8Pj9NRzhQSjtXSTpcTj5f TkVfTkVlTj1iSjpaSD9WRTxJRD1HQTs8PUA8PUA/OD5AOT9APj9BP0A3Pz4zPDs1Ojk1Ojk9Pzw7 PTo5Oj04OTw6OT08Oz87OkA7OkA7PD87PD89PT88PD47PD86Oz47Ozk6Ojg3Ozw4PD06Pj81Ojsz Oz4xOTw3Ozo5PTw8Oj0/PUA6ODk+PD04QD00PToyOj0yOj05PD83Oj01ODk1ODk5ODw7Oj48PUA5 Oj0wP0ArOjs5OEA5OEA7NT09OD81PUAxOTwsNz4sNz4wNTgvNDc1NzwyMzk0NTs1Nzw0NzM0NzMv MzI0OTgrNTItODQ5OjU4OTQyNzM0OTU0MjM3NDUyMzc3ODsxLzA0MjMxMzAxMzA4Mi44Mi4vMTAw MjEyMDEuLC00LC81LTA0MS4xLissLC4uLjA0LC00LC0yMCYxLyUtMistMisuLiQsLCIuJicyKisw LiMuLCEpLCUrLictLS0tLS0tKyotKyopLikrMCssKygrKicqKSYqKSYrLikpLCclLCIlLCIoKyYp LCclKiUmKyYoKB4pKR8mJx8mJx8rKCEpJh8iJBknKR4nKCAoKSEpKx8jJRkoJR4pJh8qJSEsJyMi JRwgIxomKh4kKBwhIxcfIRYeIBUeIBUlIxYkIhYlJhUlJhUmJBolIxkgIRkhIhokIxEkIxEcHhEd HxIhHxUhHxUgHRofHBkgHhQhHxUeGhIeGhIgGxciHRkeGRQeGRQaFxEdGhQgGhofGRkgGhIiHBQZ GxEZGxEYGRIZGhMaGA8cGhAcGQwcGQwcGw0aGQsZFRMaFhQXGhIXGhIdGQ8eGg8dGQ8YFQobFg8c Fg8fFhQgFxUeGg8bFw0WGAgXGgodGgoYFgcbEwgfFgsjFgoiFQoiFgojFwtfR0ReRkNYRj1YRj1H RURNSklaSkNdTkZoVklfTkFYSjtaTDxhSEFnTkdrTUFtTkNaTz1NQzFKPDJTRDpKRUNPSUdUREVQ QEFFRkFAQT07QDs3PDdORDVPRTdKSThMSjlKTENKTENNSDtMRzpPTERJRj5ERERDQ0M4O0A9QEZB PEQ/OkE4QD86Q0E0REMxQD88Oj0+PD9AQEM/P0E9P0A9P0A4PT84PT86PUA5PD85PD86PUA8QEE8 QEE+PEg9O0cxQEEtPD03PT05Pz85PkA4PT8zO0AyOj8vPj0sOzo5Ozw6PD01ODk6PD0wPTsuOzkv OTwuODswNzcuNDQzNTI1ODQ3NDg5NzozPDsvODcpOzclNzIsNTcuODkvNzgvNzgvNzgvNzgrNDot NzwzMjczMjczMzUyMjQzMDUzMDUxMiowMSksMystNCwjNSshMyk0OC4xNCswMDAzMzMzLig6NC4p LicrMCktKyAtKyAuKBUzLRk3LxY5MRg0Lx4sJxYvKhk3MSA8MSczKR8wKx05MyVEOzJJQDhDOSg1 LBw+MiBWSTVPTDtPTDtHPihFPCZQT0lWVU9JTEhAQz8rMiAkKxkjKiIkKyMlLCImLSMjKiIjKiIn KCQpKiYkKSQhJiElKCMnKiUkKSAgJRwiJhokKBweKBofKRspJxwqKB0oIx0mIRsiJhojJxsjJRog IhcmIyAnJCElKRwhJRgkJhsjJRoqIhkpIRgdHhYfIBgdJRgaIhYiJRMiJRMaJBYYIhUZHxQbIRYc IBQfIxYkIBYhHRQiHhUgHBMdHg4cHQ0eHQ0fHg4aHBEbHRIWGxMWGhIdHg8dHg8ZHA0ZHA0hGxMg GhIYFg4bGRAiGhMjGxQfGhYcFxQZGgwbHA4YGg8WGA4WGA4aHBEWGg0YHA8bGw8aGg8iHAcjHQge Gg8dGQ8ZGgwWFgkVFQsXFw4bGAkZFgcWFgsZGQ4dGREaFg8hGA0iGQ4aGQsWFggbGQcbGQcdFQof FgsfGQgeGAcaFQQeGAdVRUFPPzxIQz5PSUVJRD1KRT5QSENQSENbSENaR0FVRj9YSUNWTkhXT0lM RUVMRUVPSUVMRkFNR0BQSkRPSE1RSk9HRkNEQz9ESEdDR0ZFSlFBR05GQz9HREBKRz9PTERNTklK TEdFUEBATDxAREc/Q0ZAQEBFRUVEQEo+O0U6PEg8Pko6QUM6QUM7Q0g6QUc/OENDO0Y7PT4/QUM8 PUA8PUA3Oj81OT48QEE4PD0yOzo0PTw6Pj01Ojk5NEE7N0Q4PD03Ozw1OjkzODc1PDozOjg1OTw3 Oj03Oj00ODsxNzkvNDcyOTcwNzQnNTQnNTQvOjcoMi8qLisxNTIxMzIxMzI5NTI5NTIxNC81OTMn OTAnOTAzNTI0NzMuNzEuNzErMS8yOTc1NTUzMzM3MDI4MTM1MTA1MTA4LzA1LS4vLBo1MiBDPjNE PzREPTNBOzFGPzVEPTMvOS8oMSg0NypFRzpRVU1aXVVeZWVQV1ddUExnWlVyZVpqXlNaTz1WTDpb VlVnYmFqZF1nYVpnZ2RlZWNocHRiam5jZFFtbltzcm59e3h7d3V9eHdzdG15enNzfYt5g5Fed4JO ZXBHRD4zMCsqKyMqKyMpKx8oKh4jKxsjKxsnKCIoKSMiJx4hJh0jKCEkKSIlKB8hJBsgJR4hJh8k KhsiKBkeIhYjJxonJx0jIxkiIhclJRokIhclIxgmJSImJSIgJBghJRkjJRojJRomIhgkIBYfIRYd HxQaIRAgJxYiJRMbHg0jIRUjIRUjHxYhHRQfHRQhHxYeHxAdHg8dIBAbHg8aGwocHQweHQ8bGgwX GQ8VFg0VGg8YHhMWHQ0WHQ0ZHAoZHAoaGA0fHREYGhAaHBIcGg8bGQ4hGg4gGQ0eGQ0eGQ0ZHAsX GgoTGgwTGgwWGAwXGQ0aGA8ZFw4dGwkeHAoWFg0YGA8bFwcbFwcfGQ8YEwoUFQYVFgcbFAohGQ8f GAodFgkhHA8eGQ0UFAoTEwoWFgYXFgcZGAgbGgodGQUfGwcfFwQgGAVNSElKRkdIRU9MSFNKSExI RklKR09KR09PRlVORVRNR01RTFFJTE1OUFFBSU8/R01BRkdDR0hIR1BGRU5KRlRMR1VERUhDREdA P0ZFREpHQUxJRE5DQEFDQEFER0FFSENFRUNFRUNERTxDRDs8O0Q+PUY+PUFAP0Q/PUA9Oz4yPkMz P0Q6O0A8PUM5PD8zNzo+OUBAO0M6Ojw9PT81OT43Oj86OT09PEA7Pz44PDs0PToxOjcyNDE0NzM4 Ojc3OTU4Ojc1ODQzODcuMjE3ODM5OjU0Nzg1ODkxNDgyNTkyOzotNTQtMDMwMzctNDUsMzQtNTQp MTApLyssMi4zMzU0NDcyMC8zMTAwMi8xMzApMzAqNDEwMjEwMjEuMzUsMTMyNDMzNTQ0Lys3MS1A PD1HQ0RAQEBAQEBGQDpPSUNbVEloYVZvamtqZWdiYl9fX11dV1NTTUhDRzc7Py8/SEVXYV1pdIh5 hJl1gqZnc5ZnZ4RpaYd3eIx3eIx4fX56f4B9eZ16d5p3eIx4eY1wc5Btb4xkdJFpeZaAiJSGjZmC iaOHjqiEhpqLjKGDlLJ9jat7ia+AjrRrf5tbbolISU00NTkqLiEpLSAlKx8iKBwlLR0iKholKCEn KiMiKSEhKCAfKB0eJxwfIhsjJh8fJh4dJBwlKRwjJxocJBceJhkgJhkhJxojJh0iJRwnJB8nJB8j Hx4mIiEoJhsnJRomIR8mIR8eHRYiIRodJRYaIhMdIRUgJBclIRgiHhYeIBYcHhQdGRAiHhUfIxYc IBQbHxEbHxEaHw0ZHgwgHBEhHRIcGxYZGBQVGg8TGA0VGg8WHBAWHQ0WHQ0YGg4XGQ0UHQ8UHQ8Z GBQbGhYaGhAWFg0gGw0gGw0cIQ8ZHgwdHxIYGg4UGBIUGBIYGhAYGhAWFg0ZGQ8fGQ8gGhAdExEf FRMcGA4dGQ8dHAwYFwgbFgoaFgoZFAoaFQsdFgoeFwscGhAcGhAZFggbGAoeGQ0bFgoZGwgbHQoa HAkYGgcbGQYbGQZGRkZISEhNRUxNRUxOR1ZPSFdKR1FGQ01IRVxIRVxJR1VKSFZJSUlISEhHRkpD QUY7QUE9RERARko7QEVDRVBAQ047PUg5O0Y8OkY+PEhBOkBFPUQ9QUA+Q0E8REU6QUNBQURBQURB PjtDPzw9PT09PT07QEM5PkA1O0EzOT8uODsyPD85Oj85Oj8vOTovOTo9OkE8OUA6OT07Oj43NTo4 Nzs5Ozw3OTo0OTUzODQzOTMzOTM4ODU4ODU4OzU4OzU1Nzo3ODswNzcsMjI3MjM4MzQtMzMvNTUz MzM0NDQyNDMyNDMyLzc0MTkoMTQmLzIqMi8oMC0tMTAtMTAuLyswMS0uMSwxNC8pMy4pMy4rMzAq Mi8wMjExMzIuMDEvMTI3NTwwLzUtOS88SD5MT2NUV2tVYW1QXGhcXFx0dHSChJGEh5SDh556fpV6 gox/h5FjaXBXXWRXV1dMTExDVV5Ya3VrfqV3ibB9hK93fqhhcI1jc5B6f55/hKOCibR7g66De6iL g7CAhqV7gJ9ucohydYxzgpSAkKKSma6QlquMkauWm7aSlKuXmbCIn7t+lbCQkq6XmraHjJtuc4JY UVFAOjooLSgeIx4jJSIiJCEgKyIfKiEkJCQnJyckJiMiJCEcJCEcJCEdJh0cJRwaJhcaJhcjJRkk JhonKR0hIxceJBggJhogJSAgJSAkJhsjJRofIRYeIBYnJhYlJBUkHhYmIBglIB4lIB4aJBYbJRcf IR4eIB0iHRsfGhgZHxQaIBUbHhcdIBkaIRcYHxYZHREaHhIbHxEZHQ8fGxAeGg8hHRQfGxIYGg8Y Gg8ZHQ8aHhAXGgsXGgsbHg8ZHA0VHA8TGg4WFg4ZGRAeGw4cGQwiHBIgGhAaHQwaHQwdHA4bGgwc FhYdFxcXGQ8XGQ8YGg8WGA4aFgwdGQ8eEgwkFxEZGgwbHA4aHw8WGwsZGAkZGAkZFgoaFwofFhIh GBQbGgoaGQkWFgsWFgsbFg8cFg8gGQshGgwZGAoYFwogGwUhHAZDRD9GR0NJQ0NHQEBEQUNEQUNB QUQ/P0E+Pkk+Pkk9PT9BQUREREQ9PT0/PT5BP0A9PkE7PD86Pj03Ozo8Pj07PTw4O0A3Oj81Oz81 Oz86PDs6PDs6QDw3PTk7OTw8Oj09O0c8OkY5OD45OD47OD07OD04PjwzOjgwOTUzPDkwNzcwNzcw Oj0xOz4vOjcvOjc4ODo6Ojw1OUA3OkEzNTQyNDMxNTcyNzgyNDMyNDMwLi0yMC8xNTIyNzMyNS4y NS4sLisvMS4oLC0nKywuLC0uLC0vNC0vNC0uMSwuMSwwMDA1NTUxLzAwLi8mLismLisnLiYoLycu MC0sLissLi0tLy4sLyouMSwoMCsoMCsrLzAqLi8wLCs0MC8tKywzMTIuMTcqLTI6R0VUYl9ebX5f bn9zfpJteIxzeYuGjJ6ElLJ6iad5i513iJqEjJqHjp1yfotjb3tkanJUWmFEVFdfcHR4gp94gp+C f41vbXppbXB1eX2SkaaQjqOOjKt+e5qIgpubla+OkqZ7f5J1eX9/g4mRkKebmrKmocKembqalKuq o7umobSnoraboriWnbOom7KvoriSlaJ3eYZhVkdGPC4sMSgmKyImJyMlJiIgJx8iKSEjKCEiJyAj Jh8hJB0YJR0ZJh4cJRwWHxYbJRceKBoiJBgiJBgoKBslJRgdIh0iJyIaHxocIRwgIx4iJSAhJRka HhMgJBgeIhYfHRQgHhUhIB0hIB0bGxkdHRsZHRoYHBkeHxkcHRcbHBUbHBUdGhYdGhYXGxAbHxQZ HRIZHRIYHBEcIBUiHw8fHAwiHhUgHBMZHA0ZHA0cHQ0eHw8bIRUUGQ4YGg4VFgoWFw4XGQ8VGAsW GQwcGQwcGQwiGw0eFwoXGAkXGAkYFQocGA4eFg8fFw8WGg0XGw4cHBAZGQ4bGAsfHA8eGw4bGAsW FwcWFwcXHAwWGwsVGQgVGQgcGggdGwkcFwkcFwkbHA4ZGgwWGQoXGgsSEwwVFg8bFg4bFg4WFQcX FgkiHAkjHQo7QDs7QDs9PT09PT0+Pj49PT09P0A9P0A3Pj84P0A3Pzw5QT45PEE1OT47Oz0+PkA+ PUQ7OkA6PDs4Ojk7Ozk8PDo9PDk8Ozg4P0MwODszOjoyOTk4Ojc3OTU5OTc4ODUzNz4zNz4yNT0y NT04Nz01NDszOTMwNTA0Nzg3OToyOTQyOTQxNzkxNzkzNTQzNTQxMzI0NzU0NTszNDowNDMwNDMt NC4xOTIyMjAxMS8vLy8xMTEqNDEqNDEuNDAvNTEsLScsLScrLSorLSoqLTArLjExMzIxMzIoLiop LysuLjAyMjQtMzMtMzMtLS8tLS8pLikqLyorLy4oLCsrKictLCksLyouMSwtLy4pKyopKygtLywt LywsLis1Li07MzItLS0zMzNESkhiaWd1eIh+gJGRkquVlq9/kaeElqyEgJZzb4R6gpB+hpSGf5aS jKOCf5Fwbn9tcHRlaW1cXWFpam57eo59e5B+eX10b3N0cnWHhIiWla6RkKiVjJaUi5WSlKuRkqqJ h5V9eoh5eIyCgJWNja+Wlrifobqhoruiobiop7+mpsOlpcKlor+fnbqoocOnn8KNl7B5g5tiXVND PjQpLyAhJxglJiImJyMmKSAiJRwfJxkhKRsjKCMiJyIdJyQZIyAgIR0eHxsnJB0oJR4kKBkhJRYj JBMkJRQlJxwiJBkgIxoeIRgcIxkcIxkiJSAcHxodIRYcIBUcHRccHRcZHxQYHhMcHBIcHBIgGRYk HRoiHxggHRYbHxQbHxQiHhYgHBQdHRMaGhAZHREbHxMbGxEdHRMgHQ0fHAwlIRgdGREZGhMeHxca Gw0cHQ8bHRMaHBIbFw8cGA8cFxQaFhIWGgoWGgoXHxMYIBQZFggeGwwdHg8WFgkWGQ4YHBAZExAa FBEWFQobGQ4hHRQgHBMfGREhGxMZHAsYGwoaGgcbGwgdHAweHQ0YGQoXGAkeHAocGggXGAgYGQkY Gg4ZGw8XHREXHREWGAgUFgcWFgoWFgoYFwoaGQsfGQghGwo7Oz09PT89P0A9P0A7OTo/PT46QEA3 PT0zPDk1Pjs4PDs5PTw4O0A3Oj84Ojs6PD08Pj89P0A7Pz44PDs5Nzg8Ojs8PDw6Ojo5OkE1Nz43 Nzk4ODo4Ojk1ODc5Ozg1ODQwOTguNzUxMzQxMzQ6Ojo5OTk4ODU0NDIyNzUxNTQxNTIxNTIuOTMu OTMwNDMxNTQsNzUsNzU0NDc1NTg3NDU1MzQtNCwsMysxMzAvMS4wMjMyNDUxNTIsMC0wMS0uLysr KykuLiwpKScvLy0xMTMzMzUyMjQuLjAoLCsqLi0jLSgmMCsuLyszNDAwMDArKysmLygnMCkqLism KictLiYtLiYvLicwLygnLSkoLionLS0nLS0uLi4vLy84MC89NTQ3MChAOjFEUFBea2uVg5aejJ+e qMKapb6Um7aLkqyEeH53anB1eIR+gI2Cd5GSh6J/fY51c4RvcnNrbm9oZWlua29zc4B6eoh1dYNw cH5vbX6Gg5WGjqOIkaabn6iXm6WHjq5+hqWDeX+DeX90cIZ0cIaHh6ORka6enbSop7+do7qhp76a oryUm7adma+VkaeRkbOWlriJkbB5gJ9hWl5FPkMsMSghJh0nKiEoKyInJh8oJyAdJxkgKhwaJiAW IhwgICIgICIgIB4dHRsnHyArIyQhKB4gJx0jIxYkJBcnKR4iJBkeJR8dJB4dJhsZIhcfIBwdHhol JRsjIxkeJRsaIRcZHxQbIRYhIxcgIhYrHhgsHxkoIBcmHhYYHhAaIBIeIBUbHRIgHBQfGxMgGhMh GxQiHRAhHA8fGxIiHhUjIBkeGxUbHhYaHRUWHg0WHw4kHQ4iGwwgGgknIQ8qHhAlGQwUFgoYGg4d Hg4cHQ0gGgkkHgwkIwocGwQpIxAnIQ8lGgosIRAzJREwIg8wIhUoGg4aFg8cGBAbHQobHQoeGgkj Hw0kGw4gFwoiFg4jFg8kHgwgGgkaFwoaFwoaFgoaFgobGgwaGQsSGAcTGQcYFgsZFwwfGgweGQsf GwocGAc5OD47OkA5Oj85Oj84OTw1Nzo0NTk4OTw5OTc3NzQuNzEwOTM0PD0xOToxODgvNTU6PDs4 OjkxNTc3Ozw8Ojs5Nzg7OTw5NzoxMDQ3NTo5Nzo6ODs4ODU5OTczOjUwNzI3NDU5Nzg6MzM5MjIy NzgxNTc1NTU1NTUyNzgxNTc1MzI1MzIyNzgwNDUzMzUzMzUzMzM5OTkzNTcyNDUzODcuMjEyMjIz MzMwMjEvMTAtLzAwMjMyMDMvLTAsLCwvLy8sLissLispKygrLSowMDIxMTMzMzUxMTMtMTIrLzAo LykpMCosMSouMywvLy8qKioqLCkrLSopLSooLCkpLCUoKyQqKyMuLycqLygqLygmMC8hKyotLDAt LDAyLTIwKzA4LSNHPDFKV1pvfX+OkayVl7OjqMmXnbyRkqqIiaF/eH11bnOAf4l/foh5dId+eYxt b31tb311cnhzb3Vza3p3b357dYB7dYB4dYdtantjYml+fYSMl56NmZ+do7iVm7CalaiZlKeQi4x+ eXp4bnSAd32ZkJemnaWjpbqlpruZnbOeoridnauOjp2OiYuGgIKDfo6Mh5eEiJtwdIdeVl9DO0Qu KyguKygmKBwjJRkrJiQqJSMkJR8lJiAfJB8fJB8lIyIkIiEiJRwhJBsiIx8mJyMhJBscHxYeIREg IxMkKyEgJx0eIRohJB0gIxodIBckIRwnJB8oJBsnIxohIhofIBgeIRgeIRgfIRYeIBYoIBcqIhkr HhYsHxYeHw8eHw8aHhMdIRYcIRoYHRYWFRQaGBcbHxMdIRUYHhMUGQ8XGQ8bHRInIBEpIhMtIQkw JAs7Jw9ELxZDMBRFMhZINRZKOBg6MhkzLBQyLRIuKQ8tIQoxJQ46KAs9Kw5JMgxMNA5ROxlVPhxf PiJcOx9NNB5BKhUxIxM0JhYsIAo0KBA5KBE3Jg8yKhclHQwkFw8oGxMcGQocGQoaGQoaGQoaFgwZ FgsZGQ4ZGQ4WFwcXGAgZFAobFgwaFwkbGAocFwkYFAY6OUE6OUE0OD00OD0zOz4wODs1Nzw3OD01 NDk1NDksNSwwOjAuPjsoODQpPDgoOzcxPDcwOzUzODQ3Ozg3NTo3NTo6Ojw5OTsyNDE1ODQ5OTk6 Ojo4ODg1NTUoOjMpOzQzOjgzOjg+ODw7NDkzMzU0NDcvMzQvMzQxMzQ1ODk3OToyNDUyMzkyMzkz MzUzMzU1NzI0NTEyNDEyNDEzNTQxMzIwLzgwLzg0MTcyLzQzLy43MjE4MzI4MzIqMC4pLy0nMTAn MTAqLyoqLyozMzMyMjIuLiwvLy0qLTAtMDM0LjA0LjApLicsMSotLzAsLi8tLzAuMDEsMDEpLS4n LS0mLCwpKiYtLiovLSMyMCYoMC0lLSolLC0qMTIrMzArMzA4NSs5NyxQVl1zeYCJjKqRlLKjnbSX kaiIjJKGiZCDgol/foaIjKKGiZ+DgI55d4RiZW5pbXVubnpwcH15dYuJhpuGfo55coJwcnVpam5z Z2iViImSn6qSn6qboriWnbOhm6+dl6uWjKGAd4t/d4CIf4mRlKWbnq+eorqXm7ORlKWXmquMjpt9 f4xqbWtoamlvbnhwb3ltaoZdW3VRTFRFP0cxLisvLCkpKCEnJh8dJyQfKSYhKCIhKCIdJSAfJyIn JiEnJiEgJBYhJRYdJRcfJxkjJBwjJBwiJhkgJBcgKhweKBogJBghJRkkJyAiJR4YHxceJR0lIxgj IRYjIBkjIBkhIhogIRkeIRgbHhYnHRYrIRopIxonIRggIBQjIxYeIhYZHRIbIxYaIhUdIxcdIxcc JhcbJRYaIBUYHhMcHQ0hIhEtIxs1KyNFMhRJNxdROBpdQyRkRCNnRiVqTiBuUSNkSihcQyFXPyJI MRZFLQxJMQ9ILxNUOhxbPRNaPBJdQBhoSiF0Ti11Ty5oTS1UOhxFMhZALhI7KAtGMhRXOiRVOCI+ NR4vJxEoIQolHggZFwMeHAcbGwUdHQcdHA4ZGAoaHBIZGxEbGQcfHQogGQsgGQsfGAohGgwgGQ0c Fgo6Ojo4ODg5NzU5NzUxPDkuOTUzOTsxNzktNDUsMzQrNTItODQxODMxODMvPzgsPDQzOjUzOjU1 OTM4OzU1OjsyNzg0OTg1OjkvOy8wPDAxNy83PDQ6Ozc4OTQnPTgoPjkwOj0sNTkxNTczODk1NTUy MjIyNzMxNTIyMjA0NDI0NzM0NzMxMjgzNDosNTcuODkwNSwvNCszNCwyMyszMjkyMTgxMjo0NT08 NDs0LTMzMjcxMDQ0ODAyNS4pMyUoMiQnMSwoMi0sLyguMSozMzEzMzEnLywqMi8qLisnKygvLS4z MTImMiwoNC4rLy4pLSwwMTcsLTItLy4tLy4sMjApLy0mKy0nLC4vLCcwLSgqMC4pLy0hMSYmNyss NS4sNS41Ny4zNCxFUVFvfX2AjJKDjpWSkJ6SkJ6HiZeHiZeIkJqJkZuQl7eJkbCEgpR0coNPVVdY XmFla39oboJ3c4uIhJ2GgJFybX1raW1ua29zd4yAhJqOkLaOkLaVlKyUkquVkK6Qi6iLeZ+Ab5V5 dY6Cfpd6g6h/iK6Gkp96h5R6eYN7eoR0cn9oZXNTVVRQU1FOU15RVmJPVl9HTlc8PUA4OTwuLyss LSkoKCYnJyUcJR4hKiMfKSQgKiUfKSYeKCUgJCEiJiMmJxckJRYdJhQfKBYkJhglJxkhKRsfJxkc KRYYJRIdJBEcIxAdJBEeJRIaIRkeJR0fJRYbIRMfJBEdIg8aIRcbIhgaIhYZIRUhHRImIhYhIxcc HhMaJBQaJBQfIxUeIhQaIQ8aIQ8eIw8gJREgJA8eIg0hIRUdHREkHBQmHhYvJhc0KxxKOSJRPyhq SiRkRR9qSSZwTyt9UCmIWzJ6VSxyTSVvTCRoRR5kQCJiPiBXOBldPR5bQRNcQxRqRRdwShx9UCmG WDB/WjBlQRtONRVIMBBGMBBMNRVfRB9fRB9KPCA8LhQsJBAjGwkVGQcXHAoYGgYaHAcZGgobHAwZ GxEZGxEZFwwXFgoXGAoZGgwYGgYZGwcfGwgfGwg3OjQ0ODI/ODQ8NDE1PDozOjgxODMwNzItOzIt OzIyOTkxODgxNDg1OTw1PDwyOTk5OjE4OTAzODc1OjkwPDwuOjoxOjcyOzg1PTQzOzIzOjgyOTc6 PTg5PDctPz8mODgnOTcqPDoxOTwvNzo1NTg0NDc0NTE1NzI1NTU0NDQzODQzODQvNTUvNTUuNDAw NzIvODQsNDE0NzgyNDUzMDU1Mjg1NTgwMDI6MzM4MTEuMTQtMDMsNzMsNzMtMCkuMSoqLi0sMC8q Li0rLy4uLysxMi4lMS0oNDAnMCkkLSYsLisuMC0oMC0qMi8sLi0wMjEtLDQpKDAtLDAtLDApLCUt MCkuLiwrKykoLiopLyspLS4pLS4jMi0iMSwsMSwqLyo8MjE/NTRRTlhybnlteH5lcHd9fY16eot6 eJR9epaHh6iGhqeGjLB6gKVwb3dlZGtKU0xMVE1aZHRdaHheYXBtb39tantiX3BkWmdqX21fbZFi b5R6e5J5epGCe4eCe4d1dIt0c4lvZHtwZX1lYndqZ3tfbn1kc4JpcHBdZGRdW09aV0xYVklNSj5N SjNGRC00PD08REU7RD46Qz0kNywhMykmLi0nLy4jKyYgKCMkKSQjKCMlJyYmKCcmKCclJyYeIx4f JB8nJRsoJhweJBgeJBghIxcjJRkeKBoaJBYdJxYdJxYmJRYmJRYiJRIfIg8aHhMeIhYiJBkgIhcj IhMgHxAbIhgYHxYWIRQXIxYWIBAZIxMYIQ8ZIhAdJBAdJBAcJA4ZIQsTHA8WIBIeIBUdHxQbGgod HAshHxMiIBQgIg4kJhE3KRVDNB9RQyhVRitvTCNvTCNzThl9VyGQWimRWyqHWy1+UyZ9UyB4Thx0 Sh94TiJzRix1SC5nSSBnSSB0SRx7UCKDUCiMWC+NXS5zRRlYNBVRLg9MMQlWOxBoSCJkRR9VORtH LBAxJhUlGgoQFwwPFgoYGQkYGQkUFwQWGQYWFwgbHAwaGA0aGA0XGAkaGwsWHgkUGwcbFwUeGgc0 OzsyOTk9OTg4MzI0OzkxODUuOTguOTgwPTcvPDUxODMxODMxNDw1OUA1ODk1ODk5OjM5OjM3Nzc5 OTkyOzgwOTUxOjcyOzg1ODQ3OTU3PzoyOzUzPDk1PjssOkEpNz4uOjouOjo0OTgxNTQ6ODs5Nzo3 Nzc3NzczOTMyODIuPDMrOTAwOTMtNTA1ODczNTQ0NTsxMjg3NTo1NDkxMzAxMzAuNzEsNC86MzU5 MjQuLzIxMjUqNDEqNDEwMDAxMTElLyomMCssMDEsMDEwLzMwLzMsNS4sNS4tNC4nLigvLikxMCsv LicxMCkvLSwvLSwuLTEtLDAtLjMqKzAsLScvMCotLS0vLy8pKygrLSopLysnLSknLy4oMC8yLyww LSo9LitAMS5TSU1kW15fW15hXF9pZ2hpZ2hoZXNoZXN0co10co13dYlqaX1YWFtNTU9RR0ZQRkVJ UFpFTFVIT01VXFpNTlRISU9ERERQUFBPW2RVYWpkXV9kXV9rW1xvXl9fW15bVlpXTUxaT05UT1BV UFFTXE1RW0xXSkpVSEhFSTs/RDVARC87Pio9OCc5MyMtLio4OTQ1OCstLyMlMiYnNCggLSUfLCQk KSQjKCMlJRolJRojJh8jJh8kJSElJiIeJR0fJh4iJCMkJiUeKCMZIx4pJRsrJx0nKx4hJRggJhci KBknJhQmJRMeJxMdJhIcIRwdIh0gIRkhIhoiHhUfGxIgHxohIBsbJBIcJRMZJA8ZJA8eHw8eHw8h Iw0gIgwiIBYfHRMaGxQbHBUhGxEhGxEdHREdHREdIA8dIA8aHgoiJhA+LQ1NOxhbQR9hRyRtSSR0 UCqAUCCIVyaSYSqRXymMXCaIWCOHVBmIVRqAUBiAUBh7Tyh6Tid0SiFuRRxzQR59SiZ/TSWLVy6N Wy93Rh1aOA5TMQlTNQtdPxNuRiVtRSRcPh5HKw4sIQ8lGgkVFxEUFhAWFw0WFw0TFgYVFwccGQwb GAsaFggdGAocFQ4iGhMYGg8WGA4hHA8hHA84OTw3ODs5ODIzMi00OTowNDU1NDk1NDkyOzUwOTMy OTQzOjU0Mzg3NTo0ODs1OTw0Ozk3PTs9Ozo5NzUvOjcuOTUtOjMtOjM0Nzg1ODk1OzM4PTUzODc0 OTgvNzwyOj83OD0xMjg6NDI6NDI7Nzg7Nzg6OT81NDstODIuOTMvOzMqNS4uNzEvODI0NDQ1NTUz NDgwMTQyNDMzNTQqMi8sNDEoNC4pNS8xMTEzMzMzMTIxLzAsMC0tMS4qMDAqMDAmMCsjLSguLzIs LTAsKTAxLjUwMS0rLCgpLy0qMC4sLyosLyosMCQrLyMqLSYqLSYmMC0mMC0pLy8mLCwtLywtLywu KCgvKSkuMSwuMSwqLSgnKiUsLCwvLy8zMCkzMCkzMC05NTI6PThHSkVOSlNRTlZQTkNYVkpjT09l UVFhWGJlXWdhWlpUTU1MRTtAOjA9NTQ/ODc8Pzg7Pjc6PC9ERjlAOTU+NzM1Ny48PTRAQzNJTDxO STxGQTRTPDBVPjJPPjVPPjVHOCxIOS0/Ny1MQzlJPy5ANyY9My48Mi0yNDMuMC8pNCsrNy0vLCcz MCsvLy8uLi4sMSgmKyIiLyUhLiQbJhsbJhsmKSIlKCEcIxkdJBobJRYaJBYeIx4cIRwbJhsdKB0l JSMhIR8gJiQfJSMwJSEwJSEnJh8jIhsfJBsiJx4fKRocJhcaKBYXJRQbIBsdIh0nIB0rJCEeHxka GxYcHxgeIRobJRYXIRMbIA8cIRAhHRIjHxQeIhYgJBcoJR4gHRYcIBUcIBUcGQwcGQwdGgobGAka GhAZGQ8bGAsmIxU+LA9QPR1jPxluSSJzTitzTit9TSd+TiiGVSCQXiiNXCSJWCGLUyKOViV9VyF6 VR+EUyR7Sh1zSSBlPRZjOxJuRRqCTCOLVCqIVit5SB9oOxFhNAxcMAxkOBJ0RSNwQSBXPBtDKQso HA8jFwoeFBAgFhIXEw8YFBATEwgXFwwaFg4bFw8cFAkeFgoZFwwcGg8WFg0XFw4gGQ0cFgo0OTg3 Ozo8Ozg6OTU3OTg1ODc0OTgxNTQ3OTU0NzM0NDQ1NTU4NDo4NDozOTswNTgtODIxPDc6Ojo0NDQz NTQ3OTgwNzQtMzEyMjQ0NDc1NzA5OjMyNDUzNTcvOT4uOD03NToxMDQzNDA1NzI1MzQ5Nzg3NTow LzM0NzgzNTczNzEzNzEqMCwtMy81NDE0MzAwMjEvMTAsMi4tMy8qMCwqMCwtMTAuMjExMS8zMzEw LyoyMSwxMzAxMzArMzAoMC0vLy0yMjArLzAmKisrLSwsLi0sLC4pKSspKygtLywnLiYmLSUnLCUn LCUlLiUlLiUkLiklLyooMSojLCUoLCkqLissKSQrKCMhLSkhLSkqLCAoKh4oMSooMSoyLywxLisu MjEuMjEsLzIzNzo6MTc9NDo/PDk9Ojc/PDk9OjdEPjxDPTtBOzI9Ny4xMh8rLBk1Mhg3Mxk5PSU5 PSU+OSdFPy0/NCo/NCo/NSZBOChAOhpGPx9HPihDOiROOyhOOyhOPipJOiZPNCxOMytEMyhEMyg5 MyIzLh08LSozJSIwKCswKCskKiYpLysmJyMvMCwvLicsKyQqKSIqKSIhJBshJBsgJyEgJyEkJiMj JSIgJx8fJh4iJR4kJyAkJCIjIyEiKR8hKB4fJxkgKBoiIiIkJCQyIx4yIx4oJyIjIh0eJRseJRsf IxYfIxYhKRseJhgdJBweJR0nHBooHRsdHxUZGxEaHxgcIRodHhgZGhUdHBYkIxwjHxYfGxMbHxQZ HRIcHRYaGxQYGg8WFw0YGwoYGwoaHQwWGQkpGhUxIhwpIQ8uJhRELA1WPRtlQRptSCBvSCJuRyF0 RRV7TBqJVBqRWyCJWCOHViGHVCmIVSqIWCyIWCyHVyt+TyRpPxdfNxBjPA9uRheDTSKJUyeHTCWE SSN9Rx94QxtqPxpyRiB9TCxyQSNkPyRTLxYuIRgjFg8eFBIcEhAWEgoWEwoTEwoUFAoaFgwYFQod Fg8bFA0YFwgZGAkaGgcbGwgiFgogFQg5Ozo4Ojk5OTs5OTs3NDU4NTc3OTg0NzUzODczODczNDo1 Nzw4OUA1Nz4zNTQ1ODc3ODE3ODE+Ojs6NTc1NDE5ODQwNzQuNDIuNDQxODg0ODI0ODIyNTkwMzcx NDovMjgzNDw1Nz4zNzE1OTM8OTU+Ozg5NzU1MzI8OTU8OTU3OjA1OS8zNTIyNDEyMjQyMjQvMzIw NDMqNDErNTIoMSopMisrMzApMS4tLy4wMjEsLygvMis1NTMzMzEyMjAvLy0xMTEyMjIoMSonMCku Li4sLCwwLC0yLi8tLiovMCwqMCQoLiIlLSooMC0lMDAmMTEtMCsrLikpMComLScrKyksLConLCUm KyQmLCgnLSklLickLSYmLiknLyovLjIuLTEnLjMpMDUsMy0qMSswNS4wNS4yMy8xMi4rKykpKScu LB8vLSAyLR8wKx0yOCM1OyZFPCZEOyVBPi5JRjVIRTFMSDREPi1MRjRcTj5XSTpTRC1WRzBVSjlX TTtVSjxRRzlPRzhORjdTRDFQQS9OPyNJOx9HPyM/OBxBMCtALyo+MCk1KCExKikwKSgnKR4sLiMr LCQnKCAlJxkmKBolLCIgJx0cJiEgKiUmKCclJyYmKyQjKCEjIyMnJychJB8kJyIkJx4lKB8mJhsn JxwoJyIkIx4qKB4oJhwiIxslJh4lJCEjIh8mIhgnIxkhJxohJxoiJyAiJyAhIRYgIBYdIA8bHg0V IhQUIRMaIBIYHhAfHRMjIRYiIBYiIBYhHhceGxUfHRMeHBIfHRMfHRMcHBMdHRQbHRMYGhAqHQ4z JhY+KxVJNR5hPhdvTCNvTCRvTCR3TSF1TCB3ShV/UxuIWCOGViGDVCaAUSR+UyZ9USV9ViZ9ViaE WCl7UCJnPBheNBJlOg1zRhZ7USF/VSSGVCuDUSlwSBdvRxZwTR9yTiB4TjJrQyhVOyhFLBoiHhYa Fg8dFAseFQwXFgQWFAMWFAcYFgkYFgsaGA0dFw8aFQ0XGQcYGgcXGQcbHQokGAwjFws3OTg1ODc8 PDw9PT06OT04Nzs5Mzk7NTs5OzgzNTI0NzU4Ojk1Nz41Nz40NDI5OTc1NzAzNC45MzE6NDItMisv NC0yODAyODAwOTMyOzUvOjQuOTM6PTU5PDRDOzVHPzpEREFBQT9KSTVJSDRHTD4/RDdEPC1IQDFQ RjVUSTlQTUlFQT46ODs6ODsvNDcvNDc5OzwwMjMmODEmODEsMi4rMS0sNC8sNC8pOTUoODQqMjEs NDMyMTUyMTUtLy4wMjEvMi0vMi0sMi4pLyssLyoqLSgyLi0yLi0wLywzMi8uMiYvMyclLywjLSop MDEqMTItLywqLCkqMScqMScuLykpKiQqLSYoKyQpLC8rLjEpMCooLykpLikmKyYnLy4oMC8mMi4o NDAuNS8rMiwjMisnNy8kMSkmMysvLRg0Mh00MBo4Mx01Mxo7OR9IRi9GRC1WSDtVRzpKTENWV05i X0VfXUNdVUFnXkpvYVBoWklvYkhvYkhtZEdyaUxuYUlrXkdfX1NfX1NpX0dkW0NjWzxaUTNYSTJY STJOPjJQQDROQDE/MiQvMRkwMhomLRglLBcnKR0qLCAjKR0dIxchKh8fKB0eKiYeKiYiJiMgJCEh KRwhKRwnJiEoJyInKx8hJRklKCEmKSIlJRsnJx0kJhsiJBklKB8lKB8iIx0kJR8kJR8lJiAnJB8k IRwiIxsmJx8mKh0iJhkhJB8fIh0aIQ4aIQ4aIw8ZIg8gJRIgJRIdIg8fJBAkIhYlIxYgIBYgIBYi HhMiHhMlIhQkIRMiHhMlIRYjIRUgHhIvIRE6KxpOMxFhRSBrRhhyTB11USN5VSZ+USp1SSN3Rxp+ TiCAUyN+UCFzTiVvSiJpRhloRRhzTiZzTiZ7SiF0RBtdOhlaNxZfOhVrRR5vUCNyUyV6VixzTyZj QxNtTBprUCdrUCduSSplQSNJNR44JQ8ZGhYWFxQbFAocFQsZFgccGQobGRAZFw8WFgcaGQofFw4i GhAeGwsaFwgZFwYjIQ4lIQ8kIA4yNzg0OTo0PDUzOzQ1Ojk1Ojk4Mjg/Oj86PD0zNTdBOTxAODs8 PDo8PDo/PDQ+OzM8PDo3NzQ+OTRAOzc1OTMzNzFIPUBKP0NAQT05OjUxOis4QDFXRztiUUVoWklk VkZYT0VdVEltX0plWERcV0BcV0BzXEF4YUZwW09tV0xjU09TQz9BPT4/OzwyOTQxODM4OjcvMS4l NzQkNTMxMzIuMC8wMzkxNDopOzcoOjUoMCsrMy4yMzkuLzQpMS4pMS4uMjEvMzIqMTQqMTQnMjQn MjQvMTIvMTIvLCcxLikwNCguMiYpLy8qMDAvMDUsLTIoLTEoLTEnLyomLikrLSotLywuLi4sLCwh LSkiLiosLygrLicqLSYnKiMpMDMmLTAqLC0tLzArLicrLicuMSoxNC0xNCAvMh46NR9IRCxKRzdN STlPTTVTUDlnVkBnVkBuZVFuZVFoal1ucGN5d1t9el57cmSDeWuId3WId3WLf2+EeWmGeF+Je2OO e2qQfWuNf2eJe2OGd2iDdGV/d2VzalpyZEpyZEpqXExoWklWU0FMSDhNTDhOTTk7QC01OygyLx0v LBouLB8mJBcaIhUgKBocJR4eJyAZKiIYKSEcKR0gLSEoJyInJiEnJxwmJhslJiAoKSMjJiEjJiEg JiIfJSEhJh8gJR4mJyMmJyMeIxobIBclJiIjJCAhJBsiJRweJR8eJR8hISEeHh4ZIA8cIxIfJhUe JRQcIhQdIxUYIQ4ZIg8bHBUdHhYcHRcbHBYhGxQjHRYlHxUhGxEgGxYjHhgjHRUjHRUvIg81KBRO MxRcQB9lRyVtTit1TyxyTClySSptRSZzRCJ3RyVuSSRtSCNdQxhYPhVKNSBNOCJePSFnRShcOBdP LA5AKhE/KRBNMhJaPhxfQBtdPhlfQCdVNx5OMBVYOh1TPBpROxlTNR5ILBYwKBIqIg0fHBcbGBQe FhMgFxUdFgwdFgweGA8eGA8dGg0dGg0hGQ8hGQ8eHAocGggcGQodGgshHA8fGg44OTw6Oz43QDc4 QThDRzpESDtEQTVKSDxGRDVIRjhbTERaSkNXUT5XUT5bVDtWTzdVUENTTkBbTT1QQzNOPTdaSEFq TUlnSUZUSj5PRjpKRzdYVUR1Xkp7ZFB0a05rY0ZtXUNrXEFvXEBwXUF1Y0B3ZEGMbUqJakh1Y1Nw Xk5iVk1WSkFHRDw+OzM6PTU4OzMzODcyNzUpOzcmODMwMi8vMS4zMjkxMDc1Nzo3ODssMC0uMi8w NDUuMjMiMjAlNTMzMzEyMjAlLi8oMTIrMjUrMjUzMjcxMDQrLywmKicsMSwsMSwtLS8vLzEnLDAr MDQkLy8lMDAoLS8mKy0pKyorLSwoLSYpLiciMSofLicqLisoLCklLCQlLCQlLSokLCknKCApKiIs LiAsLiBAMyRAMyRFPidHQClPTjpTUT1XVEBdWkZnYkpzblZ3bVZ/dV6CeWWLgm6HgHmJg3uJg36Q iYSRg4OUhoaihoani4uWjoCUjH6hjX+hjX+lkIankoiikoCejn2ViYORhn+SgnmRgHiMg3KJgG+E em97cmd0bV9rZFdvX0lnV0FTTzxMSDVKQzNAOSo1MBUwKxAqKh8jIxggIx4dIBsbJSQbJSQlKCEo KyQmKSAkJx4mJSAjIh0lJiAlJiAeJCIiKCYmJiQlJSMlJiIlJiImKh4lKR0gJyEgJyEkJCInJyUd JSAfJyIZJBsaJRwkIB8mIiEgJBYeIhQjJh8jJh8SJBQSJBQXIBcVHRUbHxMbHxMZHQ8ZHQ8eHBMf HRQkIxIiIRAeHxkhIhweHxkbHBYhIRYoKB1DLRpKNCFWORhfQSBjRSRfQSFVOiJTOCBaNRtcOB1Y OxpXOhlNNRdFLhE1JQ06KRBGLxJJMhU0KhMxJxAwIw8tIA06KA9ALhRKLg9JLQ9JKxZDJRA4Kw84 Kw8xKhAyKxE0JhIvIQ4fHA0cGQoaFQ0YEwsYFQoZFgseFQ8gFhAkGQokGQocGA8ZFg0aGg8WFgsY GwwYGwwZFgsYFQojGxEhGQ9URT5XSEFXTkVVTENhUTpkVT1hVTppXUFkWDllWjp1Xkp1XkprWD1w XUFyXD10Xj91X0V1X0V3XTtvVjR4XD19YUF4X0l1XUdlWEFdUDphUDlvXkZ/ZE1/ZE13ZEF0Yj9w XjpnVTFvWjd/aUWDbkl/akaIbkGEaj55ZEN4Y0FqW0dbTDlKRDo+OC43Nzc6Ojo1ODcyNDMoMTIp MjMrNy8pNC0uLzQyMzktNDUsMzQ0MS40MS4vMTAvMTAqMi8qMi8xMzQxMzQqMCwqMCwyMC8zMTAu MjMsMDErLikuMSwrNC0sNS4tLzArLS4mLTAoLzIiLiojLysvMTAsLi0nLSsmLCooLSgpLiklMS8i LiwoLC0lKSokLCchKSQjJxomKh0rJxQvKxc5Lx9ANyZHOyRNQClVTjhcVT5nXEppXk11blZ4cFh0 b2GGgHKLg3mOh32VhnedjX6Zi4ajlZCalpKfm5enlI2qlpCrlZKym5mmn5qlnpmqm5mrnZqqm5uo mpqjn5uhnZmmmpSlmZKhkISjkoefl42XkIaRjYeNiYOOh32NhnuIeWiDdGNtaFxeWk5dVklVTkFQ QSdGOB4xKxcqJBEiIBYkIhcdJB4ZIBoeJBgeJBgjJyQiJiMiJCMgIiEkJyIjJiEcIx0eJR8lJCEn JiMnJSQnJSQmJSAlJB8kKyMeJR0jIxkmJhwkJhskJhseJhkdJRgjIBkmIxwkJxUdIA8eIxwgJR4S JBgRIxcWIBcSHBQaHhIaHhIdIA8bHg0aHBIaHBIaHQweIQ8cHxYdIBcaIh0WHhkWGw8XHREzJBY8 LB5HMRFIMhJBMA8+LQ07KBI9KhRHKQpHKQpMKgxIJwo7KBQyIA0lGwQtIwozKQc0KggjJRAeIAwl HQwpIQ8yIwg0JQo7KBA0IgszIw80JA8sJQouJwwsJQwoIQkkHwgjHgcgGQobFQcWFgYTEgMPEQMT FgYeFg8dFg4bGAoZFggTFQkVFgoaFwkaFwkbFw0bFw0XGQ0VFgofGg4cFwttUDt7XkhwXEluWkdz XUBzXUB/ZD6IbUaDbkOEb0SLaFCAXkd0VTGDYz6IYT6RaUaUckeScEaOaTiOaTiObj2SckCHaUR/ Yj1rVTtnUDdtUzp4XUSDZ0eCZUZzXjhwXDVyWjRzWzWMaUOWc0yObkiHZ0GOZTuWbUGMck6Eakdt Xk1aTDtKQz1AOTMtMTAxNTQyMDE0MjMmLzQoMTcyNDEwMi8xMjUxMjUvMzIvMzI3MjE3MjEyMjIy MjIuLyswMS0vMTAtLy4rMS8nLSsuLi4xMTEwMDAuLi4qMSkrMiohMCsjMi0vMTAqLCsoLCsrLy4p MS4qMi8zNDAxMi4pLy0lKykuMC8nKSglKzMlKzMmLCgjKSUcKhodKxssLBUwMBg5MyNAOypEQzFP TjxaUDtjWkRyalN3b1eDcmGRf26MhnSQiXiSi4CXkIaZlomal4umm42nnY6rmpKwn5eoop2wqqWz opqzopq7paK6o6G7paK4op+ypqKwpaG7qKW7qKWypaaypaa7pp+4o522oZ6znpuwnp+wnp+um5qn lZSmnZabkoyejYafjoeGgn6Cfnp/dWp3bWJlYVNhXE5VUT0/PCksLhUkJg4YIBQVHBAUHBYWHhcc JCEhKSYgJiQhJyUiJyAkKSIpKR8oKB4kJR8lJiAnJCEpJiMlJiAlJiAiKR8cIxkhIRYlJRolJh4k JR0jJh8kJyAgHhIfHREfHwsfHwseJhYaIhMTIxQTIxQbIRYYHhMbHxMbHxMcHw8cHw8bHxEaHhAZ Gw8cHhEYGwodIA8iIxQoKRkmKxYfJBA4Kxw5LB07NRc1MBM8MBQ8MBQ3JA07KBA/LxI8LA89LRA8 LA84Kx01KRs0LRUxKhIsJgg0Lg8wLR4vLB05MSAzLBs4LBo9MR88LxE4Kw45KxM9LxY9KhY/LBcu Lg0rKwovKRYyLBg5JBknFAocEgMcEgMVFgYTFAQcFwocFwoaFwkcGQobGgwXFgkZFQcaFgcdFxAe GBEZGw8XGQ0gHQ8eGw5+YkWGaUyDbUqDbUqNb0iSdE2UdEiXeEyVdUaOb0CGYz9/XTqEXTCbc0Sm dEOlc0GifUOadTyZd0iUckSbc0GedUSQaUaEXjxoUTVkTjJqVTtyXEF3YT13YT1wXDRwXDSAYTyG ZUCVb0aZc0mVa0CVa0Cdb0Wdb0WNbk6CY0RrYVFQRjhDQ0M3NzcwNzIxODMxMTExMTEtMDUsLzQs MTUqLzMqMTIpMDEsNC8sNC8yLi05NDM7Nzg5NDUyNTAuMSwlMSslMSssMi4mLCgqLisrLywrLCYr LCYrMCkqLyglMSskMCopMCoqMSsoMyomMSgrMCcsMSgnMSwjLSgoLyknLigrLy4oLCsnLDAnLDAq LSQkJx4lKxIwNxxAPCdDPilMQTBaTz1aUT5pYU1qZFF0bluEe2qJgG+QhnqUiX6fkIChkYKjnpKl n5Ssn5WuoZa0o5q4p56yp5uyp5u0p6izpqe6q6u8rq67rKq7rKq7rKq6q6i8rqu7rKq/rqXDsqi6 q6u8rq67rqW6rKPBqK6+pqu+qrC7p666r6O2q5+vq6ejn5uqmZGrmpKhloydkoiRjYSJhn2CenB5 cmhrbVpbXElXTzM+Nx0eJRIVGwoSHQ8UHxEiKyIeJx4lJiAmJyEnKiMkJyAeJBgfJRkjJRkkJhoh JxshJxsjJRokJhsiJx4cIRgbJB0bJB0bIx4ZIRwiIB8kIiEgIhcfIRYfIRQeIBMdHRQcHBMbIRYd IxchIh4cHRkbHhYdIBckIxQjIhMYHhAWHA8UGQ8YHhMgIxAkJxQrLxgrLxg1NyM5OiZFOypANyZH QCdDPCNEQyRBQCJIPi5GPCxMQTFHPS1IPClIPClEPTFEPTFFOi9FOi9EPi9HQTJDPTlHQT1ORTlM QzdGQDxFPztPQCpXSDFURTBRQy5XPzJbQzVWTDpQRjROREBJPzxEOzJDOjE6NCMvKhksJg8jHQgh GwgfGQciHAkhGwgYGA0XFwwUFgcWGAgXEgocFg8aGhAXFw4fGxMcGBCOa1GVcleVdE6SckyefUaj gkqif1Gee06feEyWb0SMZziOaTqhbkWve1Gwf0iufUaqe0ioekefekWmgEqsf0Wjdz2aalCJW0Fj TThfSTRkV0NlWERwVzhyWDlzWDN1WzV/YjyDZT+NaDeRazqRaTmackCXc0ORbT2IaER9XTppWExX RztERUo6O0AwOzosNzUwMjMxMzQ1ODk0NzgxMjUwMTQmNDMkMjEqNC8oMi0zMTI0MjMzMzMzMzMs NS4rNC0iMSomNS4uNzMpMS4sMSosMSosLCosLCouLjAuLjAwNDMrLy4pMikqMyokLyYhLCMqLyos MSwqLyYoLSQuLSgrKiUrLywqListKywqKCksKh0xLyI0Mxg+PSFWRz9dTkZiXEhqZFB3Z0yDc1d6 eGiGg3OQiYSXkYyekI2fkY6qlZesl5qqoZqwp6G2o5+6p6O/rK6+q6y6rqy8sK/Bqq+7paq/rqbB r6e7r6i/s6y+sLK/srPBrqzCr67BtLDCtrK+sLS+sLTDsK/DsK/Bq6jCrKq+srC8sK+7s6e4sKW2 sLK0r7C6p6O3paG3oaiym6OhmpealJGRkImHhn+HhHd6eGpwaldcVkRARi8pLhkfIw4cIAsdJRgf JxokKCUjJyQnJykkJCYiJSglKCsmKSQmKSQYKB0WJRojISAmJCMlJSchISMXHR0YHh4gIiEgIiEm IiEkIB8pJRwkIBcfIBggIRkiHhYhHRUbHRogIh8hJh8ZHhcfIR4gIh8dHRMfHxUTHBcSGxYVHhEY IhUtKyAzMSYwNydARzdPSkBMRz1bSEFeTEVeU0ldUUhTVkBUV0FjYVFcWkpdWE1bVkprWlNuXFVf WE5hWk9pWFVtXFhjXE9jXE9pV1BrWlNpV0htW0xpW0ppW0ppXEVuYUltXE9vXlF4ZVh1Y1ZvXlFy YVRvYVNzZFZqXlVlWlBYVU9NSUQ9PiovMB0uKBMlHwsiGw0kHQ8eGg8dGQ8VFwcXGgkdGAoiHQ8c FwsbFgoiGQ4iGQ6Wbkyle1ifekWbd0Grfz6ugkCsglaoflOld0iecEOWb0aac0mib0mndE6qd0Ss eUasek2vfU+sgEawhEmuhFGmfUqXalaEWEVnUUZeST5iUz9kVUF0VT14WEB/YUF9Xj9/YUGAYkOM YTSWaj2ZdDmZdDmXc0OWckGIZ0Z+XT1tXEhdTTpDQUY5ODwuODsrNDgtNDgtNDgwNTgzOTszMjcz MjcqNzIoNDAtNTArMy4vMTAuMC8uMjMuMjMrMy4uNzE0ODIwMy4uNS8mLScsLCwzMzMwMDAuLi4u Li4uLi4xMS8vLy0oLSgrMCsqMCwlKycnMSwnMSwxMiowMSk0LSgzLCcyMy0tLigpJxwsKh80MyFA PyxOTTlRUDx5Xkd9Ykp+bleDc1yQf2qbi3WXjIijl5SjnZqoop+mnpKuppq0oZq4pZ66p6O8qqbB qqrFrq6+rrC/r7K+r6/BsrLLsqzGrKfBsq/Cs7DBsrK/sLDGsLDKtLTJtrfHtLbGt7S+r6zBsrLB srLHsLPJsrTDsKzDsKy8s6y8s6y/sqjBs6q+r6++r6/CrKrBq6i/q6+7p6uzp6Gzp6GqpZeln5Kh mY6VjYOIgn16dG9iZU9WWkQ7Oh4nJg0iKBsfJRgiKCQjKSUmKCkkJicjJSQoKikqKyMmJx8YKiAW KB4jJB4lJiApKCwmJSkfIyAfIyApIyMmICAnIB8pIiEnIRklHxcgIhYeIBUdIBceIRghJiEhJiEh JSQgJCMhHx4hHx4dJRgdJRgaJRoXIhcgIhUlJxk3MSNBPC1DSTtQV0hlWFRuYVx9aWd+amh5bmh9 cmtwc2Rtb2F0clh0clh5cF14b1x6cmF6cmGAeWt5cmR/c2mAdGp6cGV7cmeLd3CEcGqEcmSLeGqQ emSNeGKIeG2IeG2HdG2HdG2IdWWMeWmMcGmOc2uLd3eQe3uEc3R7amt6b2lzaGJdXlVJSkE7QCoq LxobHgwbHgwcFg0fGQ8VGgMXHQUjHg8nIhIjHA0jHA0hGgsfGAqWbUOedEmleT+ofUOygEm3hk6z g1usfVWic0mbbUSUakOSaUGZZz6hbkWnckSqdEasfkqwgk6vgkyvgkyrfUyld0aUalV9VUBjTTxe SDhoUTVlTzN0UTh9Wj+EXEV+Vj+DXDuHXz6UZEGebkqheUObdD6eckGabj6Ga02AZ0hqW0dYSTdD Pj1APDs1OzUwNTAnNzEnNzEqNTgqNTgzMzEzMzEtNCosMykrNTIrNTIwMDAyMjIyNDMxMzIrMzAp MS41OTEvMispMSwnLyovMTItLzApMS4pMS4vLzEqKiwuLi4tLS0jLCUnMCkoMSooMSomMiwkMCou MSwvMi0zMi0xMCsxLikwLSgsJSA1LilAPy5RUD5iXkloZE+DbluIc1+Xf3qfh4KhlZGqnpqompqw oqKwpaO2qqiyrJ+vqp28qKG+qqK/qqfBq6jFrqvGr6y8s6++tLDCr7DGs7TJtK7JtK7CtrLBtLDG s7TDsLLLsK/Os7LGt7TGt7TJurfDtLLHsrTFr7LLuLrHtLbDtK/FtrC+t7S8trPDtK/DtK++r6rC s67Cr67Cr66+r6y+r6y8qqi7qKe8qJq/q52noZmlnpaqnZ6ZjI2Efm11b15PTjw/Pi0oMR4dJhQh KCIkKyUnKyolKSgmKCcmKCcoLB8lKRwbKB4bKB4hJxsfJRkoJCMnIyIjJBwjJBwnIxkmIhghIyAf IR4iHhUjHxYcIxIdJBMeIBUfIRYlJB8lJB8jIh0kIx4iIx0fIBoaIBUcIhYeIhYiJhklKBcmKRg6 OSVKSTRUVUxnaF54cGeCenCMfnuQgn+UhoaUhoaJiHSHhnKMhHeOh3mag3OWf2+Ng3WSiHqMhnKO iHSOiX6OiX6Rf3uUgn6VgoaOe3+Uf3WUf3WVgH6VgH6Uf4KVgIOagnuehn+aiHOaiHOffXihfnmX g32bh4CUgoCRf36Vf32Eb216bmVqXlZNTkc4OTIoJRQiHw8bFw8gHBQZHQcXGwYaGQkhIA8fHQoe HAodGQ8bFw2VaTydcEOqfUOugEazhk2zhk2ygFyreladb0aUZz6Raj+QaT6RZTmdcEOqd02uelCu gEivgkmqfU6nekyreE6lckiMaE15VjxfSTRfSTRiTzRiTzRyUDp3VT54VUNzUD6EVzmSZEWOZ0SS akeVb0OMZzuSaUGMYzx5ZUluWz9cUUNTSDo4Pjw3PTszODQwNDEwMi8wMi8sMjIuNDQuODArNC0v NzArMiwyNzMyNzMwMS0zNDA1ODcyNDMwMjMuMDElKysoLi4qMCwpLysnLywlLSonMS4kLisqKioq KiooKy4nKi0oLiwpLy0lMCknMisnMCkoMSosMSwpLikyNyoqLiItKx4wLiE4Lh4+NCRYRz5qWE9u aFV+eGSSgnmXh36skIuzlpGrnZ2woqK2o6K2o6K0p6i7rq+8s7K2rKvFsKjFsKjBtLC+sq7Cs7C/ sK7Cs67Cs67Cs7DHuLbFtrDHuLPGurPDt7DJs7PHsrLLtrjKtLfJtrLJtrLHuLPDtK/FsLTHs7fJ vLvGurjBs6rGuK/Dt7PDt7PGurPFuLLBtK7Dt7C+tLDBt7PCr67FsrDGr6/Frq7FrJ/Hr6K4r66y qKe8oqa0mp6ekoyLf3lkW1BWTUMxOSYjKhgiKiUjKyYjKiIkKyMuJh4rIxspJxwpJxwnKR0iJBgl JxwiJBkiIRolJB0kJhsjJRokIhgiIBYhJB8bHhkmJBclIxYeJBYcIhQWIBYWIBYlIh0jIBshIRUk JBcmIBclHxYhJRkcIBUYIBMaIhUdIg8iJxNAPC9RTT9cV01wa2F+eHOHgHuRg4CWiIafjJChjZGl lIiikYabi3+fjoOrjoujh4OjjH+nkIOXkIKXkIKei4ChjYOfi4CdiH6eh46dho2eiYOfi4SZjYmZ jYmehIajiYuiiYSji4afh36dhHuignumhn+li3+mjICih3uhhnqih3mVem2QeWqAalxjWk9JQDcv KBcoIREaFg4hHRQZGgoWFgcWFwgbHAwfGAoeFwohHA4dGAqhckere1CshE2qgkqugE+ugE+je1GZ ckiMZz6HYjqNZDuXbkSdb0WidEmfeU2adEibdEebdEeVbkqVbkqOZ0WGXj1vWEBjTTVPPzFVRTda TTlbTjpoTT1oTT1lST5pTUFtVjxzXEFyXjdyXjd6XDt4Wjl6WER3VUBlTzpaRC9HQzg1MScpLy0s MjAtMS4yNzMzMzEvLy0uNzUtNTQuOC4tNy0zMzEuLiwyNzUzODc1OTMvMi0yNzUsMC8tLDAxMDQv LS4xLzAsMC8pLSwpMCorMiwjLyklMSsuMC0rLSotMTIuMjMrMS8rMS8zNCwuLycnLSkqMCwsNDEq Mi8zNSkrLSElLBoqMR9AMxxQQypfVURzaFaCfXKQi3+dlI2mnZavopW0p5qup6Wyq6i6q6i+r6yz q6u6srK6r7C6r7DDrq7Drq7Ju7zCtLbGurjDt7bCuLLDurPDurbDurbDurjFu7rGurbKvrrNsLfR tLvNtrvHsLbGsqrGsqrKt7PLuLTHs7fHs7fFu7fGvLjLurLNu7O/trK/trLBvLO/u7K/u7K+urDB t7PBt7PBsLPDs7bDrq7KtLTGs6/BrqrCrrS8qK+6paW7pqavoqOajY5+emhhXUxBRDgxMygkJyol KCshKRwjKx4jIxkjIxknJxwoKB0nKCQkJSEoJR4oJR4mJBklIxghIhwjJB4hHRwgHBsdIh0ZHhkj IxYlJRgcJBYXHxIdIx8aIBweIBYjJRokIhYmJBcmHxwlHhsYIRgYIRgjLCMeJx4fIxUoLB1KPzhb T0dtY1h7cmeIenWShH+ejYamlY2hlJejlpqhl5SimZWil42jmY6rlZWmkJCqkYurkoynkpColJGl lIiikYanlIanlIaojpCojpCokIuqkYymjYirko2ojYmmi4edh4SeiIaejIuhjo2ijYahjISei4Sf jIaljIOji4KhiXmhiXmahHCIc191Y1RYRzk4Lh8wJxgiGhMiGhMYGA0WFgsiHBIjHRMeGQscFwop Hg4mGwufeEqje06jfUyadESZckaXcEWNZ0WHYT+CYj59XTqEYUGMaEiSakWOZ0GHZz+EZD2OZESI Xj59WEB4VDxyUDhvTjVVRjFOPys/OzBBPTJMPDBNPTFQQDJQQDJKQDJRRzlXTjdYTzheSitjTy9f RixbQShkRTRhQTFKQC9DOSg4OSUzNCEsMy0sMy0wMSs0NS8zNDAyMy8wNDUxNTcvMzQwNDUvNDct MjQuNTsuNTsyNDMxMzIuNDQrMTElNC8lNC8rMS8qMC4pLSwpLSwsLyYuMSggMCUeLiMgMSseLykq MDAtMzMoMi0mMCsqMSkpMCgrLy4rLy4jMi8hMC0qMScoLyUnLhwlLBpBMiFURDFcVUh4cGOMg3+Z kIyil5uqn6OzqJ63rKK4qqe6q6i4rKa/s6zBsrLBsrK8s6++tLDKt7jHtLbHs7fKtrrGu7zCt7jH uLPJurTCuLTDurbDurbFu7fKt7jPvL7Ps7rStrzHsLDBqqq4rqO7sKbGsLDHsrLKurzJuLvGurPJ vLbJurTKu7bJurrHuLjHu7TGurPGvLbFu7TGurbFuLTJuL7Ds7jHsrLKtLTFr6/Fr6/GrrPGrrPC rKy7pqavpaijmZ2MhnBvaVVMSjc5OCUjLicgKyQbKyAdLSIhKCAiKSEsKiApJx0kJyIjJiEkJCIk JCIgIRseHxkhIR8hIR8fHR4eHB0aIB4YHhwcIBQgJBcaIhUXHxIdIhkdIhkeHQ0lJBMkIhcfHRMg Hh8jISIeHhQfHxUpKSkjIyMiJhcqLh9KPzddUUhvZVt+dGmUf3WdiH6hjounlZGmmpajl5SnlZGr mZWml5eml5ermZemlJKmlJKrmZerlpaqlZWnlo2mlYyqlY6nkoynjYyulJKolIylkIiskY2uko6q kJSojpKhjISijYaijoiijoilkImdiIKdi4mfjYyeiIaeiIaijn6ei3qdh3OIc196YlRkTT8+NCcu JRggGBEgGBEfHAweGwseGhEcGA8gFhAgFhAlHwsiHAmMak6IZ0qLZTuIYzmJYj1+VzNzVTluUDRu US5oTCl0Vz11WD57XUB4Wj1vUTVtTzNvTzRnRy1iRyxdQyhNOilMOShDMhU8LA80MBw4Mx9DNSY/ MiM7ORg6OBc9Oig+OylJPCRFOCBHOR1KPCBIOxtFOBhJNRhHMxY3NxwyMhgxMh8uLxwrMSUpLyMs MSouMywzMTA0MjEtMTAyNzUrMjMrMjMqNzQpNTMqNDMrNTQvMisxNC0yODItMi0rMzArMzAoMSgm LyYqKigsLCosLyYsLyYoMSgkLSQgMSsjNC4nMS4qNDEoLycpMCgkMComMiwmMCslLyojMyofLyYk LSYjLCUrKR8zMSdAMyVYSjtnZFiAfnKajIyml5eoop+spqO4rKi6rqq+sKe+sKe6sKq+tK7Ct6y7 sKbCtq/BtK7Ht7rGtrjGtrjKurzHvL7Gu7zKu7jKu7jJurfKu7jFu7TFu7TGuLzLvsLOuLvKtLe8 qqaum5eonZmuop67pqPDrqvJvLvGurjLuLTKt7PJurfKu7jJv77Jv77NurbKt7PKvrfJvLbGvLvD urjLuLfJtrTLtLLKs7DDr6fKtq7Gs7LDsK/Bsq+/sK64qqWvoZuUjXt1b15eVkNAOScqLyYhJh0f LR0cKhocJhYdJxYnKR4lJxwlKBclKBckJx4lKB8hKiMbJB0kJR8kJR8aJCEWIB0TJBoTJBocIBQf IxYgJRQeIxIiJBkeIBYfHRMjIRYkIhgfHRQgHxwiIR4hHRQiHhUrHhYsHxYdIA4nKhZKQC9fVUNz ZVCEd2GZgHihiH+hlIejlomjmpajmparmZWum5esmZ+mkpmml5KilI6ilYymmZCmlJKmlJKnlouq mY2ll46hlIumkZGnkpKnlIOlkYCljIOljIOfi4SijYeji4aji4ajjoehjISZhHqWgniXfXiZfnmW g3WUgHOehnCiiXSdhnWSe2uCZVVtUUFHOCQxIxEfHA8dGg0dHAUeHQYcGwsZGAkZGAkZGAkfGgse GQpwTUZoRT5uSjVqRzJiRjFcQCxRPilKOCNVPR5UPB1WPytWPytVPylXQStYPCJWOiBQPR9JNxlI NRpFMhc8MiI5Lx89Kg8/LBA1MBM1MBM3MhM4MxQ+OBg/ORlDNRZJPBxKNx1HMxpEOCNEOCNEOBhI PBxGNBxEMho5OiY0NSIuMiUoLB8pLSEqLiIpLicrMCk0MjM3NDUwNDMuMjEsMjAuNDItOjgpNTMs NzUqNDMrMS8sMjAoMjEsNzUwMjMuMDEsMC0sMC0wMS0wMS0tLigvMColLicmLygoLycoLycnMS4q NDEuLSovLisoLS8pLjAnLSsmLCosMi4rMS0sLC4sLC4yJyc3KytDPCNWTzRqaluHh3emmpSvo52z qqO3rqe/tKq6r6XBtLDBtLC+t7S/uLa/tq+/tq/CuLTDurbDuLzDuLzKvMHJu7/JvLjNwbzPv8LN vL/LvLzNvr7Gv7zGv7zHvMHGu7/Gtru3p6yim4mRi3mbjH2jlISvmpS8p6G8sKzDt7PNwbrLv7jK u7bOv7rJvr/FurvNwb/KvrzKvrzJvLvJvLjGurbFuLTGurbGtrjDs7bFtrbHuLjDtLLBsq/CtrK/ s6/Drqu2oZ6RkId3dW1jWlBHPjUxNSgqLiEkKSAkKSAmKyQeIxwkJyAkJyAlKRwjJxoiJhciJhcb Jh8iLSYfIh0bHhkdJh0eJx4WKBwWJxshJRkfIxcgIBYiIhceJBYfJRYcJBYbIxYkIx4gHxohIB0i IR4jIh0hIBsmHw8nIBAkIR4wLSpJRzxcWk5uZVOEe2iWhHSfjX2olYuolYuonZuonZuqmp2unqGw lpeulJWjjo6bh4eajYSekYifjoOikYaqlZWqlZWjlZCjlZCeiYebh4SfhoebgoOUf3WRfXOQfnCS gHOZgHqVfXeWfniVfXeMeHWNeXeRdGGMb1yRc1qae2KagGmfhm6bgnSWfW+Ea1VqUz1FNx05KxMd HxUWGA8WFwsYGg4dHAwaGQoWGQoZHA0dHA4dHA5QPSxJNyZGOCBGOCBFLhFHMBNGORtFOBpKOBhJ NxdOOhxOOhxHOR1DNBlEMxdGNRlFOBxFOBxANCI/MyE7Nx49OSBGOCJFNyE8ORo7OBk8ORpDPyBU PixVPy1WPy1dRjNNPy9OQDBKRTVKRTVPQSlTRSxYSTdRQzBNRjBGPyoxNyEnLBcjKx4lLSAtLigu LykyMjI3NzcyOTQxODMyMjI0NDQxNTcvMzQtMjQsMTMxMjU0NTkoMzgqNTotNDgsMzcsMjIsMjIw My4wMy4uLykuLykpMCooLykpMCgpMCgoMi8nMS4pLSwsMC8uLi4xMTEwMDAuLi4wMywuMSoxMS8y MjA3LR43LR4/PRxRTyxoa1eDh3KlnZKzq6G4qqe/sK7Htq7Fs6u/trK+tLDBurfCu7i/trTCuLfG vLbGvLbGvsHFvL/Kv8PHvMHKvrzNwb/VwcXUv8PNvL/OvsHJvr/HvL7OwcXGuLy6rqemmpSejnKX iGuViXWZjXmjkouyoZm3paHFsq7Lvr/Lvr/Ov7/Nvr7NvL/NvL/NvL/NvL/Ju7zJu7zJvsLFur7K v8HFurvDu76/t7rBtre/tLbGsrbFsLTDt7bBtLPCr6u4pqKWlYx+fXRhXUxEQDAxNy0oLSQqKh0p KRwnKCQiIx8iJyAjKCEfJBshJh0mJhslJRohKSQiKiUkJyIgIx4jIxkkJBoeJhgbIxYhIhwiIx0b IRUbIRUZJxYbKRcfJhwfJhwnJRglIxYjIh0lJB8jJxshJRkeIBYiJBkqIxMxKhlGRixWVjtpZ05/ fWOWhnqjkoeslI22nZavoaGsnp6qnaGom5+zl5KvlI6jjoeZhH2ag3Sag3SdiXmei3qnkY6rlZKn koyijYeWg3OXhHSWgniQe3KLdFuHcFeMc2SQd2iQeHKNdW+MdGuReXCJdW+IdG6GblyGblyRb1+e e2udgHuliIOah3eUgHCGcldrWD9JPCQ1KRMhIxYZGw8XGQ8YGg8gGw0gGw0aGQkdHAsfHA0dGgs8 Nxw/Oh88MRY+Mxc/NBo7MBY/NBhMQCNUPyNNOR1QPSFPPCBNPiJOPyNJPiFKPyJHRSxIRi1MSDRH RDBVRypQQyZPSDBQSTFERzRJTTpYSjtcTj5uTUFvTkNqU0VtVUdaUT9bU0BbWEldW0xhUD1rW0dy X1ZrWlBeVkRWTjxHRy0zMxsjLBkhKhcqLiIuMiYvMS4zNTI0OjI0OjI4ODo0NDcwNDMtMTAxMjgw MTc0MzgzMjcpMzIoMjEwMjEwMjErMzInLy4yMDEzMTIuLiwsLCosLSktLiorLictMCkpMSwpMSwi LionMy8tMCsrLikuLi4xMTExMi4qKycqMSkpMCgvJhc8MiM7ORhWVDBzaleQh3OqnZK+sKa/sLDB srLBsqzJurTFtrbFtrbCt7jFurvCt7jBtrfJvLbLv7jJv77HvrzNwb/Nwb/OvsPPv8XPwsbSxcnL vr/OwcLLvr/Nv8HRwca7q7CsmoyfjX+XiW6fkXWbjoahlIurlZKwmpe0n5m8p6HGtrjJuLvPv8LP v8LUwb/RvrzNwsPOw8XFv8HGwcLLwcXHvMHGwcLFv8HGvr7Du7vBtry8srjGsrjGsrjGt7TCs7C+ sq62qqadlI2DenRrX1RPRDkzMC0sKSYpKiImJx8jJxshJRkpKR4qKh8hIhonKCAkKBknKxwnKiMi JR4oJhsmJBkkIBciHhYhIRYhIRYnJh8hIBkfIhIeIREgJBciJhkgJRIiJxQqJRUnIhIjIxYhIRUp Ih0oIRwoJBgnIxcvJQ83LBVOQCZfUTV0aVeDeGWVhHmikYaqlJG0npuyn6Gwnp+vm5+rl5u2nZes lI6mi4Ohhn6bfmqVeGSZf3Sdg3imjYiqkYyhjIKbh32Wf3OSe2+NemmJd2WJclSHb1GNc2WLcGON dXCMdG+McmSSeGqMcm2Ga2eEal+Jb2SQdWqZfnOfhomiiIyaiHiWhHSOdFVvVjlAOykvKhkhHhAd Gg0lHA4mHQ8gGQojHA0eGQ0bFgocFwkdGApIPi5FOytMQCdOQylTQTVVRDhRQS1bSjVbTDRYSTJi TjxiTjxfUDtlVkBtUDtuUTxlWEFpXEVtXExtXExwYU1vX0xjZVdjZVdiXVNnYld1Y1p1Y1p4a2N1 aWFwalZ0blpzcFd3dFt6cmF3bl11b155c2J6b2l3a2VtbmRjZFtYV0M+PSolMRcXIwsoKyQsLygu NDIuNDI1OTE0ODA0NDcxMTMvMTAvMTA1MzQzMTI3LjM6MTc0NzgsLi8yMC8zMTAwMSsvMCovLy0x MS8sMi4qMCwiKyQlLicoLiooLionKygsMC0kLyglMCksLScsLScwKzAwKzAzNDAtLioqMScnLiQy IiQ5KCo+OCNbVD19amGei4Cvm5XBrKbFtrbBsrLDt7bJvLvJs7bNt7rDuLrDuLrFvLzDu7vHvrzJ v77Lv77Lv77Kv8HKv8HPwsbRw8fNwsbKv8PLwcXNwsbPxcbKv8HBrLCump6ukHmrjXeokIu0m5aw oaivn6eulp6ulp60l5e+oaG7qKXDsKzHu7rOwsHUxb/PwbvJwr/Kw8HJw8LKxcPNwb/Nwb/Gvr7D u7vGu7/Fur7Ht7zGtrvHur7Hur7LvLy/sLC6r7CzqKqul5qQen1rX1ZIPTQ7LCk6KygnKiElKB8n Jx0kJBoqKB4qKB4iIR4nJiMgIxweIRodJBodJBokIxwiIRogIRsfIBolIhspJh8oKB0lJRohJRgg JBcfIxYfIxYjIw8jIw8kIRAmIxIpJhcqJxgnIxokIBceIQ8fIg8sJhEsJhFPOihoUT50YlyIdW+e iX+qlYusmpmwnp2ym6OznaW0npuznZqwmp2nkZSZhoyRfoSRd2uIbmONeHiUfn6XiYSdjomdkIaV iH6We3CSeG2UeXSNc26OdWiEa16Lc26NdXCLdXOIc3CJb2uNc2+NeXOIdG6Ib3CMc3SMenmSgH+f iYmmkJCjlISWh3iCclFoWDpQOSw7JRkpGg8lFgsiGgohGQkfGAwmHxIkHRAgGQ0gGAgjGwpeU0pe U0poVkluXE9qUU1vVlF3X054YU9wX0x3ZVGCZ2ODaGR6amN6amN3bWJ3bWJ1cl93c2F+dGp7cmiC dW2Dd259cmuCd3CEc2OLeWmJf3KHfW+Hf3SGfnOEfmqGf2uDemWDemWJemuIeWqQf3iOfneNg3mM gnh7fnplaGRaWEZDQTAxMxspKxQkJiMqLCkxLzA1MzQ3ODE1NzAzMzUxMTMvMTIwMjM7NTM4MjAz MTA3NDMrNzkoMzUuMTQqLTAqMi0pMSwtMTIrLzApMS4nLywoKyQvMisnMzEkMC4pMSwpMSwmLSco LykqLisqLisuMC8vMTAtMy8rMS0oKyInKiEuHxk/Lyk/MyFfUz6EcmSfjH6rn5u8sKzBtrfDuLrG urbHu7fHuLbLvLrFvLzFvLzGu7zKv8HJvr/Jvr/Kv8HKv8HNvL/Pv8LOwsHSxsXVx8vRw8fOwcXS xcnRycnFvLyynpCnlIami3uskYKwmqW+p7K7r7u7r7u+pq60naW2m520mpuzopnCsKfJurfSw8HO w8XNwsPKw77NxsHPxcbNwsPKv8HKv8HKwb/HvrzJvLjKvrrKur/GtrvCt77Ct77GtrjCsrS/r7e4 qLCnmZaNf31tYlNORDU1LikyKyYoKSUpKiYpKSkhISEnJRspJx0mIiMnIyQfHx0gIB4ZJBsZJBsl JCEgHxwZHBYbHhcgHxgmJR4kKBsbHxMaIxoaIxogJBciJhkpJhcpJhclJBUmJRYoKB4nJx0mJBoo JhwkJxQfIg8tIRM0KBlHNy5fTkV1Y1qHdGqaiYKjkounm5qrn56yoqW2pqi0oqGwnp2umZmmkZGV iImLfn+Lc26IcGuJeHeRf36Ri4iZkpCijoediYKRf3KNe26Mfn6Mfn6NeHWGcG6IdW+NenSUfoCN eHqMcHCRdXWJe3eGeHOHc3WHc3WLe36UhIedjoyilJGllYaXiHmDdGNuX09MOyw3JxkoHA8qHhEi Gw0fGAogGBAjGxMcGwsbGgoeGgcgHAlwaGJzamR/bl1/bl19bl+IeWqOe26MeWuOe26QfW+Sd3eS d3eHfn2LgoCIgHWNhnqDgnuCgHqGhIB+fXmGgHWIg3iagIKdg4SVgnqXhH2Mi3eQjnqRjXiSjnmV i3+Vi3+SiG6XjXOOiHOOiHOelIiXjYKViYaSh4ONiH14c2hkYUxGQy8sKw8qKQ4oKSMqKyUuLTMx MDczNTQyNDM1NzAxMiw0NzUxMzI1NzI0NTExNTQwNDMhOTQgODMpMjUmLzIpMzImMC8nMDUkLTIo MC8oMC8tLS0wMDAjMzMhMTEoMi0nMSwnLyosNC8oMi8mMC0mNTIkMzAbMCsgNTAlMioiLycuJxc3 Lx9GMRpiTDKGcF2mkHuspaW0rKy8tLTDu7vHu7fKvrrJurfLvLrKu7vLvLzHvMHLwcXNv8HNv8HR xcPRxcPWwsbYw8fOxcPOxcPUxsfSxcbNxcXNxcXLwr6+tLC0nYyokYChmY2ro5e6sre/t7zHvsbJ v8fJu7y0p6i4p5+3pp6zp6G/s6zGuLrLvr/KwsLJwcHKw8HNxsPSxcnPwsbHv8LHv8LJxcHGwr7J vLjLv7vKvrzHu7rFt7vHur7JurrGt7e/sri3qrCqlo6Vgnp3Z15TRDwvOTEqMywqKCsoJikmJyMj JCAkJhsmKB0mJiQkJCIkJyAiJR4fJh4eJR0jJBwiIxseIRoaHRYhJBskJx4fIxUcIBInKCQfIBwg IRkjJBwnJRgqKBslKRweIhYfJRkdIxcoICErIyQjJiEfIh0qJA8zLRdIPTRiVk10YlGJd2WhiIOo kIuqm5msnpuzpZ+2p6K2n6WznaKynpSolYuijoSUgHeMdWmIcmWCdG+Qgn2XkJCblJSqkYyji4aM hHmHf3SJgoSNhoiRf3uMeneNe32Ne32SeICRd3+JdW6NeXKHfn2DenmGeHOHeXSMenuUgoOfkZGh kpKljX6eh3iNc2R5X1FQQzI4KxwlIRYkIBUaGhAZGQ8eGA8dFw4WFwocHQ8gHgsgHguQg3eOgnWI hG+JhnCRhHqShnudiICeiYKfjIKZhnuMg32SiYORiIKVjIaXh36ejYSXjIaajoiWjYeVjIaZjoSZ joShjIKnkoidjX6fkICelYOhl4aWkn2XlH6ji4aljIeikoOhkYKflX2il3+blImakoidlpSXkY6S iHqDeWtwaFVORjQxMxkpKxIsKyQqKSIsMjIsMjIxNzktMjQwMi8vMS4uMy4tMi0yMy0yMy0zNzEx NC8nMSwnMSwrNTAkLikqKzAuLzQrMDIpLjAqLC0uMDEqLi0pLSwjLSwjLSwpLy0pLy0qMCwmLCgo MyopNCspNSQnMyIqNS4qNS4vMiEsLx4yLho3Mh5INRRlUSyIc12jjXeuo5e8sqa6rqrGurbHurvJ u7zJvLjKvrrPvLvPvLvLvr/Nv8HNv8HOwcLKwbzOxcHWxsnVxcfPxsXOxcPRysfRysfNycXNycXF u7e6sKy4pZq3o5murKi4t7PBwcPFxcfHw8rHw8rKwsLFvLy7sq68s6/BuLjBuLjFvrvLxcLNx8bO ycfRxcPSxsXUxsrSxcnNx8nKxcbJwr/Kw8HNwbzLv7vNvL/Lu77Kt7jKt7jHu7TFuLLFtrC+r6q2 n4ubhnJ9aFVYRTMyMysrLCQpKCUoJyQmJyEjJB4oJhsoJhsiJx4hJh0dJh8dJh8gJhogJhohJBsg IxoeHxcfIBgiJSAgIx4dIhEgJRQqJR8mIRsfIBghIhokIhgkIhglKBcjJhYjJRkgIhYhGxsnISEe IhYcIBUnJQ8pJxFFOiFfVDlvX0mCcluhg42rjZeompWun5qzpp2zpp27o6i4oaa4pZ6smZKqjoue g3+NenODcGmCd3WIfXuRhIaajY6eiIiZg4OGf26IgnCRgoSSg4aSfnuNeXeIeXuRgoSUen6SeX2J d32NeoCOfX6Qfn+QfXWUgHmRfX2WgoKekZKfkpSokIeehn2ZdV17WkNPQy03KxcZGw8bHRAXGgsa HQ4aGwsaGwscFwkiHQ4mIQomIQqiiYOehn+ZjoOakISejpGZiYyijYelkImjl4eekoKWkoyXlI2W koybl5Gekoyjl5GllIyjkoujlo2hlIuilYyjlo2flYeil4molY2nlIymlYyrmpGZlIiblouolI2o lI2mloSikoClkoKnlYSllIyikYmflpCbkoySiYiDenl1ZU9TRC89PB4qKQ4mLCooLiwsMTMwNTgx NTQuMjEqNDErNTIqMC4rMS8xMiwuLykzMzMyMjIyMy8zNDAxMiwwMSsrLy4rLy4vMS4uMC0yMDE0 MjMtMTArLy4qLi8rLzAkLygnMisvMCouLykqMiUqMiUwMiQvMSMvMCguLyctLyMrLSEzLh4vKhpD NRVlVzKEdFuaiW+yoZfHtqzBs7THurvJvr/Gu7zKvrzKvrzOwsHPw8LOwcLPwsPNv8PSxcnLx8PL x8PRx8bRx8bOxsbNxcXSys3Vzc/SysrNxcXNwb/JvLu4sKW6sqa8urjLycfJycnLy8vNy9HNy9HR ycvOxsnFwbzBvLjJvsLJvsLLwcXNwsbLxcLNxsPSw8PSw8PRw8fUxsrNxcXJwcHLwsHNw8LOwr7O wr7Lu8HJuL7NuL/Lt77GvLjCuLTJt67DsqizoZKaiHp1aFNURzM6NzMvLCkmJigoKCokJyAlKCEu LCEqKB0gJSAjKCMkJyAkJyAjJxojJxofJBsdIhkhIRclJRseJRsfJhwmJSIlJCEmIR0lIBwkJBok JBomIyAoJSIoJhsmJBkiJBgfIRYkJR8lJiAkJhgiJBYpKA8sKxE/OSRaUzxuYliCdWubhpCnkZus n5awo5qvpqKso5+8paq6oqewnaGrl5unjIiaf3uMe3SAcGmAd22Ge3KNe3qSgH+Oe26QfW+NfmuO f22VhoiUhIeVf32Vf32WhIaZh4iWg4eQfYCWf4eXgIiUeX+bgIeXe36fg4aeiIihi4ubjY2bjY2h jIyXg4Obd2OAXUpHOyY3KxcbHgweIQ8WGg8ZHREXGQcbHQolHQwlHQwqIAkoHgehjpCjkZKolJSq lZWhkZaejpSql5SrmZWinZCdl4umnZulm5qempSbl5Gjl5Snm5eql5aolpWml5Wml5Wompeompei mZKflpCnlZGolpKol5Csm5Smm5GlmpCql5alkpGml5WnmZaql5aolpWnlZSnlZSmmpmjl5aQi46E f4N4alNeUTtDQykrKxQcLScdLigeLy0lNzQmMjApNTMmNTAnNzEoMjEpMzI1MzQyMDEwNDMyNzU0 NzUxMzIwMi8yNDEkMy4fLikmLi0oMC8yNDMzNTQrMzAqMi8oLSYrMCkkMSUkMSUsLB8uLiEpLiUq LyYtMisvNC0mKikpLSwsLyoqLSgxLh0sKRg9NRpXTzGCdVidkHK3paHCr6u/tLbFurvJvr/Gu7zJ v77Jv77KwsLLw8PNv8HPwsPOwcXOwcXOxcPLwsHRx8bRx8bNxcXKwsLUy8vWzs7Wy83UycrVxcrR wca+wcLBw8XGw8XNysvRycvWztHRzdTPy9LUyc/Rxs3Nx8bJw8LNxcrSys/UxsrRw8fPw8LUx8bS x8nRxsfPxcbPxcbNycXJxcHNwb/OwsHOv7/Sw8POvsPKur/FusHHvMPNwb/Dt7bHu7rGurivpZmV i39vbVRPTTU1Mi8uKygkKCckKCcgJhofJRkpLSAnKx4nKCIkJR8lJB0mJR4cJx4cJx4cJRoYIRYj JBwhIhodJB4eJR8jISAmJCMeIRoeIRoiJBYkJhglJCElJCEjIxkjIxkjJiEkJyImKBwjJRkkJhgi JBYmKBEpKxRDOi5XTkFlXVd9dG6Ug42hkJqumpSzn5mvpaauo6W6pqyzn6avm5+qlpqhjX2Wg3OO f22Gd2SIeWqQgHKSgHOSgHOMe2KNfWOOgnWLfnKNhoaXkJCjjo6hjIyjjZKfiY6ijZCXg4aeh46i i5Kdjo6ekJCbiYiZh4ahi5Chi5CfjJKei5GbiYuXhoeRc1d5XEFFOyoyKRkWIQwVHwoWGQgbHgwZ GQ4ZGQ4gGw0fGgwiHQ8gGw2rlZqrlZqqlpqump6rlZ2slp6lm5qmnZuinpeinpehl5aimZemnZmm nZmsmpaql5SrmZWsmpalmZemmpmsl5eqlZWmnZumnZuompqnmZmnm5Wnm5WhmpejnZqsmZ2nlJem kpuqlp+mmZ2mmZ2omZ6ikpedkJSdkJSNi46Cf4N9bl9hU0VEPygtKRQgJiIhJyMiMC8jMTAfNC8f NC8lMzQlMzQtNTQtNTQ0MjMwLi8vMDUxMjgtMzMvNTUrNzckLy8oMC0pMS4mLi0kLCsvLS4vLS4l Li8hKisnMiknMikmMSgnMikwMSEwMSEqMSslLCYlLy4mMC8rKy0sLC4qLSgqLSgrJRoyLCFHOiJb TTN3blqdlH6qo6G0rqvFuLfJvLvJu7zHurvJvr/LwcLJw8LKxcPNwsPOw8XOwcLPwsPNxcXNxcXR xsfSx8nUzcrOx8XOy83Pzc7OycrSzc7Wx9TPwc3Fxs3JytHNx8nSzc7Ry8/SzdHUzdTWz9bYzdHR xsrOycrPysvNys7Nys7UxsrZy8/Yx83Zyc7Vx8vSxcnUxsfUxsfPycbOx8XKw8HJwr/Hv8LFvL/H vrzJv77Ju7/GuLzJu7/Dtrq7uLq0srOmmY6Shntta1ZRUDw1OzEoLSQgMigdLyUnKCAlJh4nJxwk JBkqKyUkJR8ZHhkdIh0WJB0XJh8YJBYWIRMgIR0dHhogIB4iIiAiIRwmJSAjJB4gIRshJRYgJBYe JRseJRsaIRsbIhwcKCIXIx0eHxcjJBwkJhsiJBkfJBAnLBc4NC9PTEZuX1GAcmOUgImijpeyl5m0 mpu0oaW6pqqwqK6ooaaumqGrl56qkoOag3SWg3WXhHeRhoSUiIeZhHqahnuWhHeQfnCXhoebiYua kZCdlJKll5mll5mjlJmjlJmrlpSqlZKmkJWokpeflJClmZWnlZahjpCikpejlJmZkpmalJqdkYuN gnuDbU9vWj1UOzQ9JiAeIBYWGA8aEgUlHA4jGxEfFw4aGA0bGQ4gGw8gGw+mmpammpalnpumn52s mZ2rl5ujmZqjmZqlmZeonZusl5eqlZWompWsnpmsnpusnpuon5umnZmimpqhmZmrmZemlJKhlpej mZqdkpaakJSnlZSolpWhkpKilJShjZGhjZGfkZGilJSomZumlpmijpWZhoyah4Cei4SNiYaEgH17 cmddVElAPCcrJxQlKiMlKiMrLSouMC0oMjEmMC8oMTQrNDgwMjE3OTgzNTQwMjEzMTQ0MjUrMjUu NTkpNTEmMi4sMC8rLy4vMCwvMCwwMDAuLi4tNDUqMTIqLisuMi8xNC0tMCktLSMzMykzLigzLigu MC8uMC80LykxLCYwMiYtLyMuJBozKR9DOx1bUzJ4bVuekn+ypaa+sLLGurjJvLvKvrzJvLvJwcHJ wcHJvr/LwcLJwcPNxcfNxcXOxsbRxsrRxsrWycrSxcbRzcnRzcnSys/VzdLOycrRy83PzdHOy8/O ytHNyc/Szc7Vz9HUy9HSys/VzdLWztTVz9HOycrVzc/Yz9LSzdHSzdHYys7Zy8/Yys7Wyc3Ryc7R yc7Sx8vRxsrUxsfUxsfNx8bJw8LGw8XFwsPLv77Lv77Ju8LJu8LKvMHGuLzCtrLBtLCyoZeWhn10 b2NQTEA9OjIuKyQnLCcnLCcsKyYmJSAjJxokKBssJyMoIx8eIB0aHBkeHh4lJSUlJRohIRYiIB8j ISAdJh8eJyAjJxofIxYhHBgkHxsuJBwrIRklJRokJBkhJB8jJiEgIR0hIh4nJRomJBkhJxgcIhQo JhksKh06OC1RT0R0XE+NdGeUh4ihlJWsmpuwnp+3paO7qKewqqWspqG2n6eul5+wmpejjYujkJaj kJaajpeWi5SfkZGilJSZkZGXkJCekpuilp+rnZ2ml5eqlp2vm6Klmpuonp+on56mnZuonZuonZus l5WumZarlZqmkJWql5molpedl5uZlJeajYSRhHuMb15wVUVUNyw+IxkqIxMiGwwfGAohGgsjHA4f GAogGhAhGxElGQ0lGQ2onZuqnp2mnZuon56rnp+om52ql5molpejlJanl5qsl5eqlZWnlo6rmpKu n5+ompqflpChl5GdlJKdlJKijpKhjZGWh4yUhImOhIiOhIibh4eeiYmXjYKUiX6WgoKeiYmbkI6i lpWlkpSXhoeOfX6MenuOg3CUiHWQiYeIgn9/b2dhUUlFPCgpIQ8mJhsrKyAqLi0qLi0sMjArMS8t MTIvMzQ0Li44MTEvMTIvMTI0MzA1NDExNTQqLi0rMS0qMCwxNC0vMisrMCksMSopMicoMSYvNTMq MC4mLCwqMDAsMystNCwtLiYtLiYwKiw0LjAsLyouMSwuMCQrLSEzMCksKSIqJRY0Lx9HMBNnTi2C bVqfiXWzpZ++r6rHurvHurvJvLvKvrzJvr/Kv8HLvr/Rw8XLw8bNxcfPxcnPxcnRxsfSx8nWy9LO w8rPx8rUy87Uyc/Wy9LPx83VzdLVz9HUzs/Uy87Sys3Ry8/Vz9TWy8/Wy8/Vz9HVz9HRy8/Ry8/Y ztbYztbUy9HVzdLWy8/Sx8vVys7Vys7Px8rSys3Vys7PxcnUw8nUw8nLycrHxcbFxcXDw8PJu7/L vsLJusbJusbLv77Hu7rFt67DtqyzpaKXiYd0bWJORz01NCQ0MyMoLiElKx4mJxchIhMkJhokJhon Ih4nIh4gIiEfISAkIx4lJB8tJhgtJhgnISEnISElJB0jIhslJh4hIhonIB8sJSQrIxopIRglJCEk IyAfIRYjJRonJiEoJyInJh8gHxgkJhgnKRsrKx4pKRw6MRxPRi9rU0iIbmOahIeqlJaompesnpu2 qJ+2qJ+0qKWzp6O4pa6ynqeym6GrlZqumaeqlaOlm6ahl6Khlp2jmZ+jmZ2nnaGunqaqmqKul5+v maGwmqWvmaOlmpunnZ6ul6KrlZ+rl5uqlpqumZmsl5eslpaokpKrlZenkZSfjJKbiI6ajouMgH2J altwU0RQNyY5IRIpIxAoIg8dGg0dGg0gGQsiGw0lGBIoGxUsHhImGA2lm5qjmpmolJGolJGql5ms mpurmZqnlZamlY2hkIifjJCjkJSikYmol5CmlpmfkJKUhoaQgoKQfn2Rf36UgHmQfXWOfnWOfnWQ g3mQg3mWh3iVhneEem+Eem+Oe3KXhHqUiY2WjJCWhIaLeXp9al2Cb2KGe26SiHqWiYuNgIJ+b2Fe UEM8NR0tJxApJhctKhsrLywsMC0xMTMxMTMqMjEtNTQ1MTA3MjEyMTUuLTEuLiwxMS8yNDMtLy4s LisrLSouMiYuMiYnMSwlLyopLyspLyswMjEtLy4jLy0gLCokLismMC0uLycqKyMrKysqKioqKS8v LjQrLy4rLy4zMCksKSIpJxoyMCNIOS1eTkGAa2mijIm2oqa+qq7GuLzKvMHHvMHKv8PHvMPHvMPL wcXOw8fKw8HKw8HPxsXPxsXOycrRy83Sys3OxsnRxsfVysvVys7Uyc3Sys3Sys3Sys/Sys/Sys/R yc7Pys7SzdHYytHZy9LSzcvW0c/Uy8vVzc3UztLVz9TVzc/Vzc/Ry83Ry83Sx8vSx8vRy83PysvR yc7OxsvOw8fPxcnPx8rKwsXHxcbFwsPKv8HLwcLOwcfNv8bLv7vFuLTGurPCtq+zp6aWi4l6bmJX TEA+OSg3MSElMiYhLiIfJxcgKBgfKBYcJRMdIhkdIhkdIBsgIx4hJRkiJhomIg8mIg8kHRosJSIr KCUlIh8kIiEhHx4mHxwrJCEoJSAkIRwmIx4lIh0iIxQgIRIlJRomJhsjJh0jJh0kJhsfIRYjHxYn Ixo4KiNIOjJdTEV5Z1+SfXqjjYuomZ6woaa0pqO0pqO3qKi0pqa4oqq0nqawoaOunqG2nqa0naWv n6Kvn6Knnqaon6esmaKvm6WvmZ6ym6G2m5q0mpmvm6KumqGmm52mm52wm56umZusl5qqlZemlJCn lZGmkZGolJSijJahi5Weh46ag4uViYiHe3p5X1hcRD1KNCg1IRYoIg8lHw0dGgsfHA0mGREmGREh Fw8iGA8nHAwlGgqlmo6hloulkIinkouomZumlpmllIyfjoehkISbi3+Vh4KbjYifjY6hjpCZi4aN f3p/c2l+cmiAdWWMgHCNhHOOhnSOh3uMhHmOfXmQfnqLeGqEcmSCb2KCb2KAdWWMgHCLgIKNg4SH dWh+bV97a1N9bVSIe2+ViHuUiY2MgoZ/c2peU0pAQCcrKxQlKR0oLCAtLy4wMjExNTItMS4rLzAs MDEsLisyNDEwNDMrLy4qLzEuMzU1NTUwMDAwMi8xMzAqNSwoMyomMSooMywtMCkuMSorLywsMC0q Mi0nLyomLyYoMSgwMSkuLyctLzApKywqMi8qMi8vMCwyMy8yMy8vMCwqLBUrLRZDNCtaSkB/a2Si jYazpqq+sLTHt7zJuL7Ju7zLvr/Fv8HGwcLLwcLNwsPNxcXNxcXNw8LOxcPOycfPysnRycvSys3V x8nUxsfUy87RycvPx8rRycvVys7RxsrUzs/Ry83Ry83Uzs/Vys7Wy8/Sys3Sys3Szc7Y0tTSys/R yc7Ryc7Ryc7Ryc7Sys/PxcnUyc3Sys3RycvPx83Ryc7NwsbPxcnPxcvLwcfJw8fHwsbLv77Lv77P wcHLvLzDurjDurjDuri/trSwqqWSjId+bmVdTkZAOC43LiUnLSsqMC4nLiQgJx0fJxkdJRcgKR4e JxwhJRkjJxsdJRccJBYmJSAkIx4oJSIpJiMiJBkfIRYfKBYdJhQiHxonJB8lJiIhIh4kHxsnIh4j JhQhJBIlJxwnKR4kKBkkKBktJRoqIhcmHxInIBMyKhc9NCFJPjVhVUx+cGuVh4KhlpqroaWsoqOr oaKyo6OwoqKzn6OynqKvoqivoqi2nqi0naewm6q0n66woqusnqesn6Oom5+ql5mvnZ6vnZ6wnp+w naGump6nmp6mmZ2qlpqnlJemlJKnlZSilYyjlo2fiZGhi5KXiZeVh5WWhpCOfoiAe3B0b2RjWEZK QC9BNB0zJxEqIxMnIBAhHRQfGxIlHA4lHA4jGxElHRMkHw8iHQ6ikYmikYmbkIyeko6WkoyUkImf i4CahnuQhniNg3WIg3iQi3+fiYmbhoaMeXJ/bWV/a2R+amODcmSSgHONhnuNhnuEg396eXV/bmp/ bmp5Z116aF56Z199aWJ6bmJ+cmWDcm6LeXWAbmF5Z1p6aVqDcmKQgn2WiIOLh42Hg4l3dW1WVU1A QykpKxQhKh8eJxwlKycuNDAuMjEuMjEzNTQvMTAsMSwuMy4sNC8nLyoqMC4vNTMyNyouMiYmMSgo MyorNCsrNCsqMi8pMS4uMC8uMC8sMC8sMC8oMCsqMi0qMywrNC0xMzIsLi0vLy8tLS0pMCorMiww My4uMSwzMCkuKyQwLRwyLx5GLR9hRjeDaWShhoCypqW+srDJu7/KvMHGvr7Hv7/GwcLGwcLKw8HL xcLJwcPNxcfOw8fPxcnSx8nPxcbPxsXPxsXVx8nSxcbSys3RycvRyc7Ryc7Uyc3Sx8vOycrOycrS x8vUyc3NysvKx8nRxsrPxcnOxsvSys/Rxs3PxcvWyc3UxsrPx8rPx8rPx8rUy87VycfUx8bOw8fR xsrNxcfLw8bNwsbNwsbHwsbJw8fHvL7Gu7zLwcXGu7/Hur7Hur7Btry4rrSmpqiQkJJ0cnNRT1Az OS8tMikoKyYoKyYjLCMgKSAiJSAgIx4iKyAeJxwfIxciJhodJxkaJBYjJCAgIR0hIh4lJiIaJBYZ IxYgJhceJBYYHxYcIxkcIiAbIR8cHhQhIxghJBQgIxMcIhYgJhoeJRQhKBYtJRoqIhcfIg8cHw0i IQcpKA08LhRbTC5vYmKGeHiVjJShl5+moaKmoaKroaWonqKsn6asn6ammqalmaWwmaawmaamlqqm lqqomauikqWbl6GdmaKekpuflJ2ilJ+llqKmkpujkJmhlJqfkpmhjZSZhoydgoieg4mXhoSXhoSH f4KEfX99en51c3d3bXBuZGhkY05XVkFUSSlBOBkyLRIrJgwiHQ8iHQ8gGhIdFw8iGA8lGxIhGgwl Hg8lHQomHguajIyajIyZjYyZjYyRjYSRjYSZhn+UgHqQe3KOenCIfXmLf3uMd3eHcnJ3al5tYVVt Ylx5bmiGeneMgH2HgHmHgHmDc2p/b2d5bWN0aF54bmN4bmN7YmiCaG56a117bV6CbmR5ZVx3X1t6 Y153a2V9cmuOeYCbho2UiJGOg4x0eXVPVFBDRDInKBgcJRwjLCMsNS4sNS4sNzMrNTIrMS8tMzE0 NDI0NDIwNS4sMSowNzItMy8nMislMCkrNC0sNS4pNTEoNDAoNDAlMS0zNTcxMzQxMTMyMjQlNC8p OTMmNyslNSotMDMuMTQtLDIvLjQtLy4wMjEmLisoMC03MCcyLCMsLBUpKRJUMh1qRzB/ZV6eg3u0 n5/Fr6+/uru/urvFvLzGvr7GvsHJwcPHwb7Jwr/HwsHJw8LNwsbPxcnUxsfVx8nSx8nRxsfKwsfN xcrPys7Oyc3Nx8vOyc3PxcbNwsPNxcXNxcXPx83NxcrNx8vLxsrSxcnUxsrPwsPSxcbOw8fNwsbR xsfPxcbKxcbKxcbRxsrRxsrPw8LUx8bUxsfUxsfVwcfUv8bPwsPLvr/DvrzFv77NvL/Lu77OwcXL vsLKu8XFtr+/t7q6srSrq6iUlJF1dXVMTEwwNSwoLSQrLiUqLSQcLiQYKiAaKSQaKSQbKCAZJh4h JB0hJB0dJBocIxknIhwpJB4hIB0iIR4gJR4cIRofJRYfJRYgIxofIhkhJBsgIxokKBwdIRYYIxwZ JB0hIxclJxsdKBUaJRInKR0nKR0oJBgjHxQaGQoUEwQjFgRAMRxVSUplWlt5cnKMhISXkpSZlJWd kJabjpWekZWekZWRi5GQiZCVjZCSi42LhJCHgIyJg46Ce4eIfYiIfYiMf4OHen6LdHuIcnmGa3KE anCCa3CAam+Ga16CaFuAYWJ/X2FqUUpwV1BlWk5fVEhWUz9VUT5fTj9XRjhUPTBOOCtBNBkzJw4u Iw8uIw8kHxAbFgkfGg4dGAwhHA4hHA4eGwwiHw8jHQgmIAqXjIuZjYyVjIiWjYmUjYiQiYSUf3+M eHiLd3SMeHWAfXl+end0cG1raGRqZF1uaGF0cG1+eneCfniHg32Gg3eHhHiOeGiHcGGGc2t/bWV5 bmh5bmh0Ymp/bXV6b2l1amR3ZVhyYVRwX2F1ZGV4bWeDeHKRe4aahI6OjpGJiYx3dX1UU1o6PzUk KSAgKSIhKiMvLy0xMS8uMTcuMTcrMzAtNTIvODcsNDMsMjAtMzEvMDMyMzctOjUpNTEyMjAxMS8o Mi8nMS4rMDIpLjAuLjA1NTgsMjIoLi4iLiokMCwiNCYkNygkMC4kMC4qLTQqLTQkLzEiLS8hMS8f Ly0wMiYtLyMoKxAsLxRMMBdhRCmEaFemiHe2o6W/rK6/tLjDuLzKvL7KvL7HurvKvL7Nwb/OwsHL wcLNwsPPwsbPwsbSx8vOw8fNxcrLw8nJvsXNwsnNxcfKwsXJwcPNxcfSxcbOwcLLw8PNxcXKw8rJ wsnLv8vNwc3Pv8XRwcbLwcLLwcLLwcXJvsLLw8bJwcPJwcbKwsfOw8XKv8HJwcPJwcPLvsLNv8PL wcXKv8POwcLNv8HJvr/HvL7Ju7/Hur7HvMPJvsXGvsHGvsHBu7y8t7iyqKWelZGId3hcTE1BOzE0 LiUkMSkhLiYeLBwfLR0mKSQkJyIcJR4bJB0gJSAfJB8dIBkhJB0iJR4fIhsgHxgjIhskJR0iIxsm IBcnIRghIhoeHxcfIRYjJRokKBwgJBgcIh4cIh4iIhcgIBYkKRciJxYkJBkmJhseJBgaIBUaHA8V FgofFAcvIxU9KiBVQDVnVkl4Z1p7cmh7cmh9b2+Ac3OCdW1/c2p3al53al5+a2J6aF51ZV1zY1tq Y1ZlXlFnWEprXU9vXU5oVkdwU0RvUUNvTTtrSThYSDlWRjdaQBxcQx5VPytVPytUOB5UOB5bPSlR NCFJMhZJMhZAMCJEMyU5KxgvIhAsIhgrIRcmIxIfHAwdIBAaHQ4cFwocFwoeHQwbGgoWGwsVGQoa GAUjIQyajIyZi4uXjIaWi4SShomRhIiQfn2GdHOAd22CeG57eG93c2pua19lY1dnXV50amuGc3eN en6Gf3qMhoCHg32EgHqQfnCJeGqAdXR7cG96bm93amt9anOAbnd4bWtyZ2VtXVNwYVZ6Z1+AbWWG dHCMeneOgoOWiYuVjJSOho2Dd31fVFo8PTkjJCAlKiMoLSYuLjAyMjQ1Mjg3MzkyNzMwNDEzODkx NTcsNDMrMzIwMjEzNTQsPDQnNy8sMSwsMSwoLykrMiwuLjAxMTMxMTEyMjIrMS0oLionLSslKykh MiojNCwtLywtLywsLi8wMjMqMjElLSwmLCopLy0yMCUwLiMkKxclLBg9Kg9bRieAalGljXOyo56/ sKvBsLbGtrvJu7zKvL7Kur/NvMLLvsLOwcXLwcXJvsLNwsbLwcXNwsnNwsnNwsnKv8bHvMHNwsbN w8LJv77LvsLOwcXPv8LOvsHKv8HJvr/LvsLNv8POvsbOvsbNv8HNv8HJvr/LwcLLvr/Nv8HNwsnK v8bKv8bLwcfJvsLFur7GvsHGvsHLvsLKvMHJvsLHvMHNv8PKvMHKvMPKvMPHtsHJt8LJu8LKvMPD uLzJvsLCurq8tLSypqKflJCEc3RaSUpIPjs9MzAkMSkeKyMdKB0cJxwiJhomKh4iJhohJRklKB8k Jx4jJCAhIh4eJhkcJBcgJBcgJBckIhgnJRsoIhooIhoiJhkgJBchJBQgIxMhIxciJBgnJB0lIhsk IhclIxggIxEcHw4fJhMfJhMfIxUgJBYfIg8ZHAobFQcjHA0pGwo3KBZKOyxURDRbSTthT0BdTkZn V09pWkZiUz9YTzhVTDRWRCpWRCpWRSdUQyVTPypTPypVQChaRSxMPiBDNRhQMBlbOiJfQCdaOyJK Nx06Jw84KANBMQlIPSJVSS1WRzBdTjdhSTRaQy5UNB4+IQ0vIBgzJBwpIhIhGgsnHxcoIBgoIBYi GhAeIREaHQ4YFgseHBAhIBEfHg8aHhAXGw4aGA8cGhGdkZCXjIuSiYiUi4mXhIiUgISHe3h7cG16 c2l5cmhubWdlZF5iXVNrZ1xya2d9d3KNeoCNeoCMgH+Lf36JhoyIhIuQfn+GdHVzcG9raWhuamd0 cG2Db32Db31+b3J/cHN0ZVR7bVuAb2KHdWiMgnSQhniRkIeWlYyUkKaMiJ54d35aWF88PTQpKiIk Kh4qMCQwMyw3OjI3ODszNDgvNTExODM0NDIyMjAuNzMuNzMyODAwNS4uOTUuOTUrNTApMy4uMjMs MDEtLSswMC4uMSwpLCcuLjAtLS8sLi0qLCspMTAoMC8pKyouMC8rLC8vMDMnMjIlMDApLy0sMjAy MCUwLiMjKBUnLBg4MSZTTD+AaWGljIOwop2/sKvCtLjHur7KvL7Lvr/KusLPv8fFvL/GvsHHwsHL xsXRxcPNwb/Gv7zJwr/NwsPHvL7Gvr7Hv7/Lv77KvrzJv77GvLvHu7rKvrzGu7zFurvKvL7Lvr/H vL7Jvr/LvsLJu7/Gu7zJvr/Jvr/Jvr/Jvr/Gu7zHuMLKu8XHvMPDuL/Du77Du77Gu8LGu8LGt8HJ usPDuLrBtrfCt77Ct77Ft77GuL/HtsHJt8LJvsLFur7FuLfDt7a+q6ynlZaAd2tUSkBBPDo3MS8s MyknLiQfLCIcKR8hJRgiJhkhJBsbHhYiJRwkJx4iIx8gIR0eJBggJhogKBsbIxYlJRslJRskJBcj IxYjKBUfJBEXIhsbJh8kIx4lJB8mJBoiIBYgIBQkJBciJBghIxcgJhkfJRgYJxEVIw4UIwkVJAoe GwsgHQ0jHA0kHQ4zKQ85LhNFMRZRPSFURTJbTDlcW0VXVkBcVERTSjtBPCtAOypEQCNEQCNJOiRO PiheT0ViU0hVUDo8OCNBMh1cTDRjXVZnYVpbVFhGP0Q/NyNORTBiXlZ4dGt4eoh6fYt+e4lzcH5o W1hFOTcyJhcmGg0jIQ4dGwkmHRElHBAeGQsgGw0eIQ8ZHAsXFwwbGw8fHg8aGQsZGgoZGgohGxEf GQ+akZCQh4aNiH2Qi3+Vg4KEc3JzbWVya2Rza2F0bWJpaGJeXVdhYVRnZ1p5d3iCf4CRh4iMgoOL goCRiIeNhoiGfoCDd2p7b2N1cGV4c2h6enp9fX2EeouJf5CLf36EeXiCem+IgHWDe3CMhHmZjoOi l4yanpeeopuVmqqGi5pubnBYWFs8Py0tMB8oLB0sMCExNzE0OjQzOjUxODMoMywrNy8yNTAyNTAx NzstMjctMTAsMC8uMSowMywnNCwlMiowNzQsMjAvMTAwMjEoMC8nLy4wLi0xLy4uMC0pKyglKSYo LCklLicnMCkuMC8uMC8qMTIqMTIqMSkqMSkrNCErNCErLRYsLhY/PDlUUE2AZGSlh4eun5+4qqrG trvJuL7Gvr7FvLzFur7Kv8PFvL/GvsHGvr7JwcHNwbzNwbzDu7vFvLzHv8LGvsHDuLrHvL7JuLvH t7rJu7/GuLzHur7Ju7/Ht7zGtrvCt7vFur7Hur7Ju7/Fur7Fur7GuL/GuL/DuLrHvL7FvL/CurzF tr/HuMLHt7zGtrvDu7vCurrFur7Ct7u/s7zCtr+/uru+uLrCs7zGt8HDtrrDtrrHt7/Ht7/FuMXF uMXGu7/Btrq/pp+njoh7dWFRTDk9PzIrLSEuLx8pKhoYKB0YKB0mJyEkJR8jJCAgIR0bJBscJRwl JiAmJyEiJBkiJBkhIxggIhcmJSAnJiEfJBMiJxYpKRwkJBcaIhUaIhUhIBkjIhsiJhoeIhYmIBgq JBwfIBwfIBwjIRUgHhIWJA4UIgwQJAwTJw8jIA8jIA8jIQomJA0nIgo1MBZAOSdcVEBhX2lranRt dIJqcn93c4hwbYJnaWpbXV5RUVFUVFRWT09kXV11a31/dYdzcnljYmlWVlRoaGV0epB+hJp1d55n aI5VU19WVGF5cJmSibOOlLCOlLCUlayGh557c3JbU1E+MBspHAohGwoiHAooHBEnGxAiGw0lHg8f Ig8cHw0eGQ0iHRAgHBEgHBEbGAogHQ4qGxQlFg+ZjpKOhIiLf3uDeHSAdWV0aVpyZ1d5bl55a2d6 bWh1aWFyZV15c2uIgnqQh5GRiJKVjoyUjYuSiYaUi4eUgoCRf36GfWiDemWDeneNhICNjY2MjIyQ jpmQjpmMi5CIh4yOkZCSlZSRjpCUkZKil5ujmZ2fnZ6in6GUmqKGjJR9c3RfVldFPy0vKhkrKRwv LSAzNy8yNS4zODQyNzMtMS4vMzAwMjExMzIyNTsuMTcuMTcuMTcxMzAvMS4wNDEwNDEkNzAiNC4v MCoxMiwqMi8nLywuLiwtLSsjLi4hLCwlKSglKSgoMCsoMCstLzAxMzQ0MjUyMDMqOC8lMiolOCEm OSIxKhI3LxZDNzRUR0V0Yliah32rnZq+r6zFtr/HuMLDu8HBuL7Jvr/Jvr/LvsLLvsLFv77Fv77F wbzCvrrFur7DuLzHusHHusHFt7jHurvHusHFt77JuMHFtLzDs7jHt7zJtLvJtLvCtsLBtMG+s7rB trzBtrq/tLjCtLjBs7fCt7vDuLzBtrrBtrrFtr/DtL7Ft7vCtLi/t7e+trbFt7vDtrq7s7i+trvD uL/Btry+rrPCsrfHtLbGs7THur7Ft7vFtsLGt8PDs7vCsrq/paamjI17cF5XTTw7PTAvMSUuLyss LSkcJyAeKSIhJxofJRggIRsjJB4fJh4cIxsoJBomIhgoJR4oJR4mJSApKCMjJBwiIxsiJBkkJhsl JRokJBkhIxYhIxYkKBsfIxYaHhIiJhkqKBsqKBsiJCMhIyIlIxkhHxYfJRkaIBUgIhUhIxYpIxgp IxgpIhIqIxMrJgw4MhZKRlVlYXBwbaV5da59grCEibiMkauJjqiSkqF9fYtyaYJ1bYZ1cICEf5CO iK6alLqWlKKEgpBqbYd6fZeQkMGWlseIkK9/h6ZvdYdkanuAgKuQkLuborSepbefmbCOiJ+Mf3Vw ZFtMNyMwHQwlHA4oHxAnGxAnGxAhHA4gGw0jIQ4mJBAjHA4hGgwiHBIgGhAcGQogHQ0sGQopFgid jJmVhJGOe3+Gc3d5a1Z/clyHf3KNhniJh4iAfn+HfnqJgH2HhIORjo2WjZeelZ+bmp+dm6GblJaZ kZSWkZWRjJCVhH2bi4OUjJGakpeimp2imp2dm6Gbmp+Ump+XnqOenp6enp6bl6Galp+fm6KemqGi n6Oin6OUlZuMjZSCeXNdVU9NPC03JxkqJyIxLik5NDU6NTczMzUzMzUuNDIuNDIzMTI3NDUxODgu NDQzNzowMzc0NzgzNTcyNDEzNTIqNDEpMzAxNSksMCQyODIxNzEyNDMqLCsnKi0tMDMsLCwrKysi My0jNC4sLC4xMTMrLzArLzAiLignMy0mOickOCUrKBcwLRxILBZdPyduZEyQhmunnZ66r7C+s7rF usHGu8LDuL/JvsXDuL+/urvCvL7BuLjDu7vFvLy+trbFurvCt7jGuLzDtrrFtLfHt7rCtr+/s7zD tL7Bsru+sLe+sLfBs7q6rLO6rre4rLa0sLywrLiuqKqwq6ywpa6zp7CwsLOvr7K7q7O6qrK/sri/ srjCtLi+sLS7trq7trq/tLu+s7q8sra+s7e/srbBs7e/r7K/r7LBsrLBsrLBs7TDtrfBtL6+sru+ s7q3rLO3nZ6ehIZ6aF5jUUhAQTswMSsjKi0lLC8kJCIoKCYrKhooJxcjJxojJxohIxciJBgnJRgn JRghJBshJBsjJBwkJR0jJRojJRomJyMkJSEmKBweIBUeHxkjJB4jJh0kJx4lJxkjJRckJBckJBcn Jx0hIRcoJhwiIBYiJBkhIxgXJRYVIhQgHhInJRgrJg4xLBMrLhs8PytWUHNya5B+erODf7iDhKeN jrKOlauQlqydl7aalbOHgpWMh5qMjK6RkbOQnb+XpcebmqKOjZV+eJ2Jg6idlcamns+em6qSkJ6M hp2IgpmQkKyenrurp76wrMOvpsGjmrSIjoZeZFw9OCkpJBYgHQ8gHQ8jHA8jHA8lIRYkIBUbGQ8j IRYgGwwfGgsmHhQjGxEeGQogGwwqHQ4sHw+ZkpuWkJmHiJGCg4yCgoKIiIiQjJWRjZaSjpqRjZmN kI6WmZeZm5qdn56dmaWbl6Ofnauhnqyhnaabl6GblqablqaZlZ6bl6GZmqGfoaeioqWdnZ+fnqOj oqejpqKipaGjoqeioaadnqWen6aoo6enoqaqnqeonaaelZ2VjJSId3NiUU5IOCk3JxkrKSoyMDE1 Mjo7OD89OTw7NzoxMzIvMTAzMTA3NDMzNTQxMzI3OjQzNzExNTcvMzQyNzUvMzIuNzMsNDEyNS4t MCktNC4qMSsyMS4rKicmKicqLispKywqLC0nNCwlMiosLyouMSwsLSkuLyspMCgpMCgoNCUoNCUv LSIzMSZTMxZbOxxwX0mSgGmjmpm3rqy/srbKvMHGu7/DuLzCuMHBt7/Ft7vGuLzFt7jCtLa/tLi7 sLTHtLbDsLK8srO8srPCtLjCtLi6sLi6sLi+rLq8q7iyp7irobKqpailn6Ovm6Kvm6Khl6WelaKS kpWWlpmUkJmWkpudnqKZmp6hnp+hnp+mo6ejoaWuorCzp7a7rrK4q6+8r7a+sLe8r7a+sLe7rrK8 r7PCsrfCsre/q6/CrrK/s7K+srC7tLu7tLu0rrenoaqmlYyUg3pzY1teT0dARDovMiksLCwuLi4l KiMhJh8fJhweJRslJxskJhofJBMjKBYnKR0nKR0gJyEgJyEfJhwiKR8iKyAfKB0eJxweJxwiKBwf JRkiJRwjJh0cJR4eJyAlJxwjJRojJhYgIxMhJRYgJBYpJxomJBciJBkfIRYfIxYgJBcgIhYfIRYo KxAkJw0uKB9FPjRhXH13cpSDe5qIgJ+Gg4SHhIaVkJ+alaWwnrOzobaRlqaSl6eXlr6dm8Oiprqm qr6snaKikpeUjaedlrCno7y0sMq0rK+hmZuXkaiXkaiXkrOmocKwp8KzqsWvqr6mobSSjoZuamJI OSUxIxEjGxEnHxUlHg8jHA4kIA4jHw0gGw8hHA8aGgcaGgceGBEfGRIkHQ4jHA0nHRYpHxedmp6b mZ2Slp+OkpuSkqGWlqWXnayVmqqbmaedmqibm6yfn7Cjnp+moaKlo6uhn6eqnqysoa+uoq6soayf naufnauen6idnqeboa6hprOipqqeoqaloqOmo6WqoqWooaOnoaemn6ajn6afm6Klnqelnqeloaef m6KijZ6VgJFub2hISUMlLRQZIQorLSowMi8zMjc6OT04OTQ1NzIzODcwNDM0NzUwMjEyMjA1NTM3 OC81Ny4rNTIpMzAtMzErMS8xNTQvMzIzNzEtMCsvNSkqMCQrKykrKykkLSYnMCkkLSYlLicmLSUr MiomMCIlLyElLSAlLSApLiUpLiUhLyAhLyAqLiEtMSQ+LhNGNRlhUUqAcGmnkZa6o6jDs7vHt7/G u7/Ct7vCur/Cur/Hs7fDr7O7s7O7s7O/trS7srDBtLPBtLO+s7e7sLS4sLa2rrOzq7uro7Oqmqyo mauhkJ2djJmRjYmLh4OUi3mUi3mDfnKGgHR6dG1+eHB7c3J7c3J+enR+enSHfnqLgn6QiI2UjJGZ laGalqKlnZ+qoqWro6i2rrOyq7K0rrS3qq66rLC7rrS6rLO+qrC+qrC8rK++rrC7tLu0rrSspqyh mqGXjX+Ad2loW1hTRkQ1P0ApMjMqLSQqLSQfKB0fKB0cLCMbKyIhIxcmKBwmKBolJxkfKxwdKRoi JhogJBgcJBcdJRgiKhwkLB4jKB8aHxYhIxYhIxYhIxYhIxYhJBsfIhkhJRgiJhkjHhEmIRQhJxod IxYiJBkiJBknIBslHhkhIxYiJBYgJBYfIxUnKRQlJxIzKhpNQzFfXnJ6eY2LhI2IgouAd2l+dGeH houRkJWrmqezoq+horqio7ulo8mencKepaqhp6yonaajl6Gdm7Clo7irrry2uMe6s7qspqyboayW m6ehl7Oso7+uo7amm66qoqefl52ViXlzaFhGOhwkGQIoGg8tHxMhGgsiGwwlHwoiHAcgGw0fGgwb GgwcGw0aGg8cHBAdGRAfGxIiGw0lHg+imaOlm6afoaqfoaqvoaqun6ijoqyhn6qjoqqjoqqsorSs orSlnqWrpaumpaqlo6ivnqizoqysoaysoaympa+lo66hoqahoqaeo7Cboa6hpa6jp7Csoayqnqqm m5+soqaqn6OjmZ2fnqaenaWenqudnaqemqGXlJqSiZR9dH5TW1EyOjEaLRIWKA4tMikwNSw1NTU7 Ozs3OjQzNzEsODArNy81OTMuMSwwNTAxNzE0NzMyNDEtNTQxOjktOTkpNDQyNzg0OToyNTAyNTAs NSwrNCsnLyonLyokLikjLSgjLiUmMSgtLiouLysnLSEmLCAjLyAlMSIqMSkoLycmMSgoMyokKhsj KRowKQ4+NxlRTEduaGOijJawmqW7r76+ssHDt8G7r7jBr7rGtL+/r7S/r7S7sLS8sra/s7K4rKu6 r7C8srO3srCvqqirp66jn6aalp2UkJaXjIuNgoCMenmAb256bm9zZ2hvaFBpYkpfXj9jYkNoVz9o Vz9oWD5pWj9bWDxfXUByXUpzXkxzYVR/bV99c2mEenCDhH2OkIiOkJaXmZ+hn6Wop6yyo6y3qLK0 qq60qq68qK+8qK+3qq64q6+0qq6uo6ejmpaSiYaHenB0aF5YUVFIQUE0PEEvNzwtLyMtLyMjKB8m KyIjMCQfLCAkKBshJRgjKRofJRYcJhcgKhsmKSIhJB0gJRwhJh0kJholJxsnJh8iIRoeJBgdIxcl IhsmIxweIhYhJRkmJhslJRokJRYnKBglJhYlJhYiIxslJh4nJB0mIxwkJhojJRkgJRIdIg8oKSUp KiY6NCZIQzNcZGp/iI6ViI6RhIt+dXJ9dHB/g4yRlZ6mmqavo6+rqMmuq8usrsejpb6ioaiZl5+h mqOooqujqMOrsMuutMuzutG2t8+vsMmlr76msL+vn7OwobSspq+ooquwn6qmlZ+dg3R+ZVdKRTUu KRsoIhcpIxgeIAodHwooIRIoIRImIxUiHxEeGw4eGw4cHQ0eHw8cHBMdHRQkGhEkGhGjnailnqqf nayhnq6vnbKvnbKonaisoaysoa+rn66rnrKnmq6qo6qup66np6enp6euoaWvoqamn6aim6Kenque nqufm6efm6eem6uem6uinrSemrChlKafkqWdkpmdkpmakJaXjZSWkJaUjZSOjZKHhouIg4d9eHtu a2pVU1E6OSgrKhopKR4tLSIvOS8vOS8zOTMxNzExNy8yODArNy8nMisxMzIwMjEyNS4yNS4xNzEs MSwsMC8sMC8nMisnMispMzArNTIuLysvMCwqMC4tMzEsMjArMS8nLigkKyUoLyklLCYrKSgpJyYm KikmKikiKyIlLiUnLSkrMS0pNC0pNC0rLBooKRc0JxM/MRxKPChuXkiUhoaompqzqrK3rra/tLi0 qq6/q7TFsLq6rLC6rLC6rLC6rLC3p6q2pqi6rLO2qK+noaefmZ+akJaUiZCGgn56d3N5bmp1amdq Yl5bU09bT0RXTEBWTjtORjNJQDRJQDRVQDNVQDNPPytMPChBOilMRDJVQS5cSDRcRjNkTjtlU0lt WlBpZFZwa11zcneDgoeMh5aZlKOnlqOqmaawo6Wypaazo6a3p6q0rLKupqumm6Khlp2RiX5/eG1y X1ZdTENIQUFDPDwtODcoMjErLBwvMCAnKx8pLSEgKyAdKB0iKSEdJBwcJyAeKSIeJx4eJx4jLCUg KSIjJxojJxokJR0mJx8oKB0kJBkgKSAbJBsjJBwjJBwjJBUkJRYqJxgnJBYhJBIiJRMhIxcgIhYh JBshJBsfIBogIRsgIx4hJB8gJBggJBgoJSItKic3LSpGPDlaXGh7fouQhpaRh5eJiI2Ih4yLjZ6V l6ilmrquo8OsqM6rp82rrr+bnq+XkqKQi5qUjq+dl7ifnsaiocmem7udmrqjo7Sfn7Cem6qsqriv orarnrKlobqqpr+rnrKilaiaeW19XVFQPzAyIxYlIB4jHhwcHw4aHQwcFwshHA8qIxYmHxIgIAoe HgkjHQsjHQshHhAgHQ8jGxMnHxahlqilmqylm6alm6anm6emmqajnaOooqivnquunaqnmaeml6am oquinqehnaahnaalmaehlaOhmqOfmaKNl6OJlJ+MjJmOjpuOjJCOjJCUjJ2Lg5SIfpCHfY6Le4OI eYCCc3WIeXuAc3N9b29wcG5hYV5iZFZbXU9QTDM/OyQ1LB0wJxgxMDQyMTUwNS4yODA3ODE3ODE0 NDQ0NDQxNDgvMjUuMjEwNDMzNy0tMCcqMi0sNC8uMTQuMTQnMS4rNTItNTApMSwxNTItMS4uMDEt LzAvMzQuMjMmLi0iKikkLB8kLB8nKx8nKx8iLSQjLiUoLB8rLyIrLiktMCstLywtLywyLCEvKR43 KRg8Lh1ONxp0Wzt/e2eUkHqsnZ+2pqi8rK+7q663qrC6rLO0qrCzqK+3qq62qKy0paqzo6iuoque kpuQi4yMh4iIenV/cm13ZF1qWFFYVklNSj5DOzg9NTJBRDQ/QTI8PTc4OTI/NT03LTQ7ODI7ODI/ MiY/MiY9KSRGMSw/MiRJPC1EMyhIOCxKPDVHOTJORDVWTD1eV01rZFpyaG5/dXuWf4yfiJWil56n naOqoqWupqinoqainaGXkY6Nh4R/clxvYk1cTTpMPSs9OjQ9OjQpOzQmODEpNCsqNSwoKSEoKSEo KB0mJhslJiIlJiIjJxsiJhoeKyMfLCQlJiAkJR8fJBsgJRwlJiAjJB4nJxomJhkgJxYeJRQgJRIj KBUgIhUgIhUoKhwgIhUgIhYiJBgcIxIdJBMbJBkbJBkjJRogIhceHxkfIBomIR8oIyEqKB4oJhwy Jio9MDRIVmhte46GhJuLiaGZlLKblrSVlbSVlbSblbuhmsGinsObl7yaoaiIjpZ7fYZ5eoOAep+I gqeUkKiNiaKGhoODg4CRh4uJf4OHe4mRhpSWjqKSi56NhqeUjK6RjI2GgIJ7ZUhrVjpPOSw6JRkn IxcgHBEeHhIdHREWFw0XGQ8kGhMlGxQgHgobGQYjGgwjGgwfGxAhHRIiGhAjGxGUkp2XlqGbmp+Z l52blJaakpWakpWakpWblZuVjpWUi5WRiJKSjJWVjpeRhpGNgo2Jh4iCf4B+e316eHl6eHd0cnB0 cnB1c3Jucl1wdF9va1praFZ0YV5zX11pY1xhW1RtVVRvV1ZkVkhjVUdXW0dQVEBHUzQ8Ryo8Mx41 LRg7MSw/NTA8ODk+Ojs6Mys+OC9HRTlJRzs8PDw1NTUxMjgxMjgxNTQwNDMwMC4yMjApNTEqNzIu LzQwMTcwODkuNTcmODMkNTEzNTcxMzQyLDAuKCwjMTAgLi0kLCsnLy4pLCUpLCUhLR4hLR4ZLyAY Lh8kMSckMScoMi0nMSwlLR8oMCIrLBooKRclKBUfIg87KBBWQSdlXUmAeGObjY2nmZmyoqe8rLK6 pqy7p66yqq+upquvp6yupqunn6WhmZ6QkYmAgnp4dWhqaFtkXk5bVUVkRUNbPDpDOzg5MS4+LzA8 LS41NTMyMjAuMC8yNDMzMzUuLjAsMy0rMiwtLiYvMCgpLSwrLy4wLSY6Ny81MisvLCUzMSY1Myg1 NCE0MyA/OilKRTNbTERoWFBzX12CbmuAfYiIhJCVjJSUi5KVkpSLiImId2d5aFhtXUVXSDFGQDE9 OCkqOC8pNy4lOzEgNSwiNDAjNTEkLygkLygpNSclMSMpKyAlJxwkJx4mKSAhLCUfKiMpKCEoJyAf KB8bJBsiJyAkKSInKR0gIhYdJBEeJRIiJRMjJhQiJBklJxwnJx0jIxkhIhwgIRsgKBodJRcYJBQd KRghJh0gJRwiIx0hIhwnKCAoKSEqKB4rKR8vKCU4MC01P05daHhzd4x6fpSQkaqQkaqRjaOMiJ6C gJWQjqOIiaF6e5J4fW5tcmNfYlVdX1NlXWdqYmt1a15uZFdiXTdXUy1pUExpUExqT1ZwVVxqXWhw Y25pYmJrZGR5ak9rXUNkTC1QORwrMh4eJRIaHwwfJBAkIBYfGxIXGgoaHQwhHRIgHBEaGQkcGwof HA8fHA8aHBEbHRIhHRIjHxSGfn6Gfn5/fYCAfoKJe3uMfn6MfnmHeXSJd3CLeHJ7cnN0amt+aW5/ am93ZGhwXmJ4ZFtzX1ZtXVVtXVVtXEhoV0RlV0dlV0dYVj5dW0NcVERcVERiSTxkTD5bT0RPRDlV QzpVQzpQQStTRC1VUENHQzU8QDM6PjE9PEBHRkpWTldWTldFUUdIVUpVVFteXWRYX11TWlc/RkQ3 PTs1NTgzMzUuNTktNDgpMzAqNDEpNTErODMwMi8wMi8yNDEwMi8mNDUmNDUyNDUwMjM0KzIzKjEj Ly0gLConKygqLisoKCgpKSkeLiUeLiUiMCEiMCEeLSghMCsqMi8lLSoeLx4gMSAjKhYfJhIoJhAj IQwvJQs9MhZUTUBrZFeDenmSiYifl5qro6asoqivpauyqLOvprCjnaOhmqGUkZCHhIN7d2puaV1r W0pfTz9JRjVFQTFDOTM7MSwwMi8wMi8vMTItLzAuLiwsLCozMTAvLSwyLywzMC0vOCosNCcwMi8t LywpLjIrMDQtMCkrLicuLysvMCwtOCgpMyQ5LyA0KxwxMCA3NSVBMy1KPDVbRjloU0VoYWFvaGh0 c3h4d3t/em91cGVvZFFdU0BHRTo3NCovNC0sMSolLyooMi0uMSovMisrMS8rMS8nLiglLCYqMSkm LSUjJxsmKh4pKyApKyAhKiEeJx4jKB8jKB8fKCEfKCEhKCIjKiQmKSAfIhkfJRghJxoqJhEqJhEk JR0hIhobIx4cJB8iJBklJxwrJiApJB4oJxcnJhYhJRkgJBgdJBwfJh4kJhsjJRokJhglJxkxLB43 MSM0NDRRUVFiW2pnX29vb3tycn5+c297cG1rYmVrYmVqaGdfXVxdU0BXTTtPQDdPQDdOPTRRQDhb RTJWQC5JOhZENBJMLSRUNCtONTJJMS5IOTVMPDlTPypUQCtNPyFHOhw/MiM3KhseIw8cIQ4eHBAf HREcHRYdHhYeHgogIAwjGxQlHRYeHQ8eHQ8dGAoeGQscGg8dGw8lHBAsIxZtXFhoV1RfXlZdXFRv WFNzXFZrWk1rWk1vVERvVERjXFFhWk9eUENaTD5XTERTRz9YRj1YRj1WTjxNRTNIPCdQRC5aSTda STdOPjtJOjdHPD9JPkFRQDtUQz1USkBQRz1OST5RTUFcT0pkV1NpWmNVRk9UTVFiW19UYXdkcohu bn5ra3tjdHVqe310eJl1eZpob4hWXXVEVVsyQ0g0NDQ3NzcpMjUmLzIsNDMsNDMsMi4sMi4wMy40 ODIzNTQtLy4xMDcxMDc1MzcuLC8tLTguLjkmMC0kLismLSMoLyUvKyorJyYjKiInLiYpLSErLyMh KyghKygqLismKicjLCMhKiEaKh0UIxYVFwcSFQUaFQMrJRBFMSVdSDtzXmGHcnSNiIyVkJSimaOm naefn66ZmaeXi46Qg4eEeG91aWFtYVdbT0ZQQzVPQTQ7OS06OCw+NC8+NC8yNS4vMiswMS0tLiol MS0jLysiKSojKispLysqMCw1MSU0MCQwLCsyLi0tLS0sLCw4Myk0MCYtMCktMCkcMicYLiMxKyAt JxwxKyAxKyAuKyQwLSZAMCJIOClKPzhcUEhlVkxvX1VpYU1fV0RWSj9KPzQ5ODwxMDQrMCssMSwp LS4qLi8tMzErMS8qMDArMTEqMC4kKigqKCcoJiUmJyEkJR8pKR4nJxwoKyQjJh8jIyEnJyUdKCEa JR4fKiMcJyAdJBwfJh4hIRYlJRorKBkqJxgfJRkgJhodIhkiJx4kJBojIxknJB8kIRwdIBkgIxwg IxohJBsgIhYgIhYmJBkmJBkqJxYrKBc0JhkyJBczKh1EOixNRUxKQ0lRSk1TTE5cTTNbTDJWQDxO OTRPRTRHPS07NCk7NCk1LyQ6Myg5JSA+KiVIMyJELx41KxQzKRI/IhlBJBs7KSc4JiQyKCAyKCA7 KhU6KRQ0KxwwJxgzKSEvJR0cHhMaHBEbGw8YGA0eHBIhHxUeIQ8dIA4fGxAhHRIdHg0ZGgodGgoi Hw8bGgweHQ8mGg8sIBVHQCtPSDJKSjJKSjJUSD1XTEBXSjdXSjdYRixUQShJRjNNSTdTRz5XTENf R01YQEZYTkpXTUlMSTtEQTNGQUBRTUxXU1FYVFNJRUZDPj9KQ0lUTFNPUV1MTlpUVFFhYV5ranR3 dX+CgJmAf5dtcn5vdICAg5SJjJ2JkriIkbeChIaLjY6DkqaElKeEibZ+g697ep5ta45UZWs7TFEx Mi45OjUyMTgtLDItLS8tLS8uLzIrLC8wMywzNy8uMjErLy40NzUzNTQxMTEuLi4uLTEsKy8sLyYt MCcnMB0pMh8uMCUuMCUqKyUsLScrLR8rLR8qLygpLicwMC4tLSsoLB8nKx4ZIxMVHg8VGAMQFAAW DAAgFgQ5IQlEKxFcTDlwX0x1cHKEf4CQhJKViZeJiJKDgox+d2tuZ1xtXERiUTpRRDRHOis6NSs5 NCo0MSozMCk5Kis6KywwMS0sLSkqKyMrLCQlLiUlLiUjLykjLyksLCosLCovKSExKyMtKicwLSor KSgrKSgvLCUtKiMxMSctLSMkMB8iLh0uLiEoKBsqLCEmKB0rKCUrKCUvKCMzLCc6LSFBNChPOiJR PCRKPitHOyg8Mi88Mi8tMTIsMDEoLSQtMikpMTAiKikoKzAsLzQmLS4jKisoLC0sMDEwLi0wLi0p KCEpKCEnKBYiIxIqKRcnJhUmJyEnKCIdIhkaHxYgKBofJxkeJBYiKBklIxYlIxYrIxgrIxghIxYj JRchJRgjJxokJhoiJBgeIBUhIxceIhYfIxcgJBgeIhYlIh0lIh0kIRMoJRYrJBUrJBUvISIsHh8f HBcxLik9Mis5LidANyZANyY+NSI4LxwwJxgwJxg3LCQwJh4rKBcsKRgsKRoqJxgwHhIzIRUvIhAy JRMiJBYlJxkxIhoyIxsvKCcoISAfIg8lKBUrJRApIw8qIxMrJBQnJBYnJBYgHhUaGA8VFgwdHxQl HwslHwsfHQodGwkbGQcdGwkdHAwdHAwaGwodHg0bGgogHw4oGg4oGg5HRjFHRjFJSDRIRzNUTU9c VVdbW1hUVFFdVDxYTzhYXFFiZVtnXmpfV2NvXGtvXGtlZ21fYWdaWlpeXl5vcHdwcnhhdHdQY2VI UUxMVU9cXWNkZWteaXlibX1rbYB4eY2Ih7CJiLKCiKx6gKV3h5+AkaqOnbSQnraMncGJmr6OkaKW maqLkrKMlLOMjq+Ii6uIg6F7d5RbX2pDR1E4MzQ4MzQ3MTcyLTIxMTMwMDItMDMrLjEuMDEvMTIm MSooMywwOTMsNC8uMDEvMTIrLzArLzAqKyUvMCosNCcsNCcvMSUtLyMtLSsqKignLCMmKyIoMC0o MC0sKyQtLCUlKBUcHw0YGwoVFwcXHAQTFwETDwAYFQIoFwE0IwlGORlXSShhVFFyZGJ4anV6bXh4 a210aGliXElbVUNUSj5MQzdEOjc+NDE3LSo6MC03MSszLigyLCQyLCQtKyosKikqKSYsKygtLSMn Jx0sKyYtLCcvKyouKikqKyctLioqLSYqLSYoKSMpKiQnLCMkKSArKyApKR4rKiMrKiMsLSksLSkp MComLSckKBwpLSEtKhkvLBsvLRgxLxo+LRY8KxQwLBg4Mx83ODEwMSsjLyklMSseLSYhMCkoMC0k LCkqKCsxLzIjLC8kLTArLjMuMTcwMS0vMCwtLSMsLCIqJyArKCEuKyYrKCMjJSQhIyIcIRoeIxwl JiIjJCAlJh4pKiIqJR8nIhwrJR0qJBwlIBomIRshIxcgIhYjJRkhIxcdJBwdJBwgIhUeIBMfIxYg JBcfIBghIhohIRUkJBcvJCAxJiIlJSUlJSUlJxklJxkwJSEwJSEvKRQ0LhgxLxotKxYrKR8oJhwp JiMrKCUqKh0rKx4qKBsmJBcwIhAuIA8jHg8oIxMmIxImIxIlIBMsJxkmJhwgIBYcIhQfJRYsJRYu JxchIA8nJhQmIBchGxMhGxMgGhIcGA8kIBYpIBEnHg8iHhMiHhMfHREdGw8fHA0gHQ4gHQ4hHg8e Gw4hHhApHRIoHBFeX1haW1RiX2Nyb3N9eZGCfpZyeH9iaG9zb3qEgIyAjZd4hI51d4t6e5CCfqKH g6eEi6F5f5V0epCAh52QlquLkaZviJdTanlaY25YYm1haHVrc4BnfY1wh5d7iaOLmbOSlr6Hi7J6 i6N4iKGHkKKQmauWp76Wp76Qp7iVrL6lqMGWmrKNjqaXmbCiobqfnreQkrBrbotWXGE/RUkuMjEt MTAxMTMtLS8sMjIoLi4yMjIwMDAsLC4vLzEoLyUtNCoxMzAvMS4kLi0kLi0hLScjLykpKygrLSor LCgvMCwwMSkwMSksLi8nKSogKiUgKiUnMCknMCkoLyUoLyUgJAsUFwIPFQQJDwANFgIKEwAOEAIP EgMWEQAkHwg8LA5DMhNFQDdNSD5UTU1bVFRdU0BbUD5YSTRMPSk+PzczNCwxMiwsLScuKyQwLSYq LCkrLSovMCorLCYpKygrLSopKCMoJyIqKSQpKCMtKyEvLSMoMCMiKh0mLikmLikiLCcjLSgnKiMn KiMhMSQeLiEjLCMgKSAnKiUoKyYoLSgoLSgkLikhKyYcMiUdMyYnLCMlKiElKxwpLyAsLiIuMCQq LiIqLiIpMS4nLywdMB4ZLBohKiElLiUvLS4xLzAqKyUuLykkLTAmLzImLTArMjUxMi4uLysrKysr KysmKSApLCMqLSYlKCEiKCYdIyEgJiIeJCAiIx8iIx8hIxgnKR4qKB0mJBkmJyEeHxkqJh0qJh0k JBkiIhciJBkfIRYeJyAZIhsfJB8eIx4fIxchJRkfIBggIRkfIRQfIRQnJRgpJxocJR4gKSIvJxwv JxwnIxkkIBYuJRYvJhYqIwwrJA0oIxMrJhYhJRkeIhYjIRUlIxYmJhsoKB0qJRcpJBYiHRApJBYq JxgnJBYYIxAdKBUYIhQXIRMYIAocJA4iIQkkIwoaHAkfIQ0iIRAcGwskGw4kGw4hHBYkHxkjIBIi HxEfHg4jIhEgIxEXGgobGQ4dGw8iGw0iGw0dGAogGwwgHgsfHQpveo15hJeJkqiUnbOOlaqEi594 iKF6i6OMnbiNnrp3lKt5lq6IkrCSnbuWnseXn8mXn7yRmbaVm8Gepcqdq8mSob6GhJtta4JhaX1n b4N1eomIjZ2Cn7eMqsKUqMmNosKVl6WChJF9gImGiZKSlKuam7Oeoa+nqrisrsOvsMarpsedl7id m7CjorehpsOQlbJ9f5BhY3NEWEozRzooNzUqOTgyMjAyMjArLy4rLy4vNTMpLy0uLi4zMzM1MTI3 MjMzMTIzMTIeMywfNC0pMSQvOCouLykvMCouMSwxNC8tNC4tNC4lLy4mMC8kLismMC0oMywoMywh KB4jKiAkJg4ZGwUPFAUKDwEJCwANDwMMEwYMEwYPGAESGwMpHwgwJg4xLhw5NSNDOi5KQTVJOzFG OC49Oig1MiEuMC8tLy4tMi0qLyoqLyorMCsrLy4rLy4cMSwaLyofKSYjLSoqKyUoKSMoKicpKygm JhstLSIlMCUhLCEcKyQhMCkgLSEgLSEmKBwoKh4oMiQlLyEfLCIeKyEjKiQlLCYlKB8oKyIjLCEj LCEiLh0jLx4qLiIoLCApKyorLSwkLCckLCcrKiMoJyArLSopKyglLCIjKiAlLSgmLikuMC8vMTAn MikrNy0nMSwpMy4oLS8qLzEvNzAtNC4pMzAlLywjKiQlLCYlLCIhKB4lKiMfJB0YJyAYJyAhIR8k JCIgKhwgKhwjJRokJhsdHhoeHxsjJh0iJRwiJBggIhYnIxomIhkhJB8iJSAdHhggIRshIxgiJBkh IhogIRkjJRohIxgmIxUqJxgjJxoeIhYuJxkwKRsmJxUjJBIsJhMrJRIsJhMsJhMpKRInJxAkJx4i JRwiJBYjJRcmJhkgIBQfHxUdHRMdHREfHxMfHxYcHBMcIBUcIBUdIxYZHxMaIxEdJhQjHxQhHRIc HAccHAcfIQ0fIQ0jHA8iGw8iHRAgGw8fHA8kIRMdIA8gIxEjJBUcHQ8fHREgHhIgHhQhHxUiHQ8k HxAjHQglHwqQkrCVl7aQoruMnreGl6qClKaJkLSMkreVmbuRlbeEkKWMl6yIl7aVpcOjrNSmr9au qsOyrseysNaurNKjq8uZocGGhpRubntqbXp3eYeAh5maobOjqs+jqs+ipsmMkLKMhImDe4CDf4uO i5aWkquemrOoqLa0tMKmrNKor9WjpsWfosGjpsKjpsKdnraSlKuIgIZ0bXJIW1g3SEYrNTIxPDky My8xMi4wMC4vLy0wMjEsLi0uLi40NDQzMzUwMDIqMCwnLSkpMisqMywqMC4oLiwvLikyMSwtMCss LyoyLywyLywyMjItLS0vLy8wMDAkLCkkLCkeIhYZHRIZGAkREAMJDAMKDQMPDQMRDwUPEgYKDgMP EgMQEwQjHA0kHQ4qIxMwKRgwJhw0KiAxLS45NDVAOTU6Mi8sLC4vLzEsLi0qLCsrLS4qLC0sLCov Ly0fMC4gMS8jKyYoMCsqKigqKigiLioiLiolLSAnLyIkLCcnLyonLyonLyoqLCAsLiIvKyEuKiAu MiYpLSEhMSgeLiUkKCUmKickIx4oJyItMSUpLSEpMBwnLhooLB8oLB8yLi0vKyohLSsfKykpKCUp KCUnLigjKiQlKykqMC4uMC0pKygoLC0qLi8rMCsxNzEtNCwrMiopMzIoMjEyNDM1ODcxMzAtLywq MSkmLSUhJh8eIxwkJhooKh4lLCQhKCAhJB8kJyIoKSUjJCAkJSEiIx8kIhcjIRYhJRYgJBYmIhgh HRQmIhknIxonIBsrJB8lIxgkIhcjIxYjIxYdIBcdIBcnKCIoKSMnLBojKBYqJBwrJR0uJxkqIxYs IQ0tIg4nJBMqJxYqJxgpJhciIxElJhQlIxklIxklIBMoIxYoJBsiHhYsIBItIRMoHxAoHxAgIBQe HhIgGxYdGBMcIBQeIhYeHhUhIRcpHBYnGhUfHQofHQojHg8iHQ4cHQ8bHA4dGAokHxAlHxYlHxYe HhQgIBYjIRYiIBYhHA4hHA4fGhUgGxYfHA8fHA8kHwgmIQqmqr6jp7ueo7CXnaqLmqyGlaeQnruU or+Wl7CVlq+NjZuVlaOZnrumq8mmt9Sis8+3tMW7uMm8wdi4vNSus86fpb+Cg4dwcnVycn6GhpKO lrOfp8WhrtGdqs2anraIjKN9gId7f4aLh5KVkZ2dmqqoprazrMW3sMmnps6op8+lpcWlpcWmq8al qsWfn66Xl6aQiJl0bX1UWm07QFMyOTcwNzQ3MzAxLisyMy0wMSswMSsvMCovMTIvMTIyMjAyMjAs LisrLSosMC0qLisuMC0pKygwLywvLisuMC8qLCsuKSUvKiYtKyouLCsyLyw1Mi8rLCQmJx8mIxIg HQ0TFAcNDgIIDAAKDgEKDwENEQMRFAQSFQUWDwYWDwYcGBAZFg4YGhAgIhcmKB0qLCErNTQqNDMs LCovLy0sLCosLCopKyooKiktLS0sLCwmKyQoLSYpLSwmKikpKScvLy0oKyQqLSYmLCgnLSkoMiQl LyEjLCUkLSYoLSYnLCUtLSIsLCEuKyQwLSYrLSomKCUkLCsoMC8qLCkpKyggKCMhKSQlKiMqLygl LR8hKRspKiYwMS0vLikpKCMnLCclKiUkKSIlKiMlKiUkKSQiLiwjLy0lLyojLSgqLCsoKiktLS8v LzEpLSorLywqNDEoMi8zNDA4OTQvNTUnLS0wMC4sLColKiMjKCEiJBkmKB0lLBokKxknKBgmJxcp JB4qJR8oJhwkIhgjIRUhHxMgIxMjJhYtJRwnHxYnIRYsJhsmIR0nIh4hIxYlJxkmIREqJRUlJxkj JRcfJB8hJiEgIRIlJhYsJSAmHxotIxsuJBwoJBgjHxQlJB8oJyIqJRclIBMqKBIpJxErIxgoIBYm IR0oIx8iJBkhIxgsIBMuIhUhHg8iHw8eIhQeIhQlHhEnIBMgIhUeIBMiHBUoIholHRMlHRMhGxQh GxQlIBMlIBMfHA8kIRMkHBQoIBckHxshHBgdHBceHRghHhAiHxEjGxMjGxMcHw4cHw4dHQodHQoh HgciHwirrMWjpbydmqiVkqGRlayZnbSaobaVm7CVma+WmrCSjqeVkaqZmq+lpruvr822ttS3usm7 vs2/xtm4v9Kwssehord/e4dzb3p5dISOiZqSlqqhpbiXp7qOnrCLlJV+h4h/h4eJkZGXlaefna+e pbuqsMesrsanqMGeobyho7+ensGiosWjo8Ghob6fn7+VlbSAgqVnaIlMVV4zPEUqOjIlNC01NzA3 ODEtMi0wNTAvMisuMSotNTIsNDEvMzAwNDEpMSwrMy4mMSomMSoxNC8sLyosKisvLS4tMzEtMzEv MistMCktLigxMiwyLyozMCstKiclIh8hIw8VFgQKEAEFCgAKDAALDQAKEQMKEQMNEAAUFwQSFAgV FgoMEQUTGAsfIRQnKRsnKxwpLR4pLjAnLC4lKSgrLy4qLyorMCstLywrLSopKyonKSggLCghLSkr LCQrLCQpLCMqLSQqKyMuLycrKyksLCooLSYrMCksLyoqLSgrLSEuMCQpKx8pKx8uLSgrKiUmKicm KicjKSkmLCwhKyohKyogLSUhLiYqMSspMCojLR8jLR8sLScqKyUnKx4kKBsmJyEoKSMjJyQiJiMn JSQpJyYkLSYkLSYiMikiMiklMS8jLy0sLi0zNTQsOTQsOTQsODAoMywqNC8uOTM0MzoxMDcrMzAo MC0mLSUhKCAkJR0mJx8nJBYnJBYpJBUpJBUmJxUlJhQgJg8eJA4cHw4hJBImIyAlIh8pJiMmIyAo Ix0sJyEkIBYkIBYnJBQlIhIjHg8qJRYsKRcsKRckJBcjIxYnIBAwKRgzJSI1JyQ1KyM0KiIqKh0n JxorIBwuIx8rKBkpJhc0KhEzKRArKRwuLB8oKh4tLyMuKxwnJBYbFw0eGg8hHwofHQkfJBEdIg8i HBIkHhQgIRIdHg8lHBclHBclGxIlGxIjHRMhGxEmHxAfGAogIAwcHAkYGwodIA4kIBUlIRYcHw8Y GwwbGhQeHRYcHRYdHhYaIAobIQseHQ8dHA4nGw8qHhGoqMuXl7qalqyUkKaSlqyUl66bmaial6eU maiQlaWRkKeXlq6lorSsqryyq8e3sM20vMm+xtLBxdm2us6orLaVmaJ7eINwbXh+d4aOh5aNkJ6R lKKRlKWMjp+IjpaGjJSGi5eOlKGbn7Obn7OaocOdo8afm7SZla6NlKiNlKiSjqiSjqiQkK+Ojq6S kbuDgqtyd5JYXXhATEo3QUAuPjcmNS4wNCg3Oy4qNDEpMzAwMy4tMCsrNTArNTAqNDEsNzMmMysn NCwmMiMoNCUyNyovMycnLCcoLSgpNC0oMywqMiUsNCcuLSYuLSYtLyQlJxwpJiMiHxwSHAkIEQEK DwAHDAAKDAANDwENDwEPEQIQEwQOEAIRGAITGgMTFgoaHhAlJxsqLCAnLCUoLSYwMSkuLycsLyYs LyYqMSknLiYrMCcqLyYkLygjLickMSkiLycrLCgsLSkiKyQiKyQoKh4uMCQuKyYrKCMpKyAyNCkr KykqKigoKh4sLiIoKh4tLyMtLiYnKCAoKSMpKiQoKicoKiclLCQiKSEiLSYgKyQlKSgkKCckJyIk JyIpLSAmKh0mKyYmKyYmJhwmJhwkJx4lKB8rKR4qKB0nKiUnKiUlMS0lMS0pMSwrMy4oLycqMSkx NSkxNSkxOiwxOiwrNTQuOTg3Nzc0NDQsMi4oLiojKiAjKiAsKRgrKBcsJRctJhgmIwssKRAmLBMl KxIrKxIrKxInJQ4oJg8uKBUuKBUwKxsyLR0qLBYqLBYxKho0LR0yLhowLBgyKCA4LSU8Lhc+MBk6 MRw4Lxo/LBZEMBk5LB0/MiNFNSg/MCM/MiM/MiM7LSc/MStBNCY/MiRGOCZENSQ9QCw6PSk4PCQ8 QCg8NBs1LhYqIxMhGgsgHggfHQciIgwhIQseHxAeHxAiIBQgHhIjHRMjHRMlGxImHBMiHhMhHRIh HRQhHRQhHwodGwcbHwoeIg0oHhYqIBgfIQ0eIAwbHAsfIA8dIhEXHAwZGgocHQwiGw0iGw0lFhEn GBOjm76dlbeXkauWkKqWl6yam7ClorCoprSen6iXmaKbnqyipbOuq7uzsMGzq86zq86zvNK3wda3 u9Kvs8qeprKHjppybnloZG93coSLhpmGhpSDg5GGg5J+e4t9gpGHjJuGjqWIkaeSn7+MmbiZlriR jrCJfox9cn9+fZF6eY15dY19eZF9fZl7e5d5fZRoa4JbW1tPT083Py4wOSgxOTAwOC8wMyozNy0p MzAoMi8xMTExMTExOzMsNS4rMS0qMCwoMywpNC0wNCgvMyc3NTA1NC8sMSorMCkqNCYqNCYwNCgu MiYyLSs4MjAuLycnKCAnIxceGg8PGAcKFAQKEAEFCgAGCgEKDwUPDAMWEwkWFAsPDgYSFgMTFgQa Fg8jHxYlJiImJyMhLCUnMisuMiUtMSQpMComLScrMiwrMiwnMCklLiclLSgnLyorLyAqLh8kLygi LSYmLColKyksLSUuLycwLCMuKiEuLSYvLicoJicpJygtKyE0MigvLCUuKyQsLiMpKyAqKiAqKiAw LSgtKiUqLCAoKh4pLSopLSorJyotKSwmKyYmKyYoKSEnKCAqJyIrKCMtKCIpJB4jJCAnKCQwKiEy LCMnLyIkLB8pMjUpMjUyMjIxMTEwMy4tMCszMC0yLywuLysvMCwtMTI3Ozw3ODM0NTEzNDAwMS0o KSEuLyc5MyUxLB45KyI/MSg3LR04Lh48LSI9LiNFMys/LiY7MSE7MSFBNCZGOSo9OSQ9OSQ/Oik/ OilFNC9IODJIQDFGPi9OPjJQQDRfPyhdPSZVRjNYSTdiTS1jTi5WUDFTTS5URTJVRjNUQzxWRT5W Q0dYRUleR0ZhSUhcUEVaTkNUUEhVUUlOUENQU0VRTjpHRDA9Oh8yLxYeIgoWGgQiHQ8nIhMYHxcb IhohHxUfHRMcGg8iIBQoIRIiGw0fGxAfGxAgHhQfHRMiIg4eHgojIA8jIA8pHg4mGwsrIxEnHw4i GQwmHQ8mHg0kHAsZHAsaHQweGwwgHQ4mHg0iGgqoo8efmr6emrCdma+jqMOjqMOjp7+ipr6Sn6yR nqujqMOmq8afpb+jqMOmorymoryrqMajob6dnbqQkKx/gJVnaHtoWl5qXGFva4NraH9oaHRpaXVq dYhfan1kbYJyepB+f5Z7fZSAeYiEfYx/eH11bnNfamJXYlpaZWJXY19dZ2VbZGNYaWpcbW5lZWNa WldcUEhPRDw+OzU8OTMoMyorNy01MzI3NDM4Mzc4MzcwMjEzNTQzOjgvNTMtNDgoLzIhMCsjMi0h MCkiMSoxNC0yNS4yMCYyMCYqMyosNSw0NS81NzA5MzExLCokLSQdJh0bHxQPEwkPFgcLEwQNDwEH CgAHCwEJDgMLDAYMDQcQEAcTEwoXEwYcFwogHxonJiEpKCMqKSQrLCYvMCooLyUmLSMsLCwpKSkp KywsLi8oMC0tNTIwOjImLygnKiEtMCcqLSgmKSQqKycqKycvLCkvLCktKx4yMCMwMSE0NSUtMycy OSw5MyU7NSc5MCU6MSYwLywuLSoqLygoLSYqKSQuLSgrLicpLCUqKycrLCgtKyosKikhLCMfKiEr KyAnJxwqKB0vLSIuKSUrJiIgJyEfJiAuLyctLiYgKyQcJyAhKi0kLTAtMjQsMTM0LzQ1MDUzMzU0 NDcxMTEyMjI0LjA4MTNAOC9HPjVMPDBNPTE+NzNEPDlORTtNRDpKRjxFQDdIPjtQRkNaRkZXRERc TkBdT0FOTTlRUDxQTkFXVUhWU0FYVURiVERoWklnVlVjU1FfWFhdVlZpW0pyY1NrYVFrYVFyYlpw YVhwX1NyYVR6ZFZ7ZVdrYV9uY2JpY15lX1tlXF1oXl9nXV5nXV50b2R1cGVubWlvbmp0dGhubmJt ZF5kXFZUU0BHRjQzMRgdGwYhGgwnIBEbGxIfHxYhHxUiIBYjHxQlIRYnIhMjHg8cHBIhIRYiIhcg IBYeIw8eIw8bHgwaHQseHAofHQohHRQiHhUoHA8pHRAjIQ4hHwwiHw8iHw8fHAwkIRAnIgomIQqf n8KWlriSl7KXnbeep8mWn8GVl7OSlbCGl6qMnrCUnb6RmruLkaeIjqWIgouJg4yXi52Mf5GHfY1+ dIRpaG9WVVxMT0VWWk9cW2JTUVhaVVhfW15aYmhVXWNfV2NlXWlra25vb3J0al9rYldtXVNdTkRX SkZUR0NNUUBPVENTVkxTVkxYTVBTR0pURkhPQURIQD1BOjczNTQxMzItNystNysvMzQuMjMvMDgx MjoyNzgzODksNDMsNDMvMjorLjUfLishMC0qMSspMCoyMS41NDEvNCssMSgoLSgrMCswNDEwNDEr LCgwMS0fMSUWKBwaFQsSDQQJCgQKCgQODQEPDgIKEgEJEAAQEAcPDwYOEQcTFgsdFAsgFg4eHRom JSIsLSksLSkwLywxMC0uLSovLiswLi8uLC0hLCwlMDApMCoqMSs1Ny4uLycuLC0sKisnKCIoKSMs KyQwLygzMzM1NTU+OjA/OzFNQTlPRDtFQzdIRjpGQTdHQzhKPS1GOSk+OjA3MikpMS4jKygoKSUr LCgrKicqKSYrKiUrKiUmKyYiJyIeKBogKhwqKh8pKR4rLSItLyQoKyIlKB8jKiAeJRssJyEsJyEk KighJyUhKyYfKSQtLS0yMjIyLTQyLTQqMC4pLy0uMSw0ODI+Ozg/PDlMQzlUSkBdSkRiT0hiUU5k VFBkV1VkV1VVWlZaXlteXlxfX11kXV9lXmFrZFpnX1VpZ1dtaltvaVhya1tzaFhzaFh/a2R5ZV53 aGp5am1vb21ubmt7d2h+eWqIdG2IdG2GeHWAc3B9dHB/d3OJc2eRem6Hfn2CeXiCeG6CeG5/d3WA eHeCen2IgIOMf4CMf4CCgHqDgnuQhICOg3+MeX2HdHhzamllXVxXVUg/PTEqJA8gGgcaGw0dHg8i IBYjIRcpGxEqHBImJBcjIRUjHhogGxcgHBEgHBEiHQ8hHA4kHQ4oIRElJBIgHw4gGRYiGxgcGQwe Gw4fIhAcHw4fHg0gHw4eHgogIAwfHwsiIg6NjayHh6aLjrCSlriLkK+Ch6aGhpSDg5F5hpJ4hJGC hox5fYN0cnNvbW5yXlx5ZWN5bW5tYWJoYl1hW1ZKUURFTD4/UUVAU0ZGSDxOUERVUU5UUE1TT0xR TkpXSFFXSFFQTEpRTUxYT0ZTSUBXRj9XRj9KRkdFQEFDRjxARDo4Pjo8Qz5KPkZGOkFHNz5GNT01 NzI0NTEqNSwpNCszNTI0NzMsMC8rLy4pMTAqMjExMzIwMjErMzItNTQ0MjU1Mzc4NTk0MjUvMi0u MSw3MjM1MTIwMDAxMTEvMTIrLS4yLzQuKzAiLiohLSkgMCMZKRwXFgcNDAALCwQODgYMDAQMDAQM DQEPEAQPEAcODwYPDwwTFBAfFhInHhklJB8oJyIyMC8sKikoKCgrKysnKC0qKzA0MDEwLC0oKikv MTAqMywoMSo0OC4vMiktMCssLyomKyQpLicwLSY3MyxEQDtFQTxEQDtNSURVUUlcWFBTVk5WWlFQ WE9PV05UUUNVU0RQTTxEQDA7PTE0NysyMCYxLyUpLCcmKSQjKiIkKyMoKyQqLSYjKB8jKB8tKyAt KyAsKB4uKiAoKyQoKyQhLCEeKR4rJx0sKB4nKCQmJyMpKygmKCUuLC0vLS4yLi81MTI1NzAyMy08 OTNFQTxKTENMTURiVVBpXFdtW151Y2d7Z2R7Z2R3bXB7cnV0c3h0c3hzeXNvdW96dYR5dIOGenmG enmCem+GfnOLgnCLgnCMe3CUg3iUhoOShIKUiISViYaRg36Qgn2NiH2JhHmUg3iXh3udiYKXhH2S i3+XkISejH6lkoSekYeViH6WjoOUjICWjYeZkImakJSZjpKbjpWdkJaSjoaSjoadkJabjpWVjZKV jZKVhouLe4B5a2dkV1NPPTc4JyEpHxgsIhslIxYlIxYrHxQoHBEfHRMfHRMjGxMmHhYmHxAnIBEl HA8pIBIlHRUjGxMiGA8pHxYlHxUkHhQcHQ8gIRIjIBIeGw4jFwonGw4jGg0iGQwdGg0iHxFzcH5q aHV3dIZ7eYtpZFpnYld0aWhpXl1oZWRnZGNiVVBhVE9hU1dhU1dbTU9YSk1cT09YTExJR0hIRkdD Tj0/Sjo/RzxBST5BQz5FRkFTSEVOREBFP0VBPEFGOkRMP0lJQ0NBOzs/QDw9Pjo+OzVFQTxAQEM6 Ojw4Pjw6QD4zOjgyOTc3OTg6PDs1NDkwLzMyMjAyMjAtOispNScxMC00MzA5NTI4NDEmNS4lNC0n MS4pMzAuMzgrMDQ1MDU1MDU3NTwzMjkuLi4wMDAzLzA0MDEuKis3MjMwMjMqLC0zKzA3LjMjLy0i LiwXIBcQGBAQEwUNDwMPCgYQCgcJCgIICgEJCwALDgANEAcPEggPEgYSFgkdIRMfIxUjLCUjLCUy MC8xLy4yMC8wLi0oKy4qLTAxLS4vKywpKCUvLispLikrMCsxOTIpMCouMSotMCkyNSw1OS85OjNA QTtQTUVXVExqW1BtXVNwZV93a2Vra19ubmJza2tza2tlZWNdXVtcVk9YU0xRVUFDRjM+PC4zMSQt MSUpLSEjKiIkKyMpKiQrLCYoLCAoLCAsKiAqKB4pJhYrKBcnKRsqLB4mLiEkLB8oKh8pKyAlKiMm KyQsKy8qKS0uKisyLi83My46NzE7OS1FQzdHST1QU0ZjXE9nX1NzbWp3cG59d3SEfnuQh4OOhoKO jI2Ni4yQjIaQjIaOkIiSlIySkJ6SkJ6WjJCZjpKmkYujjoiXjouflpKilYuilYuqlJGrlZKzlpKv ko6jlo2nmpGimpCfl42lm4mmnYuqmZCol46nm5WlmZKqlZWumZmnm5qlmZenmpunmpunlZGsmpan l5qomZusnaWrm6OlmqGnnaOqmp+llZqql5mvnZ6olpehjpCSi32AeWtqVltUQEU0KSkpHh4nJRgp JxoqJBEqJBEgJBYeIhQiGhIkHBQiGwwlHg8qHw8oHQ0fFxAkHBUkGxYqIRwhGxEiHBIcHBIeHhQf GQ8hGxEkHBQjGxMmHhQkHBIgGhAgGhAADQEAAAMAAAABAQAAAAEBAAMAAAABAQAAAAECAAMAAAAD AAMAqgEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAACAAMAsAESAAMAAAABAAEAAAEVAAMA AAABAAMAAAEWAAMAAAABAKoAAAEXAAQAAAACAAMAuAEcAAMAAAABAAEAAAFTAAMAAAADAAMAwIdz AAcAABDoAAMAxgAAAAAACAAIAAgAAAAIAAH+CAAB/gAAAQIAAAEAAQABAAAQ6GFwcGwCAAAAbW50 clJHQiBYWVogB9YABAAeABAAAgAkYWNzcEFQUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPbW AAEAAAAA0y1hcHBs8Km3nXI3UvdB2LuwZ9wBHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAOclhZWgAAASwAAAAUZ1hZWgAAAUAAAAAUYlhZWgAAAVQAAAAUd3RwdAAAAWgAAAAUY2hhZAAA AXwAAAAsclRSQwAAAagAAAAOZ1RSQwAAAbgAAAAOYlRSQwAAAcgAAAAOdmNndAAAAdgAAAYSbmRp bgAAB+wAAAY+ZGVzYwAADiwAAABkZHNjbQAADpAAAAH+bW1vZAAAEJAAAAAoY3BydAAAELgAAAAt WFlaIAAAAAAAAF1MAAA01QAAB9tYWVogAAAAAAAAdAUAALP7AAAiflhZWiAAAAAAAAAlhQAAF0sA AKjMWFlaIAAAAAAAAPNSAAEAAAABFs9zZjMyAAAAAAABDEIAAAXe///zJgAAB5IAAP2R///7ov// /aMAAAPcAADAbGN1cnYAAAAAAAAAAQHNAABjdXJ2AAAAAAAAAAEBzQAAY3VydgAAAAAAAAABAc0A AHZjZ3QAAAAAAAAAAAADAQAAAgAAAgQC9wQFBQUGCgcFCAsJCAoNCwgMCg0QDg0PDxAOERASEBMS FBIVFBYXFxgYFhkaGhsbGhwdHR0eHyAeISIiIiMjJCUlJCYmJycpJyopKyosLC0tLi8vLzAxMjEz NDQ0NTQ2Njc3OTo6Ojs7PD09PT49Pz9BQkJCQ0NEQ0VFRkZISElJSklLTExMTk1PT1BQUVJSU1RU VVVWV1dXWFhaWltbXFxdXl5eYGBhYWJiY2RkZGVlZ2doaGlpamlra2xtbW1vcHBwcXJycnNydHR1 dHZ2eHh5eHp6e3t8fH19fn5/f4CAgYGCg4SDhYSGhoeHiIiJiYqKi4uMjI2Njo6Pj5CQkZGSkpOT lJSVlZaWl5eYmJmZmpqbm5ycnZ2enp+foKChoaKio6OkpKWlpiWmpqenqKipqaqqq6usrK2trq6v r7CwsbGysrOztLS1NLW1tra3t7i4ubm6uru7vLy9vb6+v7/AP8DAwcHCwsPDxMTFxcbGx8fIyMlI ycnKysvLzMzNzc5Nzs7Pz9DQ0dHSUdLS09PU1NXV1lXW1tfX2NjZ2dpZ2trb29zc3d3eXd7e39/g 4OHh4uLjYuPj5OTl5ebm5+foZ+jo6enq6uvr7Ozt7e5t7u7v7/Dw8fHy8vNy8/P09PX19vb39/j4 +fn6efr6+/v8/P39/v7/fv//AAACBAL3A3AEBAUJBgQHCggHCQwKBwsJDA8NDA4ODw0QDxEPEhET ERQTFRYWFxcVGBkZGhoZGxwcHB0eHh0fICAgISEiIyMiJSUmJicmKCcpKCoqKyssLS0tLzAwLzEy MjIzMjQ0NTU3ODg4OTk6Ozs7PDs9PT9AQEBBQUJBQ0NEREVFR0ZIR0lKSkpLSkxMTk5PUFBRUVFS UlRVVVVWVldXWFhZWVpbXFtdXV5eX19gYWFhY2NkZGVlZmZnZmhoaWpqamxtbW1ub29vcG9xcXJx c3N0dHV0dnZ3d3l5enp7e3x8fX1+fn9/gICBgIKCg4OEhIWFhoaHh4iIiYmKiouLjIyNjY6Oj4+Q kJGRkpKTk5SUlZWWlpeXmJiZmZqam5ucnJ2dnp6fn6CgoaGioqOjpKSlpaamp6eoqKmpqqqrKqur rKytra6ur6+wsLGxsrKzs7S0tbW2tre3uDe4uLm5urq7u7y8vb2+vr+/wMDBwcLCw8PExMXFxkXG xsfHyMjJycrKy8vMzM1Mzc3Ozs/P0NDR0dLS01LT09TU1dXW1tfX2NjZ2dpZ2trb29zc3d3e3t/f 4F/g4OHh4uLj4+Tk5eXm5udm5+fo6Onp6urr6+zs7e3u7u9u7+/w8PHx8vLz8/T09fX29vd29/f4 +Pn5+vr7+/z8/f3+/v9+//8AAAGCAmUDQAQcBPEFuwaJB1wIMQkHCdUKoQtyDEUNFA3jDrUPhBBR ER4R7hK5E4cUWBUnFfMWvReHGFEZGhngGqobdRw/HQUdyh6PH1UgHSDjIaoibyMwI/ckuSV6Jjwm /ifDKIQpRSoMKswrjSxNLQgtyS6IL0UwBDDFMYUyQzMVM+00wjWWNmw3QDgROOM5tTqHO1k8Lj0I Pdw+sj+JQF5BMEIAQtJDqER9RUlGHUbvR8RImUlrSjpLEEveTK1Nf05MTxlP6lC4UYNST1MbU+dU sVV5VkZXC1fRWJdZYFoqWvJbtlx4XTxeAl7FX4hgUGERYc5ii2NNZA1k02WoZpZnjWiLaXtqdWts bHBtYG5bb0VwOHEsciJzE3QFdPh153bTd7x4pXmUeoN7b3xOfTd+JH8Mf++A1IG8gp+DfoRohUiG KocPh/KI04m0ipaLeYxcjTuOHY79j9yQvpGhkoeTb5RPlS+WEpb4l96YvpmdmoCbZ5xRnTqeHp8D n/Sg9KH6ovuj9qT0pfCm76foqOCp46rdq9SszK3Err6vuLCzsa+yrLOqtKm1qbart664xrnMutO7 2rznvgG/FcAwwUnCbMOVxMHF78cfyFDJiMrSzCPNds7M0CnRmtMS1JHWJtfD2WjbJ90C3ujg7uMZ 5Wbn1uqP7Y/xDvVt+x7//wAAbmRpbgAAAAAAAAY2AACXOAAAVsIAAFQSAACKMAAAJ6sAABaoAABQ DQAAVDkAAiFHAAIR6wABRR4AAwEAAAIAAAABAAMACwAWACUANwBNAGUAgQCfAMEA5QELATUBYQGQ AcEB9QIrAmQCnwLcAxwDXwOjA+oENAR/BM0FHQVGBXAFxAYbBnQGzwctB4wH7ggfCFIIuAkgCYoJ 9gpkCtULRwuBC7wMMgyrDSYNog4hDmEOoQ8kD6kQLxC4EUMRzxIWEl0S7hOAFBUUqxVDFZAV3RZ5 FxcXthhYGKoY/BmhGkga8RucG/McSRz4HageWx8PH2ofxSB9ITch8iKwIw8jbyQwJPMltyZ+J0Yn qygQKNwpqSp5K0osHCzxLVwtxy6gL3kwVTEzMhIy8zPVNEc0uTWgNoc3cThcOUk6ODsoPBo9Dj4D Pn8++z/0QO5B6kLoQ+hE6UXsRvFH90j/SglLFEwhTTBOQE9SUGZRe1KSU6tUxVXhVv9YHlk/WmFb hVyrXdJe+2AlYVJif2TgZhJnR2h8abRq7WwnbWRuom/hcSJyZXOpdO92NnjJehV7Y3yyfgN/VYCp gf+DVoSvhgmIwoohi4GM445Hj6yREpJ7k+SWvJgrmZubDJx/n2qg4aJao9Wmz6hOqc6rUa5ar+Cx abLytgu3mbkpurq94b93wQ7Cp8RBx3vJGcq6zFvN/9FK0vHUm9ZF1/HZn9z/3rHgZOIZ49DnQej8 6rnsdu427/fxufVC9wj40Pqa/GX//wAAAAEAAwALACUANwBNAGUAgQCfAMEA5QELATUBYQGQAcEB 9QIrAmQCnwLcAxwDXwOjA+oENAR/BM0FHQVwBcQGGwZ0Bs8HLQdcB4wH7ghSCLgJIAmKCfYKZArV Cw4LRwu8DDIMqw0mDaIOIQ5hDqEPJA+pEC8QuBFDEc8SFhJdEu4TgBQVFKsVQxXdFisWeRcXF7YY WBj8GaEZ9BpIGvEbnBxJHPgdUB2oHlsfDx/FIH0hNyHyIlEisCNvJDAk8yW3Jn4m4idGKBAo3Cmp KnkrSiwcLPEtXC3HLqAveTBVMTMyEjLzM9U0uTWgNoc3cTfmOFw5STo4Oyg8Gj0OPgM++z/0QO5B 6kLoQ+hE6UXsRvFH90j/SglLFEwhTTBOQE9SUGZRe1KSU6tUxVXhVv9YHlk/WmFbhVyrXdJe+2Al YVJif2OvZOBmEmdHaHxptGrtbCdtZG/hcSJyZXOpdO92Nnd/eMl6FXtjfLJ+A39Vgf+DVoSvhgmH ZYjCiiGLgYzjjkePrJESknuT5Ja8mCuZm5sMnH+d9J9qolqj1aVRps+oTqnOrNSuWq/gsWmy8rR+ tgu5Kbq6vE294b93wQ7EQcXdx3vJGcq6zFvN/9FK0vHUm9ZF1/HZn9tO3P/gZOIZ49DliOdB6Pzq uex27/fxufN89UL3CPjQ+pr8Zf//AAAAAAAGABIAIwA5AFUAdQCZAMEA7gEgAVQBjgHLAgsCUQKb AucDOQOMA+QEQAShBQYFbwXdBkwGvwc4B7UINgi5CUAJygpdCu4LiAwlDMQNZQ4PDrUPZRAXENMR ixJNEw8T0hSeFVkWDxbPF40YURkaGeUasxuEHFUdIh37HtAfqyCMIXIiVyM5JCwlGCYKJvgn7ijr KeIq6SvpLPMuAC8JMB8xNjJPM2o0kTWyNuE4ETlBOnA7qDzrPi0/bkC7Qf9DUkSzRglHZ0i0SdVK 7Uv6TRxONE9RUGFRilKoU91VBlYxV1tYkVnCWvhcNl15XsNgA2FGYo9j7WU7ZoZn42lEaptr/21r bsdwOHGkcw10gnX4d25443pce959Wn7hgGWB54NmhOSGdYgDiYyLEoynjkCP1JFjku6Uf5Yfl7CZ JJqOm/qdep7xoHOh66NwpP+mdqf+qY6rH6ywrkGv07Fksva0h7YZt6q5O7rLvFu9zL9ZwOjCd8QE xXfG+8hyye/LaszVzj/PqNEP0nbT3NVB1p7X59kv2nbbvN0B3kXfeeCl4c/i+eQc5THmROdX6Gjp cepw62PsU+1A7i3vDO/r8LzxjvJW8xvz2/SV9U719vaf90L32/h0+QX5h/oK+o36+vtl+8/8OvyV /OT9NP2D/dP+I/6J/vT/X//J//8AAGRlc2MAAAAAAAAACkNvbG9yIExDRAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAABtbHVjAAAAAAAAAA8AAAAMaXRJVAAAABQAAADEZnJGUgAAAEIAAADYbmJOTwAAABIA AAEaZXNFUwAAABIAAAEsZmlGSQAAABAAAAE+cHRQVAAAABgAAAFOemhUVwAAAA4AAAFmamFKUAAA AA4AAAF0bmxOTAAAABYAAAGCZGVERQAAABAAAAGYa29LUgAAAAwAAAGoZW5VUwAAABIAAAG0c3ZT RQAAABAAAAHGZGFESwAAABwAAAHWemhDTgAAAAwAAAHyAEwAQwBEACAAYwBvAGwAbwByAGkAyQBj AHIAYQBuACAA4AAgAGMAcgBpAHMAdABhAHUAeAAgAGwAaQBxAHUAaQBkAGUAcwAgAGMAbwB1AGwA ZQB1AHIARgBhAHIAZwBlAC0ATABDAEQATABDAEQAIABjAG8AbABvAHIAVgDkAHIAaQAtAEwAQwBE AEwAQwBEACAAYwBvAGwAbwByAGkAZABvX2mCcm2yZnaYb3k6VmgwqzDpMPwAIABMAEMARABLAGwA ZQB1AHIAZQBuAC0ATABDAEQARgBhAHIAYgAtAEwAQwBEzuy37AAgAEwAQwBEAEMAbwBsAG8AcgAg AEwAQwBEAEYA5AByAGcALQBMAEMARABMAEMARAAtAGYAYQByAHYAZQBzAGsA5gByAG1faYJyACAA TABDAEQAAG1tb2QAAAAAAAAGEAAAnFYAAAAAv/h7gAAAAAAAAAAAAAAAAAAAAAB0ZXh0AAAAAENv cHlyaWdodCBBcHBsZSBDb21wdXRlciwgSW5jLiwgMjAwNQAAAAA= item2.X-ABRELATEDNAMES;type=pref:9DE 'D,(1 item2.X-ABLabel:_$!<Assistant>!$_ X-ABUID:C25AB89B-020A-418C-8F48-047834773FC7\:ABPerson END:VCARD tests/auto/versit/qversit/testdata_vcf/AAB5-SingleNonAscii.vcf000066400000000000000000000003701233466112000246610ustar00rootroot00000000000000BEGIN:VCARD VERSION:3.0 N:name;ひらがな;;; FN:ひらがな name ORG:ABC; EMAIL;type=INTERNET;type=WORK;type=pref:a@b.com TEL;type=WORK;type=pref:1234 TEL;type=CELL:5678 X-ABUID:71192A64-301C-420B-AF88-DA4E0CF1B8A4\:ABPerson END:VCARD tests/auto/versit/qversit/testdata_vcf/Entourage11-basic.vcf000066400000000000000000000031661233466112000245260ustar00rootroot00000000000000begin:vcard version:3.0 fn:Firstname Lastname n:Lastname;Firstname;;Title;Suffix nickname:Nickname title:Job title org:Company Name;Department Name note:This is a note field. Pretty boring. url;type=home:http://mywebpage.com url;type=work:http://workwebpage bday:Tue, 22 Dec 2009 adr;type=work;type=pref:;;Work address street part;work city;work state;work zip;work country label;type=work;type=pref:Work address street part\nwork city\, work state work zip\nwork country adr;type=home:;;Home street;home city;home state;home zip;home country label;type=home:Home street\nhome city\, home state home zip\nhome country tel;type=home:homephone tel;type=work:workphone tel;type=work:work2phone tel;type=cell:mobile tel:custom 1 tel:custom 3 phone email;type=internet;type=pref:work@email email;type=internet:home@email end:vcard tests/auto/versit/qversit/testdata_vcf/Entourage11-image.vcf000066400000000000000000026177221233466112000245420ustar00rootroot00000000000000begin:vcard version:3.0 fn: n:;;;; org:Company VCard adr;type=work;type=pref:;;1 work street;workcity;workstate;workzip;workcountry label;type=work;type=pref:1 work street\nworkcity\, workstate workzip\nworkcountry email;type=internet;type=pref:sales@company.com photo;ENCODING=b;type=jpeg: /9j/4AAVSkZJRiABAQAAAQABAAAAAAAAAP/hHYFFeGlmAABJSSoACAAAAAEAKAEDAAEAAAAC AAAAHAAAAAAAAgABAgQAAQAAADoAAAACAgQAAQAAAD0dAAAAAAAA/9j/2wBDAAoHBwgHBgoI CAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9 Pjv/2wBDAQoLCw4NDhwQEBw7KCIoOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7 Ozs7Ozs7Ozs7Ozs7Ozs7Ozv/wAARCADwAUADASEAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAA AAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJx FDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZ WmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLD xMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAA AAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMi MoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldY WVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6 wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD0HFIB tzz1qzETGRTTQA2kNMENqFoR9oWYHBAxQA5jknPWoZII3dXYfMvQjjFMBx5phpANJwatWt8b WORAiMJBjJHSonG6NqbUWUnOW5qNh1FJBJrcrx5CsCcjccVRvWUERhTu7UySixJJDZqM9fqc Uhm1pTwxPmaMzrEd2xxjjFVdRuFl8yRIxGrMdqA9B6VhazRurcpxmsz7Q3PtXIXL75M1sYqw lum+VR711enQ4UU0ht2NuMbUApxGRTMhGOKiPBJxxQMrs+1m9TXPeJZM24GOpFA4mdpA4Y+9 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 o0BFhWZeFJGfStO0zJcH7S6n5QAD3rKXLezN4N20ItRihjIMLDnqAelZj+tOKS2CTb3Iajfk GrRmMPSq9wpMLBeuOPegDj5452c4gYDJIJHSpbdGJLFCpHHNZgk7k2zBzVe4DBSw7dxTbRaK 28xpgZ9/SqF8DjcentU21He6KsbkwsD60icP1pO9w2NW2OVBqY1aJIz1wKAafUSYoPzYz2qM jgmn5CuZt6AIZM8naa5/Q5FWSQMcZbAp9LlrWNjfUhhkGkYAqRSQmYF8ihzxitbTP+PFKJLY roWscU2ktWQ9gxxTTxRe6K0P/9n//gAaICAgICAgICAgICAgICAgICAgICAgICAg/9sAhAAI BgUIDBQaHwYGBwoNHR4cBwcIDBQdIxwHCQsPGiwoHwkLExwiNzQnDBIcICk0OS4ZICcsND08 MyQuMDE4MjQyAQkJDBgyMjIyCQsNITIyMjIMDRwyMjIyMhghMjIyMjIyMjIyMjIyMjIyMjIy MjIyMjIyMjIyMjIyMjIyMjIyMjL/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQ AAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJ ChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SF hoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk 5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAME BwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZ GiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqS k5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery 8/T19vf4+fr/wAARCASwBkADASEAAhEBAxEB/9oADAMBAAIRAxEAPwD1HGP6D1+ho2j39RWl /wCVGLAE9vqPb6im89/rQxMUCkYnv+FPT/IH6eY1X/xz7/8A16UH6/3se+O9TII+Y/659M0n 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 end:vcard tests/auto/versit/qversit/testdata_vcf/Entourage11-nonascii.vcf000066400000000000000000000006601233466112000252440ustar00rootroot00000000000000begin:vcard version:3.0 fn:Friend and personal categories n:;Friend and personal categories;;; adr;type=work;type=pref:;;0R0"`0;;;; label;type=work;type=pref:0R0"`0 email;type=internet;type=pref:home@email end:vcard tests/auto/versit/qversit/testdata_vcf/Entourage12-basic.vcf000066400000000000000000000003151233466112000245200ustar00rootroot00000000000000begin:vcard version:3.0 prodid:Microsoft-Entourage/12.23.0.091001 UID:F3236599-2900-42D3-9969-8F9714C5EE36 fn;charset=utf-8:first last n;charset=utf-8:last;first;;; org;charset=utf-8:Nokia;Qt DF end:vcard tests/auto/versit/qversit/testdata_vcf/Entourage12-kevin.vcf000066400000000000000000000002631233466112000245550ustar00rootroot00000000000000begin:vcard version:3.0 prodid:Microsoft-Entourage/12.23.0.091001 UID:01430B95-6B2D-4A6E-87F1-D165D4F8FA7A fn;charset=utf-8:Kevin Wu Won n;charset=utf-8:Wu Won;Kevin;;; end:vcard tests/auto/versit/qversit/testdata_vcf/Entourage12-nonascii.vcf000066400000000000000000000003271233466112000252450ustar00rootroot00000000000000begin:vcard version:3.0 prodid:Microsoft-Entourage/12.23.0.091001 UID:C92628BF-C7B4-42F5-9A7F-EF9D9EF6CDC3 fn;charset=utf-8:ひらがな name n;charset=utf-8:name;ひらがな;;Mr.; org;charset=utf-8:ABC; end:vcard tests/auto/versit/qversit/testdata_vcf/badwrap.vcf000066400000000000000000001310541233466112000227720ustar00rootroot00000000000000BEGIN:VCARD VERSION:2.1 N:Gump;Forrest FN:Forrest Gump ORG:Bubba Gump Shrimp Co. TITLE:Shrimp Man TEL;FOOBAR;VOICE:+1 (11) 555 1212 TEL;HOME;VOICE:(404) 555-1212 TEL;CELL;VOICE:+358 50 4871 460 ADR;FOOBAR:;;100 Waters Edge;Baytown;LA;30314;United States of America LABEL;WORK;ENCODING=QUOTED-PRINTABLE:100 Waters Edge=0D=0A= Baytown, LA 30314=0D=0A= United States of America ADR;HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America LABEL;HOME;ENCODING=QUOTED-PRINTABLE:42 Plantation St.=0D=0A= Baytown, LA 30314=0D=0A= United States of America X-MS-OL-DEFAULT-POSTAL-ADDRESS:0 EMAIL;PREF;INTERNET:forrestgump@example.com PHOTO;TYPE=JPEG;ENCODING=BASE64: /9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0a HBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIy MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAKIAXcDASIA AhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQA AAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3 ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWm p6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEA AwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSEx BhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElK U1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3 uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD1DTok WD7YUVpnkdUZhnYqnbx6ZIzUja7AjFWvoQwOCCVyKSx/5BMH/XWb/wBGNXnV+3/Exuf+urfzr0Mv wcMS2pO1jyM3zGpglFwSd77nov8Ab9v/AM/8H/fS0f2/b/8AP/B/30teZbqvDRdXIBGl3xB/6d3/ AMK9GWUUI7za+48ePEGKn8NNP7zv/wC37f8A5/4P++lo/t+3/wCf+D/vpa89n0zUbWIy3FhdQxjq 8kLKB+JFVUDyOqIrM7HCqoySfQULKKDV1N/gKXEOKi7SppP5npn9v2//AD/wf99LR/b9v/z/AMH/ AH0ted3NhfWaB7qzuIEJwGliZQT6ciq26hZPRaupP8AlxFiYu0qaX3npy67A7BVvoSxOAAVyanbU 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 1G6ot1G6gOUl3Ubqi3UbqA5SXdRuqLdRuoDlJd1G6ot1G6gOUl3Ubqi3UbqA5SXdRuqLdRuoDlJd 1G6ot1G6gOUl3Ubqi3UbqA5SXdRuqLdRuoDlJd1G6ot1G6gOUl3Ubqi3UbqA5SXdRuqLdRuoDlJd 1Lu5qHdS7qA5S7fNm8f8P5VW3U68fN0/4fyqDdSWwWvqS7qN1RbqN1MOUkIVvvKD9RQFRTkKoPsK j3UbqVgsyXdShuah3UqkswA6k0w5T2XWv+Rcvv8Atn/6GKKXWv8AkXL7/tn/AOhiivhT9DJrL/kF w/8AXSb/ANGGvO/Ecei6nM17p97iVuXQwSYY+oO3rXotl/yC4f8ArpN/6MNeR2n/AB6p+P8AOtsP iJ0Jc8GYYnDU8RHkqIofZpPQ/wDfJ/wo+zSeh/75b/CtaivQ/tiv2X4/5nnf2JQ/ml+H+Rk/ZpPQ /wDfLf4UfZpPQ/8AfLf4VrUUf2xX7L8f8xf2JQ/ml+H+Rk/ZpPQ/98t/hR9mk9D/AN8t/hWtRR/b Ffsvx/zD+xKH80vw/wAjLS3kWRWweDn7p/wpz2krSM3HJJ71pUVUc5q/aivxJlkdL7M3+H+SMv7F L7frR9il9v1rUoqv7Zn/ACon+w4fzv7jL+xS+360fYpfb9a1KKP7Zn/Kg/sOH87+4y/sUvt+tH2K X2/WtSij+2Z/yoP7Dh/O/uMv7FL7frR9il9v1rUoo/tmf8qD+w4fzv7jL+xS+360fYpfb9a1KKP7 Zn/Ig/sOH87+4y/sUvt+tH2KX2/WtSij+2Z/yIP7Dh/O/uMv7FL7frR9il9v1rUoo/tmf8iD+w4f zv7jL+xS+360fYpfb9a1KKP7Zn/Ig/sOH87+4y/sUvt+tH2KX2/WtSij+2Z/yIP7Dh/O/uMv7FL7 frR9il9v1rUoo/tmf8iD+w4fzv7jL+xS+360fYpfb9a1KKP7Zn/Kg/sOH87+4y/sUvt+tH2KX2/W tSij+2Z/yoP7Dh/O/uMv7FL7frR9il9v1rUoo/tmf8qD+w4fzv7jL+xS+360fYpfb9a1KKP7Zn/K g/sOH87+4zZoJJJWYA/98n/Co/s0nof++W/wrWorP+2K/Zfj/maLI6CVuaX4f5GT9mk9D/3y3+FH 2aT0P/fLf4VrUUf2xX7L8f8AMP7EofzS/D/Iyfs0nof++W/wo+zSeh/75b/Ctaij+2K/Zfj/AJh/ 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 /NL8P8j1q4/1Dfh/OiiivJPZP//Z X-MS-OL-DESIGN;CHARSET=utf-8: REV:20100407T092228Z END:VCARD tests/auto/versit/qversit/testdata_vcf/gmail.vcf000066400000000000000000000007131233466112000224400ustar00rootroot00000000000000BEGIN:VCARD VERSION:3.0 FN:Test Contact N:Contact;Test;;; EMAIL;TYPE=INTERNET;TYPE=HOME:a@b.com TEL;TYPE=CELL:1234 ADR;TYPE=HOME:;address;;;;; ORG:company TITLE:title BDAY:1900-01-01 URL;TYPE=HOME:website END:VCARD BEGIN:VCARD VERSION:3.0 FN:Test Contact 2 N:2;Test;Contact;; EMAIL;TYPE=INTERNET;TYPE=HOME:a@b.com TEL;TYPE=CELL:1234 ADR;TYPE=HOME:;address;;;;; ORG:ノキア TITLE:title BDAY:1900-01-01 URL;TYPE=HOME:website END:VCARD tests/auto/versit/qversit/testdata_vcf/test1.vcf000066400000000000000000000003061233466112000224050ustar00rootroot00000000000000BEGIN:VCARD VERSION:2.1 N:;name;;; X-CHILDREN:Child1 X-CHILDREN:Child2,Child3 X-NICKNAME:Nick1 NICKNAME:Nick2 X-NICKNAME:Nick3,Nick4 NICKNAME:Nick5,Nick6 X-ASSISTANT-TEL:1234 GEO:32.0;-64 END:VCARD tests/auto/versit/qversit/tst_qversit.cpp000066400000000000000000000624361233466112000213050ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/versit #include "tst_qversit.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QTCONTACTS_USE_NAMESPACE QTORGANIZER_USE_NAMESPACE QTVERSIT_USE_NAMESPACE QTVERSITORGANIZER_USE_NAMESPACE class MyQVersitResourceHandler : public QVersitResourceHandler { public: MyQVersitResourceHandler() : mIndex(0) { } bool saveResource(const QByteArray& contents, const QVersitProperty& property, QString* location) { Q_UNUSED(property) *location = QString::number(mIndex++); mObjects.insert(*location, contents); return true; } bool loadResource(const QString &location, QByteArray *contents, QString *mimeType) { Q_UNUSED(location) Q_UNUSED(contents) Q_UNUSED(mimeType) return false; } int mIndex; QMap mObjects; }; #ifndef TESTDATA_DIR #define TESTDATA_DIR "./" #endif Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(QContact) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(QOrganizerItem) class tst_QVersit : public QObject { Q_OBJECT private slots: // Tests void testImportVCardFiles(); void testImportVCardFiles_data(); void testBackupVCard(); void testBackupVCard_data(); void testPreserveVCard(); void testPreserveVCard_data(); void testPreserveVCardWithBackup(); void testPreserveVCardWithBackup_data(); void testImportICalFiles(); void testImportICalFiles_data(); private: }; void tst_QVersit::testImportVCardFiles() { QFETCH(QString, filename); QFETCH(QByteArray, charset); QFETCH(QList, expectedContacts); filename = QLatin1String(TESTDATA_DIR "testdata_vcf/") + filename; QVersitReader reader; QFile file(filename); QVERIFY2(file.open(QIODevice::ReadOnly), filename.toLatin1()); reader.setDevice(&file); if (charset != "") { reader.setDefaultCodec(QTextCodec::codecForName(charset)); } QVERIFY(reader.startReading()); QVERIFY(reader.waitForFinished()); QList documents = reader.results(); QVERIFY(!documents.isEmpty()); QCOMPARE(reader.error(), QVersitReader::NoError); QVersitContactImporter importer; MyQVersitResourceHandler resourceHandler; importer.setResourceHandler(&resourceHandler); QVERIFY(importer.importDocuments(documents)); QList contacts = importer.contacts(); QVERIFY(!contacts.isEmpty()); if (expectedContacts.size() > 0) { QCOMPARE(contacts.size(), expectedContacts.size()); QListIterator i(expectedContacts); foreach (QContact parsed, contacts) { QContact expected = i.next(); QList expectedDetails = expected.details(); foreach(QContactDetail expectedDetail, expectedDetails) { QContactDetail::DetailType type = expectedDetail.type(); QContactDetail parsedDetail = parsed.detail(type); if (parsedDetail != expectedDetail) { qDebug() << "Actual:" << parsedDetail.value(1); qDebug() << "Expected:" << expectedDetail.value(1); QCOMPARE(parsedDetail, expectedDetail); } } } } } void tst_QVersit::testImportVCardFiles_data() { QTest::addColumn("filename"); QTest::addColumn("charset"); QTest::addColumn >("expectedContacts"); QTest::newRow("AAB4-MultipleAll.vcf") << QString::fromLatin1("AAB4-MultipleAll.vcf") << QByteArray("UTF-16BE") << QList(); QTest::newRow("AAB4-MultipleAscii.vcf") << QString::fromLatin1("AAB4-MultipleAscii.vcf") << QByteArray("") << QList(); QTest::newRow("AAB4-SingleCompany.vcf") << QString::fromLatin1("AAB4-SingleCompany.vcf") << QByteArray("") << QList(); QTest::newRow("AAB4-SingleExtensive.vcf") << QString::fromLatin1("AAB4-SingleExtensive.vcf") << QByteArray("") << QList(); QTest::newRow("AAB4-SingleNonAscii.vcf") << QString::fromLatin1("AAB4-SingleNonAscii.vcf") << QByteArray("UTF-16BE") << QList(); QTest::newRow("AAB4-SingleNonAsciiWithPhoto.vcf") << QString::fromLatin1("AAB4-SingleNonAsciiWithPhoto.vcf") << QByteArray("UTF-16BE") << QList(); QTest::newRow("AAB5-SingleNonAscii.vcf") << QString::fromLatin1("AAB5-SingleNonAscii.vcf") << QByteArray("UTF-8") << QList(); { QList list; QContact contact; QContactName name; name.setCustomLabel(QLatin1String("Firstname Lastname")); name.setFirstName(QLatin1String("Firstname")); name.setLastName(QLatin1String("Lastname")); name.setPrefix(QLatin1String("Title")); name.setSuffix(QLatin1String("Suffix")); contact.saveDetail(&name); QContactOrganization org; org.setName(QLatin1String("Company Name")); org.setDepartment(QStringList(QLatin1String("Department Name"))); org.setTitle(QLatin1String("Job title")); contact.saveDetail(&org); QContactNote note; note.setNote(QLatin1String("This is a note field. Pretty boring.")); contact.saveDetail(¬e); QContactManagerEngine::setContactDisplayLabel(&contact, QLatin1String("Firstname Lastname")); list.append(contact); QContactUrl homeUrl; homeUrl.setUrl(QLatin1String("http://mywebpage.com")); homeUrl.setContexts(QContactDetail::ContextHome); contact.saveDetail(&homeUrl); QContactUrl workUrl; workUrl.setUrl(QLatin1String("http://workwebpage")); workUrl.setContexts(QContactDetail::ContextWork); contact.saveDetail(&workUrl); QTest::newRow("Entourage11-basic.vcf") << QString::fromLatin1("Entourage11-basic.vcf") << QByteArray("UTF-16BE") << list; } QTest::newRow("Entourage11-image.vcf") << QString::fromLatin1("Entourage11-image.vcf") << QByteArray("UTF-16BE") << QList(); QTest::newRow("Entourage11-nonascii.vcf") << QString::fromLatin1("Entourage11-nonascii.vcf") << QByteArray("UTF-16BE") << QList(); { QList list; QContact contact; QContactName name; name.setCustomLabel(QLatin1String("first last")); name.setFirstName(QLatin1String("first")); name.setLastName(QLatin1String("last")); contact.saveDetail(&name); QContactOrganization org; org.setName(QLatin1String("Nokia")); org.setDepartment(QStringList(QLatin1String("Qt DF"))); contact.saveDetail(&org); QContactManagerEngine::setContactDisplayLabel(&contact, QLatin1String("first last")); list.append(contact); QTest::newRow("Entourage12-basic.vcf") << QString::fromLatin1("Entourage12-basic.vcf") << QByteArray("") << list; } QTest::newRow("Entourage12-kevin.vcf") << QString::fromLatin1("Entourage12-kevin.vcf") << QByteArray("UTF-8") << QList(); QTest::newRow("Entourage12-nonascii.vcf") << QString::fromLatin1("Entourage12-nonascii.vcf") << QByteArray("UTF-8") << QList(); QTest::newRow("gmail.vcf") << QString::fromLatin1("gmail.vcf") << QByteArray("UTF-8") << QList(); { QContact contact; QContactName name; name.setFirstName(QLatin1String("name")); contact.saveDetail(&name); QContactFamily family; family.setChildren(QStringList(QLatin1String("Child1"))); contact.saveDetail(&family); family.setChildren(QStringList(QLatin1String("Child2")) << QLatin1String("Child3")); contact.saveDetail(&family); QContactNickname nickname; nickname.setNickname(QLatin1String("Nick6")); contact.saveDetail(&nickname); nickname.setNickname(QLatin1String("Nick5")); contact.saveDetail(&nickname); nickname.setNickname(QLatin1String("Nick4")); contact.saveDetail(&nickname); nickname.setNickname(QLatin1String("Nick3")); contact.saveDetail(&nickname); nickname.setNickname(QLatin1String("Nick2")); contact.saveDetail(&nickname); nickname.setNickname(QLatin1String("Nick1")); contact.saveDetail(&nickname); QContactPhoneNumber assistantphone; assistantphone.setNumber(QLatin1String("1234")); assistantphone.setSubTypes(QList () << QContactPhoneNumber::SubTypeAssistant); contact.saveDetail(&assistantphone); QContactGeoLocation geo; geo.setLatitude(32.0); geo.setLongitude(-64); contact.saveDetail(&geo); QContactManagerEngine::setContactDisplayLabel(&contact, QLatin1String("name")); QTest::newRow("test1.vcf") << QString::fromLatin1("test1.vcf") << QByteArray("UTF-8") << (QList() << contact); } // A file with bad wrapping (no preceding space on a line continuation) QTest::newRow("badwrap.vcf") << QString::fromLatin1("badwrap.vcf") << QByteArray("UTF-8") << QList(); } void tst_QVersit::testBackupVCard() { // Test that using the backup profile, a contact, when exported and imported again, is unaltered. QFETCH(QContact, contact); QVersitContactExporter exporter(QVersitContactHandlerFactory::ProfileBackup); QVERIFY(exporter.exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QList documents = exporter.documents(); QCOMPARE(documents.size(), 1); QByteArray documentBytes; QVersitWriter writer(&documentBytes); writer.startWriting(documents); writer.waitForFinished(); QVersitReader reader(documentBytes); reader.startReading(); reader.waitForFinished(); QList parsedDocuments = reader.results(); QCOMPARE(parsedDocuments.size(), 1); QVersitContactImporter importer(QVersitContactHandlerFactory::ProfileBackup); QVERIFY(importer.importDocuments(parsedDocuments)); QList contacts = importer.contacts(); QCOMPARE(contacts.size(), 1); if (contacts.first().details() != contact.details()) { qDebug() << "Versit documents:" << documentBytes; qDebug() << "Actual:" << contacts.first(); qDebug() << "Expected:" << contact; QCOMPARE(contacts.first().details(), contact.details()); } } enum Color { RED, GREEN, BLUE }; void tst_QVersit::testBackupVCard_data() { QTest::addColumn("contact"); // The test contacts need at least a name (so the resulting vCard is valid and a display label (because // otherwise, the imported display label will be set from the name or something) { QContact contact; QContactName name; name.setCustomLabel(QLatin1String("name")); contact.saveDetail(&name); QContactManagerEngine::setContactDisplayLabel(&contact, QLatin1String("name")); QTest::newRow("just a name") << contact; } { QContact contact; QContactName name; name.setFirstName(QLatin1String("first")); name.setLastName(QLatin1String("last")); name.setCustomLabel(QLatin1String("custom")); name.setValue(123456, QLatin1String("RandomValue1")); name.setValue(123457, QLatin1String("RandomValue1")); contact.saveDetail(&name); QContactDetail customDetail1(QContactDetail::TypeExtendedDetail); customDetail1.setValue(QContactExtendedDetail::FieldName, QLatin1String("CustomDetail1")); customDetail1.setValue(123456, QLatin1String("Value11")); customDetail1.setValue(123457, QLatin1String("Value12")); contact.saveDetail(&customDetail1); QContactDetail customDetail2(QContactDetail::TypeExtendedDetail); customDetail2.setValue(QContactExtendedDetail::FieldName, QLatin1String("CustomDetail2")); customDetail2.setValue(123456, QLatin1String("Value21")); customDetail2.setValue(123457, QLatin1String("Value22")); contact.saveDetail(&customDetail2); contact.setType(QContactType::TypeContact); QContactManagerEngine::setContactDisplayLabel(&contact, QLatin1String("custom")); QTest::newRow("custom detail grouping") << contact; } // Test of some non-string fields QContact contact; QContactName name; name.setCustomLabel(QLatin1String("name")); contact.saveDetail(&name); QContactManagerEngine::setContactDisplayLabel(&contact, QLatin1String("name")); QContactDetail customDetail(QContactDetail::TypeExtendedDetail); customDetail.setValue(QContactExtendedDetail::FieldName, QLatin1String("CustomDetailBlob")); customDetail.setValue(QContactExtendedDetail::FieldData, QByteArray("blob")); contact.saveDetail(&customDetail); QTest::newRow("binary field") << contact; customDetail.setValue(QContactExtendedDetail::FieldData, QDate(2010, 5, 18)); contact.saveDetail(&customDetail); QTest::newRow("date field") << contact; customDetail.setValue(QContactExtendedDetail::FieldData, QTime(11, 25)); contact.saveDetail(&customDetail); QTest::newRow("time field") << contact; customDetail.setValue(QContactExtendedDetail::FieldData, QDateTime(QDate(2010, 5, 18), QTime(11, 25))); contact.saveDetail(&customDetail); QTest::newRow("datetime field") << contact; customDetail.setValue(QContactExtendedDetail::FieldData, (int)42); contact.saveDetail(&customDetail); QTest::newRow("integer field") << contact; customDetail.setValue(QContactExtendedDetail::FieldData, UINT_MAX); contact.saveDetail(&customDetail); QTest::newRow("unsigned integer field") << contact; customDetail.setValue(QContactExtendedDetail::FieldData, QUrl(QLatin1String("http://www.nokia.com/"))); contact.saveDetail(&customDetail); QTest::newRow("url field") << contact; customDetail.setValue(QContactExtendedDetail::FieldData, (bool)true); contact.saveDetail(&customDetail); QTest::newRow("bool field") << contact; customDetail.setValue(QContactExtendedDetail::FieldData, (bool)false); contact.saveDetail(&customDetail); QTest::newRow("false bool field") << contact; customDetail.setValue(QContactExtendedDetail::FieldData, (double)3.14159265); contact.saveDetail(&customDetail); QTest::newRow("double field") << contact; customDetail.setValue(QContactExtendedDetail::FieldData, (float)3.14159265); contact.saveDetail(&customDetail); QTest::newRow("float field") << contact; customDetail.setValue(QContactExtendedDetail::FieldData, RED); contact.saveDetail(&customDetail); QTest::newRow("enum field") << contact; } /*! Returns a list of properties from \a document that have the name \a propertyName */ QList findPropertiesByName( const QVersitDocument &document, const QString &propertyName) { QList result; foreach (const QVersitProperty& property, document.properties()) { if (property.name() == propertyName) result << property; } return result; } /*! Returns true iff supers.size() == subs.size() and for every i, supers[i] has a superset of the * properties of subs[i]. */ bool areSuperdocuments(const QList& supers, const QList& subs) { if (supers.size() != subs.size()) return false; for (int i = 0; i < supers.size(); i++) { const QVersitDocument& super = supers[i]; const QVersitDocument& sub = subs[i]; if (sub.type() != super.type()) return false; if (sub.componentType() != super.componentType()) return false; foreach (const QVersitProperty& property, sub.properties()) { QList matches(findPropertiesByName(super, property.name())); bool foundMatch = false; foreach (const QVersitProperty& candidate, matches) { if (candidate.valueType() == property.valueType() && candidate.variantValue() == property.variantValue()) { foundMatch = true; } } if (!foundMatch) { return false; } } } return true; } void tst_QVersit::testPreserveVCard() { // Test that using the preserver profile, a document, when imported and exported again, is unaltered. QFETCH(QByteArray, documentBytes); QVersitReader reader(documentBytes); reader.startReading(); reader.waitForFinished(); QList parsedDocuments = reader.results(); QVersitDocument::VersitType versitType = parsedDocuments.first().type(); QVersitContactImporter importer("Preserve"); QVERIFY(importer.importDocuments(parsedDocuments)); QList contacts = importer.contacts(); QVersitContactExporter exporter("Preserve"); QVERIFY(exporter.exportContacts(contacts, versitType)); QList documents = exporter.documents(); QByteArray writtenBytes; QVersitWriter writer(&writtenBytes); writer.startWriting(documents); writer.waitForFinished(); if (!areSuperdocuments(documents, parsedDocuments)) { qDebug() << "Contacts:" << contacts; qDebug() << "Actual:" << writtenBytes; qDebug() << "Expected:" << documentBytes; QVERIFY2(false, "Actual vCards aren't supersets of Expected vCards"); } } void tst_QVersit::testPreserveVCard_data() { QTest::addColumn("documentBytes"); QTest::newRow("basic") << QByteArray( "BEGIN:VCARD\r\n" "VERSION:2.1\r\n" "FN:John\r\n" "END:VCARD\r\n"); QTest::newRow("custom property") << QByteArray( "BEGIN:VCARD\r\n" "VERSION:2.1\r\n" "FN:John\r\n" "X-SOMETHING:Nothing\r\n" "END:VCARD\r\n"); } void tst_QVersit::testPreserveVCardWithBackup() { // Test that the preserver and backup profile work well together QFETCH(QByteArray, documentBytes); // Read them QVersitReader reader(documentBytes); reader.startReading(); reader.waitForFinished(); QList parsedDocuments = reader.results(); // Import them QVersitDocument::VersitType versitType = parsedDocuments.first().type(); QVersitContactImporter importer(QStringList() << "Preserve" << QVersitContactHandlerFactory::ProfileBackup); QVERIFY(importer.importDocuments(parsedDocuments)); QList contacts = importer.contacts(); // Make a little change QVERIFY(contacts.size() >= 1); QContactDetail customDetail(QContactDetail::TypeExtendedDetail); customDetail.setValue(QContactExtendedDetail::FieldName, QLatin1String("CUSTOMDETAIL")); customDetail.setValue(QContactExtendedDetail::FieldData, QLatin1String("value")); contacts[0].saveDetail(&customDetail); // Export them QVersitContactExporter exporter(QStringList() << QVersitContactHandlerFactory::ProfileBackup << "Preserve"); QVERIFY(exporter.exportContacts(contacts, versitType)); QList documents = exporter.documents(); QByteArray writtenBytes; QVersitWriter writer(&writtenBytes); writer.startWriting(documents); writer.waitForFinished(); if (!areSuperdocuments(documents, parsedDocuments)) { qDebug() << "Contacts:" << contacts; qDebug() << "Actual:" << writtenBytes; qDebug() << "Expected:" << documentBytes; QVERIFY2(false, "Actual vCards aren't supersets of Expected vCards"); } // Import them (again) QVERIFY(importer.importDocuments(documents)); QList contacts2 = importer.contacts(); if (contacts != contacts2) { qDebug() << "Versit documents:" << writtenBytes; qDebug() << "Actual:" << contacts2; qDebug() << "Expected:" << contacts; QVERIFY(contacts == contacts2); } } void tst_QVersit::testPreserveVCardWithBackup_data() { testPreserveVCard_data(); } void tst_QVersit::testImportICalFiles() { QFETCH(QString, filename); QFETCH(QByteArray, charset); QFETCH(QList, expectedItems); filename = QLatin1String(TESTDATA_DIR "testdata_ics/") + filename; QVersitReader reader; QFile file(filename); QVERIFY2(file.open(QIODevice::ReadOnly), filename.toLatin1()); reader.setDevice(&file); if (charset != "") { reader.setDefaultCodec(QTextCodec::codecForName(charset)); } QVERIFY(reader.startReading()); QVERIFY(reader.waitForFinished()); QList documents = reader.results(); QCOMPARE(reader.error(), QVersitReader::NoError); QCOMPARE(documents.size(), 1); QVersitOrganizerImporter importer; QVERIFY(importer.importDocument(documents.first())); QList items = importer.items(); if (expectedItems.size() > 0) { QCOMPARE(items.size(), expectedItems.size()); QListIterator i(expectedItems); foreach (QOrganizerItem parsed, items) { QOrganizerItem expected = i.next(); QList expectedDetails = expected.details(); foreach(QOrganizerItemDetail expectedDetail, expectedDetails) { QOrganizerItemDetail::DetailType name = expectedDetail.type(); QOrganizerItemDetail parsedDetail = parsed.detail(name); if (parsedDetail != expectedDetail) { qDebug() << "Detail: " << name; qDebug() << "Actual:" << parsedDetail.values(); qDebug() << "Expected:" << expectedDetail.values(); QCOMPARE(parsedDetail, expectedDetail); } } } } } void tst_QVersit::testImportICalFiles_data() { QTest::addColumn("filename"); QTest::addColumn("charset"); QTest::addColumn >("expectedItems"); QTest::newRow("2010-FIFA-WorldCup.ics") << QString::fromLatin1("2010-FIFA-WorldCup.ics") << QByteArray("") << QList(); QTest::newRow("2010-India-Holidays.ics") << QString::fromLatin1("2010-India-Holidays.ics") << QByteArray("") << QList(); QTest::newRow("2010-US-Holidays.ics") << QString::fromLatin1("2010-US-Holidays.ics") << QByteArray("") << QList(); QTest::newRow("Asiacup2010.ics") << QString::fromLatin1("Asiacup2010.ics") << QByteArray("") << QList(); QTest::newRow("FinlandHolidays.ics") << QString::fromLatin1("FinlandHolidays.ics") << QByteArray("") << QList(); QTest::newRow("india2010.ics") << QString::fromLatin1("india2010.ics") << QByteArray("") << QList(); QTest::newRow("IndiaHolidays.ics") << QString::fromLatin1("IndiaHolidays.ics") << QByteArray("") << QList(); QTest::newRow("worldt2009.ics") << QString::fromLatin1("worldt2009.ics") << QByteArray("") << QList(); } QTEST_MAIN(tst_QVersit) #include "tst_qversit.moc" tests/auto/versit/qversit/tst_qversit.h000066400000000000000000000043221233466112000207400ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef tst_QVERSIT_H #define tst_QVERSIT_H #include #include QT_BEGIN_NAMESPACE_VERSIT class QVersitContactImporter; class QVersitContactImporterPrivate; class QVersitReader; QT_END_NAMESPACE_VERSIT QTVERSIT_USE_NAMESPACE #endif // tst_QVERSIT_H tests/auto/versit/qversitcontactexporter/000077500000000000000000000000001233466112000213445ustar00rootroot00000000000000tests/auto/versit/qversitcontactexporter/qversitcontactexporter.pro000066400000000000000000000002731233466112000267320ustar00rootroot00000000000000include(../../auto.pri) QT += contacts versit versit-private HEADERS += tst_qversitcontactexporter.h SOURCES += tst_qversitcontactexporter.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/versit/qversitcontactexporter/tst_qversitcontactexporter.cpp000066400000000000000000002110361233466112000276070ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/versit #include "tst_qversitcontactexporter.h" #include #include #include #include #include #include #include #include #include #include QTCONTACTS_USE_NAMESPACE QTVERSIT_USE_NAMESPACE class MyQVersitContactExporterDetailHandler : public QVersitContactExporterDetailHandler { public: MyQVersitContactExporterDetailHandler() : mPreProcess(false) { } bool preProcessDetail(const QContact& contact, const QContactDetail& detail, QVersitDocument* document) { Q_UNUSED(contact) Q_UNUSED(document) mPreProcessedDetails.append(detail); return mPreProcess; } bool postProcessDetail(const QContact& contact, const QContactDetail& detail, bool alreadyProcessed, QVersitDocument* document) { Q_UNUSED(contact) Q_UNUSED(document) if (!alreadyProcessed) mUnknownDetails.append(detail); else mPostProcessedDetails.append(detail); return false; } void clear() { mPreProcess = false; mDefinitionNamesToProcess.clear(); mUnknownDetails.clear(); mPreProcessedDetails.clear(); mPostProcessedDetails.clear(); } // a hook to control what preProcess returns: bool mPreProcess; QStringList mDefinitionNamesToProcess; QList mUnknownDetails; QList mPreProcessedDetails; QList mPostProcessedDetails; }; /* This class just logs the arguments to the last call to postProcessDetail */ class MyQVersitContactExporterDetailHandlerV2 : public QVersitContactExporterDetailHandlerV2 { public: MyQVersitContactExporterDetailHandlerV2() { } void detailProcessed(const QContact& contact, const QContactDetail& detail, const QVersitDocument& document, QSet* processedFields, QList* toBeRemoved, QList* toBeAdded) { mContact = contact; mDetail = detail; mDocument = document; mProcessedFields = *processedFields; mToBeRemoved = *toBeRemoved; mToBeAdded = *toBeAdded; } void contactProcessed(const QContact& contact, QVersitDocument* document) { mEndContact = contact; mEndDocument = *document; } QContact mContact; QContactDetail mDetail; QSet mProcessedFields; QVersitDocument mDocument; QList mToBeRemoved; QList mToBeAdded; QContact mEndContact; QVersitDocument mEndDocument; }; class MyQVersitResourceHandler : public QVersitResourceHandler { public: MyQVersitResourceHandler() : mLoadResourceCalled(false), mLoadSuccess(true) { } bool loadResource(const QString& location, QByteArray* contents, QString* mimeType) { mLocation = location; *contents = mSimulatedData; *mimeType = mSimulatedMimeType; mLoadResourceCalled = true; return mLoadSuccess; } bool saveResource(const QByteArray &contents, const QVersitProperty &property, QString *location) { Q_UNUSED(contents) Q_UNUSED(property) Q_UNUSED(location) return false; } void clear() { mSimulatedData.clear(); mSimulatedMimeType.clear(); mLocation.clear(); mLoadResourceCalled = false; mLoadSuccess = true; } QByteArray mSimulatedData; QString mSimulatedMimeType; QString mLocation; bool mLoadResourceCalled; bool mLoadSuccess; // A hook to control what loadResource returns. }; const static QByteArray SAMPLE_GIF(QByteArray::fromBase64( "R0lGODlhEgASAIAAAAAAAP///yH5BAEAAAEALAAAAAASABIAAAIdjI+py+0G" "wEtxUmlPzRDnzYGfN3KBaKGT6rDmGxQAOw==")); const QString TEST_PHOTO_FILE(QStringLiteral("versitTest001.jpg")); const QString TEST_AUDIO_FILE(QStringLiteral("versitTest001.wav")); // Checks that the property has a value of the given expectedType and the given expectedValue. #define CHECK_VALUE(property,expectedValueType,expectedValue) {\ QCOMPARE(property.valueType(), expectedValueType); \ QVariant value = property.variantValue(); \ QCOMPARE(value.type(), QVariant::StringList); \ QCOMPARE(value.toStringList(), expectedValue); \ } void tst_QVersitContactExporter::init() { mExporter = new QVersitContactExporter(); mResourceHandler = new MyQVersitResourceHandler; mExporter->setResourceHandler(mResourceHandler); } void tst_QVersitContactExporter::cleanup() { QVERIFY(mExporter->resourceHandler() == mResourceHandler); mExporter->setResourceHandler(0); delete mResourceHandler; delete mExporter; } void tst_QVersitContactExporter::testConvertContact() { QContact contact; // Adding name to the contact QContactName name; name.setFirstName(QStringLiteral("Moido")); contact.saveDetail(&name); // Adding phone number to the Contact. QContactPhoneNumber phoneNumber; phoneNumber.setNumber(QStringLiteral("12345678")); contact.saveDetail(&phoneNumber); // Convert contact into versit properties QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QList documents = mExporter->documents(); // Contact should have N and TEL properties QCOMPARE(documents.size(), 1); QCOMPARE(documents.first().properties().count(), 2); } void tst_QVersitContactExporter::testEmptyContact() { QContact contact1; // empty QList contacts; contacts << contact1; QVERIFY(!mExporter->exportContacts(contacts)); // Fail on empty contact1 QMap errorMap = mExporter->errorMap(); QCOMPARE(errorMap.size(), 1); QList documents = mExporter->documents(); QCOMPARE(documents.size(), 0); // empty contact not exported } void tst_QVersitContactExporter::testContactDetailHandler() { int invalidData = 123456; MyQVersitContactExporterDetailHandler detailHandler;; mExporter->setDetailHandler(&detailHandler); // Test1: Un-supported Avatar Test QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactDetail unknownDetail; unknownDetail.setValue(invalidData, QStringLiteral("Detail")); contact.saveDetail(&unknownDetail); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 0); QList unknownDetails = detailHandler.mUnknownDetails; QVERIFY(unknownDetails.size() > 0); QContactDetail::DetailType type = unknownDetail.type(); QContactDetail detail = findDetailByType(unknownDetails,type); QCOMPARE(type, detail.type()); // Test2: Un-supported Online Account QContactOnlineAccount onlineAccount; QString testUri = QStringLiteral("sip:abc@temp.com"); onlineAccount.setAccountUri(testUri); QList expectedSubtypes; expectedSubtypes << invalidData; onlineAccount.setSubTypes(expectedSubtypes); contact.saveDetail(&onlineAccount); detailHandler.clear(); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 0); unknownDetails = detailHandler.mUnknownDetails; QVERIFY(unknownDetails.size() > 0); type = onlineAccount.type(); detail = findDetailByType(unknownDetails, type); QCOMPARE(type, detail.type()); QVERIFY(mExporter->detailHandler() == &detailHandler); mExporter->setDetailHandler(static_cast(0)); } void tst_QVersitContactExporter::testContactDetailHandlerV2() { int invalidData = 123456; MyQVersitContactExporterDetailHandlerV2 detailHandler; mExporter->setDetailHandler(&detailHandler); QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactPhoneNumber phone; phone.setNumber(QStringLiteral("1234")); phone.setValue(invalidData, QStringLiteral("Value")); contact.saveDetail(&phone); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QCOMPARE(detailHandler.mProcessedFields.size(), 3); QVERIFY(detailHandler.mProcessedFields.contains(QContactPhoneNumber::FieldContext)); QVERIFY(detailHandler.mProcessedFields.contains(QContactPhoneNumber::FieldSubTypes)); QVERIFY(detailHandler.mProcessedFields.contains(QContactPhoneNumber::FieldNumber)); QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("TEL")); expectedProperty.setValue(QStringLiteral("1234")); QCOMPARE(detailHandler.mToBeAdded.size(), 1); QCOMPARE(detailHandler.mToBeAdded.first(), expectedProperty); mExporter->setDetailHandler(static_cast(0)); } void tst_QVersitContactExporter::testEncodeName() { QContact contact; QContactName name; // Test 1: An empty contact - can't be exported QVERIFY(!mExporter->exportContacts(QList() << contact, QVersitDocument::VCard21Type)); QCOMPARE(mExporter->documents().size(), 0); // Test 2: Special characters are NOT backslash escaped by the exporter, only by the writer. name.setFirstName(QStringLiteral("Fi;rst")); // check that semicolon is left intact name.setLastName(QStringLiteral("Last")); name.setMiddleName(QStringLiteral("Middle")); name.setPrefix(QStringLiteral("Prefix")); name.setSuffix(QStringLiteral("Suffix")); contact.saveDetail(&name); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard21Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(document.properties().count(), 1); QVersitProperty nameProperty = findPropertyByName(document, QStringLiteral("N")); QCOMPARE(nameProperty.parameters().count(), 0); QCOMPARE(nameProperty.name(), QStringLiteral("N")); CHECK_VALUE(nameProperty, QVersitProperty::CompoundType, QStringList() << QStringLiteral("Last") << QStringLiteral("Fi;rst") << QStringLiteral("Middle") << QStringLiteral("Prefix") << QStringLiteral("Suffix")); // Test 3: Just QContactName contact.clearDetails(); name = QContactName(); name.setFirstName(QStringLiteral("First")); name.setLastName(QStringLiteral("Last")); name.setMiddleName(QStringLiteral("Middle")); contact.saveDetail(&name); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard21Type)); document = mExporter->documents().first(); QCOMPARE(document.properties().count(), 1); // Unlike older versions, FN is not generated nameProperty = findPropertyByName(document, QStringLiteral("N")); QCOMPARE(nameProperty.name(), QStringLiteral("N")); CHECK_VALUE(nameProperty, QVersitProperty::CompoundType, QStringList() << QStringLiteral("Last") << QStringLiteral("First") << QStringLiteral("Middle") << QString() << QString()); // Test 4: Just nickname contact.clearDetails(); QContactNickname nickname; nickname.setNickname(QStringLiteral("Nickname")); contact.saveDetail(&nickname); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard21Type)); document = mExporter->documents().first(); QCOMPARE(document.properties().count(), 2); // N and NICKNAME // Unlike older versions, FN is not generated nameProperty = findPropertyByName(document, QStringLiteral("N")); QCOMPARE(nameProperty.name(), QStringLiteral("N")); CHECK_VALUE(nameProperty, QVersitProperty::CompoundType, QStringList() << QString() << QString() << QString() << QString() << QString()); // Test 5: Just organization contact.clearDetails(); QContactOrganization org; org.setName(QStringLiteral("Organization")); contact.saveDetail(&org); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard21Type)); document = mExporter->documents().first(); QCOMPARE(document.properties().count(), 2); // N and ORG // Unlike older versions, FN is not generated nameProperty = findPropertyByName(document, QStringLiteral("N")); QCOMPARE(nameProperty.name(), QStringLiteral("N")); CHECK_VALUE(nameProperty, QVersitProperty::CompoundType, QStringList() << QString() << QString() << QString() << QString() << QString()); // Test 6: Display label but no name set contact.clearDetails(); QContactDisplayLabel displaylabel; displaylabel.setLabel(QStringLiteral("Bobby Tables")); contact.saveDetail(&displaylabel); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard21Type)); document = mExporter->documents().first(); QCOMPARE(document.properties().count(), 2); // N // Unlike older versions, FN is not generated from display label nameProperty = findPropertyByName(document, QStringLiteral("N")); QCOMPARE(nameProperty.name(), QStringLiteral("N")); CHECK_VALUE(nameProperty, QVersitProperty::CompoundType, QStringList() << QString() << QString() << QString() << QString() << QString()); } void tst_QVersitContactExporter::testEncodePhoneNumber() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactPhoneNumber phoneNumber; QContactPhoneNumber::SubType expectedSubType = QContactPhoneNumber::SubTypeMobile; QList expectedSubTypes; expectedSubTypes << expectedSubType; phoneNumber.setNumber(QStringLiteral("12345678")); phoneNumber.setContexts(QContactDetail::ContextHome); phoneNumber.setSubTypes(expectedSubTypes); contact.saveDetail(&phoneNumber); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); QVersitProperty property = findPropertyByName(document, QStringLiteral("TEL")); QVERIFY(!property.isEmpty()); // Check parameters QCOMPARE(property.parameters().count(), 2); QVERIFY(property.parameters().contains( QStringLiteral("TYPE"),QStringLiteral("HOME"))); QVERIFY(property.parameters().contains( QStringLiteral("TYPE"),QStringLiteral("CELL"))); // Check value QCOMPARE(property.value(), phoneNumber.number()); QContactPhoneNumber assistantNumber; assistantNumber.setNumber(QStringLiteral("4321")); assistantNumber.setContexts(QContactDetail::ContextWork); expectedSubTypes.clear(); expectedSubTypes << QContactPhoneNumber::SubTypeAssistant << QContactPhoneNumber::SubTypeLandline; assistantNumber.setSubTypes(expectedSubTypes); contact.saveDetail(&assistantNumber); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 2); property = findPropertyByName(document, QStringLiteral("X-ASSISTANT-TEL")); QVERIFY(!property.isEmpty()); QCOMPARE(property.parameters().count(), 2); QVERIFY(property.parameters().contains( QStringLiteral("TYPE"),QStringLiteral("WORK"))); QVERIFY(property.parameters().contains( QStringLiteral("TYPE"), QStringLiteral("ISDN"))); QCOMPARE(property.value(), assistantNumber.number()); } void tst_QVersitContactExporter::testEncodeEmailAddress() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactEmailAddress email; email.setEmailAddress(QStringLiteral("test@test")); email.setContexts(QContactDetail::ContextHome); contact.saveDetail(&email); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); QVersitProperty property = findPropertyByName(document, QStringLiteral("EMAIL")); QVERIFY(!property.isEmpty()); // Check parameters QCOMPARE(property.parameters().count(), 1); QVERIFY(property.parameters().contains( QStringLiteral("TYPE"),QStringLiteral("HOME"))); // Check value QCOMPARE(property.value(), email.emailAddress()); } void tst_QVersitContactExporter::testEncodeStreetAddress() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactAddress address; QContactAddress::SubType expectedSubType; QList expectedSubTypes; expectedSubType = QContactAddress::SubTypePostal; expectedSubTypes << expectedSubType; address.setPostOfficeBox(QStringLiteral("1234")); address.setCountry(QStringLiteral("Finland")); address.setPostcode(QStringLiteral("00440")); // Special characters are not escaped by the exporter, but by the writer address.setStreet(QStringLiteral("HKKI; 1X 90")); address.setLocality(QStringLiteral("Helsinki")); address.setContexts(QContactDetail::ContextHome); address.setSubTypes(expectedSubTypes); contact.saveDetail(&address); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard21Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); QVersitProperty property = findPropertyByName(document, QStringLiteral("ADR")); QVERIFY(!property.isEmpty()); // Check parameters QCOMPARE(property.parameters().count(), 2); QVERIFY(property.parameters().contains( QStringLiteral("TYPE"),QStringLiteral("HOME"))); QVERIFY(property.parameters().contains( QStringLiteral("TYPE"),QStringLiteral("POSTAL"))); // Check name QCOMPARE(property.name(), QStringLiteral("ADR")); CHECK_VALUE(property, QVersitProperty::CompoundType, QStringList() << QStringLiteral("1234") << QString() << QStringLiteral("HKKI; 1X 90") << QStringLiteral("Helsinki") << QString() << QStringLiteral("00440") << QStringLiteral("Finland")); } void tst_QVersitContactExporter::testEncodeUrl() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactUrl url; url.setUrl(QStringLiteral("http://www.myhome.com")); url.setContexts(QContactDetail::ContextHome); url.setSubType(QContactUrl::SubTypeHomePage); contact.saveDetail(&url); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); QVersitProperty property = findPropertyByName(document, QStringLiteral("URL")); QVERIFY(!property.isEmpty()); // Check parameters QCOMPARE(property.parameters().count(), 1); QVERIFY(property.parameters().contains( QStringLiteral("TYPE"),QStringLiteral("HOME"))); // Check value QCOMPARE(property.value(), url.url()); } void tst_QVersitContactExporter::testEncodeUid() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactGuid guid; guid.setContexts(QContactDetail::ContextHome); guid.setGuid(QStringLiteral("0101222")); contact.saveDetail(&guid); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard21Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); QVersitProperty property = findPropertyByName(document, QStringLiteral("UID")); QVERIFY(!property.isEmpty()); // Check parameters // Contexts are not allowed for UID QCOMPARE(property.parameters().count(), 0); // Check value QCOMPARE(property.value(), guid.guid()); } void tst_QVersitContactExporter::testEncodeRev() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactTimestamp timeStamp; // Last modified time found QDateTime revisionTime = QDateTime::fromString( QStringLiteral("M1d1y200906:01:02"), QStringLiteral("'M'M'd'd'y'yyyyhh:mm:ss")); revisionTime.setTimeSpec(Qt::UTC); timeStamp.setLastModified(revisionTime); // Contexts not allowed in REV property, check that they are not added timeStamp.setContexts(QContactDetail::ContextHome); contact.saveDetail(&timeStamp); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); QVersitProperty property = findPropertyByName(document, QStringLiteral("REV")); QVERIFY(!property.isEmpty()); QCOMPARE(property.parameters().count(), 0); QString expectedValueUTCEncoded = QStringLiteral("2009-01-01T06:01:02Z"); QCOMPARE(property.value(), expectedValueUTCEncoded); // Last modified time not found, use the creation time QDateTime emptyTime; timeStamp.setLastModified(emptyTime); timeStamp.setCreated(revisionTime); contact.saveDetail(&timeStamp); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); property = findPropertyByName(document, QStringLiteral("REV")); QVERIFY(!property.isEmpty()); QCOMPARE(property.value(), expectedValueUTCEncoded); // Last modified time found, Local Time spec not UTC QDateTime localTime; revisionTime.setTimeSpec(Qt::LocalTime); timeStamp.setLastModified(revisionTime); localTime.setTimeSpec(Qt::LocalTime); timeStamp.setCreated(localTime); contact.saveDetail(&timeStamp); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); property = findPropertyByName(document, QStringLiteral("REV")); QVERIFY(!property.isEmpty()); QString expectedValueEncoded = QStringLiteral("2009-01-01T06:01:02"); QCOMPARE(property.value(), expectedValueEncoded); // Last modified time not found, creation time not found timeStamp.setLastModified(emptyTime); timeStamp.setCreated(emptyTime); contact.saveDetail(&timeStamp); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 0); } void tst_QVersitContactExporter::testEncodeVersion() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactVersion version; int sequenceNumber = 4711; QByteArray extendedVersion("134f23dbb2"); version.setSequenceNumber(sequenceNumber); version.setExtendedVersion(extendedVersion); // Contexts not allowed in X-QTPROJECT-VERSION property, check that they are not added. version.setContexts(QContactDetail::ContextHome); contact.saveDetail(&version); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); QVersitProperty property = findPropertyByName(document, QStringLiteral("X-QTPROJECT-VERSION")); QVERIFY(!property.isEmpty()); QCOMPARE(property.parameters().count(), 0); CHECK_VALUE(property, QVersitProperty::CompoundType, QStringList() << QStringLiteral("4711") << QStringLiteral("134f23dbb2")); } void tst_QVersitContactExporter::testEncodeBirthDay() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QDate date(2009,1,1); QContactBirthday birthDay; birthDay.setDate(date); // Contexts not allowed in BDAY property, check that they are not added birthDay.setContexts(QContactDetail::ContextHome); contact.saveDetail(&birthDay); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); QVersitProperty property = findPropertyByName(document, QStringLiteral("BDAY")); QVERIFY(!property.isEmpty()); QCOMPARE(property.parameters().count(), 0); QCOMPARE(property.value(), QStringLiteral("2009-01-01")); birthDay.setDateTime(QDateTime(QDate(2009, 1, 1), QTime(1, 2, 3))); contact.saveDetail(&birthDay); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); property = findPropertyByName(document, QStringLiteral("BDAY")); QCOMPARE(property.value(), QStringLiteral("2009-01-01T01:02:03")); } void tst_QVersitContactExporter::testEncodeNote() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactNote note; note.setNote(QStringLiteral("My Note")); // Contexts not allowed in NOTE property, check that they are not added note.setContexts(QContactDetail::ContextHome); contact.saveDetail(¬e); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); QVersitProperty property = findPropertyByName(document, QStringLiteral("NOTE")); QVERIFY(!property.isEmpty()); QCOMPARE(property.parameters().count(), 0); QCOMPARE(property.value(), note.note()); } void tst_QVersitContactExporter::testEncodeGeoLocation() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactGeoLocation geoLocation; QString longitude = QStringLiteral("99.9"); geoLocation.setLongitude(longitude.toDouble()); QString latitude = QStringLiteral("98.9"); geoLocation.setLatitude(latitude.toDouble()); // Contexts not allowed in GEO property, check that they are not added geoLocation.setContexts(QContactDetail::ContextHome); contact.saveDetail(&geoLocation); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); QVersitProperty property = findPropertyByName(document, QStringLiteral("GEO")); QVERIFY(!property.isEmpty()); QCOMPARE(property.parameters().count(), 0); QCOMPARE(property.name(), QStringLiteral("GEO")); CHECK_VALUE(property, QVersitProperty::CompoundType, QStringList() << QStringLiteral("98.9") << QStringLiteral("99.9")); } void tst_QVersitContactExporter::testEncodeOrganization() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactOrganization organization; QVersitDocument document; QVersitProperty property; QString title(QStringLiteral("Developer")); // TITLE organization.setTitle(title); contact.saveDetail(&organization); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); property = findPropertyByName(document, QStringLiteral("TITLE")); QVERIFY(!property.isEmpty()); QCOMPARE(property.value(), title); // ORG with name organization.setTitle(QString()); organization.setName(QStringLiteral("Nokia")); contact.saveDetail(&organization); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); property = findPropertyByName(document, QStringLiteral("ORG")); QCOMPARE(property.name(), QStringLiteral("ORG")); CHECK_VALUE(property, QVersitProperty::CompoundType, QStringList(QStringLiteral("Nokia"))); // ORG with department/unit organization.setName(QString()); QStringList departments(QStringLiteral("R&D")); departments.append(QStringLiteral("Qt")); organization.setDepartment(departments); contact.saveDetail(&organization); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); property = findPropertyByName(document, QStringLiteral("ORG")); QCOMPARE(property.name(), QStringLiteral("ORG")); CHECK_VALUE(property, QVersitProperty::CompoundType, QStringList() << QString() << QStringLiteral("R&D") << QStringLiteral("Qt")); // ORG with name and department/unit organization.setName(QStringLiteral("Nokia")); contact.saveDetail(&organization); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); property = findPropertyByName(document, QStringLiteral("ORG")); QCOMPARE(property.name(), QStringLiteral("ORG")); CHECK_VALUE(property, QVersitProperty::CompoundType, QStringList() << QStringLiteral("Nokia") << QStringLiteral("R&D") << QStringLiteral("Qt")); // TITLE and ORG organization.setTitle(QStringLiteral("Developer")); contact.saveDetail(&organization); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 2); property = findPropertyByName(document, QStringLiteral("TITLE")); QVERIFY(!property.isEmpty()); QCOMPARE(property.value(), title); property = findPropertyByName(document, QStringLiteral("ORG")); QVERIFY(!property.isEmpty()); QCOMPARE(property.name(), QStringLiteral("ORG")); CHECK_VALUE(property, QVersitProperty::CompoundType, QStringList() << QStringLiteral("Nokia") << QStringLiteral("R&D") << QStringLiteral("Qt")); // ORG LOGO Test1: LOGO as remote Resouce const QString url = QStringLiteral("http://myhome.com/test.jpg"); contact = createContactWithName(QStringLiteral("asdf")); organization = QContactOrganization(); organization.setLogoUrl(url); contact.saveDetail(&organization); mResourceHandler->mSimulatedMimeType = QStringLiteral("image/jpeg"); mExporter->setResourceHandler(mResourceHandler); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QVERIFY(!mResourceHandler->mLoadResourceCalled); // Source type is encoded, but media type is not for a URL. property = findPropertyByName(document, QStringLiteral("LOGO")); QVERIFY(!property.isEmpty()); QCOMPARE(property.parameters().count(), 1); QVERIFY(property.parameters().contains( QStringLiteral("VALUE"), QStringLiteral("uri"))); //Check property value QCOMPARE(property.value(), url); // ORG LOGO Test2: LOGO File. mResourceHandler->mSimulatedData = "simulated data"; contact = createContactWithName(QStringLiteral("asdf")); organization = QContactOrganization(); organization.setLogoUrl(TEST_PHOTO_FILE); contact.saveDetail(&organization); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QVERIFY(mResourceHandler->mLoadResourceCalled); QCOMPARE(mResourceHandler->mLocation, TEST_PHOTO_FILE); // It should be stored in the property as a QVariant of QByteArray property = findPropertyByName(document, QStringLiteral("LOGO")); QVERIFY(!property.isEmpty()); QMultiHash parameters = property.parameters(); // Media type is encoded QCOMPARE(parameters.count(), 1); QVERIFY(parameters.contains( QStringLiteral("TYPE"), QStringLiteral("JPEG"))); // Verify value. QVariant variantValue = property.variantValue(); QVERIFY(variantValue.type() == QVariant::ByteArray); QCOMPARE(variantValue.value(), mResourceHandler->mSimulatedData); // Assistant Name Test. contact = createContactWithName(QStringLiteral("asdf")); organization = QContactOrganization(); organization.setAssistantName(QStringLiteral("myAssistant")); contact.saveDetail(&organization); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); property = findPropertyByName(document, QStringLiteral("X-ASSISTANT")); QVERIFY(!property.isEmpty()); QCOMPARE(property.value(), QStringLiteral("myAssistant")); // Test: Role contact = createContactWithName(QStringLiteral("asdf")); organization = QContactOrganization(); organization.setRole(QStringLiteral("Executive")); contact.saveDetail(&organization); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); property = findPropertyByName(document, QStringLiteral("ROLE")); QVERIFY(!property.isEmpty()); QCOMPARE(property.value(), QStringLiteral("Executive")); } void tst_QVersitContactExporter::testEncodeAvatar() { QContact contact = createContactWithName(QStringLiteral("asdf")); QContactAvatar contactAvatar; mResourceHandler->mSimulatedData = "simulated data"; mResourceHandler->mSimulatedMimeType = QStringLiteral("image/jpeg"); // Test1: Web URL const QString url = QStringLiteral("http://www.myhome.com/test.jpg"); contactAvatar.setImageUrl(url); contact.saveDetail(&contactAvatar); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QVersitProperty property = findPropertyByName(document, QStringLiteral("PHOTO")); QVERIFY(!property.isEmpty()); QCOMPARE(property.parameters().count(), 1); QCOMPARE(property.value(), url); QVERIFY(!mResourceHandler->mLoadResourceCalled); // Test 2: Local Media PHOTO contactAvatar.setImageUrl(TEST_PHOTO_FILE); contact.saveDetail(&contactAvatar); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QVERIFY(mResourceHandler->mLoadResourceCalled); // This is commented out due to test failure // QCOMPARE(mResourceHandler->mLocation, TEST_PHOTO_FILE); // verify the value property = findPropertyByName(document, QStringLiteral("PHOTO")); QVERIFY(!property.isEmpty()); QVariant variantValue = property.variantValue(); QVERIFY(variantValue.type() == QVariant::ByteArray); QCOMPARE(variantValue.value(), mResourceHandler->mSimulatedData); QVERIFY(property.parameters().contains(QStringLiteral("TYPE"), QStringLiteral("JPEG"))); } void tst_QVersitContactExporter::testEncodeEmbeddedContent() { QContact contact = createContactWithName(QStringLiteral("asdf")); QContactAvatar contactAvatar; QVariant variantValue; // Test 1: URL const QString url = QStringLiteral("http://www.myhome.com/test.jpg"); contactAvatar.setImageUrl(url); contact.saveDetail(&contactAvatar); mResourceHandler->mSimulatedMimeType = QStringLiteral("image/jpeg"); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QVERIFY(!mResourceHandler->mLoadResourceCalled); QVersitProperty photoProperty = findPropertyByName(document, QStringLiteral("PHOTO")); QVERIFY(!photoProperty.isEmpty()); QCOMPARE(photoProperty.parameters().count(), 1); QVERIFY(photoProperty.parameters().contains( QStringLiteral("VALUE"),QStringLiteral("URL"))); QCOMPARE(photoProperty.value(), url); // Test 2: Local PHOTO, image loaded by the loader contactAvatar.setImageUrl(TEST_PHOTO_FILE); contact.saveDetail(&contactAvatar); mResourceHandler->clear(); mResourceHandler->mSimulatedMimeType = QStringLiteral("image/jpeg"); mResourceHandler->mSimulatedData = "simulated image data"; QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QVERIFY(mResourceHandler->mLoadResourceCalled); photoProperty = findPropertyByName(document, QStringLiteral("PHOTO")); QVERIFY(!photoProperty.isEmpty()); QCOMPARE(photoProperty.parameters().count(), 1); QVERIFY(photoProperty.parameters().contains(QStringLiteral("TYPE"), QStringLiteral("JPEG"))); variantValue = photoProperty.variantValue(); QVERIFY(variantValue.type() == QVariant::ByteArray); QCOMPARE(variantValue.value(), mResourceHandler->mSimulatedData); // Without a resource handler mExporter->setResourceHandler(0); contactAvatar.setImageUrl(TEST_PHOTO_FILE); contact.saveDetail(&contactAvatar); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 0); mExporter->setResourceHandler(mResourceHandler); } void tst_QVersitContactExporter::testEncodeRingtone() { // Test local ringtone QContactRingtone ringtone; mResourceHandler->clear(); mResourceHandler->mSimulatedMimeType = QStringLiteral("audio/wav"); mResourceHandler->mSimulatedData = "simulated audio data"; ringtone.setAudioRingtoneUrl(TEST_AUDIO_FILE); QContact contact(createContactWithName(QStringLiteral("asdf"))); contact.saveDetail(&ringtone); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QVERIFY(mResourceHandler->mLoadResourceCalled); QVersitProperty soundProperty = findPropertyByName(document, QStringLiteral("SOUND")); QVERIFY(!soundProperty.isEmpty()); QCOMPARE(soundProperty.parameters().count(), 1); QVERIFY(soundProperty.parameters().contains( QStringLiteral("TYPE"), QStringLiteral("WAV"))); QVariant variantValue = soundProperty.variantValue(); QVERIFY(variantValue.type() == QVariant::ByteArray); QCOMPARE(variantValue.value(), mResourceHandler->mSimulatedData); // Test url ringtone QContactRingtone urlRingtone; QContact urlContact(createContactWithName(QStringLiteral("asdf"))); const QUrl url(QStringLiteral("http://qt.nokia.com/audioringtoneurl")); urlRingtone.setAudioRingtoneUrl(url); urlContact.saveDetail(&urlRingtone); QVERIFY(mExporter->exportContacts(QList() << urlContact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); soundProperty = findPropertyByName(document, QStringLiteral("SOUND")); QVERIFY(!soundProperty.isEmpty()); QCOMPARE(soundProperty.parameters().count(), 1); QVERIFY(soundProperty.parameters().contains( QStringLiteral("VALUE"),QStringLiteral("URL"))); QCOMPARE(soundProperty.value(), url.toString()); } void tst_QVersitContactExporter::testEncodeParameters() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactPhoneNumber phoneNumber; phoneNumber.setNumber(QStringLiteral("12345678")); QList subtypes; subtypes.append(QContactPhoneNumber::SubTypeMobile); subtypes.append(QContactPhoneNumber::SubTypeVideo); // Add a not supported subtype in vCard, to make sure its not encoded. subtypes.append(QContactPhoneNumber::SubTypeDtmfMenu); phoneNumber.setSubTypes(subtypes); contact.saveDetail(&phoneNumber); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); QVersitProperty property = findPropertyByName(document, QStringLiteral("TEL")); QVERIFY(!property.isEmpty()); QCOMPARE(property.parameters().count(), 2); QVERIFY(property.parameters().contains( QStringLiteral("TYPE"), QStringLiteral("CELL"))); QVERIFY(property.parameters().contains( QStringLiteral("TYPE"),QStringLiteral("VIDEO"))); } void tst_QVersitContactExporter::testEncodeGender() { QContact contact(createContactWithName(QStringLiteral("asdf"))); // Check that empty gender detail is not encoded. QContactGender gender; contact.saveDetail(&gender); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 0); QVersitProperty property = findPropertyByName(document, QStringLiteral("X-GENDER")); QVERIFY(property.isEmpty()); // Check that all valid values are encoded properly. gender.setGender(QContactGender::GenderMale); gender.setContexts(QContactGender::ContextHome); // Should not be encoded contact.saveDetail(&gender); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); property = findPropertyByName(document, QStringLiteral("X-GENDER")); QVERIFY(!property.isEmpty()); QCOMPARE(property.parameters().count(), 0); QCOMPARE(property.value(), QStringLiteral("Male")); gender.setGender(QContactGender::GenderFemale); contact.saveDetail(&gender); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); property = findPropertyByName(document, QStringLiteral("X-GENDER")); QVERIFY(!property.isEmpty()); QCOMPARE(property.parameters().count(), 0); QCOMPARE(property.value(), QStringLiteral("Female")); gender.setGender(QContactGender::GenderUnspecified); contact.saveDetail(&gender); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); property = findPropertyByName(document, QStringLiteral("X-GENDER")); QVERIFY(!property.isEmpty()); QCOMPARE(property.parameters().count(), 0); QCOMPARE(property.value(), QStringLiteral("Unspecified")); //TODO: Add a helper function in this test module to convert gender versity property to gender enum } void tst_QVersitContactExporter::testEncodeNickName() { QContact contact(createContactWithName(QStringLiteral("asdf"))); // Add an extra detail QContactGender gender; gender.setGender(QContactGender::GenderMale); contact.saveDetail(&gender); // One nickname given QContactNickname firstNickname; firstNickname.setNickname(QStringLiteral("Homie")); contact.saveDetail(&firstNickname); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 2); QVersitProperty property = findPropertyByName(document, QStringLiteral("X-NICKNAME")); QCOMPARE(property.name(), QStringLiteral("X-NICKNAME")); CHECK_VALUE(property, QVersitProperty::ListType, QStringList(QStringLiteral("Homie"))); // Two nicknames given, should be collated into a single property contact = createContactWithName(QStringLiteral("asdf")); contact.saveDetail(&gender); contact.saveDetail(&firstNickname); QContactNickname secondNickname; secondNickname.setNickname(QStringLiteral("Jay")); contact.saveDetail(&secondNickname); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 2); property = findPropertyByName(document, QStringLiteral("X-NICKNAME")); QVERIFY(!property.isEmpty()); QCOMPARE(property.name(), QStringLiteral("X-NICKNAME")); CHECK_VALUE(property, QVersitProperty::ListType, QStringList() << QStringLiteral("Homie") << QStringLiteral("Jay")); } void tst_QVersitContactExporter::testEncodeTag() { QContact contact(createContactWithName(QStringLiteral("asdf"))); // Add an extra detail QContactGender gender; gender.setGender(QContactGender::GenderMale); contact.saveDetail(&gender); // One tag given QContactTag firstTag; firstTag.setTag(QStringLiteral("red")); contact.saveDetail(&firstTag); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 2); QVersitProperty property = findPropertyByName(document, QStringLiteral("CATEGORIES")); QCOMPARE(property.name(), QStringLiteral("CATEGORIES")); CHECK_VALUE(property, QVersitProperty::ListType, QStringList(QStringLiteral("red"))); // Two tags given, should be collated into a single property contact = createContactWithName(QStringLiteral("asdf")); contact.saveDetail(&firstTag); contact.saveDetail(&gender); QContactTag secondTag; secondTag.setTag(QStringLiteral("green")); contact.saveDetail(&secondTag); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 2); property = findPropertyByName(document, QStringLiteral("CATEGORIES")); QCOMPARE(property.name(), QStringLiteral("CATEGORIES")); CHECK_VALUE(property, QVersitProperty::ListType, QStringList() << QStringLiteral("red") << QStringLiteral("green")); } void tst_QVersitContactExporter::testEncodeAnniversary() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactAnniversary anniversary; QDate date(2009,1,1); anniversary.setOriginalDate(date); anniversary.setContexts(QContactDetail::ContextHome); anniversary.setSubType(QContactAnniversary::SubTypeWedding); contact.saveDetail(&anniversary); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); QVersitProperty property = findPropertyByName(document, QStringLiteral("X-ANNIVERSARY")); QVERIFY(!property.isEmpty()); // The contexts and subtypes are not defined for X-ANNIVERSARY property QCOMPARE(property.parameters().count(), 0); // Check value QCOMPARE(property.value(), date.toString(Qt::ISODate)); } void tst_QVersitContactExporter::testEncodeOnlineAccount() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactOnlineAccount onlineAccount; QString accountUri(QStringLiteral("sip:abc@temp.com")); QList expectedSubTypes; QContactOnlineAccount::SubType expectedSubType; int invalidData = 123456; onlineAccount.setAccountUri(accountUri); // Video sharing expectedSubType = QContactOnlineAccount::SubTypeVideoShare; expectedSubTypes << expectedSubType; onlineAccount.setSubTypes(expectedSubTypes); onlineAccount.setContexts(QContactDetail::ContextHome); contact.saveDetail(&onlineAccount); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); QVersitProperty property = findPropertyByName(document, QStringLiteral("X-SIP")); QVERIFY(!property.isEmpty()); // Check parameters QCOMPARE(property.parameters().count(), 2); QVERIFY(property.parameters().contains( QStringLiteral("TYPE"),QStringLiteral("HOME"))); QVERIFY(property.parameters().contains( QStringLiteral("TYPE"),QStringLiteral("SWIS"))); // Check value QCOMPARE(property.value(), accountUri); // VoIP expectedSubTypes.clear(); expectedSubType = QContactOnlineAccount::SubTypeSipVoip; expectedSubTypes << expectedSubType; onlineAccount.setSubTypes(expectedSubTypes); onlineAccount.setContexts(QContactDetail::ContextWork); contact.saveDetail(&onlineAccount); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); property = findPropertyByName(document, QStringLiteral("X-SIP")); QVERIFY(!property.isEmpty()); // Check parameters QCOMPARE(property.parameters().count(), 2); QVERIFY(property.parameters().contains( QStringLiteral("TYPE"),QStringLiteral("WORK"))); QVERIFY(property.parameters().contains( QStringLiteral("TYPE"),QStringLiteral("VOIP"))); // Check value QCOMPARE(property.value(), accountUri); // Plain SIP expectedSubTypes.clear(); expectedSubType = QContactOnlineAccount::SubTypeSip; expectedSubTypes << expectedSubType; onlineAccount.setSubTypes(expectedSubTypes); onlineAccount.setContexts(QContactDetail::ContextWork); contact.saveDetail(&onlineAccount); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); property = findPropertyByName(document, QStringLiteral("X-SIP")); QVERIFY(!property.isEmpty()); // Check parameters, SIP not added as a TYPE parameter QCOMPARE(property.parameters().count(), 1); QVERIFY(property.parameters().contains( QStringLiteral("TYPE"),QStringLiteral("WORK"))); // Check value QCOMPARE(property.value(), accountUri); // IMPP / X-IMPP expectedSubTypes.clear(); expectedSubType = QContactOnlineAccount::SubTypeImpp; expectedSubTypes << expectedSubType; onlineAccount.setSubTypes(expectedSubTypes); onlineAccount.setContexts(QContactDetail::ContextHome); contact.saveDetail(&onlineAccount); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); property = findPropertyByName(document, QStringLiteral("X-IMPP")); QVERIFY(!property.isEmpty()); // Check parameters, SIP not added as a TYPE parameter QCOMPARE(property.parameters().count(), 1); QVERIFY(property.parameters().contains( QStringLiteral("TYPE"),QStringLiteral("HOME"))); // Check value QCOMPARE(property.value(), accountUri); // Other subtypes not converted expectedSubTypes.clear(); expectedSubType = static_cast(invalidData); expectedSubTypes << expectedSubType; onlineAccount.setSubTypes(expectedSubTypes); contact.saveDetail(&onlineAccount); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 0); // Test protocols Jabber, AIM, ICQ, MSN, Yahoo, Skype contact.clearDetails(); onlineAccount = QContactOnlineAccount(); onlineAccount.setProtocol(QContactOnlineAccount::ProtocolJabber); onlineAccount.setAccountUri("a"); contact.saveDetail(&onlineAccount); onlineAccount = QContactOnlineAccount(); onlineAccount.setProtocol(QContactOnlineAccount::ProtocolAim); onlineAccount.setAccountUri("b"); contact.saveDetail(&onlineAccount); onlineAccount = QContactOnlineAccount(); onlineAccount.setProtocol(QContactOnlineAccount::ProtocolIcq); onlineAccount.setAccountUri("c"); contact.saveDetail(&onlineAccount); onlineAccount = QContactOnlineAccount(); onlineAccount.setProtocol(QContactOnlineAccount::ProtocolMsn); onlineAccount.setAccountUri("d"); contact.saveDetail(&onlineAccount); onlineAccount = QContactOnlineAccount(); onlineAccount.setProtocol(QContactOnlineAccount::ProtocolQq); onlineAccount.setAccountUri("e"); contact.saveDetail(&onlineAccount); onlineAccount = QContactOnlineAccount(); onlineAccount.setProtocol(QContactOnlineAccount::ProtocolYahoo); onlineAccount.setAccountUri("f"); contact.saveDetail(&onlineAccount); onlineAccount = QContactOnlineAccount(); onlineAccount.setProtocol(QContactOnlineAccount::ProtocolSkype); onlineAccount.setAccountUri("g"); contact.saveDetail(&onlineAccount); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); property = findPropertyByName(document, QStringLiteral("X-JABBER")); QVERIFY(!property.isEmpty()); QVERIFY(property.value() == "a"); property = findPropertyByName(document, QStringLiteral("X-AIM")); QVERIFY(!property.isEmpty()); QVERIFY(property.value() == "b"); property = findPropertyByName(document, QStringLiteral("X-ICQ")); QVERIFY(!property.isEmpty()); QVERIFY(property.value() == "c"); property = findPropertyByName(document, QStringLiteral("X-MSN")); QVERIFY(!property.isEmpty()); QVERIFY(property.value() == "d"); property = findPropertyByName(document, QStringLiteral("X-QQ")); QVERIFY(!property.isEmpty()); QVERIFY(property.value() == "e"); property = findPropertyByName(document, QStringLiteral("X-YAHOO")); QVERIFY(!property.isEmpty()); QVERIFY(property.value() == "f"); property = findPropertyByName(document, QStringLiteral("X-SKYPE")); QVERIFY(!property.isEmpty()); QVERIFY(property.value() == "g"); } void tst_QVersitContactExporter::testEncodeFamily() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactFamily family; // No spouse, no family family.setContexts(QContactDetail::ContextHome); contact.saveDetail(&family); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 0); // Only spouse present QString spouse = QStringLiteral("ABC"); family.setSpouse(spouse); contact.saveDetail(&family); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); QVersitProperty spouseProperty = findPropertyByName(document, QStringLiteral("X-SPOUSE")); QVERIFY(!spouseProperty.isEmpty()); QCOMPARE(spouseProperty.parameters().count(), 0); QCOMPARE(spouseProperty.value(), spouse); // Spouse and children QStringList children; children << QStringLiteral("A") << QStringLiteral("B") ; family.setChildren(children); family.setSpouse(spouse); contact.saveDetail(&family); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); document = mExporter->documents().first(); QCOMPARE(countProperties(document), 2); spouseProperty = findPropertyByName(document, QStringLiteral("X-SPOUSE")); QVERIFY(!spouseProperty.isEmpty()); QCOMPARE(spouseProperty.parameters().count(), 0); QCOMPARE(spouseProperty.value(), spouse); QVersitProperty childrenProperty = findPropertyByName(document, QStringLiteral("X-CHILDREN")); QVERIFY(!spouseProperty.isEmpty()); QCOMPARE(childrenProperty.parameters().count(), 0); QCOMPARE(childrenProperty.name(), QStringLiteral("X-CHILDREN")); CHECK_VALUE(childrenProperty, QVersitProperty::ListType, children); } void tst_QVersitContactExporter::testEncodeFavorite() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactFavorite favorite; int favoriteIndex = 1; favorite.setIndex(favoriteIndex); favorite.setFavorite(true); contact.saveDetail(&favorite); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); QVersitProperty favoriteProperty = findPropertyByName(document, QStringLiteral("X-QTPROJECT-FAVORITE")); QVERIFY(!favoriteProperty.isEmpty()); QCOMPARE(favoriteProperty.parameters().count(), 0); CHECK_VALUE(favoriteProperty, QVersitProperty::CompoundType, QStringList() << QStringLiteral("true") << QString::number(favoriteIndex)); } void tst_QVersitContactExporter::testEncodeExtendedDetail() { QFETCH(QString, extendedDetailName); QFETCH(QVariant, extendedDetailData); QFETCH(QString, extendedDetailDataInProperty); QFETCH(bool, extendedDetailPropertyCreated); QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactExtendedDetail extendedDetail; extendedDetail.setName(extendedDetailName); extendedDetail.setData(extendedDetailData); contact.saveDetail(&extendedDetail); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); if (!extendedDetailPropertyCreated) { QCOMPARE(countProperties(document), 0); return; } QCOMPARE(countProperties(document), 1); QVersitProperty property = findPropertyByName(document, QStringLiteral("X-QTPROJECT-EXTENDED-DETAIL")); QVERIFY(!property.isEmpty()); QCOMPARE(property.parameters().count(), 0); CHECK_VALUE(property, QVersitProperty::CompoundType, QStringList() << extendedDetailName << extendedDetailDataInProperty); } void tst_QVersitContactExporter::testEncodeExtendedDetail_data() { QTest::addColumn("extendedDetailName"); QTest::addColumn("extendedDetailData"); QTest::addColumn("extendedDetailDataInProperty"); QTest::addColumn("extendedDetailPropertyCreated"); QString jsonArrayWith("[\n %1\n]\n"); QString jsonArrayWithString = jsonArrayWith.arg("\"%1\""); { QTest::newRow("string data") << QString("name") << QVariant(QString("data")) << jsonArrayWithString.arg("data") << true; QTest::newRow("string data, empty") << QString("name") << QVariant(QString("")) << jsonArrayWithString.arg("") << true; QTest::newRow("string data, containing reserved characters") << QString("name") << QVariant(QString(",;:\\")) << jsonArrayWithString.arg(",;:\\\\") << true; } { QTest::newRow("double data") << QString("name") << QVariant((double)2.0) << jsonArrayWith.arg("2") << true; QTest::newRow("double data, negative") << QString("name") << QVariant((double)-1.0) << jsonArrayWith.arg("-1") << true; QTest::newRow("double data, multiple digits") << QString("name") << QVariant((double)10.199999999999999) << jsonArrayWith.arg("10.199999999999999") << true; } { QTest::newRow("boolean data") << QString("name") << QVariant(true) << jsonArrayWith.arg("true") << true; } { QTest::newRow("integer data, serialized as number/double") << QString("name") << QVariant((int)2) << jsonArrayWith.arg("2") << true; QTest::newRow("integer data, serialized as number/double") << QString("name") << QVariant((int)-10) << jsonArrayWith.arg("-10") << true; } { QTest::newRow("datetime data (using local time)") << QString("name") << QVariant(QDateTime::fromString("1997-07-16T19:20:30.123+01:00", Qt::ISODate)) << jsonArrayWithString.arg("1997-07-16T19:20:30+01:00") << true; QTest::newRow("datetime data (using UTC)") << QString("name") << QVariant(QDateTime::fromString("1997-07-16T19:20:30.123+01:00", Qt::ISODate).toUTC()) << jsonArrayWithString.arg("1997-07-16T18:20:30Z") << true; QTest::newRow("datetime data (using local time with unspecified timezone)") << QString("name") << QVariant(QDateTime::fromString("1997-07-16T19:20:30", Qt::ISODate)) << jsonArrayWithString.arg("1997-07-16T19:20:30") << true; } { QTest::newRow("list data") << QString("name") << QVariant(QVariantList() << QString("string 1") << QString("string 2")) << QString("[\n [\n \"string 1\",\n \"string 2\"\n ]\n]\n") << true; } { QVariantMap map; map["key 1"] = QString("string 1"); map["key 2"] = QString("string 2"); QTest::newRow("map data") << QString("name") << QVariant(map) << QString("[\n {\n \"key 1\": \"string 1\",\n \"key 2\": \"string 2\"\n }\n]\n") << true; } { QVariantMap map; map["key"] = QVariantList() << (double)1 << (double)2; QTest::newRow("map data, containing a nested list") << QString("name") << QVariant(map) << QString("[\n {\n \"key\": [\n 1,\n 2\n ]\n }\n]\n") << true; } { QTest::newRow("empty string as name") << QString("") << QVariant(QString("data")) << jsonArrayWithString.arg("data") << true; QTest::newRow("name containing reserved characters") << QString(",;:\\") << QVariant(QString("data")) << jsonArrayWithString.arg("data") << true; } { QTest::newRow("empty variant as data") << QString("name") << QVariant() << jsonArrayWith.arg("null") << true; } { QVariantHash hash; hash["key"] = QVariant(QString("string")); QTest::newRow("data type not supported") << QString("name") << QVariant(hash) << QString() << false; } } void tst_QVersitContactExporter::testEncodeMultipleExtendedDetails() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactExtendedDetail extendedDetail1; extendedDetail1.setName(QStringLiteral("detailName1")); extendedDetail1.setData(QStringLiteral("detailData1")); contact.saveDetail(&extendedDetail1); QContactExtendedDetail extendedDetail2; extendedDetail2.setName(QStringLiteral("detailName2")); extendedDetail2.setData(QStringLiteral("detailData2")); contact.saveDetail(&extendedDetail2); QList expected; expected << extendedDetail1 << extendedDetail2; QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 2); QList actual; foreach (const QVersitProperty &property, document.properties()) { if (property.name() == QStringLiteral("X-QTPROJECT-EXTENDED-DETAIL")) actual << property; } for (int i = 0; i < actual.size(); i++) { QVERIFY(!actual[i].isEmpty()); QCOMPARE(actual[i].parameters().count(), 0); QString jsonArrayWith("[\n %1\n]\n"); QString jsonArrayWithString = jsonArrayWith.arg("\"%1\""); CHECK_VALUE(actual[i], QVersitProperty::CompoundType, QStringList() << expected[i].name() << jsonArrayWithString.arg(expected[i].data().toString())); } } void tst_QVersitContactExporter::testDefaultResourceHandler() { QVersitDefaultResourceHandler handler; QByteArray contents; QString mimeType; handler.loadResource(QStringLiteral("test.jpg"), &contents, &mimeType); QCOMPARE(mimeType, QStringLiteral("image/jpeg")); QVersitProperty property; QString location; QVERIFY(!handler.saveResource("test contents", property, &location)); } void tst_QVersitContactExporter::testEncodeEmailWithContextOther() { QContact contact(createContactWithName(QStringLiteral("asdf"))); QContactEmailAddress email; email.setEmailAddress(QStringLiteral("test@test")); email.setContexts(QContactDetail::ContextOther); contact.saveDetail(&email); QVERIFY(mExporter->exportContacts(QList() << contact, QVersitDocument::VCard30Type)); QVersitDocument document = mExporter->documents().first(); QCOMPARE(countProperties(document), 1); QVersitProperty property = findPropertyByName(document, QStringLiteral("EMAIL")); QVERIFY(!property.isEmpty()); // Check parameters QCOMPARE(property.parameters().count(), 1); QVERIFY(property.parameters().contains( QStringLiteral("TYPE"),QStringLiteral("OTHER"))); // Check value QCOMPARE(property.value(), email.emailAddress()); } // Test utility functions QContact tst_QVersitContactExporter::createContactWithName(QString name) { QContact contact; QContactName nameDetail; nameDetail.setFirstName(name); contact.saveDetail(&nameDetail); return contact; } /* Counts the properties, excluding the ones generated by a * "createContactWithName" contact and excluding default-generated properties */ int tst_QVersitContactExporter::countProperties(const QVersitDocument& document) { int count = 0; foreach (const QVersitProperty& property, document.properties()) { if (property.name() != QStringLiteral("FN") && property.name() != QStringLiteral("N") && property.name() != QStringLiteral("X-NOKIA-QCONTACTFIELD")) count++; } return count; } QContactDetail tst_QVersitContactExporter::findDetailByType( QList details, QContactDetail::DetailType search) { QContactDetail detail; for (int i= 0; i < details.count(); i++) { if ( details.at(i).type() == search ) detail = details.at(i); } return detail; } QVersitProperty tst_QVersitContactExporter::findPropertyByName( const QVersitDocument &document, const QString &propertyName) { foreach (const QVersitProperty& property, document.properties()) { if (property.name() == propertyName) return property; } return QVersitProperty(); } QTEST_MAIN(tst_QVersitContactExporter) tests/auto/versit/qversitcontactexporter/tst_qversitcontactexporter.h000066400000000000000000000100531233466112000272500ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef tst_QVERSITCONTACTEXPORTER_H #define tst_QVERSITCONTACTEXPORTER_H #include #include #include #include QT_BEGIN_NAMESPACE_VERSIT class QVersitContactExporter; class QVersitContactExporterPrivate; class QVersitDocument; class QVersitProperty; QT_END_NAMESPACE_VERSIT QTCONTACTS_USE_NAMESPACE QTVERSIT_USE_NAMESPACE class MyQVersitResourceHandler; class MyQVersitContactExporterDetailHandler; class tst_QVersitContactExporter : public QObject { Q_OBJECT private slots: void init(); void cleanup(); void testConvertContact(); void testEmptyContact(); void testContactDetailHandler(); void testContactDetailHandlerV2(); void testEncodeName(); void testEncodePhoneNumber(); void testEncodeEmailAddress(); void testEncodeStreetAddress(); void testEncodeUrl(); void testEncodeParameters(); void testEncodeUid(); void testEncodeRev(); void testEncodeVersion(); void testEncodeBirthDay(); void testEncodeNote(); void testEncodeGeoLocation(); void testEncodeOrganization(); void testEncodeEmbeddedContent(); void testEncodeRingtone(); void testEncodeGender(); void testEncodeNickName(); void testEncodeTag(); void testEncodeAnniversary(); void testEncodeOnlineAccount(); void testEncodeFamily(); void testEncodeFavorite(); void testEncodeExtendedDetail(); void testEncodeExtendedDetail_data(); void testEncodeMultipleExtendedDetails(); void testEncodeAvatar(); void testDefaultResourceHandler(); void testEncodeEmailWithContextOther(); private: // Test Utility Functions QContact createContactWithName(QString name); int countProperties(const QVersitDocument& document); QContactDetail findDetailByType(QList details, QContactDetail::DetailType search); QVersitProperty findPropertyByName(const QVersitDocument& document,const QString& propertyName); private: // Data QVersitContactExporter* mExporter; MyQVersitResourceHandler* mResourceHandler; }; #endif // tst_QVERSITCONTACTEXPORTER_H tests/auto/versit/qversitcontactimporter/000077500000000000000000000000001233466112000213355ustar00rootroot00000000000000tests/auto/versit/qversitcontactimporter/qversitcontactimporter.pro000066400000000000000000000002731233466112000267140ustar00rootroot00000000000000include(../../auto.pri) QT += contacts versit versit-private HEADERS += tst_qversitcontactimporter.h SOURCES += tst_qversitcontactimporter.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/versit/qversitcontactimporter/tst_qversitcontactimporter.cpp000066400000000000000000002346671233466112000276100ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/versit #include "tst_qversitcontactimporter.h" #include #include #include #include #include #include #include QTCONTACTS_USE_NAMESPACE QTVERSIT_USE_NAMESPACE class MyQVersitContactImporterPropertyHandler : public QVersitContactImporterPropertyHandler { public: MyQVersitContactImporterPropertyHandler() : mPreProcess(false) { } bool preProcessProperty(const QVersitDocument& document, const QVersitProperty& property, int contactIndex, QContact* contact) { Q_UNUSED(document) Q_UNUSED(contact) Q_UNUSED(contactIndex); mPreProcessedProperties.append(property); return mPreProcess; } bool postProcessProperty(const QVersitDocument& document, const QVersitProperty& property, bool alreadyProcessed, int contactIndex, QContact* contact) { Q_UNUSED(document) Q_UNUSED(contact) Q_UNUSED(contactIndex) if (!alreadyProcessed) mUnknownProperties.append(property); else mPostProcessedProperties.append(property); return false; } void clear() { mPreProcess = false; mPropertyNamesToProcess.clear(); mUnknownProperties.clear(); mPreProcessedProperties.clear(); mPostProcessedProperties.clear(); } // a hook to control what preProcess returns: bool mPreProcess; QStringList mPropertyNamesToProcess; QList mUnknownProperties; QList mPreProcessedProperties; QList mPostProcessedProperties; }; class MyQVersitResourceHandler : public QVersitResourceHandler { public: MyQVersitResourceHandler() : mIndex(0) { } bool saveResource(const QByteArray& contents, const QVersitProperty& property, QString* location) { Q_UNUSED(property); *location = QString::number(mIndex++); mObjects.insert(*location, contents); return true; } bool loadResource(const QString &location, QByteArray *contents, QString *mimeType) { Q_UNUSED(location) Q_UNUSED(contents) Q_UNUSED(mimeType) return false; } void clear() { mIndex = 0; mObjects.clear(); } int mIndex; QMap mObjects; }; const static QByteArray SAMPLE_GIF(QByteArray::fromBase64( "R0lGODlhEgASAIAAAAAAAP///yH5BAEAAAEALAAAAAASABIAAAIdjI+py+0G" "wEtxUmlPzRDnzYGfN3KBaKGT6rDmGxQAOw==")); const static QByteArray NOKIA_GIF(QByteArray::fromBase64( "R0lGODdhOAAKAIQRAAAvwQAwwwAwxAAxxwAyygAzywAzzBBHwC9nz0+A0HCf35+/4LDQ78/f79/o" "8O/v8PD3/////////////////////////////////////////////////////////////ywAAAAA" "OAAKAAAFsCAiik9kRqPJHIfhGixjisuJpqk9Inb0vjaBC0UwFH+uhM+gNBUCw6Wh92vYDAXkCZhF" "apMmA3Qajppav6tr8TqUp0DqEIwtqsmRR/Kl2A4RfFKCcnBMbYR+Uw5xg2lAjIlLCS88dyYNLn1S" "TYwvk3NmkXSQLgVvXmQuBCcQXlI7Io9MpyWCbKgublgCNgxfP0eOs6dvUgsPyMgvEAUAeCafUWhe" "bpI2LQMFenuhZy8hADs=")); void tst_QVersitContactImporter::init() { mImporter = new QVersitContactImporter(); mResourceHandler = new MyQVersitResourceHandler(); mImporter->setResourceHandler(mResourceHandler); mPropertyHandler = new MyQVersitContactImporterPropertyHandler(); mImporter->setPropertyHandler(mPropertyHandler); } void tst_QVersitContactImporter::cleanup() { QVERIFY(mImporter->propertyHandler() == mPropertyHandler); MyQVersitContactImporterPropertyHandler* nullPtr = NULL; mImporter->setPropertyHandler(nullPtr); delete mPropertyHandler; QVERIFY(mImporter->resourceHandler() == mResourceHandler); mImporter->setResourceHandler(0); delete mResourceHandler; delete mImporter; } void tst_QVersitContactImporter::testName() { QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty nameProperty; QStringList value; value.append(QStringLiteral("John"));//FirstName value.append(QStringLiteral("Citizen"));//LastName value.append(QStringLiteral("Anonymous"));//GivenName value.append(QStringLiteral("Dr"));//PreFix value.append(QStringLiteral("MSc"));//Suffix nameProperty.setName(QStringLiteral("N")); nameProperty.setValue(value); nameProperty.setValueType(QVersitProperty::CompoundType); document.addProperty(nameProperty); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactName name = (QContactName)contact.detail(QContactName::Type); QCOMPARE(name.lastName(),value[0]); QCOMPARE(name.firstName(),value[1]); QCOMPARE(name.middleName(),value[2]); QCOMPARE(name.prefix(),value[3]); QCOMPARE(name.suffix(),value[4]); // Multiple names, first one will be picked and rest will be discarded nameProperty = QVersitProperty(); QStringList anotherValue; anotherValue.append(QStringLiteral("FakeJohn"));//FirstName anotherValue.append(QStringLiteral("FakeCitizen"));//LastName anotherValue.append(QStringLiteral("FakeAnonymous"));//GivenName anotherValue.append(QStringLiteral("FakeDr"));//PreFix anotherValue.append(QStringLiteral("FakeMSc"));//Suffix nameProperty.setName(QStringLiteral("N")); nameProperty.setValue(anotherValue); nameProperty.setValueType(QVersitProperty::CompoundType); document.addProperty(nameProperty); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); QList names = contact.details(QContactName::Type); QCOMPARE(names.count(),1); // anotherValue should be discarded, so check for value name = (QContactName)names[0]; QCOMPARE(name.lastName(),value[0]); QCOMPARE(name.firstName(),value[1]); QCOMPARE(name.middleName(),value[2]); QCOMPARE(name.prefix(),value[3]); QCOMPARE(name.suffix(),value[4]); } // check that it doesn't crash if the FN property comes before the N property. void tst_QVersitContactImporter::testNameWithFormatted() { QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty fnProperty; fnProperty.setName(QStringLiteral("FN")); fnProperty.setValue(QStringLiteral("First Last")); document.addProperty(fnProperty); QVersitProperty nProperty; nProperty.setName(QStringLiteral("N")); nProperty.setValue(QStringList() << QStringLiteral("Last") << QStringLiteral("First") << QStringLiteral("Middle") << QStringLiteral("Prefix") << QStringLiteral("Suffix")); nProperty.setValueType(QVersitProperty::CompoundType); document.addProperty(nProperty); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactName name = contact.detail(); QCOMPARE(name.firstName(), QStringLiteral("First")); QCOMPARE(name.lastName(), QStringLiteral("Last")); QCOMPARE(name.middleName(), QStringLiteral("Middle")); QCOMPARE(name.prefix(), QStringLiteral("Prefix")); QCOMPARE(name.suffix(), QStringLiteral("Suffix")); } void tst_QVersitContactImporter::testAddress() { QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty property; property.setName(QStringLiteral("ADR")); property.setValue(QStringList(QString())); property.setValueType(QVersitProperty::CompoundType); // Empty value for the address document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactAddress address = contact.detail(); QCOMPARE(address.postOfficeBox(),QString()); QCOMPARE(address.street(),QString()); QCOMPARE(address.locality(),QString()); QCOMPARE(address.region(),QString()); QCOMPARE(address.postcode(),QString()); QCOMPARE(address.country(),QString()); // Address with all the fields filled property.setValue(QStringList() << QStringLiteral("PO Box") << QStringLiteral("E") << QStringLiteral("My Street") << QStringLiteral("My Town") << QStringLiteral("My State") << QStringLiteral("12345") << QStringLiteral("My Country") ); property.setValueType(QVersitProperty::CompoundType); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); address = contact.detail(); QCOMPARE(address.postOfficeBox(),QStringLiteral("PO Box")); QCOMPARE(address.street(),QStringLiteral("My Street")); QCOMPARE(address.locality(),QStringLiteral("My Town")); QCOMPARE(address.region(),QStringLiteral("My State")); QCOMPARE(address.postcode(),QStringLiteral("12345")); QCOMPARE(address.country(),QStringLiteral("My Country")); // Address with TYPE parameters converted to contexts and subtypes property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("HOME")); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("WORK")); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("DOM")); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("INTL")); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("POSTAL")); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("PARCEL")); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("X-EXTENSION")); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); address = contact.detail(); QList contexts = address.contexts(); QCOMPARE(contexts.size(), 2); QVERIFY(contexts.contains(QContactDetail::ContextHome)); QVERIFY(contexts.contains(QContactDetail::ContextWork)); QList subTypes = address.subTypes(); QCOMPARE(subTypes.size(), 4); QVERIFY(subTypes.contains(QContactAddress::SubTypeDomestic)); QVERIFY(subTypes.contains(QContactAddress::SubTypeInternational)); QVERIFY(subTypes.contains(QContactAddress::SubTypePostal)); QVERIFY(subTypes.contains(QContactAddress::SubTypeParcel)); } void tst_QVersitContactImporter::testAddressWithoutSubTypes() { QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty property; property.setName(QStringLiteral("ADR")); property.setValue(QStringList() << QStringLiteral("PO Box") << QStringLiteral("E") << QStringLiteral("My Street") << QStringLiteral("My Town") << QStringLiteral("My State") << QStringLiteral("12345") << QStringLiteral("My Country") ); property.setValueType(QVersitProperty::CompoundType); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactAddress address = contact.detail(); QContactAddress otherAddress; otherAddress.setPostOfficeBox(QStringLiteral("PO Box")); otherAddress.setStreet(QStringLiteral("My Street")); otherAddress.setLocality(QStringLiteral("My Town")); otherAddress.setRegion(QStringLiteral("My State")); otherAddress.setPostcode(QStringLiteral("12345")); otherAddress.setCountry(QStringLiteral("My Country")); QCOMPARE(address, otherAddress); } void tst_QVersitContactImporter::testOrganizationName() { QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty property; // Empty value for the organization property.setName(QStringLiteral("ORG")); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactOrganization organization = contact.detail(); QCOMPARE(organization.name(),QString()); QCOMPARE(organization.department().count(),0); // Organization with single value property.setValueType(QVersitProperty::CompoundType); property.setValue(QStringList(QStringLiteral("Nokia"))); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); organization = contact.detail(); QCOMPARE(organization.name(),QStringLiteral("Nokia")); QCOMPARE(organization.department().count(),0); // Organization with one Organizational Unit property.setValue(QStringList() << QStringLiteral("Nokia") << QStringLiteral("R&D")); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); organization = contact.detail(); QCOMPARE(organization.name(),QStringLiteral("Nokia")); QCOMPARE(organization.department().count(),1); QCOMPARE(organization.department().at(0),QStringLiteral("R&D")); // Organization with more Organizational Units property.setValue(QStringList() << QStringLiteral("Nokia") << QStringLiteral("R&D") << QStringLiteral("Devices") << QStringLiteral("Qt")); property.setValueType(QVersitProperty::CompoundType); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); organization = contact.detail(); QCOMPARE(organization.name(),QStringLiteral("Nokia")); QCOMPARE(organization.department().count(),3); QCOMPARE(organization.department().at(0),QStringLiteral("R&D")); QCOMPARE(organization.department().at(1),QStringLiteral("Devices")); QCOMPARE(organization.department().at(2),QStringLiteral("Qt")); } void tst_QVersitContactImporter::testOrganizationTitle() { QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty property; // One title property.setName(QStringLiteral("TITLE")); QString titleValue(QStringLiteral("Developer")); property.setValue(titleValue); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QList organizationDetails = contact.details(QContactOrganization::Type); QCOMPARE(organizationDetails.count(), 1); QContactOrganization organization = static_cast(organizationDetails[0]); QCOMPARE(organization.title(),titleValue); // Two titles -> two QContactOrganizations created property.setName(QStringLiteral("TITLE")); QString secondTitleValue(QStringLiteral("Hacker")); property.setValue(secondTitleValue); document.addProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); organizationDetails = contact.details(QContactOrganization::Type); QCOMPARE(organizationDetails.count(), 2); QContactOrganization firstOrganization = static_cast(organizationDetails[0]); QCOMPARE(firstOrganization.title(),titleValue); QContactOrganization secondOrganization = static_cast(organizationDetails[1]); QCOMPARE(secondOrganization.title(),secondTitleValue); // Two titles and one organization name -> two QContactOrganizations created property.setName(QStringLiteral("ORG")); property.setValueType(QVersitProperty::CompoundType); property.setValue(QStringList(QStringLiteral("Nokia"))); document.addProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); organizationDetails = contact.details(QContactOrganization::Type); QCOMPARE(organizationDetails.count(), 2); firstOrganization = static_cast(organizationDetails[0]); QCOMPARE(firstOrganization.title(),titleValue); QCOMPARE(firstOrganization.name(),QStringLiteral("Nokia")); secondOrganization = static_cast(organizationDetails[1]); QCOMPARE(secondOrganization.title(),secondTitleValue); QCOMPARE(secondOrganization.name(),QString()); } void tst_QVersitContactImporter::testOrganizationAssistant() { QContact contact; QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty property; property.setName(QStringLiteral("X-ASSISTANT")); QString assistantValue(QStringLiteral("Jenny")); property.setValue(assistantValue); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); QContactOrganization organization = contact.detail(); QCOMPARE(organization.assistantName(), assistantValue); } void tst_QVersitContactImporter::testOrganizationLogo() { QContact contact; QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty property; // Embedded LOGO property.setName(QStringLiteral("LOGO")); QByteArray logo(QByteArray::fromBase64( "R0lGODlhEgASAIAAAAAAAP///yH5BAEAAAEALAAAAAASABIAAAIdjI+py+0G")); property.setValue(logo); property.insertParameter(QStringLiteral("TYPE"), QStringLiteral("GIF")); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); QContactOrganization organization = contact.detail(); QByteArray content = mResourceHandler->mObjects.value(organization.logoUrl().toString()); QCOMPARE(content, logo); // LOGO as a URL property.setName(QStringLiteral("LOGO")); QString logoUrl(QStringLiteral("http://www.organization.org/logo.gif")); property.setValue(logoUrl); property.insertParameter(QStringLiteral("VALUE"),QStringLiteral("URL")); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); organization = contact.detail(); QCOMPARE(organization.logoUrl().toString(),logoUrl); } void tst_QVersitContactImporter::testOrganizationRole() { QContact contact; QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty property; // Setting the role is not yet supported by QContactOrganization property.setName(QStringLiteral("ROLE")); QString roleValue(QStringLiteral("Very important manager and proud of it")); property.setValue(roleValue); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); QContactOrganization organization = contact.detail(); QCOMPARE(organization.role(), roleValue); } void tst_QVersitContactImporter::testTel() { QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty property; property.setName(QStringLiteral("TEL")); QString value(QStringLiteral("+35850987654321")); property.setValue(value); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("VOICE")); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("CELL")); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("MODEM")); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("CAR")); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("VIDEO")); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("FAX")); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("BBS")); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("PAGER")); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("HOME")); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("WORK")); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("ISDN")); document.addProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); const QContactPhoneNumber& phone = contact.detail(); QCOMPARE(phone.number(),QString(value)); const QList subTypes = phone.subTypes(); QCOMPARE(subTypes.count(), 9); QVERIFY(subTypes.contains(QContactPhoneNumber::SubTypeVoice)); QVERIFY(subTypes.contains(QContactPhoneNumber::SubTypeMobile)); QVERIFY(subTypes.contains(QContactPhoneNumber::SubTypeModem)); QVERIFY(subTypes.contains(QContactPhoneNumber::SubTypeCar)); QVERIFY(subTypes.contains(QContactPhoneNumber::SubTypeVideo)); QVERIFY(subTypes.contains(QContactPhoneNumber::SubTypeFax)); QVERIFY(subTypes.contains(QContactPhoneNumber::SubTypeBulletinBoardSystem)); QVERIFY(subTypes.contains(QContactPhoneNumber::SubTypePager)); QVERIFY(subTypes.contains(QContactPhoneNumber::SubTypeLandline)); const QList contexts = phone.contexts(); QCOMPARE(contexts.count(),2); QVERIFY(contexts.contains(QContactDetail::ContextWork)); QVERIFY(contexts.contains(QContactDetail::ContextHome)); } void tst_QVersitContactImporter::testTelWithoutSubTypes() { QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty property; property.setName(QStringLiteral("TEL")); QString value(QStringLiteral("+35850987654321")); property.setValue(value); document.addProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); const QContactPhoneNumber& phone = contact.detail(); QContactPhoneNumber otherPhone; otherPhone.setNumber(QStringLiteral("+35850987654321")); QCOMPARE(otherPhone, phone); } void tst_QVersitContactImporter::testEmail() { QVersitProperty property; property.setName(QStringLiteral("EMAIL")); QString value(QStringLiteral("john.citizen@example.com")); property.setValue(value); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("WORK")); QVersitDocument document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactEmailAddress email = contact.detail(); QCOMPARE(email.emailAddress(),value); const QList contexts = email.contexts(); QCOMPARE(contexts.count(),1); QVERIFY(contexts.contains(QContactDetail::ContextWork)); QCOMPARE(mPropertyHandler->mUnknownProperties.size(), 0); } void tst_QVersitContactImporter::testUrl() { QVersitProperty property; property.setName(QStringLiteral("URL")); QString value(QStringLiteral("http://example.com")); property.setValue(value); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("WORK")); QVersitDocument document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactUrl url = contact.detail(); QCOMPARE(url.url(),value); const QList contexts = url.contexts(); QCOMPARE(contexts.count(),1); QVERIFY(contexts.contains(QContactDetail::ContextWork)); } void tst_QVersitContactImporter::testUid() { QVersitProperty property; property.setName(QStringLiteral("UID")); QString value(QStringLiteral("unique identifier")); property.setValue(value); QVersitDocument document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactGuid uid = contact.detail(); QCOMPARE(uid.guid(),value); } void tst_QVersitContactImporter::testTimeStamp() { // Simple date : ISO 8601 extended format QVersitProperty property; property.setName(QStringLiteral("REV")); QString dateValue(QStringLiteral("1981-05-20")); property.setValue(dateValue); QVersitDocument document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactTimestamp timeStamp = contact.detail(); QCOMPARE(timeStamp.lastModified().date().toString(Qt::ISODate),dateValue); // Date and Time : ISO 8601 extended format without utc offset QString dateAndTimeValue(QStringLiteral("1981-05-20T23:55:55")); property.setValue(dateAndTimeValue); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); timeStamp = contact.detail(); QCOMPARE(timeStamp.lastModified().toString(Qt::ISODate),dateAndTimeValue); // Date and Time : ISO 8601 extented format with utc offset QString utcOffset(QStringLiteral("Z")); QString dateAndTimeWithUtcValue = dateAndTimeValue+utcOffset; property.setValue(dateAndTimeWithUtcValue); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); timeStamp = contact.detail(); QCOMPARE(timeStamp.lastModified().toString(Qt::ISODate), dateAndTimeWithUtcValue); QCOMPARE(timeStamp.lastModified().timeSpec(), Qt::UTC); // Date and Time : ISO 8601 in basic format without utc offset dateAndTimeValue = QStringLiteral("19810520T235555"); property.setValue(dateAndTimeValue); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); timeStamp = contact.detail(); QCOMPARE(timeStamp.lastModified().toString(QStringLiteral("yyyyMMddThhmmss")), dateAndTimeValue); // Date and Time : ISO 8601 in basic format with utc offset dateAndTimeValue = QStringLiteral("19810520T235555"); dateAndTimeWithUtcValue = dateAndTimeValue+utcOffset; property.setValue(dateAndTimeWithUtcValue); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); timeStamp = contact.detail(); QCOMPARE(timeStamp.lastModified().toString(QStringLiteral("yyyyMMddThhmmss")), dateAndTimeValue); QCOMPARE(timeStamp.lastModified().timeSpec(),Qt::UTC); } void tst_QVersitContactImporter::testVersion() { QFETCH(QString, sequenceNumber); QFETCH(QString, extendedVersion); QFETCH(bool, versionCreated); QVersitDocument document(QVersitDocument::VCard30Type); addVersionPropertyToDocument(sequenceNumber,extendedVersion,document); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); if (!versionCreated) { QCOMPARE(contact.details(QContactVersion::Type).size(), 0); return; } QContactVersion version = (QContactVersion)contact.detail(QContactVersion::Type); QCOMPARE(version.sequenceNumber(), sequenceNumber.toInt()); QCOMPARE(QString(version.extendedVersion()), extendedVersion); } void tst_QVersitContactImporter::testVersion_data() { QTest::addColumn("sequenceNumber"); QTest::addColumn("extendedVersion"); QTest::addColumn("versionCreated"); { QTest::newRow("proper version") << QString("4711") << QString("134f23dbb2") << true; QTest::newRow("all empty") << QString("") << QString("") << false; QTest::newRow("sequenceNumber empty") << QString("") << QString("134f23dbb3") << false; QTest::newRow("sequenceNumber negative") << QString("-2") << QString("134f23dbb4") << true; QTest::newRow("sequenceNumber invalid mix") << QString("2withletters") << QString("134f23dbb5") << false; QTest::newRow("extendedVersion empty") << QString("4712") << QString("") << true; QTest::newRow("extendedVersion negative") << QString("4713") << QString("-1234567") << true; } } void tst_QVersitContactImporter::testMultipleVersions() { QVersitDocument document(QVersitDocument::VCard30Type); addVersionPropertyToDocument(QStringLiteral("1"), QStringLiteral("134f23dbb2"), document); addVersionPropertyToDocument(QStringLiteral("2"), QStringLiteral("134f23dbb3"), document); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QCOMPARE(contact.details(QContactVersion::Type).size(), 1); QContactVersion version = (QContactVersion)contact.detail(QContactVersion::Type); QCOMPARE(version.sequenceNumber(), 1); QCOMPARE(version.extendedVersion(), QByteArray("134f23dbb2")); } void tst_QVersitContactImporter::addVersionPropertyToDocument(QString sequenceNumber, QString extendedVersion, QVersitDocument &document) { QVersitProperty property; property.setName(QStringLiteral("X-QTPROJECT-VERSION")); QStringList value; value.append(sequenceNumber); value.append(extendedVersion); property.setValue(value); property.setValueType(QVersitProperty::CompoundType); document.addProperty(property); } void tst_QVersitContactImporter::testAnniversary() { // Date : ISO 8601 extended format QVersitProperty property; property.setName(QStringLiteral("X-ANNIVERSARY")); property.setValue(QStringLiteral("1981-05-20")); QVersitDocument document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactAnniversary anniversary = contact.detail(); QCOMPARE(anniversary.originalDate(), QDate(1981, 5, 20)); QCOMPARE(anniversary.value(QContactAnniversary::FieldOriginalDate).type(), QVariant::Date); // Evolution format property.setName(QStringLiteral("X-EVOLUTION-ANNIVERSARY")); property.setValue(QStringLiteral("1981-05-20")); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); anniversary = contact.detail(); QCOMPARE(anniversary.originalDate(), QDate(1981, 5, 20)); QCOMPARE(anniversary.value(QContactAnniversary::FieldOriginalDate).type(), QVariant::Date); // Date : ISO 8601 in basic format property.setName(QStringLiteral("X-ANNIVERSARY")); property.setValue(QStringLiteral("19810520")); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); anniversary = contact.detail(); QCOMPARE(anniversary.originalDate(), QDate(1981, 5, 20)); QCOMPARE(anniversary.value(QContactAnniversary::FieldOriginalDate).type(), QVariant::Date); // Date time tests property.setName(QStringLiteral("X-ANNIVERSARY")); property.setValue(QStringLiteral("19810520T000102")); // localtime document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); anniversary = contact.detail(); QCOMPARE(anniversary.originalDate(), QDate(1981, 5, 20)); QCOMPARE(anniversary.originalDateTime(), QDateTime(QDate(1981, 5, 20), QTime(0,1,2), Qt::LocalTime)); QCOMPARE(anniversary.value(QContactAnniversary::FieldOriginalDate).type(), QVariant::DateTime); property.setValue(QStringLiteral("19810520T010203Z")); // utc document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); anniversary = contact.detail(); QCOMPARE(anniversary.originalDate(), QDate(1981, 5, 20)); QCOMPARE(anniversary.originalDateTime(), QDateTime(QDate(1981, 5, 20), QTime(1,2,3), Qt::UTC)); QCOMPARE(anniversary.value(QContactAnniversary::FieldOriginalDate).type(), QVariant::DateTime); } void tst_QVersitContactImporter::testBirthday() { // Date : ISO 8601 extended format QVersitProperty property; property.setName(QStringLiteral("BDAY")); QString dateValue(QStringLiteral("1981-05-20")); property.setValue(dateValue); QVersitDocument document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactBirthday bday = contact.detail(); QCOMPARE(bday.date().toString(Qt::ISODate), dateValue); QCOMPARE(bday.value(QContactBirthday::FieldBirthday).type(), QVariant::Date); // Date : ISO 8601 in basic format dateValue = QStringLiteral("19810520"); property.setValue(dateValue); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); bday = contact.detail(); QCOMPARE(bday.date(), QDate(1981, 5, 20)); QCOMPARE(bday.value(QContactBirthday::FieldBirthday).type(), QVariant::Date); dateValue = QStringLiteral("1981-05-20"); property.setValue(dateValue); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); bday = contact.detail(); QCOMPARE(bday.date(), QDate(1981, 5, 20)); QCOMPARE(bday.value(QContactBirthday::FieldBirthday).type(), QVariant::Date); dateValue = QStringLiteral("19810520T100000"); property.setValue(dateValue); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); bday = contact.detail(); QCOMPARE(bday.dateTime(), QDateTime(QDate(1981, 5, 20), QTime(10, 0, 0), Qt::LocalTime)); QCOMPARE(bday.value(QContactBirthday::FieldBirthday).type(), QVariant::DateTime); dateValue = QStringLiteral("1981-05-20T10:00:00"); property.setValue(dateValue); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); bday = contact.detail(); QCOMPARE(bday.dateTime(), QDateTime(QDate(1981, 5, 20), QTime(10, 0, 0), Qt::LocalTime)); QCOMPARE(bday.value(QContactBirthday::FieldBirthday).type(), QVariant::DateTime); dateValue = QStringLiteral("19810520T100000Z"); property.setValue(dateValue); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); bday = contact.detail(); QCOMPARE(bday.dateTime(), QDateTime(QDate(1981, 5, 20), QTime(10, 0, 0), Qt::UTC)); QCOMPARE(bday.value(QContactBirthday::FieldBirthday).type(), QVariant::DateTime); dateValue = QStringLiteral("1981-05-20T10:00:00Z"); property.setValue(dateValue); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); bday = contact.detail(); QCOMPARE(bday.dateTime(), QDateTime(QDate(1981, 5, 20), QTime(10, 0, 0), Qt::UTC)); QCOMPARE(bday.value(QContactBirthday::FieldBirthday).type(), QVariant::DateTime); } void tst_QVersitContactImporter::testGender() { // Check empty property. QVersitProperty property1; property1.setName(QStringLiteral("X-GENDER")); QString val1(QStringLiteral("")); property1.setValue(val1); QVersitDocument document1 = createDocumentWithProperty(property1); QVERIFY(mImporter->importDocuments(QList() << document1)); QContact contact1 = mImporter->contacts().first(); QCOMPARE (contact1, QContact()); // Check valid values. QVersitProperty property2; property2.setName(QStringLiteral("X-GENDER")); QString val2(QStringLiteral("male")); property2.setValue(val2); QVersitDocument document2 = createDocumentWithProperty(property2); QVERIFY(mImporter->importDocuments(QList() << document2)); QContact contact2 = mImporter->contacts().first(); QContactGender gender2 = contact2.detail(); QCOMPARE(gender2.gender(),QContactGender::GenderMale); QVersitProperty property3; property3.setName(QStringLiteral("x-gender")); QString val3(QStringLiteral("FEMALE")); property3.setValue(val3); QVersitDocument document3 = createDocumentWithProperty(property3); QVERIFY(mImporter->importDocuments(QList() << document3)); QContact contact3 = mImporter->contacts().first(); QContactGender gender3 = contact3.detail(); QCOMPARE(gender3.gender(),QContactGender::GenderFemale); QVersitProperty property4; property4.setName(QStringLiteral("x-Gender")); QString val4(QStringLiteral("Unspecified")); property4.setValue(val4); QVersitDocument document4 = createDocumentWithProperty(property4); QVERIFY(mImporter->importDocuments(QList() << document4)); QContact contact4 = mImporter->contacts().first(); QContactGender gender4 = contact4.detail(); QCOMPARE(gender4.gender(),QContactGender::GenderUnspecified); // Check property having an invalid value. QVersitProperty property5; property5.setName(QStringLiteral("X-GENDER")); QString val5(QStringLiteral("Garbage")); property5.setValue(val5); QVersitDocument document5 = createDocumentWithProperty(property5); QVERIFY(mImporter->importDocuments(QList() << document5)); QContact contact5 = mImporter->contacts().first(); QCOMPARE(contact5, QContact()); } void tst_QVersitContactImporter::testNickname() { // one value QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty nameProperty; nameProperty.setName(QStringLiteral("NICKNAME")); nameProperty.setValue(QStringList(QStringLiteral("Homie"))); nameProperty.setValueType(QVersitProperty::ListType); document.addProperty(nameProperty); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactNickname nickName = (QContactNickname)contact.detail(QContactNickname::Type); QCOMPARE(nickName.nickname(), QStringLiteral("Homie")); // comma separated values should generate multiple nickname fields contact.clearDetails(); document.clear(); document.setType(QVersitDocument::VCard30Type); QStringList multiVal; multiVal.append(QStringLiteral("Homie")); multiVal.append(QStringLiteral("SuperHero")); multiVal.append(QStringLiteral("NukeSpecialist")); nameProperty.setName(QStringLiteral("NICKNAME")); nameProperty.setValue(multiVal); nameProperty.setValueType(QVersitProperty::ListType); document.addProperty(nameProperty); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); QList nickNames = contact.details(); QCOMPARE(nickNames.count(),3); nickName = nickNames[0]; QCOMPARE(nickName.nickname(),QStringLiteral("Homie")); nickName = nickNames[1]; QCOMPARE(nickName.nickname(),QStringLiteral("SuperHero")); nickName = nickNames[2]; QCOMPARE(nickName.nickname(),QStringLiteral("NukeSpecialist")); // X-NICKNAME document.clear(); document.setType(QVersitDocument::VCard30Type); nameProperty = QVersitProperty(); nameProperty.setName(QStringLiteral("X-NICKNAME")); nameProperty.setValue(QStringList(QStringLiteral("Homie"))); nameProperty.setValueType(QVersitProperty::ListType); document.addProperty(nameProperty); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); nickName = contact.detail(); QCOMPARE(nickName.nickname(),QStringLiteral("Homie")); // both X-NICKNAME and NICKNAME nameProperty.setName(QStringLiteral("NICKNAME")); document.addProperty(nameProperty); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); nickNames = contact.details(); QCOMPARE(nickNames.size(), 1); QCOMPARE(nickNames.first().nickname(), QStringLiteral("Homie")); } void tst_QVersitContactImporter::testAvatarUrl() { QVersitProperty property; property.setName(QStringLiteral("PHOTO")); QString value(QStringLiteral("http://example.com/example.jpg")); property.setValue(value); property.insertParameter(QStringLiteral("VALUE"), QStringLiteral("URL")); QVersitDocument document(QVersitDocument::VCard30Type); document.addProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactAvatar avatar = contact.detail(); QCOMPARE(avatar.imageUrl(), QUrl(QStringLiteral("http://example.com/example.jpg"))); // A URL disguised inside a QByteArray. document.clear(); document.setType(QVersitDocument::VCard30Type); property.clear(); property.setName(QStringLiteral("PHOTO")); property.setValue(QByteArray("http://example.com/example.jpg")); property.insertParameter(QStringLiteral("VALUE"), QStringLiteral("URL")); property.insertParameter(QStringLiteral("CHARSET"), QStringLiteral("UTF-8")); document.addProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); avatar = contact.detail(); QCOMPARE(avatar.imageUrl(), QUrl(QStringLiteral("http://example.com/example.jpg"))); } void tst_QVersitContactImporter::testAvatarInvalid() { // An avatar that's a QVersitDocument? It shouldn't work. QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty property; property.setName(QStringLiteral("PHOTO")); QVersitDocument nestedDocument; property.setValue(QVariant::fromValue(nestedDocument)); property.insertParameter(QStringLiteral("VALUE"), QStringLiteral("URL")); document.addProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QCOMPARE(contact.details(QContactAvatar::Type).size(), 0); document.clear(); document.setType(QVersitDocument::VCard30Type); property.clear(); property.setName(QStringLiteral("PHOTO")); property.setValue(QVariant::fromValue(nestedDocument)); document.addProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); QCOMPARE(contact.details(QContactAvatar::Type).size(), 0); } void tst_QVersitContactImporter::testGeo() { // some positive values QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty nameProperty; QStringList val; val.append(QStringLiteral("45.32")); // Latitude val.append(QStringLiteral("18.53"));// Longtitude nameProperty.setName(QStringLiteral("GEO")); nameProperty.setValue(val); nameProperty.setValueType(QVersitProperty::CompoundType); document.addProperty(nameProperty); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactGeoLocation geo = (QContactGeoLocation)contact.detail(QContactGeoLocation::Type); QString str; str.setNum(geo.latitude(),'.',2); QCOMPARE(str,val[0]); str.setNum(geo.longitude(),'.',2); QCOMPARE(str,val[1]); // some negative values document.clear(); document.setType(QVersitDocument::VCard30Type); nameProperty = QVersitProperty(); val.append(QStringLiteral("-45.32")); // Latitude val.append(QStringLiteral("18.53"));// Longtitude nameProperty.setName(QStringLiteral("GEO")); nameProperty.setValue(val); nameProperty.setValueType(QVersitProperty::CompoundType); document.addProperty(nameProperty); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); geo = (QContactGeoLocation)contact.detail(QContactGeoLocation::Type); str.setNum(geo.latitude(),'.',2); QCOMPARE(str,val[0]); str.setNum(geo.longitude(),'.',2); QCOMPARE(str,val[1]); } void tst_QVersitContactImporter::testNote() { // single line value QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty nameProperty; QString val(QStringLiteral("I will not sleep at my work -John")); nameProperty.setName(QStringLiteral("NOTE")); nameProperty.setValue(val); document.addProperty(nameProperty); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactNote note = (QContactNote)contact.detail(QContactNote::Type); QCOMPARE(note.note(),val); // Multiline value and quoted printable encoding document.clear(); document.setType(QVersitDocument::VCard30Type); nameProperty = QVersitProperty(); val = QStringLiteral("My Dad acts like he belongs,=0D=0AHe belongs in the zoo.=0D=0A"); nameProperty.setName(QStringLiteral("NOTE")); nameProperty.setValue(val); QMultiHash params; params.insert(QStringLiteral("QUOTED-PRINTABLE"),val); nameProperty.setParameters(params); document.addProperty(nameProperty); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); note = (QContactNote)contact.detail(QContactNote::Type); QCOMPARE(note.note(),val); } void tst_QVersitContactImporter::testDisplayLabel() { QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty fnProperty; fnProperty.setName(QStringLiteral("FN")); fnProperty.setValue(QStringLiteral("fn")); document.addProperty(fnProperty); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); contact = mImporter->contacts().first(); QContactDisplayLabel displayLabel = contact.detail(); QCOMPARE(displayLabel.label(), QStringLiteral("fn")); } void tst_QVersitContactImporter::testOnlineAccount() { QString accountUri(QStringLiteral("sip:john.citizen@example.com")); QVersitDocument document; // Plain X-SIP, no TYPE -> QVersitProperty property; property.setName(QStringLiteral("X-SIP")); property.setValue(accountUri); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactOnlineAccount onlineAccount = contact.detail(); QCOMPARE(onlineAccount.accountUri(),accountUri); QList subTypes = onlineAccount.subTypes(); QCOMPARE(subTypes.count(),1); QVERIFY(subTypes.first() == QContactOnlineAccount::SubTypeSip); // X-SIP;SWIS property = QVersitProperty(); property.setName(QStringLiteral("X-SIP")); property.setValue(accountUri); QMultiHash params; params.insert(QStringLiteral("TYPE"),QStringLiteral("SWIS")); property.setParameters(params); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); onlineAccount = contact.detail(); QCOMPARE(onlineAccount.accountUri(),accountUri); subTypes = onlineAccount.subTypes(); QCOMPARE(subTypes.count(),1); QVERIFY(subTypes.first() == QContactOnlineAccount::SubTypeVideoShare); // X-SIP;VOIP property = QVersitProperty(); property.setName(QStringLiteral("X-SIP")); property.setValue(accountUri); params.clear(); params.insert(QStringLiteral("TYPE"),QStringLiteral("VOIP")); property.setParameters(params); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); onlineAccount = contact.detail(); QCOMPARE(onlineAccount.accountUri(),accountUri); subTypes = onlineAccount.subTypes(); QCOMPARE(subTypes.count(),1); QVERIFY(subTypes.first() == QContactOnlineAccount::SubTypeSipVoip); // X-IMPP property = QVersitProperty(); property.setName(QStringLiteral("X-IMPP")); property.setValue(accountUri); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); onlineAccount = contact.detail(); QCOMPARE(onlineAccount.accountUri(),accountUri); subTypes = onlineAccount.subTypes(); QCOMPARE(subTypes.count(),1); QVERIFY(subTypes.first() == QContactOnlineAccount::SubTypeImpp); // IMPP property = QVersitProperty(); property.setName(QStringLiteral("IMPP")); property.setValue(accountUri); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); onlineAccount = contact.detail(); QCOMPARE(onlineAccount.accountUri(),accountUri); subTypes = onlineAccount.subTypes(); QCOMPARE(subTypes.count(),1); QVERIFY(subTypes.first() == QContactOnlineAccount::SubTypeImpp); // X-JABBER property = QVersitProperty(); property.setName(QStringLiteral("X-JABBER")); property.setValue(accountUri); document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); onlineAccount = contact.detail(); QCOMPARE(onlineAccount.accountUri(),accountUri); subTypes = onlineAccount.subTypes(); QCOMPARE(subTypes.count(),1); QVERIFY(subTypes.first() == QContactOnlineAccount::SubTypeImpp); QVERIFY(onlineAccount.protocol() == QContactOnlineAccount::ProtocolJabber); document = QVersitDocument(QVersitDocument::VCard30Type); property.setName("X-AIM"); property.setValue("a"); document.addProperty(property); property.setName("X-ICQ"); property.setValue("b"); document.addProperty(property); property.setName("X-MSN"); property.setValue("c"); document.addProperty(property); property.setName("X-QQ"); property.setValue("d"); document.addProperty(property); property.setName("X-YAHOO"); property.setValue("e"); document.addProperty(property); property.setName("X-SKYPE"); property.setValue("f"); document.addProperty(property); property.setName("X-SKYPE-USERNAME"); property.setValue("g"); document.addProperty(property); property.setName("X-MS-IMADDRESS"); property.setValue("h"); document.addProperty(property); property.setName("X-KADDRESSBOOK-X-IMADDRESS"); property.setValue("i"); document.addProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); QList onlineAccounts = contact.details(); QCOMPARE(onlineAccounts.size(), 9); QVERIFY(onlineAccounts[0].protocol() == QContactOnlineAccount::ProtocolAim); QVERIFY(onlineAccounts[0].accountUri() == "a"); QVERIFY(onlineAccounts[1].protocol() == QContactOnlineAccount::ProtocolIcq); QVERIFY(onlineAccounts[1].accountUri() == "b"); QVERIFY(onlineAccounts[2].protocol() == QContactOnlineAccount::ProtocolMsn); QVERIFY(onlineAccounts[2].accountUri() == "c"); QVERIFY(onlineAccounts[3].protocol() == QContactOnlineAccount::ProtocolQq); QVERIFY(onlineAccounts[3].accountUri() == "d"); QVERIFY(onlineAccounts[4].protocol() == QContactOnlineAccount::ProtocolYahoo); QVERIFY(onlineAccounts[4].accountUri() == "e"); QVERIFY(onlineAccounts[5].protocol() == QContactOnlineAccount::ProtocolSkype); QVERIFY(onlineAccounts[5].accountUri() == "f"); QVERIFY(onlineAccounts[6].protocol() == QContactOnlineAccount::ProtocolSkype); QVERIFY(onlineAccounts[6].accountUri() == "g"); QVERIFY(onlineAccounts[7].protocol() == QContactOnlineAccount::ProtocolUnknown); QVERIFY(onlineAccounts[7].accountUri() == "h"); QVERIFY(onlineAccounts[8].protocol() == QContactOnlineAccount::ProtocolUnknown); QVERIFY(onlineAccounts[8].accountUri() == "i"); } void tst_QVersitContactImporter::testFamily() { // Interesting : kid but no wife :) QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty nameProperty; QString val(QStringLiteral("Jane")); // one is enough nameProperty.setName(QStringLiteral("X-CHILDREN")); nameProperty.setValue(QStringList(val)); nameProperty.setValueType(QVersitProperty::ListType); document.addProperty(nameProperty); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactFamily family = (QContactFamily)contact.detail(QContactFamily::Type); QStringList children = family.children(); QCOMPARE(children.count(),1); // ensure no other kids in list QCOMPARE(family.spouse(),QString()); // make sure no wife QCOMPARE(children[0],val); // ensure it is your kid // Critical : wife but no kids , happy hours document.clear(); document.setType(QVersitDocument::VCard30Type); nameProperty = QVersitProperty(); nameProperty.setName(QStringLiteral("X-SPOUSE")); val = QStringLiteral("Jenny"); nameProperty.setValue(val); document.addProperty(nameProperty); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); family = (QContactFamily)contact.detail(QContactFamily::Type); children = family.children(); QCOMPARE(children.count(),0); // list should be empty as you know QCOMPARE(family.spouse(),val); // make sure thats your wife:( // Hopeless : couple of kids and wife document.clear(); document.setType(QVersitDocument::VCard30Type); // Add kids first nameProperty = QVersitProperty(); nameProperty.setName(QStringLiteral("X-CHILDREN")); QStringList kidsVal; kidsVal.append(QStringLiteral("James")); kidsVal.append(QStringLiteral("Jake")); kidsVal.append(QStringLiteral("Jane")); nameProperty.setValue(kidsVal); nameProperty.setValueType(QVersitProperty::ListType); document.addProperty(nameProperty); // Add wife next val = QStringLiteral("Jenny"); nameProperty = QVersitProperty(); nameProperty.setName(QStringLiteral("X-SPOUSE")); nameProperty.setValue(val); document.addProperty(nameProperty); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); family = (QContactFamily)contact.detail(QContactFamily::Type); children = family.children(); QCOMPARE(children.count(),3); // too late , count them now. // painfull but ensure they are your kids QCOMPARE(children, kidsVal); QCOMPARE(family.spouse(),val); // make sure thats your wife:( } void tst_QVersitContactImporter::testFavorite() { QFETCH(QString, favoriteValue); QFETCH(QString, indexValue); QFETCH(bool, favoriteCreated); QVersitDocument document(QVersitDocument::VCard30Type); addFavoritePropertyToDocument(favoriteValue, indexValue, document); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); if (!favoriteCreated) { QCOMPARE(contact.details(QContactFavorite::Type).size(), 0); return; } QContactFavorite favorite = (QContactFavorite)contact.detail(QContactFavorite::Type); QString actualFavoriteValue = favorite.isFavorite() ? QStringLiteral("true") : QStringLiteral("false"); QCOMPARE(actualFavoriteValue, favoriteValue); QCOMPARE(QString::number(favorite.index()), indexValue); } void tst_QVersitContactImporter::testFavorite_data() { QTest::addColumn("favoriteValue"); QTest::addColumn("indexValue"); QTest::addColumn("favoriteCreated"); { QTest::newRow("favorite true") << QString("true") << QString("1") << true; QTest::newRow("favorite false") << QString("false") << QString("1") << true; QTest::newRow("favorite invalid") << QString("invalid") << QString("1") << false; QTest::newRow("favorite empty") << QString("") << QString("1") << false; QTest::newRow("index negative") << QString("true") << QString("-1") << true; QTest::newRow("index multiple digits") << QString("true") << QString("10") << true; QTest::newRow("index invalid") << QString("true") << QString("invalid") << false; QTest::newRow("index invalid mix") << QString("true") << QString("2letters") << false; QTest::newRow("index empty") << QString("true") << QString("") << false; } } void tst_QVersitContactImporter::testMultipleFavorites() { QVersitDocument document(QVersitDocument::VCard30Type); addFavoritePropertyToDocument(QStringLiteral("true"), QStringLiteral("1"), document); addFavoritePropertyToDocument(QStringLiteral("false"), QStringLiteral("2"), document); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QCOMPARE(contact.details(QContactFavorite::Type).size(), 1); QContactFavorite favorite = (QContactFavorite)contact.detail(QContactFavorite::Type); QCOMPARE(favorite.isFavorite(), true); QCOMPARE(favorite.index(), 1); } void tst_QVersitContactImporter::addFavoritePropertyToDocument(QString favorite, QString index, QVersitDocument &document) { QVersitProperty property; property.setName(QStringLiteral("X-QTPROJECT-FAVORITE")); QStringList value; value.append(favorite); value.append(index); property.setValue(value); property.setValueType(QVersitProperty::CompoundType); document.addProperty(property); } void tst_QVersitContactImporter::testSound() { // Test embedded sound file QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty soundProperty; QMultiHash param; param.insert(QStringLiteral("TYPE"),QStringLiteral("WAVE")); soundProperty.setName(QStringLiteral("SOUND")); QByteArray val("111110000011111"); soundProperty.setValue(val); soundProperty.setParameters(param); document.addProperty(soundProperty); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactRingtone ringtone = contact.detail(); QByteArray content = mResourceHandler->mObjects.value(ringtone.audioRingtoneUrl()); QCOMPARE(content, val); // Test sound file as URL document.clear(); soundProperty.clear(); soundProperty.setName(QStringLiteral("SOUND")); QString soundUrl(QStringLiteral("http://qt.nokia.com/audioringtoneurl")); soundProperty.setValue(soundUrl); soundProperty.insertParameter(QStringLiteral("VALUE"),QStringLiteral("URL")); document = createDocumentWithProperty(soundProperty); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); ringtone = contact.detail(); QCOMPARE(ringtone.audioRingtoneUrl().toString(), soundUrl); } void tst_QVersitContactImporter::testTag() { // one value QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty tagProperty; tagProperty.setName(QStringLiteral("CATEGORIES")); tagProperty.setValue(QStringList(QStringLiteral("red"))); tagProperty.setValueType(QVersitProperty::ListType); document.addProperty(tagProperty); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactTag tagDetail = contact.detail(); QCOMPARE(tagDetail.tag(), QStringLiteral("red")); // multiple values tagProperty.setName(QStringLiteral("CATEGORIES")); tagProperty.setValue(QStringList() << QStringLiteral("red") // duplicate from previous property should be pruned << QStringLiteral("green") << QStringLiteral("blue") << QStringLiteral("blue")); // duplicates should be pruned tagProperty.setValueType(QVersitProperty::ListType); document.addProperty(tagProperty); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); QList tagDetails = contact.details(); QCOMPARE(tagDetails.count(), 3); QCOMPARE(tagDetails.at(0).tag(), QStringLiteral("red")); QCOMPARE(tagDetails.at(1).tag(), QStringLiteral("green")); QCOMPARE(tagDetails.at(2).tag(), QStringLiteral("blue")); } void tst_QVersitContactImporter::testExtendedDetail() { QFETCH(QString, extendedDetailName); QFETCH(QVariant, extendedDetailData); QFETCH(QString, extendedDetailDataInProperty); QFETCH(bool, extendedDetailCreated); QVersitDocument document(QVersitDocument::VCard30Type); addExtendedDetailPropertyToDocument(extendedDetailName, extendedDetailDataInProperty, document); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); if (!extendedDetailCreated) { QCOMPARE(contact.details(QContactExtendedDetail::Type).size(), 0); return; } QContactExtendedDetail extendedDetail = (QContactExtendedDetail)contact.detail(QContactExtendedDetail::Type); QCOMPARE(extendedDetail.name(), extendedDetailName); QCOMPARE(extendedDetail.data(), extendedDetailData); QCOMPARE(extendedDetail.data().type(), extendedDetailData.type()); } void tst_QVersitContactImporter::testExtendedDetail_data() { QTest::addColumn("extendedDetailName"); QTest::addColumn("extendedDetailData"); QTest::addColumn("extendedDetailDataInProperty"); QTest::addColumn("extendedDetailCreated"); QString jsonArrayWith("[\n %1\n]\n"); QString jsonArrayWithString = jsonArrayWith.arg("\"%1\""); { QTest::newRow("string data") << QString("name") << QVariant(QString("data")) << jsonArrayWithString.arg("data") << true; QTest::newRow("string data, empty") << QString("name") << QVariant(QString("")) << jsonArrayWithString.arg("") << true; QTest::newRow("string data, containing reserved characters") << QString("name") << QVariant(QString(",;:\\")) << jsonArrayWithString.arg(",;:\\\\") << true; } { QTest::newRow("double data") << QString("name") << QVariant((double)2.0) << jsonArrayWith.arg("2") << true; QTest::newRow("double data, negative") << QString("name") << QVariant((double)-1.0) << jsonArrayWith.arg("-1") << true; QTest::newRow("double data, multiple digits") << QString("name") << QVariant((double)10.2) << jsonArrayWith.arg("10.2") << true; } { QTest::newRow("boolean data") << QString("name") << QVariant(true) << jsonArrayWith.arg("true") << true; } { QTest::newRow("datetime data, imported as a string") << QString("name") << QVariant(QString("1997-07-16T19:20:30+01:00")) << jsonArrayWithString.arg("1997-07-16T19:20:30+01:00") << true; } { QTest::newRow("list data") << QString("name") << QVariant(QVariantList() << QString("string 1") << QString("string 2")) << QString("[\n [\n \"string 1\",\n \"string 2\"\n ]\n]\n") << true; } { QVariantMap map; map["key 1"] = QString("string 1"); map["key 2"] = QString("string 2"); QTest::newRow("map data") << QString("name") << QVariant(map) << QString("[\n {\n \"key 1\": \"string 1\",\n \"key 2\": \"string 2\"\n }\n]\n") << true; } { QVariantMap map; map["key"] = QVariantList() << (double)1 << (double)2; QTest::newRow("map data, containing a nested list") << QString("name") << QVariant(map) << QString("[\n {\n \"key\": [\n 1,\n 2\n ]\n }\n]\n") << true; } { QTest::newRow("empty string as name") << QString("") << QVariant(QString("data")) << jsonArrayWithString.arg("data") << true; QTest::newRow("name containing reserved characters") << QString(",;:\\") << QVariant(QString("data")) << jsonArrayWithString.arg("data") << true; } { QTest::newRow("null denoting an empty variant") << QString("name") << QVariant() << jsonArrayWith.arg("null") << true; QTest::newRow("compact json with extra whitespace removed") << QString("name") << QVariant(QString("data")) << QString("[\"data\"]") << true; } { QTest::newRow("invalid property value: empty json array as property value") << QString("name") << QVariant() << jsonArrayWith.arg("") << false; QTest::newRow("invalid property value: invalid json value") << QString("name") << QVariant() << jsonArrayWith.arg("invalid") << false; } } void tst_QVersitContactImporter::addExtendedDetailPropertyToDocument(QString name, QString data, QVersitDocument &document) { QVersitProperty property; property.setName(QStringLiteral("X-QTPROJECT-EXTENDED-DETAIL")); property.setValue(QStringList() << name << data); property.setValueType(QVersitProperty::CompoundType); document.addProperty(property); } void tst_QVersitContactImporter::testMultipleExtendedDetails() { QVersitDocument document(QVersitDocument::VCard30Type); QString jsonArrayWith("[\n %1\n]\n"); QString jsonArrayWithString = jsonArrayWith.arg("\"%1\""); addExtendedDetailPropertyToDocument("detailName1", jsonArrayWithString.arg("detailData1"), document); addExtendedDetailPropertyToDocument("detailName2", jsonArrayWithString.arg("detailData2"), document); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QCOMPARE(contact.details(QContactDetail::TypeExtendedDetail).size(), 2); QList actualDetails = contact.details(QContactDetail::TypeExtendedDetail); QContactExtendedDetail extendedDetail1 = (QContactExtendedDetail)actualDetails.at(0); QContactExtendedDetail extendedDetail2 = (QContactExtendedDetail)actualDetails.at(1); QCOMPARE(extendedDetail1.name(), QString("detailName1")); QCOMPARE(extendedDetail1.data().toString(), QString("detailData1")); QCOMPARE(extendedDetail2.name(), QString("detailName2")); QCOMPARE(extendedDetail2.data().toString(), QString("detailData2")); } void tst_QVersitContactImporter::testPref() { QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty property1; property1.setName(QStringLiteral("TEL")); property1.setValue(QStringLiteral("1")); document.addProperty(property1); QVersitProperty property2; property2.setName(QStringLiteral("TEL")); property2.setValue(QStringLiteral("2")); property2.insertParameter(QStringLiteral("TYPE"), QStringLiteral("PREF")); document.addProperty(property2); QVersitProperty property3; property3.setName(QStringLiteral("TEL")); property3.setValue(QStringLiteral("3")); property3.insertParameter(QStringLiteral("TYPE"), QStringLiteral("PREF")); document.addProperty(property3); QVersitProperty property4; property4.setName(QStringLiteral("TEL")); property4.setValue(QStringLiteral("4")); document.addProperty(property4); // Test that pref details comes first. QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactPhoneNumber firstNumber = contact.detail(); QCOMPARE(firstNumber.number(), QStringLiteral("2")); QList numbers = contact.details(); QCOMPARE(numbers.at(0).number(), QStringLiteral("2")); QCOMPARE(numbers.at(1).number(), QStringLiteral("3")); QCOMPARE(numbers.at(2).number(), QStringLiteral("1")); QCOMPARE(numbers.at(3).number(), QStringLiteral("4")); } void tst_QVersitContactImporter::testPropertyHandler() { QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty property; // No unconverted properties, no converted properties either. Fails with EmptyDocumentError QVERIFY(!mImporter->importDocuments(QList() << document)); QCOMPARE(mPropertyHandler->mUnknownProperties.size(), 0); QCOMPARE(mPropertyHandler->mPreProcessedProperties.size(), 0); QCOMPARE(mPropertyHandler->mPostProcessedProperties.size(), 0); // No unconverted properties, one converted property mPropertyHandler->clear(); property.setName(QStringLiteral("FN")); property.setValue(QStringLiteral("John Citizen")); document.addProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QCOMPARE(mPropertyHandler->mUnknownProperties.size(), 0); QCOMPARE(mPropertyHandler->mPreProcessedProperties.size(), 1); QCOMPARE(mPropertyHandler->mPostProcessedProperties.size(), 1); // Set the handler to override handling of the property mPropertyHandler->clear(); mPropertyHandler->mPreProcess = true; document.clear(); document.setType(QVersitDocument::VCard30Type); property.setName(QStringLiteral("FN")); property.setValue(QStringLiteral("John Citizen")); document.addProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); contact = mImporter->contacts().first(); QCOMPARE(mPropertyHandler->mUnknownProperties.size(), 0); QCOMPARE(mPropertyHandler->mPreProcessedProperties.size(), 1); QCOMPARE(mPropertyHandler->mPostProcessedProperties.size(), 0); QContactDetail nameDetail = contact.detail(QContactName::Type); QVERIFY(nameDetail.isEmpty()); // One unknown property mPropertyHandler->clear(); property.setName(QStringLiteral("X-EXTENSION-1")); property.setValue(QStringLiteral("extension value 1")); document.addProperty(property); mImporter->importDocuments(QList() << document); QList unknownProperties = mPropertyHandler->mUnknownProperties; QCOMPARE(unknownProperties.count(), 1); QCOMPARE(unknownProperties[0].name(), QStringLiteral("X-EXTENSION-1")); QCOMPARE(unknownProperties[0].value(), QStringLiteral("extension value 1")); // Two unknown properties mPropertyHandler->clear(); property.setName(QStringLiteral("X-EXTENSION-2")); property.setValue(QStringLiteral("extension value 2")); document.addProperty(property); mImporter->importDocuments(QList() << document); unknownProperties = mPropertyHandler->mUnknownProperties; QCOMPARE(unknownProperties.count(), 2); QCOMPARE(unknownProperties[0].name(), QStringLiteral("X-EXTENSION-1")); QCOMPARE(unknownProperties[0].value(), QStringLiteral("extension value 1")); QCOMPARE(unknownProperties[1].name(), QStringLiteral("X-EXTENSION-2")); QCOMPARE(unknownProperties[1].value(), QStringLiteral("extension value 2")); } void tst_QVersitContactImporter::testInvalidDocument() { // invalid document (invalid type) QList documents; QVersitDocument document(QVersitDocument::InvalidType); QVersitProperty nameProperty; nameProperty.setName(QStringLiteral("FN")); nameProperty.setValue(QStringLiteral("John Citizen")); document.addProperty(nameProperty); documents.append(document); // valid document in the same list QVersitProperty telProperty; telProperty.setName(QStringLiteral("TEL")); telProperty.setValue(QStringLiteral("1234")); document.addProperty(telProperty); document.setType(QVersitDocument::VCard21Type); documents.append(document); QVERIFY(!mImporter->importDocuments(documents)); QMap errorMap = mImporter->errorMap(); QCOMPARE(errorMap.size(), 1); QVERIFY(errorMap.contains(0)); QVERIFY(errorMap.value(0) == QVersitContactImporter::InvalidDocumentError); QList contacts = mImporter->contacts(); QCOMPARE(contacts.size(), 1); QContactPhoneNumber phoneDetail = contacts.first().detail(); QCOMPARE(phoneDetail.number(), QStringLiteral("1234")); // empty document document.clear(); document.setType(QVersitDocument::VCard21Type); QVERIFY(!mImporter->importDocuments(QList() << document)); errorMap = mImporter->errorMap(); QCOMPARE(errorMap.size(), 1); QCOMPARE(errorMap.value(0), QVersitContactImporter::EmptyDocumentError); QCOMPARE(mImporter->errors(), errorMap); } void tst_QVersitContactImporter::testEmailWithContextOther() { QVersitProperty property; property.setName(QStringLiteral("EMAIL")); QString value(QStringLiteral("john.citizen@example.com")); property.setValue(value); property.insertParameter(QStringLiteral("TYPE"),QStringLiteral("OTHER")); QVersitDocument document = createDocumentWithProperty(property); QVERIFY(mImporter->importDocuments(QList() << document)); QContact contact = mImporter->contacts().first(); QContactEmailAddress email = contact.detail(); QCOMPARE(email.emailAddress(),value); const QList contexts = email.contexts(); QCOMPARE(contexts.count(),1); QVERIFY(contexts.contains(QContactDetail::ContextOther)); } QVersitDocument tst_QVersitContactImporter::createDocumentWithProperty( const QVersitProperty& property) { QVersitDocument document(QVersitDocument::VCard30Type); document.addProperty(property); return document; } QVersitDocument tst_QVersitContactImporter::createDocumentWithNameAndPhoto( const QString& name, QByteArray image, const QString& imageType) { QVersitDocument document(QVersitDocument::VCard30Type); QVersitProperty nameProperty; nameProperty.setName(QStringLiteral("FN")); nameProperty.setValue(name); document.addProperty(nameProperty); QVersitProperty property; property.setName(QStringLiteral("PHOTO")); property.setValue(image); if (imageType != QString()) { property.insertParameter(QStringLiteral("TYPE"), imageType); } document.addProperty(property); return document; } QTEST_MAIN(tst_QVersitContactImporter) tests/auto/versit/qversitcontactimporter/tst_qversitcontactimporter.h000066400000000000000000000105051233466112000272340ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef tst_QVERSITCONTACTIMPORTER_H #define tst_QVERSITCONTACTIMPORTER_H #include #include #include QT_BEGIN_NAMESPACE_VERSIT class QVersitContactImporter; class QVersitContactImporterPrivate; QT_END_NAMESPACE_VERSIT QTVERSIT_USE_NAMESPACE class MyQVersitContactImporterPropertyHandler; class MyQVersitResourceHandler; class tst_QVersitContactImporter : public QObject { Q_OBJECT private slots: // Tests void init(); void cleanup(); void testName(); void testNameWithFormatted(); void testAddress(); void testAddressWithoutSubTypes(); void testTel(); void testTelWithoutSubTypes(); void testEmail(); void testUrl(); void testUid(); void testOrganizationName(); void testOrganizationTitle(); void testOrganizationLogo(); void testOrganizationAssistant(); void testOrganizationRole(); void testTimeStamp(); void testVersion(); void testVersion_data(); void testMultipleVersions(); void testAnniversary(); void testBirthday(); void testGender(); void testNickname(); void testAvatarUrl(); void testAvatarInvalid(); void testGeo(); void testNote(); void testDisplayLabel(); void testOnlineAccount(); void testFamily(); void testFavorite(); void testFavorite_data(); void testMultipleFavorites(); void testSound(); void testTag(); void testExtendedDetail(); void testExtendedDetail_data(); void testMultipleExtendedDetails(); void testPref(); void testPropertyHandler(); void testInvalidDocument(); void testEmailWithContextOther(); private: // Utilities QVersitDocument createDocumentWithProperty(const QVersitProperty& property); QVersitDocument createDocumentWithNameAndPhoto( const QString& name, QByteArray image, const QString& photoType); void addVersionPropertyToDocument(QString sequenceNumber, QString extendedVersion, QVersitDocument &document); void addFavoritePropertyToDocument(QString favorite, QString index, QVersitDocument &document); void addExtendedDetailPropertyToDocument(QString name, QString data, QVersitDocument &document); private: QVersitContactImporter* mImporter; MyQVersitContactImporterPropertyHandler* mPropertyHandler; MyQVersitResourceHandler* mResourceHandler; }; #endif // tst_QVERSITCONTACTIMPORTER_H tests/auto/versit/qversitcontactplugins/000077500000000000000000000000001233466112000211555ustar00rootroot00000000000000tests/auto/versit/qversitcontactplugins/plugin1/000077500000000000000000000000001233466112000225345ustar00rootroot00000000000000tests/auto/versit/qversitcontactplugins/plugin1/plugin1.cpp000066400000000000000000000076111233466112000246240ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include "plugin1.h" QTVERSIT_USE_NAMESPACE class ContactHandler1 : public QVersitContactHandler { public: void propertyProcessed(const QVersitDocument& document, const QVersitProperty& property, const QContact& contact, bool* alreadyProcessed, QList* updatedDetails) { Q_UNUSED(document) Q_UNUSED(property) Q_UNUSED(contact) Q_UNUSED(alreadyProcessed) Q_UNUSED(updatedDetails) } void documentProcessed(const QVersitDocument& document, QContact* contact) { Q_UNUSED(document) QContactDetail detail(QContactDetail::TypeExtendedDetail); detail.setValue(0, 1); contact->saveDetail(&detail); } void detailProcessed(const QContact& contact, const QContactDetail& detail, const QVersitDocument& document, QSet* processedFields, QList* toBeRemoved, QList* toBeAdded) { Q_UNUSED(contact) Q_UNUSED(detail) Q_UNUSED(document) Q_UNUSED(processedFields) Q_UNUSED(toBeRemoved) Q_UNUSED(toBeAdded) } void contactProcessed(const QContact& contact, QVersitDocument* document) { Q_UNUSED(contact) QVersitProperty property; property.setName("TEST-PROPERTY"); property.setValue("1"); document->addProperty(property); } }; QString Plugin1::name() const { return QStringLiteral("org.qt-project.Qt.QVersitContactPluginsTest.Plugin1"); } int Plugin1::index() const { return 5; } QVersitContactHandler* Plugin1::createHandler() const { return new ContactHandler1; } QSet Plugin1::profiles() const { return QSet() << "Test"; } tests/auto/versit/qversitcontactplugins/plugin1/plugin1.h000066400000000000000000000046261233466112000242740ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef PLUGIN_H #define PLUGIN_H #include #include #include QTVERSIT_USE_NAMESPACE class Plugin1 : public QVersitContactHandlerFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QVersitContactHandlerFactoryInterface" FILE "plugin1.json") public: QString name() const; int index() const; QVersitContactHandler* createHandler() const; QSet profiles() const; }; #endif tests/auto/versit/qversitcontactplugins/plugin1/plugin1.json000066400000000000000000000001121233466112000250000ustar00rootroot00000000000000{ "Keys": [ "org.qt-project.Qt.QVersitContactPluginsTest.Plugin1" ] } tests/auto/versit/qversitcontactplugins/plugin1/plugin1.pro000066400000000000000000000001071233466112000246330ustar00rootroot00000000000000include(../versitplugin.pri) DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/versit/qversitcontactplugins/plugin2/000077500000000000000000000000001233466112000225355ustar00rootroot00000000000000tests/auto/versit/qversitcontactplugins/plugin2/plugin2.cpp000066400000000000000000000076111233466112000246260ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include "plugin2.h" QTVERSIT_USE_NAMESPACE class ContactHandler2 : public QVersitContactHandler { public: void propertyProcessed(const QVersitDocument& document, const QVersitProperty& property, const QContact& contact, bool* alreadyProcessed, QList* updatedDetails) { Q_UNUSED(document) Q_UNUSED(property) Q_UNUSED(contact) Q_UNUSED(alreadyProcessed) Q_UNUSED(updatedDetails) } void documentProcessed(const QVersitDocument& document, QContact* contact) { Q_UNUSED(document) QContactDetail detail(QContactDetail::TypeExtendedDetail); detail.setValue(0, 2); contact->saveDetail(&detail); } void detailProcessed(const QContact& contact, const QContactDetail& detail, const QVersitDocument& document, QSet* processedFields, QList* toBeRemoved, QList* toBeAdded) { Q_UNUSED(contact) Q_UNUSED(detail) Q_UNUSED(document) Q_UNUSED(processedFields) Q_UNUSED(toBeRemoved) Q_UNUSED(toBeAdded) } void contactProcessed(const QContact& contact, QVersitDocument* document) { Q_UNUSED(contact) QVersitProperty property; property.setName("TEST-PROPERTY"); property.setValue("2"); document->addProperty(property); } }; QString Plugin2::name() const { return QStringLiteral("org.qt-project.Qt.QVersitContactPluginsTest.Plugin2"); } int Plugin2::index() const { return 4; } QVersitContactHandler* Plugin2::createHandler() const { return new ContactHandler2; } QSet Plugin2::profiles() const { return QSet() << "Test"; } tests/auto/versit/qversitcontactplugins/plugin2/plugin2.h000066400000000000000000000046261233466112000242760ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef PLUGIN_H #define PLUGIN_H #include #include #include QTVERSIT_USE_NAMESPACE class Plugin2 : public QVersitContactHandlerFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QVersitContactHandlerFactoryInterface" FILE "plugin2.json") public: QString name() const; int index() const; QVersitContactHandler* createHandler() const; QSet profiles() const; }; #endif tests/auto/versit/qversitcontactplugins/plugin2/plugin2.json000066400000000000000000000001121233466112000250020ustar00rootroot00000000000000{ "Keys": [ "org.qt-project.Qt.QVersitContactPluginsTest.Plugin2" ] } tests/auto/versit/qversitcontactplugins/plugin2/plugin2.pro000066400000000000000000000001071233466112000246350ustar00rootroot00000000000000include(../versitplugin.pri) DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/versit/qversitcontactplugins/plugin3/000077500000000000000000000000001233466112000225365ustar00rootroot00000000000000tests/auto/versit/qversitcontactplugins/plugin3/plugin3.cpp000066400000000000000000000076111233466112000246300ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include "plugin3.h" QTVERSIT_USE_NAMESPACE class ContactHandler3 : public QVersitContactHandler { public: void propertyProcessed(const QVersitDocument& document, const QVersitProperty& property, const QContact& contact, bool* alreadyProcessed, QList* updatedDetails) { Q_UNUSED(document) Q_UNUSED(property) Q_UNUSED(contact) Q_UNUSED(alreadyProcessed) Q_UNUSED(updatedDetails) } void documentProcessed(const QVersitDocument& document, QContact* contact) { Q_UNUSED(document) QContactDetail detail(QContactDetail::TypeExtendedDetail); detail.setValue(0, 3); contact->saveDetail(&detail); } void detailProcessed(const QContact& contact, const QContactDetail& detail, const QVersitDocument& document, QSet* processedFields, QList* toBeRemoved, QList* toBeAdded) { Q_UNUSED(contact) Q_UNUSED(detail) Q_UNUSED(document) Q_UNUSED(processedFields) Q_UNUSED(toBeRemoved) Q_UNUSED(toBeAdded) } void contactProcessed(const QContact& contact, QVersitDocument* document) { Q_UNUSED(contact) QVersitProperty property; property.setName("TEST-PROPERTY"); property.setValue("3"); document->addProperty(property); } }; QString Plugin3::name() const { return QStringLiteral("org.qt-project.Qt.QVersitContactPluginsTest.Plugin3"); } int Plugin3::index() const { return 3; } QVersitContactHandler* Plugin3::createHandler() const { return new ContactHandler3; } QSet Plugin3::profiles() const { return QSet() << "Test"; } tests/auto/versit/qversitcontactplugins/plugin3/plugin3.h000066400000000000000000000046261233466112000243000ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef PLUGIN_H #define PLUGIN_H #include #include #include QTVERSIT_USE_NAMESPACE class Plugin3 : public QVersitContactHandlerFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QVersitContactHandlerFactoryInterface" FILE "plugin3.json") public: QString name() const; int index() const; QVersitContactHandler* createHandler() const; QSet profiles() const; }; #endif tests/auto/versit/qversitcontactplugins/plugin3/plugin3.json000066400000000000000000000001121233466112000250040ustar00rootroot00000000000000{ "Keys": [ "org.qt-project.Qt.QVersitContactPluginsTest.Plugin3" ] } tests/auto/versit/qversitcontactplugins/plugin3/plugin3.pro000066400000000000000000000001071233466112000246370ustar00rootroot00000000000000include(../versitplugin.pri) DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/versit/qversitcontactplugins/plugin4/000077500000000000000000000000001233466112000225375ustar00rootroot00000000000000tests/auto/versit/qversitcontactplugins/plugin4/plugin4.cpp000066400000000000000000000076111233466112000246320ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include "plugin4.h" QTVERSIT_USE_NAMESPACE class ContactHandler4 : public QVersitContactHandler { public: void propertyProcessed(const QVersitDocument& document, const QVersitProperty& property, const QContact& contact, bool* alreadyProcessed, QList* updatedDetails) { Q_UNUSED(document) Q_UNUSED(property) Q_UNUSED(contact) Q_UNUSED(alreadyProcessed) Q_UNUSED(updatedDetails) } void documentProcessed(const QVersitDocument& document, QContact* contact) { Q_UNUSED(document) QContactDetail detail(QContactDetail::TypeExtendedDetail); detail.setValue(0, 4); contact->saveDetail(&detail); } void detailProcessed(const QContact& contact, const QContactDetail& detail, const QVersitDocument& document, QSet* processedFields, QList* toBeRemoved, QList* toBeAdded) { Q_UNUSED(contact) Q_UNUSED(detail) Q_UNUSED(document) Q_UNUSED(processedFields) Q_UNUSED(toBeRemoved) Q_UNUSED(toBeAdded) } void contactProcessed(const QContact& contact, QVersitDocument* document) { Q_UNUSED(contact) QVersitProperty property; property.setName("TEST-PROPERTY"); property.setValue("4"); document->addProperty(property); } }; QString Plugin4::name() const { return QStringLiteral("org.qt-project.Qt.QVersitContactPluginsTest.Plugin4"); } int Plugin4::index() const { return 2; } QVersitContactHandler* Plugin4::createHandler() const { return new ContactHandler4; } QSet Plugin4::profiles() const { return QSet() << "Test"; } tests/auto/versit/qversitcontactplugins/plugin4/plugin4.h000066400000000000000000000046261233466112000243020ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef PLUGIN_H #define PLUGIN_H #include #include #include QTVERSIT_USE_NAMESPACE class Plugin4 : public QVersitContactHandlerFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QVersitContactHandlerFactoryInterface" FILE "plugin4.json") public: QString name() const; int index() const; QVersitContactHandler* createHandler() const; QSet profiles() const; }; #endif tests/auto/versit/qversitcontactplugins/plugin4/plugin4.json000066400000000000000000000001121233466112000250060ustar00rootroot00000000000000{ "Keys": [ "org.qt-project.Qt.QVersitContactPluginsTest.Plugin4" ] } tests/auto/versit/qversitcontactplugins/plugin4/plugin4.pro000066400000000000000000000001071233466112000246410ustar00rootroot00000000000000include(../versitplugin.pri) DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/versit/qversitcontactplugins/plugin5/000077500000000000000000000000001233466112000225405ustar00rootroot00000000000000tests/auto/versit/qversitcontactplugins/plugin5/plugin5.cpp000066400000000000000000000076111233466112000246340ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include "plugin5.h" QTVERSIT_USE_NAMESPACE class ContactHandler5 : public QVersitContactHandler { public: void propertyProcessed(const QVersitDocument& document, const QVersitProperty& property, const QContact& contact, bool* alreadyProcessed, QList* updatedDetails) { Q_UNUSED(document) Q_UNUSED(property) Q_UNUSED(contact) Q_UNUSED(alreadyProcessed) Q_UNUSED(updatedDetails) } void documentProcessed(const QVersitDocument& document, QContact* contact) { Q_UNUSED(document) QContactDetail detail(QContactDetail::TypeExtendedDetail); detail.setValue(0, 5); contact->saveDetail(&detail); } void detailProcessed(const QContact& contact, const QContactDetail& detail, const QVersitDocument& document, QSet* processedFields, QList* toBeRemoved, QList* toBeAdded) { Q_UNUSED(contact) Q_UNUSED(detail) Q_UNUSED(document) Q_UNUSED(processedFields) Q_UNUSED(toBeRemoved) Q_UNUSED(toBeAdded) } void contactProcessed(const QContact& contact, QVersitDocument* document) { Q_UNUSED(contact) QVersitProperty property; property.setName("TEST-PROPERTY"); property.setValue("5"); document->addProperty(property); } }; QString Plugin5::name() const { return QStringLiteral("org.qt-project.Qt.QVersitContactPluginsTest.Plugin5"); } int Plugin5::index() const { return 1; } QVersitContactHandler* Plugin5::createHandler() const { return new ContactHandler5; } QSet Plugin5::profiles() const { return QSet() << "Test"; } tests/auto/versit/qversitcontactplugins/plugin5/plugin5.h000066400000000000000000000046261233466112000243040ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef PLUGIN_H #define PLUGIN_H #include #include #include QTVERSIT_USE_NAMESPACE class Plugin5 : public QVersitContactHandlerFactory { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QVersitContactHandlerFactoryInterface" FILE "plugin5.json") public: QString name() const; int index() const; QVersitContactHandler* createHandler() const; QSet profiles() const; }; #endif tests/auto/versit/qversitcontactplugins/plugin5/plugin5.json000066400000000000000000000001121233466112000250100ustar00rootroot00000000000000{ "Keys": [ "org.qt-project.Qt.QVersitContactPluginsTest.Plugin5" ] } tests/auto/versit/qversitcontactplugins/plugin5/plugin5.pro000066400000000000000000000001071233466112000246430ustar00rootroot00000000000000include(../versitplugin.pri) DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/versit/qversitcontactplugins/qversitcontactplugins.pro000066400000000000000000000001711233466112000263510ustar00rootroot00000000000000TEMPLATE=subdirs SUBDIRS+=plugin1 SUBDIRS+=plugin2 SUBDIRS+=plugin3 SUBDIRS+=plugin4 SUBDIRS+=plugin5 SUBDIRS+=unittest tests/auto/versit/qversitcontactplugins/unittest/000077500000000000000000000000001233466112000230345ustar00rootroot00000000000000tests/auto/versit/qversitcontactplugins/unittest/tst_qversitcontactplugins.cpp000066400000000000000000000116351233466112000311130ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include QTCONTACTS_USE_NAMESPACE QTVERSIT_USE_NAMESPACE class TestFactory1 : public QVersitContactHandlerFactory { public: QString name() const { return "factory1"; } QVersitContactHandler* createHandler() const { return NULL; } }; class tst_QVersitContactPlugins : public QObject { Q_OBJECT private slots: void testDefaultFactory(); void testImporterPlugins(); void testExporterPlugins(); }; void tst_QVersitContactPlugins::testDefaultFactory() { TestFactory1 factory; QCOMPARE(factory.profiles(), QSet()); QCOMPARE(factory.index(), 0); } void tst_QVersitContactPlugins::testImporterPlugins() { QVersitContactImporter importer("Test"); QVersitDocument document; document.setComponentType("VCARD"); QVersitProperty property; property.setName("FN"); property.setValue("Bob"); document.addProperty(property); QVERIFY(importer.importDocuments(QList() << document)); QCOMPARE(importer.contacts().size(), 1); QList details(importer.contacts().first().details(QContactDetail::TypeExtendedDetail)); QCOMPARE(details.size(), 5); int pluginField = 0; // The plugins have had their index set such that they should be executed in reverse order // Check that they are all loaded, and run in the correct order QCOMPARE(details.at(0).value(pluginField), 5); QCOMPARE(details.at(1).value(pluginField), 4); QCOMPARE(details.at(2).value(pluginField), 3); QCOMPARE(details.at(3).value(pluginField), 2); QCOMPARE(details.at(4).value(pluginField), 1); } void tst_QVersitContactPlugins::testExporterPlugins() { QVersitContactExporter exporter("Test"); QContact contact; QContactName name; name.setFirstName("first name"); contact.saveDetail(&name); QVERIFY(exporter.exportContacts(QList() << contact)); QCOMPARE(exporter.documents().size(), 1); QList properties(exporter.documents().first().properties()); // The plugins have had their index set such that they should be executed in reverse order // Check that they are all loaded, and run in the correct order int n = 0; foreach (QVersitProperty property, properties) { if (property.name() == "TEST-PROPERTY") { switch (n) { case 0: QCOMPARE(property.value(), QStringLiteral("5")); break; case 1: QCOMPARE(property.value(), QStringLiteral("4")); break; case 2: QCOMPARE(property.value(), QStringLiteral("3")); break; case 3: QCOMPARE(property.value(), QStringLiteral("2")); break; case 4: QCOMPARE(property.value(), QStringLiteral("1")); break; } n++; } } QCOMPARE(n, 5); } QTEST_MAIN(tst_QVersitContactPlugins) #include "tst_qversitcontactplugins.moc" tests/auto/versit/qversitcontactplugins/unittest/unittest.pro000066400000000000000000000002521233466112000254340ustar00rootroot00000000000000include(../../../auto.pri) TARGET = tst_qversitcontactplugins QT += contacts versit SOURCES += tst_qversitcontactplugins.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/versit/qversitcontactplugins/versitplugin.pri000066400000000000000000000006221233466112000244240ustar00rootroot00000000000000NAME = $$TARGET TEMPLATE = lib CONFIG += plugin testplugin TARGET = $$qtLibraryTarget(versit_$$NAME) PLUGIN_TYPE = versit QT += contacts versit debug_and_release { CONFIG(debug, debug|release): \ INFIX = debug/ else: \ INFIX = release/ } DESTDIR = $$shadowed($$PWD)/unittest/$${INFIX}plugins/versit SOURCES += $${NAME}.cpp HEADERS += $${NAME}.h OTHER_FILES += $${NAME}.json tests/auto/versit/qversitdocument/000077500000000000000000000000001233466112000177365ustar00rootroot00000000000000tests/auto/versit/qversitdocument/qversitdocument.pro000066400000000000000000000002251233466112000237130ustar00rootroot00000000000000include(../../auto.pri) QT += versit HEADERS += tst_qversitdocument.h SOURCES += tst_qversitdocument.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/versit/qversitdocument/tst_qversitdocument.cpp000066400000000000000000000177551233466112000246070ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/versit #include "tst_qversitdocument.h" #include #include #include #include QTVERSIT_USE_NAMESPACE void tst_QVersitDocument::init() { mVersitDocument = new QVersitDocument(); QVERIFY(mVersitDocument); } void tst_QVersitDocument::cleanup() { delete mVersitDocument; } void tst_QVersitDocument::testConstructor() { QCOMPARE(QVersitDocument::InvalidType, mVersitDocument->type()); } void tst_QVersitDocument::testType() { mVersitDocument->setType(QVersitDocument::VCard21Type); QCOMPARE(QVersitDocument::VCard21Type, mVersitDocument->type()); mVersitDocument->setType(QVersitDocument::VCard30Type); QCOMPARE(QVersitDocument::VCard30Type, mVersitDocument->type()); } void tst_QVersitDocument::testAddProperty() { QCOMPARE(0, mVersitDocument->properties().count()); QVersitProperty property; mVersitDocument->addProperty(property); QCOMPARE(1, mVersitDocument->properties().count()); } void tst_QVersitDocument::testRemoveProperty() { // Remove an empty property. QCOMPARE(mVersitDocument->properties().count(), 0); QVersitProperty property; mVersitDocument->addProperty(property); mVersitDocument->removeProperty(property); QCOMPARE(mVersitDocument->properties().count(), 0); // A full property. property.setName(QStringLiteral("TEL")); property.setGroups(QStringList(QStringLiteral("HOME"))); QMultiHash params; params.insert(QStringLiteral("TYPE"), QStringLiteral("HOME")); property.setParameters(params); property.setValue(QStringLiteral("123")); mVersitDocument->addProperty(property); QCOMPARE(mVersitDocument->properties().count(), 1); QVersitProperty property2; property2.setName(QStringLiteral("TEL")); // Remove with a partial property fails. mVersitDocument->removeProperty(property2); QCOMPARE(mVersitDocument->properties().count(), 1); property2.setGroups(QStringList(QStringLiteral("HOME"))); property2.setParameters(params); property2.setValue(QStringLiteral("123")); // Remove with a fully specified property succeeds. mVersitDocument->removeProperty(property2); QCOMPARE(mVersitDocument->properties().count(), 0); } void tst_QVersitDocument::testRemoveAllProperties() { QString name(QStringLiteral("FN")); // Try to remove from an empty document QCOMPARE(0, mVersitDocument->properties().count()); mVersitDocument->removeProperties(name); QCOMPARE(0, mVersitDocument->properties().count()); // Try to remove from a non-empty document, name does not match QVersitProperty property; property.setName(QStringLiteral("TEL")); mVersitDocument->addProperty(property); QCOMPARE(1, mVersitDocument->properties().count()); mVersitDocument->removeProperties(name); QCOMPARE(1, mVersitDocument->properties().count()); // Remove from a non-empty document, name matches mVersitDocument->removeProperties(QStringLiteral("TEL")); QCOMPARE(0, mVersitDocument->properties().count()); // Remove from a document with two properties, first matches property.setName(name); mVersitDocument->addProperty(property); property.setName(QStringLiteral("TEL")); mVersitDocument->addProperty(property); QCOMPARE(2, mVersitDocument->properties().count()); mVersitDocument->removeProperties(name); QCOMPARE(1, mVersitDocument->properties().count()); // Remove from a document with two properties, second matches property.setName(name); mVersitDocument->addProperty(property); QCOMPARE(2, mVersitDocument->properties().count()); mVersitDocument->removeProperties(name); QCOMPARE(1, mVersitDocument->properties().count()); } void tst_QVersitDocument::testEquality() { QVersitDocument document1; QVersitDocument document2; QVERIFY(document1.isEmpty()); QVERIFY(document1 == document2); QVERIFY(!(document1 != document2)); QVersitProperty property; property.setName(QStringLiteral("FN")); property.setValue(QStringLiteral("John Citizen")); document2.addProperty(property); QVERIFY(!(document1 == document2)); QVERIFY(document1 != document2); QVERIFY(!document2.isEmpty()); document1.addProperty(property); QVERIFY(document1 == document2); QVERIFY(!(document1 != document2)); document2.clear(); QVERIFY(document2.isEmpty()); document1.clear(); QVERIFY(document1 == document2); QVERIFY(!(document1 != document2)); document2.setType(QVersitDocument::VCard21Type); QVERIFY(!(document1 == document2)); QVERIFY(document1 != document2); QVERIFY(!document2.isEmpty()); document2 = document1; QVERIFY(document1 == document2); } void tst_QVersitDocument::testHash() { QVersitDocument document1; document1.setType(QVersitDocument::VCard30Type); QVersitProperty property1; property1.setName(QStringLiteral("name")); property1.setValue(QStringLiteral("value")); document1.addProperty(property1); QVersitDocument document2; document2.setType(QVersitDocument::VCard30Type); document2.addProperty(property1); QVersitDocument document3; document3.setType(QVersitDocument::VCard30Type); QVersitProperty property3; property3.setName(QStringLiteral("name")); property3.setValue(QStringLiteral("another value")); document3.addProperty(property3); QVersitDocument document4; // no properties document4.setType(QVersitDocument::VCard30Type); QVersitDocument document5 = document1; document5.addSubDocument(document4); QVersitDocument document6 = document1; document6.setComponentType(QStringLiteral("VEVENT")); QVersitDocument document7 = document1; document7.addProperty(QVersitProperty()); QVERIFY(qHash(document1) == qHash(document2)); QVERIFY(qHash(document1) != qHash(document3)); QVERIFY(qHash(document1) != qHash(document4)); QVERIFY(qHash(document1) != qHash(document5)); QVERIFY(qHash(document1) != qHash(document6)); QVERIFY(qHash(document1) != qHash(document7)); } QTEST_MAIN(tst_QVersitDocument) tests/auto/versit/qversitdocument/tst_qversitdocument.h000066400000000000000000000050211233466112000242330ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef tst_QVERSITDOCUMENT_H #define tst_QVERSITDOCUMENT_H #include #include QT_BEGIN_NAMESPACE_VERSIT class QVersitDocument; QT_END_NAMESPACE_VERSIT QTVERSIT_USE_NAMESPACE class tst_QVersitDocument : public QObject { Q_OBJECT private slots: void init(); void cleanup(); void testConstructor(); void testType(); void testAddProperty(); void testRemoveProperty(); void testRemoveAllProperties(); void testEquality(); void testHash(); private: // data QVersitDocument* mVersitDocument; }; #endif // tst_QVERSITDOCUMENT_H tests/auto/versit/qversitproperty/000077500000000000000000000000001233466112000200045ustar00rootroot00000000000000tests/auto/versit/qversitproperty/qversitproperty.pro000066400000000000000000000002441233466112000240300ustar00rootroot00000000000000include(../../auto.pri) QT += versit versit-private HEADERS += tst_qversitproperty.h SOURCES += tst_qversitproperty.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/versit/qversitproperty/tst_qversitproperty.cpp000066400000000000000000000165411233466112000247130ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/versit #include "tst_qversitproperty.h" #include #include #include #include QTVERSIT_USE_NAMESPACE void tst_QVersitProperty::init() { mVersitProperty = new QVersitProperty(); QVERIFY(mVersitProperty); } void tst_QVersitProperty::cleanup() { delete mVersitProperty; } void tst_QVersitProperty::testGroup() { // One group QStringList group(QStringLiteral("GROUP_NAME")); mVersitProperty->setGroups(group); QCOMPARE(mVersitProperty->groups(), group); // Several groups QStringList groupList; groupList.append(QStringLiteral("GROUP1")); groupList.append(QStringLiteral("Group2")); groupList.append(QStringLiteral("group3")); mVersitProperty->setGroups(groupList); QCOMPARE(mVersitProperty->groups(), groupList); } void tst_QVersitProperty::testName() { // Name in upper case QString name(QStringLiteral("TEL")); mVersitProperty->setName(name); QCOMPARE(mVersitProperty->name(), name); // Name in lower case, converted automatically to upper case mVersitProperty->setName(QStringLiteral("tel")); QCOMPARE(mVersitProperty->name(), name); } void tst_QVersitProperty::testParameters() { QString typeParameterName(QStringLiteral("TYPE")); QString name(QStringLiteral("type")); QString value1(QStringLiteral("home")); mVersitProperty->insertParameter(name,value1); QMultiHash parameters = mVersitProperty->parameters(); QCOMPARE(parameters.count(), 1); QVERIFY(parameters.contains(typeParameterName,QStringLiteral("home"))); QString value2(QStringLiteral("voice")); mVersitProperty->insertParameter(name,value2); parameters = mVersitProperty->parameters(); QCOMPARE(parameters.count(), 2); QVERIFY(parameters.contains(typeParameterName,QStringLiteral("home"))); QVERIFY(parameters.contains(typeParameterName,QStringLiteral("voice"))); mVersitProperty->removeParameter(name,value1); QCOMPARE(mVersitProperty->parameters().count(), 1); QVERIFY(parameters.contains(typeParameterName,QStringLiteral("home"))); mVersitProperty->removeParameter(name,value2); QCOMPARE(mVersitProperty->parameters().count(), 0); mVersitProperty->insertParameter(name, value1); mVersitProperty->insertParameter(name, value2); QCOMPARE(mVersitProperty->parameters().count(), 2); mVersitProperty->removeParameters(name); QCOMPARE(mVersitProperty->parameters().count(), 0); } void tst_QVersitProperty::testValue() { QString value(QStringLiteral("050484747")); mVersitProperty->setValue(value); QCOMPARE(mVersitProperty->value(), value); } void tst_QVersitProperty::testEmbeddedDocument() { QVersitDocument document; QVersitProperty property; property.setName(QStringLiteral("X-tension")); document.addProperty(property); mVersitProperty->setValue(QVariant::fromValue(document)); QList embeddedDocumentProperties = mVersitProperty->value().properties(); QCOMPARE(embeddedDocumentProperties.count(),1); QCOMPARE(embeddedDocumentProperties[0].name(),QStringLiteral("X-TENSION")); } void tst_QVersitProperty::testEquality() { QVersitProperty property1; QVersitProperty property2; QVERIFY(property1.isEmpty()); QVERIFY(property1 == property2); QVERIFY(!(property1 != property2)); property2.setName(QStringLiteral("FN")); property2.setValue(QStringLiteral("John Citizen")); QVERIFY(!(property1 == property2)); QVERIFY(property1 != property2); QVERIFY(!property2.isEmpty()); property1.setName(QStringLiteral("FN")); property1.setValue(QStringLiteral("John Citizen")); QVERIFY(property1 == property2); QVERIFY(!(property1 != property2)); property2.clear(); QVERIFY(property2.isEmpty()); property1.clear(); QVERIFY(property1 == property2); QVERIFY(!(property1 != property2)); } void tst_QVersitProperty::testHash() { QVersitProperty property1; property1.setGroups(QStringList() << QStringLiteral("group1") << QStringLiteral("group2")); property1.setName(QStringLiteral("name")); property1.setValue(QStringLiteral("value")); property1.insertParameter(QStringLiteral("param"), QStringLiteral("value")); QVersitProperty property2; property2.setGroups(QStringList() << QStringLiteral("group1") << QStringLiteral("group2")); property2.setName(QStringLiteral("name")); property2.setValue(QStringLiteral("value")); property2.insertParameter(QStringLiteral("param"), QStringLiteral("value")); QVersitProperty property3; // no groups property3.setName(QStringLiteral("name")); property3.setValue(QStringLiteral("value")); property3.insertParameter(QStringLiteral("param"), QStringLiteral("value")); QVersitProperty property4; // no params property4.setGroups(QStringList() << QStringLiteral("group1") << QStringLiteral("group2")); property4.setName(QStringLiteral("name")); property4.setValue(QStringLiteral("value")); QVERIFY(qHash(property1) == qHash(property2)); QVERIFY(qHash(property1) != qHash(property3)); QVERIFY(qHash(property1) != qHash(property4)); QVERIFY(qHash(property3) != qHash(property4)); QSet set; set.insert(property1); set.insert(property2); set.insert(property3); set.insert(property4); QCOMPARE(set.size(), 3); } QTEST_MAIN(tst_QVersitProperty) tests/auto/versit/qversitproperty/tst_qversitproperty.h000066400000000000000000000050261233466112000243540ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef tst_QVERSITPROPERTY_H #define tst_QVERSITPROPERTY_H #include #include QT_BEGIN_NAMESPACE_VERSIT class QVersitProperty; QT_END_NAMESPACE_VERSIT QTVERSIT_USE_NAMESPACE class tst_QVersitProperty : public QObject { Q_OBJECT private slots: void init(); void cleanup(); private slots: //test methods void testGroup(); void testName(); void testParameters(); void testValue(); void testEmbeddedDocument(); void testEquality(); void testHash(); private: QVersitProperty* mVersitProperty; }; #endif //tst_QVERSITPROPERTY_H tests/auto/versit/qversitreader/000077500000000000000000000000001233466112000173625ustar00rootroot00000000000000tests/auto/versit/qversitreader/qversitreader.pro000066400000000000000000000002401233466112000227600ustar00rootroot00000000000000include(../../auto.pri) QT += versit versit-private HEADERS += tst_qversitreader.h SOURCES += tst_qversitreader.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/versit/qversitreader/tst_qversitreader.cpp000066400000000000000000002314321233466112000236450ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/versit #include "tst_qversitreader.h" #include #include #include #include #include // This says "NOKIA" in Katakana encoded with UTF-8 const QByteArray KATAKANA_NOKIA("\xe3\x83\x8e\xe3\x82\xad\xe3\x82\xa2"); const static QByteArray SAMPLE_GIF_BASE64(QByteArray( "R0lGODlhEgASAIAAAAAAAP///yH5BAEAAAEALAAAAAASABIAAAIdjI+py+0G" "wEtxUmlPzRDnzYGfN3KBaKGT6rDmGxQAOw==")); const static QByteArray SAMPLE_GIF(QByteArray::fromBase64(SAMPLE_GIF_BASE64)); Q_DECLARE_METATYPE(QTVERSIT_PREPEND_NAMESPACE(QVersitDocument::VersitType)) Q_DECLARE_METATYPE(QTVERSIT_PREPEND_NAMESPACE(QVersitProperty)) QTVERSIT_USE_NAMESPACE void tst_QVersitReader::init() { mInputDevice = new QBuffer; mInputDevice->open(QBuffer::ReadWrite); mReader = new QVersitReader; #ifdef QT_BUILD_INTERNAL mReaderPrivate = new QVersitReaderPrivate; #endif mSignalCatcher = new SignalCatcher; connect(mReader, SIGNAL(stateChanged(QVersitReader::State)), mSignalCatcher, SLOT(stateChanged(QVersitReader::State))); connect(mReader, SIGNAL(resultsAvailable()), mSignalCatcher, SLOT(resultsAvailable())); mAsciiCodec = QTextCodec::codecForName("ISO 8859-1"); } void tst_QVersitReader::cleanup() { #ifdef QT_BUILD_INTERNAL delete mReaderPrivate; #endif delete mReader; delete mInputDevice; delete mSignalCatcher; } void tst_QVersitReader::testDevice() { // No device QVERIFY(mReader->device() == NULL); // Device has been set mReader->setDevice(mInputDevice); QVERIFY(mReader->device() == mInputDevice); delete mInputDevice; QVERIFY(mReader->device() == NULL); mInputDevice = new QBuffer; mInputDevice->open(QBuffer::ReadWrite); QVERIFY(mReader->device() == NULL); mReader->setDevice(mInputDevice); QVERIFY(mReader->device() == mInputDevice); } void tst_QVersitReader::testNullDevice() { QVersitReader vr; QVERIFY(vr.device() == NULL); QVERIFY(vr.startReading() == false); QVERIFY(vr.error() == QVersitReader::IOError); vr.setDevice(NULL); QVERIFY(vr.device() == NULL); QVERIFY(vr.startReading() == false); QVERIFY(vr.error() == QVersitReader::IOError); QFile f("does not exist or else"); vr.setDevice(&f); QVERIFY(vr.device() == &f); QVERIFY(vr.startReading() == false); QVERIFY(vr.error() == QVersitReader::IOError); } void tst_QVersitReader::testDefaultCodec() { QVERIFY(mReader->defaultCodec() == 0); mReader->setDefaultCodec(QTextCodec::codecForName("UTF-16BE")); QVERIFY(mReader->defaultCodec() == QTextCodec::codecForName("UTF-16BE")); } void tst_QVersitReader::testValidateUtf8() { QFETCH(QByteArray, bytes); QFETCH(bool, isValid); QCOMPARE(VersitUtils::isValidUtf8(bytes), isValid); } void tst_QVersitReader::testValidateUtf8_data() { // These test cases are taken from // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt // See that page for a description of what they test QTest::addColumn("bytes"); QTest::addColumn("isValid"); // The first 18 are marked as "valid" according to the above page QTest::newRow("1") << QByteArray("\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5") << true; QTest::newRow("2") << QByteArray("\x00") << true; QTest::newRow("3") << QByteArray("\xc2\x80") << true; QTest::newRow("4") << QByteArray("\xe0\xa0\x80") << true; QTest::newRow("5") << QByteArray("\xf0\x90\x80\x80") << true; // We treat 5 and 6 byte characters as invalid as per RFC3629 QTest::newRow("6") << QByteArray("\xf8\x88\x80\x80\x80") << false; QTest::newRow("7") << QByteArray("\xfc\x84\x80\x80\x80\x80") << false; QTest::newRow("8") << QByteArray("\x7f") << true; QTest::newRow("9") << QByteArray("\xdf\xbf") << true; QTest::newRow("10") << QByteArray("\xef\xbf\xbd") << true; QTest::newRow("11") << QByteArray("\xf4\x8f\xbf\xbf") << true; // We treat 5 and 6 byte characters as invalid as per RFC3629 QTest::newRow("12") << QByteArray("\xfb\xbf\xbf\xbf\xbf") << false; QTest::newRow("13") << QByteArray("\xfd\xbf\xbf\xbf\xbf\xbf") << false; QTest::newRow("14") << QByteArray("\xed\x9f\xbf") << true; QTest::newRow("15") << QByteArray("\xee\x80\x80") << true; QTest::newRow("16") << QByteArray("\xef\xbf\xbd") << true; QTest::newRow("17") << QByteArray("\xf4\x8f\xbf\xbf") << true; QTest::newRow("18") << QByteArray("\xf4\x90\x80\x80") << false; // outside the range // The rest are marked as "invalid" according to the above page QTest::newRow("19") << QByteArray("\x80") << false; QTest::newRow("20") << QByteArray("\xbf") << false; QTest::newRow("21") << QByteArray("\x80\xbf") << false; QTest::newRow("22") << QByteArray("\x80\xbf\x80") << false; QTest::newRow("23") << QByteArray("\x80\xbf\x80\xbf") << false; QTest::newRow("24") << QByteArray("\x80\xbf\x80\xbf\x80") << false; QTest::newRow("25") << QByteArray("\x80\xbf\x80\xbf\x80\xbf") << false; QTest::newRow("26") << QByteArray("\x80\xbf\x80\xbf\x80\xbf\x80") << false; QTest::newRow("27") << QByteArray("\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d" "\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1" "\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5" "\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf") << false; QTest::newRow("28") << QByteArray("\xc0\x20\xc1\x20\xc2\x20\xc3\x20\xc4\x20\xc5\x20\xc6\x20" "\xc7\x20\xc8\x20\xc9\x20\xca\x20\xcb\x20\xcc\x20\xcd\x20\xce\x20\xcf\x20\xd0\x20" "\xd1\x20\xd2\x20\xd3\x20\xd4\x20\xd5\x20\xd6\x20\xd7\x20\xd8\x20\xd9\x20\xda\x20" "\xdb\x20\xdc\x20\xdd\x20\xde\x20\xdf\x20") << false; QTest::newRow("29") << QByteArray("\xe0\x20\xe1\x20\xe2\x20\xe3\x20\xe4\x20\xe5\x20\xe6\x20" "\xe7\x20\xe8\x20\xe9\x20\xea\x20\xeb\x20\xec\x20\xed\x20\xee\x20\xef\x20") << false; QTest::newRow("30") << QByteArray("\xf0\x20\xf1\x20\xf2\x20\xf3\x20\xf4\x20\xf5\x20\xf6\x20" "\xf7\x20") << false; QTest::newRow("31") << QByteArray("\xf8\x20\xf9\x20\xfa\x20\xfb\x20") << false; QTest::newRow("32") << QByteArray("\xfc\x20\xfd\x20") << false; QTest::newRow("33") << QByteArray("\xc0") << false; QTest::newRow("34") << QByteArray("\xe0\x80") << false; QTest::newRow("35") << QByteArray("\xf0\x80\x80") << false; QTest::newRow("36") << QByteArray("\xf8\x80\x80\x80") << false; QTest::newRow("37") << QByteArray("\xfc\x80\x80\x80\x80") << false; QTest::newRow("38") << QByteArray("\xdf") << false; QTest::newRow("39") << QByteArray("\xef\xbf") << false; QTest::newRow("40") << QByteArray("\xf7\xbf\xbf") << false; QTest::newRow("41") << QByteArray("\xfb\xbf\xbf\xbf") << false; QTest::newRow("42") << QByteArray("\xfd\xbf\xbf\xbf\xbf") << false; QTest::newRow("43") << QByteArray("\xc0\xe0\x80\xf0\x80\x80\xf8\x80\x80\x80\xfc\x80\x80\x80" "\x80\xdf\xef\xbf\xf7\xbf\xbf\xfb\xbf\xbf\xbf\xfd\xbf\xbf\xbf\xbf") << false; QTest::newRow("44") << QByteArray("\xfe") << false; QTest::newRow("45") << QByteArray("\xff") << false; QTest::newRow("46") << QByteArray("\xfe\xfe\xff\xff") << false; QTest::newRow("47") << QByteArray("\xc0\xaf") << false; QTest::newRow("48") << QByteArray("\xe0\x80\xaf") << false; QTest::newRow("49") << QByteArray("\xf0\x80\x80\xaf") << false; QTest::newRow("50") << QByteArray("\xf8\x80\x80\x80\xaf") << false; QTest::newRow("51") << QByteArray("\xfc\x80\x80\x80\x80\xaf") << false; QTest::newRow("52") << QByteArray("\xc1\xbf") << false; QTest::newRow("53") << QByteArray("\xe0\x9f\xbf") << false; QTest::newRow("54") << QByteArray("\xf0\x8f\xbf\xbf") << false; QTest::newRow("55") << QByteArray("\xf8\x87\xbf\xbf\xbf") << false; QTest::newRow("56") << QByteArray("\xfc\x83\xbf\xbf\xbf\xbf") << false; QTest::newRow("57") << QByteArray("\xc0\x80") << false; QTest::newRow("58") << QByteArray("\xe0\x80\x80") << false; QTest::newRow("59") << QByteArray("\xf0\x80\x80\x80") << false; QTest::newRow("60") << QByteArray("\xf8\x80\x80\x80\x80") << false; QTest::newRow("61") << QByteArray("\xfc\x80\x80\x80\x80\x80") << false; QTest::newRow("62") << QByteArray("\xed\xa0\x80") << false; QTest::newRow("63") << QByteArray("\xed\xad\xbf") << false; QTest::newRow("64") << QByteArray("\xed\xae\x80") << false; QTest::newRow("65") << QByteArray("\xed\xaf\xbf") << false; QTest::newRow("66") << QByteArray("\xed\xb0\x80") << false; QTest::newRow("67") << QByteArray("\xed\xbe\x80") << false; QTest::newRow("68") << QByteArray("\xed\xbf\xbf") << false; QTest::newRow("69") << QByteArray("\xed\xa0\x80\xed\xb0\x80") << false; QTest::newRow("70") << QByteArray("\xed\xa0\x80\xed\xbf\xbf") << false; QTest::newRow("71") << QByteArray("\xed\xad\xbf\xed\xb0\x80") << false; QTest::newRow("72") << QByteArray("\xed\xad\xbf\xed\xbf\xbf") << false; QTest::newRow("73") << QByteArray("\xed\xae\x80\xed\xb0\x80") << false; QTest::newRow("74") << QByteArray("\xed\xae\x80\xed\xbf\xbf") << false; QTest::newRow("75") << QByteArray("\xed\xaf\xbf\xed\xb0\x80") << false; QTest::newRow("76") << QByteArray("\xed\xaf\xbf\xed\xbf\xbf") << false; QTest::newRow("77") << QByteArray("\xef\xbf\xbe") << false; QTest::newRow("78") << QByteArray("\xef\xbf\xbf") << false; // My own tests // 0x110000 is the first one outside the Unicode range QTest::newRow("79") << QByteArray("\xf4\x90\x80\x80") << false; // a 3 byte sequence followed by a single byte QTest::newRow("80") << QByteArray("\xef\xbf\xbd\x20") << true; // a 4 byte sequence followed by a single byte QTest::newRow("81") << QByteArray("\xf4\x8f\xbf\xbf\x20") << true; } void tst_QVersitReader::testDetectCodec() { QFETCH(QByteArray, bytes); QFETCH(QString, expectedFnValue); QTextCodec::setCodecForLocale(QTextCodec::codecForName("ISO 8859-1")); mInputDevice->close(); mInputDevice->setData(bytes); mInputDevice->open(QBuffer::ReadOnly); mInputDevice->seek(0); mReader->setDevice(mInputDevice); QVERIFY(mReader->defaultCodec() == 0); QVERIFY2(mReader->startReading(), QString::number(mReader->error()).toLatin1().data()); QVERIFY2(mReader->waitForFinished(), QString::number(mReader->error()).toLatin1().data()); QCOMPARE(mReader->state(), QVersitReader::FinishedState); QCOMPARE(mReader->error(), QVersitReader::NoError); QVERIFY(mReader->defaultCodec() == 0); // shouldn't change QList results = mReader->results(); QCOMPARE(results.count(),1); QVersitDocument document = results.first(); QCOMPARE(document.properties().size(), 1); QVersitProperty property = document.properties().first(); QCOMPARE(property.name(), QStringLiteral("FN")); QCOMPARE(property.value(), expectedFnValue); } void tst_QVersitReader::testDetectCodec_data() { QTest::addColumn("bytes"); QTest::addColumn("expectedFnValue"); const QString& documentString = QStringLiteral("BEGIN:VCARD\r\nVERSION:2.1\r\nFN:John\r\nEND:VCARD\r\n"); { const QByteArray& document = "\xef\xbb\xbf" + documentString.toUtf8(); QTest::newRow("UTF-8 with BOM") << document << QString::fromLatin1("John"); } { const QByteArray& document = QTextCodec::codecForName("UTF-16BE")->fromUnicode(documentString); QTest::newRow("UTF-16BE with BOM") << document << QString::fromLatin1("John"); } { const QByteArray& document = QTextCodec::codecForName("UTF-16LE")->fromUnicode(documentString); QTest::newRow("UTF-16LE with BOM") << document << QString::fromLatin1("John"); } { const QByteArray& document = VersitUtils::encode(documentString.toLatin1(), QTextCodec::codecForName("UTF-16BE")); QTest::newRow("UTF-16BE without BOM") << document << QString::fromLatin1("John"); } { const QByteArray& document = VersitUtils::encode(documentString.toLatin1(), QTextCodec::codecForName("UTF-16LE")); QTest::newRow("UTF-16LE without BOM") << document << QString::fromLatin1("John"); } { const QByteArray& document = QTextCodec::codecForName("UTF-32BE")->fromUnicode(documentString); QTest::newRow("UTF-32BE with BOM") << document << QString::fromLatin1("John"); } { const QByteArray& document = QTextCodec::codecForName("UTF-32LE")->fromUnicode(documentString); QTest::newRow("UTF-32LE with BOM") << document << QString::fromLatin1("John"); } { const QByteArray& document = documentString.toUtf8(); QTest::newRow("Plain ASCII") << document << QString::fromLatin1("John"); } { const QByteArray& document = "BEGIN:VCARD\r\nVERSION:2.1\r\nFN:" + KATAKANA_NOKIA + "\r\nEND:VCARD\r\n"; QTest::newRow("Non-ASCII UTF-8") << document << QString::fromUtf8(KATAKANA_NOKIA); } { // some Scandinavian characters, note that "\xe4\xe4" is invalid UTF-8, as is "\xf6n" const QByteArray& document = "BEGIN:VCARD\r\nVERSION:2.1\r\n" "FN:P\xe4\xe4kk\xf6nen\r\n" "END:VCARD\r\n"; QTest::newRow("Non-ASCII Latin-1") << document << QString::fromLatin1("P\xe4\xe4kk\xf6nen"); } { // as above, but quoted-printable const QByteArray& document = "BEGIN:VCARD\r\nVERSION:2.1\r\n" "FN;ENCODING=QUOTED-PRINTABLE:P=E4=E4kk=F6nen\r\n" "END:VCARD\r\n"; QTest::newRow("Non-ASCII Latin-1 QP") << document << QString::fromLatin1("P\xe4\xe4kk\xf6nen"); } } void tst_QVersitReader::testReading() { // No I/O device set QVERIFY(!mReader->startReading()); QCOMPARE(mReader->error(), QVersitReader::IOError); // Device set, no data mReader->setDevice(mInputDevice); mInputDevice->open(QBuffer::ReadOnly); QVERIFY2(mReader->startReading(), QString::number(mReader->error()).toLatin1().data()); QVERIFY2(mReader->waitForFinished(), QString::number(mReader->error()).toLatin1().data()); QList results(mReader->results()); QCOMPARE(mReader->state(), QVersitReader::FinishedState); QCOMPARE(mReader->error(), QVersitReader::NoError); QCOMPARE(results.count(),0); // Device set, one document const QByteArray& oneDocument = "BEGIN:VCARD\r\nVERSION:2.1\r\nFN:John\r\nEND:VCARD\r\n"; mInputDevice->close(); mInputDevice->setData(oneDocument); mInputDevice->open(QBuffer::ReadOnly); mInputDevice->seek(0); QVERIFY2(mReader->startReading(), QString::number(mReader->error()).toLatin1().data()); QVERIFY2(mReader->waitForFinished(), QString::number(mReader->error()).toLatin1().data()); results = mReader->results(); QCOMPARE(mReader->state(), QVersitReader::FinishedState); QCOMPARE(mReader->error(), QVersitReader::NoError); QCOMPARE(results.count(),1); // Device set, two documents concatenated in a malformed manner (no \r\n separation) const QByteArray& twoMalformedDocument = "BEGIN:VCARD\r\nVERSION:2.1\r\nFN:John\r\nEND:VCARD" "BEGIN:VCARD\r\nVERSION:2.1\r\nFN:James\r\nEND:VCARD"; mInputDevice->close(); mInputDevice->setData(twoMalformedDocument); mInputDevice->open(QBuffer::ReadOnly); mInputDevice->seek(0); QVERIFY2(mReader->startReading(), QString::number(mReader->error()).toLatin1().data()); QVERIFY2(mReader->waitForFinished(), QString::number(mReader->error()).toLatin1().data()); results = mReader->results(); QCOMPARE(mReader->state(), QVersitReader::FinishedState); QCOMPARE(mReader->error(), QVersitReader::NoError); QCOMPARE(results.count(),2); // Exception case for a property ending in =CrLfCrLf, ie "=\r\n\r\n" const QByteArray& myTest = "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John\r\n" "EMAIL;ENCODING=QUOTED-PRINTABLE:john.citizen=40exam=\r\nple.com=abc=\r\n\r\n" "END:VCARD\r\n"; mInputDevice->close(); mInputDevice->setData(myTest); mInputDevice->open(QBuffer::ReadOnly); mInputDevice->seek(0); QVERIFY2(mReader->startReading(), QString::number(mReader->error()).toLatin1().data()); QVERIFY2(mReader->waitForFinished(), QString::number(mReader->error()).toLatin1().data()); results = mReader->results(); QCOMPARE(mReader->state(), QVersitReader::FinishedState); QCOMPARE(mReader->error(), QVersitReader::NoError); QCOMPARE(results.count(),1); // vCard 4.0 const QByteArray& vcard40 = "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John\r\nEND:VCARD\r\n"; mInputDevice->close(); mInputDevice->setData(vcard40); mInputDevice->open(QBuffer::ReadOnly); mInputDevice->seek(0); QVERIFY2(mReader->startReading(), QString::number(mReader->error()).toLatin1().data()); QVERIFY2(mReader->waitForFinished(), QString::number(mReader->error()).toLatin1().data()); results = mReader->results(); QCOMPARE(mReader->state(), QVersitReader::FinishedState); QCOMPARE(mReader->error(), QVersitReader::NoError); QCOMPARE(results.count(),1); // Wide charset with no byte-order mark QTextCodec* codec = QTextCodec::codecForName("UTF-16BE"); QTextCodec::ConverterState converterState(QTextCodec::IgnoreHeader); QString document = QStringLiteral("BEGIN:VCARD\r\nVERSION:2.1\r\nFN:John\r\nEND:VCARD\r\n"); const QByteArray& wideDocument = codec->fromUnicode(document.data(), document.length(), &converterState); mInputDevice->close(); mInputDevice->setData(wideDocument); mInputDevice->open(QBuffer::ReadOnly); mInputDevice->seek(0); mReader->setDefaultCodec(codec); QVERIFY2(mReader->startReading(), QString::number(mReader->error()).toLatin1().data()); QVERIFY2(mReader->waitForFinished(), QString::number(mReader->error()).toLatin1().data()); results = mReader->results(); QCOMPARE(mReader->state(), QVersitReader::FinishedState); QCOMPARE(mReader->error(), QVersitReader::NoError); QCOMPARE(mReader->results().count(),1); mReader->setDefaultCodec(NULL); // Two documents const QByteArray& twoDocuments = " \r\n BEGIN:VCARD\r\nFN:Jenny\r\nEND:VCARD\r\nBEGIN:VCARD\r\nFN:Jake\r\nEND:VCARD\r\n"; mInputDevice->close(); mInputDevice->setData(twoDocuments); mInputDevice->open(QBuffer::ReadOnly); mInputDevice->seek(0); QVERIFY2(mReader->startReading(), QString::number(mReader->error()).toLatin1().data()); QVERIFY2(mReader->waitForFinished(), QString::number(mReader->error()).toLatin1().data()); results = mReader->results(); QCOMPARE(mReader->state(), QVersitReader::FinishedState); QCOMPARE(mReader->error(), QVersitReader::NoError); QCOMPARE(results.count(),2); // Valid documents and a grouped document between them const QByteArray& validDocumentsAndGroupedDocument = "BEGIN:VCARD\r\nFN:Jenny\r\nEND:VCARD\r\n" "BEGIN:VCARD\r\nX-GROUPING:pub gang\r\nBEGIN:VCARD\r\nFN:Jeremy\r\nEND:VCARD\r\nBEGIN:VCARD\r\nFN:Jeffery\r\nEND:VCARD\r\nEND:VCARD\r\n" "BEGIN:VCARD\r\nFN:Jake\r\nEND:VCARD\r\n" "BEGIN:VCARD\r\nFN:James\r\nEND:VCARD\r\n" "BEGIN:VCARD\r\nFN:Jane\r\nEND:VCARD\r\n"; mInputDevice->close(); mInputDevice->setData(validDocumentsAndGroupedDocument); mInputDevice->open(QBuffer::ReadWrite); mInputDevice->seek(0); QVERIFY2(mReader->startReading(), QString::number(mReader->error()).toLatin1().data()); QVERIFY2(mReader->waitForFinished(), QString::number(mReader->error()).toLatin1().data()); results = mReader->results(); QCOMPARE(mReader->state(), QVersitReader::FinishedState); QCOMPARE(mReader->error(), QVersitReader::NoError); QCOMPARE(results.count(),5); qApp->processEvents(); // clean up before we start sniffing signals // calling setData directly on reader mReader->setData(validDocumentsAndGroupedDocument); QVERIFY(mReader->startReading()); mReader->waitForFinished(); QCOMPARE(mReader->results().size(), 5); // Asynchronous reading mReader->setDevice(mInputDevice); mInputDevice->close(); mInputDevice->setData(twoDocuments); mInputDevice->open(QBuffer::ReadWrite); mInputDevice->seek(0); mSignalCatcher->mStateChanges.clear(); mSignalCatcher->mResultsCount = 0; QVERIFY2(mReader->startReading(), QString::number(mReader->error()).toLatin1().data()); QTRY_VERIFY(mSignalCatcher->mStateChanges.count() >= 2); QCOMPARE(mSignalCatcher->mStateChanges.at(0), QVersitReader::ActiveState); QCOMPARE(mSignalCatcher->mStateChanges.at(1), QVersitReader::FinishedState); QVERIFY(mSignalCatcher->mResultsCount >= 2); QCOMPARE(mReader->results().size(), 2); QCOMPARE(mReader->error(), QVersitReader::NoError); // Cancelling mInputDevice->close(); mInputDevice->setData(twoDocuments); mInputDevice->open(QBuffer::ReadOnly); mInputDevice->seek(0); mSignalCatcher->mStateChanges.clear(); mSignalCatcher->mResultsCount = 0; QVERIFY2(mReader->startReading(), QString::number(mReader->error()).toLatin1().data()); mReader->cancel(); mReader->waitForFinished(); QTRY_VERIFY(mSignalCatcher->mStateChanges.count() >= 2); QCOMPARE(mSignalCatcher->mStateChanges.at(0), QVersitReader::ActiveState); QVersitReader::State state(mSignalCatcher->mStateChanges.at(1)); // It's possible that it finishes before it cancels. QVERIFY(state == QVersitReader::CanceledState || state == QVersitReader::FinishedState); } void tst_QVersitReader::testResult() { QCOMPARE(mReader->results().count(),0); } void tst_QVersitReader::testParseNextVersitProperty() { #ifndef QT_BUILD_INTERNAL QSKIP("Testing private API"); #else QFETCH(QVersitDocument::VersitType, documentType); QFETCH(QByteArray, input); QFETCH(QVersitProperty, expectedProperty); QBuffer buffer(&input); buffer.open(QIODevice::ReadOnly); LineReader lineReader(&buffer, mAsciiCodec); QVersitProperty property = mReaderPrivate->parseNextVersitProperty(documentType, &lineReader); if (property != expectedProperty) { // compare each part of the property separately for easier debugging QCOMPARE(property.groups(), expectedProperty.groups()); QCOMPARE(property.name(), expectedProperty.name()); QCOMPARE(property.valueType(), expectedProperty.valueType()); // QVariant doesn't support == on QVersitDocuments - do it manually if (property.variantValue().userType() == qMetaTypeId()) { QVERIFY(expectedProperty.variantValue().userType() == qMetaTypeId()); QCOMPARE(property.value(), expectedProperty.value()); } else QCOMPARE(property.variantValue(), expectedProperty.variantValue()); // Don't check parameters because the reader can add random parameters of its own (like CHARSET) // QCOMPARE(property.parameters(), expectedProperty.parameters()); } #endif } void tst_QVersitReader::testParseNextVersitProperty_data() { #ifdef QT_BUILD_INTERNAL QTest::addColumn("documentType"); QTest::addColumn("input"); QTest::addColumn("expectedProperty"); { QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("BEGIN")); expectedProperty.setValue(QStringLiteral("vcard")); QTest::newRow("begin") << QVersitDocument::VCard21Type << QByteArray("Begin:vcard\r\n") << expectedProperty; } { QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("VERSION")); expectedProperty.setValue(QStringLiteral("2.1")); expectedProperty.setValueType(QVersitProperty::PlainType); QTest::newRow("version") << QVersitDocument::VCard21Type << QByteArray("VERSION:2.1\r\n") << expectedProperty; } { QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("FN")); expectedProperty.setValue(QStringLiteral("John")); expectedProperty.setValueType(QVersitProperty::PlainType); QTest::newRow("fn") << QVersitDocument::VCard21Type << QByteArray("FN:John\r\n") << expectedProperty; } { // "NOTE:\;\,\:\\" QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("NOTE")); expectedProperty.setValue(QStringLiteral("\\;\\,\\:\\\\")); expectedProperty.setValueType(QVersitProperty::PlainType); QTest::newRow("vcard21 note") << QVersitDocument::VCard21Type << QByteArray("NOTE:\\;\\,\\:\\\\\r\n") << expectedProperty; expectedProperty.setValue(QStringLiteral(";,:\\")); QTest::newRow("vcard30 note") << QVersitDocument::VCard30Type << QByteArray("NOTE:\\;\\,\\:\\\\\r\n") << expectedProperty; } { // "N:foo\;bar;foo\,bar;foo\:bar;foo\\bar;foo\\\;bar" QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("N")); QStringList components; components << QStringLiteral("foo;bar") << QStringLiteral("foo\\,bar") << QStringLiteral("foo\\:bar") << QStringLiteral("foo\\\\bar") << QStringLiteral("foo\\\\;bar"); expectedProperty.setValue(components); expectedProperty.setValueType(QVersitProperty::CompoundType); QTest::newRow("vcard21 n") << QVersitDocument::VCard21Type << QByteArray("N:foo\\;bar;foo\\,bar;foo\\:bar;foo\\\\bar;foo\\\\\\;bar\r\n") << expectedProperty; components.clear(); components << QStringLiteral("foo;bar") << QStringLiteral("foo,bar") << QStringLiteral("foo:bar") << QStringLiteral("foo\\bar") << QStringLiteral("foo\\;bar"); expectedProperty.setValue(components); QTest::newRow("vcard30 n") << QVersitDocument::VCard30Type << QByteArray("N:foo\\;bar;foo\\,bar;foo\\:bar;foo\\\\bar;foo\\\\\\;bar\r\n") << expectedProperty; } { QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("ADR")); expectedProperty.setValue(QStringList(QString())); expectedProperty.setValueType(QVersitProperty::CompoundType); QTest::newRow("empty structured") << QVersitDocument::VCard21Type << QByteArray("ADR:\r\n") << expectedProperty; } { QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("X-QTPROJECT-FAVORITE")); QStringList components; components << QStringLiteral("false") << QStringLiteral("10"); expectedProperty.setValue(components); expectedProperty.setValueType(QVersitProperty::CompoundType); QTest::newRow("vcard21 favorite") << QVersitDocument::VCard21Type << QByteArray("X-QTPROJECT-FAVORITE:false;10") << expectedProperty; } { QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("X-QTPROJECT-EXTENDED-DETAIL")); QStringList components; components << QStringLiteral("name") << QStringLiteral("data"); expectedProperty.setValue(components); expectedProperty.setValueType(QVersitProperty::CompoundType); QTest::newRow("qtproject extended detail") << QVersitDocument::VCard21Type << QByteArray("X-QTPROJECT-EXTENDED-DETAIL:name;data") << expectedProperty; } { QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("X-CHILDREN")); expectedProperty.setValue(QStringList() << QStringLiteral("Child1") << QStringLiteral("Child2")); expectedProperty.setValueType(QVersitProperty::ListType); QTest::newRow("children") << QVersitDocument::VCard21Type << QByteArray("X-CHILDREN:Child1,Child2\r\n") << expectedProperty; } { // "NICKNAME:foo\;bar,foo\,bar,foo\:bar,foo\\bar,foo\\\,bar" QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("NICKNAME")); QStringList components; components << QStringLiteral("foo\\;bar") << QStringLiteral("foo,bar") << QStringLiteral("foo\\:bar") << QStringLiteral("foo\\\\bar") << QStringLiteral("foo\\\\,bar"); expectedProperty.setValue(components); expectedProperty.setValueType(QVersitProperty::ListType); QTest::newRow("vcard21 nickname") << QVersitDocument::VCard21Type << QByteArray("NICKNAME:foo\\;bar,foo\\,bar,foo\\:bar,foo\\\\bar,foo\\\\\\,bar\r\n") << expectedProperty; components.clear(); components << QStringLiteral("foo;bar") << QStringLiteral("foo,bar") << QStringLiteral("foo:bar") << QStringLiteral("foo\\bar") << QStringLiteral("foo\\,bar"); expectedProperty.setValue(components); QTest::newRow("vcard30 nickname") << QVersitDocument::VCard30Type << QByteArray("NICKNAME:foo\\;bar,foo\\,bar,foo\\:bar,foo\\\\bar,foo\\\\\\,bar\r\n") << expectedProperty; } { // "CATEGORIES:foo\;bar,foo\,bar,foo\:bar,foo\\bar,foo\\\,bar" QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("CATEGORIES")); QStringList components; components << QStringLiteral("foo\\;bar") << QStringLiteral("foo,bar") << QStringLiteral("foo\\:bar") << QStringLiteral("foo\\\\bar") << QStringLiteral("foo\\\\,bar"); expectedProperty.setValue(components); expectedProperty.setValueType(QVersitProperty::ListType); QTest::newRow("vcard21 categories") << QVersitDocument::VCard21Type << QByteArray("CATEGORIES:foo\\;bar,foo\\,bar,foo\\:bar,foo\\\\bar,foo\\\\\\,bar\r\n") << expectedProperty; components.clear(); components << QStringLiteral("foo;bar") << QStringLiteral("foo,bar") << QStringLiteral("foo:bar") << QStringLiteral("foo\\bar") << QStringLiteral("foo\\,bar"); expectedProperty.setValue(components); QTest::newRow("vcard30 categories") << QVersitDocument::VCard30Type << QByteArray("CATEGORIES:foo\\;bar,foo\\,bar,foo\\:bar,foo\\\\bar,foo\\\\\\,bar\r\n") << expectedProperty; // "CATEGORIES:foobar\\,foobar\\\\,foo\\\\\,bar" components.clear(); components << QStringLiteral("foobar\\") << QStringLiteral("foobar\\\\") << QStringLiteral("foo\\\\,bar"); expectedProperty.setValue(components); QTest::newRow("vcard30 unescaping") << QVersitDocument::VCard30Type << QByteArray("CATEGORIES:foobar\\\\,foobar\\\\\\\\,foo\\\\\\\\\\,bar") << expectedProperty; } { QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("ORG")); expectedProperty.setValue(QString::fromUtf8(KATAKANA_NOKIA)); expectedProperty.setValueType(QVersitProperty::CompoundType); QTest::newRow("org utf8") << QVersitDocument::VCard21Type << QByteArray("ORG;CHARSET=UTF-8:" + KATAKANA_NOKIA + "\r\n") << expectedProperty; } { QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("ORG")); expectedProperty.setValue(QString::fromUtf8(KATAKANA_NOKIA)); expectedProperty.setValueType(QVersitProperty::CompoundType); QTest::newRow("vcard21 org utf8 qp") << QVersitDocument::VCard21Type << QByteArray("ORG;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=E3=83=8E=E3=82=AD=E3=82=A2\r\n") << expectedProperty; QTest::newRow("vcard30 org utf8 qp") << QVersitDocument::VCard30Type << QByteArray("ORG;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=E3=83=8E=E3=82=AD=E3=82=A2\r\n") << expectedProperty; } { QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("PHOTO")); expectedProperty.setValue(SAMPLE_GIF); expectedProperty.setValueType(QVersitProperty::BinaryType); QTest::newRow("vcard21 photo1") << QVersitDocument::VCard21Type << QByteArray("PHOTO;ENCODING=BASE64:") + SAMPLE_GIF_BASE64 + QByteArray("\r\n\r\n") << expectedProperty; QTest::newRow("vcard30 photo1") << QVersitDocument::VCard30Type << QByteArray("PHOTO;ENCODING=B:") + SAMPLE_GIF_BASE64 + QByteArray("\r\n\r\n") << expectedProperty; // Again, but without the explicit "ENCODING" parameter QTest::newRow("vcard21 photo2") << QVersitDocument::VCard21Type << QByteArray("PHOTO;BASE64:") + SAMPLE_GIF_BASE64 + QByteArray("\r\n\r\n") << expectedProperty; QTest::newRow("vcard30 photo2") << QVersitDocument::VCard30Type << QByteArray("PHOTO;B:") + SAMPLE_GIF_BASE64 + QByteArray("\r\n\r\n") << expectedProperty; } { QVersitProperty expectedProperty; expectedProperty.setGroups(QStringList() << QStringLiteral("HOME") << QStringLiteral("Springfield")); expectedProperty.setName(QStringLiteral("EMAIL")); expectedProperty.setValue(QStringLiteral("john.citizen@example.com")); expectedProperty.setValueType(QVersitProperty::PlainType); QTest::newRow("email qp") << QVersitDocument::VCard21Type << QByteArray("HOME.Springfield.EMAIL;Encoding=Quoted-Printable:john.citizen=40exam=\r\nple.com\r\n") << expectedProperty; } { QVersitDocument subDocument; subDocument.setComponentType(QStringLiteral("VCARD")); subDocument.setType(QVersitDocument::VCard21Type); QVersitProperty subProperty; subProperty.setName(QStringLiteral("FN")); subProperty.setValue(QStringLiteral("Jenny")); subDocument.addProperty(subProperty); QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("AGENT")); expectedProperty.setValue(QVariant::fromValue(subDocument)); expectedProperty.setValueType(QVersitProperty::VersitDocumentType); QTest::newRow("agent") << QVersitDocument::VCard21Type << QByteArray("AGENT:\r\nBEGIN:VCARD\r\nFN:Jenny\r\nEND:VCARD\r\n\r\n") << expectedProperty; } // Some MeeGo.com specific types (for roundtripping) { QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("X-EDS-QTCONTACTS")); QStringList values; values << "This is a test"; values << "I have a ; in the middle"; values << "fini"; expectedProperty.setValue(values); expectedProperty.setValueType(QVersitProperty::CompoundType); QTest::newRow("org utf8") << QVersitDocument::VCard21Type << QByteArray("X-EDS-QTCONTACTS:This is a test;I have a \\; in the middle;fini\r\n") << expectedProperty; } { QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("X-SYNCEVO-QTCONTACTS")); QStringList values; values << "This is a test"; values << "I have a ; in the middle"; values << "fini"; expectedProperty.setValue(values); expectedProperty.setValueType(QVersitProperty::CompoundType); QTest::newRow("org utf8") << QVersitDocument::VCard21Type << QByteArray("X-SYNCEVO-QTCONTACTS:This is a test;I have a \\; in the middle;fini\r\n") << expectedProperty; } // This one should not be a compound type { QVersitProperty expectedProperty; expectedProperty.setName(QStringLiteral("X-NOT-A-COMPOUND")); QStringList values; values << "This is a test"; values << "I have a ; in the middle"; values << "fini"; expectedProperty.setValue(QString::fromLatin1("This is a test;I have a \\; in the middle;fini")); expectedProperty.setValueType(QVersitProperty::PlainType); QTest::newRow("org utf8") << QVersitDocument::VCard21Type << QByteArray("X-NOT-A-COMPOUND:This is a test;I have a \\; in the middle;fini\r\n") << expectedProperty; } #endif } void tst_QVersitReader::testParseVersitDocument() { QFETCH(QByteArray, vCard); QFETCH(bool, expectedSuccess); QFETCH(QVersitDocument, expectedDocument); QBuffer buffer(&vCard); buffer.open(QIODevice::ReadOnly); LineReader lineReader(&buffer, QTextCodec::codecForName("UTF-8")); mReader->setDevice(&buffer); QVERIFY2(mReader->startReading(), QString::number(mReader->error()).toLatin1().data()); QVERIFY2(mReader->waitForFinished(), QString::number(mReader->error()).toLatin1().data()); QCOMPARE(mReader->error(), expectedSuccess ? QVersitReader::NoError : QVersitReader::ParseError); if (expectedSuccess) { QList documents = mReader->results(); QCOMPARE(documents.size(), 1); QVersitDocument document = documents.at(0); if (document != expectedDocument) { qDebug() << "Expected: " << expectedDocument; qDebug() << "Actual: " << document; QCOMPARE(document, expectedDocument); } } } void tst_QVersitReader::testParseVersitDocument_data() { QTest::addColumn("vCard"); QTest::addColumn("expectedSuccess"); QTest::addColumn("expectedDocument"); { QVersitDocument expected(QVersitDocument::VCard21Type); expected.setComponentType(QStringLiteral("VCARD")); QVersitProperty property; property.setName(QStringLiteral("FN")); property.setValue(QStringLiteral("John")); expected.addProperty(property); QTest::newRow("Basic vCard 2.1") << QByteArray( "BEGIN:VCARD\r\n" "VERSION:2.1\r\n" "FN:John\r\n" "END:VCARD\r\n") << true << expected; } { QVersitDocument expected(QVersitDocument::VCard21Type); expected.setComponentType(QStringLiteral("VCARD")); QVersitProperty property; property.setName(QStringLiteral("FN")); property.setValue(QStringLiteral("John")); expected.addProperty(property); QVersitDocument agent(QVersitDocument::VCard21Type); agent.setComponentType(QStringLiteral("VCARD")); property.setValue(QStringLiteral("Jenny")); agent.addProperty(property); property.clear(); property.setName(QStringLiteral("AGENT")); property.setValue(QVariant::fromValue(agent)); property.setValueType(QVersitProperty::VersitDocumentType); expected.addProperty(property); property.clear(); property.setName(QStringLiteral("EMAIL")); property.setValue(QStringLiteral("john.citizen@example.com")); expected.addProperty(property); QTest::newRow("vCard 2.1 with Agent") << QByteArray( "BEGIN:VCARD\r\n" "VERSION:2.1\r\n" "FN:John\r\n" "AGENT:BEGIN:VCARD\r\n" "FN:Jenny\r\n" "END:VCARD\r\n" "\r\n" "EMAIL;ENCODING=QUOTED-PRINTABLE:john.citizen=40exam=\r\nple.com\r\n" "END:VCARD\r\n") << true << expected; } { QVersitDocument expected(QVersitDocument::VCard30Type); expected.setComponentType(QStringLiteral("VCARD")); QVersitProperty property; property.setName(QStringLiteral("FN")); property.setValue(QStringLiteral("John")); expected.addProperty(property); QVersitDocument agent(QVersitDocument::VCard30Type); agent.setComponentType(QStringLiteral("VCARD")); property.setValue(QStringLiteral("Jenny")); agent.addProperty(property); property.clear(); property.setName(QStringLiteral("AGENT")); property.setValue(QVariant::fromValue(agent)); property.setValueType(QVersitProperty::VersitDocumentType); expected.addProperty(property); property.clear(); property.setName(QStringLiteral("EMAIL")); property.setValue(QStringLiteral("john.citizen@example.com")); expected.addProperty(property); QTest::newRow("vCard 3.0 with Agent") << QByteArray( "BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "FN:John\r\n" "AGENT:BEGIN\\:VCARD\\nFN\\:Jenny\\nEND\\:VCARD\\n\r\n" "EMAIL:john.citizen@example.com\r\n" "END:VCARD\r\n") << true << expected; } QTest::newRow("No BEGIN found") << QByteArray( "VCARD\r\n" "VERSION:2.1\r\n" "FN:Nobody\r\n" "END:VCARD\r\n") << false << QVersitDocument(); QTest::newRow("Wrong card type") << QByteArray( "BEGIN:VCAL\r\n" "END:VCAL\r\n") << false << QVersitDocument(); QTest::newRow("Wrong version") << QByteArray( "BEGIN:VCARD\r\n" "VERSION:1234\r\n" "FN:Nobody\r\n" "END:VCARD\r\n") << false << QVersitDocument(); { QVersitDocument expected(QVersitDocument::VCard21Type); expected.setComponentType(QStringLiteral("VCARD")); QVersitProperty property; property.setName(QStringLiteral("FN")); property.setValue(QStringLiteral("Nobody")); expected.addProperty(property); QTest::newRow("No trailing crlf") << QByteArray( "BEGIN:VCARD\r\n" "VERSION:2.1\r\n" "FN:Nobody\r\n" "END:VCARD") << true << expected; } QTest::newRow("No end") << QByteArray( "BEGIN:VCARD\r\n" "VERSION:2.1\r\n" "FN:Nobody\r\n") << false << QVersitDocument(); { QVersitDocument expected(QVersitDocument::VCard21Type); expected.setComponentType(QStringLiteral("VCARD")); QVersitProperty property; property.setName(QStringLiteral("X-EXAMPLES")); property.setValue(QStringLiteral("Family vCard")); expected.addProperty(property); QVersitDocument nested1(QVersitDocument::VCard21Type); nested1.setComponentType(QStringLiteral("VCARD")); property.setName(QStringLiteral("FN")); property.setValue(QStringLiteral("John")); nested1.addProperty(property); expected.addSubDocument(nested1); QVersitDocument nested2(QVersitDocument::VCard21Type); nested2.setComponentType(QStringLiteral("VCARD")); property.setName(QStringLiteral("FN")); property.setValue(QStringLiteral("Jenny")); nested2.addProperty(property); expected.addSubDocument(nested2); QTest::newRow("Grouped vCard") << QByteArray( "BEGIN:VCARD\r\n" "VERSION:2.1\r\n" "X-EXAMPLES:Family vCard\r\n" "BEGIN:VCARD\r\n" "VERSION:2.1\r\n" "FN:John\r\n" "END:VCARD\r\n" "BEGIN:VCARD\r\n" "VERSION:2.1\r\n" "FN:Jenny\r\n" "END:VCARD\r\n" "END:VCARD") << true << expected; } { QVersitDocument expected(QVersitDocument::ICalendar20Type); expected.setComponentType(QStringLiteral("VCALENDAR")); QVersitProperty property; property.setName(QStringLiteral("PRODID")); property.setValue(QStringLiteral("-//hacksw/handcal//NONSGML v1.0//EN")); expected.addProperty(property); QVersitDocument nested(QVersitDocument::ICalendar20Type); nested.setComponentType(QStringLiteral("VEVENT")); property.setName(QStringLiteral("DTSTART")); property.setValue(QStringLiteral("19970714T170000Z")); nested.addProperty(property); property.setName(QStringLiteral("DTEND")); property.setValue(QStringLiteral("19970715T035959Z")); nested.addProperty(property); property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("Bastille Day Party")); nested.addProperty(property); QMultiHash parameters; QVersitDocument nestedAlarm(QVersitDocument::ICalendar20Type); nestedAlarm.setComponentType("VALARM"); property.setName("TRIGGER"); parameters.insert(QStringLiteral("VALUE"), QStringLiteral("DATE-TIME")); property.setParameters(parameters); property.setValue(QStringLiteral("19970714T170000Z")); nestedAlarm.addProperty(property); property.clear(); property.setName(QStringLiteral("REPEAT")); property.setValue(4); nestedAlarm.addProperty(property); property.setName("DURATION"); property.setValue(QStringLiteral("PT15M")); nestedAlarm.addProperty(property); property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("AUDIO")); nestedAlarm.addProperty(property); property.setName(QStringLiteral("ATTACH")); parameters.clear(); parameters.insert(QStringLiteral("FMTTYPE"), QStringLiteral("audio/basic")); property.setParameters(parameters); property.setValue(QUrl(QStringLiteral("ftp://host.com/pub/sounds/bell-01.aud"))); nestedAlarm.addProperty(property); nested.addSubDocument(nestedAlarm); expected.addSubDocument(nested); QTest::newRow("iCalendar sample from spec") << QByteArray( "BEGIN:VCALENDAR\r\n" "VERSION:2.0\r\n" "PRODID:-//hacksw/handcal//NONSGML v1.0//EN\r\n" "BEGIN:VEVENT\r\n" "DTSTART:19970714T170000Z\r\n" "DTEND:19970715T035959Z\r\n" "SUMMARY:Bastille Day Party\r\n" "BEGIN:VALARM\r\n" "TRIGGER;VALUE=DATE-TIME:19970714T170000Z\r\n" "REPEAT:4\r\n" "DURATION:PT15M\r\n" "ACTION:AUDIO\r\n" "ATTACH;FMTTYPE=audio/basic:ftp://host.com/pub/sounds/bell-01.aud\r\n" "END:VALARM\r\n" "END:VEVENT\r\n" "END:VCALENDAR\r\n") << true << expected; } } void tst_QVersitReader::testDecodeQuotedPrintable() { #ifndef QT_BUILD_INTERNAL QSKIP("Testing private API"); #else QFETCH(QByteArray, encoded); QFETCH(QByteArray, decoded); mReaderPrivate->decodeQuotedPrintable(&encoded); QCOMPARE(encoded, decoded); #endif } void tst_QVersitReader::testDecodeQuotedPrintable_data() { #ifdef QT_BUILD_INTERNAL QTest::addColumn("encoded"); QTest::addColumn("decoded"); QTest::newRow("Soft line breaks") << QByteArray("This=\r\n is =\r\none line.") << QByteArray("This is one line."); QTest::newRow("Characters recommended to be encoded according to RFC 1521") << QByteArray("To be decoded: =0A=0D=21=22=23=24=3D=40=5B=5C=5D=5E=60=7B=7C=7D=7E") << QByteArray("To be decoded: \n\r!\"#$=@[\\]^`{|}~"); QTest::newRow("Characters recommended to be encoded according to RFC 1521(lower case)") << QByteArray("To be decoded: =0a=0d=21=22=23=24=3d=40=5b=5c=5d=5e=60=7b=7c=7d=7e") << QByteArray("To be decoded: \n\r!\"#$=@[\\]^`{|}~"); QTest::newRow("random characters encoded") << QByteArray("=45=6E=63=6F=64=65=64 =64=61=74=61") << QByteArray("Encoded data"); QTest::newRow("short string1") << QByteArray("-=_") << QByteArray("-=_"); QTest::newRow("short string2") << QByteArray("=0") << QByteArray("=0"); QTest::newRow("short string2") << QByteArray("\r") << QByteArray("\r"); QTest::newRow("short string2") << QByteArray("\n") << QByteArray("\n"); QTest::newRow("short string2") << QByteArray("\n\r") << QByteArray("\n\r"); QTest::newRow("White spaces") << QByteArray("=09=20") << QByteArray("\t "); #endif } void tst_QVersitReader::testParamName() { #ifndef QT_BUILD_INTERNAL QSKIP("Testing private API"); #else // Empty value QByteArray param; QCOMPARE(mReaderPrivate->paramName(param, mAsciiCodec),QString()); // Only value present param = "WORK"; QCOMPARE(mReaderPrivate->paramName(param, mAsciiCodec), QStringLiteral("TYPE")); // The below tests intentionally use the misspelling TIPE to avoid the default behaviour of // returning TYPE when the name can't be parsed. // Both name and value, spaces after the name param = "TIPE \t =WORK"; QCOMPARE(mReaderPrivate->paramName(param, mAsciiCodec), QStringLiteral("TIPE")); // Both name and value, no spaces after the name param = "TIPE=WORK"; QCOMPARE(mReaderPrivate->paramName(param, mAsciiCodec), QStringLiteral("TIPE")); // Test wide character support. QTextCodec* codec = QTextCodec::codecForName("UTF-16BE"); param = codec->fromUnicode(QStringLiteral("TIPE=WORK")); QCOMPARE(mReaderPrivate->paramName(param, codec), QStringLiteral("TIPE")); #endif } void tst_QVersitReader::testParamValue() { #ifndef QT_BUILD_INTERNAL QSKIP("Testing private API"); #else // Empty value QByteArray param; QCOMPARE(mReaderPrivate->paramValue(param, mAsciiCodec),QString()); // Only value present param = "WORK"; QCOMPARE(mReaderPrivate->paramValue(param, mAsciiCodec), QStringLiteral("WORK")); // Name and equals sign, but no value param = "TYPE="; QCOMPARE(mReaderPrivate->paramValue(param, mAsciiCodec),QString()); // Name and equals sign, but value has only spaces param = "TYPE= \t "; QCOMPARE(mReaderPrivate->paramValue(param, mAsciiCodec),QString()); // Both name and value, spaces before the value param = "TYPE= \t WORK"; QCOMPARE(mReaderPrivate->paramValue(param, mAsciiCodec), QStringLiteral("WORK")); // Both name and value, no spaces before the value param = "ENCODING=QUOTED-PRINTABLE"; QCOMPARE(mReaderPrivate->paramValue(param, mAsciiCodec), QStringLiteral("QUOTED-PRINTABLE")); // Test wide character support. QTextCodec* codec = QTextCodec::codecForName("UTF-16BE"); param = codec->fromUnicode(QStringLiteral("TYPE=WORK")); QCOMPARE(mReaderPrivate->paramValue(param, codec), QStringLiteral("WORK")); #endif } void tst_QVersitReader::testExtractPart() { #ifndef QT_BUILD_INTERNAL QSKIP("Testing private API"); #else QByteArray originalStr; // Negative starting position QCOMPARE(mReaderPrivate->extractPart(originalStr,-1,1), QByteArray()); // Empty original string QCOMPARE(mReaderPrivate->extractPart(originalStr,0,1), QByteArray()); // Trimmed substring empty originalStr = " \t \t"; QCOMPARE(mReaderPrivate->extractPart(originalStr,0,4), QByteArray()); // The given substring length is greater than the original string length originalStr = "ENCODING=7BIT"; QCOMPARE(mReaderPrivate->extractPart(originalStr,0,100), originalStr); // Non-empty substring, from the beginning originalStr = " TYPE=WORK ; X-PARAM=X-VALUE; ENCODING=8BIT"; QCOMPARE(mReaderPrivate->extractPart(originalStr,0,11), QByteArray("TYPE=WORK")); // Non-empty substring, from the middle QCOMPARE(mReaderPrivate->extractPart(originalStr,12,16), QByteArray("X-PARAM=X-VALUE")); // Non-empty substring, from the middle to the end QCOMPARE(mReaderPrivate->extractPart(originalStr,29), QByteArray("ENCODING=8BIT")); #endif } void tst_QVersitReader::testExtractParts() { #ifndef QT_BUILD_INTERNAL QSKIP("Testing private API"); #else QList parts; // Empty value QByteArray text; parts = mReaderPrivate->extractParts(text,";", mAsciiCodec); QVERIFY(parts.isEmpty()); // Only separator text = ";"; parts = mReaderPrivate->extractParts(text,";", mAsciiCodec); QVERIFY(parts.isEmpty()); // One part text = "part"; parts = mReaderPrivate->extractParts(text,";", mAsciiCodec); QCOMPARE(parts.count(),1); QCOMPARE(QLatin1String(parts[0]),QLatin1String("part")); // Separator in the beginning, one part text = ";part"; parts = mReaderPrivate->extractParts(text,";", mAsciiCodec); QCOMPARE(parts.count(),1); QCOMPARE(QLatin1String(parts[0]),QLatin1String("part")); // Separator in the end, one part text = "part;"; parts = mReaderPrivate->extractParts(text,";", mAsciiCodec); QCOMPARE(parts.count(),1); QCOMPARE(QLatin1String(parts[0]),QLatin1String("part")); // One part that contains escaped separator text = "part\\;"; parts = mReaderPrivate->extractParts(text,";", mAsciiCodec); QCOMPARE(parts.count(),1); QCOMPARE(QLatin1String(parts[0]),QLatin1String("part\\;")); // Two parts text = "part1;part2"; parts = mReaderPrivate->extractParts(text,";", mAsciiCodec); QCOMPARE(parts.count(),2); QCOMPARE(QLatin1String(parts[0]),QLatin1String("part1")); QCOMPARE(QLatin1String(parts[1]),QLatin1String("part2")); // Two parts that contain escaped separators text = "pa\\;rt1;par\\;t2"; parts = mReaderPrivate->extractParts(text,";", mAsciiCodec); QCOMPARE(parts.count(),2); QCOMPARE(QLatin1String(parts[0]),QLatin1String("pa\\;rt1")); QCOMPARE(QLatin1String(parts[1]),QLatin1String("par\\;t2")); // Test wide character support QTextCodec* codec = QTextCodec::codecForName("UTF-16BE"); text = codec->fromUnicode(QStringLiteral("part1;part2")); parts = mReaderPrivate->extractParts(text,";", codec); QCOMPARE(parts.count(),2); QCOMPARE(codec->toUnicode(parts[0]),QStringLiteral("part1")); QCOMPARE(codec->toUnicode(parts[1]),QStringLiteral("part2")); #endif } void tst_QVersitReader::testExtractPropertyGroupsAndName() { #ifndef QT_BUILD_INTERNAL QSKIP("Testing private API"); #else QPair groupsAndName; // Empty string LByteArray cursor(QByteArray(" ")); groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(&cursor, mAsciiCodec); QCOMPARE(groupsAndName.first.count(),0); QCOMPARE(groupsAndName.second,QString()); // No value -> returns empty string and no groups QByteArray property("TEL"); cursor = property; groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(&cursor, mAsciiCodec); QCOMPARE(groupsAndName.first.count(),0); QCOMPARE(groupsAndName.second,QString()); // Simple name and value property = "TEL:123"; cursor = property; groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(&cursor, mAsciiCodec); QCOMPARE(groupsAndName.first.count(),0); QCOMPARE(groupsAndName.second,QStringLiteral("TEL")); // One whitespace before colon property = "TEL :123"; cursor = property; groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(&cursor, mAsciiCodec); QCOMPARE(groupsAndName.first.count(),0); QCOMPARE(groupsAndName.second,QStringLiteral("TEL")); // Several whitespaces before colon property = "TEL \t :123"; cursor = property; groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(&cursor, mAsciiCodec); QCOMPARE(groupsAndName.first.count(),0); QCOMPARE(groupsAndName.second,QStringLiteral("TEL")); // Name contains a group property = "group1.TEL:1234"; cursor = property; groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(&cursor, mAsciiCodec); QCOMPARE(groupsAndName.first.count(),1); QCOMPARE(groupsAndName.first.takeFirst(),QStringLiteral("group1")); QCOMPARE(groupsAndName.second,QStringLiteral("TEL")); // Name contains more than one group property = "group1.group2.TEL:12345"; cursor = property; groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(&cursor, mAsciiCodec); QCOMPARE(groupsAndName.first.count(),2); QCOMPARE(groupsAndName.first.takeFirst(),QStringLiteral("group1")); QCOMPARE(groupsAndName.first.takeFirst(),QStringLiteral("group2")); QCOMPARE(groupsAndName.second,QStringLiteral("TEL")); QCOMPARE(cursor.toByteArray(), QByteArray(":12345")); // Property contains one parameter property = "TEL;WORK:123"; cursor = property; groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(&cursor, mAsciiCodec); QCOMPARE(groupsAndName.first.count(),0); QCOMPARE(groupsAndName.second,QStringLiteral("TEL")); // Property contains several parameters property = "EMAIL;INTERNET;ENCODING=QUOTED-PRINTABLE:user=40ovi.com"; cursor = property; groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(&cursor, mAsciiCodec); QCOMPARE(groupsAndName.first.count(),0); QCOMPARE(groupsAndName.second,QStringLiteral("EMAIL")); // Name contains an escaped semicolon property = "X-proper\\;ty:value"; cursor = property; groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(&cursor, mAsciiCodec); QCOMPARE(groupsAndName.first.count(),0); QCOMPARE(groupsAndName.second,QStringLiteral("X-proper\\;ty")); // Test wide character support QTextCodec* codec = QTextCodec::codecForName("UTF-16BE"); property = codec->fromUnicode(QStringLiteral("group1.group2.TEL;WORK:123")); cursor = property; groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(&cursor, codec); QCOMPARE(groupsAndName.first.count(),2); QCOMPARE(groupsAndName.first.takeFirst(),QStringLiteral("group1")); QCOMPARE(groupsAndName.first.takeFirst(),QStringLiteral("group2")); QCOMPARE(groupsAndName.second,QStringLiteral("TEL")); QCOMPARE(cursor.size(), 18); // ";WORK:123" in UTF16 is 18 bytes #endif } void tst_QVersitReader::testExtractVCard21PropertyParams() { #ifndef QT_BUILD_INTERNAL QSKIP("Testing private API"); #else // No parameters LByteArray cursor(QByteArray(":123")); QCOMPARE(mReaderPrivate->extractVCard21PropertyParams(&cursor, mAsciiCodec).count(), 0); // "Empty" parameter cursor = QByteArray(";:123"); QCOMPARE(mReaderPrivate->extractVCard21PropertyParams(&cursor, mAsciiCodec).count(), 0); // Semicolon found, but no value for the property cursor = QByteArray(";TYPE=X-TYPE"); QCOMPARE(mReaderPrivate->extractVCard21PropertyParams(&cursor, mAsciiCodec).count(), 0); // The property name contains an escaped semicolon, no parameters cursor = QByteArray(":value"); QCOMPARE(mReaderPrivate->extractVCard21PropertyParams(&cursor, mAsciiCodec).count(), 0); // The property value contains a semicolon, no parameters cursor = QByteArray(":va;lue"); QCOMPARE(mReaderPrivate->extractVCard21PropertyParams(&cursor, mAsciiCodec).count(), 0); // One parameter cursor = QByteArray(";HOME:123"); QMultiHash params = mReaderPrivate->extractVCard21PropertyParams(&cursor, mAsciiCodec); QCOMPARE(1, params.count()); QCOMPARE(1, params.values(QStringLiteral("TYPE")).count()); QCOMPARE(params.values(QStringLiteral("TYPE"))[0],QStringLiteral("HOME")); // Two parameters of the same type cursor = QByteArray(";HOME;VOICE:123"); params = mReaderPrivate->extractVCard21PropertyParams(&cursor, mAsciiCodec); QCOMPARE(2, params.count()); QCOMPARE(2, params.values(QStringLiteral("TYPE")).count()); QCOMPARE(params.values(QStringLiteral("TYPE"))[0],QStringLiteral("HOME")); QCOMPARE(params.values(QStringLiteral("TYPE"))[1],QStringLiteral("VOICE")); // Two parameters, several empty parameters (extra semicolons) cursor = QByteArray(";;;;HOME;;;;;VOICE;;;:123"); params = mReaderPrivate->extractVCard21PropertyParams(&cursor, mAsciiCodec); QCOMPARE(2, params.count()); QCOMPARE(2, params.values(QStringLiteral("TYPE")).count()); QCOMPARE(params.values(QStringLiteral("TYPE"))[0],QStringLiteral("HOME")); QCOMPARE(params.values(QStringLiteral("TYPE"))[1],QStringLiteral("VOICE")); // Two parameters with different types cursor = QByteArray(";INTERNET;ENCODING=QUOTED-PRINTABLE:user=40ovi.com"); params.clear(); params = mReaderPrivate->extractVCard21PropertyParams(&cursor, mAsciiCodec); QCOMPARE(2, params.count()); QList typeParams = params.values(QStringLiteral("TYPE")); QCOMPARE(1, typeParams.count()); QCOMPARE(typeParams[0],QStringLiteral("INTERNET")); QList encodingParams = params.values(QStringLiteral("ENCODING")); QCOMPARE(1, encodingParams.count()); QCOMPARE(encodingParams[0],QStringLiteral("QUOTED-PRINTABLE")); // Test wide character support. QTextCodec* codec = QTextCodec::codecForName("UTF-16BE"); QByteArray data = VersitUtils::encode(";HOME;CHARSET=UTF-16:123", codec); cursor = data; params = mReaderPrivate->extractVCard21PropertyParams(&cursor, codec); QCOMPARE(2, params.count()); typeParams = params.values(QStringLiteral("TYPE")); QCOMPARE(1, typeParams.count()); QCOMPARE(typeParams[0],QStringLiteral("HOME")); encodingParams = params.values(QStringLiteral("CHARSET")); QCOMPARE(1, encodingParams.count()); QCOMPARE(encodingParams[0],QStringLiteral("UTF-16")); #endif } void tst_QVersitReader::testExtractVCard30PropertyParams() { #ifndef QT_BUILD_INTERNAL QSKIP("Testing private API"); #else // No parameters LByteArray cursor(QByteArray(":123")); QCOMPARE(mReaderPrivate->extractVCard30PropertyParams(&cursor, mAsciiCodec).count(), 0); // One parameter cursor = QByteArray(";TYPE=HOME:123"); QMultiHash params = mReaderPrivate->extractVCard30PropertyParams(&cursor, mAsciiCodec); QCOMPARE(params.count(), 1); QCOMPARE(params.values(QStringLiteral("TYPE")).count(), 1); QCOMPARE(params.values(QStringLiteral("TYPE"))[0], QStringLiteral("HOME")); // One parameter with an escaped semicolon cursor = QByteArray(";para\\;meter:value"); params = mReaderPrivate->extractVCard30PropertyParams(&cursor, mAsciiCodec); QCOMPARE(params.count(), 1); QCOMPARE(params.values(QStringLiteral("TYPE")).count(), 1); QCOMPARE(params.values(QStringLiteral("TYPE"))[0], QStringLiteral("para;meter")); // One parameter with and escaped comma in the name and the value cursor = QByteArray(";X-PA\\,RAM=VAL\\,UE:123"); params = mReaderPrivate->extractVCard30PropertyParams(&cursor, mAsciiCodec); QCOMPARE(params.count(), 1); QCOMPARE(params.values(QStringLiteral("X-PA,RAM")).count(), 1); QCOMPARE(params.values(QStringLiteral("X-PA,RAM"))[0], QStringLiteral("VAL,UE")); // Two parameters of the same type cursor = QByteArray(";TYPE=HOME,VOICE:123"); params = mReaderPrivate->extractVCard30PropertyParams(&cursor, mAsciiCodec); QCOMPARE(params.count(), 2); QCOMPARE(params.values(QStringLiteral("TYPE")).count(), 2); QVERIFY(params.values(QStringLiteral("TYPE")).contains(QStringLiteral("HOME"))); QVERIFY(params.values(QStringLiteral("TYPE")).contains(QStringLiteral("VOICE"))); // Two parameters of the same type in separate name-values cursor = QByteArray(";TYPE=HOME;TYPE=VOICE:123"); params = mReaderPrivate->extractVCard30PropertyParams(&cursor, mAsciiCodec); QCOMPARE(params.count(), 2); QCOMPARE(params.values(QStringLiteral("TYPE")).count(), 2); QVERIFY(params.values(QStringLiteral("TYPE")).contains(QStringLiteral("HOME"))); QVERIFY(params.values(QStringLiteral("TYPE")).contains(QStringLiteral("VOICE"))); // Three parameters of the same type cursor = QByteArray(";TYPE=PREF,HOME,VOICE:123"); params = mReaderPrivate->extractVCard30PropertyParams(&cursor, mAsciiCodec); QCOMPARE(params.count(), 3); QCOMPARE(params.values(QStringLiteral("TYPE")).count(), 3); QVERIFY(params.values(QStringLiteral("TYPE")).contains(QStringLiteral("PREF"))); QVERIFY(params.values(QStringLiteral("TYPE")).contains(QStringLiteral("HOME"))); QVERIFY(params.values(QStringLiteral("TYPE")).contains(QStringLiteral("VOICE"))); // Two parameters with different types cursor = QByteArray(";TYPE=HOME;X-PARAM=X-VALUE:Home Street 1"); params.clear(); params = mReaderPrivate->extractVCard30PropertyParams(&cursor, mAsciiCodec); QCOMPARE(params.count(), 2); QList typeParams = params.values(QStringLiteral("TYPE")); QCOMPARE(typeParams.count(), 1); QCOMPARE(typeParams[0],QStringLiteral("HOME")); QList encodingParams = params.values(QStringLiteral("X-PARAM")); QCOMPARE(encodingParams.count(), 1); QCOMPARE(encodingParams[0],QStringLiteral("X-VALUE")); // Test wide character support. QTextCodec* codec = QTextCodec::codecForName("UTF-16BE"); QByteArray data = VersitUtils::encode(";TIPE=HOME,VOICE;CHARSET=UTF-16:123", codec); cursor = data; params = mReaderPrivate->extractVCard30PropertyParams(&cursor, codec); QCOMPARE(params.count(), 3); typeParams = params.values(QStringLiteral("TIPE")); QCOMPARE(params.values(QStringLiteral("TIPE")).count(), 2); QVERIFY(params.values(QStringLiteral("TIPE")).contains(QStringLiteral("HOME"))); QVERIFY(params.values(QStringLiteral("TIPE")).contains(QStringLiteral("VOICE"))); encodingParams = params.values(QStringLiteral("CHARSET")); QCOMPARE(1, encodingParams.count()); QCOMPARE(encodingParams[0],QStringLiteral("UTF-16")); #endif } void tst_QVersitReader::testExtractParams() { #ifndef QT_BUILD_INTERNAL QSKIP("Testing private API"); #else LByteArray cursor; QByteArray data = ":123"; cursor = data; QList params = mReaderPrivate->extractParams(&cursor, mAsciiCodec); QCOMPARE(params.size(), 0); QVERIFY(cursor == QByteArray("123")); data = "a;b:123"; cursor = data; params = mReaderPrivate->extractParams(&cursor, mAsciiCodec); QCOMPARE(params.size(), 2); QVERIFY(cursor == QByteArray("123")); QCOMPARE(params.at(0), QByteArray("a")); QCOMPARE(params.at(1), QByteArray("b")); QTextCodec* codec = QTextCodec::codecForName("UTF-16BE"); data = VersitUtils::encode(":123", codec); cursor = data; params = mReaderPrivate->extractParams(&cursor, codec); QCOMPARE(params.size(), 0); QCOMPARE(cursor.size(), 6); // "123" takes up 6 bytes in UTF-16 data = VersitUtils::encode("a;b:123", codec); cursor = data; params = mReaderPrivate->extractParams(&cursor, codec); QCOMPARE(params.size(), 2); QCOMPARE(cursor.size(), 6); // "123" takes up 6 bytes in UTF-16 #endif } Q_DECLARE_METATYPE(QList) void tst_QVersitReader::testReadLine() { #ifndef QT_BUILD_INTERNAL QSKIP("Testing private API"); #else QFETCH(QByteArray, codecName); QFETCH(QString, data); QFETCH(QList, expectedLines); QTextCodec* codec = QTextCodec::codecForName(codecName); QTextEncoder* encoder = codec->makeEncoder(); encoder->fromUnicode(QString()); QByteArray bytes(encoder->fromUnicode(data)); mInputDevice->close(); mInputDevice->setData(bytes); mInputDevice->open(QIODevice::ReadWrite); LineReader lineReader(mInputDevice, codec, 10); QByteArray testLine("test pushed line"); // Check that all expected lines are read... foreach (QString expectedLine, expectedLines) { // (test push a line and read it) lineReader.pushLine(testLine); QVERIFY(!lineReader.atEnd()); LByteArray line = lineReader.readLine(); QCOMPARE(line.toByteArray(), testLine); QByteArray expectedBytes(encoder->fromUnicode(expectedLine)); QVERIFY(!lineReader.atEnd()); line = lineReader.readLine(); if(line.toByteArray() != expectedBytes) { qDebug() << line.toByteArray(); qDebug() << expectedBytes; QCOMPARE(line.toByteArray(), expectedBytes); } QCOMPARE(line.size(), expectedBytes.length()); } // (test push a line to a line reader that's reached its end) lineReader.pushLine(testLine); QVERIFY(!lineReader.atEnd()); LByteArray line = lineReader.readLine(); QCOMPARE(line.toByteArray(), testLine); // ...And that there are no more lines line = lineReader.readLine(); QVERIFY(line.isEmpty()); QVERIFY(lineReader.atEnd()); delete encoder; #endif } void tst_QVersitReader::testReadLine_data() { #ifdef QT_BUILD_INTERNAL // Note: for this test, we set mLineReader to read 10 bytes at a time. Lines of multiples of // 10 bytes are hence border cases. // Note: QVersitReaders' LineReader contains hacks that sniff for colons in the input to enable // a workaround for malformed vCards with badly wrapped lines (see the last test case) // For testing of normal wrapping behaviour, a colon must appear in every line. QTest::addColumn("codecName"); QTest::addColumn("data"); QTest::addColumn >("expectedLines"); QList codecNames; codecNames << "UTF-8" << "UTF-16"; foreach (QByteArray codecName, codecNames) { QTest::newRow("empty " + codecName) << codecName << "" << QList(); QTest::newRow("one line " + codecName) << codecName << "line:" << (QList() << QStringLiteral("line:")); QTest::newRow("one ten-byte line " + codecName) << codecName << "10letters:" << (QList() << QStringLiteral("10letters:")); QTest::newRow("one long line " + codecName) << codecName << "one:line longer than ten characters" << (QList() << QStringLiteral("one:line longer than ten characters")); QTest::newRow("one terminated line " + codecName) << codecName << "one:line longer than ten characters\r\n" << (QList() << QStringLiteral("one:line longer than ten characters")); QTest::newRow("two lines " + codecName) << codecName << "two:\r\nlines:" << (QList() << QStringLiteral("two:") << QStringLiteral("lines:")); QTest::newRow("two terminated lines " + codecName) << codecName << "two:\r\nlines:\r\n" << (QList() << QStringLiteral("two:") << QStringLiteral("lines:")); QTest::newRow("two long lines " + codecName) << codecName << "one:line longer than ten characters\r\nanother line:\r\n" << (QList() << QStringLiteral("one:line longer than ten characters") << QStringLiteral("another line:")); QTest::newRow("two full lines " + codecName) << codecName << "10letters:\r\n8letter:\r\n" << (QList() << QStringLiteral("10letters:") << QStringLiteral("8letter:")); QTest::newRow("a nine-byte line " + codecName) << codecName << "9letters:\r\nanother:line\r\n" << (QList() << QStringLiteral("9letters:") << QStringLiteral("another:line")); QTest::newRow("a blank line " + codecName) << codecName << "one:\r\n\r\ntwo:\r\n" << (QList() << QStringLiteral("one:") << QStringLiteral("two:")); QTest::newRow("folded lines " + codecName) << codecName << "fold:ed\r\n line\r\nsecond: line\r\n" << (QList() << QStringLiteral("fold:ed line") << QStringLiteral("second: line")); QTest::newRow("multiply folded lines " + codecName) << codecName << "fo\r\n lded:\r\n line\r\nseco\r\n\tnd:l\r\n ine\r\n" << (QList() << QStringLiteral("folded: line") << QStringLiteral("second:line")); QTest::newRow("fold hidden after a chunk " + codecName) << codecName << "8letter:\r\n on one line\r\n" << (QList() << QStringLiteral("8letter: on one line")); QTest::newRow("three mac lines " + codecName) << codecName << "one:\rtwo:\rthree:\r" << (QList() << QStringLiteral("one:") << QStringLiteral("two:") << QStringLiteral("three:")); // Tests a workaround to parse a certain malformed vCard QTest::newRow("badly wrapped lines " + codecName) << codecName << "one:line\r\ntwo\r\nthree\r\n" << (QList() << QStringLiteral("one:linetwothree")); } #endif } void tst_QVersitReader::testByteArrayInput() { delete mReader; const QByteArray& oneDocument = "BEGIN:VCARD\r\nVERSION:2.1\r\nFN:John\r\nEND:VCARD\r\n"; mReader = new QVersitReader(oneDocument); QVERIFY(mReader->device() == 0); QVERIFY2(mReader->startReading(), QString::number(mReader->error()).toLatin1().data()); QVERIFY2(mReader->waitForFinished(), QString::number(mReader->error()).toLatin1().data()); QList results = mReader->results(); QCOMPARE(mReader->state(), QVersitReader::FinishedState); QCOMPARE(mReader->error(), QVersitReader::NoError); QCOMPARE(results.count(),1); QVersitDocument result = results.first(); QCOMPARE(result.type(), QVersitDocument::VCard21Type); QList properties = result.properties(); QCOMPARE(properties.length(), 1); QCOMPARE(properties.first().name(), QStringLiteral("FN")); QCOMPARE(properties.first().value(), QStringLiteral("John")); } void tst_QVersitReader::testRemoveBackSlashEscaping() { #ifndef QT_BUILD_INTERNAL QSKIP("Testing private API"); #else // Empty string QString input; QVersitReaderPrivate::removeBackSlashEscaping(&input); QCOMPARE(input,QString()); // Nothing to escape in the string input = QStringLiteral("Nothing to escape"); QVersitReaderPrivate::removeBackSlashEscaping(&input); QCOMPARE(input,QStringLiteral("Nothing to escape")); // Line break, semicolon, backslash and comma in the string input = QStringLiteral("These should be unescaped \\n \\N \\; \\, \\\\"); QVersitReaderPrivate::removeBackSlashEscaping(&input); QCOMPARE(input, QStringLiteral("These should be unescaped \r\n \r\n ; , \\")); // Don't remove escaping within quotes input = QStringLiteral("\"Quoted \\n \\N \\; \\,\""); QVersitReaderPrivate::removeBackSlashEscaping(&input); QCOMPARE(input, QStringLiteral("\"Quoted \\n \\N \\; \\,\"")); #endif } QTEST_MAIN(tst_QVersitReader) tests/auto/versit/qversitreader/tst_qversitreader.h000066400000000000000000000075311233466112000233130ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef tst_QVERSITREADER_H #define tst_QVERSITREADER_H #include #include #include QT_BEGIN_NAMESPACE_VERSIT class QVersitReaderPrivate; class LineReader; QT_END_NAMESPACE_VERSIT QTVERSIT_USE_NAMESPACE // Poor man's QSignalSpy because I couldn't get QSignalSpy to work with the user type QVR::State. class SignalCatcher : public QObject { Q_OBJECT public: SignalCatcher() : mResultsCount(0) {} public slots: void stateChanged(QVersitReader::State state) { mStateChanges.append(state); } void resultsAvailable() { mResultsCount += 1; } public: QList mStateChanges; int mResultsCount; }; class tst_QVersitReader : public QObject { Q_OBJECT private slots: // Tests void init(); void cleanup(); void testDevice(); void testNullDevice(); void testDefaultCodec(); void testValidateUtf8(); void testValidateUtf8_data(); void testDetectCodec(); void testDetectCodec_data(); void testReading(); void testResult(); void testParseNextVersitProperty(); void testParseNextVersitProperty_data(); void testParseVersitDocument(); void testParseVersitDocument_data(); void testDecodeQuotedPrintable(); void testDecodeQuotedPrintable_data(); void testParamName(); void testParamValue(); void testExtractPart(); void testExtractParts(); void testExtractPropertyGroupsAndName(); void testExtractVCard21PropertyParams(); void testExtractVCard30PropertyParams(); void testExtractParams(); void testReadLine(); void testReadLine_data(); void testByteArrayInput(); void testRemoveBackSlashEscaping(); private: // Data QVersitReader* mReader; #ifdef QT_BUILD_INTERNAL QVersitReaderPrivate* mReaderPrivate; #endif QBuffer* mInputDevice; QTextCodec* mAsciiCodec; SignalCatcher* mSignalCatcher; }; #endif // tst_VERSITREADER_H tests/auto/versit/qversitwriter/000077500000000000000000000000001233466112000174345ustar00rootroot00000000000000tests/auto/versit/qversitwriter/qversitwriter.pro000066400000000000000000000002401233466112000231040ustar00rootroot00000000000000include(../../auto.pri) QT += versit versit-private HEADERS += tst_qversitwriter.h SOURCES += tst_qversitwriter.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/versit/qversitwriter/tst_qversitwriter.cpp000066400000000000000000000421441233466112000237710ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/versit #include "tst_qversitwriter.h" #include #include #include #include #include QTVERSIT_USE_NAMESPACE void tst_QVersitWriter::init() { mOutputDevice = new QBuffer; mWriter = new QVersitWriter; mSignalCatcher = new SignalCatcher; connect(mWriter, SIGNAL(stateChanged(QVersitWriter::State)), mSignalCatcher, SLOT(stateChanged(QVersitWriter::State))); } void tst_QVersitWriter::cleanup() { delete mWriter; delete mOutputDevice; delete mSignalCatcher; } void tst_QVersitWriter::testDevice() { // No device QVERIFY(mWriter->device() == NULL); // Device has been set mWriter->setDevice(mOutputDevice); QVERIFY(mWriter->device() == mOutputDevice); } void tst_QVersitWriter::testDefaultCodec() { QVERIFY(mWriter->defaultCodec() == 0); mWriter->setDefaultCodec(QTextCodec::codecForName("UTF-16BE")); QVERIFY(mWriter->defaultCodec() == QTextCodec::codecForName("UTF-16BE")); } void tst_QVersitWriter::testWritingVersions() { mWriter->setDevice(mOutputDevice); mOutputDevice->open(QBuffer::ReadWrite); QVersitDocument document; QVersitProperty property; property.setName(QString(QString::fromLatin1("FN"))); property.setValue(QString::fromLatin1("John")); document.addProperty(property); QByteArray vCard30( "BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "FN:John\r\n" "END:VCARD\r\n"); QByteArray vCard21( "BEGIN:VCARD\r\n" "VERSION:2.1\r\n" "FN:John\r\n" "END:VCARD\r\n"); // Given no type or componentType, it should be vCard 3.0 QVERIFY(mWriter->startWriting(document)); mWriter->waitForFinished(); QCOMPARE(mOutputDevice->buffer(), vCard30); // document type should override the guess document.setType(QVersitDocument::VCard21Type); mOutputDevice->buffer().clear(); mOutputDevice->seek(0); QVERIFY(mWriter->startWriting(document)); mWriter->waitForFinished(); QCOMPARE(mOutputDevice->buffer(), vCard21); // param to startWriting should override document type mOutputDevice->buffer().clear(); mOutputDevice->seek(0); QVERIFY(mWriter->startWriting(document, QVersitDocument::VCard30Type)); mWriter->waitForFinished(); QCOMPARE(mOutputDevice->buffer(), vCard30); } void tst_QVersitWriter::testWriting21() { // vCard 2.1 QByteArray vCard21( "BEGIN:VCARD\r\n\ VERSION:2.1\r\n\ FN:John\r\n\ END:VCARD\r\n"); QVersitDocument document; document.setComponentType(QStringLiteral("VCARD")); QVersitProperty property; property.setName(QString(QStringLiteral("FN"))); property.setValue(QStringLiteral("John")); document.addProperty(property); document.setType(QVersitDocument::VCard21Type); QList list; list.append(document); // Device not set QCOMPARE(mWriter->state(), QVersitWriter::InactiveState); QCOMPARE(mWriter->error(), QVersitWriter::NoError); QVERIFY(!mWriter->startWriting(list)); QCOMPARE(mWriter->state(), QVersitWriter::InactiveState); QCOMPARE(mWriter->error(), QVersitWriter::IOError); QVERIFY(!mWriter->waitForFinished()); // Device not opened mWriter->setDevice(mOutputDevice); QVERIFY(!mWriter->startWriting(list)); QCOMPARE(mWriter->state(), QVersitWriter::InactiveState); QCOMPARE(mWriter->error(), QVersitWriter::IOError); // Now open the device and it should work. mOutputDevice->open(QBuffer::ReadWrite); QVERIFY2(mWriter->startWriting(list), QString::number(mWriter->error()).toLatin1().data()); QVERIFY2(mWriter->waitForFinished(), QString::number(mWriter->error()).toLatin1().data()); QCOMPARE(mWriter->state(), QVersitWriter::FinishedState); QCOMPARE(mWriter->error(), QVersitWriter::NoError); mOutputDevice->seek(0); QByteArray result(mOutputDevice->readAll()); QCOMPARE(result, vCard21); // Try some other codec delete mOutputDevice; mOutputDevice = new QBuffer; mOutputDevice->open(QBuffer::ReadWrite); mWriter->setDevice(mOutputDevice); QTextCodec* utf16(QTextCodec::codecForName("UTF-16")); mWriter->setDefaultCodec(utf16); QVERIFY2(mWriter->startWriting(list), QString::number(mWriter->error()).toLatin1().data()); QVERIFY2(mWriter->waitForFinished(), QString::number(mWriter->error()).toLatin1().data()); QCOMPARE(mWriter->state(), QVersitWriter::FinishedState); QCOMPARE(mWriter->error(), QVersitWriter::NoError); mOutputDevice->seek(0); result = mOutputDevice->readAll(); QByteArray expected(utf16->fromUnicode(QLatin1String(vCard21.data()))); QCOMPARE(result, expected); } void tst_QVersitWriter::testWriting30() { // vCard 3.0 QByteArray vCard30( "BEGIN:VCARD\r\n\ VERSION:3.0\r\n\ FN:John\r\n\ END:VCARD\r\n"); QVersitDocument document; document.setComponentType(QStringLiteral("VCARD")); QVersitProperty property; property.setName(QString(QStringLiteral("FN"))); property.setValue(QStringLiteral("John")); document.addProperty(property); document.setType(QVersitDocument::VCard30Type); QList list; list.append(document); // Basic 3.0 test mOutputDevice->open(QBuffer::ReadWrite); mWriter->setDevice(mOutputDevice); QVERIFY2(mWriter->startWriting(list), QString::number(mWriter->error()).toLatin1().data()); QVERIFY2(mWriter->waitForFinished(), QString::number(mWriter->error()).toLatin1().data()); QCOMPARE(mWriter->state(), QVersitWriter::FinishedState); QCOMPARE(mWriter->error(), QVersitWriter::NoError); mOutputDevice->seek(0); QByteArray result(mOutputDevice->readAll()); QCOMPARE(result, vCard30); qApp->processEvents(); // clean up before we start sniffing signals // Asynchronous writing mOutputDevice->reset(); mSignalCatcher->mReceived.clear(); QVERIFY2(mWriter->startWriting(list), QString::number(mWriter->error()).toLatin1().data()); QTRY_VERIFY(mSignalCatcher->mReceived.count() >= 2); QCOMPARE(mSignalCatcher->mReceived.at(0), QVersitWriter::ActiveState); QCOMPARE(mSignalCatcher->mReceived.at(1), QVersitWriter::FinishedState); // Cancelling delete mOutputDevice; mOutputDevice = new QBuffer; mOutputDevice->open(QBuffer::ReadWrite); mSignalCatcher->mReceived.clear(); mWriter->setDevice(mOutputDevice); mWriter->startWriting(list); mWriter->cancel(); mWriter->waitForFinished(); QTRY_VERIFY(mSignalCatcher->mReceived.count() >= 2); QCOMPARE(mSignalCatcher->mReceived.at(0), QVersitWriter::ActiveState); QVersitWriter::State state(mSignalCatcher->mReceived.at(1)); // It's possible that it finishes before it cancels. QVERIFY(state == QVersitWriter::CanceledState || state == QVersitWriter::FinishedState); } void tst_QVersitWriter::testByteArrayOutput() { const QByteArray vCard30( "BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "FN:John\r\n" "END:VCARD\r\n"); delete mWriter; // we don't want the init()ed writer. QByteArray output; mWriter = new QVersitWriter(&output); QVERIFY(mWriter->device() == 0); QVersitDocument document(QVersitDocument::VCard30Type); document.setComponentType(QStringLiteral("VCARD")); QVersitProperty property; property.setName(QString(QStringLiteral("FN"))); property.setValue(QStringLiteral("John")); document.addProperty(property); QVERIFY2(mWriter->startWriting(document), QString::number(mWriter->error()).toLatin1().data()); QVERIFY2(mWriter->waitForFinished(), QString::number(mWriter->error()).toLatin1().data()); QCOMPARE(output, vCard30); } void tst_QVersitWriter::testWritingDocument() { QFETCH(QVersitDocument, document); QFETCH(QByteArray, expected); mOutputDevice->open(QBuffer::ReadWrite); mWriter->setDevice(mOutputDevice); QVERIFY2(mWriter->startWriting(document), QString::number(mWriter->error()).toLatin1().data()); QVERIFY2(mWriter->waitForFinished(), QString::number(mWriter->error()).toLatin1().data()); mOutputDevice->seek(0); QByteArray result(mOutputDevice->readAll()); if (result!=expected) qDebug() << result << expected; QCOMPARE(result, expected); // try it again in another codec QTextCodec* utf16(QTextCodec::codecForName("UTF-16")); mWriter->setDefaultCodec(utf16); mOutputDevice->buffer().clear(); mOutputDevice->seek(0); QVERIFY2(mWriter->startWriting(document), QString::number(mWriter->error()).toLatin1().data()); QVERIFY2(mWriter->waitForFinished(), QString::number(mWriter->error()).toLatin1().data()); mOutputDevice->seek(0); result = mOutputDevice->readAll(); expected = utf16->fromUnicode(QString::fromLatin1(expected)); if (result!=expected) qDebug() << result << expected; QCOMPARE(result, expected); } void tst_QVersitWriter::testWritingDocument_data() { QTest::addColumn("document"); QTest::addColumn("expected"); QVersitDocument document(QVersitDocument::VCard21Type); document.setComponentType(QStringLiteral("VCARD")); QVersitProperty property; property.setName(QStringLiteral("FN")); property.setValue(QStringLiteral("Bob")); document.addProperty(property); QTest::newRow("basic vCard 2.1") << document << QByteArray( "BEGIN:VCARD\r\n" "VERSION:2.1\r\n" "FN:Bob\r\n" "END:VCARD\r\n" ); document.setComponentType(QStringLiteral("VCARD")); document.setType(QVersitDocument::VCard30Type); QTest::newRow("basic vCard 3.0") << document << QByteArray( "BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "FN:Bob\r\n" "END:VCARD\r\n" ); document.setComponentType(QStringLiteral("VCARD")); document.setType(QVersitDocument::VCard40Type); QTest::newRow("basic vCard 4.0") << document << QByteArray( "BEGIN:VCARD\r\n" "VERSION:4.0\r\n" "FN:Bob\r\n" "END:VCARD\r\n" ); { QVersitDocument document(QVersitDocument::ICalendar20Type); document.setComponentType(QStringLiteral("VCALENDAR")); QVersitDocument subdocument(QVersitDocument::ICalendar20Type); subdocument.setComponentType(QStringLiteral("VEVENT")); property.setValueType(QVersitProperty::PreformattedType); property.setName(QStringLiteral("RRULE")); property.setValue(QStringLiteral("FREQ=MONTHLY;BYMONTHDAY=1,3")); subdocument.addProperty(property); document.addSubDocument(subdocument); QTest::newRow("basic iCalendar 2.0") << document << QByteArray( "BEGIN:VCALENDAR\r\n" "VERSION:2.0\r\n" "BEGIN:VEVENT\r\n" "RRULE:FREQ=MONTHLY;BYMONTHDAY=1,3\r\n" "END:VEVENT\r\n" "END:VCALENDAR\r\n"); } { QVersitDocument document(QVersitDocument::ICalendar20Type); document.setComponentType(QStringLiteral("VCALENDAR")); QVersitProperty property; property.setName(QStringLiteral("PRODID")); property.setValue(QStringLiteral("-//hacksw/handcal//NONSGML v1.0//EN")); document.addProperty(property); QVersitDocument nested(QVersitDocument::ICalendar20Type); nested.setComponentType(QStringLiteral("VEVENT")); property.setName(QStringLiteral("DTSTART")); property.setValue(QStringLiteral("19970714T170000Z")); nested.addProperty(property); property.setName(QStringLiteral("DTEND")); property.setValue(QStringLiteral("19970715T035959Z")); nested.addProperty(property); property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("Bastille Day Party")); nested.addProperty(property); document.addSubDocument(nested); QTest::newRow("iCalendar 2.0 from spec") << document << QByteArray( "BEGIN:VCALENDAR\r\n" "VERSION:2.0\r\n" "PRODID:-//hacksw/handcal//NONSGML v1.0//EN\r\n" "BEGIN:VEVENT\r\n" "DTSTART:19970714T170000Z\r\n" "DTEND:19970715T035959Z\r\n" "SUMMARY:Bastille Day Party\r\n" "END:VEVENT\r\n" "END:VCALENDAR\r\n"); } { QString longString(QLatin1String( "4567890123456789012345678901234567890123456789012345678901234567890123456" "234567890123456789012345678901234567890123456789012345678901234567890123456" "234567890123456789012")); QVersitDocument document(QVersitDocument::VCard21Type); document.setComponentType(QStringLiteral("VCARD")); QVersitProperty property; property.setName(QStringLiteral("FN")); property.setValue(longString); document.addProperty(property); QByteArray expected21( "BEGIN:VCARD\r\n" "VERSION:2.1\r\n" "FN:4567890123456789012345678901234567890123456789012345678901234567890123456\r\n" " 234567890123456789012345678901234567890123456789012345678901234567890123456\r\n" " 234567890123456789012\r\n" "END:VCARD\r\n"); QTest::newRow("folding 2.1") << document << expected21; document.setType(QVersitDocument::VCard30Type); QByteArray expected30( "BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "FN:4567890123456789012345678901234567890123456789012345678901234567890123456\r\n" " 234567890123456789012345678901234567890123456789012345678901234567890123456\r\n" " 234567890123456789012\r\n" "END:VCARD\r\n"); QTest::newRow("folding 3.0") << document << expected30; document.setType(QVersitDocument::VCard21Type); property.setValue(longString.toLatin1()); property.setValueType(QVersitProperty::BinaryType); document.removeProperties("FN"); document.addProperty(property); QByteArray expected21b( "BEGIN:VCARD\r\n" "VERSION:2.1\r\n" "FN;ENCODING=BASE64:\r\n" " NDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODk\r\n" " wMTIzNDU2Nzg5MDEyMzQ1NjIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MD\r\n" " EyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1NjIzNDU2Nzg5MDEyMzQ1Njc4OTAxM\r\n" " g==\r\n" "\r\n" "END:VCARD\r\n"); QTest::newRow("folding 2.1") << document << expected21b; document.setType(QVersitDocument::VCard30Type); QByteArray expected30b( "BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "FN;ENCODING=b:NDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OT\r\n" " AxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1NjIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwM\r\n" " TIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1NjIzNDU2Nzg5MDEy\r\n" " MzQ1Njc4OTAxMg==\r\n" "END:VCARD\r\n"); QTest::newRow("folding 3.0") << document << expected30b; } } QTEST_MAIN(tst_QVersitWriter) tests/auto/versit/qversitwriter/tst_qversitwriter.h000066400000000000000000000057011233466112000234340ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef tst_QVERSITWRITER_H #define tst_QVERSITWRITER_H #include #include #include QT_BEGIN_NAMESPACE_VERSIT class QVersitWriterPrivate; QT_END_NAMESPACE_VERSIT QTVERSIT_USE_NAMESPACE // Poor man's QSignalSpy because I couldn't get QSignalSpy to work with the user type QVR::State. class SignalCatcher : public QObject { Q_OBJECT public slots: void stateChanged(QVersitWriter::State state) { mReceived.append(state); } public: QList mReceived; }; class tst_QVersitWriter : public QObject { Q_OBJECT private slots: // Tests void init(); void cleanup(); void testDevice(); void testDefaultCodec(); void testWritingVersions(); void testWriting21(); void testWriting30(); void testByteArrayOutput(); void testWritingDocument(); void testWritingDocument_data(); private: // Data QVersitWriter* mWriter; QBuffer* mOutputDevice; SignalCatcher* mSignalCatcher; }; #endif // tst_QVERSITWRITER_H tests/auto/versit/versit.pro000066400000000000000000000003641233466112000165430ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS += \ qvcard21writer \ qvcard30writer \ qversitcontactexporter \ qversitcontactimporter \ qversitcontactplugins \ qversitdocument \ qversitproperty \ qversitreader \ qversitwriter tests/auto/versitorganizer/000077500000000000000000000000001233466112000164235ustar00rootroot00000000000000tests/auto/versitorganizer/qversitorganizerexporter/000077500000000000000000000000001233466112000236325ustar00rootroot00000000000000tests/auto/versitorganizer/qversitorganizerexporter/qversitorganizerexporter.pro000066400000000000000000000003011233466112000315550ustar00rootroot00000000000000include(../../auto.pri) QT += organizer versit versitorganizer HEADERS += tst_qversitorganizerexporter.h SOURCES += tst_qversitorganizerexporter.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/versitorganizer/qversitorganizerexporter/tst_qversitorganizerexporter.cpp000066400000000000000000002234501233466112000324450ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/versit #include "tst_qversitorganizerexporter.h" #include QTORGANIZER_USE_NAMESPACE QTVERSITORGANIZER_USE_NAMESPACE Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(QList) void tst_QVersitOrganizerExporter::testExport() { QFETCH(QList, items); QFETCH(QVersitDocument, expectedDocument); QVersitOrganizerExporter exporter; QVERIFY(exporter.exportItems(items)); QVERIFY(exporter.errorMap().isEmpty()); QVersitDocument document = exporter.document(); if (document != expectedDocument) { qDebug() << "Actual:" << document; qDebug() << "Expected:" << expectedDocument; QCOMPARE(document, expectedDocument); } } void tst_QVersitOrganizerExporter::testExport_data() { QTest::addColumn >("items"); QTest::addColumn("expectedDocument"); { QVersitDocument document(QVersitDocument::ICalendar20Type); document.setComponentType(QStringLiteral("VCALENDAR")); QVersitDocument nested(QVersitDocument::ICalendar20Type); nested.setComponentType(QStringLiteral("VEVENT")); QVersitProperty property; property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("Bastille Day Party")); nested.addProperty(property); property.setName(QStringLiteral("DTSTART")); property.setValue(QStringLiteral("19970714T170000Z")); nested.addProperty(property); property.setName(QStringLiteral("DTEND")); property.setValue(QStringLiteral("19970715T035959Z")); nested.addProperty(property); document.addSubDocument(nested); nested.clear(); nested.setType(QVersitDocument::ICalendar20Type); nested.setComponentType(QStringLiteral("VTODO")); property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("Take out the garbage")); nested.addProperty(property); property.setName(QStringLiteral("DTSTART")); property.setValue(QStringLiteral("20100609T080000")); nested.addProperty(property); property.setName(QStringLiteral("DUE")); property.setValue(QStringLiteral("20100610T080000")); nested.addProperty(property); document.addSubDocument(nested); nested.clear(); nested.setType(QVersitDocument::ICalendar20Type); nested.setComponentType(QStringLiteral("VJOURNAL")); property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("Trip to Thailand")); nested.addProperty(property); property.setName(QStringLiteral("DTSTART")); property.setValue(QStringLiteral("20100615T112300")); nested.addProperty(property); document.addSubDocument(nested); QOrganizerEvent event; event.setDisplayLabel(QStringLiteral("Bastille Day Party")); event.setStartDateTime(QDateTime(QDate(1997, 7, 14), QTime(17, 0, 0), Qt::UTC)); event.setEndDateTime(QDateTime(QDate(1997, 7, 15), QTime(3, 59, 59), Qt::UTC)); QOrganizerTodo todo; todo.setDisplayLabel(QStringLiteral("Take out the garbage")); todo.setStartDateTime(QDateTime(QDate(2010, 6, 9), QTime(8, 0, 0))); todo.setDueDateTime(QDateTime(QDate(2010, 6, 10), QTime(8, 0, 0))); QOrganizerJournal journal; journal.setDisplayLabel(QStringLiteral("Trip to Thailand")); journal.setDateTime(QDateTime(QDate(2010, 6, 15), QTime(11, 23, 0))); QList items; items << static_cast(event); items << static_cast(todo); items << static_cast(journal); QTest::newRow("sample event, todo and journal") << items << document; } { QVersitDocument document(QVersitDocument::ICalendar20Type); document.setComponentType(QStringLiteral("VCALENDAR")); QVersitDocument nested(QVersitDocument::ICalendar20Type); nested.setComponentType(QStringLiteral("VEVENT")); QVersitProperty property; property.setName(QStringLiteral("UID")); property.setValue(QStringLiteral("1234")); nested.addProperty(property); property.setName(QStringLiteral("RECURRENCE-ID")); property.setValue(QStringLiteral("20100608")); nested.addProperty(property); document.addSubDocument(nested); // An event occurrence with OriginalDate and Guid set QOrganizerEventOccurrence event; event.setGuid(QStringLiteral("1234")); event.setOriginalDate(QDate(2010, 6, 8)); QList items; items << static_cast(event); QTest::newRow("event occurrence exception") << items << document; } } /*! Test that bad input causes the exporter to fail with the correct error */ void tst_QVersitOrganizerExporter::testExportError() { QFETCH(QList, items); QFETCH(QVersitDocument, expectedDocument); QFETCH(int, expectedError); QVersitOrganizerExporter exporter; QVERIFY(!exporter.exportItems(items)); QVERIFY(!exporter.errorMap().isEmpty()); QVERIFY(exporter.errorMap()[0] == expectedError); QVersitDocument document = exporter.document(); if (document != expectedDocument) { qDebug() << "Actual:" << document; qDebug() << "Expected:" << expectedDocument; QCOMPARE(document, expectedDocument); } } void tst_QVersitOrganizerExporter::testExportError_data() { QTest::addColumn >("items"); QTest::addColumn("expectedDocument"); QTest::addColumn("expectedError"); QVersitDocument document(QVersitDocument::ICalendar20Type); document.setComponentType(QStringLiteral("VCALENDAR")); // An event occurrence with OriginalDate set but no Guid QOrganizerEventOccurrence event; event.setOriginalDate(QDate(2010, 6, 8)); QList items; items << static_cast(event); QTest::newRow("event occurrence exception with no guid") << items << document << (int)QVersitOrganizerExporter::UnderspecifiedOccurrenceError; } void tst_QVersitOrganizerExporter::testExportEventDetails() { QFETCH(QList, details); QFETCH(QList, expectedProperties); QVersitOrganizerExporter exporter; QOrganizerEvent item; foreach (QOrganizerItemDetail detail, details) { item.saveDetail(&detail); } QVERIFY(exporter.exportItems(QList() << item)); QVERIFY(exporter.errorMap().isEmpty()); QVersitDocument document = exporter.document(); QList subDocuments = document.subDocuments(); QCOMPARE(subDocuments.size(), 1); int currentVAlarmSubDocumentIndex = 0; foreach(const QVersitProperty& expectedProperty, expectedProperties) { QList actualProperties = findPropertiesByName(subDocuments.first(), expectedProperty.name()); if (expectedProperty.valueType() == QVersitProperty::VersitDocumentType) { QCOMPARE(subDocuments.first().subDocuments().size(), expectedProperties.size()); QVersitDocument actualVAlarmDocument = subDocuments.first().subDocuments().at(currentVAlarmSubDocumentIndex); QVersitDocument expectedVAlarmDocument = expectedProperty.variantValue().value(); foreach (const QVersitProperty &valarmProp, expectedVAlarmDocument.properties()) { if (!actualVAlarmDocument.properties().contains(valarmProp)) { qDebug() << "Actual:" << actualVAlarmDocument.properties(); qDebug() << "Expected to find:" << valarmProp; QVERIFY(false); } } currentVAlarmSubDocumentIndex ++; //move to next expected valarm document } else if (!actualProperties.contains(expectedProperty)) { qDebug() << "Actual:" << actualProperties; qDebug() << "Expected to find:" << expectedProperty; QVERIFY(false); } } } void tst_QVersitOrganizerExporter::testExportEventDetails_data() { QTest::addColumn >("details"); QTest::addColumn >("expectedProperties"); { QVersitProperty property; property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("jabberwocky")); QOrganizerItemDisplayLabel displayLabel; displayLabel.setLabel(QStringLiteral("jabberwocky")); QTest::newRow("one summary") << (QList() << displayLabel) << (QList() << property); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QMultiHash parameters; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("AUDIO")); valarmProperties << property; property.setName(QStringLiteral("ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); parameters.insert(QStringLiteral("RELATED"), QStringLiteral("START")); property.setParameters(parameters); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.clear(); valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemAudibleReminder audibleReminder; audibleReminder.setDataUrl(QUrl("http://qt.nokia.com")); audibleReminder.setRepetition(3, 5); audibleReminder.setSecondsBeforeStart(90); QTest::newRow("audible reminder") << (QList() << audibleReminder) << (QList() << property); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QMultiHash parameters; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("DISPLAY")); valarmProperties << property; property.setName(QStringLiteral("x-QTPROJECT-ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); parameters.insert(QStringLiteral("RELATED"), QStringLiteral("START")); property.setParameters(parameters); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.clear(); property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("Test visual reminder")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemVisualReminder visualReminder; visualReminder.setDataUrl(QUrl("http://qt.nokia.com")); visualReminder.setRepetition(3, 5); visualReminder.setSecondsBeforeStart(90); visualReminder.setMessage(QStringLiteral("Test visual reminder")); QTest::newRow("visual reminder") << (QList() << visualReminder) << (QList() << property); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QMultiHash parameters; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("DISPLAY")); valarmProperties << property; property.setName(QStringLiteral("x-QTPROJECT-ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); parameters.insert(QStringLiteral("RELATED"), QStringLiteral("START")); property.setParameters(parameters); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.clear(); property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemVisualReminder visualReminder; visualReminder.setDataUrl(QUrl("http://qt.nokia.com")); visualReminder.setRepetition(3, 5); visualReminder.setSecondsBeforeStart(90); QTest::newRow("visual reminder: no message") << (QList() << visualReminder) << (QList() << property); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QMultiHash parameters; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("EMAIL")); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); parameters.insert(QStringLiteral("RELATED"), QStringLiteral("START")); property.setParameters(parameters); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.clear(); property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("Test email body")); valarmProperties << property; property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("Test email subject")); valarmProperties << property; property.setName(QStringLiteral("ATTENDEE")); property.setValue(QStringLiteral("First email recipient")); valarmProperties << property; property.setValue(QStringLiteral("Second email recipient")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemEmailReminder emailReminder; QVariantList attachments; emailReminder.setRepetition(3, 5); emailReminder.setSecondsBeforeStart(90); emailReminder.setRecipients((QStringList() << QStringLiteral("First email recipient") << QStringLiteral("Second email recipient"))); emailReminder.setContents(QStringLiteral("Test email subject"), QStringLiteral("Test email body"), attachments); QTest::newRow("email reminder") << (QList() << emailReminder) << (QList() << property); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QMultiHash parameters; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("EMAIL")); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); parameters.insert(QStringLiteral("RELATED"), QStringLiteral("START")); property.setParameters(parameters); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.clear(); property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("")); valarmProperties << property; property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("")); valarmProperties << property; property.setName(QStringLiteral("ATTENDEE")); property.setValue(QStringLiteral("First email recipient")); valarmProperties << property; property.setValue(QStringLiteral("Second email recipient")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemEmailReminder emailReminder; QVariantList attachments; emailReminder.setRepetition(3, 5); emailReminder.setSecondsBeforeStart(90); emailReminder.setRecipients((QStringList() << QStringLiteral("First email recipient") << QStringLiteral("Second email recipient"))); emailReminder.setValue(QOrganizerItemEmailReminder::FieldAttachments, attachments); QTest::newRow("email reminder: no subject, no body") << (QList() << emailReminder) << (QList() << property); } { QList detailsToExport; QList expectedProperties; { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QMultiHash parameters; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("AUDIO")); valarmProperties << property; property.setName(QStringLiteral("ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); parameters.insert(QStringLiteral("RELATED"), QStringLiteral("START")); property.setParameters(parameters); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.clear(); valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemAudibleReminder audibleReminder; audibleReminder.setDataUrl(QUrl("http://qt.nokia.com")); audibleReminder.setRepetition(3, 5); audibleReminder.setSecondsBeforeStart(90); detailsToExport << audibleReminder; expectedProperties << property; } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QMultiHash parameters; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("DISPLAY")); valarmProperties << property; property.setName(QStringLiteral("x-QTPROJECT-ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); parameters.insert(QStringLiteral("RELATED"), QStringLiteral("START")); property.setParameters(parameters); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.clear(); property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemVisualReminder visualReminder; visualReminder.setDataUrl(QUrl("http://qt.nokia.com")); visualReminder.setRepetition(3, 5); visualReminder.setSecondsBeforeStart(90); detailsToExport << visualReminder; expectedProperties << property; } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QMultiHash parameters; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("EMAIL")); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); parameters.insert(QStringLiteral("RELATED"), QStringLiteral("START")); property.setParameters(parameters); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.clear(); property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("")); valarmProperties << property; property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("")); valarmProperties << property; property.setName(QStringLiteral("ATTENDEE")); property.setValue(QStringLiteral("First email recipient")); valarmProperties << property; property.setValue(QStringLiteral("Second email recipient")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemEmailReminder emailReminder; QVariantList attachments; emailReminder.setRepetition(3, 5); emailReminder.setSecondsBeforeStart(90); emailReminder.setRecipients((QStringList() << QStringLiteral("First email recipient") << QStringLiteral("Second email recipient"))); emailReminder.setValue(QOrganizerItemEmailReminder::FieldAttachments, attachments); detailsToExport << emailReminder; expectedProperties << property; } QTest::newRow("multiple reminders") << detailsToExport << expectedProperties; } { QVersitProperty property; property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("Twinkle, twinkle, little bat! How I wonder what you're at.")); QOrganizerItemDescription description; description.setDescription(QStringLiteral("Twinkle, twinkle, little bat! How I wonder what you're at.")); QTest::newRow("one description") << (QList() << description) << (QList() << property); } { QVersitProperty property = createExtendedDetailPropertyForStringData("name", "data"); QOrganizerItemExtendedDetail extendedDetail = createExtendedDetail("name", QStringLiteral("data")); QTest::newRow("extended detail") << (QList() << extendedDetail) << (QList() << property); } { QVersitProperty property; // Proper version. property.setName(QStringLiteral("X-QTPROJECT-VERSION")); property.setValueType(QVersitProperty::CompoundType); property.setValue(QStringList() << QStringLiteral("4711") << QStringLiteral("134f23dbb2")); QOrganizerItemVersion version; version.setVersion(4711); version.setExtendedVersion(QByteArray("134f23dbb2")); QTest::newRow("version") << (QList() << version) << (QList() << property); } { QVersitProperty property; // Extended version empty. property.setName(QStringLiteral("X-QTPROJECT-VERSION")); property.setValueType(QVersitProperty::CompoundType); property.setValue(QStringList() << QStringLiteral("4711") << QStringLiteral("")); QOrganizerItemVersion version; version.setVersion(4711); version.setExtendedVersion(QByteArray("")); QTest::newRow("version") << (QList() << version) << (QList() << property); } { QVersitProperty property; // No extended version. property.setName(QStringLiteral("X-QTPROJECT-VERSION")); property.setValueType(QVersitProperty::CompoundType); property.setValue(QStringList() << QStringLiteral("4711") << QStringLiteral("")); QOrganizerItemVersion version; version.setVersion(4711); QTest::newRow("version") << (QList() << version) << (QList() << property); } { QVersitProperty property; // Empty version detail. QOrganizerItemVersion version; property.setName(QStringLiteral("X-QTPROJECT-VERSION")); property.setValueType(QVersitProperty::CompoundType); property.setValue(QStringList() << QStringLiteral("0") << QStringLiteral("")); QTest::newRow("version") << (QList() << version) << (QList()); } { QVersitProperty property1; property1.setName(QStringLiteral("COMMENT")); property1.setValue(QStringLiteral("Comment 1")); QVersitProperty property2; property2.setName(QStringLiteral("COMMENT")); property2.setValue(QStringLiteral("Comment 2")); QOrganizerItemComment comment1; comment1.setComment(QStringLiteral("Comment 1")); QOrganizerItemComment comment2; comment2.setComment(QStringLiteral("Comment 2")); QTest::newRow("two comments") << (QList() << comment1 << comment2) << (QList() << property1 << property2); } { QVersitProperty property; property.setName(QStringLiteral("UID")); property.setValue(QStringLiteral("1234567")); QOrganizerItemGuid guid; guid.setGuid(QStringLiteral("1234567")); QTest::newRow("guid") << (QList() << guid) << (QList() << property); } { QList properties; QVersitProperty dtstart; dtstart.setName(QStringLiteral("DTSTART")); dtstart.setValue(QStringLiteral("20100102T030405")); properties << dtstart; QVersitProperty dtend; dtend.setName(QStringLiteral("DTEND")); dtend.setValue(QStringLiteral("20100102T030406")); properties << dtend; QOrganizerEventTime etr; etr.setStartDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 5))); etr.setEndDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 6))); QTest::newRow("dtstart and dtend") << (QList() << etr) << properties; } { QList properties; QVersitProperty dtstart; dtstart.setName(QStringLiteral("DTSTART")); dtstart.setValue(QStringLiteral("20100102")); dtstart.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DATE")); properties << dtstart; QVersitProperty dtend; dtend.setName(QStringLiteral("DTEND")); // Note: in iCalendar, the end date is exclusive while in Qt Organizer, it is inclusive. // Hence, this is an event that occurs all day on 2 January (not including 3 January) dtend.setValue(QStringLiteral("20100103")); dtend.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DATE")); properties << dtend; QOrganizerEventTime etr; etr.setStartDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 5))); etr.setEndDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 6))); etr.setAllDay(true); QTest::newRow("all day event") << (QList() << etr) << properties; } { QList properties; QVersitProperty created; created.setName(QStringLiteral("CREATED")); created.setValue(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 5)).toUTC().toString( QStringLiteral("yyyyMMddTHHmmssZ"))); properties << created; QVersitProperty modified; modified.setName(QStringLiteral("LAST-MODIFIED")); modified.setValue(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 6)).toUTC().toString( QStringLiteral("yyyyMMddTHHmmssZ"))); properties << modified; QOrganizerItemTimestamp timestamp; timestamp.setCreated(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 5))); timestamp.setLastModified(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 6))); QTest::newRow("created and last modified") << (QList() << timestamp) << properties; } { QVersitProperty rrule; rrule.setValueType(QVersitProperty::PreformattedType); rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=DAILY")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Daily); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule daily") << (QList() << recurrence) << (QList() << rrule); rrule.setValue(QStringLiteral("FREQ=WEEKLY")); recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Weekly); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule weekly") << (QList() << recurrence) << (QList() << rrule); rrule.setValue(QStringLiteral("FREQ=MONTHLY")); recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Monthly); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule monthly") << (QList() << recurrence) << (QList() << rrule); rrule.setValue(QStringLiteral("FREQ=YEARLY")); recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Yearly); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule yearly") << (QList() << recurrence) << (QList() << rrule); rrule.setValue(QStringLiteral("FREQ=MONTHLY;INTERVAL=2;BYDAY=TU")); recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Monthly); recurrenceRule.setInterval(2); recurrenceRule.setDaysOfWeek(QSet() << Qt::Tuesday); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule monthly") << (QList() << recurrence) << (QList() << rrule); } { QVersitProperty rrule; rrule.setValueType(QVersitProperty::PreformattedType); rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=MONTHLY;BYMONTHDAY=-3,1")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Monthly); recurrenceRule.setDaysOfMonth(QSet() << 1 << -3); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule bymonthday") << (QList() << recurrence) << (QList() << rrule); } { QVersitProperty rrule; rrule.setValueType(QVersitProperty::PreformattedType); rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=YEARLY;BYWEEKNO=-3,1")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Yearly); recurrenceRule.setWeeksOfYear(QSet() << 1 << -3); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule byweekno") << (QList() << recurrence) << (QList() << rrule); } { QVersitProperty rrule; rrule.setValueType(QVersitProperty::PreformattedType); rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=YEARLY;BYMONTH=1,10")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Yearly); recurrenceRule.setMonthsOfYear(QSet() << QOrganizerRecurrenceRule::January << QOrganizerRecurrenceRule::October); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule bymonth") << (QList() << recurrence) << (QList() << rrule); } { QVersitProperty rrule; rrule.setValueType(QVersitProperty::PreformattedType); rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=YEARLY;BYYEARDAY=-1,1,366")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Yearly); recurrenceRule.setDaysOfYear(QSet() << 1 << 366 << -1); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule byyearday") << (QList() << recurrence) << (QList() << rrule); } { QVersitProperty rrule; rrule.setValueType(QVersitProperty::PreformattedType); rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=WEEKLY;COUNT=4;INTERVAL=2;BYDAY=TU,SU;WKST=SU")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Weekly); recurrenceRule.setInterval(2); recurrenceRule.setLimit(4); recurrenceRule.setDaysOfWeek(QSet() << Qt::Tuesday << Qt::Sunday); recurrenceRule.setFirstDayOfWeek(Qt::Sunday); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule wkst") << (QList() << recurrence) << (QList() << rrule); } { QVersitProperty rrule; rrule.setValueType(QVersitProperty::PreformattedType); rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Monthly); recurrenceRule.setDaysOfWeek(QSet() << Qt::Monday << Qt::Tuesday << Qt::Wednesday << Qt::Thursday << Qt::Friday); recurrenceRule.setPositions(QSet() << -1); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule bysetpos") << (QList() << recurrence) << (QList() << rrule); } { QVersitProperty rrule; rrule.setValueType(QVersitProperty::PreformattedType); rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=DAILY;UNTIL=20000131;BYMONTH=1")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Daily); recurrenceRule.setLimit(QDate(2000, 1, 31)); recurrenceRule.setMonthsOfYear(QSet() << QOrganizerRecurrenceRule::January); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule until") << (QList() << recurrence) << (QList() << rrule); } { QVersitProperty rrule; rrule.setValueType(QVersitProperty::PreformattedType); rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=DAILY;COUNT=5;BYMONTH=1")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Daily); recurrenceRule.setLimit(5); recurrenceRule.setMonthsOfYear(QSet() << QOrganizerRecurrenceRule::January); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule count") << (QList() << recurrence) << (QList() << rrule); } { QVersitProperty rdate; rdate.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DATE")); rdate.setName(QStringLiteral("RDATE")); rdate.setValue(QStringLiteral("19970304")); QOrganizerEventTime etr; etr.setStartDateTime(QDateTime(QDate(1997, 3, 4), QTime(11, 0, 0))); etr.setEndDateTime(QDateTime(QDate(1997, 3, 4), QTime(11, 0, 0))); QOrganizerItemRecurrence recurrence; QSet recurrenceDates; recurrenceDates << QDate(1997, 3, 4); recurrence.setRecurrenceDates(recurrenceDates); QTest::newRow("rdate") << (QList() << etr << recurrence) << (QList() << rdate); rdate.setValue(QStringLiteral("19970304,19970504,19970704")); recurrenceDates.clear(); recurrenceDates << QDate(1997, 3, 4) << QDate(1997, 5, 4) << QDate(1997, 7, 4); recurrence.setRecurrenceDates(recurrenceDates); QTest::newRow("multiple rdate") << (QList() << etr << recurrence) << (QList() << rdate); } { QVersitProperty rdate; rdate.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DATE")); rdate.setName(QStringLiteral("EXDATE")); rdate.setValue(QStringLiteral("19970304")); QOrganizerEventTime etr; etr.setStartDateTime(QDateTime(QDate(1997, 3, 4), QTime(11, 0, 0))); etr.setEndDateTime(QDateTime(QDate(1997, 3, 4), QTime(11, 0, 0))); QOrganizerItemRecurrence recurrence; QSet exceptionDates; exceptionDates << QDate(1997, 3, 4); recurrence.setExceptionDates(exceptionDates); QTest::newRow("exdate") << (QList() << etr << recurrence) << (QList() << rdate); } { QVersitProperty property; property.setName(QStringLiteral("CATEGORIES")); property.setValue(QStringLiteral("Important Event")); QOrganizerItemTag tag; tag.setTag(QStringLiteral("Important Event")); QTest::newRow("tag 1") << (QList() << tag) << (QList() << property); // Set another one for multiple handling test property.setValue(QStringLiteral("Important Event 2")); tag.setTag(QStringLiteral("Important Event 2")); QTest::newRow("tag 2") << (QList() << tag) << (QList() << property); // Empty tag property.clear(); property.setName("CATEGORIES"); tag.removeValue(QOrganizerItemTag::FieldTag); QTest::newRow("empty tag") << (QList() << tag) << (QList() << property); } { QVersitProperty property; property.setName(QStringLiteral("PRIORITY")); property.setValue(QStringLiteral("0")); QOrganizerItemPriority detail; detail.setPriority(QOrganizerItemPriority::UnknownPriority); QTest::newRow("priority 0") << (QList() << detail) << (QList() << property); property.setValue(QStringLiteral("1")); detail.setPriority(QOrganizerItemPriority::HighestPriority); QTest::newRow("priority 1") << (QList() << detail) << (QList() << property); property.setValue(QStringLiteral("2")); detail.setPriority(QOrganizerItemPriority::ExtremelyHighPriority); QTest::newRow("priority 2") << (QList() << detail) << (QList() << property); property.setValue(QStringLiteral("3")); detail.setPriority(QOrganizerItemPriority::VeryHighPriority); QTest::newRow("priority 3") << (QList() << detail) << (QList() << property); property.setValue(QStringLiteral("4")); detail.setPriority(QOrganizerItemPriority::HighPriority); QTest::newRow("priority 4") << (QList() << detail) << (QList() << property); property.setValue(QStringLiteral("5")); detail.setPriority(QOrganizerItemPriority::MediumPriority); QTest::newRow("priority 5") << (QList() << detail) << (QList() << property); property.setValue(QStringLiteral("6")); detail.setPriority(QOrganizerItemPriority::LowPriority); QTest::newRow("priority 6") << (QList() << detail) << (QList() << property); property.setValue(QStringLiteral("7")); detail.setPriority(QOrganizerItemPriority::VeryLowPriority); QTest::newRow("priority 7") << (QList() << detail) << (QList() << property); property.setValue(QStringLiteral("8")); detail.setPriority(QOrganizerItemPriority::ExtremelyLowPriority); QTest::newRow("priority 8") << (QList() << detail) << (QList() << property); property.setValue(QStringLiteral("9")); detail.setPriority(QOrganizerItemPriority::LowestPriority); QTest::newRow("priority 9") << (QList() << detail) << (QList() << property); } } void tst_QVersitOrganizerExporter::testExportTodoDetails() { QFETCH(QList, details); QFETCH(QList, expectedProperties); QVersitOrganizerExporter exporter; QOrganizerTodo item; foreach (QOrganizerItemDetail detail, details) { item.saveDetail(&detail); } QVERIFY(exporter.exportItems(QList() << item)); QVERIFY(exporter.errorMap().isEmpty()); QVersitDocument document = exporter.document(); QList subDocuments = document.subDocuments(); QCOMPARE(subDocuments.size(), 1); foreach(const QVersitProperty& expectedProperty, expectedProperties) { QList actualProperties = findPropertiesByName(subDocuments.first(), expectedProperty.name()); if (expectedProperty.valueType() == QVersitProperty::VersitDocumentType) { QCOMPARE(subDocuments.first().subDocuments().size(), 1); QVersitDocument actualVAlarmDocument = subDocuments.first().subDocuments().first(); QVersitDocument expectedVAlarmDocument = expectedProperty.variantValue().value(); foreach (const QVersitProperty &valarmProp, expectedVAlarmDocument.properties()) { if (!actualVAlarmDocument.properties().contains(valarmProp)) { qDebug() << "Actual:" << actualVAlarmDocument.properties(); qDebug() << "Expected to find:" << valarmProp; QVERIFY(false); } } } else if (!actualProperties.contains(expectedProperty)) { qDebug() << "Actual:" << actualProperties; qDebug() << "Expected to find:" << expectedProperty; QVERIFY(false); } } } void tst_QVersitOrganizerExporter::testExportTodoDetails_data() { QTest::addColumn >("details"); QTest::addColumn >("expectedProperties"); { QList properties; QVersitProperty dtstart; dtstart.setName(QStringLiteral("DTSTART")); dtstart.setValue(QStringLiteral("20100102")); dtstart.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DATE")); properties << dtstart; QVersitProperty due; due.setName(QStringLiteral("DUE")); due.setValue(QStringLiteral("20100103")); due.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DATE")); properties << due; QOrganizerTodoTime todoTime; todoTime.setStartDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 5))); todoTime.setDueDateTime(QDateTime(QDate(2010, 1, 3), QTime(3, 4, 6))); todoTime.setAllDay(true); QTest::newRow("all day todo") << (QList() << todoTime) << properties; } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QMultiHash parameters; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("AUDIO")); valarmProperties << property; property.setName(QStringLiteral("ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); parameters.insert(QStringLiteral("RELATED"), QStringLiteral("END")); property.setParameters(parameters); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.clear(); valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemAudibleReminder audibleReminder; audibleReminder.setDataUrl(QUrl("http://qt.nokia.com")); audibleReminder.setRepetition(3, 5); audibleReminder.setSecondsBeforeStart(90); QTest::newRow("audible reminder") << (QList() << audibleReminder) << (QList() << property); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QMultiHash parameters; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("DISPLAY")); valarmProperties << property; property.setName(QStringLiteral("x-QTPROJECT-ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); parameters.insert(QStringLiteral("RELATED"), QStringLiteral("END")); property.setParameters(parameters); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.clear(); property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("Test visual reminder")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemVisualReminder visualReminder; visualReminder.setDataUrl(QUrl("http://qt.nokia.com")); visualReminder.setRepetition(3, 5); visualReminder.setSecondsBeforeStart(90); visualReminder.setMessage(QStringLiteral("Test visual reminder")); QTest::newRow("visual reminder") << (QList() << visualReminder) << (QList() << property); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QMultiHash parameters; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("DISPLAY")); valarmProperties << property; property.setName(QStringLiteral("x-QTPROJECT-ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); parameters.insert(QStringLiteral("RELATED"), QStringLiteral("END")); property.setParameters(parameters); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.clear(); property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemVisualReminder visualReminder; visualReminder.setDataUrl(QUrl("http://qt.nokia.com")); visualReminder.setRepetition(3, 5); visualReminder.setSecondsBeforeStart(90); QTest::newRow("visual reminder: no message") << (QList() << visualReminder) << (QList() << property); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QMultiHash parameters; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("EMAIL")); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); parameters.insert(QStringLiteral("RELATED"), QStringLiteral("END")); property.setParameters(parameters); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.clear(); property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("Test email body")); valarmProperties << property; property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("Test email subject")); valarmProperties << property; property.setName(QStringLiteral("ATTENDEE")); property.setValue(QStringLiteral("First email recipient")); valarmProperties << property; property.setValue(QStringLiteral("Second email recipient")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemEmailReminder emailReminder; QVariantList attachments; emailReminder.setRepetition(3, 5); emailReminder.setSecondsBeforeStart(90); emailReminder.setRecipients((QStringList() << QStringLiteral("First email recipient") << QStringLiteral("Second email recipient"))); emailReminder.setContents(QStringLiteral("Test email subject"), QStringLiteral("Test email body"), attachments); QTest::newRow("email reminder") << (QList() << emailReminder) << (QList() << property); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QMultiHash parameters; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("EMAIL")); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); parameters.insert(QStringLiteral("RELATED"), QStringLiteral("END")); property.setParameters(parameters); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.clear(); property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("")); valarmProperties << property; property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("")); valarmProperties << property; property.setName(QStringLiteral("ATTENDEE")); property.setValue(QStringLiteral("First email recipient")); valarmProperties << property; property.setValue(QStringLiteral("Second email recipient")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemEmailReminder emailReminder; QVariantList attachments; emailReminder.setRepetition(3, 5); emailReminder.setSecondsBeforeStart(90); emailReminder.setRecipients((QStringList() << QStringLiteral("First email recipient") << QStringLiteral("Second email recipient"))); emailReminder.setValue(QOrganizerItemEmailReminder::FieldAttachments, attachments); QTest::newRow("email reminder: no subject, no body") << (QList() << emailReminder) << (QList() << property); } { QVersitProperty property; property.setName(QStringLiteral("STATUS")); property.setValue(QStringLiteral("COMPLETED")); QOrganizerTodoProgress progress; progress.setStatus(QOrganizerTodoProgress::StatusComplete); QTest::newRow("status completed") << (QList() << progress) << (QList() << property); property.setValue(QStringLiteral("NEEDS-ACTION")); progress.setStatus(QOrganizerTodoProgress::StatusNotStarted); QTest::newRow("status needs-action") << (QList() << progress) << (QList() << property); property.setValue(QStringLiteral("IN-PROCESS")); progress.setStatus(QOrganizerTodoProgress::StatusInProgress); QTest::newRow("status in-process") << (QList() << progress) << (QList() << property); } { QVersitProperty property; property.setName(QStringLiteral("PERCENT-COMPLETE")); property.setValue(QStringLiteral("42")); QOrganizerTodoProgress progress; progress.setPercentageComplete(42); QTest::newRow("percent-complete") << (QList() << progress) << (QList() << property); } { QVersitProperty property; property.setName(QStringLiteral("COMPLETED")); property.setValue(QStringLiteral("20100609T161500")); QOrganizerTodoProgress progress; progress.setFinishedDateTime(QDateTime(QDate(2010, 6, 9), QTime(16, 15, 0))); QTest::newRow("completed") << (QList() << progress) << (QList() << property); } { QVersitProperty property; property.setName(QStringLiteral("CATEGORIES")); property.setValue(QStringLiteral("Important Event")); QOrganizerItemTag tag; tag.setTag(QStringLiteral("Important Event")); QTest::newRow("tag 1") << (QList() << tag) << (QList() << property); // Set another one for multiple handling test property.setValue(QStringLiteral("Important Event 2")); tag.setTag(QStringLiteral("Important Event 2")); QTest::newRow("tag 2") << (QList() << tag) << (QList() << property); // Empty tag property.clear(); property.setName("CATEGORIES"); tag.removeValue(QOrganizerItemTag::FieldTag); QTest::newRow("empty tag") << (QList() << tag) << (QList() << property); } { QVersitProperty property = createExtendedDetailPropertyForStringData("name", "data"); QOrganizerItemExtendedDetail extendedDetail = createExtendedDetail("name", QStringLiteral("data")); QTest::newRow("extended detail") << (QList() << extendedDetail) << (QList() << property); } } void tst_QVersitOrganizerExporter::testExtendedDetail() { QFETCH(QString, extendedDetailName); QFETCH(QVariant, extendedDetailData); QFETCH(QString, extendedDetailDataInProperty); QFETCH(bool, extendedDetailPropertyCreated); QOrganizerEvent item; QOrganizerItemExtendedDetail extendedDetail = createExtendedDetail(extendedDetailName, extendedDetailData); item.saveDetail(&extendedDetail); QVersitProperty expectedProperty = createExtendedDetailProperty(extendedDetailName, extendedDetailDataInProperty); QVersitOrganizerExporter exporter; QVERIFY(exporter.exportItems(QList() << item)); QVersitDocument document = exporter.document().subDocuments().first(); QList actualProperties = findPropertiesByName(document, expectedProperty.name()); if (!extendedDetailPropertyCreated) { QCOMPARE(actualProperties.size(), 0); } else { QCOMPARE(actualProperties.size(), 1); if (!actualProperties.contains(expectedProperty)) { qDebug() << "Actual:" << actualProperties; qDebug() << "Expected to find:" << expectedProperty; QVERIFY(false); } } } void tst_QVersitOrganizerExporter::testExtendedDetail_data() { QTest::addColumn("extendedDetailName"); QTest::addColumn("extendedDetailData"); QTest::addColumn("extendedDetailDataInProperty"); QTest::addColumn("extendedDetailPropertyCreated"); QString jsonArrayWith("[\n %1\n]\n"); QString jsonArrayWithString = jsonArrayWith.arg("\"%1\""); { QTest::newRow("string data") << QString("name") << QVariant(QString("data")) << jsonArrayWithString.arg("data") << true; QTest::newRow("string data, empty") << QString("name") << QVariant(QString("")) << jsonArrayWithString.arg("") << true; QTest::newRow("string data, containing reserved characters") << QString("name") << QVariant(QString(",;:\\")) << jsonArrayWithString.arg(",;:\\\\") << true; } { QTest::newRow("double data") << QString("name") << QVariant((double)2.0) << jsonArrayWith.arg("2") << true; QTest::newRow("double data, negative") << QString("name") << QVariant((double)-1.0) << jsonArrayWith.arg("-1") << true; QTest::newRow("double data, multiple digits") << QString("name") << QVariant((double)10.199999999999999) << jsonArrayWith.arg("10.199999999999999") << true; } { QTest::newRow("boolean data") << QString("name") << QVariant(true) << jsonArrayWith.arg("true") << true; } { QTest::newRow("integer data, serialized as number/double") << QString("name") << QVariant((int)2) << jsonArrayWith.arg("2") << true; QTest::newRow("integer data, serialized as number/double") << QString("name") << QVariant((int)-10) << jsonArrayWith.arg("-10") << true; } { QTest::newRow("datetime data (using local time)") << QString("name") << QVariant(QDateTime::fromString("1997-07-16T19:20:30.123+01:00", Qt::ISODate)) << jsonArrayWithString.arg("1997-07-16T19:20:30+01:00") << true; QTest::newRow("datetime data (using UTC)") << QString("name") << QVariant(QDateTime::fromString("1997-07-16T19:20:30.123+01:00", Qt::ISODate).toUTC()) << jsonArrayWithString.arg("1997-07-16T18:20:30Z") << true; QTest::newRow("datetime data (using local time with unspecified timezone)") << QString("name") << QVariant(QDateTime::fromString("1997-07-16T19:20:30", Qt::ISODate)) << jsonArrayWithString.arg("1997-07-16T19:20:30") << true; } { QTest::newRow("list data") << QString("name") << QVariant(QVariantList() << QString("string 1") << QString("string 2")) << QString("[\n [\n \"string 1\",\n \"string 2\"\n ]\n]\n") << true; } { QVariantMap map; map["key 1"] = QString("string 1"); map["key 2"] = QString("string 2"); QTest::newRow("map data") << QString("name") << QVariant(map) << QString("[\n {\n \"key 1\": \"string 1\",\n \"key 2\": \"string 2\"\n }\n]\n") << true; } { QVariantMap map; map["key"] = QVariantList() << (double)1 << (double)2; QTest::newRow("map data, containing a nested list") << QString("name") << QVariant(map) << QString("[\n {\n \"key\": [\n 1,\n 2\n ]\n }\n]\n") << true; } { QTest::newRow("empty string as name") << QString("") << QVariant(QString("data")) << jsonArrayWithString.arg("data") << true; QTest::newRow("name containing reserved characters") << QString(",;:\\") << QVariant(QString("data")) << jsonArrayWithString.arg("data") << true; } { QTest::newRow("empty variant as data") << QString("name") << QVariant() << jsonArrayWith.arg("null") << true; } { QVariantHash hash; hash["key"] = QVariant(QString("string")); QTest::newRow("data type not supported") << QString("name") << QVariant(hash) << QString() << false; } } void tst_QVersitOrganizerExporter::testMultipleExtendedDetails() { QOrganizerEvent item; QOrganizerItemExtendedDetail extendedDetail1 = createExtendedDetail("detailName1", "detailData1"); item.saveDetail(&extendedDetail1); QOrganizerItemExtendedDetail extendedDetail2 = createExtendedDetail("detailName2", "detailData2"); item.saveDetail(&extendedDetail2); QList expectedProperties; expectedProperties << createExtendedDetailPropertyForStringData(extendedDetail1.name(), "detailData1"); expectedProperties << createExtendedDetailPropertyForStringData(extendedDetail2.name(), "detailData2"); QVersitOrganizerExporter exporter; QVERIFY(exporter.exportItems(QList() << item)); QVersitDocument document = exporter.document().subDocuments().first(); QCOMPARE(findPropertiesByName(document, expectedProperties.first().name()).size(), 2); foreach (const QVersitProperty& expectedProperty, expectedProperties) { QList actualProperties = findPropertiesByName(document, expectedProperty.name()); if (!actualProperties.contains(expectedProperty)) { qDebug() << "Actual:" << actualProperties; qDebug() << "Expected to find:" << expectedProperty; QVERIFY(false); } } } QOrganizerItemExtendedDetail tst_QVersitOrganizerExporter::createExtendedDetail( const QString &name, const QVariant &data) { QOrganizerItemExtendedDetail extendedDetail; extendedDetail.setName(name); extendedDetail.setData(data); return extendedDetail; } QVersitProperty tst_QVersitOrganizerExporter::createExtendedDetailProperty( const QString &name, const QVariant &data) { QVersitProperty property; property.setName(QStringLiteral("X-QTPROJECT-EXTENDED-DETAIL")); property.setValueType(QVersitProperty::CompoundType); property.setValue(QStringList() << name << data.toString()); return property; } QVersitProperty tst_QVersitOrganizerExporter::createExtendedDetailPropertyForStringData( const QString &name, const QString &data) { QString jsonArrayWith("[\n %1\n]\n"); QString jsonArrayWithString = jsonArrayWith.arg("\"%1\""); return createExtendedDetailProperty(name, jsonArrayWithString.arg(data)); } QList tst_QVersitOrganizerExporter::findPropertiesByName( const QVersitDocument &document, const QString &propertyName) { QList retval; foreach (const QVersitProperty& property, document.properties()) { if (property.name() == propertyName) retval << property; } return retval; } void tst_QVersitOrganizerExporter::testEmptyItemShouldNotBeExported() { QVersitOrganizerExporter exporter; QList items; QOrganizerEvent ev; QVERIFY(ev.isEmpty()); QOrganizerItem item1; QVERIFY(item1.isEmpty()); items << static_cast(ev)< #include #include #include #include QTORGANIZER_USE_NAMESPACE QTVERSIT_USE_NAMESPACE QTVERSITORGANIZER_USE_NAMESPACE class tst_QVersitOrganizerExporter : public QObject { Q_OBJECT private slots: void testExport(); void testExport_data(); void testExportError(); void testExportError_data(); void testExportEventDetails(); void testExportEventDetails_data(); void testExportTodoDetails(); void testExportTodoDetails_data(); void testEmptyItemShouldNotBeExported(); void testExtendedDetail(); void testExtendedDetail_data(); void testMultipleExtendedDetails(); QOrganizerItemExtendedDetail createExtendedDetail( const QString &name, const QVariant &data); QVersitProperty createExtendedDetailProperty( const QString &name, const QVariant &data); QVersitProperty createExtendedDetailPropertyForStringData( const QString &name, const QString &data); static QList findPropertiesByName(const QVersitDocument &document, const QString &propertyName); }; #endif tests/auto/versitorganizer/qversitorganizerimporter/000077500000000000000000000000001233466112000236235ustar00rootroot00000000000000tests/auto/versitorganizer/qversitorganizerimporter/qversitorganizerimporter.pro000066400000000000000000000003011233466112000315370ustar00rootroot00000000000000include(../../auto.pri) QT += organizer versit versitorganizer HEADERS += tst_qversitorganizerimporter.h SOURCES += tst_qversitorganizerimporter.cpp DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 tests/auto/versitorganizer/qversitorganizerimporter/tst_qversitorganizerimporter.cpp000066400000000000000000002654551233466112000324420ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ //TESTED_COMPONENT=src/versit #include "tst_qversitorganizerimporter.h" #include QTORGANIZER_USE_NAMESPACE QTVERSITORGANIZER_USE_NAMESPACE Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(QList) void tst_QVersitOrganizerImporter::testImport() { QFETCH(QVersitDocument, document); QFETCH(QList, expectedItems); QVersitOrganizerImporter importer; QVERIFY(importer.importDocument(document)); QVERIFY(importer.errorMap().isEmpty()); QList items = importer.items(); if (items != expectedItems) { qDebug() << "Actual:" << items; qDebug() << "Expected:" << expectedItems; QCOMPARE(items, expectedItems); } } void tst_QVersitOrganizerImporter::testImport_data() { QTest::addColumn("document"); QTest::addColumn >("expectedItems"); { QVersitDocument document(QVersitDocument::ICalendar20Type); document.setComponentType(QStringLiteral("VCALENDAR")); QVersitProperty property; property.setName(QStringLiteral("PRODID")); property.setValue(QStringLiteral("-//hacksw/handcal//NONSGML v1.0//EN")); document.addProperty(property); QVersitDocument nested(QVersitDocument::ICalendar20Type); nested.setComponentType(QStringLiteral("VEVENT")); property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("Bastille Day Party")); nested.addProperty(property); property.setName(QStringLiteral("DTSTART")); property.setValue(QStringLiteral("19970714T170000Z")); nested.addProperty(property); property.setName(QStringLiteral("DTEND")); property.setValue(QStringLiteral("19970715T035959Z")); nested.addProperty(property); document.addSubDocument(nested); nested.clear(); nested.setType(QVersitDocument::ICalendar20Type); nested.setComponentType(QStringLiteral("VTODO")); property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("Take out the garbage")); nested.addProperty(property); property.setName(QStringLiteral("DTSTART")); property.setValue(QStringLiteral("20100609T080000")); nested.addProperty(property); property.setName(QStringLiteral("DUE")); property.setValue(QStringLiteral("20100610T080000")); nested.addProperty(property); document.addSubDocument(nested); nested.clear(); nested.setType(QVersitDocument::ICalendar20Type); nested.setComponentType(QStringLiteral("VJOURNAL")); property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("Trip to Thailand")); nested.addProperty(property); property.setName(QStringLiteral("DTSTART")); property.setValue(QStringLiteral("20100615T112300")); nested.addProperty(property); document.addSubDocument(nested); QOrganizerEvent event; event.setDisplayLabel(QStringLiteral("Bastille Day Party")); event.setStartDateTime(QDateTime(QDate(1997, 7, 14), QTime(17, 0, 0), Qt::UTC)); event.setEndDateTime(QDateTime(QDate(1997, 7, 15), QTime(3, 59, 59), Qt::UTC)); QOrganizerTodo todo; todo.setDisplayLabel(QStringLiteral("Take out the garbage")); todo.setStartDateTime(QDateTime(QDate(2010, 6, 9), QTime(8, 0, 0))); todo.setDueDateTime(QDateTime(QDate(2010, 6, 10), QTime(8, 0, 0))); QOrganizerJournal journal; journal.setDisplayLabel(QStringLiteral("Trip to Thailand")); journal.setDateTime(QDateTime(QDate(2010, 6, 15), QTime(11, 23, 0))); QList items; items << static_cast(event); items << static_cast(todo); items << static_cast(journal); QTest::newRow("sample event and todo") << document << items; } { QVersitDocument document(QVersitDocument::ICalendar20Type); document.setComponentType(QStringLiteral("VCALENDAR")); QVersitDocument nested(QVersitDocument::ICalendar20Type); nested.setComponentType(QStringLiteral("VEVENT")); QVersitProperty property; property.setName(QStringLiteral("UID")); property.setValue(QStringLiteral("1234")); nested.addProperty(property); property.setName(QStringLiteral("RECURRENCE-ID")); property.setValue(QStringLiteral("20100608")); nested.addProperty(property); document.addSubDocument(nested); // An event occurrence with OriginalDate and Guid set QOrganizerEventOccurrence event; event.setGuid(QStringLiteral("1234")); event.setOriginalDate(QDate(2010, 6, 8)); QList items; items << static_cast(event); QTest::newRow("event occurrence exception") << document << items; } { QVersitDocument document(QVersitDocument::ICalendar20Type); document.setComponentType(QStringLiteral("VCALENDAR")); QVersitDocument nested(QVersitDocument::ICalendar20Type); nested.setComponentType(QStringLiteral("VTODO")); QVersitProperty property; property.setName(QStringLiteral("RRULE")); property.setValue(QStringLiteral("FREQ=WEEKLY")); nested.addProperty(property); document.addSubDocument(nested); QOrganizerTodo todo; QOrganizerRecurrenceRule rrule; rrule.setFrequency(QOrganizerRecurrenceRule::Weekly); todo.setRecurrenceRules(QSet() << rrule); QList items; items << static_cast(todo); QTest::newRow("todo recurrence rule") << document << items; } } void tst_QVersitOrganizerImporter::testImportEventProperties() { QFETCH(QList, properties); QFETCH(QList, expectedDetails); QVersitDocument document(QVersitDocument::ICalendar20Type); document.setComponentType(QStringLiteral("VCALENDAR")); QVersitDocument nested(QVersitDocument::ICalendar20Type); nested.setComponentType(QStringLiteral("VEVENT")); foreach (const QVersitProperty& property, properties) { if (property.valueType() == QVersitProperty::VersitDocumentType) nested.addSubDocument(property.value()); else nested.addProperty(property); } document.addSubDocument(nested); QVersitOrganizerImporter importer; QVERIFY(importer.importDocument(document)); QVERIFY(importer.errorMap().isEmpty()); QList items = importer.items(); QCOMPARE(items.size(), 1); foreach (const QOrganizerItemDetail& expectedDetail, expectedDetails) { QList actualDetails = items.first().details(expectedDetail.type()); if (!actualDetails.contains(expectedDetail)) { qDebug() << "Actual:" << actualDetails; qDebug() << "Expected to find:" << expectedDetail; QVERIFY(false); } } } void tst_QVersitOrganizerImporter::testImportEventProperties_data() { QTest::addColumn >("properties"); QTest::addColumn >("expectedDetails"); { QVersitProperty property; property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("jabberwocky")); QOrganizerItemDisplayLabel displayLabel; displayLabel.setLabel(QStringLiteral("jabberwocky")); QTest::newRow("one summary") << (QList() << property) << (QList() << displayLabel); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("AUDIO")); valarmProperties << property; property.setName(QStringLiteral("ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemAudibleReminder audibleReminder; audibleReminder.setDataUrl(QUrl("http://qt.nokia.com")); audibleReminder.setRepetition(3, 5); audibleReminder.setSecondsBeforeStart(90); QTest::newRow("audible reminder") << (QList() << property) << (QList() << audibleReminder); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("AUDIO")); valarmProperties << property; property.setName(QStringLiteral("ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); property.setValue(QStringLiteral("-PT90S")); property.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DURATION")); valarmProperties << property; property.clear(); valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemAudibleReminder audibleReminder; audibleReminder.setDataUrl(QUrl("http://qt.nokia.com")); audibleReminder.setRepetition(3, 5); audibleReminder.setSecondsBeforeStart(90); QTest::newRow("audible reminder with trigger VALUE = DURATION") << (QList() << property) << (QList() << audibleReminder); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QVersitProperty dtstart; dtstart.setName(QStringLiteral("DTSTART")); dtstart.setValue(QStringLiteral("20100102T030405")); QVersitProperty dtend; dtend.setName(QStringLiteral("DTEND")); dtend.setValue(QStringLiteral("20100102T030415")); QOrganizerEventTime etr; etr.setStartDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 5))); etr.setEndDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 15))); property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("AUDIO")); valarmProperties << property; property.setName(QStringLiteral("ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); property.setValue(QStringLiteral("-PT90S")); property.insertParameter(QStringLiteral("RELATED"), QStringLiteral("END")); valarmProperties << property; property.clear(); valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemAudibleReminder audibleReminder; audibleReminder.setDataUrl(QUrl("http://qt.nokia.com")); audibleReminder.setRepetition(3, 5); audibleReminder.setSecondsBeforeStart(80); QTest::newRow("audible reminder with relative (END) trigger") << (QList() << dtstart << dtend << property) << (QList() << etr << audibleReminder); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QVersitProperty dtstart; dtstart.setName(QStringLiteral("DTSTART")); dtstart.setValue(QStringLiteral("20100102T030405")); QVersitProperty dtend; dtend.setName(QStringLiteral("DTEND")); dtend.setValue(QStringLiteral("20100102T030415")); QOrganizerEventTime etr; etr.setStartDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 5))); etr.setEndDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 15))); property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("AUDIO")); valarmProperties << property; property.setName(QStringLiteral("ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); property.setValue(QStringLiteral("-PT90S")); property.insertParameter(QStringLiteral("RELATED"), QStringLiteral("START")); valarmProperties << property; property.clear(); valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemAudibleReminder audibleReminder; audibleReminder.setDataUrl(QUrl("http://qt.nokia.com")); audibleReminder.setRepetition(3, 5); audibleReminder.setSecondsBeforeStart(90); QTest::newRow("audible reminder with relative (START) trigger") << (QList() << dtstart << dtend << property) << (QList() << etr << audibleReminder); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QVersitProperty dtstart; dtstart.setName(QStringLiteral("DTSTART")); dtstart.setValue(QStringLiteral("20100102T030405")); QVersitProperty dtend; dtend.setName(QStringLiteral("DTEND")); dtend.setValue(QStringLiteral("20100102T030415")); QOrganizerEventTime etr; etr.setStartDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 5))); etr.setEndDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 15))); property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("AUDIO")); valarmProperties << property; property.setName(QStringLiteral("ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); property.setValue(QStringLiteral("20100102T030305")); property.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DATE-TIME")); valarmProperties << property; property.clear(); valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemAudibleReminder audibleReminder; audibleReminder.setDataUrl(QUrl("http://qt.nokia.com")); audibleReminder.setRepetition(3, 5); audibleReminder.setSecondsBeforeStart(60); QTest::newRow("audible reminder with absolute trigger") << (QList() << dtstart << dtend << property) << (QList() << etr << audibleReminder); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("DISPLAY")); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("Test visual reminder")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemVisualReminder visualReminder; visualReminder.setMessage(QStringLiteral("Test visual reminder")); visualReminder.setRepetition(3, 5); visualReminder.setSecondsBeforeStart(90); QTest::newRow("visual reminder") << (QList() << property) << (QList() << visualReminder); } { QVersitProperty property; QVersitDocument valarmDocument; QList valarmProperties; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("EMAIL")); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("Test email body")); valarmProperties << property; property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("Test email subject")); valarmProperties << property; property.setName(QStringLiteral("ATTENDEE")); property.setValue(QStringLiteral("First email recipient")); valarmProperties << property; property.setValue(QStringLiteral("Second email recipient")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemEmailReminder emailReminder; emailReminder.setContents(QStringLiteral("Test email subject"), QStringLiteral("Test email body"), QVariantList()); emailReminder.setRepetition(3, 5); emailReminder.setSecondsBeforeStart(90); emailReminder.setRecipients(QStringList() << QStringLiteral("First email recipient") << QStringLiteral("Second email recipient")); QTest::newRow("email reminder") << (QList() << property) << (QList() << emailReminder); } { QList expectedDetails; QList propertiesToImport; { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("AUDIO")); valarmProperties << property; property.setName(QStringLiteral("ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemAudibleReminder audibleReminder; audibleReminder.setDataUrl(QUrl("http://qt.nokia.com")); audibleReminder.setRepetition(3, 5); audibleReminder.setSecondsBeforeStart(90); propertiesToImport << property; expectedDetails << audibleReminder; } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("DISPLAY")); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("Test visual reminder")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemVisualReminder visualReminder; visualReminder.setMessage(QStringLiteral("Test visual reminder")); visualReminder.setRepetition(3, 5); visualReminder.setSecondsBeforeStart(90); propertiesToImport << property; expectedDetails << visualReminder; } { QVersitProperty property; QVersitDocument valarmDocument; QList valarmProperties; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("EMAIL")); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("Test email body")); valarmProperties << property; property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("Test email subject")); valarmProperties << property; property.setName(QStringLiteral("ATTENDEE")); property.setValue(QStringLiteral("First email recipient")); valarmProperties << property; property.setValue(QStringLiteral("Second email recipient")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemEmailReminder emailReminder; emailReminder.setContents(QStringLiteral("Test email subject"), QStringLiteral("Test email body"), QVariantList()); emailReminder.setRepetition(3, 5); emailReminder.setSecondsBeforeStart(90); emailReminder.setRecipients(QStringList() << QStringLiteral("First email recipient") << QStringLiteral("Second email recipient")); propertiesToImport << property; expectedDetails << emailReminder; } QTest::newRow("multiple reminders") << propertiesToImport << expectedDetails; } { QVersitProperty property; property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("Twinkle, twinkle, little bat! How I wonder what you're at.")); QOrganizerItemDescription description; description.setDescription(QStringLiteral("Twinkle, twinkle, little bat! How I wonder what you're at.")); QTest::newRow("one description") << (QList() << property) << (QList() << description); } { QVersitProperty property; property.setName(QStringLiteral("CATEGORIES")); property.setValue(QStringLiteral("Important Event")); QOrganizerItemTag tag; tag.setTag(QStringLiteral("Important Event")); QTest::newRow("tag 1") << (QList() << property) << (QList() << tag); // Set another one for multiple handling test property.setValue(QStringLiteral("Important Event 2")); tag.setTag(QStringLiteral("Important Event 2")); QTest::newRow("tag 2") << (QList() << property) << (QList() << tag); // Empty tag property.clear(); property.setName("CATEGORIES"); tag.removeValue(QOrganizerItemTag::FieldTag); QTest::newRow("empty tag") << (QList() << property) << (QList() ); // Detail side should be empty } { QVersitProperty property = createExtendedDetailPropertyForStringData("name", "data"); QOrganizerItemExtendedDetail extendedDetail = createExtendedDetail("name", "data"); QTest::newRow("extended detail") << (QList() << property) << (QList() << extendedDetail); } { QVersitProperty property; // Proper property. property.setName(QStringLiteral("X-QTPROJECT-VERSION")); property.setValueType(QVersitProperty::CompoundType); property.setValue(QStringList() << QStringLiteral("4711") << QStringLiteral("134f23dbb2")); QOrganizerItemVersion version; version.setVersion(4711); version.setExtendedVersion(QByteArray("134f23dbb2")); QTest::newRow("version") << (QList() << property) << (QList() << version); } { QVersitProperty property; // Extended version empty. property.setName(QStringLiteral("X-QTPROJECT-VERSION")); property.setValueType(QVersitProperty::CompoundType); property.setValue(QStringList() << QStringLiteral("4711") << QStringLiteral("")); QOrganizerItemVersion version; version.setVersion(4711); QTest::newRow("version") << (QList() << property) << (QList() << version); } { QVersitProperty property; // No extended version. property.setName(QStringLiteral("X-QTPROJECT-VERSION")); property.setValueType(QVersitProperty::CompoundType); property.setValue(QStringList() << QStringLiteral("4711")); QOrganizerItemVersion version; version.setVersion(4711); QTest::newRow("version") << (QList() << property) << (QList() << version); } { QVersitProperty property; // Version non number. property.setName(QStringLiteral("X-QTPROJECT-VERSION")); property.setValueType(QVersitProperty::CompoundType); property.setValue(QStringList() << QStringLiteral("2someletters")); QTest::newRow("version") << (QList() << property) << (QList()); } { QVersitProperty property; // Version emptry string. property.setName(QStringLiteral("X-QTPROJECT-VERSION")); property.setValueType(QVersitProperty::CompoundType); property.setValue(QStringList() << QStringLiteral("")); QTest::newRow("version") << (QList() << property) << (QList()); } { QVersitProperty property; // Empty property. property.setName(QStringLiteral("X-QTPROJECT-VERSION")); property.setValueType(QVersitProperty::CompoundType); QTest::newRow("version") << (QList() << property) << (QList()); } { QVersitProperty property1; property1.setName(QStringLiteral("COMMENT")); property1.setValue(QStringLiteral("Comment 1")); QVersitProperty property2; property2.setName(QStringLiteral("COMMENT")); property2.setValue(QStringLiteral("Comment 2")); QOrganizerItemComment comment1; comment1.setComment(QStringLiteral("Comment 1")); QOrganizerItemComment comment2; comment2.setComment(QStringLiteral("Comment 2")); QTest::newRow("two comments") << (QList() << property1 << property2) << (QList() << comment1 << comment2); } { QVersitProperty property; property.setName(QStringLiteral("UID")); property.setValue(QStringLiteral("1234567")); QOrganizerItemGuid guid; guid.setGuid(QStringLiteral("1234567")); QTest::newRow("guid") << (QList() << property) << (QList() << guid); } { QList properties; QVersitProperty dtstart; dtstart.setName(QStringLiteral("DTSTART")); dtstart.setValue(QStringLiteral("20100102T030405")); properties << dtstart; QVersitProperty dtend; dtend.setName(QStringLiteral("DTEND")); dtend.setValue(QStringLiteral("20100102T030406")); properties << dtend; QOrganizerEventTime etr; etr.setStartDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 5))); etr.setEndDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 6))); QTest::newRow("dtstart and dtend") << properties << (QList() << etr); dtend.setValue(QStringLiteral("20100102T235959")); properties.prepend(dtend); QTest::newRow("multiple dtstart and dtend") << properties << (QList() << etr); // last takes precedence } { QList properties; QVersitProperty dtstart; dtstart.setName(QStringLiteral("DTSTART")); dtstart.setValue(QStringLiteral("20100102")); dtstart.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DATE")); properties << dtstart; QVersitProperty dtend; dtend.setName(QStringLiteral("DTEND")); // Note: in iCalendar, the end date is exclusive while in Qt Organizer, it is inclusive. // Hence, this is an event that occurs all day on 2 January (not including 3 January) dtend.setValue(QStringLiteral("20100103")); dtend.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DATE")); properties << dtend; QOrganizerEventTime etr; // The time portion must be valid but is ignored. This test cheats a bit because it knows // the implementation sets it to 00:00:00 etr.setStartDateTime(QDateTime(QDate(2010, 1, 2), QTime(0, 0, 0))); etr.setEndDateTime(QDateTime(QDate(2010, 1, 2), QTime(0, 0, 0))); etr.setAllDay(true); QTest::newRow("all day event") << properties << (QList() << etr); } { QVersitProperty dtstart; dtstart.setName(QStringLiteral("DTSTART")); dtstart.setValue(QStringLiteral("20091231T000000")); // equivalent to 2010, day zero QVersitProperty duration; duration.setName(QStringLiteral("DURATION")); duration.setValue(QStringLiteral("P15DT5H7M20S")); QOrganizerEventTime etr; etr.setStartDateTime(QDateTime(QDate(2009, 12, 31), QTime(0, 0, 0))); etr.setEndDateTime(QDateTime(QDate(2010, 1, 15), QTime(5, 7, 20))); QTest::newRow("dtstart and duration: days hours minutes seconds") << (QList() << dtstart << duration) << (QList() << etr); QTest::newRow("duration and dtstart") << (QList() << duration << dtstart) << (QList() << etr); duration.setValue(QStringLiteral("PT5H7M20S")); etr.setEndDateTime(QDateTime(QDate(2009, 12, 31), QTime(5, 7, 20))); QTest::newRow("dtstart and duration: hours minutes seconds") << (QList() << dtstart << duration) << (QList() << etr); duration.setValue(QStringLiteral("PT7M20S")); etr.setEndDateTime(QDateTime(QDate(2009, 12, 31), QTime(0, 7, 20))); QTest::newRow("dtstart and duration: minutes seconds") << (QList() << dtstart << duration) << (QList() << etr); duration.setValue(QStringLiteral("PT20S")); etr.setEndDateTime(QDateTime(QDate(2009, 12, 31), QTime(0, 0, 20))); QTest::newRow("dtstart and duration: seconds") << (QList() << dtstart << duration) << (QList() << etr); duration.setValue(QStringLiteral("P15DT5H7M")); etr.setEndDateTime(QDateTime(QDate(2010, 1, 15), QTime(5, 7, 0))); QTest::newRow("dtstart and duration: days hours minutes") << (QList() << dtstart << duration) << (QList() << etr); duration.setValue(QStringLiteral("P15DT5H")); etr.setEndDateTime(QDateTime(QDate(2010, 1, 15), QTime(5, 0, 0))); QTest::newRow("dtstart and duration: days hours") << (QList() << dtstart << duration) << (QList() << etr); duration.setValue(QStringLiteral("P15D")); etr.setEndDateTime(QDateTime(QDate(2010, 1, 15), QTime(0, 0, 0))); QTest::newRow("dtstart and duration: days") << (QList() << dtstart << duration) << (QList() << etr); duration.setValue(QStringLiteral("P7W")); etr.setEndDateTime(QDateTime(QDate(2010, 2, 18), QTime(0, 0, 0))); QTest::newRow("dtstart and duration: weeks") << (QList() << dtstart << duration) << (QList() << etr); dtstart.setValue(QStringLiteral("20100115T050720")); duration.setValue(QStringLiteral("-P15DT5H7M20S")); etr.setStartDateTime(QDateTime(QDate(2010, 1, 15), QTime(5, 7, 20))); etr.setEndDateTime(QDateTime(QDate(2009, 12, 31), QTime(0, 0, 0))); QTest::newRow("dtstart and duration: negative") << (QList() << dtstart << duration) << (QList() << etr); } { QList properties; QVersitProperty created; created.setName(QStringLiteral("CREATED")); created.setValue(QStringLiteral("20100102T030405Z")); properties << created; QVersitProperty modified; modified.setName(QStringLiteral("LAST-MODIFIED")); modified.setValue(QStringLiteral("20100102T030406Z")); properties << modified; QOrganizerItemTimestamp timestamp; timestamp.setCreated(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 5), Qt::UTC)); timestamp.setLastModified(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 6), Qt::UTC)); QTest::newRow("created and last modified") << properties << (QList() << timestamp); } { QVersitProperty rrule; rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=DAILY")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Daily); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule daily") << (QList() << rrule) << (QList() << recurrence); rrule.setValue(QStringLiteral("FREQ=WEEKLY")); recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Weekly); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule weekly") << (QList() << rrule) << (QList() << recurrence); rrule.setValue(QStringLiteral("FREQ=MONTHLY")); recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Monthly); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule monthly") << (QList() << rrule) << (QList() << recurrence); rrule.setValue(QStringLiteral("FREQ=YEARLY")); recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Yearly); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule yearly") << (QList() << rrule) << (QList() << recurrence); rrule.setValue(QStringLiteral("FREQ=MONTHLY;INTERVAL=2;BYDAY=TU")); recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Monthly); recurrenceRule.setInterval(2); recurrenceRule.setDaysOfWeek(QSet() << Qt::Tuesday); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule monthly") << (QList() << rrule) << (QList() << recurrence); } { QVersitProperty rrule; rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=MONTHLY;BYMONTHDAY=1,-3")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Monthly); recurrenceRule.setDaysOfMonth(QSet() << 1 << -3); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule bymonthday") << (QList() << rrule) << (QList() << recurrence); } { QVersitProperty rrule; rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=YEARLY;BYWEEKNO=1,-3")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Yearly); recurrenceRule.setWeeksOfYear(QSet() << 1 << -3); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule byweekno") << (QList() << rrule) << (QList() << recurrence); } { QVersitProperty rrule; rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=YEARLY;BYMONTH=1,10")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Yearly); recurrenceRule.setMonthsOfYear(QSet() << QOrganizerRecurrenceRule::January << QOrganizerRecurrenceRule::October); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule bymonth") << (QList() << rrule) << (QList() << recurrence); } { QVersitProperty rrule; rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=YEARLY;BYYEARDAY=1,366,-1")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Yearly); recurrenceRule.setDaysOfYear(QSet() << 1 << 366 << -1); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule byyearday") << (QList() << rrule) << (QList() << recurrence); } { QVersitProperty rrule; rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=WEEKLY;COUNT=4;INTERVAL=2;BYDAY=TU,SU;WKST=SU")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Weekly); recurrenceRule.setInterval(2); recurrenceRule.setLimit(4); recurrenceRule.setDaysOfWeek(QSet() << Qt::Tuesday << Qt::Sunday); recurrenceRule.setFirstDayOfWeek(Qt::Sunday); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule wkst") << (QList() << rrule) << (QList() << recurrence); } { QVersitProperty rrule; rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Monthly); recurrenceRule.setDaysOfWeek(QSet() << Qt::Monday << Qt::Tuesday << Qt::Wednesday << Qt::Thursday << Qt::Friday); recurrenceRule.setPositions(QSet() << -1); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule bysetpos") << (QList() << rrule) << (QList() << recurrence); } { QVersitProperty rrule; rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=YEARLY;BYMONTH=4;BYDAY=1SU")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Yearly); recurrenceRule.setMonthsOfYear(QSet() << QOrganizerRecurrenceRule::April); recurrenceRule.setDaysOfWeek(QSet() << Qt::Sunday); recurrenceRule.setPositions(QSet() << 1); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule byday with position") << (QList() << rrule) << (QList() << recurrence); } { QVersitProperty rrule; rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=DAILY;UNTIL=20000131;BYMONTH=1")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Daily); recurrenceRule.setLimit(QDate(2000, 1, 31)); recurrenceRule.setMonthsOfYear(QSet() << QOrganizerRecurrenceRule::January); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule until") << (QList() << rrule) << (QList() << recurrence); rrule.setValue(QStringLiteral("FREQ=DAILY;UNTIL=20000131T101112;BYMONTH=1")); QTest::newRow("rrule until with time") << (QList() << rrule) << (QList() << recurrence); } { QVersitProperty rrule; rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=DAILY;COUNT=5;BYMONTH=1")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Daily); recurrenceRule.setLimit(5); recurrenceRule.setMonthsOfYear(QSet() << QOrganizerRecurrenceRule::January); recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule count") << (QList() << rrule) << (QList() << recurrence); } { QVersitProperty rrule; rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=WEEKLY;INTERVAL=0")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Weekly); recurrenceRule.setInterval(1); // default interval when invalid interval is found recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule invalid interval1") << (QList() << rrule) << (QList() << recurrence); rrule.setValue(QStringLiteral("FREQ=WEEKLY;INTERVAL=bad")); QTest::newRow("rrule invalid interval2") << (QList() << rrule) << (QList() << recurrence); rrule.setValue(QStringLiteral("FREQ=WEEKLY;INTERVAL=")); QTest::newRow("rrule invalid interval3") << (QList() << rrule) << (QList() << recurrence); } { QVersitProperty rrule; rrule.setName(QStringLiteral("RRULE")); rrule.setValue(QStringLiteral("FREQ=WEEKLY;COUNT=-2")); QOrganizerItemRecurrence recurrence; QOrganizerRecurrenceRule recurrenceRule; recurrenceRule.setFrequency(QOrganizerRecurrenceRule::Weekly); recurrenceRule.setLimit(-1); // default count when invalid count is found recurrence.setRecurrenceRules(QSet() << recurrenceRule); QTest::newRow("rrule invalid count1") << (QList() << rrule) << (QList() << recurrence); rrule.setValue(QStringLiteral("FREQ=WEEKLY;COUNT=bad")); QTest::newRow("rrule invalid count2") << (QList() << rrule) << (QList() << recurrence); rrule.setValue(QStringLiteral("FREQ=WEEKLY;COUNT=")); QTest::newRow("rrule invalid count3") << (QList() << rrule) << (QList() << recurrence); } { QVersitProperty dtstart; dtstart.setName(QStringLiteral("DTSTART")); dtstart.setValue(QStringLiteral("19970304T110000")); QVersitProperty dtend; dtend.setName(QStringLiteral("DTEND")); dtend.setValue(QStringLiteral("19970304T110000")); QVersitProperty rdate; rdate.setName(QStringLiteral("RDATE")); rdate.setValue(QStringLiteral("19970304")); QOrganizerEventTime etr; etr.setStartDateTime(QDateTime(QDate(1997, 3, 4), QTime(11, 0, 0))); etr.setEndDateTime(QDateTime(QDate(1997, 3, 4), QTime(11, 0, 0))); QOrganizerItemRecurrence recurrence; QSet recurrenceDates; recurrenceDates << QDate(1997, 3, 4); recurrence.setRecurrenceDates(recurrenceDates); QTest::newRow("rdate") << (QList() << dtstart << dtend << rdate) << (QList() << etr << recurrence); // XXX times in RDATE are ignored rdate.setValue(QStringLiteral("19970304T133700")); recurrenceDates.clear(); recurrenceDates << QDate(1997, 3, 4); recurrence.setRecurrenceDates(recurrenceDates); QTest::newRow("rdate with time") << (QList() << dtstart << dtend << rdate) << (QList() << etr << recurrence); rdate.setValue(QStringLiteral("19970304,19970504,19970704")); recurrenceDates.clear(); recurrenceDates << QDate(1997, 3, 4) << QDate(1997, 5, 4) << QDate(1997, 7, 4); recurrence.setRecurrenceDates(recurrenceDates); QTest::newRow("multiple rdate") << (QList() << dtstart << dtend << rdate) << (QList() << etr << recurrence); } { QVersitProperty dtstart; dtstart.setName(QStringLiteral("DTSTART")); dtstart.setValue(QStringLiteral("19970304T110000")); QVersitProperty dtend; dtend.setName(QStringLiteral("DTEND")); dtend.setValue(QStringLiteral("19970304T110000")); QVersitProperty rdate; rdate.setName(QStringLiteral("EXDATE")); rdate.setValue(QStringLiteral("19970304")); QOrganizerEventTime etr; etr.setStartDateTime(QDateTime(QDate(1997, 3, 4), QTime(11, 0, 0))); etr.setEndDateTime(QDateTime(QDate(1997, 3, 4), QTime(11, 0, 0))); QOrganizerItemRecurrence recurrence; QSet exceptionDates; exceptionDates << QDate(1997, 3, 4); recurrence.setExceptionDates(exceptionDates); QTest::newRow("exdate") << (QList() << dtstart << dtend << rdate) << (QList() << etr << recurrence); } { QVersitProperty property; property.setName(QStringLiteral("PRIORITY")); property.setValue(QStringLiteral("0")); QOrganizerItemPriority detail; detail.setPriority(QOrganizerItemPriority::UnknownPriority); QTest::newRow("priority 0") << (QList() << property) << (QList() << detail); property.setValue(QStringLiteral("1")); detail.setPriority(QOrganizerItemPriority::HighestPriority); QTest::newRow("priority 1") << (QList() << property) << (QList() << detail); property.setValue(QStringLiteral("2")); detail.setPriority(QOrganizerItemPriority::ExtremelyHighPriority); QTest::newRow("priority 2") << (QList() << property) << (QList() << detail); property.setValue(QStringLiteral("3")); detail.setPriority(QOrganizerItemPriority::VeryHighPriority); QTest::newRow("priority 3") << (QList() << property) << (QList() << detail); property.setValue(QStringLiteral("4")); detail.setPriority(QOrganizerItemPriority::HighPriority); QTest::newRow("priority 4") << (QList() << property) << (QList() << detail); property.setValue(QStringLiteral("5")); detail.setPriority(QOrganizerItemPriority::MediumPriority); QTest::newRow("priority 5") << (QList() << property) << (QList() << detail); property.setValue(QStringLiteral("6")); detail.setPriority(QOrganizerItemPriority::LowPriority); QTest::newRow("priority 6") << (QList() << property) << (QList() << detail); property.setValue(QStringLiteral("7")); detail.setPriority(QOrganizerItemPriority::VeryLowPriority); QTest::newRow("priority 7") << (QList() << property) << (QList() << detail); property.setValue(QStringLiteral("8")); detail.setPriority(QOrganizerItemPriority::ExtremelyLowPriority); QTest::newRow("priority 8") << (QList() << property) << (QList() << detail); property.setValue(QStringLiteral("9")); detail.setPriority(QOrganizerItemPriority::LowestPriority); QTest::newRow("priority 9") << (QList() << property) << (QList() << detail); } } void tst_QVersitOrganizerImporter::testImportTodoProperties() { QFETCH(QList, properties); QFETCH(QList, expectedDetails); QVersitDocument document(QVersitDocument::ICalendar20Type); document.setComponentType(QStringLiteral("VCALENDAR")); QVersitDocument nested(QVersitDocument::ICalendar20Type); nested.setComponentType(QStringLiteral("VTODO")); foreach (const QVersitProperty& property, properties) { if (property.valueType() == QVersitProperty::VersitDocumentType) nested.addSubDocument(property.value()); else nested.addProperty(property); } document.addSubDocument(nested); QVersitOrganizerImporter importer; QVERIFY(importer.importDocument(document)); QVERIFY(importer.errorMap().isEmpty()); QList items = importer.items(); QCOMPARE(items.size(), 1); foreach (const QOrganizerItemDetail& expectedDetail, expectedDetails) { QOrganizerItemDetail actualDetail = items.first().detail(expectedDetail.type()); if (actualDetail != expectedDetail) { qDebug() << "Actual:" << actualDetail; qDebug() << "Expected:" << expectedDetail; QCOMPARE(actualDetail, expectedDetail); } } } void tst_QVersitOrganizerImporter::testImportTodoProperties_data() { QTest::addColumn >("properties"); QTest::addColumn >("expectedDetails"); { QList properties; QVersitProperty dtstart; dtstart.setName(QStringLiteral("DTSTART")); dtstart.setValue(QStringLiteral("20100102")); dtstart.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DATE")); properties << dtstart; QVersitProperty dtdue; dtdue.setName(QStringLiteral("DUE")); dtdue.setValue(QStringLiteral("20100103")); dtdue.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DATE")); properties << dtdue; QOrganizerTodoTime todoTime; // The time portion must be valid but is ignored. This test cheats a bit because it knows // the implementation sets it to 00:00:00 todoTime.setStartDateTime(QDateTime(QDate(2010, 1, 2), QTime(0, 0, 0))); todoTime.setDueDateTime(QDateTime(QDate(2010, 1, 3), QTime(0, 0, 0))); todoTime.setAllDay(true); QTest::newRow("all day todo") << properties << (QList() << todoTime); } { QVersitProperty property; QVersitDocument valarmDocument; QList valarmProperties; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("AUDIO")); valarmProperties << property; property.setName(QStringLiteral("ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemAudibleReminder audibleReminder; audibleReminder.setDataUrl(QUrl("http://qt.nokia.com")); audibleReminder.setRepetition(3, 5); audibleReminder.setSecondsBeforeStart(90); QTest::newRow("audible reminder") << (QList() << property) << (QList() << audibleReminder); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QVersitProperty dtstart; dtstart.setName(QStringLiteral("DTSTART")); dtstart.setValue(QStringLiteral("20100102T030405")); QVersitProperty dtend; dtend.setName(QStringLiteral("DUE")); dtend.setValue(QStringLiteral("20100102T030415")); QOrganizerTodoTime todoTime; todoTime.setStartDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 5))); todoTime.setDueDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 15))); property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("AUDIO")); valarmProperties << property; property.setName(QStringLiteral("ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT10S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); property.setValue(QStringLiteral("-PT90S")); property.insertParameter(QStringLiteral("RELATED"), QStringLiteral("END")); valarmProperties << property; property.clear(); valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemAudibleReminder audibleReminder; audibleReminder.setDataUrl(QUrl("http://qt.nokia.com")); audibleReminder.setRepetition(3, 10); audibleReminder.setSecondsBeforeStart(90); QTest::newRow("audible reminder with relative (END) trigger") << (QList() << dtstart << dtend << property) << (QList() << todoTime << audibleReminder); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QVersitProperty dtstart; dtstart.setName(QStringLiteral("DTSTART")); dtstart.setValue(QStringLiteral("20100102T030405")); QVersitProperty dtend; dtend.setName(QStringLiteral("DUE")); dtend.setValue(QStringLiteral("20100102T030415")); QOrganizerTodoTime todoTime; todoTime.setStartDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 5))); todoTime.setDueDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 15))); property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("AUDIO")); valarmProperties << property; property.setName(QStringLiteral("ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT10S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); property.setValue(QStringLiteral("-PT90S")); property.insertParameter(QStringLiteral("RELATED"), QStringLiteral("START")); valarmProperties << property; property.clear(); valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemAudibleReminder audibleReminder; audibleReminder.setDataUrl(QUrl("http://qt.nokia.com")); audibleReminder.setRepetition(3, 10); audibleReminder.setSecondsBeforeStart(100); QTest::newRow("audible reminder with relative (START) trigger") << (QList() << dtstart << dtend << property) << (QList() << todoTime << audibleReminder); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; QVersitProperty dtstart; dtstart.setName(QStringLiteral("DTSTART")); dtstart.setValue(QStringLiteral("20100102T030405")); QVersitProperty dtend; dtend.setName(QStringLiteral("DUE")); dtend.setValue(QStringLiteral("20100102T030415")); QOrganizerTodoTime todoTime; todoTime.setStartDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 5))); todoTime.setDueDateTime(QDateTime(QDate(2010, 1, 2), QTime(3, 4, 15))); property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("AUDIO")); valarmProperties << property; property.setName(QStringLiteral("ATTACH")); property.setValue(QUrl(QStringLiteral("http://qt.nokia.com"))); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT10S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); property.setValue(QStringLiteral("20100102T030400")); property.insertParameter(QStringLiteral("VALUE"), QStringLiteral("DATE-TIME")); valarmProperties << property; property.clear(); valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemAudibleReminder audibleReminder; audibleReminder.setDataUrl(QUrl("http://qt.nokia.com")); audibleReminder.setRepetition(3, 10); audibleReminder.setSecondsBeforeStart(15); QTest::newRow("audible reminder with absolute trigger") << (QList() << dtstart << dtend << property) << (QList() << todoTime << audibleReminder); } { QVersitProperty property; QVersitDocument valarmDocument(QVersitDocument::ICalendar20Type); QList valarmProperties; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("DISPLAY")); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("Test visual reminder")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemVisualReminder visualReminder; visualReminder.setMessage(QStringLiteral("Test visual reminder")); visualReminder.setRepetition(3, 5); visualReminder.setSecondsBeforeStart(90); QTest::newRow("visual reminder") << (QList() << property) << (QList() << visualReminder); } { QVersitProperty property; QVersitDocument valarmDocument; QList valarmProperties; property.setName(QStringLiteral("ACTION")); property.setValue(QStringLiteral("EMAIL")); valarmProperties << property; property.setName(QStringLiteral("DURATION")); property.setValue(QStringLiteral("PT5S")); valarmProperties << property; property.setName(QStringLiteral("REPEAT")); property.setValue(3); valarmProperties << property; property.setName(QStringLiteral("TRIGGER")); property.setValue(QStringLiteral("-PT90S")); valarmProperties << property; property.setName(QStringLiteral("DESCRIPTION")); property.setValue(QStringLiteral("Test email body")); valarmProperties << property; property.setName(QStringLiteral("SUMMARY")); property.setValue(QStringLiteral("Test email subject")); valarmProperties << property; property.setName(QStringLiteral("ATTENDEE")); property.setValue(QStringLiteral("First email recipient")); valarmProperties << property; property.setValue(QStringLiteral("Second email recipient")); valarmProperties << property; valarmDocument.setComponentType("VALARM"); valarmDocument.setProperties(valarmProperties); property.setValueType(QVersitProperty::VersitDocumentType); property.setName(QStringLiteral("VALARM")); property.setValue(QVariant::fromValue(valarmDocument)); QOrganizerItemEmailReminder emailReminder; emailReminder.setContents(QStringLiteral("Test email subject"), QStringLiteral("Test email body"), QVariantList()); emailReminder.setRepetition(3, 5); emailReminder.setSecondsBeforeStart(90); emailReminder.setRecipients(QStringList() << QStringLiteral("First email recipient") << QStringLiteral("Second email recipient")); QTest::newRow("email reminder") << (QList() << property) << (QList() << emailReminder); } { QVersitProperty property; property.setName(QStringLiteral("STATUS")); property.setValue(QStringLiteral("COMPLETED")); QOrganizerTodoProgress progress; progress.setStatus(QOrganizerTodoProgress::StatusComplete); QTest::newRow("status completed") << (QList() << property) << (QList() << progress); property.setValue(QStringLiteral("NEEDS-ACTION")); progress.setStatus(QOrganizerTodoProgress::StatusNotStarted); QTest::newRow("status needs-action") << (QList() << property) << (QList() << progress); property.setValue(QStringLiteral("IN-PROCESS")); progress.setStatus(QOrganizerTodoProgress::StatusInProgress); QTest::newRow("status in-process") << (QList() << property) << (QList() << progress); } { QVersitProperty property; property.setName(QStringLiteral("PERCENT-COMPLETE")); property.setValue(QStringLiteral("42")); QOrganizerTodoProgress progress; progress.setPercentageComplete(42); QTest::newRow("percent-complete") << (QList() << property) << (QList() << progress); } { QVersitProperty property; property.setName(QStringLiteral("COMPLETED")); property.setValue(QStringLiteral("20100609T161500")); QOrganizerTodoProgress progress; progress.setFinishedDateTime(QDateTime(QDate(2010, 6, 9), QTime(16, 15, 0))); QTest::newRow("completed") << (QList() << property) << (QList() << progress); } { QVersitProperty property; property.setName(QStringLiteral("CATEGORIES")); property.setValue(QStringLiteral("Important Event")); QOrganizerItemTag tag; tag.setTag(QStringLiteral("Important Event")); QTest::newRow("tag 1") << (QList() << property) << (QList() << tag); // Set another one for multiple handling test property.setValue(QStringLiteral("Important Event 2")); tag.setTag(QStringLiteral("Important Event 2")); QTest::newRow("tag 2") << (QList() << property) << (QList() << tag); // Empty tag property.clear(); property.setName("CATEGORIES"); tag.removeValue(QOrganizerItemTag::FieldTag); QTest::newRow("empty tag") << (QList() << property) << (QList() ); // Detail side should be empty } { QVersitProperty property = createExtendedDetailPropertyForStringData("name", "data"); QOrganizerItemExtendedDetail extendedDetail = createExtendedDetail("name", "data"); QTest::newRow("extended detail") << (QList() << property) << (QList() << extendedDetail); } } void tst_QVersitOrganizerImporter::testExtendedDetail() { QFETCH(QString, extendedDetailName); QFETCH(QVariant, extendedDetailData); QFETCH(QString, extendedDetailDataInProperty); QFETCH(bool, extendedDetailCreated); QVersitDocument document(QVersitDocument::ICalendar20Type); document.setComponentType(QStringLiteral("VCALENDAR")); QVersitDocument nested(QVersitDocument::ICalendar20Type); nested.setComponentType(QStringLiteral("VEVENT")); nested.addProperty(createExtendedDetailProperty(extendedDetailName, extendedDetailDataInProperty)); document.addSubDocument(nested); QOrganizerItemExtendedDetail expectedDetail = createExtendedDetail(extendedDetailName, extendedDetailData); QVersitOrganizerImporter importer; QVERIFY(importer.importDocument(document)); QList items = importer.items(); QList actualDetails = items.first().details(expectedDetail.type()); if (!extendedDetailCreated) { QCOMPARE(actualDetails.size(), 0); } else { QCOMPARE(actualDetails.size(), 1); if (!actualDetails.contains(expectedDetail)) { qDebug() << "Actual:" << actualDetails; qDebug() << "Expected to find:" << expectedDetail; QVERIFY(false); } } } void tst_QVersitOrganizerImporter::testExtendedDetail_data() { QTest::addColumn("extendedDetailName"); QTest::addColumn("extendedDetailData"); QTest::addColumn("extendedDetailDataInProperty"); QTest::addColumn("extendedDetailCreated"); QString jsonArrayWith("[\n %1\n]\n"); QString jsonArrayWithString = jsonArrayWith.arg("\"%1\""); { QTest::newRow("string data") << QString("name") << QVariant(QString("data")) << jsonArrayWithString.arg("data") << true; QTest::newRow("string data, empty") << QString("name") << QVariant(QString("")) << jsonArrayWithString.arg("") << true; QTest::newRow("string data, containing reserved characters") << QString("name") << QVariant(QString(",;:\\")) << jsonArrayWithString.arg(",;:\\\\") << true; } { QTest::newRow("double data") << QString("name") << QVariant((double)2.0) << jsonArrayWith.arg("2") << true; QTest::newRow("double data, negative") << QString("name") << QVariant((double)-1.0) << jsonArrayWith.arg("-1") << true; QTest::newRow("double data, multiple digits") << QString("name") << QVariant((double)10.2) << jsonArrayWith.arg("10.2") << true; } { QTest::newRow("boolean data") << QString("name") << QVariant(true) << jsonArrayWith.arg("true") << true; } { QTest::newRow("datetime data, imported as a string") << QString("name") << QVariant(QString("1997-07-16T19:20:30+01:00")) << jsonArrayWithString.arg("1997-07-16T19:20:30+01:00") << true; } { QTest::newRow("list data") << QString("name") << QVariant(QVariantList() << QString("string 1") << QString("string 2")) << QString("[\n [\n \"string 1\",\n \"string 2\"\n ]\n]\n") << true; } { QVariantMap map; map["key 1"] = QString("string 1"); map["key 2"] = QString("string 2"); QTest::newRow("map data") << QString("name") << QVariant(map) << QString("[\n {\n \"key 1\": \"string 1\",\n \"key 2\": \"string 2\"\n }\n]\n") << true; } { QVariantMap map; map["key"] = QVariantList() << (double)1 << (double)2; QTest::newRow("map data, containing a nested list") << QString("name") << QVariant(map) << QString("[\n {\n \"key\": [\n 1,\n 2\n ]\n }\n]\n") << true; } { QTest::newRow("empty string as name") << QString("") << QVariant(QString("data")) << jsonArrayWithString.arg("data") << true; QTest::newRow("name containing reserved characters") << QString(",;:\\") << QVariant(QString("data")) << jsonArrayWithString.arg("data") << true; } { QTest::newRow("null denoting an empty variant") << QString("name") << QVariant() << jsonArrayWith.arg("null") << true; QTest::newRow("compact json with extra whitespace removed") << QString("name") << QVariant(QString("data")) << QString("[\"data\"]") << true; } { QTest::newRow("invalid property value: empty json array as property value") << QString("name") << QVariant() << jsonArrayWith.arg("") << false; QTest::newRow("invalid property value: invalid json value") << QString("name") << QVariant() << jsonArrayWith.arg("invalid") << false; } } void tst_QVersitOrganizerImporter::testMultipleExtendedDetails() { QVersitDocument document(QVersitDocument::ICalendar20Type); document.setComponentType(QStringLiteral("VCALENDAR")); QVersitDocument nested(QVersitDocument::ICalendar20Type); nested.setComponentType(QStringLiteral("VEVENT")); nested.addProperty(createExtendedDetailPropertyForStringData("detailName1", "detailData1")); nested.addProperty(createExtendedDetailPropertyForStringData("detailName2", "detailData2")); document.addSubDocument(nested); QList expectedDetails; expectedDetails << createExtendedDetail("detailName1", "detailData1"); expectedDetails << createExtendedDetail("detailName2", "detailData2"); QVersitOrganizerImporter importer; QVERIFY(importer.importDocument(document)); QList items = importer.items(); QList actualDetails = items.first().details(QOrganizerItemDetail::TypeExtendedDetail); QCOMPARE(actualDetails.size(), 2); foreach (const QOrganizerItemDetail& expectedDetail, expectedDetails) { if (!actualDetails.contains(expectedDetail)) { qDebug() << "Actual:" << actualDetails; qDebug() << "Expected to find:" << expectedDetail; QVERIFY(false); } } } QOrganizerItemExtendedDetail tst_QVersitOrganizerImporter::createExtendedDetail( const QString &name, const QVariant &data) { QOrganizerItemExtendedDetail extendedDetail; extendedDetail.setName(name); extendedDetail.setData(data); return extendedDetail; } QVersitProperty tst_QVersitOrganizerImporter::createExtendedDetailProperty( const QString &name, const QVariant &data) { QVersitProperty property; property.setName(QStringLiteral("X-QTPROJECT-EXTENDED-DETAIL")); property.setValue(QStringList() << name << data.toString()); property.setValueType(QVersitProperty::CompoundType); return property; } QVersitProperty tst_QVersitOrganizerImporter::createExtendedDetailPropertyForStringData( const QString &name, const QString &data) { QString jsonArrayWith("[\n %1\n]\n"); QString jsonArrayWithString = jsonArrayWith.arg("\"%1\""); return createExtendedDetailProperty(name, jsonArrayWithString.arg(data)); } void tst_QVersitOrganizerImporter::testTimeZones() { QFETCH(QString, tzid); QFETCH(QVersitDocument, timezoneSpec); QFETCH(QString, datetimeString); QFETCH(QDateTime, expected); QVersitDocument document(QVersitDocument::ICalendar20Type); document.setComponentType(QStringLiteral("VCALENDAR")); if (!tzid.isEmpty()) { document.addSubDocument(timezoneSpec); } QVersitDocument vevent(QVersitDocument::ICalendar20Type); vevent.setComponentType(QStringLiteral("VEVENT")); QVersitProperty property; property.setName(QStringLiteral("DTSTART")); property.setValue(datetimeString); if (!tzid.isEmpty()) { property.insertParameter(QStringLiteral("TZID"), tzid); } vevent.addProperty(property); document.addSubDocument(vevent); QVersitOrganizerImporter importer; QVERIFY(importer.importDocument(document)); QVERIFY(importer.errorMap().isEmpty()); QList items = importer.items(); QCOMPARE(items.size(), 1); QOrganizerEvent event = static_cast(items.first()); QCOMPARE(event.type(), QOrganizerItemType::TypeEvent); QDateTime actualDatetime = event.startDateTime(); QCOMPARE(actualDatetime, expected); QCOMPARE(actualDatetime.timeSpec(), expected.timeSpec()); } void tst_QVersitOrganizerImporter::testTimeZones_data() { QTest::addColumn("tzid"); // set this to empty if you don't want to associate a tzid QTest::addColumn("timezoneSpec"); QTest::addColumn("datetimeString"); QTest::addColumn("expected"); QVersitDocument vtimezone(QVersitDocument::ICalendar20Type); vtimezone.setComponentType(QStringLiteral("VTIMEZONE")); QTest::newRow("utc") << QString() << QVersitDocument(QVersitDocument::ICalendar20Type) << QString::fromLatin1("20100102T030405Z") << QDateTime(QDate(2010, 1, 2), QTime(3, 4, 5), Qt::UTC); QTest::newRow("floating") << QString() << QVersitDocument(QVersitDocument::ICalendar20Type) << QString::fromLatin1("20100102T030405") << QDateTime(QDate(2010, 1, 2), QTime(3, 4, 5), Qt::LocalTime); { QVersitDocument vtimezone(QVersitDocument::ICalendar20Type); vtimezone.setComponentType(QStringLiteral("VTIMEZONE")); QVersitProperty property; property.setName(QStringLiteral("TZID")); property.setValue(QStringLiteral("Asia/Singapore")); vtimezone.addProperty(property); property.setName(QStringLiteral("X-LIC-LOCATION")); property.setValue(QStringLiteral("Asia/Singapore")); vtimezone.addProperty(property); QVersitDocument standard(QVersitDocument::ICalendar20Type); standard.setComponentType(QStringLiteral("STANDARD")); property.setName(QStringLiteral("TZOFFSETFROM")); property.setValue(QStringLiteral("+0800")); standard.addProperty(property); property.setName(QStringLiteral("TZOFFSETTO")); property.setValue(QStringLiteral("+0800")); standard.addProperty(property); property.setName(QStringLiteral("TZNAME")); property.setValue(QStringLiteral("EST")); standard.addProperty(property); property.setName(QStringLiteral("DTSTART")); property.setValue(QStringLiteral("19700101T000000")); standard.addProperty(property); vtimezone.addSubDocument(standard); QTest::newRow("no dst") << QString::fromLatin1("Asia/Singapore") << vtimezone << QString::fromLatin1("20100102T100405") << QDateTime(QDate(2010, 1, 2), QTime(2, 4, 5), Qt::UTC); } { QVersitDocument vtimezone(QVersitDocument::ICalendar20Type); vtimezone.setComponentType(QStringLiteral("VTIMEZONE")); QVersitProperty property; property.setName(QStringLiteral("TZID")); property.setValue(QStringLiteral("Australia/Sydney")); vtimezone.addProperty(property); property.setName(QStringLiteral("X-LIC-LOCATION")); property.setValue(QStringLiteral("Australia/Sydney")); vtimezone.addProperty(property); QVersitDocument standard(QVersitDocument::ICalendar20Type); standard.setComponentType(QStringLiteral("STANDARD")); property.setName(QStringLiteral("TZOFFSETFROM")); property.setValue(QStringLiteral("+1100")); standard.addProperty(property); property.setName(QStringLiteral("TZOFFSETTO")); property.setValue(QStringLiteral("+1000")); standard.addProperty(property); property.setName(QStringLiteral("TZNAME")); property.setValue(QStringLiteral("EST")); standard.addProperty(property); property.setName(QStringLiteral("DTSTART")); property.setValue(QStringLiteral("19700405T030000")); standard.addProperty(property); property.setName(QStringLiteral("RRULE")); property.setValue(QStringLiteral("FREQ=YEARLY;BYMONTH=4;BYDAY=1SU")); standard.addProperty(property); vtimezone.addSubDocument(standard); QVersitDocument daylight(QVersitDocument::ICalendar20Type); daylight.setComponentType(QStringLiteral("DAYLIGHT")); property.setName(QStringLiteral("TZOFFSETFROM")); property.setValue(QStringLiteral("+1000")); daylight.addProperty(property); property.setName(QStringLiteral("TZOFFSETTO")); property.setValue(QStringLiteral("+1100")); daylight.addProperty(property); property.setName(QStringLiteral("TZNAME")); property.setValue(QStringLiteral("EST")); daylight.addProperty(property); property.setName(QStringLiteral("DTSTART")); property.setValue(QStringLiteral("19701004T020000")); daylight.addProperty(property); property.setName(QStringLiteral("RRULE")); property.setValue(QStringLiteral("FREQ=YEARLY;BYMONTH=10;BYDAY=1SU")); daylight.addProperty(property); vtimezone.addSubDocument(daylight); QTest::newRow("dst area in standard time") << QString::fromLatin1("Australia/Sydney") << vtimezone << QString::fromLatin1("20100502T100405") << QDateTime(QDate(2010, 5, 2), QTime(0, 4, 5), Qt::UTC); QTest::newRow("dst") << QString::fromLatin1("Australia/Sydney") << vtimezone << QString::fromLatin1("20100102T100405") << QDateTime(QDate(2010, 1, 1), QTime(23, 4, 5), Qt::UTC); } { QVersitDocument vtimezone(QVersitDocument::ICalendar20Type); vtimezone.setComponentType(QStringLiteral("VTIMEZONE")); QVersitProperty property; property.setName(QStringLiteral("TZID")); property.setValue(QStringLiteral("US-Eastern")); vtimezone.addProperty(property); QVersitDocument standard(QVersitDocument::ICalendar20Type); standard.setComponentType(QStringLiteral("STANDARD")); property.setName(QStringLiteral("DTSTART")); property.setValue(QStringLiteral("19961026T020000")); standard.addProperty(property); property.setName(QStringLiteral("RDATE")); property.setValue(QStringLiteral("19971026T020000")); standard.addProperty(property); property.setName(QStringLiteral("TZOFFSETFROM")); property.setValue(QStringLiteral("-0400")); standard.addProperty(property); property.setName(QStringLiteral("TZOFFSETTO")); property.setValue(QStringLiteral("-0500")); standard.addProperty(property); vtimezone.addSubDocument(standard); QVersitDocument daylight(QVersitDocument::ICalendar20Type); daylight.setComponentType(QStringLiteral("DAYLIGHT")); property.setName(QStringLiteral("DTSTART")); property.setValue(QStringLiteral("19960406T020000")); daylight.addProperty(property); property.setName(QStringLiteral("RDATE")); property.setValue(QStringLiteral("19970406T020000")); daylight.addProperty(property); property.setName(QStringLiteral("TZOFFSETFROM")); property.setValue(QStringLiteral("-0500")); daylight.addProperty(property); property.setName(QStringLiteral("TZOFFSETTO")); property.setValue(QStringLiteral("-0400")); daylight.addProperty(property); vtimezone.addSubDocument(daylight); QTest::newRow("dst specified with rdate - daylight") << QString::fromLatin1("US-Eastern") << vtimezone << QString::fromLatin1("19970615T100000") << QDateTime(QDate(1997, 6, 15), QTime(14, 0, 0), Qt::UTC); QTest::newRow("dst specified with rdate - standard") << QString::fromLatin1("US-Eastern") << vtimezone << QString::fromLatin1("19971215T100000") << QDateTime(QDate(1997, 12, 15), QTime(15, 0, 0), Qt::UTC); } } QTEST_MAIN(tst_QVersitOrganizerImporter) tests/auto/versitorganizer/qversitorganizerimporter/tst_qversitorganizerimporter.h000066400000000000000000000061271233466112000320740ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef tst_QVERSITORGANIZERIMPORTER_H #define tst_QVERSITORGANIZERIMPORTER_H #include #include #include #include #include QTORGANIZER_USE_NAMESPACE QTVERSIT_USE_NAMESPACE QTVERSITORGANIZER_USE_NAMESPACE class tst_QVersitOrganizerImporter : public QObject { Q_OBJECT private slots: void testImport(); void testImport_data(); void testImportEventProperties(); void testImportEventProperties_data(); void testImportTodoProperties(); void testImportTodoProperties_data(); void testExtendedDetail(); void testExtendedDetail_data(); void testMultipleExtendedDetails(); QOrganizerItemExtendedDetail createExtendedDetail( const QString &name, const QVariant &data); QVersitProperty createExtendedDetailProperty( const QString &name, const QVariant &data); QVersitProperty createExtendedDetailPropertyForStringData( const QString &name, const QString &data); void testTimeZones(); void testTimeZones_data(); }; #endif tests/auto/versitorganizer/versitorganizer.pro000066400000000000000000000001411233466112000223760ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS += \ qversitorganizerexporter \ qversitorganizerimporter \ tests/benchmark/000077500000000000000000000000001233466112000141505ustar00rootroot00000000000000tests/benchmark/contacts/000077500000000000000000000000001233466112000157665ustar00rootroot00000000000000tests/benchmark/contacts/ContactsBenchmarkTestCase.qml000066400000000000000000000047131233466112000235330ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtTest 1.0 import QtContacts 5.0 TestCase { name: "ContactsBenchmarkTestCase" id: contactsBenchmarkTestCase function createContacts(count) { contactsList = new Array(count); for (var it=0;it" height: 100; width: parent.width anchors { horizontalCenter: parent.horizontalCenter } color: "White" wrapMode: TextEdit.WordWrap font { italic: true } } } Component { id: listViewDelegate Row { property bool isEnabledInCollectionFilter: modelCollectionFilter.ids.indexOf(collectionId) != -1 anchors { horizontalCenter: parent.horizontalCenter } spacing: 5 Text { text: name height: 30 color: "White" font { weight: Font.Bold } MouseArea{ anchors.fill: parent onClicked: { collectionList.currentIndex = index; modifyCollection(); } } } CheckBox { checked: isEnabledInCollectionFilter onClicked: { collectionList.currentIndex = index; isEnabledInCollectionFilter = !isEnabledInCollectionFilter; modifyCollectionFilter(isEnabledInCollectionFilter, index); } } } } function addCollection() { collectionEditorView.collection = Qt.createQmlObject('import QtQuick 2.0; import QtOrganizer 5.0; Collection {}',organizer); collectionEditorView.collection.name = "Collection"+(organizer.collections.length+1); collectionEditorView.isNewCollection = true; settingsView.state = "CollectionEditorView"; } function modifyCollection() { collectionEditorView.collection = organizer.collections[collectionList.currentIndex]; collectionEditorView.isNewCollection = false; settingsView.state = "CollectionEditorView"; } function modifyCollectionFilter(enabled, index) { //Get exist filter id list, var filterIdsList = modelCollectionFilter.ids; //Collection id will be added or removed from filter var collectionId = organizer.collections[index].collectionId; var filterIndex = filterIdsList.indexOf(collectionId); if (false == enabled) { if (filterIndex >= 0) //If the enable is false, remove from the list if we found inside list filterIdsList.splice(filterIndex, 1); else console.log("Warning: Collection id is not found in filter list" + collectionId); } else {//Add this id in the filter list if we do not have it in list if (filterIndex == -1) filterIdsList.push(collectionId); else console.log("Warning: Collection id exists in filter list :" + collectionId); } //Update model collection filter modelCollectionFilter.ids = filterIdsList; } } tests/system/qmlorganizer/contents/CollectionRoller.qml000066400000000000000000000057121233466112000241040ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtOrganizer 5.0 RollerRow { label: "Collection" valueSet: createCollectionNameValueSet(); function createCollectionNameValueSet() { var collectionNameArray = new Array(); for (var i=0;i 0) { calendar.currentDate = new Date(timelineView.year, timelineView.month, currentIndex + 1); monthList.currentIndex = timelineView.month; currentIndex = timelineView.day - 1; } } } Component { id: hourHighlight Rectangle { width: hourList.width; height: hourList.height /7 ; color: "lightsteelblue" ;radius: 5 } } Component { id: hourDelegate Item { width : hourList.width height : childrenRect.height property int rowIndex : index id:hourDelegateInstanceItem Column { // Draw a line under the previous Hour list tiem Rectangle { height : 1 width : hourList.width color : "black" } Text { // text: hour text: index + ":00" } // List all, if any, of the events within this hour. Repeater { focus: true // Simple fetch ALL events on this day...and we will filter them bu hour. model: calendar.organizer.items? calendar.organizer.itemsByTimePeriod(new Date(calendar.year, calendar.month, calendar.day) , new Date(calendar.year, calendar.month, calendar.day+1)) : 0 Row { spacing: 4 Text { id: itemText clip: true focus: true // Only display a link when the event starts within this hour...... text: (hourDelegateInstanceItem.rowIndex == Qt.formatTime(modelData.itemStartTime, "hh")) ? "" + modelData.displayLabel + "":"" onLinkActivated: { detailsView.isNewItem = false; detailsView.item = modelData; if (detailsView.item.itemType == Type.EventOccurrence || detailsView.item.itemType == Type.TodoOccurrence) calendar.state = "OccurrenceDialogView"; else calendar.state = "DetailsView"; } } Rectangle { width: 15; height: 15 anchors { verticalCenter: parent.verticalCenter } border { color: "black"; width: 1; } visible: (hourDelegateInstanceItem.rowIndex == Qt.formatTime(modelData.itemStartTime, "hh")) ? true : false color: calendar.organizer.collection(modelData.collectionId)? calendar.organizer.collection(modelData.collectionId).color : "red" } } } } } } ListModel { id : hourModel ListElement {hour : "0:00"} ListElement {hour : "1:00"} ListElement {hour : "2:00"} ListElement {hour : "3:00"} ListElement {hour : "4:00"} ListElement {hour : "5:00"} ListElement {hour : "6:00"} ListElement {hour : "7:00"} ListElement {hour : "8:00"} ListElement {hour : "9:00"} ListElement {hour : "10:00"} ListElement {hour : "11:00"} ListElement {hour : "12:00"} ListElement {hour : "13:00"} ListElement {hour : "14:00"} ListElement {hour : "15:00"} ListElement {hour : "16:00"} ListElement {hour : "17:00"} ListElement {hour : "18:00"} ListElement {hour : "19:00"} ListElement {hour : "20:00"} ListElement {hour : "21:00"} ListElement {hour : "22:00"} ListElement {hour : "23:00"} } } } tests/system/qmlorganizer/contents/DetailsView.qml000066400000000000000000000524101233466112000230460ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtOrganizer 5.0 import "datetimerollercontents" Rectangle { id:detailsView anchors.fill: parent property bool isNewItem property variant item property variant rule property variant audibleReminderdetail onOpacityChanged: { // Is this view visible if (detailsView.opacity == 1) { // Initialize empty item if (isNewItem) { if (item.itemType == Type.Event || item.itemType == Type.EventOccurrence) { item.startDateTime = new Date (calendar.year, calendar.month, calendar.day, 12); item.endDateTime = new Date (calendar.year, calendar.month, calendar.day, 13); } else if (item.itemType == Type.Todo || item.itemType == Type.TodoOccurrence) { item.startDateTime = new Date (calendar.year, calendar.month, calendar.day, 12); item.dueDateTime = new Date (calendar.year, calendar.month, calendar.day, 13); } } // Initialize recurrence rule emptyRecurrenceRule.frequency = RecurrenceRule.Invalid; emptyRecurrenceRule.interval = 1; emptyRecurrenceRule.limit = null; // Initialize UI if (item.itemType == Type.Event || item.itemType == Type.EventOccurrence) { customLabelRow.setValue(item.displayLabel); customStartDateRow.dateTimeRoller.setDateTime(item.startDateTime); customEndDateRow.dateTimeRoller.setDateTime(item.endDateTime); customAlldayCheckBox.setValue(item.allDay); customDescriptionRow.setValue(item.description); customLocationRow.setValue(item.location); customCollectionRow.value = customCollectionRow.findCollectionArrayIndex(item.collectionId); customRsvpParticipationStatusRow.value = item.details(Detail.EventRsvp).length ? item.detail(Detail.EventRsvp).participationStatus : EventAttendee.StatusUnknown; } else if (item.itemType == Type.Todo || item.itemType == Type.TodoOccurrence) { todoCustomLabelRow.setValue(item.displayLabel); todoCustomDueDateRow.dateTimeRoller.setDateTime(detailsView.item.dueDateTime); todoCustomDescriptionRow.setValue(item.description); todoCustomPriorityRow.value = detailsView.item.priority; todoCollectionRow.value = todoCollectionRow.findCollectionArrayIndex(item.collectionId); } if (item.recurrence != undefined && item.recurrence.recurrenceRules.length > 0) { // Existing rule rule = item.recurrence.recurrenceRules[0]; customRecurrenceRow.setValue(true); } else { // New rule rule = emptyRecurrenceRule; customRecurrenceRow.setValue(false); } // customInterval.setValue(rule.interval != 1); customRecurrenceLimitRow.setValue(rule != undefined && rule.limit != null); customLimitDateRow.dateTimeRoller.setDateTime((rule.limit == null || typeof rule.limit == "number")? null : rule.limit); //Audible reminder var audibleDetailList = item.details(Detail.AudibleReminder); if (audibleDetailList.length > 0) { audibleReminderdetail = item.detail(Detail.AudibleReminder); audibleReminderCheckBox.setValue(true); } else { audibleReminderdetail = emptyAudibleReminder; audibleReminderCheckBox.setValue(false); } } } RecurrenceRule { id: emptyRecurrenceRule } AudibleReminder { id: emptyAudibleReminder dataUrl: "http://www.qt.nokia.com" } Image { source: "images/stripes.png"; fillMode: Image.Tile; anchors.fill: parent; opacity: 0.8 } color: "#343434"; Rectangle { id: buttonRow anchors { bottom: parent.bottom; left: parent.left; right: parent.right; } height: saveButton.height color:"#343434" Button { id: saveButton text: "Save & Exit" //anchors { top: parent.top; left: parent.left } anchors { top: parent.top; left: parent.left } width: parent.width / 2 onClicked: { //save item saveButton.enabled = false; calendar.currentDate = item.startDateTime; if (item.detail(Detail.AudibleReminder)) { item.setDetail(audibleReminderdetail); } var storageLocation = OrganizerModel.UserDataStorage; var isEventType = (item.itemType == Type.Event || item.itemType == Type.EventOccurrence); storageLocation = settingsView.mapStorageLocationStringToInt(calendar.storageLocationModel.get( isEventType ? customStorageLocationRow.currentIndex : todoStorageLocationRow.currentIndex).name); console.log("storageLocation:"+storageLocation) calendar.organizer.saveItem(item, storageLocation); calendar.state = "DayView"; saveButton.enabled = true; //"item" will be removed after saved without any signal notify. item = null; } } Button { id: deleteButton visible: !isNewItem text: "Delete" anchors { top: saveButton.top; left: saveButton.right } //anchors { top: parent.top; left: saveButton.right } width: parent.width / 2 onClicked: { calendar.organizer.removeItem(item); calendar.state = "DayView"; } } } ListView { anchors {top: parent.top; bottom: buttonRow.top; left: parent.left; right: parent.right } clip: true opacity: 0.8 model: { if (item == undefined) return null; switch (item.itemType) { case Type.Event: //temporary comment because recurrence is not supported yet // return eventItemModel; case Type.EventOccurrence: return eventOccurrenceItemModel; case Type.Todo: return todoItemModel; default: break; } return null; } } //event occurrence VisualItemModel { id:eventOccurrenceItemModel Text { width: detailsView.width - 6; height: 30 text: "Event" verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter color: "White"; font.weight: Font.Bold } FieldRow { id: customLabelRow label: "Title" onNewValueChanged: { item.displayLabel = customLabelRow.newValue; } } DateTimeRollerRow { id: customStartDateRow label: "Start Time" onRollerChanged: { detailsView.item.startDateTime = customStartDateRow.dateTimeRoller.selectedDateTime(); } } DateTimeRollerRow { id: customEndDateRow label: "End Time" onRollerChanged: { item.endDateTime = customEndDateRow.dateTimeRoller.selectedDateTime(); } } CheckBoxRow { id: customAlldayCheckBox label: "All day" onCheckBoxChanged: { item.allDay = customAlldayCheckBox.newValue } } FieldRow { id: customDescriptionRow label: "Description" height: 80 onNewValueChanged: { item.description = customDescriptionRow.newValue; } } FieldRow { id: customLocationRow label: "Location" onNewValueChanged: { item.location = customLocationRow.newValue; } } CheckBoxRow { id: customRecurrenceRow label: "Repeated" onCheckBoxChanged: { if (customRecurrenceRow.newValue) item.recurrence.recurrenceRules = [rule]; else item.recurrence.recurrenceRules = []; } } // CheckBoxRow { // id: customInterval // label: "Modify Interval" // visible: customRecurrenceRow.newValue // onCheckBoxChanged: { // if (customInterval.newValue) // rule.interval = 15; // else // rule.interval = 1; // } // } RollerRow { id: customFrequencyRow label: "Frequency" height: visible? 100 :0 visible: customRecurrenceRow.newValue value: rule? rule.frequency : 0 valueSet: ["Invalid", "Daily", "Weekly", "Monthly", "Yearly"] onRollerChanged: { rule.frequency = customFrequencyRow.valueRoller.selectedValue(); } } CheckBoxRow { id: customRecurrenceLimitRow label: "Repeat limit" visible: customRecurrenceRow.newValue height: visible? 60 :0 } DateTimeRollerRow { id: customLimitDateRow label: "Repeat until" visible: customRecurrenceRow.newValue && customRecurrenceLimitRow.newValue height: visible? 100 :0 onRollerChanged: { if (customLimitDateRow.visible) rule.limit = customLimitDateRow.dateTimeRoller.selectedDateTime(); } onVisibleChanged: { if (!customRecurrenceLimitRow.newValue && rule) rule.limit = null; } } CheckBoxRow { id: audibleReminderCheckBox label: "Reminder" onCheckBoxChanged: { if (newValue) { audibleReminderdetail = emptyAudibleReminder; //This function will create new detail item.addDetail(audibleReminderdetail); } else { var removeDetail = item.detail(Detail.AudibleReminder); item.removeDetail(removeDetail); } } } Item { height: visible? 220 :0 width: detailsView.width id: audibleReminderDataRow visible: audibleReminderCheckBox.newValue Text { id: audibleReminderDataRowText anchors.top: parent.top width: detailsView.width; height: 20 text: "Reminder" verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter color: "White"; font.weight: Font.Bold } FieldRow { id: customAudibleReminderRepCountRow anchors.top: audibleReminderDataRowText.bottom label: "Repetition count" value: audibleReminderdetail? audibleReminderdetail.repetitionCount : 0 onNewValueChanged: { audibleReminderdetail.repetitionCount = newValue; } } FieldRow { id: customAudibleReminderRepDelayRow anchors.top: customAudibleReminderRepCountRow.bottom label: "Repetition delay" value: audibleReminderdetail? audibleReminderdetail.repetitionDelay : 0 onNewValueChanged: { audibleReminderdetail.repetitionDelay = newValue; } } FieldRow { id: customAudibleReminderSecBeforeStartRow anchors.top: customAudibleReminderRepDelayRow.bottom label: "Seconds before start" value: audibleReminderdetail? audibleReminderdetail.secondsBeforeStart : 0 onNewValueChanged: { audibleReminderdetail.secondsBeforeStart = newValue; } } FieldRow { anchors.top: customAudibleReminderSecBeforeStartRow.bottom label: "Data url" value: audibleReminderdetail? audibleReminderdetail.dataUrl : "" onNewValueChanged: { audibleReminderdetail.dataUrl = newValue; } } } CollectionRoller { id: customCollectionRow onCurrentIndexChanged: { item.collectionId = organizer.collections[currentIndex].collectionId; } } StorageLocationRoller { id: customStorageLocationRow opacity: isNewItem ? 1 : 0; } RollerRow { id: customRsvpParticipationStatusRow valueRoller.clip: true // clipping to roller-component.. label: "Partic. status" valueSet: ["Unknown", "Accepted", "Declined", "Tentative", "Delegated", "InProcess", "Completed"] onCurrentIndexChanged: { // rsvp-detail included only if modified on the view if (EventAttendee.StatusUnknown != customRsvpParticipationStatusRow.currentIndex) { var rsvpDetail; if (item.details(Detail.EventRsvp).length) { rsvpDetail = item.detail(Detail.EventRsvp); } else { rsvpDetail = Qt.createQmlObject("import QtOrganizer 5.0; EventRsvp{}", organizer); } rsvpDetail.participationStatus = customRsvpParticipationStatusRow.currentIndex; item.setDetail(rsvpDetail); } else if (item.details(Detail.EventRsvp).length) { item.removeDetail(item.detail(Detail.EventRsvp)); } } } Column { id : tagColumn Repeater { model: item? (item.itemDetails? item.details(Detail.Tag) : 0) : 0 Rectangle { width: detailsView.width; height: childrenRect.height Text { width: detailsView.width text: "Tag " + index + " : " + modelData.tag; } Button { text: "remove" width: detailsView.width / 6 anchors.right: parent.right onClicked: { item.removeDetail(modelData); } } } } } FieldRow { id: tagRow anchors { top: tagColumn.bottom; margins: height / 3 } label: "New Tag" value: "NewTag" } Row { anchors.horizontalCenter: tagRow.horizontalCenter Button { text: "Add tag" width: detailsView.width / 4 onClicked: { if (tagRow.newValue) { var tag = Qt.createQmlObject("import QtOrganizer 5.0;Tag {}", organizer); tag.tag = tagRow.newValue; item.setDetail(tag); } } } Button { text: "Birthday" width: detailsView.width / 4 onClicked: { var tagList = item.details(Detail.Tag) var found = false; for (var i=0; i< tagList.length; i++) { if (tagList[i].tag == "ANNIVERSARY") found = true; } if (!found) { var tag = Qt.createQmlObject("import QtOrganizer 5.0;Tag {}", organizer); tag.tag = "ANNIVERSARY"; item.setDetail(tag); } } } } Column { id: attendeeColumn Repeater { model: item? item.attendees : 0 Rectangle { width: detailsView.width; height: childrenRect.height Text { width: detailsView.width text: "attendee " + index + " : " + name + "," + emailAddress; } Button { text: "remove" width: detailsView.width / 6 height: parent anchors.right: parent.right onClicked: { item.removeDetail(modelData); } } } } } Button { text: "Add Attendee" width: detailsView.width / 2 anchors { horizontalCenter: tagRow.horizontalCenter; top: attendeeColumn.bottom; margins: height / 3 } onClicked: { attendeeDetailsView.item = item; calendar.state = "AttendeeDetailsView"; } } } //todo VisualItemModel { id:todoItemModel Text { width: detailsView.width - 6; height: 30 text: "ToDo" verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter color: "White"; font.weight: Font.Bold } FieldRow { id: todoCustomLabelRow label: "Title" onNewValueChanged: { item.displayLabel = todoCustomLabelRow.newValue; } } DateTimeRollerRow { id: todoCustomDueDateRow label: "Due Time" onRollerChanged: { if (item.itemType == Type.Todo) { var startDate = todoCustomDueDateRow.dateTimeRoller.selectedDateTime(); startDate.setHours(startDate.getHours()-1); item.startDateTime = startDate; item.dueDateTime = todoCustomDueDateRow.dateTimeRoller.selectedDateTime(); } } } FieldRow { id: todoCustomDescriptionRow label: "Description" height: 150 onNewValueChanged: { item.description = todoCustomDescriptionRow.newValue; } } RollerRow { id: todoCustomPriorityRow label: "Priority" valueSet: ["Unknown", "Highest", "ExtremelyHigh", "VeryHigh", "High", "Medium", "Low", "VeryLow", "ExtremelyLow", "Lowest"] onRollerChanged: { item.priority = todoCustomPriorityRow.valueRoller.selectedValue(); } } CollectionRoller { id: todoCollectionRow onCurrentIndexChanged: { item.collectionId = organizer.collections[currentIndex].collectionId; } } StorageLocationRoller { id: todoStorageLocationRow opacity: isNewItem ? 1 : 0; } } } tests/system/qmlorganizer/contents/FieldRow.qml000066400000000000000000000057021233466112000223430ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtDeclarative module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 Item { property alias label: nameField.text property variant value property alias newValue: textEdit.text height: 50 width: detailsView.width Text { id: nameField width: parent.width * 0.4 anchors { left: parent.left; margins: 3 } font.bold: true color: "white" } Rectangle { id: editorField anchors { left: nameField.right; right: parent.right; margins: 3 } height: parent.height - nameField.height; color: textEdit.activeFocus ? "white" : "lightgray"; border { width: 3; color: "white" } radius: 5 opacity: 0.95 TextEdit { id: textEdit anchors { fill: parent; margins: 3} width: parent.width text: value ? value.toString() : "" wrapMode: TextEdit.Wrap } } function setValue(initialValue) { if (textEdit.text != initialValue) { textEdit.text = initialValue; } } } tests/system/qmlorganizer/contents/InfoBar.qml000066400000000000000000000053141233466112000221470ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Organizer module. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 Rectangle { Rectangle { id: frameRateCounter anchors.fill: parent property int dummy: 0 property int fpsCount: 0 color: "black" Text { id: fpsText color: "white" anchors.centerIn: parent } NumberAnimation on dummy { duration: 500 from: 0 to: 10000 loops: Animation.Infinite } onDummyChanged: ++fpsCount; Timer { interval: 1000 repeat: true running: true onTriggered: { fpsText.text = "Frame rate: " + parent.fpsCount + " fps"; parent.fpsCount = 0; } } } } tests/system/qmlorganizer/contents/ItemView.qml000066400000000000000000000063531233466112000223640ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtOrganizer 5.0 Rectangle { id:itemView property string itemId property OrganizerItem item property int startTime property int endTime onItemIdChanged :{ if (itemId != "") { item = calendar.organizer.item(itemId); startTime = item.itemStartTime.getHours() * 60 + item.itemStartTime.getMinutes(); if (item.itemType == Type.Event) endTime = item.itemEndTime.getHours() * 60 + item.itemEndTime.getMinutes(); else endTime = startTime; itemLabel.text = item.displayLabel; itemDesc.text = item.description; } } radius: 5 color: Type.Event == item.itemType ? "steelblue" : "green" Column { spacing: 2 Text { id: itemLabel; color: "yellow"; wrapMode: Text.Wrap; font.bold: true; horizontalAlignment: Text.AlignHCenter; style: Text.Raised; verticalAlignment: Text.AlignVCenter; font.pointSize: 12 } Text { id: itemDesc; color: "white"; wrapMode: Text.Wrap; font.pointSize: 10} } MouseArea { anchors.fill: parent onClicked : { detailsView.itemId = itemId detailsView.savedItem = true calendar.state = "DetailsView" } } } tests/system/qmlorganizer/contents/MediaButton.qml000066400000000000000000000052421233466112000230420ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 Item { property string text signal clicked id: container Image { id: normal source: "images/button.png" } Image { id: pressed opacity: 0 source: "images/button-pressed.png" } MouseArea { id: clickRegion anchors.fill: normal onClicked: { container.clicked();} } Text { font.bold: true color: "white" anchors.centerIn: normal text: container.text } width: normal.width height: childrenRect.height states: State { name: "Pressed" when: clickRegion.pressed == true PropertyChanges { target: pressed; opacity: 1 } } } tests/system/qmlorganizer/contents/MenuBar.qml000066400000000000000000000064211233466112000221600ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 Rectangle { id: menuBar; property string info; BorderImage { source: "images/titlebar.sci"; width: parent.width; height: parent.height + 14; y: -7 } gradient: Gradient { GradientStop { position: 0.0; color: activePalette.dark } GradientStop { position: 1.0; color: Qt.darker(activePalette.dark); } } Row { spacing: 0 Image { id: quitButton height: monthButton.height width:height source: "images/quit.png" MouseArea { anchors.fill: parent onClicked: Qt.quit() } } Button { id: dayButton; text: "Day";onClicked: calendar.state="DayView";} Button { id: weekButton; text: "Week";onClicked: calendar.state="WeekView";} Button { id: monthButton; text: "Month"; onClicked: calendar.state="MonthView";} Button { id: timelineButton; text: "Timeline";onClicked: calendar.state="TimelineView";} Button { id: todoButton; text: "Todos";onClicked: calendar.state="TodoView"; } Button { id: settingsButton; text: "Settings";onClicked: calendar.state="SettingsView";} Text { color: "#f5f210";text:info ; font.bold: true; verticalAlignment: Text.AlignVCenter; style: Text.Sunken;font.pointSize: 6} } } tests/system/qmlorganizer/contents/MonthView.qml000066400000000000000000000137521233466112000225540ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import "month.js" as Month import QtOrganizer 5.0 Rectangle { id:monthView property int month property int year property date startDay:new Date(year, month, 1) property int startWeekday:startDay.getDay() property var containItems: calendar.organizer.items ? calendar.organizer.containsItems(Month.dateOfThisDay(startDay, 1 - startWeekday), Month.dateOfThisDay(startDay, 43 - startWeekday), 86400) : undefined anchors.fill: parent Grid { id:container anchors.fill: parent columns: 7 Repeater { model:["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] Rectangle { width: container.width / 7 height: 35 color: "lightgray" border.color: "#3f4947" Text { text: modelData font.bold: true verticalAlignment: Text.AlignVCenter style: Text.Sunken styleColor: "#1365f3" font.pointSize: 11 anchors.centerIn: parent } MouseArea { anchors.fill: parent onClicked: { calendar.state = "WeekView"; } } } } Repeater { model: 42 Rectangle { id:dayContainer radius:10 width: container.width / 7 height: (container.height - 35) / 6 color: { if (Month.isToday(startDay, index - startWeekday +1)) { dayContainer.radius= 20 return "lightsteelblue"; } else if (monthView.containItems[index]) { dayContainer.radius= 20 return "yellow"; } else { dayContainer.radius= 0 return Month.getColorOfDay(startDay, index - startWeekday +1); } } Text { color: "#6ba24b"; text: Month.getDayOfMonth(startDay, index - startWeekday +1) font.bold: true style: Text.Raised font.pointSize: 10 anchors.centerIn: parent } MouseArea { hoverEnabled:true anchors.fill: parent onEntered: { dayContainer.border.color = "#1365f3"; // Set a dark blue surrounding border... dayContainer.border.width = 3; } onExited: { dayContainer.border.color = "#ffffff"; // Must reset the border and turn off else becomes graphics artifact } onReleased: { dayContainer.border.color = "#ffffff"; // Must reset the border and turn off else becomes graphics artifact } onClicked: { dayContainer.border.color = "#ffffff"; // Must reset the border and turn off else becomes graphics artifact calendar.currentDate = new Date(calendar.year, calendar.month, index - startWeekday +1); calendar.state = "DayView"; } } } } } } tests/system/qmlorganizer/contents/RollerRow.qml000066400000000000000000000067721233466112000225670ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtDeclarative module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import "datetimerollercontents" Item { property variant value property variant valueSet property alias label: nameField.text property alias valueRoller : valueRoller property alias currentIndex : valueSpinner.currentIndex property alias spinnerDelegate : valueSpinner.delegate signal rollerChanged height: 100 width: detailsView.width onValueChanged: { valueSpinner.currentIndex = value } Text { id: nameField width: parent.width * 0.4 anchors { left: parent.left; margins: 3 } font.bold: true color: "white" } Rectangle { id: valueRoller width: roller.width height: roller.height anchors { left: nameField.right; margins: 3} color: "white"; border { width: 3; color: "white" } radius: 5 Grid { id: roller spacing: 2 columns: 1 Spinner { id: valueSpinner width: 150 model: valueSet.length //10 delegate: Text { text: valueRoller.valueName(index); } onCurrentIndexChanged: rollerChanged() } } function valueName(valueIndex) { if (valueSet.length > valueIndex) return valueSet[valueIndex]; else return valueIndex; } // component API // use this to read selected datetime function selectedValue() { return valueSpinner.currentIndex; } } } tests/system/qmlorganizer/contents/ScrollBar.qml000066400000000000000000000065141233466112000225150ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 Item { id: scrollBar // The properties that define the scrollbar's state. // position and pageSize are in the range 0.0 - 1.0. They are relative to the // height of the page, i.e. a pageSize of 0.5 means that you can see 50% // of the height of the view. // orientation can be either 'Vertical' or 'Horizontal' property real position property real pageSize property var orientation : "Vertical" property alias bgColor: background.color property alias fgColor: thumb.color // A light, semi-transparent background Rectangle { id: background radius: orientation == 'Vertical' ? (width/2 - 1) : (height/2 - 1) color: "white"; opacity: 0.3 anchors.fill: parent } // Size the bar to the required size, depending upon the orientation. Rectangle { id: thumb opacity: 0.7 color: "black" radius: orientation == 'Vertical' ? (width/2 - 1) : (height/2 - 1) x: orientation == 'Vertical' ? 1 : (scrollBar.position * (scrollBar.width-2) + 1) y: orientation == 'Vertical' ? (scrollBar.position * (scrollBar.height-2) + 1) : 1 width: orientation == 'Vertical' ? (parent.width-2) : (scrollBar.pageSize * (scrollBar.width-2)) height: orientation == 'Vertical' ? (scrollBar.pageSize * (scrollBar.height-2)) : (parent.height-2) } } tests/system/qmlorganizer/contents/SelectionView.qml000066400000000000000000000047371233466112000234170ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 Rectangle { id: selectionView property alias title: title.text property alias model: container.model height: 150 width: calendar.width anchors.centerIn: parent opacity: 0 Column { spacing: 2 anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter } Text { id: title font { pointSize: 15; weight: Font.Bold } } Repeater { id: container } } } tests/system/qmlorganizer/contents/SettingsView.qml000066400000000000000000000075271233466112000232720ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtOrganizer 5.0 Item { id: view anchors.centerIn: parent opacity: 0 width: calendar.contentArea.width; height: calendar.contentArea.height; state: "CollectionManagerView" property alias collectionManagerView: collectionManagerView property alias buttonTabsRow : buttonTabsRow states: [ State {name: "CollectionManagerView"; PropertyChanges { target: collectionManagerView; opacity: 1; }}, State {name: "CollectionEditorView"; PropertyChanges { target: collectionEditorView; opacity: 1; }}, State {name: "StorageLocationView"; PropertyChanges { target: storageLocationView; opacity: 1; }} ] transitions: [ Transition { NumberAnimation { properties: "opacity" easing.type: "Linear" duration: 10 } } ] Row { id: buttonTabsRow anchors { top: parent.top } Button { text: "Collections" width: calendar.width / 2 onClicked: { view.state = "CollectionManagerView"} } Button { text: "Storage Locations" width: calendar.width / 2 onClicked: { view.state = "StorageLocationView" } } } CollectionManagerView { id: collectionManagerView; } CollectionEditorView { id: collectionEditorView; } StorageLocationView { id: storageLocationView; } function mapStorageLocationStringToInt(storageName) { if (storageName == "UserDataStorage") return OrganizerModel.UserDataStorage; else if (storageName == "SystemStorage") return OrganizerModel.SystemStorage; } } tests/system/qmlorganizer/contents/StatusBar.qml000066400000000000000000000071651233466112000225450ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 Item { id: statusbar property string status signal leftClicked signal rightClicked signal addClicked signal managerChangeClicked BorderImage { source: "images/titlebar.sci"; width: parent.width; height: parent.height + 14; y: -7 } Button {//add new item button id: addButton text: "+" anchors.left: parent.left; anchors.leftMargin: 2; y: 3; width: 30; height: 32 onClicked: statusbar.addClicked() } Button { id: leftButton text: "<" anchors.left: addButton.right; anchors.leftMargin: 2; y: 3; width: 30; height: 32 onClicked: statusbar.leftClicked() } Text { id:statusText color: "#ecc70a" text:status font.family: "Monospace" font.bold: true verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter font.pointSize: 6 anchors.left: leftButton.right anchors.leftMargin: 2 anchors.right: rightButton.left anchors.rightMargin: 2 y: 5 } Button { id: rightButton text: ">" anchors.right: todayButton.left; anchors.rightMargin: 2; y: 3; width: 30; height: 32 onClicked: statusbar.rightClicked() } Button { //change the current Date to Today id: todayButton; anchors.right: parent.right; anchors.leftMargin: 2; y: 3; width: 50; height: 32 text: "Today"; onClicked: { var tempdate = new Date(); calendar.currentDate = new Date(tempdate.getFullYear(), tempdate.getMonth(), tempdate.getDate()); } } } tests/system/qmlorganizer/contents/StorageLocationRoller.qml000066400000000000000000000046331233466112000251070ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtPim module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtOrganizer 5.0 RollerRow { label: "Storagelocation:" valueSet: createStorageLocationNameValueSet(); spinnerDelegate: Text { text: valueRoller.valueName(index).name; } function createStorageLocationNameValueSet() { var nameArray = new Array(); for (var i=0;i 0) { calendar.currentDate = new Date(timelineView.year, timelineView.month, currentIndex + 1); monthList.currentIndex = timelineView.month; currentIndex = timelineView.day - 1; } } } Component { id: dayHighlight Rectangle { width: dayList.width; height: dayList.height /7 ; color: "lightsteelblue" ;radius: 5 } } Component { id: dayDelegate Item { width : dayList.width height : childrenRect.height Column { Rectangle { height : 1 width : dayList.width color : "black" } Text { text: day } Repeater { focus: true model:calendar.organizer.items? calendar.organizer.itemsByTimePeriod(new Date(timelineView.year,timelineView.month, index + 1), new Date(timelineView.year,timelineView.month, index + 2)) : 0 Text { clip: true focus: true text: "" + modelData.displayLabel + "" onLinkActivated: { detailsView.isNewItem = false detailsView.item = modelData; if (detailsView.item.itemType == Type.EventOccurrence || detailsView.item.itemType == Type.TodoOccurrence) calendar.state = "OccurrenceDialogView"; else calendar.state = "DetailsView"; } } } } } } ListModel { id : dayModel ListElement {day : "1"} ListElement {day : "2"} ListElement {day : "3"} ListElement {day : "4"} ListElement {day : "5"} ListElement {day : "6"} ListElement {day : "7"} ListElement {day : "8"} ListElement {day : "9"} ListElement {day : "10"} ListElement {day : "11"} ListElement {day : "12"} ListElement {day : "13"} ListElement {day : "14"} ListElement {day : "15"} ListElement {day : "16"} ListElement {day : "17"} ListElement {day : "18"} ListElement {day : "19"} ListElement {day : "20"} ListElement {day : "21"} ListElement {day : "22"} ListElement {day : "23"} ListElement {day : "24"} ListElement {day : "25"} ListElement {day : "26"} ListElement {day : "27"} ListElement {day : "28"} ListElement {day : "29"} ListElement {day : "30"} ListElement {day : "31"} } } //Month view Rectangle { id:monthView color : "lightgray" width : 30 anchors.left : yearView.right anchors.top : parent.top anchors.bottom : parent.bottom ListView { id : monthList model : monthModel anchors.fill: parent clip: true delegate : monthDelegate highlight: monthHighlight preferredHighlightBegin: monthList.height * 0.5 preferredHighlightEnd: preferredHighlightBegin highlightFollowsCurrentItem : true highlightMoveSpeed : 1000 Component.onCompleted : { var now = new Date(); var month = now.getMonth(); monthList.currentIndex = month; var d = Date.parse("Feb 31, 2010"); } onCurrentIndexChanged : { if (timelineView.opacity > 0) { calendar.currentDate = new Date(timelineView.year, currentIndex, timelineView.day); currentIndex = timelineView.month; dayList.currentIndex = timelineView.day - 1; } } } Component { id: monthHighlight Rectangle { width: monthList.width; height: monthList.height / 12 ; color: "lightsteelblue" ;radius: 5 } } Component { id: monthDelegate Item { width : monthList.width height : monthList.height / 12 Column { Rectangle { height : 1 width : monthList.width color : "black" } Text { text: month } } MouseArea { anchors.fill: parent onClicked : monthList.currentIndex = index } } } ListModel { id : monthModel ListElement {month : "Jan"} ListElement {month : "Feb"} ListElement {month : "Mar"} ListElement {month : "Apr"} ListElement {month : "May"} ListElement {month : "Jun"} ListElement {month : "Jul"} ListElement {month : "Aug"} ListElement {month : "Sep"} ListElement {month : "Oct"} ListElement {month : "Nov"} ListElement {month : "Dec"} } } //Year view Rectangle { id:yearView color : "gray" anchors.top : parent.top anchors.bottom : parent.bottom anchors.left : parent.left width : 50 Component { id: yearHighlight Rectangle { width: yearList.width; height: yearList.height / 10; color: "lightsteelblue" ;radius: 5 } } Component { id: yearDelegate Item { width : yearList.width height : yearList.height / 10 Column { Rectangle { height : 1 width : yearList.width color : "black" } Text { text: year } } MouseArea { anchors.fill: parent onClicked : yearList.currentIndex = index } } } ListView { id : yearList model : yearModel delegate : yearDelegate anchors.fill: parent clip: true highlight: yearHighlight preferredHighlightBegin: yearList.height * 0.5 preferredHighlightEnd: preferredHighlightBegin highlightRangeMode: "StrictlyEnforceRange" highlightFollowsCurrentItem : true Component.onCompleted: Timeline.extendYearModel(true); onCurrentIndexChanged: { Timeline.extendYearModel(false); if (timelineView.opacity > 0) { calendar.currentDate = new Date(yearModel.start + currentIndex, timelineView.month, timelineView.day); monthList.currentIndex = timelineView.month; dayList.currentIndex = timelineView.day - 1; } } ListModel { id : yearModel property int start : 0; property int end : 0; ListElement { year : 0; } } } } } tests/system/qmlorganizer/contents/TodoView.qml000066400000000000000000000053651233466112000223750ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtOrganizer 5.0 Item { anchors.centerIn: parent opacity: 0 width: calendar.width; height: calendar.height - menuBar.height - statusBar.height - 50; Column { spacing: 2 width: parent.width; height: parent.height; Text { text: "Todos:" height: 30 anchors { horizontalCenter: parent.horizontalCenter } color: "white" font { pointSize: 15; weight: Font.Bold } } ListView { width: parent.width; height: parent.height; model: organizer.items clip: true delegate: Text { anchors { horizontalCenter: parent.horizontalCenter } color: "white" text: "- " + displayLabel + " -" } } } } tests/system/qmlorganizer/contents/WeekView.qml000066400000000000000000000115321233466112000223540ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtOrganizer 5.0 Rectangle { id:weekView anchors.fill: parent ListView { id : dayList anchors.fill: parent clip: true focus: true opacity : parent.opacity preferredHighlightBegin: dayList.height * 0.5 preferredHighlightEnd: preferredHighlightBegin highlightFollowsCurrentItem : true highlightMoveSpeed : 2000 keyNavigationWraps : true Component.onCompleted : positionViewAtIndex(currentIndex, ListView.Beginning) onOpacityChanged: { currentIndex = calendar.weekDay; } model : ListModel { ListElement {day : "Sunday"} ListElement {day : "Monday"} ListElement {day : "Tuesday"} ListElement {day : "Wednesday"} ListElement {day : "Thursday"} ListElement {day : "Friday"} ListElement {day : "Saturday"} } delegate: dayDelegate Component { id: dayDelegate Item { width : dayList.width height : childrenRect.height Column { Rectangle { height : 1 width : dayList.width color : "black" } Text { text: day } Repeater { focus: true model: calendar.organizer.items? calendar.organizer.itemsByTimePeriod(new Date(calendar.year,calendar.month, index - calendar.weekDay + calendar.day), new Date(calendar.year,calendar.month, index - calendar.weekDay + calendar.day + 1)) : 0 Text { clip: true focus: true text: "a " + modelData.displayLabel + "" onLinkActivated: { detailsView.isNewItem = false; detailsView.item = modelData; if (detailsView.item.itemType == Type.EventOccurrence || detailsView.item.itemType == Type.TodoOccurrence) calendar.state = "OccurrenceDialogView"; else calendar.state = "DetailsView"; } } } } } } highlight: Component { Rectangle { width: dayList.width height: dayList.height /7 color: "lightsteelblue" radius: 5 } } } } tests/system/qmlorganizer/contents/datetimerollercontents/000077500000000000000000000000001233466112000247035ustar00rootroot00000000000000tests/system/qmlorganizer/contents/datetimerollercontents/Spinner.qml000077500000000000000000000056521233466112000270470ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 Image { property alias model: view.model property alias delegate: view.delegate property alias currentIndex: view.currentIndex property real itemHeight: 15 source: "spinner-bg.png" clip: true focus: true width: 30; height: 60 PathView { id: view anchors.fill: parent clip: true pathItemCount: height/itemHeight preferredHighlightBegin: 0.5 preferredHighlightEnd: 0.5 //highlight: Rectangle { color: "lightblue"; width: view.width; height: itemHeight+4; } highlight: Image { source: "spinner-select.png"; width: view.width; height: itemHeight+4 } dragMargin: view.width/2 path: Path { startX: view.width/2; startY: -itemHeight/2 PathLine { x: view.width/2; y: view.pathItemCount*itemHeight + itemHeight } } } Keys.onDownPressed: view.incrementCurrentIndex() Keys.onUpPressed: view.decrementCurrentIndex() } tests/system/qmlorganizer/contents/datetimerollercontents/Title.qml000066400000000000000000000042431233466112000265020ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtDeclarative module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 Rectangle { height: 10 property alias label: text.text color: "black" radius: 3 Text { id: text anchors.centerIn: parent font.pixelSize: 10 color: "white" } } tests/system/qmlorganizer/contents/datetimerollercontents/spinner-bg.png000077500000000000000000000005311233466112000274570ustar00rootroot00000000000000PNG  IHDRsRGBbKGD pHYs B(xtIME 4&. IDATh 0 EQ8d&hb:CeSYfʺxֺB>fD&)|==z+XaD.cV;~O@vE= Rr՘eyim#; =~%7 uAnȶQfFILT5t<3b STYJfītLhll8 n͖ǫNIENDB`tests/system/qmlorganizer/contents/datetimerollercontents/spinner-select.png000077500000000000000000000005001233466112000303420ustar00rootroot00000000000000PNG  IHDR[, sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAThԱ !QE+q Ȉ5 $`'dlAdlAdlAdlA )#v[|?IENDB`tests/system/qmlorganizer/contents/images/000077500000000000000000000000001233466112000213565ustar00rootroot00000000000000tests/system/qmlorganizer/contents/images/button-pressed.png000066400000000000000000000010731233466112000250430ustar00rootroot00000000000000PNG  IHDR^ڂsRGBbKGD̿ pHYs!3tIME.!3tEXtCommentCreated with GIMPWIDATX׽nPo&hM"hBhyz$ BmD {*[wF>g+?6S)=}'0+#q[I/FU9ut $yO{cޚZjn펉 ,ԲYEpJ+SKmJ@6$Xz!,wz݇IG# M(/:XcZ |k)gi|;[ÒD >:A vvU ޽kIG! Z{U6M9v㖵衶*]eJ"6(3xv=&6x@vHQCR a ̝z_G?(ı7w&W ޵˯,87#qiL9̘L2#7qݱ}r^zGFIENDB`tests/system/qmlorganizer/contents/images/button.png000066400000000000000000000010641233466112000234000ustar00rootroot00000000000000PNG  IHDR^ڂsRGBbKGD̿ pHYs!3tIME- tEXtCommentCreated with GIMPWIDATXՖN0?'Nz7a@b 11,0 6`ae)*MBÐK!!x?GM,fh <$$ʘ{&jrTj`0s2Sꕡ image/svg+xml weather-clear January 2006 Ryan Collier (pseudo) http://www.tango-project.org http://www.pseudocode.org weather applet notification Garrett LeSage tests/system/qmlorganizer/contents/images/gloss.png000077500000000000000000000023241233466112000232170ustar00rootroot00000000000000PNG  IHDROO4sRGBbKGD pHYs B(xtIME˒tTIDATx\R1l`m/GxqP VX(\jM =8H{uyPJ1L7o{OSkc灈EG̢Hy z50Sר J*G97 C9ע,hsMGڵ ҥAt=.Ŷh`.LIHh)Mڜ1pY+yeiՑ MҨ٤mff!֊>ӪV-2ϴ*>ѲJ#ǦekuBt+V|ʳRS1)WS6Mi{< `b@,a"H1x?nrq %SyVZq7g]r5O) iQkKQM 0JP'W1F9 Y ֭jlr<KQdvp=9ϫi%CzKK zA[hmZVgmZ7Wr n<ibdDsDb[`SrœWčQ:Z$牟bGgMs,*&t9?MoĻHl 9@^rU/zAp84Z˘L@lw6lɶxƙzϙzh~no1ϥU*Qu(,r8lEg1^xV3@\k\\iI5H/\P }%<0f)X0٥?xFlcÛ䌑:\J B#ӂga* m}̚;Z'y! xf*v N +pl΢g_X Wq Xw}k/]}G}G}G^Q.IENDB`tests/system/qmlorganizer/contents/images/lineedit.png000077500000000000000000000026071233466112000236710ustar00rootroot00000000000000PNG  IHDR,&ygsRGBbKGD pHYs  tIME*g?tEXtCommentCreated with GIMPWIDATXk\UrL&43I&R/$BE}ST/*PZ/ji49|LLl5II>g^{^cW[^l(G?i ׺\s8gN}xDZj@nrzzWtK A2e.ũZ4:LYpO(QÙs:rr_7yioǖzs@WWPιgq>`U""]e4;G@_cg_-G>eL<}3E~QVDNWۏa ƝC#m'kdr౗ttF˲TP-؝_]=P7WÄ({l)cIݽ۪|@@` +ڌhk!D`bÃ;`^/TI:~̰'8*iL)\LmKO?N!tihJ-[Ǫdi]p(8D.H}igIJ/ Zߕ2\Β%3y `6i޹h[=ϹU{UCrqiNeeW5]ƺ ?ַ=ḳxMߪ[WUr 7/_W>6xz`<8 !dYc\V5(34 Xk"SWeYW=>aX,-9MS(TEȲ c2}1s2hMaАi8F̎N :\G=̠N}M Lԓ:?~vxM!*EUKYt-iLLM04zFK4Fp#v bȱ v!Bsk3kX20jBs޽׳\ys?8aH ]44ˢ}>|Ҏ^7V9}fCy#mjI{…?1No-Rx ,,j c3轩Dykg 4 wl+xדjmC4FόҘA&SdxW4 }@W_B__ /7VsrIz(|ʁΡ"m<6Iϳ\E"U5\1lId%.1>)YzqFcIENDB`tests/system/qmlorganizer/contents/images/lineedit.sci000066400000000000000000000001271233466112000236530ustar00rootroot00000000000000border.left: 10 border.top: 10 border.bottom: 10 border.right: 10 source: lineedit.png tests/system/qmlorganizer/contents/images/quit.png000077500000000000000000000045011233466112000230510ustar00rootroot00000000000000PNG  IHDR**[sRGBbKGD pHYs  tIME/~tEXtCommentCreated with GIMPWIDATXkohf׌4޶%?"x/YYXœ`?`Bbr98@r2#,@bXьy*ë\ЌGՇ_+?tEp\X\<ͲܹbDšbS,ȥť]rjz}]׫iKv: % wkWWF(888(db8VT*ܼvj:7; Qn%I:;qW 4 SD(Jl&[l4φ]]]\HRi,h(nwP2?7w7_YYχ¶mJR!ؕ뺮uyR$Nԇ:-!>ItAP{Y\6o^]{N\>CUU\^^4TU`R ۲ aHn󼦪@Ņl.^O `#xp(Jr!N_~ᇟmiid2P] u8( !(J7 44pb,64.(1P`F(˅d2YlZp֭Bxft:p` {¢~gϞU^ɯ|vCB(/ʲ 0_a<(0 [[[=4?4mzC˲n,4M FfT(ss\0YꬋyuRJL&]8=J` ̥Pm?۶'|jy!BA>#a8w2C$AVCRE(4RkUB8Il6aY(p)}S &&0==nnջys;|p5A4 p&p<8/dJu˲oi~<+Gs0vp8B|EȈ;R ]1?7ץal0<_84߱-k7y[ y4xkFt],cm۷ 83LٟYhA8Y vq^9=` n XߙopHBq5 LRT*˴F a`gpvL&m&xG@HRGޠx,YaYk)@c. D>cC'b M&PJzv x5^~?vW9l.cUkB4$I0-  08N퓱obqhmmMy^1ǽIߏx">v*fs @njF!J,NJid dbވ}Tܼt鲐Ni@e crwh44't:ۉXXyF!"!8871J˲8۶~K˲`pV"=0S!H4G>/~?fffpppoאbDRNG!,U4o.YnQ,Ai_exa>vuQ(2lwiJ%T7#_&( DmCF" >?qU*~ya::: v%Y,p]Qo*ZDhwۉDbR(p"C4<á}\.MSё!˲D"s!vlVL :{r3|HAt:eaggRVυ<^oGzVwww1J'"ud2_(egff\.މOF.p>鿁|?y3-EIENDB`tests/system/qmlorganizer/contents/images/stripes.png000077500000000000000000000004011233466112000235530ustar00rootroot00000000000000PNG  IHDRsRGBbKGD pHYs  tIMEIDATӝͱ 0Eʎw!h1k3=+h@"R*^{twq p糔Rof YDd_a/:Uu@XJٛ٩֊xfUͫcBYU"r\!x+FļIENDB`tests/system/qmlorganizer/contents/images/titlebar.png000077500000000000000000000026341233466112000237020ustar00rootroot00000000000000PNG  IHDR1,PYsRGBbKGD pHYs  tIME/+ƵtEXtCommentCreated with GIMPWIDATh՚nGV0..p^ϒ"m<]j7V9MΤݻ㑒X܁w%,j6u4:U.ZXM` z{>˪;YMYj@ d!A

/~b9A5Ͼ3...x͟ {9>|M\!H#n/\zxϊ%#c hW* .5#=]C=zׯ\]]qyyjm["y&@@^bPcֳQ_@ fC9999ӧE#T}j9z$Wh6t=f6b @z.KK]X,\^^c|ͦDb@UٴY.=ݻ۔ C5#"Cx$X1QhLM-QMlhfofqn~4H5ͣd\ AAD,[;$V E0g ,Vri!NY$nYm#86]7`j ѹDD$<1} fPI%h:|c^pQX0S( :۳4Fb'bh5"&U*ľRBV|Bu˚"h ;7ry21f%+UC|VTv'㋙|MDP1F%9eTuR9]Z@cCZ"3ű@|CUS"˩m[K3Qr.HEvebMY-~nXs5Zzy+~m*A?DfMUq%C{ _(%&nz^2q웯):'%A7wy?`O>q}k; 鏼 9wA*"4Mgr*_N71TuIC7h=ljiT~7~~_1[jѡujO;?0XJE8l&`"?Ěq,c0) **daFA)g;'+3q@Fzf|1'"jDIENDB`tests/system/qmlorganizer/contents/images/titlebar.sci000066400000000000000000000001271233466112000236640ustar00rootroot00000000000000border.left: 10 border.top: 12 border.bottom: 12 border.right: 10 source: titlebar.png tests/system/qmlorganizer/contents/images/toolbutton.png000077500000000000000000000047661233466112000243150ustar00rootroot00000000000000PNG  IHDR@*]bKGD pHYs  tIME  תtEXtCommentCreated with GIMPW ^IDAThޝZndI=yWdFPPo@|BFزdVif7L[j,,fn\,fW%ˮfy|ꎝ~mB[M_6?V/!zfjǟ'8;;ӧOqttT,N2B隀1Hd`9~Z4D=%fg DX׸?_|vŋ׿gF pJppwxvl"B_B8>>'?īW/=3!{iQ|00`[ |?1o.@ nK|5O> "`L^睢ũT .AQW1K{kcv-sc&03&nPU03D"R?| PR @!y3*]IpH&،ȇ}v~ [n G@!0Mv9]P]M4`9ؤ"b ˎ{ !˂r% {`ay(T "s.!O|Xa @|(hoymу2)+1]my}"KMpqqB4 4L%v,, hs{o,~8Y”sa9f,(ߤsAJCŊ} X$duR봕&ct@}o6 z 0 ]`M_LU Rԩ43I> ZLZ2d@BBy)p2eqd=ߋa?bX]#3 D IFE;X[ Bǎ^/4`< iw qbKTRxc)/R.~@@WQjr{b1 @#ڨ;yۚy(!+GZ]B*,E} T1JX x&I60*JMLz7$/l)_H Txb&^g:/lK`zA o1*Z #q3r &AJ~nm6H@B?)&l,t!y*0#]XgQ)jṬaSjO9Q6VF;9-Z|!p6*mZ ^Ԉw=|YXmY,PTҞ+ι V=ڔ$CzSSY=GI x!i-g;C=z>J}rܼ_)Ȼ.Vɽ eSeKv{n@)A--G("r{/YTڳƺ8"cfza\!<gnh3%J H҉1xN'Tev~ڈYz^_1"Oy*=::9޽{Wn_///ǏlrL qnN%:/\UB9PUYhi Û7o[_~vpsSxrݳr}6Jdݫ=Y@*&U\dDIENDB`tests/system/qmlorganizer/contents/images/toolbutton.sci000066400000000000000000000001271233466112000242670ustar00rootroot00000000000000border.left: 15 border.top: 4 border.bottom: 4 border.right: 15 source: toolbutton.png tests/system/qmlorganizer/contents/month.js000066400000000000000000000053621233466112000216020ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ Date.prototype.clone = function() { return new Date(this.getTime()); } function getDayOfMonth(startDayOfMonth, offset) { var day = startDayOfMonth.clone(); day.setDate(offset); return day.getDate(); } function dateOfThisDay(startDayOfMonth, offset) { var day = startDayOfMonth.clone(); day.setDate(offset); return day; } function getColorOfDay(startDayOfMonth, offset) { var newDay = startDayOfMonth.clone(); newDay.setDate(offset); if (newDay.getMonth() == startDayOfMonth.getMonth()) return "white"; else return "lightgray"; } function isToday(startDayOfMonth, offset) { var newDay = startDayOfMonth.clone(); newDay.setDate(offset); var today = new Date(); return newDay.toDateString() == today.toDateString(); } tests/system/qmlorganizer/contents/test.ics000066400000000000000000000051461233466112000215760ustar00rootroot00000000000000BEGIN:VCALENDAR PRODID:-//Google Inc//Google Calendar 70.9054//EN VERSION:2.0 CALSCALE:GREGORIAN METHOD:PUBLISH X-WR-TIMEZONE:Australia/Brisbane BEGIN:VTIMEZONE TZID:Australia/Brisbane X-LIC-LOCATION:Australia/Brisbane BEGIN:STANDARD TZOFFSETFROM:+1000 TZOFFSETTO:+1000 TZNAME:EST DTSTART:19700101T000000 END:STANDARD END:VTIMEZONE BEGIN:VEVENT DTSTART:20101208T220000Z DTEND:20101209T070000Z DTSTAMP:20101208T051153Z UID:2b0kl4063br3vsaeucrtcjbsjg@google.com CREATED:20101208T050327Z DESCRIPTION:starts 2010-12-09 8AM finishes 5PM LAST-MODIFIED:20101208T050327Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 2 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART:20101207T220000Z DTEND:20101208T030000Z DTSTAMP:20101208T051153Z UID:damefeu5f2nqh3gn4m5k8lb5uc@google.com CREATED:20101208T050202Z DESCRIPTION:starts 2010-12-08 8AM finishes 1PM LAST-MODIFIED:20101208T050202Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 1 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART:20101216T000000Z DTEND:20101216T060000Z DTSTAMP:20101208T051153Z UID:boml7lf8psqpm0162vqj950rhc@google.com CREATED:20101208T050550Z DESCRIPTION:starts after Event 3 and finishes 4PM LAST-MODIFIED:20101208T050550Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 4 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART:20101216T060000Z DTEND:20101216T070000Z DTSTAMP:20101208T051153Z UID:ev13id8q3hivd567snio7gcga4@google.com CREATED:20101208T050643Z DESCRIPTION:start after end of Event 4 finishing at 5PM LAST-MODIFIED:20101208T050643Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 5 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART:20101215T220000Z DTEND:20101216T000000Z DTSTAMP:20101208T051153Z UID:888gl35dsh59cp9pksj05c8ruc@google.com CREATED:20101208T050446Z DESCRIPTION:starts 2010-12-15 at 8AM finishes 10AM LAST-MODIFIED:20101208T051055Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 3 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART:20110108T010000Z DTEND:20110108T020000Z DTSTAMP:20101208T051153Z UID:uh7i70c6ir733bp2i1ccpmgeas@google.com CREATED:20101208T050752Z DESCRIPTION:start a month from 2010-12-08 at 11AM finish at 2PM LAST-MODIFIED:20101208T050752Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 6 TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;TZID=Australia/Brisbane:20101210T103000 DTEND;TZID=Australia/Brisbane:20101210T113000 RRULE:FREQ=WEEKLY;INTERVAL=4;BYDAY=FR DTSTAMP:20101208T051153Z UID:0oc9mbqeddtdag0406psh8ckic@google.com CREATED:20101208T050926Z DESCRIPTION:starts 2010-12-10 at 11AM finishing 1PM\, repeating for 4 weeks LAST-MODIFIED:20101208T050926Z LOCATION: SEQUENCE:0 STATUS:CONFIRMED SUMMARY:Event 7 TRANSP:OPAQUE END:VEVENT END:VCALENDAR tests/system/qmlorganizer/contents/timeline.js000066400000000000000000000063341233466112000222630ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ function changeDate() { //TODO } function changeToday() { yearList.currentIndex = timelineView.year - yearModel.start; monthList.currentIndex = timelineView.month; dayList.positionViewAtIndex(timelineView.day, ListView.Center); dayList.currentIndex = timelineView.day; } function extendYearModel(init) { var start = yearModel.start; var end = yearModel.end; var now = new Date(); var year = 1900 + now.getYear(); if (init) { //initializes the year model if (yearModel.count == 1) { yearModel.set(0, {"year" : year}); start = year; end = year; } } if (start == 0) return; //extends forward if (yearList.currentIndex == yearList.count - 1) { for (var i = 0; i < 10; i ++) { end++; yearModel.append({"year" : end}); } } //extends backward if (yearList.currentIndex == 0) { for (var i = 0; i < 10; i ++) { start--; if (start == 1900) break; yearModel.insert(1, {"year" : start}); } yearModel.move(0, 10, 1); } yearModel.start = start; yearModel.end = end; if (init) { yearList.currentIndex = year - start; } } tests/system/qmlorganizer/organizer.qml000066400000000000000000000333471233466112000210010ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ import QtQuick 2.0 import QtOrganizer 5.0 import "contents" Rectangle { id: calendar width: 400 height: 640 property date currentDate:new Date() property int year:currentDate.getFullYear() property int month:currentDate.getMonth() property int day:currentDate.getDate() property int hour:currentDate.getHours() property int weekDay:currentDate.getDay() property string status:currentDate.toDateString() property string preState: "MonthView" property alias storageLocationModel: storageLocationModel property alias contentArea : contentArea color: "#343434"; Image { source: "contents/images/stripes.png"; fillMode: Image.Tile; anchors.fill: parent; opacity: 1 } state: "MonthView"; CollectionFilter { id: modelCollectionFilter ids: { var idList = []; var list = organizer.collections; for (var i = 0; i< list.length; i++) { idList.push(list[i].collectionId); } return idList; } } DetailFieldFilter { id: todoFilter detail: Detail.ItemType field: Type.FieldType value: Type.Todo } IntersectionFilter { id: intersectionFilter filters: [modelCollectionFilter] } SystemPalette { id: activePalette } property OrganizerModel organizer:OrganizerModel{ id: organizer manager:"qtorganizer:jsondb:id=qml" //manager:"qtorganizer:memory:id=qml" startPeriod:'2009-01-01' endPeriod:'2012-12-31' autoUpdate:true filter: intersectionFilter Component.onCompleted : { if (managerName == "memory") organizer.importItems(Qt.resolvedUrl("contents/test.ics")); } onItemsFetched: { // this is for occurrenceDialog console.log("QML --- ITEMS FETCHED" + fetchedItems[0].displayLabel + fetchedItems[0].itemStartTime); if (fetchedItems.length == 1) { detailsView.isNewItem = false; detailsView.item = fetchedItems[0]; calendar.state = "DetailsView"; } } } ListModel { id: storageLocationModel ListElement{name: "UserDataStorage"} ListElement{name: "SystemStorage"} } InfoBar { id: infoBar; anchors { left: parent.left; right: parent.right; top: parent.top } height: 20 } MenuBar { id: menuBar; anchors { left: parent.left; right: parent.right; top: infoBar.bottom } height: 35; opacity: 0.9 info: organizer.error + "\nTotal:" + organizer.itemCount } StatusBar { id: statusBar; status:calendar.status; width: parent.width; height: 35; opacity: 0.9; anchors.bottom: calendar.bottom onLeftClicked: { if (calendar.state == "MonthView") { calendar.currentDate = new Date(calendar.year, calendar.month - 1, calendar.day); } else if (calendar.state == "WeekView") { calendar.currentDate = new Date(calendar.year, calendar.month , calendar.day - 7); } else if (calendar.state == "DayView" || calendar.state == "TimelineView") { calendar.currentDate = new Date(calendar.year, calendar.month , calendar.day - 1); } } //rightClick onRightClicked: { if (calendar.state == "MonthView") { calendar.currentDate = new Date(calendar.year, calendar.month + 1, calendar.day); } else if (calendar.state == "WeekView") { calendar.currentDate = new Date(calendar.year, calendar.month , calendar.day + 7); } else if (calendar.state == "DayView" || calendar.state == "TimelineView") { calendar.currentDate = new Date(calendar.year, calendar.month , calendar.day + 1); } } //add new item clicked onAddClicked : { calendar.preState = calendar.state; if (calendar.state == "SettingsView" && settingsView.state == "CollectionManagerView") { settingsView.collectionManagerView.addCollection(); } else { calendar.state = "AddNewItemSelectView"; } } } states: [ State {name: "MonthView"; PropertyChanges { target: monthView; opacity: 1; }}, State {name: "TimelineView"; PropertyChanges { target: timelineView; opacity: 1; }}, State {name: "WeekView"; PropertyChanges { target: weekView; opacity: 1; }}, State {name: "DayView"; PropertyChanges { target: dayView; opacity: 1; }}, State {name: "AgenderView"; PropertyChanges { target: agenderView; opacity: 1; }}, State { name: "DetailsView"; PropertyChanges { target: detailsView; opacity: 1;} PropertyChanges { target: statusBar; opacity: 0; } }, State {name: "AddNewItemSelectView"; PropertyChanges { target: addNewItemview; opacity: 0.8; }}, State {name: "OccurrenceDialogView"; PropertyChanges { target: occurrenceDialog; opacity: 0.8; }}, State {name: "TodoView"; PropertyChanges { target: todoView; opacity: 1; }}, State {name: "AttendeeDetailsView"; PropertyChanges { target: attendeeDetailsView; opacity: 1; }}, State {name: "SettingsView"; PropertyChanges { target: settingsView; opacity: 1; }} ] transitions: [ Transition { NumberAnimation { properties: "opacity" easing.type: "Linear" duration: 10 } } ] // some views are based on certain filters onStateChanged: { if (state == "TodoView") { intersectionFilter.filters = [todoFilter, modelCollectionFilter]; } else if (intersectionFilter.filters.length != 1) { // No need to change the filter if filter is the same // (Currently changing the filter triggers also a full update.) intersectionFilter.filters = [modelCollectionFilter]; } } Item { id: contentArea; anchors.top: menuBar.bottom; anchors.left: calendar.left; anchors.right: calendar.right; anchors.bottom: (statusBar.opacity != 0) ? statusBar.top : statusBar.bottom; MonthView { id: monthView; width: calendar.width; height: calendar.height - menuBar.height - statusBar.height; opacity: 0; month:calendar.month year:calendar.year anchors.fill: contentArea; } TimelineView { id: timelineView; width: calendar.width; height: calendar.height - menuBar.height - statusBar.height; opacity: 0; anchors.fill: contentArea; } WeekView { id: weekView; width: calendar.width; height: calendar.height - menuBar.height - statusBar.height; opacity: 0; anchors.fill: contentArea; } DayView { id: dayView; width: calendar.width; height: calendar.height - menuBar.height - statusBar.height; opacity: 0; anchors.fill: contentArea; } AgenderView { id: agenderView; width: calendar.width; height: calendar.height - menuBar.height - statusBar.height; opacity: 0; anchors.fill: contentArea; } DetailsView { id: detailsView; width: calendar.width; height: calendar.height - menuBar.height - statusBar.height; opacity: 0; anchors.fill: contentArea; } SelectionView { id: addNewItemview; title: "Select type:" model: VisualItemModel { Button { text: "New event" width: addNewItemview.width / 2 onClicked: { detailsView.isNewItem = true; detailsView.item = createEmptyItem(Type.Event); detailsView.item.displayLabel = "Event"+(organizer.itemCount+1) calendar.state = "DetailsView"; } } Button { text: "New todo-item" width: addNewItemview.width / 2 onClicked: { detailsView.isNewItem = true; detailsView.item = createEmptyItem(Type.Todo); detailsView.item.displayLabel = "Todo"+(organizer.itemCount+1) calendar.state = "DetailsView"; } } Button { text: "Cancel" width: addNewItemview.width / 2 onClicked: { calendar.state = calendar.preState; } } } } SelectionView { id: occurrenceDialog; //title: "This is a recurring item. Open this instance or whole series?" title: "Recurring item" model: VisualItemModel { Button { text: "Open this instance" width: addNewItemview.width / 2 onClicked: { calendar.state = "DetailsView"; } } Button { text: "Open whole series" width: addNewItemview.width / 2 onClicked: { var parentDetail = detailsView.item.detail(Detail.Parent); organizer.fetchItems([parentDetail.parentId]); } } } } TodoView { id: todoView; } AttendeeDetailsView { id: attendeeDetailsView; } SettingsView { id: settingsView; } } function createEmptyItem(type) { if (type == Type.Event) { return Qt.createQmlObject("import QtOrganizer 5.0; Event { }", organizer); } else if (type == Type.Todo) { return Qt.createQmlObject("import QtOrganizer 5.0; Todo { }", organizer); // } else if (type == Type.EventOccurrence) { // return Qt.createQmlObject("import QtOrganizer 5.0; EventOccurrence { }", organizer); // } else if (type == Type.TodoOccurrence) { // return Qt.createQmlObject("import QtOrganizer 5.0; TodoOccurrence { }", organizer); } else { return Qt.createQmlObject("import QtOrganizer 5.0; Event { }", organizer); } } } tests/system/qmlorganizer/qmlorganizer.qmlproject000066400000000000000000000005571233466112000230770ustar00rootroot00000000000000import QmlProject 1.0 Project { /* Include .qml, .js, and image files from current directory and subdirectories */ QmlFiles { directory: "." } JavaScriptFiles { directory: "." } ImageFiles { directory: "." } /* List of plugin directories passed to QML runtime */ // importPaths: [ " ../exampleplugin " ] } tests/tests.pro000066400000000000000000000002271233466112000141030ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS += auto # disable 'make check' on Mac OS X and Windows for the time being mac|win32 { auto.CONFIG += no_check_target }