pax_global_header00006660000000000000000000000064135303275530014520gustar00rootroot0000000000000052 comment=6d15d4452236cab4821918b722fac38f5d00133d boats-201908/000077500000000000000000000000001353032755300127355ustar00rootroot00000000000000boats-201908/.gitignore000066400000000000000000000002771353032755300147330ustar00rootroot00000000000000# kdevelop *kdev* # qt-creator qtc-* # windows *.dll # qt .moc qrc_* *qm #products Makefile* Doxyfile doxygen/* tags .obj # release Changelog* diffstat* # project boats boats.exe *xbs boats-201908/COPYING000066400000000000000000001043741353032755300140010ustar00rootroot00000000000000 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 . boats-201908/TODO000066400000000000000000000006611353032755300134300ustar00rootroot00000000000000Important: - fix printing document - statechart Normal: - toggle number display - arrows - more boat shapes - open in new tab - more flags - fix shortcut clashes - save default series - save default mark color - spin trim Wishlist: - gpx import - backtrack edit - insert between boats - starting a boat at a later number - timing for the start boat - animate spin hoist and drop - create track in a series different from scenario boats-201908/animation/000077500000000000000000000000001353032755300147145ustar00rootroot00000000000000boats-201908/animation/angleanimation.cpp000066400000000000000000000033171353032755300204120ustar00rootroot00000000000000// // C++ Implementation: AngleAnimation // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #include "angleanimation.h" AngleAnimation::AngleAnimation(QObject *target, const QByteArray &propertyName, QObject *parent) : PropertyAnimation(target, propertyName, parent) { } QVariant AngleAnimation::interpolated ( const QVariant & from, const QVariant & to, qreal progress ) const { qreal valueBefore = from.toReal(); qreal valueAfter = to.toReal(); qreal minValue = qMin(valueBefore,valueAfter); qreal maxValue = qMax(valueBefore,valueAfter); if (maxValue - minValue <= 360 + minValue - maxValue) { // Do a simple linear interpolation. return valueBefore + (valueAfter - valueBefore) * progress; } else { // Do a reverse linear interpolation. if (valueBefore > valueAfter) return valueBefore + (360 + valueAfter - valueBefore) * progress; else return valueBefore + (valueAfter - valueBefore - 360) * progress; } } boats-201908/animation/angleanimation.h000066400000000000000000000030521353032755300200530ustar00rootroot00000000000000// // C++ Interface: AngleAnimation // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #ifndef ANGLEANIMATION_H #define ANGLEANIMATION_H #include "propertyanimation.h" /** \class AngleAnimation \brief Animation for angles with correct rotation The class is the animation helper for an angle, according to the Animation Framework. It inherits QVariantAnimation and provides proper angle animation to BoatGraphicsItem elements, like heading or sail position, so that the shortest angle is taken between two options \sa BoatGraphicsItem */ class AngleAnimation : public PropertyAnimation { public: AngleAnimation(QObject *target, const QByteArray &propertyName, QObject *parent = 0); virtual QVariant interpolated ( const QVariant & from, const QVariant & to, qreal progress ) const; }; #endif // ANGLEANIMATION_H boats-201908/animation/animation.pri000066400000000000000000000007631353032755300174150ustar00rootroot00000000000000INCLUDEPATH += $$PWD DEPENDPATH += $$PWD HEADERS += \ $$PWD/angleanimation.h \ $$PWD/boatanimation.h \ $$PWD/headinganimation.h \ $$PWD/propertyanimation.h \ $$PWD/splineanimation.h \ $$PWD/scenarioanimation.h \ $$PWD/trackanimation.h SOURCES += \ $$PWD/angleanimation.cpp \ $$PWD/boatanimation.cpp \ $$PWD/headinganimation.cpp \ $$PWD/propertyanimation.cpp \ $$PWD/splineanimation.cpp \ $$PWD/scenarioanimation.cpp \ $$PWD/trackanimation.cpp boats-201908/animation/boatanimation.cpp000066400000000000000000000141401353032755300202450ustar00rootroot00000000000000// // C++ Implementation: BoatAnimation // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #include #include "boatanimation.h" #include "boats.h" #include "angleanimation.h" #include "headinganimation.h" #include "splineanimation.h" #include "trackmodel.h" #include "boatmodel.h" /** Populates the animation items for position, Headings and other attributes. This method parses the path of the \a track, does some stalling analysis and determines specific position animation. */ BoatAnimation::BoatAnimation(TrackModel *track, BoatModel *boat, int index, QObject *parent) : QParallelAnimationGroup(parent), m_track(track), m_boat(boat) { BoatModel *model = track->boats()[index]; QPainterPath curve = model->path(); QPointF point = curve.elementAt(0); QPointF c1 = curve.elementAt(1); QPointF c2 = curve.elementAt(2); QPointF end = curve.elementAt(3); bool stalled = ((point == c1) || (c2 == end)); // position SplineAnimation *splineAnimation = new SplineAnimation(m_boat, "pos", model->path()); splineAnimation->setDuration(2000); splineAnimation->setStartValue(point); splineAnimation->setEndValue(end); addAnimation(splineAnimation); // text PropertyAnimation *textAnimation = new PropertyAnimation(boat, "text"); textAnimation->setDuration(2000); textAnimation->setStartValue(model->text()); textAnimation->setEndValue(model->text()); addAnimation(textAnimation); // text position PropertyAnimation *textPosAnimation = new PropertyAnimation(boat, "textPos"); textPosAnimation->setDuration(2000); textPosAnimation->setStartValue(model->textPosition()); textPosAnimation->setEndValue(model->textPosition()); addAnimation(textPosAnimation); // laylines PropertyAnimation *laylinesAnimation = new PropertyAnimation(boat, "laylines"); laylinesAnimation->setDuration(2000); laylinesAnimation->setStartValue(model->laylines()); laylinesAnimation->setEndValue(model->laylines()); addAnimation(laylinesAnimation); // heading QVariantAnimation *headingAnimation; if (stalled) { headingAnimation = new AngleAnimation(boat, "heading"); } else { headingAnimation = new HeadingAnimation(boat, "heading", model->path()); } headingAnimation->setDuration(2000); headingAnimation->setStartValue(model->heading()); addAnimation(headingAnimation); AngleAnimation *windAnimation = new AngleAnimation(boat, "wind"); windAnimation->setDuration(2000); windAnimation->setStartValue(model->wind()); addAnimation(windAnimation); // sail angle AngleAnimation *sailAngleAnimation = new AngleAnimation(boat, "trimSailAngle"); sailAngleAnimation->setDuration(2000); sailAngleAnimation->setStartValue(model->trimmedSailAngle()); addAnimation(sailAngleAnimation); // jib angle AngleAnimation *jibAngleAnimation = new AngleAnimation(boat, "trimJibAngle"); jibAngleAnimation->setDuration(2000); jibAngleAnimation->setStartValue(model->trimmedJibAngle()); addAnimation(jibAngleAnimation); // spin PropertyAnimation *spinAnimation = new PropertyAnimation(boat, "spin"); spinAnimation->setDuration(2000); spinAnimation->setStartValue(model->spin()); addAnimation(spinAnimation); // spin angle AngleAnimation *spinAngleAnimation = new AngleAnimation(boat, "trimSpinAngle"); spinAngleAnimation->setDuration(2000); spinAngleAnimation->setStartValue(model->trimmedSpinAngle()); addAnimation(spinAngleAnimation); // gen angle AngleAnimation *genAngleAnimation = new AngleAnimation(boat, "trimGennAngle"); genAngleAnimation->setDuration(2000); genAngleAnimation->setStartValue(model->trimmedGennAngle()); addAnimation(genAngleAnimation); // overlap PropertyAnimation *overlapAnimation = new PropertyAnimation(boat, "overlap"); overlapAnimation->setDuration(2000); overlapAnimation->setStartValue(QVariant(model->overlap())); overlapAnimation->setEndValue(QVariant(model->overlap())); addAnimation(overlapAnimation); // flag PropertyAnimation *flagAnimation = new PropertyAnimation(boat, "flag"); flagAnimation->setDuration(2000); flagAnimation->setStartValue(model->flag()); flagAnimation->setEndValue(model->flag()); addAnimation(flagAnimation); // acceleration switch(model->acceleration()) { case Boats::accelerating: setEasingCurve(QEasingCurve::InSine); break; case Boats::decelerating: setEasingCurve(QEasingCurve::OutSine); break; default: break; } // end values that need next boat position model = m_track->boats()[index+1]; headingAnimation->setEndValue(model->heading()); windAnimation->setEndValue(model->wind()); sailAngleAnimation->setEndValue(model->trimmedSailAngle()); jibAngleAnimation->setEndValue(model->trimmedJibAngle()); spinAnimation->setEndValue(model->spin()); spinAngleAnimation->setEndValue(model->trimmedSpinAngle()); genAngleAnimation->setEndValue(model->trimmedGennAngle()); } BoatAnimation::~BoatAnimation() { } void BoatAnimation::setEasingCurve(const QEasingCurve &easing) { for (int i=0; i< animationCount(); ++i) { QVariantAnimation *animation = dynamic_cast(animationAt(i)); if(animation) { animation->setEasingCurve(easing); } } } boats-201908/animation/boatanimation.h000066400000000000000000000033711353032755300177160ustar00rootroot00000000000000// // C++ Interface: BoatAnimation // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #ifndef BOATANIMATION_H #define BOATANIMATION_H #include #include class TrackModel; class BoatModel; /** \class BoatAnimation \brief The animation Item for a boat in its track The class is the animation group for a boat, according to the Animation Framework. It inherits QParallelAnimationGroup and provides all animation features, like position and heading given a time step. \sa SituationScene, SituationModel */ class BoatAnimation : public QParallelAnimationGroup { public: BoatAnimation(TrackModel *track, BoatModel *boat, int index, QObject *parent = 0); ~BoatAnimation(); BoatModel *boat() const {return m_boat; } void setEasingCurve ( const QEasingCurve & easing ); private: /// \a m_track holds the reference to the track TrackModel *m_track; /// \a m_boat holds the reference to the boat BoatModel *m_boat; }; #endif boats-201908/animation/headinganimation.cpp000066400000000000000000000025771353032755300207320ustar00rootroot00000000000000// // C++ Implementation: HeadingAnimation // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #include #include "headinganimation.h" #include "angleanimation.h" HeadingAnimation::HeadingAnimation(QObject *target, const QByteArray &propertyName, const QPainterPath &path, QObject *parent) : PropertyAnimation(target, propertyName, parent), m_path(path) { } QVariant HeadingAnimation::interpolated ( const QVariant & from, const QVariant & to, qreal progress ) const { Q_UNUSED(from) Q_UNUSED(to) qreal length = m_path.length(); qreal percent = m_path.percentAtLength(length * progress); return fmod(360+90-m_path.angleAtPercent(percent),360.0); } boats-201908/animation/headinganimation.h000066400000000000000000000032031353032755300203620ustar00rootroot00000000000000// // C++ Interface: HeadingAnimation // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #ifndef HEADINGANIMATION_H #define HEADINGANIMATION_H #include #include "propertyanimation.h" /** \class HeadingAnimation \brief Animation for angles with correct rotation The class is the animation helper for an angle, according to the Animation Framework. It inherits QVariantAnimation and provides proper animation to BoatGraphicsItem elements along a QPainterPath, like the boat heading, so that the heading follows the path \sa BoatGraphicsItem */ class HeadingAnimation : public PropertyAnimation { public: HeadingAnimation(QObject *target, const QByteArray &propertyName, const QPainterPath &path, QObject *parent = 0); virtual QVariant interpolated ( const QVariant & from, const QVariant & to, qreal progress ) const; private: QPainterPath m_path; }; #endif // HEADINGANIMATION_H boats-201908/animation/propertyanimation.cpp000066400000000000000000000042221353032755300212040ustar00rootroot00000000000000// // C++ Implementation: PropertyAnimation // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2011 Thibaut GRIDEL // // 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 . // // #include #include "propertyanimation.h" #include "boats.h" PropertyAnimation::PropertyAnimation(QObject *target, const QByteArray &propertyName, QObject *parent) : QPropertyAnimation(target, propertyName, parent) { } void PropertyAnimation::updateCurrentValue ( const QVariant & value ) { // doesn't work because QPropertyAnimationPrivate::updateProperty checks for not Stopped // QPropertyAnimation::updateCurrentValue(value); targetObject()->setProperty(propertyName(), value); } QVariant boolInterpolator(const bool &start, const bool &end, qreal progress) { Q_UNUSED(end) Q_UNUSED(progress) return start; } //qRegisterAnimationInterpolator(boolInterpolator); QVariant qstringInterpolator(const QString &start, const QString &end, qreal progress) { Q_UNUSED(end) Q_UNUSED(progress) return start; } //qRegisterAnimationInterpolator( qstringInterpolator ); QVariant PropertyAnimation::interpolated ( const QVariant & from, const QVariant & to, qreal progress ) const { if (strcmp(from.typeName(), "bool") == 0) { return boolInterpolator(from.toBool(), to.toBool(), progress); } else if (strcmp(from.typeName(), "QString") == 0) { return qstringInterpolator(from.toString(), to.toString(), progress); } return QVariantAnimation::interpolated(from, to, progress); } boats-201908/animation/propertyanimation.h000066400000000000000000000031321353032755300206500ustar00rootroot00000000000000// // C++ Interface: PropertyAnimation // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2011 Thibaut GRIDEL // // 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 . // // #ifndef PROPERTYANIMATION_H #define PROPERTYANIMATION_H #include /** \class PropertyAnimation \brief Animation for properties irrelevant of state The class is the animation helper for a property, according to the Animation Framework. It inherits QVariantAnimation and provides proper property animation so that the property is updated whatever the state, unlike QPropertyAnimation \sa QPropertyAnimation */ class PropertyAnimation : public QPropertyAnimation { public: PropertyAnimation(QObject *target, const QByteArray &propertyName, QObject *parent = 0); virtual void updateCurrentValue ( const QVariant & value ); virtual QVariant interpolated ( const QVariant & from, const QVariant & to, qreal progress ) const; }; #endif // PROPERTYANIMATION_H boats-201908/animation/scenarioanimation.cpp000066400000000000000000000077371353032755300211410ustar00rootroot00000000000000// // C++ Implementation: ScenarioAnimation // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2011 Thibaut GRIDEL // // 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 . // // #include #include "scenarioanimation.h" #include "situationmodel.h" #include "trackmodel.h" #include "boatmodel.h" #include "statemachine.h" #include "trackanimation.h" #include "angleanimation.h" #include ScenarioAnimation::ScenarioAnimation(SituationModel *situation, QObject *parent) : QParallelAnimationGroup(parent), m_situation(situation), m_windAnimation(new QSequentialAnimationGroup(this)) { addAnimation(m_windAnimation); connect(m_situation->stateMachine()->playState(), SIGNAL(entered()), this, SLOT(start())); connect(m_situation->stateMachine()->pauseState(), SIGNAL(entered()), this, SLOT(pause())); connect(m_situation->stateMachine()->stopState(), SIGNAL(entered()), this, SLOT(stop())); } ScenarioAnimation::~ScenarioAnimation() { delete m_windAnimation; } void ScenarioAnimation::setAnimation() { foreach(TrackModel *track, m_situation->tracks()) { BoatModel *boat = track->boats()[0]; BoatModel *animationBoat = new BoatModel(track); animationBoat->setOrder(0); animationBoat->setPosition(boat->position()); animationBoat->setHeading(boat->heading()); animationBoat->setWind(m_situation->wind().windAt(0)); animationBoat->setLaylines(boat->laylines()); connect(&m_situation->wind(), SIGNAL(directionChanged(qreal)), animationBoat, SLOT(setWind(qreal))); TrackAnimation *animation = new TrackAnimation(track, animationBoat, this); addAnimation(animation); m_animationItems.push_back(animation); emit trackAnimationsChanged(); } for (int i = 0; i < m_situation->wind().size()-1; ++i) { AngleAnimation *wind = new AngleAnimation(&m_situation->wind(), "direction"); wind->setDuration(2000); wind->setStartValue(m_situation->wind().windAt(i)); wind->setEndValue(m_situation->wind().windAt(i+1)); m_windAnimation->addAnimation(wind); } setCurrentTime(0); } void ScenarioAnimation::unsetAnimation() { foreach(TrackAnimation *animation, m_animationItems) { // the boat was never really part of the track, we use situation signal // directly to have the graphicsitem removed m_situation->removingBoat(animation->boat()); removeAnimation(animation); m_animationItems.removeOne(animation); emit trackAnimationsChanged(); delete animation->boat(); delete animation; } for (int i = 0; i < m_situation->wind().size()-1; ++i) { QAbstractAnimation *animation = m_windAnimation->animationAt(0); m_windAnimation->removeAnimation(animation); delete animation; } m_situation->wind().setDirection(m_situation->wind().windAt(0)); } void ScenarioAnimation::updateCurrentTime(int currentTime) { QParallelAnimationGroup::updateCurrentTime(currentTime); emit timeChanged(currentTime); } QList ScenarioAnimation::animationItems() { return m_animationItems; } #ifdef QML QQmlListProperty ScenarioAnimation::trackAnimationList() { return QQmlListProperty(this, m_animationItems); } #endif boats-201908/animation/scenarioanimation.h000066400000000000000000000047351353032755300206010ustar00rootroot00000000000000// // C++ Interface: ScenarioAnimation // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2011 Thibaut GRIDEL // // 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 . // // #ifndef SCENARIOANIMATION_H #define SCENARIOANIMATION_H #include #ifdef QML #include #endif class TrackAnimation; class SituationModel; class TrackModel; /** \class ScenarioAnimation \brief Animation for the whole scenario The class is the animation group for a scenario, according to the Animation Framework. It inherits QParallelAnimationGroup and contains TrackAnimations for the different tracks of the scenario \sa TrackAnimation */ class ScenarioAnimation: public QParallelAnimationGroup { Q_OBJECT Q_PROPERTY(int time READ currentTime WRITE setCurrentTime NOTIFY timeChanged) Q_PROPERTY(int scenarioDuration READ duration NOTIFY trackAnimationsChanged) #ifdef QML Q_PROPERTY(QQmlListProperty trackAnimationList READ trackAnimationList NOTIFY trackAnimationsChanged) #endif public: ScenarioAnimation(SituationModel *situation = 0, QObject *parent = 0); ~ScenarioAnimation(); virtual void updateCurrentTime(int currentTime); QList animationItems(); #ifdef QML QQmlListProperty trackAnimationList(); #endif signals: void timeChanged(int time); void trackAnimationsChanged(); public slots: void setAnimation(); void unsetAnimation(); private: /// \a m_animationItems holds the list of TrackAnimation items /// created for animation mode QList m_animationItems; SituationModel *m_situation; /// \a m_windAnimation holds the animation for wind QAnimationGroup *m_windAnimation; }; #endif // SCENARIOANIMATION_H boats-201908/animation/splineanimation.cpp000066400000000000000000000025271353032755300206200ustar00rootroot00000000000000// // C++ Implementation: SplineAnimation // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #include "splineanimation.h" SplineAnimation::SplineAnimation(QObject *target, const QByteArray &propertyName, const QPainterPath &path, QObject *parent) : PropertyAnimation(target, propertyName, parent), m_path(path) { } QVariant SplineAnimation::interpolated ( const QVariant & from, const QVariant & to, qreal progress ) const { Q_UNUSED(from) Q_UNUSED(to) qreal length = m_path.length(); qreal percent = m_path.percentAtLength(length * progress); return m_path.pointAtPercent(percent); } boats-201908/animation/splineanimation.h000066400000000000000000000032361353032755300202630ustar00rootroot00000000000000// // C++ Interface: SplineAnimation // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #ifndef SPLINEANIMATION_H #define SPLINEANIMATION_H #include #include "propertyanimation.h" /** \class SplineAnimation \brief Animation for the position along a spline The class is the animation helper for a position along a spline, according to the Animation Framework. It inherits QPropertyAnimation and provides animation for a QPointF to BoatGraphicsItem elements, like boat position along its track, so that the boat moves on its track \sa BoatGraphicsItem */ class SplineAnimation : public PropertyAnimation { public: SplineAnimation(QObject *target, const QByteArray &propertyName, const QPainterPath &path, QObject *parent = 0); virtual QVariant interpolated ( const QVariant & from, const QVariant & to, qreal progress ) const; private: QPainterPath m_path; }; #endif // SPLINEANIMATION_H boats-201908/animation/trackanimation.cpp000066400000000000000000000053611353032755300204310ustar00rootroot00000000000000// // C++ Implementation: TrackAnimation // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2011 Thibaut GRIDEL // // 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 . // // #include #include #include "trackanimation.h" #include "boatmodel.h" #include "trackmodel.h" #include "boatanimation.h" #include "propertyanimation.h" TrackAnimation::TrackAnimation(TrackModel *track, BoatModel *boat, QObject *parent) : QParallelAnimationGroup(parent), m_track(track), m_boat(boat) { int size = m_track->size(); if (size < 0) { return; } QSequentialAnimationGroup *boatsAnimation = new QSequentialAnimationGroup(this); // through all boats of the track for (int i=0; i< size; i++) { if (iaddAnimation(boatAnimation); } QSequentialAnimationGroup *visibilityAnimation = new QSequentialAnimationGroup(this); PropertyAnimation *invisibletAnimation = new PropertyAnimation(m_track->boats()[i],"dim", this); invisibletAnimation->setDuration(2000*i); invisibletAnimation->setStartValue(0); invisibletAnimation->setEndValue(0); visibilityAnimation->addAnimation(invisibletAnimation); PropertyAnimation *dimAnimation = new PropertyAnimation(m_track->boats()[i],"dim", this); if (isetDuration(2000); } else { dimAnimation->setDuration(0); } dimAnimation->setStartValue(255); dimAnimation->setEndValue(64); visibilityAnimation->addAnimation(dimAnimation); addAnimation(visibilityAnimation); } addAnimation(boatsAnimation); // dim all track boats foreach(BoatModel *boat, m_track->boats()) { boat->setDim(0); } } /** Resets the color and List of BoatModels in the track */ TrackAnimation::~TrackAnimation() { // undim all track boats foreach(BoatModel *boat, m_track->boats()) { boat->setDim(255); } m_track->changingTrack(m_track); } boats-201908/animation/trackanimation.h000066400000000000000000000035131353032755300200730ustar00rootroot00000000000000// // C++ Interface: TrackAnimation // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2010-2011 Thibaut GRIDEL // // 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 . // // #ifndef TRACKANIMATION_H #define TRACKANIMATION_H #include class BoatModel; class TrackModel; /** \class TrackAnimation \brief Animation for the whole track of a boat The class is the animation group for a track, according to the Animation Framework. It inherits QParallelAnimationGroup and contains various animation class for the different properties of a boat to animate along the track \sa BoatAnimation */ class TrackAnimation: public QParallelAnimationGroup { Q_OBJECT Q_PROPERTY(BoatModel* boat READ boat CONSTANT) Q_PROPERTY(TrackModel* track READ track CONSTANT) public: TrackAnimation(TrackModel *track = 0, BoatModel *boat = 0, QObject *parent = 0); ~TrackAnimation(); BoatModel *boat() const {return m_boat; } TrackModel *track() const {return m_track; } private: /// \a m_track holds the reference to the track TrackModel *m_track; /// \a m_track holds the reference to the animated boat BoatModel *m_boat; }; #endif // TRACKANIMATION_H boats-201908/boatapplication.cpp000066400000000000000000000023241353032755300166130ustar00rootroot00000000000000// // C++ Implementation: BoatApplication // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009 Thibaut GRIDEL // // 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 . // // #include "boatapplication.h" #include "mainwindow.h" bool BoatApplication::event(QEvent *event) { switch (event->type()) { case QEvent::FileOpen: if (m_window) { m_window->openFile(static_cast(event)->file()); return true; } break; default: break; } return QApplication::event(event); } boats-201908/boatapplication.h000066400000000000000000000023601353032755300162600ustar00rootroot00000000000000// // C++ Interface: BoatApplication // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009 Thibaut GRIDEL // // 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 . // // #ifndef BOATAPPLICATION_H #define BOATAPPLICATION_H #include #include class MainWindow; class BoatApplication : public QApplication { Q_OBJECT public: BoatApplication(int & argc, char ** argv) : QApplication(argc, argv) {} ~BoatApplication() {} void setWindow(MainWindow *window) { m_window = window; } protected: bool event(QEvent *event); private: MainWindow *m_window; }; #endif // BOATAPPLICATION_H boats-201908/boats.cpp000066400000000000000000000056601353032755300145600ustar00rootroot00000000000000// // C++ Implementation: boats // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009 Thibaut GRIDEL // // 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 . // // #include "boats.h" QList Boats::m_seriesList; QList Boats::seriesList() { if (!m_seriesList.size()) { for (int i=0; i< ENUM_SIZE(Boats, Series)-1; i++) { m_seriesList << ENUM_NAME(Boats, Series, i); } } // By doing it this way guarantee that labels align correctly with enum tags irrespective of order m_seriesList[keelboat] = QObject::tr("Spin Keelboat"); m_seriesList[keelboatwithgenn] = QObject::tr("Gennaker Keelboat"); m_seriesList[int49er] = QObject::tr("49er"); m_seriesList[rsfeva] = QObject::tr("RS Feva"); m_seriesList[int470] = QObject::tr("470"); m_seriesList[int420] = QObject::tr("420"); m_seriesList[int29er] = QObject::tr("29er"); m_seriesList[finn] = QObject::tr("Finn"); m_seriesList[laser] = QObject::tr("Laser"); m_seriesList[firefly] = QObject::tr("Firefly"); m_seriesList[topper] = QObject::tr("Topper"); m_seriesList[optimist] = QObject::tr("Optimist"); m_seriesList[tornado] = QObject::tr("Tornado"); m_seriesList[nacra17] = QObject::tr("Nacra 17"); m_seriesList[diam24] = QObject::tr("Diam 24"); m_seriesList[startboat] = QObject::tr("Committee boat"); m_seriesList[rib] = QObject::tr("RIB"); return m_seriesList; } QList Boats::m_seriesSizeList; QList Boats::seriesSizeList() { if (!m_seriesSizeList.size()) { for (int i=0; i< ENUM_SIZE(Boats, Series)-1; i++) { m_seriesSizeList << 100; // default size of 100 } m_seriesSizeList[keelboat] = 100; m_seriesSizeList[keelboatwithgenn] = 100; m_seriesSizeList[int49er] = 48; m_seriesSizeList[rsfeva] = 36.4; m_seriesSizeList[int470] = 47; m_seriesSizeList[int420] = 42; m_seriesSizeList[int29er] = 41; m_seriesSizeList[finn] = 45; m_seriesSizeList[laser] = 40; m_seriesSizeList[firefly] = 36.6; m_seriesSizeList[topper] = 34; m_seriesSizeList[optimist] = 23; m_seriesSizeList[tornado] = 61; m_seriesSizeList[nacra17] = 52.5; m_seriesSizeList[diam24] = 72.5; m_seriesSizeList[rib] = 60; } return m_seriesSizeList; } boats-201908/boats.h000066400000000000000000000046721353032755300142270ustar00rootroot00000000000000// // C++ Interface: boats // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009-2011 Thibaut GRIDEL // // 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 . // // #ifndef BOATS_H #define BOATS_H #include #include #include #define ENUM_NAME(o,e,v) (o::staticMetaObject.enumerator(o::staticMetaObject.indexOfEnumerator(#e)).valueToKey((v))) #define ENUM_VALUE(o,e,n) (o::staticMetaObject.enumerator(o::staticMetaObject.indexOfEnumerator(#e)).keyToValue((n))) #define ENUM_SIZE(o,e) (o::staticMetaObject.enumerator(o::staticMetaObject.indexOfEnumerator(#e)).keyCount()) #define FLAG_NAME(o,e,v) (o::staticMetaObject.enumerator(o::staticMetaObject.indexOfEnumerator(#e)).valueToKeys((v))) #define FLAG_VALUE(o,e,n) (o::staticMetaObject.enumerator(o::staticMetaObject.indexOfEnumerator(#e)).keysToValue((n))) class Boats { Q_GADGET public: enum Series { keelboat, keelboatwithgenn, int49er, rsfeva, int470, int420, int29er, finn, laser, firefly, topper, optimist, tornado, nacra17, diam24, startboat, rib, unknown }; Q_ENUMS (Series); static QList seriesList(); static QList seriesSizeList(); enum Overlap { none = 0x0, starboard = 0x1, port = 0x2 }; Q_FLAGS( Overlap Overlaps) Q_DECLARE_FLAGS(Overlaps, Overlap) enum Flag { noFlag, Y, Red, Green, Yellow, Blue }; Q_ENUMS (Flag); enum Acceleration { constant, accelerating, decelerating }; Q_ENUMS (Acceleration); private: static QList m_seriesList; static QList m_seriesSizeList; }; Q_DECLARE_METATYPE(Boats::Overlaps) #endif boats-201908/boats.pro000066400000000000000000000050701353032755300145710ustar00rootroot00000000000000TEMPLATE = app CONFIG += qt warn_on HEADERS += \ boats.h \ boatsengine.h \ commontypes.h \ enablestate.h \ statemachine.h \ trace.h \ undocommands.h \ xmlsituationreader.h \ xmlsituationwriter.h SOURCES += \ boats.cpp \ boatsengine.cpp \ enablestate.cpp \ statemachine.cpp \ trace.cpp \ undocommands.cpp \ xmlsituationreader.cpp \ xmlsituationwriter.cpp qml { TARGET = boats-qml QT += quick qml widgets DEFINES += QML SOURCES += \ main_qml.cpp qml_files.files = boats.qml INSTALL += qml_files } else { TARGET = boats QT += widgets printsupport include(graphicsview/graphicsview.pri) include(itemviews/itemviews.pri) include(locale/locale.pri) HEADERS += \ boatapplication.h \ mainwindow.h \ situationprint.h \ situationwidget.h SOURCES += \ boatapplication.cpp \ main.cpp \ mainwindow.cpp \ situationprint.cpp \ situationwidget.cpp contains(GIF_EXPORT,1) { DEFINES += GIF_EXPORT HEADERS += gifwriter.h SOURCES += gifwriter.cpp LIBS += -lgif } PRE_TARGETDEPS += compiler_updateqm_make_all QMAKE_CFLAGS_RELEASE += -fvisibility=hidden QMAKE_CXXFLAGS_RELEASE += -fvisibility=hidden -fvisibility-inlines-hidden } include(animation/animation.pri) include(model/model.pri) INCLUDEPATH += $$PWD RESOURCES += images.qrc mac { QMAKE_LFLAGS += -static QMAKE_INFO_PLIST = resources/Info.plist ICON = resources/boats.icns mime.path = boats.app/Contents/Resources mime.files = resources/xbs.icns INSTALLS += mime } win32 { RC_FILE = resources/boats.rc QMAKE_LFLAGS += -static } unix_deploy { isEmpty(PREFIX):PREFIX = /usr/local target.path = $${PREFIX}/bin INSTALLS += target desktop.path = $${PREFIX}/share/applications desktop.files = resources/boats.desktop INSTALLS += desktop icons.path = $${PREFIX}/share/icons icons.files = images/icons/* INSTALLS += icons mimetype.path = $${PREFIX}/share/mime/packages mimetype.files = resources/boats.xml INSTALLS += mimetype TRANSLATEDIR = $${PREFIX}/share/boats translations.path = $${TRANSLATEDIR} translations.files = locale/boats_*.qm INSTALLS += translations } else { !qml { RESOURCES += locales.qrc TRANSLATEDIR = ":/locale" } } DEFINES += TRANSLATEDIR=\\\"$${TRANSLATEDIR}\\\" MOC_DIR = .moc/ OBJECTS_DIR = .obj/ boats-201908/boats.qml000066400000000000000000000001271353032755300145600ustar00rootroot00000000000000import QtQuick 2.0 Rectangle { width: 962 height: 553 Canvas { } } boats-201908/boatsengine.cpp000066400000000000000000000240561353032755300157460ustar00rootroot00000000000000 // C++ Implementation: boatsengine // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2014 Thibaut GRIDEL // // 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 . // // #include "boatsengine.h" #include "commontypes.h" #include "boats.h" #include "situationmodel.h" #include "trackmodel.h" #include "boatmodel.h" #include "markmodel.h" #include "polylinemodel.h" #include "pointmodel.h" #include "statemachine.h" #include "scenarioanimation.h" #include "xmlsituationreader.h" #include "xmlsituationwriter.h" #include extern int debugLevel; BoatsEngine::BoatsEngine(QObject *parent) : QObject(parent), m_currentSituation(0) { } void BoatsEngine::newFile() { SituationModel *situation = new SituationModel(this); m_situationList.append(situation); setIndex(m_situationList.size()-1); emit sizeChanged(); } void BoatsEngine::setIndex(int index) { m_currentSituation = index; emit indexChanged(); } void BoatsEngine::removeFile(int index) { SituationModel *situation = m_situationList.at(index); m_situationList.removeAt(index); situation->deleteLater(); emit sizeChanged(); } void BoatsEngine::resetFile() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->undoStack()->setIndex(0); setCurrentFile(situation, ""); situation->undoStack()->clear(); situation->wind().clearWind(); } bool BoatsEngine::openFile(const QString &fileName) { SituationModel *situation = m_situationList.at(m_currentSituation); QFile file(fileName); if (!file.open(QFile::ReadOnly | QFile::Text)) { // QMessageBox::warning(this, tr("Open Scenario File"), // tr("Cannot read file %1:\n%2.") // .arg(fileName) // .arg(file.errorString())); return false; } XmlSituationReader reader(situation); if (!reader.read(&file)) { // QMessageBox::warning(this, tr("Open Scenario file"), // tr("Parse error in file %1 at line %2, column %3:\n%4") // .arg(fileName) // .arg(reader.lineNumber()) // .arg(reader.columnNumber()) // .arg(reader.errorString())); return false; } setCurrentFile(situation, fileName); return true; } bool BoatsEngine::saveSituation(QString fileName) { SituationModel *situation = m_situationList.at(m_currentSituation); QString name = fileName; if (name.isEmpty()) { QString defaultFile; if (situation->fileName().isEmpty()) { defaultFile = QDateTime::currentDateTime().toString("yyMMdd") + ".xbs"; } else { defaultFile = situation->fileName(); } if (QFileInfo(name).suffix().isEmpty()) name.append(".xbs"); if (QFile::exists(name)) { // if (QMessageBox::warning(this, tr("Save Scenario"), // tr("%1 already exists.\nDo you want to replace it?") // .arg(QFileInfo(name).baseName()), // QMessageBox::Yes | QMessageBox::No, QMessageBox::No) // == QMessageBox::No) { // return false; // } } } QFile file(name); if (!file.open(QFile::WriteOnly | QFile::Text)) { // QMessageBox::warning(this, tr("Save Scenario"), // tr("Cannot write file %1:\n%2.") // .arg(fileName) // .arg(file.errorString())); return false; } XmlSituationWriter writer(situation); writer.writeFile(&file); setCurrentFile(situation, name); return true; } void BoatsEngine::setCurrentFile(SituationModel *situation, const QString &fileName) { situation->setFileName(fileName); situation->undoStack()->setClean(); } QStringList BoatsEngine::fileList() { QStringList fileList; foreach(SituationModel *situation, m_situationList) { if (!situation->fileName().isEmpty()) { fileList.append(situation->fileName()); } } return fileList; } void BoatsEngine::addTrack() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->stateMachine()->createTrack(); } void BoatsEngine::deleteTrack() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->deleteTrack(); } void BoatsEngine::addBoat() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->stateMachine()->createBoat(); } void BoatsEngine::deleteModels() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->deleteModels(); } void BoatsEngine::addMark() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->stateMachine()->createMark(); } void BoatsEngine::addPolyLine() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->stateMachine()->createLine(); } void BoatsEngine::addPoint() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->stateMachine()->createPoint(); } void BoatsEngine::animate() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->stateMachine()->animate(); } void BoatsEngine::play() { if (debugLevel & 1 << ANIMATION) std::cout << "playing" << std::endl; SituationModel *situation = m_situationList.at(m_currentSituation); situation->stateMachine()->play(); } void BoatsEngine::pause() { if (debugLevel & 1 << ANIMATION) std::cout << "pausing" << std::endl; SituationModel *situation = m_situationList.at(m_currentSituation); situation->stateMachine()->pause(); } void BoatsEngine::stop() { if (debugLevel & 1 << ANIMATION) std::cout << "stopping" << std::endl; SituationModel *situation = m_situationList.at(m_currentSituation); situation->stateMachine()->stop(); situation->animation()->setCurrentTime(0); } void BoatsEngine::trimSail() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->trimSail(); } void BoatsEngine::autotrimSail() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->autotrimSail(); } void BoatsEngine::untrimSail() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->untrimSail(); } void BoatsEngine::trimJib() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->trimJib(); } void BoatsEngine::autotrimJib() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->autotrimJib(); } void BoatsEngine::untrimJib() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->untrimJib(); } void BoatsEngine::trimSpin() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->trimSpin(); } void BoatsEngine::autotrimSpin() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->autotrimSpin(); } void BoatsEngine::untrimSpin() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->untrimSpin(); } void BoatsEngine::togglePortOverlap() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->togglePortOverlap(); } void BoatsEngine::toggleStarboardOverlap() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->toggleStarboardOverlap(); } void BoatsEngine::toggleHidden() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->toggleHidden(); } void BoatsEngine::toggleText() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->toggleText(); } void BoatsEngine::toggleFlag(Boats::Flag flag) { SituationModel *situation = m_situationList.at(m_currentSituation); situation->toggleFlag(flag); } void BoatsEngine::toggleAcceleration(Boats::Acceleration acceleration) { SituationModel *situation = m_situationList.at(m_currentSituation); situation->toggleAcceleration(acceleration); } void BoatsEngine::toggleSpin() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->toggleSpin(); } void BoatsEngine::toggleMarkSide() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->toggleMarkSide(); } void BoatsEngine::toggleMarkArrow() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->toggleMarkArrow(); } void BoatsEngine::toggleMarkZone() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->toggleMarkZone(); } void BoatsEngine::setMarkColor(QColor color) { SituationModel *situation = m_situationList.at(m_currentSituation); situation->setMarkColor(color); } void BoatsEngine::toggleMarkLabel() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->toggleMarkLabel(); } void BoatsEngine::editMarkLabel(QString text) { SituationModel *situation = m_situationList.at(m_currentSituation); situation->editMarkLabel(text); } void BoatsEngine::toggleLaylines() { SituationModel *situation = m_situationList.at(m_currentSituation); situation->toggleLaylines(); } void BoatsEngine::setLookAt(int direction, int tilt) { SituationModel *situation = m_situationList.at(m_currentSituation); situation->setLookAt(direction, tilt); } boats-201908/boatsengine.h000066400000000000000000000061451353032755300154120ustar00rootroot00000000000000// // C++ Interface: boatsengine // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2014 Thibaut GRIDEL // // 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 . // // #ifndef BOATSENGINE_H #define BOATSENGINE_H #include "boats.h" #include #include #include #include class SituationModel; class BoatsEngine : public QObject { Q_OBJECT public: Q_PROPERTY(int index READ currentIndex WRITE setIndex NOTIFY indexChanged) Q_PROPERTY(int size READ situationSize NOTIFY sizeChanged) Q_PROPERTY(SituationModel* scenario READ currentModel NOTIFY indexChanged) explicit BoatsEngine(QObject *parent = 0); SituationModel *currentModel() { if(m_situationList.size()) { return m_situationList.at(m_currentSituation); } else return 0; } int situationSize() { return m_situationList.size(); } int currentIndex() { return m_currentSituation; } Q_INVOKABLE QStringList fileList(); Q_INVOKABLE bool saveSituation(QString name = QString()); public slots: // File actions void newFile(); void setIndex(int index); void removeFile(int index); void resetFile(); bool openFile(const QString &fileName); // State actions void addTrack(); void addBoat(); void addMark(); void addPolyLine(); void addPoint(); void animate(); void play(); void pause(); void stop(); // Boat actions void trimSail(); void autotrimSail(); void untrimSail(); void trimJib(); void autotrimJib(); void untrimJib(); void trimSpin(); void autotrimSpin(); void untrimSpin(); void togglePortOverlap(); void toggleStarboardOverlap(); void toggleFlag(Boats::Flag flag); void toggleAcceleration(Boats::Acceleration acceleration); void toggleHidden(); void toggleText(); void toggleSpin(); // Mark actions void toggleMarkSide(); void toggleMarkArrow(); void toggleMarkZone(); void setMarkColor(QColor color); void toggleLaylines(); void toggleMarkLabel(); void editMarkLabel(QString text); // Situation actions void deleteTrack(); void deleteModels(); void setLookAt(int direction, int tilt); signals: void indexChanged(); void sizeChanged(); private: // File methods void setCurrentFile(SituationModel *situation, const QString &fileName); // GraphicsView Framework QList m_situationList; int m_currentSituation; }; #endif boats-201908/commontypes.h000066400000000000000000000022051353032755300154620ustar00rootroot00000000000000// // C++ Interface: commontypes // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #ifndef COMMONTYPES_H #define COMMONTYPES_H enum DebugTraces { MODEL, VIEW, COMMAND, ANIMATION, DELEGATE, XML, EXPORT, STATE }; typedef enum { TRACK_COLOR, TRACK_PATH, TRACK_SERIES, TRACK_FOLLOW } TrackTableItem; typedef enum { WIND_INDEX, WIND_DIRECTION, WIND_DELETE } WindTableItem; #endif boats-201908/doc/000077500000000000000000000000001353032755300135025ustar00rootroot00000000000000boats-201908/doc/boats.1000066400000000000000000000007731353032755300147030ustar00rootroot00000000000000.\" Process this file with .\" groff -man -Tascii foo.1 .\" .TH BOATS 1 "APRIL 2009" .SH NAME boats \- a race scenario drawing tool .SH SYNOPSIS .B boats [-debug n] .I file .B ... .SH DESCRIPTION .B boats Boat Scenario is a drawing tool. It is your ideal companion for training sessions, rule learning, strategy explanations and more. All files provided in the command line will be opened on different tabs. .SH OPTIONS .IP -debug Provide debugging output .SH AUTHOR Thibaut GRIDEL boats-201908/enablestate.cpp000066400000000000000000000051421353032755300157320ustar00rootroot00000000000000// // C++ Implementation: enablestate // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2014 Thibaut GRIDEL // // 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 . // // #include "enablestate.h" #include "commontypes.h" #include #include #include #include extern int debugLevel; EnableState::EnableState(QState *parent) : QState(parent), m_active(false), m_enabled(false) { } bool EnableState::isActive() { return m_active; } bool EnableState::isEnabled() { return m_enabled; } bool EnableState::isToggable() { return m_toggable; } void EnableState::onEntry(QEvent *event) { Q_UNUSED(event); if(!m_active) { m_active = true; if (debugLevel & 1 << STATE) std::cout << "State Entry " << this->objectName().toStdString() << std::endl; emit activeChanged(m_active); foreach (QAbstractTransition* transition, transitions()) { EnableState *state = static_cast (transition->targetState()); state->setEnabled(true); } if (m_toggable) { setEnabled(true); } } } void EnableState::onExit(QEvent *event) { Q_UNUSED(event); if(m_active) { m_active = false; if (debugLevel & 1 << STATE) std::cout << "State Exit " << this->objectName().toStdString() << std::endl; emit activeChanged(m_active); foreach (QAbstractTransition* transition, transitions()) { EnableState *state = static_cast (transition->targetState()); state->setEnabled(false); } if (m_toggable) { setEnabled(false); } } } void EnableState::setEnabled(bool enabled) { if (m_enabled != enabled) { m_enabled = enabled; emit enabledChanged(m_enabled); } } void EnableState::setToggable(bool toggable) { if (m_toggable != toggable) { m_toggable = toggable; emit toggableChanged(m_toggable); } } boats-201908/enablestate.h000066400000000000000000000030761353032755300154030ustar00rootroot00000000000000// // C++ Interface: state // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2014 Thibaut GRIDEL // // 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 . // // #ifndef ENABLESTATE_H #define ENABLESTATE_H #include class EnableState : public QState { Q_OBJECT Q_PROPERTY(bool isActive READ isActive NOTIFY activeChanged) Q_PROPERTY(bool isEnabled READ isEnabled NOTIFY enabledChanged) Q_PROPERTY(bool isToggable READ isToggable WRITE setToggable NOTIFY toggableChanged) public: explicit EnableState(QState *parent = 0); bool isActive(); bool isEnabled(); bool isToggable(); void setEnabled(bool enabled); void setToggable(bool toggable); signals: void activeChanged(bool); void enabledChanged(bool); void toggableChanged(bool); protected: void onEntry(QEvent *event); void onExit(QEvent *event); private: bool m_active; bool m_enabled; bool m_toggable; }; #endif // ENABLESTATE_H boats-201908/gifwriter.cpp000066400000000000000000000055341353032755300154520ustar00rootroot00000000000000// // C++ Implementation: gifwriter // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009 Thibaut GRIDEL // // 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 . // // #include "gifwriter.h" #include #include #include GifWriter::GifWriter() { } int GifWriter::writeData(const GifByteType *bytes, int size) { return device()->write((char *)bytes, size); } static int myOutputFunc(GifFileType * fileType, const GifByteType * bytes, int size) { GifWriter *writer = reinterpret_cast(fileType->UserData); return writer->writeData(bytes, size); } bool GifWriter::write(QList imageList) { GifFileType *fileType; int retrn; QImage *image = imageList.first(); fileType = EGifOpen((void*) this, myOutputFunc, &retrn); EGifSetGifVersion(fileType, true); int colors = 256; ColorMapObject * cmap = GifMakeMapObject(colors, NULL); for (int i = 0; i< qMin(colors, m_colormap.size()); i++) { QRgb color = m_colormap[i]; cmap->Colors[i].Blue = qBlue(color); cmap->Colors[i].Green = qGreen(color); cmap->Colors[i].Red = qRed(color); } retrn = EGifPutScreenDesc(fileType, image->width(), image->height(), 8, 0, cmap); GifFreeMapObject(cmap); char nameExtension[11] = { 'N','E','T','S','C','A','P','E','2','.','0' }; char loopExtension[3] = { 1, 0, 0 }; retrn = EGifPutExtensionLeader(fileType, 0xFF); retrn = EGifPutExtensionBlock(fileType, 11, &nameExtension); retrn = EGifPutExtensionBlock(fileType, 3, &loopExtension); retrn = EGifPutExtensionTrailer(fileType); retrn = EGifPutComment(fileType, "Boat Scenario http://boats.berlios.de"); char gifExtension[4] = { 0, 8, 0, 0 }; foreach (image, imageList) { if (image == imageList.last()) { gifExtension[1] = 0; gifExtension[2] = 2; } retrn = EGifPutExtension(fileType, 0xF9, 4, &gifExtension); retrn = EGifPutImageDesc(fileType, 0, 0, image->width(), image->height(), 0, NULL); for (int i = 0; i < image->height(); i++) { retrn &= EGifPutLine(fileType, (GifPixelType*)image->scanLine(i), image->width()); } } retrn = EGifCloseFile(fileType, &retrn); return true; } boats-201908/gifwriter.h000066400000000000000000000025401353032755300151110ustar00rootroot00000000000000// // C++ Interface: gifwriter // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009 Thibaut GRIDEL // // 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 . // // #ifndef GIFWRITER_H #define GIFWRITER_H #include #include #include class GifWriter { public: GifWriter(); void setDevice (QIODevice *device) { m_device = device; } QIODevice *device() const { return m_device; } void setColorMap(QImage &image) { m_colormap = image.colorTable(); } QVector colormap() const { return m_colormap; } bool write(QList imageList); int writeData(const GifByteType *bytes, int size); private: QIODevice *m_device; QVector m_colormap; }; #endif // GIFWRITER_H boats-201908/graphicsview/000077500000000000000000000000001353032755300154305ustar00rootroot00000000000000boats-201908/graphicsview/arrow.cpp000066400000000000000000000063001353032755300172650ustar00rootroot00000000000000// // C++ Implementation: ArrowGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2011 Thibaut GRIDEL // // 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 . // // #include "arrow.h" #include "commontypes.h" #include "situationscene.h" #include "windmodel.h" #include #include #include #include extern int debugLevel; ArrowGraphicsItem::ArrowGraphicsItem(WindModel *wind, QGraphicsItem *parent) : QGraphicsItem(parent), m_wind(wind) { setFlag(QGraphicsItem::ItemIsMovable); setFlag(QGraphicsItem::ItemIsSelectable); setVisible(wind->visible()); setPosition(wind->position()); setZValue(0); m_path.moveTo(-15,-15); m_path.lineTo(15,-15); m_path.lineTo(15,0); m_path.lineTo(25,0); m_path.lineTo(0,17); m_path.lineTo(-25,0); m_path.lineTo(-15,0); m_path.lineTo(-15,-15); connect(wind, SIGNAL(windVisibleChanged(bool)), this, SLOT(deleteItem(bool))); connect(wind, SIGNAL(positionChanged(QPointF)), this, SLOT(setPosition(QPointF))); connect(wind, SIGNAL(directionChanged(qreal)), this, SLOT(setHeading(qreal))); connect(wind, SIGNAL(windReset()), this, SLOT(resetArrow())); } ArrowGraphicsItem::~ArrowGraphicsItem() {} void ArrowGraphicsItem::resetArrow() { setHeading(m_wind->windAt(0)); } void ArrowGraphicsItem::setHeading(qreal value) { m_angle = value; prepareGeometryChange(); setRotation(m_angle); update(); } void ArrowGraphicsItem::setPosition(QPointF position) { prepareGeometryChange(); setPos(position); update(); } void ArrowGraphicsItem::deleteItem(bool visible) { if(!visible) { if (debugLevel & 1 << VIEW) std::cout << "deleting ArrowGraphicsItem for model" << m_wind << std::endl; scene()->removeItem(this); delete this; } } QRectF ArrowGraphicsItem::boundingRect() const { return QRectF(-25,-15,50,32); } QPainterPath ArrowGraphicsItem::shape() const { return m_path; } void ArrowGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); if (isSelected()) painter->setPen(Qt::red); else painter->setPen(Qt::black); painter->setBrush(QColor(191,191,255,255)); painter->drawPath(m_path); QString label = tr("Wind"); painter->setPen(Qt::black); painter->drawText(QRectF(-15,-15,30,15),Qt::AlignCenter,label); } int ArrowGraphicsItem::type() const { return ARROW_TYPE; } boats-201908/graphicsview/arrow.h000066400000000000000000000040741353032755300167400ustar00rootroot00000000000000// // C++ Interface: ArrowGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2011 Thibaut GRIDEL // // 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 . // // #ifndef ARROW_H #define ARROW_H #include #include #include class WindModel; /** \class ArrowGraphicsItem \brief the QGraphicsItem for a wind Arrow The class represents the Item according to the Graphics View Framework. It inherits QGraphicsItem for Item framework and QObject for slot and signal framework. It displays the representation of an arrow on a SituationView. \sa SituationView, SituationScene, SituationModel, WindModel */ class ArrowGraphicsItem : public QObject, public QGraphicsItem { Q_OBJECT Q_INTERFACES(QGraphicsItem) public: ArrowGraphicsItem(WindModel *wind = 0, QGraphicsItem *parent = 0); ~ArrowGraphicsItem(); virtual QRectF boundingRect() const; virtual QPainterPath shape() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); virtual int type() const; public slots: void resetArrow(); void setHeading(qreal value); void setPosition(QPointF position); void deleteItem(bool visible); private: WindModel *m_wind; /// \a m_angle holds the angle of the Arrow qreal m_angle; QPainterPath m_path; }; #endif boats-201908/graphicsview/boat.cpp000066400000000000000000000651041353032755300170670ustar00rootroot00000000000000// // C++ Implementation: BoatGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #include "boat.h" #include "commontypes.h" #include "situationscene.h" #include "situationmodel.h" #include "trackmodel.h" #include "boatmodel.h" #include #include #include #include extern int debugLevel; BoatGraphicsItem::BoatGraphicsItem(BoatModel *boat, QGraphicsItem *parent) : QGraphicsItem(parent), m_boat(boat), m_angle(0), m_sail(new SailGraphicsItem(boat, this)), m_jib(new SailGraphicsItem(boat, this)), m_spin(new SpinnakerGraphicsItem(boat, this)), m_genn(new GennakerGraphicsItem(boat, this)), m_hasSpin(false), m_hasGenn(false), m_overlap(Boats::none), m_overlapLine(new QGraphicsLineItem(this)), m_color(boat->track()->color()), m_flag(Boats::noFlag), m_flagRect(new FlagGraphicsItem(this)), m_hidden(false), m_bubble(new BubbleGraphicsItem(m_boat, this)), m_series(Boats::unknown), m_order(0), m_numberPath(new QGraphicsPathItem(this)), m_laylines(new LaylinesGraphicsItem(boat, this)) { setFlag(QGraphicsItem::ItemIsMovable); setFlag(QGraphicsItem::ItemIsSelectable); setBoundingRegionGranularity(1); m_numberPath->setZValue(1); m_flagRect->setZValue(2); m_sail->setZValue(3); m_jib->setZValue(4); m_spin->setZValue(5); m_genn->setZValue(5); // Make this same as for spinnaker on basis that a boat will have one or other of these, not both m_bubble->setZValue(6); m_numberPath->setBrush(QBrush(Qt::black)); QPen dashPen(Qt::CustomDashLine); QVector dashes; dashes << 5 << 5; dashPen.setDashPattern(dashes); m_overlapLine->setPen(dashPen); setSeries(boat->track()->series()); setHeading(boat->heading()); setPos(boat->position()); setOrder(boat->order()); setDim(boat->dim()); m_sail->setSailAngle(m_boat->trimmedSailAngle()); m_jib->setSailAngle(m_boat->trimmedJibAngle()); m_spin->setHeading(m_boat->heading()); m_spin->setSailAngle(m_boat->trimmedSpinAngle()); m_genn->setSailAngle(m_boat->trimmedGennAngle()); // Use spinTrim() for both spinnaker and gennaker setSpin(boat->spin()); setOverlap(boat->overlap()); setDisplayFlag(boat->flag()); setHidden(boat->hidden()); connect(boat, SIGNAL(headingChanged(qreal)), this, SLOT(setHeading(qreal))); connect(boat, SIGNAL(headingChanged(qreal)), m_spin, SLOT(setHeading(qreal))); connect(boat, SIGNAL(positionChanged(QPointF)), this, SLOT(setPosition(QPointF))); connect(boat, SIGNAL(trimmedSailAngleChanged(qreal)), m_sail, SLOT(setSailAngle(qreal))); connect(boat, SIGNAL(trimmedJibAngleChanged(qreal)), m_jib, SLOT(setSailAngle(qreal))); connect(boat, SIGNAL(spinChanged(bool)), this, SLOT(setSpin(bool))); connect(boat, SIGNAL(trimmedSpinAngleChanged(qreal)), m_spin, SLOT(setSailAngle(qreal))); connect(boat, SIGNAL(trimmedGennAngleChanged(qreal)), m_genn, SLOT(setSailAngle(qreal))); connect(boat, SIGNAL(orderChanged(int)), this, SLOT(setOrder(int))); connect(boat, SIGNAL(overlapChanged(Boats::Overlaps)), this, SLOT(setOverlap(Boats::Overlaps))); connect(boat, SIGNAL(flagChanged(Boats::Flag)), this, SLOT(setDisplayFlag(Boats::Flag))); connect(boat, SIGNAL(hiddenChanged(bool)), this, SLOT(setHidden(bool))); connect(boat, SIGNAL(dimChanged(int)), this, SLOT(setDim(int))); connect(boat->track(), SIGNAL(colorChanged(QColor)), this, SLOT(setColor(QColor))); connect(boat->track(), SIGNAL(seriesChanged(Boats::Series)), this, SLOT(setSeries(Boats::Series))); connect(boat->situation(), SIGNAL(boatRemoved(BoatModel*)), this, SLOT(deleteItem(BoatModel*))); connect(boat->track(), SIGNAL(trackSelected(bool)), this, SLOT(setSelected(bool))); } BoatGraphicsItem::~BoatGraphicsItem() {} void BoatGraphicsItem::setHeading(qreal value) { if (m_angle != value) { m_angle = value; QTransform rotation; rotation.rotate(m_angle), setTransform(rotation, false); } } void BoatGraphicsItem::setPosition(QPointF position) { if (pos() != position) { setPos(position); update(); } } void BoatGraphicsItem::setSpin(bool value) { m_spin->setVisible(m_hasSpin && value); m_genn->setVisible(m_hasGenn && value); } void BoatGraphicsItem::setOrder(int value) { m_order = value; if (m_order && m_numberSize) { QString number = QString::number(m_order); QFont numberFont; numberFont.setPointSize(m_numberSize); QFontMetrics fm(numberFont); QPainterPath fpath; fpath.addText(-fm.width(number)/2.0, 0, numberFont, number); m_numberPath->setPath(fpath); setZValue(m_order); } else { m_numberPath->setPath(QPainterPath()); setZValue(boat()->track()->size()+1); } update(); } void BoatGraphicsItem::setOverlap(Boats::Overlaps value) { if (m_overlap != value) { m_overlap = value; setOverlapLine(); } } void BoatGraphicsItem::setOverlapLine() { qreal size = Boats::seriesSizeList()[m_series]; QLineF line; line.setP1(QPointF(m_border, size/2)); line.setP2(QPointF(-m_border, size/2)); if (m_overlap == Boats::none) { m_overlapLine->setVisible(false); } else { if (m_overlap & Boats::starboard) { line.setP2(QPointF(m_border + size, size/2)); } if (m_overlap & Boats::port) { line.setP1(QPointF(-m_border - size, size/2)); } m_overlapLine->setVisible(!m_hidden && true); m_overlapLine->setLine(line); } } void BoatGraphicsItem::setDisplayFlag(Boats::Flag value) { m_flag = value; m_flagRect->setDisplayFlag(m_flag); if (m_flag == Boats::noFlag) { m_flagRect->setVisible(false); } else { m_flagRect->setVisible(!m_hidden && true); } } void BoatGraphicsItem::setHidden(bool value) { prepareGeometryChange(); m_hidden = value; if (m_hidden) { m_sail->setVisible(false); m_jib->setVisible(false); m_spin->setVisible(false); m_genn->setVisible(false); m_overlapLine->setVisible(false); m_flagRect->setVisible(false); m_bubble->setVisible(false); m_numberPath->setVisible(false); m_laylines->setVisible(false); } else { m_sail->setVisible(true); m_jib->setVisible(true); m_spin->setVisible(boat()->spin()); m_genn->setVisible(boat()->spin()); m_overlapLine->setVisible(m_overlap != Boats::none); m_flagRect->setVisible(m_flag != Boats::noFlag); m_bubble->setVisible(!boat()->text().isEmpty()); m_numberPath->setVisible(true); m_laylines->setVisible(boat()->laylines()); } } void BoatGraphicsItem::setColor(QColor value) { if (m_color != value) { int alpha = m_color.alpha(); m_color = value; m_color.setAlpha(alpha); update(); } } void BoatGraphicsItem::setDim(int value) { m_color.setAlpha(value); if (value == 0 ) { setVisible(false); } else { setVisible(true); if (value != 255) { int maxSize = 0; foreach (const TrackModel *track, boat()->situation()->tracks()) { if (track->boats().size() > maxSize) maxSize = track->boats().size() - 1; } setZValue(maxSize+1); } else { setZValue(boat()->order()); } } update(); } void BoatGraphicsItem::setVisible(bool value) { QGraphicsItem::setVisible(value); } void BoatGraphicsItem::setSeries(Boats::Series value) { if (m_series != value) { prepareGeometryChange(); m_series = value; int posY = 0; QRectF flagRect; QPointF mast; qreal sailSize = 0; QPointF jibTackPos; qreal jibSize = 0; qreal spinSize = 0; QPointF gennTackPos; qreal gennPoleLength = 0; qreal gennSize = 0; qreal maxNormalSailAngle = 90; qreal maxNormalJibAngle = 45; qreal maxWithSpinSailAngle = 65; qreal maxWithSpinJibAngle = 35; QPainterPath path; QRectF boundingRect = QRectF(-50, -50, 100, 100); qreal border = 0; switch (m_series) { case Boats::keelboat: case Boats::keelboatwithgenn: m_numberSize = 12; posY = 25; flagRect = QRectF(-7.5, 30 , 15, 10); mast = QPointF(0, -8.7); sailSize = 41.5; jibTackPos = QPointF(0,-50); jibSize = 40; if (m_series == Boats::keelboat) { spinSize = 1.1 * sailSize; } else { spinSize = 0; } if (m_series == Boats::keelboatwithgenn) { gennTackPos = QPointF(0,-80); gennPoleLength = 30; gennSize = 70; maxWithSpinSailAngle = 40; maxWithSpinJibAngle = 35; } else { gennSize = 0; } path.moveTo(0,-50); path.cubicTo(20, 0, 18, 13, 10, 50); path.lineTo(-10, 50); path.cubicTo(-18, 13, -20, 0, 0, -50); boundingRect = QRectF(-20, -50, 40, 100); border = 10; break; case Boats::int49er: m_numberSize = 7; posY = 9; flagRect = QRectF(-3, 13, 6, 4); mast = QPointF(0, -2.5); sailSize = 27.0; jibTackPos = QPointF(0,-22.5); jibSize = 19.0; gennTackPos = QPointF(0,-42.5); gennPoleLength = 18.5; gennSize = 38.0; maxWithSpinSailAngle = 30; maxWithSpinJibAngle = 25; path.moveTo(0,-24.0); path.lineTo(-0.5,-24.0); path.cubicTo(-1.5,-23.0,-6.5,-5.5,-7.5,1.5); path.lineTo(-13.5,2.4); path.quadTo(-14.0,2.5,-14,3.0); path.lineTo(-14.0,24.0); path.lineTo(-13.5,24.0); path.lineTo(-12.5,21.0); path.lineTo(-7.5,19.0); path.lineTo(-5.0,22.0); path.lineTo(5.0,22.0); path.lineTo(7.5,19.0); path.lineTo(12.5,21.0); path.lineTo(13.5,24.0); path.lineTo(14.0,24.0); path.lineTo(14.0,3.0); path.quadTo(14.0,2.5,13.5,2.4); path.lineTo(7.5,1.5); path.cubicTo(6.5,-5.5,1.5,-23,0.5,-24.0); path.lineTo(0,-24.0); boundingRect = QRectF(-14.0, -24.0, 28.0, 48.0); border = 14.5; break; case Boats::rsfeva: m_numberSize = 7; posY = 10; flagRect = QRectF(-3, 12 , 6, 4); mast = QPointF(0, -5.8); sailSize = 23.8; jibTackPos = QPointF(0,-16.0); jibSize = 11.2; gennTackPos = QPointF(0,-27.2); gennPoleLength = 9.1; gennSize = 17.0; maxWithSpinSailAngle = 40; maxWithSpinJibAngle = 35; path.moveTo(0,-18.2); path.quadTo(0.2,-18.2,0.4,-18.0); path.quadTo(7.1,-6.8,7.1,4.8); path.quadTo(7.1,10.4,5.3,18.2); path.lineTo(-5.3,18.2); path.quadTo(-7.1,10.4,-7.1,4.8); path.quadTo(-7.1,-6.8,-0.4,-18.0); path.quadTo(-0.2,-18.2,0,-18.2); boundingRect = QRectF(-7.1,-18.2,14.2,36.4); border = 5.3; break; case Boats::int470: m_numberSize = 7; posY = 9; flagRect = QRectF(-3, 13, 6, 4); mast = QPointF(0, -7.5); sailSize = 26.5; jibTackPos = QPointF(0,-23.5); jibSize = 0.6*sailSize; spinSize = 19.0; path.moveTo(0,-23.5); path.cubicTo(0.8, -23.5, 8.5, -15.0, 8.5, 3.5); path.quadTo(8.5, 12.5, 5.8, 23.5); path.lineTo(-5.8,23.5); path.quadTo(-8.5, 12.5, -8.5, 3.5); path.cubicTo(-8.5, -15.0, -0.8, -23.5, 0, -23.5); boundingRect = QRectF(-8.5,-23.5,17.0,47.0); border = 5.8; break; case Boats::int420: m_numberSize = 7; posY = 9; flagRect = QRectF(-3, 13, 6, 4); mast = QPointF(0, -7.1); sailSize = 24.0; jibTackPos = QPointF(0,-21); jibSize = 0.6*sailSize; spinSize = 17.5; path.moveTo(0,-21); path.cubicTo(1.5, -21, 8.15, -12.5, 8.15, 3.3); path.quadTo(8.15, 11.5, 5.7, 20); path.quadTo(2.8, 21, 0, 21); path.quadTo(-2.8, 21, -5.7, 20); path.quadTo(-8.15, 11.5, -8.15, 3.3); path.cubicTo(-8.15, -12.5, -1.5, -21, 0, -21); boundingRect = QRectF(-8.15,-21,16.3,42); border = 5.7; break; case Boats::int29er: m_numberSize = 7; posY = 9; flagRect = QRectF(-3, 13, 6, 4); mast = QPointF(0, -3.4); sailSize = 20.5; jibTackPos = QPointF(0,-20.5); jibSize = 17.0; gennTackPos = QPointF(0,-34.2); gennPoleLength = 13.7; gennSize = 30.8; maxWithSpinSailAngle = 40; maxWithSpinJibAngle = 35; path.moveTo(0,-20.5); path.lineTo(-0.5,-20.5); path.cubicTo(-3.5,-10.25,-6.6,-1.1,-8.3,-0.5); path.lineTo(-8.8,18); path.lineTo(-4.4,20.5); path.lineTo(4.4,20.5); path.lineTo(8.8,18); path.lineTo(8.3,-0.5); path.cubicTo(6.6,-1.1,3.5,-10.25,0.5,-20.5); path.lineTo(0,-20.5); boundingRect = QRectF(-8.8, -20.5, 17.6, 41.0); border = 4.4; break; case Boats::finn: m_numberSize = 8; posY = 11; flagRect = QRectF(-3, 13, 6, 4); mast = QPointF(0, -15.0); sailSize = 33.3; path.moveTo(0,-22.5); path.quadTo(-7.35,-9.1,-7.35,5.1); path.quadTo(-7.35,14.9,-4.9,22.5); path.lineTo(4.9,22.5); path.quadTo(7.35,14.9,7.35,5.1); path.quadTo(7.35,-9.1,0,-22.5); boundingRect = QRectF(-7.35,-22.5,14.7,45.0); border = 4.9; break; case Boats::laser: m_numberSize = 7; posY = 10; flagRect = QRectF(-3, 12 , 6, 4); mast = QPointF(0, -8.7); sailSize = 28.5; path.moveTo(0,-20); path.cubicTo(0.3, -19.7, 0.3, -20.0, 0.7, -19.7); path.cubicTo(3.3, -14.3, 6.7, -3.3, 6.7, 4.7); path.cubicTo(6.7, 11.0, 6.7, 14.3, 5.0, 20.0); path.lineTo(-5.0, 20.0); path.cubicTo(-6.7, 14.3, -6.7, 11.0, -6.7, 4.7); path.cubicTo(-6.7, -3.3, -3.3, -14.3, -0.7, -19.7); path.cubicTo(-0.3, -20.0, -0.3, -19.7, 0, -20); boundingRect = QRectF(-6.7, -20, 13.4, 40); border = 5; break; case Boats::firefly: m_numberSize = 7; posY = 10; flagRect = QRectF(-3, 12 , 6, 4); mast = QPointF(0, -6.5); sailSize = 21.8; jibTackPos = QPointF(0, -18.3); jibSize = 14; path.moveTo(0,-18.3); path.cubicTo(3.7, -14.0, 7.6, -8.0, 7.6, 1.8); path.cubicTo(7.6, 4.2, 7.6, 9.2, 4.9, 18.3); path.lineTo(-4.9, 18.3); path.cubicTo(-7.6, 9.2, -7.6, 4.2, -7.6, 1.8); path.cubicTo(-7.6, -8.0, -3.7, -14.0, 0, -18.3); boundingRect = QRectF(-7.6, -18.3, 15.2, 36.6); border = 4.9; break; case Boats::topper: m_numberSize = 7; posY = 8; flagRect = QRectF(-3, 10 , 6, 4); mast = QPointF(0, -6.5); sailSize = 23.5; path.moveTo(0,-17); path.cubicTo(2.4, -17.0, 2.8, -16.0, 3.7, -14.0); path.cubicTo(4.6, -12.0, 5.8, -6.0, 5.8, -1.0); path.cubicTo(5.8, 1.0, 5.8, 8, 4.4, 17.0); path.lineTo(-4.4, 17.0); path.cubicTo(-5.8, 8, -5.8, 1.0, -5.8, -1.0); path.cubicTo(-5.8, -6.0, -4.6, -12.0, -3.7, -14.0); path.cubicTo(-2.8, -16.0, -2.4, -17.0, 0, -17); boundingRect = QRectF(-5.8, -17, 11.6, 34); border = 4.4; break; case Boats::optimist: m_numberSize = 6; posY = 3; flagRect = QRectF(-2.25, 5 , 4.5, 3); mast = QPointF(0,-6.9); sailSize = 16.5; path.moveTo(0,-11.5); path.cubicTo(1.5, -11.5, 1.7, -11.3, 2.9, -11.1); path.cubicTo(3.6, -9.4, 5.6, -4.0, 5.6, 1.5); path.cubicTo(5.6, 5.4, 5.0, 9.0, 4.6, 11.5); path.lineTo(-4.6, 11.5); path.cubicTo(-5.0, 9.0, -5.6, 5.4, -5.6, 1.5); path.cubicTo(-5.6, -4.0, -3.6, -9.4, -2.9, -11.1); path.cubicTo(-1.7, -11.3, -1.5, -11.5, 0, -11.5); boundingRect = QRectF(-5.6, -11.5, 11.2, 23); border = 4.6; break; case Boats::tornado: m_numberSize = 10; posY = 17; flagRect = QRectF(-4.5, 17.5 , 9, 6); mast = QPointF(0,0); sailSize = 25.5; maxNormalSailAngle = 20; gennTackPos = QPointF(0,-40); gennPoleLength = 40; gennSize = 42; maxWithSpinSailAngle = 20; path.moveTo(0,0); path.lineTo(10.7, 0); path.cubicTo(11.2, -11.7, 12.2, -19.8, 13.2, -30.5); path.cubicTo(14.7, -20.3, 15.3, -6.1, 15.3, 6.1); path.cubicTo(15.3, 13.7, 14.7, 20.8, 14.7, 30.0); path.cubicTo(13.7, 30.5, 13.7, 30.5, 12.7, 30.5); path.cubicTo(12.2, 30.5, 12.2, 30.5, 10.7, 30.0); path.lineTo(10.7, 23.9); path.lineTo(-10.7, 23.9); path.lineTo(-10.7, 30.0); path.cubicTo(-12.2, 30.5, -12.2, 30.5, -12.7, 30.5); path.cubicTo(-13.7, 30.5, -13.7, 30.5, -14.7, 30); path.cubicTo(-14.7, 20.8, -15.3, 13.7, -15.3, 6.1); path.cubicTo(-15.3, -6.1, -14.7, -20.3, -13.2, -30.5); path.cubicTo(-12.2, -19.8, -11.2, -11.7, -10.7, 0); path.lineTo(0, 0); boundingRect = QRectF(-15.3, -30.5, 30.6, 61); border = 14.7; break; case Boats::nacra17: m_numberSize = 7; posY = 14; flagRect = QRectF(-3, 16, 6, 4); mast = QPointF(0, 0); sailSize = 20.95; jibTackPos = QPointF(0,-15.05); jibSize = 14.15; gennTackPos = QPointF(0,-33.35); gennPoleLength = 0; gennSize = 33.65; maxNormalSailAngle = 20; maxNormalJibAngle = 20; maxWithSpinSailAngle = 20; maxWithSpinJibAngle = 20; path.moveTo(0,-0.65); path.lineTo(-8.95,-0.65); path.quadTo(-8.95,-16.75,-10.95,-25.90); path.quadTo(-12.95,-16.75,-12.95,-0.65); path.quadTo(-12.95,21.90,-12.70,26.60); path.lineTo(-9.20,26.60); path.lineTo(-9.00,21.45); path.lineTo(9.00,21.45); path.lineTo(9.20,26.60); path.lineTo(12.7,26.60); path.quadTo(12.95,21.90,12.95,-0.65); path.quadTo(12.95,-16.75,10.95,-25.90); path.quadTo(8.95,-16.75,8.95,-0.65); path.lineTo(0,-0.65); path.lineTo(0,-33.35); boundingRect = QRectF(-12.95,-33.35,25.90,59.95); border = 12.70; break; case Boats::diam24: m_numberSize = 10; posY = 17; flagRect = QRectF(-7.5, 17.5 , 15, 10); mast = QPointF(0,0); sailSize = 29; jibTackPos = QPointF(0,-25); jibSize = 23; gennTackPos = QPointF(0,-37.1); gennPoleLength = 0; gennSize = 57.5; maxNormalSailAngle = 20; maxNormalJibAngle = 20; maxWithSpinSailAngle = 20; maxWithSpinJibAngle = 20; path.moveTo(0,-37.1); path.cubicTo(1.3,-25.7,3.3,-14.0,3.3,-2.7); path.lineTo(23.4,-1.7); path.cubicTo(23.1,-18.4,24.1,-28.7,25.7,-36.8); path.cubicTo(29.7,-12.7,27.7,10.7,28.1,35.4); path.lineTo(23.7,35.4); path.lineTo(23.7,30.7); path.lineTo(2.7,30.7); path.lineTo(2.7,35.4); path.lineTo(-2.7,35.4); path.lineTo(-2.7,30.7); path.lineTo(-23.7,30.7); path.lineTo(-23.7,35.4); path.lineTo(-28.1,35.4); path.cubicTo(-27.7,10.7,-29.7,-12.7,-25.7,-36.8); path.cubicTo(-24.1,-28.7,-23.1,-18.4,-23.4,-1.7); path.lineTo(-3.3,-2.7); path.cubicTo(-3.3,-14.0,-1.3,-25.7,0,-37.1); boundingRect = QRectF(-29.7,-37.0,59.4,72.5); border = 28.10; break; case Boats::startboat: m_numberSize = 0; flagRect = QRectF(-7.5, 30 , 15, 10); path.moveTo(0,-50); path.cubicTo(30, -20, 20, 30, 17, 50); path.lineTo(-17, 50); path.cubicTo(-20, 30, -30, -20, 0, -50); path.addEllipse(-1, -10, 2, 2); boundingRect = QRectF(-30, -50, 60, 100); border = 17; break; case Boats::rib: m_numberSize = 10; posY = 10; flagRect = QRectF(-5, 15 , 10, 6.7); path.moveTo(0,-30); path.cubicTo(6, -26, 12.1, -22.9, 12.4, -10.3); path.lineTo(12.4, 23.5); path.lineTo(8.9, 30); path.lineTo(5.5, 23.5); path.lineTo(-5.5, 23.5); path.lineTo(-8.9, 30); path.lineTo(-12.4, 23.5); path.lineTo(-12.4, -10.3); path.cubicTo(-12.1, -22.9, -6, -26, 0, -30); boundingRect = QRectF(-12.4, -30, 24.8, 60); border = 8.9; default: break; } m_hullPath = path; m_boundingRect = boundingRect; m_numberPath->setPos(0, posY); m_flagRect->setRect(flagRect); if (sailSize) { m_boat->setMaxNormalSailAngle(maxNormalSailAngle); m_boat->setMaxWithSpinSailAngle(maxWithSpinSailAngle); m_sail->setPosition(mast); m_sail->setSailSize(sailSize); m_sail->setVisible(true); } else { m_sail->setVisible(false); } if (jibSize) { m_boat->setMaxNormalJibAngle(maxNormalJibAngle); m_boat->setMaxWithSpinJibAngle(maxWithSpinJibAngle); m_jib->setPosition(jibTackPos); m_jib->setSailSize(jibSize); m_jib->setVisible(true); } else { m_jib->setVisible(false); } m_hasSpin = false; m_hasGenn = false; if (spinSize) { m_hasSpin = true; m_spin->setPosition(mast); m_spin->setSailSize(spinSize); m_spin->setVisible(m_boat->spin()); } else { m_spin->setVisible(false); } if (gennSize) { m_hasGenn = true; m_genn->setPosition(gennTackPos); m_genn->setPoleLength(gennPoleLength); m_genn->setSailSize(gennSize); m_genn->setVisible(m_boat->spin()); } else { m_genn->setVisible(false); } m_boat->setHasSpin(m_hasSpin || m_hasGenn); // set setHasSpin true irrespective of type of spinnaker (is just used to control whether Toggle Spinnaker menu item is active) m_border = border; setOverlapLine(); setOrder(m_order); update(); } } void BoatGraphicsItem::deleteItem(BoatModel *boat) { if (boat == m_boat) { if (debugLevel & 1 << VIEW) std::cout << "deleting boatGraphics for model" << m_boat << std::endl; scene()->removeItem(this); delete this; } } void BoatGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { m_multiSelect = (event->modifiers() & Qt::ControlModifier) != 0; m_trackSelect = (event->modifiers() & Qt::ShiftModifier) != 0; bool selection = true; if (m_multiSelect) { selection = !isSelected(); } if (m_trackSelect) { m_boat->track()->setSelected(selection); } else { if (selection) { m_boat->situation()->addSelectedBoat(m_boat); } else { m_boat->situation()->removeSelectedModel(m_boat); } setSelected(selection); } update(); } void BoatGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { Q_UNUSED(event); } void BoatGraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { Q_UNUSED(event); } void BoatGraphicsItem::setSelected(bool selected) { QGraphicsItem::setSelected(selected); } QRectF BoatGraphicsItem::boundingRect() const { if (m_hidden) return QRectF(-3, -3, 6, 6); else return m_boundingRect; } QPainterPath BoatGraphicsItem::shape() const { QPainterPath path; path.addRegion(boundingRegion(QTransform())); return path; } void BoatGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); if (isSelected()) painter->setPen(Qt::red); else painter->setPen(Qt::black); painter->setBrush(m_color); if (m_hidden) painter->drawEllipse(-3, -3, 6, 6); else painter->drawPath(m_hullPath); } int BoatGraphicsItem::type() const { return BOAT_TYPE; } boats-201908/graphicsview/boat.h000066400000000000000000000116621353032755300165340ustar00rootroot00000000000000// // C++ Interface: BoatGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #ifndef BOAT_H #define BOAT_H #include "boats.h" #include "sail.h" #include "spinnaker.h" #include "gennaker.h" #include "flag.h" #include "bubble.h" #include "laylines.h" #include #include #include class BoatModel; /** \class BoatGraphicsItem \brief the QGraphicsItem for a boat The class represents the Item according to the Graphics View Framework. It inherits QGraphicsItem for Item framework and QObject for slot and signal framework. It displays the representation of a BoatModel on a SituationView. \sa SituationView, SituationScene, SituationModel, BoatModel */ class BoatGraphicsItem : public QObject, public QGraphicsItem { Q_OBJECT Q_INTERFACES(QGraphicsItem) public: BoatGraphicsItem(BoatModel *boat, QGraphicsItem *parent = 0); virtual ~BoatGraphicsItem(); BoatModel* boat() const { return m_boat; } BubbleGraphicsItem* bubble() const { return m_bubble; } QRectF boundingRect() const; QPainterPath shape() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); virtual int type() const; public slots: void setHeading(qreal value); void setPosition(QPointF position); void setOrder(int value); void setSpin(bool value); void setOverlap(Boats::Overlaps value); void setDisplayFlag(Boats::Flag value); void setHidden(bool value); void setColor(QColor value); void setDim(int value); void setVisible(bool value); void setSeries(Boats::Series value); void deleteItem(BoatModel *boat); void setSelected(bool selected); protected: virtual void mousePressEvent(QGraphicsSceneMouseEvent *event); virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event); virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); private: void setOverlapLine(); /// \a m_boat holds the BoatModel being represented BoatModel *m_boat; /// \a m_hullPath holds the path for the hull QPainterPath m_hullPath; /// \a m_boundingRect holds the bounding rectangle for the boat QRectF m_boundingRect; /// \a m_angle holds the heading of the boat qreal m_angle; /// \a m_sail holds the sail that will be drawn SailGraphicsItem *m_sail; /// \a m_jib holds the jib SailGraphicsItem *m_jib; /// \a m_spin holds the spinnaker SpinnakerGraphicsItem *m_spin; /// \a m_genn holds the gennaker GennakerGraphicsItem *m_genn; /// \a m_hasSpin is true if boat has a spinnaker bool m_hasSpin; /// \a m_hasGenn is true if boat has a gennaker bool m_hasGenn; /// \a m_overlap holds whether an overlap line should be displayed Boats::Overlaps m_overlap; QGraphicsLineItem *m_overlapLine; /// \a m_border holds the half-width of the boat at the transom qreal m_border; /// \a m_color holds the color of the TrackModel QColor m_color; /// \a m_flag holds the flag to display Boats::Flag m_flag; FlagGraphicsItem *m_flagRect; /// \a m_hidden holds whether the hidden symbol should be displayed bool m_hidden; /// \a m_bubble holds the bubble to display BubbleGraphicsItem *m_bubble; /// \a m_series holds the series of the TrackModel Boats::Series m_series; /// \a m_order holds the stacking order in the TrackModel int m_order; /// \a m_numberPath holds the number path that will be drawn on the hull QGraphicsPathItem *m_numberPath; /// \a m_numberSize holds the font size of the number int m_numberSize; /// \a m_laylines holds the laylines for the boat LaylinesGraphicsItem *m_laylines; /// \a m_multiSelect is true if Ctrl-modified was in effect when mousePressEvent happened bool m_multiSelect; /// \a m_trackSelect is true if mousePressEvent is Shift-modified bool m_trackSelect; }; #endif boats-201908/graphicsview/boatanimation.cpp000066400000000000000000000123771353032755300207730ustar00rootroot00000000000000// // C++ Implementation: BoatAnimation // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2010 Thibaut GRIDEL // // 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 . // // #include #include #include #include "boatanimation.h" #include "headinganimation.h" #include "situationmodel.h" #include "splineanimation.h" #include "trackmodel.h" #include "boatmodel.h" #include "boat.h" /** Populates the internal lists of Positions and Headings function of time This method parses the path of the \a track, does some stalling analysis and determines position and heading as a function of time. It also gives the pale color to \a track boats, while keeping original color for animation \a boat. */ BoatAnimation::BoatAnimation(TrackModel *track, BoatGraphicsItem *boat, int maxSize, QObject *parent) : QParallelAnimationGroup(parent), m_track(track), m_boat(boat), m_headingAnimation(new HeadingAnimation(boat->boat(), "heading")), m_sailAngleAnimation(new HeadingAnimation(boat->boat(), "trimSailAngle")), m_posAnimation(new QSequentialAnimationGroup), m_maxSize(maxSize), m_time(QTime::currentTime()) { QPainterPath path = m_track->path(); int size = m_track->size() - 1; if (size < 0) { return; } m_boat->setOrder(0); QPointF point = path.elementAt(0); // setPosAt(0,point); BoatModel *model = m_track->boats()[0]; m_headingAnimation->setDuration(2000*m_maxSize); m_headingAnimation->setStartValue(model->heading()); m_sailAngleAnimation->setDuration(2000*m_maxSize); m_sailAngleAnimation->setStartValue(model->trimmedSailAngle()); // through all boats of the track for (int i=0; i< size; i++) { qreal index = 0; QPointF c1 = path.elementAt(i*3+1); QPointF c2 = path.elementAt(i*3+2); QPointF end = path.elementAt(i*3+3); bool stalled = ((point == c1) || (c2 == end)); QPainterPath curve(point); curve.cubicTo(c1,c2,end); qreal length = curve.length(); float e = 8; // through 8 intermediary points between 2 boats for (int j=1; j<=e; j++) { qreal percent = curve.percentAtLength(length*j/e); index = (i*e+j)/(maxSize*e); if (!stalled) { m_headingAnimation->setKeyValueAt(index, fmod(360+90-curve.angleAtPercent(percent),360.0)); } } model = m_track->boats()[i+1]; if (stalled) { m_headingAnimation->setKeyValueAt(index, model->heading()); } m_sailAngleAnimation->setKeyValueAt(index, model->trimmedSailAngle()); SplineAnimation *splineAnimation = new SplineAnimation(m_boat->boat(), "pos", m_track->boats()[i]->path()); splineAnimation->setStartValue(point); splineAnimation->setEndValue(end); splineAnimation->setDuration(2000); m_posAnimation->addAnimation(splineAnimation); point = end; } // dim all track boats foreach(BoatModel *boat, m_track->boats()) { boat->setDim(true); } } /** Resets the color and List of BoatModels in the track */ BoatAnimation::~BoatAnimation() { while (!m_boats.isEmpty()) { m_track->addBoat(m_boats.last()); m_boats.pop_back(); } // undim all track boats foreach(BoatModel *boat, m_track->boats()) { boat->setDim(false); } } /** Updates TrackModel and animation boat position and heading at \a step time, with a rate limit of one refresh every 40ms */ void BoatAnimation::afterAnimationStep(qreal step) { // limit update rate to 40ms if ((m_time.elapsed() < 40) && (step != 0) && (step != 1)) { return; } m_headingAnimation->setCurrentTime(step*2000*m_maxSize); m_sailAngleAnimation->setCurrentTime(step*2000*m_maxSize); m_posAnimation->setCurrentTime(step*2000*m_maxSize); int index = floor(step * m_maxSize); BoatModel *boat; for (int i=m_track->size()-1; i > index; i--) { boat = m_track->boats()[i]; m_boats.push_back(boat); m_track->deleteBoat(boat); } if (!m_boats.isEmpty()) { for (int i = m_track->size()-1; i < index; i++) { m_track->addBoat(m_boats.last()); m_boats.pop_back(); } } if (index > m_track->size() - 1) { return; } boat = m_track->boats()[index]; m_boat->boat()->setOverlap(boat->overlap()); m_boat->boat()->setFlag(boat->flag()); m_boat->boat()->setText(boat->text()); m_boat->boat()->setTextPosition(boat->textPosition()); m_boat->boat()->setSpin(boat->spin()); // trigger next update rate calculation m_time.start(); } boats-201908/graphicsview/boatanimation.h000066400000000000000000000046521353032755300204350ustar00rootroot00000000000000// // C++ Interface: BoatAnimation // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2010 Thibaut GRIDEL // // 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 . // // #ifndef BOATANIMATION_H #define BOATANIMATION_H #include #include #include class BoatGraphicsItem; class TrackModel; class BoatModel; class HeadingAnimation; /** \class BoatAnimation \brief The animation Item for a scenario The class is the animation helper for a Scenario, according to the Graphics View Framework. It inherits QGraphicsItemAnimation and provides BoatGraphicsItem specific animation features, like position and heading given a time step. It also does a rate limiting update for drawing as Windows platforms suffer from that. \sa SituationScene, SituationModel */ class BoatAnimation : public QParallelAnimationGroup { public: BoatAnimation(TrackModel *track, BoatGraphicsItem *boat, int maxSize, QObject *parent = 0); ~BoatAnimation(); BoatGraphicsItem *boat() const {return m_boat; } protected: virtual void afterAnimationStep(qreal step); private: /// \a m_track holds the reference to the track TrackModel *m_track; /// \a m_boats holds the List of boats hidden during animation QList m_boats; /// \a m_boat holds the reference to the animation BoatGraphicsItem BoatGraphicsItem *m_boat; HeadingAnimation *m_headingAnimation; HeadingAnimation *m_sailAngleAnimation; QSequentialAnimationGroup *m_posAnimation; /// \a m_maxSize holds the maximum track size of the scenario int m_maxSize; /// \a m_time holds the timer used for movement filtering QTime m_time; }; #endif boats-201908/graphicsview/bubble.cpp000066400000000000000000000126011353032755300173670ustar00rootroot00000000000000// // C++ Implementation: BubbleGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2010 Thibaut GRIDEL // // 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 . // // #include "bubble.h" #include "commontypes.h" #include "situationmodel.h" #include "trackmodel.h" #include "boatmodel.h" #include #include #include #include extern int debugLevel; BubbleGraphicsItem::BubbleGraphicsItem(PositionModel *model, QGraphicsItem *parent) : QGraphicsTextItem(parent), m_model(model), m_tail(new QGraphicsPolygonItem(parent)) { setFlag(QGraphicsItem::ItemIsMovable); setFlag(QGraphicsItem::ItemIsSelectable); m_tail->setZValue(0); m_tail->setBrush(Qt::white); updateText(model->text()); setPosition(model->textPosition()); connect(document(), SIGNAL(contentsChanged()), this, SLOT(setText())); if (m_model->inherits("BoatModel")) { BoatModel *boat = static_cast (m_model); connect(boat, SIGNAL(headingChanged(qreal)), this, SLOT(setTail())); } connect(model, SIGNAL(textChanged(QString)), this, SLOT(updateText(QString))); connect(model, SIGNAL(textPositionChanged(QPointF)), this, SLOT(setPosition(QPointF))); } BubbleGraphicsItem::~BubbleGraphicsItem() {} /// update view from model changes void BubbleGraphicsItem::updateText(QString value) { setVisible(!value.isEmpty()); if (document()->toPlainText() != value) setPlainText(value); setTail(); } /// set model from control changes void BubbleGraphicsItem::setText() { if (m_model->situation()) { if (document()->toPlainText() != m_model->text()) { m_model->situation()->addSelectedModel(m_model); m_model->situation()->setText(document()->toPlainText()); setVisible(!document()->toPlainText().isEmpty()); setTail(); } } } /// update view from model changes void BubbleGraphicsItem::setPosition(QPointF position) { setPos(position); setTail(); update(); } void BubbleGraphicsItem::setTail() { //centerx,y is the centre of the text relative to pos() double centerx = boundingRect().width()/2 - 4; double centery = boundingRect().height()/2 - 4; double a = 0; if (BoatModel *boat = dynamic_cast (m_model)) { a = -boat->heading(); // set the transform to have horizontal text setTransformOriginPoint(centerx, centery); // Rotate about centre of text rather than top left corner setRotation(a); } // x,y is the center of the text double x = pos().x() + centerx; double y = pos().y() + centery; // t is a factor for the width of the tail double t = boundingRect().height()/5; // q is the angle of the tail double q = atan2(y, x); QPolygonF polygon; polygon << QPointF(x,y) << QPointF(x - t*sin(q), y + t*cos(q)) << QPointF(x/4, y/4) << QPointF(x + t*sin(q), y - t*cos(q)); m_tail->setPolygon(polygon); } QRectF BubbleGraphicsItem::boundingRect() const { return QGraphicsTextItem::boundingRect().adjusted(-4, -4, 4, 4); } void BubbleGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { QPainterPath path; path.addRoundedRect(QGraphicsTextItem::boundingRect().adjusted(-2, -2, 2, 2), 15.0, 15.0, Qt::RelativeSize); painter->save(); painter->setBrush(Qt::white); painter->drawPath(path); painter->restore(); QGraphicsTextItem::paint(painter, option, widget); } void BubbleGraphicsItem::setVisible(bool visible) { QGraphicsItem::setVisible(visible); m_tail->setVisible(visible); } void BubbleGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { m_clickTime.start(); QGraphicsTextItem::mousePressEvent(event); m_fromPosition = m_model->textPosition(); } void BubbleGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { QGraphicsTextItem::mouseMoveEvent(event); setTail(); } void BubbleGraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { bool click = (m_clickTime.elapsed() < 250); if (click) { setTextInteractionFlags(Qt::TextEditorInteraction); setFocus(Qt::MouseFocusReason); } else { setTextInteractionFlags(Qt::NoTextInteraction); } QGraphicsTextItem::mouseReleaseEvent(event); if (pos() != m_fromPosition) { m_model->situation()->addSelectedModel(m_model); m_model->situation()->moveText(pos()); } } void BubbleGraphicsItem::focusOutEvent(QFocusEvent *event) { setTextInteractionFlags(Qt::NoTextInteraction); QGraphicsTextItem::focusOutEvent(event); } boats-201908/graphicsview/bubble.h000066400000000000000000000050641353032755300170410ustar00rootroot00000000000000// // C++ Interface: BubbleGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009-2010 Thibaut GRIDEL // // 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 . // // #ifndef BUBBLE_H #define BUBBLE_H #include "boats.h" #include #include #include class PositionModel; /** \class BubbleGraphicsItem \brief the QGraphicsItem for a bubble The class represents the Item according to the Graphics View Framework. It inherits QGraphicsItem for Item framework and QObject for slot and signal framework. It displays the text representation of a PositionModel on a SituationView. \sa SituationView, SituationScene, SituationModel, BoatModel */ class BubbleGraphicsItem : public QGraphicsTextItem { Q_OBJECT public: BubbleGraphicsItem(PositionModel *model, QGraphicsItem *parent = 0); virtual ~BubbleGraphicsItem(); PositionModel* model() const { return m_model; } QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void setVisible(bool visible); public slots: void updateText(QString value); void setText(); void setPosition(QPointF position); void setTail(); protected: virtual void mousePressEvent(QGraphicsSceneMouseEvent *event); virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event); virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); virtual void focusOutEvent (QFocusEvent *event); private: /// \a m_model holds the PositionModel being represented PositionModel *m_model; /// \a m_fromPosition holds the position of the mousePress QPointF m_fromPosition; /// \a m_tail holds the leader line to the boat QGraphicsPolygonItem *m_tail; QTime m_clickTime; }; #endif // BUBBLE_H boats-201908/graphicsview/flag.cpp000066400000000000000000000061401353032755300170460ustar00rootroot00000000000000// // C++ Implementation: FlagGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009 Thibaut GRIDEL // // 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 . // // #include "flag.h" #include "commontypes.h" #include FlagGraphicsItem::FlagGraphicsItem(QGraphicsItem *parent) : QGraphicsItem(parent) { } FlagGraphicsItem::~FlagGraphicsItem() {} QRectF FlagGraphicsItem::boundingRect() const { return m_rect; } QPainterPath FlagGraphicsItem::shape() const { QPainterPath path; path.addRect(m_rect); return path; } void FlagGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); switch (m_flag) { case Boats::Y: { painter->setBrush(Qt::red); painter->drawRect(m_rect); painter->translate(m_rect.left(), m_rect.top()); QPolygonF polygon; qreal sixth = m_rect.width() / 6; qreal height = m_rect.height(); polygon << QPointF(0, 0) << QPointF(sixth, 0) << QPointF(-3*sixth, height) << QPointF(-4*sixth, height); painter->setPen(Qt::transparent); painter->setBrush(Qt::yellow); for (int i=0; i<5; i++) { painter->drawPolygon(polygon.intersected(QPolygonF(QRectF(0, 0, m_rect.width(), m_rect.height())))); polygon.translate(2*sixth, 0); } break; } case Boats::Red: painter->setBrush(Qt::red); painter->drawRect(m_rect); break; case Boats::Yellow: painter->setBrush(Qt::yellow); painter->drawRect(m_rect); break; case Boats::Blue: painter->setBrush(Qt::blue); painter->drawRect(m_rect); break; case Boats::Green: { painter->setBrush(Qt::white); painter->drawRect(m_rect); painter->translate(m_rect.left(), m_rect.top()); QPolygonF polygon; qreal halfw = m_rect.width() / 2; qreal halfh = m_rect.height() / 2; polygon << QPointF(0, 0) << QPointF(halfw, 0) << QPointF(halfw, halfh) << QPointF(0, halfh); painter->setBrush(Qt::green); painter->drawPolygon(polygon); polygon.translate(halfw, halfh); painter->drawPolygon(polygon); break; } default: break; } } boats-201908/graphicsview/flag.h000066400000000000000000000034471353032755300165220ustar00rootroot00000000000000// // C++ Interface: FlagGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009 Thibaut GRIDEL // // 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 . // // #ifndef FLAG_H #define FLAG_H #include "boats.h" #include /** \class FlagGraphicsItem \brief the QGraphicsItem for a flag The class represents the Item according to the Graphics View Framework. It inherits QGraphicsItem for Item framework. It displays the representation of a Flag on a SituationView. \sa SituationView, SituationScene, */ class FlagGraphicsItem : public QGraphicsItem { public: FlagGraphicsItem(QGraphicsItem *parent = 0); virtual ~FlagGraphicsItem(); QRectF boundingRect() const; QPainterPath shape() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void setDisplayFlag(Boats::Flag value) {m_flag = value; update();} void setRect(QRectF value) { prepareGeometryChange(); m_rect = value; update();} private: /// \a m_flag holds the flag to display Boats::Flag m_flag; QRectF m_rect; }; #endif // FLAG_H boats-201908/graphicsview/gennaker.cpp000066400000000000000000000074721353032755300177400ustar00rootroot00000000000000// // C++ Implementation: GennakerGraphicsItem // // Description: // // // Author: Graham Louth // // Copyright (c) 2013 Graham Louth // // 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 . // // #include "gennaker.h" #include "commontypes.h" #include "trackmodel.h" #include "boatmodel.h" #include #include #include extern int debugLevel; GennakerGraphicsItem::GennakerGraphicsItem(BoatModel *boat, QGraphicsItem *parent) : SailGraphicsItem(boat, parent), m_pole(new QGraphicsLineItem(parent)) { //NB boat as parent to avoid need to offset sail rotatation for pole } GennakerGraphicsItem::~GennakerGraphicsItem() {} void GennakerGraphicsItem::setSailSize(qreal sailSize) { m_sailSize = sailSize; QPainterPath sailPathStalled; sailPathStalled.cubicTo(.1 * sailSize, .2 * sailSize, .1 * sailSize, .2 * sailSize, 0, .3 * sailSize); sailPathStalled.cubicTo(-.1 * sailSize, .4 * sailSize, -.1 * sailSize, .4 * sailSize, 0, .5 * sailSize); sailPathStalled.cubicTo(.1 * sailSize, .6 * sailSize, .1 * sailSize, .6 * sailSize, 0, .7 * sailSize); sailPathStalled.cubicTo(-.1 * sailSize, .8 * sailSize, -.1 * sailSize, .8 * sailSize, 0, sailSize); sailPathStalled.lineTo(0, 0); m_sailPathStalled = sailPathStalled; QPainterPath sailPathStarboard; sailPathStarboard.cubicTo(.35 * sailSize, .3 * sailSize, .28 * sailSize, .6 * sailSize, 0, sailSize); sailPathStarboard.cubicTo(.2 * sailSize, .6 * sailSize, .25 * sailSize, .3 * sailSize, 0, 0); m_sailPathStarboard = sailPathStarboard; QPainterPath sailPathPort; sailPathPort.cubicTo(-.35 * sailSize, .3 * sailSize, -.28 * sailSize, .6 * sailSize, 0, sailSize); sailPathPort.cubicTo(-.2 * sailSize, .6 * sailSize, -.25 * sailSize, .3 * sailSize, 0, 0); m_sailPathPort = sailPathPort; setSailAngle(m_sailAngle); } void GennakerGraphicsItem::setPoleLength(qreal length) { m_pole->setLine(0,0,0,length); QPen polePen; polePen.setWidthF(0.5); polePen.setCapStyle(Qt::FlatCap); m_pole->setPen(polePen); } void GennakerGraphicsItem::setPosition(QPointF position) { if (pos() != position) { setPos(position); m_pole->setPos(position); update(); } } void GennakerGraphicsItem::setVisible(bool visibility) { if (isVisible() != visibility) { SailGraphicsItem::setVisible(visibility); m_pole->setVisible(visibility); update(); } } /// calculate a sail incidence angle, corrected with user trimming void GennakerGraphicsItem::setSailAngle(qreal value) { m_sailAngle = value; qreal angle = fmod(m_boat->heading() - m_boat->wind() - m_sailAngle +360, 360); if(angle < 0) angle +=360; if ((angle < 55 || angle > 305 || (angle > 170 && angle < 190)) && path() != m_sailPathStalled) { setPath(m_sailPathStalled); } else if (angle >= 55 && angle <= 170 && path() != m_sailPathStarboard) { setPath(m_sailPathStarboard); } else if (angle >= 190 && angle <= 305 && path() != m_sailPathPort) { setPath(m_sailPathPort); } QTransform transform; transform.rotate(- m_sailAngle); setTransform(transform, false); } boats-201908/graphicsview/gennaker.h000066400000000000000000000035071353032755300174000ustar00rootroot00000000000000// // C++ Interface: GennakerGraphicsItem // // Description: // // // Author: Graham Louth // // Copyright (c) 2013 Graham Louth // // 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 . // // #ifndef GENNAKER_H #define GENNAKER_H #include "boats.h" #include "sail.h" #include #include class BoatModel; /** \class GennakerGraphicsItem \brief the QGraphicsItem for a gennaker The class represents the Item according to the Graphics View Framework. It inherits QGraphicsPathItem for Item framework and QObject for slot and signal framework. It displays the sail representation of a BoatModel on a SituationView. \sa SituationView, SituationScene, SituationModel, BoatModel */ class GennakerGraphicsItem : public SailGraphicsItem { Q_OBJECT public: GennakerGraphicsItem(BoatModel *boat, QGraphicsItem *parent = 0); virtual ~GennakerGraphicsItem(); void setSailSize(qreal size); void setPoleLength(qreal length); void setPosition(QPointF position); void setVisible(bool visibility); public slots: void setSailAngle(qreal value); private: QGraphicsLineItem *m_pole; }; #endif // GENNAKER_H boats-201908/graphicsview/graphicsview.pri000066400000000000000000000013031353032755300206340ustar00rootroot00000000000000INCLUDEPATH += $$PWD DEPENDPATH += $$PWD HEADERS += \ $$PWD/arrow.h \ $$PWD/boat.h \ $$PWD/bubble.h \ $$PWD/flag.h \ $$PWD/laylines.h \ $$PWD/mark.h \ $$PWD/point.h \ $$PWD/polyline.h \ $$PWD/sail.h \ $$PWD/situationscene.h \ $$PWD/situationview.h \ $$PWD/spinnaker.h \ $$PWD/gennaker.h \ $$PWD/track.h SOURCES += \ $$PWD/arrow.cpp \ $$PWD/boat.cpp \ $$PWD/bubble.cpp \ $$PWD/flag.cpp \ $$PWD/laylines.cpp \ $$PWD/mark.cpp \ $$PWD/point.cpp \ $$PWD/polyline.cpp \ $$PWD/sail.cpp \ $$PWD/situationscene.cpp \ $$PWD/situationview.cpp \ $$PWD/spinnaker.cpp \ $$PWD/gennaker.cpp \ $$PWD/track.cpp boats-201908/graphicsview/laylines.cpp000066400000000000000000000073321353032755300177610ustar00rootroot00000000000000// // C++ Implementation: LaylinesGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2011 Thibaut GRIDEL // // 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 . // // #include "laylines.h" #include "commontypes.h" #include "situationmodel.h" #include #include #include extern int debugLevel; LaylinesGraphicsItem::LaylinesGraphicsItem(PositionModel *model, QGraphicsItem *parent) : QGraphicsPathItem(parent), m_model(model), m_length(model->situation()->situationLength()), m_boatLength(Boats::seriesSizeList()[model->situation()->situationSeries()]), m_laylineAngle(model->situation()->laylineAngle()), m_heading(model->heading()) { setFlag(QGraphicsItem::ItemStacksBehindParent); setPen(Qt::DashLine); updatePath(); setVisible(m_model->laylines()); setWind(model->wind()); connect(m_model, SIGNAL(laylinesChanged(bool)), this, SLOT(setVisible(bool))); connect(m_model, SIGNAL(headingChanged(qreal)), this, SLOT(setHeading(qreal))); connect(m_model, SIGNAL(windChanged(qreal)), this, SLOT(setWind(qreal))); connect(m_model->situation(), SIGNAL(lengthChanged(int)), this, SLOT(setLength(int))); connect(m_model->situation(), SIGNAL(laylineChanged(int)), this, SLOT(setLaylineAngle(int))); connect(m_model->situation(), SIGNAL(seriesChanged(int)), this, SLOT(setSeries(int))); } LaylinesGraphicsItem::~LaylinesGraphicsItem() {} void LaylinesGraphicsItem::setLength(int value) { if (m_length != value) { prepareGeometryChange(); m_length = value; updatePath(); } } void LaylinesGraphicsItem::setSeries(int value) { int boatLength = Boats::seriesSizeList()[value]; if (m_boatLength != boatLength) { m_boatLength = boatLength; updatePath(); } } void LaylinesGraphicsItem::setLaylineAngle(int value) { if (m_laylineAngle != value) { m_laylineAngle = value; updatePath(); } } void LaylinesGraphicsItem::setHeading(qreal value) { if(m_heading != value) { m_heading = value; QTransform transform; transform.rotate(m_wind - m_heading); setTransform(transform, false); } } void LaylinesGraphicsItem::setWind(qreal value) { if(m_wind != value) { m_wind = value; QTransform transform; transform.rotate(m_wind - m_heading); setTransform(transform, false); } } void LaylinesGraphicsItem::setVisible(bool visible) { QGraphicsItem::setVisible(visible); // hack for performance issues. It seems an invisible path costs much if(m_visible != visible) { m_visible = visible; updatePath(); } } void LaylinesGraphicsItem::updatePath() { QPainterPath path; int r = 1.25 * m_length * m_boatLength; qreal theta = m_laylineAngle * M_PI /180; if(m_visible) { path.lineTo(r*sin(theta), r*cos(theta)); // starboard layline path.moveTo(0, 0); path.lineTo(-r*sin(theta), r*cos(theta)); // port layline } setPath(path); } boats-201908/graphicsview/laylines.h000066400000000000000000000044601353032755300174250ustar00rootroot00000000000000// // C++ Interface: LaylinesGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2011 Thibaut GRIDEL // // 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 . // // #ifndef LAYLINES_H #define LAYLINES_H #include "boats.h" #include #include class PositionModel; /** \class LaylinesGraphicsItem \brief the QGraphicsItem for laylines The class represents the Item according to the Graphics View Framework. It inherits QGraphicsPathItem for Item framework and QObject for slot and signal framework. It displays the laylines of a PositionModel on a SituationView. \sa SituationView, SituationScene, SituationModel, PositionModel */ class LaylinesGraphicsItem : public QObject, public QGraphicsPathItem { Q_OBJECT Q_INTERFACES(QGraphicsItem) public: LaylinesGraphicsItem(PositionModel *model, QGraphicsItem *parent = 0); virtual ~LaylinesGraphicsItem(); void updatePath(); public slots: void setLength(int length); void setSeries(int value); void setLaylineAngle(int value); void setHeading(qreal value); void setWind(qreal value); void setVisible(bool visible); protected: /// \a m_model holds the PositionModel being represented PositionModel *m_model; /// \a m_length holds the number of boat lengths for zone int m_length; /// \a m_boatLength holds the size in scene coordinates for main series int m_boatLength; /// \a m_laylineAngle holds the layline angle int m_laylineAngle; qreal m_heading; qreal m_wind; bool m_visible; }; #endif // LAYLINES_H boats-201908/graphicsview/mark.cpp000066400000000000000000000174141353032755300170750ustar00rootroot00000000000000// // C++ Implementation: MarkGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #include "mark.h" #include "commontypes.h" #include "situationscene.h" #include "situationmodel.h" #include "markmodel.h" #include "bubble.h" #include #include #include #include extern int debugLevel; MarkGraphicsItem::MarkGraphicsItem(MarkModel *mark, QGraphicsItem *parent) : QGraphicsItem(parent), m_mark(mark), m_color(mark->color()), m_zone(mark->zone()), m_length(mark->length()), m_boatLength(Boats::seriesSizeList()[m_mark->situation()->situationSeries()]), m_bubble(new BubbleGraphicsItem(m_mark, this)), m_order(mark->order()), m_laylines(new LaylinesGraphicsItem(m_mark, this)), m_heading(mark->heading()), m_arrowVisible(mark->arrowVisible()), m_leaveToPort(mark->leaveToPort()), m_labelVisible(mark->labelVisible()), m_labelText(mark->labelText()) { setFlag(QGraphicsItem::ItemIsMovable); setFlag(QGraphicsItem::ItemIsSelectable); setPos(mark->position()); setZValue(m_order); m_bubble->setZValue(1); connect(mark, SIGNAL(positionChanged(QPointF)), this, SLOT(setPosition(QPointF))); connect(mark, SIGNAL(orderChanged(int)), this, SLOT(setOrder(int))); connect(mark, SIGNAL(colorChanged(QColor)), this, SLOT(setColor(QColor))); connect(mark, SIGNAL(zoneChanged(bool)), this, SLOT(setZone(bool))); connect(mark, SIGNAL(lengthChanged(int)), this, SLOT(setLength(int))); connect(mark->situation(), SIGNAL(seriesChanged(int)), this, SLOT(setSeries(int))); connect(mark->situation(), SIGNAL(markRemoved(MarkModel*)), this, SLOT(deleteItem(MarkModel*))); connect(mark, SIGNAL(headingChanged(qreal)), this, SLOT(setHeading(qreal))); connect(mark, SIGNAL(arrowVisibilityChanged(bool)), this, SLOT(setArrowVisible(bool))); connect(mark, SIGNAL(leaveToPortChanged(bool)), this, SLOT(setLeaveToPort(bool))); connect(mark, SIGNAL(labelVisibilityChanged(bool)), this, SLOT(setLabelVisible(bool))); connect(mark, SIGNAL(labelTextChanged(QString)), this, SLOT(setLabelText(QString))); } MarkGraphicsItem::~MarkGraphicsItem() {} void MarkGraphicsItem::setPosition(QPointF position) { if (pos() != position) { setPos(position); update(); } } void MarkGraphicsItem::setOrder(int value) { if (m_order != value) { m_order = value; update(); } } void MarkGraphicsItem::setColor(QColor value) { if (m_color != value) { m_color = value; update(); } } void MarkGraphicsItem::setZone(bool value) { if (m_zone != value) { prepareGeometryChange(); m_zone = value; update(); } } void MarkGraphicsItem::setLength(int value) { if (m_length != value) { prepareGeometryChange(); m_length = value; update(); } } void MarkGraphicsItem::setSeries(int value) { int boatLength = Boats::seriesSizeList()[value]; if (m_boatLength != boatLength) { prepareGeometryChange(); m_boatLength = boatLength; update(); } } void MarkGraphicsItem::deleteItem(MarkModel *mark) { if (mark == m_mark) { if (debugLevel & 1 << VIEW) std::cout << "deleting markGraphics for model" << m_mark << std::endl; scene()->removeItem(this); delete this; } } void MarkGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { m_multiSelect = (event->modifiers() & Qt::ControlModifier) != 0; bool selection = true; if (m_multiSelect) { selection = !isSelected(); } setSelected(selection); if (selection) { m_mark->situation()->addSelectedMark(m_mark); } else { m_mark->situation()->removeSelectedModel(m_mark); } update(); } void MarkGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { Q_UNUSED(event); } void MarkGraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { Q_UNUSED(event); } void MarkGraphicsItem::setHeading(qreal heading) { if (m_heading != heading) { m_heading = heading; QTransform rotation; rotation.rotate(m_heading), setTransform(rotation, false); } } void MarkGraphicsItem::setArrowVisible(bool visible) { if (m_arrowVisible != visible) { m_arrowVisible = visible; update(); } } void MarkGraphicsItem::setLeaveToPort(bool leaveToPort) { if (m_leaveToPort != leaveToPort) { m_leaveToPort = leaveToPort; update(); } } void MarkGraphicsItem::setLabelVisible(bool visible) { if (m_labelVisible != visible) { m_labelVisible = visible; update(); } } void MarkGraphicsItem::setLabelText(QString text) { if (m_labelText != text) { m_labelText = text; update(); } } QRectF MarkGraphicsItem::boundingRect() const { int r = m_length * m_boatLength; return QRectF(-r, -r, 2*r, 2*r); } QPainterPath MarkGraphicsItem::shape() const { QPainterPath path; // Need to increase size of shape to include mark arrow so that can click on it to rotate path.addEllipse(QPointF(0,0),35,35); return path; } void MarkGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); if (isSelected()) painter->setPen(Qt::red); else painter->setPen(Qt::black); painter->setBrush(m_color); QPointF point(0, 0); painter->drawEllipse(point,10,10); if (m_labelVisible) { painter->rotate(-m_heading); painter->drawText(QRectF(-35,-35,70,70),Qt::AlignCenter,m_labelText); painter->rotate(m_heading); } if (m_zone) { painter->setBrush(Qt::NoBrush); painter->setPen(Qt::DashLine); int r = m_length * m_boatLength; painter->drawEllipse(point, r, r); } // Additional code to draw mark rounding arrow if (m_arrowVisible) { QPainterPath port_path, stbd_path; port_path.moveTo(-25,0); port_path.lineTo(-20,0); port_path.lineTo(-30,10); port_path.lineTo(-40,0); port_path.lineTo(-35,0); port_path.arcTo(-35,-35,70,70,180,-90); port_path.arcTo(-25,-25,50,50,90,90); stbd_path.moveTo(0,-25); stbd_path.lineTo(0,-20); stbd_path.lineTo(10,-30); stbd_path.lineTo(0,-40); stbd_path.lineTo(0,-35); stbd_path.arcTo(-35,-35,70,70,90,90); stbd_path.arcTo(-25,-25,50,50,180,-90); QPen arrowpen; arrowpen.setWidth(2); arrowpen.setColor(Qt::lightGray); painter->setPen(arrowpen); painter->setBrush(Qt::NoBrush); painter->rotate(45.0); if (m_leaveToPort) { painter->drawPath(port_path); } else { painter->drawPath(stbd_path); } } } int MarkGraphicsItem::type() const { return MARK_TYPE; } boats-201908/graphicsview/mark.h000066400000000000000000000075571353032755300165510ustar00rootroot00000000000000// // C++ Interface: MarkGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #ifndef MARK_H #define MARK_H #include "commontypes.h" #include "bubble.h" #include "laylines.h" #include #include #include class MarkModel; /** \class MarkGraphicsItem \brief the QGraphicsItem for a mark The class represents the Item according to the Graphics View Framework. It inherits QGraphicsItem for Item framework and QObject for slot and signal framework. It displays the representation of a MarkModel on a SituationView. \sa SituationView, SituationScene, SituationModel, MarkModel */ class MarkGraphicsItem : public QObject, public QGraphicsItem { Q_OBJECT Q_INTERFACES(QGraphicsItem) public: MarkGraphicsItem(MarkModel *mark = 0, QGraphicsItem *parent = 0); ~MarkGraphicsItem(); MarkModel* mark() const { return m_mark; } BubbleGraphicsItem* bubble() const { return m_bubble; } QRectF boundingRect() const; QPainterPath shape() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); int type() const; public slots: void setPosition(QPointF position); void setOrder(int value); void setColor(QColor value); void setZone(bool zone); void setLength(int length); void setSeries(int value); void deleteItem(MarkModel *mark); void setHeading(qreal heading); void setArrowVisible(bool visible); void setLeaveToPort(bool leaveToPort); void setLabelVisible(bool visible); void setLabelText(QString text); protected: virtual void mousePressEvent(QGraphicsSceneMouseEvent *event); virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event); virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); private: /// \a m_mark holds the MarkModel being represented MarkModel *m_mark; /// \a m_color holds the color of the mark QColor m_color; /// \a m_zone holds wether zone is displayed bool m_zone; /// \a m_length holds the number of boat lengths for zone int m_length; /// \a m_boatLength holds the size in scene coordinates for main series int m_boatLength; /// \a m_bubble holds the bubble to display BubbleGraphicsItem *m_bubble; /// \a m_order holds the stacking order in the mark list int m_order; /// \a m_laylines holds the laylines for the mark LaylinesGraphicsItem *m_laylines; /// \a m_heading holds the orientation of the arrow qreal m_heading; /// \a m_arrowVisible holds wether the mark arrow is visible bool m_arrowVisible; /// \a m_leaveToPort holds the orientation of the arrow bool m_leaveToPort; /// \a m_multiSelect is true if Ctrl-modified was in effect when mousePressEvent happened /// Need to save this state until receive mouseReleaseEvent to determine what to do bool m_multiSelect; bool m_labelVisible; QString m_labelText; }; #endif boats-201908/graphicsview/point.cpp000066400000000000000000000070331353032755300172700ustar00rootroot00000000000000// // C++ Implementation: PointGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009-2011 Thibaut GRIDEL // // 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 . // // #include "point.h" #include "commontypes.h" #include "situationscene.h" #include "situationmodel.h" #include "polylinemodel.h" #include "pointmodel.h" #include #include #include #include extern int debugLevel; const int size = 3; PointGraphicsItem::PointGraphicsItem(PointModel *point, QGraphicsItem *parent) : QGraphicsItem(parent), m_point(point), m_bubble(new BubbleGraphicsItem(m_point, this)), m_laylines(new LaylinesGraphicsItem(m_point, this)) { setZValue(1); setFlag(QGraphicsItem::ItemIsMovable); setFlag(QGraphicsItem::ItemIsSelectable); setPos(point->position()); m_bubble->setZValue(1); m_points << QPoint(0, -size) << QPoint(size, 0) << QPoint(0, size) << QPoint(-size, 0) << QPoint(0, -size); connect(point, SIGNAL(positionChanged(QPointF)), this, SLOT(setPosition(QPointF))); connect(point->polyLine()->situation(), SIGNAL(pointRemoved(PointModel *)), this, SLOT(deleteItem(PointModel*))); } PointGraphicsItem::~PointGraphicsItem() {} void PointGraphicsItem::setPosition(QPointF position) { if (pos() != position) { setPos(position); update(); } } void PointGraphicsItem::deleteItem(PointModel *point) { if (point == m_point) { if (debugLevel & 1 << VIEW) std::cout << "deleting pointGraphics for model" << m_point << std::endl; scene()->removeItem(this); delete this; } } void PointGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { bool selection = true; if ((event->modifiers() & Qt::ControlModifier) != 0) { selection = !isSelected(); } setSelected(selection); if (selection) { m_point->situation()->addSelectedPoint(m_point); } else { m_point->situation()->removeSelectedModel(m_point); } } void PointGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { Q_UNUSED(event); } void PointGraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { Q_UNUSED(event); } QRectF PointGraphicsItem::boundingRect() const { return QRectF(-size, -size, 2*size, 2*size); } QPainterPath PointGraphicsItem::shape() const { QPainterPath path; path.addPolygon(m_points); return path; } void PointGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); if (isSelected()) painter->setPen(Qt::red); else painter->setPen(Qt::black); painter->setBrush(Qt::black); painter->drawPolygon(m_points); } int PointGraphicsItem::type() const { return POINT_TYPE; } boats-201908/graphicsview/point.h000066400000000000000000000050601353032755300167330ustar00rootroot00000000000000// // C++ Interface: PointGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009-2011 Thibaut GRIDEL // // 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 . // // #ifndef POINT_H #define POINT_H #include "commontypes.h" #include "bubble.h" #include "laylines.h" #include #include class PointModel; /** \class PointGraphicsItem \brief the QGraphicsItem for a point The class represents the Item according to the Graphics View Framework. It inherits QGraphicsItem for Item framework and QObject for slot and signal framework. It displays the representation of a PositionModel on a SituationView. \sa SituationView, SituationScene, SituationModel, PositionModel */ class PointGraphicsItem : public QObject, public QGraphicsItem { Q_OBJECT Q_INTERFACES(QGraphicsItem) public: PointGraphicsItem(PointModel *point = 0, QGraphicsItem *parent = 0); ~PointGraphicsItem(); PointModel* point() const { return m_point; } BubbleGraphicsItem* bubble() const { return m_bubble; } QRectF boundingRect() const; QPainterPath shape() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); int type() const; public slots: void setPosition(QPointF position); void deleteItem(PointModel *point); protected: virtual void mousePressEvent(QGraphicsSceneMouseEvent *event); virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event); virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); private: /// \a m_point holds the PointModel being represented PointModel *m_point; /// \a m_bubble holds the bubble to display BubbleGraphicsItem *m_bubble; QPolygon m_points; /// \a m_laylines holds the laylines for the mark LaylinesGraphicsItem *m_laylines; }; #endif boats-201908/graphicsview/polyline.cpp000066400000000000000000000036051353032755300177730ustar00rootroot00000000000000// // C++ Implementation: PolyLineGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009-2010 Thibaut GRIDEL // // 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 . // // #include "polyline.h" #include "commontypes.h" #include "situationmodel.h" #include "polylinemodel.h" #include #include #include #include extern int debugLevel; PolyLineGraphicsItem::PolyLineGraphicsItem(PolyLineModel *polyline, QGraphicsItem *parent) : QGraphicsPathItem(parent), m_polyline(polyline) { setZValue(0); setPolyLine(); connect(polyline, SIGNAL(polyLineChanged(PolyLineModel*)), this, SLOT(setPolyLine())); connect(polyline->situation(), SIGNAL(polyLineRemoved(PolyLineModel*)), this, SLOT(deleteItem(PolyLineModel*))); } PolyLineGraphicsItem::~PolyLineGraphicsItem() {} void PolyLineGraphicsItem::setPolyLine() { prepareGeometryChange(); setPath(m_polyline->path()); update(); } void PolyLineGraphicsItem::deleteItem(PolyLineModel *polyline) { if (polyline == m_polyline) { if (debugLevel & 1 << VIEW) std::cout << "deleting polylineGraphics for model" << m_polyline << std::endl; scene()->removeItem(this); delete this; } } boats-201908/graphicsview/polyline.h000066400000000000000000000034711353032755300174410ustar00rootroot00000000000000// // C++ Interface: PolyLineGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009-2010 Thibaut GRIDEL // // 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 . // // #ifndef POLYLINE_H #define POLYLINE_H #include #include #include class PolyLineModel; /** \class PolyLineGraphicsItem \brief the QGraphicsItem for a polyline The class represents the Item according to the Graphics View Framework. It inherits QGraphicsItem for Item framework and QObject for slot and signal framework. It displays the representation of a PolyLineModel on a SituationView. \sa SituationView, SituationScene, SituationModel, PolyLineModel */ class PolyLineGraphicsItem : public QObject, public QGraphicsPathItem { Q_OBJECT public: PolyLineGraphicsItem(PolyLineModel *polyline = 0, QGraphicsItem *parent = 0); ~PolyLineGraphicsItem(); PolyLineModel* polyline() const { return m_polyline; } public slots: void setPolyLine(); void deleteItem(PolyLineModel *polyline); private: /// \a m_polyline holds the PolyLineModel being represented PolyLineModel *m_polyline; }; #endif boats-201908/graphicsview/sail.cpp000066400000000000000000000066071353032755300170750ustar00rootroot00000000000000// // C++ Implementation: SailGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #include "sail.h" #include "commontypes.h" #include "trackmodel.h" #include "boatmodel.h" #include #include #include extern int debugLevel; SailGraphicsItem::SailGraphicsItem(BoatModel *boat, QGraphicsItem *parent) : QGraphicsPathItem(parent), m_boat(boat), m_sailAngle(0), m_sailSize(0) { setZValue(0); setBrush(QBrush(Qt::white)); } SailGraphicsItem::~SailGraphicsItem() {} void SailGraphicsItem::setPosition(QPointF position) { if (pos() != position) { setPos(position); update(); } } void SailGraphicsItem::setSailSize(qreal sailSize) { m_sailSize = sailSize; QPainterPath sailPathStalled; sailPathStalled.cubicTo(.1 * sailSize, .2 * sailSize, .1 * sailSize, .2 * sailSize, 0, .3 * sailSize); sailPathStalled.cubicTo(-.1 * sailSize, .4 * sailSize, -.1 * sailSize, .4 * sailSize, 0, .5 * sailSize); sailPathStalled.cubicTo(.1 * sailSize, .6 * sailSize, .1 * sailSize, .6 * sailSize, 0, .7 * sailSize); sailPathStalled.cubicTo(-.1 * sailSize, .8 * sailSize, -.1 * sailSize, .8 * sailSize, 0, sailSize); sailPathStalled.lineTo(0, 0); m_sailPathStalled = sailPathStalled; QPainterPath sailPathStarboard; sailPathStarboard.cubicTo(.1 * sailSize, .4 * sailSize, .1 * sailSize, .6 * sailSize, 0, sailSize); sailPathStarboard.lineTo(0, 0); m_sailPathStarboard = sailPathStarboard; QPainterPath sailPathPort; sailPathPort.cubicTo(-.1 * sailSize, .4 * sailSize, -.1 * sailSize, .6 * sailSize, 0, sailSize); sailPathPort.lineTo(0, 0); m_sailPathPort = sailPathPort; setSailAngle(m_sailAngle); // Need to call setSailAngle() to update path, but don't want this to be sail specific. But not looking to change sail angle per se, so no need to use anything other than m_sailAngle. } /// calculate a sail incidence angle, corrected with user trimming void SailGraphicsItem::setSailAngle(qreal value) { m_sailAngle = value; qreal angle = fmod(m_boat->heading() - m_boat->wind() - m_sailAngle +360, 360); if(angle < 0) angle +=360; if ((angle < 10 || angle > 350 || (angle > 170 && angle < 190)) && path() != m_sailPathStalled) { setPath(m_sailPathStalled); } else if (angle >= 10 && angle <= 170 && path() != m_sailPathStarboard) { setPath(m_sailPathStarboard); } else if (angle >= 190 && angle <= 350 && path() != m_sailPathPort) { setPath(m_sailPathPort); } QTransform transform; transform.rotate(- m_sailAngle); setTransform(transform, false); } boats-201908/graphicsview/sail.h000066400000000000000000000044631353032755300165400ustar00rootroot00000000000000// // C++ Interface: SailGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2010 Thibaut GRIDEL // // 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 . // // #ifndef SAIL_H #define SAIL_H #include "boats.h" #include #include class BoatModel; /** \class SailGraphicsItem \brief the QGraphicsItem for a sail The class represents the Item according to the Graphics View Framework. It inherits QGraphicsPathItem for Item framework and QObject for slot and signal framework. It displays the sail representation of a BoatModel on a SituationView. \sa SituationView, SituationScene, SituationModel, BoatModel */ class SailGraphicsItem : public QObject, public QGraphicsPathItem { Q_OBJECT public: SailGraphicsItem(BoatModel *boat, QGraphicsItem *parent = 0); virtual ~SailGraphicsItem(); BoatModel* boat() const { return m_boat; } void setPosition(QPointF position); void setSailSize(qreal size); public slots: void setSailAngle(qreal value); protected: /// \a m_boat holds the BoatModel being represented BoatModel *m_boat; /// \a m_sailAngle holds the ideal sail trimming angle qreal m_sailAngle; /// \a m_sailSize holds the size of the foot qreal m_sailSize; /// \a m_sailPathPort holds the sail path when on port tack QPainterPath m_sailPathPort; /// \a m_sailPathStarboard holds the sail path when on starboard tack QPainterPath m_sailPathStarboard; /// \a m_sailPathStalled holds the sail path when head to wind QPainterPath m_sailPathStalled; }; #endif // SAIL_H boats-201908/graphicsview/situationscene.cpp000066400000000000000000000303611353032755300211740ustar00rootroot00000000000000// // C++ Implementation: situationscene // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #include "situationscene.h" #include "commontypes.h" #include "situationmodel.h" #include "trackmodel.h" #include "boatmodel.h" #include "markmodel.h" #include "polylinemodel.h" #include "pointmodel.h" #include "windmodel.h" #include "scenarioanimation.h" #include "trackanimation.h" #include "statemachine.h" #include "boat.h" #include "track.h" #include "mark.h" #include "polyline.h" #include "point.h" #include "arrow.h" #include #include #include #include #include extern int debugLevel; SituationScene::SituationScene(SituationModel *situation) : QGraphicsScene(situation), m_situation(situation), m_time(QTime::currentTime()), m_clickTime(QTime::currentTime()), m_clickState(SINGLE), m_defaultPopup(0), m_boatPopup(0), m_markPopup(0), m_pointPopup(0) { setItemIndexMethod(NoIndex); // set a minimum scene rect setSceneRect(-10000, -10000, 20000, 20000); // react to self change of selection connect(this, SIGNAL(selectionChanged()), this, SLOT(setSelectedModels())); // react to model track add connect(situation, SIGNAL(trackAdded(TrackModel*)), this, SLOT(addTrack(TrackModel*))); // react to model boat add connect(situation, SIGNAL(boatAdded(BoatModel*)), this, SLOT(addBoatItem(BoatModel*))); // react to model mark add connect(situation, SIGNAL(markAdded(MarkModel*)), this, SLOT(addMarkItem(MarkModel*))); // react to model line add connect(situation, SIGNAL(polyLineAdded(PolyLineModel*)), this, SLOT(addPolyLine(PolyLineModel*))); // react to model point add connect(situation, SIGNAL(pointAdded(PointModel*)), this, SLOT(addPoint(PointModel*))); // react to layline angle connect(situation, SIGNAL(laylineChanged(const int)), this, SLOT(setLaylines(const int))); connect(&situation->wind(), SIGNAL(windVisibleChanged(bool)), this, SLOT(setWind(bool))); connect(situation, SIGNAL(lookDirectionChanged(qreal)), this, SIGNAL(lookDirectionChanged(qreal))); connect(situation, SIGNAL(tiltChanged(qreal)), this, SIGNAL(tiltChanged(qreal))); setLaylines(situation->laylineAngle()); connect(situation->stateMachine()->animationState(), SIGNAL(entered()), this, SLOT(setAnimation())); connect(situation->stateMachine()->noSelectionState(), SIGNAL(entered()), this, SLOT(clearSelection())); } void SituationScene::addTrack(TrackModel *track) { if (debugLevel & 1 << VIEW) std::cout << "adding track graphics for model " << track << std::endl; TrackGraphicsItem *trackItem = new TrackGraphicsItem(track); addItem(trackItem); } void SituationScene::addBoatItem(BoatModel *boat) { if (debugLevel & 1 << VIEW) std::cout << "adding boat graphics for model " << boat << std::endl; BoatGraphicsItem *boatItem = new BoatGraphicsItem(boat); addItem(boatItem); } void SituationScene::addMarkItem(MarkModel *mark) { if (debugLevel & 1 << VIEW) std::cout << "adding mark graphics for model " << mark << std::endl; MarkGraphicsItem *markItem = new MarkGraphicsItem(mark); addItem(markItem); } void SituationScene::addPolyLine(PolyLineModel *polyline) { if (debugLevel & 1 << VIEW) std::cout << "adding line graphics for model " << polyline << std::endl; PolyLineGraphicsItem *polyLineItem = new PolyLineGraphicsItem(polyline); addItem(polyLineItem); } void SituationScene::addPoint(PointModel *point) { if (debugLevel & 1 << VIEW) std::cout << "adding point graphics for model " << point << std::endl; PointGraphicsItem *pointItem = new PointGraphicsItem(point); addItem(pointItem); } void SituationScene::setWind(bool visible) { if (visible) { if (debugLevel & 1 << VIEW) std::cout << "adding wind graphics" << std::endl; ArrowGraphicsItem *arrow= new ArrowGraphicsItem(&m_situation->wind()); addItem(arrow); } } /** Creates a BoatGraphicsItem for animation purpose */ void SituationScene::setAnimation() { clearSelection(); foreach (TrackAnimation *track, m_situation->animation()->animationItems()) { BoatGraphicsItem *boatItem = new BoatGraphicsItem(track->boat()); boatItem->setOrder(0); addItem(boatItem); QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect; shadow->setXOffset(4); shadow->setYOffset(4); shadow->setBlurRadius(4); boatItem->setGraphicsEffect(shadow); if(track->boat()->track()->followTrack()) { connect(boatItem->boat(), SIGNAL(positionChanged(QPointF)), this, SIGNAL(centerChanged(QPointF))); disconnect(m_situation, SIGNAL(lookDirectionChanged(qreal))); connect(boatItem->boat(), SIGNAL(headingChanged(qreal)), this, SIGNAL(lookDirectionChanged(qreal))); } } } /** Reacts to user keyboard actions in the Scene This method modifies the associated SituationModel. Handled keys are for - heading of selected boats (+,-) - position of selected objects (H (left), L (right), J (down), K (up)) */ void SituationScene::keyPressEvent(QKeyEvent *event) { if (!m_situation->selectedModels().isEmpty()) { if (event->key() == Qt::Key_Left) { m_situation->setCurPosition(m_situation->curPosition() + QPointF(-5,0)); m_situation->stateMachine()->lmbMove(); } else if (event->key() == Qt::Key_Right) { m_situation->setCurPosition(m_situation->curPosition() + QPointF(5,0)); m_situation->stateMachine()->lmbMove(); } else if (event->key() == Qt::Key_Up) { m_situation->setCurPosition(m_situation->curPosition() + QPointF(0,-5)); m_situation->stateMachine()->lmbMove(); } else if (event->key() == Qt::Key_Down) { m_situation->setCurPosition(m_situation->curPosition() + QPointF(0,5)); m_situation->stateMachine()->lmbMove(); } else if (event->key() == Qt::Key_Plus) { m_situation->rotateModel(5.0); } else if (event->key() == Qt::Key_Minus) { m_situation->rotateModel(-5.0); } else { QGraphicsScene::keyPressEvent(event); } } else { QGraphicsScene::keyPressEvent(event); } } void SituationScene::mousePressEvent(QGraphicsSceneMouseEvent *event) { if (debugLevel & 1 << VIEW) std::cout << "Mouse Press " << m_situation->selectedModels().size() << std::endl; // if we don't use Ctrl, then clear selection if ((event->modifiers() & Qt::ControlModifier) == 0) { m_situation->clearSelectedModels(); clearSelection(); } QGraphicsScene::mousePressEvent(event); m_clickTime.start(); } /** Handles a timer to limit user interaction This method limits the move processing to one event handled per 40ms. This is very noticeable on Windows platform, and the culprit is the drawing, not the model setting. */ void SituationScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { // limit update rate to 40ms if ((m_time.elapsed() <40) ) { return; } QGraphicsScene::mouseMoveEvent(event); m_situation->setCurPosition(event->scenePos()); if (event->buttons() == Qt::LeftButton && (event->modifiers() & Qt::MetaModifier) == 0) { m_situation->stateMachine()->lmbMove(); } if (event->buttons() == Qt::RightButton || (event->buttons() == Qt::LeftButton && ((event->modifiers() & Qt::MetaModifier) != 0))) { m_situation->stateMachine()->rmbMove(); } if (event->buttons() == Qt::NoButton) { m_situation->stateMachine()->move(); } // trigger next update rate calculation m_time.start(); } /** Performs action depending of button and mode This method will trigger performing of actions like: - setting position of selected objects - setting heading of selected BoatGraphicsItem - creating new TrackModel, BoatModel or MarkModel according to mode. */ void SituationScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { QGraphicsScene::mouseReleaseEvent(event); if (m_clickState != SINGLE) { m_clickState = SINGLE; return; } mouseClickEvent(event); } void SituationScene::mouseClickEvent(QGraphicsSceneMouseEvent *event) { if (debugLevel & 1 << VIEW) std::cout << "Mouse Release " << m_situation->selectedModels().size() << std::endl; // do not deliver events in animation mode if (m_situation->stateMachine()->animationState()->active()) { return; } bool click = (m_clickTime.elapsed() < 250); if (click && (event->button() == Qt::RightButton || (event->button() == Qt::LeftButton && ((event->modifiers() & Qt::MetaModifier) != 0)))) { if (!m_situation->selectedModels().isEmpty()) { switch(selectedItems()[0]->type()) { case BOAT_TYPE: { m_boatPopup->popup(event->screenPos()); return; } break; case MARK_TYPE: { m_markPopup->popup(event->screenPos()); return; } break; case POINT_TYPE: { m_pointPopup->popup(event->screenPos()); return; } break; } } m_defaultPopup->popup(event->screenPos()); return; } if (event->button() == Qt::RightButton || (event->button() == Qt::LeftButton && ((event->modifiers() & Qt::MetaModifier) != 0))) { m_situation->stateMachine()->rmbclick(); } if (event->button() == Qt::LeftButton) { m_situation->stateMachine()->lmbclick(); } } void SituationScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) { Q_UNUSED(event); m_clickState = DOUBLE; m_situation->stateMachine()->noState(); m_situation->undo(); } void SituationScene::setSelectedModels() { if (debugLevel & 1 << VIEW) std::cout << "SelectedModels update " << m_situation->selectedModels().size() << std::endl; emit selectedModelsChanged(); } void SituationScene::setLaylines(const int angle) { if (!m_situation->showLayline()) { if (debugLevel & 1 << VIEW) std::cout << "resetting empty Background" << std::endl; setBackgroundBrush(Qt::NoBrush); return; } if (debugLevel & 1 << VIEW) std::cout << "creating layline Background for " << angle << std::endl; qreal theta = angle * M_PI /180; int length = Boats::seriesSizeList()[m_situation->situationSeries()]; // draw 4 times as big, then use transform to scale back the brush // gives better precision grid qreal x = 2*length*sin(theta) * 4; qreal y = 2*length*cos(theta) * 4; QPixmap pixmap(x,y); pixmap.fill(Qt::transparent); QPainter painter(&pixmap); QPen pen; pen.setWidth(2); QVector dashes; dashes << length/10.0 << length/5.0; pen.setStyle(Qt::CustomDashLine); pen.setDashPattern(dashes); painter.setPen(pen); painter.setRenderHints(QPainter::Antialiasing); painter.drawLine(QLineF(0,0,x,y)); painter.drawLine(QLineF(0,y,x,0)); QBrush brush; QTransform transform; transform.scale(0.25, 0.25); brush.setTransform(transform); brush.setTexture(pixmap); setBackgroundBrush(brush); } boats-201908/graphicsview/situationscene.h000066400000000000000000000101661353032755300206420ustar00rootroot00000000000000// // C++ Interface: situationscene // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #ifndef SITUATIONSCENE_H #define SITUATIONSCENE_H #include #include #include #include class TrackModel; class PositionModel; class BoatModel; class MarkModel; class PolyLineModel; class PointModel; #include "situationmodel.h" enum { BOAT_TYPE = QGraphicsItem::UserType + 1, MARK_TYPE, POINT_TYPE, ARROW_TYPE }; /** \class SituationScene \brief The Scene for a scenario The class represents the Scene for a Scenario, according to the Graphics View Framework. It inherits QGraphicsScene and manages user input. The class handles a modal input status to trigger various actions depending on the mode. Action on Objects is handled by overloading the event methods: keyPressEvent(), mousePressEvent(), mouseReleaseEvent() and mouseMoveEvent(). Specialised methods handle the possible events and modify the ScenarioModel. QGraphicsItem Objects selection is handled through setSelectedModels() slot, and updates the various Lists needed to determine matching Model objects. The class provides slots to react to SituationModel changes and update drawing accordingly. The class prepares the drawing of animation boats as well. \sa QGraphicsScene, SituationModel, BoatGraphicsItem, MarkGraphicsItem */ class SituationScene : public QGraphicsScene { Q_OBJECT public: SituationScene(SituationModel* situation); ~SituationScene() {} void setDefaultPopup(QMenu *theValue) { m_defaultPopup = theValue; } void setBoatPopup(QMenu *theValue) { m_boatPopup = theValue; } void setMarkPopup(QMenu *theValue) { m_markPopup = theValue; } void setPointPopup(QMenu *theValue) { m_pointPopup = theValue; } signals: void selectedModelsChanged(); void centerChanged(QPointF position); void lookDirectionChanged( const qreal lookDirection); void tiltChanged( const qreal tilt); public slots: // Slot for selection mechanism void setSelectedModels(); // Slots for SituationModel signals void addTrack(TrackModel *track); void addBoatItem(BoatModel *boat); void addMarkItem(MarkModel *mark); void addPolyLine(PolyLineModel *polyline); void addPoint(PointModel *point); void setLaylines(const int angle); void setWind(bool visible); // Slots for animation signals void setAnimation(); protected: // Overloaded methods for Events void keyPressEvent(QKeyEvent *event); void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseMoveEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); void mouseClickEvent(QGraphicsSceneMouseEvent *event); void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); private: SituationModel *m_situation; /// \a m_time holds the timer used for movement filtering QTime m_time; /// \a m_clickTime holds the timer used for click/press detection QTime m_clickTime; enum { SINGLE, DOUBLE, NONE } m_clickState; QMenu *m_defaultPopup; QMenu *m_boatPopup; QMenu *m_markPopup; QMenu *m_pointPopup; }; #endif boats-201908/graphicsview/situationview.cpp000066400000000000000000000107331353032755300210520ustar00rootroot00000000000000// // C++ Implementation: situationview // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2010 Thibaut GRIDEL // // 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 . // // #include #include #include "commontypes.h" #include "situationview.h" #include extern int debugLevel; SituationView::SituationView(QWidget *parent) :QGraphicsView(parent) { } SituationView::SituationView(QGraphicsScene *scene, QWidget *parent) : QGraphicsView(scene, parent), scaleValue(1), lookDirectionValue(0), tiltValue(0) { setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform); setViewportUpdateMode(QGraphicsView::SmartViewportUpdate); setResizeAnchor(QGraphicsView::AnchorViewCenter); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); connect( scene, SIGNAL(centerChanged(QPointF)), this, SLOT(setCenter(QPointF))); connect( scene, SIGNAL(lookDirectionChanged(qreal)), this, SLOT(setLookDirection(qreal))); connect( scene, SIGNAL(tiltChanged(qreal)), this, SLOT(setTilt(qreal))); } SituationView::~SituationView() { } /** Takes a picture of the current view and returns it as a QPixmap. This method creates a QPixmap and calls render() on that pixmap before returning it. */ QPixmap SituationView::screenShot() { QPixmap pixmap(size()); pixmap.fill(Qt::white); QPainter painter(&pixmap); painter.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform); render(&painter); return pixmap; } /** Handles mouse wheel event to zoom in or out. This method will call setScale() with rotation side. */ void SituationView::wheelEvent(QWheelEvent *event) { if (debugLevel & 1 << VIEW) std::cout << "wheel event " << event->delta() << std::endl; setScale(event->delta() > 0); } void SituationView::mousePressEvent(QMouseEvent *event) { setDragMode(QGraphicsView::ScrollHandDrag); QGraphicsView::mousePressEvent(event); } void SituationView::mouseReleaseEvent(QMouseEvent *event) { QGraphicsView::mouseReleaseEvent(event); setDragMode(QGraphicsView::NoDrag); } void SituationView::zoomIn() { setScale(true); } void SituationView::zoomOut() { setScale(false); } /** Sets an acceptable scale that fits the whole scenario. This method compares the matrix() before and after fitInView() and possibly adopts the scale, rounded to the nearest .05 */ void SituationView::zoomFit() { fitInView(scene()->itemsBoundingRect(),Qt::KeepAspectRatio); // Qt doesn't seem to get this quite right when the view is tilted, but close enough qreal s = sqrt (transform().det()); if (s < 10.0 && s > 0.1) { // adapt scaling scaleValue = pow(1.1, round(log(s)/log(1.1))); } transformView(); } void SituationView::setLookDirection(qreal value) { lookDirectionValue = value; emit lookDirectionChanged(qRound(lookDirectionValue)); transformView(); } void SituationView::setTilt(qreal value) { tiltValue = value; emit tiltChanged(qRound(tiltValue)); transformView(); } void SituationView::setCenter(QPointF position) { centerOn(position); } /** Zoom in if \a in otherwise zoom out by .05 increments between 0.1 and 10 */ void SituationView::setScale(bool in) { if (in) { if (scaleValue < 10.0) { scaleValue *= 1.1; } } else if (scaleValue > 0.1) { scaleValue /= 1.1; } if (debugLevel & 1 << VIEW) std::cout << "new scale values " << scaleValue << std::endl; transformView(); } void SituationView::transformView() { QTransform transform; transform.rotate(tiltValue, Qt::XAxis); transform.rotate(-lookDirectionValue, Qt::ZAxis); transform.scale(scaleValue, scaleValue); setTransform(transform, false); } boats-201908/graphicsview/situationview.h000066400000000000000000000041141353032755300205130ustar00rootroot00000000000000// // C++ Interface: situationview // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2010 Thibaut GRIDEL // // 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 . // // #ifndef SITUATIONVIEW_H #define SITUATIONVIEW_H #include /** \class SituationView \brief The View for a scenario The class represents the View for a Scenario, according to the Graphics View Framework. It inherits QGraphicsView and manages zoom and screenshot capture. \sa SituationScene, SituationModel */ class SituationView : public QGraphicsView { Q_OBJECT public: SituationView(QWidget *parent = 0); SituationView(QGraphicsScene *scene, QWidget *parent = 0); ~SituationView(); QPixmap screenShot(); protected: void wheelEvent(QWheelEvent *event); void mousePressEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); public slots: void zoomIn(); void zoomOut(); void zoomFit(); void setLookDirection(qreal value); void setTilt(qreal value); void setCenter(QPointF position); signals: void lookDirectionChanged( const int lookDirection); void tiltChanged( const int tilt); private: void setScale(bool in); void transformView(); /// \a scaleValue holds the value for the viewing scale qreal scaleValue; qreal lookDirectionValue; qreal tiltValue; }; #endif boats-201908/graphicsview/spinnaker.cpp000066400000000000000000000131641353032755300201330ustar00rootroot00000000000000// // C++ Implementation: SpinnakerGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2010 Thibaut GRIDEL // // 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 . // // #include "spinnaker.h" #include "commontypes.h" #include "trackmodel.h" #include "boatmodel.h" #include #include #include extern int debugLevel; SpinnakerGraphicsItem::SpinnakerGraphicsItem(BoatModel *boat, QGraphicsItem *parent) : SailGraphicsItem(boat, parent), m_pole(new QGraphicsLineItem(this)) { setHeading(boat->heading()); } SpinnakerGraphicsItem::~SpinnakerGraphicsItem() {} void SpinnakerGraphicsItem::setSailSize(qreal sailSize) { m_sailSize = sailSize; QPainterPath sailPathStarboard; sailPathStarboard.cubicTo(.4 * sailSize, .2 * sailSize, .6 * sailSize, .2 * sailSize, sailSize, 0); sailPathStarboard.arcTo(-sailSize, -sailSize, 2 * sailSize, 2 * sailSize, 0, -120); sailPathStarboard.cubicTo(-.12 * sailSize, .62 * sailSize, -.03 * sailSize, .44 * sailSize, 0, 0); m_sailPathStarboard = sailPathStarboard; QPainterPath sailPathPort; sailPathPort.cubicTo(-.4 * sailSize, .2 * sailSize, -.6 * sailSize, .2 * sailSize, -sailSize, 0); sailPathPort.arcTo(- sailSize, -sailSize, 2 * sailSize, 2 * sailSize, 180, 120); sailPathPort.cubicTo(.12 * sailSize, .62 * sailSize, .03 * sailSize, .44 * sailSize, 0, 0); m_sailPathPort = sailPathPort; QLineF line; line.setP2(QPointF(0, -sailSize)); m_pole->setLine(line); setSailAngle(m_boat->spinAngle() + m_boat->spinTrim()); } /// calculate a sail incidence angle, corrected with user trimming void SpinnakerGraphicsItem::setSailAngle(qreal value) { m_sailAngle = value; qreal angle = fmod(m_boat->heading() - m_boat->wind() - m_sailAngle, 360); if(angle < 0) angle += 360; if ((angle < 10 || angle > 350 || (angle > 170 && angle < 190)) && path() != m_sailPathStalled) { setPath(m_sailPathStalled); } else if (angle >= 10 && angle <= 170 && path() != m_sailPathStarboard) { setPath(m_sailPathStarboard); } else if (angle >= 190 && angle <= 350 && path() != m_sailPathPort) { setPath(m_sailPathPort); } } void SpinnakerGraphicsItem::setHeading(qreal value) { qreal angle = fmod(value - m_boat->wind(), 360); if (angle < 0) angle += 360; QTransform rotation; rotation.rotate(-angle); setTransform(rotation, false); QTransform poleRotation; if (angle > 90 && angle < 180) { poleRotation.rotate(90); } else if (angle >= 180 && angle < 270) { poleRotation.rotate(-90); } else { poleRotation.rotate(angle); } m_pole->setTransform(poleRotation, false); QPainterPath sailPathStalled; QPointF pole = m_pole->mapToParent(m_pole->line().p2()); double sinx = sin(angle* M_PI/180.0); double cosx = cos(angle* M_PI/180.0); double sign = .1; if (angle>180) { sign = -.1; } sailPathStalled.cubicTo(sign * m_sailSize * cosx + .4 * m_sailSize * sinx, sign * m_sailSize * sinx - .4 * m_sailSize * cosx, sign * m_sailSize * cosx + .6 * m_sailSize * sinx, sign * m_sailSize * sinx - .6 * m_sailSize * cosx, pole.x(), pole.y()); sailPathStalled.cubicTo(pole.x() + sign * m_sailSize, pole.y() + .2 * m_sailSize, pole.x() + sign * m_sailSize, pole.y() + .2 * m_sailSize, pole.x(), pole.y() + .3 * m_sailSize); sailPathStalled.cubicTo(pole.x() - sign * m_sailSize, pole.y() + .4 * m_sailSize, pole.x() - sign * m_sailSize, pole.y() + .4 * m_sailSize, pole.x(), pole.y() + .5 * m_sailSize); sailPathStalled.cubicTo(pole.x() + sign * m_sailSize, pole.y() + .6 * m_sailSize, pole.x() + sign * m_sailSize, pole.y() + .6 * m_sailSize, pole.x(), pole.y() + .7 * m_sailSize); sailPathStalled.cubicTo(pole.x() - sign * m_sailSize, pole.y() + .8 * m_sailSize, pole.x() - sign * m_sailSize, pole.y() + .8 * m_sailSize, pole.x(), pole.y() + m_sailSize); sailPathStalled.cubicTo(.8* pole.x(), pole.y() + m_sailSize, .8 * pole.x(), pole.y() + m_sailSize, .7 * pole.x(), .7 * (pole.y() + m_sailSize)); sailPathStalled.cubicTo(.6 * pole.x(), .4 * (pole.y() + m_sailSize), .6 * pole.x(), .4 * (pole.y() + m_sailSize), .5 * pole.x(), .5 * (pole.y() + m_sailSize)); sailPathStalled.cubicTo(.4 * pole.x(), .6 * (pole.y() + m_sailSize), .4 * pole.x(), .6 * (pole.y() + m_sailSize), .3 * pole.x(), .3 * (pole.y() + m_sailSize)); sailPathStalled.cubicTo(.2 * pole.x(), 0, .2 * pole.x(), 0, 0, 0); m_sailPathStalled = sailPathStalled; } boats-201908/graphicsview/spinnaker.h000066400000000000000000000033771353032755300176050ustar00rootroot00000000000000// // C++ Interface: SpinnakerGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2010 Thibaut GRIDEL // // 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 . // // #ifndef SPINNAKER_H #define SPINNAKER_H #include "boats.h" #include "sail.h" #include #include class BoatModel; /** \class SpinnakerGraphicsItem \brief the QGraphicsItem for a sail The class represents the Item according to the Graphics View Framework. It inherits SailGraphicsItem which provides necessary update framework. It displays the spin representation of a BoatModel on a SituationView. \sa SituationView, SituationScene, SituationModel, BoatModel */ class SpinnakerGraphicsItem : public SailGraphicsItem { Q_OBJECT public: SpinnakerGraphicsItem(BoatModel *boat, QGraphicsItem *parent = 0); virtual ~SpinnakerGraphicsItem(); void setSailSize(qreal size); public slots: void setSailAngle(qreal value); void setHeading(qreal value); private: /// \a m_pole holds the spinnaker pole QGraphicsLineItem *m_pole; }; #endif // SPINNAKER_H boats-201908/graphicsview/track.cpp000066400000000000000000000036551353032755300172510ustar00rootroot00000000000000// // C++ Implementation: TrackGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2009 Thibaut GRIDEL // // 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 . // // #include #include #include #include #include "track.h" #include "commontypes.h" #include "situationmodel.h" #include "trackmodel.h" extern int debugLevel; TrackGraphicsItem::TrackGraphicsItem(TrackModel *track, QGraphicsItem *parent) : QGraphicsPathItem(parent), m_track(track) { setZValue(0); setTrack(); setShowPath(track->showPath()); connect(track, SIGNAL(showPathChanged(bool)), this, SLOT(setShowPath(bool))); connect(track, SIGNAL(trackChanged(TrackModel*)), this, SLOT(setTrack())); connect(track->situation(), SIGNAL(trackRemoved(TrackModel*)), this, SLOT(deleteItem(TrackModel*))); } TrackGraphicsItem::~TrackGraphicsItem() {} void TrackGraphicsItem::setTrack() { prepareGeometryChange(); setPath(m_track->path()); update(); } void TrackGraphicsItem::deleteItem(TrackModel *track) { if (track == m_track) { if (debugLevel & 1 << VIEW) std::cout << "deleting trackGraphics for model" << m_track << std::endl; scene()->removeItem(this); delete this; } } boats-201908/graphicsview/track.h000066400000000000000000000034631353032755300167130ustar00rootroot00000000000000// // C++ Interface: BoatGraphicsItem // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2009 Thibaut GRIDEL // // 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 . // // #ifndef TRACK_H #define TRACK_H #include #include #include class TrackModel; /** \class TrackGraphicsItem \brief the QGraphicsItem for a track The class represents the Item according to the Graphics View Framework. It inherits QGraphicsItem for Item framework and QObject for slot and signal framework. It displays the representation of a TrackModel on a SituationView. \sa SituationView, SituationScene, SituationModel, TrackModel */ class TrackGraphicsItem : public QObject, public QGraphicsPathItem { Q_OBJECT public: TrackGraphicsItem(TrackModel *track = 0, QGraphicsItem *parent = 0); ~TrackGraphicsItem(); TrackModel* track() const { return m_track; } public slots: void setTrack(); void setShowPath(bool showPath) {setVisible(showPath);} void deleteItem(TrackModel *track); private: /// \a m_track holds the TrackModel being represented TrackModel *m_track; }; #endif boats-201908/images.qrc000066400000000000000000000022101353032755300147040ustar00rootroot00000000000000 images/about.png images/animate.png images/addboat.png images/addmark.png images/addpoint.png images/addpoly.png images/addtrack.png images/export.png images/filenew.png images/fileopen.png images/fileprint.png images/filequickprint.png images/filesave.png images/filesaveas.png images/laylines.png images/pdf.png images/player_loop.png images/player_play.png images/player_pause.png images/player_stop.png images/redo.png images/undo.png images/tab_new.png images/tab_remove.png images/video.png images/zone.png images/zoomfit.png images/zoomin.png images/zoomout.png boats-201908/images/000077500000000000000000000000001353032755300142025ustar00rootroot00000000000000boats-201908/images/about.png000066400000000000000000000627601353032755300160350ustar00rootroot00000000000000‰PNG  IHDR––³cæµsRGB®Îé pHYs  šœtIMEØ  Þ½š>tEXtCommentCreated with GIMPW IDATxÚ|½iwYrwÉÌÊÚìnݣѴd[ïÑ—÷øgúƒäãåƒd5ÖŒ–iizZÝä4 n ˆ}©}Ïí^ˆª@àÇ88< P••yãÞXžx"B\\\dY–¦i–eBRzžÖÚZ›e!„ÖZcL’$Æ¥¾ Žã4M1RJ¥”"MÓ(Šð5Aär9¥^ÊZ«”’Rc²ùWš¦q ZJ-…’Ry¾ÎR)!„”ðeÆ ûÁð¯øq¾ï !ðâÖZ|ÀÛ¶Ö !´Öxóøâ4Mññ“$‰¢<ÏóÈ2~)ÒišXR1d‹QÆàñ¼fÆX:ôÖZ4ŸüAèa _zþ2.<~BðÑH±¡ ·ã#ж!ð[)¥5~0©u2$ü àÇ“lèÙø£Ïã¯ÇkÒ»Ü=eî¡Å;Æ3‡*5·4Ήwö5?Có=Bp1koM¦Â›ei–¥Yf¬½Ýpôqü¹øŽä2#SÍ%Í× - É’Ô¾cþÉØÏ>B ŸÇXk¬Õ(îᆥ3GËMû…\.þÁ\·ÐùrÖ⶘¤ND–†«)Z>hÇ\tiY¹¹"3‰¿ç;š›ú ’4Ùg;þ’;É´£ÉƒÇ·ZkÐ#™È4M¥Z+)@j-ÓTÚÓ´èüHñÀ}†^ý¢Eä¯$À) ÆÈOæ^ÞüægšÜZ›™Ì˜Ì‚ÖX“fYf4WPŽ¡"%C:WÂÚìÜÛäwìØ ¼-ÚÎ\ó U Y>î[s?‚’"WkÌ~ ¬5óMJ ¥@J”¨EÑJ‰j¤T<êàNÙ`ÒäûpaóxŒ?<öàFwqŒ1· À¤Æ!%€´ŒµÖXÍÕ1)nZiùÈ}À­ÁýUŠø=¡ãîb–e<ât‚tЇø“Ði¦ëðÀ”‡­´yñ žç)¥¬5B41E—÷<)„g-(ƤB€Öó¤” @‘ÛL¶Ù‰¾ÐÜp°‚< ¾“ÈâÅän~úJŽ;I¯$ói­É2B¨Y”h…BsÅEQã‹rwœ0|Š—9JÂ&ù„hN¸J¤‡äÛ#*p׎ÄÏãh®Qé¿išfY*„ñ}Ââ)´V(t¤4ZÃ\»ÇèåIi …í˜g~(ñaQ`ø\ä “i [%ÆÙŽx¼Ðg!µO;_†7ed•’B )„1XÍO%{³è=óøEÅcmòƒhqI-®8¿…|_ÓGÓJ‘’çz‰»…iš“xžR …VˆLƒï›GARi-#}_“;æ8#NLLú‰ü#ǬЩàn9·ýäs¢û†®·îˆ‚Y BÎ×_kH%µ’“!ÅKóã@’%Æ¤Ü t|Ñ4M“$Áè•dÏ̓žSœÊÝòB¹oÌÒyqš¦ÓéHʉÖÖ@Z h\PoI ƤÆdóGÏÓJi<‚J„œ-ê ’ZAR-|wò˜˜Gú³å»8äÀü7 ¤PZ))-X#­´ ¥Ò§’ðÎè&¸ãÎ7ɘÇ|t2˜Ò³@çQ£syÃCIçIè99 ê8îÆ˜(šÄq'—‹¬•Y¦€@ð'ˤ”ê9!RkgQ¼¾”­ ?ñ|¹ÉoHç_\!‘÷@ñ™4®c)2!Ô‚¤¨x2Ë2!@â WÒ#…´ÒJ¡tìÒýQ@»ƒßñìuÁWÄãd-:ܱµ ”šL&qçóy ¶h·rÓë„ÒÜYuPnW6f¬µqFm­»ÆdÓ©R ¡„1èS())R6©µFx:M½4URjä9 ŸöœÊ,‚!Ž¿Æ}=\OT’´¨T°‰ÂSººs„cT¾<%tƒAî…:ˆ3?(ôI˜ ßZ£µžL&xâ¹åÀ¿¿øÓ»Øš\¾4M†Ã¦”—a8¾¹1£lm)ÌÍ?Bd™´Vâ6P CaŒ6ÆK$!òÓé?݉ÄIÛó×Õð<à!£û窅T~éy)aç ¥(;k2ÖX „RM.EÄ<.&Í€&Š‹ãdó#27]FkåyZÈ2ƒ†NÜe\1r7ÇAS9šÌ½eî‹rCÕï·âø´Vk“F‘‰c1™È|~R\ˆ^3?ÌÒ¦:IT>¯Ã0$ÌD®·iAƒQº%Œm¤†žú‹á =)ù±r–›!„5Æ‚Q@½ :¶$ßÜX)¡Z£‘(—m–ñ˜ÉL5‡O¥”JeŒŸ¦!€•Röz½$IÊå2íZŽ:ðダÎ5 a¸\¨¨é˜rKÏ/~kYí,oa §Ó)ßÅüýdH„d)gài£áp8s¹Ü|ßFB¾ï÷ûý‹‹‹••• ø]:À??å q¶*Çá¢hzzú>ŽØØÆ14›¢X”Õª8=ƒõ}Š Qð0ƒúA !…PJ¦~åÓt°7›Í~¿_©TÐDñ”üH1’GƱGÇâ¾G¯Ð±””?_L|j¥l’ØY¦@HÒ4Õ$ž›åš; 9ã@9Zqw»Ý òù¼µ˜Øº…ês¹\µZ½ººº¾¾^]]õ<>…'˜è~¡¦EmƒWNÓ¤Ù¼¼¹yyÿþ±Ö¦×ƒ\Y‘…‚ÔZ´Û¶X4¾?{Ÿ1¯d­B,ÆZi­4FO&ÁhTËç£4M=Ï«T*§§§q×ëuв‡Ž;e´Ý)J¦,•ƒhs×¹?Q·“iŠ `Œ1©æiUg³8 ã×”™"“Nê·X,†aH[ŒÛTĽjµZE`Èy?Ñ#ÑÖáš^3Œ..”z]¯7WWR†àû2 ÅÙ ‡¶^7Ö~ ˜øFF#³L&‰óƒÁ°TŠ’$QJåóù¥¥%DìP¨¼É]bÛ’Y™ã|wøqݹã¹x¼8GHx>ÙI¯ÎØ0Ë–¥B` lŠr×NªóˆæHcFþ´b_<”β¬ÝnƒååeÔ!¡À ¿ ïo2™´ÛíÍÍÍjµŠ×áŸBޏƒ-8>!.Ù`Ð;=ý®\~W¯w“®®DšŠjUxžBär†p}m·¶l¹,¤Äí"ÄoSBH­¥µ2Ë Ë¬Rš°üa8¶ÛmÏóÐÆóà•'jH1¾O‡•“hÈä!Àb*†|ÃYNW+k™‰5Ri%•Òšá™hîóœ$‡ûè¾;ÎÍÍÍêêjµZ%h”nŠ[‚`mm-I’ÏŸ?cªÕ*ßtŽ©£`:κ@þËññÇáðw{{ž—öûâü\„¡(‘#<êu8:²‹1ä¡(kZD!$¢©RÊ\.GQ”2 ÃÍÍÍwïÞ}úô)R3Î"Íæ®‡­¹’$ ÉÅÞ"×®I’L§SäK‚gí3´‚ ŒBè\.‡çƒc<ƒãàÎFšüq<™LÖ××—––´ÖÜé Ã'Aýîû~.—ÓZçóù·oßöz=DmœT;wĨ–#WWçoßþݽ{KKã,ƒ‹ èvÅ“'àyX ¥ ^‡B¾ÿâööD`€¤ÆP~B¨4ÕÖÊ\.ä˜Z‡ýýý“““^¯W*•pŠr–œ;òÇüR–9ÀKñÏi 5)µÒVc…µ!Cíû¾sÀôy‘9áx¤RÊ ß÷Q…r(7`EÓé4Ë2ÜÚ¸ûðvvv†Ãa’$döéÓòw¸探éÉÉA’ü°µÕ Çpu%¤„F•Ê^„!¬®Âo i ëë" Á˜Y‚5 êÒ,“qœÇ”…ãÓ£QÌçó0'M[œ^Ì3bde1¬EÔ—ÞÂÝIÞP@ó€Ê )„)ÀdV€µ4׿‹É"VÂ-yÇ'''ÖÚÕÕUç$’{Fdg¡Bär¹ñx|ttT«Õ677}ßçg—a"(pÇZÛé4//ÿmeåS½i ý¾h·aiI”ËH­cr ÈåàúÚöûbe¤$~¨T$‰Š"t&…³â³AÐívß½{wÿþýÕÕU’"¶“‡áOÍÿDnƒ“=ƒA¡P@Å6?ÇV ä‘Zk,k5¹¹¤#åÈ GP*I’\\\t:µµ5.]RdØ<ÏC¹zž‡Î§0år¹J¥òñãÇ$I>|˜Ëåȵái9âžK’äððÇ8þíÇ­\.L§°µax»õ³ Œ…r<„W/áê >kª’ù@ıœLT©’}âü]ÜIAxž÷æÍ›$I677ñYHØÜ"r'‘@‚O @Ò? Y–õûýÁ`†!y¶Æš™&ÀZ £#BhÎaá2ãJl¦…¶Çq³Ù kkk•J…n‚‡d®)ïáû>a†ôZëõõõ(Š®¯¯Ëåòææ&¿'‹ÒnÅÿv»íóóß7'µZ,„LÄÅÔëÐh>ÊÇkAkØX‡?¼‚f¢_ æ~)GÇ2Š‚F£ ”FØÁ²±ÚáñãÇ>|@ÍQ©T8âð}k?8¦Èé üxL&“f³Y,«Õ*z¶RJJóàV Ô¾7pË£QªƒAßDk½¼¼\­Vñl°±bõ ±l»"‚T«ÕB¡àìÂØ/MÓ««Óñø‡/zAý¾¸¼„½=(•e-(5{b­`c––àæF#Q(ÀœÍ†§ÐJi¦S=r¹2EÓ¼<‘jµúôéÓëëk$*þ16ÃnurNr{4€›Í Ký ÖkŒµÆ‚”B!˜46ɪZOy(BÑÐ…!Ti!Y‘-ÂÖxÄy.]>ÏóJ¥Òd2ùøñc¥R Ã?”GÍœ½Òïwß½ûíúú§õõØó ŽáüÜfÜ»A@{,`¥Æò2llÀá!ôz¶PRÎ^e6ËÀ1Ë(* UÎL Å¥.J±\.7›ÍN§³²²‚wËŸÎa„r,†°VrìÉ}ÇÝn7 Ãz½ÎI'¸Ä`¥”bF·B Rj©4§ßsƨ KJ9N¯¯¯ã8FOi‘)êdÛ¹¢à3RÍÈñA…™$ÉÑÑQ.—ÛÙÙ©ÕjFL—J’äêê´Óùñ›ozù¼¢®® \†Jˆ) (qÔ«…Ü»>Àx RZT@BX)Eš‚1Ç Ì狼LÉaŽÓ6ÕZO§Ó> ‡Ã½½=nzŸ‹ã8ÄpçŽ+UÒ o¦ä.wÍ)ç„Ì1ɹ»h5€” ØŒ8hÑ­ˆã¸Ýn§iÚh4i㎠Ϩ9¥(L¾'Jåóù­­­·oßc¾þúkÏó8¶@Ë1 _Á»F#•Ò@¯ý><|arfêÑ#!@J<–—Á÷¡Ù„½=PÊâÊ—(‚ñXú~É÷}Ži8<>^__‡‡‡‡¾ïonnÒjpvŒS ²§á¿I’ÜÜÜ\]]­¯¯×j5‚/Xk¥PB 5&M³DÇqL¨+FßÄ+D׃sî0—½´´”Ïç9õ‡qÜÑr*ÜœjER><=½¹¹©”:>>Ž¢ƒhz=Biš^\œœŸÿ~cã¼T2¨$//AØÜ„¹Ë5“ß\€1¦0™@.ÇÇðÕWËV|¢ÓºÝ|±¸&„D‚½ƒD“sH!y>ŸòäI777ËËË5:d¾qmÍ=$Iúýþƒ0V!îÙíáÎ2‹d»Ù^P ÉÒdkrGéƒñ\¢ã…“Ƙ0 …Á:êÂɹsM¦”—s’='òÄF!ä(QõBEgg³ì`mmây D´ÛP©@µÊaÆ[_e9@¯kkÐïC³ Å"º¬V)<¢Û-ÔjkR*B¹8”ç 0±½½Ýh40Ñ–Ëåœü-íNòÏ9Ÿ /…¥Å>äT4^6w© –îá 6É2I®&¯G!†™Ü4Mƒyt7¼È˜XEõ›$ Õv;Ú•×"qŒ¸X,¶Z­×¯_w»]¦™N§×ׇùüùÒR‚"¡ß‡ÕUÈåf³v&BtM³ ’Z-ˆcxø 8;ƒá’d&])m’ÀpV«+¾à‚àNÅ@ˆ—¾òÂ9ts|ß?:::88@a.ÏéœüÙyà?ß½{wrr‚D^ª^% ¬cÍÌ#ÝA¦7„ .(ŠF£ÑÅÙSì„U#œ#ËA–/VÀâ‹Åâééé÷ßÿ³Ÿý¬Z­’½ÞËçϻժõ<È2¸¾†4…ÕUÐÐ1´¾õhŒéÎÏÁ÷asnnàì ¶·auåÖÂp¨¬­U«Ë*cÉ E /¾ËK¥Òëׯã8Þßß/ \~¸!ð²<ñƒÁàðð°ßï?yò„ÇÐä|¸EóRK‘€…̦IœDÓÛdÏar„3MSËóù<Ïûp:%ÅN¥ëÂ>ºƒ:¥ýüY–‹Åýý}­õû÷ïGÅß÷zM!Ž×Ö¦ahµ†8ŒèóyȲÙ7 wžËÁ¨Õ —ƒj¦Sh6}8k!ME§úþf>_@øƒo/‡˜â4z@S×h4?~Üjµ>þL\ÒLD,æ»:~þü¹Ùl>xð:ÜïÅå¢(Š"ÄÂ’$ÉLfmf‘„³m¤{äµçÜEÄ­äû>A.l^×Ãé÷Ž” ÍŽ!OÓÓJ©z½þìÙ³V«…X¹”r2_]V«­Z-Ãs6ÂÕìî‚R$3‘ @JÐL’@³ P«Ö3“ys»»3IÇ1´Ûanû~¦Æôa‚Ÿ‹e.BÏóÖÖÖÚß"5†·0Æ …Gaö Ïœñß¡® ÂJ)¤BbÁ2€Mˆ!Ñ\a&I‚ 3®BI·Ðq¤Þ‹¥*Ì:©Q.°?Vu†hii)—Ë]\\cVVV†ÃþååOÏŸ· #Ä1œA’@£R‚13û‡:«5V«P,‚”P­B½D ã±èõŠÕê¦çùdD8I€k&^­Ç çy÷ïßÇggg•J¥V«ñbf®Ò4m·ÛÝnwuu5—Ë‘€¹Þ^¼>ÆóZ+©””2³V2¡F> ?|´¡îr ´‹ù|‰Ù-{¡pV•£]©´‘ §Öz8žYkÇãn–/-%˜"›Làò ¨TfÆä‡ß3Ž‚ºý/är°¶¯_Ãd2{}·ë¥ézµºêbyë^ßãt… åÆÓÙjµNOOŸ?Þh48J‡ÏbŒÁt‡ïû«««N1á8ÛêvÇÏãz!¥òý@{þm­5/ãç¡!÷<ù¥’Ú<ÍsO¸]ˆ…·Håá×ÎÎNE¯_¿Ž¢Ë\î´Z5xæú}èt ^‡r<ïÖøá7Êq™åe¸¸€É||VVààúýÙ+[­@ˆíFcðXr ) "/}Ñjp\ñÞ—/_¾yóæÅ‹•J…‡ËRÊn·ûþý{cÌ£G …‚“Ôs.®d°aMJ©¥ÒaÝsX{‘ ä8…"ܤ Í8ŽIþtµøb…êpÔížçÕëõ?ù“?©V+>üP,ž Öˆc¸¼„8†ÕUÐzv­…4½ãÑø>¬­AšÂõ5Ä1`u..`:…énnŠ…ÂÎÒÒj.—Ëåraó/òŽÖbé>¸µ¶V«ýìg?Ãä"îW즅‹Ùï÷Ã0üÙÏ~¶´´Ä#4§6ƒÀ,6µ`AÌV{¦#¤šXh¸^Üå],%J/á䆼5ä]95 ܨpsÍ_@ åxñ"¾ïon®ß»—_^6žgÓânn@J¨ÕnO0Ñ7‹P,B«kkÏæñß¾E¼Töûî„ažL‰SòÂÕŒcÎIUòú/Lñ#r¬vDHÆãñêê*2¡¿ØÉd±ŸÀí Â-•0Öc-È,ÃBà ÷Áh»aËŠôzŠ‚¿XnOéÍÅ^8‹FxHËëc)ÆŠ¢avêõÚdÎLY6•Öày 52ˆA…ÖP«Át Ãá /­×!Ž¡Û…v[EÑÖÊÊCßï ¡ ‡uÀ+aùAáaF.—‹¢èÕ«W×××øâ«««—/_&I‚ð¤:.:óÎ °¥ŠI wI”D“X;tžûåí+8Žº˜B#ŸÖ¡©órÄѦ†Óé–Š“À¸[a­éõ®”:©ÕbT ã1 ‡°¿ži:;J;ï’¬¬ÀÍ ´Ûpÿ>(µ ðé“ȲR­ö|ii•ªÏb'CŠŠûؼEÉÈ„ûÃþ€Háááa£Ñ(•Jžçqkâ”—òzØ;½$fŠÊ‚5 ÀdƤ&v¼‰Eûä`ÓŒ’åœ0§ëÈ"ÛÇ) ¿­]i¯ŽLSÚA0Êç Jk<†,ƒ¥%@ï-ßœ¬}G‹¢h‹E¨×¡Õ‚(‚\ŠE(•àøXfYã›ožåóGÁ,ÖÁ8¤HbãK*Ó(ÌØÛÛûôéÓ«W¯¤”ÛÛÛOž§ÉÀb9© y—ŽNYJ¥²¿¿†¡”rgg§X,~±Ï‡ckWáe訬%bó` °4çêp*·cN©ëÅj<¿HöâÙ«Åøƒ¼AdF)w)ÈøÃ`Ð?>x0ò<+L&p~kkP(̾YŸ9–š d!žÚ4…á‚æ´K¨V@yyùÆ ¥èx‡º‡–Gsj1Êkíd2év»kkkÖÚ››)e±Xä•*¼H‘_ù‹E–Ø)ÒX ¬Æ c­v¢=Jù:ùw^kÕ'¹N— ªîq8mü.û“Ù[Ñ:ô…ÑhÇ×¥R,%¤) ÐéÀö6øþ,l'_%‡ÿZ;ót² †Cèt ZÅL!ÌÏ®\^ÉårWÁIØr¤kN¦¹u§MEѧOŸ...=zdŒùøñc·ÛÝßßǼì™eT6Ì«a(8¦VÀ“aî_š%Iª)¶s:¢.¦:4j§wêbG®x³ÅÅ>·ï`¬ÉLf­¥†¨“ÇãRCÄÕ¢hfÒ0ã9BÔ¨à6f|ã[¶¶@HHètôtš‹ã$M3‡Æé´ótÜ:²¸zÜë&í•$ÉÉÉɧOŸ¨n¤×ëzž·µµ¥µFùQ}=5¯YlX6û±Ö¦Y–eÆš,K³4ÍÒL;a^‘+@~àËí³ïðI:ü ¾Kœ2%ú/iO’¸ß¿ªÕ&…‚¦S¸¾† €0œ>ŸwÖw÷q ¤óÐê¡ IDAT…±‰r =z´¾¾Ž²³³ãû~³Ù¬×ëaò¨ŒwI z?ï×£µÆ¤¯5Xj?k)§´ÔØjЩÛ#èPê8TOº…gðy<Îcv:úFâ"çú™›ŠùÍdƒÁe¥2ô}ÜÐëAÎDˆ ?Ý\p9áämÛœZg§- q½yß§(Šúý‹¥¥±ÖÖˆ"¡Z…|”­oýT§èÝdœœÀö6äóÐí‚Ðh€ïƒÖ0Šv»ÐhìU«÷//Ïûý>†n¼*Ýi?Âs¿Ž5áíÕ±‰ÓÛ·o}ßßÝÝ¥öå<Ð<<<Œ¢hooÑ;ž#ä÷<6îÚ‚+¬‚ÌX 4‘”xª—“¾ï—Ëe”"_wÄÏ8ZÍ¡^ ¸³}1»äôì¡]2™LÞ¾=øôé૯"Hèõ Ë ZÉcyŠ(P—zT*` ‡P*Áõ5,/C¥X¨ÛéxýþæÓ§_û~µXl¶ÛÍÉdé N7é/vW$Â>2žBÔºãñøýû÷½^ïÅ‹XŒH¨¾Ñ÷ýjµúæÍ›wïÞ=~ü¸R©8þÏpª‡µ†SBXe­ šc%¤ÐyiR’$¹\™9øâÅOZŒ™8_ˆ×N: 0óf7¼÷Æùùù«W?N&-ÔˆËåò,ƒE¢bR©T‚~ È2Cð<ÄÄÅE1ö*•F–ÉR) º”quàˆ? b~Cĸ­ÏÎÎz½ÞW_}µ¾¾ÎÛVs© .ýøñãÕÕU©Tâ•1ÜQbM§gT‰çAHa…±ÖdÆfF;< Ìñ“ÓE0mÈ©]‹m–é#¹$Ë*Ò:‚“QÄ’¶F£’¦¹ ˜ùŸqD‚+1ìp†a©äÇ#­±k6L& 5`¥&Åøñ Õ÷ai ~üÒ*h·gÌŒ££À~£±)„ôïîàpñƒ‘EÍ"”ogÀM"ä`@T,É53ÒŽ§Óé³gÏVVVZ­fO*•±Íé¢VVf|'ÂÏÐ.¢D‰¾E %„!äó 5|ü¨ãxsoïçÕj}l)^ÈétDi§¡m,DEN8y(½^ëñ$qkêH·ÄE—¾\.?~üøôôt0 ðæä³¸U‚y/¦9¯Ä€•·ÝŸ[Øñ2W,Ç¥úBªÐçñ‹¬'^E'DóF%´ßÃ0üú믑Z’$‘ãBÁ⺡b$׆òõ›;Šàâ*•Y•6€8>.Ý¿¿¿²²†”Òó”Â÷uˆ8ŽŸ‚ %ÞC€÷|Ä:“¥¥¥Z­†Ž>[¥ÔÆÆÆååååå%V9¥ ·Lp°Âmg½Õõb“>~Ì!|øš|>¿¼¼Ì[K/fhà@8‡S‹\¤¥¥¥íííb±Èñét 0‚Y¼$4ýÀ3…ä‹¢t¯¯a4‚'OÀ÷a8”§§•jõùÊʽ ðætv¼ð<íûj:5DªboNLÃG,Rœ@ðÄd2ÙÜÜä <<üüùó³gϰYÜb¼O›Ïç¿úêO?~6 ®¯ÏOOß}wrò/_Nƒxi)*RϳgcÖ÷à~õ+xû®®äd"nn¦qœÎA …B¡Ùl~øðaÿq¥RÆžùJ)ß|ßçu9(¡ápˆ9“¯¾ú ©ÓëÀÌÅîéÄ…qú/–9#xÎÃŒ|>¯•–R*)…Êh+u‡xÁC{18uŽ Šj{{;Š¢ÏŸ? …R©Ä#Bï˾¾¾¾¸¸xðàÁÖÖV@ ·X7îa‘X¥R]YYßÞÞ«TþÏÿ9²Ö¿¹™6›Ç¾ßªT†KK“jÕ‹»'ÃÙ™<=õ‚@|þÜ¢b±0§ŒC±XÜÚÚ:;;=??‚@kÏì®ä¶¿ÃówttÔl6¿þúkìÙéÄN'`žesÊ8KÏ™„¼€‘A ‚ R©ŒF£(ŠDNJ¥6Á•`|a¤Ò„esú) :—‹1©¡‹Å‡æóy‚åœ ^¼“ËåvwwùœƒÅI«‹0¥”•JueemeeëÑ£ûKKù~ÿ¦×»j6¯®>•˽åå~½žlnšÿð ÕR^æºÝA¿?Z]­+%1”RÕjUÏÓÓéD‘e&Ë0Kz›£çuUÅbñÉ“'ËËË| .yæ‹ (?‹L"'OÎ]•ÅÙ•Ä2óJP°ÆNŸ”RxóÐžÄÆo‘‘p2‹cŒ|߯×ëÅb±Ùl^^^6 „ˆ—ÂFWWW+++Ø)Žª5xÑö"ãË5ž”~µº´ººfÌó(šôûNçôââóéé Àe«5jµ¤1¢X¤’Aü!Ö¬TÓ´Øíö&“étšær:M"ÐblÞjµºÝîúú:>‹“6Â3Š~œÓÖ‰ P>{Óiø¼8uÏ7Ç”ñ@"— ¢(J“Ä“J̨PJK¥4±çœS'ÇÁG“q: w¾iÚÈÑÑÑh4ÚÝÝåÝ1ÐA£R¯×;ì-NŸ R1Þ*[[#¤)¥ò}?ŸkµÚÆÆædòu«uùéÓÏ»þÅ/Ì÷ßn·{Rš0ô¸ ~2Y&ÆãáÑÑi»=½wO ¾'ñ´Z­Ÿ~ú)—Ë­¯¯ó<-‰ªÝn¿|ù‡^¯÷ñï‘ýÆÙ¤Î=ó ½þXz¤¼ŸÖÙcÒ4ñ”B`ɯИ£r†9#“cRYXâ<Ÿ••„€s¹ÜƒXÂaúîÝ»v»½³³SÀF,qA q…óxtž“òã8ÅéódT´Ö¹\(„pÐi6žg¤TIçrÖ˜{ ’qÛù¾·¶¶Öé nnÎWVR~è»ÝîÛ·o³,ÛÝÝÅ#È×!I’v»óË_~ûw÷›JÅÿÅ/þ”xf˜Üwæà:!oµ°8p‹¢I§EšT2— DjŒ§µ”³'Ñ_Ävåç$•øÔn„èètzž·³³ãy^§ÓY__Gº ÎrŠ¢èùóçKKKœŸÏKàøˆÞ…wÃ\ä£( âVÐZÍKÅT™/›ÍÁx<ÙÛÛ\[«N§€Ùk˜ßBÈR©¸±±éyŸ©<_Óï÷óùü‹/íBüS¯×ÿþäW¿zý?þGg4jüÇÿXÃì"Ÿ«Bu¬3 Õ3J`ð UVÊ Ø¢ÛZ ©'%¯Ü\œŽÁ£=JBñ´“‚ßÜÜ\YYÁø‡A€Mhæ2×N_Lè/V€çùž—›N{$ÐÖB„aðgö¤Z-~útyxxqï^meÅ›N''÷›ˆÃGùHš !„PZaD!•5;dÈ‚ÒVó„;‡êy3)úxRkÎøQþ.Ü}‡‡‡q7³³³ÕÕÕtœÆßø…á3ê–§øvŒFQ’¤ÆøJ Ç[_oT«Å|>—eÙãÇ÷²¬?O’$Źèo‚äˆ[rZq’1G¬ˆt/o¹X”ãÔ§9ÕÌx3a˜Ï21EÔûùn©L§q¿? à ƒB¡Ðn·Æãq–kgÑż±‰M’l<ŽòùÁfo’‰Šã¸Óé½|ùþ¯ÿúÝ¿þkíææyo(ãy½õuX^®8ô"^&¸ØZÂá^/N{%.<î‰[¤Ó‚5V‹!¤TB©yon'_¿8Èž sÈž9Q9²cß½{—Ëå~þóŸ_^^~øð᫯¾Âö9Žxœñ‡ƒ§3ç/ ¦Ó€wm¬Ó+b{V‰48¤‰â<ƒyë›e&ŽÓá0ÃZ«Õ:==ÛÚÚÊårçççårykkËZ{zzñÏÿüúý¯³ï¾{Ðï?Ͳ'}xÞpeEÕjeŠœQÜt9Äèp6ù$ygHï1H§Y€±ÖZ%•ïë »8 ŽHl¼¨‡c»Ô실Ùlj­Ÿ>}Úh4–——1X¤ ©ÅU¼¯ÓiŸâ/ËåªRagØ< é x¶ñja”Ëùv{eY¹\ò²@ç fº1M3cl© ÃáT)U,–šÍÖd2ñ<“ „LÇãéÕU»Z½öN3SgŒ2÷Qy¢+MÓÓÓÓóóó >“ï B~©&ïøøx}}Æ?9ó'˜Ðžðý Ýî(Ž´…ó+Üú®F¹Z-zž–R”J¥0 ûýþÆFEÙééi¯×—²”$vŸ/•ŠƒA<·Û­Á ¿±±qq1j·Çoß~zùrãðð«ñø±µ8lõ'úŸ€žÇ‚øøIv¶¶rår‰+:žñ'åÁ[¬ñ“êÀ1NE&­Î9Àj$Œ(uÔ›wHÒúîy_ÌÁR]?òË#ƒ¶Õj!†ÊâŒç9³,+ ÏŸ?ÿðáÃ`0À R¿†/–èñ&åXÍU¯¯~¦ÅbHœ *À‘¼Bq×êõúÑÑçËË ¸;Ÿ/üË¿|þöÛÎÇ[íö_¦é:@0w[R€ €BDm~.ãb±µ»[À ž/ éF§sÙ|â,8$n6f#B…ûC¸Bˆ|>ŠŠ»'Ö€ 6Ó—À]ší9êtéBæ¶iD"çÀñÓÃ驤±óùü³gÏ(;O 0÷b¸ºæH‚çy¥Rya‹ËžÄ†áx<~óæM½^ߨØàõø_œH±#ìJ£'xÚŒ` €hµÁ%Å÷ïo_]u~ó›ƒï¾‹{½GY¶HÑœÉLÌŸc1r~%€Ô:¾wOom­ÌÊžÁkÀ`VO+¥äcfq4]N˜×éIP’xRHƒ­t¥ÆÚ8ŽGãq¾PОw§ ‚5â¦Í² ¤­æ)`Ù,¢ð…“…ˆãøôôt8nnnR’…’[|ªCîæåw€C3_¿~=p3¹|.³“mö<¯R©”Ë^ošeV)ä<¥$º´sjšIÓ,MÓápxxx$„ì÷»¿ýíË7oâ~..6“ä>€?›`?ȹãÏu)º6™R½0l…¡Â×X#•T™JiKǵ†µÂX›c­QB*ìk`™óŽ÷¬:ÕÚá`Ðï÷ÃB¡Ïc0~ yaF™5&µFK¥ Kg{o%ãòñ:1nÐ…iµZ£ÑèÁƒˆ¢q¸Ï)·ç+O]Q¨pïÞ½ÑhtqqQ*•¶··´}±~÷V±XÌçËÝîE'aèÏiœjn Št*¥¶¶îÿ}ÿ÷¿¿¾ºÚë÷ qܳv ˜2 )sйüHÀY>ß/ãëëË8ŽÃ|h­”Q`A)@ RI)æôk ™1"3ÖL³)4xs!k*0`!šNûƒA>Ÿ¯V«~ÌfES²æ3*”ÌŒ±™ÑJk…"déxǃpf?£˜Ã0ÜÚÚÂBCÞåÐ~ÇKÔêuú ݦX,"…À™LãÔrП<Ï ÃâÕÕ¤ßÏk¯¥1ôΜ IDATÖhŒg~5¬ÕªÖÂÉÉÍñ±:;Û›N&I+ËòÖÖrÌþYZ&¹[hôÞ½É_þåÏŸg”P—@®¨ŽŽÚíöÞÞÒÂ8 sa††(•ÊÁ`ÝÍFáÎc²N§óùóç0Ìé?ýSóÇõñxÓZe£(eÙ0æË&Qòh @âyÍ ]¯—-Ø^·÷áÇn¯÷xoO{¾˜ÜØË@X#ì\bÖÚÌdÖšùDz9KxIIJ2I“‹ËË^¯·´´>&w…𸟥)š†y¯@+…Ð|¾‹ƒï9ãÝ‘Úuss“¦éÒÒ²~јq"¿w²&þüГ¾-‹;;;oß¾MÓû±.Î.ãœÕR©¬un<Žp$ùðR kM·Û;::BµÛ£ßý®ùîÝúx¼iL Dše“(šSÇé\„’)OËpQ`„–Ë»»5ß×I’`6æÃ»÷RÊû[÷}?³) B€Â·£1‰II±án ‘…t}s}y~±´¼”Ëå’4ÍDf­課FáYšZ3«nBÌE; ÷5xÁ4u(ŦÈépˆ!N%ï™Á›g¤ç@¸i¶··µÖ8l¼^¯©ÙéÚ‡WÈçó…BåüüÃÁÁibg±ÙÖI’äøøx8%‰úá‡ë÷ï+ágÌͼGm:Ö–,@$–ù5r­‰¹3);++§;;O¥8™zkkNONJÅb¥\™w”RAÆiœ$I–¥(?œËKl¢(º¹¾Y^Z®ÕëBˆ4I¥[=AvËÛ Ÿ·€VéO‹sòxýe}Ó4- D­¤”Åâh<Î çÒ"N˜ó³Óabmm ›±&I‚°x6ŸöJ–eZ{ëë›NóÍ›žï{lC:Oâ8;?¾z5™L­…z}óŸþ©ùêÕŸŽÇfmyrbþV9=›Ðü7f.EúÁ ѯV¯ž=«W*%TK¤oð6Þ¼y“$É“'O*• ¯~šùÕBJ5GKXcÓ4í÷ûŸ>}‡{{{Î\î£Ü˜y¹¶16ÍR¥µ²ZR:ƒ©Ô ˆãx4¡ßïð>ø‘uZ¸Ý±Àól°“ütZ}:@ZµZEæýû÷“Éä‹¥T¾EüŒv»}yyY,Vúiôí·µNç+kË@äB€ÜÜÂq‡SøÁ‹Ñs‰’_j=¯½±olÔ[€`¯æýýýf³ytt4ŽtÎTI,”˲ KÆwvv†²xx‡“™®šw\•ø BÓI§¦F¼]þLƒ(©Ó扼™‡'ÿ(whǤ±ù$Z2–F#—ËÇãEw×éM›©X,®­müÛ¿}þÍoÔååÏÒtAjnÕà.&”æg‘"B 6$€Éånvv`uµJ£”¨s).ÈÒÒÒãÇy¡O :¤P¥^*• ÆKÌœ’Œ[xrþ-¥µJ ©„ÐNïpçlaÎ{-–³òö·œÏÃsÈ‹ýd›;r$†ŸãÎÅö.777°¼¼Ì{Ó¡‰%¯x04›Í0̶ÿëí~üøq¼… ±/Y;žÂ½øÀÿ°~÷tÚy°BŒêõ‹ÝÝr­Vâ ªÍÃuÛØØH’äüü¼^¯cƒ:N>ãp¯×ët:+++TÝï—(Ç·¾U »´ wVS=ã"1,uÞpÚÓp!Ñt gÇ¡>q€‚Eb§½’ â%–ívûäääñãÇKKKØ3Ê·7 Þ½{×jµâ8øïÿýô§Ÿv’dmîmJ&6¸ë° 4¢¹ÌPlwl¡”7÷îõ=º†9§ÒÌq7’$AeŽã˜óƘN§óöí[l.ÃÙëœ`¶ØøLJ BZ)€•p\ XÉ[ÿÚ™û› ðVCã PÒÄ©â¾è€:¯ámqizXÊç™rˇ8x©TúñÇÏÎ΢("kÛ?Š¢·oß^__û~î׿þôÝwå~ÿµbí™…#(ïþæËæ™Þ”é[xÞÍýûfcc‰b*Ú œ‚­”ÂÞliš`.g¦,vñŽãx{{Õ›Óªôÿ1zgb?Òl6þÇèÉdÂùáÜ~:åN«=‡=NËÍ+ÊÛªòpÅ!8a×®ù|~?ŽãV«…À¯Á £Ñ¸ÑXùÝïõ« Ù|fLq޶ÔóŸ ù¤£I'5›Ÿ?BJ5€rP«½x±R*åyY$¯8ÓRJ¥Ò‹/>þÜn·K¥’3ϧÛízž·¿¿_*•åpà_nGIŸÍì«%o†±eq¢‰‘ÍW7õãµΘ=î;9…U¤{û/8ÍÔ1Õh]*-êÕÝÝ]ŒPñ ã_ñ¿»»»ß}÷îW¿\]}“¦KÌ¥t²Ef.N3ÿM 0ž§ 3âÛÏEˆ¤…áÚÚàÑ£í ¸ÓB×apSª›dìïï[kûý>M>ãx0`Bä=ðÒ ^b¸È䓳UšÏ¶ÖšLÂÉ+óy½™7®ôølÞ/6øsš=8õb;”ũΠìL6–——·¶¶|ßïv»‡‡‡KKK“Iöÿpzp°™$Û9¦- ³6ÌUá(hÐçÇÑQ°Jˆ¸P¸Þß66VÐ3çµ.Aœæ”Ëåz½Þ›7oíïïÓˆ/B6xO/S‰Ä]·_RHÖ£kã0'O’SÄù¨IgűÕ§+.f<(&q‚ÂEŽ k‚¨¡fõzýÝ»wQ5·oßj­­…¿ýÛŸ~ýëB§ó ϶r.<ž…àì 0hæŠT²ã+¤”ý••‹?ÿóµµµâñ*Wg iÁó¼jµšÏç_¿~=Nqe¥RÁ)ÎäƒÅ 'm®5bÉÀ œÃE©Z§÷ŸCû¤‚&§õÿb>ÖÁw8̶Xf·XàÏ¡v*bæ¾îææf–eGGG>|X^^~øðáááå?üÃøúúO³¬ÊÜ5?Ofn䃭é F=€½yî^2Ci.wþèQôèÑçwñµ^¼sRi¾ï?}úôààà§Ÿ~²ÖnllàCÞÞ™+§ †Ó­Yk˜e ¥˜Ñò­RiêýÎ œâ.æ‹ì'ÅAæwQ!ð­êŒ®%?–×:ý¢ö}žçíîîæóù^¯wÿþý~ò›ß|üðá~ÝðZmïra€až“ô ¨„óò»3!¦Åâé“'ÁææÊç‡9¡¥wh V*•§OŸ"jÿþ}žåxqú8‘ÝI 3æÅŒ}#@è$Ix6•6‡¤ ]oç¬òb ^Gè~òÎô:ÓÕh‡öû}lAûþýÇï¾ûüþôz/¬-²âs7 /ÔÌÅŒ?£ÅÄá  –p7HyõèÑÕ/~ñ´Ñ¨.NÚ^äfòF3ø€8Žyss=g j¹cÈ“êÎc.–îγŒ3¢—BahÏ©.‚ÅËÞ :Al~q0OT}q˜÷­Éys ޲Ù¼‚û›"íøåË·÷w×''ÿ·®/mŽãJ®=™y«ºzÆBq_$P–4£‘g4r8üa>8ü£Ÿ¿>ˇÃcé,ŠI‰D,­Ý]7ó}ÈêËË‚&‚Á@€ ÙUwÉÌ“'ÏùRu#»ý &f( ²óW¯òÏ9ð àx¼ž»ÀŸ€1pYU/þîïäÞ½kÉŠîjC´ÕbË¡«ù|þøñãýýý½½=?üðÃéééƒRÖÝ:ÁW}IZ°¢ª2¹yaÓï…/ZP^.©”^têÖ¦žÆUÚkk®.×nÍHæêW À[Ö¸ùìÁÁÁÁ£Gvwwoß¾½\Ö‡‡xòdk:½·:dš/]ÅËÖ¯ÎnÑsàð=0f@ ,C`Ç«æÓŸÿþïoŒÇ£¤ÍrÕýª§?Ù1ß½{w<»Üóçϻݮ«è¶n¯ü*j!«YÝlï¸=Ö°TÃU Ë«ôiÔ¸…›´Ì§sEïäÔNrMÀü&IST~ø’fJß“ÁÜööögŸ}Bøî»¿þzvxø³M"·'~/RéûXè8΀cà˜Ï€sà7Ào€øPàÀˆhÞïÿü›ß,~÷»ûý^‚ø¯šM_Å›üag³ÙáááÞÞÞÍ›7ýÆzðàµloo»f[>ÒÕš)¹åkÜ"“ùÉHÊ‹y«º¥”‹‹¦ƒ•Bl‹¦Ý’’¾¸¸xòäÉ‹/>þøc§]}ýõ“¿þuã-¢ÎŠª.Hâ¥Ñààèè·À`…Ý(ðX[€§Àkà!°E¤Ì“;wžýó?_/Kýæ›o¾øâ 7ÍçX“æ~ÈÌ, v~üñÇy£ö£Ñ(Ù1çÖ8­iÙ”Ö5‹só‹˜LÎcøUw¶\ÈÝM SU”öcjôç£L9"ïV·Jœ–óHŽ· ·†ßž?ßÿöÛË““˜f°Y ,‰.€à5ð†È3”-à·D@…¢)‘·#ŽgÀÀp ࢪžþîwóù—?óo¾ù¦Óéìíí¥­œHu9°’h;?ÿü³‡Àáp˜hyï<$»»»DôÃ?¼|ùÒõ>[B]­Açá3˜8 ¿» ¡EìÌ?S ‰n)¹eznúÝÂYòã˜Ñ«"d¹¦G¾r~뺇®«\U•Êo¿}ñôéz]_gfÀÌ.Í|å^û@m}¢ÂSÕÎð_ `Œ<7;>Ö€¥ÈáÎÎ㯾ÚùðÃÛfúç?ÿù_ÿõÿL&ÇŸ|òi¿ßO}´ÖØBz¢~¿ÿÑGÇãä}˜ii|sä¤%Ú”C%u][£ùd Wµ¨Jf!—é¿jÚ“òãù|žkQ%¯¾\þ¶…wç#w9(Úº[ò+:_?9??ßßßÇ[[[©0ýùçýÿøÉÉÉoEzÌÇfGf¯Ì^­@–ß;À:Q€Èˆ" ®ø”=Ü9m`<î·!š­­=ýòËùÿ¸×ëu|ðÁµÿú¯ÿ»¿ÿÃË—_=|øÛ;wînmmçšè¹¯g›¾ÕòÂ)aËé‘‹¢ØÝݽ¸¸xóæÍúúº«öµZîùÇmDcb-Ñ'nØT›¢"iåâ}9Üç»À ,‰‘ÿ•–jGÚ’ öMMÇ_õnnõµS†::rÌÝwÛÕV]Ž«å7Ód2q¥äõõõ«c-½]Š«¡"‚i4U"„„ŽæÓI-õ5³n—’÷ó<$xg#_¿Ç÷W'¤ÒyUÌeן>}zrrâˉuppôÝwÏ..8„ïÌ`66ûHuÇlͬð¸¸Z'ZŒú1fŸÔ$¢Ñ%°a6Ž€ÛÀ–™Í77_ýáÅçŸßëtJÿœ[[;7oÞ –ý~ Ììí_þòõë×·ïÝûèÆNGUOOOüñGUu€œÝÊ Å ÓéܼyóÙ³g/_¾,ŠÂS›–,aËWÙÌçµ U#Ì‚çx-°§Å-03Ÿç^,Éœ¨å}Òê·€‚ò¾j@uUÒÒíŠöööÖÖÖ<ï½¼œÿòËÛÿ÷¿|ÿýq·{½;ª×TǪ•*7ôwb"2f#R"÷òŠXW¿ fßCàÐEñú÷¿ŸüéO?ø`;=ã`0¸qãnŒ§[[ ,ww‡fÕë×/þû¿_¾zuÿÖ­;£Ñ¦«@;ƒ´ÅË1ÅYäš@ÃáðÁƒ‡‡‡———ƒÁ 7[IÓ ¡”˜5h½ÖµÖ1.5\£M@» )®ú4S^l¶.ÌVxû[űeCë BðºØÝµŽ&ûûo?~ý?ÿszvöE§ó¹ê@µˆÑ|îÜ/uÄ%Q$ZÕÌ+IëU°&špdV›}ô‰jæÉÖÖO_~9ºuk'ô1Óñxë§Ÿªª*€Ë““³»wÇÛÛ£ƒƒóW¯~úöÛ·››×>øàÖ½{÷Ö×ד—Lª˜[NážF¤l6Ù1;c}¹\z+*·˜{_30hTC´ö^e¤ù_Jî[¦”þ.¦Óé|>ïv»yÁ›ß„92›·õ[9í¯zÏÄONNÞ¼y³µµuãÆËËùÛ·o?~ztt®ZÍ盋ÅVQ|ÒëíªzeéQÙT}ñxåjD5sÍ òHÝH}ÐQ ”ff»f›€M{½Ÿ~:øð~U•¹ßŠªöûk½Þp>Ÿ]¿¾{||xt4¹}ûöƒÃÍÍõ§O_?þÿNNNîßÿ¨Óéxõ•Íœ‚›§¯WýSvãS›››Ž½µ˜ >e籠a…Vó/Ÿ3Jü”÷»\àl6;??O^K¹:Gk¢º%õœ…¾ðž¤€vvöüùsw#xöìù‹¯ß¼9"êF{ÝîÆññ¢ª×ÛŒ~þêÚük3ZITjÊB™M„B ŸÄ'ÑŒùÀ™j×ì¶YI´,Ë×wïþÃ?\7Zî×ÌÜï÷¯]»õæÍ÷ÎÚÝ»ûû¯';;תª{ãñúå%^¾ü~2y{ûöƒÑhÓÓ…Üš+ÏÔrOî|ûúÉ9<<FnÙ²‡Ökƒ0„™„Ä„TëpmM½äΊªªÚØØ888X,NìÉ}ªZ6Òy2–°Ó¿¥ïèÛ™ƒÁ›7o÷÷ ^[»1îv»ƒ‹‹ËÉ$2{½£ÿ#ˆ‘c´}²P™]]K‰Ô!¿(ñF˜#ÑpéÍ ³›.ôÄ<ÝÙ9úâ‹ÑÝ»×\Ä"egþUUmoo½Z.iww“ˆ'““·ocÔáp´¹9h2™œÿôÓ£Édkss«ªºyVŸó'Ò7ó‹Ê_ÈÚÚÚùùùùùyÂRr¾`\ S7£d'%¾sµÈÅ‹ZC÷­^DY–I0?Éæ¹r‹›š{޵þY——óýýÃÙL;­ÑhX–Ý:fzrr6ŸUÕ- 7,dU‹‘bL²ÕDÊlD¾©ÝÑИ£3;Ç"š]2o×€˜UÕñçŸw?ýô¦K4ç"))[__ßØØžÏk¢b<Þ‘ŽŽŽƒÁÆÆVUõˆ(„N¿ß=<<ŸLÞžžN67·‡ÃQ·ÛuÆTë ¶4µ’P¨ûA¦é¢üˆu4SB&ƒ$ä.­W1ÜÜ}*S÷ v6›%nu‹“·Ör¸ÎƒD®@â?3™LŽNOÏNO—Îöh´QUŠá.¿Ì<î÷KO}TÙ «õkÆ—‰"³z:‚{ŒyP„1›3¢žÈ-æ°¨ªÓ[·ê¯¾úp<Þȳª–HR·ÛÛÞÞ=;{;›Ù`Bè…0ŸÏãtº Á˜©®‰(ŒFëý~=™LŽ^Ïf[[»)iH}´«²— .NC£ÞšÍ+€šjñðÄ"L-Y’« ªf:'£å°éëál×–Ôó¯ŠsæÉN¾KNNN=z|v6ï÷G;;7:³¢kÍç‹Å‚;nQðJ߇{H•V–¾oŒH™UDWK‰ ¢Ì'ªs30o…ÐeFYžïìÌ>ùd{{{‚äÔìVß5„0nÌçÓé´6³ù<——ÓÓÓ ‘NUUu­ÞT)F£õnwyyOOEx8ùàD>”’S+R ÊMŸ½~ËEÏÓ´ikx@”’“B~ÀZôˆ<¢æ9®ûCN§Óétš{0åý³–Ãtb–¦X.—''§/^¼šÏ1ßÇEQ¹ÔGbšéb±TíUUé æ<"3NºOf^³®J{c†È%p©j1®1¯B˜‡“ªë×Ç>!œƒ½‰µV±Ûíu»ƒÙì”Hûýõ¢(:îÅÅÅùù¥‹ÔuZr^_¯Ö×¹®u6;[.ýþšW yAìUGÞ_Ë_Èù|î9mÂak_B@D4Xr]ú–èL‹—–çiIË»ßï'BMËѰ¥6ÔRÅ1Îf—‡‡GÇÇgªÅ{U•:Naõ˜b¬ëš;¾ÝaY’šYðŒ ЕúEG×D‚È’yf&u=ŠqDTż×;¹sGnÞ•eÈ-Troд+ɰõÉdÞï÷ƒžŠ¢Bu~~qzz!œRTe’ºu­Óéüââd: N·Ó餑¶u1ˆÉ-ÖˆÖÃ;jôꋨ‘4øÍõçU«$'“%Ó¿Ù]r+½…œ5š÷ØZB£Óéôððøôô²Ûv»ëeYeêá¦êð Ìt¹\šeÙ!BŒêšˆ›†øÇ¦UiC€S®}§sd^0תݺ^WíYUÍvvpýú¨,‹4+rU;´¥äX–e¯·Î˜ v°„‚*Ÿ­Zre§Sù¹Y‰ PQ”E1??Ÿ-—Ì”.¡”æäiy~)æÕH9­ˆ©F3RÓ:q˜Ïç^©ä=„Dƒla§ž°ù’¤ULýåÔmÍ)æð©}qqqttrr2+ŠÁ`0 ¡Xe~@•f p]ÇåREª^¯¬kRe@ˆ‚ˆ"³ø­#œ)^Z–¶BWü¤.€¹™­«®™Aä|8\^¿¾¶¶6P¿°óA_'Üöz=Õ¥™0;áŠ{=ÔµÎçs")ŠNYVeYº¡Éêãn—šÏëår „"©‰ä×R"=§?ʨÌB ƒ™€„X âEE38œeº-3l¬æKSÎ;ù%zyj>º3f3;þR..¦§§Ë%õû£µµa§S2“0KW‡óZ4V•Ùì’¸èöúUUÅHªP#˜gÌ DìÚjFࢲä"ˆƒÚDK³Ëº6³°N$D—kkqm­Ûí!ð·ú½{PX4±&e¬dÌçº\ªH`)ˆ†Ã°X,Ì´(ª¢è®êlÛ^É‘H¨*V³å¢žÏcQ”É5ÝÃÅb1›ÍüVUÅ"ü>—̰j=š˜ B`X(;„åý7{ŸÄB*Ký²æ ¢>㣭”/c]»ø0±4TŽÀ,Æz:Φs‘ª(:½^¯,½‹D…WàÂQ©®¡AШ— .Ê.K1Rô™)“kÝÓ*O3±HQHYJ„ „¥ê¢®£Hú"KìTºµµÞëu꺎¦Ï2,Û¯”$wY Ò:ŠH·ªˆYcŒ\ÄÚbôÒÖï¶ÐéT1ª›î¤ŠW®|  CªP×j¦u¬›Q$XŒÑõ|Þá!#]g¦Ÿ„Á”†ipCk"¨)™ó5–Të©(‚iãVҸȺ½:QV‘hIÖ>535fr±[˜šª©™-µ*÷úC‘’‰C(ˆ™@ÿÝ…A,•ÍtIf,^Ç´0ãZÕ4A R ³ÓÔØXˆ-”\ª Df±^b±µ¡‚UU\Vëþ„«{1w9IDATÆöÍÓÿ€jÚÈÔˆ ‚Aµ”"HPÑ,„P×Ëzƒ®K)œq^@¦Ðhîò…& ¸ª,„ x]yùR"–ˆê-Æ Ú84í¼Ôj-*beaQc¬—qID 3ƒ“6s¥Â 2çð§0(Ș© AIÌÌa¶¸œk­EŒ„„ 0µX׋ÅR¤X_€„Þ)m±‘à±½áΓEбV ÒéPŠjdÊ ØL8•Í FdlPê”ÄPY.I¥VUËá°X[ïK!Õ+ÂlP(±˜ŒÈV/k³Ø eA U(̨¢êRjs!´ ] %¬v­45323¹Ä¦©$x…ßµ1F £2”vE¦¦­N¼zµÜ°¶ê]k„©&Š05õ!`‚Ë¿)|Æ 8¥¿±ÎSCsXÄØÌV¸ZuQër5kx:Þ ‘²ìbªLljpsL¿BƒEšc°Æà ‘©!jÔP`@ jƒƒˆEIY,‚XÁŠh«ÑŒ©îB·Wº<¤ÁÔ¢Ç+‹1Rcpl´’í%†ÆF!ÄI‚¦00±¬u­V,nËjjbbÀuÞiSXˆ}‚͈ؗÙÔ¡k2¿-ƒƒ¾æl5(i »Âc£L94’n)»õ¸á{¯´Â@ù©‘A ±xqëÚ¢ªFÓhëÚd¹.LØ BA c˜IÈ¡F,AÓ¥ÆCÒ J V ÑÈŒ4¨‚(0V ¨ý9‹‚ÀÍ#ˆ Ø !Ú’µe'¼ÃºbTS"báhºŒµ€Í``ƒbŒ b«µ™°õ7Ík±%!šEÕZA6&c?0ÒP˜}_xùÊľˆâ–ÂÖèÚ(<Þ°¦òÁ˜ æ +mEUI´òÕhZ#ˆ»Ç˜¸˜­°«µST5õÐgOa ê[³h0#fV¬AP™H ñMÑ;„(À µ ‹k 0…„YÕUDc„²ß= (‘MUÄÀêûOÉ"HÌ"ˆQ V¯…˜Iˆ‚ y‡É°l^¾ˆQ›£›W‚øÎƒPMf€˜‘Â/XaƒE¨)L˜8ˆ“a©F¦Þ#" Ƀ‚ˆWE.µejD"ØVé|£†66O>M£G)ráG-6E·=j)5& …„ÈN)©šܬÖ(ì2§ YjSójƒI„„ÅD˜M™™êX¯Ü‚íAP4)ܺ±QôSîÁ €QŒkRq}$“‹@fT+‚Š5jE4 B$DÂÆP2%FA,fcb±X§ÓFqYa~%Zt4­M•Ì÷¦²gñ¿]R m&76‰f†X˜ªkr±ùÝgÌ–—ÃÑpsw%J<éÀÂÞ¢6…ÆZÍÍ]ä©¥Wˆ‡€˜œ‘ ¡¦Œ ªÍ¬ŒZt-~9ÑM£áw ©Ùr¾ŒªÁ¬¨ªPfñž£pÇÓ=Á˜™7£ÏL´Ž«DÙGŽ-"Öˆ`Ä1 P”% Gµ¥b®±¶x©Ñ`b,æïªJªÁX¸l¤Œ”T„DJ<Ž(¾S1‡šF³QÉ)íàH+¯@!. f1!f¿„µ l‰lLÀâ…«Ûî6æ:êÊv9„@,B S«µÖÚbŒJM}ƒ21‡àç^Ý O£¨³óš AÀÿ`ÝôHÙ]¹êIEND®B`‚boats-201908/images/addboat.png000066400000000000000000000017571353032755300163200ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sBIT|dˆ pHYs²Žø±tEXtSoftwarewww.inkscape.org›î<lIDAT8­Ô_L[UÀñïí½-÷ÞÒu-kk±lË"ŒBA¡€•ìA¶l,S²©‰Éöà›ÉÈ2ÿ™¨h6Ñd2|‚DC¦2ã¦&¸ v#©&f¸„–B¡´¥-÷ø0ÃtaС'ù%'¿sÎçœüNΑ„dÓ$I’ŸÜ«}×Û—_¯(À¼ÒJÿv^ÉJê,ï~ßP‰îñ9>œˆ¢Ë…„âaÊòdNÖl§ØaY™Ÿ\^¡ìù¤­Õí‘M{úo²(ùyÚ¿‡B­´‘& ÒÜßKg›;=˜²(=PoîØ»_u¾xa–Ñ´JõÎjBé?iÊ{œœ¤Óa¦º¤#ƘN,e?ö„òQG§^šJÁ'¿Äqºœü–¸L$9Š)b±$Ä%4}y«Æ+CW×/EÅòsïh»­V‰‰™4óº÷ºÎ¾]-h2ØsìÔæ7â9)XˆÒ—ù‹“°¸¸¼6¬i¨Ñ9‘HÆô˜Î}v/ª ÉÌ«÷’–g1Ý„´ÕXÿć–»(¹f³üjI™êÈ‘$†]ÛÞG© ÅWùvò ×g1AJ…í²U5­_ãá3§ßz=Ñyy4}P±€Saø#ó&¸2 £S $‰¼^dYBÊöÔÖç´?sÔzüÍÍ ¹7ä 9q¨Î-&"B\3fHÉp(UÀ©G˰oÖ³‡êÔ×ZŸÝÔö¹-n½äJ2o˜ p-Y8œÎçh·Çƹ¾Pøžà[¸Ö~ìD^[Aº)–P·[Çí±[0‰ö—®Œ G#„X3ÛV·RñÏ\ ^;ÑÿuQ$¼è³É*‘u⃮û§êqœÌBî ZÊš›mŸvt¸~ojÒ¾¼s¼¶No;û±wzì×òtËÁ-ßmSËÿu ;ìØ¡6¶´8zz‹f2Ë>qî3O¤h›òÐj›»=æ*¥Þ·îjUxW©õ@k«kp` dÞU•Â>qð)ÛÀzåZ-…ýˆßŸû|WWIe  kÒœ?¿ —Þ¸§Ûý»IWÆS>Ÿf¾…¥¹ g8|hò«žž¹ÝM>ŸÍ¼ÚÀààB,L¾½Öø6»»#?ML$¿ù_á¡¡XüçÑø»Eï Ÿí Œ'¾ø/ð_÷'ÒµÁ%EIEND®B`‚boats-201908/images/addmark.png000066400000000000000000000020051353032755300163100ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sBIT|dˆ pHYs²Žø±tEXtSoftwarewww.inkscape.org›î<‚IDAT8µ•ML]EÇs¹÷½wß+Ü^Zª…4@ʇÑE)©hJSC†¤1,ìŽ4.ŒnHlLã^»1¶¦aaª.Ú¤Æ6ib&ÕXh$ˆµ´0’R¾äµÀ£\¸ûwñ( Or2s&ÿùÍ™™3#‰ÿÃìÿ2É|`ŽçõЂВÑÙQe‚Ì÷ÄyG¯ªoY³•Œ1†³|y ô@SKe »ÝÝ„QHOº‡ ½æ&§'Ûô†>°¶”êûœõwúMõ•õL„÷izâ%âAD¡ïp°ö KŸšÌŽ-11àhaQ!¿ÍýL:èFZ “¹Çœ~ÂMN’*M%˜ã=ø·glŒÙñ®]=æg½ä@’#5/ãæ÷x~g#þD!%3S\Ê~ s¼`ŒI­×Õ%ñ¼"¢ÈÇÉåÐ!»½k¨ò­WIf’”ze$,°Ã“©Bžš/ƒÅX#@A.ÙpCƒ‹ëî§  ‚(²0&ç`aŒU?ìyy‘ÉvuÚíãG¨Íƒª’Û|5ØA¿ßÁ @À ¬€‰¤gVÖÛj'•¨øÃ¿Sõh×🟂¬`Ú‚Þq螀"ºøPÒâJ¹••}Ly¹³t¡«Ý<†Lmo=þËþñâÐ?‚úmU¤5A4É‚_Äú¾éXhôpœHÜÁug©­ pcÌÚEÖ™éäÛE=/fÜLbѲPá˜=ú ™×:Õµ7 ›aõ‰ÅnÕÀ$ñø©Tß/ ??†1Û°¬ØÒbát}ãã‹£33óÎü¼µ'ŠüRH¥¤K„a+’rî8·ä8ú›;&]¾,íÛ·2vê”tþ¼"ÏÓ¬ã(X­·íÏó6_¿.…¡tíZ.®©‘¦§¥tZª¬Ô:ý*ðæ/ϲ"ff"„ç pò䱘ˆÇ…1‹›N]g{@?pÉNç*£­­œÃ‡=2ƒd²ÀïH½ÀÈf`HO#U íB2twÉàûpñâ’Zq¤=À³HÅH¿ñÀcH{7Ü×™3ŸCCpóæFŠ< Ë^6¦kC(À•+pî´·ÿ£dɾ^î-WÄä8oÊqúeÛ’mK§OKW¯j9¶m©¹Yº{W*.^sœ‹µ.³¤ ~c‰F¢¨é9 Øøä.÷!Æ<@ºNÂðk@[úš¶b|Þ9¿#{IEND®B`‚boats-201908/images/addpoint.png000066400000000000000000000012611353032755300165120ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sBIT|dˆ pHYsvv}Õ‚ÌtEXtSoftwarewww.inkscape.org›î<.IDAT8í“¿kSQÇ?÷ýHÞË3‰†¶ƒt)Vt°ˆ:™A´¢‹ ›ƒqDÁ‚TŠ8ùtA‡â¬.®‚ !…jÒÚHÞ{yùñî»M0­‚¯:ù]îåžïùÞsÎý^¡”b/ í‰êá"l$!½/[LNÚR)<·²’Ù•ðô|S@õ×k5llV«öô|S b£Š¡ý×Ç9 ’øxIÿ8 .*•,@!ÍmH$¼)`Š­n+@ð'Ò(×Zw?ÞºvþÍñoWރוg¯v’F©@?ñbªvïôí1͈Y.-·ëÚZöíl)’¸Bg«} tûgÒk´rØEɘz³aù¢k»6€B?i³ÝP²zH":aD”ŠKú" ˜Yš™[uW¤»fÞT:Ó¦Ù¢GÜÕQ=£uáå1 Ù\xöÐìóÄ3¶žXë7O^Ÿõ©KtRtÐ4°„¥gÙô¬­W98…‰~€Œ¤úR-ã ö9SG‰º¦Hk6e÷?ü&QÛÀÉ%w…x(n¤ëé§–´rB²8zfœ”%q? Úš‡å¤3"°óbaT»‘Ët‹—›ùœÎÊ—Ðñ2•;õpáƒAója¿™ínDÇH¥ˆ¤ÜÆJ<ã_8e„Ѹc™FXwU6ð;xÒo+)£aÖ_’v!rƒloñ{eÝ+—«-ÏôÕîû½aÖO‚Ññ›JoÓIEND®B`‚boats-201908/images/addpoly.png000066400000000000000000000013761353032755300163530ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sRGB®ÎébKGDÿÿÿ ½§“ pHYs „ „ªâcytIMEÙ $ßð•~IDAT8Ë¥”OHQÇ¿ogÖÅvA´ÜC‘E »‘‰¡HŠnãZ¤àaW( "¨D°Èe™F—ÀC—0‰.Ñ¡Cˆ™¨]ó`^¼lfHëÎ{³Îί‹«ìŒ³ø.?~üæ}Þw¾¿ß{ÀîÖþ`0H à@ªàÚ%87 €”ʳNàñòo+¹c鹜X’‘ÿh†š‰àÛV’³§TÏÑy•=•ìmr?*θz'9àá4 ÍR‘ͧeáp˜6GJmO å¢gR %‰§²$­Ø€ÝÛ­±_{µo^~uB×g܇w uæWÏ÷ãa«úHM›X)•¬ …øÍ<ÅP¼ ‹Xì2¦üˆ?0Hv8Û‡É0ߢŠ|òX>ó!üÈ3ò˜[rÁhˆÒk#ìJ~&ó”RÙÿ|?÷B  œWŽO¯Ò)Ì1=QÎ0Ú 7UlFp¾ÇS]¡ë뺉gDÄf48VtÌxöêrª=)=÷ç7ó{p3X> Îy >Éãˆß¿bU0xðÃÒÒïj ã.À ŸAéÄ…Þ¸ƒ¤À¨§¯BW/8àpœwEÄáïuÓîÖ®.*ê8‰ŒIMœ­¨HÎñùžÒ‡[™Åg\âkÄø¹{_DªÛ ÄØ‹°—WØä‹ª67wÜœœÆ§JJfEt]Ƕ!™d]OÏ©†‡kEÄIw£”R˜Íó4SK¹$±!ʶ±•÷Eä2€wwMMs™×«¥,ËíŠÇw¦¡š6-¢Ô¬&¥ê=îØ‘5J©F  (’@/pYDÆÒExË|>- LGk4ús§inÔõ™ 4í¶ç £mŽH]®e=ü€ˆ\SJu~À‘ÄÄYMÚŠË’ö1ßÙ@`åAÃx¤V©ûslÆÇןénKçIj8ã™¶ê:ض º¼60#î4--Ðê ËÛ‘>9ö•ˆX™@“À–ëòN’ob•ôú›Èšv»_Ù)`:,«õ'×í|s¢X)¥!˜~àb„‘¼ £[7S-Gqœ#{E$þ`ñ‚9/?ñdpÞ‰Nó"ðàD°fßòzV8cK¦¹ñÇ9Ú PX¨W,^œ¿£¥uz×î=áÇe}W’¯ÞÔŠŒDÀu‡H&·‡Ã¾Ë–¾ÔÜ\>wõš`±®;€ÍŽ£?\8oŸ˜2Ø4[z–,97{ժʵ+Bùš–R^íßéîN¼žI›캣,ZÔQòå®;ËRoàFó?ýäÏ£çÎ&:2é3~ôÅÅ-lù8G¿ÙÝ¡C×bÝÝfk&-À_L µuWIEND®B`‚boats-201908/images/animate.png000066400000000000000000000022711353032755300163300ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sBIT|dˆ pHYsaaÁ0UútEXtSoftwarewww.inkscape.org›î<6IDATxÚT}LeþݵW®Œ¯B¡kSZ(_AA™«Î(»%d0AYM¬ñ™‰ h$L…iÆâ?&˜-ºÄÌ2ðc&ë²°.j™âÇ Â2»6ÆÇh -…¶´ôz¯ï‘5²Ù>É“_îÞ÷yÞßýî¹#B'ˆ‡5. 1Ðßß/ ¢Ž$3®‡ãÁOårvú++šj½ÍØ´!»ú£©¿”ª-B®ã ª¤\m¯£. ¹ë––®C̓=uª™Ïç£ÚÚZgWW¹pÔìú62ƒ ƒƒM‚?<¿ó×=M §çC²‘Z©  ±úbJ|@ÊP? I“Œ€ –j¿>õþþ«ªª2»»»[$¨w¿«¡ÈÏê cbò‹K ¥¬b¡1=%Ïd2ê-3ãg{<ä÷ç#I¿¡A©ö­eF#ÓétáÞÞÞþ¦¦&éèè¨]¯×Â6àƒÀñ;ôfåkôÿ¢8Eu†éëu'ÿÝ¥´žÏ_yNwd_ŽÒår%z<`fÐb±ÀÌÌŒ›Ððˆ’¤JºÀ¿ä™Ü˜¯÷Lr{¾x-pœÎÉËËËœýDÛêj{Í}¬5Úíö'ÛÛÛêëëµXëÌÍÍE<€×8]”š€ ýœö+S±k#p8 #]/G᫚¸MƒApíloöâ]Ç¥ÃÍK‹ŠŠö€V&J PuuõPt„câÑÈùt?ºB³kf8 ÉOÊÜ©…À’L0ŒFãú÷—-nKé¡o“hh``€ NœŠ4AªØÌÙ•)DÏ.t"_¦¬ î`WWï:áÝ´½=|¹¹$ËÑGùë+Óº—ºèÿ7Ý6ç^¼â¾õîKO)¥î›:%…*)÷¼"ÈG…%¹‰$“,Œ¤¤±c>ìµ.nÿÐ6Ra=›ÓT¦R½Šˆ%,ó€'`€/C(HHÂë‹ø¼l覗\;ö·'#û™:ûé¾¾Bx 6r¼ûÕ{çp9ÇEÆÞ-®MÝ!1 :ñvðþ˜œ^ ý8â¸ašÉ I’L™UℜÐkؼ¿7fƒG/ü£Ÿ}~…ÉÉÉ/¼)Æ% ÊÏÏÿâq:bobqñØl¶…B±2==íÇÞp8€¯› ç•jwÛžìÙÖ½ÂxBQó0.Þ‰‰ §\._¢iúö[÷ù|RÀó$B§Y‚¹ŸÀgR·Çi¾Æ™OMMÍáè-ây[­Vï¿‹˜wȳk³ n>D:6Þ‹ç|’“«TªÕïêr%î+›÷nÇû2eEð8VE"w³‘[‹a“ј–óx¼!\~‚«±ã7OÀlÇ]#¡PÈ€8¶qüæ2ÌL„ùiìÇŸ”y\Î`Þ­©©±Ã&ü³•÷@­Í,IEND®B`‚boats-201908/images/boats.ico000066400000000000000000002414461353032755300160210ustar00rootroot00000000000000€€(F00¨%n ¨.h¾>(€ ÿ==ÿÿ==ÿÿ<<ÿÿ<<ÿÿ;;ÿÿ;;ÿÿ::ÿÿ::ÿÿ99ÿÿ99ÿÿ88ÿÿ88ÿÿ77ÿÿ66ÿÿ66ÿÿ66ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿEÿÿý(ÿ==ÿÿ==ÿÿ<<ÿÿ<<ÿÿ;;ÿÿ;;ÿÿ::ÿÿ::ÿÿ99ÿÿ99ÿÿ88ÿÿ88ÿÿ77ÿÿ66ÿÿ66ÿÿ66ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉ ÿÿÿ£ÿ==ÿÿ==ÿÿ<<ÿÿ<<ÿÿ;;ÿÿ;;ÿÿ::ÿÿ::ÿÿ99ÿÿ99ÿÿ88ÿÿ88ÿÿ77ÿÿ66ÿÿ66ÿÿ66ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿGÿÿü%ÿ==ÿÿ==ÿÿ<<ÿÿ<<ÿÿ;;ÿÿ;;ÿÿ::ÿÿ::ÿÿ99ÿÿ99ÿÿ88ÿÿ88ÿÿ77ÿÿ66ÿÿ66ÿÿ66ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅ ÿÿÿ¡ÿ==ÿÿ==ÿÿ<<ÿÿ<<ÿÿ;;ÿÿ;;ÿÿ::ÿÿ::ÿÿ99ÿÿ99ÿÿ88ÿÿ88ÿÿ77ÿÿ66ÿÿ66ÿÿ66ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿDÿÿü#ÿ==ÿÿ==ÿÿ<<ÿÿ<<ÿÿ;;ÿÿ;;ÿÿ::ÿÿ::ÿÿ99ÿÿ99ÿÿ88ÿÿ88ÿÿ77ÿÿ66ÿÿ66ÿÿ66ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà ÿÿÿžÿ==ÿÿ==ÿÿ<<ÿÿ<<ÿÿ;;ÿÿ;;ÿÿ::ÿÿ::ÿÿ99ÿÿ99ÿÿ88ÿÿ88ÿÿ77ÿÿ66ÿÿ66ÿÿ66ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿBÿÿû"ÿ==ÿÿ==ÿÿ<<ÿÿ<<ÿÿ;;ÿÿ;;ÿÿ::ÿÿ::ÿÿ99ÿÿ99ÿÿ88ÿÿ88ÿÿ77ÿÿ66ÿÿ66ÿÿ66ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁ ÿÿÿœÿ==ÿÿ==ÿÿ<<ÿÿ<<ÿÿ;;ÿÿ;;ÿÿ::ÿÿ::ÿÿ99ÿÿ99ÿÿ88ÿÿ88ÿÿ77ÿÿ66ÿÿ66ÿÿ66ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?ÿÿú _ÿå77ÿÿ<<ÿÿ<<ÿÿ;;ÿÿ;;ÿÿ::ÿÿ::ÿÿ99ÿÿ99ÿÿ88ÿÿ88ÿÿ77ÿÿ66ÿÿ66ÿÿ66ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ ÿÿÿšÿ ÿ€ÿõ::ÿÿ;;ÿÿ;;ÿÿ::ÿÿ::ÿÿ99ÿÿ99ÿÿ88ÿÿ88ÿÿ77ÿÿ66ÿÿ66ÿÿ66ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ=ÿÿúÿÿÿÿ¢%%ÿþ;;ÿÿ::ÿÿ::ÿÿ99ÿÿ99ÿÿ88ÿÿ88ÿÿ77ÿÿ66ÿÿ66ÿÿ66ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼ ÿÿÿ˜Vßÿÿÿ4 ÿÃ,,ÿÿ::ÿÿ99ÿÿ99ÿÿ88ÿÿ88ÿÿ77ÿÿ66ÿÿ66ÿÿ66ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ;ÿÿùxòÿÿÿUÿÞ22ÿÿ99ÿÿ88ÿÿ88ÿÿ77ÿÿ66ÿÿ66ÿÿ66ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹ ÿÿÿ–šýÿÿÿvÿñ55ÿÿ88ÿÿ77ÿÿ66ÿÿ66ÿÿ66ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ8ÿÿø.¼ÿÿÿÿ˜!!ÿü66ÿÿ66ÿÿ66ÿÿ66ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ· ÿÿÿ”MÙÿÿÿ, ÿº''ÿÿ66ÿÿ66ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ6ÿÿøoîÿÿÿKÿÚ..ÿÿ55ÿÿ55ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´ ÿÿÿ’‘ûÿÿÿdÿâ//ÿÿ44ÿÿ44ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ4ÿÿ÷'²ÿÿÿÿpÿé00ÿÿ33ÿÿ22ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿ² ÿÿÿ8¾ÿÿÿ ÿ{ÿð//ÿÿ22ÿÿ11ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿ2ÿÿöDÉÿÿÿÿ‡ÿõ//ÿÿ11ÿÿ00ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿ°ÿÿÿPÓÿÿÿÿ“ÿù//ÿÿ00ÿÿ//ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿþ ÿ/ÿÿõ\Ýÿÿÿÿžÿü..ÿÿ//ÿÿ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿ­ÿÿÿ‹håÿÿÿ'ÿªÿþ..ÿÿ..ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿþ ÿ-ÿÿô tìÿÿÿ1 ÿµ!!ÿÿ--ÿÿ--ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿ«ÿÿÿ‰ €òÿÿÿ; ÿÁ""ÿÿ,,ÿÿ,,ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿþ ÿ+ÿÿôŒ÷ÿÿÿG ÿÌ##ÿÿ++ÿÿ++ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿ¨ÿÿÿ„˜úÿÿÿJ ÿÇ""ÿÿ**ÿÿ**ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿý ÿ'ÿÿð#£ýÿÿÿD ÿÁ ÿÿ))ÿÿ))ÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿ ÿÿÿz+¦üÿÿÿ> ÿºÿÿ((ÿÿ((ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿû ÿÿÿê %Ÿûÿÿÿ8 ÿ´ÿÿ''ÿÿ''ÿÿ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿ•ÿÿÿo!™ùÿÿÿ2ÿ®ÿþ&&ÿÿ&&ÿÿ%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿ÷ ÿÿÿã“÷ÿÿÿ,ÿ¨ÿý%%ÿÿ%%ÿÿ$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿŠÿÿÿdõÿÿÿ'ÿ¢ÿü$$ÿÿ$$ÿÿ##ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿó ÿÿÿ܇òÿÿÿ"ÿ›ÿú""ÿÿ##ÿÿ""ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿ€ÿÿÿZ€ïÿÿÿÿ•ÿø!!ÿÿ""ÿÿ!!ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿîÿ ÿÿÓzëÿÿÿÿÿõ ÿÿ!!ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿuÿÿÿO tèÿÿÿÿˆÿðÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿçÿÿÿÊ näÿÿÿ ÿrÿâÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿjÿÿÿDgßÿÿÿÿ] ÿÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿàÿÿÿ¿XÍÿÿÿÿGÿ¼ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿ`ÿÿÿ:C¸ÿÿÿÿ2ÿ§ÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿØÿÿÿ´.¢úÿÿÿ ÿ‘ÿôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿÿÿUÿÿÿ0òÿÿÿÿ| ÿéÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿÿÿÏÿÿÿªwæÿÿÿÿf ÿÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿJÿÿý'bÖÿÿÿÿQÿÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÅÿÿÿŸLÂÿÿÿÿ;ÿ°ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿ@ÿÿû7¬ýÿÿÿ'ÿ› ÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿºÿÿÿ”$—öÿÿÿÿ„ ÿêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿ5ÿÿ÷ìÿÿÿÿ`ÿÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿ°ÿÿÿŠ lÝÿÿÿÿ>ÿ­ ÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ,ÿÿï M»ÿÿÿÿÿŠ ÿîÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿk+˜õÿÿÿ ÿhÿÖ ÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÿÿÿÚuàÿÿÿÿEÿ´ ÿþÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿLRÁÿÿÿÿ%ÿ’ÿò ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéÿÿÿ¼0ž÷ÿÿÿÿoÿÜ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿaÿÿÿ-{äÿÿÿÿMÿ¼ÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐÿÿÿœF^ XÆÿÿÿÿ+ÿ™ÿö ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿAÿÿ÷žÿÿèk5£ùÿÿÿÿwÿâÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±ÿÿÿ}ŸÿÿÿÿÿÜY€èÿÿÿÿTÿÀÿþÿÿÿÿÿÿÿÿÿÿÿÿÿýÿ#ÿÿç¡ÿÿ ÿ‘ÿÿÿÿÿÏH]Ìÿÿÿÿ$ÿ‡ÿæÿÿÿÿÿÿÿÿÿÿÿ‘ÿÿÿ]¢ÿÿ!!ÿ'Ýàÿ-üÿÿ,óöÿ†ˆÿÿÿÿÿº#:©ûÿÿÿÿNÿ±ÿûÿÿÿÿÿòÿÿÿÍ£ÿÿ""ÿ&Þáÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ-êíÿstÿÿÿÿ÷|€áÿÿÿÿÿxÿÛÿÿÿqÿÿÿ>¤ÿÿ##ÿ%ßâÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ+ÝàÿNOÿÿÿÿÖ>G«úÿÿÿÿ?ÿÿÿÿš¥ÿÿ##ÿ$àãÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ#®°ÿÿÿÿþžrÖÿÿÿÿÿÿÀ¦ÿÿ$$ÿ$áäÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ2ñôÿrsÿÿÿÿë`:ôÿÿÿž§ÿÿ%%ÿ#áäÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ,ÎÐÿ 78ÿÿÿÿÀ( a` ¨ÿÿ&&ÿ"âåÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7úýÿ!•—ÿÿÿÿù‚©ÿÿ''ÿ"ãæÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ4åèÿYZÿÿÿÿÛDªÿÿ((ÿ"äçÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ+¹»ÿ %%ÿÿÿÿ¥«ÿÿ))ÿ!äçÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ9õøÿ}~ÿÿÿÿìZ¬ÿÿ**ÿ åèÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ4ÕØÿ@Aÿÿÿÿ±®ÿÿ*+ÿæéÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ&™›ÿÿÿÿï`¯ÿÿ+,ÿæéÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ9àãÿIJÿÿÿÿ¸°ÿÿ,-ÿçêÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿ*Ÿ¡ÿÿÿÿòg±ÿÿ-.ÿèëÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿ=äçÿOPÿÿÿÿ¾!²ÿÿ./ÿéìÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿ-¥§ÿÿÿÿôn³ÿÿ/0ÿéìÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿ?çêÿUVÿÿÿÿÃ&´ÿÿ01ÿêíÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿ0«­ÿÿÿÿ÷uµÿÿ12ÿêíÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿCêíÿ[\ÿÿÿÿÉ+¶ÿÿ23ÿëîÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿ4±³ÿÿÿÿù{·ÿÿ34ÿìïÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿGíðÿabÿÿÿÿÎ/ »ÿÿ45ÿìïÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿ8·¹ÿ ÿÿÿú‚¸ÿÿ56ÿíðÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿJïòÿ ghÿÿÿÿÓ5³ÿÿ34ÿíðÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿ;¼¾ÿ ""ÿÿÿü‰®ÿÿ/0ÿëîÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿMñôÿ#mnÿÿÿÿØ:©ÿÿ+,ÿèëÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿ?ÁÃÿ &&ÿÿÿý ¤ÿÿ))ÿæéÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿPóöÿ&stÿÿÿÿÜ@žÿÿ%%ÿãæÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿCÆÈÿ**ÿÿÿþ– ™ÿÿ""ÿàãÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿTöøÿ)yzÿÿÿÿáF“ÿÿÿÝàÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿFËÍÿ..ÿÿÿÿÿÿÿÚÝÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿVøúÿ-€ÿÿÿÿäL‡ÿÿÿ×Úÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿJÐÒÿ33ÿÿÿÿ£ÿÿÿ Ô×ÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿZùûÿ0…†ÿÿÿÿèR{ÿÿÿ ÑÓÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿNÔÖÿ88ÿÿÿÿ¨tÿÿÿ ÍÏÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ]ûýÿ4‹Œÿ ÿÿÿá=oÿÿÿ ÉËÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿRØÚÿ77ÿÿÿýƒiÿÿ ÿ ÅÇÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ^ùûÿ/{|ÿÿÿÿÇ"cþÿ ÿÁÃÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿKÀÂÿ ÿÿÿò]^ýÿ ÿ½¿ÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿ]íïÿ"VWÿÿÿÿ¦Xüÿÿ¸ºÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿ?Ÿ ÿ ÿÿÿß:Sûÿÿ´¶ÿüÿÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿXÚÜÿ77ÿÿÿü€Núÿÿ¯±ÿüÿÿüÿÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿeùûÿ2z{ÿÿÿÿÅ Iùÿÿª¬ÿüÿÿüÿÿüÿÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿO¿Áÿ ÿÿÿðZD÷ÿÿ¥§ÿüÿÿüÿÿüÿÿüÿÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿcíïÿ$UVÿÿÿÿ¢ @öÿÿ ¢ÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿCžŸÿ ÿÿÿÝ8.ôÿÿšœÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿ]ÚÜÿ66ÿÿÿûiÿÿijÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿkùûÿ5yzÿÿÿóÿÿåèÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿT¿Áÿ ÿhÿÿPQÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿiíïÿ½ÿÿ¥§ÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿpýÿÿ óÿÿæéÿüÿÿüÿÿüÿÿüÿÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿpýÿÿjÿÿRSÿüÿÿüÿÿüÿÿüÿÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿpýÿÿ¾ÿÿ§©ÿüÿÿüÿÿüÿÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿpýÿÿ!ôÿÿçêÿüÿÿüÿÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿpýÿÿkÿÿTUÿüÿÿüÿÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿpýÿÿ¿ÿÿ©«ÿüÿÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿpýÿÿ"ôÿÿèëÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿpýÿÿlÿÿVWÿ üÿÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿpýÿÿÁÿÿ«­ÿ üÿÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿpýÿÿ#õÿÿ éìÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿpýÿÿnÿÿXYÿ üÿÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿpýÿÿÂÿÿ­¯ÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿpýÿÿ$õÿÿ ëîÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿpýÿÿoÿÿXYÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿpýÿÿÄÿÿ¤¦ÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿpýÿÿ"òÿÿ àãÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ üÿÿ!üÿÿ"üÿÿ#üÿÿ$üÿÿ%üÿÿ&üÿÿ&üÿÿ'üÿÿ(üÿÿ)üÿÿ*üÿÿ+üÿÿ,üÿÿ-üÿÿ.üÿÿ/üÿÿ0üÿÿ0üÿÿ1üÿÿ2üÿÿ3üÿÿ4üÿÿ5üÿÿ6üÿÿ7üÿÿ8üÿÿ9üÿÿ:üÿÿ:üÿÿ;üÿÿ<üÿÿ=üÿÿ>üÿÿ?üÿÿ@üÿÿAüÿÿBüÿÿCüÿÿDüÿÿEüÿÿEüÿÿFüÿÿGüÿÿHüÿÿIüÿÿJüÿÿKüÿÿLüÿÿMüÿÿNüÿÿOüÿÿOüÿÿPüÿÿQüÿÿRüÿÿSüÿÿTüÿÿUýÿÿVýÿÿVýÿÿWýÿÿXýÿÿYýÿÿZýÿÿ[ýÿÿ\ýÿÿ]ýÿÿ^ýÿÿ_ýÿÿ`ýÿÿ`ýÿÿaýÿÿbýÿÿcýÿÿdýÿÿeýÿÿfýÿÿgýÿÿhýÿÿiýÿÿjýÿÿkýÿÿkýÿÿlýÿÿmýÿÿnýÿÿoýÿÿpýÿÿÿÿÿÿ?ÿÿÿÿ?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿàÿÿÿÿðÿÿÿÿüÿÿÿÿÿÿÿÿÀ?ÿÿÿÿà?ÿÿÿÿøÿÿÿÿþÿÿÿÿÿ€ÿÿÿÿÿàÿÿÿÿÿøÿÿÿÿÿþÿÿÿÿÿÿ€ÿÿÿÿÿÿÀÿÿÿÿÿÿðÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀÿÿÿÿÿÿÿðÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ?ÿÿÿÿÿÿÿð?ÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿàÿÿÿÿÿÿÿÿøÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿàÿÿÿÿÿÿÿÿÿøÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÀÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀÿÿÿÿÿÿÿÿÿÿð?ÿÿÿÿÿÿÿÿÿÿþ?ÿÿÿÿÿÿÿÿÿÿÿ€?ÿÿÿÿÿÿÿÿÿÿÿàÿÿÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀÿÿÿÿÿÿÿÿÿÿÿÿøÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿáÿÿÿÿÿ€ÿÿÿÿÿÿÿÀÿÿÿÿàÿÿÿÿÿÿÿ€ÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÀÿÿÿÿÿÿüÿÿÿÿÿøÿÿÿÿÿÿø?ÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÀÿÿÿÿÿÿàÿÿÿÿÿÿÿÿÿÿÿÿÀÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿ?ÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿøÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿàÿÿÿÿÿÿÿÿÿÀÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿø?ÿÿÿÿÿÿÿðÿÿÿÿÿÿÿàÿÿÿÿÿÿÿÀÿÿÿÿÿÿÿ€ÿÿÿÿÿÿ?ÿÿÿÿÿþÿÿÿÿÿüÿÿÿÿÿøÿÿÿÿÿðÿÿÿÿÿà?ÿÿÿÿÀÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿþÿÿüÿÿøÿÿðÿÿàÿÿÀÿÿ€?ÿþüøðððøøüþþÿÿ€ÿ€ÿÀÿàÿàÿðÿøÿøÿüÿþÿþÿÿ(0` $ÿ==ÿÿ;;ÿÿ::ÿÿ99ÿÿ77ÿÿ66ÿÿ55ÿÿ33ÿÿ22ÿÿ00ÿÿ//ÿÿ..ÿÿ,,ÿÿ++ÿÿ**ÿÿ((ÿÿ''ÿÿ&&ÿÿ$$ÿÿ##ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþ{ÿ–þ==ÿÿ;;ÿÿ::ÿÿ99ÿÿ77ÿÿ66ÿÿ55ÿÿ33ÿÿ22ÿÿ00ÿÿ//ÿÿ..ÿÿ,,ÿÿ++ÿÿ**ÿÿ((ÿÿ''ÿÿ&&ÿÿ$$ÿÿ##ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿýü ÿ#ÿ>>ÿü;;ÿÿ::ÿÿ99ÿÿ77ÿÿ66ÿÿ55ÿÿ33ÿÿ22ÿÿ00ÿÿ//ÿÿ..ÿÿ,,ÿÿ++ÿÿ**ÿÿ((ÿÿ''ÿÿ&&ÿÿ$$ÿÿ##ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþÿ—´++þÿAAüý::ÿý88ÿÿ77ÿÿ66ÿÿ55ÿÿ33ÿÿ22ÿÿ00ÿÿ//ÿÿ..ÿÿ,,ÿÿ++ÿÿ**ÿÿ((ÿÿ''ÿÿ&&ÿÿ$$ÿÿ##ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿùüÿ*Ñ4 ÿÜ22üÿ==ýý77ÿý66ÿÿ55ÿÿ33ÿÿ22ÿÿ00ÿÿ//ÿÿ..ÿÿ,,ÿÿ++ÿÿ**ÿÿ((ÿÿ''ÿÿ&&ÿÿ$$ÿÿ##ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþyÿ‘YîYÿñ55üÿ99þü44ÿþ33ÿÿ22ÿÿ00ÿÿ//ÿÿ..ÿÿ,,ÿÿ++ÿÿ**ÿÿ((ÿÿ''ÿÿ&&ÿÿ$$ÿÿ##ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿûüÿ$Eû„þý66üþ55ÿü11ÿþ00ÿÿ//ÿÿ..ÿÿ,,ÿÿ++ÿÿ**ÿÿ((ÿÿ''ÿÿ&&ÿÿ$$ÿÿ##ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿþtÿ‘ ¤ÿ—þÿ44üþ22ÿü..ÿþ..ÿÿ,,ÿÿ++ÿÿ**ÿÿ((ÿÿ''ÿÿ&&ÿÿ$$ÿÿ##ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿö üý6²ÿ¦þÿ22üþ//ÿü,,ÿÿ++ÿÿ**ÿÿ((ÿÿ''ÿÿ&&ÿÿ$$ÿÿ##ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿþuÿ'¿ÿ´ þÿ//üþ,,ÿü))ÿÿ((ÿÿ''ÿÿ&&ÿÿ$$ÿÿ##ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý ÿõ üþ31Ë&ÿ¼þÿ,,üþ))ÿü&&ÿþ&&ÿÿ$$ÿÿ##ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ ÿÿ þoÿˆ=Î#ÿ´þÿ))üþ''ÿü$$ÿþ##ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿý ÿô üúP 7Èÿ­þÿ''üþ$$ÿü!!ÿþ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿý ÿÿ þdÿ€1Âÿ¥þÿ##üþ""þüÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿþ ÿè ýòÿ +»ÿšþþ üþþýÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿý ÿÿ ýYÿp&® ÿ~þõüÿýýÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿäýî “ûc ÿéýÿýþÿüÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ÿÿ ÿý ÿÿýLÿf  yïJÿÕþÿüþÿüÿþÿÿÿÿÿÿ ÿÿ ÿÿ ÿÿ ÿÿÿÖþãJ_á4ÿ¼þÿüþþüÿýÿÿ ÿÿ ÿÿ ÿÿ ÿüÿÿýCÿXFÉÿ‘ ÿûüÿýþ ÿü ÿÿ ÿÿ ÿÿÿÿÿÌÿÛ%¢ûeÿå þÿ üÿ ÿû ÿþÿüÿÿü.ÿ>&±±TU wë>ÿÄþÿ üÿÿüÿÿÿ¨ÿ¹./ ÞÛ@NÏÿšÿûüûýÿüÿ EF á’þ%ÍÐý%&ÿÉ )¨üWÿÛþ‘ÿ• BC á‘“þ.ÿÿþ.ûüÿ4ÿÿü £¥ý ÿ†Mÿÿ gØÿê NO â’“ÿ+ÿÿþ+ùüÿ.üÿÿ/ùüÿ4þþÿ5÷ùüXYÿè >,1=> ä“”þ)ÿÿþ(ùüÿ,üÿÿ.üÿÿ0üÿÿ2ûþÿ4ùüÿ<ÿÿý/ÊÌüÿ® ;<ã–˜þ'ÿÿþ&ùüÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ7ùýÿ;üýÿ@ÿÿü ƒÿõSBBä—™þ$ÿÿþ$ùüÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ;ùüÿBÿÿþ;ßâü ()ÿ¹ ""67瘚þ!ÿÿÿ!ùüÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ>úýÿAûüÿIÿÿý&‰‹þ÷[66曜þÿÿþùüÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿBüÿÿCùüÿIÿÿþBåçü-.ÿÀ<=眞þÿÿþùüÿüÿÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿAüÿÿDüÿÿEúýÿHúüÿPÿÿý,‘“ýûbAB æ ˜šþÿÿÿùýÿüÿÿüÿÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿAüÿÿDüÿÿFüÿÿIüÿÿJùüÿQÿÿÿJéëü33ÿÆST à ”•þÿÿþùüÿüÿÿüÿÿüÿÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿAüÿÿDüÿÿFüÿÿIüÿÿKüÿÿMûþÿOúüÿXÿÿý3˜™ýýj ŽÝ ŒŽÿÿÿþùüÿüÿÿüÿÿüÿÿüÿÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿAüÿÿDüÿÿFüÿÿIüÿÿKüÿÿNüÿÿPüÿÿQùüÿYÿÿÿRíîü9:ÿÍ ‘’؆‡ÿÿÿþùüÿüÿÿüÿÿüÿÿüÿÿüÿÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿAüÿÿDüÿÿFüÿÿIüÿÿKüÿÿNüÿÿPüÿÿRüÿÿUûþÿVûüÿ`ÿÿý: ¡ýÿqÿÿÒ€‚ÿÿÿþùüÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿAüÿÿDüÿÿFüÿÿIüÿÿKüÿÿNüÿÿPüÿÿRüÿÿUýÿÿWýÿÿXúüÿ_ÿÿÿ[ðñü==ÿÈ Ïyzÿ ÿÿþùüÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿAüÿÿDüÿÿFüÿÿIüÿÿKüÿÿNüÿÿPüÿÿRüÿÿUýÿÿWýÿÿYýÿÿ[üþÿ^úüÿgÿÿý8‘“ýø LÈtuÿ ÿÿþ ùüÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿAüÿÿDüÿÿFüÿÿIüÿÿKüÿÿNüÿÿPüÿÿRüÿÿUýÿÿWýÿÿYýÿÿ\ýÿÿ_ýÿÿ_úüÿhÿÿÿZÞßû ÿŸÃmnÿÿÿþ øüÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿAüÿÿDüÿÿFüÿÿIüÿÿKüÿÿNüÿÿPüÿÿRüÿÿUýÿÿWýÿÿYýÿÿ\ýÿÿ_ýÿÿ`ýÿÿbûþÿfûüÿmÿÿü*efÿé&ŸXYÿÿÿýøûÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿAüÿÿDüÿÿFüÿÿIüÿÿKüÿÿNüÿÿPüÿÿRüÿÿUýÿÿWýÿÿYýÿÿ\ýÿÿ_ýÿÿ`ýÿÿcýÿÿfýÿÿfúüÿrÿÿþQ½¾þ û;ÿýÿüùüÿ üÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿAüÿÿDüÿÿFüÿÿIüÿÿKüÿÿNüÿÿPüÿÿRüÿÿUýÿÿWýÿÿYýÿÿ\ýÿÿ_ýÿÿ`ýÿÿcýÿÿeýÿÿhýÿÿiûýÿpÿÿÿjîîü•bcÿÿÿþ úýÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿAüÿÿDüÿÿFüÿÿIüÿÿKüÿÿNüÿÿPüÿÿRüÿÿUýÿÿWýÿÿYýÿÿ\ýÿÿ_ýÿÿ`ýÿÿcýÿÿfýÿÿhýÿÿkýÿÿlüþÿoüýÿåÄÄþ ÿÿÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿAüÿÿDüÿÿFüÿÿIüÿÿKüÿÿNüÿÿPüÿÿRüÿÿUýÿÿWýÿÿYýÿÿ\ýÿÿ_ýÿÿ`ýÿÿcýÿÿfýÿÿhýÿÿkýÿÿlýÿÿoýÿÿ;ÿ ÿÿü ùüÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿAüÿÿDüÿÿFüÿÿIüÿÿKüÿÿNüÿÿPüÿÿRüÿÿUýÿÿWýÿÿYýÿÿ\ýÿÿ_ýÿÿ`ýÿÿcýÿÿfýÿÿhýÿÿkýÿÿlýÿÿoýÿÿ—fgÿ ÿÿþúýÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿAüÿÿDüÿÿFüÿÿIüÿÿKüÿÿNüÿÿPüÿÿRüÿÿUýÿÿWýÿÿYýÿÿ\ýÿÿ_ýÿÿ`ýÿÿcýÿÿfýÿÿhýÿÿkýÿÿlýÿÿoýÿÿë ÈÊþÿÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿAüÿÿDüÿÿFüÿÿIüÿÿKüÿÿNüÿÿPüÿÿRüÿÿUýÿÿWýÿÿYýÿÿ\ýÿÿ_ýÿÿ`ýÿÿcýÿÿfýÿÿhýÿÿkýÿÿlýÿÿoýÿÿ 8ÿûÿüúýÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ"üÿÿ%üÿÿ&üÿÿ)üÿÿ+üÿÿ.üÿÿ0üÿÿ2üÿÿ5üÿÿ8üÿÿ:üÿÿ<üÿÿ?üÿÿAüÿÿDüÿÿFüÿÿIüÿÿKüÿÿNüÿÿPüÿÿRüÿÿUýÿÿWýÿÿYýÿÿ\ýÿÿ_ýÿÿ`ýÿÿcýÿÿfýÿÿhýÿÿkýÿÿlýÿÿoýÿÿ?ÿ?ÿÿÿÿÀÿàÿøÿþÿÿ€ÿÿàÿÿøÿÿþÿÿÿ€ÿÿÿàÿÿÿøÿÿþÿÿÿÀ?ÿÿÿð?ÿÿÿüÿÿÿÿÿÿÿÿàÿÿçÿøÿÿÁÿþÿÿ€ÿÇÿÿ?ÿÿÿþÿÿÿüÿÿÿøÿÿÿðÿÿÿà?ÿÿÀÿÿ€ÿÿÿþÿüøðàÀ€ÀÀàððøü( @ þ<<ÿÿ::ÿÿ99ÿÿ66ÿÿ55ÿÿ22ÿÿ00ÿÿ..ÿÿ,,ÿÿ**ÿÿ((ÿÿ&&ÿÿ$$ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿý¾ ÿwÿ@@üü99ÿÿ88ÿÿ66ÿÿ55ÿÿ22ÿÿ00ÿÿ..ÿÿ,,ÿÿ**ÿÿ((ÿÿ&&ÿÿ$$ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿüÿÿý<ô† ÿÿ>>ýþ::ýü55ÿÿ55ÿÿ22ÿÿ00ÿÿ..ÿÿ,,ÿÿ**ÿÿ((ÿÿ&&ÿÿ$$ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ¼ ÿtAç°''ÿÿ;;üý55þü11ÿÿ00ÿÿ..ÿÿ,,ÿÿ**ÿÿ((ÿÿ&&ÿÿ$$ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÿý:ò i5 ÷Î**ÿÿ66üý00þü..ÿÿ,,ÿÿ**ÿÿ((ÿÿ&&ÿÿ$$ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý¹ ÿr'„D ûØ))ÿÿ22üý,,þü**ÿÿ((ÿÿ&&ÿÿ$$ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÿý7ð   “Pÿâ''þÿ..üý((þü&&ÿÿ$$ÿÿ""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý· ÿo  TÿÞ##þÿ))üý$$þü""ÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü ÿÿ ý3í ˜N þÙþÿ%%üþ þüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿ ý®ÿd ‘H üÐÿÿ üþýûÿÿÿÿÿÿÿÿÿÿÿÿ ÿü ÿÿ þ*ç ‚8õ¹ÿÿýþüüÿþÿÿÿÿÿÿ ÿÿ ÿÿ ý¤ÿW$g&è ÿþþþûýÿýÿÿÿÿ ÿû ÿÿÿ!ÝM׃ ÿòÿÿûþþü ÿþ ÿÿü˜ÿL1´]ÿ× ÿÿ üÿ ýøÿÿÿÌ</ Š;õ´ÿÿþÿø{ÿ- Å‚ƒÿÐ*^Ý}ÿÚÿ¬Ç#ÒÔÿ1ÿÿû1øúýceÿ˜NO + ¡ ×È!ÔÕÿ*þÿþ-úýÿ0ùüÿ9ÿÿü/ÑÓÿ **òKÉÕ×ÿ'þÿþ)ûþÿ.üÿÿ1üÿÿ4úýÿ9ûüþ@ÿÿü"‰‹ÿµ &'ÊÖØÿ#þÿþ&ûþÿ)üÿÿ.üÿÿ1üÿÿ4üÿÿ9üÿÿ:ùüÿCÿÿü>ãæþ45øU ËØÙÿþÿþ"ûþÿ&üÿÿ)üÿÿ.üÿÿ1üÿÿ4üÿÿ9üÿÿ<üÿÿ?ûþÿCúüþLÿÿû*‘“ÿ½ÊØÙÿýÿþûþÿ#üÿÿ&üÿÿ)üÿÿ.üÿÿ1üÿÿ4üÿÿ9üÿÿ;üÿÿ?üÿÿDüÿÿEùüÿOÿÿüIèêý9:û\ÅÔÕÿþÿþûþÿüÿÿ#üÿÿ&üÿÿ)üÿÿ.üÿÿ1üÿÿ4üÿÿ9üÿÿ;üÿÿ?üÿÿDüÿÿGüÿÿJûþÿNùüÿXÿÿü3˜šÿÄ ¿ÎÐÿÿÿþûþÿüÿÿüÿÿ#üÿÿ&üÿÿ)üÿÿ.üÿÿ1üÿÿ4üÿÿ9üÿÿ;üÿÿ?üÿÿDüÿÿFüÿÿKüÿÿNüÿÿPùüÿYÿÿýTíîý?@ýdº ÉÊÿÿÿþûþÿüÿÿüÿÿüÿÿ#üÿÿ&üÿÿ)üÿÿ.üÿÿ1üÿÿ4üÿÿ9üÿÿ;üÿÿ?üÿÿDüÿÿFüÿÿKüÿÿNüÿÿQüÿÿUüþÿWúüÿeÿÿû;žÿ¿  ´ÃÅÿ ÿÿýûþÿüÿÿüÿÿüÿÿüÿÿ#üÿÿ&üÿÿ)üÿÿ.üÿÿ1üÿÿ4üÿÿ9üÿÿ;üÿÿ?üÿÿDüÿÿFüÿÿKüÿÿNüÿÿQüÿÿUýÿÿYýÿÿ[úüÿeÿÿý\æçþ--õB²½¾ÿ ÿÿý ûþÿüÿÿüÿÿüÿÿüÿÿüÿÿ#üÿÿ&üÿÿ)üÿÿ.üÿÿ1üÿÿ4üÿÿ9üÿÿ;üÿÿ?üÿÿDüÿÿFüÿÿKüÿÿNüÿÿQüÿÿUýÿÿXýÿÿ]ýÿÿ`üþÿcúüþoÿÿû0tuÿ“()õÿÿû÷úÿ üÿÿüÿÿüÿÿüÿÿüÿÿüÿÿ#üÿÿ&üÿÿ)üÿÿ.üÿÿ1üÿÿ4üÿÿ9üÿÿ;üÿÿ?üÿÿDüÿÿFüÿÿKüÿÿNüÿÿQüÿÿUýÿÿXýÿÿ]ýÿÿ`ýÿÿdýÿÿfúüÿuÿÿüUÂÃÿ?wxÿ ÿÿü úþÿüÿÿüÿÿüÿÿüÿÿüÿÿ#üÿÿ&üÿÿ)üÿÿ.üÿÿ1üÿÿ4üÿÿ9üÿÿ;üÿÿ?üÿÿDüÿÿFüÿÿKüÿÿNüÿÿQüÿÿUýÿÿXýÿÿ]ýÿÿ`ýÿÿcýÿÿhýÿÿjûýÿpýþþÏÐÿ ýÿþüÿÿüÿÿüÿÿüÿÿüÿÿ#üÿÿ&üÿÿ)üÿÿ.üÿÿ1üÿÿ4üÿÿ9üÿÿ;üÿÿ?üÿÿDüÿÿFüÿÿKüÿÿNüÿÿQüÿÿUýÿÿXýÿÿ]ýÿÿ`ýÿÿcýÿÿhýÿÿkýÿÿnýÿÿ *+ï ÿÿþùüÿüÿÿüÿÿüÿÿüÿÿ#üÿÿ&üÿÿ)üÿÿ.üÿÿ1üÿÿ4üÿÿ9üÿÿ;üÿÿ?üÿÿDüÿÿFüÿÿKüÿÿNüÿÿQüÿÿUýÿÿXýÿÿ]ýÿÿ`ýÿÿcýÿÿhýÿÿkýÿÿoýÿÿ?wxÿÿÿüûýÿüÿÿüÿÿüÿÿ#üÿÿ&üÿÿ)üÿÿ.üÿÿ1üÿÿ4üÿÿ9üÿÿ;üÿÿ?üÿÿDüÿÿFüÿÿKüÿÿNüÿÿQüÿÿUýÿÿXýÿÿ]ýÿÿ`ýÿÿcýÿÿhýÿÿkýÿÿoýÿÿÿÿÿ€ÿàÿðüÿ?ÿÀ?ÿðÿüÿÿ€ÿÿàÿÿøÿÿþÿñÿÃÿàóÿÀ?ÿÿ€ÿÿÿþÿüÿø?ðàÀ€€ÀÀàð(  ÿ??þþ77üþ33ÿÿ//ÿÿ++ÿÿ''ÿÿ##ÿÿÿÿÿÿýëÿVxÏë33ÿÿ66üý//ýþ++ÿÿ''ÿÿ##ÿÿÿÿÿþÿÿÿŒ á\R“êó--ÿÿ--üý''ýþ##ÿÿÿÿÿÿÿÿýç ÿ TLZ bœðô&&ÿÿ%%ýýüýÿÿÿþÿÿÿŠÝWfAb—ìðÿÿýþüýÿÿüä ÿKpU‰Þäÿÿþýûÿ ÿ‚ÔF ,,3Ö ‘ŽÉòGŒÅŠï…?ÅG¢Û"øCþ.|ô{Ù²Qèð(ùáŒN _„s8|™þÀ·…$¹åÂ/@§WŠ]RÝ-'UHa~7™4øŸ±X…÷–›éÐ×£kµ¬8omoUѽÀ&r(éʶk‚ƒ{ Ú†4" íÄ’Sw5Ø é"T»¤jë+Pmåq=öyo´¼ð' ÚNðw‚"±³~w‚¼<â•íj2×[‹Æˆ¶E¡ 3ó0WE¦o¾ÁÜ"àçKÜqÞãù……çªíŠù/j]ùržž÷=$æPßy*[Àò›É0Ïq^ºº©!.©šÛ[¶óöð[nî½É¡®Cöod˾X>>ÓÊ!vI)€Óø÷â¢lÜøv¸ÄõéÆãñœBŒÐ€·fw·V¬&wS×euiÓÆv;,Ë’³©¸.…¦‹Å"étš¥¥%Êå²#7 #Ì‚›nû‡Â¾I,// ûaš¦¨T*NªÕª°N¦¦¦Ô±s}||¬‡ÊæÿG}k©ãÚ§lW(P«xýzÒ¼v­ïL6›ÄMM¬©4‘K±ýDŽtrò•98xãB<þ~´éŽÝ"7öZl¹àÅ‹ñRìt<þnŒ&è¬{§äóÆì¥K—¯&]ÿ¤…)ÖÀ_Þ˜MOÁ@qIEND®B`‚boats-201908/images/fileopen.png000066400000000000000000000014341353032755300165130ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sBIT|dˆ pHYsvv}Õ‚ÌtEXtSoftwarewww.inkscape.org›î<™IDATxÚµ“ËjA†ÿS§ú6—ÄDР(A‚èV\$H|"ø®t%¾Œ‹¼ƒä DÑˆŠ‰DÐ$šÛLgúRå©t:¦Ó3AŒþðÏTMM}õŸÓ]d­ÅÿÆ!ÑyçÊ)þLVüEB. ‹...-͆"9JL{F9.TNššº  ~0óþíN:1}¤4|Ï“oå@UUÕÀ̬p@û“8»pëÚn\9 ­ÙA]Úº•r®ŽÅgo>|rïñÂZ Ì»(2TZà~¯ºX?TÍäåQŠ“|´Ö ÏcJs g:-Æ%ìðƒF)·Ï÷}ÔÀÌ>:q†¸g ªÐ2] ë ß‘}:`Ô[Áš:²'­´ Tœ°Öýklõ2t“¼²±ÜÒÊC)ºúhÎXk ÿP³Ú•|gr|s¸éY&OÑ ýZ"P_, uâJi¬n¦ë³sŸOèÐ÷²×Ë©=5Áƒ<FZê7˜Ž¾È¶øÀzÐcu#FàëL7¢ Ë Ü 5ò\ÀlбR4(n Ï÷óÊ6W¥€5Ö6À0èêvü$àt#² 0 oÅ ^¤©ùpJ’Û=ƒ(dDF·¢`E·›Á2g"Ï ¥–­œ¾w«úWT|Ÿä€ ±ÉùÊåÞË5ï’˜?Å;éõ€S0 ¬‘VÀ$`!'nMBt’¡‘“fÃ͆÷QG´³(¥\eY^lŒw1ú·ÂZ ouê£"f—ت¦gÞhê­¼Ð<†<ÏÄÅÉßVÖþòýUКPw^/Î?{:>}×DÑ0»ò#WM;JÌ÷W/Ÿ“›p¢Ñj]Ú{8tŒgºÛÛï„ù¡{Ú8¾¬xK˜Ù/qåùTÚÕy©IEND®B`‚boats-201908/images/fileprint.png000066400000000000000000000015601353032755300167060ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sBIT|dˆ pHYsvv}Õ‚ÌtEXtSoftwarewww.inkscape.org›î<íIDATxÚµ•=H[Q†ß›{óçO‰!ÑAk%…Ó©Cp¨‰P;wëÚÅÍÍÅ­éæè"ˆÐÁÝY+th5m!Ôÿb4¹1?æô{¯Þx½â´/|\8ùÎs¾ó~çœhJ)¸¥iÚ€8î§´0¶áÁî½U÷ÔE.ÜáÁÿÑÍàF£ñoÁËËËúÈÈÈójµv'œ¿3—sà’—jµÚ»T*õ±T*ãì¬ ¿ß ]7$ŽŽŠ­#¨ ˜뎊 nàô´Ê\0¿¸¸8||Œr¹LÔ +“@(‹ØÝÝE¡pÄŠÄ ?|>˘;9¯˜¹œÓÛÛk/* ž¦Ny<žVµ¥R ¡P33˜xå¬S„3lI.íCGG+§EìÓ˜‘{zú ¶öööÐß߇(¢§§¶¶3ßãF°­M¶êƒ-nKüÂC455…¾¾>Ø&›ís‚鑸[€-6-;­ ¯Ü²sΙÚÖÖöF>Ÿ‹5›MPò¥GÎIlœb£¹ -BÙ'Šßp8²ihšÎs[„T*8uà x¹õ`ÐZlmm ‰DBî€ #›ý#~\&ÍÍÍauu¦iºNÄu(¿„&“ILNN²(ž ›ÍC[Xø¼GY2-°ü#Çí·È[g_a\¡01ñ+++Èçó-ŸÓ雯áaÁ¤xà#‘tw?–Ëa5–ç”Á÷Âö²ÕÑѤµX.—³.E¦ûD*Qƒjxø…vö^@uB%hC Xa}ý‹J§¿iÓÓÓÚ­ÐÁÁ … ô:ØmEWW—Æ9wþ5‰^g2¿Šò)Ó¬(ñ]É‘S7IšU0îæÐ V¡ AA‰A‰—^޹‚7ØöÚñý*‘‘0%*Ìù >ûùÎÔ¥¿aIEND®B`‚boats-201908/images/filequickprint.png000066400000000000000000000021231353032755300177370ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sBIT|dˆ pHYsvv}Õ‚ÌtEXtSoftwarewww.inkscape.org›î<ÐIDATxÚ­•]h[eÇÿçœ÷ä³ÍÎ6šK—mØ-z1±Rü ÇôND½!La¼Q¬x!B¡èUŠ·Å»âLð²LE¬¸R?Ö­ÛXÚ4KóÙœää|¾>ç­'véÂzáþ yÓþòäÿ<ωsŽnI’t@{Ó1®£[t¸Ë¤çù%þö ÿ“–_Ò´¥Ʊ­Þ`Çq°Wýv6™RÓö—°”µžàùùyebbâAÓ´îïŸ}|üá¾SíÏåW2_ovÀ ]²,ëål6û‘®7áºÂaŠÂȲßT‘“˜Fo<Á> Yܼ~¤ž`ÆX4ŸÏ£ÑÐEE–†ªªt®`:waÛ6ôÅ/I/‚;¬eüþö.ÎÌÌÌa7@ŠÇãÂ¥R µZ 6"‹À~ÅÊŽŠ]˜·Axk,î¢Ú‰/½ø~ùû‹Ñl6AJ3§Éˆ ¿¿õzÅbÕj ¦iRa„B"ŽÿÀÍkï"i@’rm‰D¢ó¡ív;냳²,wªÕuš¦azúÃ`¦wÎ7 8÷èMœyr2d—¼ úûúDt‘ß§ÓÒâÏ?.=r|,Èfss+++è¥Då;Œ&?õÀÔí¹²Íƒ€·®wO᎞ɱh,F_5„@~sss¸—(:.<·…ybªU³p®]„c^…m A>ߢf‡îSF”oü¦ Bö\œ¸Myj°<Ú€…dÔ‚Bppa‚3ÄÒŸBÁ#]»v}¹PØó:J;ÐËåÖÁ˜„@³³³XXX@«Õ  Ý`1vçŸ.ãÍ×!d›2ª‘Ï`8:AÈå `·nåh£Á:crr—.}Cãö±Èž¶.X`IÈÀ¡ÚÛà^‰r*[o¡ &qìXJ4ßg²J¥ŠB¡’XˆT*º„LÐrˆÆúkî[ÜþÌÃuo@ýÄqè© Ø¸²$–É—Ïìþ¡J§ùÈHšŸ<ù´ Vÿ…*>”,Á­m ´~ Nà§ê¿ò×UijjJäÙóÚ,—ËþjKš–À¾}»,V7¬_¦j rü+hS’ÿ?÷ýi"=³ºz³N—oµ N¹s9Þ‘çñÊåwx{mY¼¤fÕœéæHâÁŸ9 LŽ’ÓäGȪÖey8®„‰yÝÛ`’-ò¯äUr‹lÜ«<öV6AÌIEND®B`‚boats-201908/images/filesave.png000066400000000000000000000014361353032755300165120ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sBIT|dˆ pHYsvv}Õ‚ÌtEXtSoftwarewww.inkscape.org›î<›IDATxÚµ•]kA†ßÝM‚oô&é—”šŠ½Åký¼‘Z?ÀoêMAHñ¨?£‚¿@èHHJAk‘‚Ûš¸™ÝYçÌÄa– Bßp朰sž}÷lvãåyŽ£P‰–×®oGQ4ë>î®lLZÇRJ´Ö[hži‚g™2èY°…JÃ3‰N§£ÁRæ°`šs¦€t‚–Ë>6>uqݼ09õR(–DÁñBsAŸÀó8ðpëâ<\©C097Yª…æ+„$·Æ±EǧK£M,Íðqs‡Ñ•Å)d2'¨q­X®c:`ÀªfI†Ëç¦`ÝšÕ¸Ú—Cçqj›QÈ8ÎmæØìüÀa´8{Ê‚5'wÁt BPiŸ>9É S;޹ ~vÓ\4ĹÀ°„§aV:r#dd̰\Çf¤Õûw0†ˆå:¶£[dpÔñø}@èÉû«—¯VQ«Õþëléöúýžîž4?O!0ýT8ç¶) C<}üiš¢Z­âÙ‹ç°r^z{{%ìïÛ~!²"8ã¼ÐÈ¢ˆš©va ¬CpN@ê‚€XX z« ¡ÃS®ƒ µ‘pnßhÔ_o4ðuç›só„”1c~©¤¿"IIÁâQQsì[¨,fjoLµvÜïõŠŽ}/¸÷3 [µ‰ ‚kÐʃ‡HyªoRO58@ë~ð;BÌbð4¯T°»»[w¶»oNÏÌ <Øo!‡ßn·QQíó/¥ Wß(M]3Æ °\Š(ٟ餻P—ÿïâ€IEND®B`‚boats-201908/images/filesaveas.png000066400000000000000000000023211353032755300170300ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sBIT|dˆ pHYsvv}Õ‚ÌtEXtSoftwarewww.inkscape.org›î<NIDATxÚµ”mlEÇ{¯¥Ò* ¨ô–J[l ©­b­@LK‚/ˆ‘Z“*ØŠZ¬–bñ%! /‘Ô@BP£ 1AH š U±FPЄ¤- ´\KKz×wÛ»Ù»uç6ÙÞYùàÿ—çf63ó›ÿ>³Ï8t]çÿ`ieUO ȱÙm¬}s=N£%~C›((Jì Ö7[…ÐØ¹m ÑH”¼¼¼¾Ÿ–Õ`NCC]ÝÝ”‚c~©\ ]’ะ 0¶b3Àv…ee³þÉäb{#yiž:!( ‚W¯¦âôÒ­ ™çDÇ.ó1Ô°Æ÷g{ˆW°ç+žÎoãÙM¿_&<Ó€?žŠíò\D$*¡¦k]Op,L°ÑWC çdYÐþÎïŒüúøåÏ ÓRl«…Ðm0XZC$¬GoáX‹èŒ…g;¾ T¥mcÏaÁ±S†£Ð-o9ð+¹9_ÎU¹òÀ$Øäèñ`ù Æ`9ž=•13ûi?íåÓÃ:#ÞëØà&uJ‡.=ÉýÅ÷qgzV,QœèXæE¥„ˆ »‘ð(3‚»èìê¢y¯ß[êläfºØ×±„»s¦3ëÞ¢Øyè€&™¬xÇf*¤65®A!ÊÆÕI\óÿĪ-“ñú‚¬ªòR>ÏEÝÎF~&o濞l%A&+Þ±•Š˜Ö,sÏBVÔ^⦪²dÞuË]Ô2™nÂüL0×$Ê28Áñ"R±¸š•µû¸âÌšÖËæZïH¢ã<4§ÓiΟ¨‰"+/;­Ÿú—Ÿ¡ný~þêLK¾NK£/OÞÁéËSiÞð•••–³Ú—jñûG͵™YHEâ D~*S’Ùón1M|ÁguR\£ìm süŒƒ]_ÃÒ¥%”””0<aËsBIT|dˆ pHYs2™2™hœåùtEXtSoftwarewww.inkscape.org›î<mIDATxœíy˜ÕÕÆ·×™žf†™F$aÍbŒD6õqÃÜY4"â…Ä`Íf¢Ñh—îA~nì¸ aQ ‘ ‹ Ã6[uŸïªjª‡aèîé¥z¨÷yú©®[·ªî­sÎ{·sïU"B¼PJ ¬òòóë×äPVæ”åG½óÆ~™7AÏVéK§‹8¡”r3<€??짬,îÇ9Hš"± À#Gx9›˜9H)Tù(@«V.ìKÕÉŠ{J)<û³ŸV­Tã79°-â)&gŒáæâ¡îĦÈAJS ”ê¬ÅÅŠ ë³(,<õÛ‰ª“÷*êSÿ£x),t¨?ÓK0èpÅån.½Ä¡þæ€¨Š£ÃgÓºµbÃzEEÇ£~;Qu²âžE€Aý9~ØCQ‘CýÍÑ7g æbØeNo_sB£E€Rª#°È)*‚ ë}´n íØ…ª“·õ©ÿ‘?y á;hNhŒÏÇ.»ÔÅåÃêoŽh°PJu@§þÜÂBذÞKqq)Ò¶q›a`¡þ\€‡rS\\?–ƒæ‚†xýÇÀ@€K.VŒ¸Â)÷›+öí«‰T¥T9ð[€‚xô§Üo®xë­ÿÑ«Ïì£à< êq…¢¤$õ s\hZˆ)SßgÀ 9|ñÅ<õ®¿ r}Lè×W3Ú)š 6o®äÊ«ç³rå.3H‹`Y\T‰À7…˜5;~§QöÁSÿø”>ýž³  pæQ…¼ˆ,.ªC!5&ÈsχR—R ž}5\1âMF^ÄÁƒufð, ˆ,o°–'" KÚ`®¹.Èœ—%È4¼õÖz÷}ž^üT \%"׊Èh¤'PD^†uš#¯Ò˜÷Š£™M 1uÚJ šÇ¶mÍà÷ЭþkÜFÛy"2 huu0üŠ:^ÃQ;cóæýü°ÿ\PH‚Àt ¿ˆl­ÿ¸ }™\kkáÒau,Xè(ñ™ŸÑ÷Ô9¬X±Û ÚŠ.ø_ŠH°¡{¢êé‘ç€Ñ@¨º†^RËÒeŽØûöÕ2bäb®µŒÂ½gÑ)ÿ½Æîº«ODf7RU\TÃ;ï:Jn¼ýö.z÷}™ç_Øb®‘+E¤òx÷ÇÔ×+"£;‡rèœwA ËW8JhZˆi¿XÍY_³Vô–£[ý¬hŸsg¿ˆ< üàÀáÜóªùp•£©Ä¦Mûùaÿ×¹çÞµfE/Üœ)"[¿;qöˆÈƒÀd€ÊJáìs«X³ÖQ‚d#~ÿÀÇôî;—+ö˜ÁÛ€³Ddšˆh±>3®É¡á›•š‚®y)–,Ê¡Gwg}€d8„|òÉ~F}ŸåËÂx¸IDö'š4Þ+"÷w|õ•0hÈ!>ýÔa‚DBÓ„ûîÿˆ¾§¾jþ.à2ÑáC ü¥î~ЦbÙâ<ºt1™Òo©ÉŠ›\X¿¾’Qc–³jÕ7ÖÏ=¨‘ˆÀx‘¹xàË/…C²e‹Ãñ¢®.Äô»?âÔï¼aþÿ€ EäšD Äá‡)õ'à€òrË· ¼<Ú)dv±êXâ&žV¯ÞǨ1+Y·.‚ÙŸn‹¦]+íóu+ðW€Ï?1pÈ~¶ow˜ ÔÔ„˜2u=ß;}UøÛ€sDdL2„ fÀêU<  K7Ë· M›ú­»Zu,qóì+÷2zìlذßüŒ<ÜaÛ& ÷ú]£ÆOlÚdÐÙûÙ½Ûa‚ú¨ª 2ùŽuœqæb«ð7Eäæd ’Àá+åž.èÑÝÍ’Eù5Æ4rÍŽqãö;ï~ØVñÙgánÜð0EDGñ‰‚¤ù}ÃW/|ôq!çT²wï‰ícxèPЉëøÑ€·¬Â߈Þ;1•‡$2@øJy—ÐM9íT çвeC­k> °déWŒ½q [¶2?Oø=p—ˆTÇðY†¤Ïü‘:`ð&À‡«4Î>w/••'8 1nüZ y×*ü€ÓEäŽt RÀá)• Ì|ç4/óß( ?ßZ'€dX^òâ?þü_qÃײm[•ù)ê€û{D¤6Öï˜h¤L”R~à_À¹§êeÁ›…%€ô 5–¸ÇŽ_Yä¶IxâÉ/¬ŸàßÀ(Yë·KR:ùODj€‹×>\UÇ೿fïÞæÕDü¿WwÓ­ç2«ðk©Àwí$|H1„_ª3ÁKèsé××Ë‚7[SPiÝÆ‘ñ¿ùF£bâ'Ì~z‡5»+Ñ"òq¼ß+™HËô_ƒ .^Xýï:Ÿ½‡o¾É\&xéå]tëù¶UøÕÀOØUø&¿\)0£‰Ø§·—…ó‹),Ì”ncÅž=µÜrëF^xq§5kïcD䳦~£d#­ µàË€WÖ¬­cÐÝ|ýuf0ÁsÏï¤[Ï÷¬Â?T?ÊáCš œ ^DŸ”J¯ž^-(µm·ñÎuŒÿ)ÿšážµÝêÿ›’"Øb ƒ †sÖ­¯cààìÙÓàd–´bæ¬tëù¾Uø€›€A™&|° ˜0º_@o*Ò£»E J).önؾ½–ûŒ×^ÿÚšä7D$¢±ŸI°˜0º/'<€TËÀÁ;Ù½;½Lð÷Ç¿¤Gï¬Â߇ޡsn& lÆ&”Rà9ô "ݺùX¼ -%%V&€d3ÀÆÕŒÿ–,pÏšŒ‘ˆÆ~¦ÂV `˜à0ø'À† µ ¼ƒ;SÃ55!îš¾ÞýV[…ÿ5úâ C›‹ðÁ¦ `Â`‚gЪà[ßò±da¥¥æÚV‰g€…‹*¹ù–-lÚ¼ÁHÃDÙM3ƒ­ÂJð4†gQ×®º´iÓPÅ0~صKã¶I[yæÙ¯¬¯ÿ:Ý/Lj&Ó[VÅÁUèӠظ±–³mgÇŽ˜§Á5ˆPûË.¾Õ}UøµÀ¯€žÍYø `Âð1œ^7 KKžD»v^âe€µëª¸iÜV–¯8h}Õ2ôùvŸ&?W‰…R*½?¥=ð9ðO9Øè=™¢V‚Yèëѹ³¥‹ÚG¡Dœ:$Ü5}>´ M çÿ+`²ˆ<•ªü$ J©c€+0öw0𤈌nôÞLR+ÁLàJ€N¼,]TNY™h`î¼J&T|Á_„qx ]ø½£lÿ°¾µ+ÕYÜîûÄëÝ!^¯ˆÇ£ÿLPêAkükN°(Á“GÓ#‚Þ™3£Âk×PjÐüÎÈô»Åí&^ïRñùBâó‰x½Ò€:™÷}mñ–öŒ'X ž°|¼èKÚyÓ¶ã¤û;F]¦&ÒÚ Äã¹Cüþm’•%â÷‹ø|rLp¹æZ•~Z´ïϸf`cPJ¹Ð'ž”sDäÐqnI Œîíaèë)œn½æruÇã¹·û S<º±ù_ÿ Dd èΨíE$Â]éX¨¿cHFCDBè•<[B)U„¾)×8 Ý‘+.Üîóñz+p¹G„ DÖšÂx:ZáC3S»B)ÕÝÚGYGÂ[àñŒÂë€R€ØoB_¶1Œ¯Á´5§"ÀN0z,‡¢{ ÷·^s¹ºàõNÀë½È§wóØðµÝhZ{ô*‹Ddp,ét ÁPJµBïž”[¯yßX”jÕdKo"ÏbàÁX…Ä¥T}‰ÀÉÖkO²²*ðz‡î&Y|ýcd˜ i½ùt/å²xš½Äcöôò½ðH¸Ÿo$YY¸Ý}€¦—íÇC(4Ï>Àßâíóp (¥:·×Ùf¸ËUJVÖ-de݈®‰±ôc­ÿ5í»ˆ|zÇOG‰˜-hJ©ï¡ï‹p ÿI·»+À$üþk?|‹·Bd)|€Çã>8 pŒ•NÏGŸÛ¦õš×{Àdüþ‹ŽjÆ¥âhþ¯«€¾Á+ÐYD>/·„aÌP¾˜D„‹•‹¬¬¡“ñzOO©¥7‘÷LáÌnŠðÁQ”R-Ñg÷VmŽ„g‘}-Àíx<§¤]ð&‚Á{Í¿!ôÕÆš„V”R'¡o~uf,àr“s3À\®bÛ@d ¡ÐkæéÙØÔgžp  ”ê…Nó#¯îvw 7w"À”ʱ•àMƒ3¬§3Ž/œ0  ”„^£?Çîõö#/o2ÙÙñvÜØ " …昧¯ŠÈšD<·Y+€Åñb2ÐÏz-+ëòò&ã÷Ç>þžƒ÷£û@‚¬š©5úQèYu8î%A‹“ðz{E4­ì ‘Ï g›§Kå8ûÇ‚f¥ÆzÄ7 ·áÃ7.W¹¹7—÷<ž“2BèVhÚoЛüÜÛHÔ˜Ñ,:‚”R9ènV“€3Üí.¦eËÛÈͽ —«e Nö9†B;©©éˆÞãË"òÝÆ¿FlÈhPJµ@œ™™ánw[òó'“—w#J2Îâ­À>$Øú!CÀðº©0~ùf¸ÇÓžüü;È˃Rþ&¸YÙã(²—êêöÀAÐ÷èϘcÈ(0†coCŽ wÞx½'ÓªÕäæ^‡RÞŒ¶x+‚Á‡0„p_¢…ÂJ©Rô¦ÜMXÜ­|¾®L!7÷Jâu¾°ƒ¥7|›9)ÛËØŠ”R]€Ÿ£ˆӖý}Cðç™ÑyÓhÚX|<~—,áƒMÀp©ž‚¾œÛ úST4•@`\' û5ªª:c óï:H÷L+(}>Ô/Ñ×û »\åä ¦uëidgëjl £)ƒ¦=ÅÇãÉ>¤‰”Rm_ »V‡‡dssÏ£¸xYYßRÕÖNþ;¢?†¨ªêF(´ (—$ín"¥  ”*îDoLJkõ¹¹çPRr/YY§'–Å[¡i/šÂx8Ù‡1€±€áíè8áœ@à JKgDR}*ö°|ªªz } úÖ3D$bÝÚd © `ŒÎGoÒ…'Rdg÷¥´ôrsÏN\‹·BÓæ˜Âx$‡$1€±óÇ`ÐÖ ÷û»RZú+Z¶F¢ÚñM=Ú! >Ü›Ph=èÖß1–Eš‚„2€1YòJ`:–9s>_9¥¥w‘Ÿ-JÙ×í*]д—Má<š*áC@)u1ú Û=Ì0§„ÒÒ©ÞˆR>›X›Ý@8|¸/¡ÐZ€ÃèÖŸ²} šÌJ©ÁèãÔaG·»%%?¥uë[q¹2{<>Ùдy¦ðK¥ð¡  ”ú>ºsâ3ÌåÊ¥¤¤‚ââɸÝ-ÁGÚڻͿUÀoSýþ˜@)Õ=¡ óS\<Ž6mîÄí¶×d ;CÓ^!Zmžþ¥)“<ãEÔ  ”*în6ïSÊCQÑõ´mû ¼Þ“§I ,Ö_ ü&i8®Mº›Ñ…ßÊ ÏÏ?Ÿöíÿ€ßßp+4íU‚ÁÍÓ¿‰È—éHG£  ”ºøpŠ–݃öí eËÈ:±Ábý5$`’g¼hPŒùsƒÌ0¯·˜²²»iÝz,–[q@ÓÞ \ižþ]Ò¸a„(¥JÐÛòc0†g].?¥¥´k7·»EDÛÙA|¨©™þK­•9åG÷­ÿ9–ÁšÂÂá”—ÿ¿¿£#ôAÓæ .7OŸ‘íéLG)u9ðk,sèrsO£C‡?ТÅÁ'55Ჿ¸/ItxÞ<ñûËhß~ÅÅWc®ã qдEƒïš§OŠ v÷¸Ý9´k7™²²Éèk :H,e6°~OIÉutè0¯Wµu(?9д%hÚÛæéSM]Ü)QðœrÊS€#ød£žõ'l‡¦Âö›G7hÚ[hÚ2ót¦ˆlMcr"à(@ P]¶~ Y?8 thÚ;hÚbót–ˆlIgzêÃQ€$ÃbýA’°ÀCSá(@¡iï¡i ÍÓ§Eds:ÓÓH"ª«Ã½~Aàž4&å˜p Iд•ÔÕ½iž>+"›Ò™žcÁQ€$¡ºzªù7„ Ë~ÿõ½:û•*gGIEND®B`‚boats-201908/images/icons/hicolor/16x16/000077500000000000000000000000001353032755300175415ustar00rootroot00000000000000boats-201908/images/icons/hicolor/16x16/apps/000077500000000000000000000000001353032755300205045ustar00rootroot00000000000000boats-201908/images/icons/hicolor/16x16/apps/boats.png000066400000000000000000000012071353032755300223220ustar00rootroot00000000000000‰PNG  IHDRóÿasBIT|dˆ pHYsTTÄðtEXtSoftwarewww.inkscape.org›î<IDAT8’Mha†gv÷c’naÓJ“ˆJÄÒŠÔ*ÄÆ‚4Ðä TZiS*xóPDü;xÔŠQ¨"¢ñ"^´ J±ŠÐ‹Trð"´ÔCk[TöÛí¦›¸Ut``æå}Ÿ¹ d‰cµb<®)2¬·òûŒµ»nA#ŸFãqAÿ(©­ ™´¸ý_ƒ¶MpýÆô”X©À›ÙYˆõô°vYf Á%˜›3ÍÓg><8w~*D€CƒìÞòbˆGˆøV"¾“ˆwñ=DÖµÛ—të‚ÐeìDQ’JÄÃÏ6Ô7cíªz²äó½œ×4"Ʀ—:ºÿ P”}Ɇ†³ ãÝ‚aé:‘¦‰âèÇ#y=‰ß +ËÝø™BL¨œ˜¦ó@Ÿ¾ÛöD±ú‰Î€ˆ¨ë™~UMö‡B·÷"nfœp^ ·¬Ò„mO>­¹pøÒHccß.€ à\½þù§e½¸åÖ¤HäÔ£XìêAÄfÏ‹î2ÍñW–õú±[¢Ñ ½Œ5³cÎõ/&çÏïÔë¿ÆÍd‚Ú[IEND®B`‚boats-201908/images/icons/hicolor/16x16/mimetypes/000077500000000000000000000000001353032755300215555ustar00rootroot00000000000000boats-201908/images/icons/hicolor/16x16/mimetypes/application-x-boats.png000066400000000000000000000011641353032755300261430ustar00rootroot00000000000000‰PNG  IHDRóÿasBIT|dˆ pHYs±±a˜(tEXtSoftwarewww.inkscape.org›î<ñIDAT8•“½kSaÆçÜŒmB/¢6ˆ;ˆN‚»mwGÿ[Š‹ƒCÿg;wk§NûtIQ¡(5܆|h$ä³ñ½÷on“6¾px?Ÿçœç9¼bfˆHXâÿFßÌÂL²)¶Z­óØ UEU‘™ÒYUÉ--•€ï:¡‹íßéD„ßã?Ô©óIxž¦'h’=Žcjµ2^æ‹·_¡ª7 TÌD‚Š ªüjʬ­yœ=ceå>2E®$èT úMDÞS*Õ ŸÕÕ—xž‡7@¯E„vûÅbZí¹Ük—S#oJ˜8œh :ärMªÕGd³o)îÎ5÷J‚ꌄ|Þ'>°»û¦¥sM”‰"´Û}ö÷›–ØØÈâûù´t3ÃÌn€G£1{{mNOãûììÜK[†!QÍ÷@D03ªt:OÉdºloGøþ2q3 h4˜õzã3P›õ899§ßÂÂB‹­­.››pÎÅb‘jµÚZ_ñÜÌ\ª x8l8ÚÑÑ' ‚–EQdq[†Öív­R©X¹\næóùÂÄ3»"]^Úx<6çœ9çRçœõz=;>>þd§Áf†$ßù0¿Ñ‡f]¿øªûçÐÎúcIEND®B`‚boats-201908/images/icons/hicolor/32x32/000077500000000000000000000000001353032755300175355ustar00rootroot00000000000000boats-201908/images/icons/hicolor/32x32/apps/000077500000000000000000000000001353032755300205005ustar00rootroot00000000000000boats-201908/images/icons/hicolor/32x32/apps/boats.png000066400000000000000000000023401353032755300223150ustar00rootroot00000000000000‰PNG  IHDR szzôsBIT|dˆ pHYs ¨ ¨·N ÖtEXtSoftwarewww.inkscape.org›î<]IDATX…½—mL[UÇŸçô¾´·-( ª´ö2+ 0‡{Q£ ›¨|R?l™‹&êfLäƒ:‡(É’%¾f¢Ë4ƒ/!š™ìÅ-nè  ¸L…!Œ0憡+M)½÷>~jyo 쟜ܓç<çüÏ9÷ÞÜ‹D¥§³ò];¹Æ†·Vtðï5Þ~T ç‰Eõ1€ˆIk‹XçùŸ%Ç%bš‹Tï°ã'‡ù\Žƒ[*`µbyEn)(`l© +-!ÿ¸Ôéásy~9Ûÿ|z¤×Ï@8‚KgÏQÖý[A¸Uƒ TVýxõ\ëèGHD€ˆœÓ‰Ç [J7òüjî@OO€žxêÔåÿ.Ÿ/ÔʈHñziûÓ•S­¿œW•ÕªüãFïÄöG~ø©«kì>Ÿ/Ô ÿ?†ˆ(8ìÔçGŠ×‰º•ÚIx¦ªm°¿oâÐЕÀAŠ20 ¡—ìtÓIÅ……"[€ªê ®Þ;~ô³AOo¯'ÎÞ•9Ó’ìÔiþÒ²ÖíÖ³D::&´šÝþ¾|u4Ø¼Ð±Ì 0 a’eÝ™æ¦ÔB·ÛÀböîë½~âäØ o_ šˆ&2"Z°€)Oæ<]™*)v"E&R\DŠ›H)"RЉ” DJ)‘²•¾k)åç›þHJâJ[w†Ç’ FYæ=¿ud« \YO[GCp±šÇð„“oÿÕs‡:àÃ÷í5k¤v¸+㸦!$Ù)´u¶ÛURÜÔÛí¢Í›ÌWrïŸ} ®@nÈñÄ p:…¶=»SÇóòôÇ =†9zÄÛŸCÌ¿ÈØ«7ó€«%!€„(âÆòÒË=ÄXIŸ |E•xž±j$ú¸âˆÁØ¥ÓÉßòücƒ’Ô¡D‘(ñü5pKèXª!šË8ÎÕª×ïµX†Éb!2›‰¢{v·¬ïDtºìjƬ5&ÓëÙz}õmš&€¦Í~ߌÑé‹DÔOS9ÎQ+Š›v˜L/æˆâ£¢¦áÓh©êþëD=/ÌŽÇ€ˆ©<﨓¤²‡-–6Ž+DM›[ílƒ¦ï& w%€ˆ)<ï¨3·mKI©·ñ|ALÆ)JÝ ¢þ—æ[“Á~Àh|¨,-­>GŠX<ÆD~дï½DÁö˜Ñ"ö7M¦wddÔçˆâ½qG¿ó·¦ í[h|"ZDÑþ†Ùü@yfæ[9z}qÂÆDPÕoú‰nž]™(ÚöšÍ¥5ÙÙ 6ƒaý²Œ# ‡Ž ½²XÇqI›%Éõ^VV­=9ùIÃJ!nº¬i¾“‹ddT~e³í·H+bÑÔÔ»7‰Fj—ÊÃÒR¢HÕ+ÑT€(>ߺßåBÁR«ò/89ù_Óþz-–ܪÝûG<®nIEND®B`‚boats-201908/images/icons/hicolor/32x32/mimetypes/000077500000000000000000000000001353032755300215515ustar00rootroot00000000000000boats-201908/images/icons/hicolor/32x32/mimetypes/application-x-boats.png000066400000000000000000000024121353032755300261340ustar00rootroot00000000000000‰PNG  IHDR szzôsBIT|dˆ pHYsvv}Õ‚ÌtEXtSoftwarewww.inkscape.org›î<‡IDATX…µ—OhwÇ?¿Ùlb6ÉnMbJM°$kÄ^ )M¯‚, EŠ˜ÐKPèEŠà¥ž{íEh¡¥'A„ˆ‚ÐcNµi¨”HÜÝ9ĘYM²Iܸ»™™_;3™¿»)Å™}ûÛ÷ý¾ï{¿ßÌ )%Ž !@áýš%¥´\L‡€"ß# ¨ÛRÊ&@—çË$ÝÙÝýÃÐuáx‹¡ûhÅì¸]€›Hxâ^K$¤z{¿þBÐmèºa€Û®(@.¡À5\ø¯Ý^þ]Í žJE j®ß¯’c¡^Ç !P"î}WEñÅ*Ûe––~oÅmZHa3UìÅ>v…íÚ €uvvÞcpðJUbZ „$? Cß§\~̱ÿ!;™¦\îãøñÏB­jK@Q,Ë M~'©¦=¡«ë!“&È4 ¨ÕN24”ŽÜ)±mB„I8‰<÷…Âod³I&ÓÀ oÞ( }î¶2Ž@ôcD*¾bñg²Ù»twÖu‹Zm†£‘;©=6 Aõê1¿ÒÓãO¡ªŒ‹¬-hát¶×ìí­ÓßÿGޏG9RB¡0ȉ—"«=”‡¨Tn22²áûíó瓌ŽÞ$•ˆÍÕYvn'yýz™‘‘'´ ë°²ò%SSß‘H$B í,|tRAFG?euõGà{ªÕö÷opæL.”ã09„vÀøø×T*?ÑhüÂ;Y––^ÆJ'$¯ÜQ$œ„{{u=çâÅ4…‚ÂôôX¬‚íT‰<ŠÝ T`Y¨Ü¿?@±8ÁÛ·W¯nÇx߸¢,reDõOŸ¾âÞ=RiŠÝÝÖú³gKäó>p)¥Ïÿ»R¼DØ–—5îÞíg{û˜»´§§ÊíÛ_b˲|î%µC¢À?8šVaaAÁ²}ÕÌΖ™žžtcÍf˲h6›î½CÂ& Ó†ˆQ¿TïÞÕYX¨bŸøÖŒ—¸~ýd¨rÓ4ÑuÃ00MÓŽK¤´xöìÙŸÀ`´%àMúðáKÓ¾øðð*·n¥I&»Bà†a°¹¹ÉÆFë”ìëë#™LR*•^äóùoÍy#nK@Áâ¢J½~Ú+pãÆ0ÃÃi7.¥teo4hšÆÌÌ µZõõuVVVÖòùü, º§m ö÷»ÝÏRšd³E®\'•êu×8Cæ0M“fÓ¢^¯£iªªnÎÍÍÍ+@]öeôIhÛùó‘ÉQ”2.¬qíÚ)R©^ßr)¥oê Ã`` MÓ(•J[óóóßT«Õ¿ZüÿŒzsõFc)[/Š‚”’jµF&Ó: ·jgð¶¶¶°, UU×._¾Úý×ÞúHhºE&âÊ•~úô}§½š;~¥ë¡ûtŒ¬Xúà =ø³ÃfûÁC‘ðt £×ÍeüVïèð®ÑõÐs"DDÒèAþÃÌüðGáË‹Pº}›E?ÝÔáñió3­þ Í]7ð ·G_³g褺†ƒÁ®×^™S'u,ï¼ÛNóîýïì¹ÎáÏÄ‹'Š«@ ½Ì¼t÷‹Á÷-aÖ¦§ó§ôápçN6nzß×Òr§K×COø{¢± …è2 ÔmÛ1Ô{ò`47RÇòÖéÛ˜_ý{_ssÏu=T=‘x¢¸ÏBã`ž£i|ùè/´¢µk 8W²îî0mØØæ»ÔÚß®ëáU>JÆð¤­à߆6o1 D>Dêð§–TUåN‡ÔBDeéÞs\UhªÐ{âØ,s<=Ÿ,ÆÚ53|š&vÑW2Kä&¢¹Y33¡ªBÏñ¦ÙfÌ@$¸?}µ4b³‰†ª /Q^ù­D´žÈñQA€ÈúIV ߤ\U…ž¦ÆÙæŸ/ºáv+ƒ‡ô"š“I‹iDy^Q\®KÒ{„a"{SÖ ÄLX­B·]:ˆè‹äy˜¹ð,s¡a±¼*(èE~> (C rz‰¨"'†o®‘”FœDDë˜×E±JWÕ7£G6`µùù€$1‰œ¿›Miw ‚²›YíS”/èNg3ŠŠ€ÂBÀáÀ(&˜gD´8>Ç´|aaæ*AÐv1[×|ƒUuG T‘iEÇy²˜æ;>ðÁ˜\Ã31%0ó£¢èØK$ÔÚíÛ-š¶E&rŽ?ÆÞûýÕpm%€ ñ9s^f–‰h­ Ø÷)ʼ¢3öÙ¬ÖÕÊ#"'Â4ÏÐÝ5žx¢`f— ([A}Þj}P,*ÚcËÏ$a›$"ª7ˆö$ºžuÌ\!ŠÚ>A(Xår=ÉÅÅ; $©2©ÙŽÇ4Û¸ê#¢wÉšfž#ŠŽƒ¢h_YR²S).Þ*9Fú8‚Áý^`耄Ö36ÀÌ¥¢hkEÛeeÛ•™3¿'3« '"ŠFo‘iž…O4.mÌ\"ŠöI²­.+Û®””<'3k)÷x"—‡€è«êHueæY²l?`]yù÷•ÒÒçeA°%ÜÓ9÷’®Ïõ¾O˜HOÒ`æ{dÙ¾_’l߬¨ø®\^¾Ma¶§µ8'#øIOL&ž(¹ŸUŠeÙVз*+¿-WVîPÁ‘µÙŽ?gšýäñ”ùß<“HXf.’eÇ>I²mp»·Ên÷NE’œ/ÎÉðù^ ©dÄSfvʲ­žÝîg•êê]Š$æd¶ãÏ…Ã}Ô×Wî|n]É©3 ‚ o’$õ·{C^MÍ^‹$¹rÒã‰ðù^âÉdÅ `æ:E±Ÿ,,\8û¡‡UUŸóV‰'í¥¡¡_šD¾ý©ÄI‹óL~þ=Ë—-;b-+[9åÂcÆ¡ ÀMzR‰“-úÎW—,Ù-åMY«Äcš=ä÷7Fˆü ©ÆJµµäéšõƒƒ @< àNª±Óþ’iv‘ß H-‘òU©”ðÒ*U‚šJEr(Qcã¾|ecïÚ²g/³3§³»žÛ®7n•O^ÍÌ93g~ÿïûÎÅ3#¤”¸M!<…_“>°Â]&„ˆU€ |]„H d¤”¦³Buä<_ ì¶Úÿ‹°„@˜BèÎH¨®ð8Рëú¿’©4Bä  Öù¿Ü•ëÁÊg '|®Ìô»ÖÏ4U¥ªªê‡À ÄŽˆ¯€|Ym2•&™L"DÚ) (Âܧ¬\ˆ\M­¯Ÿ€Â}ðA|· Ͼ ¶T”¼:Ê€@z¿"ðˆp³„D'P_Ñû%„ˆ0M“H$RBV Nx¿œ¯•ZøbÎ)Ä<~hh–wß]âÆ$‡Y[ƒW^ùœúú=›]¶OÚ”¢ë)Þzk’>ØÉêênÒi»nëÖ\º´»,ø€G• Ê¿oßžâw,>lƲŠëNœø’;v‚H) à›'À í8v7jš&×®ÝãÎý$“užs¶náÒ¥]¾yp··ˆ°'e¥PAˆËû†‘åêÕ&&Žx…Hiòê«‹44´„‚[–å+&ßNÅ3±;…œ†Á›oÞc~þˆnûücÇú¸p¡Í>mYVáWB€7·Âø qØÛoH´^NóÚkÏxàà¦iÁç·ùsLì'Ô¦ë6¥û@‘œ7nܸÇÒRkà¥Rštu=bÿþ6GÙºwóàî­ógšBÒé”ìííýR¸"QÖDæ´ññ9ŸFQÖ/uçè¡CŸráÂQ—(é41 ƒtÚ~ žÍfÉd2d³Y„¢P¥i†ÁÀÀÀ­óçÏÿ˜’î·4šÈ’ɼ‚¦5a~£ŠÅÑ£}\¹ÒZ$ÊÝaMÓÄ4MR©sssÄãq4M#²¥¾ž*M#‘H0<<ÜwöìÙßcÀö[š"+ë1@>}>üpMkÃ0¼çH™æøñ^~ù°Oôä½išèºÎââ"Ç@×uâñ8ñxœ±±±‰S§N]îc§á÷ެì瓬­5zÅ +œ<9Ë /´ÊÜð~8NÆød2Y€ðàÁܹsç.£À#ìwc¾£PéåtÎöí{)‡=åª:NW×—<ÿü_qAÃf6›%•JF ð‰D‚™™™øÅ‹/ëº>Ì© ø ¨©©æÅ÷“ÍÞÀ4“ìÜù==[8vloàLëÜwŽõyÛ·o'‘HÇ™÷ôôüfaa¡˜-˜‰jk£¼ôÒ!Þ{ïSÚÛŸ £ãpÉU¥{†Í 1 ˲¨©©)¤MwwwO~Ð¥”Vhã%ø˜¦ituygß x¿2KJ à®®ŽD"ÁØØØÄ™3g~­ëúvڔؓEQÐ4áááþÎÎÎËØ£Í.6Ð6ÛEappð?¿†€yìœ/ü#`™H$âÿðÊe¥ê„(Š‚ªª!ˆD"(ŠbÞ¼yó£Ó§O¿L`OR¾ã|)+úVBØßžžÆþfbsþ÷[7‰½¦™f€e [ <ø P±?7¨æ“b{U™ÄbV x¿V‚Íö¼ÛäWÏÛ•Å8úì™äIEND®B`‚boats-201908/images/laylines.png000066400000000000000000000007171353032755300165350ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sBIT|dˆ pHYs²Žø±tEXtSoftwarewww.inkscape.org›î<LIDAT8Å•Ï+„Q†ŸwŒ%„¦‘fE¶¶b!5 »±coeckgE)ÿˆ²U”¤ØZ±‘RC‘ÿÀkákLŸ;ßý¾iäÔYÜsÞót:Ýs¯lóVÊ+”´$i%·>oÇ’NAÛõ^ƒK¶?{ .jÑKjHm;KjDɶ;:PÎR1ÀdVmæ($ïÀp`û9‰¯uÛ»Ýv<•tg`#•«fÕfÎØöK å^³jƒ`I󒦳 ]MÒln0pôÇÀÀ0p˜ ,©ÜÙ~ŠQmßo’Ó¹r@ÿìG{ý±=`$ ¶ýPŠí&ÐLÇ[£4 ©ZÚn’*’ú~m`½[0° lµNÉe®€r`IÎù¾Ë·ÀIÊÚtCÀ5ßOk <,wؾËò”v ¨Eß Is@p €ÛÁºÿóŠÚónüŽý?æIEND®B`‚boats-201908/images/pdf.png000066400000000000000000000016061353032755300154640ustar00rootroot00000000000000‰PNG  IHDRójœ sBITÛáOà pHYsvv}Õ‚ÌtEXtSoftwarewww.inkscape.org›î<•PLTEÿÿÿ!!!999………†††‹‹‹Œ’’’îîîðððÒÒÒÏÏÏÏÒÒÓÓ33ÓÓÓÔssÕ««ÖÖÖÙ99Ú››ÛÛÛܪªÜ··Ý™™ÝÝÝÞ,,Þ¼¼ßLLà22áCCáNNá™™â;;âqqâ››ãQQãÒÒäWWåJJå]]æccæeeç00çUUçèèèèèééêêêêêêëëëuu눈뫫봴뼼ëëëëììììììííí~~í€€í‰‰í¤¤íííîççîííîîîîïïïï¡¡ïÀÀïïïïððð‰‰ððððñññ··ñññò¦¦òääòòòòóóóÞÞóììóóóóôôô³³ôØØôôôôõõõÍÍõóóõõõõöööÏÏöòòöööö÷÷÷ßß÷÷÷÷øøøÖÖøëëø÷÷øøøøøùøùùùààùååùõõùùùùúúúððúøøúúúúûûûïïûôôûûûüüüýýýþþþÿÿÿPŠoþtRNS#&MS\l¦¦±±ºøùþ>ôz#BIDATxÚmÑWWÂ0†ap âVÜ{ï½Á u à[ÑVQ‹ 5¢ š6þn)'õÂÃ{ùœïî³Xsfp‘ý_ŃíZü¡†§S„½,?Ë‘ç27Ñåò Á€(I ÆQ§›kÙßë\QŽDRŒ›ó÷ՉݧW±Xš±Êùƒ¡öæa mò!q<£’<Öxù€OÆ&CC£êhïÖ9ï½]LÚ:Ê•ÎêêüÎ_çüŽà>oñåì4˜Y¬Íƒl€5›nÚl,ÄoÅ'Ož¼g®¸ô1àg`!¨!Ü&aõ:°"LúöÁä÷A O:uðâË+ó ^A¹Ï ù‰™©²|`g–}Sr®æj»È× ÃV³iÚ½ 0iðk‡¿y4þã‡a¦w 8ÿ}Äëx壇Éñ“#%ì¶T'JîÆÍè R®mVå»ËaáýÏ:?бû²÷ì¯öš?Ÿ’ÿQ|[éëÅrùÇO©È§=&K ! ŠS¬µ(¥PJ"Å8Í5ÿ¸8âÔ{mÛhu¯xºýҞባ(Òÿ¥Øþ¯tô‡‹žúž‹ë6¶|tâ¹’\Æ%J4Çõp XŽìÍ‘¤“âÄßÌÂh ^(V2™ÌPÕ~ò˜P™W¿;_Î]t‘¢8¦’Õìyh‚]ÕÕ&Ë&Ë7[ qbKœZ&‹?vXk&s[z÷ʬº|]-þ✠¼R*•¾ÿƒC±£A2[±ìÛ3MÆsR ”D)É•›£È%ョb®Õç‡i¢ÒÞû0 *—+r"›Òé[œ0Ìצšå¯†tšR^bŒåæVB>;îu½—rn½BÇÏ3 ( Ýý}Q«9À,BÖ¦J‚ 6dËÜLGIºÃ”w>jÆ€Fw슬J™™,sd¡HÖ“¼ø&4}Àq@¨Y”Ws€<2SÈ» ]ßà9–êäxn4cú¾ÆØñxwx=,Ì0Uvx¨ZܾmB€Ìä„¡t×ýÐ¥Þ³ä<îö´=’|¼Z J%G¿Óå…'¿½ fŒBÜ69 0Ò`¢QÝÏáë,Õ’¡Þ‰Ù]Í1UvС>Rô“äž«ßõSâT!ÄØ~è06$ÖlbõzodèEYÖ:ÿ¼’ åÑ]¿~Fs`—åÂFŽ«7zÛÀ¡Æ‹+!Ž#°º!¿)Ý´ÙVˆÚÆU‹âÌ…,W7S„€ùÙ ¿{ÞðÛŸ&ÌïªlW<Ô`5ž£qË8„_NØÕ ¹¿ “¾­£^ÏÕ¦ó>Iò‡¿|±DÏu˜ÚQÂZ††ÕFB¬…ÖÀPpF¸ºƒ ;~V×ÏVÔV[?ž§å[Ü#lz¸6íPΤX“ri=¤ÙKhRÖ› —o†|tÑçì¿}˜V_sa-b8ŠYßhù3Ä_N;®ÐÊŒ^ Ä·VoñøÁGŠÒsÇ_×6"®oFflg².\¯Ç¬6b¢8eõÖÐ û½Ovš‹BR°¼¼ÌS‹n«ekçz³¿?JçJy—É’ƒçJ\Gà*ç|®‚­nÂg×zl6ûç¦ÓO_­Éå[€Vw†pøða[åR»eç>í*ßì%sƒQêeTrŽ”HÖjZŸÏ¿™Kkƒa·Û?³S_xã6´$KKKÛ/ÈñãÇ%Pø\<óD*‹Ç î~)Ål)§rƒ@‡F›†CøeV×Ïîå¯cêRÊ–”2]ZZº÷i:vìårY^3~`Ú3{­Pûd¦b¥òaÓHs®nxñºoŒéFQä[kÓÓ§Oßåü˜–óMK$3IEND®B`‚boats-201908/images/player_pause.png000066400000000000000000000022471353032755300174060ustar00rootroot00000000000000‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs ?@"ÈtIMEÓ %&KXÛ94IDATxÚ•ÏoEÇ?3»³þÛqó£Á…º ªP)TEê !U(T T¤ Ä-¨= !qàÀ©ô°\z€SåDU "u•šPT©iš’8±¯kïÚ»;ÃÁmš¤IA<é«Ñ›}ó™§Ù÷fO°Ò™ftcÒ`ãcô’Ò^õŠ{¼»ÛZñè1à˜#ŠÕ€î´1ñ]`Fèè|É|uÙuÝø_Á¥33ã >ÆRïeúÒù§†rrd0ÉÁ¡&)s«ža¾ªY]YÑõ†ïëÈ¿¬ÌýÏ_4ßüâºn´#¸tfæˆs8¹É#Ϧxãh–Ãû {ó KöB °ÖŒ˜[ ¹P¸TY£ë{7ºþé æü…‡pk[¦_dr¹×ß:Ö/>x5Ås‹„²cèFfC– ålPŒìQÜ®š!//×ÌXyr"u¯\.› páèé³8ÙwO¼’oN(%éF†N¸M]M'Ôt"ƒ1†}ŠlZ‰?îŃAר}\»811Z½l¯VâìKã¹ädI!%tCÍO¹Å€¹Å€Â€¢ÝÑ\ª4™[ì0·0²G„†Œ¤ÕµYX GWâý3#ܸm—NO+Œ9•éKæJã±Ö4Z€Þ.lœÿW×w‹µá`!æú­D¶^Ëœ¬†£¿ÚÀ0p$—ë—ùd„×’»Öu£mñ¯ÏT–òx~š&©Y?ä‰bÑ YÊ ü®ÆïjÌuX™o“KmÝtz¡WÚ÷¼do¶AX,§hid¢/­BÖ[)À±wîÏ×[ü¿šYëQo!@&RB“µ7Šå†Á’M ̶œ¿ÝÇ@Êç“m›uc ñ0ÔºÓ^n¥hÅÉf‡n¯û©]Ï¿70QÇÆè%àn£­Çb‘âÿ˜mîû0qUêÖ’TÚ«3têZYg“þ‹¥ULÒŽqìÑ­i›àfÞÜY”WÜã]¡£óq§ÑPñÃé{h³å›Œå› ™^¬Š×ÐÁZ+/_ì·Vë`jjʺjM}™Ëf><<¾‡„-zõf¶]UæÑ°ù‡u"CåÖÍõÕïJ|ý0o”ËeóLéý¹NÈóZÇž´D:!H(Ó“½uL:|AÈÍO×êéÁxö\^ü= 47.¡%U«™ât÷yíh4›V dm%qlñ˜”«Ê\ƒ¥oz8ºv¶(Ë¿«®ëÆàr¹l&'Òõš½Öð­ôJ#m¶#'iG¢/ecK‰`LLm­ÅïómýçBóþúº÷ý`<{î´êºn¸ã 255%¾ ï¼ÉÌI:$¥(dSV  éÇŽuÕ&¸™Œ—/”?þ ,뻾 ›àpªñhÞÅ"–Sh)LÔ‘ºµ”7wû­Õ:Ð|×u·ôû?¥Ö$B`ÙIEND®B`‚boats-201908/images/player_play.png000066400000000000000000000023571353032755300172400ustar00rootroot00000000000000‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs ?@"ÈtIMEÓ $*[õ¦S|IDATxÚ…•Ýo”E‡Ÿ™÷c¿ºÛ--–",b©@ÝŠ?‚QS‰&¢1~Å»¸ á†ø(é ¦¯ráM/äÒ#Æ‚„P–ÔŠ ”*ín»Ý}—íîû5ãÅ–¥5´Nr2™Ì™'¿srÎÁ+bÒ½TZ'AºVs–r‹Wœ×üõÞŠ  ‡€÷A"È!¬Ê[FG÷€I¡Âsy}ö’ã8Ñÿ‚ó'&wƒ8…a}Ô‘Jf·ôddowœ==5VÄ_åî ¥’*W 6.YúÁûõøeÇqÂÇ‚ó'&w€ÃÎ îJðæÁ4û¶kžÈZ²åª¥ZÈô\ÀùB“‹SKø ÷fL•?{ZŸ;ÿnüGé×™Ìoꟾ’à©>ƒ˜eðÍOöná‡?ÔRГ1ٷâ·ËâvQ÷¸Mñì¢ÞYJÌ Ý÷<>ŠþàÈsYñÖ…mIüPãšw^ÈòÕ‹ìÚbáù /Px¡FkÍÖMé¤%þ˜º›¾¶¶rýÂÐÐP`´Ô^;$ŒØèÝ™øpÞBJðE3hA¶õÄxq ÅÉï4Ïä¼@¯Ü·"ØÔ!©û&3¥ ¿mŸìåæm#|ÂN¥Óé—^L‰®ðM3h©õÍî­qÞ{Þàô¸`ïÍ}3P؆Ïô¼²ëÍ00ÂêEØ f22qër£Òfü¤Í‡_ÂÑ.Q¤™-GLÍeqIjtHEeÀ¹œ ô!d®'-høŠ†¯Ð+å¢×)ô<ÃþÞ “§˜u[aš Œ> ;'$2–JZ•ºÂ]Vx¾¦éëö¾žòßæ³”–“šB±„€t;n·i1_•]Iµ!©4 îUb\Im˜?2‚–µ›CItå-Ï×Ô£x+~@#¨ù6Z¯Ÿï×GWµ°XiŸ¨Ù:ôL´šîU—ÕÎH$Ö<ÔÈu§Éá3¬LÓ 1³uŽÛ­=fj·f\µX®NtG7ƲâŸm0À‘¼µ¸¨sÕ†9à.‡ýé¤Å¦´‰mI.߬c›bY,T¦¦«Ì•܉ÍáõÑœ,\ljÚàB¡ ‡‡’åEݽÚ0’¥jÐ_[í¸ŠTÂÄ”)@ëˆÅ¥:¿ßYVÎÔT*îÝѱhÑqœà±?ÈÈȈRS¼{8”GÖ€”¢/0µFÔT‘*š4oÅ£ù +9*ëþ «à°‹QÖ¹†()tèIUŸËê»÷;…2PŽã¨ÕŒeóAnZÈEèIEND®B`‚boats-201908/images/player_stop.png000066400000000000000000000021421353032755300172500ustar00rootroot00000000000000‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs m„·tIMEÓ  ©ÒyAïIDATxÚ•ÛSEÇ?3çÒžnOé^¸µ,¢Ù˜‚­˜ðBŒ‰$ú€ QLôi ¼™øà“OšðNB|ãAL4&B„˜E¼,ݬˆÈ-K·»í¶§´§í9gƇ²Ë‚€‹ßä›Éo.Ÿùå7™ÁCT80m^ *‡Ö)ЀЪl)¿ò‹÷rïAkÅC Û= ·"È#¬T·Ž¯ÓBEG úð)ÏóâÿLoñ>†µ7=Ê®ÉȵÃIÆFš8VÌ¥Zš+ÅüÜœª5‚@EÁ)Kßúd‹>rÚó¼è¾àÂé agvn}Òáµm.›ŸÐ¬ÉZ²?U ͈Ëåc¥?œ] ø%TíÃgõÑc‹pãžL?Mg2¯¾±}•x÷E‡§s Ë Œ¡é%R0’1Ù¼Ábí Åß=âwÄóU½±´³èÜ(•Jz œÛ¶ÿ ¶ûÖ®²âõ¢…mIz‘¦Þ㞢*º‘FkÍú! 7e‰?oÄÞ¶Ö3u¢X,†¢Ÿí™íÂH~³å©¡ìÞ®#ùv*`%zå9‡0Ò|õkÀ÷S•†Ñyo3_7 û'-´Þ“Hf ›lb¥h´4½½æ¡ÐThwžb,óû¥„[«¦wWÂÑŸM`5°5“Y%³É¿%y}öc?HÑ$-õq_äó&CÈüˆ+zŠ §è绲 „êq±–é¦ ÂÈaØyH!)+¤ÞRH¶)V½4Ÿ¼{!@&¡pÍÅN¿c1ÛÐ\G¬¸kÄât½x9”4A¨n{¶åЊ“KƒñHµîÃ5ÄŽÐQ×D«2p½ÑVcáðdý¹tAÇ©Zei)¿LÓ­)ËÐØË¼R%ÍÛŒ½ª2é\Èê«7@qßéÂN™Ë­tíG*‚!!R°ÐìQž™i:óŒÉ“_›}øÔ™hâóf}nßèð SôŸ'}ÏS¥ï4ˬi®ÕP½ÆwcòäO@Ó(•JúñÂ;—»!Ï(oxlØ©„ aé¾Í»Û¤}'„\¸æ«j­19Ÿ;”3ç–À» Vµªó“À÷ÛѨ›²rMlKb›â_¶ ˜o„œ½Ü <çO®Ž¦æeé7`Þó¼x \*•ôÎbªVÕ£SÀHÍ5ÂÑf;²“f$SJ¤­cª -þ¸ÒVç¯5oÕëþñáøÜ¡ÛЊçyá}‰‰ œåÍ—"™Þ­°Æ¥9×1€fwT¬*& ÉxöÄíšÎõþ Ëà°+ñhÖù<†à ”:êJÕ*gõÕ›«ŒùÐÏóÔrÆ?B¸ä¿.ì¬ìIEND®B`‚boats-201908/images/redo.png000066400000000000000000000023351353032755300156440ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sBIT|dˆ pHYsvv}Õ‚ÌtEXtSoftwarewww.inkscape.org›î<ZIDATxÚµ”Ëk\UÇ¿¿û˜¹w&“ÌL&“G›ÎäÙ¼ÚÔZhE+VêB²PÑ…èκð_Tp!Нºpá¦P•(RŠ­–RE¬µ©6˜&ihš™¦ÉÌ$™ÌëÞ¹÷œã¹ÃÐ6ºèçðåpœÏùq—„؉ñÏ•ó>ëñ˜’¹RïÏ]~sfížyŠãžx'éó¤Ð‰æÎæx8Ü„Ø0ºÂI„´ÖÊë˜Î\ÅÍÌÒ‹wrÜoIÑ)9§ ÀÉz!ïÕÅ[€Oaóåý£Ý¡"z¦®¡"Šð0©.S‘uòX.­azêŠXË& '{w N ¾„ONTª‹·¤—Ûû;ö v !êóÁ…¡“ªëÃlúˆm ”XþMhØæDkwÇž¾D\¶‰;~—D#8¸`@B TìiKÊq²»·®ÔêÕ¾¢›¾z]ÈUV dóPHAÚÊ`±|©Ê2á ¢7a—ÇH°¦æP@Ñëjót½4öÙÞ€Ù‘±±HÙØDÅ)ÃÃÇÕ lVAÒìF܃97ƒŒ˜‹¾P/b¾hMÎ ÀeÂf —~½T«û˜?;FUÆ*ïWtÛTôê­Xúƒ$EýETP•_eM\™·ÿå©bÖ•R¦p;Qû ÉJFI¡‰xG„.ø¯ý¤”‚œä¤\.P2ÍÒÕU‰8~îðü]Õ œo¥™£+â>±ÇSï&"}‰`ÏHˆ~X¸È¤Ø­ËmSÕ¿¢qþã¡ipŸØƒd›¸p<I¤Ô• øZy“åAUg†¢‹ïþ# yHñ6Oþ~„Æ[;µ¹õ4ï›"WZ_^<”øQ àñ?ôƒ#û[JMGIEND®B`‚boats-201908/images/tab_new.png000066400000000000000000000010101353032755300163170ustar00rootroot00000000000000‰PNG  IHDRóÿasBIT|dˆ pHYs»»:ìãâtEXtSoftwarewww.inkscape.org›î<…IDATxÚ¥“9KA†ßÙLöˆÙ¸³D㉉b#i±ÒJÁÊÊ4ÚXçoXŠ…­ølµPñ(ÚˆˆW‰˜(Ù5{9·ˆGÌÌÁ÷Î70CÇÁÁ”În¤Ë%˜ÙtªøÄp¸ŒHj6¼ÁË‚\-ÔÎMíí²p¢mNoAÐãÙLѕ쳓&ö¢£ƒIAV‚p §œ»+Ý^©ÇÛç½»ÙlvÕï÷Ñ„a¯ªªÓJS%Ã}2ø8`k$¨>F”'+=’Lļâ/xë=t}×Ñqj‡ckŽÏÇÖ*¥¨uš_Ñ‹/÷øš« 546ßU}6!…ò° u+#NN¥3ý³Ðu‚  BÈGÀM5:°ˆÚµýáÍÁ§<@bÅ.’$ýÜ>^‚<’,ƒM¨›&¾ï ŽãÐx”‡GkÞÄ£õ«Ý€×vlJ)LÓD+¸5.„`Žâœ5~c;ÐOwrÇâ'oŸ0m¦Á¬3uOn¿PÁ|„ÐÖIEND®B`‚boats-201908/images/tab_remove.png000066400000000000000000000011501353032755300170300ustar00rootroot00000000000000‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYsvv}Õ‚ÌtIME× '4bÝ"ºõIDATxÚ¥OhAÅßìì$¤ÛnLÛ”VÓ¸¤´ J#‚xAô’£ÿצiΓ2Ÿˆ3%Iº…K$\t]ï„âS¶m¯Â1Óuçœ3Ç@† P( "+êô.ÂKé ,FÅŽœmf½__ ×ç¶Ké '#»¯ž#ûðê• ¬¦‰Ígóغ‡é¯½µKòîtì}üâÕDM²ñ¹ÚÀ`ðÆ+Ôþ!kûˆÏ¿tãw0 áp”Ò„üv=÷BØHø-c ;ØAµÉpø‰ð[ŽØ+ˆhfõ••Ù¹Û %\ìq}×nb"™tÐP(t”€sã[´XI! ZEcGGPUAC/-y4:|çÒHðñÐFŽ,ÏL[ ÖåµMÿᴆ׿› O úÞC,Öª<=~AÛýE>1R_’¼ÕªEÎÏ.Ó·ýgNŒ©­ï?>Â÷±Åâ€rrq=sïi.e9ÏMQ–ÚV¾­ÔÚõe  þ›ÐËcIî8½IEND®B`‚boats-201908/images/undo.png000066400000000000000000000023041353032755300156540ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sBIT|dˆ pHYsvv}Õ‚ÌtEXtSoftwarewww.inkscape.org›î<AIDATxÚµ‘Ih]U€¿sïy÷™3u°Ú–mmµˆ¥H-‚B[Ä\ºR.Ä…‹¢ àÆ…‹Š«.ÔVp,h•ª­mB‹¦C’¶¯ÉËôò^ÞpÇó{“NÁT©‚|p¹Üÿ;?ç*á»w•uÿKb˜Gü®Ø¬¿ì:.qdžã¹2×A}û¯¯G§âGãèÊRï·-J·6¶hšZ–Ê.eá»#T¦.R›bü¢7&FvÇ3û®®n|ä…LþÔGŒ ÌüœhhKí\qg/Nn v®+% %æPMDõSŸ>ÈÀñ<•Rø ðb|ÀÄü°lz`3J÷2>ú5"ŠÎîû°oA)BŒC4uœÂðaÎöÎ l¸ׄ*ÓA[ûv”T ãù ó"Ø(% †kø¨ì:–´Ÿ/Îû»®†#‰âPºQ\zV`|—êàI‚ñ‚É–m“hé"Õ³‚ô¢e€a»®Åk˜(ôïŒÿÏÓñÖ{ç®bÖµ}7X Ô¬àÏŒR:ÇëäšCœ´ÁBðK†ê9C¢q) kîÅJ$ õ™ýüÑWœVh#Q‚­b!r‹” ‡ÐÙˆlahÀàWCW„ø;µè6†ÎI¹ßW «·‚µšTîp³[‰¶YÄÑ0p‰"c<‰é7n) d}¥:|×8ªù Û#9µG+qŒÕ¼R'—Ëx[eµÍŽØ=™œEXÑ'µLFi b2ºÁ­€_„/EJZ¢IË ÿÐfM#sì§ÒÃqp—Ö476ÙÇfÊN·c&BJA*Ž'CG- ýàÈ‚°$³q´Yþä˜ ì[ªZ[JvÕWé¤Ã¡¤mßK½¹';"aUGp&ÄÚDU*ülÛ|¹ ŒãÄa›UÏO ÀÊÃrâÓ[ :ø¦êEM›7÷ŽÍn'A˜A!¤Êµ¢¥Û‹ùú~ö>û¦””ˆp#ôµÖ”2F„ZARyãUÜôxÅãÅRÓèX”ù=9 ¹Anð„éû~½"Œ@ÁIئ³·t»ôuýͼVæ*sÿ[Ù¤tí‚ÕÜdl¯0´?.ŒˆÈöÔ¿ŸŸÛøÿàO³mGG yIEND®B`‚boats-201908/images/video.png000066400000000000000000000023621353032755300160210ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sRGB®Îé pHYs „ „ªâcytIMEØ $Œ0ª”bKGDÿÿÿ ½§“rIDATxÚ¥•L”uÇßÏ;¸ãCÀ Â…dú‡ÂÂu©ÿ–ÆÈ6ZåV±¥dÓ¨e´m¶VÿôW[µœ8ÅMk±”ZË9täÉ`fpp¿žçû<ß>ß»cºÝQk}oï}Ÿï÷¹ïëó~>ŸÏs'ጽ¯ì[9êým›ª¨›s/twwˆù¿€ššš–¹Ý[žìïø˜s~ùÀÁc’¤~6û׳¹¹ÎjÄÆ¿ƒÕÁÁÁj‚¼éõzÏ_ºtyza>tú–÷öM¬s8Ò¤ƒ^Åý˲ÀMÙ{FB*"‘HÑÔÔÔ¶;ž¨5MóÑ‚‚•öcÇŽÂn·ãìÙïÐÚú!l©v””×n½áñx†:;¿¿ØÓsî}ÿz‚»ÂÂÂ×úúúFy|9r”;%¼ôÁ þüs/ñ±±qƒîßܳçå7lØôÃqßv.RIÅR#;;ûí¢¢~õêUøÜÜon>䯪r_\¿~Ó§yyùÏXGÊ"Y“=mÒT¬]»ö‘ââÊ^ÆBlãÆŠSßœ¸ `‚4CŠø?Á’/+k…gÊçƒæŽ:¿©ëêêú¤­­­¯¢bý­òòŠPOO/ ‡Cúñã'íqÚ㢽Ä^{ûIVY¹'¯^íÒÂZ—¯ÁÍûe«ÕªžlÿZQäT”­)Guu•¬(ªÚ~âttÏåz555ñ½¯U±Ý¥Þ›t›Ýj6í}[·RRR *W,$²¬QЍW4&ÆÀ„ Ö`$€éLô0Ó éÑ5‡ I|$uÊMá”ÀÌŠ‚uhzDÜOžã²2—”‘nEzz¨S¡ª IÌZËäÌ.£`ÝÁ9ìi6x½7°<;WõzÁÏ5žšÊ œNw¡¡a7œÎUx¿õ#ÌÎÎÑSÈT lLLÜD8„NN~—Àvì~ºA¤(y»-Ö‘é ×F®£¥¥¿ß¼…ÌÌ ê’uC æÑ"xëÐëùƒƒWÀãÈ%ÀQ ¡ƒaìÜYK•žAÿE!‚AhÔ(ðÞ}°Ùl(]ãB9æÜ¤ æRàyQiQ?úkÛ½½½?L#X:Í¢¿™®¡£ã[|ñù—((ÈÇ™3dÈ ±äÅ«¯¯—‡‡G(ÇËpë?I“XUèĤÏ‹/ñh±ŠŠK(‰@(ŒÎÎcÊÅ¥e¢[¤ññÑÄÄétª“tYQ¡ˆ–*˜¸–!ÑcÄú ±ÂŒ=ºi°X§Ðlùöí %Á1å–K°è€l !˜,îVŠsc®E?›È¡L¬µ%ÿA,k:b9¶“DPTÍ|Ñp&f!2À8—²,ùÓçY§…T‡¤¿6&IEND®B`‚boats-201908/images/zone.png000066400000000000000000000012051353032755300156610ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sBIT|dˆ pHYs²Žø±tEXtSoftwarewww.inkscape.org›î<IDAT8µÕOˆŽQÇñÏäO©‰ÂbFIÓÌFòo$i&)ì”ÊŠdmáß[“R² ÓllldÁjj„ŒAYPvÔeDÃL˜cñÞy=šç}fœº‹çžsó=÷ž'E„ÿas9RJMØu苈)¥ù¸ˆ7Â@Dü*ˆˆi mx€ ÚŠö‚ó±? {͸‘Rꊈɩò{r3Z H–¢£d]Æ(F°±ß’5zêŒqC3qÃö,ø GJüOp6"ê÷¸O«`¦”ZpKÐWKÂúsÖõŒË*Édž!ÔzÑÔ n9N”^·nçF½BsEÜQ¼MUågç l÷EÄçŠð jœë(zp­$ƒ=x80‹Ê†qªx+º3ô–BÐjµa3†K³Dö Ýõ——'Ù}|‰ˆ]Á0ÖçŒß5(}$"v°­Å‹ˆ˜,>é6<DZˆèK)½ÄâZ0kÊuá,¾_#b ¥´PíéVÙ©5uç_ž ^+±¢Âß‚»øŽãÓü{sFC¸‚ƒß6Œã!Ú+Çæ4F~M›±Im°wEÄxÆÔ‰Áˆ˜œ‘ñ¿´ßyf €IEND®B`‚boats-201908/images/zoomfit.png000066400000000000000000000012611353032755300163770ustar00rootroot00000000000000‰PNG  IHDRÄ´l;sRGB®ÎébKGDÿÿÿ ½§“ pHYs „ „ªâcytIMEØ —wŒ1IDATxÚµ”¿oÓPÇÏV X‰XE S±…F„b+ÊÀ‚ÿkùÿéF‹¥P3¤,Hí€Z©CZGrAÂvEÿH|œOéÕq“Wâ¤Ë÷|v>ïùë÷ü¯ÐõåÍäíRÅE&Ѭ:޳žÉœ¶ñ\ò‚®ëÏçŸÐU3 †\.§µ·¶àȲ4_§P(Àíùy®¹G)5œ†aR38=x·Û…z½Î²Ñh@µZåëiàön÷¯éðÓùµÀéx:|î\„~€Ðóz¤ýX}älïE`\@h¾Tƒ¨ÇJ\1”#ékÀ*Ï~üÁÃ:\¾¤©Ásä釵µx¦ -•JÀƒHN‚ßÔ໵h ði?®GbK2¡°b „“^S´ë¯Áâï ŽAiMÿO f?Œ¤)¨®^ÇÚ 8äÝ$PW/·~͉"¢è¹V€Xqf0®×ÃCÞÂÙlNÎR~gî¼ñŒ‘5=ã®iB¥R™8¨Ž]hµZ.Õßå[ÍúºÃQº'‡ÍèúƆ»´ôêµv`J,†aˆžçáæ×Ö^»Øüâáp8ÄÑh„Q±v:©mû'./¿uhw>—IªÀ¿h­Ý{ã¢Ó “`®MÓLBíb±øL *°ïûþpM3teåM3¡â“¬À²,\]}o—Ëå§*hòÆUÊ;ðgaRnS"̈ßo¥Gݹð¯IEND®B`‚boats-201908/images/zoomin.png000066400000000000000000000022471353032755300162300ustar00rootroot00000000000000‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs „ „ªâcytIME× (£{O´4IDATxÚµ•_L[UÇÏýß0Ú¤£üºn-m¡dt›ÉpfM‡fêƒóÅÄHŒ&dfK|$ñA3Œ &S7l#Û” ™†´ÝÊŸB¡·ÿè¥í½½½ÿ<{0!Íô“|_Nî÷›_Î=ßsÀÿþ†^¯GFF¾l-¯¨è£Hª A¼ÀÖÒLú‡W_¸Å0Œü¯ƒ‡‡‡«ÛÛŸÇ’Ù³S÷ÂH˜ÎÊ‚ ƒ¥$â:V‰´Zª½ápø­ ÞœûÇÁ—._®³Ûì·&]7yW¹®Ù,[Ûj^R$&ΟwU â³ \2¾Õ78øîØ Šxíüù7gMÒç8íJyzÚÄ…Àª*“Ë"Ïô8ÒhØN2’ðË̪®ã¨¡Ï ×}ÿþ|½xñÃ8#Ûî…œãLÇr•µf£D¯I¬®'Èå•(…ëT1Ýáò°«Ç±¬ª"³ö€ÅÒüØ”$‰þ¹P^Ô5˜ýyCYFqÿŠ„.ípØÉÀ/ ¡ I¬…üúHãÜü†Ì$ù"ô’ ¸¬(Oms¸¨>fZóÏ.šS‘|'ª;±ˆl \üø‰KCcÍ=ãñgßT#‰è-…Jì91PŠ ((Õ°|A™‚ð¸‘UÐG©  H.—Gò\^Ä4ëÔð `FX’$Z¯Ã YAÌ}ǧFÍwVÿÂ{èðúGï¿d<ÁŠšÔJ²Â ¥(Ta2З)ºÇétú§3¥a7bF‘1Ø C‘BÄáÅ 1”%Yb.O9k¨ªX,vzsEƒ'&&FJ †qhù6.šÖ³¼HòŠœ®Êo:=–¯$D‘ó¢‚siN[FÇZÛMò™™ß>^©è9f –îõ@³ÈÁ匒´*¾ÖÙøÐdo^¥YIGÓ£i‹>ñR ße®2àð¸åX–ý1 ÊÅ ü~ß"¬àæi{íó]åºv½ ’2…JÀ°õµ|ÞéA™çZ ºîæøu‘es䉓§œÖ#–§eYž\XX(ì Q|>ßü;·¿5–R¸^L5ÕÉÛ–Ã<]¯¬/æg~þìʧïD"W <ßné::Ý»­ídYYÙ÷ÓÓÓ¹=.¡]kj¨(*ÅBÉât:­---7à Xóò¹s@MQÆÆÆz‡††ížx7Â㿞…*@)à1›››qؼñr£ñLpiÉXm2»Üî~‹År{rrr³HðþD"‘mA®××ן ¯­U©Ôê’N·{Àår-ŒŽŽ>,¼?Ñh4 ;pÍjµº·S)3ÇqܦþîîîH6›ÇÀÇ9𦝵@`›`q0›ÍÖ _¢žD"Q…B£v»½F«Õ¶z½Þ?À%ð_a2™°÷ Ç Öön£áIEND®B`‚boats-201908/images/zoomout.png000066400000000000000000000022251353032755300164250ustar00rootroot00000000000000‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs „ „ªâcytIME× 2Ç:"IDATxÚµ“_L[UÇϹÿz{oKKKÙ¤0†Ð ¡´ü´8‘ 5Žöâ23™ò Éß|Â%cdñMͶ„ìÉ¡¶l£&Ë&:`>H¤ü§T …—û§÷öž{½$>—uÓà'ùæä¼|ò;9ßø¿€àtwwÛ½^ß)–eŽ$éRUuS’¤Ñéðt_[Û{«ÿZ̲,¼zíZ›Ãáìº?ºà ÇA‚—u MÀ‚+¬¯> P$öéÛçÞº´½½žU ¯\¹ú™ -|õ}XǬYèx£ðC3†~ü]ODþ _©´àÞBÛ·ïœ?ÿf*•J+ÇAW×Çg1ÊöÉ—ß-¡‚ ïV]S@(-ËÓÕL;w8? eÌáÚÌߎá6J)oijPîܾýàibSss˾‡¼…É/ŒV¨ˆeçØd‹•æŒ:2…Hb›µg¬4->Zdj<æ£ëëñ¯£Ñ¨ô$1vá»5ŠF˜Žk›ež_× 1½Žáá NË4µ>–Âæ6q":‡°°üܾ 9#sv~M³{éØI‚¢Hï–qzvT4Ñ«¡onµÁŒ’ @|  ­E€¤M %·@ùÅ¿°tG6Ĺb7EyÓ‰1 ®SVËÆ2·ƒ€ˆˆh'„4 5‚‚r AžÉš’”ÝÂc©Ði'6ª³à)%4´¨Pèä¥s9Í„h%1Ù„yªŸ€¢ªS›2¢—7FS¶ÏNšv"B$íĽ½½C„ç-Ø~$¥(i©N@Þd‚EÁ bESTÂÄq¹…Ù86<22˜V,ËÉÕ™Ù™ž&ŸÉ-…—ó¥m‰M"Øm8KB‡PS4e•’—®SE”oi~êîèÈÈoO«›>1>þ¨.XñjÝ!wåÔ’$ñ4Í!ŠÐDI‰q²Mœ‰yšY¡¥Ä™ÌôûülQQá`(ZM» Æ “““w¼÷‘ÓþÜÆ< H eË[’Û™Lzý*ÿòk|ÃÊÄ}edø¡¹¸ä…ŒÊÊÊ3eeeæŸ(ޅ㸭Ph°WvÂvLÌËA‰’Cp³˜Ïd%¿,Þìûüòåî÷m6[ör$Ržår™àÁ``naaabee< „‘L#¹FœF(ðyyyxKKKwGG‡Þsýº¾‰¨ýýýëëëáã?Žf$i„3"A{™Îóü]‡Ã‹‚ðb"‘Àjkëý~Ÿ=‹ý0;;«¥§gWn÷X–å)Š:¾¸°ˆU© ÖTW+ŠrsllLÅÁ$ƒñññŸ –].×ëá©)üpqq©!¯e¦‚= ½½ý´Ñ’Q™@ 4Mû ‚=ÂøÌ†`0xcmmÍb\Ï‚½¤³³³²µµõ 0øtªÞFÕFc2IEND®B`‚boats-201908/itemviews/000077500000000000000000000000001353032755300147515ustar00rootroot00000000000000boats-201908/itemviews/colorpickerwidget.cpp000066400000000000000000000027161353032755300212030ustar00rootroot00000000000000// // C++ Implementation: colorpickerwidget // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009 Thibaut GRIDEL // // 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 . // // #include #include "colorpickerwidget.h" #include "commontypes.h" #include extern int debugLevel; ColorPickerWidget::ColorPickerWidget(QWidget *parent) : QWidget(parent) { } void ColorPickerWidget::setColor(QColor color) { if (color != m_color) { m_color = color; QColorDialog::setCustomColor(0, color.rgb()); pick(); } } void ColorPickerWidget::pick() { if (debugLevel & 1 << DELEGATE) std::cout << "pick" << std::endl; // get the new color from dialog QColor newColor(QColorDialog::getColor(m_color)); if (newColor.isValid()) { m_color = newColor; } emit editingFinished(); } boats-201908/itemviews/colorpickerwidget.h000066400000000000000000000023271353032755300206460ustar00rootroot00000000000000// // C++ Interface: colorpickerwidget // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009 Thibaut GRIDEL // // 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 . // // #ifndef COLORPICKERWIDGET_H #define COLORPICKERWIDGET_H #include class ColorPickerWidget : public QWidget { Q_OBJECT public: ColorPickerWidget(QWidget *parent = 0); ~ColorPickerWidget() {} void setColor(QColor color); const QColor color() const { return m_color; } signals: void editingFinished(); private: void pick(); QColor m_color; }; #endif boats-201908/itemviews/itemviews.pri000066400000000000000000000005601353032755300175020ustar00rootroot00000000000000INCLUDEPATH += $$PWD DEPENDPATH += $$PWD HEADERS += \ $$PWD/colorpickerwidget.h \ $$PWD/trackdelegate.h \ $$PWD/tracktablemodel.h \ $$PWD/winddelegate.h \ $$PWD/windtablemodel.h SOURCES += \ $$PWD/colorpickerwidget.cpp \ $$PWD/trackdelegate.cpp \ $$PWD/tracktablemodel.cpp \ $$PWD/winddelegate.cpp \ $$PWD/windtablemodel.cpp boats-201908/itemviews/trackdelegate.cpp000066400000000000000000000126171353032755300202630ustar00rootroot00000000000000// // C++ Implementation: trackdelegate // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009 Thibaut GRIDEL // // 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 . // // #include #include "trackdelegate.h" #include "colorpickerwidget.h" #include "commontypes.h" #include "boats.h" #include extern int debugLevel; void TrackDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { painter->save(); drawBackground(painter, option, index); switch (index.column()) { case TRACK_COLOR: { QColor trackColor = index.data().value(); painter->setPen(Qt::NoPen); painter->setBrush(trackColor); qreal height = option.rect.height() / 8.0; qreal width = option.rect.width() / 8.0; QRectF rect(option.rect); rect.adjust(width, height, -width, -height); painter->drawRoundedRect(rect, width, height); } break; case TRACK_SERIES: { int series = index.data().value(); drawDisplay(painter, option, option.rect, Boats::seriesList().at(series)); } break; default: QItemDelegate::paint(painter, option, index); break; } drawFocus(painter, option, option.rect); painter->restore(); } QSize TrackDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { return QItemDelegate::sizeHint(option, index); } QWidget * TrackDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option); if (debugLevel & 1 << DELEGATE) std::cout << "createEditor " << index.column() << std::endl; switch (index.column()) { case TRACK_COLOR: { ColorPickerWidget *editor = new ColorPickerWidget(parent); connect(editor, SIGNAL(editingFinished()), this, SLOT(commitAndCloseColor()), Qt::QueuedConnection); return editor; } break; case TRACK_SERIES: { QComboBox *editor = new QComboBox(parent); editor->addItems(Boats::seriesList()); connect(editor, SIGNAL(activated(int)), this, SLOT(commitAndCloseCombo())); return editor; } break; default: return 0; break; } } void TrackDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if (debugLevel & 1 << DELEGATE) std::cout << "setEditorData " << index.column() << std::endl; switch (index.column()) { case TRACK_COLOR: { ColorPickerWidget *colorEditor = getColorEditor(editor); QColor color = index.data().value(); colorEditor->setColor(color); } break; case TRACK_SERIES: { QComboBox *seriesEditor = getComboEditor(editor); int series = index.data().value(); seriesEditor->setCurrentIndex(series); } break; default: QItemDelegate::setEditorData(editor, index); break; } } void TrackDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { if (debugLevel & 1 << DELEGATE) std::cout << "setModelData " << index.column() << std::endl; switch (index.column()) { case TRACK_COLOR: { ColorPickerWidget *colorEditor = getColorEditor(editor); QColor color = colorEditor->color(); model->setData(index, qVariantFromValue(color)); } break; case TRACK_SERIES: { QComboBox *seriesEditor = getComboEditor(editor); int series = seriesEditor->currentIndex(); model->setData(index, qVariantFromValue(series)); } break; default: QItemDelegate::setModelData(editor, model, index); break; } } void TrackDelegate::commitAndCloseColor() { if (debugLevel & 1 << DELEGATE) std::cout << "commitAndCloseColor" << std::endl; ColorPickerWidget *colorEditor = getColorEditor(sender()); emit commitData(colorEditor); emit closeEditor(colorEditor); } void TrackDelegate::commitAndCloseCombo() { if (debugLevel & 1 << DELEGATE) std::cout << "commitAndCloseCombo" << std::endl; QComboBox *pathEditor = getComboEditor(sender()); emit commitData(pathEditor); emit closeEditor(pathEditor); } ColorPickerWidget * TrackDelegate::getColorEditor(QObject *editor) const { return qobject_cast(editor); } QComboBox * TrackDelegate::getComboEditor(QObject *editor) const { return qobject_cast(editor); } boats-201908/itemviews/trackdelegate.h000066400000000000000000000034661353032755300177320ustar00rootroot00000000000000// // C++ Interface: trackdelegate // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009 Thibaut GRIDEL // // 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 . // // #ifndef TRACKDELEGATE_H #define TRACKDELEGATE_H #include #include class ColorPickerWidget; class TrackDelegate : public QItemDelegate { Q_OBJECT public: TrackDelegate(QWidget *parent = 0) : QItemDelegate(parent) {} void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; private slots: void commitAndCloseColor(); void commitAndCloseCombo(); private: ColorPickerWidget * getColorEditor(QObject *editor) const; QComboBox * getComboEditor(QObject *editor) const; }; #endif boats-201908/itemviews/tracktablemodel.cpp000066400000000000000000000160631353032755300206200ustar00rootroot00000000000000// // C++ Implementation: tracktablemodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2009 Thibaut GRIDEL // // 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 . // // #include #include "tracktablemodel.h" #include "commontypes.h" #include "boats.h" #include "situationmodel.h" #include "trackmodel.h" extern int debugLevel; TrackTableModel::TrackTableModel(SituationModel *situation, QObject *parent) : QAbstractTableModel(parent), m_situation(situation) { if (debugLevel & 1 << MODEL) std::cout << "new tracktable " << this << std::endl; } TrackTableModel::~TrackTableModel() { if (debugLevel & 1 << MODEL) std::cout << "end tracktable " << this << std::endl; } void TrackTableModel::setSituation(SituationModel *situation) { if (m_situation != situation) { beginResetModel(); m_situation = situation; endResetModel(); } } QVariant TrackTableModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (index.row() >= m_situation->size() || index.row() < 0) return QVariant(); if (role == Qt::DisplayRole) { switch (index.column()) { case TRACK_COLOR: return m_situation->tracks()[index.row()]->color(); break; case TRACK_SERIES: return m_situation->tracks()[index.row()]->series(); break; default: return QVariant(); break; } } else if (role == Qt::CheckStateRole) { switch (index.column()) { case TRACK_PATH: if (m_situation->tracks()[index.row()]->showPath()) { return Qt::Checked; } return Qt::Unchecked; break; case TRACK_FOLLOW: if (m_situation->tracks()[index.row()]->followTrack()) { return Qt::Checked; } return Qt::Unchecked; break; default: return QVariant(); break; } } return QVariant(); } QVariant TrackTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) { switch (section) { case TRACK_COLOR: return tr("Color"); case TRACK_PATH: return tr("Path"); case TRACK_SERIES: return tr("Series"); case TRACK_FOLLOW: return tr("Follow"); default: return QVariant(); } } return QVariant(); } Qt::ItemFlags TrackTableModel::flags(const QModelIndex &index) const { Q_UNUSED(index); return Qt::ItemIsEnabled|Qt::ItemIsEditable|Qt::ItemIsUserCheckable; } bool TrackTableModel::setData(const QModelIndex &index, const QVariant &value, int role) { Q_UNUSED(role); if (!index.isValid()) return false; if (index.row() >= m_situation->size() || index.row() < 0) return false; switch (index.column()) { case TRACK_COLOR: if (value.canConvert()) { QColor newValue = value.value(); TrackModel *track = m_situation->tracks()[index.row()]; if (newValue != track->color()) { m_situation->clearSelectedModels(); m_situation->addSelectedBoat(track->boats().first()); m_situation->setColor(newValue); } return true; } break; case TRACK_PATH: if (value.isValid()) { bool newValue = (static_cast(value.toInt()) == Qt::Checked); TrackModel *track = m_situation->tracks()[index.row()]; if (newValue != track->showPath()) { m_situation->clearSelectedModels(); m_situation->addSelectedBoat(track->boats().first()); m_situation->setShowPath(); } return true; } break; case TRACK_SERIES: if (value.canConvert()) { int newValue = value.value(); if (newValue >= 0 && newValue < Boats::unknown) { Boats::Series seriesValue = (Boats::Series)newValue; TrackModel *track = m_situation->tracks()[index.row()]; if (seriesValue != track->series()) { m_situation->clearSelectedModels(); m_situation->addSelectedBoat(track->boats().first()); m_situation->setSeries(seriesValue); } return true; } } break; case TRACK_FOLLOW: if (value.isValid()) { bool newValue = (static_cast(value.toInt()) == Qt::Checked); TrackModel *track = m_situation->tracks()[index.row()]; if (newValue != track->followTrack()) { m_situation->clearSelectedModels(); m_situation->addSelectedBoat(track->boats().first()); m_situation->setFollowTrack(); } return true; } break; default: return false; break; } return false; } void TrackTableModel::addTrack(TrackModel *track) { int order = track->order(); connect(track, SIGNAL(colorChanged(QColor)), this, SLOT(updateTrack())); connect(track, SIGNAL(seriesChanged(Boats::Series)), this, SLOT(updateTrack())); connect(track, SIGNAL(showPathChanged(bool)), this, SLOT(updateTrack())); connect(track, SIGNAL(followTrackChanged(bool)), this, SLOT(updateTrack())); beginInsertRows(QModelIndex(), order, order); endInsertRows(); } void TrackTableModel::updateTrack() { TrackModel *track = (TrackModel*) sender(); int order = track->order(); for (int i = TRACK_COLOR; i <= TRACK_FOLLOW; i++) { QModelIndex ind = index(i, order); emit dataChanged(ind, ind); } } void TrackTableModel::deleteTrack(TrackModel *track) { int order = track->order(); disconnect(track,0,this,0); beginRemoveRows(QModelIndex(), order, order); endRemoveRows(); } boats-201908/itemviews/tracktablemodel.h000066400000000000000000000035401353032755300202610ustar00rootroot00000000000000// // C++ Interface: tracktablemodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2009 Thibaut GRIDEL // // 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 . // // #ifndef TRACKTABLEMODEL_H #define TRACKTABLEMODEL_H #include "commontypes.h" #include "situationmodel.h" #include class TrackModel; class TrackTableModel : public QAbstractTableModel { Q_OBJECT public: TrackTableModel(SituationModel* situation = 0, QObject *parent = 0); ~TrackTableModel(); void setSituation(SituationModel *situation); int rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_situation->size(); } int columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 4;} QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; Qt::ItemFlags flags(const QModelIndex &index) const; bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole); public slots: void addTrack(TrackModel *track); void updateTrack(); void deleteTrack(TrackModel *track); private: SituationModel *m_situation; }; #endif boats-201908/itemviews/winddelegate.cpp000066400000000000000000000041351353032755300201140ustar00rootroot00000000000000// // C++ Implementation: winddelegate // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2011 Thibaut GRIDEL // // 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 . // // #include #include "winddelegate.h" #include "commontypes.h" #include "boats.h" #include extern int debugLevel; QWidget * WindDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option); if (debugLevel & 1 << DELEGATE) std::cout << "createEditor " << index.column() << std::endl; switch (index.column()) { case WIND_DIRECTION: { QDoubleSpinBox *editor = new QDoubleSpinBox(parent); editor->setMinimum(0); editor->setMaximum(359); editor->setDecimals(0); editor->setWrapping(true); return editor; } break; default: return 0; break; } } void WindDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if (debugLevel & 1 << DELEGATE) std::cout << "setEditorData " << index.column() << std::endl; switch (index.column()) { case WIND_DIRECTION: { QDoubleSpinBox *spin = static_cast(editor); spin->setValue(index.data().value()); } break; default: QItemDelegate::setEditorData(editor, index); break; } } boats-201908/itemviews/winddelegate.h000066400000000000000000000023311353032755300175550ustar00rootroot00000000000000// // C++ Interface: winddelegate // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2011 Thibaut GRIDEL // // 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 . // // #ifndef WINDDELEGATE_H #define WINDDELEGATE_H #include class WindDelegate : public QItemDelegate { Q_OBJECT public: WindDelegate(QWidget *parent = 0) : QItemDelegate(parent) {} QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; }; #endif boats-201908/itemviews/windtablemodel.cpp000066400000000000000000000103471353032755300204540ustar00rootroot00000000000000// // C++ Implementation: windtablemodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2011 Thibaut GRIDEL // // 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 . // // #include #include "windtablemodel.h" #include "commontypes.h" #include "situationmodel.h" #include "windmodel.h" extern int debugLevel; WindTableModel::WindTableModel(WindModel *wind, QObject *parent) : QAbstractTableModel(parent), m_wind(wind) { if (debugLevel & 1 << MODEL) std::cout << "new windtable " << this << std::endl; } WindTableModel::~WindTableModel() { if (debugLevel & 1 << MODEL) std::cout << "end windtable " << this << std::endl; } void WindTableModel::setWind(WindModel *wind) { if (m_wind != wind) { beginResetModel(); m_wind = wind; endResetModel(); } } QVariant WindTableModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (index.row() >= m_wind->size() || index.row() < 0) return QVariant(); if (role == Qt::DisplayRole) { switch (index.column()) { case WIND_INDEX: return index.row()+1; break; case WIND_DIRECTION: return m_wind->windAt(index.row()); break; default: return QVariant(); break; } } else if (role == Qt::CheckStateRole) { switch (index.column()) { case WIND_DELETE: return false; break; default: return QVariant(); break; } } return QVariant(); } QVariant WindTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) { switch (section) { case WIND_INDEX: return tr("Index"); case WIND_DIRECTION: return tr("Direction"); case WIND_DELETE: return tr("Delete"); default: return QVariant(); } } return QVariant(); } Qt::ItemFlags WindTableModel::flags(const QModelIndex &index) const { Q_UNUSED(index); return Qt::ItemIsEnabled|Qt::ItemIsEditable|Qt::ItemIsUserCheckable; } bool WindTableModel::setData(const QModelIndex &index, const QVariant &value, int role) { Q_UNUSED(role); if (!index.isValid()) return false; if (index.row() > m_wind->size() || index.row() < 0) return false; switch (index.column()) { case WIND_DIRECTION: if (value.canConvert()) { qreal newValue = value.value(); if (debugLevel & 1 << MODEL) std::cout << "setting wind " << newValue; if (index.row() == m_wind->size()) { m_wind->situation()->addWind(newValue); } else { if (newValue != m_wind->windAt(index.row())) { m_wind->situation()->setWind(index.row(), newValue); } } return true; } break; case WIND_DELETE: if (debugLevel & 1 << MODEL) std::cout << "deleting index " << index.row(); if (index.row() < m_wind->size()) { m_wind->situation()->deleteWind(index.row()); } return true; break; default: return false; break; } return false; } void WindTableModel::updateWind() { beginResetModel(); endResetModel(); } boats-201908/itemviews/windtablemodel.h000066400000000000000000000032771353032755300201250ustar00rootroot00000000000000// // C++ Interface: windtablemodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2011 Thibaut GRIDEL // // 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 . // // #ifndef WINDTABLEMODEL_H #define WINDTABLEMODEL_H #include #include "commontypes.h" #include "windmodel.h" class WindTableModel : public QAbstractTableModel { Q_OBJECT public: WindTableModel(WindModel* wind = 0, QObject *parent = 0); ~WindTableModel(); void setWind(WindModel *wind); int rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_wind->size()+1; } int columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 3;} QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; Qt::ItemFlags flags(const QModelIndex &index) const; bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole); public slots: void updateWind(); private: WindModel *m_wind; }; #endif boats-201908/locale/000077500000000000000000000000001353032755300141745ustar00rootroot00000000000000boats-201908/locale/boats.ts000066400000000000000000000521211353032755300156550ustar00rootroot00000000000000 ArrowGraphicsItem Wind MainWindow &New File Ctrl+N &Open File... Ctrl+O &Save File Ctrl+S Save &As... Ctrl+Shift+S New &Tab Ctrl+T &Close Tab Ctrl+W &Restore Last Session... &Print... Print P&review... &Export Pdf... Ctrl+E Export &Image... Ctrl+I Export Ani&mation... Ctrl+V E&xit Ctrl+Q Exit the application Create &Track Ctrl+Ins Create &Boat Ins Create &Mark Alt+Ins &Port Overlap Alt+< &Starboard Overlap Alt+> &Text Alt+T Toggle Mark &Zone Delete Track Ctrl+Del &Delete Selection Del &Animate Ctrl+A &Play P &Pause M &Stop Space &Loop L &Undo Ctrl+Z &Redo Ctrl+Y Shift+Ctrl+Z Zoom &In Ctrl++ Zoom &Out Ctrl+- Zoom &Fit Ctrl+F Main Toolbar Animation Toolbar Scenario Dock &About %1 - %2 [*] Boat Scenario &Recent &File &Edit &History &Animation &Zoom &View &%1 %2 The document %1 has been modified. Do you want to save your changes? Ctrl+P Ctrl+R &Flag &Help Choose &Language Open Scenario File xmlscenario Files (*.xbs) File loaded Save Scenario %1 already exists. Do you want to replace it? Cannot write file %1: %2. File saved Print Document Export Image Export Animation Exporting Animation... Abort About Boat Scenario Create &PolyLine Ctrl+Alt+Ins Create Poin&t Toggle &Spinnaker Alt+S Alt+Z &Hide Alt+D Trim Sail > Untrim Sail < Auto Trim = &Acceleration Toggle &Laylines Alt+L Set Mark &Color Alt+C Toggle &Mark Side Alt+M Toggle Mark Arro&w Alt+W Toggle Mark &Label &Edit Mark Label Alt+E Edit mark label Label text: Trim Jib , Auto Jib ? Untrim Jib . Trim Spin Ctrl+, Auto Spin Ctrl+? Untrim Spin Ctrl+. QObject The file is not an xmlscenario version 1.0 file. Laser Optimist Tornado Committee boat RIB Spin Keelboat Gennaker Keelboat Topper Firefly 29er 49er 420 470 RS Feva Finn Nacra 17 Diam 24 SituationModel Protest! SituationPrint Rule SituationWidget Options Series Zone Length Tracks Scenario Title Rules Abstract Description Wind Show Wind Show Grid Layline Angle TrackTableModel Color Path Series Follow WindTableModel Index Direction Delete boats-201908/locale/boats_da.ts000066400000000000000000000501401353032755300163200ustar00rootroot00000000000000 ArrowGraphicsItem Wind Vind MainWindow &New File &Ny fil Ctrl+N Ctrl+N &Open File... &Ã…bn fil... Ctrl+O Ctrl+O &Save File &Gem fil Ctrl+S Ctrl+S Save &As... Gem &som... Ctrl+Shift+S Ctrl+Shift+S New &Tab Nyt &faneblad Ctrl+T Ctrl+T &Close Tab &Luk faneblad Ctrl+W Ctrl+W &Restore Last Session... &Gendan sidste session... &Print... &Udskriv... Print P&review... &Vis udskrift... &Export Pdf... Eksporter &Pdf... Ctrl+E Ctrl+E Export &Image... Eksporter &billede... Ctrl+I Ctrl+I Export Ani&mation... Eksporter &Animation... Ctrl+V Ctrl+V E&xit Afsl&ut Ctrl+Q Ctrl+Q Exit the application Afslut programmet Create &Track Opret &Spor Ctrl+Ins Ctrl+Ins Create &Boat Opret &BÃ¥d Ins Ins Create &Mark Opret &mærke Alt+Ins Alt+Ins &Port Overlap &Bagbord overlap Alt+< Alt+< &Starboard Overlap &Styrbord overlap Alt+> Alt+> &Text &Tekst Alt+T Alt+T Toggle Mark &Zone Vis/skjul mærke&zone Delete Track Slet spor Ctrl+Del Ctrl+Del &Delete Selection Slet &markering Del Del &Animate &Animér Ctrl+A Ctrl+A &Play A&fspil P P &Pause &Pause M M &Stop &Stop Space Space &Loop &Løkke L L &Undo &Fortryd Ctrl+Z Ctrl+Z &Redo &Omgør Ctrl+Y Ctrl+Y Shift+Ctrl+Z Ctrl+Shift+Z Zoom &In Zoom &ind Ctrl++ Ctrl++ Zoom &Out Zoom &ud Ctrl+- Ctrl+- Zoom &Fit Zoo&m tilpas Ctrl+F Ctrl+F Main Toolbar Hovedværktøjslinje Animation Toolbar Værktøjslinjen Animation Scenario Dock Scenarie dock &About &Om %1 - %2 [*] %1 - %2 [*] Boat Scenario BÃ¥dsecnarie &Recent N&ylige &File &Fil &Edit &Redigér &History &Historie &Animation &Animation &Zoom &Zoom &View &Vis &%1 %2 &%1 %2 The document %1 has been modified. Do you want to save your changes? Dokumentet %1 er ændret.\n Vil du gemme ændringerne? Ctrl+P Ctrl+P Ctrl+R Ctrl+R &Flag &Flag &Help &Hjælp Choose &Language Væ&lg sprog Open Scenario File Ã…ben senarie-fil xmlscenario Files (*.xbs) xmlscenario filer (*.xbs) File loaded Fil indlæst Save Scenario Gem scenarie %1 already exists. Do you want to replace it? %1 findes allerede. Vil du overskrive den? Cannot write file %1: %2. Kan ikke skrive fil %1: %2. File saved Fil gemt Print Document Udskriv dokument Export Image Eksportér billede Export Animation Eksportér animation Exporting Animation... Eksporteter animation... Abort Afbryd About Boat Scenario Om Boat Scenario Create &PolyLine Opret &polylinje Ctrl+Alt+Ins Ctrl+Alt+Ins Create Poin&t Opret punk&t Toggle &Spinnaker Vis/skjul &spiler Alt+S Alt+Z &Hide Skjul Alt+D Trim Sail Hal hjem < Auto Trim Automatisk skødning = Untrim Sail Slæk ud > &Acceleration Acceleration Toggle &Laylines Vis/skjul laylines Alt+L Set Mark &Color Alt+C Toggle &Mark Side Alt+M Toggle Mark Arro&w Alt+W Toggle Mark &Label &Edit Mark Label Alt+E Edit mark label Label text: Trim Jib , Auto Jib ? Untrim Jib . Trim Spin Ctrl+, Auto Spin Ctrl+? Untrim Spin Ctrl+. QObject The file is not an xmlscenario version 1.0 file. Filen er ikke en xmlscenario version 1.0 fil. Laser Optimist Tornado Committee boat RIB Spin Keelboat Gennaker Keelboat Topper Firefly 29er 49er 420 470 RS Feva Finn Nacra 17 Diam 24 SituationModel Protest! Protest! SituationPrint Rule Regel SituationWidget Options Indstillinger Series BÃ¥dtype Zone Length Mærkexone længde Tracks Spor Scenario Situation Title Titel Rules Regler Abstract Opsummering Description Beskrivelse Wind Vind Show Wind Vis vind Show Grid Vis gitter Layline Angle Layline vinkel TrackTableModel Color Farve Path Spor Series BÃ¥dtype Follow WindTableModel Index Indeks Direction Retning Delete Slet boats-201908/locale/boats_de.ts000066400000000000000000000503351353032755300163320ustar00rootroot00000000000000 ArrowGraphicsItem Wind Wind MainWindow &New File &Neu Ctrl+N Ctrl+N &Open File... &Öffnen... Ctrl+O Ctrl+O &Save File &Speichern Ctrl+S Ctrl+S Save &As... Speichern &unter... Ctrl+Shift+S Ctrl+Shift+S New &Tab Neuer &Tab Ctrl+T Ctrl+T &Close Tab Tab &schließen Ctrl+W Ctrl+W &Restore Last Session... Letzte &Sitzung wiederherstellen... &Print... &Drucken... Print P&review... Seiten&ansicht zeigen... &Export Pdf... &Exportieren als PDF... Ctrl+E Ctrl+E Export &Image... Exportieren als B&ild... Ctrl+I Ctrl+I Export Ani&mation... Exportieren als &Animation... Ctrl+V Ctrl+V E&xit &Beenden Ctrl+Q Ctrl+Q Exit the application Boat Scenario beenden Create &Track &Kurs erstellen Ctrl+Ins Ctrl+Ins Create &Boat &Boot erstellen Ins Ins Create &Mark Bahn&marke erstellen Alt+Ins Alt+Ins Create &PolyLine &PolyLinie estellen Ctrl+Alt+Ins Create Poin&t Pun&kt erstellen &Port Overlap Überlappung nach &Backbord Alt+< Alt+< &Starboard Overlap Überlappung nach &Steuerbord Alt+> Alt+> &Text Alt+T Toggle &Spinnaker &Spinnaker anzeigen Toggle Mark &Zone Bahnmarkenzone &anzeigen Delete Track Kurs löschen Ctrl+Del Ctrl+Del &Delete Selection &Auswahl löschen Del Del &Animate &Animation Ctrl+A Ctrl+A &Play &Play P P &Pause &Pause M M &Stop &Stop Space Space &Loop &Wiederholung L L &Undo &Rückgängig Ctrl+Z Ctrl+Z &Redo &Wiederherstellen Ctrl+Y Ctrl+Y Shift+Ctrl+Z Shift+Ctrl+Z Zoom &In Ansicht ver&größern Ctrl++ Ctrl++ Zoom &Out Ansicht ver&kleinern Ctrl+- Ctrl+- Zoom &Fit Ansicht an&passen Ctrl+F Ctrl+F Main Toolbar Hauptwerkzeugleiste Animation Toolbar Animationswerkzeugleiste Scenario Dock Scenarienbeschreibung &About &Über %1 - %2 [*] %1 - %2 [*] Boat Scenario Boat Scenario &Recent &Zuletzt benutzt &File &Datei &Edit &Editieren &History &Historie &Animation &Animation &Zoom &View &Ansicht &%1 %2 &%1 %2 The document %1 has been modified. Do you want to save your changes? Das Dokument %1 wurde verändert. Möchten Sie ihre Änderungen speichern? Ctrl+P Ctrl+R &Flag &Flagge &Help &Hilfe Choose &Language &Sprache wählen Open Scenario File Open Scenario File xmlscenario Files (*.xbs) xmlscenario Dateien (*.xbs) File loaded Datei geladen Save Scenario Speichere Scenario %1 already exists. Do you want to replace it? %1 existiert bereits. Möchten Sie die Datei ersetzen? Cannot write file %1: %2. Kann Datei %1:%2 nicht schreiben. File saved Datei gespeichert Print Document Dokument drucken Export Image Bild exportieren Export Animation Animation exportieren Exporting Animation... Animation exportieren... Abort Abbruch About Boat Scenario Über Boat Scenario Alt+S Alt+Z &Hide Ausblenden Alt+D Trim Sail Segel dichtholen < Auto Trim Auto Segeltrimm = Untrim Sail Segel auffieren > &Acceleration Beschleunigung Toggle &Laylines Layline anzeigen Alt+L Set Mark &Color Bahnmarkenfarbe setzen Alt+C Toggle &Mark Side Bahnmarkenseite anzeigen Alt+M Toggle Mark Arro&w Bahnmarkenpfeil anzeigen Alt+W Toggle Mark &Label Bahnmarkenschriftung anzeigen &Edit Mark Label Bahnmarkenschriftung bearbeiten Alt+E Edit mark label Bahnmarkenschriftung bearbeiten Label text: Beschriftungstext Trim Jib , Auto Jib ? Untrim Jib . Trim Spin Ctrl+, Auto Spin Ctrl+? Untrim Spin Ctrl+. QObject The file is not an xmlscenario version 1.0 file. Diese Datei ist keine xml-Scenario-Datei Version 1.0. Laser Optimist Tornado Committee boat WFL Boot RIB Schlauchboot Spin Keelboat Drehung Kielboot Gennaker Keelboat Gennacker Kielboot Topper Firefly 29er 49er 420 470 RS Feva Finn Nacra 17 Diam 24 SituationModel Protest! SituationPrint Rule Regel SituationWidget Options Optionen Series Serien Zone Length Größe der Zone Tracks Kurse Scenario Szenario Title Titel Rules Regeln Abstract Kurzbeschreibung Description Beschreibung Wind Wind Show Wind Windrichtung anzeigen Show Grid Raster einlenden Layline Angle Layline Winkel TrackTableModel Color Farbe Path Pfad Series Reihe Follow Folge WindTableModel Index Index Direction Richtung Delete löschen boats-201908/locale/boats_es.ts000066400000000000000000000500501353032755300163430ustar00rootroot00000000000000 ArrowGraphicsItem Wind Viento MainWindow &New File &Nuevo archivo Ctrl+N &Open File... A&brir archivo... Ctrl+O Ctrl+B &Save File &Guardar archivo Ctrl+S Ctrl+G Save &As... Guardar &como... Ctrl+Shift+S Ctrl+Shift+C New &Tab Nueva &Solapa Ctrl+T Ctrl+S &Close Tab C&errar Solapa Ctrl+W Ctrl+E &Restore Last Session... &Restaurar la última sesión... &Print... &Imprimir... Print P&review... Vista P&revia... &Export Pdf... &Exportar a Pdf... Ctrl+E Export &Image... Exportar Ima&gen... Ctrl+I Ctrl+G Export Ani&mation... Exportar Ani&mación... Ctrl+V Ctrl+V E&xit Sa&lir Ctrl+Q Ctrl+Q Exit the application Salir de la aplicación Create &Track Crear &Trayectoria Ctrl+Ins Ctrl+Ins Create &Boat Crear &Barco Ins Create &Mark Crear &Baliza Alt+Ins Create &PolyLine Crear &PoliLinea Ctrl+Alt+Ins Create Poin&t Crear Pun&to &Port Overlap Comprometido por &Babor Alt+< &Starboard Overlap Comprometido por &Estribor Alt+> &Text &Texto Alt+T Toggle &Spinnaker Sacar &Spi Toggle Mark &Zone &Zona Alrededor de la Baliza Delete Track Eliminar Trayectoria Ctrl+Del &Delete Selection Elimina&r Selección Del &Animate &Animar Ctrl+A &Play &Iniciar P I &Pause &Pausa M P &Stop P&arar Space &Loop &Bucle L B &Undo D&eshacer Ctrl+Z &Redo &Rehacer Ctrl+Y Shift+Ctrl+Z Zoom &In A&cercar Ctrl++ Zoom &Out Ale&jar Ctrl+- Zoom &Fit Aj&ustar Ctrl+F Ctrl+U Main Toolbar Barra Principal Animation Toolbar Barra de Animación Scenario Dock Control de Escenario &About &Acerca de %1 - %2 [*] Boat Scenario &Recent &Reciente &File &Archivo &Edit &Editar &History &Historial &Animation &Animación &Zoom &Zoom &View &Ver &%1 %2 The document %1 has been modified. Do you want to save your changes? El archivo %1 fué modificado. ¿Quiere guardar sus cambios? Ctrl+P Ctrl+R &Flag &Bandera &Help &Ayuda Choose &Language &Escoge idioma Open Scenario File Abrir Archivo de Escenario xmlscenario Files (*.xbs) Archivos xmlscenario (*.xbs) File loaded Archivo cargado Save Scenario Guardar Escenario %1 already exists. Do you want to replace it? %1 ya existe. ¿Quiere reemplazarlo? Cannot write file %1: %2. No puedo escribir el archivo %1: %2. File saved Archivo guardado Print Document Imprimir Archivo Export Image Exportar Imagen Export Animation Exportar Animación Exporting Animation... Exportando animación... Abort Abortar About Boat Scenario Acerca de Boat Scenario Alt+S Alt+Z &Hide Esconder Alt+D Trim Sail Cazar < Auto Trim Auto Trimado = Untrim Sail Amollar > &Acceleration Aceleración Toggle &Laylines Mostrar laylines Alt+L Set Mark &Color Alt+C Toggle &Mark Side Alt+M Toggle Mark Arro&w Alt+W Toggle Mark &Label &Edit Mark Label Alt+E Edit mark label Label text: Trim Jib , Auto Jib ? Untrim Jib . Trim Spin Ctrl+, Auto Spin Ctrl+? Untrim Spin Ctrl+. QObject The file is not an xmlscenario version 1.0 file. El archivo no es un archivo xmlscenario versión 1.0. Laser Optimist Tornado Committee boat RIB Spin Keelboat Gennaker Keelboat Topper Firefly 29er 49er 420 470 RS Feva Finn Nacra 17 Diam 24 SituationModel Protest! Protesto! SituationPrint Rule Regla SituationWidget Options Opciones Series Series Zone Length Zona en Esloras Tracks Trayectorias Scenario Escenario Title Título Rules Reglas Abstract Resumen Description Descripción Wind Viento Show Wind Mostrar Viento Show Grid Mostrar Cuadrícula Layline Angle Ãngulo del Layline TrackTableModel Color Color Path Ruta Series Series Follow WindTableModel Index Listado Direction Dirección Delete Borrar boats-201908/locale/boats_fr.ts000066400000000000000000000477671353032755300163700ustar00rootroot00000000000000 ArrowGraphicsItem Wind Vent MainWindow &New File &Nouveau Ctrl+N &Open File... &Ouvrir... Ctrl+O &Save File &Enregistrer Ctrl+S Save &As... Enregistrer &sous... Ctrl+Shift+S Export &Image... Exporter &Image... Ctrl+E E&xit &Quitter Ctrl+Q Exit the application Quitter le programme Create &Track Ajouter un &Bateau Ctrl+Ins Create &Boat Ajouter une &Position Ins Create &Mark Ajouter une &Marque Alt+Ins Delete Track Supprimer un Bateau Ctrl+Del &Delete Selection &Supprimer la Sélection Del &Animate &Animer Ctrl+A &Play &Lire P &Pause &Pause M &Stop &Stop Space &Loop &Boucle L &Undo &Annuler Ctrl+Z &Redo &Refaire Ctrl+Y Shift+Ctrl+Z &About &A Propos &File &Fichier &Edit &Edition &History &Historique &Animation &Animation Open Scenario File Ouvrir un fichier Scénario xmlscenario Files (*.xbs) Fichiers xmlscenario (*.xbs) File loaded Fichier chargé Save Scenario Sauvegarder un Scénario Cannot write file %1: %2. Ne peut pas écrire le fichier %1: %2. File saved Fichier sauvegardé Export Image Exporter une Image %1 - %2 [*] Boat Scenario About Boat Scenario A propos de Boat Scenario Zoom &In Zoom A&vant Ctrl++ Zoom &Out Zoom A&rrière Ctrl+- Zoom &Fit Zoom &Cadrage Ctrl+F &Zoom &Zoom Toggle Mark &Zone &Zone à la marque New &Tab Nouvel &Onglet Ctrl+T &Close Tab &Fermer Onglet Ctrl+W &Print... &Imprimer... Print P&review... &Apercu avant Impression... &Export Pdf... &Exporter Pdf... Ctrl+I Export Ani&mation... Exporter Ani&mation... Ctrl+V &Port Overlap Engagement à &Bâbord Alt+< &Starboard Overlap Engagement à &Tribord Alt+> Main Toolbar Barre d'Outils Animation Toolbar Barre d'Animation Scenario Dock Paramètres du Scénario &Recent &Récent &View &Vues &%1 %2 Print Document Imprimer le Document Export Animation Exporter l'Animation The document %1 has been modified. Do you want to save your changes? Le document %1 a été modifié. Voulez-vous sauver vos modifications? Exporting Animation... Export de l'animation... Abort Annuler &Restore Last Session... &Restaurer la Session précédente... %1 already exists. Do you want to replace it? %1 existe déja. Voulez-vous le remplacer? Ctrl+P Ctrl+R &Text &Texte Alt+T &Flag &Pavillon &Help &Aide Choose &Language Choisir &Langue Create &PolyLine Ajouter une &Polyligne Ctrl+Alt+Ins Create Poin&t Ajouter un Poin&t Toggle &Spinnaker &Spinnaker Alt+S Alt+Z &Hide &Masquer Alt+D Trim Sail Border la Voile < Auto Trim Réglage Auto = Untrim Sail Choquer la Voile > &Acceleration &Accélération Toggle &Laylines Afficher les Laylines Alt+L Set Mark &Color Changer la &Couleur de la Marque Alt+C Toggle &Mark Side &Sens de la Marque Alt+M Toggle Mark Arro&w &Fleche de la marque Alt+W Toggle Mark &Label &Nom de la Marque &Edit Mark Label &Editer la Marque Alt+E Edit mark label Editer la Marque Label text: Nom de la Marque Trim Jib , Auto Jib ? Untrim Jib . Trim Spin Ctrl+, Auto Spin Ctrl+? Untrim Spin Ctrl+. QObject The file is not an xmlscenario version 1.0 file. Le fichier n'est pas un fichier xmlscenario version 1.0. Laser Optimist Tornado Committee boat Bateau Comité RIB Semi-Rigide Spin Keelboat Quillard avec Spi Gennaker Keelboat Quillard avec Gennaker Topper Firefly 29er 49er 420 470 RS Feva Finn Nacra 17 Diam 24 SituationModel Protest! SituationPrint Rule Règle SituationWidget Scenario Scénario Title Titre Rules Règles Series Séries Description Description Abstract Abstract Zone Length Taille de Zone Tracks Pistes Options Wind Vent Show Wind Afficher le Vent Show Grid Afficher la Grille Layline Angle Angle des Laylines TrackTableModel Color Couleur Series Séries Path Chemin Follow Suivre WindTableModel Index Index Direction Direction Delete Supprimer boats-201908/locale/boats_it.ts000066400000000000000000000500501353032755300163500ustar00rootroot00000000000000 ArrowGraphicsItem Wind Vento MainWindow &New File &Nuovo file Ctrl+N Ctrl+N &Open File... A&pri file... Ctrl+O Ctrl+B &Save File &Salva file Ctrl+S Ctrl+G Save &As... Salva &come... Ctrl+Shift+S Ctrl+Shift+C New &Tab Nuova &Etichetta Ctrl+T Ctrl+S &Close Tab C&hiudi etichetta Ctrl+W Ctrl+E &Restore Last Session... &Ripristina ultima sessione... &Print... &Stampa... Print P&review... Anteprima di S&tampa... &Export Pdf... &Esporta Pdf... Ctrl+E Ctrl+E Export &Image... Esporta Imma&gine... Ctrl+I Ctrl+G Export Ani&mation... Esporta Ani&mazione... Ctrl+V Ctrl+V E&xit Es&ci Ctrl+Q Ctrl+Q Exit the application Esci dall'aplicazione Create &Track Crea &Traccia Ctrl+Ins Ctrl+Ins Create &Boat Crea &Barca Ins Ins Create &Mark Crea &Boa Alt+Ins Alt+Ins Create &PolyLine Crea &linee multiple Ctrl+Alt+Ins Ctrl+Alt+Ins Create Poin&t Crea Pun&to &Port Overlap Ingaggio a &Sinistra Alt+< Alt+< &Starboard Overlap Ingaggio a &Destra Alt+> Alt+> &Text &Testo Alt+T Toggle &Spinnaker Disegna &Spinnaker Toggle Mark &Zone &Disegna Zona Delete Track Cancella traccia Ctrl+Del Ctrl+Del &Delete Selection Cancella&r Selezione Del Del &Animate &Animazione Ctrl+A Ctrl+A &Play &Esegui P I &Pause &Pausa M P &Stop &Stop Space Spazio &Loop &Ciclo L B &Undo &Ripristina Ctrl+Z Ctrl+Z &Redo &Torna indietro Ctrl+Y Ctrl+Y Shift+Ctrl+Z Shift+Ctrl+Z Zoom &In Zoom &avanti Ctrl++ Ctrl++ Zoom &Out Zoom &indietro Ctrl+- Ctrl+- Zoom &Fit Adatta &zoom Ctrl+F Ctrl+U Main Toolbar Barra Principale Animation Toolbar Barra de Animazione Scenario Dock Scenario Dock &About &About %1 - %2 [*] %1 - %2 [*] Boat Scenario &Recent &Recenti &File &File &Edit &Modifica &History &Storico &Animation &Animazione &Zoom &Zoom &View &Vedi &%1 %2 &%1 %2 The document %1 has been modified. Do you want to save your changes? Il documento %1 e stato modificato. Vuoi salvare le modifche? Ctrl+P Ctrl+R &Flag &Bandiera &Help &Aiuto Choose &Language &Scegli lingua Open Scenario File Apri scenario xmlscenario Files (*.xbs) xmlscenario Files(*.xbs) File loaded File caricato Save Scenario Salva scenario %1 already exists. Do you want to replace it? %1 esiste gia. Vuoi rimpiazzarlo? Cannot write file %1: %2. Non posso escrivere il file %1: %2. File saved File salvato Print Document Stampa documento Export Image Esportare Image Export Animation Esportare Animazione Exporting Animation... Esportando l'animazione... Abort Anulla About Boat Scenario In merito allo scenario della barca Alt+S Alt+Z &Hide Nascondi Alt+D Trim Sail Regola Vele < Auto Trim Auto Regola = Untrim Sail Molla Vele > &Acceleration Accelerazione Toggle &Laylines Mostra Laylines Alt+L Set Mark &Color Imposta &Colore Boa Alt+C Toggle &Mark Side &Inverti lato della boa Alt+M Toggle Mark Arro&w Mostra &Freccia Alt+W Toggle Mark &Label Mostra &Nome boa &Edit Mark Label &Edita nome boa Alt+E Edit mark label Edita nome boa Label text: Casella di testo Trim Jib , Auto Jib ? Untrim Jib . Trim Spin Ctrl+, Auto Spin Ctrl+? Untrim Spin Ctrl+. QObject The file is not an xmlscenario version 1.0 file. Il file non e uno xmlscenario versione 1.0. Laser Optimist Tornado Committee boat Barca Comitato RIB Gommone Spin Keelboat Barcha a chiglia con Spinnaker Gennaker Keelboat Barcha a chiglia con Gennaker Topper Firefly 29er 49er 420 470 RS Feva Finn Nacra 17 Diam 24 SituationModel Protest! Protesto! SituationPrint Rule Regola SituationWidget Options Opzioni Series Serie Zone Length Lunghezza Zona Tracks Tracce Scenario Scenario Title Ti­tolo Rules Regole Abstract Estratto Description Descrizione Wind Vento Show Wind Mostra Vento Show Grid Mostra Griglia Layline Angle Angolo Layline TrackTableModel Color Colore Path Percorso Series Serie Follow Segue WindTableModel Index Indice Direction Direzione Delete Cancele Linea boats-201908/locale/boats_ja.ts000066400000000000000000000502401353032755300163270ustar00rootroot00000000000000 ArrowGraphicsItem Wind 風情報 MainWindow &New File æ–°è¦(&N) Ctrl+N &Open File... é–‹ã(&O) Ctrl+O &Save File 上書ãä¿å­˜(&S) Ctrl+S Save &As... åå‰ã‚’付ã‘ã¦ä¿å­˜(&A) Ctrl+Shift+S New &Tab æ–°ã—ã„タブ(&T) Ctrl+T &Close Tab タブを閉ã˜ã‚‹(&C) Ctrl+W &Restore Last Session... 最後ã®ã‚»ãƒƒã‚·ãƒ§ãƒ³ã‚’å†é–‹(&R) &Print... å°åˆ·(&P) Print P&review... å°åˆ·ãƒ—レビュー(&R) &Export Pdf... Pdfã¨ã—ã¦ä¿å­˜(&E) Ctrl+E Export &Image... ç”»åƒã¨ã—ã¦ä¿å­˜(&I) Ctrl+I Export Ani&mation... アニメã¨ã—ã¦ä¿å­˜(&M) Ctrl+V E&xit 終了(&X) Ctrl+Q Exit the application アプリケーションを終了 Create &Track 航跡を作æˆ(&T) Ctrl+Ins Create &Boat 艇を付加(&B) Ins Create &Mark マークを作æˆ(&M) Alt+Ins Create &PolyLine æ–°ã—ã„ç·š(&P) Ctrl+Alt+Ins Create Poin&t ç·šã‚’å»¶é•·(&T) &Port Overlap オーãƒãƒ¼ãƒ©ãƒƒãƒ— ãƒãƒ¼ãƒˆ(&P) Alt+< &Starboard Overlap オーãƒãƒ¼ãƒ©ãƒƒãƒ— スターボ(&S) Alt+> &Text テキスト(&T) Alt+T Toggle &Spinnaker スピãƒãƒ¼ã‚«ãƒ¼è¡¨ç¤º/éžè¡¨ç¤º(&S) Toggle Mark &Zone マークゾーン表示/éžè¡¨ç¤º(&Z) Delete Track 航跡を消去 Ctrl+Del &Delete Selection é¸æŠžã—ãŸã‚¢ã‚¤ãƒ†ãƒ ã‚’消去(&D) Del &Animate アニメを作æˆ(&A) Ctrl+A &Play å†ç”Ÿ(&P) P &Pause ä¸€æ™‚åœæ­¢(&M) M &Stop åœæ­¢(&S) Space &Loop 繰り返ã—(&L) L &Undo å…ƒã«æˆ»ã™(&U) Ctrl+Z &Redo やり直ã™(&R) Ctrl+Y Shift+Ctrl+Z Zoom &In ズームイン(&I) Ctrl++ Zoom &Out ズームアウト(&O) Ctrl+- Zoom &Fit ウィンドウã«åˆã‚ã›ã‚‹(&F) Ctrl+F Main Toolbar メインツールãƒãƒ¼ Animation Toolbar アニメツールãƒãƒ¼ Scenario Dock シナリオドック &About %1 - %2 [*] Boat Scenario &Recent 最近開ã„ãŸãƒ•ァイル(&R) &File ファイル(&F) &Edit 編集(&E) &History 履歴(&H) &Animation アニメ(&A) &Zoom ズーム(&Z) &View 表示(&V) &%1 %2 The document %1 has been modified. Do you want to save your changes? Ctrl+P Ctrl+R &Flag æ——(&F) &Help ヘルプ(&H) Choose &Language Open Scenario File xmlscenario Files (*.xbs) File loaded Save Scenario %1 already exists. Do you want to replace it? Cannot write file %1: %2. File saved ä¿å­˜ã•れã¾ã—㟠Print Document å°åˆ· Export Image ç”»åƒã¨ã—ã¦ä¿å­˜ Export Animation アニメã¨ã—ã¦ä¿å­˜ Exporting Animation... 出力ã—ã¦ã„ã¾ã™ Abort About Boat Scenario BoatScenarioã«ã¤ã„㦠Alt+S Alt+Z &Hide 艇を隠ã™(&H) Alt+D Trim Sail セールを引ã込む < Auto Trim セールをトリム(自動) = Untrim Sail セールを緩ã‚ã‚‹ > &Acceleration 加速(&A) Toggle &Laylines レイライン表示/éžè¡¨ç¤º(&L) Alt+L Set Mark &Color マーク:色(&C) Alt+C Toggle &Mark Side マーク:ãƒãƒ¼ãƒˆ/スターボå´(&M) Alt+M Toggle Mark Arro&w 次ã®ãƒžãƒ¼ã‚¯(&W) Alt+W Toggle Mark &Label マークå表示/éžè¡¨ç¤º(&L) &Edit Mark Label マークå編集(&E) Alt+E Edit mark label マークå編集 Label text: マークå: Trim Jib , Auto Jib ? Untrim Jib . Trim Spin Ctrl+, Auto Spin Ctrl+? Untrim Spin Ctrl+. QObject The file is not an xmlscenario version 1.0 file. Laser レーザー Optimist OP Tornado トーãƒãƒ¼ãƒ‰ Committee boat é‹å–¶è‰‡ RIB ゴムボート Spin Keelboat キールボート(スピン) Gennaker Keelboat キールボート(ジェãƒã‚«ãƒ¼ï¼‰ Topper トッパー Firefly ファイヤーフライ 29er 49er 420 470 RS Feva Finn Nacra 17 Diam 24 SituationModel Protest! プロテスト! SituationPrint Rule è¦å‰‡ SituationWidget Options オプション Series 艇種 Zone Length ゾーンã®åŠå¾„ Tracks 航跡 Scenario シナリオ Title 題å Rules è¦å‰‡ Abstract è¦ç´„ Description 説明 Wind 風情報 Show Wind 風を表示 Show Grid グリッド表示 Layline Angle レイラインã®è§’度 TrackTableModel Color 色 Path 航跡表示 Series 艇種 Follow 追跡視点 WindTableModel Index 一覧 Direction é¢¨å‘ Delete 消去 boats-201908/locale/boats_nl.ts000066400000000000000000000502651353032755300163550ustar00rootroot00000000000000 ArrowGraphicsItem Wind Wind MainWindow &New File &Nieuw Bestand Ctrl+N Ctrl+N &Open File... &Open Bestand... Ctrl+O Ctrl+O &Save File &Bestand Opslaan Ctrl+S Ctrl+S Save &As... Opslaan &Als... Ctrl+Shift+S Ctrl+Shift+S New &Tab Nieuw &Tabblad Ctrl+T Ctrl+T &Close Tab &Sluit Tabblad Ctrl+W Ctrl+W &Restore Last Session... &Laatste Sessie Terughalen... &Print... &Afdrukken... Print P&review... A&fdrukvoorbeeld... &Export Pdf... &Opslaan als Pdf... Ctrl+E Ctrl+E Export &Image... Opslaan als &Afbeelding... Ctrl+I Ctrl+I Export Ani&mation... Opslaan als Ani&matie... Ctrl+V Ctrl+V E&xit A&fsluiten Ctrl+Q Ctrl+Q Exit the application Sluit de toepassing af Create &Track &Koers Ctrl+Ins Ctrl+Ins Create &Boat &Boot Ins Ins Create &Mark &Boei Alt+Ins Alt+Ins Create &PolyLine &Polylijn Ctrl+Alt+Ins Ctrl+Alt+Ins Create Poin&t Pun&t Polyline &Port Overlap &Bakboord Overlap Alt+< Alt+< &Starboard Overlap &Stuurboord Overlap Alt+> Alt+> &Text &Tekst Alt+T Alt+T Toggle &Spinnaker Toggle &Spinnaker Toggle Mark &Zone Toggle Boei &Zone Delete Track Verwijder Koers Ctrl+Del Ctrl+Del &Delete Selection &Verwijder Selectie Del Del &Animate &Animatie Ctrl+A Ctrl+A &Play &AfSpelen P P &Pause &Pauze M M &Stop &Stop Space Afstand &Loop &Continue afspelen L L &Undo &Ongedaan maken Ctrl+Z Ctrl+Z &Redo &Opnieuw maken Ctrl+Y Ctrl+Y Shift+Ctrl+Z Shift+Ctrl+Z Zoom &In Zoom &In Ctrl++ Ctrl++ Zoom &Out Zoom &Uit Ctrl+- Ctrl+- Zoom &Fit Zoom &Passend Ctrl+F Ctrl+F Main Toolbar Hoofd Werkbalk Animation Toolbar Animatie Werkbalk Scenario Dock Scenario Station &About &Over %1 - %2 [*] %1 - %2 [*] Boat Scenario Boot Scenario &Recent &Recent geopend &File &Bestand &Edit &Bewerken &History &Historie &Animation &Animatie &Zoom &Zoom &View &Bekijken &%1 %2 &%1 %2 The document %1 has been modified. Do you want to save your changes? Het document %1 is gewijzigd. Wil u de wijzigingen opslaan? Ctrl+P Ctrl+P Ctrl+R Ctrl+R &Flag &Vlag &Help &Help Choose &Language Kies &Taal Open Scenario File Open Scenario Bestand xmlscenario Files (*.xbs) xmlscenario Bestanden (*.xbs) File loaded Bestand geopend Save Scenario Bewaar Scenario %1 already exists. Do you want to replace it? %1 bestaat al. Wilt u het vervangen? Cannot write file %1: %2. Kant het bestand niet opslaan %1: %2. File saved Bestand opgeslagen Print Document Document Afdrukken Export Image Opslaan als Afbeelding Export Animation Opslaan als Animatie Exporting Animation... Opslaan Animatie... Abort Afsluiten About Boat Scenario Info Boat Scenario Alt+S Alt+S Alt+Z Alt+Z &Hide &Verberg Alt+D Alt+D Trim Sail Schoot Aan < < Auto Trim Auto Trim = = Untrim Sail Schoot Vieren > > &Acceleration &Versnelling Toggle &Laylines Toggle &Laylines Alt+L Alt+L Set Mark &Color Set Merkteken &Kleur Alt+C Alt+C Toggle &Mark Side Toggle &Merkteken Kant Alt+M Alt+M Toggle Mark Arro&w Toggle Merkteken Pij&l Alt+W Alt+L Toggle Mark &Label Toggle Merkteken &Label &Edit Mark Label Bewerk Merkteken Label Alt+E Alt+B Edit mark label Bewerk merkteken label Label text: Label tekst: Trim Jib , Auto Jib ? Untrim Jib . Trim Spin Ctrl+, Auto Spin Ctrl+? Untrim Spin Ctrl+. QObject The file is not an xmlscenario version 1.0 file. Dit is geen xmlscenario versie 1.0 bestand. Laser Laser Optimist Optimist Tornado Tornado Committee boat Comit\cf1\f1\'e9boot\cf0\f0 RIB Rubberboot Spin Keelboat Spin Kielboot Gennaker Keelboat Gennaker Kielboot Topper Topper Firefly Firefly 29er 29er 49er 49er 420 420 470 470 RS Feva Finn Nacra 17 Diam 24 SituationModel Protest! Protest! SituationPrint Rule Regel SituationWidget Options Opties Series Series Zone Length Zone Lengten Tracks Koersen Scenario Scenario Title Titel Rules Regels Abstract Kort overzicht Description Omschrijving Wind Wind Show Wind Toon Wind Show Grid Toon Rooster Layline Angle Layline Hoek TrackTableModel Color Kleur Path Baan Series Serie Follow Volg WindTableModel Index Index Direction Richting Delete Verwijder boats-201908/locale/boats_pl.ts000066400000000000000000000476241353032755300163640ustar00rootroot00000000000000 ArrowGraphicsItem Wind Wiatr MainWindow &New File &Nowy plik Ctrl+N &Open File... O&tworz plik... Ctrl+O Ctrl+B &Save File &Zapisz plik Ctrl+S Ctrl+G Save &As... Zapiz &jako... Ctrl+Shift+S Ctrl+Shift+C New &Tab Nowa &zakladka Ctrl+T Ctrl+S &Close Tab Z&amknij zakladke Ctrl+W Ctrl+E &Restore Last Session... &Przywroc poprzednia sesje... &Print... &Drukuj... Print P&review... Podglad w&ydruku... &Export Pdf... &Eksportuj Pdf... Ctrl+E Export &Image... Eksportuj ob&raz... Ctrl+I Ctrl+G Export Ani&mation... Eksportuj Ani&macje... Ctrl+V E&xit Wy&jdz Ctrl+Q Ctrl+Q Exit the application Wydjz z aplikacji Create &Track Utworz &Trase Ctrl+Ins Create &Boat Utworz &lodke Ins Create &Mark Utworz &znak Alt+Ins Create &PolyLine Utworz &linie Ctrl+Alt+Ins Create Poin&t Utworz pun&kt &Port Overlap Krycie na &lewym halsie Alt+< &Starboard Overlap Krycie na &prawym halsie Alt+> &Text &Tekst Alt+T Toggle &Spinnaker Wstaw &spinaker Toggle Mark &Zone &Wstaw strefe przy znaku Delete Track Usun trase Ctrl+Del &Delete Selection Usu&n sesje Del &Animate &Animuj Ctrl+A &Play &Graj P I &Pause &Pauza M P &Stop Z&atrzymaj Space &Loop &Petla L B &Undo C&ofnij Ctrl+Z &Redo &Przywroc Ctrl+Y Shift+Ctrl+Z Zoom &In P&owieksz Ctrl++ Zoom &Out Pom&niejsz Ctrl+- Zoom &Fit Do&pasuj obraz Ctrl+F Ctrl+U Main Toolbar Glowny pasek narzedzi Animation Toolbar Pasek animacji Scenario Dock Miejsce na opis &About &O %1 - %2 [*] Boat Scenario &Recent &Ostatni &File &Plik &Edit &Edytuj &History &Historia &Animation &Animacja &Zoom &Zoom &View &Widok &%1 %2 The document %1 has been modified. Do you want to save your changes? Dokument zostal %1 zsmodyfikowany. Czy chcesz zapisac zmiany? Ctrl+P Ctrl+R &Flag &Flaga &Help &Pomoc Choose &Language &Wybierz jezyk Open Scenario File Otworz scenariusz pliku xmlscenario Files (*.xbs) Pliku xmlscenario (*.xbs) File loaded Plik zaladowany Save Scenario Zapisz scenariusz %1 already exists. Do you want to replace it? %1 Juz istnieje. Czy chcesz go zastapic? Cannot write file %1: %2. Nie mozna zapisac pliku %1: %2. File saved Pilk zapisany Print Document Drukuj dokument Export Image Eksportuj obraz Export Animation Eksportuj animacje Exporting Animation... Eksportuje animacje... Abort Zatrzymanie About Boat Scenario O programie Alt+S Alt+Z &Hide &Ukryj Alt+D Trim Sail Wybierz Å»agiel < Auto Trim Auto Trym = Untrim Sail Poluzug Å»agiel > &Acceleration Przyspieszenie Toggle &Laylines Wyznacz &Laylines Alt+L Set Mark &Color Ustaw &Kolor znaku Alt+C Toggle &Mark Side Przełącz &StronÄ™ znaku Alt+M Toggle Mark Arro&w Przełącz &StrzaÅ‚kÄ™ znaku Alt+W Toggle Mark &Label Przełącz &EtykietÄ™ znaku &Edit Mark Label &Edytuj etykietÄ™ znaku Alt+E Edit mark label Edytuj etykietÄ™ znaku Label text: Tekst etykiety Trim Jib , Auto Jib ? Untrim Jib . Trim Spin Ctrl+, Auto Spin Ctrl+? Untrim Spin Ctrl+. QObject The file is not an xmlscenario version 1.0 file. Plik nie jest plikiem xmlscenario version 1.0. Laser Optimist Tornado Committee boat Tatek komisji RIB Ponton Spin Keelboat Jacht Kilowy ze Spinakerem Gennaker Keelboat Jacht Kilowy ze Genakerem Topper Firefly 29er 49er 420 470 RS Feva Finn Nacra 17 Diam 24 SituationModel Protest! Protest! SituationPrint Rule Przepis SituationWidget Options Opcje Series Serie Zone Length Duglosc strefy Tracks Trasy Scenario Scenariusz Title Tytul Rules Przepisy Abstract Podsumowanie Description Opis Wind Wiatr Show Wind Pokaz strzalke wiatru Show Grid Pokaz siatke pola Layline Angle Kat layline TrackTableModel Color Kolor Path Sciezka Series Serie Follow Podążaj WindTableModel Index Index Direction Kierunek Delete Usun boats-201908/locale/boats_pt.ts000066400000000000000000000500201353032755300163540ustar00rootroot00000000000000 ArrowGraphicsItem Wind Vento MainWindow &New File &Novo ficheiro Ctrl+N &Open File... A&brir ficheiro... Ctrl+O Ctrl+B &Save File &Salvar ficheiro Ctrl+S Ctrl+G Save &As... Salvar &como... Ctrl+Shift+S Ctrl+Shift+C New &Tab Nova &etiqueta Ctrl+T Ctrl+S &Close Tab F&echa etiqueta Ctrl+W Ctrl+E &Restore Last Session... &Restaurar ultima sessao... &Print... &Imprimir... Print P&review... Visualizaçao de &impressao... &Export Pdf... &Exportar Pdf... Ctrl+E Export &Image... Exportar Ima&gem... Ctrl+I Ctrl+G Export Ani&mation... Exportar Ani&maçao... Ctrl+V E&xit Fe&char Ctrl+Q Exit the application Fechar aplicaçao Create &Track Criar &Trajecto Ctrl+Ins Create &Boat Criar &Barco Ins Create &Mark Criar &Baliza Alt+Ins Create &PolyLine Criar &linha multipla Ctrl+Alt+Ins Create Poin&t Criar Pon&to &Port Overlap Sobreladeamento a &Bombordo Alt+< &Starboard Overlap Sobreladeamento a &Estribordo Alt+> &Text &Texto Alt+T Toggle &Spinnaker Activar &Spinnaker Toggle Mark &Zone &Activar area de baliza Delete Track Cancelar trajecto Ctrl+Del &Delete Selection Cancela&r Selecçao Del &Animate &Animaçao Ctrl+A &Play &Executar P I &Pause &Pausa M &Stop &Parar Space &Loop &Volta L B &Undo &Desfazer Ctrl+Z &Redo &Volta a fazer Ctrl+Y Shift+Ctrl+Z Zoom &In A&mpliar Ctrl++ Zoom &Out R&eduzir Ctrl+- Zoom &Fit A&justar Ctrl+F Ctrl+U Main Toolbar Barra de ferramenta principal Animation Toolbar Barra de Animaçao Scenario Dock Controle de cenario &About &Acerca de %1 - %2 [*] Boat Scenario &Recent &Recente &File &Ficheiro &Edit &Editar &History &Historico &Animation &Animaçao &Zoom &Zoom &View &Vista &%1 %2 The document %1 has been modified. Do you want to save your changes? Este documento %1 foi alterado. Deseja salvar as alteraçoes? Ctrl+P Ctrl+R &Flag &Bandeira &Help &Ajuda Choose &Language &Escolher idioma Open Scenario File Abrir ficheiro de cenario xmlscenario Files (*.xbs) xmlscenario Files(*.xbs) File loaded Ficheiro baixado Save Scenario Salvar cenario %1 already exists. Do you want to replace it? %1 Ja existe. Deseja substitui-lo? Cannot write file %1: %2. Nao e possivel escrever o ficheiro %1: %2. File saved Ficheiro salvo Print Document Imprimir documento Export Image Exportar Imagem Export Animation Exportar Animaçao Exporting Animation... Exportando l'animaçao... Abort Abortar About Boat Scenario Acerca do cenario de barco Alt+S Alt+Z &Hide &Occultar Barco Alt+D Trim Sail Caçar Vela < Auto Trim Velas Trimadas = Untrim Sail Folgar Vela > &Acceleration Acelerçao Toggle &Laylines Mostrar Laylines Alt+L Set Mark &Color Escolher &Cor da baliza Alt+C Toggle &Mark Side Activar &Lado da baliza Alt+M Toggle Mark Arro&w Activar &Seta da baliza Alt+W Toggle Mark &Label Activar &Etiqueta da baliza &Edit Mark Label Editar &Etiqueta da baliza Alt+E Edit mark label Editar Etiqueta da baliza Label text: Texto da etiqueta: Trim Jib , Auto Jib ? Untrim Jib . Trim Spin Ctrl+, Auto Spin Ctrl+? Untrim Spin Ctrl+. QObject The file is not an xmlscenario version 1.0 file. Este ficheiro nao e um xmlscenario versao 1.0. Laser Optimist Tornado Committee boat Barco da CR RIB Pneumatico Spin Keelboat Barca con Spi simétrico Gennaker Keelboat Barca con Spi assimétrico Topper Firefly 29er 49er 420 470 RS Feva Finn Nacra 17 Diam 24 SituationModel Protest! Protesto! SituationPrint Rule Regra SituationWidget Options Opçoes Series Series Zone Length Comprimento da area Tracks Trajectorias Scenario Cenario Title Ti­tulo Rules Regras Abstract Extrato Description Descriçao Wind Vento Show Wind Mostrar Vento Show Grid Mostrar Grelha Layline Angle Angulo Layline TrackTableModel Color Cor Path Percurso Series Series Follow Seguir WindTableModel Index Indice Direction Direcçao Delete Apagar boats-201908/locale/boats_ru.ts000066400000000000000000000513541353032755300163720ustar00rootroot00000000000000 ArrowGraphicsItem Wind ветер MainWindow &New File Создать Ctrl+N &Open File... Открыть... Ctrl+O &Save File Сохранить Ctrl+S Save &As... Сохранить как... Ctrl+Shift+S New &Tab ÐÐ¾Ð²Ð°Ñ Ð²ÐºÐ»Ð°Ð´ÐºÐ° Ctrl+T &Close Tab Закрыть вкладку Ctrl+W &Restore Last Session... ВоÑÑтановить предыдущий ÑеанÑ... &Print... Печать... Print P&review... Предварительный проÑмотр... &Export Pdf... ЭкÑпорт в PDF-файл... Ctrl+E Export &Image... ЭкÑпорт в файл изображениÑ... Ctrl+I Export Ani&mation... ЭкÑпорт анимации... Ctrl+V E&xit Выход Ctrl+Q Exit the application Create &Track Создать траекторию Ctrl+Ins Ctrl+Ins Create &Boat Создать лодку Ins Ins Create &Mark Создать знак Alt+Ins Alt+Ins Create &PolyLine Создать линию Ctrl+Alt+Ins Ctrl+Alt+Ins Create Poin&t Создать точку &Port Overlap СвÑзанноÑть Ñлева Alt+< Alt+< &Starboard Overlap СвÑзанноÑть Ñправа Alt+> Alt+> &Text ТекÑÑ‚ Alt+T Alt+T Toggle &Spinnaker ÐариÑовать Ñпинакер Toggle Mark &Zone Обозначить зону у знака Delete Track Удалить траекторию Ctrl+Del Ctrl+Del &Delete Selection Удалить выбранные объекты Del Del &Animate ÐÐ½Ð¸Ð¼Ð°Ñ†Ð¸Ñ Ctrl+A Ctrl+A &Play Старт P P &Pause Пауза M M &Stop Стоп Space &Loop Ð’ цикле L L &Undo Отменить Ctrl+Z Ctrl+Z &Redo Вернуть Ctrl+Y Ctrl+Y Shift+Ctrl+Z Shift+Ctrl+Z Zoom &In Увеличить Ctrl++ Ctrl++ Zoom &Out Уменьшить Ctrl+- Ctrl+- Zoom &Fit Ð’ размер Ñтраницы Ctrl+F Ctrl+F Main Toolbar ОÑÐ½Ð¾Ð²Ð½Ð°Ñ Ð¿Ð°Ð½ÐµÐ»ÑŒ инÑтрументов Animation Toolbar Панель инÑтрументов ÐÐ½Ð¸Ð¼Ð°Ñ†Ð¸Ñ Scenario Dock Панель Ð¼Ð¾Ð´ÐµÐ»Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ &About О программе %1 - %2 [*] %1 - %2 [*] Boat Scenario &Recent ПоÑледние &File Файл &Edit Редактирование &History ИÑÑ‚Ð¾Ñ€Ð¸Ñ &Animation ÐÐ½Ð¸Ð¼Ð°Ñ†Ð¸Ñ &Zoom МаÑштаб &View ПроÑмотр &%1 %2 &%1 %2 The document %1 has been modified. Do you want to save your changes? Файл был изменен. Сохранить изменениÑ? Ctrl+P Ctrl+R &Flag Флаг &Help Помощь Choose &Language Выбрать Ñзык интерфейÑа Open Scenario File xmlscenario Files (*.xbs) Файл xmlscenario (*.xbs) File loaded Ôàéë çàãðóæåí Save Scenario Сохранить %1 already exists. Do you want to replace it? Cannot write file %1: %2. %1: %2. File saved Print Document Export Image Export Animation Exporting Animation... ... Abort About Boat Scenario О Boat Scenario Alt+S Alt+Z &Hide Скрыть Alt+D Trim Sail Добрать Ð¿Ð°Ñ€ÑƒÑ < Auto Trim ÐвтонаÑтройка паруÑов = Untrim Sail Потравить Ð¿Ð°Ñ€ÑƒÑ > &Acceleration СкороÑть Toggle &Laylines Обозначить Laylines Alt+L Set Mark &Color Изменить цвет знака Alt+C Toggle &Mark Side Изменить направление Ð¾Ð³Ð¸Ð±Ð°Ð½Ð¸Ñ Ð·Ð½Ð°ÐºÐ° Alt+M Toggle Mark Arro&w Ðаправление Ð¾Ð³Ð¸Ð±Ð°Ð½Ð¸Ñ Ð·Ð½Ð°ÐºÐ° Alt+W Toggle Mark &Label Обозначить номер знака &Edit Mark Label Изменить номер знака Alt+E Edit mark label Изменить номер знака Label text: ТекÑÑ‚: Trim Jib , Auto Jib ? Untrim Jib . Trim Spin Ctrl+, Auto Spin Ctrl+? Untrim Spin Ctrl+. QObject The file is not an xmlscenario version 1.0 file. Äàííûé ôàéë íå ÿâëÿåòñÿ ôàéëîì xmlscenario 1.0. Laser Optimist Tornado Committee boat Судно Гоночного комитета RIB Катер Spin Keelboat ÐšÐ¸Ð»ÐµÐ²Ð°Ñ Ñхта Ñо Ñпинакером Gennaker Keelboat ÐšÐ¸Ð»ÐµÐ²Ð°Ñ Ñхта Ñ Ð³ÐµÐ½Ð½Ð°ÐºÐµÑ€Ð¾Ð¼ Topper Firefly 29er 49er 420 470 RS Feva Finn Nacra 17 Diam 24 SituationModel Protest! ! SituationPrint Rule Правила SituationWidget Options Параметры Series КлаÑÑ Zone Length Длина зоны Tracks Траектории Scenario Ð¡Ð¸Ñ‚ÑƒÐ°Ñ†Ð¸Ñ Title Заголовок Rules Правила Abstract Краткое опиÑание Description ОпиÑание Wind ветер Show Wind Показать ветер Show Grid Показать Ñетку Layline Angle Угол Layline TrackTableModel Color Цвет Path Отобразить Series КлаÑÑ Follow Следовать курÑу WindTableModel Index â„– Direction Ðаправление Delete Удалить boats-201908/locale/boats_sl.ts000066400000000000000000000500041353032755300163510ustar00rootroot00000000000000 ArrowGraphicsItem Wind Veter MainWindow &New File &Nova datoteka Ctrl+N &Open File... &Odpri datoteko... Ctrl+O &Save File &Shrani datoteko Ctrl+S Save &As... Shrani &kot... Ctrl+Shift+S New &Tab Nov &zavihek Ctrl+T &Close Tab &Zapri zavihek Ctrl+W &Restore Last Session... &Obnovi zadnjo sejo... &Print... &Natisni... &Export Pdf... &Izvozi PDF... Ctrl+E Export &Image... Izvozi &sliko... Ctrl+I Export Ani&mation... Izvozi &animacijo... Ctrl+V Ctrl+Q Exit the application Izhod iz programa Create &Track Dodaj &sled Ctrl+Ins Create &Boat Dodaj &plovilo Ins Create &Mark Dodaj &oznako Alt+Ins &Port Overlap Prekrivanje na &levi Alt+< &Starboard Overlap Prekrivanje na &desni Alt+> &Text &Besedilo Alt+T Toggle Mark &Zone Prikaži/skrij &cono oznake Delete Track IzbriÅ¡i sled Ctrl+Del &Delete Selection &IzbriÅ¡i izbrano Del &Animate &Animacija Ctrl+A &Play &Predvajaj P &Pause &Premor M &Stop &Ustavi Space Presledek &Loop &Neprekinjeno predvajanje L &Undo &Razveljavi Ctrl+Z &Redo &Ponovi Ctrl+Y Shift+Ctrl+Z Zoom &In Po&eÄaj Ctrl++ Zoom &Out Po&manjÅ¡aj Ctrl+- Zoom &Fit &Prikaži vse Ctrl+F Main Toolbar Glavna orodna vrstica Animation Toolbar Animacijska orodna vrstica Scenario Dock Scenarij &About &Vizitka %1 - %2 [*] Boat Scenario Boat Scenario &Recent &Nazadnje urejano &File &Datoteka &Edit &Uredi &History &Zgodovina &Animation &Animacija &Zoom PoveÄava &View &Pogled &%1 %2 The document %1 has been modified. Do you want to save your changes? Datoteka %1 je bila spremenjena. Ali želite shraniti spremembe? Ctrl+P Ctrl+R &Flag &Zastava &Help &PomoÄ Choose &Language Izberi &jezik Open Scenario File Odpri datoteko s scenarijem xmlscenario Files (*.xbs) Datoteke xmlscenario (*.xbs) File loaded Datoteka je naložena Save Scenario Shrani scenarij %1 already exists. Do you want to replace it? Datoteka %1 že obstaja. Ali jo želite prepisati? Cannot write file %1: %2. Ne morem shraniti datoteke %1: %2. File saved Datoteka je shranjena Print Document Natisni dokument Export Image Izvozi sliko Export Animation Izvozi animacijo Exporting Animation... Izvažam animacijo... Abort Prekini About Boat Scenario O programu Boat Scenario Print P&review... Predogled &tiskanja... E&xit &Izhod Create &PolyLine Ustvari &Ärto Ctrl+Alt+Ins Create Poin&t Ustvari &toÄko Toggle &Spinnaker Prikaži/skrij &Å¡pinaker Alt+S Alt+Z &Hide &Skrij Alt+D Trim Sail Nastavi trim jadra < < Auto Trim Samodejno nastavljanje trima = = Untrim Sail Odstrani roÄni trim jadra > > &Acceleration &PospeÅ¡ek Toggle &Laylines Prikaži/skrij layline Alt+L Toggle &Mark Side Prikaži/skrij stran oznake Alt+M Toggle Mark Arro&w Prikaži/skrij puÅ¡Äico Alt+W Set Mark &Color Nastavi barvo oznake Alt+C Toggle Mark &Label Prikaži/skrij opis oznake &Edit Mark Label Uredi opis oznake Alt+E Edit mark label Uredi opis oznake Label text: Opis: Trim Jib , Auto Jib ? Untrim Jib . Trim Spin Ctrl+, Auto Spin Ctrl+? Untrim Spin Ctrl+. QObject The file is not an xmlscenario version 1.0 file. Datoteka ni tipa xmlscenario razliÄice 1.0. Spin Keelboat Jadrnica s Å¡pinakerjem Gennaker Keelboat Jadrnica z gennakerjem 49er 49er 470 470 420 420 29er 29er Laser Laser Firefly Firefly Topper Topper Optimist Optimist Tornado tornado Committee boat Regatna barka RIB Gumijast Äoln RS Feva Finn Nacra 17 Diam 24 SituationModel Protest! SituationPrint Rule Pravilo SituationWidget Options Možnosti Series Razred Zone Length Velikost cone Tracks Sledi Scenario Scenarij Title Naslov Rules Pravila Abstract Povzetek Description Opis Wind Veter Show Wind Prikaži smer vetra Show Grid Prikaži mrežo Layline Angle Kot za layline TrackTableModel Color Barva Path Sled Series Razred Follow Sledi WindTableModel Index Položaj Direction Smer Delete IzbriÅ¡i boats-201908/locale/boats_sv.ts000066400000000000000000000503231353032755300163670ustar00rootroot00000000000000 ArrowGraphicsItem Wind Vind MainWindow &New File &Ny fil Ctrl+N &Open File... &Öppna fil... Ctrl+O &Save File &Spara Ctrl+S Save &As... Spara s&om... Ctrl+Shift+S New &Tab Ny &flik Ctrl+T &Close Tab Stäng fli&k Ctrl+W &Restore Last Session... &Ã…terskapa senaste session... &Print... Skri&v ut... Print P&review... Fö&rhandsgranska utskrift... &Export Pdf... &Exportera PDF... Ctrl+E Export &Image... Exportera b&ild... Ctrl+I Export Ani&mation... Exportera ani&mation... Ctrl+V Ctrl+V E&xit &Avsluta Ctrl+Q Exit the application Avsluta programmet Create &Track Skapa &spÃ¥r Ctrl+Ins Create &Boat Skapa &bÃ¥t Ins Create &Mark Skapa &märke Alt+Ins &Port Overlap Överlapp till b&abord Alt+< &Starboard Overlap Överlapp till st&yrbord Alt+> Toggle Mark &Zone Märkes&zonen pÃ¥ och av Delete Track Radera spÃ¥r Ctrl+Del &Delete Selection Ra&dera markerade Del &Animate &Animera Ctrl+A &Play S&pela upp P &Pause &Paus M &Stop &Stanna Space &Loop S&linga L &Undo &Ã…ngra Ctrl+Z &Redo &Gör om Ctrl+Y Shift+Ctrl+Z Zoom &In Zooma &in Ctrl++ Zoom &Out Zooma &ut Ctrl+- Zoom &Fit &Anpassa zoom Ctrl+F Main Toolbar Huvudverktygsfält Animation Toolbar Animeringsverktygsfält Scenario Dock Scenario fält &About &Om %1 - %2 [*] Boat Scenario &Recent N&yligen &File &Fil &Edit R&edigera &History &Historia &Animation &Animering &Zoom &Zoom &View &Visa &%1 %2 The document %1 has been modified. Do you want to save your changes? Dokumentet %1 har ändrats. Vill du spara dina ändringar? Open Scenario File Öppna Scenario-fil xmlscenario Files (*.xbs) xmlscenario filer (*.xbs) File loaded Filen inläst Save Scenario Spara Scenario %1 already exists. Do you want to replace it? %1 finns redan. Vill du ersätta den? Cannot write file %1: %2. Kan inte skriva filen %1:%2. File saved Filen sparad Print Document Skriv ut dokument Export Image Exportera bild Export Animation Exportera animation Exporting Animation... Exporterar animation... Abort Avbryt About Boat Scenario Om Boat Scenario Ctrl+P Ctrl+R &Text Alt+T &Flag &Flagga &Help &Hjälp Choose &Language Välj &sprÃ¥k Create &PolyLine Skapa &polylinje Ctrl+Alt+Ins Create Poin&t Skapa punk&t Toggle &Spinnaker &Spinnaker pÃ¥ och av Alt+S Alt+Z &Hide Could not find a phrase including the letter H. &Göm Alt+D Alt+D Trim Sail Alternatively this could be only Skota, but then we run into trouble with the words for easing the sheet. Skota in < < Auto Trim Auto Trim = = Untrim Sail Skota ut is not really good Swedish, but the correct term "slacka pÃ¥ skotet" is probably too long in this context. Skota ut > > &Acceleration &Acceleration Toggle &Laylines &Laylines pÃ¥ och av Alt+L Alt+L Set Mark &Color Ändra märkets &Färg Alt+C Toggle &Mark Side &Byt märkessida Alt+M Toggle Mark Arro&w Märke&spil pÃ¥ och av Alt+W Toggle Mark &Label Märkes&etikett pÃ¥ och av &Edit Mark Label &Redigera märkesetikett Alt+E Edit mark label Redigera märkesetikett Label text: Etikettext Trim Jib , Auto Jib ? Untrim Jib . Trim Spin Ctrl+, Auto Spin Ctrl+? Untrim Spin Ctrl+. QObject The file is not an xmlscenario version 1.0 file. Den här filen är inte xmlscenario version 1.0. Laser Optimist Tornado Committee boat Startfartyg RIB Spin Keelboat Spinnaker-kölbÃ¥t Gennaker Keelboat Gennaker-kölbÃ¥t Topper Firefly 29er 49er 420 470 RS Feva Finn Nacra 17 Diam 24 SituationModel Protest! SituationPrint Rule Regel SituationWidget Options Inställningar Series BÃ¥ttyp Zone Length Zonens storlek Tracks SpÃ¥r Scenario Scenario Title Titel Rules Regler Abstract Sammanfattning Description Beskrivning Wind Vind Show Wind Visa vinden Show Grid Visa rutnät Layline Angle Kryssvinkel TrackTableModel Color Färg Series BÃ¥ttyp Path SpÃ¥r Follow Följ WindTableModel Index Index Direction Riktning Delete Radera boats-201908/locale/locale.pri000066400000000000000000000017431353032755300161540ustar00rootroot00000000000000TRANSLATIONS += \ $$PWD/boats_da.ts \ $$PWD/boats_de.ts \ $$PWD/boats_es.ts \ $$PWD/boats_fr.ts \ $$PWD/boats_it.ts \ $$PWD/boats_ja.ts \ $$PWD/boats_nl.ts \ $$PWD/boats_pl.ts \ $$PWD/boats_pt.ts \ $$PWD/boats_ru.ts \ $$PWD/boats_sl.ts \ $$PWD/boats_sv.ts QM_FILES = $$replace(TRANSLATIONS, "\\.ts", ".qm") isEmpty(QMAKE_LUPDATE) { win32:QMAKE_LUPDATE = $$[QT_INSTALL_BINS]/lupdate.exe else:QMAKE_LUPDATE = $$[QT_INSTALL_BINS]/lupdate } isEmpty(QMAKE_LRELEASE) { win32:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease.exe else:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease } updatets.commands = $$QMAKE_LUPDATE -locations none -noobsolete $$_PRO_FILE_ QMAKE_EXTRA_TARGETS += updatets updateqm.depends = updatets updateqm.input = TRANSLATIONS updateqm.output = locale/${QMAKE_FILE_BASE}.qm updateqm.commands = $$QMAKE_LRELEASE ${QMAKE_FILE_IN} updateqm.CONFIG += no_link QMAKE_EXTRA_COMPILERS += updateqm rcc.depends = $$QM_FILES boats-201908/locales.qrc000066400000000000000000000010011353032755300150560ustar00rootroot00000000000000 locale/boats_da.qm locale/boats_de.qm locale/boats_es.qm locale/boats_fr.qm locale/boats_it.qm locale/boats_ja.qm locale/boats_nl.qm locale/boats_pl.qm locale/boats_pt.qm locale/boats_ru.qm locale/boats_sl.qm locale/boats_sv.qm boats-201908/main.cpp000066400000000000000000000026751353032755300143770ustar00rootroot00000000000000// // C++ Implementation: main // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2009 Thibaut GRIDEL // // 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 . // // #include #include #include "boatapplication.h" #include "mainwindow.h" int debugLevel = 0; int main(int argc, char *argv[]) { BoatApplication app(argc, argv); int i; QStringList arguments = QCoreApplication::arguments(); arguments.removeAt(0); if (-1 != (i=arguments.indexOf("-debug"))) { debugLevel = arguments[i+1].toInt(); std::cout << "debug level set to " << debugLevel << std::endl; arguments.removeAt(i+1); arguments.removeAt(i); } // MainWindow MainWindow window; app.setWindow(&window); window.show(); window.openFiles(arguments); return app.exec(); } boats-201908/main_qml.cpp000066400000000000000000000031001353032755300152300ustar00rootroot00000000000000// // C++ Implementation: main // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2014 Thibaut GRIDEL // // 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 . // // #include #include #include #include #include int debugLevel = 0; int main(int argc, char* argv[]) { int i; QStringList arguments = QCoreApplication::arguments(); arguments.removeAt(0); if (-1 != (i=arguments.indexOf("-debug"))) { debugLevel = arguments[i+1].toInt(); std::cout << "debug level set to " << debugLevel << std::endl; arguments.removeAt(i+1); arguments.removeAt(i); } QGuiApplication app(argc,argv); app.setApplicationName("boats-qml"); QQuickView view; view.connect(view.engine(), SIGNAL(quit()), &app, SLOT(quit())); view.setSource(QUrl("boats.qml")); view.setResizeMode(QQuickView::SizeRootObjectToView); view.show(); return app.exec(); } boats-201908/mainwindow.cpp000066400000000000000000002010661353032755300156220ustar00rootroot00000000000000// // C++ Implementation: mainwindow // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2019 Thibaut GRIDEL // // 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 . // // #include "mainwindow.h" #include "commontypes.h" #include "boats.h" #include "situationmodel.h" #include "trackmodel.h" #include "boatmodel.h" #include "markmodel.h" #include "polylinemodel.h" #include "pointmodel.h" #include "statemachine.h" #include "scenarioanimation.h" #include "xmlsituationreader.h" #include "xmlsituationwriter.h" #include "situationwidget.h" #include "situationscene.h" #include "situationview.h" #include "colorpickerwidget.h" #ifdef GIF_EXPORT #include "gifwriter.h" #endif #include "boatsengine.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define VERSION "201908" extern int debugLevel; const int MainWindow::maxRecent(5); MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), menubar(new QMenuBar(this)), toolbar(new QToolBar(this)), tabWidget(new QTabWidget(this)), animationBar(new QToolBar(this)), situationDock(new QDockWidget(this)), situationWidget(new SituationWidget(situationDock)), statusbar(new QStatusBar(this)), qtTranslator(new QTranslator(this)), translator(new QTranslator(this)), engine(new BoatsEngine()) { // Actions createActions(); // Bars createMenus(); setMenuBar(menubar); addToolBar(toolbar); addToolBar(Qt::BottomToolBarArea, animationBar); setStatusBar(statusbar); // Docks createDocks(); addDockWidget(Qt::LeftDockWidgetArea, situationDock); // View newTabButton = new QPushButton(QIcon(":/images/tab_new.png"),QString(""), this); connect(newTabButton, SIGNAL(clicked()), newTabAction, SLOT(trigger())); tabWidget->setCornerWidget(newTabButton, Qt::TopLeftCorner); removeTabButton = new QPushButton(QIcon(":/images/tab_remove.png"),QString(""), this); removeTabButton->setEnabled(removeTabAction->isEnabled()); connect(removeTabButton, SIGNAL(clicked()), removeTabAction, SLOT(trigger())); tabWidget->setCornerWidget(removeTabButton, Qt::TopRightCorner); connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(setTab(int))); newTab(); setCentralWidget(tabWidget); readSettings(); // Locale and translation setup createTranslations(QLocale::system().name()); } MainWindow::~MainWindow() {} void MainWindow::createTranslations(QString locale) { qtTranslator->load("qt_" + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath)); qApp->installTranslator(qtTranslator); if (translator->load(QString("boats_").append(locale).append(".qm"), TRANSLATEDIR)) { qApp->installTranslator(translator); } else { QCoreApplication::postEvent(QCoreApplication::instance(), new QEvent(QEvent::LanguageChange)); } } void MainWindow::createActions() { newFileAction = new QAction(this); newFileAction->setIcon(QIcon(":/images/filenew.png")); connect(newFileAction, SIGNAL(triggered()), this, SLOT(newFile())); openFileAction = new QAction(this); openFileAction->setIcon(QIcon(":/images/fileopen.png")); connect(openFileAction, SIGNAL(triggered()), this, SLOT(openFile())); saveFileAction = new QAction(this); saveFileAction->setIcon(QIcon(":/images/filesave.png")); saveFileAction->setEnabled(false); connect(saveFileAction, SIGNAL(triggered()), this, SLOT(saveFile())); saveAsAction = new QAction(this); saveAsAction->setIcon(QIcon(":/images/filesaveas.png")); saveAsAction->setEnabled(false); connect(saveAsAction, SIGNAL(triggered()), this, SLOT(saveAs())); newTabAction = new QAction(this); newTabAction->setIcon(QIcon(":/images/tab_new.png")); connect(newTabAction, SIGNAL(triggered()), this, SLOT(newTab())); removeTabAction = new QAction(this); removeTabAction->setIcon(QIcon(":/images/tab_remove.png")); removeTabAction->setEnabled(false); connect(removeTabAction, SIGNAL(triggered()), this, SLOT(removeTab())); restoreFilesAction = new QAction(this); connect(restoreFilesAction, SIGNAL(triggered()), this, SLOT(restoreFiles())); printAction = new QAction(this); printAction->setIcon(QIcon(":images/fileprint.png")); connect(printAction, SIGNAL(triggered()), this, SLOT(print())); printPreviewAction = new QAction(this); printPreviewAction->setIcon(QIcon(":images/filequickprint.png")); connect(printPreviewAction, SIGNAL(triggered()), this, SLOT(printPreview())); exportPdfAction = new QAction(this); exportPdfAction->setIcon(QIcon(":/images/pdf.png")); connect(exportPdfAction, SIGNAL(triggered()), this, SLOT(exportPdf())); exportImageAction = new QAction(this); exportImageAction->setIcon(QIcon(":/images/export.png")); connect(exportImageAction, SIGNAL(triggered()), this, SLOT(exportImage())); #ifdef GIF_EXPORT exportAnimationAction = new QAction(this); exportAnimationAction->setIcon(QIcon(":/images/video.png")); connect(exportAnimationAction, SIGNAL(triggered()), this, SLOT(exportAnimation())); #endif exitAction = new QAction(this); connect(exitAction, SIGNAL(triggered()), this, SLOT(close())); addTrackAction = new QAction(this); addTrackAction->setIcon(QIcon(":/images/addtrack.png")); addTrackAction->setCheckable(true); connect(addTrackAction, SIGNAL(triggered()), engine, SLOT(addTrack())); addBoatAction = new QAction(this); addBoatAction->setIcon(QIcon(":/images/addboat.png")); addBoatAction->setCheckable(true); connect(addBoatAction, SIGNAL(triggered()), engine, SLOT(addBoat())); addMarkAction = new QAction(this); addMarkAction->setIcon(QIcon(":/images/addmark.png")); addMarkAction->setCheckable(true); connect(addMarkAction, SIGNAL(triggered()), engine, SLOT(addMark())); addPolyLineAction = new QAction(this); addPolyLineAction->setIcon(QIcon(":/images/addpoly.png")); addPolyLineAction->setCheckable(true); connect(addPolyLineAction, SIGNAL(triggered()), engine, SLOT(addPolyLine())); addPointAction = new QAction(this); addPointAction->setIcon(QIcon(":/images/addpoint.png")); addPointAction->setCheckable(true); connect(addPointAction, SIGNAL(triggered()), engine, SLOT(addPoint())); trimSailAction = new QAction(this); connect(trimSailAction, SIGNAL(triggered()), engine, SLOT(trimSail())); autotrimSailAction = new QAction(this); connect(autotrimSailAction, SIGNAL(triggered()), engine, SLOT(autotrimSail())); untrimSailAction = new QAction(this); connect(untrimSailAction, SIGNAL(triggered()), engine, SLOT(untrimSail())); trimJibAction = new QAction(this); connect(trimJibAction, SIGNAL(triggered()), engine, SLOT(trimJib())); autotrimJibAction = new QAction(this); connect(autotrimJibAction, SIGNAL(triggered()), engine, SLOT(autotrimJib())); untrimJibAction = new QAction(this); connect(untrimJibAction, SIGNAL(triggered()), engine, SLOT(untrimJib())); trimSpinAction = new QAction(this); connect(trimSpinAction, SIGNAL(triggered()), engine, SLOT(trimSpin())); autotrimSpinAction = new QAction(this); connect(autotrimSpinAction, SIGNAL(triggered()), engine, SLOT(autotrimSpin())); untrimSpinAction = new QAction(this); connect(untrimSpinAction, SIGNAL(triggered()), engine, SLOT(untrimSpin())); togglePortOverlapAction = new QAction(this); togglePortOverlapAction->setCheckable(true); connect(togglePortOverlapAction, SIGNAL(triggered()), engine, SLOT(togglePortOverlap())); toggleStarboardOverlapAction = new QAction(this); toggleStarboardOverlapAction->setCheckable(true); connect(toggleStarboardOverlapAction, SIGNAL(triggered()), engine, SLOT(toggleStarboardOverlap())); toggleHiddenAction = new QAction(this); toggleHiddenAction->setCheckable(true); connect(toggleHiddenAction, SIGNAL(triggered()), engine, SLOT(toggleHidden())); toggleTextAction = new QAction(this); toggleTextAction->setCheckable(true); connect(toggleTextAction, SIGNAL(triggered()), engine, SLOT(toggleText())); toggleSpinAction = new QAction(this); toggleSpinAction->setCheckable(true); toggleSpinAction->setEnabled(false); connect(toggleSpinAction, SIGNAL(triggered()), engine, SLOT(toggleSpin())); toggleMarkSideAction = new QAction(this); connect(toggleMarkSideAction, SIGNAL(triggered()), engine, SLOT(toggleMarkSide())); toggleMarkArrowAction = new QAction(this); toggleMarkArrowAction->setCheckable(true); connect(toggleMarkArrowAction, SIGNAL(triggered()), engine, SLOT(toggleMarkArrow())); toggleMarkZoneAction = new QAction(this); toggleMarkZoneAction->setIcon(QIcon(":/images/zone.png")); toggleMarkZoneAction->setCheckable(true); connect(toggleMarkZoneAction, SIGNAL(triggered()), engine, SLOT(toggleMarkZone())); setMarkColorAction = new QAction(this); setMarkColorAction->setIcon(QIcon()); connect(setMarkColorAction, SIGNAL(triggered()), this, SLOT(setMarkColor())); toggleLaylinesAction = new QAction(this); toggleLaylinesAction->setIcon(QIcon(":/images/laylines.png")); toggleLaylinesAction->setCheckable(true); connect(toggleLaylinesAction, SIGNAL(triggered()), engine, SLOT(toggleLaylines())); toggleMarkLabelAction = new QAction(this); toggleMarkLabelAction->setCheckable(true); connect(toggleMarkLabelAction, SIGNAL(triggered()), engine, SLOT(toggleMarkLabel())); editMarkLabelAction = new QAction(this); connect(editMarkLabelAction, SIGNAL(triggered()), this, SLOT(editMarkLabel())); deleteTrackAction = new QAction(this); connect(deleteTrackAction, SIGNAL(triggered()), engine, SLOT(deleteTrack())); deleteAction = new QAction(this); connect(deleteAction, SIGNAL(triggered()), engine, SLOT(deleteModels())); animateAction = new QAction(this); animateAction->setIcon(QIcon(":/images/animate.png")); animateAction->setCheckable(true); connect(animateAction, SIGNAL(triggered(bool)), this, SLOT(animate(bool))); startAction = new QAction(this); startAction->setIcon(QIcon(":/images/player_play.png")); startAction->setEnabled(false); connect(startAction, SIGNAL(triggered()), engine, SLOT(play())); pauseAction = new QAction(this); pauseAction->setIcon(QIcon(":/images/player_pause.png")); pauseAction->setEnabled(false); pauseAction->setCheckable(true); connect(pauseAction, SIGNAL(triggered()), engine, SLOT(pause())); stopAction = new QAction(this); stopAction->setIcon(QIcon(":/images/player_stop.png")); stopAction->setEnabled(false); connect(stopAction, SIGNAL(triggered()), engine, SLOT(stop())); loopAction = new QAction(this); loopAction->setIcon(QIcon(":/images/player_loop.png")); loopAction->setEnabled(false); loopAction->setCheckable(true); connect(loopAction, SIGNAL(triggered(bool)), this, SLOT(loop(bool))); undoAction = new QAction(this); undoAction->setIcon(QIcon(":/images/undo.png")); undoAction->setEnabled(false); redoAction = new QAction(this); redoAction->setIcon(QIcon(":/images/redo.png")); redoAction->setEnabled(false); zoomInAction = new QAction(this); zoomInAction->setIcon(QIcon(":/images/zoomin.png")); zoomOutAction = new QAction(this); zoomOutAction->setIcon(QIcon(":/images/zoomout.png")); zoomFitAction = new QAction(this); zoomFitAction->setIcon(QIcon(":/images/zoomfit.png")); toggleMainToolbarAction = new QAction(this); toggleMainToolbarAction->setCheckable(true); toggleMainToolbarAction->setChecked(true); connect(toggleMainToolbarAction, SIGNAL(toggled(bool)), toolbar, SLOT(setVisible(bool))); toggleAnimationToolbarAction = new QAction(this); toggleAnimationToolbarAction->setCheckable(true); toggleAnimationToolbarAction->setChecked(true); connect(toggleAnimationToolbarAction, SIGNAL(toggled(bool)), animationBar, SLOT(setVisible(bool))); toggleScenarioDockAction = new QAction(this); toggleScenarioDockAction->setCheckable(true); toggleScenarioDockAction->setChecked(true); connect(toggleScenarioDockAction, SIGNAL(toggled(bool)), situationDock, SLOT(setVisible(bool))); aboutAction = new QAction(this); connect(aboutAction, SIGNAL(triggered()), this, SLOT(about())); } void MainWindow::updateActions() { SituationModel *situation = engine->currentModel(); bool selectedItems = !situation->selectedModels().isEmpty(); bool selectedBoats = !situation->selectedBoatModels().isEmpty(); bool selectedMarks = !situation->selectedMarkModels().isEmpty(); toggleTextAction->setEnabled(selectedItems); toggleMarkSideAction->setEnabled(selectedMarks); toggleMarkArrowAction->setEnabled(selectedMarks); toggleMarkLabelAction->setEnabled(selectedMarks); setMarkColorAction->setEnabled(selectedMarks); editMarkLabelAction->setEnabled(selectedMarks); deleteAction->setEnabled(selectedItems); bool allPortSet = 1; bool allStarboardSet = 1; bool allHiddenSet = 1; bool allSpinBoat = 1; bool allSpinSet = 1; bool allTextSet = 1; bool allLaylinesSet = 1; bool allMarkArrowSet = 1; bool allMarkLabelSet = 1; int flagSize = ENUM_SIZE(Boats,Flag); bool allFlagSet[flagSize]; for(int i=0; i < flagSize; i++) { allFlagSet[i] = 1; } int accelerationSize = ENUM_SIZE(Boats,Acceleration); bool allAccelerationSet[accelerationSize]; for(int i=0; iselectedBoatModels()) { allPortSet = allPortSet && (boat->overlap() & Boats::port); allStarboardSet = allStarboardSet && (boat->overlap() & Boats::starboard); allHiddenSet = allHiddenSet && boat->hidden(); allSpinBoat = allSpinBoat && (boat->hasSpin()); allSpinSet = allSpinSet && (boat->hasSpin()) && (boat->spin()); for (int i = 0; i < flagSize; i++) { allFlagSet[i] = allFlagSet[i] && (boat->flag() == i); } for (int i=0; iacceleration() == i); } } togglePortOverlapAction->setChecked(selectedBoats && allPortSet); toggleStarboardOverlapAction->setChecked(selectedBoats && allStarboardSet); toggleHiddenAction->setChecked(selectedBoats && allHiddenSet); toggleSpinAction->setChecked(selectedBoats && allSpinSet); toggleSpinAction->setEnabled(selectedBoats && allSpinBoat); foreach(PositionModel *position, situation->selectedModels()) { allTextSet = allTextSet && (!position->text().isEmpty()); allLaylinesSet = allLaylinesSet && position->laylines(); } toggleTextAction->setEnabled(situation->selectedModels().size()==1); toggleTextAction->setChecked(selectedItems && allTextSet); for (int i = 0; i < flagSize; i++) { QAction *flagAction = flagMenu->actions()[i]; flagAction->setChecked(allFlagSet[i]); } for (int i=0; iactions()[i]; accelerationAction->setChecked(allAccelerationSet[i]); } toggleLaylinesAction->setChecked(selectedItems && allLaylinesSet); toggleLaylinesAction->setEnabled(selectedItems); foreach(MarkModel *mark, situation->selectedMarkModels()) { allMarkArrowSet = allMarkArrowSet && mark->arrowVisible(); allMarkLabelSet = allMarkLabelSet && mark->labelVisible(); } toggleMarkArrowAction->setChecked(allMarkArrowSet); toggleMarkLabelAction->setChecked(allMarkLabelSet); } void MainWindow::enterCreateState() { SituationView *view = viewList.at(engine->currentIndex()); view->setCursor(Qt::CrossCursor); } void MainWindow::exitCreateState() { SituationView *view = viewList.at(engine->currentIndex()); view->unsetCursor(); } void MainWindow::cleanState(bool state) { SituationModel *situation = engine->currentModel(); if (situation->fileName().isEmpty()) saveFileAction->setEnabled(false); else saveFileAction->setEnabled(!state); saveAsAction->setEnabled(!state); QString shownName = QFileInfo(situation->fileName()).fileName(); setWindowTitle(tr("%1 - %2 [*]").arg(tr("Boat Scenario")).arg(shownName)); if (!state) { tabWidget->setTabText(engine->currentIndex(),shownName.append(" *")); } else { tabWidget->setTabText(engine->currentIndex(),shownName); } setWindowModified(!state); } void MainWindow::createMenus() { recentMenu = new QMenu(this); for (int i = 0; i < maxRecent; ++i) { QAction * recentAction = new QAction(this); recentMenu->addAction(recentAction); connect(recentAction, SIGNAL(triggered()), this, SLOT(openRecent())); } fileMenu = new QMenu(this); menubar->addMenu(fileMenu); fileMenu->addAction(newFileAction); fileMenu->addAction(openFileAction); fileMenu->addMenu(recentMenu); fileMenu->addAction(saveFileAction); fileMenu->addAction(saveAsAction); fileMenu->addSeparator(); fileMenu->addAction(printAction); fileMenu->addAction(printPreviewAction); fileMenu->addAction(exportPdfAction); fileMenu->addAction(exportImageAction); #ifdef GIF_EXPORT fileMenu->addAction(exportAnimationAction); #endif fileMenu->addSeparator(); fileMenu->addAction(newTabAction); fileMenu->addAction(removeTabAction); fileMenu->addAction(restoreFilesAction); fileMenu->addSeparator(); fileMenu->addAction(exitAction); trackMenu = new QMenu(this); menubar->addMenu(trackMenu); trackMenu->addAction(addTrackAction); trackMenu->addAction(addBoatAction); trackMenu->addAction(addMarkAction); trackMenu->addAction(addPolyLineAction); trackMenu->addAction(addPointAction); trackMenu->addSeparator(); trackMenu->addAction(trimSailAction); trackMenu->addAction(autotrimSailAction); trackMenu->addAction(untrimSailAction); trackMenu->addAction(trimJibAction); trackMenu->addAction(autotrimJibAction); trackMenu->addAction(untrimJibAction); trackMenu->addAction(trimSpinAction); trackMenu->addAction(autotrimSpinAction); trackMenu->addAction(untrimSpinAction); trackMenu->addAction(togglePortOverlapAction); trackMenu->addAction(toggleStarboardOverlapAction); trackMenu->addAction(toggleHiddenAction); trackMenu->addAction(toggleTextAction); int flagSize = ENUM_SIZE(Boats, Flag); flagMenu = new QMenu(this); QActionGroup *flagGroup = new QActionGroup(flagMenu); for (int i = 0; i < flagSize; i++) { QAction * flagAction = new QAction(ENUM_NAME(Boats, Flag, i), this); flagAction->setCheckable(true); flagAction->setData(i); flagGroup->addAction(flagAction); flagMenu->addAction(flagAction); connect(flagAction, SIGNAL(triggered()), this, SLOT(toggleFlag())); } trackMenu->addMenu(flagMenu); int accelerationSize = ENUM_SIZE(Boats, Acceleration); accelerationMenu = new QMenu(this); QActionGroup *accelerationGroup = new QActionGroup(accelerationMenu); for (int i = 0; i < accelerationSize; i++) { QAction * accelerationAction = new QAction(ENUM_NAME(Boats, Acceleration, i), this); accelerationAction->setCheckable(true); accelerationAction->setData(i); accelerationGroup->addAction(accelerationAction); accelerationMenu->addAction(accelerationAction); connect(accelerationAction, SIGNAL(triggered()), this, SLOT(toggleAcceleration())); } trackMenu->addMenu(accelerationMenu); trackMenu->addAction(toggleSpinAction); trackMenu->addSeparator(); trackMenu->addAction(toggleMarkSideAction); trackMenu->addAction(toggleMarkArrowAction); trackMenu->addAction(toggleMarkZoneAction); trackMenu->addAction(setMarkColorAction); trackMenu->addAction(toggleLaylinesAction); trackMenu->addAction(toggleMarkLabelAction); trackMenu->addAction(editMarkLabelAction); trackMenu->addSeparator(); trackMenu->addAction(deleteTrackAction); trackMenu->addAction(deleteAction); defaultPopup = new QMenu(this); defaultPopup->addAction(addTrackAction); defaultPopup->addAction(addMarkAction); defaultPopup->addAction(addPolyLineAction); boatPopup = new QMenu(this); boatPopup->addAction(addBoatAction); boatPopup->addSeparator(); boatPopup->addAction(trimSailAction); boatPopup->addAction(autotrimSailAction); boatPopup->addAction(untrimSailAction); boatPopup->addAction(trimJibAction); boatPopup->addAction(autotrimJibAction); boatPopup->addAction(untrimJibAction); boatPopup->addAction(trimSpinAction); boatPopup->addAction(autotrimSpinAction); boatPopup->addAction(untrimSpinAction); boatPopup->addAction(togglePortOverlapAction); boatPopup->addAction(toggleStarboardOverlapAction); boatPopup->addAction(toggleHiddenAction); boatPopup->addAction(toggleTextAction); boatPopup->addMenu(flagMenu); boatPopup->addMenu(accelerationMenu); boatPopup->addAction(toggleSpinAction); boatPopup->addAction(toggleLaylinesAction); boatPopup->addSeparator(); boatPopup->addAction(deleteTrackAction); boatPopup->addAction(deleteAction); markPopup = new QMenu(this); markPopup->addAction(toggleTextAction); markPopup->addAction(toggleMarkSideAction); markPopup->addAction(toggleMarkArrowAction); markPopup->addAction(toggleMarkZoneAction); markPopup->addAction(toggleLaylinesAction); markPopup->addAction(setMarkColorAction); markPopup->addAction(toggleMarkLabelAction); markPopup->addAction(editMarkLabelAction); markPopup->addSeparator(); markPopup->addAction(deleteAction); pointPopup = new QMenu(this); pointPopup->addAction(addPointAction); pointPopup->addSeparator(); pointPopup->addAction(toggleTextAction); pointPopup->addAction(toggleLaylinesAction); pointPopup->addSeparator(); pointPopup->addAction(deleteAction); historyMenu = new QMenu(this); menubar->addMenu(historyMenu); historyMenu->addAction(undoAction); historyMenu->addAction(redoAction); animationMenu = new QMenu(this); menubar->addMenu(animationMenu); animationMenu->addAction(animateAction); animationMenu->addSeparator(); animationMenu->addAction(startAction); animationMenu->addAction(pauseAction); animationMenu->addAction(stopAction); animationMenu->addAction(loopAction); zoomMenu = new QMenu(this); menubar->addMenu(zoomMenu); zoomMenu->addAction(zoomInAction); zoomMenu->addAction(zoomFitAction); zoomMenu->addAction(zoomOutAction); viewMenu = new QMenu(this); menubar->addMenu(viewMenu); viewMenu->addAction(toggleMainToolbarAction); viewMenu->addAction(toggleAnimationToolbarAction); viewMenu->addAction(toggleScenarioDockAction); aboutMenu = new QMenu(this); menubar->addMenu(aboutMenu); langMenu = new QMenu(this); QActionGroup *langGroup = new QActionGroup(langMenu); QDir dir(TRANSLATEDIR); QStringList fileNames = dir.entryList(QStringList("*.qm"), QDir::Files, QDir::Name); fileNames.prepend("boats_en.qm"); QMutableStringListIterator i(fileNames); while (i.hasNext()) { i.next(); QRegExp rx("boats_(.+)\\.qm"); rx.indexIn(i.value()); QLocale locale(rx.cap(1)); QAction *langAction = new QAction( QLocale::languageToString(locale.language()), this); langAction->setCheckable(true); langAction->setData(locale.name()); langGroup->addAction(langAction); langMenu->addAction(langAction); connect(langAction, SIGNAL(triggered()), this, SLOT(toggleLang())); } aboutMenu->addMenu(langMenu); aboutMenu->addAction(aboutAction); toolbar->addAction(newFileAction); toolbar->addAction(openFileAction); toolbar->addAction(saveFileAction); toolbar->addAction(saveAsAction); toolbar->addAction(exportImageAction); #ifdef GIF_EXPORT toolbar->addAction(exportAnimationAction); #endif toolbar->addSeparator(); toolbar->addAction(undoAction); toolbar->addAction(redoAction); toolbar->addSeparator(); toolbar->addAction(addTrackAction); toolbar->addAction(addBoatAction); toolbar->addAction(addMarkAction); toolbar->addAction(addPolyLineAction); toolbar->addAction(addPointAction); toolbar->addSeparator(); toolbar->addAction(animateAction); toolbar->addSeparator(); toolbar->addAction(zoomOutAction); toolbar->addAction(zoomFitAction); toolbar->addAction(zoomInAction); toolbar->addSeparator(); lookDirectionSlider = new QSlider(Qt::Horizontal); lookDirectionSlider->setMaximum(360); lookDirectionSlider->setSingleStep(15); lookDirectionSlider->setTickInterval(15); lookDirectionSlider->setTickPosition(QSlider::TicksBelow); toolbar->addWidget(lookDirectionSlider); tiltSlider = new QSlider(Qt::Horizontal); tiltSlider->setMinimum(0); tiltSlider->setMaximum(90); tiltSlider->setSingleStep(15); tiltSlider->setTickInterval(15); tiltSlider->setTickPosition(QSlider::TicksBelow); toolbar->addWidget(tiltSlider); animationSlider = new QSlider(Qt::Horizontal, this); animationSlider->setTickInterval(2000); animationSlider->setTickPosition(QSlider::TicksBelow); animationSlider->setSingleStep(400); animationSlider->setPageStep(2000); animationSlider->setEnabled(false); animationBar->addAction(startAction); animationBar->addAction(pauseAction); animationBar->addAction(stopAction); animationBar->addAction(loopAction); animationBar->addSeparator(); animationBar->addWidget(animationSlider); } void MainWindow::createDocks() { QScrollArea *area = new QScrollArea(this); area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); area->setWidgetResizable(true); area->setWidget(situationWidget); situationDock->setWidget(area); situationDock->setFeatures(QDockWidget::NoDockWidgetFeatures); } void MainWindow::newTab() { engine->newFile(); SituationModel *situation = engine->currentModel(); SituationScene *scene = new SituationScene(situation); SituationView *view =new SituationView(scene); sceneList.append(scene); scene->setDefaultPopup(defaultPopup); scene->setBoatPopup(boatPopup); scene->setMarkPopup(markPopup); scene->setPointPopup(pointPopup); viewList.append(view); tabWidget->addTab(view, ""); tabWidget->setCurrentIndex(engine->situationSize() - 1); view->setFocus(); view->setMouseTracking(true); if (engine->situationSize() > 1) { removeTabAction->setEnabled(true); removeTabButton->setEnabled(true); } } void MainWindow::unsetTab() { if (engine->situationSize() == 1) { return; } SituationModel *situation = engine->currentModel(); SituationScene *scene = sceneList.at(engine->currentIndex()); StateMachine *machine = situation->stateMachine(); SituationView *view = viewList.at(engine->currentIndex()); animate(false); disconnect(situation->undoStack(), 0, this, 0); disconnect(scene, 0, this, 0); disconnect(view, 0, 0, 0); disconnect(machine->createState(), 0, this, 0); disconnect(machine->createTrackState(), 0, this, 0); disconnect(machine->boatSelectionState(), 0, this, 0); disconnect(machine->createBoatState(), 0, this, 0); disconnect(machine->createMarkState(), 0, this, 0); disconnect(machine->createLineState(), 0, this, 0); disconnect(machine->pointSelectionState(), 0, this, 0); disconnect(machine->createPointState(), 0, this, 0); disconnect(machine->animationState(), 0, this, 0); disconnect(machine->playState(), 0, this, 0); disconnect(machine->pauseState(), 0, this, 0); disconnect(machine->stopState(), 0, this, 0); disconnect(undoAction, 0, 0, 0); disconnect(redoAction, 0, 0, 0); disconnect(zoomInAction, 0, 0, 0); disconnect(zoomOutAction, 0, 0, 0); disconnect(zoomFitAction, 0, 0, 0); disconnect(lookDirectionSlider, 0, 0, 0); disconnect(lookDirectionSlider); disconnect(tiltSlider, 0, 0, 0); disconnect(tiltSlider); situationWidget->unSetSituation(); } void MainWindow::setTab(int index) { if (index != engine->currentIndex()) { unsetTab(); engine->setIndex(index); } SituationModel *situation = engine->currentModel(); SituationScene *scene = sceneList.at(index); SituationView *view = viewList.at(index); StateMachine *machine = situation->stateMachine(); connect(machine->createState(), SIGNAL(entered()), this, SLOT(enterCreateState())); connect(machine->createState(), SIGNAL(exited()), this, SLOT(exitCreateState())); connect(machine->createTrackState(), SIGNAL(enabledChanged(bool)), addTrackAction, SLOT(setEnabled(bool))); connect(machine->createTrackState(), SIGNAL(activeChanged(bool)), addTrackAction, SLOT(setChecked(bool))); addTrackAction->setEnabled(machine->createTrackState()->isEnabled()); addTrackAction->setChecked(machine->createTrackState()->isActive()); connect(machine->boatSelectionState(), SIGNAL(activeChanged(bool)), addBoatAction, SLOT(setEnabled(bool))); connect(machine->createBoatState(), SIGNAL(activeChanged(bool)), addBoatAction, SLOT(setChecked(bool))); addBoatAction->setEnabled(machine->boatSelectionState()->isActive()); addBoatAction->setChecked(machine->createBoatState()->isActive()); connect(machine->boatSelectionState(), SIGNAL(activeChanged(bool)), trimSailAction, SLOT(setEnabled(bool))); trimSailAction->setEnabled(machine->boatSelectionState()->isActive()); connect(machine->boatSelectionState(), SIGNAL(activeChanged(bool)), autotrimSailAction, SLOT(setEnabled(bool))); autotrimSailAction->setEnabled(machine->boatSelectionState()->isActive()); connect(machine->boatSelectionState(), SIGNAL(activeChanged(bool)), untrimSailAction, SLOT(setEnabled(bool))); untrimSailAction->setEnabled(machine->boatSelectionState()->isActive()); connect(machine->boatSelectionState(), SIGNAL(activeChanged(bool)), trimJibAction, SLOT(setEnabled(bool))); trimJibAction->setEnabled(machine->boatSelectionState()->isActive()); connect(machine->boatSelectionState(), SIGNAL(activeChanged(bool)), autotrimJibAction, SLOT(setEnabled(bool))); autotrimJibAction->setEnabled(machine->boatSelectionState()->isActive()); connect(machine->boatSelectionState(), SIGNAL(activeChanged(bool)), untrimJibAction, SLOT(setEnabled(bool))); untrimJibAction->setEnabled(machine->boatSelectionState()->isActive()); connect(machine->boatSelectionState(), SIGNAL(activeChanged(bool)), trimSpinAction, SLOT(setEnabled(bool))); trimSpinAction->setEnabled(machine->boatSelectionState()->isActive()); connect(machine->boatSelectionState(), SIGNAL(activeChanged(bool)), autotrimSpinAction, SLOT(setEnabled(bool))); autotrimSpinAction->setEnabled(machine->boatSelectionState()->isActive()); connect(machine->boatSelectionState(), SIGNAL(activeChanged(bool)), untrimSpinAction, SLOT(setEnabled(bool))); untrimSpinAction->setEnabled(machine->boatSelectionState()->isActive()); connect(machine->boatSelectionState(), SIGNAL(activeChanged(bool)), togglePortOverlapAction, SLOT(setEnabled(bool))); togglePortOverlapAction->setEnabled(machine->boatSelectionState()->isActive()); connect(machine->boatSelectionState(), SIGNAL(activeChanged(bool)), toggleStarboardOverlapAction, SLOT(setEnabled(bool))); toggleStarboardOverlapAction->setEnabled(machine->boatSelectionState()->isActive()); connect(machine->boatSelectionState(), SIGNAL(activeChanged(bool)), toggleHiddenAction, SLOT(setEnabled(bool))); toggleHiddenAction->setEnabled(machine->boatSelectionState()->isActive()); connect(machine->boatSelectionState(), SIGNAL(activeChanged(bool)), flagMenu, SLOT(setEnabled(bool))); flagMenu->setEnabled(machine->boatSelectionState()->isActive()); connect(machine->boatSelectionState(), SIGNAL(activeChanged(bool)), accelerationMenu, SLOT(setEnabled(bool))); accelerationMenu->setEnabled(machine->boatSelectionState()->isActive()); connect(machine->boatSelectionState(), SIGNAL(activeChanged(bool)), deleteTrackAction, SLOT(setEnabled(bool))); deleteTrackAction->setEnabled(machine->boatSelectionState()->isActive()); connect(machine->createMarkState(), SIGNAL(enabledChanged(bool)), addMarkAction, SLOT(setEnabled(bool))); connect(machine->createMarkState(), SIGNAL(activeChanged(bool)), addMarkAction, SLOT(setChecked(bool))); addMarkAction->setEnabled(machine->createMarkState()->isEnabled()); addMarkAction->setChecked(machine->createMarkState()->isActive()); connect(machine->createLineState(), SIGNAL(enabledChanged(bool)), addPolyLineAction, SLOT(setEnabled(bool))); connect(machine->createLineState(), SIGNAL(activeChanged(bool)), addPolyLineAction, SLOT(setChecked(bool))); addPolyLineAction->setEnabled(machine->createLineState()->isEnabled()); addPolyLineAction->setChecked(machine->createLineState()->isActive()); connect(machine->pointSelectionState(), SIGNAL(activeChanged(bool)), addPointAction, SLOT(setEnabled(bool))); connect(machine->createPointState(), SIGNAL(activeChanged(bool)), addPointAction, SLOT(setChecked(bool))); addPointAction->setEnabled(machine->pointSelectionState()->isActive()); addPointAction->setChecked(machine->createPointState()->isActive()); connect(machine->animationState(), SIGNAL(enabledChanged(bool)), animateAction, SLOT(setEnabled(bool))); connect(machine->animationState(), SIGNAL(activeChanged(bool)), animateAction, SLOT(setChecked(bool))); animateAction->setEnabled(machine->animationState()->isEnabled()); animateAction->setChecked(machine->animationState()->isActive()); connect(machine->animationState(), SIGNAL(enabledChanged(bool)), loopAction, SLOT(setEnabled(bool))); connect(machine->animationState(), SIGNAL(activeChanged(bool)), loopAction, SLOT(setChecked(bool))); loopAction->setEnabled(machine->animationState()->isEnabled()); loopAction->setChecked(machine->animationState()->isActive()); connect(machine->playState(), SIGNAL(enabledChanged(bool)), startAction, SLOT(setEnabled(bool))); connect(machine->playState(), SIGNAL(activeChanged(bool)), startAction, SLOT(setChecked(bool))); startAction->setEnabled(machine->playState()->isEnabled()); startAction->setChecked(machine->playState()->isActive()); connect(machine->pauseState(), SIGNAL(enabledChanged(bool)), pauseAction, SLOT(setEnabled(bool))); connect(machine->pauseState(), SIGNAL(activeChanged(bool)), pauseAction, SLOT(setChecked(bool))); pauseAction->setEnabled(machine->pauseState()->isEnabled()); pauseAction->setChecked(machine->pauseState()->isActive()); connect(machine->stopState(), SIGNAL(enabledChanged(bool)), stopAction, SLOT(setEnabled(bool))); connect(machine->stopState(), SIGNAL(activeChanged(bool)), stopAction, SLOT(setChecked(bool))); stopAction->setEnabled(machine->stopState()->isEnabled()); stopAction->setChecked(machine->stopState()->isActive()); connect(undoAction, SIGNAL(triggered()), situation->undoStack(), SLOT(undo())); connect(situation->undoStack(), SIGNAL(canUndoChanged(bool)), undoAction, SLOT(setEnabled(bool))); undoAction->setEnabled(situation->undoStack()->canUndo()); connect(redoAction, SIGNAL(triggered()), situation->undoStack(), SLOT(redo())); connect(situation->undoStack(), SIGNAL(canRedoChanged(bool)), redoAction, SLOT(setEnabled(bool))); redoAction->setEnabled(situation->undoStack()->canRedo()); connect(situation->undoStack(), SIGNAL(cleanChanged(bool)), this, SLOT(cleanState(bool))); cleanState(situation->undoStack()->isClean()); connect(zoomInAction, SIGNAL(triggered()), view, SLOT(zoomIn())); connect(zoomOutAction, SIGNAL(triggered()), view, SLOT(zoomOut())); connect(zoomFitAction, SIGNAL(triggered()), view, SLOT(zoomFit())); connect(view, SIGNAL(lookDirectionChanged(int)), lookDirectionSlider, SLOT(setValue(int))); connect(view, SIGNAL(tiltChanged(int)), tiltSlider, SLOT(setValue(int))); lookDirectionSlider->setValue(situation->lookDirection()); tiltSlider->setValue(situation->tilt()); connect(lookDirectionSlider, SIGNAL(valueChanged(int)), this, SLOT(setLookAt())); connect(tiltSlider, SIGNAL(valueChanged(int)), this, SLOT(setLookAt())); situationWidget->setSituation(situation); connect(scene, SIGNAL(selectedModelsChanged()), this, SLOT(updateActions())); updateActions(); } void MainWindow::removeTab() { int index = tabWidget->currentIndex(); SituationScene *scene = sceneList.at(index); SituationView *view = viewList.at(index); if (!maybeSave()) { return; } if (index == engine->situationSize() - 1) { tabWidget->setCurrentIndex(index - 1); } else if (index != engine->situationSize() - 2) { tabWidget->setCurrentIndex(index + 1); } sceneList.removeAt(index); viewList.removeAt(index); tabWidget->removeTab(index); view->deleteLater(); scene->deleteLater(); engine->removeFile(index); if (engine->situationSize() == 1) { removeTabAction->setEnabled(false); removeTabButton->setEnabled(false); } } void MainWindow::writeSettings() { QSettings settings("Boats"); settings.beginGroup("MainWindow"); settings.setValue("size", size()); settings.setValue("pos", pos()); settings.setValue("AnimationBar", animationBar->isVisible()); settings.setValue("AnimationBarArea", toolBarArea(animationBar)); settings.setValue("ToolBar", toolbar->isVisible()); settings.setValue("ToolBarArea", toolBarArea(toolbar)); settings.setValue("ScenarioDock", situationDock->isVisible()); settings.setValue("recentList", recentList); settings.setValue("fileList",engine->fileList()); settings.setValue("filePath", filePath); settings.endGroup(); } void MainWindow::readSettings() { QSettings settings("Boats"); settings.beginGroup("MainWindow"); QSize size = settings.value("size").toSize(); QPoint pos = settings.value("pos").toPoint(); if ( (size.isValid()) && (!pos.isNull()) ) { resize(size); move(pos); } else { showMaximized(); } toggleAnimationToolbarAction->setChecked(settings.value("AnimationBar", true).toBool()); addToolBar((Qt::ToolBarArea)(settings.value("AnimationBarArea").toInt()), animationBar); toggleMainToolbarAction->setChecked(settings.value("ToolBar", true).toBool()); addToolBar((Qt::ToolBarArea)(settings.value("ToolBarArea").toInt()), toolbar); toggleScenarioDockAction->setChecked(settings.value("ScenarioDock", true).toBool()); recentList = settings.value("recentList").toStringList(); updateRecentList(); fileList = settings.value("fileList").toStringList(); filePath = settings.value("filePath").toString(); settings.endGroup(); } void MainWindow::updateRecentList() { int numRecentFiles = qMin(recentList.size(), maxRecent); for (int i = 0; i < numRecentFiles; ++i) { QString text = tr("&%1 %2").arg(i + 1).arg(QFileInfo(recentList[i]).fileName()); QAction * recentAction = recentMenu->actions()[i]; recentAction->setText(text); recentAction->setData(i); recentAction->setVisible(true); } for (int j = numRecentFiles; j < maxRecent; ++j) { QAction * recentAction = recentMenu->actions()[j]; recentAction->setVisible(false); } } bool MainWindow::maybeSave() { SituationModel *situation = engine->currentModel(); if (!situation->undoStack()->isClean()) { QString shownName = QFileInfo(situation->fileName()).fileName(); QMessageBox::StandardButton ret; ret = QMessageBox::warning(this, shownName, tr("The document %1 has been modified.\n" "Do you want to save your changes?").arg(shownName), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); if (ret == QMessageBox::Save) { return saveSituation(""); } else if (ret == QMessageBox::Cancel) { return false; } } return true; } void MainWindow::closeEvent(QCloseEvent *event) { animate(false); while(engine->situationSize()>1) { if (!maybeSave()) { event->ignore(); return; } removeTab(); } writeSettings(); event->accept(); } void MainWindow::changeEvent(QEvent *event) { if(event->type() == QEvent::LanguageChange) { newFileAction->setText(tr("&New File")); newFileAction->setShortcut(tr("Ctrl+N")); openFileAction->setText(tr("&Open File...")); openFileAction->setShortcut(tr("Ctrl+O")); saveFileAction->setText(tr("&Save File")); saveFileAction->setShortcut(tr("Ctrl+S")); saveAsAction->setText(tr("Save &As...")); saveAsAction->setShortcut(tr("Ctrl+Shift+S")); newTabAction->setText(tr("New &Tab")); newTabAction->setShortcut(tr("Ctrl+T")); removeTabAction->setText(tr("&Close Tab")); removeTabAction->setShortcut(tr("Ctrl+W")); restoreFilesAction->setText(tr("&Restore Last Session...")); printAction->setText(tr("&Print...")); printAction->setShortcut(tr("Ctrl+P")); printPreviewAction->setText(tr("Print P&review...")); printPreviewAction->setShortcut(tr("Ctrl+R")); exportPdfAction->setText(tr("&Export Pdf...")); exportPdfAction->setShortcut(tr("Ctrl+E")); exportImageAction->setText(tr("Export &Image...")); exportImageAction->setShortcut(tr("Ctrl+I")); #ifdef GIF_EXPORT exportAnimationAction->setText(tr("Export Ani&mation...")); exportAnimationAction->setShortcut(tr("Ctrl+V")); #endif exitAction->setText(tr("E&xit")); exitAction->setShortcut(tr("Ctrl+Q")); exitAction->setStatusTip(tr("Exit the application")); addTrackAction->setText(tr("Create &Track")); addTrackAction->setShortcut(tr("Ctrl+Ins")); addBoatAction->setText(tr("Create &Boat")); addBoatAction->setShortcut(tr("Ins")); addMarkAction->setText(tr("Create &Mark")); addMarkAction->setShortcut(tr("Alt+Ins")); addPolyLineAction->setText(tr("Create &PolyLine")); addPolyLineAction->setShortcut(tr("Ctrl+Alt+Ins")); addPointAction->setText(tr("Create Poin&t")); addPointAction->setShortcut(tr("Ctrl+T")); trimSailAction->setText(tr("Trim Sail")); trimSailAction->setShortcut(tr("<")); autotrimSailAction->setText(tr("Auto Trim")); autotrimSailAction->setShortcut(tr("=")); untrimSailAction->setText(tr("Untrim Sail")); untrimSailAction->setShortcut(tr(">")); trimJibAction->setText(tr("Trim Jib")); trimJibAction->setShortcut(tr(",")); autotrimJibAction->setText(tr("Auto Jib")); autotrimJibAction->setShortcut(tr("?")); untrimJibAction->setText(tr("Untrim Jib")); untrimJibAction->setShortcut(tr(".")); trimSpinAction->setText(tr("Trim Spin")); trimSpinAction->setShortcut(tr("Ctrl+,")); autotrimSpinAction->setText(tr("Auto Spin")); autotrimSpinAction->setShortcut(tr("Ctrl+?")); untrimSpinAction->setText(tr("Untrim Spin")); untrimSpinAction->setShortcut(tr("Ctrl+.")); togglePortOverlapAction->setText(tr("&Port Overlap")); togglePortOverlapAction->setShortcut(tr("Alt+<")); toggleStarboardOverlapAction->setText(tr("&Starboard Overlap")); toggleStarboardOverlapAction->setShortcut(tr("Alt+>")); toggleHiddenAction->setText(tr("&Hide")); toggleHiddenAction->setShortcut(tr("Alt+D")); toggleTextAction->setText(tr("&Text")); toggleTextAction->setShortcut(tr("Alt+T")); toggleSpinAction->setText(tr("Toggle &Spinnaker")); toggleSpinAction->setShortcut(tr("Alt+S")); toggleMarkSideAction->setText(tr("Toggle &Mark Side")); toggleMarkSideAction->setShortcut(tr("Alt+M")); toggleMarkArrowAction->setText(tr("Toggle Mark Arro&w")); toggleMarkArrowAction->setShortcut(tr("Alt+W")); toggleMarkZoneAction->setText(tr("Toggle Mark &Zone")); toggleMarkZoneAction->setShortcut(tr("Alt+Z")); setMarkColorAction->setText(tr("Set Mark &Color")); setMarkColorAction->setShortcut(tr("Alt+C")); toggleLaylinesAction->setText(tr("Toggle &Laylines")); toggleLaylinesAction->setShortcut(tr("Alt+L")); toggleMarkLabelAction->setText(tr("Toggle Mark &Label")); toggleMarkLabelAction->setShortcut(tr("Alt+L")); editMarkLabelAction->setText(tr("&Edit Mark Label")); editMarkLabelAction->setShortcut(tr("Alt+E")); deleteTrackAction->setText(tr("Delete Track")); deleteTrackAction->setShortcut(tr("Ctrl+Del")); deleteAction->setText(tr("&Delete Selection")); deleteAction->setShortcut(tr("Del")); animateAction->setText(tr("&Animate")); animateAction->setShortcut(tr("Ctrl+A")); startAction->setText(tr("&Play")); startAction->setShortcut(tr("P")); pauseAction->setText(tr("&Pause")); pauseAction->setShortcut(tr("M")); stopAction->setText(tr("&Stop")); stopAction->setShortcut(tr("Space")); loopAction->setText(tr("&Loop")); loopAction->setShortcut(tr("L")); undoAction->setText(tr("&Undo")); undoAction->setShortcut(tr("Ctrl+Z")); redoAction->setText(tr("&Redo")); QList redoShortcuts; redoShortcuts << tr("Ctrl+Y") << tr("Shift+Ctrl+Z"); redoAction->setShortcuts(redoShortcuts); zoomInAction->setText(tr("Zoom &In")); zoomInAction->setShortcut(tr("Ctrl++")); zoomOutAction->setText(tr("Zoom &Out")); zoomOutAction->setShortcut(tr("Ctrl+-")); zoomFitAction->setText(tr("Zoom &Fit")); zoomFitAction->setShortcut(tr("Ctrl+F")); toggleMainToolbarAction->setText(tr("Main Toolbar")); toggleAnimationToolbarAction->setText(tr("Animation Toolbar")); toggleScenarioDockAction->setText(tr("Scenario Dock")); aboutAction->setText(tr("&About")); recentMenu->setTitle(tr("&Recent")); fileMenu->setTitle(tr("&File")); trackMenu->setTitle(tr("&Edit")); flagMenu->setTitle(tr("&Flag")); accelerationMenu->setTitle(tr("&Acceleration")); historyMenu->setTitle(tr("&History")); animationMenu->setTitle(tr("&Animation")); zoomMenu->setTitle(tr("&Zoom")); viewMenu->setTitle(tr("&View")); aboutMenu->setTitle(tr("&Help")); langMenu->setTitle(tr("Choose &Language")); } else { QMainWindow::changeEvent(event); } } void MainWindow::newFile() { SituationModel *situation = engine->currentModel(); SituationView *view = viewList.at(engine->currentIndex()); if (maybeSave()) { // tidy up animation (does no harm if not in animation mode) animate(false); engine->resetFile(); situationWidget->unSetSituation(); situationWidget->setSituation(situation); view->centerOn(0,0); view->resetMatrix(); } } void MainWindow::openFile() { QString fileName = QFileDialog::getOpenFileName(this, tr("Open Scenario File"), filePath, tr("xmlscenario Files (*.xbs)")); if (fileName.isEmpty()) return; filePath = QFileInfo(fileName).absolutePath(); openFile(fileName); } void MainWindow::openRecent() { QAction *action = qobject_cast(sender()); if (action) { int item = action->data().toInt(); if (recentList.size() > item) { openFile(recentList[item]); } } } void MainWindow::restoreFiles() { openFiles(fileList); } void MainWindow::openFiles(QStringList fileList) { foreach (const QString fileName, fileList) { std::cout << "opening " << fileName.toStdString() << std::endl; QFile file(fileName); if (file.open(QFile::ReadOnly | QFile::Text)) { openFile(fileName, fileList.first() != fileName); } } } void MainWindow::openFile(const QString &fileName, bool inNewTab) { if (inNewTab) { // create new tab newTab(); tabWidget->setCurrentIndex(engine->situationSize() - 1); } else { // delete situation; newFile(); } if (!engine->openFile(fileName)) { return; } situationWidget->update(); SituationScene *scene = sceneList.at(engine->currentIndex()); SituationView *view = viewList.at(engine->currentIndex()); view->centerOn(scene->itemsBoundingRect().center()); statusbar->showMessage(tr("File loaded"), 2000); } bool MainWindow::saveSituation(QString fileName) { SituationModel *situation = engine->currentModel(); QString name = fileName; if (name.isEmpty()) { QString defaultFile; if (situation->fileName().isEmpty()) { defaultFile = QDateTime::currentDateTime().toString("yyMMdd") + ".xbs"; } else { defaultFile = situation->fileName(); } name = QFileDialog::getSaveFileName(this, tr("Save Scenario"), defaultFile, tr("xmlscenario Files (*.xbs)"), NULL, QFileDialog::DontConfirmOverwrite); if (name.isEmpty()) { return false; } if (QFileInfo(name).suffix().isEmpty()) name.append(".xbs"); if (QFile::exists(name)) { if (QMessageBox::warning(this, tr("Save Scenario"), tr("%1 already exists.\nDo you want to replace it?") .arg(QFileInfo(name).baseName()), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No) { return false; } } } QFile file(name); if (!file.open(QFile::WriteOnly | QFile::Text)) { QMessageBox::warning(this, tr("Save Scenario"), tr("Cannot write file %1:\n%2.") .arg(fileName) .arg(file.errorString())); return false; } if(!engine->saveSituation(name)) { return false; } statusbar->showMessage(tr("File saved"), 2000); return true; } bool MainWindow::saveFile() { SituationModel *situation = engine->currentModel(); bool animated = situation->stateMachine()->animationState()->isActive(); animate(false); bool saved = saveSituation(situation->fileName()); if (animated) { animate(true); } return saved; } bool MainWindow::saveAs() { SituationModel *situation = engine->currentModel(); bool animated = situation->stateMachine()->animationState()->isActive(); animate(false); bool saved = saveSituation(""); if (animated) { animate(true); } return saved; } void MainWindow::print() { SituationModel *situation = engine->currentModel(); SituationScene *scene = sceneList.at(engine->currentIndex()); SituationView *view = viewList.at(engine->currentIndex()); QPrinter printer(QPrinter::HighResolution); QPrintDialog *dialog = new QPrintDialog(&printer, this); dialog->setWindowTitle(tr("Print Document")); if (dialog->exec() != QDialog::Accepted) { return; } scene->clearSelection(); SituationPrint printSituation(situation, view); printSituation.render(printer.paperRect(QPrinter::Millimeter).adjusted(20, 20, -20, -20)); printSituation.print(&printer); } void MainWindow::printPreview() { SituationModel *situation = engine->currentModel(); SituationScene *scene = sceneList.at(engine->currentIndex()); SituationView *view = viewList.at(engine->currentIndex()); scene->clearSelection(); SituationPrint printSituation(situation, view); QPrinter printer(QPrinter::HighResolution); QPrintPreviewDialog dialog(&printer); printSituation.render(printer.paperRect(QPrinter::Millimeter).adjusted(20, 20, -20, -20)); connect(&dialog, SIGNAL(paintRequested(QPrinter*)), &printSituation, SLOT(print(QPrinter*))); dialog.exec(); } void MainWindow::exportPdf() { SituationModel *situation = engine->currentModel(); SituationScene *scene = sceneList.at(engine->currentIndex()); SituationView *view = viewList.at(engine->currentIndex()); QString defaultName(situation->fileName()); defaultName.chop(4); QString fileName = QFileDialog::getSaveFileName(this, "Export to PDF", defaultName, "PDF Files (*.pdf)"); if (fileName.isEmpty()) { return; } if (QFileInfo(fileName).suffix().isEmpty()) fileName.append(".pdf"); QPrinter printer(QPrinter::HighResolution); printer.setOutputFormat(QPrinter::PdfFormat); printer.setOutputFileName(fileName); scene->clearSelection(); SituationPrint printSituation(situation, view); printSituation.render(printer.paperRect(QPrinter::Millimeter).adjusted(20, 20, -20, -20)); printSituation.print(&printer); } void MainWindow::exportImage() { SituationModel *situation = engine->currentModel(); SituationScene *scene = sceneList.at(engine->currentIndex()); SituationView *view = viewList.at(engine->currentIndex()); QString defaultName(situation->fileName()); defaultName.chop(4); QList formatsList = QImageWriter::supportedImageFormats(); #if defined(Q_WS_MAC) QString formats("PNG Image Files (*.png)"); #else QString formats; for (int i=0; iclearSelection(); QPixmap pixmap = view->screenShot(); QFile file(fileName); if (!file.open(QFile::WriteOnly | QFile::Text)) { QMessageBox::warning(this, tr("Export Image"), tr("Cannot write file %1:\n%2.") .arg(fileName) .arg(file.errorString())); return; } pixmap.save(fileName); } #ifdef GIF_EXPORT void MainWindow::exportAnimation() { SituationModel *situation = engine->currentModel(); SituationView *view = viewList.at(engine->currentIndex()); QString defaultName(situation->fileName()); defaultName.chop(4); QString format("GIF Files (*.gif)"); QString ext; QString fileName = QFileDialog::getSaveFileName(this, tr("Export Animation"), defaultName, format, &ext); if (fileName.isEmpty()) { return; } // if no provided extension or incorrect extension, use selected filter int dotIndex = fileName.lastIndexOf("."); QString baseName = fileName.left(dotIndex); QString newExt = fileName.right(fileName.size()-dotIndex-1); if (!newExt.toUtf8().contains("gif")) { fileName.append(".gif"); } GifWriter *writer = new GifWriter(); animate(true, false); QProgressDialog progress(tr("Exporting Animation..."), tr("Abort"), 0, situation->animation()->duration(), this); progress.setWindowModality(Qt::WindowModal); statusbar->showMessage("Exporting animation"); situation->animation()->setCurrentTime(situation->animation()->duration()/2); QPixmap pixmap = view->screenShot(); QImage shot = pixmap.toImage().convertToFormat(QImage::Format_Indexed8); writer->setColorMap(shot); QList imageList; for (int i=0; i<=situation->animation()->duration(); i+=80) { situation->animation()->setCurrentTime(i); pixmap = view->screenShot(); QImage *image = new QImage(pixmap.toImage() .convertToFormat(QImage::Format_Indexed8, writer->colormap())); imageList.append(image); progress.setValue(i); if (progress.wasCanceled()) { break; } } if (!progress.wasCanceled()) { QFile file(fileName); if (!file.open(QFile::WriteOnly)) { QMessageBox::warning(this, tr("Export Animation"), tr("Cannot write file %1:\n%2.") .arg(fileName) .arg(file.errorString())); return; } writer->setDevice(&file); writer->write(imageList); } animate(false); } #endif void MainWindow::toggleFlag() { QAction *action = qobject_cast(sender()); if (action) { Boats::Flag flag = (Boats::Flag)action->data().toInt(); engine->toggleFlag(flag); } } void MainWindow::toggleAcceleration() { QAction *action = qobject_cast(sender()); if (action) { Boats::Acceleration acceleration = (Boats::Acceleration)action->data().toInt(); engine->toggleAcceleration(acceleration); } } void MainWindow::setMarkColor() { if (! engine->currentModel()->selectedMarkModels().isEmpty()) { ColorPickerWidget colorEditor; colorEditor.setColor( engine->currentModel()->selectedMarkModels().first()->color()); engine->setMarkColor(colorEditor.color()); } } void MainWindow::editMarkLabel() { SituationModel *situation = engine->currentModel(); QList markList = situation->selectedMarkModels(); if (! markList.isEmpty()) { QString oldText = markList.first()->labelText(); bool ok; QString newText = QInputDialog::getText(this, tr("Edit mark label"), tr("Label text:"), QLineEdit::Normal, oldText, &ok); if (ok) { engine->editMarkLabel(newText); } } } void MainWindow::setLookAt() { engine->setLookAt(lookDirectionSlider->value(), tiltSlider->value()); } void MainWindow::toggleLang() { QAction *action = qobject_cast(sender()); if (action) { qApp->removeTranslator(qtTranslator); qApp->removeTranslator(translator); createTranslations(action->data().toString()); } } void MainWindow::animate(bool state, bool interactive) { SituationModel *situation = engine->currentModel(); if (state) { if(!situation->stateMachine()->animationState()->isActive()) { engine->animate(); undoAction->setEnabled(false); redoAction->setEnabled(false); connect(situation->animation(), SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)), this, SLOT(changeAnimationState(QAbstractAnimation::State, QAbstractAnimation::State))); connect(situation->animation(), SIGNAL(timeChanged(int)), animationSlider, SLOT(setValue(int))); connect(animationSlider, SIGNAL(valueChanged(int)), situation->animation(), SLOT(setCurrentTime(int))); if (interactive) { if (!toggleAnimationToolbarAction->isChecked()) { toggleAnimationToolbarAction->setChecked(true); } animationSlider->setRange(0,situation->animation()->duration()); animationSlider->setEnabled(true); } } } else { if(situation->stateMachine()->animationState()->isActive()) { engine->animate(); undoAction->setEnabled(situation->undoStack()->canUndo()); redoAction->setEnabled(situation->undoStack()->canRedo()); disconnect(this, SLOT(changeAnimationState(QAbstractAnimation::State,QAbstractAnimation::State))); disconnect(animationSlider, SLOT(setValue(int))); disconnect(situation->animation(), SLOT(setCurrentTime(int))); animationSlider->setEnabled(false); situation->animation()->stop(); } } } void MainWindow::loop(bool loop) { SituationModel *situation = engine->currentModel(); if (loop) { if (debugLevel & 1 << ANIMATION) std::cout << "loop play" << std::endl; situation->animation()->setLoopCount(-1); } else { if (debugLevel & 1 << ANIMATION) std::cout << "single play" << std::endl; situation->animation()->setLoopCount(1); } } void MainWindow::changeAnimationState(QAbstractAnimation::State newState, QAbstractAnimation::State) { switch(newState) { case QAbstractAnimation::Running: if (debugLevel & 1 << ANIMATION) std::cout << "state running" << std::endl; animationSlider->blockSignals(true); break; case QAbstractAnimation::Paused: if (debugLevel & 1 << ANIMATION) std::cout << "state paused" << std::endl; animationSlider->blockSignals(false); break; case QAbstractAnimation::Stopped: if (debugLevel & 1 << ANIMATION) std::cout << "state not running" << std::endl; animationSlider->blockSignals(false); break; } } void MainWindow::about() { QMessageBox::about(this, tr("About Boat Scenario"), QString("
" "

Boat Scenario - a Race Scenario drawing tool.

" "

Version %1

" "

Copyright (C) 2008-2019 Thibaut GRIDEL

" "

" "

visit http://boats.sf.net

" "

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.

").arg(VERSION)); } boats-201908/mainwindow.h000066400000000000000000000145411353032755300152670ustar00rootroot00000000000000// // C++ Interface: mainwindow // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2014 Thibaut GRIDEL // // 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 . // // #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "situationwidget.h" #include "situationscene.h" #include "situationview.h" #include "situationprint.h" #include #include class BoatsEngine; class SituationModel; class TrackWidget; /** \class MainWindow \brief The Main Window This class initialises a lot of objects. They are roughly sorted into three categories: - The GraphicsView objects - The Widgets available from the main window - The QActions triggered in the program There are many slot methods which will be connected to QAction signals */ class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); ~MainWindow(); virtual QMenu *createPopupMenu(){return 0;} public slots: // State management void cleanState(bool state); void updateActions(); void enterCreateState(); void exitCreateState(); // File actions void newTab(); void setTab(int index); void removeTab(); void newFile(); void restoreFiles(); void openFile(); void openFiles(QStringList fileList); void openFile(const QString &fileName, bool inNewTab = false); void openRecent(); bool saveFile(); bool saveAs(); void print(); void printPreview(); void exportPdf(); void exportImage(); #ifdef GIF_EXPORT void exportAnimation(); #endif // Track actions void toggleFlag(); void toggleAcceleration(); void setMarkColor(); void editMarkLabel(); void setLookAt(); // Animation actions void animate(bool state, bool interactive=true); void loop(bool loop); void changeAnimationState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState); // About actions void toggleLang(); void about(); protected: void closeEvent(QCloseEvent *event); void changeEvent(QEvent *event); private: // Initialisation methods void createTranslations(QString locale); void createActions(); void createMenus(); void createDocks(); // Configuration methods void unsetTab(); void writeSettings(); void readSettings(); void updateRecentList(); bool maybeSave(); // File methods bool saveSituation(QString name); // GraphicsView Framework QList sceneList; QList viewList; static const int maxRecent; QStringList recentList; QStringList fileList; QString filePath; // Widgets on the window QMenuBar *menubar; QToolBar *toolbar; QTabWidget *tabWidget; QPushButton *newTabButton; QPushButton *removeTabButton; QSlider *lookDirectionSlider; QSlider *tiltSlider; QToolBar *animationBar; QDockWidget *situationDock; SituationWidget *situationWidget; QStatusBar *statusbar; QSlider *animationSlider; QTranslator *qtTranslator; QTranslator *translator; BoatsEngine *engine; // QActions QAction *newFileAction; QAction *openFileAction; QAction *restoreFilesAction; QAction *saveFileAction; QAction *saveAsAction; QAction *newTabAction; QAction *removeTabAction; QAction *printAction; QAction *printPreviewAction; QAction *exportPdfAction; QAction *exportImageAction; #ifdef GIF_EXPORT QAction *exportAnimationAction; #endif QAction *exitAction; QAction *addTrackAction; QAction *addBoatAction; QAction *addMarkAction; QAction *addPolyLineAction; QAction *addPointAction; QAction *trimSailAction; QAction *autotrimSailAction; QAction *untrimSailAction; QAction *trimJibAction; QAction *autotrimJibAction; QAction *untrimJibAction; QAction *trimSpinAction; QAction *autotrimSpinAction; QAction *untrimSpinAction; QAction *togglePortOverlapAction; QAction *toggleStarboardOverlapAction; QAction *toggleHiddenAction; QAction *toggleTextAction; QAction *toggleSpinAction; QAction *toggleMarkSideAction; QAction *toggleMarkArrowAction; QAction *toggleMarkZoneAction; QAction *setMarkColorAction; QAction *toggleLaylinesAction; QAction *toggleMarkLabelAction; QAction *editMarkLabelAction; QAction *deleteTrackAction; QAction *deleteAction; QAction *animateAction; QAction *startAction; QAction *pauseAction; QAction *stopAction; QAction *loopAction; QAction *undoAction; QAction *redoAction; QAction *zoomInAction; QAction *zoomOutAction; QAction *zoomFitAction; QAction *toggleMainToolbarAction; QAction *toggleAnimationToolbarAction; QAction *toggleScenarioDockAction; QAction *aboutAction; // QMenu QMenu *fileMenu; QMenu *recentMenu; QMenu *trackMenu; QMenu *flagMenu; QMenu *accelerationMenu; QMenu *defaultPopup; QMenu *boatPopup; QMenu *markPopup; QMenu *pointPopup; QMenu *historyMenu; QMenu *zoomMenu; QMenu *animationMenu; QMenu *viewMenu; QMenu *aboutMenu; QMenu *langMenu; }; #endif boats-201908/model/000077500000000000000000000000001353032755300140355ustar00rootroot00000000000000boats-201908/model/boatmodel.cpp000066400000000000000000000266111353032755300165150ustar00rootroot00000000000000// // C++ Implementation: boatmodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #include #include #include "boatmodel.h" #include "commontypes.h" #include "situationmodel.h" #include "trackmodel.h" extern int debugLevel; BoatModel::BoatModel(TrackModel* track, QObject *parent) : PositionModel(track->situation(), parent), m_trim(0), m_jibTrim(0), m_spin(false), m_spinTrim(0), m_overlap(Boats::none), m_flag(Boats::noFlag), m_hidden(false), m_acceleration(Boats::constant), m_track(track), m_hasSpin(false), m_maxNormalSailAngle(0), m_maxNormalJibAngle(0), m_maxWithSpinSailAngle(0), m_maxWithSpinJibAngle(0), m_dim(255) { if (debugLevel & 1 << MODEL) std::cout << "new Boat " << this << std::endl; setOrder(track->size()+1); } BoatModel::~BoatModel() { if (debugLevel & 1 << MODEL) std::cout << "delete Boat " << this << std::endl; } void BoatModel::setHeading(const qreal& theValue) { if (theValue != m_heading) { m_heading = fmod(theValue,360.0); // This is always between -360 and +360 if (m_heading < 0) m_heading += 360.0; // This ensures it is between 0 and +360 emit headingChanged(m_heading); qreal trimmedSail = trimmedSailAngle(); qreal trimmedJib = trimmedJibAngle(); qreal trimmedSpin = trimmedSpinAngle(); qreal trimmedGenn = trimmedGennAngle(); emit trimmedSailAngleChanged(trimmedSail); emit trimmedJibAngleChanged(trimmedJib); emit trimmedSpinAngleChanged(trimmedSpin); emit trimmedGennAngleChanged(trimmedGenn); m_track->changingTrack(m_track); if (debugLevel & 1 << MODEL) std::cout << "heading = " << theValue << " sail = " << trimmedSail << " jib = " << trimmedJib << " spin = " << trimmedSpin << " genn = " << trimmedGenn << std::endl; } } void BoatModel::setPosition(const QPointF& theValue) { PositionModel::setPosition(theValue); m_track->changingTrack(m_track); } void BoatModel::setTrim(const qreal& theValue) { qreal newAngle = sailAngle() + theValue; if (theValue != m_trim && newAngle < 180 && newAngle > -180) { m_trim = theValue; qreal trimmedSail = trimmedSailAngle(); emit trimmedSailAngleChanged(trimmedSail); if (debugLevel & 1 << MODEL) std::cout << "trim = " << theValue << " heading = " << m_heading << " sail = " << trimmedSail << std::endl; } } qreal BoatModel::trimmedSailAngle() const { return sailAngle() + m_trim; } void BoatModel::setTrimmedSailAngle(qreal theValue) { m_trim = theValue - sailAngle(); emit trimmedSailAngleChanged(theValue); } void BoatModel::setJibTrim(const qreal& theValue) { qreal newAngle = jibAngle() + theValue; if (theValue != m_jibTrim && newAngle < 180 && newAngle > -180) { m_jibTrim = theValue; qreal trimmedJib = trimmedJibAngle(); emit trimmedJibAngleChanged(trimmedJib); if (debugLevel & 1 << MODEL) std::cout << "jibtrim = " << theValue << " heading = " << m_heading << " jib = " << trimmedJib << std::endl; } } qreal BoatModel::trimmedJibAngle() const { return jibAngle() + m_jibTrim; } void BoatModel::setTrimmedJibAngle(qreal theValue) { m_jibTrim = theValue - jibAngle(); emit trimmedJibAngleChanged(theValue); } void BoatModel::setHasSpin(const bool theValue) { if (theValue != m_hasSpin) { m_hasSpin = theValue; } } void BoatModel::setSpin(const bool theValue) { if (theValue != m_spin) { m_spin = theValue; emit spinChanged(m_spin); emit trimmedSailAngleChanged(trimmedSailAngle()); emit trimmedJibAngleChanged(trimmedJibAngle()); } } void BoatModel::setSpinTrim(const qreal& theValue) { // Spinnaker graphics code doesn't actually respect spin trim so use this for gennaker trimming qreal sailAngle = gennAngle(); // spinAngle(); qreal newAngle = sailAngle + theValue; if (theValue != m_spinTrim && newAngle < 180 && newAngle > -180) { m_spinTrim = theValue; qreal trimmedSpin = trimmedSpinAngle(); qreal trimmedGenn = trimmedGennAngle(); emit trimmedSpinAngleChanged(trimmedSpin); emit trimmedGennAngleChanged(trimmedGenn); if (debugLevel & 1 << MODEL) std::cout << "spintrim = " << theValue << " heading = " << m_heading << " spin = " << trimmedSpin << " genn = " << trimmedGenn << std::endl; } } qreal BoatModel::trimmedSpinAngle() const { return spinAngle() + m_spinTrim; } void BoatModel::setTrimmedSpinAngle(qreal theValue) { m_spinTrim = theValue - spinAngle(); emit trimmedSpinAngleChanged(theValue); } qreal BoatModel::trimmedGennAngle() const { return gennAngle() + m_spinTrim; } void BoatModel::setTrimmedGennAngle(qreal theValue) { emit trimmedGennAngleChanged(theValue); } void BoatModel::setMaxNormalSailAngle(const qreal& theValue) { m_maxNormalSailAngle = theValue; emit trimmedSailAngleChanged(trimmedSailAngle()); } void BoatModel::setMaxNormalJibAngle(const qreal& theValue) { m_maxNormalJibAngle = theValue; emit trimmedJibAngleChanged(trimmedJibAngle()); } void BoatModel::setMaxWithSpinSailAngle(const qreal& theValue) { m_maxWithSpinSailAngle = theValue; emit trimmedSailAngleChanged(trimmedSailAngle()); } void BoatModel::setMaxWithSpinJibAngle(const qreal& theValue) { m_maxWithSpinJibAngle = theValue; emit trimmedJibAngleChanged(trimmedJibAngle()); } void BoatModel::setOverlap(const Boats::Overlaps theValue) { if (theValue != m_overlap) { if (debugLevel & 1 << MODEL) std::cout << "overlap = " << theValue << std::endl; m_overlap = theValue; emit overlapChanged(m_overlap); } } void BoatModel::setFlag(const Boats::Flag theValue) { if (theValue != m_flag) { if (debugLevel & 1 << MODEL) std::cout << "flag = " << theValue << std::endl; m_flag = theValue; emit flagChanged(m_flag); } } void BoatModel::setHidden(const bool theValue) { if (theValue != m_hidden) { if (debugLevel & 1 << MODEL) std::cout << "hidden = " << theValue << std::endl; m_hidden = theValue; emit hiddenChanged(m_hidden); } } void BoatModel::setAcceleration(const Boats::Acceleration theValue) { if (theValue != m_acceleration) { if (debugLevel & 1 << MODEL) std::cout << "acceleration = " << theValue << std::endl; m_acceleration = theValue; } } qreal BoatModel::sailAngle(qreal heading) const { qreal layline = situation()->laylineAngle(); qreal sailAngle; if (heading == -1) { heading = fmod(m_heading - wind() + 360, 360); } // within 10° inside layline angle, the sail is headed if (heading < layline-10) { sailAngle = fmin(layline-20,heading); // layline-20 so that sail is at stall angle of 10 degrees when heading = layline-10 } else if (heading > 360 - (layline-10)) { sailAngle = fmax(-(layline-20),heading - 360); } else { qreal maxSailAngle; if (m_hasSpin && m_spin) { maxSailAngle = m_maxWithSpinSailAngle; } else { maxSailAngle = m_maxNormalSailAngle; } // linear incidence variation // incidence is 15° at layline angle and maxWithSpinSailAngle downwind qreal a = (180 - layline) / (maxSailAngle - 15); qreal b = layline / a - 15; if (heading<180) { sailAngle = heading/a - b; } else { sailAngle = heading/a - b - 2*maxSailAngle; } } return sailAngle; } qreal BoatModel::jibAngle(qreal heading) const { qreal layline = situation()->laylineAngle(); qreal sailAngle; if (heading == -1) { heading = fmod(m_heading - wind() + 360, 360); } // within 10° inside layline angle, the sail is headed if (heading < layline-10) { sailAngle = fmin(layline-20,heading); // layline-20 so that sail is at stall angle of 10 degrees when heading = layline-10 } else if (heading > 360 - (layline-10)) { sailAngle = fmax(-(layline-20),heading - 360); } else { qreal maxJibAngle; if (m_hasSpin && m_spin) { maxJibAngle = m_maxWithSpinJibAngle; } else { maxJibAngle = m_maxNormalJibAngle; } // linear incidence variation // incidence is 20° at layline angle and maxWithSpinJibAngle downwind qreal a = (180 - layline) / (maxJibAngle - 20); qreal b = layline / a - 20; if (heading<180) { sailAngle = heading/a - b; } else { sailAngle = heading/a - b - 2*maxJibAngle; } } return sailAngle; } qreal BoatModel::spinAngle(qreal heading) const { qreal sailAngle; if (heading == -1) { heading = fmod(m_heading - wind() + 360, 360); } // within 10° above downwind angle, the sail is headed if (heading < 80) { sailAngle = heading; } else if (heading > 280) { sailAngle = heading - 360; } else { if (heading<180) { sailAngle = heading - 20; } else { sailAngle = heading + 20; } } return sailAngle; } qreal BoatModel::gennAngle(qreal heading) const { qreal sailAngle; if (heading == -1) { heading = fmod(m_heading - wind() + 360, 360); } // within 10° above downwind angle, the sail is headed if (heading < 80) { sailAngle = heading / 80 * 20; } else if (heading > 360 - 80) { sailAngle = - (360 - heading) / 80 * 20; } else { // linear incidence variation // incidence is 20° at 80° heading and 30° downwind qreal a = (180 - 80) / (30 - 20); qreal b = 80 / a - 20; if (heading<180) { sailAngle = heading/a - b; } else { sailAngle = heading/a - b - 2*30; } } return sailAngle; } void BoatModel::setWind(qreal wind) { PositionModel::setWind(wind); emit headingChanged(m_heading); emit trimmedSailAngleChanged(trimmedSailAngle()); emit trimmedJibAngleChanged(trimmedJibAngle()); emit trimmedSpinAngleChanged(trimmedSpinAngle()); emit trimmedGennAngleChanged(trimmedGennAngle()); } void BoatModel::setDim(int dim) { m_dim = dim; emit dimChanged(dim); } void BoatModel::setPath(QPainterPath path) { m_path = path; } boats-201908/model/boatmodel.h000066400000000000000000000171261353032755300161630ustar00rootroot00000000000000// // C++ Interface: boatmodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #ifndef BOATMODEL_H #define BOATMODEL_H #include #include "boats.h" #include "positionmodel.h" class SituationModel; class TrackModel; /** \class BoatModel \brief The Model for a Boat Position The class represents the Model for a Boat, according to an Observer Pattern. BoatModel inherits PositionModel and contains data which represents one boat at a given time, like its position and heading. It shall not be mistaken with TrackModel, which holds the list of all positions where a Boat has navigated. More exactly, a TrackModel will hold a List of BoatModels. \sa SituationModel, TrackModel */ class BoatModel : public PositionModel { Q_OBJECT Q_PROPERTY(qreal trim READ trim WRITE setTrim NOTIFY trimmedSailAngleChanged) Q_PROPERTY(qreal maxNormalSailAngle READ maxNormalSailAngle WRITE setMaxNormalSailAngle) Q_PROPERTY(qreal maxWithSpinSailAngle READ maxWithSpinSailAngle WRITE setMaxWithSpinSailAngle) Q_PROPERTY(qreal trimSailAngle READ trimmedSailAngle WRITE setTrimmedSailAngle NOTIFY trimmedSailAngleChanged) Q_PROPERTY(qreal jibTrim READ jibTrim WRITE setJibTrim NOTIFY trimmedJibAngleChanged) Q_PROPERTY(qreal maxNormalJibAngle READ maxNormalJibAngle WRITE setMaxNormalJibAngle) Q_PROPERTY(qreal maxWithSpinJibAngle READ maxWithSpinJibAngle WRITE setMaxWithSpinJibAngle) Q_PROPERTY(qreal trimJibAngle READ trimmedJibAngle WRITE setTrimmedJibAngle NOTIFY trimmedJibAngleChanged) Q_PROPERTY(bool spin READ spin WRITE setSpin NOTIFY spinChanged) Q_PROPERTY(qreal spinTrim READ spinTrim WRITE setSpinTrim NOTIFY trimmedSpinAngleChanged) Q_PROPERTY(qreal trimSpinAngle READ trimmedSpinAngle WRITE setTrimmedSpinAngle NOTIFY trimmedSpinAngleChanged) Q_PROPERTY(qreal trimGennAngle READ trimmedGennAngle WRITE setTrimmedGennAngle NOTIFY trimmedGennAngleChanged) Q_PROPERTY(Boats::Overlaps overlap READ overlap WRITE setOverlap NOTIFY overlapChanged) Q_PROPERTY(Boats::Flag flag READ flag WRITE setFlag NOTIFY flagChanged) Q_PROPERTY(int dim READ dim WRITE setDim NOTIFY dimChanged) public: BoatModel(TrackModel *track = 0, QObject *parent = 0); ~BoatModel(); // Setters and Getters for Model Data virtual void setPosition(const QPointF& theValue); void setHeading(const qreal& theValue); qreal trim() const { return m_trim; } void setTrim(const qreal& theValue); qreal trimmedSailAngle() const; void setTrimmedSailAngle(qreal theValue); qreal jibTrim() const { return m_jibTrim; } void setJibTrim(const qreal& theValue); qreal trimmedJibAngle() const; void setTrimmedJibAngle(qreal theValue); bool hasSpin() const {return m_hasSpin; } void setHasSpin(const bool theValue); bool spin() const { return m_spin; } void setSpin(const bool theValue); qreal spinTrim() const { return m_spinTrim; } void setSpinTrim(const qreal& theValue); qreal trimmedSpinAngle() const; void setTrimmedSpinAngle(qreal theValue); qreal trimmedGennAngle() const; void setTrimmedGennAngle(qreal theValue); qreal maxNormalSailAngle() const {return m_maxNormalSailAngle; } void setMaxNormalSailAngle(const qreal& theValue); qreal maxNormalJibAngle() const {return m_maxNormalJibAngle; } void setMaxNormalJibAngle(const qreal& theValue); qreal maxWithSpinSailAngle() const {return m_maxWithSpinSailAngle; } void setMaxWithSpinSailAngle(const qreal& theValue); qreal maxWithSpinJibAngle() const {return m_maxWithSpinJibAngle; } void setMaxWithSpinJibAngle(const qreal& theValue); Boats::Overlaps overlap() const {return m_overlap; } void setOverlap(const Boats::Overlaps theValue); Boats::Flag flag() const {return m_flag; } void setFlag(const Boats::Flag theValue); bool hidden() const {return m_hidden; } void setHidden(const bool theValue); Boats::Acceleration acceleration() const {return m_acceleration; } void setAcceleration(const Boats::Acceleration theValue); // Setters and Getters for Non model Data Q_INVOKABLE TrackModel* track() const { return m_track; } qreal sailAngle(qreal heading = -1) const; qreal jibAngle(qreal heading = -1) const; qreal spinAngle(qreal heading = -1) const; void setWind(qreal wind); qreal gennAngle(qreal heading = -1) const; void setDim(int dim); int dim() const { return m_dim; } void setPath(QPainterPath path); const QPainterPath path() const { return m_path; } signals: void trimmedSailAngleChanged(qreal sailAngle); void trimmedJibAngleChanged(qreal jibAngle); void spinChanged(bool spin); void trimmedSpinAngleChanged(qreal spinAngle); void trimmedGennAngleChanged(qreal gennAngle); void overlapChanged(Boats::Overlaps overlap); void flagChanged(Boats::Flag flag); void hiddenChanged(bool hidden); void dimChanged(int dim); private: // Model Data /// \a m_trim holds the sailing trim of a Boat qreal m_trim; /// \a m_jibTrim holds the jib trim qreal m_jibTrim; /// \a m_spin holds whether a spinnaker (or gennaker) is used bool m_spin; /// \a m_spinTrim holds the spinnaker (or gennaker) trim qreal m_spinTrim; /// \a m_overlap holds whether an overlap line should be displayed Boats::Overlaps m_overlap; /// \a m_flag holds the flag to display Boats::Flag m_flag; /// \a m_hidden holds whether the boat should display the hidden symbol bool m_hidden; /// \a m_acceleration holds how the boat speed should vary for the next period Boats::Acceleration m_acceleration; // Non model Data /// \a m_track keeps a pointer to the TrackModel to which /// it belongs TrackModel *m_track; /// \a m_hasSpin holds whether this type of boat has a spinnaker (or gennaker) bool m_hasSpin; /// \a m_maxNormalSailAngle holds the max sail angle to use when sailing without a spinnaker qreal m_maxNormalSailAngle; /// \a m_maxNormalJibAngle holds the max jib angle to use when sailing without a spinnaker qreal m_maxNormalJibAngle; /// \a m_maxWithSpinSailAngle holds the max sail angle to use when sailing with a spinnaker qreal m_maxWithSpinSailAngle; /// \a m_maxWithSpinJibAngle holds the max jib angle to use when sailing with a spinnaker qreal m_maxWithSpinJibAngle; int m_dim; /// \a m_path holds the QPainterPath to the next boat QPainterPath m_path; }; #endif boats-201908/model/markmodel.cpp000066400000000000000000000065261353032755300165250ustar00rootroot00000000000000// // C++ Implementation: markmodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2010 Thibaut GRIDEL // // 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 . // // #include #include "markmodel.h" #include "commontypes.h" #include "situationmodel.h" extern int debugLevel; MarkModel::MarkModel(SituationModel* situation, QObject *parent) : PositionModel(situation, parent), m_color(Qt::gray), m_zone(false), m_arrowVisible(false), m_leaveToPort(true), m_labelVisible(true), m_length(situation->situationLength()) { if (debugLevel & 1 << MODEL) std::cout << "new Mark " << this << std::endl; setOrder(situation->markSize()+1); setLabelText(QString::number(order())); } MarkModel::~MarkModel() { if (debugLevel & 1 << MODEL) std::cout << "delete Mark " << this << std::endl; } void MarkModel::setColor(const QColor& theValue) { if (theValue != m_color) { m_color = theValue; emit colorChanged(m_color); } } void MarkModel::setZone(const bool theValue) { if (theValue != m_zone) { if (debugLevel & 1 << MODEL) std::cout << "Mark " << this << " zone " << theValue << std::endl; m_zone = theValue; emit zoneChanged(m_zone); } } void MarkModel::setLength(const int theValue) { if (theValue != m_length) { if (debugLevel & 1 << MODEL) std::cout << "Mark " << this << " length " << theValue << std::endl; m_length = theValue; emit lengthChanged(m_length); } } void MarkModel::setArrowVisible(const bool theValue) { if (theValue != m_arrowVisible) { if (debugLevel & 1 << MODEL) std::cout << "Mark " << this << " arrowVisible " << theValue << std::endl; m_arrowVisible = theValue; emit arrowVisibilityChanged(m_arrowVisible); } } void MarkModel::setLeaveToPort(const bool theValue) { if (theValue != m_leaveToPort) { if (debugLevel & 1 << MODEL) std::cout << "Mark " << this << " leaveToPort " << theValue << std::endl; m_leaveToPort = theValue; emit leaveToPortChanged(m_leaveToPort); } } void MarkModel::setLabelVisible(const bool theValue) { if (theValue != m_labelVisible) { if (debugLevel & 1 << MODEL) std::cout << "Mark " << this << " labelVisible " << theValue << std::endl; m_labelVisible = theValue; emit labelVisibilityChanged(m_labelVisible); } } void MarkModel::setLabelText(const QString theValue) { if (theValue != m_labelText) { if (debugLevel & 1 << MODEL) std::cout << "Mark " << this << " labelText " << theValue.toStdString() << std::endl; m_labelText = theValue; emit labelTextChanged(m_labelText); } } boats-201908/model/markmodel.h000066400000000000000000000067231353032755300161710ustar00rootroot00000000000000// // C++ Interface: markmodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2009 Thibaut GRIDEL // // 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 . // // #ifndef MARKMODEL_H #define MARKMODEL_H #include #include "positionmodel.h" class SituationModel; /** \class MarkModel \brief The Model for a Mark The class represents the Model for a Mark, according to an Observer Pattern. MarkModel inherits PositionModel and contains data which represents a mark, like its color, and whether the zone is displayed. \sa SituationModel */ class MarkModel : public PositionModel { Q_OBJECT Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) Q_PROPERTY(bool zone READ zone WRITE setZone NOTIFY zoneChanged) Q_PROPERTY(bool arrowVisible READ arrowVisible WRITE setArrowVisible NOTIFY arrowVisibilityChanged) public: MarkModel(SituationModel *situation = 0, QObject *parent = 0); ~MarkModel(); // Setters and Getters for Model Data QColor color() const { return m_color; } void setColor(const QColor& theValue); bool zone() const { return m_zone; } void setZone(const bool theValue); bool arrowVisible() const { return m_arrowVisible; } void setArrowVisible(const bool theValue); bool leaveToPort() const { return m_leaveToPort; } void setLeaveToPort(const bool theValue); bool labelVisible() const { return m_labelVisible; } void setLabelVisible(const bool theValue); QString labelText() const { return m_labelText; } void setLabelText(const QString theValue); // Setters and Getters for Non model Data int length() const { return m_length; } void setLength(const int theValue); signals: void colorChanged(QColor color); void zoneChanged(bool zone); void lengthChanged(int length); void arrowVisibilityChanged(bool visible); void leaveToPortChanged(bool leaveToPort); void labelVisibilityChanged(bool visible); void labelTextChanged(QString text); private: // Model Data /// \a m_color holds the color of the Mark QColor m_color; /// \a m_zone holds whether the zone of the Mark is displayed bool m_zone; /// \a m_arrowVisible is true if the mark rounding arrow should be visible bool m_arrowVisible; /// \a m_leaveToPort is true if the mark should be left to port bool m_leaveToPort; /// \a m_labelVisible is true if the mark label should be visible bool m_labelVisible; /// \a m_labelText holds the text to use for the label QString m_labelText; // Non model Data /// \a m_length keeps the length of Main Series of the Scenario int m_length; }; #endif boats-201908/model/model.pri000066400000000000000000000007571353032755300156620ustar00rootroot00000000000000INCLUDEPATH += $$PWD DEPENDPATH += $$PWD HEADERS += \ $$PWD/boatmodel.h \ $$PWD/markmodel.h \ $$PWD/pointmodel.h \ $$PWD/polylinemodel.h \ $$PWD/positionmodel.h \ $$PWD/situationmodel.h \ $$PWD/trackmodel.h \ $$PWD/windmodel.h SOURCES += \ $$PWD/boatmodel.cpp \ $$PWD/markmodel.cpp \ $$PWD/pointmodel.cpp \ $$PWD/polylinemodel.cpp \ $$PWD/positionmodel.cpp \ $$PWD/situationmodel.cpp \ $$PWD/trackmodel.cpp \ $$PWD/windmodel.cpp boats-201908/model/pointmodel.cpp000066400000000000000000000027411353032755300167170ustar00rootroot00000000000000// // C++ Implementation: pointmodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009-2010 Thibaut GRIDEL // // 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 . // // #include #include #include "pointmodel.h" #include "commontypes.h" #include "situationmodel.h" #include "polylinemodel.h" extern int debugLevel; PointModel::PointModel(PolyLineModel* polyline, QObject *parent) : PositionModel(polyline->situation(), parent), m_polyline(polyline) { if (debugLevel & 1 << MODEL) std::cout << "new Point " << this << std::endl; setOrder(polyline->size()+1); } PointModel::~PointModel() { if (debugLevel & 1 << MODEL) std::cout << "delete Point " << this << std::endl; } void PointModel::setPosition(const QPointF& theValue) { PositionModel::setPosition(theValue); m_polyline->changingPolyLine(m_polyline); } boats-201908/model/pointmodel.h000066400000000000000000000034121353032755300163600ustar00rootroot00000000000000// // C++ Interface: pointmodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009-2010 Thibaut GRIDEL // // 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 . // // #ifndef POINTMODEL_H #define POINTMODEL_H #include #include "positionmodel.h" class PolyLineModel; /** \class PointModel \brief The Model for a Point Position The class represents the Model for a Point, according to an Observer Pattern. PointModel inherits PositionModel and contains data which represents one point at a given time, as part of a PolyLine. \sa SituationModel, PolyLineModel */ class PointModel : public PositionModel { Q_OBJECT public: PointModel(PolyLineModel *polyline, QObject *parent = 0); ~PointModel(); // Setters and Getters for Model Data virtual void setPosition(const QPointF& theValue); // Setters and Getters for Non model Data PolyLineModel* polyLine() const { return m_polyline; } private: // Non model Data /// \a m_polyline keeps a pointer to the PolyLineModel to which /// it belongs PolyLineModel *m_polyline; }; #endif boats-201908/model/polylinemodel.cpp000066400000000000000000000056511353032755300174240ustar00rootroot00000000000000// // C++ Implementation: polylinemodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009-2010 Thibaut GRIDEL // // 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 . // // #include #include "polylinemodel.h" #include "commontypes.h" #include "situationmodel.h" #include "pointmodel.h" extern int debugLevel; PolyLineModel::PolyLineModel(SituationModel* situation, QObject *parent) : QObject(parent), m_situation(situation) { if (debugLevel & 1 << MODEL) std::cout << "new PolyLine " << this << std::endl; } PolyLineModel::~PolyLineModel() { if (debugLevel & 1 << MODEL) std::cout << "delete PolyLine " << this << std::endl; } void PolyLineModel::appendDiscardedXml(const QString& theValue) { if (!m_discardedXml.contains(theValue)) { m_discardedXml.append(theValue); } } void PolyLineModel::changingPolyLine(PolyLineModel *polyline) { QPainterPath path; if (m_points.size() < 1) { m_path = path; return; } path.moveTo(m_points[0]->position()); foreach(PointModel *point, m_points) { path.lineTo(point->position()); } m_path = path; emit polyLineChanged(polyline); } PointModel* PolyLineModel::addPoint(PointModel *point, int order) { if (order == -1) { order = m_points.size(); } m_points.insert(order, point); connect(&m_situation->wind(), SIGNAL(directionChanged(qreal)), point, SLOT(setWind(qreal))); m_situation->addingPoint(point); if (debugLevel & 1 << MODEL) std::cout << "Adding Point " << order+1 << std::endl; changingPolyLine(this); return point; } int PolyLineModel::deletePoint(PointModel *point) { int order = m_points.indexOf(point); m_points.removeOne(point); if (debugLevel & 1 << MODEL) std::cout << "Removing Point " << order+1 << std::endl; m_situation->removingPoint(point); changingPolyLine(this); return order; } void PolyLineModel::displayPoints() { if (debugLevel & 1 << MODEL) std::cout << "Displaying points" << std::endl; foreach (PointModel* point, m_points) { m_situation->addingPoint(point); } } void PolyLineModel::hidePoints() { if (debugLevel & 1 << MODEL) std::cout << "Hiding points" << std::endl; foreach (PointModel* point, m_points) { m_situation->removingPoint(point); } } boats-201908/model/polylinemodel.h000066400000000000000000000052421353032755300170650ustar00rootroot00000000000000// // C++ Interface: polylinemodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009-2010 Thibaut GRIDEL // // 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 . // // #ifndef POLYLINEMODEL_H #define POLYLINEMODEL_H #include #include "boats.h" class SituationModel; class PointModel; /** \class PolyLineModel \brief The Model for a list of Points The class represents the Model for a Line, according to an Observer Pattern. PolyLineModel contains data which describe one line, like the list of points. \sa SituationModel, PointModel */ class PolyLineModel : public QObject { Q_OBJECT public: PolyLineModel(SituationModel* situation = 0, QObject *parent = 0); ~PolyLineModel(); PointModel * addPoint(PointModel *point, int order = -1); int deletePoint(PointModel *point); void displayPoints(); void hidePoints(); // Setters and Getters for Model Data int size() const { return m_points.size();} const QList points() const { return m_points; } const QPainterPath path() const { return m_path; } // Setters and Getters for Non model Data SituationModel* situation() const { return m_situation; } QStringList discardedXml() const { return m_discardedXml; } void appendDiscardedXml(const QString& theValue); void changingPolyLine(PolyLineModel *polyline); signals: // Signals for PolyLineModel parameters void polyLineChanged(PolyLineModel *polyLine); private: // Model Data /// \a m_points holds the List of Point Positions of the PolyLine QList m_points; // Non model Data /// \a m_situation keeps a pointer to the SituationModel to which /// it belongs SituationModel *m_situation; /// \a m_path holds the QPainterPath of the PolyLine QPainterPath m_path; /// \a m_discardedXml keeps all unparsed xml tags QStringList m_discardedXml; }; #endif // POLYLINEMODEL_H boats-201908/model/positionmodel.cpp000066400000000000000000000063651353032755300174400ustar00rootroot00000000000000// // C++ Implementation: positionmodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #include #include "positionmodel.h" #include "situationmodel.h" #include "commontypes.h" extern int debugLevel; PositionModel::PositionModel(SituationModel* situation, QObject *parent) : QObject(parent), m_position(), m_heading(0), m_textPosition(10,10), m_laylines(false), m_situation(situation), m_wind(-1) { if (debugLevel & 1 << MODEL) std::cout << "new Position " << this << std::endl; } PositionModel::~PositionModel() { if (debugLevel & 1 << MODEL) std::cout << "delete Position " << this << std::endl; } void PositionModel::setPosition(const QPointF& theValue) { if (theValue != m_position) { if (debugLevel & 1 << MODEL) std::cout << "Position " << this << " position " << theValue.x() << ", " << theValue.y() << std::endl; m_position = theValue; emit positionChanged(m_position); } } void PositionModel::setOrder(const int theValue) { if (theValue != m_order) { if (debugLevel & 1 << MODEL) std::cout << "Position " << this << " order " << theValue << std::endl; m_order = theValue; emit orderChanged(m_order); } } void PositionModel::setHeading(const qreal& theValue) { if (theValue != m_heading) { if (debugLevel & 1 << MODEL) std::cout << "Heading " << this << " heading " << theValue << std::endl; m_heading = theValue; emit headingChanged(m_heading); } } void PositionModel::setText(const QString theValue) { if (theValue != m_text) { if (debugLevel & 1 << MODEL) std::cout << "text = " << theValue.toStdString() << std::endl; m_text = theValue; emit textChanged(m_text); } } void PositionModel::setTextPosition(const QPointF& theValue) { if (theValue != m_textPosition) { m_textPosition = theValue; emit textPositionChanged(m_textPosition); } } void PositionModel::setLaylines(bool theValue) { if(theValue != m_laylines) { m_laylines = theValue; emit laylinesChanged(m_laylines); } } void PositionModel::setWind(qreal wind) { m_wind = wind; emit windChanged(m_wind); } qreal PositionModel::wind() const { if (m_wind == -1) { return m_situation->wind().windAt(m_order-1); } return m_wind; } void PositionModel::appendDiscardedXml(const QString& theValue) { if (!m_discardedXml.contains(theValue)) { m_discardedXml.append(theValue); } } boats-201908/model/positionmodel.h000066400000000000000000000102261353032755300170740ustar00rootroot00000000000000// // C++ Interface: positionmodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #ifndef POSITIONMODEL_H #define POSITIONMODEL_H #include class SituationModel; /** \class PositionModel \brief The Model for a Position The class represents the Model for a Position, according to an Observer Pattern. PositionModel contains data which represents an object with a position, and a stacking order. It is inherited by BoatModel and PointModel. \sa SituationModel, BoatModel, PointModel */ class PositionModel : public QObject { Q_OBJECT Q_PROPERTY(QPointF pos READ position WRITE setPosition NOTIFY positionChanged) Q_PROPERTY(int order READ order WRITE setOrder NOTIFY orderChanged) Q_PROPERTY(qreal heading READ heading WRITE setHeading NOTIFY headingChanged) Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) Q_PROPERTY(QPointF textPos READ textPosition WRITE setTextPosition NOTIFY textPositionChanged) Q_PROPERTY(bool laylines READ laylines() WRITE setLaylines NOTIFY laylinesChanged) Q_PROPERTY(qreal wind READ wind WRITE setWind NOTIFY windChanged) public: PositionModel(SituationModel* situation = 0, QObject *parent = 0); virtual ~PositionModel(); // Setters and Getters for Model Data QPointF position() const { return m_position; } virtual void setPosition(const QPointF& theValue); int order() const { return m_order; } void setOrder(const int theValue); qreal heading() const { return m_heading; } virtual void setHeading(const qreal& theValue); QString text() const {return m_text; } void setText(const QString theValue); QPointF textPosition() const {return m_textPosition; } void setTextPosition(const QPointF& theValue); bool laylines() const {return m_laylines; } void setLaylines(bool theValue); // Setters and Getters for Non model Data SituationModel* situation() const { return m_situation; } virtual qreal wind() const; QStringList discardedXml() const { return m_discardedXml; } void appendDiscardedXml(const QString& theValue); public slots: virtual void setWind(qreal wind); signals: void positionChanged(QPointF position); void orderChanged(int order); void headingChanged(qreal heading); void textChanged(QString text); void textPositionChanged(QPointF textPosition); void windChanged(qreal wind); void laylinesChanged(bool laylines); protected: // Model Data /// \a m_position holds the position of the object QPointF m_position; /// \a m_order holds the stacking order of the object. It starts at 1 for track boat int m_order; /// \a m_heading holds the heading of the object qreal m_heading; /// \a m_text holds the text to display QString m_text; /// \a m_textPosition holds the position of the text to display QPointF m_textPosition; /// \a m_laylines holds wether the object should display laylines bool m_laylines; // Non model Data /// \a m_situation keeps a pointer to the SituationModel to which /// it belongs SituationModel *m_situation; qreal m_wind; /// \a m_discardedXml keeps all unparsed xml tags QStringList m_discardedXml; }; #endif boats-201908/model/situationmodel.cpp000066400000000000000000000616411353032755300176110ustar00rootroot00000000000000// // C++ Implementation: situationmodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2014 Thibaut GRIDEL // // 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 . // // #include #include "situationmodel.h" #include "commontypes.h" #include "trackmodel.h" #include "boatmodel.h" #include "markmodel.h" #include "polylinemodel.h" #include "pointmodel.h" #include "undocommands.h" #include "statemachine.h" #include "scenarioanimation.h" extern int debugLevel; SituationModel::SituationModel(QObject *parent) : QObject(parent), m_showLayline(true), m_laylineAngle(40), m_situationSeries(Boats::keelboat), m_situationLength(3), m_lookDirection(0), m_tilt(0), m_wind(this), m_undoStack(new QUndoStack(this)), m_stateMachine(new StateMachine(this)), m_scenarioAnimation(new ScenarioAnimation(this, this)) { if (debugLevel & 1 << MODEL) std::cout << "new situation " << this << std::endl; connect(&m_wind, SIGNAL(windReset()), this, SLOT(resetWind())); connect(m_undoStack, SIGNAL(canUndoChanged(bool)), this, SIGNAL(canUndoChanged(bool))); connect(m_undoStack, SIGNAL(canRedoChanged(bool)), this, SIGNAL(canRedoChanged(bool))); connect(m_stateMachine->animationState(), SIGNAL(entered()), m_scenarioAnimation, SLOT(setAnimation())); connect(m_stateMachine->animationState(), SIGNAL(exited()), m_scenarioAnimation, SLOT(unsetAnimation())); connect(m_stateMachine->createTrackState(), SIGNAL(entered()), this, SLOT(createTrack())); connect(m_stateMachine->createBoatState(), SIGNAL(entered()), this, SLOT(createBoat())); connect(m_stateMachine->createMarkState(), SIGNAL(entered()), this, SLOT(createMark())); connect(m_stateMachine->createLineState(), SIGNAL(entered()), this, SLOT(createLine())); connect(m_stateMachine->createPointState(), SIGNAL(entered()), this, SLOT(createPoint())); connect(m_stateMachine->moveState(), SIGNAL(entered()), this, SLOT(moveModel())); connect(m_stateMachine->rotateState(), SIGNAL(entered()), this, SLOT(rotateModel())); connect(m_stateMachine->createState(), SIGNAL(exited()), this, SLOT(exitCreateState())); m_stateMachine->start(); } SituationModel::~SituationModel() { if (debugLevel & 1 << MODEL) std::cout << "end situation " << this << std::endl; } void SituationModel::setTitle(const QString theValue) { if (theValue != m_title) { if (debugLevel & 1 << MODEL) std::cout << "Setting Title " << theValue.toStdString() << std::endl; m_title = theValue; emit titleChanged(m_title); } } void SituationModel::setRules(const QString theValue) { if (theValue != m_rules) { if (debugLevel & 1 << MODEL) std::cout << "Setting Rules " << theValue.toStdString() << std::endl; m_rules = theValue; emit rulesChanged(m_rules); } } void SituationModel::setShowLayline(const bool theValue) { if (theValue != m_showLayline) { if (debugLevel & 1 << MODEL) std::cout << "Show Layline " << theValue << std::endl; m_showLayline = theValue; emit showLaylineChanged(m_showLayline); emit laylineChanged(m_laylineAngle); } } void SituationModel::setLaylineAngle(const int theValue) { if (theValue != m_laylineAngle) { if (debugLevel & 1 << MODEL) std::cout << "Situation " << this << " Layline Angle " << theValue << std::endl; m_laylineAngle = theValue; emit laylineChanged(m_laylineAngle); } } void SituationModel::setSituationSeries(const int theValue) { if (theValue != m_situationSeries) { if (debugLevel & 1 << MODEL) std::cout << "Situation " << this << " Series " << ENUM_NAME(Boats, Series, theValue) << std::endl; m_situationSeries = (Boats::Series) theValue; emit seriesChanged(m_situationSeries); emit laylineChanged(m_laylineAngle); } } void SituationModel::setSituationLength(const int theValue) { if (theValue != m_situationLength) { if (debugLevel & 1 << MODEL) std::cout << "Situation " << this << " Length " << theValue << std::endl; m_situationLength = theValue; foreach (MarkModel *mark, m_marks) { mark->setLength(m_situationLength); } emit lengthChanged(m_situationLength); } } void SituationModel::setAbstract(const QString theValue) { if (theValue != m_abstract) { if (debugLevel & 1 << MODEL) std::cout << "Setting Abstract " << theValue.toStdString() << std::endl; m_abstract = theValue; emit abstractChanged(m_abstract); } } void SituationModel::setDescription(const QString theValue) { if (theValue != m_description) { if (debugLevel & 1 << MODEL) std::cout << "Setting Description " << theValue.toStdString() << std::endl; m_description = theValue; emit descriptionChanged(m_description); } } void SituationModel::setLookDirection(qreal theValue) { if (theValue != m_lookDirection) { if (debugLevel & 1 << MODEL) std::cout << "Setting lookDirection " << theValue << std::endl; m_lookDirection = theValue; emit lookDirectionChanged(m_lookDirection); } } void SituationModel::setTilt(qreal theValue) { if (theValue != m_tilt) { if (debugLevel & 1 << MODEL) std::cout << "Setting tilt " << theValue << std::endl; m_tilt = theValue; emit tiltChanged(m_tilt); } } void SituationModel::appendDiscardedXml(const QString& theValue) { if (!m_discardedXml.contains(theValue)) { m_discardedXml.append(theValue); } } void SituationModel::addTrack(TrackModel *track, int order) { if (order == -1) { order = m_tracks.size(); } m_tracks.insert(order,track); if (debugLevel & 1 << MODEL) std::cout << "Adding Track " << order+1 << std::endl; for (int i=order+1; isetOrder(i+1); } track->displayBoats(); emit trackAdded(track); emit tracksChanged(); } void SituationModel::deleteTrack(TrackModel *track) { int index = m_tracks.indexOf(track); if (debugLevel & 1 << MODEL) std::cout << "Removing Track " << index+1 << " with " << track->size() << std::endl; track->hideBoats(); m_tracks.removeOne(track); for (int i=index; isetOrder(i+1); } emit trackRemoved(track); emit tracksChanged(); } void SituationModel::addMark(MarkModel *mark, int order) { if (order == -1) { order = m_marks.size(); } m_marks.insert(order, mark); connect(&m_wind, SIGNAL(directionChanged(qreal)), mark, SLOT(setWind(qreal))); if (debugLevel & 1 << MODEL) std::cout << "Adding Mark " << order+1 << std::endl; for (int i=order+1; isetOrder(i+1); } emit markAdded(mark); emit marksChanged(); } int SituationModel::deleteMark(MarkModel *mark) { int index = m_marks.indexOf(mark); if (debugLevel & 1 << MODEL) std::cout << "Removing Mark " << index+1 << std::endl; m_marks.removeOne(mark); for (int i=index; isetOrder(i+1); } emit markRemoved(mark); emit marksChanged(); return index; } void SituationModel::addPolyLine(PolyLineModel *polyline, int order) { if (order == -1) { order = m_lines.size(); } m_lines.insert(order,polyline); if (debugLevel & 1 << MODEL) std::cout << "Adding PolyLine " << order+1 << std::endl; polyline->displayPoints(); emit polyLineAdded(polyline); } void SituationModel::deletePolyLine(PolyLineModel *polyline) { int index = m_lines.indexOf(polyline); if (debugLevel & 1 << MODEL) std::cout << "Removing Line " << index+1 << " with " << polyline->size() << std::endl; polyline->hidePoints(); m_lines.removeOne(polyline); emit polyLineRemoved(polyline); } void SituationModel::resetWind() { if (debugLevel & 1 << MODEL) std::cout << "Resetting Wind " << std::endl; foreach (TrackModel *track, m_tracks) { foreach (BoatModel *boat, track->boats()) { boat->setWind(m_wind.windAt(boat->order()-1)); } } foreach (MarkModel *mark, m_marks) { mark->setWind(m_wind.windAt(0)); } } void SituationModel::changeTitle(QString title) { if (title != m_title) { m_undoStack->push(new SetTitleUndoCommand(this, title)); } } void SituationModel::changeRules(QString rules) { if (rules != m_rules) { m_undoStack->push(new SetRulesUndoCommand(this, rules)); } } void SituationModel::toggleShowLayline(bool showlayline) { if (showlayline != m_showLayline) { m_undoStack->push(new SetShowLaylineUndoCommand(this)); } } void SituationModel::changeSeries(Boats::Series series) { if (series != m_situationSeries) { m_undoStack->push(new SetSituationSeriesUndoCommand(this, series)); } } void SituationModel::changeLaylineAngle(int angle) { if (angle != m_laylineAngle) { m_undoStack->push(new SetLaylineUndoCommand(this, angle)); } } void SituationModel::changeLength(int length) { if (length != m_situationLength) { m_undoStack->push(new LengthMarkUndoCommand(this, length)); } } void SituationModel::changeAbstract(QString abstract) { if (abstract != m_abstract) { m_undoStack->push(new SetAbstractUndoCommand(this, abstract)); } } void SituationModel::changeDescription(QString description) { if (description != m_description) { m_undoStack->push(new SetDescriptionUndoCommand(this, description)); } } void SituationModel::toggleWind() { m_undoStack->push(new SetShowWindUndoCommand(&m_wind)); } void SituationModel::moveModel() { if (!m_selectedModels.isEmpty()) { m_undoStack->push(new MoveModelUndoCommand(m_selectedModels, m_curPosition - m_selectedModels.first()->position())); } if (!m_selectedBoatModels.isEmpty()) { BoatModel *boat = m_selectedBoatModels[0]; TrackModel *track = boat->track(); if(boat->order() > 1) { qreal heading = track->headingForNext( boat->order()-2, boat->position()); boat->setHeading(heading); } } } void SituationModel::rotateModel() { if (!m_selectedModels.isEmpty()) { QPointF pos = m_curPosition - m_selectedModels.last()->position(); if (pos == QPointF()) { return; } qreal wind = m_selectedModels.last()->wind(); qreal theta = fmod((atan2 (pos.x(), -pos.y()) * 180 / M_PI) + 360.0, 360.0); qreal delta = fmod(theta - wind + 360, 360); qreal snap = m_laylineAngle; // face to wind if (fabs(delta)<=5) { theta = wind; // port closehauled } else if (fabs(delta - snap)<=5) { theta = fmod(wind + snap, 360); // port downwind } else if (fabs(delta -(180-snap)) <=5) { theta = fmod(wind + 180-snap, 360); // deadwind } else if (fabs(delta - 180)<=5) { theta = fmod(wind - 180, 360); // starboard downwind } else if (fabs(delta - (180+snap)) <=5) { theta = fmod(wind + 180 + snap, 360); // starboard closehauled } else if (fabs(delta - (360-snap)) <=5) { theta = fmod(wind + 360 - snap, 360); } m_undoStack->push(new RotateModelsUndoCommand(m_selectedModels, theta-m_selectedModels.last()->heading())); } } void SituationModel::rotateModel(qreal angle) { m_undoStack->push(new RotateModelsUndoCommand(m_selectedModels, angle)); } void SituationModel::deleteModels() { foreach (BoatModel *boat, m_selectedBoatModels) { TrackModel* track = boat->track(); if (track->size() > 1) { m_undoStack->push(new DeleteBoatUndoCommand(track, boat)); } else { m_undoStack->push(new DeleteTrackUndoCommand(this, track)); } } foreach (MarkModel *mark, m_selectedMarkModels) { m_undoStack->push(new DeleteMarkUndoCommand(this, mark)); } foreach (PointModel *point, m_selectedPointModels) { PolyLineModel *polyLine = point->polyLine(); if (polyLine->size() > 1) { m_undoStack->push(new DeletePointUndoCommand(polyLine, point)); } else { m_undoStack->push(new DeletePolyLineUndoCommand(this, polyLine)); } } } void SituationModel::createTrack() { AddTrackUndoCommand *command = new AddTrackUndoCommand(this); m_undoStack->push(command); TrackModel *track = command->track(); BoatModel *boat = new BoatModel(track, track); boat->setDim(64); boat->setPosition(m_curPosition); track->addBoat(boat); clearSelectedModels(); addSelectedBoat(boat); } void SituationModel::createBoat() { if(!selectedBoatModels().isEmpty()) { TrackModel *track = m_selectedBoatModels[0]->track(); m_undoStack->endMacro(); track->boats().last()->setDim(255); qreal heading = track->boats().last()->heading(); AddBoatUndoCommand *command = new AddBoatUndoCommand(track, m_curPosition, heading); command->boat()->setDim(64); m_undoStack->beginMacro(""); m_undoStack->push(command); clearSelectedModels(); addSelectedBoat(command->boat()); } } void SituationModel::deleteTrack() { // TODO trick to delete first selected track if (!selectedBoatModels().isEmpty()) { BoatModel *boat = selectedBoatModels()[0]; TrackModel * track = boat->track(); m_undoStack->push(new DeleteTrackUndoCommand(this, track)); } } void SituationModel::createMark() { m_undoStack->endMacro(); AddMarkUndoCommand *command = new AddMarkUndoCommand(this, m_curPosition); m_undoStack->beginMacro(""); m_undoStack->push(command); clearSelectedModels(); addSelectedMark(command->mark()); } void SituationModel::createLine() { AddPolyLineUndoCommand *command = new AddPolyLineUndoCommand(this); m_undoStack->push(command); PointModel *point = new PointModel(command->polyLine()); point->setPosition(m_curPosition); command->polyLine()->addPoint(point); clearSelectedModels(); addSelectedPoint(point); } void SituationModel::createPoint() { if(!selectedPointModels().isEmpty()) { PolyLineModel *poly = m_selectedPointModels[0]->polyLine(); m_undoStack->endMacro(); AddPointUndoCommand *command = new AddPointUndoCommand(poly, m_curPosition); m_undoStack->beginMacro(""); m_undoStack->push(command); clearSelectedModels(); addSelectedPoint(command->point()); } } void SituationModel::setCurPosition(QPointF pos) { m_curPosition = pos; } void SituationModel::exitCreateState() { m_undoStack->endMacro(); m_undoStack->undo(); clearSelectedModels(); } void SituationModel::setColor(QColor color) { if (!m_selectedBoatModels.isEmpty()) { TrackModel *track = m_selectedBoatModels.first()->track(); m_undoStack->push(new SetColorUndoCommand(track, color)); } } void SituationModel::setShowPath() { if (!m_selectedBoatModels.isEmpty()) { TrackModel *track = m_selectedBoatModels.first()->track(); m_undoStack->push(new SetShowPathUndoCommand(track)); } } void SituationModel::setSeries(Boats::Series series) { if (!m_selectedBoatModels.isEmpty()) { TrackModel *track = m_selectedBoatModels.first()->track(); m_undoStack->push(new SetSeriesUndoCommand(track, series)); } } void SituationModel::setFollowTrack() { if (!m_selectedBoatModels.isEmpty()) { TrackModel *track = m_selectedBoatModels.first()->track(); m_undoStack->push(new SetFollowTrackUndoCommand(this, track)); } } void SituationModel::addWind(qreal wind) { m_undoStack->push(new AddWindUndoCommand(&m_wind, wind)); } void SituationModel::setWind(int index, qreal wind) { m_undoStack->push(new SetWindUndoCommand(&m_wind, index, wind)); } void SituationModel::deleteWind(int index) { m_undoStack->push(new DeleteWindUndoCommand(&m_wind, index)); } void SituationModel::trimSail() { if (! m_selectedBoatModels.isEmpty()) { qreal trim = m_selectedBoatModels[0]->trim(); qreal heading = fmod(m_selectedBoatModels[0]->heading() - m_selectedBoatModels[0]->wind() + 360, 360); if(heading < 0) heading +=360; if (heading < 180) { trim -= 5; } else { trim += 5; } m_undoStack->push(new TrimBoatUndoCommand(m_selectedBoatModels, trim)); } } void SituationModel::autotrimSail() { if (! m_selectedBoatModels.isEmpty()) { m_undoStack->push(new TrimBoatUndoCommand(m_selectedBoatModels, 0)); } } void SituationModel::untrimSail() { if (! m_selectedBoatModels.isEmpty()) { qreal trim = m_selectedBoatModels[0]->trim(); qreal heading = fmod(m_selectedBoatModels[0]->heading() - m_selectedBoatModels[0]->wind() + 360, 360); if(heading < 0) heading +=360; if (heading < 180) { trim += 5; } else { trim -= 5; } m_undoStack->push(new TrimBoatUndoCommand(m_selectedBoatModels, trim)); } } void SituationModel::trimJib() { if (! m_selectedBoatModels.isEmpty()) { qreal trim = m_selectedBoatModels[0]->jibTrim(); qreal heading = fmod(m_selectedBoatModels[0]->heading() - m_selectedBoatModels[0]->wind() + 360, 360); if(heading < 0) heading +=360; if (heading < 180) { trim -= 5; } else { trim += 5; } m_undoStack->push(new TrimJibUndoCommand(m_selectedBoatModels, trim)); } } void SituationModel::autotrimJib() { if (! m_selectedBoatModels.isEmpty()) { m_undoStack->push(new TrimJibUndoCommand(m_selectedBoatModels, 0)); } } void SituationModel::untrimJib() { if (! m_selectedBoatModels.isEmpty()) { qreal trim = m_selectedBoatModels[0]->jibTrim(); qreal heading = fmod(m_selectedBoatModels[0]->heading() - m_selectedBoatModels[0]->wind() + 360, 360); if(heading < 0) heading +=360; if (heading < 180) { trim += 5; } else { trim -= 5; } m_undoStack->push(new TrimJibUndoCommand(m_selectedBoatModels, trim)); } } void SituationModel::trimSpin() { if (! m_selectedBoatModels.isEmpty()) { qreal trim = m_selectedBoatModels[0]->spinTrim(); qreal heading = fmod(m_selectedBoatModels[0]->heading() - m_selectedBoatModels[0]->wind() + 360, 360); if(heading < 0) heading +=360; if (heading < 180) { trim -= 5; } else { trim += 5; } m_undoStack->push(new TrimSpinUndoCommand(m_selectedBoatModels, trim)); } } void SituationModel::autotrimSpin() { if (! m_selectedBoatModels.isEmpty()) { m_undoStack->push(new TrimSpinUndoCommand(m_selectedBoatModels, 0)); } } void SituationModel::untrimSpin() { if (! m_selectedBoatModels.isEmpty()) { qreal trim = m_selectedBoatModels[0]->spinTrim(); qreal heading = fmod(m_selectedBoatModels[0]->heading() - m_selectedBoatModels[0]->wind() + 360, 360); if(heading < 0) heading +=360; if (heading < 180) { trim += 5; } else { trim -= 5; } m_undoStack->push(new TrimSpinUndoCommand(m_selectedBoatModels, trim)); } } void SituationModel::togglePortOverlap() { if (! m_selectedBoatModels.isEmpty()) { m_undoStack->push(new OverlapBoatUndoCommand(this, m_selectedBoatModels, Boats::port)); } } void SituationModel::toggleStarboardOverlap() { if (! m_selectedBoatModels.isEmpty()) { m_undoStack->push(new OverlapBoatUndoCommand(this, m_selectedBoatModels, Boats::starboard)); } } void SituationModel::toggleHidden() { if (! m_selectedBoatModels.isEmpty()) { m_undoStack->push(new HiddenBoatUndoCommand(this, m_selectedBoatModels, !m_selectedBoatModels.first()->hidden())); } } void SituationModel::toggleText() { if (! m_selectedModels.isEmpty()) { QString text; if (m_selectedModels.first()->text().isEmpty()) { text = tr("Protest!"); } m_undoStack->push(new SetTextUndoCommand(m_selectedModels.first(), text)); } } void SituationModel::setText(QString text) { if (! m_selectedModels.isEmpty()) { m_undoStack->push(new SetTextUndoCommand(m_selectedModels.first(), text)); } } void SituationModel::moveText(QPointF pos) { if (! m_selectedModels.isEmpty()) { PositionModel *model = m_selectedModels.first(); m_undoStack->push(new MoveTextUndoCommand(model, pos - model->textPosition())); } } void SituationModel::toggleFlag(Boats::Flag flag) { if (! m_selectedBoatModels.isEmpty()) { m_undoStack->push(new FlagBoatUndoCommand(this, m_selectedBoatModels, flag)); } } void SituationModel::toggleAcceleration(Boats::Acceleration acceleration) { if (! m_selectedBoatModels.isEmpty()) { m_undoStack->push(new AccelerateBoatUndoCommand(this, m_selectedBoatModels, acceleration)); } } void SituationModel::toggleSpin() { if (! m_selectedBoatModels.isEmpty()) { m_undoStack->push(new SpinBoatUndoCommand(this, m_selectedBoatModels, !m_selectedBoatModels.first()->spin())); } } void SituationModel::toggleMarkSide() { if (! m_selectedMarkModels.isEmpty()) { m_undoStack->push(new ToggleMarkSideUndoCommand(m_selectedMarkModels)); } else { m_undoStack->push(new ToggleMarkSideUndoCommand(m_marks)); } } void SituationModel::toggleMarkArrow() { if (! m_selectedMarkModels.isEmpty()) { m_undoStack->push(new ToggleMarkArrowUndoCommand(m_selectedMarkModels)); } else { m_undoStack->push(new ToggleMarkArrowUndoCommand(m_marks)); } } void SituationModel::toggleMarkZone() { if (! m_selectedMarkModels.isEmpty()) { m_undoStack->push(new ZoneMarkUndoCommand(this, m_selectedMarkModels)); } else { m_undoStack->push(new ZoneMarkUndoCommand(this, m_marks)); } } void SituationModel::setMarkColor(QColor color) { if (! m_selectedMarkModels.isEmpty()) { m_undoStack->push(new ColorMarkUndoCommand(this, m_selectedMarkModels, color)); } } void SituationModel::toggleMarkLabel() { if (! m_selectedMarkModels.isEmpty()) { m_undoStack->push(new ToggleMarkLabelUndoCommand(m_selectedMarkModels)); } else { m_undoStack->push(new ToggleMarkLabelUndoCommand(m_marks)); } } void SituationModel::editMarkLabel(QString text) { if (! m_selectedMarkModels.isEmpty()) { m_undoStack->push(new SetMarkLabelUndoCommand(m_selectedMarkModels.first(), text)); } } void SituationModel::toggleLaylines() { if (! m_selectedModels.isEmpty()) { m_undoStack->push(new SetLaylinesUndoCommand(m_selectedModels, !m_selectedModels.first()->laylines())); } } void SituationModel::setLookAt(int direction, int tilt) { m_undoStack->push(new SetLookAtUndoCommand(this, direction, tilt)); } void SituationModel::clearSelectedModels() { m_selectedModels.clear(); m_selectedBoatModels.clear(); m_selectedMarkModels.clear(); m_selectedPointModels.clear(); m_stateMachine->clearSelection(); } void SituationModel::addSelectedBoat(BoatModel *boat) { m_selectedBoatModels << boat; m_selectedModels << boat; m_stateMachine->selectBoat(); } void SituationModel::addSelectedMark(MarkModel *mark) { m_selectedMarkModels << mark; m_selectedModels << mark; m_stateMachine->selectMark(); } void SituationModel::addSelectedPoint(PointModel *point) { m_selectedPointModels << point; m_selectedModels << point; m_stateMachine->selectPoint(); } void SituationModel::addSelectedModel(PositionModel *position) { m_selectedModels << position; } void SituationModel::removeSelectedModel(PositionModel *position) { m_selectedModels.removeOne(position); m_selectedBoatModels.removeOne(qobject_cast(position)); m_selectedMarkModels.removeOne(qobject_cast(position)); m_selectedPointModels.removeOne(qobject_cast(position)); } #ifdef QML QQmlListProperty SituationModel::trackList() { return QQmlListProperty(this, m_tracks); } QQmlListProperty SituationModel::markList() { return QQmlListProperty(this, m_marks); } #endif boats-201908/model/situationmodel.h000066400000000000000000000323401353032755300172500ustar00rootroot00000000000000// // C++ Interface: situationmodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2014 Thibaut GRIDEL // // 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 . // // #ifndef SITUATIONMODEL_H #define SITUATIONMODEL_H #include "boats.h" #include "windmodel.h" #include #include #ifdef QML #include #endif class TrackModel; class BoatModel; class MarkModel; class PolyLineModel; class PointModel; class StateMachine; class ScenarioAnimation; /** \class SituationModel \brief The main Model for a scenario This Class represents the Model for a Scenario, according to an Observer Pattern. It holds the actual data for the scenario and signals whoever is interested in changes to the model. There are mainly: - description attributes and technical data which define the scenario - a List of TrackModel, which in turn contains a List of BoatModel - a List of MarkModel \sa SituationModel, TrackModel, BoatModel, MarkModel */ class SituationModel : public QObject { Q_OBJECT Q_ENUMS(Boats::Series) public: Q_PROPERTY(QString title READ title WRITE changeTitle NOTIFY titleChanged) Q_PROPERTY(QString rules READ rules WRITE changeRules NOTIFY rulesChanged) Q_PROPERTY(bool grid READ showLayline WRITE toggleShowLayline NOTIFY showLaylineChanged) Q_PROPERTY(int laylineAngle READ laylineAngle WRITE changeLaylineAngle NOTIFY laylineChanged) Q_PROPERTY(Boats::Series situationSeries READ situationSeries WRITE changeSeries NOTIFY seriesChanged) Q_PROPERTY(int situationLength READ situationLength WRITE changeLength NOTIFY lengthChanged) Q_PROPERTY(QString abstract READ abstract WRITE changeAbstract NOTIFY abstractChanged) Q_PROPERTY(QString description READ description WRITE changeDescription NOTIFY descriptionChanged) Q_PROPERTY(qreal lookDirection READ lookDirection WRITE setLookDirection NOTIFY lookDirectionChanged) Q_PROPERTY(qreal tilt READ tilt WRITE setTilt NOTIFY tiltChanged) #ifdef QML Q_PROPERTY(QQmlListProperty trackList READ trackList NOTIFY tracksChanged) Q_PROPERTY(QQmlListProperty markList READ markList NOTIFY marksChanged) #endif Q_PROPERTY(int size READ size NOTIFY tracksChanged) Q_PROPERTY(StateMachine* stateMachine READ stateMachine CONSTANT) Q_PROPERTY(ScenarioAnimation *animation READ animation CONSTANT) Q_PROPERTY(bool canUndo READ canUndo NOTIFY canUndoChanged) Q_PROPERTY(bool canRedo READ canRedo NOTIFY canRedoChanged) SituationModel(QObject *parent = 0); ~SituationModel(); // Setters and Getters for Model Data QString title() const { return m_title; } void setTitle(const QString theValue); QString rules() const { return m_rules; } void setRules(const QString theValue); bool showLayline() const { return m_showLayline; } void setShowLayline(const bool theValue); int laylineAngle() const { return m_laylineAngle; } void setLaylineAngle(const int theValue); Boats::Series situationSeries() const { return m_situationSeries; } void setSituationSeries(const int theValue); int situationLength() const { return m_situationLength; } void setSituationLength(const int theValue); QString abstract() const { return m_abstract; } void setAbstract(const QString theValue); QString description() const { return m_description; } void setDescription(const QString theValue); qreal lookDirection() const { return m_lookDirection; } void setLookDirection(qreal theValue); qreal tilt() const { return m_tilt; } void setTilt(qreal theValue); int size() const { return m_tracks.size();} const QList tracks() const { return m_tracks; } #ifdef QML QQmlListProperty trackList(); QQmlListProperty markList(); #endif int markSize() const { return m_marks.size();} const QList marks() const { return m_marks; } const QList polyLines() const { return m_lines; } WindModel& wind() { return m_wind; } // Setters and Getters for Non model Data QUndoStack * undoStack() const { return m_undoStack;} StateMachine *stateMachine(){ return m_stateMachine; } ScenarioAnimation *animation() const { return m_scenarioAnimation; } QList< PositionModel * > selectedModels() { return m_selectedModels; } QList< BoatModel * > selectedBoatModels() { return m_selectedBoatModels; } QList< MarkModel * > selectedMarkModels() { return m_selectedMarkModels; } QList< PointModel * > selectedPointModels() { return m_selectedPointModels; } QStringList discardedXml() const { return m_discardedXml; } void appendDiscardedXml(const QString& theValue); QString fileName() const { return m_fileName; } void setFileName(const QString theValue) {m_fileName = theValue; } // Tracks void addTrack(TrackModel *track, int order = -1); void deleteTrack(TrackModel *track); // Marks void addMark(MarkModel *mark, int order = -1); int deleteMark(MarkModel *mark); // Lines void addPolyLine(PolyLineModel *polyline, int order = -1); void deletePolyLine(PolyLineModel *polyline); Q_INVOKABLE void setCurPosition(QPointF pos); QPointF curPosition() { return m_curPosition; } // Helper to remotely trigger boat signals from elsewhere void addingBoat(BoatModel *boat) {emit boatAdded(boat);} void removingBoat(BoatModel *boat) {emit boatRemoved(boat);} // Helper to remotely trigger point signals from elsewhere void addingPoint(PointModel *point) {emit pointAdded(point);} void removingPoint(PointModel *point) {emit pointRemoved(point);} // UndoCommand actions Q_INVOKABLE void undo() { m_undoStack->undo(); } bool canUndo() { return m_undoStack->canUndo(); } Q_INVOKABLE void redo() { m_undoStack->redo(); } bool canRedo() { return m_undoStack->canRedo(); } // Scenario undo actions void changeTitle(QString title); void changeRules(QString rules); void toggleShowLayline(bool showlayline); void changeLaylineAngle(int angle); void changeSeries(Boats::Series series); void changeLength(int length); void changeAbstract(QString abstract); void changeDescription(QString description); void setLookAt(int direction, int tilt); Q_INVOKABLE void rotateModel(qreal angle); void deleteModels(); void deleteTrack(); // Track undo actions Q_INVOKABLE void setColor(QColor color); Q_INVOKABLE void setShowPath(); Q_INVOKABLE void setSeries(Boats::Series series); Q_INVOKABLE void setFollowTrack(); // Wind undo actions Q_INVOKABLE void toggleWind(); Q_INVOKABLE void addWind(qreal wind); Q_INVOKABLE void setWind(int index, qreal wind); Q_INVOKABLE void deleteWind(int index); // Boat undo actions void trimSail(); void autotrimSail(); void untrimSail(); void trimJib(); void autotrimJib(); void untrimJib(); void trimSpin(); void autotrimSpin(); void untrimSpin(); void togglePortOverlap(); void toggleStarboardOverlap(); void toggleFlag(Boats::Flag flag); void toggleAcceleration(Boats::Acceleration acceleration); void toggleHidden(); void toggleText(); Q_INVOKABLE void setText(QString text); Q_INVOKABLE void moveText(QPointF pos); void toggleSpin(); // Mark undo actions void toggleMarkSide(); void toggleMarkArrow(); void toggleMarkZone(); void setMarkColor(QColor color); void toggleMarkLabel(); void editMarkLabel(QString text); void toggleLaylines(); // selection mechanism Q_INVOKABLE void clearSelectedModels(); Q_INVOKABLE void addSelectedBoat(BoatModel *boat); Q_INVOKABLE void addSelectedMark(MarkModel *mark); Q_INVOKABLE void addSelectedPoint(PointModel *point); Q_INVOKABLE void addSelectedModel(PositionModel *position); Q_INVOKABLE void removeSelectedModel(PositionModel *position); public slots: // Wind void resetWind(); // Slots for state signals void createTrack(); void createBoat(); void createMark(); void createLine(); void createPoint(); void moveModel(); void rotateModel(); void exitCreateState(); signals: // Signals for Track void trackAdded(TrackModel *track); void trackRemoved(TrackModel *track); void tracksChanged(); // Signals for Boat void boatAdded(BoatModel *boat); void boatRemoved(BoatModel *boat); // Signals for Scenario Parameters void titleChanged(const QString title); void rulesChanged(const QString rules); void showLaylineChanged(const bool show); void laylineChanged(const int angle); void seriesChanged(const int series); void lengthChanged(const int length); void abstractChanged(const QString abstract); void descriptionChanged(const QString description); void lookDirectionChanged( const qreal lookDirection); void tiltChanged( const qreal tilt); // Signals for Marks void markAdded(MarkModel *mark); void markRemoved(MarkModel *mark); void marksChanged(); // Signals for Lines void polyLineAdded(PolyLineModel *polyline); void polyLineRemoved(PolyLineModel *polyline); // Signals for Line Points void pointAdded(PointModel *point); void pointRemoved(PointModel *point); void canUndoChanged(bool canUndo); void canRedoChanged(bool canRedo); private: // Model Data /// \a m_title holds the Title of the Scenario QString m_title; /// \a m_rules holds the Rules of the Scenario QString m_rules; /// \a m_abstract holds the Abstract of the Scenario QString m_abstract; /// \a m_description holds the Description of the Scenario QString m_description; /// \a m_showLayline holds whether the Laylines will be displayed bool m_showLayline; /// \a m_laylineAngle holds the Layline Angle of the Scenario int m_laylineAngle; /// \a m_situationSeries holds the Main Series of the Scenario Boats::Series m_situationSeries; /// \a m_situationLength holds the size of the Zone at Marks of /// the Scenario int m_situationLength; /// \a m_lookDirection holds the direction of the view qreal m_lookDirection; /// \a m_tilt holds the tilt of the view qreal m_tilt; /// \a m_wind holds the WindModel of the Scenario WindModel m_wind; /// \a m_tracks holds the List of Tracks of the Scenario QList m_tracks; /// \a m_marks holds the List of Marks of the Scenario QList m_marks; /// \a m_lines holds the List of PolyLines of the Scenario QList m_lines; // Non model Data /// \a m_undoStack maintains the Undo Stack for the Scenario QUndoStack *m_undoStack; /// \a m_state holds the SceneState for the current scenario StateMachine *m_stateMachine; /// \a m_scenarioAnimation holds the general AnimationGroup /// manipulated during animation mode ScenarioAnimation* m_scenarioAnimation; /// \a m_curPosition holds the QPointF where mouse was last seen QPointF m_curPosition; // Bookkeeping references to selected models /// \a m_selectedModels holds the list of selected PositionModel QList m_selectedModels; /// \a m_selectedBoatsModels holds the list of selected BoatModel QList m_selectedBoatModels; /// \a m_selectedMarkModels holds the list of selected MarkModel QList m_selectedMarkModels; /// \a m_selectedPointModels holds the list of selected PointModel QList m_selectedPointModels; /// \a m_discardedXml keeps all unparsed xml tags QStringList m_discardedXml; /// \a m_fileName holds the name of the file on disk QString m_fileName; }; #endif boats-201908/model/trackmodel.cpp000066400000000000000000000154511353032755300166740ustar00rootroot00000000000000// // C++ Implementation: trackmodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #include #include #include "trackmodel.h" #include "commontypes.h" #include "situationmodel.h" #include "boatmodel.h" extern int debugLevel; TrackModel::TrackModel(SituationModel *situation, QObject *parent) : QObject(parent), m_color(), m_series(Boats::unknown), m_showPath(true), m_followTrack(false), m_situation(situation), m_length(0) { m_order = situation->size(); if (debugLevel & 1 << MODEL) std::cout << "new track " << this << std::endl; switch (situation->size() % 6) { case 0: m_color = QColor(Qt::yellow); break; case 1: m_color = QColor(Qt::blue); break; case 2: m_color = QColor(Qt::green); break; case 3: m_color = QColor(Qt::red); break; case 4: m_color = QColor(Qt::cyan); break; case 5: m_color = QColor(Qt::magenta); break; } emit colorChanged(m_color); setSeries(situation->situationSeries()); } TrackModel::~TrackModel() { if (debugLevel & 1 << MODEL) std::cout << "end track " << this << std::endl; } void TrackModel::setOrder(const int theValue) { if (theValue != m_order) { m_order = theValue; emit orderChanged(m_order); } } void TrackModel::setColor(const QColor& theValue) { if (theValue != m_color) { m_color = theValue; emit colorChanged(m_color); } } void TrackModel::setShowPath(const bool theValue) { if (theValue != m_showPath) { m_showPath = theValue; emit showPathChanged(m_showPath); } } void TrackModel::setFollowTrack(bool theValue) { if (theValue != m_followTrack) { m_followTrack = theValue; emit followTrackChanged(m_followTrack); } if (m_followTrack && m_situation) { foreach (TrackModel *model, m_situation->tracks()) { if (model != this) { model->setFollowTrack(false); } } } } void TrackModel::setSeries(const Boats::Series theValue) { if (theValue != m_series) { m_series = theValue; m_length = Boats::seriesSizeList()[m_series]; emit seriesChanged(m_series); } } void TrackModel::appendDiscardedXml(const QString& theValue) { if (!m_discardedXml.contains(theValue)) { m_discardedXml.append(theValue); } } BoatModel * TrackModel::addBoat(BoatModel *boat, int order) { if (order == -1) { order = m_boats.size(); } m_boats.insert(order, boat); if (debugLevel & 1 << MODEL) std::cout << "Adding Boat " << order+1 << std::endl; for (int i=order+1; isetOrder(i+1); } m_situation->addingBoat(boat); changingTrack(this); emit boatsChanged(); return boat; } int TrackModel::deleteBoat(BoatModel *boat) { int order = m_boats.indexOf(boat); m_boats.removeOne(boat); if (debugLevel & 1 << MODEL) std::cout << "Removing Boat " << order+1 << std::endl; for (int i=order; isetOrder(i+1); } m_situation->removingBoat(boat); changingTrack(this); emit boatsChanged(); return order; } void TrackModel::displayBoats() { if (debugLevel & 1 << MODEL) std::cout << "Displaying boats" << std::endl; foreach (BoatModel* boat, m_boats) { m_situation->addingBoat(boat); } } void TrackModel::hideBoats() { if (debugLevel & 1 << MODEL) std::cout << "Hiding boats" << std::endl; foreach (BoatModel* boat, m_boats) { m_situation->removingBoat(boat); } } // calculate new heading: // from position of head of index boat to new potential position qreal TrackModel::headingForNext(int index, QPointF point) { const BoatModel* boat = m_boats.at(index); qreal length = m_length / 2.0; qreal theta0 = boat->heading() * M_PI /180; QPointF point2 = point - (boat->position() + QPointF(length*sin(theta0),-length*cos(theta0))); return fmod(atan2 (point2.x(), -point2.y()) * 180 / M_PI + 360.0, 360.0); } void TrackModel::changingTrack(TrackModel *track) { QPainterPath path; QPainterPath wholePath; if (m_boats.size() < 1) { m_path = path; return; } QPointF pos0(m_boats[0]->position()); qreal heading0 = m_boats[0]->heading(); wholePath.moveTo(pos0); BoatModel *oldBoat = m_boats[0]; int i = 1; while (i < m_boats.size() && m_boats[i]->dim() ) { path.moveTo(pos0); BoatModel *boat = m_boats[i]; QPointF pos1(boat->position()); qreal heading1 = boat->heading(); // distance and angle between positions QPointF delta = pos1-pos0; qreal dist = sqrt(pow(delta.x(),2) + pow(delta.y(),2)); qreal theta = fmod(atan2 (delta.x(), -delta.y()) *180/M_PI +360, 360); // empirical factor for control point distance qreal factor = dist*2/5; QPointF c1(pos0); // stalled condition when next boat in the back qreal angle0 = fmod(theta - heading0 +360, 360); bool stalled0 = ((angle0 >= 90) && (angle0 <= 270)); if (!stalled0) { c1 += QPointF(factor*sin(heading0 * M_PI /180), -factor*cos(heading0 * M_PI /180)); } QPointF c2(pos1); // stalled condition when previous boat in the back qreal angle1 = fmod(theta- heading1 +360, 360); bool stalled1 = ((angle1 >= 90) && (angle1 <= 270)) ; if (!stalled1) { c2 -= QPointF(factor*sin(heading1 * M_PI /180), -factor*cos(heading1 * M_PI /180)); } path.cubicTo(c1, c2, pos1); oldBoat->setPath(path); oldBoat = boat; wholePath.cubicTo(c1, c2, pos1); pos0 = pos1; heading0 = heading1; path = QPainterPath(); i++; } m_path = wholePath; emit trackChanged(track); } void TrackModel::setSelected(bool selected) { foreach(BoatModel *boat, m_boats) { m_situation->addSelectedBoat(boat); } emit trackSelected(selected); } #ifdef QML QQmlListProperty TrackModel::boatList() { return QQmlListProperty(this, m_boats); } #endif boats-201908/model/trackmodel.h000066400000000000000000000120621353032755300163340ustar00rootroot00000000000000// // C++ Interface: trackmodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2010 Thibaut GRIDEL // // 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 . // // #ifndef TRACKMODEL_H #define TRACKMODEL_H #include #ifdef QML #include #endif #include "boats.h" class SituationModel; class BoatModel; /** \class TrackModel \brief The Model for the Track of a Boat The class represents the Model for a Track, according to an Observer Pattern. TrackModel contains data which describe one boat, like the series of the boat, the color it is drawn and the List of Positions where the boat navigates. It shall not be mistaken with BoatModel, which holds one position at a given time. More exactly, a TrackModel will hold a List of BoatModels. \sa SituationModel, BoatModel */ class TrackModel : public QObject { Q_OBJECT Q_ENUMS(Boats::Series) public: Q_PROPERTY(int order READ order WRITE setOrder NOTIFY orderChanged) Q_PROPERTY(QColor trackColor READ color WRITE setColor NOTIFY colorChanged) Q_PROPERTY(Boats::Series series READ series WRITE setSeries NOTIFY seriesChanged) Q_PROPERTY(bool showPath READ showPath WRITE setShowPath NOTIFY showPathChanged) Q_PROPERTY(bool followTrack READ followTrack WRITE setFollowTrack NOTIFY followTrackChanged) #ifdef QML Q_PROPERTY(QQmlListProperty boatList READ boatList NOTIFY boatsChanged) #endif Q_PROPERTY(int size READ size NOTIFY boatsChanged) TrackModel(SituationModel* situation = 0, QObject *parent = 0); ~TrackModel(); BoatModel * addBoat(BoatModel *boat, int order = -1); int deleteBoat(BoatModel *boat); void displayBoats(); void hideBoats(); // Setters and Getters for Model Data int order() const { return m_order; } void setOrder(const int theValue); QColor color() const { return m_color;} void setColor(const QColor& theValue); Boats::Series series() const { return m_series;} void setSeries(const Boats::Series theValue); bool showPath() const { return m_showPath;} void setShowPath(const bool theValue); bool followTrack() const { return m_followTrack; } void setFollowTrack(bool theValue); int size() const { return m_boats.size();} const QList boats() const { return m_boats; } #ifdef QML QQmlListProperty boatList(); #endif const QPainterPath path() const { return m_path; } // Setters and Getters for Non model Data SituationModel* situation() const { return m_situation; } int length() const { return m_length; } QStringList discardedXml() const { return m_discardedXml; } void appendDiscardedXml(const QString& theValue); Q_INVOKABLE qreal headingForNext(int index, QPointF point); void changingTrack(TrackModel *track); Q_INVOKABLE void setSelected(bool selected); signals: // Signals for TrackModel parameters void orderChanged(int order); void colorChanged(QColor color); void seriesChanged(Boats::Series series); void showPathChanged(bool showPath); void followTrackChanged(bool followTrack); void trackChanged(TrackModel *track); void boatsChanged(); void trackSelected(bool selected); private: // Model Data /// \a m_order holds the stacking order of the Track int m_order; /// \a m_color holds the color of the Track QColor m_color; /// \a m_series holds the series of the Track Boats::Series m_series; /// \a m_boats holds the List of Boat Positions of the Track QList m_boats; /// \a m_showPath holds whether the track path will be displayed bool m_showPath; /// \a m_followTrack holds whether this track will be followed /// during the animation bool m_followTrack; // Non model Data /// \a m_situation keeps a pointer to the SituationModel to which /// it belongs SituationModel *m_situation; /// \a m_length holds the size of the Boat in World Coordinates int m_length; /// \a m_path holds the QPainterPath of the Track QPainterPath m_path; /// \a m_discardedXml keeps all unparsed xml tags QStringList m_discardedXml; }; #endif boats-201908/model/windmodel.cpp000066400000000000000000000051771353032755300165350ustar00rootroot00000000000000// // C++ Implementation: windmodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2011 Thibaut GRIDEL // // 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 . // // #include #include "windmodel.h" #include "commontypes.h" extern int debugLevel; WindModel::WindModel(SituationModel *situation, QObject *parent) : PositionModel(situation, parent), m_visible(false) { if (debugLevel & 1 << MODEL) std::cout << "new Wind " << this << std::endl; } WindModel::~WindModel() { if (debugLevel & 1 << MODEL) std::cout << "delete Wind " << this << std::endl; } void WindModel::setVisible(bool visible) { if (m_visible != visible) { m_visible = visible; emit windVisibleChanged(visible); } } void WindModel::addWind(const qreal direction, int order) { if (order == -1) { order = m_directions.size(); } if (debugLevel & 1 << MODEL) std::cout << "Adding Wind " << order+1 << std::endl; m_directions.insert(order, direction); emit windReset(); } void WindModel::deleteWind(int order) { if (debugLevel & 1 << MODEL) std::cout << "Removing Wind " << order+1 << std::endl; m_directions.removeAt(order); emit windReset(); } void WindModel::clearWind() { setVisible(false); setPosition(QPointF()); m_discardedXml.clear(); m_directions.clear(); emit windReset(); } qreal WindModel::windAt(int order) const { if (order == -1 || order >= m_directions.size()) { order = m_directions.size()-1; } else if (order < -1) { order = 0; } if (m_directions.isEmpty()) { return 0; } return m_directions.at(order); } void WindModel::setWindAt(qreal direction, int order) { if (order < 0 || order > m_directions.size()) { return; } m_directions[order] = direction; if (debugLevel & 1 << MODEL) std::cout << "wind for " << order+1 << " " << direction; emit windReset(); } void WindModel::setDirection(qreal direction) { m_direction = direction; emit directionChanged(m_direction); } boats-201908/model/windmodel.h000066400000000000000000000044431353032755300161750ustar00rootroot00000000000000// // C++ Interface: windmodel // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2011 Thibaut GRIDEL // // 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 . // // #ifndef WINDMODEL_H #define WINDMODEL_H #include "positionmodel.h" /** \class PointModel \brief The Model for Wind directions The class represents the Model for Wind, according to an Observer Pattern. WindModel inherits PositionModel and contains data which represents the directions of the wind of the scenario \sa SituationModel */ class WindModel : public PositionModel { Q_OBJECT Q_PROPERTY(qreal direction READ direction WRITE setDirection) public: WindModel(SituationModel* situation, QObject *parent = 0); ~WindModel(); // Setters and Getters for Model Data bool visible() const { return m_visible; }; void setVisible(bool visible); void addWind(const qreal direction, int order = -1); void deleteWind(int order = -1); void clearWind(); int size() const { return m_directions.size(); }; qreal windAt(int order = -1) const; void setWindAt(qreal direction, int order); // Setters and Getters for Non model Data qreal direction() const { return m_direction; } void setDirection(qreal direction); signals: void windVisibleChanged(bool visible); void windReset(); void directionChanged(qreal); private: // Model Data bool m_visible; QList m_directions; // Non model Data /// m_direction holds the current wind, signalling animation qreal m_direction; }; #endif // WINDMODEL_H boats-201908/resources/000077500000000000000000000000001353032755300147475ustar00rootroot00000000000000boats-201908/resources/Info.plist000066400000000000000000000017141353032755300167220ustar00rootroot00000000000000 CFBundleDocumentTypes CFBundleTypeExtensions xbs CFBundleTypeIconFile xbs.icns CFBundleTypeName Boat Scenario document CFBundleTypeRole Editor CFBundleExecutable boats CFBundleGetInfoString Created by Thibaut GRIDEL CFBundleIconFile boats.icns CFBundleName Boat Scenario CFBundlePackageType APPL CFBundleShortVersionString 201908.0.0 CFBundleSignature boat boats-201908/resources/boats.desktop000066400000000000000000000003241353032755300174510ustar00rootroot00000000000000[Desktop Entry] Type=Application Name=Boat Scenario Comment=Race scenario drawing tool Exec=boats %F Icon=boats.png Categories=Education;Qt; MimeType=application/x-boats Keywords=drawing;scenario;sailing;regatta boats-201908/resources/boats.icns000066400000000000000000001070241353032755300167410ustar00rootroot00000000000000icnsŽis32<ÿÌãŠÿcÀ‹ÿ˜í‰ÿýµ•ð‡ÿêªÿ™ô…ÿºªÿžõ‚ÿߙǃÿ˜ôÿÿû³Æ†ÿ•óØœ€ÿƒÿ’â€ÿ‡ÿ …ÿ „ÿÿ  #ÿ *!)+'# €ÿ &33/+'#ÿ ;73/+'#ÿÿÉà†üýU½‡üý–ê‡üýýû´‘í†üýèªÿ—ñ…ü¹ªÿœò‚üݖǃÿ—ñüüø°Ã†ÿ‘ðÕ›€ÿƒÿÞ€ÿ‡ÿ …ÿ „ÿÿ  #ÿ *!)+'# €ÿ &33/+'#ÿ ;73/+'#ÿMÿ $+3:AIPW^fm$+3:AIPW^fm$+3:AIPW^eM $+3:AIPWTDÿ$+3:AIP>;ÿ $+3:A>.7ƒÿ"+39+9†ÿ)) €ÿƒÿ%€ÿZ¾¹w‡ÿ »ÏÿÚÃ…ÿºÖ€ÿ§„ÿÕ¸ÜÿÞ¹ÿ¿²ãƒÿ©ÿײé„ÿã²€ÿ±è†ÿ«‹ÿæ¬ÿs8mk`÷ÿÿÿÿÿÿÿÿÿÿÿÿÿ×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿwýÿÿÿÿÿÿÿÿÿÿÿÿÿÊŸþÿÿÿÿÿÿÿÿÿÿú¥ £ÿÿÿÿÿÿÿÿÿÙl¦ÿÿÿÿÿÿõ¦§ÿÿÿÿÍU¦ÿì” (>—ܹ/¢ëÿó@>«ðÿÿÿ²PµöÿÿÿÿöI`¿úÿÿÿÿÿÿ¶`ÈýÿÿÿÿÿÿÿøP¬úÿÿÿÿÿÿÿÿÿºÿÿÿÿÿÿÿÿÿÿúWil32c€ÿ>zšÿ7ûšÿ0Ïšÿ8{šÿ0ú™ÿ¥1À—ÿöa9ÿ/Ä•ÿÖ44€ÿ.È“ÿ¡*‚ÿ-ÊÿÞG>„ÿ-Îÿü‰1‡ÿ-Ñ‹ÿËyü…ýÿÿ6øü…ýÿÿ/Ì‘ü…ýÿ8z’ü…ý/÷’ü„ý¤/¾’ü‚ýô`9ÿ/‘üýÔ44€ÿ.Æü€ý *‚ÿ-ÈüýÜG<„ÿ,Ëüùˆ1‡ÿ+΋üÈ8?Šÿ,Έüñn4Œÿ-͆ü´25ÿ-̃üæV<‘ÿ+Ëü›/”ÿ,Êü×BD‡ÿ€Šÿ,n2…ÿ‹ÿ…ÿ€“ÿ ’ÿ  ÿ   Žÿ Œÿ  ‹ÿ "  ‰ÿ &$"  ˆÿ !*(&$"  †ÿ $.,*(&$"  …ÿ '20.,*(&$" „ÿ $6420.,*(&$" …ÿ886420.,*(&$" …ÿ<:86420.,*(&$" †ÿ<:86420.,*(&$" †ÿ€ÿ"&*-158<@CGJNRUY\`dgkoÿÿ "&*-158<@CGJNRUY\`dgkoÿÿ "&*-158<@CGJNRUY\`dgkoÿ "&*-158<@CGJNRUY\`dgko[ "&*-158<@CGJNRUY\`dgkF "&*-158<@CGJNRUY\`dc'ÿ "&*-158<@CGJNRUY\`S€ÿ"&*-158<@CGJNRUY\;‚ÿ "&*-158<@CGJNRUM„ÿ"&*-158<@CGJNQ,‡ÿ"&*-158<@CGJ<Šÿ"&*-158<@CD Œÿ"&*-158<@. ÿ &*-1587 ‘ÿ *-15! ”ÿ -* ‡ÿ3#Šÿ…ÿ32ƒÌ9‹ÿ…ÿ6?±ýÿm“ÿ4WË€ÿé7’ÿ&3tä‚ÿsÿ=7•õƒÿì7Žÿ:D²þ…ÿw Œÿ6P͇ÿï6‹ÿ7NΉÿ}‰ÿ6LÍŠÿò4ˆÿ6IËŒÿ„3†ÿ7HÉÿô5…ÿ†ÿl8mkMëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿMìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿò¶ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ总úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÜ:ÀûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóÁÄüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàrÈýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿï» ËýÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÝT Íýÿÿÿÿÿÿÿÿÿÿÿÿê¦ ÌýÿÿÿÿÿÿÿÿÿùÖ9 ÌýÿÿÿÿÿÿÿæŒ ËýÿÿÿÿõË$ Êýÿÿão9²Ì Êð»nÕìÿ² ;’Ü÷ÿÿæ$*°áþÿÿÿÿ·IÆèÿÿÿÿÿÿè)lÔðÿÿÿÿÿÿÿÿ½Ûøÿÿÿÿÿÿÿÿÿê/šàþÿÿÿÿÿÿÿÿÿÿÿÁ™àþÿÿÿÿÿÿÿÿÿÿÿÿë5—àýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆ•àýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿí<“àýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊwßýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïC>ÔöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñJÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòRit32@IŒÿ€ã÷ÿ¦øÿ€Yøÿ€î÷ÿ¯øÿ€Yøÿ€ì÷ÿ­øÿ€Wøÿ€ë÷ÿ«øÿ€Uøÿ€ê÷ÿ©øÿ€Søÿ€é÷ÿ§øÿ€Qôÿï€ÿ€èóÿÁ€ÿ€jòÿûz€ÿ€œðÿÜ6€ÿ€¢îÿŸ ‚ÿ€§ëÿïV‚„ÿ€¬éÿÁ‡ÿ€±æÿû{‰ÿ€¶äÿÜ7‚‹ÿ€ºâÿ  Žÿ€ ¿ßÿïW‚ÿ€ ÃÝÿ“ÿ€ ÇÚÿû|•ÿ€ËØÿÚ7‚—ÿ€ÏÕÿýŒ šÿ€ÓÓÿÖ8‚œÿ€×Ðÿû†Ÿÿ€ÚÎÿÒ3‚¡ÿ€ÝËÿú€¤ÿ€àÉÿÍ.‚¥ÿ"ãÆÿøz¨ÿ%æÄÿÈ*‚ªÿ)éÁÿöt­ÿ,ë¿ÿÃ&‚¯ÿ0î¼ÿôn²ÿ4ðºÿ¾"‚´ÿ6ð·ÿòh·ÿ5ïµÿ¹‚¹ÿ4ï²ÿðb‚¼ÿ3î°ÿ³‚¾ÿ2í­ÿí\‚Áÿ1í«ÿ­‚Ãÿ0ì¨ÿêV‚Æÿ/ì¦ÿ§‚Èÿ.ë£ÿçP‚Ëÿ-ê¡ÿ¡‚Íÿ,éžÿãJ‚Ðÿ+éœÿ›Óÿ*è™ÿØA‚Õÿ)ç–ÿø~Øÿ(ç”ÿ»%‚Úÿ'æ‘ÿèZ‚Ýÿ&åŽÿý—‚ßÿ%äŒÿÐ8‚ªÿ‚°ÿ$ä‰ÿôsªÿ„±ÿ#ã‡ÿ°‚¨ÿ‡²ÿ#â„ÿàO‚§ÿ†€³ÿ"áÿít‚¦ÿ…€´ÿ!àÿöˆ‚¦ÿ„€¶ÿ ‘‚¥ÿ„€·ÿ‡¤ÿ„ ¸ÿ„¤ÿƒ €ºÿ£ÿ„ €âÿ„ €áÿƒ €Þÿ„€  €Ýÿ„  €Ûÿƒ  Øÿ„  €×ÿ„  €Õÿƒ €Ôÿƒ  €Òÿƒ € €Ðÿ„" €Îÿ„# €Íÿƒ&  €Ëÿƒ'  €Êÿƒ) €Èÿƒ* €Æÿ„, €Äÿƒ.  €Ãÿƒ0 €Áÿƒ1  €Àÿƒ3 !  €¾ÿƒ4!"!!  ¼ÿƒ6"#""!!  €»ÿƒ8$$##""!!  €¹ÿƒ9%%$$##""!!  €¸ÿƒ;&&%%$$##""!!  €¶ÿƒ< ''&&%%$$##""!!  €µÿƒ> ((''&&%%$$##""!!  €³ÿƒ? ))((''&&%%$$##""!!  €²ÿƒA "**))((''&&%%$$##""!!  €°ÿƒB #++**))((''&&%%$$##""!!  €¯ÿƒD ",,++**))((''&&%%$$##""!!  €­ÿƒE !--,,++**))((''&&%%$$##""!!  €¬ÿƒG..--,,++**))((''&&%%$$##""!!  €ªÿƒH./..--,,++**))((''&&%%$$##""!!  €ªÿ‚J/0//..--,,++**))((''&&%%$$##""!!  €¨ÿ‚K/100//..--,,++**))((''&&%%$$##""!!  €§ÿ‚M/21100//..--,,++**))((''&&%%$$##""!!  €¥ÿ‚N03221100//..--,,++**))((''&&%%$$##""!!  €¤ÿ‚P/443221100//..--,,++**))((''&&%%$$##""!!  €¢ÿƒP.55443221100//..--,,++**))((''&&%%$$##""!!  €¢ÿ‚R '6655443221100//..--,,++**))((''&&%%$$##""!! € ÿ‚!6M55443221100//..--,,++**))((''&&%%$$##""!!  €Ÿÿ‚587€6M55443221100//..--,,++**))((''&&%%$$##""!! €ÿƒ29887€6L55443221100//..--,,++**))((''&&%%$$##""!!  €ÿ‚ ,:99887€6L55443221100//..--,,++**))((''&&%%$$##""!! €ÿ€ %;::99887€6K55443221100//..--,,++**))((''&&%%$$##""!!  €žÿ :;;::99887€6K55443221100//..--,,++**))((''&&%%$$##""!! €žÿ 7<<;;::99887€6J55443221100//..--,,++**))((''&&%%$$##""!!  €Ÿÿ ==<<;;::99887€6J55443221100//..--,,++**))((''&&%%$$##""!! €Ÿÿ ==<<;;::99887€6I55443221100//..--,,++**))((''&&%%$$##""!!  € ÿ ==<<;;::99887€6I55443221100//..--,,++**))((''&&%%$$##""!! € ÿ ==<<;;::99887€6H55443221100//..--,,++**))((''&&%%$$##""!!  €¡ÿ ==<<;;::99887€6H55443221100//..--,,++**))((''&&%%$$##""!! €¡ÿ ==<<;;::99887€6G55443221100//..--,,++**))((''&&%%$$##""!!  €¢ÿ ==<<;;::99887€6G55443221100//..--,,++**))((''&&%%$$##""!! €¢ÿ ==<<;;::99887€6F55443221100//..--,,++**))((''&&%%$$##""!!  €£ÿ ==<<;;::99887€6F55443221100//..--,,++**))((''&&%%$$##""!! €£ÿŒÿ€àÊüœý‹ÿ¤Ëüœý‹ÿ€XÌüœýŠÿ€ëÌüœý‰ÿ­Íüœý‰ÿ€XÎüœýˆÿ€éÎüœý‡ÿ«Ïüœý‡ÿ€VÐüœý†ÿ€èÐüœý…ÿ©Ñüœý…ÿ€TÒüœý„ÿ€çÒüœýƒÿ§Óüœýƒÿ€RÔüœý‚ÿ€æÔüœýÿ¥Õüœýÿ€PÖü›ýí€ÿ€åÖüšý¿€ÿ€i×ü˜ýùy€ÿ€šÖü—ýÚ6€ÿ€ Õü–ýž ‚ÿ€¥Ôü”ýíU‚„ÿ€ªÓü“ý¿‡ÿ€¯Òü‘ýùz‰ÿ€´ÑüýÚ7‚‹ÿ€¸ÐüýŸ Žÿ€ ½ÏüýíV‚ÿ€ ÁÎüŒýÀ“ÿ€ ÅÍüŠýù{•ÿ€ÉÌü‰ýØ7‚—ÿ€ÍËü‡ýû‹ šÿ€ÑÊü†ýÔ8‚œÿ€ÔÉü„ýù…Ÿÿ€×ÈüƒýÐ3‚¡ÿ€ÚÇüýø¤ÿ€ÝÆü€ýË.‚¥ÿ"àÅüýöy¨ÿ%ãÄüÆ*‚ªÿ)æÁüós­ÿ+è¿üÁ&‚¯ÿ/ë¼üñm²ÿ3íºü¼"‚´ÿ5í·üïg·ÿ4ìµü·‚¹ÿ3ì²üía‚¼ÿ2ë°ü±‚¾ÿ1ê­üê[‚Áÿ0ê«ü«‚Ãÿ/é¨üçU‚Æÿ.é¦ü¥‚Èÿ-è£üäO‚Ëÿ,ç¡üŸ‚Íÿ+æžüàI‚Ðÿ*æœü™Óÿ*å™üÕ@‚Õÿ)ä–üõ}Øÿ(ä”ü¹%‚Úÿ'ã‘üåY‚Ýÿ&âŽüú•‚ßÿ%áŒüÎ7‚ªÿ‚°ÿ$á‰üñrªÿ„±ÿ#à‡ü®‚¨ÿ‡²ÿ#ß„üÝN‚§ÿ†€³ÿ"Þüês‚¦ÿ…€´ÿ!Ýü󆂦ÿ„€¶ÿ ‚¥ÿ„€·ÿ‡¤ÿ„ ¸ÿ„¤ÿƒ €ºÿ£ÿ„ €âÿ„ €áÿƒ €Þÿ„€  €Ýÿ„  €Ûÿƒ  Øÿ„  €×ÿ„  €Õÿƒ €Ôÿƒ  €Òÿƒ € €Ðÿ„" €Îÿ„# €Íÿƒ&  €Ëÿƒ'  €Êÿƒ) €Èÿƒ* €Æÿ„, €Äÿƒ.  €Ãÿƒ0 €Áÿƒ1  €Àÿƒ3 !  €¾ÿƒ4!"!!  ¼ÿƒ6"#""!!  €»ÿƒ8$$##""!!  €¹ÿƒ9%%$$##""!!  €¸ÿƒ;&&%%$$##""!!  €¶ÿƒ< ''&&%%$$##""!!  €µÿƒ> ((''&&%%$$##""!!  €³ÿƒ? ))((''&&%%$$##""!!  €²ÿƒA "**))((''&&%%$$##""!!  €°ÿƒB #++**))((''&&%%$$##""!!  €¯ÿƒD ",,++**))((''&&%%$$##""!!  €­ÿƒE !--,,++**))((''&&%%$$##""!!  €¬ÿƒG..--,,++**))((''&&%%$$##""!!  €ªÿƒH./..--,,++**))((''&&%%$$##""!!  €ªÿ‚J/0//..--,,++**))((''&&%%$$##""!!  €¨ÿ‚K/100//..--,,++**))((''&&%%$$##""!!  €§ÿ‚M/21100//..--,,++**))((''&&%%$$##""!!  €¥ÿ‚N03221100//..--,,++**))((''&&%%$$##""!!  €¤ÿ‚P/443221100//..--,,++**))((''&&%%$$##""!!  €¢ÿƒP.55443221100//..--,,++**))((''&&%%$$##""!!  €¢ÿ‚R '6655443221100//..--,,++**))((''&&%%$$##""!! € ÿ‚!6M55443221100//..--,,++**))((''&&%%$$##""!!  €Ÿÿ‚587€6M55443221100//..--,,++**))((''&&%%$$##""!! €ÿƒ29887€6L55443221100//..--,,++**))((''&&%%$$##""!!  €ÿ‚ ,:99887€6L55443221100//..--,,++**))((''&&%%$$##""!! €ÿ€ %;::99887€6K55443221100//..--,,++**))((''&&%%$$##""!!  €žÿ :;;::99887€6K55443221100//..--,,++**))((''&&%%$$##""!! €žÿ 7<<;;::99887€6J55443221100//..--,,++**))((''&&%%$$##""!!  €Ÿÿ ==<<;;::99887€6J55443221100//..--,,++**))((''&&%%$$##""!! €Ÿÿ ==<<;;::99887€6I55443221100//..--,,++**))((''&&%%$$##""!!  € ÿ ==<<;;::99887€6I55443221100//..--,,++**))((''&&%%$$##""!! € ÿ ==<<;;::99887€6H55443221100//..--,,++**))((''&&%%$$##""!!  €¡ÿ ==<<;;::99887€6H55443221100//..--,,++**))((''&&%%$$##""!! €¡ÿ ==<<;;::99887€6G55443221100//..--,,++**))((''&&%%$$##""!!  €¢ÿ ==<<;;::99887€6G55443221100//..--,,++**))((''&&%%$$##""!! €¢ÿ ==<<;;::99887€6F55443221100//..--,,++**))((''&&%%$$##""!!  €£ÿ ==<<;;::99887€6F55443221100//..--,,++**))((''&&%%$$##""!! €£ÿŒÿ€m  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnop‹ÿm !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnop‹ÿ€n  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnopŠÿ€o  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnop‰ÿo  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnop‰ÿ€p  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnopˆÿ€q  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnop‡ÿq  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnop‡ÿ€r  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnop†ÿ€s  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnop…ÿs  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnop…ÿ€t  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnop„ÿt  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnopƒÿu  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnopƒÿ€v  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnop‚ÿv  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnopÿw  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnopÿ€x  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnoi€ÿx  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklmnT €ÿ€y  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkklk5€ÿu  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkk]€ÿs  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghijkC‚ÿp  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghic$‚„ÿn  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefghO ‡ÿl  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdefe2‰ÿi  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcdeX‚‹ÿg  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``abcd?Žÿd  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``ab]"‚ÿb  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_``aK “ÿ€a  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_`^/•ÿ€^  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]^_R‚—ÿ€\  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\]]4šÿ€Y  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZ[\N‚œÿ€W  !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYZZ0Ÿÿ€T !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWXYJ‚¡ÿ€R !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVWV-¤ÿ€O !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUVVF‚¥ÿM !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTUT)¨ÿJ !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRSTC‚ªÿH !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQRP&­ÿE !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOPQ? ‚¯ÿC !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNOOM#²ÿ@ !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMNO; ‚´ÿ= !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKLMJ ‚·ÿ; !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJKL8 ‚¹ÿ8 !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHIJG‚¼ÿ6 !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGHI4‚¾ÿ3 !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEFGC‚Áÿ1 !"#$%&&'()*+,-./00123456789::;<=>?@ABCDEEF0‚Ãÿ. !"#$%&&'()*+,-./00123456789::;<=>?@ABCDE?‚Æÿ, !"#$%&&'()*+,-./00123456789::;<=>?@ABCD-‚Èÿ) !"#$%&&'()*+,-./00123456789::;<=>?@AB=‚Ëÿ'!"#$%&&'()*+,-./00123456789::;<=>?@A*‚Íÿ$"#$%&&'()*+,-./00123456789::;<=>?9‚Ðÿ"#$%&&'()*+,-./00123456789::;<=>&Óÿ $%&&'()*+,-./00123456789::;<4‚Õÿ!%&&'()*+,-./00123456789::9Øÿ"&&'()*+,-./00123456789:+ ‚Úÿ"&'()*+,-./00123456784‚Ýÿ"'()*+,-./001234567!‚ßÿ#()*+,-./0012345, ‚ªÿ‚°ÿ$)*+,-./001232ªÿ„±ÿ $*+,-./0012#‚¨ÿ‡²ÿ %+,-./00+‚§ÿ…?€³ÿ&,-./-‚¦ÿ„xÛÿq€´ÿ'-,‚¦ÿƒ N±ûÿÿò¶ÿ‚¥ÿ„$‡æ‚ÿ‘€·ÿ‡¤ÿƒTÀþƒÿý#€¸ÿ„¤ÿƒwâ†ÿ±€ºÿ£ÿ„+™öˆÿA€âÿƒM¼ŠÿЀáÿƒoÜŒÿa€Þÿ„%’òÿéÝÿ„E´þÿ€Ûÿƒ hÖ‘ÿù€Øÿ„Šî“ÿ €×ÿ„>­ü”ÿþ,€Õÿƒ`Ï—ÿ°€Ôÿƒ„ê™ÿ5€Òÿƒ'›øšÿº€Ðÿ„;°þœÿ@€ÎÿƒQÆžÿÅ€ÍÿƒfÙ ÿJ€Ëÿƒ|é¡ÿÏ€Êÿƒ ‘ô£ÿU€Èÿƒ2§ü¤ÿØÆÿ„G¼§ÿ`€Äÿƒ]ѨÿàÃÿƒ râªÿj€Áÿƒˆð«ÿçÀÿƒõ­ÿu€¾ÿƒ•ø®ÿî €¼ÿƒ"›ú°ÿ€€»ÿƒ'¢ü±ÿó€¹ÿƒ,¨ý³ÿŠ€¸ÿƒ2®þ´ÿ÷€¶ÿƒ8´·ÿ•€µÿƒ>º¸ÿû€³ÿƒDÁºÿ €²ÿƒJÇ»ÿý'€°ÿƒG̽ÿ¨€¯ÿƒ;Á¾ÿþ+€­ÿƒ1µÀÿ«€¬ÿƒ'ªþÀÿþ-€ªÿƒžüÂÿ­€ªÿ‚“ùÃÿþ/€¨ÿ‚‡õÅÿ°€§ÿ‚ {ðÇÿ2€¥ÿ‚péÈÿ²€¤ÿ‚dâÊÿ4€¢ÿƒKÚËÿ´€¢ÿ‚,ºÍÿ6€ ÿ‚˜üÍÿ·€Ÿÿ‚vñÏÿ8€ÿƒUÞÐÿ¹€ÿ‚4ÃÒÿ;€ÿ€¢þÒÿ¼€žÿ €õÔÿ=€žÿ_åÕÿ¾€ùÿ?€øÿÁ€ùÿB€øÿÀùÿD€øÿÅ€ùÿG€øÿÉ€ùÿE€£ÿt8mk@"òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ$õÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿnÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ#õÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿlÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿkÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ óÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ½ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿhÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿiÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ.ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû@öÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝ8D÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ IùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðZNúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅ Sûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü€Xüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿß:^ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¦cþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿò]iÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇ"oÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýƒtÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿá={ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¨ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèR‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäL“ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáFžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ– ¤ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜ@©ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý ®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØ:³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü‰¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓ5 »ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú‚·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎ/¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿù{µÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉ+´ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷u³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃ&²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôn±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾!°ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòg¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¸®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿï`¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±«ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìZªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¥©ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛD¨ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿù‚§ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀ( a` ¦ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿë`:ôÿÿÿž¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþžrÖÿÿÿÿÿÿÀ¤ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖ>G«úÿÿÿÿÿÿÿÿš£ÿÿÿÿÿÿÿÿÿÿÿÿÿ÷|€áÿÿÿÿÿÿÿÿÿÿÿ>¢ÿÿÿÿÿÿÿÿÿÿÿº#:©ûÿÿÿÿÿÿÿÿÿÿÿÿÍ¡ÿÿÿÿÿÿÿÿÏH]Ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ]ŸÿÿÿÿÿÜY€èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçžÿÿèk5£ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ}F^ XÆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷{äÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœ0ž÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ-RÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼uàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿL+˜õÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚM»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿk lÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿï ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠ$—öÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷7¬ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”LÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûbÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸwæÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý'òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿª.¢úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ0C¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´XÍÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ:gßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿ näÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿD tèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊzëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿO€ïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓ‡òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿZõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜ“÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿd!™ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿã%Ÿûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿo+¦üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿê #£ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿz˜úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðŒ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„ €òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿô tìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‰håÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿô\Ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‹PÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõDÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ8¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö'²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷oîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’MÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø.¼ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”šýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøxòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ–Vßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿšÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû"ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü%ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý(icnV ¿€boats-201908/resources/boats.rc000066400000000000000000000001041353032755300164000ustar00rootroot00000000000000IDI_ICON1 ICON DISCARDABLE "images/boats.ico" boats-201908/resources/boats.xml000066400000000000000000000005651353032755300166070ustar00rootroot00000000000000 Boat Scenario file boats-201908/resources/xbs.icns000066400000000000000000001153171353032755300164310ustar00rootroot00000000000000icnsšÏis32ºÿ‹[ÿÿêèêìíîïððññóÿÿèèêìíîïðñëÝôÿÿëëìîïñòðÖÙäöÿÿííîðòóáÜûÕ÷øÿÿîîðòôèâýÞðúûÿÿððòôõÕþâë€û ÿÿòòôöëâßèü ÿÿóóöøúÅ_é€ý@þÿÿôõ÷åŠ7yýþþýýÿÿôöî€SKêþüøõõÿÿõõšpWºþüñÙØãÿÿöÂŽuÅýþ÷ÔõÉ€ÿ öö³ãýþüòÒÉáÿ ÷ùûûýþûóã‚ÿ‡‚ÿÿ‹[ÿÿêéëíîïðññòòôÿÿééëíîïðñòëÝôÿÿììíïðñòðÖÚäöÿÿîíïñòôáÜûÕ÷ùÿÿïïñóôèâýÞðúûÿÿññóõõÕþâë€û ÿÿòòôöëâßèü ÿÿôôöøúÅ_é€ý@þÿÿôõ÷åŠ7yýþþýýÿÿõöî€SKêþüøõõÿÿöõšpWºþüñÙØãÿÿöÂŽuÅýþ÷ÔõÉ€ÿ ÷ö³ãýþüòÒÉáÿ øùûûýþûóã‚ÿ‡‚ÿÿ‹[ÿÿêéëíîïðññòòôÿÿêéëíîïðñòé×ôÿÿììíïðñòðª‡ÝöÿÿîíïñòôÐjmŸ÷ùÿÿïïñóôÝLQíúûÿÿññóõõU3gä€û ÿÿòòôöæ>WÚü ÿÿôôöøúλî€ý@þÿÿôõ÷ìÕï×ýþþýýÿÿõöðÙþâðþüøõõÿÿöõÑýðßþüñÙØäÿÿöÔüçâýþ÷ÔõÉ€ÿ ÷öÕêýþüòÒÉáÿ øùûûýþûóã‚ÿ‡‚ÿs8mk ÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÕ ÿÿÿÿÿÿÿÿÿÿé ÿÿÿÿÿÿÿÿÿé ÿÿÿÿÿÿÿÿ×  il32 ¢€ÿ—‚ÿ™ÿ ðëëìíîîïððñ€òƒóöÿëåæçèéëëìííîî€ï‚ðññôÿëçèéêëììíîîïïðð€ñ‚òõÿíèéêëìíîîïððññ€òóóàÁóóöÿîéëëìíîïïðññòòóóôäÔëÓõõ÷ÿîëììíîïðññòóóôôï×êúâëööøÿðìííîïðñòòóôõõÜà€ûØ€÷ùÿñíîïïðñòóôôõöÑ÷€üæí€ùúÿñíîïðñòóôõööÒ÷€ýöÜúûÿñîïðñòóôõö÷Ûöþ€ýÔ‚úûÿ òïðñòóôõö÷ëç€þýØñ‚ûüÿ óðñòóôõö÷÷ÙþÔñûûüýÿôñòóôõö÷øáðþþøÑù„üýÿôòóôõö÷øùúÜðãÜüƒýÿôòóõö÷øùúúûÞÛq˃ýþþýÿõóôõ÷øùúúûï-L€ý„þÿõôõö÷øúúø¦P,°ý‚þýÿöôõ÷øùúÆnK<,Oý€þýüû€úûÿöõö÷ùúÐkZK<3Ì€þ üúøöõôô÷ÿ ÷õ÷øùá}iZK=Œ€þ ûùöóñïîîðÿ÷ö÷øîŽxiZK\ùþþüùõïêæäâáæÿ÷ö÷öž†xiZaØþþüúõðÛÆÆÑÖÑ‚ÿ÷öø¶•†xjxçýþþû÷òèÂþôÚÇ€‚ÿ÷÷ø¯•‡wóýþþýúôïä¿õÛÆ2ƒÿ÷÷øõ¤‰ÀüýýþþüøòíßÌÜÅV„ÿ÷÷øúùìûüýýþþû÷ñìÙ×Ê3…ÿ÷÷øúúûûüýýþÿû÷ñéÙÓ€†ÿúùúûûüü€ýþþüùôí瀇ÿ’ˆÿˆÿ€ÿ—‚ÿ™ÿ ñëìíîîïðñòòó‚ô÷ÿëæçèéêììííîîïï€ðƒñôÿìèéêëìííîïïðð€ñ„òõÿîéêëìíîîïððññòòóáÁôô÷ÿîêììíîïïðññòòóóôôäÕëÓõõ÷ÿïìííîïðññòóóôôõï×ëúâëööøÿñííîïðñòòóôôõõÜàüûûØ÷øøùÿñíîïðñòóóôõõöÑ÷€üæí€ùúÿñîïðñòóôôõööÒ÷€ýöÜúûÿòïðñòóôõõö÷Ûöþ€ýÔ‚úûÿ óðñòóôõöö÷ëç€þýØñ‚ûüÿ ôñòóôõöö÷÷ÙþÔñûûüýÿôñòóôõö÷øáðþþøÑù„üýÿôòóôõö÷øùúÜðãÜüƒýÿõóôõö÷øùúúûÞÛq˃ýþþýÿõóõö÷øùúúûï-L€ý„þÿöôõö÷øúúø¦P,°ý‚þýÿöõö÷øùúÆnK<,Oý€þýüû€úûÿ÷õö÷ùúÐkZK<3Ì€þ üúøöõôô÷ÿ ÷õ÷øùá}iZK=Œ€þ ûùöóñïîîðÿ÷ö÷øîŽxiZK\ùþþüùõïêæäâáæÿ÷ö÷öž†xiZaØþþüúõðÛÆÆÑÖÑ‚ÿ÷öø¶•†xjxçýþþû÷òèÂþôÚÇ€‚ÿ÷÷ø¯•‡wóýþþýúôïä¿õÛÆ2ƒÿø÷øõ¤‰ÀüýýþþüøòíßÌÜÅV„ÿø÷øúùìûüýýþþû÷ñìÙ×Ê3…ÿø÷øúúûûüýýþÿû÷ñéÙÓ€†ÿúùúûûüü€ýþþüùôí瀇ÿ’ˆÿˆÿ€ÿ—‚ÿ™ÿ ñëìíîîïðñòòó‚ô÷ÿìçèéêëììííîîïï€ðƒñôÿíèéêëìííîïïðð€ñ„òõÿîéêëìíîîïððññòòóÚ©ôô÷ÿîêììíîïïðññòòóóôôÚ™´õõ÷ÿïìííîïðññòóóôôõî¢v„éööøÿñííîïðñòòóôôõõ»kgu…°÷øøùÿñíîïðñòóóôõõö•IWgv‡é€ùúÿñîïðñòóôôõöö:IXhtÃúûÿòïðñòóôõõö÷ª,:IXh˜‚úûÿóðñòóôõöö÷Ý1+:JXzî‚ûüÿôñòóôõöö÷÷Q+:J}îûûüýÿôñòóôõö÷øÎ+:‡ù„üýÿôòóôõö÷øùú§#B±üƒýÿõóôõö÷øùúúû»Þ×߃ýþþýÿõóõö÷øùúúûòÚìÙ€ý„þÿöôõö÷øúúøÖãþüÛý‚þýÿ öõö÷øùúßÙ€þÙý€þýüû€úûÿ ÷õö÷ùúáë€þñè€þ üúøöõôô÷ÿ ÷õ÷øùèãýþþÿÙ€þ ûùöóñïîîðÿ÷ö÷øðÛýýþþÜúþþüùõïêæäãáæÿ÷ö÷öÓüýýþàêþþüúõðÛÆÆÑÖÑ‚ÿ÷öøÕüüýþÙïýþþû÷òèÂþôÚÇ€‚ÿ÷÷øÛüýùÕôýþþýúôïäÀõÛÆ2ƒÿø÷øõØåÝüýýþþüøòíßÌÜÅV„ÿø÷øúùïûüýýþþû÷ñìÙ×Ê3…ÿø÷øúúûûüýýþÿû÷ñéÙÓ€†ÿúùúûûüü€ýþþüùôí瀇ÿ’ˆÿˆÿl8mk&((((((((((((((((((((((&:ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ: @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ@ @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ< @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÅ  @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙ, @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿã2  @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿë=  @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìG @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿã<  @ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙ/ <ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÅ)  <@@@@@@@@@@@@@@:it32IGÿÿŒÿÛšÿå”ÿç’ÿé‘ÿéÿëŽÿíÿ…Ýÿ…ÿ…ÿëã€ä倿çèéê„ë„ì…í‡î‘ïˆðïïóÿ…ÿ…ÿä€åæçèéêƒë„ì„í‡îŠï•ðÿ…ÿ…ÿ€ä€åæ€çèéêƒëƒì…í…î‰ïšðÿ…ÿ…ÿä倿çèé€êƒëƒì„í…î‡ï‘ðˆñ€ðÿ…ÿ…ÿ€åæç€èéê‚ëƒì„í…î†ïŠð•ñÿ…ÿ…ÿå忀çèé€êƒë‚ì„í„î…ï‰ðšñÿ…ÿ…ÿ倿ç€èéê‚ëƒìƒí„î„ïˆðŽñŒòñÿ…ÿ…ÿ€æç€èé€êƒë‚ìƒíƒî…ï†ðŠñ•òÿ…ÿ…ÿææ€çè€éê‚ë‚ìƒíƒî„ï†ðˆñšòÿ…ÿ…ÿæ€çè€éê‚ë‚ìƒí‚î„ï…ð†ñòŒóòÿ…ÿ…ÿ€çè€é€ê‚ë‚ìƒíƒîƒï„ð†ñŠò•óÿ…ÿ…ÿçç€èé€ê‚ë‚ì‚íƒîƒï„ð…ñˆòšóÿ…ÿ…ÿç€èé€ê‚ëìƒí‚îƒï„ð„ñ‡òó€ôîžZó‡ôÿ…ÿ…ÿ€è€éêë‚ì‚í‚îƒï„ð„ñ…ò‰ó†ôᎉڎˆôÿ…ÿ…ÿèè€é€ê‚ë‚ì‚í‚î‚ï„ðƒñ…òˆóˆô̆˜íø®Ôˆôÿ…ÿ…ÿè€é€ê‚ëì‚í‚îƒïƒðƒñ…ò†óŠô±„¯øùùø‹‰õÿ…ÿ…ÿ€é€ê‚ëì‚í‚î‚ïƒðƒñ„ò…ó‰ôõõð‡Ë‚ù˸‰õÿ…ÿ…ÿéé€êë‚ì‚íî‚ïƒðƒñ„ò„ó‡ô‚õô¥‡àúƒù‹Šõÿ…ÿ…ÿé€êë‚ìí‚î‚ï‚ðƒñ„ò„ó†ô„õ³‚Õ€úùùúúùá Šöÿ…ÿ…ÿ€êë‚ìí‚î‚ï‚ð‚ñ„òƒó…ô†õ€Æûû„úù‰ñŠöÿ…ÿ…ÿêêëì‚íî‚ï‚ðƒñƒòƒó…ô†õѶû„úÞ ‹öÿ…ÿ…ÿêëì‚íî‚ï‚ð‚ñƒòƒó„ô†õöᄦú‚û„ú‰ó‹÷ÿ…ÿ…ÿëì‚íî‚ï‚ð‚ñ‚òƒó„ô…õ€ö­”÷ˆûúúÛ¤Œ÷ÿ…ÿ…ÿ€ëì‚íîï‚ð‚ñƒò‚óƒô…õöè„Éüûüü†ûúúˆõŒ÷ÿ…ÿ…ÿëëìí‚îï‚ð‚ñ‚ò‚óƒô…õ‚ö¸‹ô‚ü‡ûÖ©øÿ…ÿ…ÿëìí‚îï‚ðñ‚òƒó‚ô„õƒö툾„ü€ûüü‚ûˆ÷øÿ…ÿ…ÿìí‚îïð‚ñ‚ò‚óƒôƒõ„öÄïŠüûӝ޸ÿ…ÿ…ÿ€ìíî‚ïð‚ñ‚ò‚ó‚ôƒõ„ö÷·§ýýŠü€ûü‰ùÿ…ÿ…ÿììíî‚ïðñ‚ò‚ó‚ôƒõ„ö÷÷¸ªƒýŠüƸùÿ…ÿ…ÿì‚í€î‚ïðñ‚ò‚ó‚ô‚õƒö÷¸ª„ý‰üù‚ùÿ…ÿ…ÿìíîïðñ‚òó‚ôƒõƒö÷¹©‰ý…ü™áúÿ…ÿ…ÿíîïðñ‚òó‚ô‚õƒö‚÷»©Šý€ü€ýüàœ‘úÿ…ÿ…ÿ€íîïðñò‚óôƒõ‚öƒ÷½©þýüø‘úÿ…ÿ…ÿííîïðñò‚óô‚õ‚öƒ÷ø¾©þþý±É’úÿ…ÿ…ÿíîïðñòó‚ô‚õ‚öƒ÷øÀ©‚þýñŠ“úÿ…ÿ…ÿîïðñòó‚ôõ‚öƒ÷øøÖ–ƒþýýþŠýˆí“úÿ…ÿ…ÿ€îïðñòóô‚õ‚ö‚÷€øôzý†þ‰ý¦Ï„úŒûúÿ…ÿ…ÿîîïðñòóô‚õ‚ö‚÷øå‡þˆýÉ­úú“ûÿ…ÿ…ÿîî€ïðñòóô‚õö‚÷‚ø½µˆþ‡ýè–ûÿ…ÿ…ÿî€ïðñòóô‚õö‚÷‚ø艉þ†ýù‚ø–ûÿ…ÿ…ÿïð€ñòóôõ‚ö‚÷øùù€ö‰þýþýŽè—ûÿ…ÿ…ÿ€ïð€ñòóôõ‚ö÷‚øùù£ÓŠþýý€þ€ý‘јûÿ…ÿ…ÿïïðñ€òóôõ‚ö÷‚øùùÕ þýýÖŽû‚üƒûÿ…ÿ…ÿïðñò€óôõö‚÷ø€ùôÿÿþü‹Û†û‘üÿ…ÿ…ÿï€ðñòó€ôõö‚÷øùìÿÿŽþü‰ßƒû•üÿ…ÿ…ÿð€ñòó€ôõö‚÷øù½¾€ÿþ÷…ã€û™üÿ…ÿ…ÿ€ðñ€òóôõ€ö‚÷øùîˆÿŒþÝëûû›üÿ…ÿ…ÿððñ€òóôõö÷øùú…òÿ‹þµ™ûžüÿ…ÿ…ÿðð€ñò€óôõö÷øùúØ—‚ÿ‰þûÁ üÿ…ÿ…ÿð€ñòó€ôõö÷øù€úŒÔÿþÿ†þææüŽýÿ…ÿ…ÿñ€òó€ôõö÷øù‚ú‰Ú‚ÿ…þ²•úŒü“ýÿ…ÿ…ÿ€ñò€óô€õö÷øù„ú†àÿƒþæ‚É‹ü–ýÿ…ÿ…ÿññò€óôõ€ö÷øù…úù…ä€ÿ‚þ¤–ø‰ü™ýÿ…ÿ…ÿññ€òó€ôõ€ö÷øù†úûù†åÿÿ€þÛ€Öˆüœýÿ…ÿ…ÿñò€óô€õö€÷øù†ú€ûù‡æÿþû™ û‡üžýÿ…ÿ…ÿñ€òó€ôõ€ö÷€øù†ú‚ûùˆç΂â‚üân©üü“ý†þýÿ…ÿ…ÿò€ó€ôõ€ö÷ø€ù†ú„ûùŽ®‚üÌSÍüŽýþÿ…ÿ…ÿ€ò€óô€õö€÷ø€ù†ú†û‚ü¿O MŒý”þÿ…ÿ…ÿòòó€ôõ€ö÷€øù…ú†ûü²K Éý—þÿ…ÿ…ÿòò€ó€ôõ€ö÷€øù…ú†û€ü¥H! Iˆý™þÿ…ÿ…ÿòó€ô€õö€÷ø€ù…ú†û üú˜E'$ º†ý›þÿ…ÿ…ÿò€ó€ôõ€ö÷€ø€ù†ú…û ÷ŽD**($ F…ýþÿ…ÿ…ÿó€ô€õ€ö÷€øù…ú„ûò„C22.*($ ®ƒýŽþÿŒþÿÿ…ÿ…ÿ€ó€ôõ€ö€÷ø€ù…úƒûú‘B9:62.*($ B‚ýŒþÿ‚þŠýþþÿ…ÿ…ÿ€ó€ô€õ€ö÷€ø€ù…úƒûÅO>A>:62.*($ £ýŠþÿ€þƒýˆü€ýÿ…ÿ…ÿóó€ôõ€ö€÷ø€ù…úûëhBIEA>:62.*($ @ýŒþý„ü„û‚üÿ…ÿ…ÿóó€ô€õ€ö÷€ø€ù…úûHNMIEA>:62.+($ ™ýýŒþ€ýüûÿ…ÿ…ÿó€ôõ€ö€÷€øù„ú€ûïfNUQMIEA>:62/+($ >ûý‹þýýüûŽúÿ…ÿ…ÿó€ô€õ€ö÷€ø€ù…úûûÁPXXUQMIEA>:63/+($ ýŠþýý€üû‚ú‚ù€ø‚ùúúÿ…ÿ…ÿô€õ€ö€÷€øù„úûû‚R_\XUQMIEA>:63/+($ <ù‰þ€ýüü€ûúùùø†÷øÿ…ÿ…ÿ€ô€õ€ö÷€ø€ù…úöi]c_\XUQMIEA>:73/+($"¥‰þýýüü€û€úùùøø€÷Šö÷÷ÿ…ÿ…ÿ€ô€õ€ö€÷€øù„úí]cgc_\XUQMIEA>;73/+($M†þÿþýý€üûû€úùøø÷÷€ö€õ†ô€õöÿ…ÿ…ÿôôõ€ö€÷€ø€ù„úÝVkjgc_\XUQMIEA?;73/+(-Ö…þÿþýýüüûû€úùøø÷ööõõ€ôŠóôôÿ…ÿ…ÿôô€õ€ö€÷ø€ùƒúÙTrnjgc_\XUQMIEA?;73/+&l‡þýýüüûûúúùøø÷ööõôô€óò„ñòóÿ…ÿ…ÿôô€õ€ö€÷€ø€ùƒúãXvrnjgc_\XUQMIEA?;73/+:ó†þýýüüûûúúùø÷ööõôôóóòò€ñˆð€ñÿ…ÿ…ÿô€õ€ö÷€ø€ù‚úëZyvrnjgc`\XUQMIEA?;73/*—†þýýüûûúúùøø÷öõôôóòòññððï„îïðÿ…ÿ…ÿô€õ€ö€÷€ø€ù‚úñ`zzvrnjgc`\XUQMIEA?;73/I„þÿþýýüûûúúùø÷öõõôóòòñððïï€îˆíîîïÿ…ÿ…ÿô€õ€ö€÷€ø€ù‚úi{}zvrnjgd`\XUQMIEA?;737Ð…þýýüûûúúùø÷öõôóóòñððïïî€í‚ì€ë‚ìíïÿ…ÿ…ÿ€õ€ö÷€ø€ùú•q€}zvrnjgd`\XUQMIEA?;71•…þýýüüûúúùø÷öõôóòññðïîîííììëëêééèêëëîñ…ÿ…ÿ€õ€ö€÷€ø€ùúÏb„}zvrnjhd`\XUQMIEA?;4b†þýüüûúúùø÷öõôóòñðïïîíììëêêéé€çææ€åççèéÛÎF…ÿ…ÿ€õ€ö€÷€ø€ù€úò]ˆ„}zvrnjhd`\XUQMIEA?;Eõƒþ ÿþýüüûúúùø÷öõôóòñðïîíìëêéèèçæåäãã‚áãäÞÏÇk…Žÿ…ÿ€õ€ö€÷€ø€ù€útƒˆ„}zvrnkhd`\XUQMIEA?<Ñ…þ)ýýüûûúùø÷öõôóòñðïíìêêèæååäâááßÝÜÛÙØÙÛÝÕÌÄ‘†Žÿ…ÿõõ€ö€÷€ø€ù€ú¦uŒˆ…}zvrnkhd`\XUQMIEA:—†þ(ýüûûúúø÷öõôóòñðïíëéæäâáàÞÜÚÙ×ÕÓÑÎÍÍÓ×ÓÌÆ§†ÿ…ÿõõ€ö€÷€ø€ùúúÛdŒˆ…}zvrnkhd`\XUQMIEA>:62.*($ £ýŠþÿ€þƒýˆü€ýÿ…ÿ…ÿó€ôõ€ö÷€øù…úûëhBIEA>:62.*($ @ýŒþý„ü„û‚üÿ…ÿ…ÿô€õö€÷ø€ù…úûHNMIEA>:62.+($ ™ýýŒþ€ýüûÿ…ÿ…ÿ€ôõ€ö÷€øù„ú€ûïfNUQMIEA>:62/+($ >ûý‹þýýüûŽúÿ…ÿ…ÿ€ô€õö€÷ø€ù…úûûÁPXXUQMIEA>:63/+($ ýŠþýý€üû‚ú‚ù€ø‚ùúúÿ…ÿ…ÿôôõ€ö÷€øù„úûû‚R_\XUQMIEA>:63/+($ <ù‰þ€ýüü€ûúùùø†÷øÿ…ÿ…ÿôô€õö€÷ø€ù…úöi]c_\XUQMIEA>:73/+($"¥‰þýýüü€û€úùùøø€÷Šö÷÷ÿ…ÿ…ÿôô€õ€ö÷€øù„úí]cgc_\XUQMIEA>;73/+($M†þÿþýý€üûû€úùøø÷÷€ö€õ†ô€õöÿ…ÿ…ÿôõ€ö€÷ø€ù„úÝVkjgc_\XUQMIEA?;73/+(-Ö…þÿþýýüüûû€úùøø÷ööõõ€ôŠóôôÿ…ÿ…ÿô€õö€÷€øùƒúÙTrnjgc_\XUQMIEA?;73/+&l‡þýýüüûûúúùøø÷ööõôô€óò„ñòóÿ…ÿ…ÿô€õ€ö÷€ø€ùƒúãXvrnjgc_\XUQMIEA?;73/+:ó†þýýüüûûúúùø÷ööõôôóóòò€ñˆð€ñÿ…ÿ…ÿõ€ö€÷ø€ù‚úëZyvrnjgc`\XUQMIEA?;73/*—†þýýüûûúúùøø÷öõôôóòòññððï„îïðÿ…ÿ…ÿ€õö€÷€ø€ù‚úñ`zzvrnjgc`\XUQMIEA?;73/I„þÿþýýüûûúúùø÷öõõôóòòñððïï€îˆíîîïÿ…ÿ…ÿ€õ€ö÷€ø€ù‚úi{}zvrnjgd`\XUQMIEA?;737Ð…þýýüûûúúùø÷öõôóóòñððïïî€í‚ì€ë‚ìíïÿ…ÿ…ÿ€õ€ö€÷€øùú•q€}zvrnjgd`\XUQMIEA?;71•…þýýüüûúúùø÷öõôóòññðïîîííììëëêééèêëëîñ…ÿ…ÿõõö€÷€ø€ùúÏb„}zvrnjhd`\XUQMIEA?;4b†þýüüûúúùø÷öõôóòñðïïîíììëêêéé€çææ€åççèéÛÎF…ÿ…ÿõõ€ö÷€ø€ù€úò]ˆ„}zvrnjhd`\XUQMIEA?;Eõƒþ!ÿþýüüûúúùø÷öõôóòñðïîíìëêéèèçæåäããâáãäÞÏÇk…Žÿ…ÿõõ€ö€÷ø€ù€útƒˆ„}zvrnkhd`\XUQMIEA?<Ñ…þ)ýýüûûúùø÷öõôóòñðïíìêêèææåäâááßÝÜÛÙØÙÛÝÕÌÄ‘†Žÿ…ÿõö€÷€ø€ù€ú¦uŒˆ…}zvrnkhd`\XUQMIEA:—†þ(ýüûûúúø÷öõôóòñðïíëéæäãáàÞÜÚÙ×ÖÓÑÏÎÎÔ×ÓÌÆ§†ÿ…ÿõ€ö÷€ø€ùúúÛdŒˆ…}zvrnkhd`\XUQMIE!%)-158<@DHLPSW[^Q؆û‘üÿ…ÿ…ÿððñòóôõ‚ö÷‚øùj!%)-158<@DHLPSWZQ݃û•üÿ…ÿ…ÿðñòóôõ‚ö÷‚øù¯!%)-158<@DHLPSUOâ€û™üÿ…ÿ…ÿð€ñòóôõ‚ö÷‚øùî1 !%)-158<@DHLPK\ëûû›üÿ…ÿ…ÿñòóôõö÷‚øùú[ !%)-158<@DHLGƒûžüÿ…ÿ…ÿ€ñòóôõö÷‚øùú× !%)-158<@DGH¸ üÿ…ÿ…ÿññòóôõö÷‚øù€úh !%)-158<@?UåüŽýÿ…ÿ…ÿññò€óôõö÷‚øù‚úb!%)-158<={úŒü“ýÿ…ÿ…ÿñòó€ôõö÷‚øù„ú\!%)-154I‹ü–ýÿ…ÿ…ÿñ€òóôõö÷øù…ú ùY!%)-17|ø‰ü™ýÿ…ÿ…ÿò€óôõö÷øù†ú ûùY!%)*KÒˆüœýÿ…ÿ…ÿ€òó€ôõö÷øù†ú€ûùY!%4‰û‡üžýÿ…ÿ…ÿòòóôõ€ö÷øù†ú‚ûùZ"Qà‚ü㕼üü“ý†þýÿ…ÿ…ÿòò€óôõö€÷øù†ú„ûùfš‚üÒ‰§­ÔüŽýþÿ…ÿ…ÿòóô€õö÷ø€ù†ú†û‚üȈ´þÿ‹Œý”þÿ…ÿ…ÿò€óôõ€ö÷øù…ú†ûü½ˆ¾€ÿ½Ì‰ý—þÿ…ÿ…ÿó€ôõö€÷øù…ú†û€ü³ˆÉ‚ÿ‹ˆý™þÿ…ÿ…ÿ€óô€õö÷€øù…ú†ûüúªˆÓƒÿÆÅ†ý›þÿ…ÿ…ÿ€ó€ôõ€ö÷ø€ù†ú…û÷¢ŠÛþ„ÿ‹…ýþÿ…ÿ…ÿóóô€õö€÷øù…ú„ûò›ã€þƒÿϼƒýŽþÿŒþÿÿ…ÿ…ÿóó€ôõ€ö÷ø€ù…úƒûú¢Œê‚þƒÿ‹‚ýŒþÿ‚þŠýþþÿ…ÿ…ÿóô€õö€÷ø€ù…úƒûË~Ø„þ‚ÿ×´ýŠþÿ€þƒýˆü€ýÿ…ÿ…ÿó€ôõ€ö÷€øù…úû뇮†þ‚ÿŒýŒþý„ü„û‚üÿ…ÿ…ÿô€õö€÷ø€ù…úû­Žô†þ‚ÿÞ­ýýŒþ€ýüûÿ…ÿ…ÿ€ôõ€ö÷€øù„ú€ûï†Ï‡þƒÿûý‹þýýüûŽúÿ…ÿ…ÿ€ô€õö€÷ø€ù…úûûdžô‡þƒÿå¦ýŠþýý€üû‚ú‚ù€ø‚ùúúÿ…ÿ…ÿôôõ€ö÷€øù„úûû•¬ýˆþƒÿ‹ù‰þ€ýüü€ûúùùø†÷øÿ…ÿ…ÿôô€õö€÷ø€ù…úö†Ýýý‡þƒÿ̵‰þýýüü€û€úùùøø€÷Šö÷÷ÿ…ÿ…ÿôô€õ€ö÷€øù„úí€ì€ý†þƒÿþ‚†þÿþýý€üûû€úùøø÷÷€ö€õ†ô€õöÿ…ÿ…ÿôõ€ö€÷ø€ù„úß‚öý…þ„ÿ¦Ú…þÿþýýüüûû€úùøø÷ööõõ€ôŠóôôÿ…ÿ…ÿô€õö€÷€øùƒú܇ü‚ý…þƒÿð‡þýýüüûûúúùøø÷ööõôô€óò„ñòóÿ…ÿ…ÿô€õ€ö÷€ø€ùƒúä…üƒý…þƒÿŒó†þýýüüûûúúùø÷ööõôôóóòò€ñˆð€ñÿ…ÿ…ÿõ€ö€÷ø€ù‚úëúƒý†þ‚ÿÔª†þýýüûûúúùøø÷öõôôóòòññððï„îïðÿ…ÿ…ÿ€õö€÷€ø€ù‚úñ÷„ý†þ‚ÿ€„þÿþýýüûûúúùø÷öõõôóòòñððïï€îˆíîîïÿ…ÿ…ÿ€õ€ö÷€ø€ù‚ú‚ò„ý‡þÿ¨Ö…þýýüûûúúùø÷öõôóóòñððïïî€í‚ì€ë‚ìíïÿ…ÿ…ÿ€õ€ö€÷€øùú¢Ñü„ý‡þ€ÿÒ«…þýýüüûúúùø÷öõôóòññðïîîííììëëêééèêëëîñ…ÿ…ÿõõö€÷€ø€ùúÒ ü„ýˆþÿÿóІþýüüûúúùø÷öõôóòñðïïîíììëêêéé‚ç€æççèéÛÎF…ÿ…ÿõõ€ö÷€ø€ù€úòüü„ýˆþÿÿ…õƒþ!ÿþýüüûúúùø÷öõôóòñðïîíìëêéèèççåäããâáãäÞÏÇk…Žÿ…ÿõõ€ö€÷ø€ù€ú‡ìüüƒý‰þÿ£×…þ)ýýüûûúùø÷öõôóòñðïíìêêèææåäâááàÝÜÛÚÙÚÛÝÕÌÄ‘†Žÿ…ÿõö€÷€ø€ù€ú¯Äüü„ý‰þÍ«†þ(ýüûûúúø÷öõôóòñðïíëéæäãáàÞÜÚÙ×ÖÓÑÏÎÎÔ×ÓÌÆ§†ÿ…ÿõ€ö÷€ø€ùúúÝ•€ü„ýˆþߌ†þ(ýüüûúúù÷öõôóòñðîìêçãßÛÙÖÔÒÐÏÌÈÇÃÁÄÓÜÙÓÌÆ¸‡ÿ…ÿõ€ö€÷ø€ùú÷}ú€üƒýˆþÞˆü…þ(ýýüûûúùø÷õôóòñðïìêçáÚÔÎÉÆÄ¿½¹·µÇÛåàÙÓÌÆÀ‡ÿ…ÿõ€ö€÷€øùúã€ü„ý‡þÞˆüý…þ(ýüüûúúø÷öõóòñðïîëèãÚÐÆ½·´°¯¬«»ÓííçàÚÓÍÆÁ†‘ÿ…ÿõ€ö€÷€ø€ùú»·ü„ý†þÞˆüý…þ(ýýüûûúùøöõôóñðïîìêåÞÒŸ±³·¾ÎåûúôíçàÚÓÍÆÀ&†’ÿ…ÿö€÷€ø€ùæ‹‚üƒý†þÞˆüýý…þýüüûúúø÷öôóòðïîíëèäÚ̼°ƒþ ûôîçáÚÔÍÇÀB†“ÿ…ÿ€ö÷€ø€ù~÷‚üƒý…þÔŒüýý†þýüûûúù÷öõôòñðîíìêæâ×ǵ±‚þ ûôîçáÚÔÍÇÀb‡“ÿ…ÿ€ö€÷øùù¢Ó‚ü„ý„þ­©ý…þýüüûúúø÷õôóòðïîìëéåßÔð¶þ ûôîçáÛÔÎÇÀy‡”ÿ…ÿ€ö€÷€øùùÝ™ƒü„ý‚þøŒÍ‚ý…þýüûûúùøöõôòñðîíìêèäÝÑ¿­½€þ ûõîèáÛÔÎÇÀ’‡•ÿ…ÿ€ö€÷€øùù}úƒüƒý‚þàíƒý„þ%ýýüûúúø÷öôóòðïîìëéçâÚϽªÌþþûõîèáÛÔÎÇÀ£‡–ÿ…ÿ€ö€÷€øùù¾¥ƒüƒýþ¹•ûƒý…þ$ýüüûúùøöõôòñðîíìêèåáØË¹¦äþüõïèâÛÕÎÈÁ¯‡—ÿ…ÿ€ö€÷€ø€ù¹ª‚üƒýþþ÷Ž»üüƒý…þ#ýüûûúù÷öõóòðïîìëéèäàÖȶ¥ûüõïèâÛÕÎÈÁ¸‡˜ÿ…ÿöö÷€ø€ùúµ¯€ü„ýþÄ…êüü„ýƒþ$ÿþýüûúúø÷öôóñðïíìêèæãÝÓŲ¶üöïéâÜÕÏȼ‡™ÿ…ÿöö÷€ø€ùúú°³üüƒýòŒ·ü„ý„þ#ýýüûúúø÷õôòñðîíìêèåáÛÐÀ­ÐöïéâÜÕÏȾ†šÿ…ÿöö€÷ø€ù€ú®´ü‚ý»Šï‚ü„ý„þ"ýýüûúùøöõóòñïîìëéçäàÙͼ«îïéâÜÕÏȼ†›ÿ…ÿöö€÷€ø€ù‚ú®´€ýíˆÀ„üƒý…þ ýüüûúù÷öõóòðïíìêéæãßÖÈ·ÀðéãÜÖÏɸ‡œÿ…ÿöö€÷€ø€ùƒú®²ý³óû„üƒý…þýüûûúù÷öôóñðïíìêèåâÝÓÆ³ÚéãÜÖÏÉÁ³‡ÿ…ÿöö€÷€ø€ù„ú¿†Ê€û„üƒý…þýüûûúø÷öôóñðîíìêèåáÛÏÁ·éãÝÖÐɦ‡žÿ…ÿöö€÷€ø€ù„úƒû„üƒý…þýüûúúø÷õôòñðîíëéçäà×Í¿ÍãÝÖÐÉ“ˆžÿ…ÿöö€÷€ø€ù„úƒû„üƒýƒþÿþýüûúúø÷õôòñïîìëéçãßÖʼÜÝÖÐÉÇ ÿ…ÿöö€÷€ø€ù„úƒû„üƒýƒþÿþýüûúúø÷õôòñïîìëéæãÝÔÈÈÝ×ÐÊÃf‡¡ÿ…ÿöö€÷€ø€ù„úƒû„üƒýƒþÿþýüûúúø÷õôòñïîìëéæâÝÔÉÖ×ÐÊÃC‡¢ÿ…ÿöö€÷€ø€ù„úƒû„üƒýƒþÿþýüûúúø÷õôòñïîìëéæâÝÕÑ×ÑÊÄ(‡£ÿ…ÿöö€÷€ø€ù„úƒû„üƒýƒþÿþýüûúúø÷õôòñïîìëéæâÞÖÖÑÊć¤ÿ…ÿöö€÷€ø€ù„úƒû„üƒýƒþÿþýüûúúø÷õôòñïîíëéæãßÙÑʾˆ¥ÿ…ÿöö€÷€ø€ù„úƒû„üƒý…þýüûúúø÷õôóñðîíëéçäáÒʲˆ¦ÿ…ÿöö€÷€ø€ù„úƒû„üƒý…þýüûûúù÷öôóñðîíìêèåÛÌšˆ§ÿ…ÿùö€÷€ø€ù„úƒû„üƒý…þýüûûúù÷öôóòðïííëéçÑvˆ¨ÿ…¼ÿöPˆ©ÿÏ«ÿέÿ̯ÿʰÿɲÿƵÿüÿº¯ÿt8mk@     '.1334444444444444444444444444444444444444444444444444444444444444444444444444444444444444444331.'  -9BGJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJGB9-  (9IÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿI9(  .BUÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿUB. 1G[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ[G13J^ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^J34K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`K44K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ_J34K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^I24K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ\G04K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×WB, 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷lO:& 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüyVD04K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ‰ZI7% 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ\M;)4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±^O>, 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇ`Q@/ 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖdSB1! 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáiTD3# 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéoVE4$ 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñwWG6&4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö€XH7'4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû‹ZJ9( 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý—[K:* 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤\L;+ 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²]N=, 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁ^O>- 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌ_P?. 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁ_P@/  4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²^O?/! 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý£]N>/! 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû–[M=.  4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöŠZK;, 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð~XI:+ 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéuVH8) 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàmUF6( 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖfSD4& 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇbQB3$ 4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±]O@1#4K`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþš[M>/! 3J^ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý‡XJ;- 1G[ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùvUG8*  .BUÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖhQD5'  (9IU[_`aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa``^ZSJ>1$  -9BGJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJHE>6+  '.1334444444444444444444444444444444444444444444444444444444331.)"     icnV ¿€boats-201908/situationprint.cpp000066400000000000000000000061001353032755300165320ustar00rootroot00000000000000// // C++ Implementation: situationprint // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009-2012 Thibaut GRIDEL // // 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 . // // #include #include "situationprint.h" #include "commontypes.h" #include "situationmodel.h" #include "situationview.h" extern int debugLevel; SituationPrint::SituationPrint(SituationModel *situation, SituationView *view, QWidget *parent) : QTextEdit(parent), m_situation(situation), m_view(view) { setAcceptRichText(true); } void SituationPrint::render(QRectF pageRect) { Q_UNUSED(pageRect); if (!m_situation) { return; } QTextCursor cursor(document()); QTextBlockFormat headingblock; headingblock.setAlignment(Qt::AlignHCenter); QTextCharFormat headingchar; headingchar.setFontPointSize(20); headingchar.setFontWeight(2); QTextBlockFormat descblock; descblock.setAlignment(Qt::AlignLeading); QTextCharFormat descchar; descchar.setFontPointSize(14); descchar.setFontWeight(2); QTextBlockFormat textblock; textblock.setAlignment(Qt::AlignLeading); textblock.setIndent(1); QTextCharFormat textchar; textchar.setFontPointSize(12); cursor.insertBlock(headingblock); cursor.insertText(m_situation->title(), headingchar); if (!m_situation->rules().isEmpty()) { cursor.insertBlock(); cursor.insertBlock(descblock); cursor.insertText(tr("Rule") + " ", descchar); cursor.insertText(m_situation->rules(), textchar); } if (!m_situation->abstract().isEmpty()) { cursor.insertBlock(); cursor.insertBlock(descblock); cursor.insertBlock(); cursor.insertBlock(textblock); cursor.insertText(m_situation->abstract(), textchar); } cursor.insertBlock(); cursor.insertBlock(headingblock); QPixmap image(rect().size()); image.fill(Qt::white); QPainter painter(&image); painter.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform); m_view->render(&painter); document()->addResource(QTextDocument::ImageResource, QUrl("mydata://image.png"), QVariant(image)); cursor.insertImage("mydata://image.png"); if (!m_situation->description().isEmpty()) { cursor.insertBlock(); cursor.insertBlock(descblock); cursor.insertBlock(); cursor.insertBlock(textblock); cursor.insertText(m_situation->description(), textchar); } } boats-201908/situationprint.h000066400000000000000000000025341353032755300162060ustar00rootroot00000000000000// // C++ Interface: situationprint // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2009 Thibaut GRIDEL // // 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 . // // #ifndef SITUATIONPRINT_H #define SITUATIONPRINT_H #include #include class SituationModel; class SituationView; class SituationPrint : public QTextEdit { Q_OBJECT public: SituationPrint(SituationModel *situation, SituationView *view, QWidget *parent = 0); ~SituationPrint() {} void render(QRectF pageRect); public slots: void print(QPrinter *printer) const { QTextEdit::print(printer); } private: SituationModel *m_situation; SituationView *m_view; }; #endif // SITUATIONPRINT_H boats-201908/situationwidget.cpp000066400000000000000000000307271353032755300166750ustar00rootroot00000000000000// // C++ Implementation: situationwidget // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #include "situationwidget.h" #include "trackdelegate.h" #include "winddelegate.h" #include "situationmodel.h" #include #include #include extern int debugLevel; SituationWidget::SituationWidget(QWidget *parent) : QTabWidget(parent), m_situation(0) { // Scenario layout scenarioFrame = new QFrame(); scenarioLayout = new QVBoxLayout(scenarioFrame); // Options layout optionsGroup = new QGroupBox(scenarioFrame); optionsGroup->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Maximum); optionsForm = new QFormLayout(optionsGroup); seriesCombo = new QComboBox(optionsGroup); seriesCombo->addItems(Boats::seriesList()); connect (seriesCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setSeries(int))); seriesLabel = new QLabel(optionsGroup); optionsForm->addRow(seriesLabel, seriesCombo); laylineCheck = new QCheckBox(optionsGroup); connect(laylineCheck, SIGNAL(toggled(bool)), this, SLOT(setShowLayline(bool))); laylineCheckLabel = new QLabel(optionsGroup); optionsForm->addRow(laylineCheckLabel, laylineCheck); laylineSpin = new QSpinBox(optionsGroup); laylineSpin->setRange(0, 359); laylineSpin->setWrapping(true); connect (laylineSpin, SIGNAL(valueChanged(int)), this, SLOT(setLayline(int))); laylineSpinLabel = new QLabel(optionsGroup); optionsForm->addRow(laylineSpinLabel, laylineSpin); lengthSpin = new QSpinBox(optionsGroup); lengthSpin->setRange(1,5); connect (lengthSpin, SIGNAL(valueChanged(int)), this, SLOT(setLength(int))); lengthSpinLabel = new QLabel(optionsGroup); optionsForm->addRow(lengthSpinLabel, lengthSpin); windCheck = new QCheckBox(optionsGroup); connect(windCheck, SIGNAL(toggled(bool)), this, SLOT(setShowWind(bool))); windCheckLabel = new QLabel(optionsGroup); optionsForm->addRow(windCheckLabel, windCheck); // Track layout trackGroup = new QGroupBox(scenarioFrame); trackLayout = new QGridLayout(trackGroup); trackTableModel = new TrackTableModel(m_situation); trackTableView = new QTableView(trackGroup); trackTableView->setItemDelegate(new TrackDelegate); trackTableView->setEditTriggers(QAbstractItemView::CurrentChanged); trackTableView->verticalHeader()->hide(); #if QT_VERSION < 0x050000 trackTableView->horizontalHeader()->setResizeMode(QHeaderView::Fixed); trackTableView->horizontalHeader()->setClickable(false); #else trackTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); trackTableView->horizontalHeader()->setSectionsClickable(false); #endif trackTableView->horizontalHeader()->setDefaultSectionSize(60); trackTableView->horizontalHeader()->setStretchLastSection(true); trackLayout->addWidget(trackTableView); // Wind layout windGroup = new QGroupBox(scenarioFrame); windLayout = new QGridLayout(windGroup); windTableModel = new WindTableModel(&m_situation->wind()); windTableView = new QTableView(windGroup); windTableView->setItemDelegate(new WindDelegate); windTableView->setEditTriggers(QAbstractItemView::CurrentChanged); windTableView->verticalHeader()->hide(); #if QT_VERSION < 0x050000 windTableView->horizontalHeader()->setResizeMode(QHeaderView::Stretch); windTableView->horizontalHeader()->setClickable(false); #else windTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); windTableView->horizontalHeader()->setSectionsClickable(false); #endif windLayout->addWidget(windTableView); // last bricks scenarioLayout->addWidget(optionsGroup); scenarioLayout->addWidget(trackGroup); scenarioLayout->addWidget(windGroup); connect(windCheck, SIGNAL(toggled(bool)), windGroup, SLOT(setVisible(bool))); addTab(scenarioFrame,tr("Scenario")); // description group descriptionFrame = new QFrame(); descriptionGrid = new QGridLayout(descriptionFrame); descriptionForm = new QFormLayout(); descriptionGrid->addLayout(descriptionForm, 0, 0); titleEdit = new QLineEdit(descriptionFrame); connect (titleEdit, SIGNAL(editingFinished()), this, SLOT(setTitle())); titleLabel = new QLabel(descriptionFrame); descriptionForm->addRow(titleLabel, titleEdit); rulesEdit = new QLineEdit(descriptionFrame); connect (rulesEdit, SIGNAL(editingFinished()), this, SLOT(setRules())); rulesLabel = new QLabel(descriptionFrame); descriptionForm->addRow(rulesLabel, rulesEdit); abstractLabel = new QLabel(descriptionFrame); descriptionGrid->addWidget(abstractLabel,1,0); abstractEdit = new QPlainTextEdit(descriptionFrame); abstractEdit->setUndoRedoEnabled(false); abstractEdit->setContextMenuPolicy(Qt::NoContextMenu); connect(abstractEdit->document(), SIGNAL(contentsChanged()), this, SLOT(setAbstract())); descriptionGrid->addWidget(abstractEdit,2,0); descriptionLabel = new QLabel(descriptionFrame); descriptionGrid->addWidget(descriptionLabel,3,0); descriptionEdit = new QPlainTextEdit(descriptionFrame); descriptionEdit->setUndoRedoEnabled(false); descriptionEdit->setContextMenuPolicy(Qt::NoContextMenu); connect(descriptionEdit->document(), SIGNAL(contentsChanged()), this, SLOT(setDescription())); descriptionGrid->addWidget(descriptionEdit,4,0); addTab(descriptionFrame,tr("Description")); } void SituationWidget::changeEvent(QEvent *event) { if(event->type() == QEvent::LanguageChange) { optionsGroup->setTitle(tr("Options")); seriesLabel->setText(tr("Series")); QStringList seriesList = Boats::seriesList(); for (int i=0; isetItemText(i, seriesList[i]); } laylineCheckLabel->setText(tr("Show Grid")); laylineSpinLabel->setText(tr("Layline Angle")); lengthSpinLabel->setText(tr("Zone Length")); windCheckLabel->setText(tr("Show Wind")); trackGroup->setTitle(tr("Tracks")); windGroup->setTitle(tr("Wind")); setTabText(0, tr("Scenario")); titleLabel->setText(tr("Title")); rulesLabel->setText(tr("Rules")); abstractLabel->setText(tr("Abstract")); descriptionLabel->setText(tr("Description")); setTabText(1, tr("Description")); } else { QTabWidget::changeEvent(event); } } void SituationWidget::update() { if (m_situation) { titleEdit->setText(m_situation->title()); rulesEdit->setText(m_situation->rules()); seriesCombo->setCurrentIndex(m_situation->situationSeries()); laylineCheck->setChecked(m_situation->showLayline()); laylineSpin->setValue(m_situation->laylineAngle()); lengthSpin->setValue(m_situation->situationLength()); windCheck->setChecked(m_situation->wind().visible()); windGroup->setVisible(m_situation->wind().visible()); abstractEdit->setPlainText(m_situation->abstract()); descriptionEdit->setPlainText(m_situation->description()); } } void SituationWidget::setSituation(SituationModel *situation) { if (situation != m_situation) { m_situation = situation; update(); // Scenario Group connect (situation, SIGNAL(titleChanged(QString)), titleEdit, SLOT(setText(QString))); connect (situation, SIGNAL(rulesChanged(QString)), rulesEdit, SLOT(setText(QString))); connect (situation, SIGNAL(seriesChanged(int)), seriesCombo, SLOT(setCurrentIndex(int))); connect(situation, SIGNAL(showLaylineChanged(bool)), laylineCheck, SLOT(setChecked(bool))); connect (situation, SIGNAL(laylineChanged(const int)), laylineSpin, SLOT(setValue(int))); connect (situation, SIGNAL(lengthChanged(const int)), lengthSpin, SLOT(setValue(int))); connect(&situation->wind(), SIGNAL(windVisibleChanged(bool)), windCheck, SLOT(setChecked(bool))); connect(situation, SIGNAL(abstractChanged(const QString)), this, SLOT(updateAbstract(const QString))); connect(situation, SIGNAL(descriptionChanged(const QString)), this, SLOT(updateDescription(const QString))); // Track group trackTableModel->setSituation(m_situation); connect(situation, SIGNAL(trackAdded(TrackModel*)), trackTableModel, SLOT(addTrack(TrackModel*))); connect(situation, SIGNAL(trackRemoved(TrackModel*)), trackTableModel, SLOT(deleteTrack(TrackModel*))); trackTableView->setModel(trackTableModel); // Wind group windTableModel->setWind(&m_situation->wind()); connect(&m_situation->wind(), SIGNAL(windReset()), windTableModel, SLOT(updateWind())); windTableView->setModel(windTableModel); } } void SituationWidget::unSetSituation() { // Scenario Group disconnect(m_situation, 0, seriesCombo, 0); disconnect(m_situation, 0, laylineCheck, 0); disconnect(m_situation, 0, laylineSpin, 0); disconnect(m_situation, 0, lengthSpin, 0); disconnect(&m_situation->wind(), 0, windCheck, 0); disconnect(m_situation, 0, this, 0); // Track Group disconnect(m_situation, 0, trackTableModel, 0); disconnect(&m_situation->wind(), 0, windTableModel, 0); m_situation = 0; titleEdit->clear(); rulesEdit->clear(); seriesCombo->setCurrentIndex(0); laylineCheck->setChecked(true); laylineSpin->setValue(40); lengthSpin->setValue(3); windCheck->setChecked(false); abstractEdit->clear(); descriptionEdit->clear(); } void SituationWidget::setTitle() { if (m_situation) { m_situation->changeTitle(titleEdit->text()); } } void SituationWidget::setRules() { if (m_situation) { m_situation->changeRules(rulesEdit->text()); } } void SituationWidget::setShowLayline(bool show) { if (m_situation) { m_situation->toggleShowLayline(show); } } void SituationWidget::setLayline(int angle) { if (m_situation) { m_situation->changeLaylineAngle(angle); } } void SituationWidget::setLength(int length) { if (m_situation) { m_situation->changeLength(length); } } void SituationWidget::setShowWind(bool show) { if (m_situation) { if (show != m_situation->wind().visible()) { m_situation->toggleWind(); } } } void SituationWidget::setSeries(int series) { if (m_situation) { m_situation->changeSeries((Boats::Series)series); } } void SituationWidget::setAbstract() { if (m_situation) { m_situation->changeAbstract(abstractEdit->document()->toPlainText()); } } void SituationWidget::setDescription() { if (m_situation) { m_situation->changeDescription(descriptionEdit->document()->toPlainText()); } } void SituationWidget::updateAbstract(const QString abstract) { if (m_situation) { if (abstractEdit->document()->toPlainText() != abstract) { disconnect(abstractEdit->document(), 0, 0, 0); abstractEdit->document()->setPlainText(abstract); connect(abstractEdit->document(), SIGNAL(contentsChanged()), this, SLOT(setAbstract())); } } } void SituationWidget::updateDescription(const QString description) { if (m_situation) { if (descriptionEdit->document()->toPlainText() != description) { disconnect(descriptionEdit->document(), 0, 0, 0); descriptionEdit->document()->setPlainText(description); connect(descriptionEdit->document(), SIGNAL(contentsChanged()), this, SLOT(setDescription())); } } } boats-201908/situationwidget.h000066400000000000000000000060571353032755300163410ustar00rootroot00000000000000// // C++ Interface: situationwidget // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #ifndef SITUATIONWIDGET_H #define SITUATIONWIDGET_H #include "tracktablemodel.h" #include "windtablemodel.h" #include #include #include #include #include #include #include #include #include class SituationModel; class SituationWidget : public QTabWidget { Q_OBJECT public: SituationWidget(QWidget *parent = 0); ~SituationWidget() {} void update(); public slots: void setSituation(SituationModel *situation); void unSetSituation(); // set the model from widgets void setTitle(); void setRules(); void setShowLayline(bool); void setLayline(int angle); void setSeries(int series); void setLength(int length); void setShowWind(bool); void setAbstract(); void setDescription(); // set the widget from the model void updateAbstract(const QString abstract); void updateDescription(const QString description); protected: void changeEvent(QEvent *event); private: SituationModel *m_situation; QFrame *scenarioFrame; QVBoxLayout *scenarioLayout; QGroupBox *optionsGroup; QComboBox *seriesCombo; QLabel *seriesLabel; QCheckBox *laylineCheck; QLabel *laylineCheckLabel; QSpinBox *laylineSpin; QLabel *laylineSpinLabel; QSpinBox *lengthSpin; QLabel *lengthSpinLabel; QCheckBox *windCheck; QLabel *windCheckLabel; QFormLayout *optionsForm; QGroupBox *trackGroup; TrackTableModel *trackTableModel; QTableView *trackTableView; QGridLayout *trackLayout; QGroupBox *windGroup; WindTableModel *windTableModel; QTableView *windTableView; QGridLayout *windLayout; QFrame *descriptionFrame; QLineEdit *titleEdit; QLabel *titleLabel; QLineEdit *rulesEdit; QLabel *rulesLabel; QPlainTextEdit *abstractEdit; QLabel *abstractLabel; QPlainTextEdit *descriptionEdit; QLabel *descriptionLabel; QGridLayout *descriptionGrid; QFormLayout *descriptionForm; }; #endif boats-201908/statemachine.cpp000066400000000000000000000276261353032755300161230ustar00rootroot00000000000000// // C++ Implementation: statemachine // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2014 Thibaut GRIDEL // // 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 . // // #include "statemachine.h" StateMachine::StateMachine(QObject *parent) : QStateMachine(parent), m_parallelEditionState(new EnableState(this)), m_editingState(new EnableState(m_parallelEditionState)), m_mouseState(new EnableState(m_parallelEditionState)), m_selectionState(new EnableState(m_parallelEditionState)), m_animationState(new EnableState(this)), m_noStateState(new EnableState(m_editingState)), m_createState(new EnableState(m_editingState)), m_createTrackState(new EnableState(m_createState)), m_createBoatState(new EnableState(m_createState)), m_createMarkState(new EnableState(m_createState)), m_createLineState(new EnableState(m_createState)), m_createPointState(new EnableState(m_createState)), m_selectState(new EnableState(m_mouseState)), m_moveState(new EnableState(m_mouseState)), m_rotateState(new EnableState(m_mouseState)), m_noSelectionState(new EnableState(m_selectionState)), m_boatSelectionState(new EnableState(m_selectionState)), m_markSelectionState(new EnableState(m_selectionState)), m_pointSelectionState(new EnableState(m_selectionState)), m_stopState(new EnableState(m_animationState)), m_playState(new EnableState(m_animationState)), m_pauseState(new EnableState(m_animationState)) { m_parallelEditionState->setObjectName("ParallelEdition"); m_editingState->setObjectName("Edition"); m_mouseState->setObjectName("Mouse"); m_animationState->setObjectName("ANIMATE"); m_noStateState->setObjectName("Edition_NO_STATE"); m_createState->setObjectName("Edition_Create"); m_createTrackState->setObjectName("Edition_CREATE_TRACK"); m_createBoatState->setObjectName("Edition_CREATE_BOAT"); m_createMarkState->setObjectName("Edition_CREATE_MARK"); m_createLineState->setObjectName("Edition_CREATE_LINE"); m_createPointState->setObjectName("Edition_CREATE_POINT"); m_selectState->setObjectName("Mouse_Select"); m_moveState->setObjectName("Mouse_Move"); m_rotateState->setObjectName("Mouse_Rotate"); m_noSelectionState->setObjectName("Mouse_Select_No Selection"); m_boatSelectionState->setObjectName("Mouse_Select_Boat"); m_markSelectionState->setObjectName("Mouse_Select_Mark"); m_pointSelectionState->setObjectName("Mouse_Select_Point"); m_stopState->setObjectName("Animation_STOP"); m_playState->setObjectName("Animation_PLAY"); m_pauseState->setObjectName("Animation_PAUSE"); setInitialState(m_parallelEditionState); m_parallelEditionState->setChildMode(QStateMachine::ParallelStates); m_editingState->setInitialState(m_noStateState); m_editingState->addTransition(this, SIGNAL(animate()), m_animationState); m_editingState->addTransition(this, SIGNAL(noState()), m_noStateState); m_mouseState->setInitialState(m_selectState); m_selectionState->setInitialState(m_noSelectionState); m_animationState->setInitialState(m_stopState); m_animationState->setToggable(true); m_animationState->addTransition(this, SIGNAL(animate()), m_editingState); m_animationState->addTransition(this, SIGNAL(noState()), m_editingState); m_noStateState->addTransition(this, SIGNAL(createTrack()), m_createTrackState); m_noStateState->addTransition(this, SIGNAL(createBoat()), m_createBoatState); m_noStateState->addTransition(this, SIGNAL(createMark()), m_createMarkState); m_noStateState->addTransition(this, SIGNAL(createLine()), m_createLineState); m_noStateState->addTransition(this, SIGNAL(createPoint()), m_createPointState); m_createTrackState->setToggable(true); m_createTrackState->addTransition(this, SIGNAL(lmbclick()), m_createBoatState); m_createTrackState->addTransition(this, SIGNAL(createTrack()), m_noStateState); m_createTrackState->addTransition(this, SIGNAL(createBoat()), m_createBoatState); m_createTrackState->addTransition(this, SIGNAL(createMark()), m_createMarkState); m_createTrackState->addTransition(this, SIGNAL(createLine()), m_createLineState); m_createBoatState->setToggable(true); m_createBoatState->addTransition(this, SIGNAL(lmbclick()), m_createBoatState); m_createBoatState->addTransition(this, SIGNAL(createTrack()), m_createTrackState); m_createBoatState->addTransition(this, SIGNAL(createBoat()), m_noStateState); m_createBoatState->addTransition(this, SIGNAL(createMark()), m_createMarkState); m_createBoatState->addTransition(this, SIGNAL(createLine()), m_createLineState); m_createMarkState->setToggable(true); m_createMarkState->addTransition(this, SIGNAL(lmbclick()), m_createMarkState); m_createMarkState->addTransition(this, SIGNAL(createTrack()), m_createTrackState); m_createMarkState->addTransition(this, SIGNAL(createMark()), m_noStateState); m_createMarkState->addTransition(this, SIGNAL(createLine()), m_createLineState); m_createLineState->setToggable(true); m_createLineState->addTransition(this, SIGNAL(lmbclick()), m_createPointState); m_createLineState->addTransition(this, SIGNAL(createTrack()), m_createTrackState); m_createLineState->addTransition(this, SIGNAL(createMark()), m_createMarkState); m_createLineState->addTransition(this, SIGNAL(createLine()), m_noStateState); m_createLineState->addTransition(this, SIGNAL(createPoint()), m_createPointState); m_createPointState->setToggable(true); m_createPointState->addTransition(this, SIGNAL(lmbclick()), m_createPointState); m_createPointState->addTransition(this, SIGNAL(createTrack()), m_createTrackState); m_createPointState->addTransition(this, SIGNAL(createMark()), m_createMarkState); m_createPointState->addTransition(this, SIGNAL(createLine()), m_createLineState); m_createPointState->addTransition(this, SIGNAL(createPoint()), m_noStateState); m_noSelectionState->addTransition(this, SIGNAL(selectBoat()), m_boatSelectionState); m_noSelectionState->addTransition(this, SIGNAL(selectMark()), m_markSelectionState); m_noSelectionState->addTransition(this, SIGNAL(selectPoint()), m_pointSelectionState); m_boatSelectionState->addTransition(this, SIGNAL(clearSelection()), m_noSelectionState); m_markSelectionState->addTransition(this, SIGNAL(clearSelection()), m_noSelectionState); m_pointSelectionState->addTransition(this, SIGNAL(clearSelection()), m_noSelectionState); m_stopState->addTransition(this, SIGNAL(play()), m_playState); m_playState->addTransition(this, SIGNAL(stop()), m_stopState); m_playState->addTransition(this, SIGNAL(pause()), m_pauseState); m_playState->addTransition(this, SIGNAL(play()), m_pauseState); m_pauseState->setToggable(true); m_pauseState->addTransition(this, SIGNAL(stop()), m_stopState); m_pauseState->addTransition(this, SIGNAL(pause()), m_playState); m_pauseState->addTransition(this, SIGNAL(play()), m_playState); connect(m_createState, SIGNAL(entered()), this, SLOT(setCreateMouseTransitions())); connect(m_createState, SIGNAL(exited()), this, SLOT(unsetCreateMouseTransitions())); connect(m_noStateState, SIGNAL(entered()), this, SLOT(setNoStateMouseTransitions())); connect(m_noStateState, SIGNAL(exited()), this, SLOT(unsetNoStateMouseTransitions())); } void StateMachine::setCreateMouseTransitions() { m_selectState->addTransition(this, SIGNAL(move()), m_moveState); m_selectState->addTransition(this, SIGNAL(rmbMove()), m_rotateState); m_moveState->addTransition(this, SIGNAL(lmbclick()), m_selectState); m_moveState->addTransition(this, SIGNAL(move()), m_moveState); m_moveState->addTransition(this, SIGNAL(lmbMove()), m_moveState); m_moveState->addTransition(this, SIGNAL(rmbMove()), m_rotateState); m_rotateState->addTransition(this, SIGNAL(move()), m_moveState); m_rotateState->addTransition(this, SIGNAL(rmbMove()), m_rotateState); } void StateMachine::unsetCreateMouseTransitions() { foreach(QAbstractTransition *transition, m_selectState->transitions()) { m_selectState->removeTransition(transition); } foreach(QAbstractTransition *transition, m_moveState->transitions()) { m_moveState->removeTransition(transition); } m_moveState->addTransition(m_noStateState, SIGNAL(entered()), m_selectState); foreach(QAbstractTransition *transition, m_rotateState->transitions()) { m_rotateState->removeTransition(transition); } m_rotateState->addTransition(m_noStateState, SIGNAL(entered()), m_selectState); } void StateMachine::setNoStateMouseTransitions() { m_selectState->addTransition(this, SIGNAL(lmbMove()), m_moveState); m_selectState->addTransition(this, SIGNAL(rmbMove()), m_rotateState); m_moveState->addTransition(this, SIGNAL(lmbclick()), m_selectState); m_moveState->addTransition(this, SIGNAL(lmbMove()), m_moveState); m_rotateState->addTransition(this, SIGNAL(rmbclick()), m_selectState); m_rotateState->addTransition(this, SIGNAL(rmbMove()), m_rotateState); } void StateMachine::unsetNoStateMouseTransitions() { foreach(QAbstractTransition *transition, m_selectState->transitions()) { m_selectState->removeTransition(transition); } foreach(QAbstractTransition *transition, m_moveState->transitions()) { m_moveState->removeTransition(transition); } m_moveState->addTransition(m_createState, SIGNAL(entered()), m_selectState); foreach(QAbstractTransition *transition, m_rotateState->transitions()) { m_rotateState->removeTransition(transition); } m_rotateState->addTransition(m_createState, SIGNAL(entered()), m_selectState); } EnableState *StateMachine::parallelEditionState() { return m_parallelEditionState; } EnableState *StateMachine::editingState() { return m_editingState; } EnableState *StateMachine::mouseState() { return m_mouseState; } EnableState *StateMachine::selectionState() { return m_selectionState; } EnableState *StateMachine::animationState() { return m_animationState; } EnableState *StateMachine::noStateState() { return m_noStateState; } EnableState *StateMachine::createState() { return m_createState; } EnableState *StateMachine::createTrackState() { return m_createTrackState; } EnableState *StateMachine::createBoatState() { return m_createBoatState; } EnableState *StateMachine::createMarkState() { return m_createMarkState; } EnableState *StateMachine::createLineState() { return m_createLineState; } EnableState *StateMachine::createPointState() { return m_createPointState; } EnableState *StateMachine::selectState() { return m_selectState; } EnableState *StateMachine::moveState() { return m_moveState; } EnableState *StateMachine::rotateState() { return m_rotateState; } EnableState *StateMachine::noSelectionState() { return m_noSelectionState; } EnableState *StateMachine::boatSelectionState() { return m_boatSelectionState; } EnableState *StateMachine::markSelectionState() { return m_markSelectionState; } EnableState *StateMachine::pointSelectionState() { return m_pointSelectionState; } EnableState *StateMachine::stopState() { return m_stopState; } EnableState *StateMachine::playState() { return m_playState; } EnableState *StateMachine::pauseState() { return m_pauseState; } boats-201908/statemachine.h000066400000000000000000000113451353032755300155570ustar00rootroot00000000000000// // C++ Interface: statemachine // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2014 Thibaut GRIDEL // // 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 . // // #ifndef STATEMACHINE_H #define STATEMACHINE_H #include #include "enablestate.h" class StateMachine : public QStateMachine { Q_OBJECT Q_PROPERTY(EnableState* parallelEditionState READ parallelEditionState CONSTANT) Q_PROPERTY(EnableState* editingState READ editingState CONSTANT) Q_PROPERTY(EnableState* mouseState READ mouseState CONSTANT) Q_PROPERTY(EnableState* selectionState READ selectionState CONSTANT) Q_PROPERTY(EnableState* animationState READ animationState CONSTANT) Q_PROPERTY(EnableState* noStateState READ noStateState CONSTANT) Q_PROPERTY(EnableState* createState READ createState CONSTANT) Q_PROPERTY(EnableState* createTrackState READ createTrackState CONSTANT) Q_PROPERTY(EnableState* createBoatState READ createBoatState CONSTANT) Q_PROPERTY(EnableState* createMarkState READ createMarkState CONSTANT) Q_PROPERTY(EnableState* createLineState READ createLineState CONSTANT) Q_PROPERTY(EnableState* createPointState READ createPointState CONSTANT) Q_PROPERTY(EnableState* selectState READ selectState CONSTANT) Q_PROPERTY(EnableState* moveState READ moveState CONSTANT) Q_PROPERTY(EnableState* rotateState READ rotateState CONSTANT) Q_PROPERTY(EnableState* noSelectionState READ noSelectionState CONSTANT) Q_PROPERTY(EnableState* boatSelectionState READ boatSelectionState CONSTANT) Q_PROPERTY(EnableState* markSelectionState READ markSelectionState CONSTANT) Q_PROPERTY(EnableState* pointSelectionState READ pointSelectionState CONSTANT) Q_PROPERTY(EnableState* stopState READ stopState CONSTANT) Q_PROPERTY(EnableState* playState READ playState CONSTANT) Q_PROPERTY(EnableState* pauseState READ pauseState CONSTANT) public: explicit StateMachine(QObject *parent = 0); EnableState *parallelEditionState(); EnableState *editingState(); EnableState *mouseState(); EnableState *selectionState(); EnableState *animationState(); EnableState *noStateState(); EnableState *createState(); EnableState *createTrackState(); EnableState *createBoatState(); EnableState *createMarkState(); EnableState *createLineState(); EnableState *createPointState(); EnableState *selectState(); EnableState *moveState(); EnableState *rotateState(); EnableState *noSelectionState(); EnableState *boatSelectionState(); EnableState *markSelectionState(); EnableState *pointSelectionState(); EnableState *stopState(); EnableState *playState(); EnableState *pauseState(); signals: void animate(); Q_INVOKABLE void noState(); void createTrack(); void createBoat(); void createMark(); void createLine(); void createPoint(); Q_INVOKABLE void move(); Q_INVOKABLE void lmbMove(); Q_INVOKABLE void rmbMove(); Q_INVOKABLE void lmbclick(); Q_INVOKABLE void rmbclick(); void selectBoat(); void selectMark(); void selectPoint(); void clearSelection(); void stop(); void play(); void pause(); public slots: void setCreateMouseTransitions(); void unsetCreateMouseTransitions(); void setNoStateMouseTransitions(); void unsetNoStateMouseTransitions(); private: EnableState* m_parallelEditionState; EnableState* m_editingState; EnableState* m_mouseState; EnableState* m_selectionState; EnableState* m_animationState; EnableState* m_noStateState; EnableState* m_createState; EnableState* m_createTrackState; EnableState* m_createBoatState; EnableState* m_createMarkState; EnableState* m_createLineState; EnableState* m_createPointState; EnableState* m_selectState; EnableState* m_moveState; EnableState* m_rotateState; EnableState* m_noSelectionState; EnableState* m_boatSelectionState; EnableState* m_markSelectionState; EnableState* m_pointSelectionState; EnableState* m_stopState; EnableState* m_playState; EnableState* m_pauseState; }; #endif // STATEMACHINE_H boats-201908/trace.cpp000066400000000000000000000027331353032755300145440ustar00rootroot00000000000000// // C++ Implementation: Trace // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2009 Thibaut GRIDEL // // 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 . // // #include #include #include "trace.h" int TraceBlock::indent = 0; TraceBlock::TraceBlock(bool active, QString value, int *duration) : m_active(active), m_value(value.toStdString()), m_duration(duration) { if (m_active) { indent++; std::cout << std::string(indent,' ') << "enter " << m_value << std::endl; m_time.start(); } } TraceBlock::~TraceBlock() { if (m_active) { int duration = m_time.elapsed(); std::cout << std::string(indent,' ') << "exit " << m_value << " " << duration << std::endl; indent--; if (m_duration) { *m_duration += duration; } } } boats-201908/trace.h000066400000000000000000000021611353032755300142040ustar00rootroot00000000000000// // C++ Interface: Trace // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2009 Thibaut GRIDEL // // 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 . // // #ifndef TRACE_H #define TRACE_H #include #include class TraceBlock { public: TraceBlock(bool active, QString value, int *duration = 0); ~TraceBlock(); private: static int indent; bool m_active; std::string m_value; QTime m_time; int *m_duration; }; #endif boats-201908/undocommands.cpp000066400000000000000000001502361353032755300161370ustar00rootroot00000000000000// // C++ Implementation: undocommands // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #include "undocommands.h" #include #include #include "commontypes.h" #include "boats.h" #include "situationmodel.h" #include "trackmodel.h" #include "boatmodel.h" #include "markmodel.h" #include "polylinemodel.h" #include "pointmodel.h" extern int debugLevel; // Set Title SetTitleUndoCommand::SetTitleUndoCommand(SituationModel* situation, QString title, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_oldTitle(situation->title()), m_newTitle(title) { if (debugLevel & 1 << COMMAND) std::cout << "new settitleundocommand" << std::endl; } SetTitleUndoCommand::~SetTitleUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end settitleundocommand" << std::endl; } void SetTitleUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo settitleundocommand"<< std::endl; m_situation->setTitle(m_oldTitle); } void SetTitleUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo settitleundocommand" << std::endl; m_situation->setTitle(m_newTitle); } bool SetTitleUndoCommand::mergeWith(const QUndoCommand *command) { const SetTitleUndoCommand *settitleCommand = static_cast(command); if (m_situation != settitleCommand->m_situation) return false; m_newTitle = settitleCommand->m_newTitle; return true; } // Set Rules SetRulesUndoCommand::SetRulesUndoCommand(SituationModel* situation, QString rules, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_oldRules(situation->rules()), m_newRules(rules) { if (debugLevel & 1 << COMMAND) std::cout << "new setrulesundocommand" << std::endl; } SetRulesUndoCommand::~SetRulesUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end setrulesundocommand" << std::endl; } void SetRulesUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo setrulesundocommand"<< std::endl; m_situation->setRules(m_oldRules); } void SetRulesUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo setrulesundocommand" << std::endl; m_situation->setRules(m_newRules); } bool SetRulesUndoCommand::mergeWith(const QUndoCommand *command) { const SetRulesUndoCommand *setrulesCommand = static_cast(command); if (m_situation != setrulesCommand->m_situation) return false; m_newRules = setrulesCommand->m_newRules; return true; } // Show Layline SetShowLaylineUndoCommand::SetShowLaylineUndoCommand(SituationModel *situation, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation) { if (debugLevel & 1 << COMMAND) std::cout << "new showlaylineundocommand" << std::endl; } SetShowLaylineUndoCommand::~SetShowLaylineUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end showlaylineundocommand" << std::endl; } void SetShowLaylineUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo showlaylineundocommand" << std::endl; m_situation->setShowLayline(!m_situation->showLayline()); } void SetShowLaylineUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo showlaylineundocommand" << std::endl; m_situation->setShowLayline(!m_situation->showLayline()); } bool SetShowLaylineUndoCommand::mergeWith(const QUndoCommand *command) { const SetShowLaylineUndoCommand *zoneCommand = static_cast(command); if (m_situation != zoneCommand->m_situation) return false; undo(); m_situation->undoStack()->setIndex(m_situation->undoStack()->index()-1); return true; } // Set Layline SetLaylineUndoCommand::SetLaylineUndoCommand(SituationModel* situation, const int angle, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_oldAngle(situation->laylineAngle()), m_newAngle(angle) { if (debugLevel & 1 << COMMAND) std::cout << "new setlaylineundocommand" << std::endl; } SetLaylineUndoCommand::~SetLaylineUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end setlaylineundocommand" << std::endl; } void SetLaylineUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo setlaylineundocommand" << std::endl; m_situation->setLaylineAngle(m_oldAngle); } void SetLaylineUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo setlaylineundocommand" << std::endl; m_situation->setLaylineAngle(m_newAngle); } bool SetLaylineUndoCommand::mergeWith(const QUndoCommand *command) { const SetLaylineUndoCommand *setlaylineCommand = static_cast(command); if (m_situation != setlaylineCommand->m_situation) return false; m_newAngle = setlaylineCommand->m_newAngle; return true; } // Set Series SetSituationSeriesUndoCommand::SetSituationSeriesUndoCommand(SituationModel* situation, const int series, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_oldSeries(situation->situationSeries()), m_newSeries(series) { if (debugLevel & 1 << COMMAND) std::cout << "new SetSituationSeriesUndoCommand" << std::endl; } SetSituationSeriesUndoCommand::~SetSituationSeriesUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end SetSituationSeriesUndoCommand" << std::endl; } void SetSituationSeriesUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo SetSituationSeriesUndoCommand" << std::endl; m_situation->setSituationSeries(m_oldSeries); } void SetSituationSeriesUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo SetSituationSeriesUndoCommand" << std::endl; m_situation->setSituationSeries(m_newSeries); } bool SetSituationSeriesUndoCommand::mergeWith(const QUndoCommand *command) { const SetSituationSeriesUndoCommand *setseriesCommand = static_cast(command); if (m_situation != setseriesCommand->m_situation) return false; m_newSeries = setseriesCommand->m_newSeries; return true; } // Set Abstract SetAbstractUndoCommand::SetAbstractUndoCommand(SituationModel* situation, QString abstract, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_oldAbstract(situation->abstract()), m_newAbstract(abstract) { if (debugLevel & 1 << COMMAND) std::cout << "new setabstractundocommand" << std::endl; } SetAbstractUndoCommand::~SetAbstractUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end setabstractundocommand" << std::endl; } void SetAbstractUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo setabstractundocommand"<< std::endl; m_situation->setAbstract(m_oldAbstract); } void SetAbstractUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo setabstractundocommand" << std::endl; m_situation->setAbstract(m_newAbstract); } bool SetAbstractUndoCommand::mergeWith(const QUndoCommand *command) { const SetAbstractUndoCommand *setabstractCommand = static_cast(command); if (m_situation != setabstractCommand->m_situation) return false; m_newAbstract = setabstractCommand->m_newAbstract; return true; } // Set Description SetDescriptionUndoCommand::SetDescriptionUndoCommand(SituationModel* situation, QString description, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_oldDescription(situation->description()), m_newDescription(description) { if (debugLevel & 1 << COMMAND) std::cout << "new setdescriptionundocommand" << std::endl; } SetDescriptionUndoCommand::~SetDescriptionUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end setdescriptionundocommand" << std::endl; } void SetDescriptionUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo setdescriptionundocommand"<< std::endl; m_situation->setDescription(m_oldDescription); } void SetDescriptionUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo setdescriptionundocommand" << std::endl; m_situation->setDescription(m_newDescription); } bool SetDescriptionUndoCommand::mergeWith(const QUndoCommand *command) { const SetDescriptionUndoCommand *setdescriptionCommand = static_cast(command); if (m_situation != setdescriptionCommand->m_situation) return false; m_newDescription = setdescriptionCommand->m_newDescription; return true; } // Add Track AddTrackUndoCommand::AddTrackUndoCommand(SituationModel* situation, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation) { if (debugLevel & 1 << COMMAND) std::cout << "new addtrackundocommand" << std::endl; m_track = new TrackModel(situation); } AddTrackUndoCommand::~AddTrackUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end addtrackundocommand" << std::endl; delete m_track; } void AddTrackUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo addtrackundocommand" << std::endl; m_situation->addTrack(m_track); } void AddTrackUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo addtrackundocommand" << std::endl; m_situation->deleteTrack(m_track); } // Delete Track DeleteTrackUndoCommand::DeleteTrackUndoCommand(SituationModel* situation, TrackModel* track, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_track(track) { if (debugLevel & 1 << COMMAND) std::cout << "new removetrackundocommand" << std::endl; } DeleteTrackUndoCommand::~DeleteTrackUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end removetrackundocommand" << std::endl; } void DeleteTrackUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo removetrackundocommand" << std::endl; m_situation->deleteTrack(m_track); } void DeleteTrackUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo removetrackundocommand" << std::endl; m_situation->addTrack(m_track); } // Set Series SetSeriesUndoCommand::SetSeriesUndoCommand(TrackModel* track, const Boats::Series series, QUndoCommand *parent) : QUndoCommand(parent), m_track(track), m_oldSeries(track->series()), m_newSeries(series) { if (debugLevel & 1 << COMMAND) std::cout << "new setseriesundocommand" << std::endl; } SetSeriesUndoCommand::~SetSeriesUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end setseriesundocommand" << std::endl; } void SetSeriesUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo setseriesundocommand" << std::endl; m_track->setSeries(m_oldSeries); } void SetSeriesUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo setseriesundocommand" << std::endl; m_track->setSeries(m_newSeries); } bool SetSeriesUndoCommand::mergeWith(const QUndoCommand *command) { const SetSeriesUndoCommand *seriesCommand = static_cast(command); if (m_track != seriesCommand->m_track) return false; m_newSeries = seriesCommand->m_newSeries; return true; } // Set Color SetColorUndoCommand::SetColorUndoCommand(TrackModel* track, const QColor color, QUndoCommand *parent) : QUndoCommand(parent), m_track(track), m_oldColor(track->color()), m_newColor(color) { if (debugLevel & 1 << COMMAND) std::cout << "new setcolorundocommand" << std::endl; } SetColorUndoCommand::~SetColorUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end setcolorundocommand" << std::endl; } void SetColorUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo setcolorundocommand" << std::endl; m_track->setColor(m_oldColor); } void SetColorUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo setcolorundocommand" << std::endl; m_track->setColor(m_newColor); } bool SetColorUndoCommand::mergeWith(const QUndoCommand *command) { const SetColorUndoCommand *colorCommand = static_cast(command); if (m_track != colorCommand->m_track) return false; m_newColor = colorCommand->m_newColor; return true; } // Show Path SetShowPathUndoCommand::SetShowPathUndoCommand(TrackModel *track, QUndoCommand *parent) : QUndoCommand(parent), m_track(track) { if (debugLevel & 1 << COMMAND) std::cout << "new showlaylineundocommand" << std::endl; } SetShowPathUndoCommand::~SetShowPathUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end showlaylineundocommand" << std::endl; } void SetShowPathUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo showlaylineundocommand" << std::endl; m_track->setShowPath(!m_track->showPath()); } void SetShowPathUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo showlaylineundocommand" << std::endl; m_track->setShowPath(!m_track->showPath()); } bool SetShowPathUndoCommand::mergeWith(const QUndoCommand *command) { const SetShowPathUndoCommand *showPathCommand = static_cast(command); if (m_track != showPathCommand->m_track) return false; undo(); m_track->situation()->undoStack()->setIndex(m_track->situation()->undoStack()->index()-1); return true; } // Follow Track SetFollowTrackUndoCommand::SetFollowTrackUndoCommand(SituationModel* situation, TrackModel *track, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_track(track) { if (debugLevel & 1 << COMMAND) std::cout << "new followtrackundocommand" << std::endl; foreach (TrackModel *tracks, situation->tracks()) { m_followTrackList.append(tracks->followTrack()); } } SetFollowTrackUndoCommand::~SetFollowTrackUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end followtrackundocommand" << std::endl; } void SetFollowTrackUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo followtrackundocommand" << std::endl; for (int i=0; isize(); ++i) { TrackModel *tracks = m_situation->tracks()[i]; tracks->setFollowTrack(m_followTrackList.at(i)); } } void SetFollowTrackUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo followtrackundocommand" << std::endl; m_track->setFollowTrack(!m_track->followTrack()); } bool SetFollowTrackUndoCommand::mergeWith(const QUndoCommand *command) { Q_UNUSED(command); return false; } // Lookat SetLookAtUndoCommand::SetLookAtUndoCommand(SituationModel* situation, int lookDirection, int tilt, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_oldLookDirection(situation->lookDirection()), m_newLookDirection(lookDirection), m_oldTilt(situation->tilt()), m_newTilt(tilt) { if (debugLevel & 1 << COMMAND) std::cout << "new lookatundocommand" << std::endl; } SetLookAtUndoCommand::~SetLookAtUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end lookatundocommand" << std::endl; } void SetLookAtUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo lookatundocommand" << std::endl; m_situation->setLookDirection(m_oldLookDirection); m_situation->setTilt(m_oldTilt); } void SetLookAtUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo lookatundocommand" << std::endl; m_situation->setLookDirection(m_newLookDirection); m_situation->setTilt(m_newTilt); } bool SetLookAtUndoCommand::mergeWith(const QUndoCommand *command) { const SetLookAtUndoCommand *lookatCommand = static_cast(command); if (m_situation != lookatCommand->m_situation) return false; m_newLookDirection = lookatCommand->m_newLookDirection; m_newTilt = lookatCommand->m_newTilt; return true; } // Show Wind SetShowWindUndoCommand::SetShowWindUndoCommand(WindModel *wind, QUndoCommand *parent) : QUndoCommand(parent), m_wind(wind) { if (debugLevel & 1 << COMMAND) std::cout << "new showwindundocommand" << std::endl; } SetShowWindUndoCommand::~SetShowWindUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end showwindundocommand" << std::endl; } void SetShowWindUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo showwindundocommand" << std::endl; m_wind->setVisible(!m_wind->visible()); } void SetShowWindUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo showwindundocommand" << std::endl; m_wind->setVisible(!m_wind->visible()); } bool SetShowWindUndoCommand::mergeWith(const QUndoCommand *command) { const SetShowWindUndoCommand *visibleCommand = static_cast(command); if (m_wind != visibleCommand->m_wind) return false; undo(); m_wind->situation()->undoStack()->setIndex(m_wind->situation()->undoStack()->index()-1); return true; } // Add Wind AddWindUndoCommand::AddWindUndoCommand(WindModel* wind, qreal heading, QUndoCommand *parent) : QUndoCommand(parent), m_wind(wind), m_heading(heading) { if (debugLevel & 1 << COMMAND) std::cout << "new addwindundocommand" << std::endl; } AddWindUndoCommand::~AddWindUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end addwindundocommand" << std::endl; } void AddWindUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo addwindundocommand" << std::endl; m_wind->addWind(m_heading); } void AddWindUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo addwindundocommand" << std::endl; m_wind->deleteWind(m_wind->size()-1); } // Set Wind SetWindUndoCommand::SetWindUndoCommand(WindModel* wind, int index, qreal direction, QUndoCommand *parent) : QUndoCommand(parent), m_wind(wind), m_index(index), m_oldDirection(wind->windAt(index)), m_newDirection(direction) { if (debugLevel & 1 << COMMAND) std::cout << "new setwindundocommand" << std::endl; } SetWindUndoCommand::~SetWindUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end setwindundocommand" << std::endl; } void SetWindUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo setwindundocommand" << std::endl; m_wind->setWindAt(m_oldDirection, m_index); } void SetWindUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo setwindundocommand" << std::endl; m_wind->setWindAt(m_newDirection, m_index); } bool SetWindUndoCommand::mergeWith(const QUndoCommand *command) { const SetWindUndoCommand *setWindCommand = static_cast(command); if (m_index != setWindCommand->m_index) return false; m_newDirection = setWindCommand->m_newDirection; return true; } // Delete Wind DeleteWindUndoCommand::DeleteWindUndoCommand(WindModel* wind, int index, QUndoCommand *parent) : QUndoCommand(parent), m_wind(wind), m_index(index), m_heading(m_wind->windAt(index)) { if (debugLevel & 1 << COMMAND) std::cout << "new deletewindundocommand" << std::endl; } DeleteWindUndoCommand::~DeleteWindUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end deletewindundocommand" << std::endl; } void DeleteWindUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo deletewindundocommand" << std::endl; m_wind->deleteWind(m_index); } void DeleteWindUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo deletewindundocommand" << std::endl; m_wind->addWind(m_heading, m_index); } // Move Model MoveModelUndoCommand::MoveModelUndoCommand(QList &modelList, const QPointF &deltaPosition, QUndoCommand *parent) : QUndoCommand(parent), m_modelList(modelList), m_deltaPosition(deltaPosition) { if (debugLevel & 1 << COMMAND) std::cout << "new movemodelundocommand" << std::endl; } MoveModelUndoCommand::~MoveModelUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end movemodelundocommand" << std::endl; } void MoveModelUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo movemodelundocommand" << std::endl; for(int i=0; i< m_modelList.size(); i++) { PositionModel *model = m_modelList[i]; model->setPosition(model->position() - m_deltaPosition); } } void MoveModelUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo movemodelundocommand" << std::endl; for(int i=0; i< m_modelList.size(); i++) { PositionModel *model = m_modelList[i]; model->setPosition(model->position() + m_deltaPosition); } } bool MoveModelUndoCommand::mergeWith(const QUndoCommand *command) { const MoveModelUndoCommand *moveCommand = static_cast(command); if (m_modelList != moveCommand->m_modelList) return false; m_deltaPosition += moveCommand->m_deltaPosition; return true; } // Laylines Boat SetLaylinesUndoCommand::SetLaylinesUndoCommand(QList &modelList, bool laylines, QUndoCommand *parent) : QUndoCommand(parent), m_modelList(modelList), m_laylines(laylines) { if (debugLevel & 1 << COMMAND) std::cout << "new laylinesboatundocommand" << std::endl; foreach (const PositionModel *model, modelList) { m_laylinesList << model->laylines(); } } SetLaylinesUndoCommand::~SetLaylinesUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end laylinesboatundocommand" << std::endl; } void SetLaylinesUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo laylinesboatundocommand" << std::endl; for(int i=0; i< m_modelList.size(); i++) { PositionModel *model = m_modelList[i]; model->setLaylines(m_laylinesList[i]); } } void SetLaylinesUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo laylinesboatundocommand" << std::endl; for(int i=0; i< m_modelList.size(); i++) { PositionModel *model = m_modelList[i]; model->setLaylines(m_laylines); } } bool SetLaylinesUndoCommand::mergeWith(const QUndoCommand *command) { const SetLaylinesUndoCommand *laylinesCommand = static_cast(command); if (m_modelList != laylinesCommand->m_modelList) return false; m_laylines = laylinesCommand->m_laylines; return true; } // Add Boat AddBoatUndoCommand::AddBoatUndoCommand(TrackModel* track, QPointF& position, qreal heading, QUndoCommand *parent) : QUndoCommand(parent), m_track(track) { if (debugLevel & 1 << COMMAND) std::cout << "new addboatundocommand" << std::endl; m_boat = new BoatModel(track); m_boat->setPosition(position); m_boat->setHeading(heading); } AddBoatUndoCommand::~AddBoatUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end addboatundocommand" << std::endl; delete m_boat; } void AddBoatUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo addboatundocommand" << std::endl; m_track->addBoat(m_boat); } void AddBoatUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo addboatundocommand" << std::endl; m_track->deleteBoat(m_boat); } // Rotate Models RotateModelsUndoCommand::RotateModelsUndoCommand(QList &modelList, const qreal &angle, QUndoCommand *parent) : QUndoCommand(parent), m_modelList(modelList), m_angle(angle) { if (debugLevel & 1 << COMMAND) std::cout << "new rotatemodelsundocommand" << std::endl; foreach (const PositionModel *model, modelList) { m_headingList << model->heading(); } } RotateModelsUndoCommand::~RotateModelsUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end rotatemodelsundocommand" << std::endl; } void RotateModelsUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo rotatemodelsundocommand" << std::endl; for(int i=0; i< m_modelList.size(); i++) { PositionModel *model = m_modelList[i]; model->setHeading(m_headingList[i]); } } void RotateModelsUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo rotatemodelsundocommand" << std::endl; for(int i=0; i< m_modelList.size(); i++) { PositionModel *model = m_modelList[i]; model->setHeading(model->heading() + m_angle); } } bool RotateModelsUndoCommand::mergeWith(const QUndoCommand *command) { const RotateModelsUndoCommand *headingCommand = static_cast(command); if (m_modelList != headingCommand->m_modelList) return false; m_angle += headingCommand->m_angle; return true; } // Overlap Boat OverlapBoatUndoCommand::OverlapBoatUndoCommand(SituationModel* situation, QList &boatList, Boats::Overlaps overlaps, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_boatList(boatList), m_overlaps(overlaps) { if (debugLevel & 1 << COMMAND) std::cout << "new overlapboatundocommand" << std::endl; } OverlapBoatUndoCommand::~OverlapBoatUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end overlapboatundocommand" << std::endl; } void OverlapBoatUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo overlapboatundocommand" << std::endl; for(int i=0; i< m_boatList.size(); i++) { BoatModel *boat = m_boatList[i]; boat->setOverlap(boat->overlap() ^ m_overlaps); } } void OverlapBoatUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo overlapboatundocommand" << std::endl; for(int i=0; i< m_boatList.size(); i++) { BoatModel *boat = m_boatList[i]; boat->setOverlap(boat->overlap() ^ m_overlaps); } } bool OverlapBoatUndoCommand::mergeWith(const QUndoCommand *command) { const OverlapBoatUndoCommand *overlapCommand = static_cast(command); if (m_boatList != overlapCommand->m_boatList) return false; if ((m_overlaps ^ overlapCommand->m_overlaps) == Boats::none) { undo(); m_situation->undoStack()->setIndex(m_situation->undoStack()->index()-1); } else { m_overlaps = m_overlaps ^ overlapCommand->m_overlaps; } return true; } // Flag Boat FlagBoatUndoCommand::FlagBoatUndoCommand(SituationModel* situation, QList &boatList, Boats::Flag flag, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_boatList(boatList), m_flag(flag) { if (debugLevel & 1 << COMMAND) std::cout << "new flagboatundocommand" << std::endl; foreach (const BoatModel *boat, boatList) { m_flagList << boat->flag(); } } FlagBoatUndoCommand::~FlagBoatUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end flagboatundocommand" << std::endl; } void FlagBoatUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo flagboatundocommand" << std::endl; for(int i=0; i< m_boatList.size(); i++) { BoatModel *boat = m_boatList[i]; boat->setFlag(m_flagList[i]); } } void FlagBoatUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo flagboatundocommand" << std::endl; for(int i=0; i< m_boatList.size(); i++) { BoatModel *boat = m_boatList[i]; boat->setFlag(m_flag); } } bool FlagBoatUndoCommand::mergeWith(const QUndoCommand *command) { const FlagBoatUndoCommand *flagCommand = static_cast(command); if (m_boatList != flagCommand->m_boatList) return false; m_flag = flagCommand->m_flag; return true; } // Trim Boat TrimBoatUndoCommand::TrimBoatUndoCommand(QList &boatList, const qreal &trim, QUndoCommand *parent) : QUndoCommand(parent), m_boatList(boatList), m_trim(trim) { if (debugLevel & 1 << COMMAND) std::cout << "new trimboatundocommand" << std::endl; foreach (const BoatModel *boat, boatList) { m_trimList << boat->trim(); m_jibTrimList << boat->jibTrim(); } } TrimBoatUndoCommand::~TrimBoatUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end trimboatundocommand" << std::endl; } void TrimBoatUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo trimboatundocommand" << std::endl; for(int i=0; i< m_boatList.size(); i++) { BoatModel *model = m_boatList[i]; model->setTrim(m_trimList[i]); model->setJibTrim(m_jibTrimList[i]); } } void TrimBoatUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo trimboatundocommand" << std::endl; for(int i=0; i< m_boatList.size(); i++) { BoatModel *model = m_boatList[i]; model->setTrim(m_trim); model->setJibTrim(m_trim); } } bool TrimBoatUndoCommand::mergeWith(const QUndoCommand *command) { const TrimBoatUndoCommand *trimCommand = static_cast(command); if (m_boatList != trimCommand->m_boatList) return false; m_trim = trimCommand->m_trim; return true; } // Trim Jib TrimJibUndoCommand::TrimJibUndoCommand(QList &boatList, const qreal &trim, QUndoCommand *parent) : QUndoCommand(parent), m_boatList(boatList), m_trim(trim) { if (debugLevel & 1 << COMMAND) std::cout << "new trimjibundocommand" << std::endl; foreach (const BoatModel *boat, boatList) { m_trimList << boat->jibTrim(); } } TrimJibUndoCommand::~TrimJibUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end trimjibundocommand" << std::endl; } void TrimJibUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo trimjibundocommand" << std::endl; for(int i=0; i< m_boatList.size(); i++) { BoatModel *model = m_boatList[i]; model->setJibTrim(m_trimList[i]); } } void TrimJibUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo trimjibundocommand" << std::endl; for(int i=0; i< m_boatList.size(); i++) { BoatModel *model = m_boatList[i]; model->setJibTrim(m_trim); } } bool TrimJibUndoCommand::mergeWith(const QUndoCommand *command) { const TrimJibUndoCommand *trimCommand = static_cast(command); if (m_boatList != trimCommand->m_boatList) return false; m_trim = trimCommand->m_trim; return true; } // Trim Spin TrimSpinUndoCommand::TrimSpinUndoCommand(QList &boatList, const qreal &trim, QUndoCommand *parent) : QUndoCommand(parent), m_boatList(boatList), m_trim(trim) { if (debugLevel & 1 << COMMAND) std::cout << "new trimspinundocommand" << std::endl; foreach (const BoatModel *boat, boatList) { m_trimList << boat->spinTrim(); } } TrimSpinUndoCommand::~TrimSpinUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end trimspinundocommand" << std::endl; } void TrimSpinUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo trimspinundocommand" << std::endl; for(int i=0; i< m_boatList.size(); i++) { BoatModel *model = m_boatList[i]; model->setSpinTrim(m_trimList[i]); } } void TrimSpinUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo trimspinundocommand" << std::endl; for(int i=0; i< m_boatList.size(); i++) { BoatModel *model = m_boatList[i]; model->setSpinTrim(m_trim); } } bool TrimSpinUndoCommand::mergeWith(const QUndoCommand *command) { const TrimSpinUndoCommand *trimCommand = static_cast(command); if (m_boatList != trimCommand->m_boatList) return false; m_trim += trimCommand->m_trim; return true; } // Spin Boat SpinBoatUndoCommand::SpinBoatUndoCommand(SituationModel* situation, QList &boatList, bool spin, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_boatList(boatList), m_spin(spin) { if (debugLevel & 1 << COMMAND) std::cout << "new spinboatundocommand" << std::endl; foreach (const BoatModel *boat, boatList) { m_spinList << boat->spin(); } } SpinBoatUndoCommand::~SpinBoatUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end spinboatundocommand" << std::endl; } void SpinBoatUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo spinboatundocommand" << std::endl; for(int i=0; i< m_boatList.size(); i++) { BoatModel *boat = m_boatList[i]; boat->setSpin(m_spinList[i]); } } void SpinBoatUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo spinboatundocommand" << std::endl; for(int i=0; i< m_boatList.size(); i++) { BoatModel *boat = m_boatList[i]; boat->setSpin(m_spin); } } bool SpinBoatUndoCommand::mergeWith(const QUndoCommand *command) { const SpinBoatUndoCommand *spinCommand = static_cast(command); if (m_boatList != spinCommand->m_boatList) return false; m_spin = spinCommand->m_spin; return true; } // Hidden Boat HiddenBoatUndoCommand::HiddenBoatUndoCommand(SituationModel* situation, QList &boatList, bool hidden, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_boatList(boatList), m_hidden(hidden) { if (debugLevel & 1 << COMMAND) std::cout << "new hiddenboatundocommand" << std::endl; foreach (const BoatModel *boat, boatList) { m_hiddenList << boat->hidden(); } } HiddenBoatUndoCommand::~HiddenBoatUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end hiddenboatundocommand" << std::endl; } void HiddenBoatUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo hiddenboatundocommand" << std::endl; for(int i=0; i< m_boatList.size(); i++) { BoatModel *boat = m_boatList[i]; boat->setHidden(m_hiddenList[i]); } } void HiddenBoatUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo hiddenboatundocommand" << std::endl; for(int i=0; i< m_boatList.size(); i++) { BoatModel *boat = m_boatList[i]; boat->setHidden(m_hidden); } } bool HiddenBoatUndoCommand::mergeWith(const QUndoCommand *command) { const HiddenBoatUndoCommand *hiddenCommand = static_cast(command); if (m_boatList != hiddenCommand->m_boatList) return false; m_hidden = hiddenCommand->m_hidden; return true; } // Accelerate Boat AccelerateBoatUndoCommand::AccelerateBoatUndoCommand(SituationModel* situation, QList &boatList, Boats::Acceleration acceleration, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_boatList(boatList), m_acceleration(acceleration) { if (debugLevel & 1 << COMMAND) std::cout << "new accelerateboatundocommand" << std::endl; foreach (const BoatModel *boat, boatList) { m_accelerationList << boat->acceleration(); } } AccelerateBoatUndoCommand::~AccelerateBoatUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end accelerateboatundocommand" << std::endl; } void AccelerateBoatUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo accelerateboatundocommand" << std::endl; for(int i=0; i< m_boatList.size(); i++) { BoatModel *boat = m_boatList[i]; boat->setAcceleration(m_accelerationList[i]); } } void AccelerateBoatUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo accelerateboatundocommand" << std::endl; for(int i=0; i< m_boatList.size(); i++) { BoatModel *boat = m_boatList[i]; boat->setAcceleration(m_acceleration); } } bool AccelerateBoatUndoCommand::mergeWith(const QUndoCommand *command) { const AccelerateBoatUndoCommand *accelerationCommand = static_cast(command); if (m_boatList != accelerationCommand->m_boatList) return false; m_acceleration = accelerationCommand->m_acceleration; return true; } // Set Text SetTextUndoCommand::SetTextUndoCommand(PositionModel* model, QString text, QUndoCommand *parent) : QUndoCommand(parent), m_model(model), m_oldText(model->text()), m_newText(text) { if (debugLevel & 1 << COMMAND) std::cout << "new settextundocommand" << std::endl; } SetTextUndoCommand::~SetTextUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end settextundocommand" << std::endl; } void SetTextUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo settextundocommand"<< std::endl; m_model->setText(m_oldText); } void SetTextUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo settextundocommand" << std::endl; m_model->setText(m_newText); } bool SetTextUndoCommand::mergeWith(const QUndoCommand *command) { const SetTextUndoCommand *setTextCommand = static_cast(command); if (m_model != setTextCommand->m_model) return false; m_newText = setTextCommand->m_newText; return true; } // Move Text MoveTextUndoCommand::MoveTextUndoCommand(PositionModel *model, const QPointF &deltaPosition, QUndoCommand *parent) : QUndoCommand(parent), m_model(model), m_deltaPosition(deltaPosition) { if (debugLevel & 1 << COMMAND) std::cout << "new movetextundocommand" << std::endl; } MoveTextUndoCommand::~MoveTextUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end movetextundocommand" << std::endl; } void MoveTextUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo movetextundocommand" << std::endl; m_model->setTextPosition(m_model->textPosition() - m_deltaPosition); } void MoveTextUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo movetextundocommand" << std::endl; m_model->setTextPosition(m_model->textPosition() + m_deltaPosition); } bool MoveTextUndoCommand::mergeWith(const QUndoCommand *command) { const MoveTextUndoCommand *moveCommand = static_cast(command); if (m_model != moveCommand->m_model) return false; m_deltaPosition += moveCommand->m_deltaPosition; return true; } // Delete Boat DeleteBoatUndoCommand::DeleteBoatUndoCommand(TrackModel* track, BoatModel* boat, QUndoCommand *parent) : QUndoCommand(parent), m_track(track), m_boat(boat) { if (debugLevel & 1 << COMMAND) std::cout << "new deleteboatundocommand" << std::endl; } DeleteBoatUndoCommand::~DeleteBoatUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end deletebboatundocommand" << std::endl; } void DeleteBoatUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo deletebboatundocommand" << std::endl; m_order = m_track->deleteBoat(m_boat); } void DeleteBoatUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo deletebboatundocommand" << std::endl; m_track->addBoat(m_boat, m_order); } // Add Mark AddMarkUndoCommand::AddMarkUndoCommand(SituationModel* situation, QPointF& position, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation) { if (debugLevel & 1 << COMMAND) std::cout << "new addmarkundocommand" << std::endl; m_mark = new MarkModel(situation); m_mark->setPosition(position); } AddMarkUndoCommand::~AddMarkUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end addmarkundocommand" << std::endl; delete m_mark; } void AddMarkUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo addmarkundocommand" << std::endl; m_situation->addMark(m_mark); } void AddMarkUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo addmarkundocommand" << std::endl; m_situation->deleteMark(m_mark); } // Zone Mark ZoneMarkUndoCommand::ZoneMarkUndoCommand(SituationModel *situation, const QList &markList, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_markList(markList) { if (debugLevel & 1 << COMMAND) std::cout << "new zonemarkundocommand" << std::endl; } ZoneMarkUndoCommand::~ZoneMarkUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end zonemarkundocommand" << std::endl; } void ZoneMarkUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo zonemarkundocommand" << std::endl; for(int i=0; i< m_markList.size(); i++) { MarkModel *mark = m_markList[i]; mark->setZone(!mark->zone()); } } void ZoneMarkUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo zonemarkundocommand" << std::endl; for(int i=0; i< m_markList.size(); i++) { MarkModel *mark = m_markList[i]; mark->setZone(!mark->zone()); } } bool ZoneMarkUndoCommand::mergeWith(const QUndoCommand *command) { const ZoneMarkUndoCommand *zoneCommand = static_cast(command); if (m_markList != zoneCommand->m_markList) return false; undo(); m_situation->undoStack()->setIndex(m_situation->undoStack()->index()-1); return true; } // Color Mark ColorMarkUndoCommand::ColorMarkUndoCommand(SituationModel *situation, const QList &markList, const QColor &color, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_markList(markList), m_newColor(color) { if (debugLevel & 1 << COMMAND) std::cout << "new colormarkundocommand" << std::endl; foreach( MarkModel *mark, markList) { m_oldColors.append(mark->color()); } } ColorMarkUndoCommand::~ColorMarkUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end colormarkundocommand" << std::endl; } void ColorMarkUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo colormarkundocommand" << std::endl; for(int i=0; i< m_markList.size(); i++) { MarkModel *mark = m_markList[i]; mark->setColor(m_oldColors[i]); } } void ColorMarkUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo colormarkundocommand" << std::endl; for(int i=0; i< m_markList.size(); i++) { MarkModel *mark = m_markList[i]; mark->setColor(m_newColor); } } bool ColorMarkUndoCommand::mergeWith(const QUndoCommand *command) { const ColorMarkUndoCommand *colorCommand = static_cast(command); if (m_markList != colorCommand->m_markList) return false; m_newColor = colorCommand->m_newColor; return true; } // Set Length LengthMarkUndoCommand::LengthMarkUndoCommand(SituationModel* situation, const int length, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_oldLength(situation->situationLength()), m_newLength(length) { if (debugLevel & 1 << COMMAND) std::cout << "new lengthmarkundocommand" << std::endl; } LengthMarkUndoCommand::~LengthMarkUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end lengthmarkundocommand" << std::endl; } void LengthMarkUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo lengthmarkundocommand" << std::endl; m_situation->setSituationLength(m_oldLength); } void LengthMarkUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo lengthmarkundocommand" << std::endl; m_situation->setSituationLength(m_newLength); } bool LengthMarkUndoCommand::mergeWith(const QUndoCommand *command) { const LengthMarkUndoCommand *lengthmarkCommand = static_cast(command); if (m_situation != lengthmarkCommand->m_situation) return false; m_newLength = lengthmarkCommand->m_newLength; return true; } // Toggle Mark Side ToggleMarkSideUndoCommand::ToggleMarkSideUndoCommand(const QList &markList, QUndoCommand *parent) : QUndoCommand(parent), m_markList(markList) { if (debugLevel & 1 << COMMAND) std::cout << "new togglemarksideundocommand" << std::endl; } ToggleMarkSideUndoCommand::~ToggleMarkSideUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end togglemarksideundocommand" << std::endl; } void ToggleMarkSideUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo togglemarksideundocommand" << std::endl; for(int i=0; i< m_markList.size(); i++) { MarkModel *mark = m_markList[i]; mark->setLeaveToPort(!mark->leaveToPort()); } } void ToggleMarkSideUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo togglemarksideundocommand" << std::endl; for(int i=0; i< m_markList.size(); i++) { MarkModel *mark = m_markList[i]; mark->setLeaveToPort(!mark->leaveToPort()); } } // Toggle Mark Arrow ToggleMarkArrowUndoCommand::ToggleMarkArrowUndoCommand(const QList &markList, QUndoCommand *parent) : QUndoCommand(parent), m_markList(markList) { if (debugLevel & 1 << COMMAND) std::cout << "new togglemarkarrowundocommand" << std::endl; } ToggleMarkArrowUndoCommand::~ToggleMarkArrowUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end togglemarkarrowundocommand" << std::endl; } void ToggleMarkArrowUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo togglemarkarrowundocommand" << std::endl; for(int i=0; i< m_markList.size(); i++) { MarkModel *mark = m_markList[i]; mark->setArrowVisible(!mark->arrowVisible()); } } void ToggleMarkArrowUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo togglemarkarrowundocommand" << std::endl; for(int i=0; i< m_markList.size(); i++) { MarkModel *mark = m_markList[i]; mark->setArrowVisible(!mark->arrowVisible()); } } // Toggle Mark Label ToggleMarkLabelUndoCommand::ToggleMarkLabelUndoCommand(const QList &markList, QUndoCommand *parent) : QUndoCommand(parent), m_markList(markList) { if (debugLevel & 1 << COMMAND) std::cout << "new togglemarklabelundocommand" << std::endl; } ToggleMarkLabelUndoCommand::~ToggleMarkLabelUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end togglemarklabelundocommand" << std::endl; } void ToggleMarkLabelUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo togglemarklabelundocommand" << std::endl; for(int i=0; i< m_markList.size(); i++) { MarkModel *mark = m_markList[i]; mark->setLabelVisible(!mark->labelVisible()); } } void ToggleMarkLabelUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo togglemarklabelundocommand" << std::endl; for(int i=0; i< m_markList.size(); i++) { MarkModel *mark = m_markList[i]; mark->setLabelVisible(!mark->labelVisible()); } } // Set Mark Text SetMarkLabelUndoCommand::SetMarkLabelUndoCommand(MarkModel* mark, QString text, QUndoCommand *parent) : QUndoCommand(parent), m_mark(mark), m_oldText(mark->labelText()), m_newText(text) { if (debugLevel & 1 << COMMAND) std::cout << "new setmarklabelundocommand" << std::endl; } SetMarkLabelUndoCommand::~SetMarkLabelUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end setmarklabelundocommand" << std::endl; } void SetMarkLabelUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo setmarklabelundocommand"<< std::endl; m_mark->setLabelText(m_oldText); } void SetMarkLabelUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo setmarklabelundocommand" << std::endl; m_mark->setLabelText(m_newText); } bool SetMarkLabelUndoCommand::mergeWith(const QUndoCommand *command) { const SetMarkLabelUndoCommand *setMarkLabelCommand = static_cast(command); if (m_mark != setMarkLabelCommand->m_mark) return false; m_newText = setMarkLabelCommand->m_newText; return true; } // Delete Mark DeleteMarkUndoCommand::DeleteMarkUndoCommand(SituationModel* situation, MarkModel* mark, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_mark(mark) { if (debugLevel & 1 << COMMAND) std::cout << "new deletemarkundocommand" << std::endl; } DeleteMarkUndoCommand::~DeleteMarkUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end deletebmarkundocommand" << std::endl; } void DeleteMarkUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo deletebmarkundocommand" << std::endl; m_order = m_situation->deleteMark(m_mark); } void DeleteMarkUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo deletebmarkundocommand" << std::endl; m_situation->addMark(m_mark, m_order); } // Add PolyLine AddPolyLineUndoCommand::AddPolyLineUndoCommand(SituationModel* situation, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation) { if (debugLevel & 1 << COMMAND) std::cout << "new addpolylineundocommand" << std::endl; m_polyLine = new PolyLineModel(situation); } AddPolyLineUndoCommand::~AddPolyLineUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end addpolylineundocommand" << std::endl; delete m_polyLine; } void AddPolyLineUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo addpolylineundocommand" << std::endl; m_situation->addPolyLine(m_polyLine); } void AddPolyLineUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo addpolylineundocommand" << std::endl; m_situation->deletePolyLine(m_polyLine); } // Delete PolyLine DeletePolyLineUndoCommand::DeletePolyLineUndoCommand(SituationModel* situation, PolyLineModel* polyLine, QUndoCommand *parent) : QUndoCommand(parent), m_situation(situation), m_polyLine(polyLine) { if (debugLevel & 1 << COMMAND) std::cout << "new removepolylineundocommand" << std::endl; } DeletePolyLineUndoCommand::~DeletePolyLineUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end removepolylineundocommand" << std::endl; } void DeletePolyLineUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo removepolylineundocommand" << std::endl; m_situation->deletePolyLine(m_polyLine); } void DeletePolyLineUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo removepolylineundocommand" << std::endl; m_situation->addPolyLine(m_polyLine); } // Add Point AddPointUndoCommand::AddPointUndoCommand(PolyLineModel* polyLine, QPointF& position, QUndoCommand *parent) : QUndoCommand(parent), m_polyLine(polyLine) { if (debugLevel & 1 << COMMAND) std::cout << "new addpointundocommand" << std::endl; m_point = new PointModel(polyLine); m_point->setPosition(position); } AddPointUndoCommand::~AddPointUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end addpointundocommand" << std::endl; delete m_point; } void AddPointUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo addpointundocommand" << std::endl; m_polyLine->addPoint(m_point); } void AddPointUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo addpointundocommand" << std::endl; m_polyLine->deletePoint(m_point); } // Delete Point DeletePointUndoCommand::DeletePointUndoCommand(PolyLineModel* polyLine, PointModel* point, QUndoCommand *parent) : QUndoCommand(parent), m_polyLine(polyLine), m_point(point) { if (debugLevel & 1 << COMMAND) std::cout << "new deletepointundocommand" << std::endl; } DeletePointUndoCommand::~DeletePointUndoCommand() { if (debugLevel & 1 << COMMAND) std::cout << "end deletepointundocommand" << std::endl; } void DeletePointUndoCommand::redo() { if (debugLevel & 1 << COMMAND) std::cout << "redo deletepointundocommand" << std::endl; m_order = m_polyLine->deletePoint(m_point); } void DeletePointUndoCommand::undo() { if (debugLevel & 1 << COMMAND) std::cout << "undo deletepointundocommand" << std::endl; m_polyLine->addPoint(m_point, m_order); } boats-201908/undocommands.h000066400000000000000000000510371353032755300156030ustar00rootroot00000000000000// // C++ Interface: undocommands // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #ifndef UNDOCOMMANDS_H #define UNDOCOMMANDS_H #include "boats.h" #include #include #include class SituationModel; class TrackModel; class PositionModel; class BoatModel; class MarkModel; class PolyLineModel; class PointModel; class WindModel; enum { SET_TITLE, SET_RULES, SET_SHOWLAYLINE, SET_LAYLINE, SET_SITUATIONSERIES, SET_ABSTRACT, SET_DESCRIPTION, SET_SERIES, SET_COLOR, SET_SHOWPATH, SET_FOLLOWTRACK, SET_LOOKAT, SET_SHOWWIND, SET_WIND, MOVE_MODEL, SET_LAYLINES, ROTATE_BOATS, OVERLAP_BOAT, FLAG_BOAT, TRIM_BOAT, TRIM_JIB, TRIM_SPIN, SPIN_BOAT, HIDE_BOAT, ACCELERATE_BOAT, SET_TEXT, MOVE_TEXT, ZONE_MARK, COLOR_MARK, LENGTH_MARK, LABEL_MARK }; class SetTitleUndoCommand : public QUndoCommand { public: SetTitleUndoCommand(SituationModel* situation, QString title, QUndoCommand *parent = 0); ~SetTitleUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return SET_TITLE; } private: SituationModel *m_situation; QString m_oldTitle; QString m_newTitle; }; class SetRulesUndoCommand : public QUndoCommand { public: SetRulesUndoCommand(SituationModel* situation, QString rules, QUndoCommand *parent = 0); ~SetRulesUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return SET_RULES; } private: SituationModel *m_situation; QString m_oldRules; QString m_newRules; }; class SetShowLaylineUndoCommand : public QUndoCommand { public: SetShowLaylineUndoCommand(SituationModel* situation, QUndoCommand *parent = 0); ~SetShowLaylineUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return SET_SHOWLAYLINE; } private: SituationModel *m_situation; }; class SetLaylineUndoCommand : public QUndoCommand { public: SetLaylineUndoCommand(SituationModel* situation, const int angle, QUndoCommand *parent = 0); ~SetLaylineUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return SET_LAYLINE; } private: SituationModel *m_situation; int m_oldAngle; int m_newAngle; }; class SetSituationSeriesUndoCommand : public QUndoCommand { public: SetSituationSeriesUndoCommand(SituationModel* situation, const int series, QUndoCommand *parent = 0); ~SetSituationSeriesUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return SET_SITUATIONSERIES; } private: SituationModel *m_situation; int m_oldSeries; int m_newSeries; }; class SetAbstractUndoCommand : public QUndoCommand { public: SetAbstractUndoCommand(SituationModel* situation, QString abstract, QUndoCommand *parent = 0); ~SetAbstractUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return SET_ABSTRACT; } private: SituationModel *m_situation; QString m_oldAbstract; QString m_newAbstract; }; class SetDescriptionUndoCommand : public QUndoCommand { public: SetDescriptionUndoCommand(SituationModel* situation, QString description, QUndoCommand *parent = 0); ~SetDescriptionUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return SET_DESCRIPTION; } private: SituationModel *m_situation; QString m_oldDescription; QString m_newDescription; }; class AddTrackUndoCommand : public QUndoCommand { public: AddTrackUndoCommand(SituationModel* situation, QUndoCommand *parent = 0); ~AddTrackUndoCommand(); void undo(); void redo(); TrackModel *track() {return m_track; } private: SituationModel *m_situation; TrackModel *m_track; }; class DeleteTrackUndoCommand : public QUndoCommand { public: DeleteTrackUndoCommand(SituationModel* situation, TrackModel* track, QUndoCommand *parent = 0); ~DeleteTrackUndoCommand(); void undo(); void redo(); private: SituationModel *m_situation; TrackModel *m_track; }; class SetSeriesUndoCommand : public QUndoCommand { public: SetSeriesUndoCommand(TrackModel* track, const Boats::Series series, QUndoCommand *parent = 0); ~SetSeriesUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return SET_SERIES; } private: TrackModel *m_track; Boats::Series m_oldSeries; Boats::Series m_newSeries; }; class SetColorUndoCommand : public QUndoCommand { public: SetColorUndoCommand(TrackModel* track, const QColor color, QUndoCommand *parent = 0); ~SetColorUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return SET_COLOR; } private: TrackModel *m_track; QColor m_oldColor; QColor m_newColor; }; class SetShowPathUndoCommand : public QUndoCommand { public: SetShowPathUndoCommand(TrackModel* track, QUndoCommand *parent = 0); ~SetShowPathUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return SET_SHOWPATH; } private: TrackModel *m_track; }; class SetFollowTrackUndoCommand : public QUndoCommand { public: SetFollowTrackUndoCommand(SituationModel* situation, TrackModel* track, QUndoCommand *parent = 0); ~SetFollowTrackUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return SET_FOLLOWTRACK; } private: SituationModel *m_situation; TrackModel *m_track; QList m_followTrackList; }; class SetLookAtUndoCommand : public QUndoCommand { public: SetLookAtUndoCommand(SituationModel* situation, int lookDirection, int tilt, QUndoCommand *parent = 0); ~SetLookAtUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return SET_LOOKAT; } private: SituationModel *m_situation; int m_oldLookDirection; int m_newLookDirection; int m_oldTilt; int m_newTilt; }; class SetShowWindUndoCommand : public QUndoCommand { public: SetShowWindUndoCommand(WindModel* wind, QUndoCommand *parent = 0); ~SetShowWindUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return SET_SHOWWIND; } private: WindModel *m_wind; }; class AddWindUndoCommand : public QUndoCommand { public: AddWindUndoCommand(WindModel* wind, qreal heading, QUndoCommand *parent = 0); ~AddWindUndoCommand(); void undo(); void redo(); private: WindModel *m_wind; qreal m_heading; }; class SetWindUndoCommand : public QUndoCommand { public: SetWindUndoCommand(WindModel* wind, int index, qreal direction, QUndoCommand *parent = 0); ~SetWindUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return SET_WIND; } private: WindModel *m_wind; int m_index; qreal m_oldDirection; qreal m_newDirection; }; class DeleteWindUndoCommand : public QUndoCommand { public: DeleteWindUndoCommand(WindModel* wind, int index, QUndoCommand *parent = 0); ~DeleteWindUndoCommand(); void undo(); void redo(); private: WindModel *m_wind; int m_index; qreal m_heading; }; class MoveModelUndoCommand : public QUndoCommand { public: MoveModelUndoCommand(QList &modelList, const QPointF &deltaPosition, QUndoCommand *parent = 0); ~MoveModelUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return MOVE_MODEL; } private: QList m_modelList; QPointF m_deltaPosition; }; class SetLaylinesUndoCommand : public QUndoCommand { public: SetLaylinesUndoCommand(QList &modelList, bool laylines, QUndoCommand *parent = 0); ~SetLaylinesUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return SET_LAYLINES; } private: QList m_modelList; QList m_laylinesList; bool m_laylines; }; class AddBoatUndoCommand : public QUndoCommand { public: AddBoatUndoCommand(TrackModel* track, QPointF& position, qreal heading, QUndoCommand *parent = 0); ~AddBoatUndoCommand(); void undo(); void redo(); BoatModel *boat() {return m_boat; } private: TrackModel *m_track; BoatModel *m_boat; }; class RotateModelsUndoCommand : public QUndoCommand { public: RotateModelsUndoCommand(QList &modelList, const qreal &angle, QUndoCommand *parent = 0); ~RotateModelsUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return ROTATE_BOATS; } private: QList m_modelList; QList m_headingList; qreal m_angle; }; class OverlapBoatUndoCommand : public QUndoCommand { public: OverlapBoatUndoCommand(SituationModel* situation, QList &boatList, Boats::Overlaps overlaps, QUndoCommand *parent = 0); ~OverlapBoatUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return OVERLAP_BOAT; } private: SituationModel *m_situation; QList m_boatList; Boats::Overlaps m_overlaps; }; class FlagBoatUndoCommand : public QUndoCommand { public: FlagBoatUndoCommand(SituationModel* situation, QList &boatList, Boats::Flag flag, QUndoCommand *parent = 0); ~FlagBoatUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return FLAG_BOAT; } private: SituationModel *m_situation; QList m_boatList; QList m_flagList; Boats::Flag m_flag; }; class TrimBoatUndoCommand : public QUndoCommand { public: TrimBoatUndoCommand(QList &boatList, const qreal &trim, QUndoCommand *parent = 0); ~TrimBoatUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return TRIM_BOAT; } private: QList m_boatList; QList m_trimList; QList m_jibTrimList; qreal m_trim; }; class TrimJibUndoCommand : public QUndoCommand { public: TrimJibUndoCommand(QList &boatList, const qreal &trim, QUndoCommand *parent = 0); ~TrimJibUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return TRIM_JIB; } private: QList m_boatList; QList m_trimList; qreal m_trim; }; class TrimSpinUndoCommand : public QUndoCommand { public: TrimSpinUndoCommand(QList &boatList, const qreal &trim, QUndoCommand *parent = 0); ~TrimSpinUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return TRIM_SPIN; } private: QList m_boatList; QList m_trimList; qreal m_trim; }; class SpinBoatUndoCommand : public QUndoCommand { public: SpinBoatUndoCommand(SituationModel* situation, QList &boatList, bool spin, QUndoCommand *parent = 0); ~SpinBoatUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return SPIN_BOAT; } private: SituationModel *m_situation; QList m_boatList; QList m_spinList; bool m_spin; }; class HiddenBoatUndoCommand : public QUndoCommand { public: HiddenBoatUndoCommand(SituationModel* situation, QList &boatList, bool hidden, QUndoCommand *parent = 0); ~HiddenBoatUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return HIDE_BOAT; } private: SituationModel *m_situation; QList m_boatList; QList m_hiddenList; bool m_hidden; }; class AccelerateBoatUndoCommand : public QUndoCommand { public: AccelerateBoatUndoCommand(SituationModel* situation, QList &boatList, Boats::Acceleration acceleration, QUndoCommand *parent = 0); ~AccelerateBoatUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return ACCELERATE_BOAT; } private: SituationModel *m_situation; QList m_boatList; QList m_accelerationList; Boats::Acceleration m_acceleration; }; class SetTextUndoCommand : public QUndoCommand { public: SetTextUndoCommand(PositionModel *model, QString text, QUndoCommand *parent = 0); ~SetTextUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return SET_TEXT; } private: PositionModel *m_model; QString m_oldText; QString m_newText; }; class MoveTextUndoCommand : public QUndoCommand { public: MoveTextUndoCommand(PositionModel *model, const QPointF &deltaPosition, QUndoCommand *parent = 0); ~MoveTextUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return MOVE_TEXT; } private: PositionModel *m_model; QPointF m_deltaPosition; }; class DeleteBoatUndoCommand : public QUndoCommand { public: DeleteBoatUndoCommand(TrackModel* track, BoatModel* boat, QUndoCommand *parent = 0); ~DeleteBoatUndoCommand(); void undo(); void redo(); private: TrackModel *m_track; BoatModel *m_boat; int m_order; }; class AddMarkUndoCommand : public QUndoCommand { public: AddMarkUndoCommand(SituationModel* situation, QPointF& position, QUndoCommand *parent = 0); ~AddMarkUndoCommand(); void undo(); void redo(); MarkModel *mark() {return m_mark; } private: SituationModel *m_situation; MarkModel *m_mark; }; class ZoneMarkUndoCommand : public QUndoCommand { public: ZoneMarkUndoCommand(SituationModel* situation, const QList &markList, QUndoCommand *parent = 0); ~ZoneMarkUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return ZONE_MARK; } private: SituationModel *m_situation; QList m_markList; }; class ColorMarkUndoCommand : public QUndoCommand { public: ColorMarkUndoCommand(SituationModel* situation, const QList &markList, const QColor &color, QUndoCommand *parent = 0); ~ColorMarkUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return COLOR_MARK; } private: SituationModel *m_situation; QList m_markList; QList m_oldColors; QColor m_newColor; }; class LengthMarkUndoCommand : public QUndoCommand { public: LengthMarkUndoCommand(SituationModel* situation, const int length, QUndoCommand *parent = 0); ~LengthMarkUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return LENGTH_MARK; } private: SituationModel *m_situation; int m_oldLength; int m_newLength; }; class ToggleMarkSideUndoCommand : public QUndoCommand { public: ToggleMarkSideUndoCommand(const QList &markList, QUndoCommand *parent = 0); ~ToggleMarkSideUndoCommand(); void undo(); void redo(); private: QList m_markList; }; class ToggleMarkArrowUndoCommand : public QUndoCommand { public: ToggleMarkArrowUndoCommand(const QList &markList, QUndoCommand *parent = 0); ~ToggleMarkArrowUndoCommand(); void undo(); void redo(); private: QList m_markList; }; class ToggleMarkLabelUndoCommand : public QUndoCommand { public: ToggleMarkLabelUndoCommand(const QList &markList, QUndoCommand *parent = 0); ~ToggleMarkLabelUndoCommand(); void undo(); void redo(); private: QList m_markList; }; class SetMarkLabelUndoCommand : public QUndoCommand { public: SetMarkLabelUndoCommand(MarkModel* mark, QString text, QUndoCommand *parent = 0); ~SetMarkLabelUndoCommand(); void undo(); void redo(); bool mergeWith(const QUndoCommand *command); int id() const { return LABEL_MARK; } private: MarkModel* m_mark; QString m_oldText; QString m_newText; }; class DeleteMarkUndoCommand : public QUndoCommand { public: DeleteMarkUndoCommand(SituationModel* situation, MarkModel* mark, QUndoCommand *parent = 0); ~DeleteMarkUndoCommand(); void undo(); void redo(); private: SituationModel *m_situation; MarkModel *m_mark; int m_order; }; class AddPolyLineUndoCommand : public QUndoCommand { public: AddPolyLineUndoCommand(SituationModel* situation, QUndoCommand *parent = 0); ~AddPolyLineUndoCommand(); void undo(); void redo(); PolyLineModel *polyLine() {return m_polyLine; } private: SituationModel *m_situation; PolyLineModel *m_polyLine; }; class DeletePolyLineUndoCommand : public QUndoCommand { public: DeletePolyLineUndoCommand(SituationModel* situation, PolyLineModel* polyLine, QUndoCommand *parent = 0); ~DeletePolyLineUndoCommand(); void undo(); void redo(); private: SituationModel *m_situation; PolyLineModel *m_polyLine; }; class AddPointUndoCommand : public QUndoCommand { public: AddPointUndoCommand(PolyLineModel* polyLine, QPointF& position, QUndoCommand *parent = 0); ~AddPointUndoCommand(); void undo(); void redo(); PointModel *point() {return m_point; } private: PolyLineModel *m_polyLine; PointModel *m_point; }; class DeletePointUndoCommand : public QUndoCommand { public: DeletePointUndoCommand(PolyLineModel* polyLine, PointModel* point, QUndoCommand *parent = 0); ~DeletePointUndoCommand(); void undo(); void redo(); private: PolyLineModel *m_polyLine; PointModel *m_point; int m_order; }; #endif boats-201908/xmlsituationreader.cpp000066400000000000000000000331551353032755300173730ustar00rootroot00000000000000// // C++ Implementation: XmlSituationReader // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #include #include "xmlsituationreader.h" #include "commontypes.h" #include "situationmodel.h" #include "trackmodel.h" #include "boatmodel.h" #include "markmodel.h" #include "polylinemodel.h" #include "pointmodel.h" #include "windmodel.h" #include "undocommands.h" XmlSituationReader::XmlSituationReader(SituationModel *situation) : m_situation(situation) { } XmlSituationReader::~XmlSituationReader() {} bool XmlSituationReader::read(QIODevice *device) { setDevice(device); while (!atEnd()) { readNext(); if (isStartElement()) { if (name() == "xmlscenario" && attributes().value("version") == "1.0") readSituation(); else raiseError(QObject::tr("The file is not an xmlscenario version 1.0 file.")); } } return !error(); } QString XmlSituationReader::readUnknownElement() { QString elem; elem.append("<").append(name()).append(">"); while (!atEnd()) { readNext(); if (isCharacters()) elem.append(text()); else if (isStartElement()) elem.append(readUnknownElement()); else if (isEndElement()) { elem.append(""); break; } } std::cout << "unread data " << elem.toStdString() << std::endl; return elem; } void XmlSituationReader::readSituation() { while (!atEnd()) { readNext(); if (isEndElement()) break; if (isStartElement()) { if (name() == "title") { m_situation->changeTitle(readElementText()); } else if (name() == "rules") { m_situation->changeRules(readElementText()); } else if (name() == "abstract") { m_situation->changeAbstract(readElementText()); } else if (name() == "description") { m_situation->changeDescription(readElementText()); } else if (name() == "series") { m_situation->changeSeries(series(readElementText())); } else if (name() == "showlayline") { if (readElementText() == "0") { m_situation->toggleShowLayline(false); } } else if (name() == "layline") { m_situation->changeLaylineAngle(readElementText().toInt()); } else if (name() == "length") { m_situation->setSituationLength(readElementText().toInt()); } else if (name() == "look_direction") { m_situation->setLookDirection(readElementText().toFloat()); } else if (name() == "tilt") { m_situation->setTilt(readElementText().toFloat()); } else if (name() == "mark") { readMark(m_situation); } else if (name() == "track") { readTrack(m_situation); } else if (name() == "polyline") { readPolyLine(m_situation); } else if (name() == "wind") { readWind(m_situation); } else { m_situation->appendDiscardedXml(readUnknownElement()); } } } } void XmlSituationReader::readTrack(SituationModel *situation) { AddTrackUndoCommand *command = new AddTrackUndoCommand(situation); situation->undoStack()->push(command); TrackModel *track = command->track(); while (!atEnd()) { readNext(); if (isEndElement()) break; if (isStartElement()) { if (name() == "color") track->setColor(QColor(readElementText())); else if (name() == "series") track->setSeries(series(readElementText())); else if (name() == "path") track->setShowPath(readElementText() == "1"); else if (name() == "follow_track") track->setFollowTrack(readElementText() == "1"); else if (name() == "boat") readBoat(situation, track); else track->appendDiscardedXml(readUnknownElement()); } } } void XmlSituationReader::readBoat(SituationModel *situation, TrackModel *track) { QPointF pos; qreal heading = 0; qreal trim = 0; qreal jibTrim = 0; bool spin = false; qreal spinTrim = 0; Boats::Overlaps overlap = Boats::none; Boats::Flag flag = Boats::noFlag; bool hidden = false; Boats::Acceleration acceleration = Boats::constant; QPointF textPos(10,10); QString text; bool laylines = 0; QStringList discarded; while (!atEnd()) { readNext(); if (isEndElement()) break; if (isStartElement()) { if (name() == "x") pos.setX(readElementText().toFloat()); else if (name() == "y") pos.setY(readElementText().toFloat()); else if (name() == "heading") heading = readElementText().toFloat(); else if (name() == "trim") trim = readElementText().toFloat(); else if (name() == "jibtrim") jibTrim = readElementText().toFloat(); else if (name() == "spin") spin = (readElementText() == "1"); else if (name() == "spintrim") spinTrim = readElementText().toFloat(); else if (name() == "overlap") { overlap = (Boats::Overlaps)FLAG_VALUE(Boats, Overlap, readElementText().toStdString().c_str()); } else if (name() == "flag") { flag = (Boats::Flag)ENUM_VALUE(Boats, Flag, readElementText().toStdString().c_str()); } else if (name() == "hidden") { hidden = (readElementText() == "1"); } else if (name() == "acceleration") { acceleration = (Boats::Acceleration)ENUM_VALUE(Boats, Acceleration, readElementText().toStdString().c_str()); } else if (name() == "bubble_x") textPos.setX(readElementText().toFloat()); else if (name() == "bubble_y") textPos.setY(readElementText().toFloat()); else if (name() == "bubble_text") text = readElementText(); else if (name() == "laylines") laylines = (readElementText() == "1"); else discarded.append(readUnknownElement()); } } BoatModel *boat; if (track->size() == 0) { boat = new BoatModel(track); boat->setPosition(pos); boat->setHeading(heading); track->addBoat(boat); } else { AddBoatUndoCommand *command = new AddBoatUndoCommand(track, pos, heading); situation->undoStack()->push(command); boat = command->boat(); } boat->setTrim(trim); boat->setJibTrim(jibTrim); boat->setSpin(spin); boat->setSpinTrim(spinTrim); boat->setOverlap(overlap); boat->setFlag(flag); boat->setHidden(hidden); boat->setAcceleration(acceleration); boat->setTextPosition(textPos); boat->setText(text); boat->setLaylines(laylines); foreach (const QString elem, discarded) { boat->appendDiscardedXml(elem); } } void XmlSituationReader::readMark(SituationModel *situation) { QPointF pos; QColor color; bool zone = false; int length = 0; QPointF textPos(10,10); QString text; bool laylines = false; qreal heading = 0; bool arrowVisible = false; bool leaveToPort = true; bool labelVisible = true; QString labelText; QStringList discarded; while (!atEnd()) { readNext(); if (isEndElement()) break; if (isStartElement()) { if (name() == "x") pos.setX(readElementText().toFloat()); else if (name() == "y") pos.setY(readElementText().toFloat()); else if (name() == "color") color.setNamedColor(readElementText()); else if (name() == "zone") zone = (readElementText() == "1"); else if (name() == "length") length = (readElementText().toInt()); else if (name() == "bubble_x") textPos.setX(readElementText().toFloat()); else if (name() == "bubble_y") textPos.setY(readElementText().toFloat()); else if (name() == "bubble_text") text = readElementText(); else if (name() == "laylines") laylines = (readElementText() == "1"); else if (name() == "heading") heading = (readElementText().toFloat()); else if (name() == "arrowVisible") arrowVisible = (readElementText() == "1"); else if (name() == "leaveToPort") leaveToPort = (readElementText() == "1"); else if (name() == "labelVisible") labelVisible = (readElementText() == "1"); else if (name() == "labelText") labelText = (readElementText()); else discarded.append(readUnknownElement()); } } AddMarkUndoCommand *command = new AddMarkUndoCommand(situation, pos); situation->undoStack()->push(command); MarkModel *mark = command->mark(); mark->setColor(color); mark->setZone(zone); if (length != 0) { mark->setLength(length); } mark->setTextPosition(textPos); mark->setText(text); mark->setLaylines(laylines); mark->setHeading(heading); mark->setArrowVisible(arrowVisible); mark->setLeaveToPort(leaveToPort); mark->setLabelVisible(labelVisible); mark->setLabelText(labelText); foreach (const QString elem, discarded) { mark->appendDiscardedXml(elem); } } void XmlSituationReader::readPolyLine(SituationModel *situation) { QStringList discarded; AddPolyLineUndoCommand *command = new AddPolyLineUndoCommand(situation); situation->undoStack()->push(command); PolyLineModel *polyLine = command->polyLine(); while (!atEnd()) { readNext(); if (isEndElement()) break; if (isStartElement()) { if (name() == "point") readPoint(situation, polyLine); else polyLine->appendDiscardedXml(readUnknownElement()); } } } void XmlSituationReader::readPoint(SituationModel *situation, PolyLineModel *polyLine) { QPointF pos; QPointF textPos(10,10); QString text; bool laylines = 0; QStringList discarded; while (!atEnd()) { readNext(); if (isEndElement()) break; if (isStartElement()) { if (name() == "x") pos.setX(readElementText().toFloat()); else if (name() == "y") pos.setY(readElementText().toFloat()); else if (name() == "bubble_x") textPos.setX(readElementText().toFloat()); else if (name() == "bubble_y") textPos.setY(readElementText().toFloat()); else if (name() == "bubble_text") text = readElementText(); else if (name() == "laylines") laylines = (readElementText() == "1"); else discarded.append(readUnknownElement()); } } AddPointUndoCommand *command = new AddPointUndoCommand(polyLine, pos); situation->undoStack()->push(command); PointModel *point = command->point(); point->setTextPosition(textPos); point->setText(text); point->setLaylines(laylines); foreach (const QString elem, discarded) { point->appendDiscardedXml(elem); } } void XmlSituationReader::readWind(SituationModel *situation) { bool visible = false; QPointF pos; QList winds; QStringList discarded; while (!atEnd()) { readNext(); if (isEndElement()) break; if (isStartElement()) { if (name() == "visible") visible = (readElementText() == "1"); else if (name() == "x") pos.setX(readElementText().toFloat()); else if (name() == "y") pos.setY(readElementText().toFloat()); else if (name() == "direction") { winds.append(readElementText().toFloat()); } else discarded.append(readUnknownElement()); } } foreach (const QString elem, discarded) { situation->wind().appendDiscardedXml(elem); } situation->wind().setVisible(visible); foreach (qreal heading, winds) { situation->undoStack()->push(new AddWindUndoCommand(&situation->wind(), heading)); } situation->wind().setPosition(pos); } Boats::Series XmlSituationReader::series(const QString series) { int i; i = ENUM_VALUE(Boats, Series, series.toStdString().c_str()); if (i != -1) { return (Boats::Series)i; } else { return Boats::keelboat; } } boats-201908/xmlsituationreader.h000066400000000000000000000032701353032755300170330ustar00rootroot00000000000000// // C++ Interface: XmlSituationReader // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #ifndef XMLSITUATIONREADER_H #define XMLSITUATIONREADER_H #include #include "boats.h" class SituationModel; class TrackModel; class BoatModel; class PolyLineModel; class XmlSituationReader : public QXmlStreamReader { public: XmlSituationReader(SituationModel *situation); ~XmlSituationReader(); bool read(QIODevice *device); private: QString readUnknownElement(); void readSituation(); void readTrack(SituationModel *situation); void readBoat(SituationModel *situation, TrackModel *track); void readMark(SituationModel *situation); void readPolyLine(SituationModel *situation); void readPoint(SituationModel *situation, PolyLineModel *polyLine); void readWind(SituationModel *situation); Boats::Series series(const QString series); SituationModel *m_situation; }; #endif boats-201908/xmlsituationwriter.cpp000066400000000000000000000247161353032755300174500ustar00rootroot00000000000000// // C++ Implementation: XmlSituationWriter // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #include "xmlsituationwriter.h" #include #include "commontypes.h" #include "situationmodel.h" #include "trackmodel.h" #include "boatmodel.h" #include "markmodel.h" #include "polylinemodel.h" #include "pointmodel.h" extern int debugLevel; XmlSituationWriter::XmlSituationWriter(SituationModel *situation) : m_situation(situation) { setAutoFormatting(true); if (debugLevel & 1 << XML) std::cout << "new XmlSituationWriter" << std::endl; } XmlSituationWriter::~XmlSituationWriter() {} bool XmlSituationWriter::writeFile(QIODevice *device) { setDevice(device); if (debugLevel & 1 << XML) std::cout << "WriteFile" << std::endl; writeStartDocument(); writeDTD(""); writeStartElement("xmlscenario"); writeAttribute("version", "1.0"); if (!m_situation->title().isEmpty()) { writeTextElement("title", m_situation->title()); } if (!m_situation->rules().isEmpty()) { writeTextElement("rules", m_situation->rules()); } if (!m_situation->abstract().isEmpty()) { writeTextElement("abstract", m_situation->abstract()); } if (!m_situation->description().isEmpty()) { writeTextElement("description", m_situation->description()); } writeTextElement("series",ENUM_NAME(Boats, Series, m_situation->situationSeries())); writeTextElement("showlayline",QString::number(m_situation->showLayline())); writeTextElement("layline",QString::number(m_situation->laylineAngle())); writeTextElement("length",QString::number(m_situation->situationLength())); writeTextElement("look_direction",QString::number(m_situation->lookDirection())); writeTextElement("tilt",QString::number(m_situation->tilt())); writeWind(m_situation->wind()); foreach (const QString discarded, m_situation->discardedXml()) writeUnknownElement(discarded); foreach (const MarkModel *mark, m_situation->marks()) writeMark(mark); foreach (const TrackModel *track, m_situation->tracks()) writeTrack(track); foreach (const PolyLineModel *polyLine, m_situation->polyLines()) writePolyLine(polyLine); writeEndDocument(); if (debugLevel & 1 << XML) std::cout << "done WriteFile" << std::endl; return true; } void XmlSituationWriter::writeTrack(const TrackModel *track) { if (debugLevel & 1 << XML) { std::cout << "WriteTrack" << std::endl; std::cout << " color=" << track->color().name().toStdString() << std::endl; std::cout << " series=" << ENUM_NAME(Boats, Series, track->series()) << std::endl; } writeStartElement("track"); writeTextElement("color",track->color().name()); writeTextElement("series",ENUM_NAME(Boats, Series, track->series())); if (!track->showPath()) { writeTextElement("path",QString::number(track->showPath())); } if (track->followTrack()) { writeTextElement("follow_track",QString::number(track->followTrack())); } foreach (const QString discarded, track->discardedXml()) writeUnknownElement(discarded); foreach (const BoatModel *boat, track->boats()) writeBoat(boat); writeEndElement(); } void XmlSituationWriter::writeBoat(const BoatModel *boat) { if (debugLevel & 1 << XML) { std::cout << "WriteBoat" << std::endl; std::cout << " x=" << boat->position().x() << std::endl; std::cout << " y=" << boat->position().y() << std::endl; std::cout << " heading=" << boat->heading() << std::endl; } writeStartElement("boat"); writeTextElement("x",QString::number(boat->position().x())); writeTextElement("y",QString::number(boat->position().y())); writeTextElement("heading",QString::number(boat->heading())); if (boat->trim() != 0) { writeTextElement("trim",QString::number(boat->trim())); } if (boat->jibTrim() != 0) { writeTextElement("jibtrim",QString::number(boat->jibTrim())); } if (boat->spin()) { writeTextElement("spin",QString::number(boat->spin())); if (boat->spinTrim() != 0) { writeTextElement("spintrim",QString::number(boat->spinTrim())); } } if (boat->overlap() != Boats::none) { writeTextElement("overlap", FLAG_NAME(Boats, Overlap, boat->overlap())); } if (boat->flag() != Boats::noFlag) { writeTextElement("flag", ENUM_NAME(Boats, Flag, boat->flag())); } if (boat->hidden()) { writeTextElement("hidden",QString::number(boat->hidden())); } if (boat->acceleration() != Boats::constant) { writeTextElement("acceleration", ENUM_NAME(Boats, Acceleration, boat->acceleration())); } if (!boat->text().isEmpty()) { writeTextElement("bubble_x",QString::number(boat->textPosition().x())); writeTextElement("bubble_y",QString::number(boat->textPosition().y())); writeTextElement("bubble_text",boat->text()); } if (boat->laylines()) { writeTextElement("laylines", QString::number(boat->laylines())); } foreach (const QString discarded, boat->discardedXml()) writeUnknownElement(discarded); writeEndElement(); } void XmlSituationWriter::writeMark(const MarkModel *mark) { if (debugLevel & 1 << XML) { std::cout << "WriteMark" << std::endl; std::cout << " x=" << mark->position().x() << std::endl; std::cout << " y=" << mark->position().y() << std::endl; std::cout << " color=" << mark->color().name().toStdString() << std::endl; std::cout << " zone=" << mark->zone() << std::endl; std::cout << " length=" << mark->length() << std::endl; std::cout << " heading=" << mark->heading() << std::endl; std::cout << " arrowVisible=" << mark->arrowVisible() << std::endl; std::cout << " leaveToPort=" << mark->leaveToPort() << std::endl; std::cout << " labelVisible=" << mark->labelVisible() << std::endl; std::cout << " labelText=" << mark->labelText().toStdString() << std::endl; } writeStartElement("mark"); writeTextElement("x",QString::number(mark->position().x())); writeTextElement("y",QString::number(mark->position().y())); writeTextElement("color",mark->color().name()); if (mark->zone()) { writeTextElement("zone",QString::number(mark->zone())); } if (mark->length() != mark->situation()->situationLength()) { writeTextElement("length",QString::number(mark->length())); } if (!mark->text().isEmpty()) { writeTextElement("bubble_x",QString::number(mark->textPosition().x())); writeTextElement("bubble_y",QString::number(mark->textPosition().y())); writeTextElement("bubble_text",mark->text()); } if (mark->laylines()) { writeTextElement("laylines", QString::number(mark->laylines())); } writeTextElement("heading",QString::number(mark->heading())); writeTextElement("arrowVisible",QString::number(mark->arrowVisible())); writeTextElement("leaveToPort",QString::number(mark->leaveToPort())); writeTextElement("labelVisible",QString::number(mark->labelVisible())); writeTextElement("labelText",mark->labelText()); foreach (const QString discarded, mark->discardedXml()) writeUnknownElement(discarded); writeEndElement(); } void XmlSituationWriter::writePolyLine(const PolyLineModel *polyLine) { if (debugLevel & 1 << XML) { std::cout << "WritePolyLine" << std::endl; std::cout << " points=" << polyLine->size() << std::endl; } writeStartElement("polyline"); foreach (const QString discarded, polyLine->discardedXml()) writeUnknownElement(discarded); foreach (const PointModel *point, polyLine->points()) { writePoint(point); } writeEndElement(); } void XmlSituationWriter::writePoint(const PointModel *point) { if (debugLevel & 1 << XML) { std::cout << "WritePoint" << std::endl; std::cout << " x=" << point->position().x() << std::endl; std::cout << " y=" << point->position().y() << std::endl; } writeStartElement("point"); foreach (const QString discarded, point->discardedXml()) writeUnknownElement(discarded); writeTextElement("x",QString::number(point->position().x())); writeTextElement("y",QString::number(point->position().y())); if (!point->text().isEmpty()) { writeTextElement("bubble_x",QString::number(point->textPosition().x())); writeTextElement("bubble_y",QString::number(point->textPosition().y())); writeTextElement("bubble_text",point->text()); } if (point->laylines()) { writeTextElement("laylines", QString::number(point->laylines())); } writeEndElement(); } void XmlSituationWriter::writeWind(const WindModel &wind) { if (debugLevel & 1 << XML) { std::cout << "WritePoint" << std::endl; } writeStartElement("wind"); foreach (const QString discarded, wind.discardedXml()) writeUnknownElement(discarded); if (wind.visible()) { writeTextElement("visible",QString::number(wind.visible())); } writeTextElement("x",QString::number(wind.position().x())); writeTextElement("y",QString::number(wind.position().y())); for(int i = 0; i < wind.size(); ++i) { writeTextElement("direction", QString::number(wind.windAt(i))); } writeEndElement(); } void XmlSituationWriter::writeUnknownElement(const QString &discarded) { if (debugLevel & 1 << XML) std::cout << "WriteUnknownElement" << std::endl; if (!discarded.isEmpty()) { QXmlStreamReader reader(discarded); while (!reader.atEnd()) { reader.readNext(); if (reader.isStartElement() || reader.isEndElement() || reader.isCharacters()) { writeCurrentToken(reader); } } } } boats-201908/xmlsituationwriter.h000066400000000000000000000031561353032755300171100ustar00rootroot00000000000000// // C++ Interface: XmlSituationWriter // // Description: // // // Author: Thibaut GRIDEL // // Copyright (c) 2008-2011 Thibaut GRIDEL // // 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 . // // #ifndef XMLSITUATIONWRITER_H #define XMLSITUATIONWRITER_H #include class SituationModel; class TrackModel; class BoatModel; class MarkModel; class PolyLineModel; class PointModel; class WindModel; class XmlSituationWriter : public QXmlStreamWriter { public: XmlSituationWriter(SituationModel *situation); ~XmlSituationWriter(); bool writeFile(QIODevice *device); private: void writeTrack(const TrackModel *track); void writeBoat(const BoatModel *boat); void writeMark(const MarkModel *mark); void writePolyLine(const PolyLineModel *polyLine); void writePoint(const PointModel *point); void writeWind(const WindModel &wind); void writeUnknownElement(const QString &discarded); SituationModel *m_situation; }; #endif